turbo_tasks_testing/
run.rs1use std::{env, fmt::Debug, future::Future, sync::Arc};
2
3use anyhow::Result;
4use turbo_tasks::{TurboTasks, TurboTasksApi, trace::TraceRawVcs};
5use turbo_tasks_backend::{BackingStorage, TurboTasksBackend};
6
7pub struct TestInstance {
15 pub tt: Arc<dyn TurboTasksApi>,
16 pub snapshot_and_evict: Box<dyn Fn() + Send + Sync>,
17}
18
19pub struct Registration {
23 create_turbo_tasks: fn(&str, bool) -> TestInstance,
24}
25
26impl Registration {
27 #[doc(hidden)]
28 pub const fn new(create_turbo_tasks: fn(&str, bool) -> TestInstance) -> Self {
29 Registration { create_turbo_tasks }
30 }
31
32 pub fn create_turbo_tasks(&self, name: &str, initial: bool) -> TestInstance {
33 (self.create_turbo_tasks)(name, initial)
34 }
35}
36
37pub fn test_instance<B>(tt: Arc<TurboTasks<TurboTasksBackend<B>>>) -> TestInstance
42where
43 B: BackingStorage + 'static,
44{
45 let tt_for_evict = tt.clone();
46 let snapshot_and_evict = Box::new(move || {
47 let _ = tt_for_evict
48 .backend()
49 .snapshot_and_evict_for_testing(&*tt_for_evict);
50 });
51 TestInstance {
52 tt: tt as Arc<dyn TurboTasksApi>,
53 snapshot_and_evict,
54 }
55}
56
57#[macro_export]
58macro_rules! register {
59 () => {{
60 fn create_turbo_tasks(name: &str, initial: bool) -> turbo_tasks_testing::TestInstance {
61 let inner = include!(concat!(
62 env!("CARGO_MANIFEST_DIR"),
63 "/tests/test_config.trs"
64 ));
65 turbo_tasks_testing::test_instance((inner)(name, initial))
66 }
67 turbo_tasks_testing::Registration::new(create_turbo_tasks)
68 }};
69}
70
71pub async fn run_once_without_cache_check<T>(
72 registration: &Registration,
73 fut: impl Future<Output = T> + Send + 'static,
74) -> T
75where
76 T: TraceRawVcs + Send + 'static,
77{
78 let name = closure_to_name(&fut);
79 let instance = registration.create_turbo_tasks(&name, true);
80 turbo_tasks::run_once(instance.tt, async move { Ok(fut.await) })
81 .await
82 .unwrap()
83}
84
85pub async fn run_without_cache_check<T>(
86 registration: &Registration,
87 fut: impl Future<Output = T> + Send + 'static,
88) -> T
89where
90 T: TraceRawVcs + Send + 'static,
91{
92 let name = closure_to_name(&fut);
93 let instance = registration.create_turbo_tasks(&name, true);
94 turbo_tasks::run(instance.tt, async move { Ok(fut.await) })
95 .await
96 .unwrap()
97}
98
99fn closure_to_name<T>(value: &T) -> String {
100 let name = std::any::type_name_of_val(value);
101 name.replace("::{{closure}}", "").replace("::", "_")
102}
103
104pub async fn run_once<T, F>(
105 registration: &Registration,
106 mut fut: impl FnMut() -> F + Send + 'static,
107) -> Result<()>
108where
109 F: Future<Output = Result<T>> + Send + 'static,
110 T: Debug + PartialEq + Eq + TraceRawVcs + Send + 'static,
111{
112 run_with_tt(registration, move |tt| turbo_tasks::run_once(tt, fut())).await
113}
114
115pub async fn run<T, F>(
116 registration: &Registration,
117 mut fut: impl FnMut() -> F + Send + 'static,
118) -> Result<()>
119where
120 F: Future<Output = Result<T>> + Send + 'static,
121 T: Debug + PartialEq + Eq + TraceRawVcs + Send + 'static,
122{
123 run_with_tt(registration, move |tt| turbo_tasks::run(tt, fut())).await
124}
125
126pub async fn run_with_tt<T, F>(
127 registration: &Registration,
128 mut fut: impl FnMut(Arc<dyn TurboTasksApi>) -> F + Send + 'static,
129) -> Result<()>
130where
131 F: Future<Output = Result<T>> + Send + 'static,
132 T: Debug + PartialEq + Eq + TraceRawVcs + Send + 'static,
133{
134 let infinite_initial_runs = env::var("INFINITE_INITIAL_RUNS").is_ok();
135 let infinite_memory_runs = !infinite_initial_runs && env::var("INFINITE_MEMORY_RUNS").is_ok();
136 let single_run = infinite_initial_runs || env::var("SINGLE_RUN").is_ok();
137 let name = closure_to_name(&fut);
138 let mut i = 1;
139 loop {
140 let instance = registration.create_turbo_tasks(&name, true);
141 println!("Run #{i} (without cache)");
142 let start = std::time::Instant::now();
143 let first = fut(instance.tt.clone()).await?;
144 println!("Run #{i} took {:?}", start.elapsed());
145 i += 1;
146 if !single_run {
147 let max_run = if infinite_memory_runs { usize::MAX } else { 10 };
148 for _ in 0..max_run {
149 (instance.snapshot_and_evict)();
154 println!("Run #{i} (with memory cache, same TurboTasks instance, post-evict)");
155 let start = std::time::Instant::now();
156 let second = fut(instance.tt.clone()).await?;
157 println!("Run #{i} took {:?}", start.elapsed());
158 i += 1;
159 assert_eq!(first, second);
160 }
161 }
162 let start = std::time::Instant::now();
163 instance.tt.stop_and_wait().await;
164 println!("Stopping TurboTasks took {:?}", start.elapsed());
165 if !single_run {
166 for _ in 10..20 {
167 let instance = registration.create_turbo_tasks(&name, false);
168 println!("Run #{i} (with filesystem cache if available, new TurboTasks instance)");
169 let start = std::time::Instant::now();
170 let third = fut(instance.tt.clone()).await?;
171 println!("Run #{i} took {:?}", start.elapsed());
172 i += 1;
173 let start = std::time::Instant::now();
174 instance.tt.stop_and_wait().await;
175 println!("Stopping TurboTasks took {:?}", start.elapsed());
176 assert_eq!(first, third);
177 }
178 }
179 if !infinite_initial_runs {
180 break;
181 }
182 }
183 Ok(())
184}