use super::{
SequencerFacts, SequencerOutputError, SequencerState, sequencer_head_paths,
sequencer_query_arguments, sequencer_state, sequencer_state_when_query_fails,
};
use crate::command_test_support::{
fixture, git, git_output, os_arguments, remove, repository, repository_with_tracked_file,
start_conflicted_merge,
};
use std::path::{Path, PathBuf};
use std::process::Output;
#[test]
fn decides_from_the_three_presence_facts() {
for (merge_head, cherry_pick_head, revert_head, expected) in [
(false, false, false, SequencerState::NotInProgress),
(true, false, false, SequencerState::InProgress),
(false, true, false, SequencerState::InProgress),
(false, false, true, SequencerState::InProgress),
(true, true, true, SequencerState::InProgress),
] {
assert_eq!(
sequencer_state(SequencerFacts {
merge_head,
cherry_pick_head,
revert_head
}),
expected
);
}
assert_eq!(
sequencer_state_when_query_fails(),
SequencerState::NotInProgress
);
}
#[test]
fn builds_the_query_after_the_global_prefix() {
assert_eq!(
sequencer_query_arguments(os_arguments(&["-C", "/r"]).as_slice()),
os_arguments(&[
"-C",
"/r",
"rev-parse",
"--path-format=absolute",
"--git-path",
"MERGE_HEAD",
"--git-path",
"CHERRY_PICK_HEAD",
"--git-path",
"REVERT_HEAD"
])
);
}
#[test]
fn reads_exact_query_output() {
assert_eq!(
sequencer_head_paths(
b"/r/.git/MERGE_HEAD\n/r/.git/CHERRY_PICK_HEAD\n/r/.git/REVERT_HEAD\n"
),
Ok(vec![
b"/r/.git/MERGE_HEAD".to_vec(),
b"/r/.git/CHERRY_PICK_HEAD".to_vec(),
b"/r/.git/REVERT_HEAD".to_vec()
])
);
assert_eq!(
sequencer_head_paths(b"/a\nb/MERGE_HEAD\n/a\nb/CHERRY_PICK_HEAD\n/a\nb/REVERT_HEAD\n"),
Ok(vec![
b"/a\nb/MERGE_HEAD".to_vec(),
b"/a\nb/CHERRY_PICK_HEAD".to_vec(),
b"/a\nb/REVERT_HEAD".to_vec()
])
);
assert_eq!(
sequencer_head_paths(b"MERGE_HEAD\nCHERRY_PICK_HEAD\nREVERT_HEAD\n"),
Ok(vec![
b"MERGE_HEAD".to_vec(),
b"CHERRY_PICK_HEAD".to_vec(),
b"REVERT_HEAD".to_vec()
])
);
assert_eq!(
sequencer_head_paths(b"MERGE_HEAD\nCHERRY_PICK_HEAD\nREVERT_HEAD"),
Err(SequencerOutputError)
);
for malformed in [
&b""[..],
b"/r/.git/MERGE_HEAD\n",
b"/r/.git/MERGE_HEAD\n/r/.git/CHERRY_PICK_HEAD\n/r/.git/REVERT_HEAD",
b"/r/.git/MERGE_HEAD\n/x/.git/CHERRY_PICK_HEAD\n/r/.git/REVERT_HEAD\n",
b"/r/.git/MERGE_HEAD\n/r/.git/CHERRY_PICK_HEAD\n/r/.git/REVERT_HEAD\n\n\n\n",
b"/r/.git/REVERT_HEAD\n/r/.git/CHERRY_PICK_HEAD\n/r/.git/MERGE_HEAD\n",
b"/r/.git/MERGE_HEAD /r/.git/CHERRY_PICK_HEAD\n/r/.git/REVERT_HEAD\n",
b"/r/.git/MERGE_HEAD\n/r/.git/CHERRY_PICK_HEAD\n/r/.git/REVERT_HEAD\nx",
b"/r/.git/MERGE_HEAD\n/r/.git/CHERRY_PICK_HEAD\n/r/.git/REVERT_HEAD\n\n\n",
] {
assert_eq!(
sequencer_head_paths(malformed),
Err(SequencerOutputError),
"{:?}",
String::from_utf8_lossy(malformed)
);
}
assert!(SequencerOutputError.to_string().contains("--git-path"));
}
#[cfg(unix)]
fn observed_facts(directory: &Path) -> (Vec<PathBuf>, SequencerFacts) {
use std::os::unix::ffi::OsStringExt;
let output: Output = git_output(directory, sequencer_query_arguments(&[]).as_slice());
assert!(output.status.success());
let mut paths: Vec<PathBuf> = Vec::<PathBuf>::new();
for bytes in sequencer_head_paths(output.stdout.as_slice()).expect("three paths") {
paths.push(PathBuf::from(std::ffi::OsString::from_vec(bytes)));
}
let facts: SequencerFacts = SequencerFacts {
merge_head: paths[0].exists(),
cherry_pick_head: paths[1].exists(),
revert_head: paths[2].exists(),
};
return (paths, facts);
}
#[cfg(unix)]
#[test]
fn real_git_merge_conflict_is_in_progress() {
let (directory, root): (PathBuf, PathBuf) = repository_with_tracked_file("sequencer-merge");
let (paths, before): (Vec<PathBuf>, SequencerFacts) = observed_facts(root.as_path());
assert_eq!(paths[0], root.join(".git").join("MERGE_HEAD"));
assert_eq!(sequencer_state(before), SequencerState::NotInProgress);
start_conflicted_merge(root.as_path());
let (_, during): (Vec<PathBuf>, SequencerFacts) = observed_facts(root.as_path());
assert!(during.merge_head && !during.cherry_pick_head && !during.revert_head);
assert_eq!(sequencer_state(during), SequencerState::InProgress);
git(root.as_path(), &["merge", "--abort"]);
let (_, after): (Vec<PathBuf>, SequencerFacts) = observed_facts(root.as_path());
assert_eq!(sequencer_state(after), SequencerState::NotInProgress);
remove(directory.as_path());
}
#[cfg(unix)]
#[test]
fn real_git_paths_follow_linked_worktrees_and_newlines() {
let directory: PathBuf = fixture("sequencer-paths");
let root: PathBuf = repository(directory.as_path(), "line\nbreak");
let (paths, _): (Vec<PathBuf>, SequencerFacts) = observed_facts(root.as_path());
assert_eq!(
paths,
vec![
root.join(".git").join("MERGE_HEAD"),
root.join(".git").join("CHERRY_PICK_HEAD"),
root.join(".git").join("REVERT_HEAD")
]
);
let linked: PathBuf = directory.join("linked");
git(
root.as_path(),
&["worktree", "add", "--quiet", "../linked", "--detach"],
);
let (linked_paths, _): (Vec<PathBuf>, SequencerFacts) = observed_facts(linked.as_path());
assert_eq!(
linked_paths[0],
root.join(".git/worktrees/linked/MERGE_HEAD")
);
let outside: Output = git_output(
directory.as_path(),
sequencer_query_arguments(&[]).as_slice(),
);
assert!(!outside.status.success());
remove(directory.as_path());
}