use std::path::PathBuf;
use std::process::Command;
use std::sync::Mutex;
const CARGO_LIHAAF_BIN: &str = env!("CARGO_BIN_EXE_cargo-lihaaf");
static SPAWN_LOCK: Mutex<()> = Mutex::new(());
fn run_binary(args: &[&str]) -> std::process::Output {
let _guard = SPAWN_LOCK.lock().expect("SPAWN_LOCK poisoned");
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
Command::new(CARGO_LIHAAF_BIN)
.args(args)
.current_dir(&manifest_dir)
.output()
.expect("spawning cargo-lihaaf must succeed")
}
fn stderr_string(out: &std::process::Output) -> String {
String::from_utf8(out.stderr.clone()).expect("stderr must be UTF-8")
}
fn assert_cli_mode_error(out: &std::process::Output, expected_substrings: &[&str]) {
let stderr = stderr_string(out);
assert_eq!(
out.status.code(),
Some(2),
"expected CLI mode-error exit code 2 (got {:?}); stderr was:\n{stderr}",
out.status.code(),
);
for needle in expected_substrings {
assert!(
stderr.contains(needle),
"stderr must contain `{needle}`; got:\n{stderr}",
);
}
}
#[test]
fn non_compat_run_rejects_compat_root() {
let out = run_binary(&["--compat-root", "."]);
assert_cli_mode_error(&out, &["--compat-root", "--compat"]);
}
#[test]
fn non_compat_run_rejects_compat_report() {
let out = run_binary(&["--compat-report", "/tmp/lihaaf-compat-report-test"]);
assert_cli_mode_error(&out, &["--compat-report", "--compat"]);
}
#[test]
fn non_compat_run_rejects_compat_filter() {
let out = run_binary(&["--compat-filter", "phase7"]);
assert_cli_mode_error(&out, &["--compat-filter", "--compat"]);
}
#[test]
fn non_compat_run_rejects_compat_manifest() {
let out = run_binary(&["--compat-manifest", "/tmp/lihaaf-compat-manifest-test"]);
assert_cli_mode_error(&out, &["--compat-manifest", "--compat"]);
}
#[test]
fn non_compat_run_rejects_compat_cargo_test_argv() {
let out = run_binary(&["--compat-cargo-test-argv", r#"["cargo","test"]"#]);
assert_cli_mode_error(&out, &["--compat-cargo-test-argv", "--compat"]);
}
#[test]
fn non_compat_run_rejects_compat_commit() {
let out = run_binary(&["--compat-commit", "deadbeef"]);
assert_cli_mode_error(&out, &["--compat-commit", "--compat"]);
}
#[test]
fn non_compat_run_rejects_compat_trybuild_macro() {
let out = run_binary(&["--compat-trybuild-macro", "crate::aliased_tests"]);
assert_cli_mode_error(&out, &["--compat-trybuild-macro", "--compat"]);
}
#[test]
fn compat_run_rejects_bare_filter() {
let out = run_binary(&[
"--compat",
"--compat-root",
".",
"--compat-report",
"/tmp/lihaaf-compat-report-test",
"--filter",
"phase7",
]);
assert_cli_mode_error(&out, &["--filter", "--compat-filter"]);
}
#[test]
fn compat_run_rejects_bare_manifest_path() {
let out = run_binary(&[
"--compat",
"--compat-root",
".",
"--compat-report",
"/tmp/lihaaf-compat-report-test",
"--manifest-path",
"/tmp/lihaaf-compat-manifest-path-test",
]);
assert_cli_mode_error(&out, &["--manifest-path", "--compat-manifest"]);
}
#[test]
fn compat_run_requires_compat_root() {
let out = run_binary(&[
"--compat",
"--compat-report",
"/tmp/lihaaf-compat-report-test",
]);
assert_cli_mode_error(&out, &["--compat-root", "required"]);
}
#[test]
fn compat_run_requires_compat_report() {
let out = run_binary(&["--compat", "--compat-root", "."]);
assert_cli_mode_error(&out, &["--compat-report", "required"]);
}
#[test]
fn compat_run_accepts_pass_through_flags() {
use clap::Parser;
let cli = lihaaf::Cli::try_parse_from([
"cargo-lihaaf",
"--compat",
"--compat-root",
".",
"--compat-report",
"/tmp/lihaaf-compat-report-pass-through.json",
"--bless",
"--no-cache",
"--jobs",
"4",
"-v",
])
.expect("clap must accept the fully-formed compat invocation with pass-through flags");
let argv: Vec<String> = [
"cargo-lihaaf",
"--compat",
"--compat-root",
".",
"--compat-report",
"/tmp/lihaaf-compat-report-pass-through.json",
"--bless",
"--no-cache",
"--jobs",
"4",
"-v",
]
.iter()
.map(|s| s.to_string())
.collect();
lihaaf::cli::parse_from(argv).expect(
"cli::parse_from must accept the fully-formed compat invocation: clap parse + \
mode-consistency validator both green",
);
assert!(cli.compat, "compat flag must be set");
assert!(cli.bless, "--bless must propagate");
assert!(cli.no_cache, "--no-cache must propagate");
assert_eq!(cli.jobs, Some(4), "--jobs must propagate");
assert!(cli.verbose, "-v / --verbose must propagate");
}
#[test]
fn compat_run_accepts_omitted_cargo_test_argv() {
let argv: Vec<String> = [
"cargo-lihaaf",
"--compat",
"--compat-root",
".",
"--compat-report",
"/tmp/lihaaf-compat-report-omitted-argv.json",
]
.iter()
.map(|s| s.to_string())
.collect();
let cli = lihaaf::cli::parse_from(argv).expect(
"cli::parse_from must accept compat without --compat-cargo-test-argv (validator must \
not require the flag)",
);
assert!(cli.compat, "compat flag must be set");
assert!(
cli.compat_cargo_test_argv.is_none(),
"--compat-cargo-test-argv must be None when omitted; got {:?}",
cli.compat_cargo_test_argv,
);
let args = lihaaf::CompatArgs::from_cli(cli)
.expect("CompatArgs::from_cli must succeed when --compat-cargo-test-argv is omitted");
let _ = args;
}
#[test]
fn rust_api_run_enforces_mode_consistency() {
use clap::Parser;
let cli = lihaaf::Cli::try_parse_from([
"cargo-lihaaf",
"--compat-root",
"/tmp/lihaaf-rust-api-validator-test",
])
.expect("clap parse should succeed; validation runs inside lihaaf::run");
let err = lihaaf::run(cli).expect_err("validator must fire on the Rust API path");
match err {
lihaaf::Error::Cli {
clap_exit_code,
message,
} => {
assert_eq!(clap_exit_code, 2, "mode-consistency must use exit code 2");
assert!(
message.contains("--compat-root"),
"diagnostic must name the offending flag; got: {message}"
);
assert!(
message.contains("--compat"),
"diagnostic must point at --compat; got: {message}"
);
}
other => panic!("expected Error::Cli, got {other:?}"),
}
}
#[test]
fn non_compat_run_unchanged() {
let out = run_binary(&[
"--filter",
"phase7",
"--manifest-path",
"/tmp/lihaaf-non-existent-manifest.toml",
]);
let stderr = stderr_string(&out);
let code = out.status.code();
assert_ne!(
code,
Some(2),
"non-compat invocation must not be a CLI mode error; got exit {:?}, stderr:\n{stderr}",
code,
);
assert!(
!stderr.contains("--compat"),
"non-compat stderr must not mention any --compat flag; got:\n{stderr}"
);
}