use std::{
convert::Infallible,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
};
use eh0 as embedded_hal;
use embedded_hal::timer::{Cancel, CountDown, Periodic};
pub use embedded_time::Clock;
use embedded_time::{clock, duration::*, fraction::Fraction, Instant};
use void::Void;
#[derive(Clone, Debug)]
pub struct MockClock {
ticks: Arc<AtomicU64>,
}
impl Clock for MockClock {
type T = u64;
const SCALING_FACTOR: Fraction = Fraction::new(1, 1_000_000_000);
fn try_now(&self) -> Result<Instant<Self>, clock::Error> {
let ticks: u64 = self.ticks.load(Ordering::Relaxed);
Ok(Instant::<Self>::new(ticks))
}
}
impl Default for MockClock {
fn default() -> Self {
MockClock {
ticks: Arc::new(AtomicU64::new(0)),
}
}
}
impl MockClock {
pub fn new() -> Self {
Self::default()
}
pub fn elapsed(&self) -> Nanoseconds<u64> {
Nanoseconds(self.ticks.load(Ordering::Relaxed))
}
pub fn tick<T>(&mut self, ticks: T)
where
T: Into<Nanoseconds<u64>>,
{
self.ticks.fetch_add(ticks.into().0, Ordering::Relaxed);
}
pub fn get_timer(&self) -> MockTimer {
let clock = self.clone();
let duration = Nanoseconds(1);
let expiration = clock.try_now().unwrap();
MockTimer {
clock: self.clone(),
duration,
expiration,
started: false,
}
}
}
pub struct MockTimer {
clock: MockClock,
duration: Nanoseconds<u64>,
expiration: Instant<MockClock>,
started: bool,
}
impl CountDown for MockTimer {
type Time = Nanoseconds<u64>;
fn start<T>(&mut self, count: T)
where
T: Into<Self::Time>,
{
let now = self.clock.try_now().unwrap();
self.duration = count.into();
self.expiration = now + self.duration;
self.started = true;
}
fn wait(&mut self) -> nb::Result<(), Void> {
let now = self.clock.try_now().unwrap();
if self.started && now >= self.expiration {
self.expiration = now + self.duration;
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
impl Periodic for MockTimer {}
impl Cancel for MockTimer {
type Error = Infallible;
fn cancel(&mut self) -> Result<(), Self::Error> {
self.started = false;
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn count_down() {
let mut clock = MockClock::new();
let mut timer = clock.get_timer();
timer.start(100.nanoseconds());
clock.tick(50.nanoseconds());
assert_eq!(timer.wait(), Err(nb::Error::WouldBlock));
clock.tick(50.nanoseconds());
assert_eq!(timer.wait(), Ok(()));
clock.tick(50.nanoseconds());
assert_eq!(timer.wait(), Err(nb::Error::WouldBlock));
clock.tick(50.nanoseconds());
assert_eq!(timer.wait(), Ok(()));
}
}