Minimize code generation for synchronous `gpui::test` macro

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/editor/Cargo.toml      |  3 
crates/gpui/Cargo.toml        |  4 
crates/gpui/src/lib.rs        |  4 
crates/gpui/src/test.rs       | 84 ++++++++++++++++++++++++++++++++++++
crates/gpui_macros/src/lib.rs | 70 ++----------------------------
5 files changed, 94 insertions(+), 71 deletions(-)

Detailed changes

crates/editor/Cargo.toml 🔗

@@ -4,7 +4,7 @@ version = "0.1.0"
 edition = "2018"
 
 [features]
-test-support = ["buffer/test-support"]
+test-support = ["buffer/test-support", "gpui/test-support"]
 
 [dependencies]
 buffer = { path = "../buffer" }
@@ -23,6 +23,7 @@ smol = "1.2"
 
 [dev-dependencies]
 buffer = { path = "../buffer", features = ["test-support"] }
+gpui = { path = "../gpui", features = ["test-support"] }
 rand = "0.8"
 unindent = "0.1.7"
 tree-sitter = "0.19"

crates/gpui/Cargo.toml 🔗

@@ -5,15 +5,15 @@ name = "gpui"
 version = "0.1.0"
 
 [features]
-test-support = []
+test-support = ["env_logger"]
 
 [dependencies]
 gpui_macros = { path = "../gpui_macros" }
 sum_tree = { path = "../sum_tree" }
-
 async-task = "4.0.3"
 backtrace = "0.3"
 ctor = "0.1"
+env_logger = { version = "0.8", optional = true }
 etagere = "0.2"
 image = "0.23"
 lazy_static = "1.4.0"

crates/gpui/src/lib.rs 🔗

@@ -1,8 +1,8 @@
 mod app;
 pub use app::*;
 mod assets;
-#[cfg(test)]
-mod test;
+#[cfg(any(test, feature = "test-support"))]
+pub mod test;
 pub use assets::*;
 pub mod elements;
 pub mod font_cache;

crates/gpui/src/test.rs 🔗

@@ -1,8 +1,88 @@
-use ctor::ctor;
+use std::{
+    panic::{self, RefUnwindSafe},
+    rc::Rc,
+    sync::{
+        atomic::{AtomicU64, Ordering::SeqCst},
+        Arc,
+    },
+};
 
-#[ctor]
+use crate::{executor, platform, FontCache, MutableAppContext, Platform, TestAppContext};
+
+#[cfg(test)]
+#[ctor::ctor]
 fn init_logger() {
     env_logger::builder()
         .filter_level(log::LevelFilter::Info)
         .init();
 }
+
+pub fn run_sync_test(
+    mut num_iterations: u64,
+    mut starting_seed: u64,
+    max_retries: usize,
+    test_fn: &mut (dyn RefUnwindSafe + Fn(&mut MutableAppContext, u64)),
+) {
+    let is_randomized = num_iterations > 1;
+    if is_randomized {
+        if let Ok(value) = std::env::var("SEED") {
+            starting_seed = value.parse().expect("invalid SEED variable");
+        }
+        if let Ok(value) = std::env::var("ITERATIONS") {
+            num_iterations = value.parse().expect("invalid ITERATIONS variable");
+        }
+    }
+
+    let atomic_seed = AtomicU64::new(starting_seed as u64);
+    let mut retries = 0;
+
+    loop {
+        let result = panic::catch_unwind(|| {
+            let foreground_platform = Rc::new(platform::test::foreground_platform());
+            let platform = Arc::new(platform::test::platform());
+            let font_system = platform.fonts();
+            let font_cache = Arc::new(FontCache::new(font_system));
+
+            loop {
+                let seed = atomic_seed.load(SeqCst);
+                if seed >= starting_seed + num_iterations {
+                    break;
+                }
+
+                if is_randomized {
+                    dbg!(seed);
+                }
+
+                let (foreground, background) = executor::deterministic(seed);
+                let mut cx = TestAppContext::new(
+                    foreground_platform.clone(),
+                    platform.clone(),
+                    foreground.clone(),
+                    background.clone(),
+                    font_cache.clone(),
+                    0,
+                );
+                cx.update(|cx| test_fn(cx, seed));
+
+                atomic_seed.fetch_add(1, SeqCst);
+            }
+        });
+
+        match result {
+            Ok(_) => {
+                break;
+            }
+            Err(error) => {
+                if retries < max_retries {
+                    retries += 1;
+                    println!("retrying: attempt {}", retries);
+                } else {
+                    if is_randomized {
+                        eprintln!("failing seed: {}", atomic_seed.load(SeqCst));
+                    }
+                    panic::resume_unwind(error);
+                }
+            }
+        }
+    }
+}

crates/gpui_macros/src/lib.rs 🔗

@@ -192,70 +192,12 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
             fn #outer_fn_name() {
                 #inner_fn
 
-                let is_randomized = #num_iterations > 1;
-                let mut num_iterations = #num_iterations as u64;
-                let mut starting_seed = #starting_seed as u64;
-                if is_randomized {
-                    if let Ok(value) = std::env::var("SEED") {
-                        starting_seed = value.parse().expect("invalid SEED variable");
-                    }
-                    if let Ok(value) = std::env::var("ITERATIONS") {
-                        num_iterations = value.parse().expect("invalid ITERATIONS variable");
-                    }
-                }
-
-                let mut atomic_seed = std::sync::atomic::AtomicU64::new(starting_seed as u64);
-                let mut retries = 0;
-
-                loop {
-                    let result = std::panic::catch_unwind(|| {
-                        let foreground_platform = std::rc::Rc::new(#namespace::platform::test::foreground_platform());
-                        let platform = std::sync::Arc::new(#namespace::platform::test::platform());
-                        let font_system = #namespace::Platform::fonts(platform.as_ref());
-                        let font_cache = std::sync::Arc::new(#namespace::FontCache::new(font_system));
-
-                        loop {
-                            let seed = atomic_seed.load(std::sync::atomic::Ordering::SeqCst);
-                            if seed >= starting_seed + num_iterations {
-                                break;
-                            }
-
-                            if is_randomized {
-                                dbg!(seed);
-                            }
-
-                            let (foreground, background) = #namespace::executor::deterministic(seed);
-                            let mut cx = #namespace::TestAppContext::new(
-                                foreground_platform.clone(),
-                                platform.clone(),
-                                foreground.clone(),
-                                background.clone(),
-                                font_cache.clone(),
-                                0,
-                            );
-                            cx.update(|cx| #inner_fn_name(#inner_fn_args));
-
-                            atomic_seed.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
-                        }
-                    });
-
-                    match result {
-                        Ok(_) => {
-                            break;
-                        }
-                        Err(error) => {
-                            if retries < #max_retries {
-                                retries += 1;
-                                println!("retrying: attempt {}", retries);
-                            } else {
-                                if is_randomized {
-                                    eprintln!("failing seed: {}", atomic_seed.load(std::sync::atomic::Ordering::SeqCst));
-                                }
-                                std::panic::resume_unwind(error);
-                            }
-                        }
-                    }
-                }
+                #namespace::test::run_sync_test(
+                    #num_iterations as u64,
+                    #starting_seed as u64,
+                    #max_retries,
+                    &mut |cx, seed| #inner_fn_name(#inner_fn_args)
+                );
             }
         }
     };