diff options
Diffstat (limited to 'vendor/dissimilar/src/tests.rs')
-rw-r--r-- | vendor/dissimilar/src/tests.rs | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/vendor/dissimilar/src/tests.rs b/vendor/dissimilar/src/tests.rs new file mode 100644 index 000000000..450d7f7e4 --- /dev/null +++ b/vendor/dissimilar/src/tests.rs @@ -0,0 +1,580 @@ +use super::*; + +macro_rules! diff_list { + () => { + Solution { + text1: Range::empty(), + text2: Range::empty(), + diffs: Vec::new(), + utf8: true, + } + }; + ($($kind:ident($text:literal)),+ $(,)?) => {{ + macro_rules! text1 { + (Insert, $s:literal) => { "" }; + (Delete, $s:literal) => { $s }; + (Equal, $s:literal) => { $s }; + } + macro_rules! text2 { + (Insert, $s:literal) => { $s }; + (Delete, $s:literal) => { "" }; + (Equal, $s:literal) => { $s }; + } + let text1 = concat!($(text1!($kind, $text)),*); + let text2 = concat!($(text2!($kind, $text)),*); + let (_i, _j) = (&mut 0, &mut 0); + macro_rules! range { + (Insert, $s:literal) => { + Diff::Insert(range(text2, _j, $s)) + }; + (Delete, $s:literal) => { + Diff::Delete(range(text1, _i, $s)) + }; + (Equal, $s:literal) => { + Diff::Equal(range(text1, _i, $s), range(text2, _j, $s)) + }; + } + Solution { + text1: Range::new(text1, ..), + text2: Range::new(text2, ..), + diffs: vec![$(range!($kind, $text)),*], + utf8: true, + } + }}; +} + +fn range<'a>(doc: &'a str, offset: &mut usize, text: &str) -> Range<'a> { + let range = Range { + doc, + offset: *offset, + len: text.len(), + }; + *offset += text.len(); + range +} + +macro_rules! assert_diffs { + ([$($kind:ident($text:literal)),* $(,)?], $solution:ident, $msg:expr $(,)?) => { + let expected = &[$(Chunk::$kind($text)),*]; + assert!( + same_diffs(expected, &$solution.diffs), + concat!($msg, "\nexpected={:#?}\nactual={:#?}"), + expected, $solution.diffs, + ); + }; +} + +fn same_diffs(expected: &[Chunk], actual: &[Diff]) -> bool { + expected.len() == actual.len() + && expected.iter().zip(actual).all(|pair| match pair { + (Chunk::Insert(expected), Diff::Insert(actual)) => *expected == str(*actual), + (Chunk::Delete(expected), Diff::Delete(actual)) => *expected == str(*actual), + (Chunk::Equal(expected), Diff::Equal(actual1, actual2)) => { + *expected == str(*actual1) && *expected == str(*actual2) + } + (_, _) => false, + }) +} + +#[test] +fn test_common_prefix() { + let text1 = Range::new("abc", ..); + let text2 = Range::new("xyz", ..); + assert_eq!(0, common_prefix_bytes(text1, text2), "Null case"); + + let text1 = Range::new("1234abcdef", ..); + let text2 = Range::new("1234xyz", ..); + assert_eq!(4, common_prefix_bytes(text1, text2), "Non-null case"); + + let text1 = Range::new("1234", ..); + let text2 = Range::new("1234xyz", ..); + assert_eq!(4, common_prefix_bytes(text1, text2), "Whole case"); +} + +#[test] +fn test_common_suffix() { + let text1 = Range::new("abc", ..); + let text2 = Range::new("xyz", ..); + assert_eq!(0, common_suffix(text1, text2), "Null case"); + assert_eq!(0, common_suffix_bytes(text1, text2), "Null case"); + + let text1 = Range::new("abcdef1234", ..); + let text2 = Range::new("xyz1234", ..); + assert_eq!(4, common_suffix(text1, text2), "Non-null case"); + assert_eq!(4, common_suffix_bytes(text1, text2), "Non-null case"); + + let text1 = Range::new("1234", ..); + let text2 = Range::new("xyz1234", ..); + assert_eq!(4, common_suffix(text1, text2), "Whole case"); + assert_eq!(4, common_suffix_bytes(text1, text2), "Whole case"); +} + +#[test] +fn test_common_overlap() { + let text1 = Range::empty(); + let text2 = Range::new("abcd", ..); + assert_eq!(0, common_overlap(text1, text2), "Null case"); + + let text1 = Range::new("abc", ..); + let text2 = Range::new("abcd", ..); + assert_eq!(3, common_overlap(text1, text2), "Whole case"); + + let text1 = Range::new("123456", ..); + let text2 = Range::new("abcd", ..); + assert_eq!(0, common_overlap(text1, text2), "No overlap"); + + let text1 = Range::new("123456xxx", ..); + let text2 = Range::new("xxxabcd", ..); + assert_eq!(3, common_overlap(text1, text2), "Overlap"); + + // Some overly clever languages (C#) may treat ligatures as equal to their + // component letters. E.g. U+FB01 == 'fi' + let text1 = Range::new("fi", ..); + let text2 = Range::new("\u{fb01}i", ..); + assert_eq!(0, common_overlap(text1, text2), "Unicode"); +} + +#[test] +fn test_cleanup_merge() { + let mut solution = diff_list![]; + cleanup_merge(&mut solution); + assert_diffs!([], solution, "Null case"); + + let mut solution = diff_list![Equal("a"), Delete("b"), Insert("c")]; + cleanup_merge(&mut solution); + assert_diffs!( + [Equal("a"), Delete("b"), Insert("c")], + solution, + "No change case", + ); + + let mut solution = diff_list![Equal("a"), Equal("b"), Equal("c")]; + cleanup_merge(&mut solution); + assert_diffs!([Equal("abc")], solution, "Merge equalities"); + + let mut solution = diff_list![Delete("a"), Delete("b"), Delete("c")]; + cleanup_merge(&mut solution); + assert_diffs!([Delete("abc")], solution, "Merge deletions"); + + let mut solution = diff_list![Insert("a"), Insert("b"), Insert("c")]; + cleanup_merge(&mut solution); + assert_diffs!([Insert("abc")], solution, "Merge insertions"); + + let mut solution = diff_list![ + Delete("a"), + Insert("b"), + Delete("c"), + Insert("d"), + Equal("e"), + Equal("f"), + ]; + cleanup_merge(&mut solution); + assert_diffs!( + [Delete("ac"), Insert("bd"), Equal("ef")], + solution, + "Merge interweave", + ); + + let mut solution = diff_list![Delete("a"), Insert("abc"), Delete("dc")]; + cleanup_merge(&mut solution); + assert_diffs!( + [Equal("a"), Delete("d"), Insert("b"), Equal("c")], + solution, + "Prefix and suffix detection", + ); + + let mut solution = diff_list![ + Equal("x"), + Delete("a"), + Insert("abc"), + Delete("dc"), + Equal("y"), + ]; + cleanup_merge(&mut solution); + assert_diffs!( + [Equal("xa"), Delete("d"), Insert("b"), Equal("cy")], + solution, + "Prefix and suffix detection with equalities", + ); + + let mut solution = diff_list![Equal("a"), Insert("ba"), Equal("c")]; + cleanup_merge(&mut solution); + assert_diffs!([Insert("ab"), Equal("ac")], solution, "Slide edit left"); + + let mut solution = diff_list![Equal("c"), Insert("ab"), Equal("a")]; + cleanup_merge(&mut solution); + assert_diffs!([Equal("ca"), Insert("ba")], solution, "Slide edit right"); + + let mut solution = diff_list![ + Equal("a"), + Delete("b"), + Equal("c"), + Delete("ac"), + Equal("x"), + ]; + cleanup_merge(&mut solution); + assert_diffs!( + [Delete("abc"), Equal("acx")], + solution, + "Slide edit left recursive", + ); + + let mut solution = diff_list![ + Equal("x"), + Delete("ca"), + Equal("c"), + Delete("b"), + Equal("a"), + ]; + cleanup_merge(&mut solution); + assert_diffs!( + [Equal("xca"), Delete("cba")], + solution, + "Slide edit right recursive", + ); + + let mut solution = diff_list![Delete("b"), Insert("ab"), Equal("c")]; + cleanup_merge(&mut solution); + assert_diffs!([Insert("a"), Equal("bc")], solution, "Empty range"); + + let mut solution = diff_list![Equal(""), Insert("a"), Equal("b")]; + cleanup_merge(&mut solution); + assert_diffs!([Insert("a"), Equal("b")], solution, "Empty equality"); +} + +#[test] +fn test_cleanup_semantic_lossless() { + let mut solution = diff_list![]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!([], solution, "Null case"); + + let mut solution = diff_list![ + Equal("AAA\r\n\r\nBBB"), + Insert("\r\nDDD\r\n\r\nBBB"), + Equal("\r\nEEE"), + ]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!( + [ + Equal("AAA\r\n\r\n"), + Insert("BBB\r\nDDD\r\n\r\n"), + Equal("BBB\r\nEEE"), + ], + solution, + "Blank lines", + ); + + let mut solution = diff_list![Equal("AAA\r\nBBB"), Insert(" DDD\r\nBBB"), Equal(" EEE")]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!( + [Equal("AAA\r\n"), Insert("BBB DDD\r\n"), Equal("BBB EEE")], + solution, + "Line boundaries", + ); + + let mut solution = diff_list![Equal("The c"), Insert("ow and the c"), Equal("at.")]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!( + [Equal("The "), Insert("cow and the "), Equal("cat.")], + solution, + "Word boundaries", + ); + + let mut solution = diff_list![Equal("The-c"), Insert("ow-and-the-c"), Equal("at.")]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!( + [Equal("The-"), Insert("cow-and-the-"), Equal("cat.")], + solution, + "Alphanumeric boundaries", + ); + + let mut solution = diff_list![Equal("a"), Delete("a"), Equal("ax")]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!([Delete("a"), Equal("aax")], solution, "Hitting the start"); + + let mut solution = diff_list![Equal("xa"), Delete("a"), Equal("a")]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!([Equal("xaa"), Delete("a")], solution, "Hitting the end"); + + let mut solution = diff_list![Equal("The xxx. The "), Insert("zzz. The "), Equal("yyy.")]; + cleanup_semantic_lossless(&mut solution); + assert_diffs!( + [Equal("The xxx."), Insert(" The zzz."), Equal(" The yyy.")], + solution, + "Sentence boundaries", + ); +} + +#[test] +fn test_cleanup_semantic() { + let mut solution = diff_list![]; + cleanup_semantic(&mut solution); + assert_diffs!([], solution, "Null case"); + + let mut solution = diff_list![Delete("ab"), Insert("cd"), Equal("12"), Delete("e")]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Delete("ab"), Insert("cd"), Equal("12"), Delete("e")], + solution, + "No elimination #1", + ); + + let mut solution = diff_list![Delete("abc"), Insert("ABC"), Equal("1234"), Delete("wxyz")]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Delete("abc"), Insert("ABC"), Equal("1234"), Delete("wxyz")], + solution, + "No elimination #2", + ); + + let mut solution = diff_list![Delete("a"), Equal("b"), Delete("c")]; + cleanup_semantic(&mut solution); + assert_diffs!([Delete("abc"), Insert("b")], solution, "Simple elimination",); + + let mut solution = diff_list![ + Delete("ab"), + Equal("cd"), + Delete("e"), + Equal("f"), + Insert("g"), + ]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Delete("abcdef"), Insert("cdfg")], + solution, + "Backpass elimination", + ); + + let mut solution = diff_list![ + Insert("1"), + Equal("A"), + Delete("B"), + Insert("2"), + Equal("_"), + Insert("1"), + Equal("A"), + Delete("B"), + Insert("2"), + ]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Delete("AB_AB"), Insert("1A2_1A2")], + solution, + "Multiple elimination", + ); + + let mut solution = diff_list![Equal("The c"), Delete("ow and the c"), Equal("at.")]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Equal("The "), Delete("cow and the "), Equal("cat.")], + solution, + "Word boundaries", + ); + + let mut solution = diff_list![Delete("abcxx"), Insert("xxdef")]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Delete("abcxx"), Insert("xxdef")], + solution, + "No overlap elimination", + ); + + let mut solution = diff_list![Delete("abcxxx"), Insert("xxxdef")]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Delete("abc"), Equal("xxx"), Insert("def")], + solution, + "Overlap elimination", + ); + + let mut solution = diff_list![Delete("xxxabc"), Insert("defxxx")]; + cleanup_semantic(&mut solution); + assert_diffs!( + [Insert("def"), Equal("xxx"), Delete("abc")], + solution, + "Reverse overlap elimination", + ); + + let mut solution = diff_list![ + Delete("abcd1212"), + Insert("1212efghi"), + Equal("----"), + Delete("A3"), + Insert("3BC"), + ]; + cleanup_semantic(&mut solution); + assert_diffs!( + [ + Delete("abcd"), + Equal("1212"), + Insert("efghi"), + Equal("----"), + Delete("A"), + Equal("3"), + Insert("BC"), + ], + solution, + "Two overlap eliminations", + ); +} + +#[test] +fn test_bisect() { + let text1 = Range::new("cat", ..); + let text2 = Range::new("map", ..); + let solution = Solution { + text1, + text2, + diffs: bisect(text1, text2), + utf8: false, + }; + assert_diffs!( + [ + Delete("c"), + Insert("m"), + Equal("a"), + Delete("t"), + Insert("p"), + ], + solution, + "Normal", + ); +} + +#[test] +fn test_main() { + let solution = main(Range::empty(), Range::empty()); + assert_diffs!([], solution, "Null case"); + + let solution = main(Range::new("abc", ..), Range::new("abc", ..)); + assert_diffs!([Equal("abc")], solution, "Equality"); + + let solution = main(Range::new("abc", ..), Range::new("ab123c", ..)); + assert_diffs!( + [Equal("ab"), Insert("123"), Equal("c")], + solution, + "Simple insertion", + ); + + let solution = main(Range::new("a123bc", ..), Range::new("abc", ..)); + assert_diffs!( + [Equal("a"), Delete("123"), Equal("bc")], + solution, + "Simple deletion", + ); + + let solution = main(Range::new("abc", ..), Range::new("a123b456c", ..)); + assert_diffs!( + [ + Equal("a"), + Insert("123"), + Equal("b"), + Insert("456"), + Equal("c"), + ], + solution, + "Two insertions", + ); + + let solution = main(Range::new("a123b456c", ..), Range::new("abc", ..)); + assert_diffs!( + [ + Equal("a"), + Delete("123"), + Equal("b"), + Delete("456"), + Equal("c"), + ], + solution, + "Two deletions", + ); + + let solution = main(Range::new("a", ..), Range::new("b", ..)); + assert_diffs!([Delete("a"), Insert("b")], solution, "Simple case #1"); + + let solution = main( + Range::new("Apples are a fruit.", ..), + Range::new("Bananas are also fruit.", ..), + ); + assert_diffs!( + [ + Delete("Apple"), + Insert("Banana"), + Equal("s are a"), + Insert("lso"), + Equal(" fruit."), + ], + solution, + "Simple case #2", + ); + + let solution = main(Range::new("ax\t", ..), Range::new("\u{0680}x\000", ..)); + assert_diffs!( + [ + Delete("a"), + Insert("\u{0680}"), + Equal("x"), + Delete("\t"), + Insert("\000"), + ], + solution, + "Simple case #3", + ); + + let solution = main(Range::new("1ayb2", ..), Range::new("abxab", ..)); + assert_diffs!( + [ + Delete("1"), + Equal("a"), + Delete("y"), + Equal("b"), + Delete("2"), + Insert("xab"), + ], + solution, + "Overlap #1", + ); + + let solution = main(Range::new("abcy", ..), Range::new("xaxcxabc", ..)); + assert_diffs!( + [Insert("xaxcx"), Equal("abc"), Delete("y")], + solution, + "Overlap #2", + ); + + let solution = main( + Range::new("ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", ..), + Range::new("a-bcd-efghijklmnopqrs", ..), + ); + assert_diffs!( + [ + Delete("ABCD"), + Equal("a"), + Delete("="), + Insert("-"), + Equal("bcd"), + Delete("="), + Insert("-"), + Equal("efghijklmnopqrs"), + Delete("EFGHIJKLMNOefg"), + ], + solution, + "Overlap #3", + ); + + let solution = main( + Range::new("a [[Pennsylvania]] and [[New", ..), + Range::new(" and [[Pennsylvania]]", ..), + ); + assert_diffs!( + [ + Insert(" "), + Equal("a"), + Insert("nd"), + Equal(" [[Pennsylvania]]"), + Delete(" and [[New"), + ], + solution, + "Large equality", + ); +} |