use std::thread;
use std::time::{Duration, Instant};
use super::{
InterruptClock, InterruptTime, PreciseInterruptClock, PreciseUnbiasedInterruptClock,
UnbiasedInterruptClock, UnbiasedInterruptTime,
};
use crate::{Clock, Steady, Ticks, Timeline};
const ACROSS_A_TICK: Duration = Duration::from_millis(50);
const WITHIN_A_TICK: Duration = Duration::from_micros(50);
fn spin(duration: Duration) {
let started = Instant::now();
while started.elapsed() < duration {
std::hint::spin_loop();
}
}
fn never_goes_backwards<C: Steady>(clock: C) {
let mut last = clock.now();
for _ in 0..1_000 {
let next = clock.now();
assert!(next >= last, "{next:?} after {last:?}");
last = next;
}
}
fn advances_within_a_tick<C: Clock>(clock: C) {
for window in 0..20 {
let before = clock.now();
spin(WITHIN_A_TICK);
let after = clock.now();
assert!(after > before, "window {window}: still at {before:?}");
}
}
fn advances_across_a_tick<C: Clock>(clock: C) {
let before = clock.now();
thread::sleep(ACROSS_A_TICK);
assert!(clock.now() > before);
}
fn plain_lags_precise_by_at_most_a_tick<T: Timeline>(
plain: impl Clock<Timeline = T>,
precise: impl Clock<Timeline = T>,
) {
let before = precise.now();
let reading = plain.now();
let after = precise.now();
assert!(reading <= after, "{reading:?} is ahead of {after:?}");
let lag = before - reading;
assert!(
lag < Ticks::from_duration(Duration::from_millis(20)).unwrap(),
"{lag:?} behind"
);
}
const RATE_SLACK: Duration = Duration::from_millis(2);
fn agrees_with_instant<C: Clock>(clock: C) {
let outer_start = Instant::now();
let start = clock.now();
let inner_start = Instant::now();
thread::sleep(Duration::from_millis(100));
let inner = inner_start.elapsed();
let end = clock.now();
let outer = outer_start.elapsed();
let measured = (end - start).to_duration().expect("forwards");
assert!(
inner <= measured + RATE_SLACK,
"{measured:?} is shorter than {inner:?} inside it"
);
assert!(
measured <= outer + RATE_SLACK,
"{measured:?} is longer than {outer:?} around it"
);
}
#[test]
fn no_clock_goes_backwards() {
never_goes_backwards(InterruptClock);
never_goes_backwards(PreciseInterruptClock);
never_goes_backwards(UnbiasedInterruptClock);
never_goes_backwards(PreciseUnbiasedInterruptClock);
}
#[test]
fn the_precise_clocks_advance_within_a_fraction_of_a_tick() {
advances_within_a_tick(PreciseInterruptClock);
advances_within_a_tick(PreciseUnbiasedInterruptClock);
}
#[test]
fn the_plain_clocks_advance_across_a_tick() {
advances_across_a_tick(InterruptClock);
advances_across_a_tick(UnbiasedInterruptClock);
}
#[test]
fn a_plain_reading_is_a_precise_one_as_of_the_last_tick() {
plain_lags_precise_by_at_most_a_tick(InterruptClock, PreciseInterruptClock);
plain_lags_precise_by_at_most_a_tick(UnbiasedInterruptClock, PreciseUnbiasedInterruptClock);
}
#[test]
fn an_interval_measures_what_instant_measures() {
agrees_with_instant(PreciseInterruptClock);
agrees_with_instant(PreciseUnbiasedInterruptClock);
}
#[test]
fn unbiased_time_is_never_ahead_of_interrupt_time() {
let unbiased = PreciseUnbiasedInterruptClock.now();
let interrupt = PreciseInterruptClock.now();
assert!(unbiased.ticks() <= interrupt.ticks());
let unbiased = UnbiasedInterruptClock.now();
let interrupt = InterruptClock.now();
assert!(unbiased.ticks() <= interrupt.ticks());
}
#[test]
fn both_timelines_tick_every_100_nanoseconds() {
assert_eq!(InterruptTime::ticks_per_second().get(), 10_000_000);
assert_eq!(UnbiasedInterruptTime::ticks_per_second().get(), 10_000_000);
assert_eq!(
Ticks::<InterruptTime>::new(10_000_000).to_duration(),
Some(Duration::from_secs(1))
);
}
#[test]
fn a_clock_is_a_zero_sized_value() {
assert_eq!(size_of::<InterruptClock>(), 0);
assert_eq!(size_of::<PreciseInterruptClock>(), 0);
assert_eq!(size_of::<UnbiasedInterruptClock>(), 0);
assert_eq!(size_of::<PreciseUnbiasedInterruptClock>(), 0);
}