From 894764ecb04f9c456b9ebda77d155bf6128e2175 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 22 Jun 2026 23:39:59 +0900 Subject: test(cli): serialize cli_tests via serial_test Replace the hand-rolled `SERIAL` mutex guarding process-global env access with serial_test's `#[serial]` attribute on each test. Co-Authored-By: Claude Opus 4.8 --- crates/shirabe/src/lib.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'crates/shirabe/src/lib.rs') diff --git a/crates/shirabe/src/lib.rs b/crates/shirabe/src/lib.rs index ec63ee1..9182cbe 100644 --- a/crates/shirabe/src/lib.rs +++ b/crates/shirabe/src/lib.rs @@ -47,8 +47,9 @@ pub fn run(argv: Vec) -> anyhow::Result { #[cfg(test)] mod cli_tests { + use serial_test::serial; use std::panic::{AssertUnwindSafe, catch_unwind}; - use std::sync::{Mutex, Once}; + use std::sync::Once; const COMMANDS: &[&str] = &[ "about", @@ -87,20 +88,15 @@ mod cli_tests { static QUIET_PANIC: Once = Once::new(); - /// `crate::run` reads/writes process-global env, so concurrent invocations race; - /// serialize them since the default test harness runs tests on many threads. - static SERIAL: Mutex<()> = Mutex::new(()); - /// Runs the CLI with `args`. Returns true on clean exit, false on any panic / error / non-zero /// exit. fn run(args: &[&str]) -> bool { QUIET_PANIC.call_once(|| std::panic::set_hook(Box::new(|_| {}))); - let _guard = SERIAL.lock().unwrap_or_else(|e| e.into_inner()); // Each invocation must look like a fresh process. // - // SAFETY: all environment access in these tests happens through `crate::run` while holding - // `SERIAL`, so no other thread reads or writes the environment concurrently with these calls. + // SAFETY: every test reaching this code is marked `#[serial]`, so no other thread reads or + // writes the environment concurrently with these calls. unsafe { std::env::remove_var("COLUMNS"); std::env::remove_var("LINES"); @@ -115,16 +111,19 @@ mod cli_tests { } #[test] + #[serial] fn version_flag() { assert!(run(&["--version"])); } #[test] + #[serial] fn help_flag() { assert!(run(&["--help"])); } #[test] + #[serial] fn each_command_help() { let failed: Vec<&&str> = COMMANDS.iter().filter(|c| !run(&[c, "--help"])).collect(); assert!(failed.is_empty(), "` --help` failed for: {failed:?}"); @@ -134,14 +133,13 @@ mod cli_tests { /// not panic (any exit code, including non-zero or an `Err` return, counts as success). fn run_no_panic(args: &[&str]) -> bool { QUIET_PANIC.call_once(|| std::panic::set_hook(Box::new(|_| {}))); - let _guard = SERIAL.lock().unwrap_or_else(|e| e.into_inner()); let original = std::env::current_dir().ok(); let dir = tempfile::tempdir().expect("create temp dir"); std::env::set_current_dir(dir.path()).expect("chdir to temp dir"); - // SAFETY: all environment access here happens while holding `SERIAL`, so no other thread - // touches the environment or working directory concurrently. + // SAFETY: every test reaching this code is marked `#[serial]`, so no other thread touches + // the environment or working directory concurrently. unsafe { std::env::remove_var("COLUMNS"); std::env::remove_var("LINES"); @@ -163,6 +161,7 @@ mod cli_tests { $( $(#[$attr])* #[test] + #[serial] fn $name() { assert!(run_no_panic(&[$cmd]), "`{}` panicked", $cmd); } -- cgit v1.3.1