#![cfg(unix)]
use super::CandidateStore;
use super::tests::{rev_parse, store, write};
use crate::candidate_object::{CandidateMode, ObjectId};
use crate::candidate_record::CandidateRecord;
use crate::candidate_tree::TreeRecord;
use crate::test_support::{fixture, git, remove, repository};
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
#[test]
fn lists_every_file_of_head_with_its_mode() {
let root: PathBuf = fixture("store-head-tree");
let repo: PathBuf = repository(root.as_path(), "repo");
std::fs::create_dir_all(repo.join("deep/er")).expect("directories");
write(repo.as_path(), b"deep/er/file.txt", b"nested\n");
write(repo.as_path(), b"run.sh", b"#!/bin/sh\n");
std::fs::set_permissions(repo.join("run.sh"), std::fs::Permissions::from_mode(0o755))
.expect("executable");
std::os::unix::fs::symlink("run.sh", repo.join("link")).expect("link");
git(repo.as_path(), &["add", "--all"]);
git(repo.as_path(), &["commit", "--quiet", "--message=files"]);
write(repo.as_path(), b"later.txt", b"after the commit\n");
git(repo.as_path(), &["add", "later.txt"]);
let mut subject: CandidateStore = store(repo.join("deep").as_path());
let records: Vec<TreeRecord> = subject.head_tree_records().expect("tree");
assert_eq!(
records,
vec![
TreeRecord {
mode: CandidateMode::Regular,
object: rev_parse(repo.as_path(), "HEAD:deep/er/file.txt"),
path: b"deep/er/file.txt".to_vec(),
},
TreeRecord {
mode: CandidateMode::Symlink,
object: rev_parse(repo.as_path(), "HEAD:link"),
path: b"link".to_vec(),
},
TreeRecord {
mode: CandidateMode::Executable,
object: rev_parse(repo.as_path(), "HEAD:run.sh"),
path: b"run.sh".to_vec(),
},
]
);
remove(root.as_path());
}
#[test]
fn unborn_head_has_no_records() {
let root: PathBuf = fixture("store-head-tree-unborn");
let repo: PathBuf = root.join("repo");
std::fs::create_dir(&repo).expect("repository directory");
git(
repo.as_path(),
&["init", "--quiet", "--initial-branch=main"],
);
write(repo.as_path(), b"staged.txt", b"staged\n");
git(repo.as_path(), &["add", "staged.txt"]);
let mut subject: CandidateStore = store(repo.as_path());
assert_eq!(subject.head_tree_records().expect("tree"), Vec::new());
remove(root.as_path());
}
#[test]
fn a_failed_listing_is_reported() {
let root: PathBuf = fixture("store-head-tree-failed");
let repo: PathBuf = repository(root.as_path(), "repo");
write(repo.as_path(), b"file.txt", b"content\n");
git(repo.as_path(), &["add", "file.txt"]);
git(repo.as_path(), &["commit", "--quiet", "--message=file"]);
let head: String =
String::from_utf8(git(repo.as_path(), &["rev-parse", "HEAD:"]).stdout).expect("tree name");
let tree: &str = head.trim();
let object: PathBuf = repo.join(".git/objects").join(&tree[..2]).join(&tree[2..]);
std::fs::remove_file(object).expect("remove the tree object");
let mut subject: CandidateStore = store(repo.as_path());
let error = subject.head_tree_records().expect_err("listing fails");
assert!(error.message.contains("ls-tree"), "{}", error.message);
remove(root.as_path());
}
#[test]
fn a_pushed_tree_lists_a_name_that_is_not_utf8_exactly() {
let root: PathBuf = fixture("store-pushed-tree-bytes");
let repo: PathBuf = repository(root.as_path(), "repo");
write(repo.as_path(), b"bad\xff.txt", b"bytes\n");
write(repo.as_path(), b"plain.txt", b"text\n");
git(repo.as_path(), &["add", "--all"]);
git(repo.as_path(), &["commit", "--quiet", "--message=files"]);
let tree: ObjectId = rev_parse(repo.as_path(), "HEAD^{tree}");
let mut subject: CandidateStore = store(repo.as_path());
let records: Vec<CandidateRecord> = subject.tree_records(&tree).expect("tree records");
let paths: Vec<&[u8]> = records.iter().map(|record| { return record.path.as_slice(); }).collect();
assert_eq!(paths, vec![b"bad\xff.txt".as_slice(), b"plain.txt".as_slice()]);
remove(root.as_path());
}