steam_client/utils/
clock.rs1use std::{
29 future::Future,
30 pin::Pin,
31 sync::{Arc, Mutex},
32 time::{Duration, Instant, SystemTime, UNIX_EPOCH},
33};
34
35pub trait Clock: Send + Sync + std::fmt::Debug {
40 fn now(&self) -> Instant;
42
43 fn timestamp(&self) -> u64;
45
46 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
52}
53
54#[derive(Debug, Clone, Copy, Default)]
58pub struct SystemClock;
59
60impl Clock for SystemClock {
61 fn now(&self) -> Instant {
62 Instant::now()
63 }
64
65 fn timestamp(&self) -> u64 {
66 SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or(Duration::ZERO).as_secs()
67 }
68
69 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
70 Box::pin(tokio::time::sleep(duration))
71 }
72}
73
74#[derive(Debug, Clone)]
94pub struct MockClock {
95 offset: Arc<Mutex<Duration>>,
97 base: Instant,
99 base_system: SystemTime,
101}
102
103impl MockClock {
104 pub fn new() -> Self {
106 Self { offset: Arc::new(Mutex::new(Duration::ZERO)), base: Instant::now(), base_system: SystemTime::now() }
107 }
108
109 pub fn advance(&self, duration: Duration) {
113 let mut offset = self.offset.lock().expect("mutex poisoned");
114 *offset += duration;
115 }
116
117 pub fn set_offset(&self, offset: Duration) {
119 let mut current = self.offset.lock().expect("mutex poisoned");
120 *current = offset;
121 }
122
123 pub fn current_offset(&self) -> Duration {
125 *self.offset.lock().expect("mutex poisoned")
126 }
127
128 pub fn reset(&self) {
130 let mut offset = self.offset.lock().expect("mutex poisoned");
131 *offset = Duration::ZERO;
132 }
133}
134
135impl Default for MockClock {
136 fn default() -> Self {
137 Self::new()
138 }
139}
140
141impl Clock for MockClock {
142 fn now(&self) -> Instant {
143 let offset = self.offset.lock().expect("mutex poisoned");
144 self.base + *offset
145 }
146
147 fn timestamp(&self) -> u64 {
148 let offset = self.offset.lock().expect("mutex poisoned");
149 self.base_system.checked_add(*offset).unwrap_or(self.base_system).duration_since(UNIX_EPOCH).unwrap_or(Duration::ZERO).as_secs()
150 }
151
152 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
153 self.advance(duration);
155 Box::pin(std::future::ready(()))
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn test_system_clock_advances() {
165 let clock = SystemClock;
166 let t1 = clock.now();
167 std::thread::sleep(Duration::from_millis(10));
168 let t2 = clock.now();
169
170 assert!(t2 > t1);
171 }
172
173 #[test]
174 fn test_system_clock_timestamp() {
175 let clock = SystemClock;
176 let t1 = clock.timestamp();
177 std::thread::sleep(Duration::from_millis(1100));
178 let t2 = clock.timestamp();
179
180 assert!(t2 > t1);
181 }
182
183 #[test]
184 fn test_mock_clock_initial_time() {
185 let clock = MockClock::new();
186 let t1 = clock.now();
187 let t2 = clock.now();
188
189 assert_eq!(t1, t2);
191 }
192
193 #[test]
194 fn test_mock_clock_advance() {
195 let clock = MockClock::new();
196
197 let t1 = clock.now();
198 clock.advance(Duration::from_secs(10));
199 let t2 = clock.now();
200
201 assert_eq!(t2 - t1, Duration::from_secs(10));
202 }
203
204 #[test]
205 fn test_mock_clock_timestamp_advances() {
206 let clock = MockClock::new();
207 let t1 = clock.timestamp();
208
209 clock.advance(Duration::from_secs(10));
210 let t2 = clock.timestamp();
211
212 assert_eq!(t2, t1 + 10);
213 }
214
215 #[test]
216 fn test_mock_clock_multiple_advances() {
217 let clock = MockClock::new();
218
219 let t1 = clock.now();
220 clock.advance(Duration::from_secs(5));
221 clock.advance(Duration::from_secs(3));
222 let t2 = clock.now();
223
224 assert_eq!(t2 - t1, Duration::from_secs(8));
225 }
226
227 #[test]
228 fn test_mock_clock_set_offset() {
229 let clock = MockClock::new();
230
231 clock.set_offset(Duration::from_secs(100));
232 assert_eq!(clock.current_offset(), Duration::from_secs(100));
233 }
234
235 #[test]
236 fn test_mock_clock_reset() {
237 let clock = MockClock::new();
238
239 clock.advance(Duration::from_secs(50));
240 clock.reset();
241
242 assert_eq!(clock.current_offset(), Duration::ZERO);
243 }
244
245 #[test]
246 fn test_mock_clock_clone_shares_state() {
247 let clock = MockClock::new();
248 let clone = clock.clone();
249
250 let t1 = clock.now();
251 clone.advance(Duration::from_secs(20));
252 let t2 = clock.now();
253
254 assert_eq!(t2 - t1, Duration::from_secs(20));
256 }
257
258 #[tokio::test]
259 async fn test_system_clock_sleep() {
260 let clock = SystemClock;
261 let start = clock.now();
262
263 clock.sleep(Duration::from_millis(10)).await;
265
266 let elapsed = clock.now() - start;
267 assert!(elapsed >= Duration::from_millis(10));
269 }
270
271 #[tokio::test]
272 async fn test_mock_clock_sleep_advances_time() {
273 let clock = MockClock::new();
274
275 assert_eq!(clock.current_offset(), Duration::ZERO);
276
277 clock.sleep(Duration::from_secs(10)).await;
279 assert_eq!(clock.current_offset(), Duration::from_secs(10));
280
281 clock.sleep(Duration::from_secs(5)).await;
283 assert_eq!(clock.current_offset(), Duration::from_secs(15));
284 }
285
286 #[tokio::test]
287 async fn test_mock_clock_sleep_is_instant() {
288 let clock = MockClock::new();
289
290 let real_start = std::time::Instant::now();
292 clock.sleep(Duration::from_secs(3600)).await; let real_elapsed = real_start.elapsed();
294
295 assert!(real_elapsed < Duration::from_secs(1));
297
298 assert_eq!(clock.current_offset(), Duration::from_secs(3600));
300 }
301}