diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-20 23:47:43 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-21 00:19:14 +0900 |
| commit | a01e3a45d1b183ee7fe9ca45543b0663d5995faa (patch) | |
| tree | dcfebf464a987dc6eba7365a37c5af18393452f2 /crates/shirabe/src | |
| parent | 84655bf3a4a21dbbe7aec8e3aee1e661505bbb6d (diff) | |
| download | php-shirabe-a01e3a45d1b183ee7fe9ca45543b0663d5995faa.tar.gz php-shirabe-a01e3a45d1b183ee7fe9ca45543b0663d5995faa.tar.zst php-shirabe-a01e3a45d1b183ee7fe9ca45543b0663d5995faa.zip | |
test: add empty test files
Diffstat (limited to 'crates/shirabe/src')
| -rw-r--r-- | crates/shirabe/src/lib.rs | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/crates/shirabe/src/lib.rs b/crates/shirabe/src/lib.rs index 4bd52c2..8d8cc96 100644 --- a/crates/shirabe/src/lib.rs +++ b/crates/shirabe/src/lib.rs @@ -44,3 +44,170 @@ pub fn run(argv: Vec<String>) -> anyhow::Result<i32> { as std::rc::Rc<std::cell::RefCell<dyn InputInterface>>; Application::run(&application, Some(input), None) } + +#[cfg(test)] +mod cli_tests { + use std::panic::{AssertUnwindSafe, catch_unwind}; + use std::sync::{Mutex, Once}; + + const COMMANDS: &[&str] = &[ + "about", + "archive", + "audit", + "browse", + "bump", + "check-platform-reqs", + "clear-cache", + "config", + "create-project", + "depends", + "diagnose", + "dump-autoload", + "exec", + "fund", + "global", + "init", + "install", + "licenses", + "outdated", + "prohibits", + "reinstall", + "remove", + "repository", + "require", + "run-script", + "search", + "self-update", + "show", + "status", + "suggests", + "update", + "validate", + ]; + + 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. + unsafe { + std::env::remove_var("COLUMNS"); + std::env::remove_var("LINES"); + } + + let mut argv = vec!["composer".to_string()]; + argv.extend(args.iter().map(|s| s.to_string())); + matches!( + catch_unwind(AssertUnwindSafe(|| crate::run(argv))), + Ok(Ok(0)) + ) + } + + #[test] + fn version_flag() { + assert!(run(&["--version"])); + } + + #[test] + fn help_flag() { + assert!(run(&["--help"])); + } + + #[test] + fn each_command_help() { + let failed: Vec<&&str> = COMMANDS.iter().filter(|c| !run(&[c, "--help"])).collect(); + assert!(failed.is_empty(), "`<cmd> --help` failed for: {failed:?}"); + } + + /// Runs the CLI with `args` from inside an empty temporary directory. Returns true if the call did + /// 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. + unsafe { + std::env::remove_var("COLUMNS"); + std::env::remove_var("LINES"); + } + + let mut argv = vec!["composer".to_string()]; + argv.extend(args.iter().map(|s| s.to_string())); + let result = catch_unwind(AssertUnwindSafe(|| crate::run(argv))); + + if let Some(orig) = original { + let _ = std::env::set_current_dir(orig); + } + + result.is_ok() + } + + macro_rules! run_no_panic_tests { + ($( $(#[$attr:meta])* $name:ident => $cmd:expr ),* $(,)?) => { + $( + $(#[$attr])* + #[test] + fn $name() { + assert!(run_no_panic(&[$cmd]), "`{}` panicked", $cmd); + } + )* + }; + } + + run_no_panic_tests! { + run_about => "about", + #[ignore = "currently panics"] + run_archive => "archive", + run_audit => "audit", + #[ignore = "currently panics"] + run_browse => "browse", + run_bump => "bump", + run_check_platform_reqs => "check-platform-reqs", + #[ignore = "currently panics"] + run_clear_cache => "clear-cache", + run_config => "config", + run_create_project => "create-project", + run_depends => "depends", + #[ignore = "currently panics"] + run_diagnose => "diagnose", + run_dump_autoload => "dump-autoload", + run_exec => "exec", + run_fund => "fund", + run_global => "global", + #[ignore = "currently panics"] + run_init => "init", + run_install => "install", + run_licenses => "licenses", + run_outdated => "outdated", + run_prohibits => "prohibits", + run_reinstall => "reinstall", + run_remove => "remove", + run_repository => "repository", + #[ignore = "currently panics"] + run_require => "require", + run_run_script => "run-script", + run_search => "search", + run_self_update => "self-update", + run_show => "show", + run_status => "status", + run_suggests => "suggests", + run_update => "update", + run_validate => "validate", + } +} |
