my fork of dmp
Diffstat (limited to 'tests/test.rs')
| -rw-r--r-- | tests/test.rs | 547 |
1 files changed, 491 insertions, 56 deletions
diff --git a/tests/test.rs b/tests/test.rs index fd00a4e..bc8262b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -4,7 +4,7 @@ use chrono::Utc; use diff_match_patch_rs::dmp::Diff; -use diff_match_patch_rs::{DiffMatchPatch, Error, Ops, PatchInput}; +use diff_match_patch_rs::{Compat, DiffMatchPatch, Efficient, Error, Ops, PatchInput}; // const tests = [ // 'testDiffIsDestructurable', @@ -85,7 +85,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Which means the the diff should an equality block of 3 bytes folloed by insert and delete let old = "π€ͺ"; // [240, 159, 164, 170] let new = "π€"; // [240, 159, 164, 148] - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<span></span><del style=\"background:#ffe6e6;\">π€ͺ</del><ins style=\"background:#e6ffe6;\">π€</ins>", dmp.diff_pretty_html(&diffs)? @@ -94,7 +94,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Now Case 1. but with some text before and after let old = "I'm puzzledπ€ͺ or am I?"; let new = "I'm puzzledπ€ or thinking I guess!"; - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<span>I'm puzzled</span><del style=\"background:#ffe6e6;\">π€ͺ</del><ins style=\"background:#e6ffe6;\">π€</ins><span> or </span><del style=\"background:#ffe6e6;\">am I?</del><ins style=\"background:#e6ffe6;\">thinking I guess!</ins>", dmp.diff_pretty_html(&diffs)? @@ -103,7 +103,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Case 2. Emoticons with the third position different let old = "π"; // [240, 159, 141, 138] let new = "π"; // [240, 159, 140, 138] - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<span></span><del style=\"background:#ffe6e6;\">π</del><ins style=\"background:#e6ffe6;\">π</ins>", dmp.diff_pretty_html(&diffs)? @@ -112,7 +112,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Now Case 2. but with some text, lets complicate this let old = "π, aah orange is the new black!"; // [240, 159, 141, 138] let new = "Aah orange!πis the new π"; // [240, 159, 140, 138] - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<del style=\"background:#ffe6e6;\">π, a</del><ins style=\"background:#e6ffe6;\">A</ins><span>ah orange</span><del style=\"background:#ffe6e6;\"> </del><ins style=\"background:#e6ffe6;\">!π</ins><span>is the new </span><del style=\"background:#ffe6e6;\">black!</del><ins style=\"background:#e6ffe6;\">π</ins>", dmp.diff_pretty_html(&diffs)? @@ -121,7 +121,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Case 3. with second and third different, but lets complicate this with an equality let old = "π "; // [240, 160, 140, 138] let new = "π "; // [240, 150, 160, 138] - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<span></span><ins style=\"background:#e6ffe6;\">π </ins><del style=\"background:#ffe6e6;\">π </del>", dmp.diff_pretty_html(&diffs)? @@ -130,7 +130,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Case 3. but let there be a swap let old = "π "; // [240, 158, 160, 132] let new = std::str::from_utf8(&[240, 160, 158, 132]).unwrap(); // basically an undefined element `π `. Should still work - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<span></span><del style=\"background:#ffe6e6;\">π </del><ins style=\"background:#e6ffe6;\">π </ins>", dmp.diff_pretty_html(&diffs)? @@ -139,7 +139,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Case 4. swap at the last 2 positions let old = "π"; // [240, 159, 141, 140] -- FINALLY A BANANA let new = "π"; // [240, 159, 140, 141] -- interesting revelation - last 2 bytes swapped and π becomes π. Guess the world is going `Bananas!!` - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<span></span><del style=\"background:#ffe6e6;\">π</del><ins style=\"background:#e6ffe6;\">π</ins>", dmp.diff_pretty_html(&diffs)? @@ -148,7 +148,7 @@ fn test_diff_pretty_html() -> Result<(), Error> { // Let's do this with a slightly longish string let old = "Now, let's explore some emotional extremes π.\nWe've got your ecstatic face π€©, your devastated face π, and your utterly confused face π€―. But that's not all! π€ We've also got some subtle emotions like π, π, and π."; let new = "Let's start with some basics π.\nWe've got your standard smiley face π, your sad face βΉοΈ, and your angry face π . But wait, there's more! π€© We've also got some more complex emotions like π, π€€, and π. And let's not forget about the classics: π, π, and π."; - let diffs = dmp.diff_main(old, new)?; + let diffs = dmp.diff_main::<Efficient>(old, new)?; assert_eq!( "<del style=\"background:#ffe6e6;\">Now, let's explore some emotional extreme</del><ins style=\"background:#e6ffe6;\">Let's start with some basic</ins><span>s </span><del style=\"background:#ffe6e6;\">π</del><ins style=\"background:#e6ffe6;\">π</ins><span>.¶<br>We've got your </span><del style=\"background:#ffe6e6;\">ec</del><span>sta</span><del style=\"background:#ffe6e6;\">tic</del><ins style=\"background:#e6ffe6;\">ndard smiley</ins><span> face </span><del style=\"background:#ffe6e6;\">π€©</del><ins style=\"background:#e6ffe6;\">π</ins><span>, your </span><del style=\"background:#ffe6e6;\">devastate</del><ins style=\"background:#e6ffe6;\">sa</ins><span>d face </span><del style=\"background:#ffe6e6;\">π</del><ins style=\"background:#e6ffe6;\">βΉοΈ</ins><span>, and your </span><del style=\"background:#ffe6e6;\">utterly confused</del><ins style=\"background:#e6ffe6;\">angry</ins><span> face </span><del style=\"background:#ffe6e6;\">π€―</del><ins style=\"background:#e6ffe6;\">π </ins><span>. But </span><del style=\"background:#ffe6e6;\">that's not all</del><ins style=\"background:#e6ffe6;\">wait, there's more</ins><span>! </span><del style=\"background:#ffe6e6;\">π€</del><ins style=\"background:#e6ffe6;\">π€©</ins><span> We've also got some </span><del style=\"background:#ffe6e6;\">subt</del><ins style=\"background:#e6ffe6;\">more comp</ins><span>le</span><ins style=\"background:#e6ffe6;\">x</ins><span> emotions like </span><del style=\"background:#ffe6e6;\">π</del><ins style=\"background:#e6ffe6;\">π, π€€, and π. And let's not forget about the classics: π</ins><span>, </span><del style=\"background:#ffe6e6;\">π</del><ins style=\"background:#e6ffe6;\">π</ins><span>, and </span><del style=\"background:#ffe6e6;\">π</del><ins style=\"background:#e6ffe6;\">π</ins><span>.</span>", @@ -164,21 +164,24 @@ fn test_diff_main() -> Result<(), Error> { // Perform a trivial diff. // Null case. - assert!(dmp.diff_main("", "")?.is_empty()); + assert!(dmp.diff_main::<Efficient>("", "")?.is_empty()); // Equality - assert_eq!(vec![Diff::equal(b"abc")], dmp.diff_main("abc", "abc")?); + assert_eq!( + vec![Diff::equal(b"abc")], + dmp.diff_main::<Efficient>("abc", "abc")? + ); // Simple insert assert_eq!( vec![Diff::equal(b"ab"), Diff::insert(b"123"), Diff::equal(b"c")], - dmp.diff_main("abc", "ab123c")? + dmp.diff_main::<Efficient>("abc", "ab123c")? ); // Simple delete assert_eq!( vec![Diff::equal(b"a"), Diff::delete(b"123"), Diff::equal(b"bc")], - dmp.diff_main("a123bc", "abc")? + dmp.diff_main::<Efficient>("a123bc", "abc")? ); // Two insertions @@ -190,7 +193,7 @@ fn test_diff_main() -> Result<(), Error> { Diff::insert(b"456"), Diff::equal(b"c"), ], - dmp.diff_main("abc", "a123b456c")? + dmp.diff_main::<Efficient>("abc", "a123b456c")? ); // Two deletions. @@ -202,7 +205,7 @@ fn test_diff_main() -> Result<(), Error> { Diff::delete(b"456"), Diff::equal(b"c"), ], - dmp.diff_main("a123b456c", "abc")? + dmp.diff_main::<Efficient>("a123b456c", "abc")? ); // Perform a real diff. @@ -211,7 +214,7 @@ fn test_diff_main() -> Result<(), Error> { // Simple cases. assert_eq!( vec![Diff::delete(b"a"), Diff::insert(b"b"),], - dmp.diff_main("a", "b")? + dmp.diff_main::<Efficient>("a", "b")? ); assert_eq!( @@ -222,7 +225,7 @@ fn test_diff_main() -> Result<(), Error> { Diff::insert(b"lso"), Diff::equal(b" fruit.") ], - dmp.diff_main("Apples are a fruit.", "Bananas are also fruit.")? + dmp.diff_main::<Efficient>("Apples are a fruit.", "Bananas are also fruit.")? ); assert_eq!( @@ -233,7 +236,7 @@ fn test_diff_main() -> Result<(), Error> { Diff::delete(b"\t"), Diff::insert(b"\0") ], - dmp.diff_main("ax\t", "\u{0680}x\0")? + dmp.diff_main::<Efficient>("ax\t", "\u{0680}x\0")? ); // Overlaps. @@ -246,7 +249,7 @@ fn test_diff_main() -> Result<(), Error> { Diff::delete(b"2"), Diff::insert(b"xab"), ], - dmp.diff_main("1ayb2", "abxab")? + dmp.diff_main::<Efficient>("1ayb2", "abxab")? ); assert_eq!( @@ -255,7 +258,7 @@ fn test_diff_main() -> Result<(), Error> { Diff::equal(b"abc"), Diff::delete(b"y"), ], - dmp.diff_main("abcy", "xaxcxabc")? + dmp.diff_main::<Efficient>("abcy", "xaxcxabc")? ); assert_eq!( @@ -270,7 +273,7 @@ fn test_diff_main() -> Result<(), Error> { Diff::equal(b"efghijklmnopqrs"), Diff::delete(b"EFGHIJKLMNOefg"), ], - dmp.diff_main( + dmp.diff_main::<Efficient>( "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", "a-bcd-efghijklmnopqrs" )? @@ -285,7 +288,219 @@ fn test_diff_main() -> Result<(), Error> { Diff::equal(b" [[Hepatopancreatic]]"), Diff::delete(b" and [[New"), ], - dmp.diff_main( + dmp.diff_main::<Efficient>( + "a [[Hepatopancreatic]] and [[New", + " and [[Hepatopancreatic]]" + )? + ); + + // Timeout. + const LOW_TIMEOUT: u32 = 100; + dmp.set_timeout(Some(LOW_TIMEOUT)); + let a = vec!["`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n"; 2048].join(""); + let b = vec!["I am the very model of a modern major general,\nI\'ve information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n"; 2048].join(""); + + let start = Utc::now().time(); + dmp.diff_main::<Efficient>(&a, &b)?; + let end = Utc::now().time(); + // Test that we took at least the timeout period (+ 5ms being generous). + assert!((end - start).num_milliseconds() <= LOW_TIMEOUT as i64 + 5); + + // Test the linemode speedup. + // Must be long to pass the 100 char cutoff. + // Simple line-mode. + dmp.set_timeout(Some(1000)); + let a = "12345678901234567890123456789 0123456 78901234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; + let b = "abcdefghij abcdefghij abcdefghij abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n"; + dmp.set_checklines(false); + let res_no_lm = dmp.diff_main::<Efficient>(a, b)?; + dmp.set_checklines(true); + let res_yes_lm = dmp.diff_main::<Efficient>(a, b)?; + + // Now, we'll run 2 checks - one for result equality + assert_eq!(res_no_lm, res_yes_lm); + + // Single line-mode. + let a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + let b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"; + dmp.set_checklines(false); + let yes_lm = dmp.diff_main::<Efficient>(a, b)?; + dmp.set_checklines(true); + let no_lm = dmp.diff_main::<Efficient>(a, b)?; + assert_eq!(no_lm, yes_lm); + + // Overlap line-mode. + let a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; + let b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n"; + dmp.set_checklines(false); + let no_lm = dmp.diff_main::<Efficient>(a, b)?; + dmp.set_checklines(true); + let yes_lm = dmp.diff_main::<Efficient>(a, b)?; + assert_eq!(rebuild_text(&yes_lm[..])?, rebuild_text(&no_lm[..])?); + + // Benefits of checklines can only be realized in text with many lines + let mut dmp = DiffMatchPatch::default(); + let old = std::fs::read_to_string("testdata/txt_old.txt").unwrap(); + let new = std::fs::read_to_string("testdata/txt_new.txt").unwrap(); + + let start = Instant::now(); + let diff_yes_lm = dmp.diff_main::<Efficient>(&old, &new); + let yes_lm_dur = Instant::now() - start; + assert!(diff_yes_lm.is_ok()); + + dmp.set_checklines(false); + let start = Instant::now(); + let diff_no_lm = dmp.diff_main::<Efficient>(&old, &new); + let no_lm_dur = Instant::now() - start; + assert!(diff_no_lm.is_ok()); + + assert!(no_lm_dur > yes_lm_dur); + + Ok(()) +} + +#[test] +fn test_diff_main_compat() -> Result<(), Error> { + let mut dmp = DiffMatchPatch::default(); + + // Perform a trivial diff. + // Null case. + assert!(dmp.diff_main::<Compat>("", "")?.is_empty()); + + // Equality + assert_eq!( + vec![Diff::equal(&"abc".chars().collect::<Vec<_>>()[..])], + dmp.diff_main::<Compat>("abc", "abc")? + ); + + // Simple insert + assert_eq!( + vec![ + Diff::equal(&"ab".chars().collect::<Vec<_>>()[..]), + Diff::insert(&"123".chars().collect::<Vec<_>>()[..]), + Diff::equal(&['c']) + ], + dmp.diff_main::<Compat>("abc", "ab123c")? + ); + + // Simple delete + assert_eq!( + vec![ + Diff::equal(&['a']), + Diff::delete(&"123".chars().collect::<Vec<_>>()[..]), + Diff::equal(&['b', 'c']) + ], + dmp.diff_main::<Compat>("a123bc", "abc")? + ); + + // Two insertions + assert_eq!( + vec![ + Diff::equal(&['a']), + Diff::insert(&"123".chars().collect::<Vec<_>>()[..]), + Diff::equal(&['b']), + Diff::insert(&['4', '5', '6']), + Diff::equal(&['c']), + ], + dmp.diff_main::<Compat>("abc", "a123b456c")? + ); + + // Two deletions. + assert_eq!( + vec![ + Diff::equal(&['a']), + Diff::delete(&"123".chars().collect::<Vec<_>>()[..]), + Diff::equal(&['b']), + Diff::delete(&"456".chars().collect::<Vec<_>>()[..]), + Diff::equal(&['c']), + ], + dmp.diff_main::<Compat>("a123b456c", "abc")? + ); + + // Perform a real diff. + // Switch off the timeout. + dmp.set_timeout(None); + // Simple cases. + assert_eq!( + vec![Diff::delete(&['a']), Diff::insert(&['b']),], + dmp.diff_main::<Compat>("a", "b")? + ); + + assert_eq!( + vec![ + Diff::delete(&"Apple".chars().collect::<Vec<_>>()[..]), + Diff::insert(&"Banana".chars().collect::<Vec<_>>()[..]), + Diff::equal(&"s are a".chars().collect::<Vec<_>>()[..]), + Diff::insert(&"lso".chars().collect::<Vec<_>>()[..]), + Diff::equal(&" fruit.".chars().collect::<Vec<_>>()[..]) + ], + dmp.diff_main::<Compat>("Apples are a fruit.", "Bananas are also fruit.")? + ); + + assert_eq!( + vec![ + Diff::delete(&['a']), + Diff::insert(&"\u{0680}".chars().collect::<Vec<_>>()[..]), + Diff::equal(&['x']), + Diff::delete(&['\t']), + Diff::insert(&['\0']) + ], + dmp.diff_main::<Compat>("ax\t", "\u{0680}x\0")? + ); + + // Overlaps. + assert_eq!( + vec![ + Diff::delete(&['1']), + Diff::equal(&['a']), + Diff::delete(&['y']), + Diff::equal(&['b']), + Diff::delete(&['2']), + Diff::insert(&"xab".chars().collect::<Vec<_>>()[..]), + ], + dmp.diff_main::<Compat>("1ayb2", "abxab")? + ); + + assert_eq!( + vec![ + Diff::insert(&"xaxcx".chars().collect::<Vec<_>>()[..]), + Diff::equal(&"abc".chars().collect::<Vec<_>>()[..]), + Diff::delete(&['y']), + ], + dmp.diff_main::<Compat>("abcy", "xaxcxabc")? + ); + + assert_eq!( + vec![ + Diff::delete(&"ABCD".chars().collect::<Vec<_>>()[..]), + Diff::equal(&"a".chars().collect::<Vec<_>>()[..]), + Diff::delete(&"=".chars().collect::<Vec<_>>()[..]), + Diff::insert(&"-".chars().collect::<Vec<_>>()[..]), + Diff::equal(&"bcd".chars().collect::<Vec<_>>()[..]), + Diff::delete(&"=".chars().collect::<Vec<_>>()[..]), + Diff::insert(&"-".chars().collect::<Vec<_>>()[..]), + Diff::equal(&"efghijklmnopqrs".chars().collect::<Vec<_>>()[..]), + Diff::delete(&"EFGHIJKLMNOefg".chars().collect::<Vec<_>>()[..]), + ], + dmp.diff_main::<Compat>( + "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", + "a-bcd-efghijklmnopqrs" + )? + ); + + // Large equality. + assert_eq!( + vec![ + Diff::insert(&[' ']), + Diff::equal(&['a']), + Diff::insert(&['n', 'd']), + Diff::equal(&[ + ' ', '[', '[', 'H', 'e', 'p', 'a', 't', 'o', 'p', 'a', 'n', 'c', 'r', 'e', 'a', + 't', 'i', 'c', ']', ']' + ]), + Diff::delete(&" and [[New".chars().collect::<Vec<_>>()[..]), + ], + dmp.diff_main::<Compat>( "a [[Hepatopancreatic]] and [[New", " and [[Hepatopancreatic]]" )? @@ -298,7 +513,7 @@ fn test_diff_main() -> Result<(), Error> { let b = vec!["I am the very model of a modern major general,\nI\'ve information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n"; 2048].join(""); let start = Utc::now().time(); - dmp.diff_main(&a, &b)?; + dmp.diff_main::<Efficient>(&a, &b)?; let end = Utc::now().time(); // Test that we took at least the timeout period (+ 5ms being generous). assert!((end - start).num_milliseconds() <= LOW_TIMEOUT as i64 + 5); @@ -310,9 +525,9 @@ fn test_diff_main() -> Result<(), Error> { let a = "12345678901234567890123456789 0123456 78901234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; let b = "abcdefghij abcdefghij abcdefghij abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n"; dmp.set_checklines(false); - let res_no_lm = dmp.diff_main(a, b)?; + let res_no_lm = dmp.diff_main::<Efficient>(a, b)?; dmp.set_checklines(true); - let res_yes_lm = dmp.diff_main(a, b)?; + let res_yes_lm = dmp.diff_main::<Efficient>(a, b)?; // Now, we'll run 2 checks - one for result equality assert_eq!(res_no_lm, res_yes_lm); @@ -321,18 +536,18 @@ fn test_diff_main() -> Result<(), Error> { let a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; let b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"; dmp.set_checklines(false); - let yes_lm = dmp.diff_main(a, b)?; + let yes_lm = dmp.diff_main::<Efficient>(a, b)?; dmp.set_checklines(true); - let no_lm = dmp.diff_main(a, b)?; + let no_lm = dmp.diff_main::<Efficient>(a, b)?; assert_eq!(no_lm, yes_lm); // Overlap line-mode. let a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n"; let b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n"; dmp.set_checklines(false); - let no_lm = dmp.diff_main(a, b)?; + let no_lm = dmp.diff_main::<Efficient>(a, b)?; dmp.set_checklines(true); - let yes_lm = dmp.diff_main(a, b)?; + let yes_lm = dmp.diff_main::<Efficient>(a, b)?; assert_eq!(rebuild_text(&yes_lm[..])?, rebuild_text(&no_lm[..])?); // Benefits of checklines can only be realized in text with many lines @@ -341,13 +556,13 @@ fn test_diff_main() -> Result<(), Error> { let new = std::fs::read_to_string("testdata/txt_new.txt").unwrap(); let start = Instant::now(); - let diff_yes_lm = dmp.diff_main(&old, &new); + let diff_yes_lm = dmp.diff_main::<Efficient>(&old, &new); let yes_lm_dur = Instant::now() - start; assert!(diff_yes_lm.is_ok()); dmp.set_checklines(false); let start = Instant::now(); - let diff_no_lm = dmp.diff_main(&old, &new); + let diff_no_lm = dmp.diff_main::<Efficient>(&old, &new); let no_lm_dur = Instant::now() - start; assert!(diff_no_lm.is_ok()); @@ -442,28 +657,51 @@ fn rebuild_text(diffs: &[Diff<u8>]) -> Result<(String, String), Error> { fn test_patch_from_text() -> Result<(), Error> { let dmp = DiffMatchPatch::new(); - assert!(dmp.patch_from_text("")?.is_empty()); + assert!(dmp.patch_from_text::<Efficient>("")?.is_empty()); let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n"; - assert_eq!(strp, dmp.patch_from_text(strp)?[0].to_string()); + assert_eq!(strp, dmp.patch_from_text::<Efficient>(strp)?[0].to_string()); assert_eq!( "@@ -1 +1 @@\n-a\n+b\n", - dmp.patch_from_text("@@ -1 +1 @@\n-a\n+b\n")?[0].to_string() + dmp.patch_from_text::<Efficient>("@@ -1 +1 @@\n-a\n+b\n")?[0].to_string() ); assert_eq!( "@@ -1,3 +0,0 @@\n-abc\n", - dmp.patch_from_text("@@ -1,3 +0,0 @@\n-abc\n")?[0].to_string() + dmp.patch_from_text::<Efficient>("@@ -1,3 +0,0 @@\n-abc\n")?[0].to_string() ); assert_eq!( "@@ -0,0 +1,3 @@\n+abc\n", - dmp.patch_from_text("@@ -0,0 +1,3 @@\n+abc\n")?[0].to_string() + dmp.patch_from_text::<Efficient>("@@ -0,0 +1,3 @@\n+abc\n")?[0].to_string() ); // Generates error. - assert!(dmp.patch_from_text("Bad\nPatch\n").is_err()); + assert!(dmp.patch_from_text::<Efficient>("Bad\nPatch\n").is_err()); + + assert!(dmp.patch_from_text::<Compat>("")?.is_empty()); + + let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n"; + assert_eq!(strp, dmp.patch_from_text::<Compat>(strp)?[0].to_string()); + + assert_eq!( + "@@ -1 +1 @@\n-a\n+b\n", + dmp.patch_from_text::<Compat>("@@ -1 +1 @@\n-a\n+b\n")?[0].to_string() + ); + + assert_eq!( + "@@ -1,3 +0,0 @@\n-abc\n", + dmp.patch_from_text::<Compat>("@@ -1,3 +0,0 @@\n-abc\n")?[0].to_string() + ); + + assert_eq!( + "@@ -0,0 +1,3 @@\n+abc\n", + dmp.patch_from_text::<Compat>("@@ -0,0 +1,3 @@\n+abc\n")?[0].to_string() + ); + + // Generates error. + assert!(dmp.patch_from_text::<Compat>("Bad\nPatch\n").is_err()); Ok(()) } @@ -473,11 +711,19 @@ fn test_patch_to_text() -> Result<(), Error> { let dmp = DiffMatchPatch::new(); let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"; - let patches = dmp.patch_from_text(strp)?; + let patches = dmp.patch_from_text::<Efficient>(strp)?; + assert_eq!(strp, dmp.patch_to_text(&patches)); + + let strp = "@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n"; + let patches = dmp.patch_from_text::<Efficient>(strp)?; + assert_eq!(strp, dmp.patch_to_text(&patches)); + + let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"; + let patches = dmp.patch_from_text::<Compat>(strp)?; assert_eq!(strp, dmp.patch_to_text(&patches)); let strp = "@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n"; - let patches = dmp.patch_from_text(strp)?; + let patches = dmp.patch_from_text::<Compat>(strp)?; assert_eq!(strp, dmp.patch_to_text(&patches)); Ok(()) @@ -486,22 +732,75 @@ fn test_patch_to_text() -> Result<(), Error> { #[test] fn test_patch_make() -> Result<(), Error> { let dmp = DiffMatchPatch::default(); - let patches = dmp.patch_make(PatchInput::Texts("", ""))?; + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", ""))?; + assert!(patches.is_empty()); + + let txt1 = "The quick brown fox jumps over the lazy dog."; + let txt2 = "That quick brown fox jumped over a lazy dog."; + + // The second patch must be "-21,17 +21,18", not "-22,17 +21,18" due to rolling context. + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(txt2, txt1))?; + assert_eq!("@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n", dmp.patch_to_text(&patches)); + + // Text1+Text2 inputs. + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(txt1, txt2))?; + assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches)); + + // Diff input. + let diffs = dmp.diff_main::<Efficient>(txt1, txt2)?; + let patches = dmp.patch_make(PatchInput::Diffs(&diffs[..]))?; + assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches)); + + // Text1+Diff inputs. + let patches = dmp.patch_make(PatchInput::TextDiffs(txt1, &diffs[..]))?; + assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches)); + + // Character encoding. + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>( + "`1234567890-=[]\\;',./", + "~!@#$%^&*()_+{}|:\"<>?", + ))?; + + assert_eq!( + "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n", + dmp.patch_to_text(&patches) + ); + + // Character decoding. + let diffs = vec![ + Diff::delete(b"`1234567890-=[]\\;',./"), + Diff::insert(b"~!@#$%^&*()_+{}|:\"<>?"), + ]; + assert_eq!( + diffs, + dmp.patch_from_text("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n")?[0].diffs() + ); + + // Long string with repeats. + let txt1 = vec!["abcdef"; 100].join(""); + let txt2 = [&txt1, "123"].join(""); + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(&txt1, &txt2))?; + assert_eq!( + "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n", + dmp.patch_to_text(&patches) + ); + + let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", ""))?; assert!(patches.is_empty()); let txt1 = "The quick brown fox jumps over the lazy dog."; let txt2 = "That quick brown fox jumped over a lazy dog."; // The second patch must be "-21,17 +21,18", not "-22,17 +21,18" due to rolling context. - let patches = dmp.patch_make(PatchInput::Texts(txt2, txt1))?; + let patches = dmp.patch_make(PatchInput::Texts::<Compat>(txt2, txt1))?; assert_eq!("@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n", dmp.patch_to_text(&patches)); // Text1+Text2 inputs. - let patches = dmp.patch_make(PatchInput::Texts(txt1, txt2))?; + let patches = dmp.patch_make(PatchInput::Texts::<Compat>(txt1, txt2))?; assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches)); // Diff input. - let diffs = dmp.diff_main(txt1, txt2)?; + let diffs = dmp.diff_main::<Efficient>(txt1, txt2)?; let patches = dmp.patch_make(PatchInput::Diffs(&diffs[..]))?; assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches)); @@ -510,7 +809,7 @@ fn test_patch_make() -> Result<(), Error> { assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches)); // Character encoding. - let patches = dmp.patch_make(PatchInput::Texts( + let patches = dmp.patch_make(PatchInput::Texts::<Compat>( "`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?", ))?; @@ -533,7 +832,7 @@ fn test_patch_make() -> Result<(), Error> { // Long string with repeats. let txt1 = vec!["abcdef"; 100].join(""); let txt2 = [&txt1, "123"].join(""); - let patches = dmp.patch_make(PatchInput::Texts(&txt1, &txt2))?; + let patches = dmp.patch_make(PatchInput::Texts::<Compat>(&txt1, &txt2))?; assert_eq!( "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n", dmp.patch_to_text(&patches) @@ -568,11 +867,147 @@ fn test_diff_text() { fn test_patch_apply() -> Result<(), Error> { let mut dmp = DiffMatchPatch::default(); - let patches = dmp.patch_make(PatchInput::Texts("", ""))?; + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", ""))?; + let (txt, results) = dmp.patch_apply(&patches, "Hello world.")?; + assert_eq!(format!("{}\t{}", txt, results.len()), "Hello world.\t0"); + + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>( + "The quick brown fox jumps over the lazy dog.", + "That quick brown fox jumped over a lazy dog.", + ))?; + + // Exact match + assert_eq!( + ( + "That quick brown fox jumped over a lazy dog.".to_string(), + vec![true, true] + ), + dmp.patch_apply(&patches, "The quick brown fox jumps over the lazy dog.")? + ); + + // Partial match + assert_eq!( + ( + "That quick red rabbit jumped over a tired tiger.".to_string(), + vec![true, true] + ), + dmp.patch_apply(&patches, "The quick red rabbit jumps over the tired tiger.")? + ); + + // Failed match + assert_eq!( + ( + "I am the very model of a modern major general.".to_string(), + vec![false, false] + ), + dmp.patch_apply(&patches, "I am the very model of a modern major general.")? + ); + + // Big delete, small change + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>( + "x1234567890123456789012345678901234567890123456789012345678901234567890y", + "xabcy", + ))?; + assert_eq!( + ("xabcy".to_string(), vec![true, true]), + dmp.patch_apply( + &patches, + "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y" + )? + ); + + // Big delete, large change + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>( + "x1234567890123456789012345678901234567890123456789012345678901234567890y", + "xabcy", + ))?; + assert_eq!( + ( + "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y" + .to_string(), + vec![false, true] + ), + dmp.patch_apply( + &patches, + "x12345678901234567890---------------++++++++++---------------12345678901234567890y" + )? + ); + + dmp.set_delete_threshold(0.6); + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>( + "x1234567890123456789012345678901234567890123456789012345678901234567890y", + "xabcy", + ))?; + assert_eq!( + ("xabcy".to_string(), vec![true, true]), + dmp.patch_apply( + &patches, + "x12345678901234567890---------------++++++++++---------------12345678901234567890y" + )? + ); + dmp.set_delete_threshold(0.5); + + // Compesate for failed patch + dmp.set_match_threshold(0.); + dmp.set_match_distance(0); + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>( + "abcdefghijklmnopqrstuvwxyz--------------------1234567890", + "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", + ))?; + assert_eq!( + ( + "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890".to_string(), + vec![false, true] + ), + dmp.patch_apply( + &patches, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890" + )? + ); + + dmp.set_match_threshold(0.5); + dmp.set_match_distance(1000); + + // No side-effects - kinds useless cos patches is not mutable in rust + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", "test"))?; + let srcstr = dmp.patch_to_text(&patches); + dmp.patch_apply(&patches, "")?; + assert_eq!(srcstr, dmp.patch_to_text(&patches)); + + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>( + "The quick brown fox jumps over the lazy dog.", + "Woof", + ))?; + let srcstr = dmp.patch_to_text(&patches); + dmp.patch_apply(&patches, "The quick brown fox jumps over the lazy dog.")?; + assert_eq!(srcstr, dmp.patch_to_text(&patches)); + + // Edge exact match + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", "test"))?; + assert_eq!( + ("test".to_string(), vec![true]), + dmp.patch_apply(&patches, "")? + ); + + // Near edge exact match + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("XY", "XtestY"))?; + assert_eq!( + ("XtestY".to_string(), vec![true]), + dmp.patch_apply(&patches, "XY")? + ); + + // Edge partial match + let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("y", "y123"))?; + assert_eq!( + ("x123".to_string(), vec![true]), + dmp.patch_apply(&patches, "x")? + ); + + let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", ""))?; let (txt, results) = dmp.patch_apply(&patches, "Hello world.")?; assert_eq!(format!("{}\t{}", txt, results.len()), "Hello world.\t0"); - let patches = dmp.patch_make(PatchInput::Texts( + let patches = dmp.patch_make(PatchInput::Texts::<Compat>( "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", ))?; @@ -605,7 +1040,7 @@ fn test_patch_apply() -> Result<(), Error> { ); // Big delete, small change - let patches = dmp.patch_make(PatchInput::Texts( + let patches = dmp.patch_make(PatchInput::Texts::<Compat>( "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", ))?; @@ -618,7 +1053,7 @@ fn test_patch_apply() -> Result<(), Error> { ); // Big delete, large change - let patches = dmp.patch_make(PatchInput::Texts( + let patches = dmp.patch_make(PatchInput::Texts::<Compat>( "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", ))?; @@ -635,7 +1070,7 @@ fn test_patch_apply() -> Result<(), Error> { ); dmp.set_delete_threshold(0.6); - let patches = dmp.patch_make(PatchInput::Texts( + let patches = dmp.patch_make(PatchInput::Texts::<Compat>( "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", ))?; @@ -651,7 +1086,7 @@ fn test_patch_apply() -> Result<(), Error> { // Compesate for failed patch dmp.set_match_threshold(0.); dmp.set_match_distance(0); - let patches = dmp.patch_make(PatchInput::Texts( + let patches = dmp.patch_make(PatchInput::Texts::<Compat>( "abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", ))?; @@ -670,12 +1105,12 @@ fn test_patch_apply() -> Result<(), Error> { dmp.set_match_distance(1000); // No side-effects - kinds useless cos patches is not mutable in rust - let patches = dmp.patch_make(PatchInput::Texts("", "test"))?; + let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", "test"))?; let srcstr = dmp.patch_to_text(&patches); dmp.patch_apply(&patches, "")?; assert_eq!(srcstr, dmp.patch_to_text(&patches)); - let patches = dmp.patch_make(PatchInput::Texts( + let patches = dmp.patch_make(PatchInput::Texts::<Compat>( "The quick brown fox jumps over the lazy dog.", "Woof", ))?; @@ -684,21 +1119,21 @@ fn test_patch_apply() -> Result<(), Error> { assert_eq!(srcstr, dmp.patch_to_text(&patches)); // Edge exact match - let patches = dmp.patch_make(PatchInput::Texts("", "test"))?; + let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", "test"))?; assert_eq!( ("test".to_string(), vec![true]), dmp.patch_apply(&patches, "")? ); // Near edge exact match - let patches = dmp.patch_make(PatchInput::Texts("XY", "XtestY"))?; + let patches = dmp.patch_make(PatchInput::Texts::<Compat>("XY", "XtestY"))?; assert_eq!( ("XtestY".to_string(), vec![true]), dmp.patch_apply(&patches, "XY")? ); // Edge partial match - let patches = dmp.patch_make(PatchInput::Texts("y", "y123"))?; + let patches = dmp.patch_make(PatchInput::Texts::<Compat>("y", "y123"))?; assert_eq!( ("x123".to_string(), vec![true]), dmp.patch_apply(&patches, "x")? |