use super::*;
use crate::test_env::CargoTargetDirGuard;
use std::process::Command;
#[test]
fn target_dir_for_worktree_keeps_source_and_branches_as_siblings() {
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("target");
let source = root.join("_base");
let _target_dir = CargoTargetDirGuard::set(&root);
let branch_target = target_dir_for_worktree(Some("feat/shared-cache")).unwrap();
let source_target = target_dir_for_worktree(None).unwrap();
assert_eq!(
branch_target,
root.join("feat-shared-cache").to_string_lossy()
);
assert_eq!(
source_target,
source.to_string_lossy()
);
assert!(!std::path::Path::new(&branch_target).exists());
}
#[test]
fn seed_branch_target_dir_copies_base_without_existing_branches() {
let _clone = super::super::cargo_target::CloneSeedGuard::regular_copy();
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("target");
let source = root.join("_base");
let existing_branch = root.join("existing-branch");
let debug = source.join("debug");
std::fs::create_dir_all(&debug).unwrap();
std::fs::create_dir_all(&existing_branch).unwrap();
std::fs::write(debug.join("artifact.txt"), "cached").unwrap();
let _target_dir = CargoTargetDirGuard::set(&root);
let outcome = seed_branch_target_dir("feat/shared-cache").unwrap();
assert!(matches!(outcome, BranchTargetSeedOutcome::Seeded { .. }));
assert_eq!(
std::fs::read_to_string(root.join("feat-shared-cache/debug/artifact.txt")).unwrap(),
"cached"
);
assert!(!root.join("feat-shared-cache/existing-branch").exists());
}
#[test]
fn seed_branch_target_dir_falls_back_to_root_artifact_entries() {
let clone_guard = super::super::cargo_target::CloneSeedGuard::regular_copy();
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("target");
let debug = root.join("debug");
let release = root.join("release");
let existing_branch = root.join("existing-branch");
std::fs::create_dir_all(&debug).unwrap();
std::fs::create_dir_all(&release).unwrap();
std::fs::create_dir_all(&existing_branch).unwrap();
std::fs::write(debug.join("artifact.txt"), "debug").unwrap();
std::fs::write(release.join("artifact.txt"), "release").unwrap();
std::fs::write(root.join(".rustc_info.json"), "{}").unwrap();
let target_dir_guard = CargoTargetDirGuard::set(&root);
let outcome = seed_branch_target_dir("feat/shared-cache").unwrap();
assert!(matches!(outcome, BranchTargetSeedOutcome::Seeded { .. }));
assert_eq!(
std::fs::read_to_string(root.join("feat-shared-cache/debug/artifact.txt")).unwrap(),
"debug"
);
assert_eq!(
std::fs::read_to_string(root.join("feat-shared-cache/release/artifact.txt")).unwrap(),
"release"
);
assert!(root.join("feat-shared-cache/.rustc_info.json").is_file());
assert!(!root.join("feat-shared-cache/existing-branch").exists());
drop((target_dir_guard, clone_guard));
}
#[test]
fn seed_branch_target_dir_prefers_base_over_root_artifact_entries() {
let clone_guard = super::super::cargo_target::CloneSeedGuard::regular_copy();
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("target");
let base_debug = root.join("_base/debug");
let root_debug = root.join("debug");
std::fs::create_dir_all(&base_debug).unwrap();
std::fs::create_dir_all(&root_debug).unwrap();
std::fs::create_dir_all(root.join("release")).unwrap();
std::fs::write(base_debug.join("artifact.txt"), "base").unwrap();
std::fs::write(root_debug.join("artifact.txt"), "fallback").unwrap();
let target_dir_guard = CargoTargetDirGuard::set(&root);
let outcome = seed_branch_target_dir("feat/shared-cache").unwrap();
assert!(matches!(outcome, BranchTargetSeedOutcome::Seeded { .. }));
assert_eq!(
std::fs::read_to_string(root.join("feat-shared-cache/debug/artifact.txt")).unwrap(),
"base"
);
assert!(!root.join("feat-shared-cache/release").exists());
drop((target_dir_guard, clone_guard));
}
#[test]
fn seed_branch_target_dir_skips_when_no_seed_source_exists() {
let clone_guard = super::super::cargo_target::CloneSeedGuard::regular_copy();
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("target");
let target_dir_guard = CargoTargetDirGuard::set(&root);
let outcome = seed_branch_target_dir("feat/shared-cache").unwrap();
assert_eq!(
outcome,
BranchTargetSeedOutcome::Skipped {
target: root.join("feat-shared-cache").to_string_lossy().into_owned(),
reason: "base target directory is missing and no root cargo artifacts are present"
.to_string(),
}
);
drop((target_dir_guard, clone_guard));
}
#[test]
fn seed_branch_target_dir_keeps_multiple_branch_targets_flat() {
let _clone = super::super::cargo_target::CloneSeedGuard::regular_copy();
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("target");
let source = root.join("_base/debug");
std::fs::create_dir_all(&source).unwrap();
std::fs::write(source.join("artifact.txt"), "cached").unwrap();
let _target_dir = CargoTargetDirGuard::set(&root);
for branch in ["feat/cache-a", "feat/cache-b", "feat/cache-c"] {
let outcome = seed_branch_target_dir(branch).unwrap();
assert!(matches!(outcome, BranchTargetSeedOutcome::Seeded { .. }));
}
for target in ["feat-cache-a", "feat-cache-b", "feat-cache-c"] {
assert!(root.join(target).join("debug/artifact.txt").is_file());
for nested in ["feat-cache-a", "feat-cache-b", "feat-cache-c"] {
assert!(!root.join(target).join(nested).exists());
}
}
}
#[test]
fn explicit_cargo_target_dir_keeps_branch_target_inside_project_root() {
let temp = tempfile::tempdir().unwrap();
let root = temp.path().join("some/root/project");
let escaped = temp.path().join("some/root/fix-foo");
let _target_dir = CargoTargetDirGuard::set(&root);
let branch_target = PathBuf::from(target_dir_for_worktree(Some("fix/foo")).unwrap());
let source_target = PathBuf::from(target_dir_for_worktree(None).unwrap());
assert!(branch_target.starts_with(&root));
assert_eq!(branch_target, root.join("fix-foo"));
assert_ne!(branch_target, escaped);
assert_eq!(source_target, root.join("_base"));
}
#[test]
fn explicit_cargo_target_dir_namespaces_same_branch_per_project() {
let temp = tempfile::tempdir().unwrap();
let first_project = temp.path().join("root/project-a");
let second_project = temp.path().join("root/project-b");
let first_target = {
let _target_dir = CargoTargetDirGuard::set(&first_project);
target_dir_for_worktree(Some("fix/shared")).unwrap()
};
let second_target = {
let _target_dir = CargoTargetDirGuard::set(&second_project);
target_dir_for_worktree(Some("fix/shared")).unwrap()
};
assert_eq!(
first_target,
first_project.join("fix-shared").to_string_lossy()
);
assert_eq!(
second_target,
second_project.join("fix-shared").to_string_lossy()
);
assert_ne!(first_target, second_target);
}
#[test]
fn apply_rust_build_cache_env_sets_target_only() {
let temp = tempfile::tempdir().unwrap();
let project = temp.path().join("project");
let target = temp.path().join("target");
std::fs::create_dir_all(&project).unwrap();
std::fs::write(project.join("Cargo.toml"), "[package]\nname = \"demo\"\n").unwrap();
let _target_dir = CargoTargetDirGuard::set(&target);
let mut cmd = Command::new("echo");
apply_rust_build_cache_env(&mut cmd, project.to_str(), None);
let expected = target.join("_base").to_string_lossy().to_string();
assert_eq!(
command_env(&cmd, "CARGO_TARGET_DIR").as_deref(),
Some(expected.as_str())
);
assert!(command_env(&cmd, "RUSTC_WRAPPER").is_none());
}
fn command_env(cmd: &Command, name: &str) -> Option<String> {
cmd.get_envs()
.find(|(key, _)| *key == name)
.and_then(|(_, value)| value)
.map(|value| value.to_string_lossy().to_string())
}