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}