Skip to main content

otter_support/
fake-time.rs

1// Copyright 2020-2021 Ian Jackson and contributors to Otter
2// SPDX-License-Identifier: AGPL-3.0-or-later
3// There is NO WARRANTY.
4
5use 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}