otter_support/
fake-time.rs1use crate::prelude::*;
6
7use parking_lot::Mutex;
8
9type Millis = u32;
10type Micros = u64;
11
12#[derive(Serialize,Deserialize,Error,Debug,Clone)]
13#[error("Time is real")]
14pub struct TimeIsReal;
15
16#[derive(Deserialize,Debug,Clone,Default)]
17#[serde(transparent)]
18pub struct FakeTimeConfig(pub Option<FakeTimeSpec>);
19
20#[derive(Deserialize,Serialize,Debug,Clone,Default)]
21#[serde(into="Vec<Millis>", try_from="Vec<Millis>")]
22pub struct FakeTimeSpec(pub Option<Millis>);
23
24#[derive(Error,Debug)]
25#[error("invalid fake time: must be list of 0 or 1 numbers (ms)")]
26pub struct InvalidFakeTime;
27
28impl TryFrom<Vec<Millis>> for FakeTimeSpec {
29 type Error = InvalidFakeTime;
30 #[throws(InvalidFakeTime)]
31 fn try_from(l: Vec<Millis>) -> FakeTimeSpec {
32 FakeTimeSpec(match &*l {
33 [] => None,
34 &[ms] => Some(ms),
35 _ => throw!(InvalidFakeTime),
36 })
37 }
38}
39
40impl Into<Vec<Millis>> for FakeTimeSpec {
41 fn into(self) -> Vec<Millis> {
42 self.0.into_iter().collect()
43 }
44}
45
46#[derive(Debug)]
47pub struct GlobalClock {
48 fakeable: Option<Mutex<Option<FakeClock>>>,
49}
50
51#[derive(Debug)]
52struct FakeClock {
53 start: Instant,
54 current: Micros,
55}
56
57impl FakeClock {
58 fn from_millis(ms: Millis) -> FakeClock { FakeClock {
59 start: Instant::now(),
60 current: (ms as Micros) * 1000,
61 } }
62
63 fn from_spec(FakeTimeSpec(fspec): FakeTimeSpec) -> Option<FakeClock> {
64 fspec.map(FakeClock::from_millis)
65 }
66}
67
68impl FakeTimeConfig {
69 pub fn make_global_clock(self) -> GlobalClock {
70 let fakeable = self.0.map(|fspec| FakeClock::from_spec(fspec).into());
71 GlobalClock { fakeable }
72 }
73}
74
75impl GlobalClock {
76 pub fn now(&self) -> Instant {
77 self.now_fake().unwrap_or_else(|| Instant::now())
78 }
79
80 #[throws(as Option)]
81 fn now_fake(&self) -> Instant {
82 let mut guard = self.fakeable.as_ref()?.lock();
83 let fake = guard.as_mut()?;
84 fake.current += 1;
85 fake.start + Duration::from_micros(fake.current)
86 }
87
88 #[throws(TimeIsReal)]
89 pub fn set_fake(&self, fspec: FakeTimeSpec, _: AuthorisationSuperuser) {
90 let mut guard = self.fakeable.as_ref().ok_or(TimeIsReal)?.lock();
91 *guard = FakeClock::from_spec(fspec)
92 }
93
94 pub fn is_fake(&self) -> bool {
95 self.fakeable.is_some()
96 }
97}