use super::ambient::{declares_inputs, hash_declared_inputs};
use super::io_tracking::hash_inputs;
use crate::core::types::{Resource, ResourceType};
use std::path::Path;
fn task(inputs: &[&str], ambient: &[&str], working_dir: Option<&str>) -> Resource {
Resource {
resource_type: ResourceType::Task,
task_inputs: inputs.iter().map(|s| (*s).to_string()).collect(),
ambient_inputs: ambient.iter().map(|s| (*s).to_string()).collect(),
working_dir: working_dir.map(str::to_string),
..Default::default()
}
}
#[test]
fn a_resource_declaring_only_ambient_inputs_declares_inputs() {
assert!(!declares_inputs(&task(&[], &[], None)));
assert!(declares_inputs(&task(&["src/a.c"], &[], None)));
assert!(declares_inputs(&task(&[], &["echo v1"], None)));
}
#[test]
fn no_ambient_inputs_is_byte_identical_to_the_file_only_hash() {
let dir = std::env::temp_dir().join(format!("forjar-amb-id-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("src")).unwrap();
std::fs::write(dir.join("src/a.c"), "int main(){}").unwrap();
let r = task(&["src/a.c"], &[], dir.to_str());
assert_eq!(
hash_declared_inputs(&r, &dir),
hash_inputs(&r.task_inputs, &dir).unwrap()
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn an_ambient_command_changes_the_hash_when_its_stdout_changes() {
let base = Path::new(".");
let a = hash_declared_inputs(&task(&[], &["echo v1"], Some(".")), base);
let b = hash_declared_inputs(&task(&[], &["echo v2"], Some(".")), base);
assert!(
a.is_some(),
"an ambient-only resource still has an input hash"
);
assert_ne!(a, b, "the ambient fingerprint is not in the hash");
}
#[test]
fn the_same_ambient_command_settles() {
let base = Path::new(".");
let r = task(&[], &["echo stable"], Some("."));
assert_eq!(
hash_declared_inputs(&r, base),
hash_declared_inputs(&r, base)
);
}
#[test]
fn a_failing_ambient_command_is_not_silently_dropped() {
let base = Path::new(".");
let failing = hash_declared_inputs(&task(&[], &["exit 7"], Some(".")), base);
assert!(failing.is_some());
assert_ne!(
failing,
hash_declared_inputs(&task(&[], &[], Some(".")), base),
"a broken fingerprint collapsed to the no-ambient hash"
);
assert_ne!(
failing,
hash_declared_inputs(&task(&[], &["exit 9"], Some(".")), base),
"two different failures hash the same"
);
}
#[test]
fn stderr_is_not_hashed() {
let base = Path::new(".");
let a = hash_declared_inputs(&task(&[], &["echo same; echo $$ >&2"], Some(".")), base);
let b = hash_declared_inputs(&task(&[], &["echo same; echo $$ >&2"], Some(".")), base);
assert_eq!(a, b);
}
#[test]
fn ambient_commands_run_in_the_resources_working_dir() {
let dir = std::env::temp_dir().join(format!("forjar-amb-cwd-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("fonts.txt"), "v1").unwrap();
let r = task(&[], &["cat fonts.txt"], dir.to_str());
let from_probe = hash_declared_inputs(&r, &dir);
let from_executor = hash_declared_inputs(&r, Path::new("."));
assert_eq!(
from_probe, from_executor,
"the ambient component depended on which caller asked"
);
std::fs::write(dir.join("fonts.txt"), "v2").unwrap();
assert_ne!(from_probe, hash_declared_inputs(&r, &dir));
let _ = std::fs::remove_dir_all(&dir);
}