Skip to main content

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}