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