144 lines
5.5 KiB
Rust
144 lines
5.5 KiB
Rust
use super::*;
|
||
|
||
fn test_sources(sides: usize) -> Vec<Source> {
|
||
(0..sides * 2 - 1)
|
||
.map(|index| Source {
|
||
label: format!("source {index}"),
|
||
base: index % 2 == 1,
|
||
kind: FileKind::Text,
|
||
bytes: b"historical full file; must never replace working text\n".to_vec(),
|
||
tree_id: String::new(),
|
||
term: index / 2,
|
||
sides,
|
||
bases: sides - 1,
|
||
node: Node::Absent,
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
fn snapshot(left: &str, base: &str, right: &str) -> String {
|
||
format!(
|
||
"<<<<<<< conflict\n+++++++ side 1\n{left}------- base\n{base}+++++++ side 2\n{right}>>>>>>> conflict ends\n"
|
||
)
|
||
}
|
||
|
||
#[test]
|
||
fn current_working_projections_keep_partial_resolutions_and_multiple_regions() {
|
||
let sources = test_sources(2);
|
||
let raw = format!(
|
||
"unsaved heading\nalready manually resolved\n{}auto-merged middle\n<<<<<<< conflict\n%%%%%%% Changes from base to side 1\n\\\\\\\\\\\\\\ continuation\n-old two\n+left two\n+++++++ side 2\nright two\n>>>>>>> conflict ends\nunsaved tail",
|
||
snapshot("left one\n", "old one\n", "right one\n")
|
||
);
|
||
for (index, first, second) in [
|
||
(0, "left one", "left two"),
|
||
(1, "old one", "old two"),
|
||
(2, "right one", "right two"),
|
||
] {
|
||
assert_eq!(
|
||
project(&raw, &sources[index]).unwrap(),
|
||
format!(
|
||
"unsaved heading\nalready manually resolved\n{first}\nauto-merged middle\n{second}\nunsaved tail"
|
||
)
|
||
);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn alternatives_are_frozen_from_the_same_working_text_as_the_seed() {
|
||
let sources = test_sources(2);
|
||
let raw = format!(
|
||
"unsaved heading\n{}auto-merged middle\n{}unsaved tail",
|
||
snapshot("left-one\n", "old-one\n", "right-one\n"),
|
||
snapshot("left-two\n", "old-two\n", "right-two\n")
|
||
);
|
||
let projection = Projection::new(&raw, &sources, 0).unwrap();
|
||
let mut draft = projection.seed().to_owned();
|
||
draft = draft.replace("unsaved heading", "later draft edit");
|
||
draft.push_str("\nnew draft ending\n");
|
||
assert_eq!(
|
||
projection.alternative(2).unwrap(),
|
||
"unsaved heading\nright-one\nauto-merged middle\nright-two\nunsaved tail"
|
||
);
|
||
assert_eq!(
|
||
projection.alternative(1).unwrap(),
|
||
"unsaved heading\nold-one\nauto-merged middle\nold-two\nunsaved tail"
|
||
);
|
||
assert_eq!(
|
||
projection.seed(),
|
||
"unsaved heading\nleft-one\nauto-merged middle\nleft-two\nunsaved tail"
|
||
);
|
||
assert_eq!(
|
||
draft,
|
||
"later draft edit\nleft-one\nauto-merged middle\nleft-two\nunsaved tail\nnew draft ending\n"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn all_multiway_sides_and_bases_project_the_same_current_context() {
|
||
let sources = test_sources(3);
|
||
let raw = "unsaved α\n<<<<<<<<<<< conflict\n+++++++++++ side 1\none\n----------- base 1\nold one\n+++++++++++ side 2\ntwo\n----------- base 2\nold two\n+++++++++++ side 3\nthree\n>>>>>>>>>>> conflict ends\nunsaved ω";
|
||
let projection = Projection::new(raw, &sources, 0).unwrap();
|
||
for (index, choice) in ["one", "old one", "two", "old two", "three"]
|
||
.into_iter()
|
||
.enumerate()
|
||
{
|
||
let expected = format!("unsaved α\n{choice}\nunsaved ω");
|
||
assert_eq!(project(raw, &sources[index]).unwrap(), expected);
|
||
assert_eq!(projection.alternative(index).unwrap(), expected);
|
||
}
|
||
assert_eq!(
|
||
Projection::new(raw, &sources, 4).unwrap().seed(),
|
||
"unsaved α\nthree\nunsaved ω"
|
||
);
|
||
}
|
||
|
||
#[test]
|
||
fn projections_preserve_mixed_eof_and_crlf_terms() {
|
||
let sources = test_sources(2);
|
||
for eol in ["\n", "\r\n"] {
|
||
let choices = [
|
||
format!("left{eol}"),
|
||
"old".to_owned(),
|
||
format!("right{eol}{eol}"),
|
||
];
|
||
// jj appends one separator EOL to *each* term when any lacks an EOL,
|
||
// and omits the EOL on the final marker. Existing term EOLs remain.
|
||
let raw = format!(
|
||
"heading{eol}<<<<<<< conflict{eol}+++++++ side 1{eol}{}{eol}------- base{eol}{}{eol}+++++++ side 2{eol}{}{eol}>>>>>>> conflict ends",
|
||
choices[0], choices[1], choices[2]
|
||
);
|
||
let projection = Projection::new(&raw, &sources, 0).unwrap();
|
||
for (index, choice) in choices.iter().enumerate() {
|
||
assert_eq!(
|
||
project(&raw, &sources[index]).unwrap(),
|
||
format!("heading{eol}{choice}")
|
||
);
|
||
assert_eq!(
|
||
projection.alternative(index).unwrap(),
|
||
format!("heading{eol}{choice}")
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn empty_choices_markerfree_text_and_invalid_choices_are_handled() {
|
||
let mut sources = test_sources(2);
|
||
sources[1].kind = FileKind::Deleted;
|
||
let raw = format!("before\n{}after\n", snapshot("", "", "new\n"));
|
||
let projection = Projection::new(&raw, &sources, 0).unwrap();
|
||
assert_eq!(projection.seed(), "before\nafter\n");
|
||
assert_eq!(projection.alternative(1).unwrap(), "before\nafter\n");
|
||
assert_eq!(projection.alternative(2).unwrap(), "before\nnew\nafter\n");
|
||
let resolved = Projection::new("manually resolved, no final EOL", &sources, 0).unwrap();
|
||
assert_eq!(
|
||
resolved.alternative(2).unwrap(),
|
||
"manually resolved, no final EOL"
|
||
);
|
||
assert!(resolved.alternative(sources.len()).is_err());
|
||
assert!(Projection::new(&raw, &sources, sources.len()).is_err());
|
||
assert!(Projection::new(&raw, &test_sources(3), 0).is_err());
|
||
sources[0].kind = FileKind::Binary;
|
||
assert!(project(&raw, &sources[0]).is_err());
|
||
}
|