use super::*;
use crate::test_support::executable;
use crate::test_support::{fixture, remove};
const GENEROUS: Duration = Duration::from_secs(60);
const BRIEF: Duration = Duration::from_millis(300);
const FAILING_STATUS: i32 = 7;
const SLOW_SECONDS: u64 = 30;
#[test]
fn finds_the_first_executable_in_search_order() {
let root: PathBuf = fixture("finds-first");
let first: PathBuf = root.join("first");
let second: PathBuf = root.join("second");
std::fs::create_dir_all(&first).expect("first");
std::fs::create_dir_all(&second).expect("second");
executable(first.join("tool").as_path(), b"#!/bin/sh\nexit 0\n");
executable(second.join("tool").as_path(), b"#!/bin/sh\nexit 0\n");
let search: OsString = std::env::join_paths([&first, &second]).expect("search path");
assert_eq!(find_program("tool", Some(search.as_os_str())), Some(first.join("tool")));
remove(&root);
}
#[test]
fn reports_missing_and_non_executable_programs_as_absent() {
let root: PathBuf = fixture("finds-absent");
std::fs::write(root.join("plain"), b"data").expect("plain file");
let search: OsString = root.clone().into_os_string();
assert_eq!(find_program("absent", Some(search.as_os_str())), None);
assert_eq!(find_program("plain", Some(search.as_os_str())), None);
assert_eq!(find_program("plain", None), None);
remove(&root);
}
#[test]
fn runs_in_the_directory_with_the_variables_and_reports_success() {
let root: PathBuf = fixture("runs-success");
let program: PathBuf = root.join("tool");
let record: PathBuf = root.join("record");
executable(
program.as_path(),
format!("#!/bin/sh\npwd -P > '{}'\nprintf '%s' \"$REPAIR_PROBE\" >> '{}'\n", record.display(), record.display()).as_bytes(),
);
let command: ToolCommand = ToolCommand { arguments: Vec::new(), directory: root.clone(), set: vec![(OsString::from("REPAIR_PROBE"), OsString::from("kept"))] };
let run: ToolRun = run_tool(program.as_path(), &command, GENEROUS);
assert_eq!(run.end, ToolEnd::Succeeded);
let recorded: String = std::fs::read_to_string(&record).expect("record");
assert!(recorded.starts_with(root.to_str().expect("utf8 root")), "ran in {recorded}");
assert!(recorded.ends_with("kept"), "variable missing in {recorded}");
remove(&root);
}
#[test]
fn reports_a_failure_with_its_status_and_output() {
let root: PathBuf = fixture("runs-failure");
let program: PathBuf = root.join("tool");
executable(program.as_path(), format!("#!/bin/sh\necho 'reason printed on stderr' >&2\nexit {FAILING_STATUS}\n").as_bytes());
let command: ToolCommand = ToolCommand { arguments: Vec::new(), directory: root.clone(), set: Vec::new() };
let run: ToolRun = run_tool(program.as_path(), &command, GENEROUS);
let ToolEnd::Failed(text) = run.end else { panic!("expected a failure, got {:?}", run.end) };
assert!(text.contains(FAILING_STATUS.to_string().as_str()), "status missing in {text}");
assert!(text.contains("reason printed on stderr"), "output missing in {text}");
remove(&root);
}
#[test]
fn stops_a_program_at_its_limit() {
let root: PathBuf = fixture("runs-limit");
let program: PathBuf = root.join("tool");
executable(program.as_path(), format!("#!/bin/sh\nexec sleep {SLOW_SECONDS}\n").as_bytes());
let command: ToolCommand = ToolCommand { arguments: Vec::new(), directory: root.clone(), set: Vec::new() };
let run: ToolRun = run_tool(program.as_path(), &command, BRIEF);
assert_eq!(run.end, ToolEnd::TimedOut);
assert!(run.millis < u128::from(SLOW_SECONDS) * 1000, "the run waited {} ms for a program stopped at its limit", run.millis);
remove(&root);
}
#[test]
fn reports_a_program_that_cannot_start() {
let root: PathBuf = fixture("runs-unstartable");
let command: ToolCommand = ToolCommand { arguments: Vec::new(), directory: root.clone(), set: Vec::new() };
let run: ToolRun = run_tool(root.join("absent").as_path(), &command, GENEROUS);
assert!(matches!(run.end, ToolEnd::Failed(_)), "got {:?}", run.end);
remove(&root);
}
#[test]
fn a_failure_text_drops_wait_notices() {
let text: String = tail_of(b"Blocking waiting for file lock on package cache\nerror: the real reason\n");
assert_eq!(text, "error: the real reason");
}