1use std::{
2 panic::{self, RefUnwindSafe},
3 rc::Rc,
4 sync::{
5 atomic::{AtomicU64, Ordering::SeqCst},
6 Arc,
7 },
8};
9
10use crate::{executor, platform, FontCache, MutableAppContext, Platform, TestAppContext};
11
12#[cfg(test)]
13#[ctor::ctor]
14fn init_logger() {
15 env_logger::builder()
16 .filter_level(log::LevelFilter::Info)
17 .init();
18}
19
20pub fn run_test(
21 mut num_iterations: u64,
22 mut starting_seed: u64,
23 max_retries: usize,
24 test_fn: &mut (dyn RefUnwindSafe
25 + Fn(&mut MutableAppContext, Rc<platform::test::ForegroundPlatform>, u64)),
26) {
27 let is_randomized = num_iterations > 1;
28 if is_randomized {
29 if let Ok(value) = std::env::var("SEED") {
30 starting_seed = value.parse().expect("invalid SEED variable");
31 }
32 if let Ok(value) = std::env::var("ITERATIONS") {
33 num_iterations = value.parse().expect("invalid ITERATIONS variable");
34 }
35 }
36
37 let atomic_seed = AtomicU64::new(starting_seed as u64);
38 let mut retries = 0;
39
40 loop {
41 let result = panic::catch_unwind(|| {
42 let foreground_platform = Rc::new(platform::test::foreground_platform());
43 let platform = Arc::new(platform::test::platform());
44 let font_system = platform.fonts();
45 let font_cache = Arc::new(FontCache::new(font_system));
46
47 loop {
48 let seed = atomic_seed.load(SeqCst);
49 if seed >= starting_seed + num_iterations {
50 break;
51 }
52
53 if is_randomized {
54 dbg!(seed);
55 }
56
57 let (foreground, background) = executor::deterministic(seed);
58 let mut cx = TestAppContext::new(
59 foreground_platform.clone(),
60 platform.clone(),
61 foreground.clone(),
62 background.clone(),
63 font_cache.clone(),
64 0,
65 );
66 cx.update(|cx| test_fn(cx, foreground_platform.clone(), seed));
67
68 atomic_seed.fetch_add(1, SeqCst);
69 }
70 });
71
72 match result {
73 Ok(_) => {
74 break;
75 }
76 Err(error) => {
77 if retries < max_retries {
78 retries += 1;
79 println!("retrying: attempt {}", retries);
80 } else {
81 if is_randomized {
82 eprintln!("failing seed: {}", atomic_seed.load(SeqCst));
83 }
84 panic::resume_unwind(error);
85 }
86 }
87 }
88 }
89}