aboutsummaryrefslogtreecommitdiff
path: root/crates/tor-rtmock/src/time_core.rs
blob: 35fdb60efc35d7c2ad79595979cb0d2cb4c9b1aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! [`MockTimeCore`] and [`MockCoarseTimeProvider`]

use derive_deftly::{Deftly, define_derive_deftly};
use tor_rtcompat::{CoarseDuration, CoarseInstant};
use tor_rtcompat::{CoarseTimeProvider, RealCoarseTimeProvider};
use web_time_compat::{Duration, Instant, SystemTime};

define_derive_deftly! {
    /// Derive getters for struct fields.
    ///
    /// Like `amplify::Getters` but `pub(crate)`.
    ///
    /// TODO add this feature to `amplify`.
    CrateGetters:

    ${define REF ${if not(fmeta(getter_copy)) { & }}}
    impl $ttype {
        $(
            ${fattrs doc}
            pub(crate) fn $fname(&self) -> $REF $ftype {
                $REF self.$fname
            }
        )
    }
}

/// Mock time, as (mostly) a value
///
/// Contains an `Instant`, `SystemTime` and `CoarseTimeProvider`.
///
/// Arranges that they are all moved in step,
/// unless explicitly requested otherwise.
#[derive(Clone, Debug, Deftly)]
#[derive_deftly(CrateGetters)]
pub(crate) struct MockTimeCore {
    /// Current time (monotonic clock)
    #[deftly(getter_copy)]
    instant: Instant,

    /// Current wallclock time
    #[deftly(getter_copy)]
    wallclock: SystemTime,

    /// Coarse time tracking
    coarse: MockCoarseTimeProvider,
}

impl MockTimeCore {
    /// Create a new `MockTimeCore`
    pub(crate) fn new(instant: Instant, wallclock: SystemTime) -> Self {
        MockTimeCore {
            instant,
            coarse: MockCoarseTimeProvider::new(),
            wallclock,
        }
    }

    /// Advance by a duration
    ///
    /// All three time values are advanced in step.
    pub(crate) fn advance(&mut self, d: Duration) {
        self.instant += d;
        self.wallclock += d;
        self.coarse.advance(d);
    }

    /// Warp the wallclock (only)
    //
    // We *could* just expose the field for mutable access,
    // but this way seems more regular.
    pub(crate) fn jump_wallclock(&mut self, new_wallclock: SystemTime) {
        self.wallclock = new_wallclock;
    }
}

/// A mockable [`CoarseTimeProvider`]
#[derive(Clone, Debug)]
pub(crate) struct MockCoarseTimeProvider {
    /// Starting point
    started: CoarseInstant,

    /// How much we have advanced
    ///
    /// We track this as a `Duration`, not a [`CoarseDuration`] (or [`CoarseInstant`])
    /// to avoid accumulating rounding errors,
    /// which might otherwise cause the mocked `Instant` and `CoarseInstant`
    /// clocks to run at noticeably different *rates*.
    elapsed: Duration,
}

impl MockCoarseTimeProvider {
    /// Start a new [`MockCoarseTimeProvider`]
    pub(crate) fn new() -> Self {
        MockCoarseTimeProvider {
            started: RealCoarseTimeProvider::new().now_coarse(),
            elapsed: Duration::ZERO,
        }
    }

    /// Advance the mocked coarse time by `dur`
    pub(crate) fn advance(&mut self, dur: Duration) {
        self.elapsed += dur;
    }
}

impl CoarseTimeProvider for MockCoarseTimeProvider {
    fn now_coarse(&self) -> CoarseInstant {
        self.started + CoarseDuration::from(self.elapsed)
    }
}