omaha_client/time/time_source.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::{
10 sync::{Arc, RwLock},
11 time::{Duration, Instant, SystemTime},
12};
13
14use super::{ComplexTime, TimeSource};
15
16/// A default `TimeSource` implementation that uses `SystemTime::now()` and `Instant::now()` to get
17/// the current times.
18#[derive(Clone, Debug, Default)]
19pub struct StandardTimeSource;
20
21impl TimeSource for StandardTimeSource {
22 fn now_in_walltime(&self) -> SystemTime {
23 SystemTime::now()
24 }
25 fn now_in_monotonic(&self) -> Instant {
26 Instant::now()
27 }
28 fn now(&self) -> ComplexTime {
29 ComplexTime::from((SystemTime::now(), Instant::now()))
30 }
31}
32
33/// A mock `TimeSource` that can be manipulated as needed.
34///
35/// Meant to be used when writing tests. As `SystemTime` is partly opaque and is `Instant`
36/// completely are opaque, it needs to be initially constructed from a real source of those objects.
37///
38/// # Example
39/// ```
40/// use omaha_client::time::MockTimeSource;
41/// let mock_source = MockTimeSource::new_from_now();
42/// ```
43///
44/// The MockTimeSource uses `Clone`-able interior mutability to allow it to be manipulated and used
45/// concurrently.
46#[derive(Clone, Debug)]
47pub struct MockTimeSource {
48 time: Arc<RwLock<ComplexTime>>,
49}
50
51impl TimeSource for MockTimeSource {
52 fn now_in_walltime(&self) -> SystemTime {
53 self.time.read().unwrap().wall
54 }
55 fn now_in_monotonic(&self) -> Instant {
56 self.time.read().unwrap().mono
57 }
58 fn now(&self) -> ComplexTime {
59 *(self.time.read().unwrap())
60 }
61}
62
63impl MockTimeSource {
64 /// Create a new `MockTimeSource` from a `ComplexTime` to be it's initial time.
65 ///
66 /// # Example
67 /// ```
68 /// use omaha_client::time::{ComplexTime, MockTimeSource, TimeSource};
69 /// use std::time::{Duration, Instant, SystemTime};
70 /// let mut mock_source = MockTimeSource::new_from_now();
71 /// mock_source.advance(Duration::from_secs(3600));
72 /// let next_time = mock_source.now();
73 /// ```
74 pub fn new(t: impl Into<ComplexTime>) -> Self {
75 MockTimeSource { time: Arc::new(RwLock::new(t.into())) }
76 }
77
78 /// Create a new `MockTimeSource`, initialized to the values from `SystemTime` and `Instant`
79 pub fn new_from_now() -> Self {
80 Self::new(StandardTimeSource.now())
81 }
82
83 /// Advance the mock time source forward (e.g. during a test)
84 ///
85 /// If the `MockTimeSource` has been Cloned, this will advance both.
86 ///
87 /// # Example
88 /// ```
89 /// use omaha_client::time::{ComplexTime, MockTimeSource, TimeSource};
90 /// use std::time::{Duration, Instant, SystemTime};
91 /// let mut one_mock_source = MockTimeSource::new_from_now();
92 /// let initial_time: ComplexTime = one_mock_source.now();
93 /// let two_mock_source = one_mock_source.clone();
94 ///
95 /// let one_hour = Duration::from_secs(3600);
96 /// one_mock_source.advance(one_hour);
97 /// let later_time = one_mock_source.now();
98 ///
99 /// assert_eq!(one_mock_source.now(), two_mock_source.now());
100 /// assert_eq!(one_mock_source.now(), initial_time + one_hour);
101 /// assert_eq!(two_mock_source.now(), initial_time + one_hour);
102 /// ```
103 ///
104 /// This method uses a mutable reference for `self` for clarity. The interior mutability of the
105 /// time does not require it.
106 pub fn advance(&mut self, duration: Duration) {
107 let mut borrowed_time = self.time.write().unwrap();
108 *borrowed_time += duration;
109 }
110
111 /// Truncate the submicrosecond part of the walltime in mock time source.
112 /// This is useful for tests that involves storing time in storage, which will lose
113 /// submicrosecond precision.
114 pub fn truncate_submicrosecond_walltime(&mut self) {
115 let mut borrowed_time = self.time.write().unwrap();
116 *borrowed_time = borrowed_time.truncate_submicrosecond_walltime();
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_impl_time_source_for_mock_time_source() {
126 let time = StandardTimeSource.now();
127 let mock_source = MockTimeSource::new(time);
128 assert_eq!(mock_source.now(), time);
129 assert_eq!(mock_source.now_in_walltime(), time.wall);
130 assert_eq!(mock_source.now_in_monotonic(), time.mono);
131 }
132
133 /// Test that the MockTimeSource doesn't move on it's own.
134 #[test]
135 fn test_mock_timesource_doesnt_advance_on_its_own() {
136 let source = MockTimeSource::new_from_now();
137 let now = source.now();
138 std::thread::sleep(Duration::from_millis(100));
139 let same_as_now = source.now();
140
141 assert_eq!(same_as_now, now);
142 }
143
144 /// Test that the mock timesource will advance in the expected way when it's asked to.
145 #[test]
146 fn test_mock_timesource_will_advance() {
147 let mut source = MockTimeSource::new_from_now();
148 let now = source.now();
149
150 let duration = Duration::from_secs(60);
151 source.advance(duration);
152
153 let later = source.now();
154 let expected = now + duration;
155
156 assert_eq!(later, expected);
157 }
158
159 #[test]
160 fn test_cloned_mock_time_source_also_advances() {
161 let mut one_mock_source = MockTimeSource::new_from_now();
162 let initial_time = one_mock_source.now();
163 let two_mock_source = one_mock_source.clone();
164
165 let one_hour = Duration::from_secs(3600);
166 one_mock_source.advance(one_hour);
167 let later_time = one_mock_source.now();
168
169 assert_eq!(later_time, initial_time + one_hour);
170 assert_eq!(two_mock_source.now(), initial_time + one_hour);
171 }
172}