use std::sync::{Arc, Mutex};
use chrono::{DateTime, Duration, Utc};
use crate::Clock;
#[derive(Clone)]
pub struct SystemClock;
impl SystemClock {
#[allow(clippy::new_ret_no_self)]
pub fn new() -> Arc<dyn Clock> {
Arc::new(Self)
}
}
impl Default for SystemClock {
fn default() -> Self {
Self
}
}
impl Clock for SystemClock {
fn now(&self) -> DateTime<Utc> {
Utc::now()
}
}
#[derive(Clone)]
pub struct TestClock {
pub(crate) time: Arc<Mutex<DateTime<Utc>>>,
}
impl TestClock {
#[allow(clippy::new_ret_no_self)]
pub fn new() -> Arc<dyn Clock> {
Arc::new(Self {
time: Arc::new(Mutex::new(Utc::now())),
})
}
pub fn new_at(time: DateTime<Utc>) -> Arc<dyn Clock> {
Arc::new(Self {
time: Arc::new(Mutex::new(time)),
})
}
pub fn build_for_testing() -> Self {
Self {
time: Arc::new(Mutex::new(Utc::now())),
}
}
pub fn build_for_testing_at(time: DateTime<Utc>) -> Self {
Self {
time: Arc::new(Mutex::new(time)),
}
}
pub fn advance(&self, duration: Duration) {
let mut time = self.time.lock().unwrap();
*time += duration;
}
pub fn set(&self, new_time: DateTime<Utc>) {
let mut time = self.time.lock().unwrap();
*time = new_time;
}
}
impl Default for TestClock {
fn default() -> Self {
Self {
time: Arc::new(Mutex::new(Utc::now())),
}
}
}
impl Clock for TestClock {
fn now(&self) -> DateTime<Utc> {
*self.time.lock().unwrap()
}
}
#[cfg(test)]
mod tests {
use std::{
sync::{Arc, Mutex},
thread,
};
use chrono::TimeZone;
use super::*;
#[test]
fn test_system_clock_new() {
let clock = SystemClock::new();
let _ = clock.now(); }
#[test]
fn test_system_clock_returns_real_time() {
let clock = SystemClock::new();
let before = Utc::now();
let clock_time = clock.now();
let after = Utc::now();
assert!(clock_time >= before - Duration::seconds(1));
assert!(clock_time <= after + Duration::seconds(1));
}
#[test]
fn test_system_clock_is_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<SystemClock>();
assert_sync::<SystemClock>();
}
#[test]
fn test_test_clock_new() {
let clock = TestClock::new();
let _ = clock.now(); }
#[test]
fn test_test_clock_new_at() {
let time = Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap();
let clock = TestClock::new_at(time);
assert_eq!(clock.now(), time);
}
#[test]
fn test_test_clock_advance() {
let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap();
let test_clock = TestClock {
time: Arc::new(Mutex::new(start_time)),
};
test_clock.advance(Duration::hours(5));
let expected = start_time + Duration::hours(5);
assert_eq!(test_clock.now(), expected);
}
#[test]
fn test_test_clock_advance_is_additive() {
let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap();
let test_clock = TestClock {
time: Arc::new(Mutex::new(start_time)),
};
test_clock.advance(Duration::hours(2));
test_clock.advance(Duration::hours(3));
let expected = start_time + Duration::hours(5);
assert_eq!(test_clock.now(), expected);
}
#[test]
fn test_test_clock_set() {
let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap();
let test_clock = TestClock {
time: Arc::new(Mutex::new(start_time)),
};
let new_time = Utc.with_ymd_and_hms(2025, 6, 15, 12, 30, 0).unwrap();
test_clock.set(new_time);
assert_eq!(test_clock.now(), new_time);
}
#[test]
fn test_test_clock_is_clone() {
let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap();
let test_clock = TestClock {
time: Arc::new(Mutex::new(start_time)),
};
let clock2 = test_clock.clone();
test_clock.advance(Duration::hours(1));
assert_eq!(clock2.now(), start_time + Duration::hours(1));
}
#[test]
fn test_test_clock_concurrent_access() {
let start_time = Utc.with_ymd_and_hms(2025, 1, 1, 0, 0, 0).unwrap();
let test_clock = TestClock {
time: Arc::new(Mutex::new(start_time)),
};
let clock1 = test_clock.clone();
let clock2 = test_clock.clone();
let handle1 = thread::spawn(move || {
clock1.advance(Duration::hours(1));
});
let handle2 = thread::spawn(move || {
clock2.advance(Duration::hours(2));
});
handle1.join().unwrap();
handle2.join().unwrap();
assert_eq!(test_clock.now(), start_time + Duration::hours(3));
}
#[test]
fn test_arc_dyn_clock_pattern() {
let clock: Arc<dyn Clock> = SystemClock::new();
let _ = clock.now();
let test_clock: Arc<dyn Clock> = TestClock::new();
let _ = test_clock.now();
}
}