quanta/
mock.rs

1use crossbeam_utils::atomic::AtomicCell;
2use std::{sync::Arc, time::Duration};
3
4/// Type which can be converted into a nanosecond representation.
5///
6/// This allows users of [`Mock`] to increment/decrement the time both with raw
7/// integer values and the more convenient [`Duration`] type.
8pub trait IntoNanoseconds {
9    /// Consumes this value, converting it to a nanosecond representation.
10    fn into_nanos(self) -> u64;
11}
12
13impl IntoNanoseconds for u64 {
14    fn into_nanos(self) -> u64 {
15        self
16    }
17}
18
19impl IntoNanoseconds for Duration {
20    fn into_nanos(self) -> u64 {
21        self.as_nanos() as u64
22    }
23}
24
25/// Controllable time source for use in tests.
26///
27/// A mocked clock allows the caller to adjust the given time backwards and forwards by whatever
28/// amount they choose.  While [`Clock`](crate::Clock) promises monotonic values for normal readings,
29/// when running in mocked mode, these guarantees do not apply: the given `Clock`/`Mock` pair are
30/// directly coupled.
31///
32/// This can be useful for not only testing code that depends on the passage of time, but also for
33/// testing that code can handle large shifts in time.
34#[derive(Debug, Clone)]
35pub struct Mock {
36    offset: Arc<AtomicCell<u64>>,
37}
38
39impl Mock {
40    pub(crate) fn new() -> Self {
41        Self {
42            offset: Arc::new(AtomicCell::new(0)),
43        }
44    }
45
46    /// Increments the time by the given amount.
47    pub fn increment<N: IntoNanoseconds>(&self, amount: N) {
48        let amount = amount.into_nanos();
49        self.offset
50            .fetch_update(|current| Some(current + amount))
51            .expect("should never return an error");
52    }
53
54    /// Decrements the time by the given amount.
55    pub fn decrement<N: IntoNanoseconds>(&self, amount: N) {
56        let amount = amount.into_nanos();
57        self.offset
58            .fetch_update(|current| Some(current - amount))
59            .expect("should never return an error");
60    }
61
62    /// Gets the current value of this `Mock`.
63    pub fn value(&self) -> u64 {
64        self.offset.load()
65    }
66}