1#[cfg(any(test, feature = "test-clock"))]
22use core::sync::atomic::{AtomicU64, Ordering};
23
24pub trait Clock: Send + Sync + 'static {
25 fn now_ms(&self) -> u64;
27}
28
29pub struct SystemClock;
31
32impl Clock for SystemClock {
33 fn now_ms(&self) -> u64 {
34 std::time::SystemTime::now()
35 .duration_since(std::time::UNIX_EPOCH)
36 .map(|d| d.as_millis() as u64)
37 .unwrap_or(0)
38 }
39}
40
41#[cfg(any(test, feature = "test-clock"))]
42pub mod testing {
43 use super::*;
44 use std::sync::Arc;
45
46 #[derive(Clone, Default)]
48 pub struct MockClock {
49 ms: Arc<AtomicU64>,
50 }
51
52 impl MockClock {
53 pub fn new(start_ms: u64) -> Self {
54 MockClock {
55 ms: Arc::new(AtomicU64::new(start_ms)),
56 }
57 }
58 pub fn advance(&self, by_ms: u64) {
59 self.ms.fetch_add(by_ms, Ordering::AcqRel);
60 }
61 pub fn set(&self, to_ms: u64) {
62 self.ms.store(to_ms, Ordering::Release);
63 }
64 }
65
66 impl Clock for MockClock {
67 fn now_ms(&self) -> u64 {
68 self.ms.load(Ordering::Acquire)
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use testing::MockClock;
77
78 #[test]
79 fn system_clock_returns_nonzero() {
80 let clock = SystemClock;
81 let now = clock.now_ms();
82 assert!(now > 1_700_000_000_000, "current time after 2023-11");
83 }
84
85 #[test]
86 fn mock_clock_starts_at_seed() {
87 let clock = MockClock::new(42);
88 assert_eq!(clock.now_ms(), 42);
89 }
90
91 #[test]
92 fn mock_clock_advance() {
93 let clock = MockClock::new(100);
94 clock.advance(50);
95 assert_eq!(clock.now_ms(), 150);
96 }
97
98 #[test]
99 fn mock_clock_set() {
100 let clock = MockClock::new(100);
101 clock.set(999);
102 assert_eq!(clock.now_ms(), 999);
103 }
104}