turbo_tasks_testing/
run.rs

1use std::{
2    fmt::Debug,
3    future::Future,
4    sync::{Arc, OnceLock},
5};
6
7use anyhow::Result;
8use turbo_tasks::{TurboTasksApi, run_once, trace::TraceRawVcs};
9
10pub struct Registration {
11    execution_lock: OnceLock<()>,
12    func: fn(),
13    create_turbo_tasks: fn(&str, bool) -> Arc<dyn TurboTasksApi>,
14}
15
16impl Registration {
17    #[doc(hidden)]
18    pub const fn new(
19        create_turbo_tasks: fn(&str, bool) -> Arc<dyn TurboTasksApi>,
20        func: fn(),
21    ) -> Self {
22        Registration {
23            execution_lock: OnceLock::new(),
24            func,
25            create_turbo_tasks,
26        }
27    }
28
29    /// Called by [`run`]. You can call this manually if you're not using
30    /// [`run`] (e.g. because you're using a customized `turbo_tasks`
31    /// implementation or tokio runtime).
32    pub fn ensure_registered(&self) {
33        self.execution_lock.get_or_init(self.func);
34    }
35
36    pub fn create_turbo_tasks(&self, name: &str, initial: bool) -> Arc<dyn TurboTasksApi> {
37        (self.create_turbo_tasks)(name, initial)
38    }
39}
40
41#[macro_export]
42macro_rules! register {
43    ($($other_register_fns:expr),* $(,)?) => {{
44        use turbo_tasks::TurboTasksApi;
45        use std::sync::Arc;
46        fn create_turbo_tasks(name: &str, initial: bool) -> Arc<dyn TurboTasksApi> {
47            let inner = include!(concat!(
48                env!("CARGO_MANIFEST_DIR"),
49                "/tests/test_config.trs"
50            ));
51            (inner)(name, initial)
52        }
53        fn register_impl() {
54            $($other_register_fns();)*
55            turbo_tasks::register();
56            include!(concat!(
57                env!("OUT_DIR"),
58                "/register_test_",
59                module_path!(),
60                ".rs",
61            ));
62        }
63        turbo_tasks_testing::Registration::new(create_turbo_tasks, register_impl)
64    }};
65}
66
67pub async fn run_without_cache_check<T>(
68    registration: &Registration,
69    fut: impl Future<Output = T> + Send + 'static,
70) -> T
71where
72    T: TraceRawVcs + Send + 'static,
73{
74    registration.ensure_registered();
75    let name = closure_to_name(&fut);
76    let tt = registration.create_turbo_tasks(&name, true);
77    run_once(tt, async move { Ok(fut.await) }).await.unwrap()
78}
79
80fn closure_to_name<T>(value: &T) -> String {
81    let name = std::any::type_name_of_val(value);
82    name.replace("::{{closure}}", "").replace("::", "_")
83}
84
85pub async fn run<T, F>(
86    registration: &Registration,
87    fut: impl Fn() -> F + Send + 'static,
88) -> Result<()>
89where
90    F: Future<Output = Result<T>> + Send + 'static,
91    T: Debug + PartialEq + Eq + TraceRawVcs + Send + 'static,
92{
93    run_with_tt(registration, move |tt| run_once(tt, fut())).await
94}
95
96pub async fn run_with_tt<T, F>(
97    registration: &Registration,
98    fut: impl Fn(Arc<dyn TurboTasksApi>) -> F + Send + 'static,
99) -> Result<()>
100where
101    F: Future<Output = Result<T>> + Send + 'static,
102    T: Debug + PartialEq + Eq + TraceRawVcs + Send + 'static,
103{
104    registration.ensure_registered();
105
106    let name = closure_to_name(&fut);
107    let tt = registration.create_turbo_tasks(&name, true);
108    println!("Run #1 (without cache)");
109    let start = std::time::Instant::now();
110    let first = fut(tt.clone()).await?;
111    println!("Run #1 took {:?}", start.elapsed());
112    for i in 2..10 {
113        println!("Run #{i} (with memory cache, same TurboTasks instance)");
114        let start = std::time::Instant::now();
115        let second = fut(tt.clone()).await?;
116        println!("Run #{i} took {:?}", start.elapsed());
117        assert_eq!(first, second);
118    }
119    let start = std::time::Instant::now();
120    tt.stop_and_wait().await;
121    println!("Stopping TurboTasks took {:?}", start.elapsed());
122    for i in 10..20 {
123        let tt = registration.create_turbo_tasks(&name, false);
124        println!("Run #{i} (with persistent cache if available, new TurboTasks instance)");
125        let start = std::time::Instant::now();
126        let third = fut(tt.clone()).await?;
127        println!("Run #{i} took {:?}", start.elapsed());
128        let start = std::time::Instant::now();
129        tt.stop_and_wait().await;
130        println!("Stopping TurboTasks took {:?}", start.elapsed());
131        assert_eq!(first, third);
132    }
133    Ok(())
134}