Skip to main content

omaha_client/time/
timers.rs

1// Copyright 2020 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use std::time::Duration;
10
11use super::PartialComplexTime;
12
13#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14pub enum ExpectedWait {
15    Until(PartialComplexTime),
16    For(Duration, Duration),
17}
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20pub enum RequestedWait {
21    Until(PartialComplexTime),
22    For(Duration),
23}
24
25pub use stub::StubTimer;
26
27mod stub {
28    use super::super::*;
29    use futures::future::BoxFuture;
30    use futures::prelude::*;
31
32    pub struct StubTimer;
33    impl Timer for StubTimer {
34        /// Wait until at least one of the given time bounds has been reached.
35        fn wait_until(&mut self, _time: impl Into<PartialComplexTime>) -> BoxFuture<'static, ()> {
36            future::ready(()).boxed()
37        }
38
39        /// Wait for the given duration (from now).
40        fn wait_for(&mut self, _duration: Duration) -> BoxFuture<'static, ()> {
41            future::ready(()).boxed()
42        }
43    }
44
45    #[cfg(test)]
46    mod tests {
47        use super::*;
48        use futures::executor::block_on;
49        use std::time::Duration;
50
51        #[test]
52        fn test_wait_always_ready() {
53            block_on(StubTimer.wait_until(StandardTimeSource.now() + Duration::from_secs(5555)));
54            block_on(StubTimer.wait_for(Duration::from_secs(5555)));
55        }
56    }
57}
58
59pub use mock::MockTimer;
60
61mod mock {
62    use super::super::*;
63    use super::{ExpectedWait, RequestedWait};
64    use futures::future::BoxFuture;
65    use futures::prelude::*;
66    use std::{cell::RefCell, collections::VecDeque, fmt::Debug, rc::Rc};
67
68    /// A mocked timer that will assert expected waits, and block forever after it has used them.
69    #[derive(Debug)]
70    pub struct MockTimer {
71        expected_waits: VecDeque<ExpectedWait>,
72        requested_waits: Rc<RefCell<Vec<RequestedWait>>>,
73    }
74
75    impl MockTimer {
76        pub fn new() -> Self {
77            MockTimer {
78                expected_waits: VecDeque::new(),
79                requested_waits: Rc::new(RefCell::new(Vec::new())),
80            }
81        }
82
83        /// Expect a wait until the given PartialComplexTime.
84        pub fn expect_until(&mut self, time: impl Into<PartialComplexTime>) {
85            self.expected_waits.push_back(ExpectedWait::Until(time.into()))
86        }
87
88        /// Expect a wait for the given Duration.
89        pub fn expect_for(&mut self, duration: Duration) {
90            self.expected_waits.push_back(ExpectedWait::For(duration, duration))
91        }
92
93        /// Add a new wait to the end of the expected durations.
94        pub fn expect_for_range(&mut self, min: Duration, max: Duration) {
95            self.expected_waits.push_back(ExpectedWait::For(min, max))
96        }
97
98        /// Check that a given Wait was expected.  If no expected waits have been set, then
99        /// do nothing after recording the Wait.
100        fn handle_wait(&mut self, requested: RequestedWait) -> BoxFuture<'static, ()> {
101            if let Some(expected) = self.expected_waits.pop_front() {
102                match (requested, expected) {
103                    (RequestedWait::For(duration), ExpectedWait::For(min, max)) => {
104                        assert!(
105                            duration >= min && duration <= max,
106                            "{duration:?} out of range [{min:?}, {max:?}]",
107                        );
108                    }
109                    (RequestedWait::Until(requested), ExpectedWait::Until(expected)) => {
110                        assert!(
111                            requested == expected,
112                            "wait_until() called with wrong time: {requested}, expected {expected}"
113                        );
114                    }
115                    (requested, expected) => {
116                        panic!(
117                            "Timer called with wrong wait: {requested:?}, expected {expected:?}"
118                        );
119                    }
120                }
121                self.requested_waits.borrow_mut().push(requested);
122                future::ready(()).boxed()
123            } else {
124                // No more expected durations left, blocking the Timer forever.
125                // Users of MockTimer are expected to use run_until_stalled()
126                // if timer is used in an infinite loop.
127                future::pending().boxed()
128            }
129        }
130
131        pub fn get_requested_waits_view(&self) -> Rc<RefCell<Vec<RequestedWait>>> {
132            Rc::clone(&self.requested_waits)
133        }
134    }
135
136    impl Default for MockTimer {
137        fn default() -> Self {
138            Self::new()
139        }
140    }
141
142    impl Timer for MockTimer {
143        /// Wait until at least one of the given time bounds has been reached.
144        fn wait_until(&mut self, time: impl Into<PartialComplexTime>) -> BoxFuture<'static, ()> {
145            self.handle_wait(RequestedWait::Until(time.into()))
146        }
147
148        /// Wait for the given duration (from now).
149        fn wait_for(&mut self, duration: Duration) -> BoxFuture<'static, ()> {
150            self.handle_wait(RequestedWait::For(duration))
151        }
152    }
153
154    impl Drop for MockTimer {
155        fn drop(&mut self) {
156            // Make sure all the expected durations have been waited on.
157            assert!(self.expected_waits.is_empty());
158        }
159    }
160
161    #[cfg(test)]
162    mod tests {
163        use super::*;
164        use futures::executor::{LocalPool, block_on};
165        use futures::task::LocalSpawnExt;
166        use std::time::Duration;
167
168        #[test]
169        fn test_wait_until_expected() {
170            let mock_time = MockTimeSource::new_from_now();
171            let time = mock_time.now() + Duration::from_secs(5555);
172
173            let mut timer = MockTimer::new();
174            timer.expect_until(time);
175
176            block_on(timer.wait_until(time));
177        }
178
179        #[test]
180        fn test_wait_for_expected() {
181            let mut timer = MockTimer::new();
182            timer.expect_for(Duration::from_secs(5555));
183
184            block_on(timer.wait_for(Duration::from_secs(5555)));
185        }
186
187        #[test]
188        fn test_wait_for_twice() {
189            let mut timer = MockTimer::new();
190            timer.expect_for(Duration::from_secs(5555));
191            timer.expect_for(Duration::from_secs(6666));
192
193            block_on(async {
194                timer.wait_for(Duration::from_secs(5555)).await;
195                timer.wait_for(Duration::from_secs(6666)).await;
196            });
197        }
198
199        #[test]
200        fn test_wait_for_loop() {
201            let mut timer = MockTimer::new();
202            timer.expect_for(Duration::from_secs(1));
203            timer.expect_for(Duration::from_secs(2));
204            timer.expect_for(Duration::from_secs(3));
205
206            let mut pool = LocalPool::new();
207            pool.spawner()
208                .spawn_local(async move {
209                    let mut i = 1;
210                    loop {
211                        timer.wait_for(Duration::from_secs(i)).await;
212                        i += 1;
213                    }
214                })
215                .unwrap();
216            pool.run_until_stalled();
217        }
218
219        #[test]
220        fn test_wait_for_expected_duration() {
221            let mut timer = MockTimer::new();
222            timer.expect_for_range(Duration::from_secs(10), Duration::from_secs(20));
223
224            block_on(timer.wait_for(Duration::from_secs(15)));
225        }
226
227        #[test]
228        #[should_panic(expected = "out of range")]
229        fn test_wait_for_expected_duration_out_of_range_low() {
230            let mut timer = MockTimer::new();
231            timer.expect_for_range(Duration::from_secs(10), Duration::from_secs(20));
232
233            block_on(timer.wait_for(Duration::from_secs(3)));
234        }
235
236        #[test]
237        #[should_panic(expected = "out of range")]
238        fn test_wait_for_expected_duration_out_of_range_high() {
239            let mut timer = MockTimer::new();
240            timer.expect_for_range(Duration::from_secs(10), Duration::from_secs(20));
241
242            block_on(timer.wait_for(Duration::from_secs(30)));
243        }
244
245        #[test]
246        #[should_panic(expected = "5555")]
247        fn test_wait_for_wrong_duration() {
248            let mut timer = MockTimer::new();
249            timer.expect_for(Duration::from_secs(5555));
250
251            block_on(timer.wait_for(Duration::from_secs(6666)));
252        }
253
254        #[test]
255        #[should_panic(expected = "is_empty()")]
256        fn test_expect_more_wait_for() {
257            let mut timer = MockTimer::new();
258            timer.expect_for(Duration::from_secs(5555));
259            timer.expect_for(Duration::from_secs(6666));
260
261            block_on(timer.wait_for(Duration::from_secs(5555)));
262        }
263
264        #[test]
265        #[should_panic(expected = "Timer called with wrong wait")]
266        fn test_wait_for_wrong_wait() {
267            let mock_time = MockTimeSource::new_from_now();
268            let time = mock_time.now() + Duration::from_secs(5555);
269
270            let mut timer = MockTimer::new();
271            timer.expect_until(time);
272
273            block_on(timer.wait_for(Duration::from_secs(6666)));
274        }
275    }
276}
277
278pub use blocking::{BlockedTimer, BlockingTimer, InfiniteTimer};
279
280mod blocking {
281    use super::super::*;
282    use super::RequestedWait;
283    use futures::channel::{mpsc, oneshot};
284    use futures::future::BoxFuture;
285    use futures::prelude::*;
286
287    /// A mock timer that will notify a channel when creating a timer.
288    #[derive(Debug)]
289    pub struct BlockingTimer {
290        chan: mpsc::Sender<BlockedTimer>,
291    }
292
293    /// An omaha state machine timer waiting to be unblocked. Dropping a BlockedTimer will cause
294    /// the timer to panic.
295    #[derive(Debug)]
296    pub struct BlockedTimer {
297        wait: RequestedWait,
298        unblock: oneshot::Sender<()>,
299    }
300
301    impl BlockingTimer {
302        /// Returns a new BlockingTimer and a channel to receive BlockedTimer instances.
303        pub fn new() -> (Self, mpsc::Receiver<BlockedTimer>) {
304            let (send, recv) = mpsc::channel(0);
305            (Self { chan: send }, recv)
306        }
307
308        fn wait(&mut self, wait: RequestedWait) -> BoxFuture<'static, ()> {
309            let mut chan = self.chan.clone();
310
311            async move {
312                let (send, recv) = oneshot::channel();
313                chan.send(BlockedTimer { wait, unblock: send }).await.unwrap();
314
315                recv.await.unwrap();
316            }
317            .boxed()
318        }
319    }
320
321    impl BlockedTimer {
322        /// The requested duration of this timer.
323        pub fn requested_wait(&self) -> RequestedWait {
324            self.wait
325        }
326
327        /// Unblock the timer, panicing if it no longer exists.
328        pub fn unblock(self) {
329            self.unblock.send(()).unwrap()
330        }
331    }
332
333    impl Timer for BlockingTimer {
334        /// Wait until at least one of the given time bounds has been reached.
335        fn wait_until(&mut self, time: impl Into<PartialComplexTime>) -> BoxFuture<'static, ()> {
336            self.wait(RequestedWait::Until(time.into()))
337        }
338
339        /// Wait for the given duration (from now).
340        fn wait_for(&mut self, duration: Duration) -> BoxFuture<'static, ()> {
341            self.wait(RequestedWait::For(duration))
342        }
343    }
344
345    /// A mock timer that will block forever.
346    #[derive(Debug)]
347    pub struct InfiniteTimer;
348
349    impl Timer for InfiniteTimer {
350        /// Wait until at least one of the given time bounds has been reached.
351        fn wait_until(&mut self, _time: impl Into<PartialComplexTime>) -> BoxFuture<'static, ()> {
352            future::pending().boxed()
353        }
354
355        /// Wait for the given duration (from now).
356        fn wait_for(&mut self, _duration: Duration) -> BoxFuture<'static, ()> {
357            future::pending().boxed()
358        }
359    }
360}