tor_rtmock/
time_core.rs

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