From 0d6094c8b10da141d048ec23133df4525befbd44 Mon Sep 17 00:00:00 2001 From: "Andres O. Vela" Date: Sun, 29 Oct 2023 19:49:52 +0100 Subject: [PATCH] time: add MockDriver for testing purposes --- embassy-time/Cargo.toml | 3 ++ embassy-time/src/driver_mock.rs | 73 +++++++++++++++++++++++++++++++++ embassy-time/src/lib.rs | 6 +++ 3 files changed, 82 insertions(+) create mode 100644 embassy-time/src/driver_mock.rs diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 62404863..8b5d31ee 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -59,6 +59,9 @@ generic-queue-32 = ["generic-queue"] generic-queue-64 = ["generic-queue"] generic-queue-128 = ["generic-queue"] +# Create a `MockDriver` that can be manually advanced for testing purposes. +mock-driver = ["tick-hz-1_000_000"] + # Set the `embassy_time` tick rate. # # At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs new file mode 100644 index 00000000..4ae5a2d9 --- /dev/null +++ b/embassy-time/src/driver_mock.rs @@ -0,0 +1,73 @@ +use core::cell::Cell; + +use critical_section::Mutex as CsMutex; + +use crate::driver::{AlarmHandle, Driver}; +use crate::{Duration, Instant}; + +/// A mock driver that can be manually advanced. +/// This is useful for testing code that works with [`Instant`] and [`Duration`]. +/// +/// This driver cannot currently be used to test runtime functionality, such as +/// timers, delays, etc. +/// +/// # Example +/// +/// ```ignore +/// fn has_a_second_passed(reference: Instant) -> bool { +/// Instant::now().duration_since(reference) > Duration::from_secs(1) +/// } +/// +/// fn test_second_passed() { +/// let driver = embassy_time::MockDriver::get(); +/// let reference = Instant::now(); +/// assert_eq!(false, has_a_second_passed(reference)); +/// driver.advance(Duration::from_secs(1)); +/// assert_eq!(true, has_a_second_passed(reference)); +/// } +/// ``` +pub struct MockDriver { + now: CsMutex>, +} + +crate::time_driver_impl!(static DRIVER: MockDriver = MockDriver { + now: CsMutex::new(Cell::new(Instant::from_ticks(0))), +}); + +impl MockDriver { + /// Gets a reference to the global mock driver. + pub fn get() -> &'static MockDriver { + &DRIVER + } + + /// Sets the current time of the mock driver. + pub fn set_current_time(&self, now: Instant) { + critical_section::with(|cs| self.now.borrow(cs).set(now)) + } + + /// Advances the time by the specified [`Duration`]. + pub fn advance(&self, duration: Duration) { + critical_section::with(|cs| { + let now = self.now.borrow(cs).get().as_ticks(); + self.now.borrow(cs).set(Instant::from_ticks(now + duration.as_ticks())); + }); + } +} + +impl Driver for MockDriver { + fn now(&self) -> u64 { + critical_section::with(|cs| self.now.borrow(cs).get().as_micros() as u64) + } + + unsafe fn allocate_alarm(&self) -> Option { + unimplemented!("MockDriver does not support runtime features that require an executor"); + } + + fn set_alarm_callback(&self, _alarm: AlarmHandle, _callback: fn(*mut ()), _ctx: *mut ()) { + unimplemented!("MockDriver does not support runtime features that require an executor"); + } + + fn set_alarm(&self, _alarm: AlarmHandle, _timestamp: u64) -> bool { + unimplemented!("MockDriver does not support runtime features that require an executor"); + } +} diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 8f57eabc..45c1e882 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -15,6 +15,12 @@ pub mod queue; mod tick; mod timer; +#[cfg(feature = "mock-driver")] +mod driver_mock; + +#[cfg(feature = "mock-driver")] +pub use driver_mock::MockDriver; + #[cfg(feature = "std")] mod driver_std; #[cfg(feature = "wasm")]