qubit_clock/mock/mock_time.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Facade for a complete mock time runtime.
11
12use std::time::Duration as StdDuration;
13
14use chrono::{
15 DateTime,
16 Utc,
17};
18
19use crate::sleep::MockSleeper;
20use crate::{
21 ControllableClock,
22 MockClock,
23 MockTimeError,
24 MockTimeline,
25};
26
27/// A complete mock time runtime sharing one timeline across clocks and sleepers.
28///
29/// `MockTime` is the recommended entry point for tests that need both "what
30/// time is it?" and "how long should this operation sleep?" to use the same
31/// deterministic time source. It constructs one [`MockTimeline`], then creates
32/// a [`MockClock`] and [`MockSleeper`](crate::sleep::MockSleeper) over that
33/// timeline.
34///
35/// Advancing the runtime through [`advance`](Self::advance) advances every
36/// component derived from it. This avoids a common testing bug where a mock
37/// clock reports one logical time while a mock sleeper or monitor waits on a
38/// different logical time source.
39///
40/// Wall-clock anchoring is separate from elapsed mock time. Calling
41/// [`set_time`](Self::set_time) changes the UTC value read at the current
42/// timeline instant, but it does not advance or rewind the timeline. Calling
43/// [`reset`](Self::reset) restores both the elapsed timeline and the clock
44/// anchor, and fails if any timeline waiter is active.
45///
46/// # Example
47///
48/// ```
49/// use std::time::Duration;
50///
51/// use qubit_clock::{Clock, MockTime};
52///
53/// let mock = MockTime::unix_epoch();
54/// let clock = mock.clock();
55///
56/// assert_eq!(0, clock.millis());
57///
58/// mock.advance(Duration::from_millis(250));
59///
60/// assert_eq!(250, clock.millis());
61/// assert_eq!(Duration::from_millis(250), mock.elapsed());
62/// ```
63#[derive(Debug, Clone)]
64pub struct MockTime {
65 timeline: MockTimeline,
66 clock: MockClock,
67 sleeper: MockSleeper,
68}
69
70impl MockTime {
71 /// Creates a mock runtime anchored at a UTC time.
72 ///
73 /// # Parameters
74 /// - `start`: UTC reading returned before the timeline advances.
75 ///
76 /// # Returns
77 /// A mock runtime with a clock and sleeper sharing one timeline.
78 #[must_use]
79 pub fn at(start: DateTime<Utc>) -> Self {
80 let timeline = MockTimeline::new();
81 let clock = MockClock::with_timeline(start, timeline.clone());
82 let sleeper = MockSleeper::with_timeline(timeline.clone());
83 Self {
84 timeline,
85 clock,
86 sleeper,
87 }
88 }
89
90 /// Creates a mock runtime anchored at the Unix epoch.
91 ///
92 /// # Returns
93 /// A mock runtime starting at `1970-01-01T00:00:00Z`.
94 #[must_use]
95 pub fn unix_epoch() -> Self {
96 Self::at(DateTime::<Utc>::UNIX_EPOCH)
97 }
98
99 /// Returns the shared timeline.
100 ///
101 /// # Returns
102 /// The timeline that drives all components in this runtime.
103 #[inline]
104 pub fn timeline(&self) -> MockTimeline {
105 self.timeline.clone()
106 }
107
108 /// Returns a clock view over the shared timeline.
109 ///
110 /// # Returns
111 /// Cloneable mock clock.
112 #[inline]
113 pub fn clock(&self) -> MockClock {
114 self.clock.clone()
115 }
116
117 /// Returns a sleeper view over the shared timeline.
118 ///
119 /// # Returns
120 /// Cloneable mock sleeper.
121 #[inline]
122 pub fn sleeper(&self) -> MockSleeper {
123 self.sleeper.clone()
124 }
125
126 /// Returns elapsed time on the shared timeline.
127 ///
128 /// # Returns
129 /// Elapsed mock time.
130 #[inline]
131 pub fn elapsed(&self) -> StdDuration {
132 self.timeline.elapsed()
133 }
134
135 /// Advances the shared timeline.
136 ///
137 /// # Parameters
138 /// - `duration`: Duration to add to mock elapsed time.
139 #[inline]
140 pub fn advance(&self, duration: StdDuration) {
141 self.timeline.advance(duration);
142 }
143
144 /// Reanchors the runtime clock at the current timeline instant.
145 ///
146 /// # Parameters
147 /// - `instant`: New UTC time returned at the current timeline instant.
148 #[inline]
149 pub fn set_time(&self, instant: DateTime<Utc>) {
150 self.clock.set_time(instant);
151 }
152
153 /// Resets the timeline and clock anchor to the runtime's initial state.
154 ///
155 /// # Returns
156 /// `Ok(())` when reset succeeds.
157 ///
158 /// # Errors
159 /// Returns [`MockTimeError::ActiveWaiters`] when timeline waiters are active.
160 pub fn reset(&self) -> Result<(), MockTimeError> {
161 self.timeline.reset()?;
162 self.clock.reset_wall_anchor();
163 Ok(())
164 }
165}