use super::*;
use tempfile::TempDir;
fn sh_args(script: &str) -> Vec<OsString> {
vec![OsString::from("-c"), OsString::from(script)]
}
#[test]
fn succeeds_when_process_writes_output_and_exits_zero() {
let dir = TempDir::new().unwrap();
let output = dir.path().join("out.txt");
let script = format!("printf hi > {}", output.display());
let result = execute(
"sh-test",
"n/a",
"sh",
&sh_args(&script),
&[&output],
TOOL_TIMEOUT,
);
assert!(result.is_ok(), "{result:?}");
}
#[test]
fn maps_nonzero_exit_to_exit_non_zero_with_captured_stderr() {
let dir = TempDir::new().unwrap();
let output = dir.path().join("out.txt");
let result = execute(
"sh-test",
"n/a",
"sh",
&sh_args("echo boom 1>&2; exit 3"),
&[&output],
TOOL_TIMEOUT,
);
match result {
Err(ToolError::ExitNonZero { code, stderr, .. }) => {
assert_eq!(code, Some(3));
assert!(stderr.contains("boom"), "stderr was: {stderr}");
}
other => panic!("expected ExitNonZero, got {other:?}"),
}
}
#[test]
fn maps_missing_binary_to_not_found() {
let dir = TempDir::new().unwrap();
let output = dir.path().join("out.txt");
let result = execute(
"missing-test",
"install it from nowhere",
"definitely-not-a-real-binary-9f3c2a",
&[],
&[&output],
TOOL_TIMEOUT,
);
assert!(
matches!(result, Err(ToolError::NotFound { .. })),
"{result:?}"
);
}
#[test]
fn zero_exit_with_no_output_file_is_no_output() {
let dir = TempDir::new().unwrap();
let output = dir.path().join("out.txt");
let result = execute(
"sh-test",
"n/a",
"sh",
&sh_args("true"),
&[&output],
TOOL_TIMEOUT,
);
assert!(
matches!(result, Err(ToolError::NoOutput { .. })),
"{result:?}"
);
}
#[test]
fn timed_out_process_is_reported() {
let dir = TempDir::new().unwrap();
let output = dir.path().join("out.txt");
let result = execute(
"sh-test",
"n/a",
"sh",
&sh_args("sleep 5"),
&[&output],
Duration::from_millis(50),
);
assert!(
matches!(result, Err(ToolError::TimedOut { .. })),
"{result:?}"
);
}
#[test]
fn locates_a_binary_known_to_exist_on_path() {
assert!(
locate_on_path("sh"),
"sh should be on PATH in any test environment"
);
}
#[test]
fn does_not_locate_a_nonexistent_binary() {
assert!(!locate_on_path("definitely-not-a-real-binary-9f3c2a"));
}
#[test]
fn a_hanging_tool_is_killed_at_the_timeout() {
let dir = TempDir::new().unwrap();
let output = dir.path().join("never-written.txt");
let started = std::time::Instant::now();
let result = execute(
"sh-test",
"n/a",
"sh",
&sh_args("sleep 30"),
&[&output],
Duration::from_millis(200),
);
assert!(
matches!(result, Err(ToolError::TimedOut { .. })),
"expected TimedOut, got: {result:?}"
);
assert!(
started.elapsed() < Duration::from_secs(5),
"the call returned only after {:?} — the timeout is not bounding anything",
started.elapsed()
);
}
#[test]
fn a_timed_out_tool_leaves_no_process_behind() {
let dir = TempDir::new().unwrap();
let output = dir.path().join("never-written.txt");
let marker = dir.path().join("still-alive.txt");
let script = format!("sleep 1; printf alive > {}", marker.display());
let result = execute(
"sh-test",
"n/a",
"sh",
&sh_args(&script),
&[&output],
Duration::from_millis(100),
);
assert!(matches!(result, Err(ToolError::TimedOut { .. })));
std::thread::sleep(Duration::from_millis(1500));
assert!(
!marker.exists(),
"the timed-out child kept running and wrote its marker — it was abandoned, not killed"
);
}