use super::*;
use crate::direct_fix_install::InstallChange;
use crate::test_support::{REAL_GIT, fixture, git, remove, repository};
use std::ffi::OsString;
use std::path::PathBuf;
use std::rc::Rc;
fn context() -> GitContext {
return GitContext {
real_git: PathBuf::from(REAL_GIT),
overlay: vec![
(OsString::from("GIT_AUTHOR_NAME"), OsString::from("Fixture")),
(OsString::from("GIT_AUTHOR_EMAIL"), OsString::from("fixture@example.invalid")),
],
global_prefix: Vec::new(),
common_dir: Some(crate::test_support::fixture_root()),
};
}
#[test]
fn selected_corrections_are_recorded_only_while_the_worktree_holds_the_original() {
let root: PathBuf = fixture("worktree-records-selected");
let repo: PathBuf = repository(root.as_path(), "repo");
std::fs::write(repo.join("a.txt"), b"one").expect("a");
std::fs::write(repo.join("b.txt"), b"two").expect("b");
std::fs::write(repo.join("c.txt"), b"three").expect("c");
git(repo.as_path(), &["add", "--", "a.txt", "b.txt", "c.txt"]);
let index: PathBuf = repo.join(".git/index");
let corrected = |name: &str, bytes: &[u8]| {
let blob = {
let mut child = std::process::Command::new(REAL_GIT)
.current_dir(&repo)
.args(["hash-object", "-w", "--stdin"])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
.expect("hash-object");
std::io::Write::write_all(&mut child.stdin.take().expect("stdin"), bytes).expect("write");
String::from_utf8_lossy(child.wait_with_output().expect("output").stdout.as_slice()).trim().to_string()
};
git(repo.as_path(), &["update-index", "--cacheinfo", "100644", blob.as_str(), name]);
};
corrected("a.txt", b"one\n");
corrected("b.txt", b"two\n");
corrected("c.txt", b"three\n");
std::fs::write(repo.join("b.txt"), b"two edited").expect("edited since");
std::fs::remove_file(repo.join("c.txt")).expect("removed since");
let change = |name: &str, original: &[u8], replacement: &[u8]| {
return InstallChange {
path: name.as_bytes().to_vec(),
mode: CandidateMode::Regular,
original: Rc::from(original),
replacement: Rc::from(replacement),
};
};
let records: Vec<AddedPath> = selected_worktree_records(
&context(),
repo.as_path(),
repo.as_path(),
index.as_path(),
&[
change("a.txt", b"one", b"one\n"),
change("b.txt", b"two", b"two\n"),
change("c.txt", b"three", b"three\n"),
],
)
.expect("records");
assert_eq!(records.len(), 1, "{records:?}");
assert_eq!(records[0].path, b"a.txt");
assert_eq!(records[0].git_mode, "100644");
assert_ne!(records[0].original_oid, records[0].intended_oid);
remove(root.as_path());
}
#[test]
fn non_ordinary_modes_get_no_completion() {
assert_eq!(ordinary_git_mode(CandidateMode::Regular), Some("100644"));
assert_eq!(ordinary_git_mode(CandidateMode::Executable), Some("100755"));
assert_eq!(ordinary_git_mode(CandidateMode::Symlink), None);
assert_eq!(ordinary_git_mode(CandidateMode::Gitlink), None);
}
#[test]
fn a_changed_mode_or_an_unchanged_blob_gets_no_completion() {
let root: PathBuf = fixture("worktree-records-skips");
let repo: PathBuf = repository(root.as_path(), "repo");
std::fs::write(repo.join("same.txt"), b"same").expect("same");
std::fs::write(repo.join("moded.txt"), b"moded").expect("moded");
git(repo.as_path(), &["add", "--", "same.txt", "moded.txt"]);
let blob = |bytes: &[u8]| -> String {
let mut child = std::process::Command::new(REAL_GIT)
.current_dir(&repo)
.args(["hash-object", "-w", "--stdin"])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()
.expect("hash-object");
std::io::Write::write_all(&mut child.stdin.take().expect("stdin"), bytes).expect("write");
return String::from_utf8_lossy(child.wait_with_output().expect("output").stdout.as_slice()).trim().to_string();
};
git(repo.as_path(), &["update-index", "--cacheinfo", "100755", blob(b"moded\n").as_str(), "moded.txt"]);
let change = |name: &str, original: &[u8], replacement: &[u8]| {
return InstallChange {
path: name.as_bytes().to_vec(),
mode: CandidateMode::Regular,
original: Rc::from(original),
replacement: Rc::from(replacement),
};
};
let records: Vec<AddedPath> = selected_worktree_records(
&context(),
repo.as_path(),
repo.as_path(),
repo.join(".git/index").as_path(),
&[change("same.txt", b"same", b"same\n"), change("moded.txt", b"moded", b"moded\n")],
)
.expect("records");
assert_eq!(records, Vec::<AddedPath>::new());
remove(root.as_path());
}
#[test]
fn only_a_stage_zero_entry_is_settled() {
let root: PathBuf = fixture("worktree-records-settled");
let repo: PathBuf = repository(root.as_path(), "repo");
std::fs::write(repo.join("a.txt"), b"settled").expect("a");
git(repo.as_path(), &["add", "--", "a.txt"]);
let blob: String = String::from_utf8_lossy(git(repo.as_path(), &["rev-parse", ":a.txt"]).stdout.as_slice())
.trim()
.to_string();
let entries: String = format!("100644 {blob} 1\tu.txt\n100644 {blob} 2\tu.txt\n100644 {blob} 3\tu.txt\n");
let mut child = std::process::Command::new(REAL_GIT)
.current_dir(&repo)
.args(["update-index", "--index-info"])
.stdin(std::process::Stdio::piped())
.spawn()
.expect("update-index");
std::io::Write::write_all(&mut child.stdin.take().expect("stdin"), entries.as_bytes()).expect("write");
assert!(child.wait().expect("wait").success());
let index: PathBuf = repo.join(".git/index");
let settled = settled_entry(&context(), repo.as_path(), index.as_path(), b"a.txt").expect("settled");
assert_eq!(settled, Some((String::from("100644"), blob.clone())));
let unmerged = settled_entry(&context(), repo.as_path(), index.as_path(), b"u.txt").expect("unmerged");
assert_eq!(unmerged, None);
remove(root.as_path());
}