s3/utils/
time_utils.rs

1use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH};
2use time::OffsetDateTime;
3
4fn real_time() -> Result<u64, SystemTimeError> {
5    SystemTime::now()
6        .duration_since(UNIX_EPOCH)
7        .map(|t| t.as_secs())
8}
9
10#[cfg(not(test))]
11pub fn current_time() -> Result<u64, SystemTimeError> {
12    real_time()
13}
14
15pub fn now_utc() -> OffsetDateTime {
16    OffsetDateTime::from_unix_timestamp(current_time().unwrap() as i64).unwrap()
17}
18
19#[cfg(test)]
20mod mocked_time {
21    use super::*;
22
23    use std::cell::Cell;
24
25    thread_local! {
26            static TIMESTAMP: Cell<u64> = Cell::new(0);
27    }
28
29    pub fn current_time() -> Result<u64, SystemTimeError> {
30        TIMESTAMP.with(|ts| {
31            let time = ts.get();
32            if time == 0 { real_time() } else { Ok(time) }
33        })
34    }
35
36    fn set_timestamp(timestamp: u64) -> u64 {
37        TIMESTAMP.with(|ts| {
38            let old = ts.get();
39            ts.set(timestamp);
40            old
41        })
42    }
43
44    pub struct MockTimestamp {
45        old: u64,
46    }
47
48    impl MockTimestamp {
49        // Get the real clock time
50        pub fn real_time(&self) -> u64 {
51            real_time().unwrap()
52        }
53
54        // Get the old time before this call to with_timestamp
55        pub fn old_time(&self) -> u64 {
56            self.old
57        }
58
59        // Sets the time to the exact unix timestamp
60        // 0 means use real_time
61        // Returns old time
62        pub fn set_time(&self, timestamp: u64) -> u64 {
63            set_timestamp(timestamp)
64        }
65
66        // Add this many seconds to the current time
67        // Can be negative
68        // Returns new now, old time
69        pub fn add_time(&self, time_delta: i64) -> (u64, u64) {
70            let now = ((current_time().unwrap() as i64) + time_delta) as u64;
71            (now, set_timestamp(now))
72        }
73    }
74
75    impl Drop for MockTimestamp {
76        fn drop(&mut self) {
77            set_timestamp(self.old);
78        }
79    }
80
81    pub fn with_timestamp(timestamp: u64) -> MockTimestamp {
82        MockTimestamp {
83            old: set_timestamp(timestamp),
84        }
85    }
86
87    #[cfg(test)]
88    mod tests {
89        use super::*;
90
91        const MOCKED_TIMESTAMP: u64 = 5_000_000;
92
93        mod current_time {
94            use super::*;
95
96            #[test]
97            fn when_set_timestamp_not_called() {
98                let now = real_time().unwrap();
99
100                assert!(current_time().unwrap() >= now);
101            }
102
103            #[test]
104            fn when_set_timestamp_was_called() {
105                set_timestamp(MOCKED_TIMESTAMP);
106
107                assert_eq!(current_time().unwrap(), MOCKED_TIMESTAMP);
108
109                set_timestamp(0);
110            }
111        }
112
113        mod with_timestamp {
114            use super::*;
115
116            #[test]
117            fn when_resets_when_result_dropped() {
118                let now = real_time().unwrap();
119                let ts = with_timestamp(MOCKED_TIMESTAMP);
120
121                assert_eq!(current_time().unwrap(), MOCKED_TIMESTAMP);
122
123                drop(ts);
124
125                assert!(current_time().unwrap() >= now);
126            }
127
128            #[test]
129            fn when_nested() {
130                let now = real_time().unwrap();
131                {
132                    let _ts = with_timestamp(MOCKED_TIMESTAMP);
133
134                    assert_eq!(current_time().unwrap(), MOCKED_TIMESTAMP);
135
136                    {
137                        let _ts = with_timestamp(MOCKED_TIMESTAMP + 1_000);
138
139                        assert_eq!(current_time().unwrap(), MOCKED_TIMESTAMP + 1_000);
140                    }
141
142                    {
143                        let _ts = with_timestamp(0);
144
145                        assert!(current_time().unwrap() >= now);
146                    }
147
148                    assert_eq!(current_time().unwrap(), MOCKED_TIMESTAMP);
149                }
150
151                assert!(current_time().unwrap() >= now);
152            }
153        }
154    }
155}
156#[cfg(test)]
157pub use mocked_time::*;