From 049f1638d93ef3cf355d86a66cc25d5c8b293cae Mon Sep 17 00:00:00 2001 From: Jan Haller Date: Thu, 23 Mar 2023 00:14:06 +0100 Subject: [PATCH] Test runner: print test name before execution --- godot-core/src/lib.rs | 9 ++++++++ itest/godot/TestSuite.gd | 12 ++++++---- itest/rust/src/array_test.rs | 2 +- itest/rust/src/runner.rs | 43 ++++++++++++++++++++++-------------- 4 files changed, 45 insertions(+), 21 deletions(-) diff --git a/godot-core/src/lib.rs b/godot-core/src/lib.rs index bfd7fdbd7..80144d856 100644 --- a/godot-core/src/lib.rs +++ b/godot-core/src/lib.rs @@ -69,12 +69,21 @@ pub mod private { match std::panic::catch_unwind(code) { Ok(result) => Some(result), Err(err) => { + // Flush, to make sure previous Rust output (e.g. test announcement, or debug prints during app) have been printed + // TODO write custom panic handler and move this there, before panic backtrace printing + flush_stdout(); + log::godot_error!("Rust function panicked. Context: {}", error_context()); print_panic(err); None } } } + + pub fn flush_stdout() { + use std::io::Write; + std::io::stdout().flush().expect("flush stdout"); + } } #[cfg(feature = "trace")] diff --git a/itest/godot/TestSuite.gd b/itest/godot/TestSuite.gd index bde7b4b3c..b766a5fca 100644 --- a/itest/godot/TestSuite.gd +++ b/itest/godot/TestSuite.gd @@ -14,10 +14,12 @@ func assert_that(what: bool, message: String = "") -> bool: return true _assertion_failed = true + + printerr() # previous line not yet broken if message: - print("assertion failed: %s" % message) + push_error("GDScript assertion failed: %s" % message) else: - print("assertion failed") + push_error("GDScript assertion failed.") return false func assert_eq(left, right, message: String = "") -> bool: @@ -25,8 +27,10 @@ func assert_eq(left, right, message: String = "") -> bool: return true _assertion_failed = true + + printerr() # previous line not yet broken if message: - print("assertion failed: %s\n left: %s\n right: %s" % [message, left, right]) + push_error("GDScript assertion failed: %s\n left: %s\n right: %s" % [message, left, right]) else: - print("assertion failed: `(left == right)`\n left: %s\n right: %s" % [left, right]) + push_error("GDScript assertion failed: `(left == right)`\n left: %s\n right: %s" % [left, right]) return false diff --git a/itest/rust/src/array_test.rs b/itest/rust/src/array_test.rs index 9f830d327..c3c9cab77 100644 --- a/itest/rust/src/array_test.rs +++ b/itest/rust/src/array_test.rs @@ -143,7 +143,7 @@ fn array_slice_shallow() { #[itest] fn array_slice_deep() { let array = array![0, 1, 2, 3, 4, 5]; - let slice = array.slice_deep(5, 1, Some(-2)); + let slice = array.slice_deep(1, 1, Some(-2)); assert_eq!(slice, array![5, 3]); let subarray = array![2, 3]; diff --git a/itest/rust/src/runner.rs b/itest/rust/src/runner.rs index b072d5e98..70c452f73 100644 --- a/itest/rust/src/runner.rs +++ b/itest/rust/src/runner.rs @@ -71,27 +71,29 @@ impl IntegrationTests { let mut last_file = None; for test in tests { + print_test_pre(test.name, test.file.to_string(), &mut last_file, false); let outcome = run_rust_test(&test, &ctx); self.update_stats(&outcome); - print_test(test.file.to_string(), test.name, outcome, &mut last_file); + print_test_post(test.name, outcome); } } fn run_gdscript_tests(&mut self, tests: VariantArray) { let mut last_file = None; for test in tests.iter_shared() { + let test_file = get_property(&test, "suite_name"); + let test_case = get_property(&test, "method_name"); + + print_test_pre(&test_case, test_file, &mut last_file, true); let result = test.call("run", &[]); let success = result.try_to::().unwrap_or_else(|_| { panic!("GDScript test case {test} returned non-bool: {result}") }); - - let test_file = get_property(&test, "suite_name"); - let test_case = get_property(&test, "method_name"); let outcome = TestOutcome::from_bool(success); self.update_stats(&outcome); - print_test(test_file, &test_case, outcome, &mut last_file); + print_test_post(&test_case, outcome); } } @@ -178,16 +180,7 @@ fn run_rust_test(test: &RustTestCase, ctx: &TestContext) -> TestOutcome { TestOutcome::from_bool(success.is_some()) } -/// Prints a test name and its outcome. -/// -/// Note that this is run after a test run, so stdout/stderr output during the test will be printed before. -/// It would be possible to print the test name before and the outcome after, but that would split or duplicate the line. -fn print_test( - test_file: String, - test_case: &str, - outcome: TestOutcome, - last_file: &mut Option, -) { +fn print_test_pre(test_case: &str, test_file: String, last_file: &mut Option, flush: bool) { // Check if we need to open a new category for a file let print_file = last_file .as_ref() @@ -203,12 +196,30 @@ fn print_test( println!("\n {file_subtitle}:"); } - println!(" -- {test_case} ... {outcome}"); + print!(" -- {test_case} ... "); + if flush { + // Flush in GDScript, because its own print may come sooner than Rust prints otherwise + // (strictly speaking, this can also happen from Rust, when Godot prints something. So far, it didn't though... + godot::private::flush_stdout(); + } // State update for file-category-print *last_file = Some(test_file); } +/// Prints a test name and its outcome. +/// +/// Note that this is run after a test run, so stdout/stderr output during the test will be printed before. +/// It would be possible to print the test name before and the outcome after, but that would split or duplicate the line. +fn print_test_post(test_case: &str, outcome: TestOutcome) { + // If test failed, something was printed (e.g. assertion), so we can print the entire line again; otherwise just outcome on same line. + if matches!(outcome, TestOutcome::Failed) { + println!(" -- {test_case} ... {outcome}"); + } else { + println!("{outcome}"); + } +} + fn get_property(test: &Variant, property: &str) -> String { test.call("get", &[property.to_variant()]).to::() }