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