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 {
76 time: Arc::new(RwLock::new(t.into())),
77 }
78 }
79
80 /// Create a new `MockTimeSource`, initialized to the values from `SystemTime` and `Instant`
81 pub fn new_from_now() -> Self {
82 Self::new(StandardTimeSource.now())
83 }
84
85 /// Advance the mock time source forward (e.g. during a test)
86 ///
87 /// If the `MockTimeSource` has been Cloned, this will advance both.
88 ///
89 /// # Example
90 /// ```
91 /// use omaha_client::time::{ComplexTime, MockTimeSource, TimeSource};
92 /// use std::time::{Duration, Instant, SystemTime};
93 /// let mut one_mock_source = MockTimeSource::new_from_now();
94 /// let initial_time: ComplexTime = one_mock_source.now();
95 /// let two_mock_source = one_mock_source.clone();
96 ///
97 /// let one_hour = Duration::from_secs(3600);
98 /// one_mock_source.advance(one_hour);
99 /// let later_time = one_mock_source.now();
100 ///
101 /// assert_eq!(one_mock_source.now(), two_mock_source.now());
102 /// assert_eq!(one_mock_source.now(), initial_time + one_hour);
103 /// assert_eq!(two_mock_source.now(), initial_time + one_hour);
104 /// ```
105 ///
106 /// This method uses a mutable reference for `self` for clarity. The interior mutability of the
107 /// time does not require it.
108 pub fn advance(&mut self, duration: Duration) {
109 let mut borrowed_time = self.time.write().unwrap();
110 *borrowed_time += duration;
111 }
112
113 /// Truncate the submicrosecond part of the walltime in mock time source.
114 /// This is useful for tests that involves storing time in storage, which will lose
115 /// submicrosecond precision.
116 pub fn truncate_submicrosecond_walltime(&mut self) {
117 let mut borrowed_time = self.time.write().unwrap();
118 *borrowed_time = borrowed_time.truncate_submicrosecond_walltime();
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_impl_time_source_for_mock_time_source() {
128 let time = StandardTimeSource.now();
129 let mock_source = MockTimeSource::new(time);
130 assert_eq!(mock_source.now(), time);
131 assert_eq!(mock_source.now_in_walltime(), time.wall);
132 assert_eq!(mock_source.now_in_monotonic(), time.mono);
133 }
134
135 /// Test that the MockTimeSource doesn't move on it's own.
136 #[test]
137 fn test_mock_timesource_doesnt_advance_on_its_own() {
138 let source = MockTimeSource::new_from_now();
139 let now = source.now();
140 std::thread::sleep(Duration::from_millis(100));
141 let same_as_now = source.now();
142
143 assert_eq!(same_as_now, now);
144 }
145
146 /// Test that the mock timesource will advance in the expected way when it's asked to.
147 #[test]
148 fn test_mock_timesource_will_advance() {
149 let mut source = MockTimeSource::new_from_now();
150 let now = source.now();
151
152 let duration = Duration::from_secs(60);
153 source.advance(duration);
154
155 let later = source.now();
156 let expected = now + duration;
157
158 assert_eq!(later, expected);
159 }
160
161 #[test]
162 fn test_cloned_mock_time_source_also_advances() {
163 let mut one_mock_source = MockTimeSource::new_from_now();
164 let initial_time = one_mock_source.now();
165 let two_mock_source = one_mock_source.clone();
166
167 let one_hour = Duration::from_secs(3600);
168 one_mock_source.advance(one_hour);
169 let later_time = one_mock_source.now();
170
171 assert_eq!(later_time, initial_time + one_hour);
172 assert_eq!(two_mock_source.now(), initial_time + one_hour);
173 }
174}