-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from niclashoyer/timers
Add mock for timers
- Loading branch information
Showing
6 changed files
with
188 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
msrv = "1.56" | ||
msrv = "1.60" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
//! Provides a mocked [embedded_time::Clock] that can be used for host-side testing | ||
//! crates that use [embedded_hal::timer]. | ||
//! | ||
//! The provided [embedded_time::Clock] implementation is thread safe and can be freely | ||
//! skipped forward with nanosecond precision. | ||
//! | ||
//! # Usage | ||
//! | ||
//! ```rust | ||
//! use embedded_hal::timer::CountDown; | ||
//! use embedded_time::duration::*; | ||
//! use embedded_hal_mock::timer::MockClock; | ||
//! | ||
//! let mut clock = MockClock::new(); | ||
//! let mut timer = clock.get_timer(); | ||
//! timer.start(100.nanoseconds()); | ||
//! // hand over timer to embedded-hal based driver | ||
//! // continue to tick clock | ||
//! 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(())); | ||
//! ``` | ||
|
||
use std::{ | ||
convert::Infallible, | ||
sync::{ | ||
atomic::{AtomicU64, Ordering}, | ||
Arc, | ||
}, | ||
}; | ||
use void::Void; | ||
|
||
use embedded_hal::timer::{Cancel, CountDown, Periodic}; | ||
pub use embedded_time::Clock; | ||
use embedded_time::{clock, duration::*, fraction::Fraction, Instant}; | ||
|
||
/// A simulated clock that can be used in tests. | ||
#[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 { | ||
/// Creates a new simulated clock. | ||
pub fn new() -> Self { | ||
Self::default() | ||
} | ||
|
||
/// Returns the number of elapsed nanoseconds. | ||
pub fn elapsed(&self) -> Nanoseconds<u64> { | ||
Nanoseconds(self.ticks.load(Ordering::Relaxed)) | ||
} | ||
|
||
/// Forward the clock by `ticks` amount. | ||
pub fn tick<T>(&mut self, ticks: T) | ||
where | ||
T: Into<Nanoseconds<u64>>, | ||
{ | ||
self.ticks.fetch_add(ticks.into().0, Ordering::Relaxed); | ||
} | ||
|
||
/// Get a new timer based on the clock. | ||
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, | ||
} | ||
} | ||
} | ||
|
||
/// A simulated timer that can be used in tests. | ||
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(())); | ||
} | ||
} |