1use crate::{PlatformDispatcher, Priority, RunnableVariant};
2use scheduler::{Clock, Scheduler, SessionId, TestScheduler, TestSchedulerConfig, Yield};
3use std::{
4 sync::Arc,
5 time::{Duration, Instant},
6};
7
8/// TestDispatcher provides deterministic async execution for tests.
9///
10/// This implementation delegates task scheduling to the scheduler crate's `TestScheduler`.
11/// Access the scheduler directly via `scheduler()` for clock, rng, and parking control.
12#[doc(hidden)]
13pub struct TestDispatcher {
14 session_id: SessionId,
15 scheduler: Arc<TestScheduler>,
16}
17
18impl TestDispatcher {
19 pub fn new(seed: u64) -> Self {
20 let scheduler = Arc::new(TestScheduler::new(TestSchedulerConfig {
21 seed,
22 randomize_order: true,
23 allow_parking: false,
24 capture_pending_traces: std::env::var("PENDING_TRACES")
25 .map_or(false, |var| var == "1" || var == "true"),
26 timeout_ticks: 0..=1000,
27 }));
28
29 let session_id = scheduler.allocate_session_id();
30
31 TestDispatcher {
32 session_id,
33 scheduler,
34 }
35 }
36
37 pub fn scheduler(&self) -> &Arc<TestScheduler> {
38 &self.scheduler
39 }
40
41 pub fn session_id(&self) -> SessionId {
42 self.session_id
43 }
44
45 pub fn advance_clock(&self, by: Duration) {
46 self.scheduler.advance_clock(by);
47 }
48
49 pub fn advance_clock_to_next_timer(&self) -> bool {
50 self.scheduler.advance_clock_to_next_timer()
51 }
52
53 pub fn simulate_random_delay(&self) -> Yield {
54 self.scheduler.yield_random()
55 }
56
57 pub fn tick(&self, background_only: bool) -> bool {
58 if background_only {
59 self.scheduler.tick_background_only()
60 } else {
61 self.scheduler.tick()
62 }
63 }
64
65 pub fn run_until_parked(&self) {
66 while self.tick(false) {}
67 }
68}
69
70impl Clone for TestDispatcher {
71 fn clone(&self) -> Self {
72 let session_id = self.scheduler.allocate_session_id();
73 Self {
74 session_id,
75 scheduler: self.scheduler.clone(),
76 }
77 }
78}
79
80impl PlatformDispatcher for TestDispatcher {
81 fn get_all_timings(&self) -> Vec<crate::ThreadTaskTimings> {
82 Vec::new()
83 }
84
85 fn get_current_thread_timings(&self) -> Vec<crate::TaskTiming> {
86 Vec::new()
87 }
88
89 fn is_main_thread(&self) -> bool {
90 self.scheduler.is_main_thread()
91 }
92
93 fn now(&self) -> Instant {
94 self.scheduler.clock().now()
95 }
96
97 fn dispatch(&self, runnable: RunnableVariant, priority: Priority) {
98 self.scheduler
99 .schedule_background_with_priority(runnable, priority);
100 }
101
102 fn dispatch_on_main_thread(&self, runnable: RunnableVariant, _priority: Priority) {
103 self.scheduler
104 .schedule_foreground(self.session_id, runnable);
105 }
106
107 fn dispatch_after(&self, _duration: Duration, _runnable: RunnableVariant) {
108 panic!(
109 "dispatch_after should not be called in tests. \
110 Use BackgroundExecutor::timer() which uses the scheduler's native timer."
111 );
112 }
113
114 fn as_test(&self) -> Option<&TestDispatcher> {
115 Some(self)
116 }
117
118 fn spawn_realtime(&self, f: Box<dyn FnOnce() + Send>) {
119 std::thread::spawn(move || {
120 f();
121 });
122 }
123}