Skip to main content

embassy_mock/time/
timer.rs

1//! Traits and mocked types to allow unit testing functions that require an
2//! [`embassy_time::Timer`].
3//!
4//! # Examples
5//! ```
6//! use embassy_mock::time::Timer;
7//! use embassy_time::Duration;
8//!
9//! // Generic over the `Timer` trait
10//! async fn wait_for_timer<T: Timer>() {
11//!     T::after(Duration::from_secs(1)).await;
12//!     // Do something..
13//! }
14//!
15//! // The real task that runs on the Embassy executor.
16//! #[embassy_executor::task]
17//! async fn some_task() {
18//!     wait_for_timer::<embassy_time::Timer>().await;
19//! }
20//!
21//! # test_timer_after();
22//! // The unit tests that use the `MockTimer`.
23//! #[cfg(test)]
24//! mod tests {
25//!     use super::*;
26//! # }
27//!     use embassy_futures::block_on;
28//!     use embassy_mock::time::MockTimer;
29//!
30//!     #[test]
31//!     # fn hidden_fake_test(){}
32//!     fn test_timer_after() {
33//!         block_on(wait_for_timer::<MockTimer>());
34//!     }
35//! # mod closing {
36//! }
37//! ```
38
39use core::{
40    future::Future,
41    pin::Pin,
42    task::{Context, Poll},
43};
44use embassy_time::{Duration, Timer as EmbassyTimer};
45
46/// The trait to replace the [`embassy_time::Timer`] in code to allow the [`MockTimer`] to
47/// be used in its place for tests.
48pub trait Timer: Future {
49    /// Wrapper for [`embassy_time::Timer::after()`].
50    fn after(duration: Duration) -> Self;
51}
52
53impl Timer for EmbassyTimer {
54    /// Expire after specified [`Duration`].
55    /// This can be used as a sleep abstraction.
56    ///
57    /// Example:
58    /// ``` no_run
59    /// # fn foo() {}
60    /// use embassy_time::{Duration, Timer};
61    ///
62    /// #[embassy_executor::task]
63    /// async fn demo_sleep_seconds() {
64    ///     // suspend this task for one second.
65    ///     Timer::after(Duration::from_secs(1)).await;
66    /// }
67    /// ```
68    fn after(duration: Duration) -> Self {
69        Self::after(duration)
70    }
71}
72
73/// A mocked version of [`embassy_time::Timer`] that can be used in its place for unit tests.
74///
75/// This mocked version just immediately returns [`Poll::Ready`] when `await`'ed on.
76///
77/// # Examples
78///
79/// ```
80/// use embassy_futures::block_on;
81/// use embassy_mock::time::{MockTimer, Timer};
82/// use embassy_time::Duration;
83///
84/// let timer = MockTimer::after(Duration::from_secs(1));
85/// block_on(timer);
86/// ```
87#[derive(Debug, PartialEq, Eq)]
88pub struct MockTimer;
89
90impl Future for MockTimer {
91    type Output = ();
92
93    /// Immediately return [`Poll::Ready`].
94    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
95        Poll::Ready(())
96    }
97}
98
99impl Timer for MockTimer {
100    /// Create a [`MockTimer`] that can be used to unit test code.
101    ///
102    /// # Examples
103    /// ```
104    /// use embassy_mock::time::Timer;
105    /// use embassy_time::Duration;
106    ///
107    /// async fn production_code<T: Timer>() {
108    ///     // Do something...
109    ///     T::after(Duration::from_millis(100)).await;
110    ///     // Do something else...
111    /// }
112    ///
113    /// # test_creating_timer();
114    /// // The unit tests that use the `MockTimer`
115    /// #[cfg(test)]
116    /// mod tests {
117    ///     use super::*;
118    /// # }
119    ///     use embassy_futures::block_on;
120    ///     use embassy_mock::time::MockTimer;
121    ///
122    ///     #[test]
123    ///     # fn hidden_fake_test(){}
124    ///     fn test_creating_timer() {
125    ///         block_on(production_code::<MockTimer>());
126    ///     }
127    /// # mod closing {
128    /// }
129    /// ```
130    fn after(_duration: Duration) -> Self {
131        Self {}
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138    use embassy_futures::block_on;
139
140    #[test]
141    fn can_create_timer_with_after() {
142        let timer = MockTimer::after(Duration::from_secs(1));
143
144        assert_eq!(timer, MockTimer);
145    }
146
147    #[test]
148    fn can_timer_impls_future() {
149        let timer = MockTimer::after(Duration::from_secs(1));
150
151        block_on(timer);
152    }
153}