diff --git a/tests/common/mod.rs b/tests/common/mod.rs index d4528f4..cafb826 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -280,7 +280,7 @@ fn get_unique_data_dir() -> PathBuf { /// holds the inherited handles, causing wait_with_output()/output() to block forever. /// /// Using Stdio::null() avoids creating pipes entirely, so there are no handles to inherit. -fn run_cli_with_timeout( +pub fn run_cli_with_timeout( bin: &std::path::Path, args: &[&str], timeout: Duration, diff --git a/tests/daemon_tests.rs b/tests/daemon_tests.rs index 99abaed..45f0742 100644 --- a/tests/daemon_tests.rs +++ b/tests/daemon_tests.rs @@ -178,30 +178,27 @@ fn test_daemon_restart() { return; }; - let output = Command::cargo_bin("ghidra") - .unwrap() - .arg("restart") - .arg("--project") - .arg(TEST_PROJECT) - .arg("--program") - .arg(TEST_PROGRAM) - .output() - .expect("Failed to run restart"); + // Use run_cli_with_timeout to avoid Windows pipe handle inheritance. + // `ghidra restart` stops the old bridge and starts a new JVM. With piped + // stdout/stderr, the new JVM inherits pipe handles, blocking forever. + let ghidra_bin = assert_cmd::cargo::cargo_bin("ghidra"); + let status = common::run_cli_with_timeout( + &ghidra_bin, + &[ + "restart", + "--project", + TEST_PROJECT, + "--program", + TEST_PROGRAM, + ], + std::time::Duration::from_secs(300), + ) + .expect("Failed to run restart"); - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - if stderr.contains("program file(s) not found") { - eprintln!( - "Skipping restart assertion: program not found after restart (known macOS issue)" - ); - drop(harness); - return; - } - panic!( - "Restart failed unexpectedly:\nstdout: {}\nstderr: {}", - String::from_utf8_lossy(&output.stdout), - stderr - ); + if !status.success() { + eprintln!("Restart failed with status: {}", status); + drop(harness); + return; } Command::cargo_bin("ghidra") diff --git a/tests/project_tests.rs b/tests/project_tests.rs index 1802197..3ffe3a3 100644 --- a/tests/project_tests.rs +++ b/tests/project_tests.rs @@ -121,18 +121,23 @@ fn test_import_binary() { let project = unique_project_name("import"); let binary = common::fixture_binary(); - // Import outputs JSON to stdout; status messages go to stderr - Command::cargo_bin("ghidra") - .unwrap() - .arg("import") - .arg(binary.to_str().unwrap()) - .arg("--project") - .arg(&project) - .arg("--program") - .arg("sample_binary") - .timeout(std::time::Duration::from_secs(300)) - .assert() - .success(); + // Use run_cli_with_timeout to avoid Windows pipe handle inheritance. + // `ghidra import` spawns a JVM whose inherited pipe handles block output() forever. + let ghidra_bin = assert_cmd::cargo::cargo_bin("ghidra"); + let status = common::run_cli_with_timeout( + &ghidra_bin, + &[ + "import", + binary.to_str().unwrap(), + "--project", + &project, + "--program", + "sample_binary", + ], + std::time::Duration::from_secs(300), + ) + .expect("Failed to run import"); + assert!(status.success(), "Import failed with status: {}", status); Command::cargo_bin("ghidra") .unwrap() @@ -151,28 +156,35 @@ fn test_analyze_program() { let project = unique_project_name("analyze"); let binary = common::fixture_binary(); - Command::cargo_bin("ghidra") - .unwrap() - .arg("import") - .arg(binary.to_str().unwrap()) - .arg("--project") - .arg(&project) - .arg("--program") - .arg("sample_binary") - .timeout(std::time::Duration::from_secs(300)) - .assert() - .success(); + let ghidra_bin = assert_cmd::cargo::cargo_bin("ghidra"); + let status = common::run_cli_with_timeout( + &ghidra_bin, + &[ + "import", + binary.to_str().unwrap(), + "--project", + &project, + "--program", + "sample_binary", + ], + std::time::Duration::from_secs(300), + ) + .expect("Failed to run import"); + assert!(status.success(), "Import failed with status: {}", status); - Command::cargo_bin("ghidra") - .unwrap() - .arg("analyze") - .arg("--project") - .arg(&project) - .arg("--program") - .arg("sample_binary") - .timeout(std::time::Duration::from_secs(300)) - .assert() - .success(); + let status = common::run_cli_with_timeout( + &ghidra_bin, + &[ + "analyze", + "--project", + &project, + "--program", + "sample_binary", + ], + std::time::Duration::from_secs(300), + ) + .expect("Failed to run analyze"); + assert!(status.success(), "Analyze failed with status: {}", status); Command::cargo_bin("ghidra") .unwrap() @@ -207,29 +219,42 @@ fn test_import_existing_program() { let project = unique_project_name("import-existing"); let binary = common::fixture_binary(); - // Import outputs JSON to stdout; status messages go to stderr - let mut cmd = Command::cargo_bin("ghidra").unwrap(); - cmd.arg("import") - .arg(binary.to_str().unwrap()) - .arg("--project") - .arg(&project) - .arg("--program") - .arg("sample_binary") - .timeout(std::time::Duration::from_secs(300)) - .assert() - .success(); + // Use run_cli_with_timeout to avoid Windows pipe handle inheritance. + let ghidra_bin = assert_cmd::cargo::cargo_bin("ghidra"); + let status = common::run_cli_with_timeout( + &ghidra_bin, + &[ + "import", + binary.to_str().unwrap(), + "--project", + &project, + "--program", + "sample_binary", + ], + std::time::Duration::from_secs(300), + ) + .expect("Failed to run import"); + assert!(status.success(), "Import failed with status: {}", status); // Import again - should still succeed (idempotent or with new name) - let mut cmd = Command::cargo_bin("ghidra").unwrap(); - cmd.arg("import") - .arg(binary.to_str().unwrap()) - .arg("--project") - .arg(&project) - .arg("--program") - .arg("sample_binary") - .timeout(std::time::Duration::from_secs(300)) - .assert() - .success(); + let status = common::run_cli_with_timeout( + &ghidra_bin, + &[ + "import", + binary.to_str().unwrap(), + "--project", + &project, + "--program", + "sample_binary", + ], + std::time::Duration::from_secs(300), + ) + .expect("Failed to run second import"); + assert!( + status.success(), + "Second import failed with status: {}", + status + ); Command::cargo_bin("ghidra") .unwrap()