use super::*;
fn machine() -> (tokio::runtime::Runtime, tempfile::TempDir, Arc<MachineState>) {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("a runtime");
let dir = tempfile::tempdir().expect("a scratch directory");
let machine = {
let _inside = runtime.enter();
MachineState::new(
dir.path().to_path_buf(),
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
0,
std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
0,
)
.expect("a machine with an identity and a store")
};
(runtime, dir, machine)
}
fn git(dir: &Path, args: &[&str]) {
let output = wsb::git_backend::git_command()
.arg("-C")
.arg(dir)
.args(args)
.output()
.expect("git runs");
assert!(
output.status.success(),
"git {args:?} in {}: {}",
dir.display(),
String::from_utf8_lossy(&output.stderr)
);
}
fn repository(dir: &Path) {
std::fs::create_dir_all(dir).expect("the folder is created");
git(dir, &["init", "--quiet", "-b", "main"]);
git(dir, &["config", "user.name", "Test"]);
git(dir, &["config", "user.email", "test@example.com"]);
std::fs::write(dir.join("README.md"), "one\n").expect("a file");
git(dir, &["add", "."]);
git(dir, &["commit", "--quiet", "-m", "one"]);
}
fn nested_repositories() -> (tempfile::TempDir, PathBuf, PathBuf) {
let dir = tempfile::tempdir().expect("a scratch directory");
let root = wsb::platform::canonicalize(dir.path()).expect("the scratch directory resolves");
let inner = root.join("wsb");
std::fs::create_dir_all(inner.join("src")).expect("the tree is created");
for repository in [&root, &inner] {
git(repository, &["init", "--quiet"]);
}
(dir, root, inner)
}
#[test]
fn test9578_a_directory_inside_a_worktree_is_not_a_worktree() {
let (runtime, _scratch, machine) = machine();
let (_repos, root, inner) = nested_repositories();
for standing_in in [inner.clone(), inner.join("src")] {
let refusal = match runtime.block_on(machine.register(&standing_in)) {
Err(refusal) => refusal,
Ok(opened) => panic!(
"{} was opened as a worktree of its own, at {}",
standing_in.display(),
opened.registered.super_root.display()
),
};
assert!(
refusal.contains(&root.display().to_string()),
"the refusal must name the worktree this directory belongs to, so the \
caller can resolve instead of guessing; it said: {refusal}"
);
}
}
#[test]
fn test9579_a_worktree_inside_another_is_forgotten() {
let (_runtime, _scratch, machine) = machine();
let outer = PathBuf::from("/machines/ws/prj/machinefabric/main-worktree");
let inner = outer.join("wsb");
let elsewhere = PathBuf::from("/machines/ws/prj/other");
for (index, root) in [&outer, &inner, &elsewhere].into_iter().enumerate() {
machine
.store
.register_worktree(&RegisteredWorktree {
id: WorktreeId::parse(&format!("~{:016x}", index + 1)).expect("an id"),
project: None,
name: root.file_name().expect("a name").to_string_lossy().into_owned(),
workspace: None,
super_root: root.clone(),
root_commit: Some(format!("{:040x}", index + 1)),
last_seen: 0,
available: true,
})
.expect("the record is written");
}
let dropped = machine.forget_contained_worktrees().expect("the registry reconciles");
assert_eq!(dropped, vec![inner.clone()], "only the worktree inside another one is dropped");
let left: Vec<PathBuf> = machine
.store
.worktrees()
.expect("the registry reads")
.into_iter()
.map(|worktree| worktree.super_root)
.collect();
assert!(
left.contains(&outer) && left.contains(&elsewhere) && !left.contains(&inner),
"the container and the unrelated worktree stay, the contained one goes; the \
registry holds {left:?}"
);
}
#[test]
fn test20035_two_worktrees_of_one_repository_are_two_worktrees() {
let (runtime, _scratch, machine) = machine();
let scratch = tempfile::tempdir().expect("a scratch directory");
let base = wsb::platform::canonicalize(scratch.path()).expect("resolves");
let main = base.join("main-worktree");
repository(&main);
let fix = base.join("fix-worktree");
git(&main, &["worktree", "add", "--quiet", "-b", "fix", fix.to_str().expect("a path")]);
let first = runtime.block_on(machine.register(&main)).expect("main opens");
let second = runtime.block_on(machine.register(&fix)).expect("fix opens");
assert_ne!(first.registered.id, second.registered.id);
assert_eq!(first.web.workspace_root, main);
assert_eq!(second.web.workspace_root, fix, "fix is served from its own checkout");
assert!(main.join(".wsb/wsb.db").is_file());
assert!(fix.join(".wsb/wsb.db").is_file(), "fix has a database of its own");
let (one, two) = (
first.registered.workspace.clone().expect("main has continuity"),
second.registered.workspace.clone().expect("fix has continuity"),
);
assert_eq!(one.family(), two.family());
assert_ne!(one, two);
let again = runtime.block_on(machine.register(&fix)).expect("fix again");
assert!(Arc::ptr_eq(&again, &second));
let by_id = runtime.block_on(machine.open(&second.registered.id)).expect("by id");
assert!(Arc::ptr_eq(&by_id, &second));
assert_eq!(runtime.block_on(machine.all()).len(), 2);
let advertised: Vec<String> = runtime
.block_on(machine.summaries())
.into_iter()
.map(|summary| summary.workspace.to_string())
.collect();
assert_eq!(advertised.len(), 2, "both worktrees are offered to peers: {advertised:?}");
}
#[test]
fn test20036_a_continuity_id_two_worktrees_claim_is_refused() {
let (runtime, _scratch, machine) = machine();
let scratch = tempfile::tempdir().expect("a scratch directory");
let base = wsb::platform::canonicalize(scratch.path()).expect("resolves");
let origin = base.join("origin");
repository(&origin);
let mut opened = Vec::new();
for project in ["one", "two"] {
let clone = base.join(project).join("main-worktree");
std::fs::create_dir_all(clone.parent().expect("a parent")).expect("mkdir");
git(&base, &["clone", "--quiet", origin.to_str().expect("a path"), clone.to_str().expect("a path")]);
opened.push(runtime.block_on(machine.register(&clone)).expect("opens"));
}
let shared = opened[0].registered.workspace.clone().expect("continuity");
assert_eq!(opened[1].registered.workspace.as_ref(), Some(&shared));
let refusal = match runtime.block_on(machine.continuity(&shared)) {
Err(refusal) => refusal,
Ok(found) => panic!("two claims were not refused: {:?}", found.map(|(r, _)| r.registered.id.clone())),
};
for runtime in &opened {
assert!(
refusal.contains(&runtime.registered.super_root.display().to_string()),
"the refusal names both worktrees: {refusal}"
);
}
assert!(
runtime.block_on(machine.summaries()).is_empty(),
"an ambiguous id is offered to no peer"
);
}