#![cfg(unix)]
use std::path::PathBuf;
use std::process::Command;
use std::time::Instant;
fn script() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/test-stall-watchdog.sh")
}
#[derive(Debug)]
struct Run {
code: Option<i32>,
stdout: String,
elapsed_secs: u64,
}
fn watchdog(envs: &[(&str, &str)], shell_cmd: &str) -> Run {
let started = Instant::now();
let out = Command::new("bash")
.arg(script())
.arg("sh")
.arg("-c")
.arg(shell_cmd)
.envs(envs.iter().map(|(k, v)| (k.to_string(), v.to_string())))
.output()
.expect("watchdog ran");
Run {
code: out.status.code(),
stdout: String::from_utf8_lossy(&out.stdout).into_owned(),
elapsed_secs: started.elapsed().as_secs(),
}
}
#[test]
fn stalled_suite_is_dumped_killed_and_flagged_not_silently_waited() {
let dump_on = tempfile::NamedTempFile::new().expect("dump file");
let run = watchdog(
&[
("WATCHDOG_STALL_SECS", "1"),
("WATCHDOG_POLL_SECS", "0.3"),
("WATCHDOG_DUMP_ON", dump_on.path().to_str().unwrap()),
("WATCHDOG_DUMPER", "/usr/bin/true"),
],
r#"echo "test a ... ok"; sleep 30"#,
);
assert_eq!(run.code, Some(97), "stall must exit 97, got: {run:?}",);
assert!(
run.stdout.contains("STALL DETECTED"),
"marker missing: {}",
run.stdout
);
let dumps = std::fs::read_to_string(dump_on.path()).expect("dump record");
assert!(
dumps.contains("dumped:"),
"dumper never ran for any pid: {dumps}"
);
assert!(
run.elapsed_secs < 15,
"watchdog waited on the stalled child instead of killing it ({}s)",
run.elapsed_secs
);
}
#[test]
fn progressing_results_reset_the_stall_clock() {
let run = watchdog(
&[
("WATCHDOG_STALL_SECS", "2"),
("WATCHDOG_POLL_SECS", "0.2"),
("WATCHDOG_DUMPER", "/usr/bin/true"),
],
r#"for i in 1 2 3 4; do echo "test $i ... ok"; sleep 0.5; done"#,
);
assert_eq!(run.code, Some(0), "healthy suite was interfered with");
assert!(!run.stdout.contains("STALL DETECTED"));
}
#[test]
fn compile_phase_never_counts_as_a_stall() {
let run = watchdog(
&[
("WATCHDOG_STALL_SECS", "1"),
("WATCHDOG_POLL_SECS", "0.2"),
("WATCHDOG_DUMPER", "/usr/bin/true"),
],
r#"echo Compiling; sleep 1.5; exit 3"#,
);
assert_eq!(run.code, Some(3), "exit code must pass through");
assert!(run.stdout.contains("Compiling"), "log not cat'd");
assert!(!run.stdout.contains("STALL DETECTED"));
}
#[test]
fn result_output_and_exit_code_pass_through_when_green() {
let run = watchdog(
&[("WATCHDOG_STALL_SECS", "5"), ("WATCHDOG_POLL_SECS", "0.2")],
r#"echo "test a ... ok"; echo "test b ... FAILED"; exit 0"#,
);
assert_eq!(run.code, Some(0));
assert!(run.stdout.contains("test a ... ok"), "{}", run.stdout);
assert!(run.stdout.contains("test b ... FAILED"));
}