Skip to main content

fdf_env/
test.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Helpers for writing tests using drivers and dispatchers
6
7use std::sync::{Arc, mpsc};
8
9use fdf_core::dispatcher::CurrentDispatcher;
10use libasync_dispatcher::{AsAsyncDispatcherRef, OnDispatcher};
11
12use super::*;
13
14/// Runs the given closure under the a new root driver dispatcher. Use [`fdf::CurrentDispatcher`]
15/// to reference the dispatcher if needed.
16pub fn run_in_driver<T: Send + 'static>(name: &str, p: impl FnOnce() -> T + Send + 'static) -> T {
17    run_in_driver_raw(name, true, false, |tx| tx.send(p()).unwrap())
18}
19
20/// Runs the given closure under a new root driver dispatcher, with some additional options.
21/// Use [`fdf::CurrentDispatcher`] to reference the dispatcher if needed.
22pub fn run_in_driver_etc<T: Send + 'static>(
23    name: &str,
24    allow_thread_blocking: bool,
25    unsynchronized: bool,
26    p: impl FnOnce() -> T + Send + 'static,
27) -> T {
28    run_in_driver_raw(name, allow_thread_blocking, unsynchronized, |tx| tx.send(p()).unwrap())
29}
30
31/// Runs the given future to completion under the a new root driver dispatcher. Use
32/// [`fdf::CurrentDispatcher`] to reference the dispatcher if needed.
33pub fn spawn_in_driver<T: Send + 'static>(
34    name: &str,
35    p: impl Future<Output = T> + Send + 'static,
36) -> T {
37    run_in_driver_raw(name, true, false, |tx| {
38        CurrentDispatcher.spawn(async move { tx.send(p.await).unwrap() });
39    })
40}
41
42/// Runs the given future to completion under a new root driver dispatcher, with some additional
43/// options. Use [`fdf::CurrentDispatcher`] to reference the dispatcher if needed.
44pub fn spawn_in_driver_etc<T: Send + 'static>(
45    name: &str,
46    allow_thread_blocking: bool,
47    unsynchronized: bool,
48    p: impl Future<Output = T> + Send + 'static,
49) -> T {
50    run_in_driver_raw(name, allow_thread_blocking, unsynchronized, |tx| {
51        CurrentDispatcher.spawn(async move { tx.send(p.await).unwrap() });
52    })
53}
54
55fn run_in_driver_raw<T: Send + 'static>(
56    name: &str,
57    allow_thread_blocking: bool,
58    unsynchronized: bool,
59    p: impl FnOnce(mpsc::Sender<T>) + Send + 'static,
60) -> T {
61    let env = Arc::new(Environment::start(0).unwrap());
62    let env_clone = env.clone();
63
64    let (shutdown_tx, shutdown_rx) = mpsc::channel();
65    let driver_value: u32 = 0x1337;
66    let driver_value_ptr = &driver_value as *const u32;
67    let driver = env.new_driver(driver_value_ptr);
68    let dispatcher = DispatcherBuilder::new().name(name);
69    let dispatcher =
70        if allow_thread_blocking { dispatcher.allow_thread_blocking() } else { dispatcher };
71    let dispatcher = if unsynchronized { dispatcher.unsynchronized() } else { dispatcher };
72    let dispatcher = dispatcher.shutdown_observer(move |dispatcher| {
73        // We verify that the dispatcher has no tasks left queued in it,
74        // just because this is testing code.
75        assert!(!env_clone.dispatcher_has_queued_tasks(dispatcher.as_dispatcher_ref()));
76    });
77    let dispatcher = driver.new_dispatcher(dispatcher).unwrap().release();
78
79    let (finished_tx, finished_rx) = mpsc::channel();
80    dispatcher
81        .post_task_sync(move |_| {
82            p(finished_tx);
83        })
84        .unwrap();
85    let res = finished_rx.recv().unwrap();
86
87    driver.shutdown(move |driver| {
88        // SAFETY: driver lives on the stack, it's safe to dereference it.
89        assert!(unsafe { *driver.0 } == 0x1337);
90        shutdown_tx.send(()).unwrap();
91    });
92
93    shutdown_rx.recv().unwrap();
94
95    res
96}