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