use std::time::{Duration, Instant};
#[derive(Debug)]
pub(crate) struct AwakeClock {
base: Instant,
#[cfg(test)]
skew_ms: std::sync::atomic::AtomicU64,
}
impl AwakeClock {
pub(crate) fn new() -> Self {
Self {
base: Instant::now(),
#[cfg(test)]
skew_ms: std::sync::atomic::AtomicU64::new(0),
}
}
pub(crate) fn now(&self) -> Duration {
let elapsed = self.base.elapsed();
#[cfg(test)]
let elapsed = elapsed
+ Duration::from_millis(self.skew_ms.load(std::sync::atomic::Ordering::Relaxed));
elapsed
}
#[cfg(test)]
pub(crate) fn advance(&self, by: Duration) {
self.skew_ms.fetch_add(
u64::try_from(by.as_millis()).unwrap_or(u64::MAX),
std::sync::atomic::Ordering::Relaxed,
);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_new_clock_starts_near_zero() {
let clock = AwakeClock::new();
assert!(clock.now() < Duration::from_secs(60));
}
#[test]
fn now_never_runs_backwards() {
let clock = AwakeClock::new();
let first = clock.now();
let second = clock.now();
assert!(second >= first);
}
#[test]
fn advance_moves_now_forward_by_at_least_that_much() {
let clock = AwakeClock::new();
let before = clock.now();
clock.advance(Duration::from_secs(120));
assert!(clock.now() >= before + Duration::from_secs(120));
}
#[test]
fn advances_accumulate() {
let clock = AwakeClock::new();
clock.advance(Duration::from_secs(10));
clock.advance(Duration::from_secs(20));
assert!(clock.now() >= Duration::from_secs(30));
}
#[test]
fn an_absurd_advance_saturates_instead_of_wrapping() {
let clock = AwakeClock::new();
clock.advance(Duration::MAX);
assert!(clock.now() >= Duration::from_secs(1_000_000));
}
}