use std::time::Instant;
use diff_match_patch_rs::dmp::Diff;
use diff_match_patch_rs::html::HtmlConfig;
use diff_match_patch_rs::{Compat, DiffMatchPatch, Efficient, Error, Ops, PatchInput};
#[test]
fn test_diff_levenshtein() {
let dmp = DiffMatchPatch::new();
let diffs = vec![
Diff::delete(b"abc"),
Diff::insert(b"1234"),
Diff::equal(b"xyz"),
];
assert_eq!(4, dmp.diff_levenshtein(&diffs));
let diffs = vec![
Diff::equal(b"xyz"),
Diff::delete(b"abc"),
Diff::insert(b"1234"),
];
assert_eq!(4, dmp.diff_levenshtein(&diffs));
let diffs = vec![
Diff::delete(b"abc"),
Diff::equal(b"xyz"),
Diff::insert(b"1234"),
];
assert_eq!(7, dmp.diff_levenshtein(&diffs));
}
#[test]
fn test_diff_bisect() -> Result<(), Error> {
let mut dmp = DiffMatchPatch::default();
// Normal.
// Since the resulting diff hasn't been normalized, it would be ok if
// the insertion and deletion pairs are swapped.
// If the order changes, tweak this test as required.
assert_eq!(
vec![
Diff::delete(b"c"),
Diff::insert(b"m"),
Diff::equal(b"a"),
Diff::delete(b"t"),
Diff::insert(b"p")
],
dmp.bisect(b"cat", b"map", None)?
);
// Timeout.
dmp.set_timeout(Some(0));
let deadline = dmp.deadline();
assert_eq!(
vec![Diff::delete(b"cat"), Diff::insert(b"map"),],
dmp.bisect(b"cat", b"map", deadline)?
);
Ok(())
}
#[test]
fn test_diff_pretty_html() -> Result<(), Error> {
let dmp = DiffMatchPatch::new();
let mut html_cfg = HtmlConfig::new();
html_cfg.set_insert_style(Some("background:#e6ffe6;"));
html_cfg.set_delete_style(Some("background:#ffe6e6;"));
// Basic
let diffs = [
Diff::equal(b"a\n"),
Diff::delete(b"b"),
Diff::insert(b"c&d"),
];
assert_eq!("a
<B>b</B>c&d", dmp.diff_pretty_html(&diffs, &html_cfg)?);
// Monkey busiess around Emoticons and extended utf-8 π€ͺπ€©π€
// This gave me a lot of heart-burn
// Case 1. Two similar emoticons
// In bytes representation, these would have the last u8 different
// 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)?;
assert_eq!(
"π€ͺπ€",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"I'm puzzledπ€ͺπ€ or am I?thinking I guess!",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"ππ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"π, aAah orange !πis the new black!π",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"π π ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"π π ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"ππ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"Now, let's explore some emotional extremeLet's start with some basics ππ.
We've got your ecstaticndard smiley face π€©π, your devastatesad face πβΉοΈ, and your utterly confusedangry face π€―π . But that's not allwait, there's more! π€π€© We've also got some subtmore complex emotions like ππ, π€€, and π. And let's not forget about the classics: π, ππ, and ππ.",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// Compat mode
// Basic
let diffs = [
Diff::equal(&['a', '\n']),
Diff::delete(&"b".chars().collect::>()[..]),
Diff::insert(&"c&d".chars().collect::>()[..]),
];
assert_eq!("a
<B>b</B>c&d", dmp.diff_pretty_html(&diffs, &html_cfg)?);
// `Compat` mode shouldn't require monkey business atall
// Case 1. Two similar emoticons
// In bytes representation, these would have the last u8 different
// 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)?;
assert_eq!(
"π€ͺπ€",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"I'm puzzledπ€ͺπ€ or am I?thinking I guess!",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"ππ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"π, aAah orange !πis the new black!π",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"π π ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"π π ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"ππ",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
// 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)?;
assert_eq!(
"Now, let's explore some emotional extremeLet's start with some basics ππ.
We've got your ecstaticndard smiley face π€©π, your devastatesad face πβΉοΈ, and your utterly confusedangry face π€―π . But that's not allwait, there's more! π€π€© We've also got some subtmore complex emotions like ππ, π€€, and π. And let's not forget about the classics: π, ππ, and ππ.",
dmp.diff_pretty_html(&diffs, &html_cfg)?
);
Ok(())
}
#[test]
fn test_diff_main() -> Result<(), Error> {
let mut dmp = DiffMatchPatch::default();
// Perform a trivial diff.
// Null case.
assert!(dmp.diff_main::("", "")?.is_empty());
// Equality
assert_eq!(
vec![Diff::equal(b"abc")],
dmp.diff_main::("abc", "abc")?
);
// Simple insert
assert_eq!(
vec![Diff::equal(b"ab"), Diff::insert(b"123"), Diff::equal(b"c")],
dmp.diff_main::("abc", "ab123c")?
);
// Simple delete
assert_eq!(
vec![Diff::equal(b"a"), Diff::delete(b"123"), Diff::equal(b"bc")],
dmp.diff_main::("a123bc", "abc")?
);
// Two insertions
assert_eq!(
vec![
Diff::equal(b"a"),
Diff::insert(b"123"),
Diff::equal(b"b"),
Diff::insert(b"456"),
Diff::equal(b"c"),
],
dmp.diff_main::("abc", "a123b456c")?
);
// Two deletions.
assert_eq!(
vec![
Diff::equal(b"a"),
Diff::delete(b"123"),
Diff::equal(b"b"),
Diff::delete(b"456"),
Diff::equal(b"c"),
],
dmp.diff_main::("a123b456c", "abc")?
);
// Perform a real diff.
// Switch off the timeout.
dmp.set_timeout(None);
// Simple cases.
assert_eq!(
vec![Diff::delete(b"a"), Diff::insert(b"b"),],
dmp.diff_main::("a", "b")?
);
assert_eq!(
vec![
Diff::delete(b"Apple"),
Diff::insert(b"Banana"),
Diff::equal(b"s are a"),
Diff::insert(b"lso"),
Diff::equal(b" fruit.")
],
dmp.diff_main::("Apples are a fruit.", "Bananas are also fruit.")?
);
assert_eq!(
vec![
Diff::delete(b"a"),
Diff::insert("\u{0680}".as_bytes()),
Diff::equal(b"x"),
Diff::delete(b"\t"),
Diff::insert(b"\0")
],
dmp.diff_main::("ax\t", "\u{0680}x\0")?
);
// Overlaps.
assert_eq!(
vec![
Diff::delete(b"1"),
Diff::equal(b"a"),
Diff::delete(b"y"),
Diff::equal(b"b"),
Diff::delete(b"2"),
Diff::insert(b"xab"),
],
dmp.diff_main::("1ayb2", "abxab")?
);
assert_eq!(
vec![
Diff::insert(b"xaxcx"),
Diff::equal(b"abc"),
Diff::delete(b"y"),
],
dmp.diff_main::("abcy", "xaxcxabc")?
);
assert_eq!(
vec![
Diff::delete(b"ABCD"),
Diff::equal(b"a"),
Diff::delete(b"="),
Diff::insert(b"-"),
Diff::equal(b"bcd"),
Diff::delete(b"="),
Diff::insert(b"-"),
Diff::equal(b"efghijklmnopqrs"),
Diff::delete(b"EFGHIJKLMNOefg"),
],
dmp.diff_main::(
"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg",
"a-bcd-efghijklmnopqrs"
)?
);
// Large equality.
assert_eq!(
vec![
Diff::insert(b" "),
Diff::equal(b"a"),
Diff::insert(b"nd"),
Diff::equal(b" [[Hepatopancreatic]]"),
Diff::delete(b" and [[New"),
],
dmp.diff_main::(
"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 = Instant::now();
dmp.diff_main::(&a, &b)?;
let end = Instant::now();
// Test that we took at least the timeout period (+ 5ms being generous).
assert!((end - start).as_millis() <= LOW_TIMEOUT as u128 + 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::(a, b)?;
dmp.set_checklines(true);
let res_yes_lm = dmp.diff_main::(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::(a, b)?;
dmp.set_checklines(true);
let no_lm = dmp.diff_main::(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)?;
dmp.set_checklines(true);
let yes_lm = dmp.diff_main::(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::(&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 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::("", "")?.is_empty());
// Equality
assert_eq!(
vec![Diff::equal(&"abc".chars().collect::>()[..])],
dmp.diff_main::("abc", "abc")?
);
// Simple insert
assert_eq!(
vec![
Diff::equal(&"ab".chars().collect::>()[..]),
Diff::insert(&"123".chars().collect::>()[..]),
Diff::equal(&['c'])
],
dmp.diff_main::("abc", "ab123c")?
);
// Simple delete
assert_eq!(
vec![
Diff::equal(&['a']),
Diff::delete(&"123".chars().collect::>()[..]),
Diff::equal(&['b', 'c'])
],
dmp.diff_main::("a123bc", "abc")?
);
// Two insertions
assert_eq!(
vec![
Diff::equal(&['a']),
Diff::insert(&"123".chars().collect::>()[..]),
Diff::equal(&['b']),
Diff::insert(&['4', '5', '6']),
Diff::equal(&['c']),
],
dmp.diff_main::("abc", "a123b456c")?
);
// Two deletions.
assert_eq!(
vec![
Diff::equal(&['a']),
Diff::delete(&"123".chars().collect::>()[..]),
Diff::equal(&['b']),
Diff::delete(&"456".chars().collect::>()[..]),
Diff::equal(&['c']),
],
dmp.diff_main::("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::("a", "b")?
);
assert_eq!(
vec![
Diff::delete(&"Apple".chars().collect::>()[..]),
Diff::insert(&"Banana".chars().collect::>()[..]),
Diff::equal(&"s are a".chars().collect::>()[..]),
Diff::insert(&"lso".chars().collect::>()[..]),
Diff::equal(&" fruit.".chars().collect::>()[..])
],
dmp.diff_main::("Apples are a fruit.", "Bananas are also fruit.")?
);
assert_eq!(
vec![
Diff::delete(&['a']),
Diff::insert(&"\u{0680}".chars().collect::>()[..]),
Diff::equal(&['x']),
Diff::delete(&['\t']),
Diff::insert(&['\0'])
],
dmp.diff_main::("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::>()[..]),
],
dmp.diff_main::("1ayb2", "abxab")?
);
assert_eq!(
vec![
Diff::insert(&"xaxcx".chars().collect::>()[..]),
Diff::equal(&"abc".chars().collect::>()[..]),
Diff::delete(&['y']),
],
dmp.diff_main::("abcy", "xaxcxabc")?
);
assert_eq!(
vec![
Diff::delete(&"ABCD".chars().collect::>()[..]),
Diff::equal(&"a".chars().collect::>()[..]),
Diff::delete(&"=".chars().collect::>()[..]),
Diff::insert(&"-".chars().collect::>()[..]),
Diff::equal(&"bcd".chars().collect::>()[..]),
Diff::delete(&"=".chars().collect::>()[..]),
Diff::insert(&"-".chars().collect::>()[..]),
Diff::equal(&"efghijklmnopqrs".chars().collect::>()[..]),
Diff::delete(&"EFGHIJKLMNOefg".chars().collect::>()[..]),
],
dmp.diff_main::(
"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::>()[..]),
],
dmp.diff_main::(
"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 = Instant::now();
dmp.diff_main::(&a, &b)?;
let end = Instant::now();
// Test that we took at least the timeout period (+ 5ms being generous).
assert!((end - start).as_millis() <= LOW_TIMEOUT as u128 + 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::(a, b)?;
dmp.set_checklines(true);
let res_yes_lm = dmp.diff_main::(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::(a, b)?;
dmp.set_checklines(true);
let no_lm = dmp.diff_main::(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)?;
dmp.set_checklines(true);
let yes_lm = dmp.diff_main::(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::(&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 no_lm_dur = Instant::now() - start;
assert!(diff_no_lm.is_ok());
assert!(no_lm_dur > yes_lm_dur);
Ok(())
}
#[test]
fn test_diff_delta() -> Result<(), Error> {
let dmp = DiffMatchPatch::new();
let diffs = vec![
Diff::equal(b"jump"),
Diff::delete(b"s"),
Diff::insert(b"ed"),
Diff::equal(b" over "),
Diff::delete(b"the"),
Diff::insert(b"a"),
Diff::equal(b" lazy"),
Diff::insert(b"old dog"),
];
let txt_old = "jumps over the lazy";
assert_eq!(txt_old.as_bytes(), DiffMatchPatch::diff_text_old(&diffs));
let delta = dmp.diff_to_delta(&diffs)?;
assert_eq!("=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", &delta);
// Convert delta string into a diff.
assert_eq!(diffs, dmp.diff_from_delta(txt_old, &delta)?);
// Generates error (19 != 20).
assert!(dmp
.diff_from_delta::(&[txt_old, "+"].concat()[..], &delta)
.is_err());
// Generates error (19 != 18).
assert!(dmp
.diff_from_delta::(&txt_old[1..], &delta)
.is_err());
let diffs = vec![
Diff::equal(&"jump".chars().collect::>()[..]),
Diff::delete(&['s']),
Diff::insert(&['e', 'd']),
Diff::equal(&" over ".chars().collect::>()[..]),
Diff::delete(&"the".chars().collect::>()[..]),
Diff::insert(&['a']),
Diff::equal(&" lazy".chars().collect::>()[..]),
Diff::insert(&"old dog".chars().collect::>()[..]),
];
// let txt_old = &"jumps over the lazy".chars().collect::>()[..];
assert_eq!(
&txt_old.chars().collect::>()[..],
DiffMatchPatch::diff_text_old(&diffs)
);
let delta = dmp.diff_to_delta(&diffs)?;
assert_eq!("=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", &delta);
// Convert delta string into a diff.
assert_eq!(diffs, dmp.diff_from_delta(txt_old, &delta)?);
// Generates error (19 != 20).
assert!(dmp
.diff_from_delta::(&[txt_old, "+"].concat()[..], &delta)
.is_err());
// Generates error (19 != 18).
assert!(dmp
.diff_from_delta::(&txt_old[1..], &delta)
.is_err());
// Test deltas with special characters.
let diffs = vec![
Diff::equal("\u{0680} \x00 \t %".as_bytes()),
Diff::delete("\u{0681} \x01 \n ^".as_bytes()),
Diff::insert("\u{0682} \x02 \\ |".as_bytes()),
];
let txt_old = DiffMatchPatch::diff_text_old(&diffs);
assert_eq!("\u{0680} \x00 \t %\u{0681} \x01 \n ^".as_bytes(), txt_old);
let delta = dmp.diff_to_delta(&diffs)?;
assert_eq!("=8\t-8\t+%DA%82 %02 %5C %7C", &delta[..]);
// Convert delta string into a diff.
assert_eq!(
&diffs,
&dmp.diff_from_delta(std::str::from_utf8(&txt_old).unwrap(), &delta)?
);
let diffs = vec![
Diff::equal(&"\u{0680} \x00 \t %".chars().collect::>()[..]),
Diff::delete(&"\u{0681} \x01 \n ^".chars().collect::>()[..]),
Diff::insert(&"\u{0682} \x02 \\ |".chars().collect::>()[..]),
];
let txt_old = DiffMatchPatch::diff_text_old(&diffs);
assert_eq!(
&"\u{0680} \x00 \t %\u{0681} \x01 \n ^"
.chars()
.collect::>()[..],
txt_old
);
let delta = dmp.diff_to_delta(&diffs)?;
assert_eq!("=7\t-7\t+%DA%82 %02 %5C %7C", &delta);
// Convert delta string into a diff.
assert_eq!(
&diffs,
&dmp.diff_from_delta(&txt_old.iter().collect::(), &delta)?
);
// Verify pool of unchanged characters.
let diffs = vec![Diff::insert(
"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ".as_bytes(),
)];
let txt_new = DiffMatchPatch::diff_text_new(&diffs);
assert_eq!(
"A-Z a-z 0-9 - _ . ! ~ * \' ( ) ; / ? : @ & = + $ , # ",
std::str::from_utf8(&txt_new).unwrap()
);
let diffs = vec![Diff::insert(
&"A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # "
.chars()
.collect::>()[..],
)];
let txt_new = DiffMatchPatch::diff_text_new(&diffs);
assert_eq!(
"A-Z a-z 0-9 - _ . ! ~ * \' ( ) ; / ? : @ & = + $ , # ",
&txt_new.iter().collect::()
);
let delta = dmp.diff_to_delta(&diffs)?;
assert_eq!(
"+A-Z a-z 0-9 - _ . ! ~ * \' ( ) ; / ? : @ & = + $ , # ",
&delta
);
// Convert delta string into a diff.
assert_eq!(diffs, dmp.diff_from_delta("", &delta)?);
Ok(())
}
// Helper to construct the two texts which made up the diff originally.
fn rebuild_text(diffs: &[Diff]) -> Result<(String, String), Error> {
let mut txt1 = vec![];
let mut txt2 = vec![];
diffs.iter().for_each(|d| {
if d.op() != Ops::Insert {
txt1.push(d.data());
}
if d.op() != Ops::Delete {
txt2.push(d.data());
}
});
Ok((
String::from_utf8(txt1.concat()).map_err(|_| Error::Utf8Error)?,
String::from_utf8(txt2.concat()).map_err(|_| Error::Utf8Error)?,
))
}
#[test]
fn test_patch_from_text() -> Result<(), Error> {
let dmp = DiffMatchPatch::new();
assert!(dmp.patch_from_text::("")?.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!(
"@@ -1 +1 @@\n-a\n+b\n",
dmp.patch_from_text::("@@ -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()
);
assert_eq!(
"@@ -0,0 +1,3 @@\n+abc\n",
dmp.patch_from_text::("@@ -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::("")?.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!(
"@@ -1 +1 @@\n-a\n+b\n",
dmp.patch_from_text::("@@ -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()
);
assert_eq!(
"@@ -0,0 +1,3 @@\n+abc\n",
dmp.patch_from_text::("@@ -0,0 +1,3 @@\n+abc\n")?[0].to_string()
);
// Generates error.
assert!(dmp.patch_from_text::("Bad\nPatch\n").is_err());
Ok(())
}
#[test]
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)?;
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)?;
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::(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)?;
assert_eq!(strp, dmp.patch_to_text(&patches));
Ok(())
}
#[test]
fn test_patch_make() -> Result<(), Error> {
let dmp = DiffMatchPatch::default();
let patches = dmp.patch_make(PatchInput::Texts::("", ""))?;
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))?;
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))?;
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 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::(
"`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::(&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::("", ""))?;
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))?;
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))?;
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 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::(
"`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::(&txt1, &txt2))?;
assert_eq!(
"@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n",
dmp.patch_to_text(&patches)
);
Ok(())
}
#[test]
fn test_diff_text() {
let diffs = vec![
Diff::equal(b"jump"),
Diff::delete(b"s"),
Diff::insert(b"ed"),
Diff::equal(b" over "),
Diff::delete(b"the"),
Diff::insert(b"a"),
Diff::equal(b" lazy"),
];
assert_eq!(
b"jumps over the lazy",
&DiffMatchPatch::diff_text_old(&diffs[..])[..]
);
assert_eq!(
b"jumped over a lazy",
&DiffMatchPatch::diff_text_new(&diffs[..])[..]
);
}
#[test]
fn test_patch_apply() -> Result<(), Error> {
let mut dmp = DiffMatchPatch::default();
let patches = dmp.patch_make(PatchInput::Texts::("", ""))?;
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::(
"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::(
"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::(
"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::(
"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::(
"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::("", "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::(
"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::("", "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"))?;
assert_eq!(
("XtestY".to_string(), vec![true]),
dmp.patch_apply(&patches, "XY")?
);
// Edge partial match
let patches = dmp.patch_make(PatchInput::Texts::("y", "y123"))?;
assert_eq!(
("x123".to_string(), vec![true]),
dmp.patch_apply(&patches, "x")?
);
let patches = dmp.patch_make(PatchInput::Texts::("", ""))?;
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::(
"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::(
"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::(
"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::(
"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::(
"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::("", "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::(
"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::("", "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"))?;
assert_eq!(
("XtestY".to_string(), vec![true]),
dmp.patch_apply(&patches, "XY")?
);
// Edge partial match
let patches = dmp.patch_make(PatchInput::Texts::("y", "y123"))?;
assert_eq!(
("x123".to_string(), vec![true]),
dmp.patch_apply(&patches, "x")?
);
let dmp = DiffMatchPatch::new();
// Tests for issue https://github.com/AnubhabB/diff-match-patch-rs/issues/2
let strp = "@@ -1,11 +1,5 @@\n-%F0%9F%8D%8A, a\n+A\n ah o\n@@ -3,17 +3,21 @@\n h orange\n- \n+!%F0%9F%8C%8A\n is the n\n@@ -23,10 +23,8 @@\n new \n-black!\n+%F0%9F%8C%8A\n";
let patches = dmp.patch_from_text::(strp)?;
assert_eq!(strp, dmp.patch_to_text(&patches));
let (patched, _) = dmp.patch_apply(&patches, "π, aah orange is the new black!")?;
assert_eq!(patched, "Aah orange!πis the new π");
Ok(())
}
#[test]
fn test_match_main() {
let dmp = DiffMatchPatch::default();
// Full match.
// Shortcut matches.
assert_eq!(
Some(0),
dmp.match_main::("abcdef", "abcdef", 1000)
);
assert_eq!(None, dmp.match_main::("", "abcdef", 1));
assert_eq!(Some(3), dmp.match_main::("abcdef", "", 3));
assert_eq!(Some(3), dmp.match_main::("abcdef", "de", 3));
// Beyond end match.
assert_eq!(Some(3), dmp.match_main::("abcdef", "defy", 4));
// Oversized pattern.
assert_eq!(Some(0), dmp.match_main::("abcdef", "abcdefy", 0));
// Complex match.
assert_eq!(
Some(4),
dmp.match_main::(
"I am the very model of a modern major general.",
" that berry ",
5
)
);
assert_eq!(Some(0), dmp.match_main::("abcdef", "abcdef", 1000));
assert_eq!(None, dmp.match_main::("", "abcdef", 1));
assert_eq!(Some(3), dmp.match_main::("abcdef", "", 3));
assert_eq!(Some(3), dmp.match_main::("abcdef", "de", 3));
// Beyond end match.
assert_eq!(Some(3), dmp.match_main::("abcdef", "defy", 4));
// Oversized pattern.
assert_eq!(Some(0), dmp.match_main::("abcdef", "abcdefy", 0));
// Complex match.
assert_eq!(
Some(4),
dmp.match_main::(
"I am the very model of a modern major general.",
" that berry ",
5
)
);
}