use super::*;
use crate::state::stamp_config::DeclaredHook;
fn git(dir: &Path, args: &[&str]) {
let output = crate::git_backend::git_command()
.arg("-C")
.arg(dir)
.args(["-c", "user.name=Test", "-c", "user.email=test@example.com"])
.args(args)
.output()
.expect("git runs");
assert!(output.status.success(), "git {args:?}: {}", String::from_utf8_lossy(&output.stderr));
}
fn hook(run: &[&str]) -> Hook {
Hook::Declared(DeclaredHook {
run: run.iter().map(|word| word.to_string()).collect(),
inputs: Some(HookInputs {
repositories: vec![PathBuf::from("lib")],
files: vec![PathBuf::from("deps.lock")],
programs: Vec::new(),
}),
produces: vec![PathBuf::from(".build/dist")],
})
}
fn reason(decision: Decision) -> String {
match decision {
Decision::Run { reason } => reason,
other => panic!("expected a run, got {other:?}"),
}
}
#[test]
fn test20227_prepare_runs_only_when_what_it_builds_from_changed() {
let scratch = tempfile::tempdir().unwrap();
let root = scratch.path().join("tree");
let lib = root.join("lib");
std::fs::create_dir_all(&lib).unwrap();
git(&lib, &["init", "--quiet", "-b", "main"]);
std::fs::write(lib.join("lib.rs"), "fn one() {}\n").unwrap();
git(&lib, &["add", "-A"]);
git(&lib, &["commit", "--quiet", "-m", "one"]);
std::fs::write(root.join("deps.lock"), "a = 1\n").unwrap();
std::fs::create_dir_all(root.join(".build/dist")).unwrap();
let state = scratch.path().join("state");
let prepare = hook(&["make", "local-deps"]);
let always = Hook::Command(vec!["make".to_string()]);
assert_eq!(decide(&always, &root, &state).unwrap(), (Decision::Always, None));
let (first, fingerprint) = decide(&prepare, &root, &state).unwrap();
assert!(reason(first).contains("not prepared here"));
record(&state, fingerprint.unwrap()).unwrap();
let ran_with = |root: &Path| {
let (decision, fingerprint) = decide(&prepare, root, &state).unwrap();
(decision, fingerprint.unwrap())
};
assert!(matches!(ran_with(&root).0, Decision::Unchanged { .. }));
std::fs::write(lib.join("lib.rs"), "fn two() {}\n").unwrap();
let (changed, now) = ran_with(&root);
assert_eq!(reason(changed), "repository lib changed");
record(&state, now).unwrap();
git(&lib, &["commit", "--quiet", "-am", "two"]);
assert!(matches!(ran_with(&root).0, Decision::Unchanged { .. }), "a commit of what was prepared from");
std::fs::write(lib.join("new.rs"), "\n").unwrap();
assert_eq!(reason(ran_with(&root).0), "repository lib changed", "an untracked file");
std::fs::remove_file(lib.join("new.rs")).unwrap();
std::fs::write(root.join("deps.lock"), "a = 2\n").unwrap();
let (changed, now) = ran_with(&root);
assert_eq!(reason(changed), "file deps.lock changed");
record(&state, now).unwrap();
std::fs::remove_dir_all(root.join(".build/dist")).unwrap();
assert_eq!(reason(ran_with(&root).0), ".build/dist is not there");
std::fs::create_dir_all(root.join(".build/dist")).unwrap();
let other = hook(&["make", "other-deps"]);
assert_eq!(reason(decide(&other, &root, &state).unwrap().0), "command changed");
forget(&state).unwrap();
assert!(reason(ran_with(&root).0).contains("not prepared here"));
}