use std::sync::{Arc, Mutex};
use std::time::Duration;
use super::{ClockReading, ClockSource, TimeUnsynchronized};
use crate::bus::{RobotInstant, TimelineId};
use crate::participant::lock;
#[derive(Clone)]
pub struct TestClock {
state: Arc<Mutex<(TimelineId, u64)>>,
unsynchronized: Arc<Mutex<Option<TimeUnsynchronized>>>,
}
impl TestClock {
pub fn new() -> Self {
TestClock {
state: Arc::new(Mutex::new((TimelineId::mint(), 0))),
unsynchronized: Arc::new(Mutex::new(None)),
}
}
pub fn timeline(&self) -> TimelineId {
lock(&self.state).0
}
pub fn set_unsynchronized(&self, reason: TimeUnsynchronized) {
*lock(&self.unsynchronized) = Some(reason);
}
pub fn advance(&self, delta: Duration) {
let mut state = lock(&self.state);
let ticks = u64::try_from(delta.as_nanos()).unwrap_or(u64::MAX);
state.1 = state.1.saturating_add(ticks);
}
pub fn replace_timeline(&self) -> TimelineId {
let mut state = lock(&self.state);
state.0 = TimelineId::mint();
state.1 = 0;
state.0
}
}
impl Default for TestClock {
fn default() -> Self {
TestClock::new()
}
}
impl ClockSource for TestClock {
fn read(&self) -> ClockReading {
if let Some(reason) = *lock(&self.unsynchronized) {
return ClockReading::Unsynchronized(reason);
}
let state = lock(&self.state);
ClockReading::Synchronized(RobotInstant::new(state.0, state.1))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_test_clock_is_deterministic_and_resets_onto_a_new_timeline() {
let clock = TestClock::new();
let first = clock.timeline();
assert_eq!(
clock.read(),
ClockReading::Synchronized(RobotInstant::new(first, 0))
);
clock.advance(Duration::from_nanos(5));
clock.advance(Duration::from_nanos(7));
assert_eq!(
clock.read(),
ClockReading::Synchronized(RobotInstant::new(first, 12))
);
let second = clock.replace_timeline();
assert_ne!(second, first);
assert_eq!(
clock.read(),
ClockReading::Synchronized(RobotInstant::new(second, 0))
);
}
}