From 77550a2f0df75e4c5729f786d0090bfc74c408d7 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Wed, 3 Jan 2024 20:57:28 +0100 Subject: [PATCH] Turn FFI tests into native Rust tests Keep running tests serially to avoid breaking assumptions. I think many of these tests can run in parallel and/or don't need test_init(). Use the safe variant everywhere, to get it done faster. --- CONTRIBUTING.rst | 4 +- Cargo.lock | 110 ++++++++++++++++--- Cargo.toml | 9 +- cmake/Rust.cmake | 6 +- fish-rust/build.rs | 2 - fish-rust/src/abbrs.rs | 10 +- fish-rust/src/ast.rs | 10 +- fish-rust/src/autoload.rs | 10 +- fish-rust/src/builtins/mod.rs | 2 +- fish-rust/src/builtins/tests/string_tests.rs | 37 +++++-- fish-rust/src/builtins/tests/test_tests.rs | 8 +- fish-rust/src/fds.rs | 9 +- fish-rust/src/ffi_tests.rs | 63 ----------- fish-rust/src/future_feature_flags.rs | 16 ++- fish-rust/src/input.rs | 10 +- fish-rust/src/lib.rs | 4 +- fish-rust/src/parse_util.rs | 24 ++-- fish-rust/src/signal.rs | 11 +- fish-rust/src/smoke.rs | 26 ----- fish-rust/src/termsize.rs | 11 +- fish-rust/src/tests/abbrs.rs | 8 +- fish-rust/src/tests/complete.rs | 23 ++-- fish-rust/src/tests/debounce.rs | 16 ++- fish-rust/src/tests/env.rs | 15 ++- fish-rust/src/tests/env_universal_common.rs | 46 +++++--- fish-rust/src/tests/expand.rs | 25 +++-- fish-rust/src/tests/fd_monitor.rs | 9 +- fish-rust/src/tests/highlight.rs | 15 ++- fish-rust/src/tests/history.rs | 38 +++++-- fish-rust/src/tests/mod.rs | 9 +- fish-rust/src/tests/pager.rs | 16 ++- fish-rust/src/tests/parse_util.rs | 9 +- fish-rust/src/tests/parser.rs | 79 +++++++++---- fish-rust/src/tests/screen.rs | 23 ++-- fish-rust/src/tests/topic_monitor.rs | 16 ++- fish-rust/src/threads.rs | 3 + fish-rust/src/wutil/gettext.rs | 10 +- fish-rust/src/wutil/tests.rs | 9 +- src/fish_tests.cpp | 11 -- 39 files changed, 463 insertions(+), 299 deletions(-) delete mode 100644 fish-rust/src/ffi_tests.rs delete mode 100644 fish-rust/src/smoke.rs diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2844be013..5be3e4921 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -266,14 +266,12 @@ regressions in the future (i.e., we don’t reintroduce the bug). The tests can be found in three places: -- src/fish_tests.cpp for tests to the core C++ code +- fish-rust/src/tests for unit tests. - tests/checks for script tests, run by `littlecheck `__ - tests/pexpects for interactive tests using `pexpect `__ When in doubt, the bulk of the tests should be added as a littlecheck test in tests/checks, as they are the easiest to modify and run, and much faster and more dependable than pexpect tests. The syntax is fairly self-explanatory. It's a fish script with the expected output in ``# CHECK:`` or ``# CHECKERR:`` (for stderr) comments. -fish_tests.cpp is mostly useful for unit tests - if you wish to test that a function does the correct thing for given input, use it. - The pexpects are written in python and can simulate input and output to/from a terminal, so they are needed for anything that needs actual interactivity. The runner is in build_tools/pexpect_helper.py, in case you need to modify something there. Local testing diff --git a/Cargo.lock b/Cargo.lock index bde8dee82..76a59d5eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", "version_check", ] @@ -183,6 +183,12 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" @@ -200,6 +206,15 @@ dependencies = [ "libloading", ] +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -339,7 +354,6 @@ dependencies = [ "fast-float", "git-version", "hexponent", - "inventory", "lazy_static", "libc", "lru", @@ -353,6 +367,7 @@ dependencies = [ "rand", "rand_pcg", "rsconf", + "serial_test", "unixstring", "widestring", "widestring-suffix", @@ -364,7 +379,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", ] @@ -455,12 +470,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306" -[[package]] -name = "inventory" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53088c87cf71c9d4f3372a2cb9eea1e7b8a0b1bf8b7f7d23fe5b76dbb07e63b" - [[package]] name = "itertools" version = "0.9.0" @@ -518,7 +527,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "winapi", ] @@ -537,6 +546,15 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.20" @@ -604,7 +622,7 @@ checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ "autocfg", "bitflags 1.3.2", - "cfg-if", + "cfg-if 1.0.0", "libc", ] @@ -644,6 +662,30 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93f386bb233083c799e6e642a9d73db98c24a5deeb95ffc85bf281255dffc98" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "smallvec", + "winapi", +] + [[package]] name = "pcre2" version = "0.2.3" @@ -791,6 +833,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.3.5" @@ -868,6 +916,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "scratch" version = "1.0.7" @@ -905,12 +959,40 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef5f7c7434b2f2c598adc6f9494648a1e41274a75c0ba4056f680ae0c117fd6" +dependencies = [ + "lazy_static", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08338d8024b227c62bd68a12c7c9883f5c66780abaef15c550dc56f46ee6515" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "shlex" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + [[package]] name = "strum_macros" version = "0.24.3" @@ -952,9 +1034,9 @@ version = "3.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", "windows-sys", ] @@ -994,7 +1076,7 @@ version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "once_cell", ] diff --git a/Cargo.toml b/Cargo.toml index 1ca5a435a..696463d7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,6 @@ autocxx = "0.23.1" bitflags = "2.4.0" cxx = "1.0" errno = "0.2.8" -inventory = { version = "0.3.3", optional = true} lazy_static = "1.4.0" libc = "0.2.137" lru = "0.10.0" @@ -62,6 +61,9 @@ widestring = "1.0.2" rand_pcg = "0.3.1" git-version = "0.3" +[dev-dependencies] +serial_test = "0.4.0" + [build-dependencies] autocxx-build = "0.23.1" cc = { git = "https://github.com/mqudsi/cc-rs", branch = "fish" } @@ -74,10 +76,7 @@ crate-type = ["staticlib"] path = "fish-rust/src/lib.rs" [features] -# The fish-ffi-tests feature causes tests to be built which need to use the FFI. -# These tests are run by fish_tests(). -default = ["fish-ffi-tests"] -fish-ffi-tests = ["inventory"] +default = [] benchmark = [] # The following features are auto-detected by the build-script and should not be enabled manually. diff --git a/cmake/Rust.cmake b/cmake/Rust.cmake index 5e5dbb94e..389a3e414 100644 --- a/cmake/Rust.cmake +++ b/cmake/Rust.cmake @@ -25,7 +25,7 @@ set(fish_rust_target "fish-rust") set(fish_autocxx_gen_dir "${CMAKE_BINARY_DIR}/fish-autocxx-gen/") -set(FISH_CRATE_FEATURES "fish-ffi-tests") +set(FISH_CRATE_FEATURES) if(NOT DEFINED CARGO_FLAGS) # Corrosion doesn't like an empty string as FLAGS. This is basically a no-op alternative. # See https://github.com/corrosion-rs/corrosion/issues/356 @@ -33,13 +33,13 @@ if(NOT DEFINED CARGO_FLAGS) endif() if(DEFINED ASAN) list(APPEND CARGO_FLAGS "-Z" "build-std") - list(APPEND FISH_CRATE_FEATURES "asan") + list(APPEND FISH_CRATE_FEATURES FEATURES "asan") endif() corrosion_import_crate( MANIFEST_PATH "${CMAKE_SOURCE_DIR}/Cargo.toml" CRATES "fish-rust" - FEATURES "${FISH_CRATE_FEATURES}" + "${FISH_CRATE_FEATURES}" FLAGS "${CARGO_FLAGS}" ) diff --git a/fish-rust/build.rs b/fish-rust/build.rs index dc94f9072..a90be9400 100644 --- a/fish-rust/build.rs +++ b/fish-rust/build.rs @@ -113,7 +113,6 @@ fn main() { "fish-rust/src/fd_readable_set.rs", "fish-rust/src/fds.rs", "fish-rust/src/ffi_init.rs", - "fish-rust/src/ffi_tests.rs", "fish-rust/src/fish_indent.rs", "fish-rust/src/fish_key_reader.rs", "fish-rust/src/fish.rs", @@ -138,7 +137,6 @@ fn main() { "fish-rust/src/redirection.rs", "fish-rust/src/screen.rs", "fish-rust/src/signal.rs", - "fish-rust/src/smoke.rs", "fish-rust/src/termsize.rs", "fish-rust/src/threads.rs", "fish-rust/src/timer.rs", diff --git a/fish-rust/src/abbrs.rs b/fish-rust/src/abbrs.rs index 41556bdf3..150ffea2a 100644 --- a/fish-rust/src/abbrs.rs +++ b/fish-rust/src/abbrs.rs @@ -11,6 +11,8 @@ use once_cell::sync::Lazy; use crate::abbrs::abbrs_ffi::abbrs_replacer_t; use crate::parse_constants::SourceRange; +#[cfg(test)] +use crate::tests::prelude::*; use pcre2::utf32::Regex; use self::abbrs_ffi::{abbreviation_t, abbrs_position_t, abbrs_replacement_t}; @@ -439,8 +441,10 @@ impl<'a> GlobalAbbrs<'a> { self.g.erase(name.as_wstr()); } } -use crate::ffi_tests::add_test; -add_test!("rename_abbrs", || { +#[test] +#[serial] +fn rename_abbrs() { + test_init(); use crate::abbrs::{Abbreviation, Position}; use crate::wchar::prelude::*; @@ -473,4 +477,4 @@ add_test!("rename_abbrs", || { assert!(abbrs_g.erase(L!("gcc"))); assert!(!abbrs_g.erase(L!("gcc"))); }) -}); +} diff --git a/fish-rust/src/ast.rs b/fish-rust/src/ast.rs index 296c53601..933da72e0 100644 --- a/fish-rust/src/ast.rs +++ b/fish-rust/src/ast.rs @@ -18,6 +18,8 @@ use crate::parse_constants::{ SOURCE_OFFSET_INVALID, }; use crate::parse_tree::ParseToken; +#[cfg(test)] +use crate::tests::prelude::*; use crate::tokenizer::{ variable_assignment_equals_pos, TokFlags, TokenType, Tokenizer, TokenizerError, TOK_ACCEPT_UNFINISHED, TOK_CONTINUE_AFTER_ERROR, TOK_SHOW_COMMENTS, @@ -4011,12 +4013,14 @@ fn keyword_for_token(tok: TokenType, token: &wstr) -> ParseKeyword { result } -use crate::ffi_tests::add_test; -add_test!("test_ast_parse", || { +#[test] +#[serial] +fn test_ast_parse() { + test_init(); let src = L!("echo"); let ast = Ast::parse(src, ParseTreeFlags::empty(), None); assert!(!ast.any_error); -}); +} pub use ast_ffi::{Category, Type}; diff --git a/fish-rust/src/autoload.rs b/fish-rust/src/autoload.rs index 03da67dc6..e5b9352cd 100644 --- a/fish-rust/src/autoload.rs +++ b/fish-rust/src/autoload.rs @@ -4,6 +4,8 @@ use crate::common::{escape, ScopeGuard}; use crate::env::Environment; use crate::io::IoChain; use crate::parser::Parser; +#[cfg(test)] +use crate::tests::prelude::*; use crate::wchar::{wstr, WString, L}; use crate::wutil::{file_id_for_path, FileId, INVALID_FILE_ID}; use lru::LruCache; @@ -315,9 +317,11 @@ impl AutoloadFileCache { } } -use crate::ffi_tests::add_test; #[widestring_suffix::widestrs] -add_test!("test_autoload", || { +#[test] +#[serial] +fn test_autoload() { + test_init(); use crate::common::{charptr2wcstring, wcs2zstring, write_loop}; use crate::fds::wopen_cloexec; use crate::wutil::sprintf; @@ -387,4 +391,4 @@ add_test!("test_autoload", || { run!("rm -Rf %ls"L, p1); run!("rm -Rf %ls"L, p2); -}); +} diff --git a/fish-rust/src/builtins/mod.rs b/fish-rust/src/builtins/mod.rs index 352a4ccd1..88c8e26e0 100644 --- a/fish-rust/src/builtins/mod.rs +++ b/fish-rust/src/builtins/mod.rs @@ -40,7 +40,7 @@ pub mod r#type; pub mod ulimit; pub mod wait; -// Note these tests will NOT run with cfg(test). +#[cfg(test)] mod tests; mod prelude { diff --git a/fish-rust/src/builtins/tests/string_tests.rs b/fish-rust/src/builtins/tests/string_tests.rs index 74fe4b231..951772b62 100644 --- a/fish-rust/src/builtins/tests/string_tests.rs +++ b/fish-rust/src/builtins/tests/string_tests.rs @@ -1,14 +1,20 @@ -use crate::ffi_tests::add_test; +use crate::tests::prelude::*; +use crate::wchar::prelude::*; -add_test! {"test_string", || { - use crate::parser::Parser; +#[test] +#[serial] +fn test_string() { + test_init(); + use crate::builtins::shared::{STATUS_CMD_ERROR, STATUS_CMD_OK, STATUS_INVALID_ARGS}; use crate::builtins::string::string; - use crate::wchar::prelude::*; use crate::common::escape; - use crate::builtins::shared::{STATUS_CMD_ERROR,STATUS_CMD_OK, STATUS_INVALID_ARGS}; - use crate::io::{IoStreams, OutputStream, StringOutputStream}; - use crate::future_feature_flags::{scoped_test, FeatureFlag}; + use crate::io::{IoStreams, OutputStream, StringOutputStream}; + use crate::parser::Parser; + use crate::tests::prelude::*; + use crate::wchar::prelude::*; + + test_init(); // avoid 1.3k L!()'s macro_rules! test_cases { @@ -26,13 +32,21 @@ add_test! {"test_string", || { let rc = string(parser, &mut streams, args.as_mut_slice()).expect("string failed"); - assert_eq!(expected_rc.unwrap(), rc, "string builtin returned unexpected return code"); + assert_eq!( + expected_rc.unwrap(), + rc, + "string builtin returned unexpected return code" + ); let actual = escape(outs.contents()); let expected = escape(expected_out); - assert_eq!(expected, actual, "string builtin returned unexpected output"); + assert_eq!( + expected, actual, + "string builtin returned unexpected output" + ); } + #[rustfmt::skip] let tests = test_cases![ (["string", "escape"], STATUS_CMD_ERROR, ""), (["string", "escape", ""], STATUS_CMD_OK, "''\n"), @@ -269,6 +283,7 @@ add_test! {"test_string", || { string_test(cmd, expected_status, expected_stdout); } + #[rustfmt::skip] let qmark_noglob_tests = test_cases![ (["string", "match", "a*b?c", "axxb?c"], STATUS_CMD_OK, "axxb?c\n"), (["string", "match", "*?", "a"], STATUS_CMD_ERROR, ""), @@ -284,6 +299,7 @@ add_test! {"test_string", || { } }); + #[rustfmt::skip] let qmark_glob_tests = test_cases![ (["string", "match", "a*b?c", "axxbyc"], STATUS_CMD_OK, "axxbyc\n"), (["string", "match", "*?", "a"], STATUS_CMD_OK, "a\n"), @@ -298,5 +314,4 @@ add_test! {"test_string", || { string_test(cmd, expected_status, expected_stdout); } }); - -}} +} diff --git a/fish-rust/src/builtins/tests/test_tests.rs b/fish-rust/src/builtins/tests/test_tests.rs index da1a38ce5..1cf568462 100644 --- a/fish-rust/src/builtins/tests/test_tests.rs +++ b/fish-rust/src/builtins/tests/test_tests.rs @@ -1,6 +1,7 @@ use crate::builtins::prelude::*; use crate::builtins::test::test as builtin_test; use crate::io::OutputStream; +use crate::tests::prelude::*; fn run_one_test_test_mbracket(expected: i32, lst: &[&str], bracket: bool) -> bool { let parser = Parser::principal_parser(); @@ -164,7 +165,10 @@ fn test_test() { assert!(run_test_test(2, &["1", "-eq", "-99999999999999999999999999.9"])); } -crate::ffi_tests::add_test!("test_test_builtin", || { +#[test] +#[serial] +fn test_test_builtin() { + test_init(); test_test_brackets(); test_test(); -}); +} diff --git a/fish-rust/src/fds.rs b/fish-rust/src/fds.rs index 9f9981e5e..71e95e72c 100644 --- a/fish-rust/src/fds.rs +++ b/fish-rust/src/fds.rs @@ -1,5 +1,7 @@ use crate::common::wcs2zstring; use crate::flog::FLOG; +#[cfg(test)] +use crate::tests::prelude::*; use crate::wchar::prelude::*; use crate::wutil::perror; use libc::{ @@ -292,7 +294,10 @@ pub fn make_fd_blocking(fd: RawFd) -> Result<(), io::Error> { Ok(()) } -crate::ffi_tests::add_test!("test_pipes", || { +#[test] +#[serial] +fn test_pipes() { + test_init(); // Here we just test that each pipe has CLOEXEC set and is in the high range. // Note pipe creation may fail due to fd exhaustion; don't fail in that case. let mut pipes = vec![]; @@ -309,4 +314,4 @@ crate::ffi_tests::add_test!("test_pipes", || { assert!(flags & FD_CLOEXEC != 0); } } -}); +} diff --git a/fish-rust/src/ffi_tests.rs b/fish-rust/src/ffi_tests.rs deleted file mode 100644 index d5427c24e..000000000 --- a/fish-rust/src/ffi_tests.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Support for tests which need to cross the FFI. -//! -//! Because the C++ is not compiled by `cargo test` and there is no natural way to -//! do it, use the following facilities for tests which need to use C++ types. -//! This uses the inventory crate to build a custom-test harness -//! as described at -//! See smoke.rs add_test for an example of how to use this. - -#[cfg(all(feature = "fish-ffi-tests", not(test)))] -mod ffi_tests_impl { - /// A test which needs to cross the FFI. - #[derive(Debug)] - pub struct FFITest { - pub name: &'static str, - pub func: fn(), - } - - /// Add a new test. - /// Example usage: - /// ``` - /// add_test!("test_name", || { - /// assert!(1 + 2 == 3); - /// }); - /// ``` - macro_rules! add_test { - ($name:literal, $func:expr) => { - inventory::submit!(crate::ffi_tests::FFITest { - name: $name, - func: $func, - }); - }; - } - pub(crate) use add_test; - - inventory::collect!(crate::ffi_tests::FFITest); - - /// Runs all ffi tests. - pub fn run_ffi_tests() { - for test in inventory::iter:: { - println!("Running ffi test {}", test.name); - (test.func)(); - } - } -} - -#[cfg(not(all(feature = "fish-ffi-tests", not(test))))] -mod ffi_tests_impl { - macro_rules! add_test { - ($name:literal, $func:expr) => {}; - } - pub(crate) use add_test; - pub fn run_ffi_tests() {} -} - -pub(crate) use ffi_tests_impl::*; - -#[allow(clippy::module_inception)] -#[cxx::bridge(namespace = rust)] -mod ffi_tests { - extern "Rust" { - fn run_ffi_tests(); - } -} diff --git a/fish-rust/src/future_feature_flags.rs b/fish-rust/src/future_feature_flags.rs index 76fd8adb8..8b47d3310 100644 --- a/fish-rust/src/future_feature_flags.rs +++ b/fish-rust/src/future_feature_flags.rs @@ -31,8 +31,6 @@ mod future_feature_flags_ffi { extern "Rust" { #[cxx_name = "feature_test"] fn test(flag: FeatureFlag) -> bool; - #[cxx_name = "feature_set"] - fn set(flag: FeatureFlag, value: bool); #[cxx_name = "feature_set_from_string"] fn set_from_string(str: wcharz_t); } @@ -106,7 +104,7 @@ pub const METADATA: &[FeatureMetadata] = &[ ]; thread_local!( - #[cfg(any(test, feature = "fish-ffi-tests"))] + #[cfg(test)] static LOCAL_FEATURES: std::cell::RefCell> = std::cell::RefCell::new(None); ); @@ -115,11 +113,11 @@ static FEATURES: Features = Features::new(); /// Perform a feature test on the global set of features. pub fn test(flag: FeatureFlag) -> bool { - #[cfg(any(test, feature = "fish-ffi-tests"))] + #[cfg(test)] { LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).test(flag)) } - #[cfg(not(any(test, feature = "fish-ffi-tests")))] + #[cfg(not(test))] { FEATURES.test(flag) } @@ -128,7 +126,7 @@ pub fn test(flag: FeatureFlag) -> bool { pub use test as feature_test; /// Set a flag. -#[cfg(any(test, feature = "fish-ffi-tests"))] +#[cfg(test)] pub fn set(flag: FeatureFlag, value: bool) { LOCAL_FEATURES.with(|fc| fc.borrow().as_ref().unwrap_or(&FEATURES).set(flag, value)); } @@ -139,7 +137,7 @@ pub fn set(flag: FeatureFlag, value: bool) { /// Unknown features are silently ignored. pub fn set_from_string<'a>(str: impl Into<&'a wstr>) { let wstr: &wstr = str.into(); - #[cfg(any(test, feature = "fish-ffi-tests"))] + #[cfg(test)] { LOCAL_FEATURES.with(|fc| { fc.borrow() @@ -148,7 +146,7 @@ pub fn set_from_string<'a>(str: impl Into<&'a wstr>) { .set_from_string(wstr) }); } - #[cfg(not(any(test, feature = "fish-ffi-tests")))] + #[cfg(not(test))] { FEATURES.set_from_string(wstr) } @@ -216,7 +214,7 @@ impl Features { } } -#[cfg(any(test, feature = "fish-ffi-tests"))] +#[cfg(test)] pub fn scoped_test(flag: FeatureFlag, value: bool, test_fn: impl FnOnce()) { LOCAL_FEATURES.with(|fc| { assert!( diff --git a/fish-rust/src/input.rs b/fish-rust/src/input.rs index bdcaebff9..78c646a8a 100644 --- a/fish-rust/src/input.rs +++ b/fish-rust/src/input.rs @@ -2,7 +2,6 @@ use crate::common::{get_by_sorted_name, shell_modes, str2wcstring, Named}; use crate::curses; use crate::env::{EnvMode, Environment, CURSES_INITIALIZED}; use crate::event; -use crate::ffi_tests::add_test; use crate::flog::FLOG; use crate::input_common::{ CharEvent, CharEventType, CharInputStyle, InputEventQueuer, ReadlineCmd, R_END_INPUT_FUNCTIONS, @@ -13,6 +12,8 @@ use crate::reader::{ reader_reading_interrupted, reader_reset_interrupted, reader_schedule_prompt_repaint, }; use crate::signal::signal_clear_cancel; +#[cfg(test)] +use crate::tests::prelude::*; use crate::threads::assert_is_main_thread; use crate::wchar::prelude::*; use errno::{set_errno, Errno}; @@ -1191,7 +1192,10 @@ pub fn input_function_get_code(name: &wstr) -> Option { get_by_sorted_name(name, INPUT_FUNCTION_METADATA).map(|md| md.code) } -add_test!("test_input", || { +#[test] +#[serial] +fn test_input() { + test_init(); use crate::env::EnvStack; let parser = Parser::new(Arc::pin(EnvStack::new()), false); let mut input = Inputter::new(parser, libc::STDIN_FILENO); @@ -1233,4 +1237,4 @@ add_test!("test_input", || { } else if evt.get_readline() != ReadlineCmd::DownLine { panic!("Expected to read char down_line"); } -}); +} diff --git a/fish-rust/src/lib.rs b/fish-rust/src/lib.rs index 409f1f029..82f5075da 100644 --- a/fish-rust/src/lib.rs +++ b/fish-rust/src/lib.rs @@ -58,7 +58,6 @@ mod fds; #[allow(unused_imports)] mod ffi; mod ffi_init; -mod ffi_tests; mod fish; mod fish_indent; mod fish_key_reader; @@ -98,7 +97,6 @@ mod reader_history_search; mod redirection; mod screen; mod signal; -mod smoke; mod termsize; mod threads; mod timer; @@ -118,6 +116,6 @@ mod widecharwidth; mod wildcard; mod wutil; -#[cfg(any(test, feature = "fish-ffi-tests"))] +#[cfg(test)] #[allow(unused_imports)] // Easy way to suppress warnings while we have two testing modes. mod tests; diff --git a/fish-rust/src/parse_util.rs b/fish-rust/src/parse_util.rs index d6a3e494c..44526b714 100644 --- a/fish-rust/src/parse_util.rs +++ b/fish-rust/src/parse_util.rs @@ -9,7 +9,6 @@ use crate::expand::{ expand_one, expand_to_command_and_args, ExpandFlags, ExpandResultCode, BRACE_BEGIN, BRACE_END, BRACE_SEP, INTERNAL_SEPARATOR, VARIABLE_EXPAND, VARIABLE_EXPAND_EMPTY, VARIABLE_EXPAND_SINGLE, }; -use crate::ffi_tests::add_test; use crate::future_feature_flags::{feature_test, FeatureFlag}; use crate::operation_context::OperationContext; use crate::parse_constants::{ @@ -20,6 +19,8 @@ use crate::parse_constants::{ ERROR_NOT_PID, ERROR_NOT_STATUS, ERROR_NO_VAR_NAME, INVALID_BREAK_ERR_MSG, INVALID_CONTINUE_ERR_MSG, INVALID_PIPELINE_CMD_ERR_MSG, UNKNOWN_BUILTIN_ERR_MSG, }; +#[cfg(test)] +use crate::tests::prelude::*; use crate::tokenizer::{ comment_end, is_token_delimiter, quote_end, Tok, TokenType, Tokenizer, TOK_ACCEPT_UNFINISHED, TOK_SHOW_COMMENTS, @@ -1785,7 +1786,10 @@ const TIME_IN_PIPELINE_ERR_MSG: &str = /// Maximum length of a variable name to show in error reports before truncation const var_err_len: usize = 16; -add_test!("test_parse_util_cmdsubst_extent", || { +#[test] +#[serial] +fn test_parse_util_cmdsubst_extent() { + test_init(); const a: &wstr = L!("echo (echo (echo hi"); assert_eq!(parse_util_cmdsubst_extent(a, 0), 0..a.len()); assert_eq!(parse_util_cmdsubst_extent(a, 1), 0..a.len()); @@ -1799,9 +1803,12 @@ add_test!("test_parse_util_cmdsubst_extent", || { parse_util_cmdsubst_extent(a, 17), "echo (echo (".chars().count()..a.len() ); -}); +} -add_test!("test_escape_quotes", || { +#[test] +#[serial] +fn test_escape_quotes() { + test_init(); macro_rules! validate { ($cmd:expr, $quote:expr, $no_tilde:expr, $expected:expr) => { assert_eq!( @@ -1839,9 +1846,12 @@ add_test!("test_escape_quotes", || { validate!("~abc'def", Some('"'), true, "~abc'def"); validate!("foo\nba'r", Some('"'), false, "foo\"\\n\"ba'r"); validate!("foo\\\\bar", Some('"'), false, "foo\\\\\\\\bar"); -}); +} -add_test!("test_indents", || { +#[test] +#[serial] +fn test_indents() { + test_init(); // A struct which is either text or a new indent. struct Segment { // The indent to set @@ -2025,7 +2035,7 @@ add_test!("test_indents", || { 0, "\nend" ); })(); -}); +} #[cxx::bridge] mod parse_util_ffi { diff --git a/fish-rust/src/signal.rs b/fish-rust/src/signal.rs index 60a0ef45c..45a1a8268 100644 --- a/fish-rust/src/signal.rs +++ b/fish-rust/src/signal.rs @@ -582,19 +582,20 @@ impl From for NonZeroI32 { } // Need to use add_test for wgettext support. -use crate::ffi_tests::add_test; -add_test!("test_signal_name", || { +#[test] +fn test_signal_name() { let sig = Signal::new(libc::SIGINT); assert_eq!(sig.name(), "SIGINT"); -}); +} fn new_sighupint_checker() -> Box { Box::new(SigChecker::new_sighupint()) } #[rustfmt::skip] -add_test!("test_signal_parse", || { +#[test] +fn test_signal_parse() { assert_eq!(Signal::parse(L!("SIGHUP")), Some(Signal::new(libc::SIGHUP))); assert_eq!(Signal::parse(L!("sigwinch")), Some(Signal::new(libc::SIGWINCH))); assert_eq!(Signal::parse(L!("TSTP")), Some(Signal::new(libc::SIGTSTP))); @@ -609,7 +610,7 @@ add_test!("test_signal_parse", || { assert_eq!(Signal::parse(L!("0")), None); assert_eq!(Signal::parse(L!("-0")), None); assert_eq!(Signal::parse(L!("-1")), None); -}); +} #[test] #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] diff --git a/fish-rust/src/smoke.rs b/fish-rust/src/smoke.rs deleted file mode 100644 index 0d07a5db0..000000000 --- a/fish-rust/src/smoke.rs +++ /dev/null @@ -1,26 +0,0 @@ -#[cxx::bridge(namespace = rust)] -mod ffi { - extern "Rust" { - fn add(left: usize, right: usize) -> usize; - } -} - -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -use crate::ffi_tests::add_test; -add_test!("test_add", || { - assert_eq!(add(2, 3), 5); -}); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} diff --git a/fish-rust/src/termsize.rs b/fish-rust/src/termsize.rs index 9eac421ad..17bfe4f68 100644 --- a/fish-rust/src/termsize.rs +++ b/fish-rust/src/termsize.rs @@ -2,6 +2,8 @@ use crate::common::assert_sync; use crate::env::{EnvMode, EnvVar, Environment}; use crate::flog::FLOG; +#[cfg(test)] +use crate::tests::prelude::*; use crate::wchar::prelude::*; use crate::wutil::fish_wcstoi; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; @@ -288,10 +290,11 @@ pub fn termsize_invalidate_tty() { TermsizeContainer::invalidate_tty(); } -use crate::ffi_tests::add_test; - use self::termsize_ffi::Parser; -add_test!("test_termsize", || { +#[test] +#[serial] +fn test_termsize() { + test_init(); let env_global = EnvMode::GLOBAL; let parser = Parser::principal_parser(); let vars = parser.vars(); @@ -364,4 +367,4 @@ add_test!("test_termsize", || { assert_eq!(ts.last(), Termsize::new(83, 38)); TermsizeContainer::handle_winch(); assert_eq!(ts2.updating(parser), stubby_termsize().unwrap()); -}); +} diff --git a/fish-rust/src/tests/abbrs.rs b/fish-rust/src/tests/abbrs.rs index 8e6a49473..a31e0f1c7 100644 --- a/fish-rust/src/tests/abbrs.rs +++ b/fish-rust/src/tests/abbrs.rs @@ -7,9 +7,13 @@ use crate::reader::{ combine_command_and_autosuggestion, completion_apply_to_command_line, reader_expand_abbreviation_at_cursor, }; +use crate::tests::prelude::*; use crate::wchar::prelude::*; -crate::ffi_tests::add_test!("test_abbreviations", || { +#[test] +#[serial] +fn test_abbreviations() { + test_init(); { let mut abbrs = abbrs_get_set(); abbrs.add(Abbreviation::new( @@ -136,4 +140,4 @@ crate::ffi_tests::add_test!("test_abbreviations", || { // yin/yang expands everywhere. validate!("command yin", None, "command yang"); -}); +} diff --git a/fish-rust/src/tests/complete.rs b/fish-rust/src/tests/complete.rs index 8e454523d..09138c1d4 100644 --- a/fish-rust/src/tests/complete.rs +++ b/fish-rust/src/tests/complete.rs @@ -5,7 +5,6 @@ use crate::complete::{ CompletionList, CompletionMode, CompletionRequestOptions, }; use crate::env::{EnvMode, Environment}; -use crate::ffi_tests::add_test; use crate::io::IoChain; use crate::operation_context::{ no_cancel, OperationContext, EXPANSION_LIMIT_BACKGROUND, EXPANSION_LIMIT_DEFAULT, @@ -13,6 +12,7 @@ use crate::operation_context::{ use crate::parser::Parser; use crate::reader::completion_apply_to_command_line; use crate::tests::prelude::*; +use crate::tests::prelude::*; use crate::wchar::prelude::*; use crate::wcstringutil::join_strings; use std::collections::HashMap; @@ -23,7 +23,10 @@ fn comma_join(lst: Vec) -> WString { join_strings(&lst, ',') } -add_test!("test_complete", || { +#[test] +#[serial] +fn test_complete() { + test_init(); let vars = PwdEnvironment { parent: TestEnvironment { vars: HashMap::from([ @@ -424,11 +427,14 @@ add_test!("test_complete", || { complete_remove_wrapper(L!("cdwrap1").into(), L!("cd")); complete_remove_wrapper(L!("cdwrap2").into(), L!("cdwrap1")); popd(); -}); +} // Testing test_autosuggest_suggest_special, in particular for properly handling quotes and // backslashes. -add_test!("test_autosuggest_suggest_special", || { +#[test] +#[serial] +fn test_autosuggest_suggest_special() { + test_init(); macro_rules! perform_one_autosuggestion_cd_test { ($command:literal, $expected:literal, $vars:expr) => { let mut comps = complete( @@ -593,9 +599,12 @@ add_test!("test_autosuggest_suggest_special", || { .vars() .remove(L!("HOME"), EnvMode::LOCAL | EnvMode::EXPORT); popd(); -}); +} -add_test!("test_autosuggestion_ignores", || { +#[test] +#[serial] +fn test_autosuggestion_ignores() { + test_init(); // Testing scenarios that should produce no autosuggestions macro_rules! perform_one_autosuggestion_should_ignore_test { ($command:literal) => { @@ -613,4 +622,4 @@ add_test!("test_autosuggestion_ignores", || { perform_one_autosuggestion_should_ignore_test!("echo PIPE_TEST&"); perform_one_autosuggestion_should_ignore_test!("echo PIPE_TEST#comment"); perform_one_autosuggestion_should_ignore_test!("echo PIPE_TEST;"); -}); +} diff --git a/fish-rust/src/tests/debounce.rs b/fish-rust/src/tests/debounce.rs index fa70a4e33..853803e26 100644 --- a/fish-rust/src/tests/debounce.rs +++ b/fish-rust/src/tests/debounce.rs @@ -5,14 +5,17 @@ use std::sync::{ use std::time::Duration; use crate::common::ScopeGuard; -use crate::ffi_tests::add_test; use crate::global_safety::RelaxedAtomicBool; use crate::parser::Parser; use crate::reader::{reader_current_data, reader_pop, reader_push, ReaderConfig, ReaderData}; +use crate::tests::prelude::*; use crate::threads::{iothread_drain_all, iothread_service_main, Debounce}; use crate::wchar::prelude::*; -add_test!("test_debounce", || { +#[test] +#[serial] +fn test_debounce() { + test_init(); // Run 8 functions using a condition variable. // Only the first and last should run. let db = Debounce::new(Duration::from_secs(0)); @@ -80,9 +83,12 @@ add_test!("test_debounce", || { assert_eq!(ctx.handler_ran[idx].load(), ctx.completion_ran[idx].load()); } assert!(total_ran <= 2); -}); +} -add_test!("test_debounce_timeout", || { +#[test] +#[serial] +fn test_debounce_timeout() { + test_init(); // Verify that debounce doesn't wait forever. // Use a shared_ptr so we don't have to join our threads. let timeout = Duration::from_millis(500); @@ -126,4 +132,4 @@ add_test!("test_debounce_timeout", || { let mut exit_ok = data.exit_ok.lock().unwrap(); *exit_ok = true; data.cv.notify_all(); -}); +} diff --git a/fish-rust/src/tests/env.rs b/fish-rust/src/tests/env.rs index f0e01e510..b96010a47 100644 --- a/fish-rust/src/tests/env.rs +++ b/fish-rust/src/tests/env.rs @@ -1,5 +1,4 @@ use crate::env::{EnvMode, EnvVar, EnvVarFlags, Environment}; -use crate::ffi_tests::add_test; use crate::parser::Parser; use crate::tests::prelude::*; use crate::wchar::prelude::*; @@ -89,7 +88,10 @@ fn test_timezone_env_vars() { } // Verify that setting special env vars have the expected effect on the current shell process. -add_test!("test_env_vars", || { +#[test] +#[serial] +fn test_env_vars() { + test_init(); test_timezone_env_vars(); // TODO: Add tests for the locale and ncurses vars. @@ -103,9 +105,12 @@ add_test!("test_env_vars", || { assert!(v1 == v2 && !(v1 != v2)); assert!(v1 != v3 && !(v1 == v3)); assert!(v1 != v4 && !(v1 == v4)); -}); +} -add_test!("test_env_snapshot", || { +#[test] +#[serial] +fn test_env_snapshot() { + test_init(); std::fs::create_dir_all("test/fish_env_snapshot_test/").unwrap(); pushd("test/fish_env_snapshot_test/"); let vars = Parser::principal_parser().vars(); @@ -171,4 +176,4 @@ add_test!("test_env_snapshot", || { vars.pop(); popd(); -}); +} diff --git a/fish-rust/src/tests/env_universal_common.rs b/fish-rust/src/tests/env_universal_common.rs index 1ffa46eb8..b3fbc2063 100644 --- a/fish-rust/src/tests/env_universal_common.rs +++ b/fish-rust/src/tests/env_universal_common.rs @@ -2,10 +2,10 @@ use crate::common::wcs2osstring; use crate::common::ScopeGuard; use crate::env::{EnvVar, EnvVarFlags, VarTable}; use crate::env_universal_common::{CallbackDataList, EnvUniversal, UvarFormat}; -use crate::ffi_tests::add_test; use crate::flog::FLOG; use crate::parser::Parser; use crate::reader::{reader_current_data, reader_pop, reader_push, ReaderConfig}; +use crate::tests::prelude::*; use crate::threads::{iothread_drain_all, iothread_perform}; use crate::wchar::prelude::*; use crate::wutil::file_id_for_path; @@ -15,6 +15,7 @@ const UVARS_PER_THREAD: usize = 8; const UVARS_TEST_PATH: &wstr = L!("test/fish_uvars_test/varsfile.txt"); fn test_universal_helper(x: usize) { + test_init(); let mut callbacks = CallbackDataList::new(); let mut uvars = EnvUniversal::new(); uvars.initialize_at_path(&mut callbacks, UVARS_TEST_PATH.to_owned()); @@ -36,7 +37,10 @@ fn test_universal_helper(x: usize) { assert!(synced, "Failed to sync universal variables after deletion"); } -add_test!("test_universal", || { +#[test] +#[serial] +fn test_universal() { + test_init(); let _ = std::fs::remove_dir_all("test/fish_uvars_test/"); std::fs::create_dir_all("test/fish_uvars_test/").unwrap(); @@ -70,9 +74,12 @@ add_test!("test_universal", || { } std::fs::remove_dir_all("test/fish_uvars_test/").unwrap(); -}); +} -add_test!("test_universal_output", || { +#[test] +#[serial] +fn test_universal_output() { + test_init(); let flag_export = EnvVarFlags::EXPORT; let flag_pathvar = EnvVarFlags::PATHVAR; @@ -116,9 +123,10 @@ add_test!("test_universal_output", || { ) .as_bytes(); assert_eq!(text, expected); -}); +} fn test_universal_parsing() { + test_init(); let input = concat!( "# This file contains fish universal variable definitions.\n", "# VERSION: 3.0\n", @@ -167,7 +175,10 @@ fn test_universal_parsing() { assert_eq!(vars, parsed_vars); } -add_test!("test_universal_parsing_legacy", || { +#[test] +#[serial] +fn test_universal_parsing_legacy() { + test_init(); let input = concat!( "# This file contains fish universal variable definitions.\n", "SET varA:ValA1\\x1eValA2\n", @@ -191,9 +202,12 @@ add_test!("test_universal_parsing_legacy", || { let mut parsed_vars = VarTable::new(); EnvUniversal::populate_variables(input, &mut parsed_vars); assert_eq!(vars, parsed_vars); -}); +} -add_test!("test_universal_callbacks", || { +#[test] +#[serial] +fn test_universal_callbacks() { + test_init(); std::fs::create_dir_all("test/fish_uvars_test/").unwrap(); let mut callbacks = CallbackDataList::new(); let mut uvars1 = EnvUniversal::new(); @@ -245,9 +259,12 @@ add_test!("test_universal_callbacks", || { assert_eq!(callbacks[2].key, L!("delta")); assert_eq!(callbacks[2].val, None); std::fs::remove_dir_all("test/fish_uvars_test/").unwrap(); -}); +} -add_test!("test_universal_formats", || { +#[test] +#[serial] +fn test_universal_formats() { + test_init(); macro_rules! validate { ( $version_line:literal, $expected_format:expr ) => { assert_eq!( @@ -264,9 +281,12 @@ add_test!("test_universal_formats", || { validate!(b"# blah\n#VERSION: 3.0", UvarFormat::fish_3_0); validate!(b"# blah\n#VERSION:3.0", UvarFormat::fish_3_0); validate!(b"# blah\n#VERSION:3.1", UvarFormat::future); -}); +} -add_test!("test_universal_ok_to_save", || { +#[test] +#[serial] +fn test_universal_ok_to_save() { + test_init(); // Ensure we don't try to save after reading from a newer fish. std::fs::create_dir_all("test/fish_uvars_test/").unwrap(); let contents = b"# VERSION: 99999.99\n"; @@ -297,4 +317,4 @@ add_test!("test_universal_ok_to_save", || { "UVARS_TEST_PATH should not have changed", ); std::fs::remove_dir_all("test/fish_uvars_test/").unwrap(); -}); +} diff --git a/fish-rust/src/tests/expand.rs b/fish-rust/src/tests/expand.rs index cd14f511f..ed5ce6472 100644 --- a/fish-rust/src/tests/expand.rs +++ b/fish-rust/src/tests/expand.rs @@ -4,11 +4,11 @@ use crate::abbrs::{with_abbrs, with_abbrs_mut}; use crate::complete::{CompletionList, CompletionReceiver}; use crate::env::{EnvMode, EnvStackSetResult}; use crate::expand::{expand_to_receiver, ExpandResultCode}; -use crate::ffi_tests::add_test; use crate::operation_context::{no_cancel, EXPANSION_LIMIT_DEFAULT}; use crate::parse_constants::ParseErrorList; use crate::parser::Parser; use crate::tests::prelude::*; +use crate::tests::prelude::*; use crate::wildcard::ANY_STRING; use crate::{ expand::{expand_string, ExpandFlags}, @@ -63,7 +63,10 @@ fn expand_test_impl( } // Test globbing and other parameter expansion. -add_test!("test_expand", || { +#[test] +#[serial] +fn test_expand() { + test_init(); /// Perform parameter expansion and test if the output equals the zero-terminated parameter list /// supplied. /// /// \param in the string to expand @@ -342,9 +345,12 @@ add_test!("test_expand", || { expand_test!("l///n", fuzzy_comp, "lol///nub/", "Wrong fuzzy matching 6"); popd(); -}); +} -add_test!("test_expand_overflow", || { +#[test] +#[serial] +fn test_expand_overflow() { + test_init(); // Testing overflowing expansions // Ensure that we have sane limits on number of expansions - see #7497. @@ -376,9 +382,12 @@ add_test!("test_expand_overflow", || { assert_eq!(res, ExpandResultCode::error); parser.vars().pop(); -}); +} -add_test!("test_abbreviations", || { +#[test] +#[serial] +fn test_abbreviations() { + test_init(); // Testing abbreviations with_abbrs_mut(|abbrset| { @@ -442,6 +451,4 @@ add_test!("test_abbreviations", || { ); assert_eq!(abbr_expand_1(L!("foo"), cmd), Some(L!("bar").into())); - - // todo!("port the rest"); -}); +} diff --git a/fish-rust/src/tests/fd_monitor.rs b/fish-rust/src/tests/fd_monitor.rs index b0e85a6f7..43c2df313 100644 --- a/fish-rust/src/tests/fd_monitor.rs +++ b/fish-rust/src/tests/fd_monitor.rs @@ -8,7 +8,7 @@ use crate::fd_monitor::{ FdEventSignaller, FdMonitor, FdMonitorItem, FdMonitorItemId, ItemWakeReason, }; use crate::fds::{make_autoclose_pipes, AutoCloseFd}; -use crate::ffi_tests::add_test; +use crate::tests::prelude::*; /// Helper to make an item which counts how many times its callback was invoked. /// @@ -104,7 +104,10 @@ impl ItemMaker { } } -add_test!("fd_monitor_items", || { +#[test] +#[serial] +fn fd_monitor_items() { + test_init(); let monitor = FdMonitor::new(); // Items which will never receive data or be called. @@ -189,7 +192,7 @@ add_test!("fd_monitor_items", || { assert_eq!(item_pokee.length_read.load(Ordering::Relaxed), 0); assert_eq!(item_pokee.total_calls.load(Ordering::Relaxed), 1); assert_eq!(item_pokee.pokes.load(Ordering::Relaxed), 1); -}); +} #[test] fn test_fd_event_signaller() { diff --git a/fish-rust/src/tests/highlight.rs b/fish-rust/src/tests/highlight.rs index 1b826546c..7d10a9474 100644 --- a/fish-rust/src/tests/highlight.rs +++ b/fish-rust/src/tests/highlight.rs @@ -1,6 +1,5 @@ use crate::common::ScopeGuard; use crate::env::EnvMode; -use crate::ffi_tests::add_test; use crate::future_feature_flags::{self, FeatureFlag}; use crate::parser::Parser; use crate::tests::prelude::*; @@ -25,7 +24,10 @@ fn get_overlong_path() -> String { longpath } -add_test!("test_is_potential_path", || { +#[test] +#[serial] +fn test_is_potential_path() { + test_init(); // Directories std::fs::create_dir_all("test/is_potential_path_test/alpha/").unwrap(); std::fs::create_dir_all("test/is_potential_path_test/beta/").unwrap(); @@ -156,9 +158,12 @@ add_test!("test_is_potential_path", || { &ctx, PathFlags::PATH_REQUIRE_DIR )); -}); +} -add_test!("test_highlighting", || { +#[test] +#[serial] +fn test_highlighting() { + test_init(); // Testing syntax highlighting pushd("test/fish_highlight_test/"); let _popd = ScopeGuard::new((), |_| popd()); @@ -634,4 +639,4 @@ add_test!("test_highlighting", || { (">", fg(HighlightRole::error)), ("echo", fg(HighlightRole::error)), ); -}); +} diff --git a/fish-rust/src/tests/history.rs b/fish-rust/src/tests/history.rs index e67773564..5cc18baaa 100644 --- a/fish-rust/src/tests/history.rs +++ b/fish-rust/src/tests/history.rs @@ -3,10 +3,10 @@ use crate::common::{ }; use crate::env::{EnvDyn, EnvMode, EnvStack, Environment}; use crate::fds::{wopen_cloexec, AutoCloseFd}; -use crate::ffi_tests::add_test; use crate::history::{self, History, HistoryItem, HistorySearch, PathList, SearchDirection}; use crate::path::path_get_data; use crate::tests::prelude::*; +use crate::tests::prelude::*; use crate::tests::string_escape::ESCAPE_TEST_CHAR; use crate::wchar::prelude::*; use crate::wcstringutil::{string_prefixes_string, string_prefixes_string_case_insensitive}; @@ -43,7 +43,10 @@ fn random_string() -> WString { result } -add_test!("test_history", || { +#[test] +#[serial] +fn test_history() { + test_init(); macro_rules! test_history_matches { ($search:expr, $expected:expr) => { let expected: Vec<&wstr> = $expected; @@ -199,7 +202,7 @@ add_test!("test_history", || { // Clean up after our tests. history.clear(); -}); +} // Wait until the next second. fn time_barrier() { @@ -226,6 +229,7 @@ fn generate_history_lines(item_count: usize, idx: usize) -> Vec { } fn test_history_races_pound_on_history(item_count: usize, idx: usize) { + test_init(); // Called in child thread to modify history. let hist = History::new(L!("race_test")); let hist_lines = generate_history_lines(item_count, idx); @@ -235,7 +239,10 @@ fn test_history_races_pound_on_history(item_count: usize, idx: usize) { } } -add_test!("test_history_races", || { +#[test] +#[serial] +fn test_history_races() { + test_init(); // This always fails under WSL if is_windows_subsystem_for_linux() { return; @@ -331,9 +338,12 @@ add_test!("test_history_races", || { assert_eq!(list, Vec::::new(), "Lines still left in the array"); } hist.clear(); -}); +} -add_test!("test_history_merge", || { +#[test] +#[serial] +fn test_history_merge() { + test_init(); // In a single fish process, only one history is allowed to exist with the given name But it's // common to have multiple history instances with the same name active in different processes, // e.g. when you have multiple shells open. We try to get that right and merge all their history @@ -437,9 +447,12 @@ add_test!("test_history_merge", || { } } everything.clear(); -}); +} -add_test!("test_history_path_detection", || { +#[test] +#[serial] +fn test_history_path_detection() { + test_init(); // Regression test for #7582. let tmpdirbuff = CString::new("/tmp/fish_test_history.XXXXXX").unwrap(); let tmpdir = unsafe { libc::mkdtemp(tmpdirbuff.into_raw()) }; @@ -541,7 +554,7 @@ add_test!("test_history_path_detection", || { std::thread::sleep(std::time::Duration::from_millis(2)); } history.clear(); -}); +} fn install_sample_history(name: &wstr) { let path = path_get_data().expect("Failed to get data directory"); @@ -552,7 +565,10 @@ fn install_sample_history(name: &wstr) { .unwrap(); } -add_test!("test_history_formats", || { +#[test] +#[serial] +fn test_history_formats() { + test_init(); // Test inferring and reading legacy and bash history formats. let name = L!("history_sample_fish_2_0"); install_sample_history(name); @@ -598,4 +614,4 @@ add_test!("test_history_formats", || { ]; assert_eq!(test_history_imported_from_corrupted.get_history(), expected); test_history_imported_from_corrupted.clear(); -}); +} diff --git a/fish-rust/src/tests/mod.rs b/fish-rust/src/tests/mod.rs index e25f21502..82bebb70e 100644 --- a/fish-rust/src/tests/mod.rs +++ b/fish-rust/src/tests/mod.rs @@ -1,13 +1,10 @@ use crate::wchar::prelude::*; mod abbrs; -#[cfg(test)] mod common; mod complete; mod debounce; -#[cfg(test)] mod editable_line; -#[cfg(test)] mod encoding; mod env; mod env_universal_common; @@ -18,15 +15,11 @@ mod history; mod pager; mod parse_util; mod parser; -#[cfg(test)] mod reader; -#[cfg(test)] mod redirection; mod screen; mod string_escape; -#[cfg(test)] mod threads; -#[cfg(test)] mod tokenizer; mod topic_monitor; mod wgetopt; @@ -87,4 +80,6 @@ pub mod prelude { EnvStack::principal().set_pwd_from_getcwd(); }); } + + pub use serial_test::serial; } diff --git a/fish-rust/src/tests/pager.rs b/fish-rust/src/tests/pager.rs index 57f35f3f9..7ded85168 100644 --- a/fish-rust/src/tests/pager.rs +++ b/fish-rust/src/tests/pager.rs @@ -1,13 +1,16 @@ use crate::common::get_ellipsis_char; use crate::complete::{CompleteFlags, Completion}; -use crate::ffi_tests::add_test; use crate::pager::{Pager, SelectionMotion}; use crate::termsize::Termsize; +use crate::tests::prelude::*; use crate::wchar::prelude::*; use crate::wchar_ext::WExt; use crate::wcstringutil::StringFuzzyMatch; -add_test!("test_pager_navigation", || { +#[test] +#[serial] +fn test_pager_navigation() { + test_init(); // Generate 19 strings of width 10. There's 2 spaces between completions, and our term size is // 80; these can therefore fit into 6 columns (6 * 12 - 2 = 70) or 5 columns (58) but not 7 // columns (7 * 12 - 2 = 82). @@ -92,9 +95,12 @@ add_test!("test_pager_navigation", || { validate!(pager, render, SelectionMotion::North, 2); validate!(pager, render, SelectionMotion::PageNorth, 0); validate!(pager, render, SelectionMotion::PageSouth, 3); -}); +} -add_test!("test_pager_layout", || { +#[test] +#[serial] +fn test_pager_layout() { + test_init(); // These tests are woefully incomplete // They only test the truncation logic for a single completion @@ -189,4 +195,4 @@ add_test!("test_pager_layout", || { validate!(&mut pager, 18, L!("abcdefghijklmnopq…")); validate!(&mut pager, 17, L!("abcdefghijklmnop…")); validate!(&mut pager, 16, L!("abcdefghijklmno…")); -}); +} diff --git a/fish-rust/src/tests/parse_util.rs b/fish-rust/src/tests/parse_util.rs index 068c1a90b..eb20e41f8 100644 --- a/fish-rust/src/tests/parse_util.rs +++ b/fish-rust/src/tests/parse_util.rs @@ -1,16 +1,19 @@ use pcre2::utf32::Regex; -use crate::ffi_tests::add_test; use crate::parse_constants::{ ERROR_BAD_VAR_CHAR1, ERROR_BRACKETED_VARIABLE1, ERROR_BRACKETED_VARIABLE_QUOTED1, ERROR_NOT_ARGV_AT, ERROR_NOT_ARGV_COUNT, ERROR_NOT_ARGV_STAR, ERROR_NOT_PID, ERROR_NOT_STATUS, ERROR_NO_VAR_NAME, }; use crate::parse_util::parse_util_detect_errors; +use crate::tests::prelude::*; use crate::wchar::prelude::*; use crate::wchar_ext::WExt; -add_test!("test_error_messages", || { +#[test] +#[serial] +fn test_error_messages() { + test_init(); // Given a format string, returns a list of non-empty strings separated by format specifiers. The // format specifiers themselves are omitted. fn separate_by_format_specifiers(format: &wstr) -> Vec<&wstr> { @@ -67,4 +70,4 @@ add_test!("test_error_messages", || { validate!("echo foo\"$\"bar", ERROR_NO_VAR_NAME); validate!("echo \"foo\"$\"bar\"", ERROR_NO_VAR_NAME); validate!("echo foo $ bar", ERROR_NO_VAR_NAME); -}); +} diff --git a/fish-rust/src/tests/parser.rs b/fish-rust/src/tests/parser.rs index 31db49ec0..9cf8aac9a 100644 --- a/fish-rust/src/tests/parser.rs +++ b/fish-rust/src/tests/parser.rs @@ -13,14 +13,17 @@ use crate::reader::{ }; use crate::signal::{signal_clear_cancel, signal_reset_handlers, signal_set_handlers}; use crate::tests::prelude::*; +use crate::tests::prelude::*; use crate::threads::{iothread_drain_all, iothread_perform}; use crate::wchar::prelude::*; use crate::wcstringutil::join_strings; use libc::SIGINT; use std::time::Duration; -use crate::ffi_tests::add_test; -add_test!("test_parser", || { +#[test] +#[serial] +fn test_parser() { + test_init(); macro_rules! detect_errors { ($src:literal) => { parse_util_detect_errors(L!($src), None, true /* accept incomplete */) @@ -297,9 +300,12 @@ add_test!("test_parser", || { detect_errors!("true || \n") == Err(ParserTestErrorBits::INCOMPLETE), "unterminated conjunction not reported properly" ); -}); +} -add_test!("test_new_parser_correctness", || { +#[test] +#[serial] +fn test_new_parser_correctness() { + test_init(); macro_rules! validate { ($src:expr, $ok:expr) => { let ast = Ast::parse(L!($src), ParseTreeFlags::default(), None); @@ -324,9 +330,12 @@ add_test!("test_new_parser_correctness", || { validate!("true || ||", false); validate!("|| true", false); validate!("true || \n\n false", true); -}); +} -add_test!("test_new_parser_correctness", || { +#[test] +#[serial] +fn test_new_parser_correctness_by_fuzzing() { + test_init(); let fuzzes = [ L!("if"), L!("else"), @@ -379,13 +388,16 @@ add_test!("test_new_parser_correctness", || { Ast::parse(&src, ParseTreeFlags::default(), None); } } -}); +} // Test the LL2 (two token lookahead) nature of the parser by exercising the special builtin and // command handling. In particular, 'command foo' should be a decorated statement 'foo' but 'command // -help' should be an undecorated statement 'command' with argument '--help', and NOT attempt to // run a command called '--help'. -add_test!("test_new_parser_ll2", || { +#[test] +#[serial] +fn test_new_parser_ll2() { + test_init(); // Parse a statement, returning the command, args (joined by spaces), and the decoration. Returns // true if successful. fn test_1_parse_ll2(src: &wstr) -> Option<(WString, WString, StatementDecoration)> { @@ -501,9 +513,12 @@ add_test!("test_new_parser_ll2", || { check_function_help!("function --help", ast::Type::decorated_statement); check_function_help!("function --foo; end", ast::Type::function_header); check_function_help!("function foo; end", ast::Type::function_header); -}); +} -add_test!("test_new_parser_ad_hoc", || { +#[test] +#[serial] +fn test_new_parser_ad_hoc() { + test_init(); // Very ad-hoc tests for issues encountered. // Ensure that 'case' terminates a job list. @@ -559,9 +574,12 @@ add_test!("test_new_parser_ad_hoc", || { ); assert!(errors.len() == 1); assert!(errors[0].code == ParseErrorCode::tokenizer_unterminated_quote); -}); +} -add_test!("test_new_parser_errors", || { +#[test] +#[serial] +fn test_new_parser_errors() { + test_init(); macro_rules! validate { ($src:expr, $expected_code:expr) => { let mut errors = vec![]; @@ -590,9 +608,12 @@ add_test!("test_new_parser_errors", || { validate!("true | and", ParseErrorCode::andor_in_pipeline); validate!("a=", ParseErrorCode::bare_variable_assignment); -}); +} -add_test!("test_eval_recursion_detection", || { +#[test] +#[serial] +fn test_eval_recursion_detection() { + test_init(); // Ensure that we don't crash on infinite self recursion and mutual recursion. These must use // the principal parser because we cannot yet execute jobs on other parsers. let parser = Parser::principal_parser().shared(); @@ -608,9 +629,12 @@ add_test!("test_eval_recursion_detection", || { )), &IoChain::new(), ); -}); +} -add_test!("test_eval_illegal_exit_code", || { +#[test] +#[serial] +fn test_eval_illegal_exit_code() { + test_init(); macro_rules! validate { ($cmd:expr, $result:expr) => { let parser = Parser::principal_parser(); @@ -637,17 +661,23 @@ add_test!("test_eval_illegal_exit_code", || { validate!(L!("?"), STATUS_UNMATCHED_WILDCARD.unwrap()); validate!(L!("abc?def"), STATUS_UNMATCHED_WILDCARD.unwrap()); popd(); -}); +} -add_test!("test_eval_empty_function_name", || { +#[test] +#[serial] +fn test_eval_empty_function_name() { + test_init(); let parser = Parser::principal_parser().shared(); parser.eval( L!("function '' ; echo fail; exit 42 ; end ; ''"), &IoChain::new(), ); -}); +} -add_test!("test_expand_argument_list", || { +#[test] +#[serial] +fn test_expand_argument_list() { + test_init(); let parser = Parser::principal_parser().shared(); let comps: Vec = Parser::expand_argument_list( L!("alpha 'beta gamma' delta"), @@ -658,7 +688,7 @@ add_test!("test_expand_argument_list", || { .map(|c| c.completion) .collect(); assert_eq!(comps, &[L!("alpha"), L!("beta gamma"), L!("delta"),]); -}); +} fn test_1_cancellation(src: &wstr) { let filler = IoBufferfill::create().unwrap(); @@ -688,7 +718,10 @@ fn test_1_cancellation(src: &wstr) { } } -add_test!("test_cancellation", || { +#[test] +#[serial] +fn test_cancellation() { + test_init(); reader_push(Parser::principal_parser(), L!(""), ReaderConfig::default()); let _pop = ScopeGuard::new((), |()| reader_pop()); @@ -717,4 +750,4 @@ add_test!("test_cancellation", || { // Ensure that we don't think we should cancel. reader_reset_interrupted(); signal_clear_cancel(); -}); +} diff --git a/fish-rust/src/tests/screen.rs b/fish-rust/src/tests/screen.rs index 3261e1d43..c90b11cbe 100644 --- a/fish-rust/src/tests/screen.rs +++ b/fish-rust/src/tests/screen.rs @@ -1,10 +1,13 @@ use crate::common::get_ellipsis_char; -use crate::ffi_tests::add_test; use crate::screen::{LayoutCache, PromptCacheEntry, PromptLayout}; +use crate::tests::prelude::*; use crate::wchar::prelude::*; use crate::wcstringutil::join_strings; -add_test!("test_complete", || { +#[test] +#[serial] +fn test_complete() { + test_init(); let mut lc = LayoutCache::new(); assert_eq!(lc.escape_code_length(L!("")), 0); assert_eq!(lc.escape_code_length(L!("abcd")), 0); @@ -34,9 +37,12 @@ add_test!("test_complete", || { ); assert_eq!(lc.escape_code_length(L!("\x1B]blahblahblah\x1B\\")), 16); assert_eq!(lc.escape_code_length(L!("\x1B]blahblahblah\x07")), 15); -}); +} -add_test!("test_layout_cache", || { +#[test] +#[serial] +fn test_layout_cache() { + test_init(); let mut seqs = LayoutCache::new(); // Verify escape code cache. @@ -105,9 +111,12 @@ add_test!("test_layout_cache", || { seqs.prompt_cache.front().unwrap().layout.max_line_width, 100 ); -}); +} -add_test!("test_prompt_truncation", || { +#[test] +#[serial] +fn test_prompt_truncation() { + test_init(); let mut cache = LayoutCache::new(); let mut trunc = WString::new(); @@ -235,4 +244,4 @@ add_test!("test_prompt_truncation", || { }, ); assert_eq!(trunc, ellipsis()); -}); +} diff --git a/fish-rust/src/tests/topic_monitor.rs b/fish-rust/src/tests/topic_monitor.rs index 7eb58828d..d5805063c 100644 --- a/fish-rust/src/tests/topic_monitor.rs +++ b/fish-rust/src/tests/topic_monitor.rs @@ -1,11 +1,14 @@ -use crate::ffi_tests::add_test; +use crate::tests::prelude::*; use crate::topic_monitor::{topic_monitor_t, topic_t, GenerationsList}; use std::sync::{ atomic::{AtomicU32, AtomicU64, Ordering}, Arc, }; -add_test!("test_topic_monitor", || { +#[test] +#[serial] +fn test_topic_monitor() { + test_init(); let monitor = topic_monitor_t::default(); let gens = GenerationsList::new(); let t = topic_t::sigchld; @@ -26,9 +29,12 @@ add_test!("test_topic_monitor", || { let changed = monitor.check(&gens, true /* wait */); assert!(changed); assert_eq!(gens.sigchld.get(), 2); -}); +} -add_test!("test_topic_monitor_torture", || { +#[test] +#[serial] +fn test_topic_monitor_torture() { + test_init(); let monitor = Arc::new(topic_monitor_t::default()); const THREAD_COUNT: usize = 64; let t1 = topic_t::sigchld; @@ -68,4 +74,4 @@ add_test!("test_topic_monitor_torture", || { for t in threads { t.join().unwrap(); } -}); +} diff --git a/fish-rust/src/threads.rs b/fish-rust/src/threads.rs index fd99ca5cd..10fae51dd 100644 --- a/fish-rust/src/threads.rs +++ b/fish-rust/src/threads.rs @@ -23,7 +23,10 @@ impl FloggableDebug for ThreadId {} /// The thread id of the main thread, as set by [`init()`] at startup. static mut MAIN_THREAD_ID: Option = None; /// Used to bypass thread assertions when testing. +#[cfg(not(test))] static THREAD_ASSERTS_CFG_FOR_TESTING: AtomicBool = AtomicBool::new(false); +#[cfg(test)] +static THREAD_ASSERTS_CFG_FOR_TESTING: AtomicBool = AtomicBool::new(true); /// This allows us to notice when we've forked. static IS_FORKED_PROC: AtomicBool = AtomicBool::new(false); diff --git a/fish-rust/src/wutil/gettext.rs b/fish-rust/src/wutil/gettext.rs index 175690cd6..6cd1f3af4 100644 --- a/fish-rust/src/wutil/gettext.rs +++ b/fish-rust/src/wutil/gettext.rs @@ -7,6 +7,8 @@ use std::sync::Mutex; use crate::common::{charptr2wcstring, wcs2zstring}; use crate::fish::PACKAGE_NAME; +#[cfg(test)] +use crate::tests::prelude::*; use crate::wchar::prelude::*; use crate::wchar_ffi::wchar_t; use errno::{errno, set_errno}; @@ -150,10 +152,12 @@ macro_rules! wgettext_maybe_fmt { } pub(crate) use wgettext_maybe_fmt; -use crate::ffi_tests::add_test; -add_test!("test_untranslated", || { +#[test] +#[serial] +fn test_untranslated() { + test_init(); let s: &'static wstr = wgettext!("abc"); assert_eq!(s, "abc"); let s2: &'static wstr = wgettext!("static"); assert_eq!(s2, "static"); -}); +} diff --git a/fish-rust/src/wutil/tests.rs b/fish-rust/src/wutil/tests.rs index bf76e25ff..92b1e4aa6 100644 --- a/fish-rust/src/wutil/tests.rs +++ b/fish-rust/src/wutil/tests.rs @@ -1,4 +1,4 @@ -use crate::ffi_tests::add_test; +use crate::tests::prelude::*; use libc::{c_void, O_CREAT, O_RDWR, O_TRUNC, SEEK_SET}; use rand::random; use std::{ffi::CString, ptr}; @@ -57,7 +57,10 @@ fn test_wdirname_wbasename() { assert_eq!(wbasename(&longpath), "overlong"L); } -add_test!("test_wwrite_to_fd", || { +#[test] +#[serial] +fn test_wwrite_to_fd() { + test_init(); let (fd, filename) = fish_mkstemp_cloexec(CString::new("/tmp/fish_test_wwrite.XXXXXX").unwrap()); { @@ -98,4 +101,4 @@ add_test!("test_wwrite_to_fd", || { assert_eq!(&contents, &narrow); } unsafe { libc::remove(filename.as_ptr()) }; -}); +} diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 4652eb916..0de0db3b0 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -66,7 +66,6 @@ #include "fds.h" #include "ffi_baggage.h" #include "ffi_init.rs.h" -#include "ffi_tests.rs.h" #include "function.h" #include "future_feature_flags.h" #include "global_safety.h" @@ -90,7 +89,6 @@ #include "redirection.h" #include "screen.h" #include "signals.h" -#include "smoke.rs.h" #include "termsize.h" #include "threads.rs.h" #include "tokenizer.h" @@ -801,13 +799,6 @@ void test_dirname_basename() { do_test(wbasename(longpath) == L"overlong"); } -void test_rust_smoke() { - size_t x = rust::add(37, 5); - do_test(x == 42); -} - -void test_rust_ffi() { rust::run_ffi_tests(); } - // typedef void (test_entry_point_t)(); using test_entry_point_t = void (*)(); struct test_t { @@ -841,8 +832,6 @@ static const test_t s_tests[]{ {TEST_GROUP("maybe"), test_maybe}, {TEST_GROUP("normalize"), test_normalize_path}, {TEST_GROUP("dirname"), test_dirname_basename}, - {TEST_GROUP("rust_smoke"), test_rust_smoke}, - {TEST_GROUP("rust_ffi"), test_rust_ffi}, }; void list_tests() {