Skip to main content

qubit_clock/sleep/
mock_sleeper.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//! Sleeper backed by a shared mock timeline.
11
12use std::time::Duration;
13
14use crate::sleep::Sleeper;
15#[cfg(feature = "tokio")]
16use crate::sleep::{
17    AsyncSleepFuture,
18    AsyncSleeper,
19};
20use crate::{
21    MockTimeline,
22    MockWaiterKind,
23};
24
25/// Relative sleeper driven by a [`crate::MockTimeline`].
26///
27/// `MockSleeper` is the test implementation of [`Sleeper`]. It does not wait
28/// for real wall-clock time. Instead, each sleep captures a deadline on the
29/// backing [`crate::MockTimeline`] and completes only after that timeline has
30/// advanced to the deadline.
31///
32/// Cloned sleepers share the same timeline. A sleeper created through
33/// [`with_timeline`](Self::with_timeline) can therefore be paired with a
34/// [`crate::MockClock`] or future monitor implementation that uses the same
35/// timeline. For most tests, [`crate::MockTime`] is the simpler way to create
36/// that shared runtime.
37///
38/// Blocking sleeps register themselves as [`crate::MockWaiterKind::Sleep`] so
39/// tests can use
40/// [`MockTimeline::wait_for_blocked_waiters`](crate::MockTimeline::wait_for_blocked_waiters)
41/// before advancing time. With the `tokio` feature enabled, async sleeps use
42/// the same timeline and waiter registration model.
43#[derive(Clone, Debug)]
44pub struct MockSleeper {
45    timeline: MockTimeline,
46}
47
48impl MockSleeper {
49    /// Creates a mock sleeper with a fresh timeline.
50    ///
51    /// # Returns
52    /// A sleeper backed by a new zero-elapsed timeline.
53    #[must_use]
54    pub fn new() -> Self {
55        Self::with_timeline(MockTimeline::new())
56    }
57
58    /// Creates a mock sleeper backed by an existing timeline.
59    ///
60    /// # Parameters
61    /// - `timeline`: Shared mock timeline to wait on.
62    ///
63    /// # Returns
64    /// A sleeper view over the provided timeline.
65    #[must_use]
66    pub fn with_timeline(timeline: MockTimeline) -> Self {
67        Self { timeline }
68    }
69
70    /// Returns the timeline backing this sleeper.
71    ///
72    /// # Returns
73    /// The shared mock timeline.
74    #[inline]
75    pub fn timeline(&self) -> MockTimeline {
76        self.timeline.clone()
77    }
78}
79
80impl Default for MockSleeper {
81    /// Creates a mock sleeper with a fresh timeline.
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87impl Sleeper for MockSleeper {
88    /// Blocks until the shared mock timeline advances by `duration`.
89    fn sleep_for(&self, duration: Duration) {
90        let deadline = self.timeline.now().saturating_add(duration);
91        self.timeline
92            .wait_until_with_kind(deadline, MockWaiterKind::Sleep)
93            .expect("mock sleeper deadlines should belong to the sleeper timeline");
94    }
95}
96
97#[cfg(feature = "tokio")]
98impl AsyncSleeper for MockSleeper {
99    /// Returns a future that resolves when the shared mock timeline reaches the sleep deadline.
100    fn sleep_for_async<'a>(&'a self, duration: Duration) -> AsyncSleepFuture<'a> {
101        let deadline = self.timeline.now().saturating_add(duration);
102        self.timeline
103            .wait_until_async_with_kind(deadline, MockWaiterKind::Sleep)
104            .expect("mock sleeper deadlines should belong to the sleeper timeline")
105    }
106}