aboutsummaryrefslogtreecommitdiff
path: root/embassy-time
diff options
context:
space:
mode:
authorChris Price <[email protected]>2024-01-08 21:05:43 +0000
committerChris Price <[email protected]>2024-01-09 15:17:25 +0000
commit8dab88f96d0873851557281128aeb887a10d7c37 (patch)
treef149dec9fab986658bddc130547a8435291780c8 /embassy-time
parent49ee0564ed715edfc1c86c8cced57fd488f5c52a (diff)
Merge TestDriver into MockDriver
Diffstat (limited to 'embassy-time')
-rw-r--r--embassy-time/src/driver_mock.rs134
1 files changed, 116 insertions, 18 deletions
diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs
index c255615c7..128f48af9 100644
--- a/embassy-time/src/driver_mock.rs
+++ b/embassy-time/src/driver_mock.rs
@@ -1,4 +1,4 @@
1use core::cell::Cell; 1use core::cell::RefCell;
2 2
3use critical_section::Mutex as CsMutex; 3use critical_section::Mutex as CsMutex;
4 4
@@ -8,7 +8,7 @@ use crate::{Duration, Instant};
8/// A mock driver that can be manually advanced. 8/// A mock driver that can be manually advanced.
9/// This is useful for testing code that works with [`Instant`] and [`Duration`]. 9/// This is useful for testing code that works with [`Instant`] and [`Duration`].
10/// 10///
11/// This driver cannot currently be used to test runtime functionality, such as 11/// This driver can also be used to test runtime functionality, such as
12/// timers, delays, etc. 12/// timers, delays, etc.
13/// 13///
14/// # Example 14/// # Example
@@ -26,43 +26,141 @@ use crate::{Duration, Instant};
26/// assert_eq!(true, has_a_second_passed(reference)); 26/// assert_eq!(true, has_a_second_passed(reference));
27/// } 27/// }
28/// ``` 28/// ```
29pub struct MockDriver { 29pub struct MockDriver(CsMutex<RefCell<InnerMockDriver>>);
30 now: CsMutex<Cell<Instant>>,
31}
32 30
33crate::time_driver_impl!(static DRIVER: MockDriver = MockDriver { 31crate::time_driver_impl!(static DRIVER: MockDriver = MockDriver::new());
34 now: CsMutex::new(Cell::new(Instant::from_ticks(0))),
35});
36 32
37impl MockDriver { 33impl MockDriver {
34 /// Creates a new mock driver.
35 pub const fn new() -> Self {
36 Self(CsMutex::new(RefCell::new(InnerMockDriver::new())))
37 }
38
38 /// Gets a reference to the global mock driver. 39 /// Gets a reference to the global mock driver.
39 pub fn get() -> &'static MockDriver { 40 pub fn get() -> &'static MockDriver {
40 &DRIVER 41 &DRIVER
41 } 42 }
42 43
43 /// Advances the time by the specified [`Duration`]. 44 /// Resets the internal state of the mock driver
44 pub fn advance(&self, duration: Duration) { 45 /// This will clear and deallocate all alarms, and reset the current time to 0.
46 fn reset(&self) {
45 critical_section::with(|cs| { 47 critical_section::with(|cs| {
46 let now = self.now.borrow(cs).get().as_ticks(); 48 self.0.borrow(cs).replace(InnerMockDriver::new());
47 self.now.borrow(cs).set(Instant::from_ticks(now + duration.as_ticks()));
48 }); 49 });
49 } 50 }
51
52 /// Advances the time by the specified [`Duration`].
53 /// Calling any alarm callbacks that are due.
54 pub fn advance(&self, duration: Duration) {
55 let notify = {
56 critical_section::with(|cs| {
57 let mut inner = self.0.borrow_ref_mut(cs);
58
59 // TODO: store as Instant?
60 let now = (Instant::from_ticks(inner.now) + duration).as_ticks();
61
62
63 inner.now = now;
64
65 if inner.alarm <= now {
66 inner.alarm = u64::MAX;
67
68 Some((inner.callback, inner.ctx))
69 } else {
70 None
71 }
72 })
73 };
74
75 if let Some((callback, ctx)) = notify {
76 (callback)(ctx);
77 }
78 }
50} 79}
51 80
52impl Driver for MockDriver { 81impl Driver for MockDriver {
53 fn now(&self) -> u64 { 82 fn now(&self) -> u64 {
54 critical_section::with(|cs| self.now.borrow(cs).get().as_ticks() as u64) 83 critical_section::with(|cs| self.0.borrow_ref(cs).now)
55 } 84 }
56 85
57 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 86 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
58 unimplemented!("MockDriver does not support runtime features that require an executor"); 87 Some(AlarmHandle::new(0))
88 }
89
90 fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
91 critical_section::with(|cs| {
92 let mut inner = self.0.borrow_ref_mut(cs);
93
94 inner.callback = callback;
95 inner.ctx = ctx;
96 });
97 }
98
99 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool {
100 critical_section::with(|cs| {
101 let mut inner = self.0.borrow_ref_mut(cs);
102
103 if timestamp <= inner.now {
104 false
105 } else {
106 inner.alarm = timestamp;
107 true
108 }
109 })
110 }
111}
112
113struct InnerMockDriver {
114 now: u64,
115 alarm: u64,
116 callback: fn(*mut ()),
117 ctx: *mut (),
118}
119
120impl InnerMockDriver {
121 const fn new() -> Self {
122 Self {
123 now: 0,
124 alarm: u64::MAX,
125 callback: Self::noop,
126 ctx: core::ptr::null_mut(),
127 }
128 }
129
130 fn noop(_ctx: *mut ()) {}
131}
132
133unsafe impl Send for InnerMockDriver {}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_advance() {
141 let driver = MockDriver::get();
142 let reference = driver.now();
143 driver.advance(Duration::from_secs(1));
144 assert_eq!(Duration::from_secs(1).as_ticks(), driver.now() - reference);
59 } 145 }
60 146
61 fn set_alarm_callback(&self, _alarm: AlarmHandle, _callback: fn(*mut ()), _ctx: *mut ()) { 147 #[test]
62 unimplemented!("MockDriver does not support runtime features that require an executor"); 148 fn test_set_alarm_not_in_future() {
149 let driver = MockDriver::get();
150 let alarm = unsafe { AlarmHandle::new(0) };
151 assert_eq!(false, driver.set_alarm(alarm, driver.now()));
63 } 152 }
64 153
65 fn set_alarm(&self, _alarm: AlarmHandle, _timestamp: u64) -> bool { 154 #[test]
66 unimplemented!("MockDriver does not support runtime features that require an executor"); 155 fn test_alarm() {
156 let driver = MockDriver::get();
157 let alarm = unsafe { driver.allocate_alarm() }.expect("No alarms available");
158 static mut CALLBACK_CALLED: bool = false;
159 let ctx = &mut () as *mut ();
160 driver.set_alarm_callback(alarm, |_| unsafe { CALLBACK_CALLED = true }, ctx);
161 driver.set_alarm(alarm, driver.now() + 1);
162 assert_eq!(false, unsafe { CALLBACK_CALLED });
163 driver.advance(Duration::from_secs(1));
164 assert_eq!(true, unsafe { CALLBACK_CALLED });
67 } 165 }
68} 166}