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}