term_test_support/
clock.rs1use std::fmt;
4use std::sync::Arc;
5use std::sync::atomic::{AtomicU64, Ordering};
6use std::time::{Duration, Instant};
7
8#[derive(Clone)]
19pub struct ManualClock {
20 base: Instant,
21 elapsed_nanos: Arc<AtomicU64>,
22}
23
24impl ManualClock {
25 pub fn new() -> Self {
27 Self {
28 base: Instant::now(),
29 elapsed_nanos: Arc::new(AtomicU64::new(0)),
30 }
31 }
32
33 pub fn advance(&self, by: Duration) {
38 let add = u64::try_from(by.as_nanos()).unwrap_or(u64::MAX);
39 self.elapsed_nanos.fetch_add(add, Ordering::Release);
40 }
41
42 pub fn now(&self) -> Instant {
44 let offset = Duration::from_nanos(self.elapsed_nanos.load(Ordering::Acquire));
45 self.base
46 .checked_add(offset)
47 .expect("virtual clock overflowed Instant range")
48 }
49
50 pub fn as_closure(self: &Arc<Self>) -> Arc<dyn Fn() -> Instant + Send + Sync> {
53 let clone = Arc::clone(self);
54 Arc::new(move || clone.now())
55 }
56}
57
58impl Default for ManualClock {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl fmt::Debug for ManualClock {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.debug_struct("ManualClock")
67 .field(
68 "elapsed",
69 &Duration::from_nanos(self.elapsed_nanos.load(Ordering::Acquire)),
70 )
71 .finish()
72 }
73}
74
75#[allow(clippy::unwrap_used)]
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn now_starts_at_construction_time() {
82 let clock = ManualClock::new();
83 let before = Instant::now();
84 let now = clock.now();
85 let after = Instant::now();
86 assert!(now <= after && now >= before - Duration::from_millis(10));
87 }
88
89 #[test]
90 fn advance_moves_now_forward_monotonically() {
91 let clock = ManualClock::new();
92 let t0 = clock.now();
93 clock.advance(Duration::from_millis(50));
94 let t1 = clock.now();
95 assert_eq!(t1.duration_since(t0), Duration::from_millis(50));
96 clock.advance(Duration::from_millis(25));
97 assert_eq!(clock.now().duration_since(t0), Duration::from_millis(75));
98 }
99
100 #[test]
101 fn clones_share_the_offset() {
102 let clock = Arc::new(ManualClock::new());
103 let other = Arc::clone(&clock);
104 std::thread::spawn(move || {
105 other.advance(Duration::from_millis(10));
106 })
107 .join()
108 .unwrap();
109 assert_eq!(
110 clock.now().duration_since(clock.base),
111 Duration::from_millis(10)
112 );
113 }
114}