use super::*;
fn rendered(hunks: &[Hunk]) -> Vec<String> {
hunks
.iter()
.flat_map(|hunk| &hunk.lines)
.map(|line| match line {
DiffLine::Context { old, new, text, .. } => format!("{old}|{new}| |{text}"),
DiffLine::Removed { old, text, .. } => format!("{old}|-|-|{text}"),
DiffLine::Added { new, text, .. } => format!("-|{new}|+|{text}"),
})
.collect()
}
#[test]
fn test11110_a_replacement_that_adds_lines_renumbers_everything_after_it() {
let before = "one\nTARGET\nthree\nfour\n";
let after = "one\nA\nB\nC\nthree\nfour\n";
let found = hunks(before, after, 1).expect("diffs");
assert_eq!(
rendered(&found),
vec![
"1|1| |one",
"2|-|-|TARGET",
"-|2|+|A",
"-|3|+|B",
"-|4|+|C",
"3|5| |three",
]
);
}
#[test]
fn test11111_a_replacement_that_removes_lines_is_shown_as_removal() {
let before = "keep\nfirst\nsecond\nthird\nkeep2\n";
let after = "keep\nmerged\nkeep2\n";
let found = hunks(before, after, 1).expect("diffs");
let lines = rendered(&found);
assert!(lines.contains(&"2|-|-|first".to_string()), "{lines:?}");
assert!(lines.contains(&"3|-|-|second".to_string()), "{lines:?}");
assert!(lines.contains(&"4|-|-|third".to_string()), "{lines:?}");
assert!(lines.contains(&"-|2|+|merged".to_string()), "{lines:?}");
assert!(lines.contains(&"5|3| |keep2".to_string()), "{lines:?}");
}
#[test]
fn test11112_a_line_deleted_outright_is_not_paired_with_anything() {
let before = "a\nDROP ME\nb\n";
let after = "a\nb\n";
let lines = rendered(&hunks(before, after, 1).expect("diffs"));
assert!(lines.contains(&"2|-|-|DROP ME".to_string()), "{lines:?}");
assert!(
!lines.iter().any(|l| l.contains("+|")),
"nothing was added: {lines:?}"
);
}
#[test]
fn test11113_only_the_changed_parts_are_shown_with_context_around_them() {
let before: String = (1..=200).map(|n| format!("line {n}\n")).collect();
let after = before.replace("line 100\n", "CHANGED\n");
let found = hunks(&before, &after, 2).expect("diffs");
assert_eq!(found.len(), 1, "one change is one hunk");
assert_eq!(rendered(&found).len(), 2 + 2 + 2);
assert_eq!(found[0].old_start, 98);
assert_eq!(found[0].new_start, 98);
}
#[test]
fn test11114_changes_far_apart_are_separate_hunks_and_near_ones_are_joined() {
let before: String = (1..=60).map(|n| format!("line {n}\n")).collect();
let after = before
.replace("line 10\n", "TEN\n")
.replace("line 12\n", "TWELVE\n")
.replace("line 50\n", "FIFTY\n");
let found = hunks(&before, &after, 2).expect("diffs");
assert_eq!(found.len(), 2, "{:?}", found.iter().map(|h| h.old_start).collect::<Vec<_>>());
assert_eq!(found[0].old_start, 8);
assert_eq!(found[1].old_start, 48);
}
#[test]
fn test11115_a_missing_trailing_newline_is_a_real_change() {
let found = hunks("a\nb\n", "a\nb", 1).expect("diffs");
assert!(!found.is_empty(), "the change is visible");
let added = hunks("a\nb", "a\nb\n", 1).expect("diffs");
assert!(!added.is_empty());
}
#[test]
fn test11116_identical_text_has_no_hunks() {
assert!(hunks("same\ntext\n", "same\ntext\n", 2).expect("diffs").is_empty());
assert!(hunks("", "", 2).expect("diffs").is_empty());
}
#[test]
fn test11117_a_file_replaced_wholesale_is_still_correct() {
let before = "alpha\nbeta\ngamma\n";
let after = "one\ntwo\nthree\nfour\n";
let lines = rendered(&hunks(before, after, 2).expect("diffs"));
assert!(lines.contains(&"1|-|-|alpha".to_string()), "{lines:?}");
assert!(lines.contains(&"3|-|-|gamma".to_string()), "{lines:?}");
assert!(lines.contains(&"-|1|+|one".to_string()), "{lines:?}");
assert!(lines.contains(&"-|4|+|four".to_string()), "{lines:?}");
}
#[test]
fn test11118_an_empty_file_gaining_content_is_all_additions() {
let lines = rendered(&hunks("", "new\nlines\n", 2).expect("diffs"));
assert_eq!(lines, vec!["-|1|+|new", "-|2|+|lines"]);
let emptied = rendered(&hunks("gone\n", "", 2).expect("diffs"));
assert_eq!(emptied, vec!["1|-|-|gone"]);
}
#[test]
fn test11119_the_edit_script_is_shortest_not_merely_correct() {
let before = "a\nb\nc\n";
let after = "a\nCHANGED\nc\n";
let lines = rendered(&hunks(before, after, 0).expect("diffs"));
assert_eq!(lines, vec!["2|-|-|b", "-|2|+|CHANGED"]);
}
#[test]
fn test11120_a_diff_beyond_the_limit_is_refused_and_says_so() {
let before: String = (0..MAX_EDITS).map(|n| format!("old {n}\n")).collect();
let after: String = (0..MAX_EDITS).map(|n| format!("new {n}\n")).collect();
let refused = hunks(&before, &after, 2).expect_err("past the limit");
assert!(refused.contains("still applies"), "actionable: {refused}");
}