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}