tests.rs

  1use super::*;
  2use futures::{
  3    FutureExt,
  4    channel::{mpsc, oneshot},
  5    executor::block_on,
  6    future,
  7    sink::SinkExt,
  8    stream::{FuturesUnordered, StreamExt},
  9};
 10use std::{
 11    cell::RefCell,
 12    collections::{BTreeSet, HashSet},
 13    pin::Pin,
 14    rc::Rc,
 15    sync::Arc,
 16    task::{Context, Poll},
 17};
 18
 19#[test]
 20fn test_foreground_executor_spawn() {
 21    let result = TestScheduler::once(async |scheduler| {
 22        let task = scheduler.foreground().spawn(async move { 42 });
 23        task.await
 24    });
 25    assert_eq!(result, 42);
 26}
 27
 28#[test]
 29fn test_background_executor_spawn() {
 30    TestScheduler::once(async |scheduler| {
 31        let task = scheduler.background().spawn(async move { 42 });
 32        let result = task.await;
 33        assert_eq!(result, 42);
 34    });
 35}
 36
 37#[test]
 38fn test_foreground_ordering() {
 39    let mut traces = HashSet::new();
 40
 41    TestScheduler::many(100, async |scheduler| {
 42        #[derive(Hash, PartialEq, Eq)]
 43        struct TraceEntry {
 44            session: usize,
 45            task: usize,
 46        }
 47
 48        let trace = Rc::new(RefCell::new(Vec::new()));
 49
 50        let foreground_1 = scheduler.foreground();
 51        for task in 0..10 {
 52            foreground_1
 53                .spawn({
 54                    let trace = trace.clone();
 55                    async move {
 56                        trace.borrow_mut().push(TraceEntry { session: 0, task });
 57                    }
 58                })
 59                .detach();
 60        }
 61
 62        let foreground_2 = scheduler.foreground();
 63        for task in 0..10 {
 64            foreground_2
 65                .spawn({
 66                    let trace = trace.clone();
 67                    async move {
 68                        trace.borrow_mut().push(TraceEntry { session: 1, task });
 69                    }
 70                })
 71                .detach();
 72        }
 73
 74        scheduler.run();
 75
 76        assert_eq!(
 77            trace
 78                .borrow()
 79                .iter()
 80                .filter(|entry| entry.session == 0)
 81                .map(|entry| entry.task)
 82                .collect::<Vec<_>>(),
 83            (0..10).collect::<Vec<_>>()
 84        );
 85        assert_eq!(
 86            trace
 87                .borrow()
 88                .iter()
 89                .filter(|entry| entry.session == 1)
 90                .map(|entry| entry.task)
 91                .collect::<Vec<_>>(),
 92            (0..10).collect::<Vec<_>>()
 93        );
 94
 95        traces.insert(trace.take());
 96    });
 97
 98    assert!(traces.len() > 1, "Expected at least two traces");
 99}
