use std::cell::Cell;
use std::time::Duration;
thread_local! {
static SIMULATED_CLOCK: Cell<u64> = const { Cell::new(u64::MAX) }; }
pub struct TestClock;
impl TestClock {
pub fn init(initial_ms: u64) {
SIMULATED_CLOCK.with(|c| c.set(initial_ms));
}
pub fn advance(delta_ms: u64) {
SIMULATED_CLOCK.with(|c| {
let current = c.get();
let base = if current == u64::MAX { 0 } else { current };
c.set(base + delta_ms);
});
}
pub fn reset() {
SIMULATED_CLOCK.with(|c| c.set(u64::MAX));
}
pub fn current_time_ms() -> u64 {
SIMULATED_CLOCK.with(|c| {
let millis = c.get();
if millis == u64::MAX {
c.set(0);
0
} else {
millis
}
})
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct Instant {
millis_since_start: u64,
}
impl Instant {
pub fn now() -> Self {
Self {
millis_since_start: TestClock::current_time_ms(),
}
}
pub fn elapsed(&self, now: &Self) -> Duration {
if now.millis_since_start >= self.millis_since_start {
Duration::from_millis(now.millis_since_start - self.millis_since_start)
} else {
Duration::ZERO
}
}
pub fn until(&self, now: &Self) -> Duration {
if self.millis_since_start >= now.millis_since_start {
Duration::from_millis(self.millis_since_start - now.millis_since_start)
} else {
Duration::ZERO
}
}
pub fn is_after(&self, other: &Self) -> bool {
self.millis_since_start > other.millis_since_start
}
pub fn add_millis(&mut self, millis: u32) {
self.millis_since_start = self.millis_since_start.saturating_add(millis as u64);
}
pub fn subtract_millis(&mut self, millis: u32) {
self.millis_since_start = self.millis_since_start.saturating_sub(millis as u64);
}
pub fn inner(&self) -> std::time::Instant {
panic!("inner() is not available in test backend. Use the Instant API directly.");
}
}