#[macro_export]
macro_rules! godot_test_main {
($($test_name:ident),* $(,)?) => {
fn main() {
use $crate::runtime::{GodotRuntime, RuntimeConfig, UserCallbacks};
use $crate::prelude::*;
use $crate::__private::libtest_mimic::{Arguments, Trial};
use ::std::sync::{Arc, Mutex};
let mut args = Arguments::from_args();
args.test_threads = Some(1);
let tests: Vec<(&str, fn(&mut ::godot::prelude::Gd<::godot::prelude::SceneTree>) -> TestResult<()>)> = vec![
$((stringify!($test_name), $test_name),)*
];
if tests.is_empty() {
eprintln!("No tests defined!");
std::process::exit(1);
}
let test_results = Arc::new(Mutex::new(Vec::new()));
let test_results_clone = test_results.clone();
let filter = args.filter.as_ref().map(|s| s.as_str());
let filtered_tests: Vec<_> = tests
.into_iter()
.filter(|(name, _)| filter.map_or(true, |f| name.contains(f)))
.collect();
if filtered_tests.is_empty() {
println!("No tests to run with filter: {:?}", filter);
std::process::exit(0);
}
let callbacks = UserCallbacks {
initialize_ffi: |get_proc_addr, library| {
unsafe {
type GetProcAddrFn = unsafe extern "C" fn(*const i8) -> Option<unsafe extern "C" fn()>;
let get_proc_addr_fn = std::mem::transmute::<*const std::ffi::c_void, GetProcAddrFn>(get_proc_addr);
let library_ptr = library as *mut ::godot::sys::__GdextClassLibrary;
let config = ::godot::sys::GdextConfig::new(false);
::godot::sys::initialize(Some(get_proc_addr_fn), library_ptr, config);
}
Ok(())
},
load_class_method_table: |level| {
let init_level = match level {
0 => ::godot::init::InitLevel::Core,
1 => ::godot::init::InitLevel::Servers,
2 => ::godot::init::InitLevel::Scene,
3 => ::godot::init::InitLevel::Editor,
_ => return,
};
unsafe {
::godot::sys::load_class_method_table(init_level);
}
},
};
let config = RuntimeConfig {
headless: true,
verbose: false,
custom_args: vec![],
};
let runtime_result = GodotRuntime::run_godot(config, callbacks, move |scene_tree_ptr| {
let mut scene_tree = unsafe {
let obj_ptr = scene_tree_ptr as ::godot::sys::GDExtensionObjectPtr;
::godot::prelude::Gd::<::godot::prelude::SceneTree>::from_sys_init_opt(|ptr| {
*(ptr as *mut ::godot::sys::GDExtensionObjectPtr) = obj_ptr;
})
.expect("Failed to create SceneTree from pointer")
};
println!("\n🧪 Running {} Godot tests...\n", filtered_tests.len());
for (name, test_fn) in &filtered_tests {
print!("test {} ... ", name);
match test_fn(&mut scene_tree) {
Ok(()) => {
println!("ok");
test_results_clone
.lock()
.unwrap()
.push((name.to_string(), Ok(())));
}
Err(e) => {
println!("FAILED");
println!(" Error: {}", e);
test_results_clone
.lock()
.unwrap()
.push((name.to_string(), Err(format!("{}", e))));
}
}
}
println!();
scene_tree.quit();
Ok(())
});
if let Err(e) = runtime_result {
eprintln!("Runtime error: {}", e);
std::process::exit(1);
}
let results = test_results.lock().unwrap().clone();
let trials: Vec<Trial> = results
.into_iter()
.map(|(name, result)| {
Trial::test(name.clone(), move || {
result.clone().map_err(|e| $crate::__private::libtest_mimic::Failed::from(e))
})
})
.collect();
$crate::__private::libtest_mimic::run(&args, trials).exit();
}
};
}