100
101#[test]
102fn test_timer_ordering() {
103    TestScheduler::many(1, async |scheduler| {
104        let background = scheduler.background();
105        let futures = FuturesUnordered::new();
106        futures.push(
107            async {
108                background.timer(Duration::from_millis(100)).await;
109                2
110            }
111            .boxed(),
112        );
113        futures.push(
114            async {
115                background.timer(Duration::from_millis(50)).await;
116                1
117            }
118            .boxed(),
119        );
120        futures.push(
121            async {
122                background.timer(Duration::from_millis(150)).await;
123                3
124            }
125            .boxed(),
126        );
127        assert_eq!(futures.collect::<Vec<_>>().await, vec![1, 2, 3]);
128    });
129}
130
131#[test]
132fn test_send_from_bg_to_fg() {
133    TestScheduler::once(async |scheduler| {
134        let foreground = scheduler.foreground();
135        let background = scheduler.background();
136
137        let (sender, receiver) = oneshot::channel::<i32>();
138
139        background
140            .spawn(async move {
141                sender.send(42).unwrap();
142            })
143            .detach();
144
145        let task = foreground.spawn(async move { receiver.await.unwrap() });
146        let result = task.await;
147        assert_eq!(result, 42);
148    });
149}
150
151#[test]
152fn test_randomize_order() {
153    // Test deterministic mode: different seeds should produce same execution order
154    let mut deterministic_results = HashSet::new();
155    for seed in 0..10 {
156        let config = SchedulerConfig {
157            seed,
158            randomize_order: false,
159            ..Default::default()
160        };
161        let order = block_on(capture_execution_order(config));
162        assert_eq!(order.len(), 6);
163        deterministic_results.insert(order);
164    }
165
166    // All deterministic runs should produce the same result
167    assert_eq!(
168        deterministic_results.len(),
169        1,
170        "Deterministic mode should always produce same execution order"
171    );
172
173    // Test randomized mode: different seeds can produce different execution orders
174    let mut randomized_results = HashSet::new();
175    for seed in 0..20 {
176        let config = SchedulerConfig::with_seed(seed);
177        let order = block_on(capture_execution_order(config));
178        assert_eq!(order.len(), 6);
179        randomized_results.insert(order);
180    }
181
182    // Randomized mode should produce multiple different execution orders
183    assert!(
184        randomized_results.len() > 1,
185        "Randomized mode should produce multiple different orders"
186    );
187}
188
189async fn capture_execution_order(config: SchedulerConfig) -> Vec<String> {
190    let scheduler = Arc::new(TestScheduler::new(config));
191    let foreground = scheduler.foreground();
192    let background = scheduler.background();
193
194    let (sender, receiver) = mpsc::unbounded::<String>();
195
196    // Spawn foreground tasks
197    for i in 0..3 {
198        let mut sender = sender.clone();
199        foreground
200            .spawn(async move {
201                sender.send(format!("fg-{}", i)).await.ok();
202            })
203            .detach();
204    }
205
206    // Spawn background tasks
207    for i in 0..3 {
208        let mut sender = sender.clone();
209        background
210            .spawn(async move {
211                sender.send(format!("bg-{}", i)).await.ok();
212            })
213            .detach();
214    }
215
216    drop(sender); // Close sender to signal no more messages
217    scheduler.run();
218
219    receiver.collect().await
220}
221
222#[test]
223fn test_block() {
224    let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::default()));
225    let executor = BackgroundExecutor::new(scheduler);
226    let (tx, rx) = oneshot::channel();
227
228    // Spawn background task to send value
229    let _ = executor
230        .spawn(async move {
231            tx.send(42).unwrap();
232        })
233        .detach();
234
235    // Block on receiving the value
236    let result = executor.block_on(async { rx.await.unwrap() });
237    assert_eq!(result, 42);
238}
239
240#[test]
241#[should_panic(expected = "Parking forbidden")]
242fn test_parking_panics() {
243    let scheduler = Arc::new(TestScheduler::new(SchedulerConfig::default()));
244    let executor = BackgroundExecutor::new(scheduler);
245    executor.block_on(future::pending::<()>());
246}
247
248#[test]
249fn test_block_with_parking() {
250    let config = SchedulerConfig {
251        allow_parking: true,
252        ..Default::default()
253    };
254    let scheduler = Arc::new(TestScheduler::new(config));
255    let executor = BackgroundExecutor::new(scheduler);
256    let (tx, rx) = oneshot::channel();
257
258    // Spawn background task to send value
259    let _ = executor
260        .spawn(async move {
261            tx.send(42).unwrap();
262        })
263        .detach();
264
265    // Block on receiving the value (will park if needed)
266    let result = executor.block_on(async { rx.await.unwrap() });
267    assert_eq!(result, 42);
268}
269
270#[test]
271fn test_helper_methods() {
272    // Test the once method
273    let result = TestScheduler::once(async |scheduler: Arc<TestScheduler>| {
274        let background = scheduler.background();
275        background.spawn(async { 42 }).await
276    });
277    assert_eq!(result, 42);
278
279    // Test the many method
280    let results = TestScheduler::many(3, async |scheduler: Arc<TestScheduler>| {
281        let background = scheduler.background();
282        background.spawn(async { 10 }).await
283    });
284    assert_eq!(results, vec![10, 10, 10]);
285
286    // Test the with_seed method
287    let result = TestScheduler::with_seed(123, async |scheduler: Arc<TestScheduler>| {
288        let background = scheduler.background();
289
290        // Spawn a background task and wait for its result
291        let task = background.spawn(async { 99 });
292        task.await
293    });
294    assert_eq!(result, 99);
295}
296
297#[test]
298fn test_block_with_timeout() {
299    // Test case: future completes within timeout
300    TestScheduler::once(async |scheduler| {
301        let background = scheduler.background();
302        let mut future = future::ready(42);
303        let output = background.block_with_timeout(&mut future, Duration::from_millis(100));
304        assert_eq!(output, Some(42));
305    });
306
307    // Test case: future times out
308    TestScheduler::once(async |scheduler| {
309        let background = scheduler.background();
310        let mut future = future::pending::<()>();
311        let output = background.block_with_timeout(&mut future, Duration::from_millis(50));
312        assert_eq!(output, None);
313    });
314
315    // Test case: future makes progress via timer but still times out
316    let mut results = BTreeSet::new();
317    TestScheduler::many(100, async |scheduler| {
318        let background = scheduler.background();
319        let mut task = background.spawn(async move {
320            Yield { polls: 10 }.await;
321            42
322        });
323        let output = background.block_with_timeout(&mut task, Duration::from_millis(50));
324        results.insert(output);
325    });
326    assert_eq!(
327        results.into_iter().collect::<Vec<_>>(),
328        vec![None, Some(42)]
329    );
330}
331
332struct Yield {
333    polls: usize,
334}
335
336impl Future for Yield {
337    type Output = ();
338
339    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
340        self.polls -= 1;
341        if self.polls == 0 {
342            Poll::Ready(())
343        } else {
344            cx.waker().wake_by_ref();
345            Poll::Pending
346        }
347    }
348}