1#![forbid(
17 bad_style,
18 arithmetic_overflow,
19 mutable_transmutes,
20 no_mangle_const_items,
21 unknown_crate_types
22)]
23#![deny(
24 missing_docs,
25 overflowing_literals,
26 unsafe_code,
27 warnings,
28 trivial_casts,
29 trivial_numeric_casts,
30 unused_extern_crates,
31 unused_import_braces,
32 unused_qualifications,
33 unused_must_use
34)]
35
36use std::cell::Cell;
37use std::convert::TryInto;
38use std::ops::{Add, AddAssign, Sub, SubAssign};
39use std::time::Duration;
40
41thread_local! {
42 static FAKE_TIME: Cell<u64> = Default::default();
43}
44
45#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
47pub struct FakeInstant {
48 time_created: u64,
49}
50
51impl FakeInstant {
52 pub fn set_time(time: u64) -> u64 {
55 FAKE_TIME.with(|c| c.replace(time))
56 }
57
58 pub fn advance_time(millis: u64) -> u64 {
61 FAKE_TIME.with(|c| {
62 let new_time = c.get() + millis;
63 c.set(new_time);
64 new_time
65 })
66 }
67
68 pub fn time() -> u64 {
70 FAKE_TIME.with(|c| c.get())
71 }
72
73 pub fn now() -> Self {
76 let time = Self::time();
77 Self { time_created: time }
78 }
79
80 pub fn duration_since(self, earlier: Self) -> Duration {
86 self.checked_duration_since(earlier).unwrap_or_default()
87 }
88
89 pub fn checked_duration_since(&self, earlier: Self) -> Option<Duration> {
92 self.time_created
93 .checked_sub(earlier.time_created)
94 .map(Duration::from_millis)
95 }
96
97 pub fn saturating_duration_since(&self, earlier: Self) -> Duration {
101 self.checked_duration_since(earlier).unwrap_or_default()
102 }
103
104 pub fn elapsed(self) -> Duration {
114 Duration::from_millis(Self::time() - self.time_created)
115 }
116
117 pub fn checked_add(&self, duration: Duration) -> Option<Self> {
120 duration
121 .as_millis()
122 .checked_add(self.time_created as u128)
123 .and_then(|time| time.try_into().ok())
124 .map(|time| Self { time_created: time })
125 }
126
127 pub fn checked_sub(&self, duration: Duration) -> Option<Self> {
130 duration
131 .as_millis()
132 .try_into()
133 .ok()
134 .and_then(|dur| self.time_created.checked_sub(dur))
135 .map(|time| Self { time_created: time })
136 }
137}
138
139impl Add<Duration> for FakeInstant {
140 type Output = Self;
141 fn add(self, other: Duration) -> Self {
142 self.checked_add(other)
143 .expect("overflow when adding duration to instant")
144 }
145}
146
147impl AddAssign<Duration> for FakeInstant {
148 fn add_assign(&mut self, rhs: Duration) {
149 *self = *self + rhs;
150 }
151}
152
153impl Sub<Duration> for FakeInstant {
154 type Output = Self;
155 fn sub(self, other: Duration) -> Self {
156 self.checked_sub(other)
157 .expect("overflow when subtracting duration from instant")
158 }
159}
160
161impl SubAssign<Duration> for FakeInstant {
162 fn sub_assign(&mut self, rhs: Duration) {
163 *self = *self - rhs;
164 }
165}
166
167impl Sub<Self> for FakeInstant {
168 type Output = Duration;
169 fn sub(self, other: Self) -> Duration {
170 self.duration_since(other)
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_auto_traits() {
180 use std::any::Any;
181 use std::panic::{RefUnwindSafe, UnwindSafe};
182
183 fn check_traits<T: 'static + RefUnwindSafe + Send + Sync + Unpin + UnwindSafe + Any>() {}
184 check_traits::<FakeInstant>();
185 }
186
187 #[test]
188 fn test_advance_time() {
189 const DUR: u64 = 5300;
190 let clock = FakeInstant::now();
191 FakeInstant::advance_time(DUR);
192 assert_eq!(Duration::from_millis(DUR), clock.elapsed());
193 }
194
195 #[test]
196 fn test_checked_add_some() {
197 FakeInstant::set_time(0);
198
199 let inst = FakeInstant::now();
200 let dur = Duration::from_millis(std::u64::MAX);
201 FakeInstant::set_time(std::u64::MAX);
202
203 assert_eq!(Some(FakeInstant::now()), inst.checked_add(dur));
204 }
205
206 #[test]
207 fn test_checked_add_none() {
208 FakeInstant::set_time(1);
209
210 let inst = FakeInstant::now();
211 let dur = Duration::from_millis(std::u64::MAX);
212
213 assert_eq!(None, inst.checked_add(dur));
214 }
215
216 #[test]
217 fn test_checked_sub_some() {
218 FakeInstant::set_time(std::u64::MAX);
219
220 let inst = FakeInstant::now();
221 let dur = Duration::from_millis(std::u64::MAX);
222 FakeInstant::set_time(0);
223
224 assert_eq!(Some(FakeInstant::now()), inst.checked_sub(dur));
225 }
226
227 #[test]
228 fn test_checked_sub_none() {
229 FakeInstant::set_time(std::u64::MAX - 1);
230
231 let inst = FakeInstant::now();
232 let dur = Duration::from_millis(std::u64::MAX);
233
234 assert_eq!(None, inst.checked_sub(dur));
235 }
236
237 #[test]
238 fn checked_duration_since_some() {
239 FakeInstant::set_time(0);
240 let inst0 = FakeInstant::now();
241 FakeInstant::set_time(std::u64::MAX);
242 let inst_max = FakeInstant::now();
243
244 assert_eq!(
245 Some(Duration::from_millis(std::u64::MAX)),
246 inst_max.checked_duration_since(inst0)
247 );
248 }
249
250 #[test]
251 fn checked_duration_since_none() {
252 FakeInstant::set_time(1);
253 let inst1 = FakeInstant::now();
254 FakeInstant::set_time(0);
255 let inst0 = FakeInstant::now();
256
257 assert_eq!(None, inst0.checked_duration_since(inst1));
258 }
259
260 #[test]
261 fn saturating_duration_since_nonzero() {
262 FakeInstant::set_time(0);
263 let inst0 = FakeInstant::now();
264 FakeInstant::set_time(std::u64::MAX);
265 let inst_max = FakeInstant::now();
266
267 assert_eq!(
268 Duration::from_millis(std::u64::MAX),
269 inst_max.saturating_duration_since(inst0)
270 );
271 }
272
273 #[test]
274 fn saturating_duration_since_zero() {
275 FakeInstant::set_time(1);
276 let inst1 = FakeInstant::now();
277 FakeInstant::set_time(0);
278 let inst0 = FakeInstant::now();
279
280 assert_eq!(Duration::new(0, 0), inst0.saturating_duration_since(inst1));
281 }
282
283 #[test]
284 fn test_debug() {
285 let inst = FakeInstant::now();
286 assert_eq!("FakeInstant { time_created: 0 }", format!("{:?}", inst));
287 }
288
289 #[test]
290 fn test_threads() {
291 FakeInstant::set_time(200);
292 let inst1 = FakeInstant::now();
293 assert!(std::thread::spawn(move || {
294 FakeInstant::set_time(500);
295 let inst2 = FakeInstant::now();
296 assert_eq!(Duration::from_millis(300), inst1.elapsed());
297 assert_eq!(Duration::from_millis(0), inst2.elapsed());
298 })
299 .join()
300 .is_ok());
301 }
302}