Skip to main content

qubit_clock/clock/
controllable_clock.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//! Controllable clock trait for testing.
11//!
12//! This module defines the [`ControllableClock`] trait, which extends
13//! [`Clock`] to provide methods for controlling the clock's time. This is
14//! primarily useful for testing scenarios.
15//!
16
17use crate::Clock;
18use chrono::{
19    DateTime,
20    Duration,
21    Utc,
22};
23
24/// A trait representing a clock that can be controlled.
25///
26/// This trait extends [`Clock`] to provide methods for manually setting and
27/// advancing the clock's time. It's primarily designed for testing scenarios
28/// where you need precise control over time.
29///
30/// # Warning
31///
32/// This trait should only be used in testing code, not in production code.
33///
34/// # Examples
35///
36/// ```
37/// use qubit_clock::{Clock, ControllableClock, MockClock};
38/// use chrono::{DateTime, Duration, Utc};
39///
40/// let clock = MockClock::new();
41///
42/// // Set to a specific time
43/// let fixed_time = DateTime::parse_from_rfc3339(
44///     "2024-01-01T00:00:00Z"
45/// ).unwrap().with_timezone(&Utc);
46/// clock.set_time(fixed_time);
47///
48/// assert_eq!(clock.time(), fixed_time);
49///
50/// // Advance by 1 hour
51/// clock.add_duration(Duration::hours(1));
52/// assert_eq!(
53///     clock.time(),
54///     fixed_time + Duration::hours(1)
55/// );
56/// ```
57///
58pub trait ControllableClock: Clock {
59    /// Sets or aligns the clock to a specific time.
60    ///
61    /// The exact semantics depend on the implementation. For
62    /// [`MockClock`](crate::MockClock), this reanchors the current timeline
63    /// instant to read as `instant` without changing elapsed mock time.
64    ///
65    /// # Arguments
66    ///
67    /// * `instant` - The time to set the clock to (UTC).
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use qubit_clock::{Clock, ControllableClock, MockClock};
73    /// use chrono::{DateTime, Utc};
74    ///
75    /// let clock = MockClock::new();
76    /// let time = DateTime::parse_from_rfc3339(
77    ///     "2024-01-01T00:00:00Z"
78    /// ).unwrap().with_timezone(&Utc);
79    ///
80    /// clock.set_time(time);
81    /// assert_eq!(clock.time(), time);
82    /// ```
83    fn set_time(&self, instant: DateTime<Utc>);
84
85    /// Advances the clock by the specified duration.
86    ///
87    /// Implementations may reject durations they cannot represent. For
88    /// [`MockClock`](crate::MockClock), negative durations panic because mock
89    /// timeline time is monotonic.
90    ///
91    /// # Arguments
92    ///
93    /// * `duration` - The duration to advance the clock by.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use qubit_clock::{Clock, ControllableClock, MockClock};
99    /// use chrono::Duration;
100    ///
101    /// let clock = MockClock::new();
102    /// let before = clock.time();
103    ///
104    /// clock.add_duration(Duration::hours(1));
105    ///
106    /// let after = clock.time();
107    /// assert_eq!(after - before, Duration::hours(1));
108    /// ```
109    fn add_duration(&self, duration: Duration);
110
111    /// Resets the clock to its initial state.
112    ///
113    /// The exact behavior of this method depends on the implementation. For
114    /// [`MockClock`](crate::MockClock), it resets the shared mock timeline and
115    /// wall-clock anchor captured when the clock was created.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use qubit_clock::{Clock, ControllableClock, MockClock};
121    /// use chrono::Duration;
122    ///
123    /// let clock = MockClock::new();
124    /// let initial = clock.time();
125    ///
126    /// clock.add_duration(Duration::hours(1));
127    /// clock.reset();
128    ///
129    /// // After reset, time should return to the initial frozen value.
130    /// assert_eq!(clock.time(), initial);
131    /// ```
132    fn reset(&self);
133}