use crate::context::Host;
pub struct Clock<'a> {
host: &'a dyn Host,
}
impl<'a> Clock<'a> {
#[must_use]
pub(crate) fn new(host: &'a dyn Host) -> Self {
Self { host }
}
#[must_use]
pub fn now(&self) -> u64 {
self.host.monotonic_nanos()
}
pub fn start(&self) -> Timer<'a> {
Timer {
host: self.host,
start: self.host.monotonic_nanos(),
}
}
pub fn sleep_ms(&self, ms: u64) {
self.host.sleep_nanos(ms.saturating_mul(1_000_000));
}
}
#[must_use = "a timer measures nothing until `.elapsed_ms()` is read"]
pub struct Timer<'a> {
host: &'a dyn Host,
start: u64,
}
impl Timer<'_> {
#[must_use]
pub fn elapsed_ms(&self) -> u64 {
self.host.monotonic_nanos().saturating_sub(self.start) / 1_000_000
}
}
#[cfg(test)]
mod tests {
use crate::Context;
use crate::testing::MockHost;
#[test]
fn timer_elapsed_ms_from_scripted_clock() {
let host = MockHost::new().with_clock(&[0, 5_000_000]);
let ctx = Context::new(&host, "/w".into(), "s".into());
let t = ctx.clock().start();
assert_eq!(t.elapsed_ms(), 5);
}
#[test]
fn now_passes_through_and_saturates_non_increasing() {
let host = MockHost::new().with_clock(&[10, 3]); let ctx = Context::new(&host, "/w".into(), "s".into());
let t = ctx.clock().start(); assert_eq!(t.elapsed_ms(), 0); }
#[test]
fn empty_clock_script_reads_zero() {
let host = MockHost::new();
let ctx = Context::new(&host, "/w".into(), "s".into());
assert_eq!(ctx.clock().now(), 0);
}
#[test]
fn sleep_ms_converts_to_nanos_and_records() {
let host = MockHost::new();
let ctx = Context::new(&host, "/w".into(), "s".into());
ctx.clock().sleep_ms(5);
ctx.clock().sleep_ms(0);
assert_eq!(host.recorded_sleeps(), vec![5_000_000, 0]);
}
}