tor_rtmock/
time_core.rs

1//! [`MockTimeCore`] and [`MockCoarseTimeProvider`]
2
3use derive_deftly::{define_derive_deftly, Deftly};
4use std::time::{Duration, Instant, SystemTime};
5use tor_rtcompat::{CoarseDuration, CoarseInstant};
6use tor_rtcompat::{CoarseTimeProvider, RealCoarseTimeProvider};
7
8define_derive_deftly! {
9    /// Derive getters for struct fields.
10    ///
11    /// Like `amplify::Getters` but `pub(crate)`.
12    ///
13    /// TODO add this feature to `amplify`.
14    CrateGetters:
15    ${define REF ${if not(fmeta(getter_copy)) { & }}}
16    $(
17        impl $ttype {
18            ${fattrs doc}
19            pub(crate) fn $fname(&self) -> $REF $ftype {
20                $REF self.$fname
21            }
22        }
23    )
24}
25
26/// Mock time, as a value
27///
28/// Contains an `Instant`, `SystemTime` and `CoarseInstant`.
29///
30/// Arranges that they are all moved in step,
31/// unless explicitly requested otherwise.
32#[derive(Clone, Debug, Deftly)]
33#[derive_deftly(CrateGetters)]
34pub(crate) struct MockTimeCore {
35    /// Current time (monotonic clock)
36    #[deftly(getter_copy)]
37    instant: Instant,
38
39    /// Current wallclock time
40    #[deftly(getter_copy)]
41    wallclock: SystemTime,
42
43    /// Coarse time tracking
44    coarse: MockCoarseTimeProvider,
45}
46
47impl MockTimeCore {
48    /// Create a new `MockTimeCore`
49    pub(crate) fn new(instant: Instant, wallclock: SystemTime) -> Self {
50        MockTimeCore {
51            instant,
52            coarse: MockCoarseTimeProvider::new(),
53            wallclock,
54        }
55    }
56
57    /// Advance by a duration
58    ///
59    /// All three time values are advanced in step.
60    pub(crate) fn advance(&mut self, d: Duration) {
61        self.instant += d;
62        self.wallclock += d;
63        self.coarse.advance(d);
64    }
65
66    /// Warp the wallclock (only)
67    //
68    // We *could* just expose the field for mutable access,
69    // but this way seems more regular.
70    pub(crate) fn jump_wallclock(&mut self, new_wallclock: SystemTime) {
71        self.wallclock = new_wallclock;
72    }
73}
74
75/// A mockable [`CoarseTimeProvider`]
76#[derive(Clone, Debug)]
77pub(crate) struct MockCoarseTimeProvider {
78    /// Starting point
79    started: CoarseInstant,
80
81    /// How much we have advanced
82    ///
83    /// We track this as a `Duration`, not a [`CoarseDuration`] (or [`CoarseInstant`])
84    /// to avoid accumulating rounding errors,
85    /// which might otherwise cause the mocked `Instant` and `CoarseInstant`
86    /// clocks to run at noticeably different *rates*.
87    elapsed: Duration,
88}
89
90impl MockCoarseTimeProvider {
91    /// Start a new [`MockCoarseTimeProvider`]
92    pub(crate) fn new() -> Self {
93        MockCoarseTimeProvider {
94            started: RealCoarseTimeProvider::new().now_coarse(),
95            elapsed: Duration::ZERO,
96        }
97    }
98
99    /// Advance the mocked coarse time by `dur`
100    pub(crate) fn advance(&mut self, dur: Duration) {
101        self.elapsed += dur;
102    }
103}
104
105impl CoarseTimeProvider for MockCoarseTimeProvider {
106    fn now_coarse(&self) -> CoarseInstant {
107        self.started + CoarseDuration::from(self.elapsed)
108    }
109}