| // Copyright (c) 2012 Sergi Mansilla <sergi.mansilla@gmail.com> |
| // https://github.com/sergi/go-diff |
| // See the included LICENSE file for license details. |
| // |
| // go-diff is a Go implementation of Google's Diff, Match, and Patch library |
| // Original library is Copyright (c) 2006 Google Inc. |
| // http://code.google.com/p/google-diff-match-patch/ |
| |
| package diffmatchpatch |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io/ioutil" |
| "runtime" |
| "strconv" |
| "strings" |
| "testing" |
| "time" |
| "unicode/utf8" |
| |
| "github.com/stretchr/testify/assert" |
| ) |
| |
| func caller() string { |
| _, _, line, ok := runtime.Caller(2) |
| if !ok { |
| return "" |
| } |
| |
| return fmt.Sprintf("at line %d ", line) |
| } |
| |
| func pretty(diffs []Diff) string { |
| var w bytes.Buffer |
| |
| for i, diff := range diffs { |
| _, _ = w.WriteString(fmt.Sprintf("%v. ", i)) |
| |
| switch diff.Type { |
| case DiffInsert: |
| _, _ = w.WriteString("DiffIns") |
| case DiffDelete: |
| _, _ = w.WriteString("DiffDel") |
| case DiffEqual: |
| _, _ = w.WriteString("DiffEql") |
| default: |
| _, _ = w.WriteString("Unknown") |
| } |
| |
| _, _ = w.WriteString(fmt.Sprintf(": %v\n", diff.Text)) |
| } |
| |
| return w.String() |
| } |
| |
| func diffRebuildTexts(diffs []Diff) []string { |
| texts := []string{"", ""} |
| |
| for _, d := range diffs { |
| if d.Type != DiffInsert { |
| texts[0] += d.Text |
| } |
| if d.Type != DiffDelete { |
| texts[1] += d.Text |
| } |
| } |
| |
| return texts |
| } |
| |
| func readFile(filepath string) string { |
| data, err := ioutil.ReadFile(filepath) |
| if err != nil { |
| panic(err) |
| } |
| |
| return string(data) |
| } |
| |
| func speedtestTexts() (s1 string, s2 string) { |
| s1 = readFile("../testdata/speedtest1.txt") |
| s2 = readFile("../testdata/speedtest2.txt") |
| |
| return s1, s2 |
| } |
| |
| func TestDiffCommonPrefix(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Text1 string |
| Text2 string |
| |
| Expected int |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"Null", "abc", "xyz", 0}, |
| {"Non-null", "1234abcdef", "1234xyz", 4}, |
| {"Whole", "1234", "1234xyz", 4}, |
| } { |
| actual := dmp.DiffCommonPrefix(tc.Text1, tc.Text2) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestCommonPrefixLength(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| |
| Expected int |
| } |
| |
| for i, tc := range []TestCase{ |
| {"abc", "xyz", 0}, |
| {"1234abcdef", "1234xyz", 4}, |
| {"1234", "1234xyz", 4}, |
| } { |
| actual := commonPrefixLength([]rune(tc.Text1), []rune(tc.Text2)) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestDiffCommonSuffix(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Text1 string |
| Text2 string |
| |
| Expected int |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"Null", "abc", "xyz", 0}, |
| {"Non-null", "abcdef1234", "xyz1234", 4}, |
| {"Whole", "1234", "xyz1234", 4}, |
| } { |
| actual := dmp.DiffCommonSuffix(tc.Text1, tc.Text2) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestCommonSuffixLength(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| |
| Expected int |
| } |
| |
| for i, tc := range []TestCase{ |
| {"abc", "xyz", 0}, |
| {"abcdef1234", "xyz1234", 4}, |
| {"1234", "xyz1234", 4}, |
| {"123", "a3", 1}, |
| } { |
| actual := commonSuffixLength([]rune(tc.Text1), []rune(tc.Text2)) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestRunesIndexOf(t *testing.T) { |
| type TestCase struct { |
| Pattern string |
| Start int |
| |
| Expected int |
| } |
| |
| for i, tc := range []TestCase{ |
| {"abc", 0, 0}, |
| {"cde", 0, 2}, |
| {"e", 0, 4}, |
| {"cdef", 0, -1}, |
| {"abcdef", 0, -1}, |
| {"abc", 2, -1}, |
| {"cde", 2, 2}, |
| {"e", 2, 4}, |
| {"cdef", 2, -1}, |
| {"abcdef", 2, -1}, |
| {"e", 6, -1}, |
| } { |
| actual := runesIndexOf([]rune("abcde"), []rune(tc.Pattern), tc.Start) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestDiffCommonOverlap(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Text1 string |
| Text2 string |
| |
| Expected int |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"Null", "", "abcd", 0}, |
| {"Whole", "abc", "abcd", 3}, |
| {"Null", "123456", "abcd", 0}, |
| {"Null", "123456xxx", "xxxabcd", 3}, |
| // Some overly clever languages (C#) may treat ligatures as equal to their component letters, e.g. U+FB01 == 'fi' |
| {"Unicode", "fi", "\ufb01i", 0}, |
| } { |
| actual := dmp.DiffCommonOverlap(tc.Text1, tc.Text2) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffHalfMatch(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| |
| Expected []string |
| } |
| |
| dmp := New() |
| dmp.DiffTimeout = 1 |
| |
| for i, tc := range []TestCase{ |
| // No match |
| {"1234567890", "abcdef", nil}, |
| {"12345", "23", nil}, |
| |
| // Single Match |
| {"1234567890", "a345678z", []string{"12", "90", "a", "z", "345678"}}, |
| {"a345678z", "1234567890", []string{"a", "z", "12", "90", "345678"}}, |
| {"abc56789z", "1234567890", []string{"abc", "z", "1234", "0", "56789"}}, |
| {"a23456xyz", "1234567890", []string{"a", "xyz", "1", "7890", "23456"}}, |
| |
| // Multiple Matches |
| {"121231234123451234123121", "a1234123451234z", []string{"12123", "123121", "a", "z", "1234123451234"}}, |
| {"x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=", []string{"", "-=-=-=-=-=", "x", "", "x-=-=-=-=-=-=-="}}, |
| {"-=-=-=-=-=-=-=-=-=-=-=-=y", "-=-=-=-=-=-=-=yy", []string{"-=-=-=-=-=", "", "", "y", "-=-=-=-=-=-=-=y"}}, |
| |
| // Non-optimal halfmatch, ptimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy |
| {"qHilloHelloHew", "xHelloHeHulloy", []string{"qHillo", "w", "x", "Hulloy", "HelloHe"}}, |
| } { |
| actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| |
| dmp.DiffTimeout = 0 |
| |
| for i, tc := range []TestCase{ |
| // Optimal no halfmatch |
| {"qHilloHelloHew", "xHelloHeHulloy", nil}, |
| } { |
| actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestDiffBisectSplit(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| } |
| |
| dmp := New() |
| |
| for _, tc := range []TestCase{ |
| {"STUV\x05WX\x05YZ\x05[", "WĺĻļ\x05YZ\x05ĽľĿŀZ"}, |
| } { |
| diffs := dmp.diffBisectSplit([]rune(tc.Text1), |
| []rune(tc.Text2), 7, 6, time.Now().Add(time.Hour)) |
| |
| for _, d := range diffs { |
| assert.True(t, utf8.ValidString(d.Text)) |
| } |
| |
| // TODO define the expected outcome |
| } |
| } |
| |
| func TestDiffLinesToChars(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| |
| ExpectedChars1 string |
| ExpectedChars2 string |
| ExpectedLines []string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"", "alpha\r\nbeta\r\n\r\n\r\n", "", "\u0001\u0002\u0003\u0003", []string{"", "alpha\r\n", "beta\r\n", "\r\n"}}, |
| {"a", "b", "\u0001", "\u0002", []string{"", "a", "b"}}, |
| // Omit final newline. |
| {"alpha\nbeta\nalpha", "", "\u0001\u0002\u0003", "", []string{"", "alpha\n", "beta\n", "alpha"}}, |
| } { |
| actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(tc.Text1, tc.Text2) |
| assert.Equal(t, tc.ExpectedChars1, actualChars1, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| assert.Equal(t, tc.ExpectedChars2, actualChars2, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| assert.Equal(t, tc.ExpectedLines, actualLines, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| |
| // More than 256 to reveal any 8-bit limitations. |
| n := 300 |
| lineList := []string{ |
| "", // Account for the initial empty element of the lines array. |
| } |
| var charList []rune |
| for x := 1; x < n+1; x++ { |
| lineList = append(lineList, strconv.Itoa(x)+"\n") |
| charList = append(charList, rune(x)) |
| } |
| lines := strings.Join(lineList, "") |
| chars := string(charList) |
| assert.Equal(t, n, utf8.RuneCountInString(chars)) |
| |
| actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(lines, "") |
| assert.Equal(t, chars, actualChars1) |
| assert.Equal(t, "", actualChars2) |
| assert.Equal(t, lineList, actualLines) |
| } |
| |
| func TestDiffCharsToLines(t *testing.T) { |
| type TestCase struct { |
| Diffs []Diff |
| Lines []string |
| |
| Expected []Diff |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| Diffs: []Diff{ |
| {DiffEqual, "\u0001\u0002\u0001"}, |
| {DiffInsert, "\u0002\u0001\u0002"}, |
| }, |
| Lines: []string{"", "alpha\n", "beta\n"}, |
| |
| Expected: []Diff{ |
| {DiffEqual, "alpha\nbeta\nalpha\n"}, |
| {DiffInsert, "beta\nalpha\nbeta\n"}, |
| }, |
| }, |
| } { |
| actual := dmp.DiffCharsToLines(tc.Diffs, tc.Lines) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| |
| // More than 256 to reveal any 8-bit limitations. |
| n := 300 |
| lineList := []string{ |
| "", // Account for the initial empty element of the lines array. |
| } |
| charList := []rune{} |
| for x := 1; x <= n; x++ { |
| lineList = append(lineList, strconv.Itoa(x)+"\n") |
| charList = append(charList, rune(x)) |
| } |
| assert.Equal(t, n, len(charList)) |
| |
| actual := dmp.DiffCharsToLines([]Diff{Diff{DiffDelete, string(charList)}}, lineList) |
| assert.Equal(t, []Diff{Diff{DiffDelete, strings.Join(lineList, "")}}, actual) |
| } |
| |
| func TestDiffCleanupMerge(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Diffs []Diff |
| |
| Expected []Diff |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| "Null case", |
| []Diff{}, |
| []Diff{}, |
| }, |
| { |
| "No Diff case", |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, |
| }, |
| { |
| "Merge equalities", |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffEqual, "b"}, Diff{DiffEqual, "c"}}, |
| []Diff{Diff{DiffEqual, "abc"}}, |
| }, |
| { |
| "Merge deletions", |
| []Diff{Diff{DiffDelete, "a"}, Diff{DiffDelete, "b"}, Diff{DiffDelete, "c"}}, |
| []Diff{Diff{DiffDelete, "abc"}}, |
| }, |
| { |
| "Merge insertions", |
| []Diff{Diff{DiffInsert, "a"}, Diff{DiffInsert, "b"}, Diff{DiffInsert, "c"}}, |
| []Diff{Diff{DiffInsert, "abc"}}, |
| }, |
| { |
| "Merge interweave", |
| []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}, Diff{DiffDelete, "c"}, Diff{DiffInsert, "d"}, Diff{DiffEqual, "e"}, Diff{DiffEqual, "f"}}, |
| []Diff{Diff{DiffDelete, "ac"}, Diff{DiffInsert, "bd"}, Diff{DiffEqual, "ef"}}, |
| }, |
| { |
| "Prefix and suffix detection", |
| []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}}, |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "c"}}, |
| }, |
| { |
| "Prefix and suffix detection with equalities", |
| []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}}, |
| []Diff{Diff{DiffEqual, "xa"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, |
| }, |
| { |
| "Same test as above but with unicode (\u0101 will appear in diffs with at least 257 unique lines)", |
| []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "\u0101"}, Diff{DiffInsert, "\u0101bc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}}, |
| []Diff{Diff{DiffEqual, "x\u0101"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, |
| }, |
| { |
| "Slide edit left", |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "ba"}, Diff{DiffEqual, "c"}}, |
| []Diff{Diff{DiffInsert, "ab"}, Diff{DiffEqual, "ac"}}, |
| }, |
| { |
| "Slide edit right", |
| []Diff{Diff{DiffEqual, "c"}, Diff{DiffInsert, "ab"}, Diff{DiffEqual, "a"}}, |
| []Diff{Diff{DiffEqual, "ca"}, Diff{DiffInsert, "ba"}}, |
| }, |
| { |
| "Slide edit left recursive", |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "ac"}, Diff{DiffEqual, "x"}}, |
| []Diff{Diff{DiffDelete, "abc"}, Diff{DiffEqual, "acx"}}, |
| }, |
| { |
| "Slide edit right recursive", |
| []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "ca"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "a"}}, |
| []Diff{Diff{DiffEqual, "xca"}, Diff{DiffDelete, "cba"}}, |
| }, |
| } { |
| actual := dmp.DiffCleanupMerge(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffCleanupSemanticLossless(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Diffs []Diff |
| |
| Expected []Diff |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| "Null case", |
| []Diff{}, |
| []Diff{}, |
| }, |
| { |
| "Blank lines", |
| []Diff{ |
| Diff{DiffEqual, "AAA\r\n\r\nBBB"}, |
| Diff{DiffInsert, "\r\nDDD\r\n\r\nBBB"}, |
| Diff{DiffEqual, "\r\nEEE"}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "AAA\r\n\r\n"}, |
| Diff{DiffInsert, "BBB\r\nDDD\r\n\r\n"}, |
| Diff{DiffEqual, "BBB\r\nEEE"}, |
| }, |
| }, |
| { |
| "Line boundaries", |
| []Diff{ |
| Diff{DiffEqual, "AAA\r\nBBB"}, |
| Diff{DiffInsert, " DDD\r\nBBB"}, |
| Diff{DiffEqual, " EEE"}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "AAA\r\n"}, |
| Diff{DiffInsert, "BBB DDD\r\n"}, |
| Diff{DiffEqual, "BBB EEE"}, |
| }, |
| }, |
| { |
| "Word boundaries", |
| []Diff{ |
| Diff{DiffEqual, "The c"}, |
| Diff{DiffInsert, "ow and the c"}, |
| Diff{DiffEqual, "at."}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "The "}, |
| Diff{DiffInsert, "cow and the "}, |
| Diff{DiffEqual, "cat."}, |
| }, |
| }, |
| { |
| "Alphanumeric boundaries", |
| []Diff{ |
| Diff{DiffEqual, "The-c"}, |
| Diff{DiffInsert, "ow-and-the-c"}, |
| Diff{DiffEqual, "at."}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "The-"}, |
| Diff{DiffInsert, "cow-and-the-"}, |
| Diff{DiffEqual, "cat."}, |
| }, |
| }, |
| { |
| "Hitting the start", |
| []Diff{ |
| Diff{DiffEqual, "a"}, |
| Diff{DiffDelete, "a"}, |
| Diff{DiffEqual, "ax"}, |
| }, |
| []Diff{ |
| Diff{DiffDelete, "a"}, |
| Diff{DiffEqual, "aax"}, |
| }, |
| }, |
| { |
| "Hitting the end", |
| []Diff{ |
| Diff{DiffEqual, "xa"}, |
| Diff{DiffDelete, "a"}, |
| Diff{DiffEqual, "a"}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "xaa"}, |
| Diff{DiffDelete, "a"}, |
| }, |
| }, |
| { |
| "Sentence boundaries", |
| []Diff{ |
| Diff{DiffEqual, "The xxx. The "}, |
| Diff{DiffInsert, "zzz. The "}, |
| Diff{DiffEqual, "yyy."}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "The xxx."}, |
| Diff{DiffInsert, " The zzz."}, |
| Diff{DiffEqual, " The yyy."}, |
| }, |
| }, |
| { |
| "UTF-8 strings", |
| []Diff{ |
| Diff{DiffEqual, "The ♕. The "}, |
| Diff{DiffInsert, "♔. The "}, |
| Diff{DiffEqual, "♖."}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "The ♕."}, |
| Diff{DiffInsert, " The ♔."}, |
| Diff{DiffEqual, " The ♖."}, |
| }, |
| }, |
| { |
| "Rune boundaries", |
| []Diff{ |
| Diff{DiffEqual, "♕♕"}, |
| Diff{DiffInsert, "♔♔"}, |
| Diff{DiffEqual, "♖♖"}, |
| }, |
| []Diff{ |
| Diff{DiffEqual, "♕♕"}, |
| Diff{DiffInsert, "♔♔"}, |
| Diff{DiffEqual, "♖♖"}, |
| }, |
| }, |
| } { |
| actual := dmp.DiffCleanupSemanticLossless(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffCleanupSemantic(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Diffs []Diff |
| |
| Expected []Diff |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| "Null case", |
| []Diff{}, |
| []Diff{}, |
| }, |
| { |
| "No elimination #1", |
| []Diff{ |
| {DiffDelete, "ab"}, |
| {DiffInsert, "cd"}, |
| {DiffEqual, "12"}, |
| {DiffDelete, "e"}, |
| }, |
| []Diff{ |
| {DiffDelete, "ab"}, |
| {DiffInsert, "cd"}, |
| {DiffEqual, "12"}, |
| {DiffDelete, "e"}, |
| }, |
| }, |
| { |
| "No elimination #2", |
| []Diff{ |
| {DiffDelete, "abc"}, |
| {DiffInsert, "ABC"}, |
| {DiffEqual, "1234"}, |
| {DiffDelete, "wxyz"}, |
| }, |
| []Diff{ |
| {DiffDelete, "abc"}, |
| {DiffInsert, "ABC"}, |
| {DiffEqual, "1234"}, |
| {DiffDelete, "wxyz"}, |
| }, |
| }, |
| { |
| "No elimination #3", |
| []Diff{ |
| {DiffEqual, "2016-09-01T03:07:1"}, |
| {DiffInsert, "5.15"}, |
| {DiffEqual, "4"}, |
| {DiffDelete, "."}, |
| {DiffEqual, "80"}, |
| {DiffInsert, "0"}, |
| {DiffEqual, "78"}, |
| {DiffDelete, "3074"}, |
| {DiffEqual, "1Z"}, |
| }, |
| []Diff{ |
| {DiffEqual, "2016-09-01T03:07:1"}, |
| {DiffInsert, "5.15"}, |
| {DiffEqual, "4"}, |
| {DiffDelete, "."}, |
| {DiffEqual, "80"}, |
| {DiffInsert, "0"}, |
| {DiffEqual, "78"}, |
| {DiffDelete, "3074"}, |
| {DiffEqual, "1Z"}, |
| }, |
| }, |
| { |
| "Simple elimination", |
| []Diff{ |
| {DiffDelete, "a"}, |
| {DiffEqual, "b"}, |
| {DiffDelete, "c"}, |
| }, |
| []Diff{ |
| {DiffDelete, "abc"}, |
| {DiffInsert, "b"}, |
| }, |
| }, |
| { |
| "Backpass elimination", |
| []Diff{ |
| {DiffDelete, "ab"}, |
| {DiffEqual, "cd"}, |
| {DiffDelete, "e"}, |
| {DiffEqual, "f"}, |
| {DiffInsert, "g"}, |
| }, |
| []Diff{ |
| {DiffDelete, "abcdef"}, |
| {DiffInsert, "cdfg"}, |
| }, |
| }, |
| { |
| "Multiple eliminations", |
| []Diff{ |
| {DiffInsert, "1"}, |
| {DiffEqual, "A"}, |
| {DiffDelete, "B"}, |
| {DiffInsert, "2"}, |
| {DiffEqual, "_"}, |
| {DiffInsert, "1"}, |
| {DiffEqual, "A"}, |
| {DiffDelete, "B"}, |
| {DiffInsert, "2"}, |
| }, |
| []Diff{ |
| {DiffDelete, "AB_AB"}, |
| {DiffInsert, "1A2_1A2"}, |
| }, |
| }, |
| { |
| "Word boundaries", |
| []Diff{ |
| {DiffEqual, "The c"}, |
| {DiffDelete, "ow and the c"}, |
| {DiffEqual, "at."}, |
| }, |
| []Diff{ |
| {DiffEqual, "The "}, |
| {DiffDelete, "cow and the "}, |
| {DiffEqual, "cat."}, |
| }, |
| }, |
| { |
| "No overlap elimination", |
| []Diff{ |
| {DiffDelete, "abcxx"}, |
| {DiffInsert, "xxdef"}, |
| }, |
| []Diff{ |
| {DiffDelete, "abcxx"}, |
| {DiffInsert, "xxdef"}, |
| }, |
| }, |
| { |
| "Overlap elimination", |
| []Diff{ |
| {DiffDelete, "abcxxx"}, |
| {DiffInsert, "xxxdef"}, |
| }, |
| []Diff{ |
| {DiffDelete, "abc"}, |
| {DiffEqual, "xxx"}, |
| {DiffInsert, "def"}, |
| }, |
| }, |
| { |
| "Reverse overlap elimination", |
| []Diff{ |
| {DiffDelete, "xxxabc"}, |
| {DiffInsert, "defxxx"}, |
| }, |
| []Diff{ |
| {DiffInsert, "def"}, |
| {DiffEqual, "xxx"}, |
| {DiffDelete, "abc"}, |
| }, |
| }, |
| { |
| "Two overlap eliminations", |
| []Diff{ |
| {DiffDelete, "abcd1212"}, |
| {DiffInsert, "1212efghi"}, |
| {DiffEqual, "----"}, |
| {DiffDelete, "A3"}, |
| {DiffInsert, "3BC"}, |
| }, |
| []Diff{ |
| {DiffDelete, "abcd"}, |
| {DiffEqual, "1212"}, |
| {DiffInsert, "efghi"}, |
| {DiffEqual, "----"}, |
| {DiffDelete, "A"}, |
| {DiffEqual, "3"}, |
| {DiffInsert, "BC"}, |
| }, |
| }, |
| { |
| "Test case for adapting DiffCleanupSemantic to be equal to the Python version #19", |
| []Diff{ |
| {DiffEqual, "James McCarthy "}, |
| {DiffDelete, "close to "}, |
| {DiffEqual, "sign"}, |
| {DiffDelete, "ing"}, |
| {DiffInsert, "s"}, |
| {DiffEqual, " new "}, |
| {DiffDelete, "E"}, |
| {DiffInsert, "fi"}, |
| {DiffEqual, "ve"}, |
| {DiffInsert, "-yea"}, |
| {DiffEqual, "r"}, |
| {DiffDelete, "ton"}, |
| {DiffEqual, " deal"}, |
| {DiffInsert, " at Everton"}, |
| }, |
| []Diff{ |
| {DiffEqual, "James McCarthy "}, |
| {DiffDelete, "close to "}, |
| {DiffEqual, "sign"}, |
| {DiffDelete, "ing"}, |
| {DiffInsert, "s"}, |
| {DiffEqual, " new "}, |
| {DiffInsert, "five-year deal at "}, |
| {DiffEqual, "Everton"}, |
| {DiffDelete, " deal"}, |
| }, |
| }, |
| } { |
| actual := dmp.DiffCleanupSemantic(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffCleanupEfficiency(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Diffs []Diff |
| |
| Expected []Diff |
| } |
| |
| dmp := New() |
| dmp.DiffEditCost = 4 |
| |
| for i, tc := range []TestCase{ |
| { |
| "Null case", |
| []Diff{}, |
| []Diff{}, |
| }, |
| { |
| "No elimination", |
| []Diff{ |
| Diff{DiffDelete, "ab"}, |
| Diff{DiffInsert, "12"}, |
| Diff{DiffEqual, "wxyz"}, |
| Diff{DiffDelete, "cd"}, |
| Diff{DiffInsert, "34"}, |
| }, |
| []Diff{ |
| Diff{DiffDelete, "ab"}, |
| Diff{DiffInsert, "12"}, |
| Diff{DiffEqual, "wxyz"}, |
| Diff{DiffDelete, "cd"}, |
| Diff{DiffInsert, "34"}, |
| }, |
| }, |
| { |
| "Four-edit elimination", |
| []Diff{ |
| Diff{DiffDelete, "ab"}, |
| Diff{DiffInsert, "12"}, |
| Diff{DiffEqual, "xyz"}, |
| Diff{DiffDelete, "cd"}, |
| Diff{DiffInsert, "34"}, |
| }, |
| []Diff{ |
| Diff{DiffDelete, "abxyzcd"}, |
| Diff{DiffInsert, "12xyz34"}, |
| }, |
| }, |
| { |
| "Three-edit elimination", |
| []Diff{ |
| Diff{DiffInsert, "12"}, |
| Diff{DiffEqual, "x"}, |
| Diff{DiffDelete, "cd"}, |
| Diff{DiffInsert, "34"}, |
| }, |
| []Diff{ |
| Diff{DiffDelete, "xcd"}, |
| Diff{DiffInsert, "12x34"}, |
| }, |
| }, |
| { |
| "Backpass elimination", |
| []Diff{ |
| Diff{DiffDelete, "ab"}, |
| Diff{DiffInsert, "12"}, |
| Diff{DiffEqual, "xy"}, |
| Diff{DiffInsert, "34"}, |
| Diff{DiffEqual, "z"}, |
| Diff{DiffDelete, "cd"}, |
| Diff{DiffInsert, "56"}, |
| }, |
| []Diff{ |
| Diff{DiffDelete, "abxyzcd"}, |
| Diff{DiffInsert, "12xy34z56"}, |
| }, |
| }, |
| } { |
| actual := dmp.DiffCleanupEfficiency(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.DiffEditCost = 5 |
| |
| for i, tc := range []TestCase{ |
| { |
| "High cost elimination", |
| []Diff{ |
| Diff{DiffDelete, "ab"}, |
| Diff{DiffInsert, "12"}, |
| Diff{DiffEqual, "wxyz"}, |
| Diff{DiffDelete, "cd"}, |
| Diff{DiffInsert, "34"}, |
| }, |
| []Diff{ |
| Diff{DiffDelete, "abwxyzcd"}, |
| Diff{DiffInsert, "12wxyz34"}, |
| }, |
| }, |
| } { |
| actual := dmp.DiffCleanupEfficiency(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffPrettyHtml(t *testing.T) { |
| type TestCase struct { |
| Diffs []Diff |
| |
| Expected string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| Diffs: []Diff{ |
| {DiffEqual, "a\n"}, |
| {DiffDelete, "<B>b</B>"}, |
| {DiffInsert, "c&d"}, |
| }, |
| |
| Expected: "<span>a¶<br></span><del style=\"background:#ffe6e6;\"><B>b</B></del><ins style=\"background:#e6ffe6;\">c&d</ins>", |
| }, |
| } { |
| actual := dmp.DiffPrettyHtml(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestDiffPrettyText(t *testing.T) { |
| type TestCase struct { |
| Diffs []Diff |
| |
| Expected string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| Diffs: []Diff{ |
| {DiffEqual, "a\n"}, |
| {DiffDelete, "<B>b</B>"}, |
| {DiffInsert, "c&d"}, |
| }, |
| |
| Expected: "a\n\x1b[31m<B>b</B>\x1b[0m\x1b[32mc&d\x1b[0m", |
| }, |
| } { |
| actual := dmp.DiffPrettyText(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestDiffText(t *testing.T) { |
| type TestCase struct { |
| Diffs []Diff |
| |
| ExpectedText1 string |
| ExpectedText2 string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| Diffs: []Diff{ |
| {DiffEqual, "jump"}, |
| {DiffDelete, "s"}, |
| {DiffInsert, "ed"}, |
| {DiffEqual, " over "}, |
| {DiffDelete, "the"}, |
| {DiffInsert, "a"}, |
| {DiffEqual, " lazy"}, |
| }, |
| |
| ExpectedText1: "jumps over the lazy", |
| ExpectedText2: "jumped over a lazy", |
| }, |
| } { |
| actualText1 := dmp.DiffText1(tc.Diffs) |
| assert.Equal(t, tc.ExpectedText1, actualText1, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| |
| actualText2 := dmp.DiffText2(tc.Diffs) |
| assert.Equal(t, tc.ExpectedText2, actualText2, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestDiffDelta(t *testing.T) { |
| dmp := New() |
| |
| // Convert a diff into delta string. |
| diffs := []Diff{ |
| Diff{DiffEqual, "jump"}, |
| Diff{DiffDelete, "s"}, |
| Diff{DiffInsert, "ed"}, |
| Diff{DiffEqual, " over "}, |
| Diff{DiffDelete, "the"}, |
| Diff{DiffInsert, "a"}, |
| Diff{DiffEqual, " lazy"}, |
| Diff{DiffInsert, "old dog"}, |
| } |
| text1 := dmp.DiffText1(diffs) |
| assert.Equal(t, "jumps over the lazy", text1) |
| |
| delta := dmp.DiffToDelta(diffs) |
| assert.Equal(t, "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta) |
| |
| // Convert delta string into a diff. |
| deltaDiffs, err := dmp.DiffFromDelta(text1, delta) |
| assert.Equal(t, diffs, deltaDiffs) |
| |
| // Generates error (19 < 20). |
| _, err = dmp.DiffFromDelta(text1+"x", delta) |
| if err == nil { |
| t.Fatal("Too long.") |
| } |
| |
| // Generates error (19 > 18). |
| _, err = dmp.DiffFromDelta(text1[1:], delta) |
| if err == nil { |
| t.Fatal("Too short.") |
| } |
| |
| // Generates error (%xy invalid URL escape). |
| _, err = dmp.DiffFromDelta("", "+%c3%xy") |
| if err == nil { |
| assert.Fail(t, "expected Invalid URL escape.") |
| } |
| |
| // Generates error (invalid utf8). |
| _, err = dmp.DiffFromDelta("", "+%c3xy") |
| if err == nil { |
| assert.Fail(t, "expected Invalid utf8.") |
| } |
| |
| // Test deltas with special characters. |
| diffs = []Diff{ |
| Diff{DiffEqual, "\u0680 \x00 \t %"}, |
| Diff{DiffDelete, "\u0681 \x01 \n ^"}, |
| Diff{DiffInsert, "\u0682 \x02 \\ |"}, |
| } |
| text1 = dmp.DiffText1(diffs) |
| assert.Equal(t, "\u0680 \x00 \t %\u0681 \x01 \n ^", text1) |
| |
| // Lowercase, due to UrlEncode uses lower. |
| delta = dmp.DiffToDelta(diffs) |
| assert.Equal(t, "=7\t-7\t+%DA%82 %02 %5C %7C", delta) |
| |
| deltaDiffs, err = dmp.DiffFromDelta(text1, delta) |
| assert.Equal(t, diffs, deltaDiffs) |
| assert.Nil(t, err) |
| |
| // Verify pool of unchanged characters. |
| diffs = []Diff{ |
| Diff{DiffInsert, "A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # "}, |
| } |
| |
| delta = dmp.DiffToDelta(diffs) |
| assert.Equal(t, "+A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ", delta, "Unchanged characters.") |
| |
| // Convert delta string into a diff. |
| deltaDiffs, err = dmp.DiffFromDelta("", delta) |
| assert.Equal(t, diffs, deltaDiffs) |
| assert.Nil(t, err) |
| } |
| |
| func TestDiffXIndex(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Diffs []Diff |
| Location int |
| |
| Expected int |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"Translation on equality", []Diff{{DiffDelete, "a"}, {DiffInsert, "1234"}, {DiffEqual, "xyz"}}, 2, 5}, |
| {"Translation on deletion", []Diff{{DiffEqual, "a"}, {DiffDelete, "1234"}, {DiffEqual, "xyz"}}, 3, 1}, |
| } { |
| actual := dmp.DiffXIndex(tc.Diffs, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffLevenshtein(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Diffs []Diff |
| |
| Expected int |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"Levenshtein with trailing equality", []Diff{{DiffDelete, "abc"}, {DiffInsert, "1234"}, {DiffEqual, "xyz"}}, 4}, |
| {"Levenshtein with leading equality", []Diff{{DiffEqual, "xyz"}, {DiffDelete, "abc"}, {DiffInsert, "1234"}}, 4}, |
| {"Levenshtein with middle equality", []Diff{{DiffDelete, "abc"}, {DiffEqual, "xyz"}, {DiffInsert, "1234"}}, 7}, |
| } { |
| actual := dmp.DiffLevenshtein(tc.Diffs) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffBisect(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Time time.Time |
| |
| Expected []Diff |
| } |
| |
| dmp := New() |
| |
| text1 := "cat" |
| text2 := "map" |
| |
| for i, tc := range []TestCase{ |
| { |
| Name: "normal", |
| Time: time.Date(9999, time.December, 31, 23, 59, 59, 59, time.UTC), |
| |
| Expected: []Diff{ |
| {DiffDelete, "c"}, |
| {DiffInsert, "m"}, |
| {DiffEqual, "a"}, |
| {DiffDelete, "t"}, |
| {DiffInsert, "p"}, |
| }, |
| }, |
| { |
| Name: "Negative deadlines count as having infinite time", |
| Time: time.Date(0001, time.January, 01, 00, 00, 00, 00, time.UTC), |
| |
| Expected: []Diff{ |
| {DiffDelete, "c"}, |
| {DiffInsert, "m"}, |
| {DiffEqual, "a"}, |
| {DiffDelete, "t"}, |
| {DiffInsert, "p"}, |
| }, |
| }, |
| { |
| Name: "Timeout", |
| Time: time.Now().Add(time.Nanosecond), |
| |
| Expected: []Diff{ |
| {DiffDelete, "cat"}, |
| {DiffInsert, "map"}, |
| }, |
| }, |
| } { |
| actual := dmp.DiffBisect(text1, text2, tc.Time) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestDiffMain(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| |
| Expected []Diff |
| } |
| |
| dmp := New() |
| |
| // Perform a trivial diff. |
| for i, tc := range []TestCase{ |
| { |
| "", |
| "", |
| nil, |
| }, |
| { |
| "abc", |
| "abc", |
| []Diff{Diff{DiffEqual, "abc"}}, |
| }, |
| { |
| "abc", |
| "ab123c", |
| []Diff{Diff{DiffEqual, "ab"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "c"}}, |
| }, |
| { |
| "a123bc", |
| "abc", |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "bc"}}, |
| }, |
| { |
| "abc", |
| "a123b456c", |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "b"}, Diff{DiffInsert, "456"}, Diff{DiffEqual, "c"}}, |
| }, |
| { |
| "a123b456c", |
| "abc", |
| []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "b"}, Diff{DiffDelete, "456"}, Diff{DiffEqual, "c"}}, |
| }, |
| } { |
| actual := dmp.DiffMain(tc.Text1, tc.Text2, false) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| |
| // Perform a real diff and switch off the timeout. |
| dmp.DiffTimeout = 0 |
| |
| for i, tc := range []TestCase{ |
| { |
| "a", |
| "b", |
| []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}}, |
| }, |
| { |
| "Apples are a fruit.", |
| "Bananas are also fruit.", |
| []Diff{ |
| Diff{DiffDelete, "Apple"}, |
| Diff{DiffInsert, "Banana"}, |
| Diff{DiffEqual, "s are a"}, |
| Diff{DiffInsert, "lso"}, |
| Diff{DiffEqual, " fruit."}, |
| }, |
| }, |
| { |
| "ax\t", |
| "\u0680x\u0000", |
| []Diff{ |
| Diff{DiffDelete, "a"}, |
| Diff{DiffInsert, "\u0680"}, |
| Diff{DiffEqual, "x"}, |
| Diff{DiffDelete, "\t"}, |
| Diff{DiffInsert, "\u0000"}, |
| }, |
| }, |
| { |
| "1ayb2", |
| "abxab", |
| []Diff{ |
| Diff{DiffDelete, "1"}, |
| Diff{DiffEqual, "a"}, |
| Diff{DiffDelete, "y"}, |
| Diff{DiffEqual, "b"}, |
| Diff{DiffDelete, "2"}, |
| Diff{DiffInsert, "xab"}, |
| }, |
| }, |
| { |
| "abcy", |
| "xaxcxabc", |
| []Diff{ |
| Diff{DiffInsert, "xaxcx"}, |
| Diff{DiffEqual, "abc"}, Diff{DiffDelete, "y"}, |
| }, |
| }, |
| { |
| "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", |
| "a-bcd-efghijklmnopqrs", |
| []Diff{ |
| Diff{DiffDelete, "ABCD"}, |
| Diff{DiffEqual, "a"}, |
| Diff{DiffDelete, "="}, |
| Diff{DiffInsert, "-"}, |
| Diff{DiffEqual, "bcd"}, |
| Diff{DiffDelete, "="}, |
| Diff{DiffInsert, "-"}, |
| Diff{DiffEqual, "efghijklmnopqrs"}, |
| Diff{DiffDelete, "EFGHIJKLMNOefg"}, |
| }, |
| }, |
| { |
| "a [[Pennsylvania]] and [[New", |
| " and [[Pennsylvania]]", |
| []Diff{ |
| Diff{DiffInsert, " "}, |
| Diff{DiffEqual, "a"}, |
| Diff{DiffInsert, "nd"}, |
| Diff{DiffEqual, " [[Pennsylvania]]"}, |
| Diff{DiffDelete, " and [[New"}, |
| }, |
| }, |
| } { |
| actual := dmp.DiffMain(tc.Text1, tc.Text2, false) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestDiffMainWithTimeout(t *testing.T) { |
| dmp := New() |
| dmp.DiffTimeout = 200 * time.Millisecond |
| |
| a := "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n" |
| b := "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" |
| // Increase the text lengths by 1024 times to ensure a timeout. |
| for x := 0; x < 13; x++ { |
| a = a + a |
| b = b + b |
| } |
| |
| startTime := time.Now() |
| dmp.DiffMain(a, b, true) |
| endTime := time.Now() |
| |
| delta := endTime.Sub(startTime) |
| |
| // Test that we took at least the timeout period. |
| assert.True(t, delta >= dmp.DiffTimeout, fmt.Sprintf("%v !>= %v", delta, dmp.DiffTimeout)) |
| |
| // Test that we didn't take forever (be very forgiving). |
| // Theoretically this test could fail very occasionally if the |
| // OS task swaps or locks up for a second at the wrong moment. |
| assert.True(t, delta < (dmp.DiffTimeout*100), fmt.Sprintf("%v !< %v", delta, dmp.DiffTimeout*100)) |
| } |
| |
| func TestDiffMainWithCheckLines(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| } |
| |
| dmp := New() |
| dmp.DiffTimeout = 0 |
| |
| // Test cases must be at least 100 chars long to pass the cutoff. |
| for i, tc := range []TestCase{ |
| { |
| "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", |
| "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n", |
| }, |
| { |
| "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", |
| "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij", |
| }, |
| { |
| "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", |
| "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n", |
| }, |
| } { |
| resultWithoutCheckLines := dmp.DiffMain(tc.Text1, tc.Text2, false) |
| resultWithCheckLines := dmp.DiffMain(tc.Text1, tc.Text2, true) |
| |
| // TODO this fails for the third test case, why? |
| if i != 2 { |
| assert.Equal(t, resultWithoutCheckLines, resultWithCheckLines, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| assert.Equal(t, diffRebuildTexts(resultWithoutCheckLines), diffRebuildTexts(resultWithCheckLines), fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestMatchAlphabet(t *testing.T) { |
| type TestCase struct { |
| Pattern string |
| |
| Expected map[byte]int |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| { |
| Pattern: "abc", |
| |
| Expected: map[byte]int{ |
| 'a': 4, |
| 'b': 2, |
| 'c': 1, |
| }, |
| }, |
| { |
| Pattern: "abcaba", |
| |
| Expected: map[byte]int{ |
| 'a': 37, |
| 'b': 18, |
| 'c': 8, |
| }, |
| }, |
| } { |
| actual := dmp.MatchAlphabet(tc.Pattern) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestMatchBitap(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Text string |
| Pattern string |
| Location int |
| |
| Expected int |
| } |
| |
| dmp := New() |
| dmp.MatchDistance = 100 |
| dmp.MatchThreshold = 0.5 |
| |
| for i, tc := range []TestCase{ |
| {"Exact match #1", "abcdefghijk", "fgh", 5, 5}, |
| {"Exact match #2", "abcdefghijk", "fgh", 0, 5}, |
| {"Fuzzy match #1", "abcdefghijk", "efxhi", 0, 4}, |
| {"Fuzzy match #2", "abcdefghijk", "cdefxyhijk", 5, 2}, |
| {"Fuzzy match #3", "abcdefghijk", "bxy", 1, -1}, |
| {"Overflow", "123456789xx0", "3456789x0", 2, 2}, |
| {"Before start match", "abcdef", "xxabc", 4, 0}, |
| {"Beyond end match", "abcdef", "defyy", 4, 3}, |
| {"Oversized pattern", "abcdef", "xabcdefy", 0, 0}, |
| } { |
| actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.MatchThreshold = 0.4 |
| |
| for i, tc := range []TestCase{ |
| {"Threshold #1", "abcdefghijk", "efxyhi", 1, 4}, |
| } { |
| actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.MatchThreshold = 0.3 |
| |
| for i, tc := range []TestCase{ |
| {"Threshold #2", "abcdefghijk", "efxyhi", 1, -1}, |
| } { |
| actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.MatchThreshold = 0.0 |
| |
| for i, tc := range []TestCase{ |
| {"Threshold #3", "abcdefghijk", "bcdef", 1, 1}, |
| } { |
| actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.MatchThreshold = 0.5 |
| |
| for i, tc := range []TestCase{ |
| {"Multiple select #1", "abcdexyzabcde", "abccde", 3, 0}, |
| {"Multiple select #2", "abcdexyzabcde", "abccde", 5, 8}, |
| } { |
| actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| // Strict location. |
| dmp.MatchDistance = 10 |
| |
| for i, tc := range []TestCase{ |
| {"Distance test #1", "abcdefghijklmnopqrstuvwxyz", "abcdefg", 24, -1}, |
| {"Distance test #2", "abcdefghijklmnopqrstuvwxyz", "abcdxxefg", 1, 0}, |
| } { |
| actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| // Loose location. |
| dmp.MatchDistance = 1000 |
| |
| for i, tc := range []TestCase{ |
| {"Distance test #3", "abcdefghijklmnopqrstuvwxyz", "abcdefg", 24, 0}, |
| } { |
| actual := dmp.MatchBitap(tc.Text, tc.Pattern, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestMatchMain(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Text1 string |
| Text2 string |
| Location int |
| |
| Expected int |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"Equality", "abcdef", "abcdef", 1000, 0}, |
| {"Null text", "", "abcdef", 1, -1}, |
| {"Null pattern", "abcdef", "", 3, 3}, |
| {"Exact match", "abcdef", "de", 3, 3}, |
| {"Beyond end match", "abcdef", "defy", 4, 3}, |
| {"Oversized pattern", "abcdef", "abcdefy", 0, 0}, |
| } { |
| actual := dmp.MatchMain(tc.Text1, tc.Text2, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.MatchThreshold = 0.7 |
| |
| for i, tc := range []TestCase{ |
| {"Complex match", "I am the very model of a modern major general.", " that berry ", 5, 4}, |
| } { |
| actual := dmp.MatchMain(tc.Text1, tc.Text2, tc.Location) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestPatchString(t *testing.T) { |
| type TestCase struct { |
| Patch Patch |
| |
| Expected string |
| } |
| |
| for i, tc := range []TestCase{ |
| { |
| Patch: Patch{ |
| start1: 20, |
| start2: 21, |
| length1: 18, |
| length2: 17, |
| |
| diffs: []Diff{ |
| {DiffEqual, "jump"}, |
| {DiffDelete, "s"}, |
| {DiffInsert, "ed"}, |
| {DiffEqual, " over "}, |
| {DiffDelete, "the"}, |
| {DiffInsert, "a"}, |
| {DiffEqual, "\nlaz"}, |
| }, |
| }, |
| |
| Expected: "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n", |
| }, |
| } { |
| actual := tc.Patch.String() |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestPatchFromText(t *testing.T) { |
| type TestCase struct { |
| Patch string |
| |
| ErrorMessagePrefix string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"", ""}, |
| {"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n", ""}, |
| {"@@ -1 +1 @@\n-a\n+b\n", ""}, |
| {"@@ -1,3 +0,0 @@\n-abc\n", ""}, |
| {"@@ -0,0 +1,3 @@\n+abc\n", ""}, |
| {"Bad\nPatch\n", "Invalid patch string"}, |
| } { |
| patches, err := dmp.PatchFromText(tc.Patch) |
| if tc.ErrorMessagePrefix == "" { |
| assert.Nil(t, err) |
| |
| if tc.Patch == "" { |
| assert.Equal(t, []Patch{}, patches, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } else { |
| assert.Equal(t, tc.Patch, patches[0].String(), fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } else { |
| e := err.Error() |
| if strings.HasPrefix(e, tc.ErrorMessagePrefix) { |
| e = tc.ErrorMessagePrefix |
| } |
| assert.Equal(t, tc.ErrorMessagePrefix, e) |
| } |
| } |
| |
| diffs := []Diff{ |
| {DiffDelete, "`1234567890-=[]\\;',./"}, |
| {DiffInsert, "~!@#$%^&*()_+{}|:\"<>?"}, |
| } |
| |
| patches, err := dmp.PatchFromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n") |
| assert.Len(t, patches, 1) |
| assert.Equal(t, diffs, |
| patches[0].diffs, |
| ) |
| assert.Nil(t, err) |
| } |
| |
| func TestPatchToText(t *testing.T) { |
| type TestCase struct { |
| Patch string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"}, |
| {"@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n"}, |
| } { |
| patches, err := dmp.PatchFromText(tc.Patch) |
| assert.Nil(t, err) |
| |
| actual := dmp.PatchToText(patches) |
| assert.Equal(t, tc.Patch, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestPatchAddContext(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Patch string |
| Text string |
| |
| Expected string |
| } |
| |
| dmp := New() |
| dmp.PatchMargin = 4 |
| |
| for i, tc := range []TestCase{ |
| {"Simple case", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps over the lazy dog.", "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n"}, |
| {"Not enough trailing context", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps.", "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n"}, |
| {"Not enough leading context", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.", "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n qui\n"}, |
| {"Ambiguity", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps. The quick brown fox crashes.", "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n quick brown fox jumps. \n"}, |
| } { |
| patches, err := dmp.PatchFromText(tc.Patch) |
| assert.Nil(t, err) |
| |
| actual := dmp.PatchAddContext(patches[0], tc.Text) |
| assert.Equal(t, tc.Expected, actual.String(), fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| // TODO |
| func TestPatchMakeAndPatchToText(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Input1 interface{} |
| Input2 interface{} |
| Input3 interface{} |
| |
| Expected string |
| } |
| |
| dmp := New() |
| |
| text1 := "The quick brown fox jumps over the lazy dog." |
| text2 := "That quick brown fox jumped over a lazy dog." |
| |
| for i, tc := range []TestCase{ |
| {"Null case", "", "", nil, ""}, |
| {"Text2+Text1 inputs", text2, text1, nil, "@@ -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"}, |
| {"Text1+Text2 inputs", text1, text2, nil, "@@ -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"}, |
| {"Diff input", dmp.DiffMain(text1, text2, false), nil, nil, "@@ -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"}, |
| {"Text1+Diff inputs", text1, dmp.DiffMain(text1, text2, false), nil, "@@ -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"}, |
| {"Text1+Text2+Diff inputs (deprecated)", text1, text2, dmp.DiffMain(text1, text2, false), "@@ -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"}, |
| {"Character encoding", "`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?", nil, "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n"}, |
| {"Long string with repeats", strings.Repeat("abcdef", 100), strings.Repeat("abcdef", 100) + "123", nil, "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"}, |
| {"Corner case of #31 fixed by #32", "2016-09-01T03:07:14.807830741Z", "2016-09-01T03:07:15.154800781Z", nil, "@@ -15,16 +15,16 @@\n 07:1\n+5.15\n 4\n-.\n 80\n+0\n 78\n-3074\n 1Z\n"}, |
| } { |
| var patches []Patch |
| if tc.Input3 != nil { |
| patches = dmp.PatchMake(tc.Input1, tc.Input2, tc.Input3) |
| } else if tc.Input2 != nil { |
| patches = dmp.PatchMake(tc.Input1, tc.Input2) |
| } else if ps, ok := tc.Input1.([]Patch); ok { |
| patches = ps |
| } else { |
| patches = dmp.PatchMake(tc.Input1) |
| } |
| |
| actual := dmp.PatchToText(patches) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| // Corner case of #28 wrong patch with timeout of 0 |
| dmp.DiffTimeout = 0 |
| |
| text1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum libero vel." |
| text2 = "Lorem a ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum liberovel." |
| |
| diffs := dmp.DiffMain(text1, text2, true) |
| // Additional check that the diff texts are equal to the originals even if we are using DiffMain with checklines=true #29 |
| assert.Equal(t, text1, dmp.DiffText1(diffs)) |
| assert.Equal(t, text2, dmp.DiffText2(diffs)) |
| |
| patches := dmp.PatchMake(text1, diffs) |
| |
| actual := dmp.PatchToText(patches) |
| assert.Equal(t, "@@ -1,14 +1,16 @@\n Lorem \n+a \n ipsum do\n@@ -148,13 +148,12 @@\n m libero\n- \n vel.\n", actual) |
| } |
| |
| func TestPatchSplitMax(t *testing.T) { |
| type TestCase struct { |
| Text1 string |
| Text2 string |
| |
| Expected string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0", "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n ij\n+X\n kl\n+X\n mn\n+X\n op\n+X\n qr\n+X\n st\n+X\n uv\n+X\n wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n 45\n+X\n 67\n+X\n 89\n+X\n 0\n"}, |
| {"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz", "@@ -3,78 +3,8 @@\n cdef\n-1234567890123456789012345678901234567890123456789012345678901234567890\n uvwx\n"}, |
| {"1234567890123456789012345678901234567890123456789012345678901234567890", "abc", "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n"}, |
| {"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1", "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n"}, |
| } { |
| patches := dmp.PatchMake(tc.Text1, tc.Text2) |
| patches = dmp.PatchSplitMax(patches) |
| |
| actual := dmp.PatchToText(patches) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestPatchAddPadding(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Text1 string |
| Text2 string |
| |
| Expected string |
| ExpectedWithPadding string |
| } |
| |
| dmp := New() |
| |
| for i, tc := range []TestCase{ |
| {"Both edges full", "", "test", "@@ -0,0 +1,4 @@\n+test\n", "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n"}, |
| {"Both edges partial", "XY", "XtestY", "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n"}, |
| {"Both edges none", "XXXXYYYY", "XXXXtestYYYY", "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n"}, |
| } { |
| patches := dmp.PatchMake(tc.Text1, tc.Text2) |
| |
| actual := dmp.PatchToText(patches) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| |
| dmp.PatchAddPadding(patches) |
| |
| actualWithPadding := dmp.PatchToText(patches) |
| assert.Equal(t, tc.ExpectedWithPadding, actualWithPadding, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestPatchApply(t *testing.T) { |
| type TestCase struct { |
| Name string |
| |
| Text1 string |
| Text2 string |
| TextBase string |
| |
| Expected string |
| ExpectedApplies []bool |
| } |
| |
| dmp := New() |
| dmp.MatchDistance = 1000 |
| dmp.MatchThreshold = 0.5 |
| dmp.PatchDeleteThreshold = 0.5 |
| |
| for i, tc := range []TestCase{ |
| {"Null case", "", "", "Hello world.", "Hello world.", []bool{}}, |
| {"Exact match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", []bool{true, true}}, |
| {"Partial match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick red rabbit jumps over the tired tiger.", "That quick red rabbit jumped over a tired tiger.", []bool{true, true}}, |
| {"Failed match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "I am the very model of a modern major general.", "I am the very model of a modern major general.", []bool{false, false}}, |
| {"Big delete, small Diff", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y", "xabcy", []bool{true, true}}, |
| {"Big delete, big Diff 1", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y", []bool{false, true}}, |
| } { |
| patches := dmp.PatchMake(tc.Text1, tc.Text2) |
| |
| actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.PatchDeleteThreshold = 0.6 |
| |
| for i, tc := range []TestCase{ |
| {"Big delete, big Diff 2", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabcy", []bool{true, true}}, |
| } { |
| patches := dmp.PatchMake(tc.Text1, tc.Text2) |
| |
| actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.MatchDistance = 0 |
| dmp.MatchThreshold = 0.0 |
| dmp.PatchDeleteThreshold = 0.5 |
| |
| for i, tc := range []TestCase{ |
| {"Compensate for failed patch", "abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890", []bool{false, true}}, |
| } { |
| patches := dmp.PatchMake(tc.Text1, tc.Text2) |
| |
| actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| |
| dmp.MatchThreshold = 0.5 |
| dmp.MatchDistance = 1000 |
| |
| for i, tc := range []TestCase{ |
| {"No side effects", "", "test", "", "test", []bool{true}}, |
| {"No side effects with major delete", "The quick brown fox jumps over the lazy dog.", "Woof", "The quick brown fox jumps over the lazy dog.", "Woof", []bool{true, true}}, |
| {"Edge exact match", "", "test", "", "test", []bool{true}}, |
| {"Near edge exact match", "XY", "XtestY", "XY", "XtestY", []bool{true}}, |
| {"Edge partial match", "y", "y123", "x", "x123", []bool{true}}, |
| } { |
| patches := dmp.PatchMake(tc.Text1, tc.Text2) |
| |
| actual, actualApplies := dmp.PatchApply(patches, tc.TextBase) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) |
| } |
| } |
| |
| func TestIndexOf(t *testing.T) { |
| type TestCase struct { |
| String string |
| Pattern string |
| Position int |
| |
| Expected int |
| } |
| |
| for i, tc := range []TestCase{ |
| {"hi world", "world", -1, 3}, |
| {"hi world", "world", 0, 3}, |
| {"hi world", "world", 1, 3}, |
| {"hi world", "world", 2, 3}, |
| {"hi world", "world", 3, 3}, |
| {"hi world", "world", 4, -1}, |
| {"abbc", "b", -1, 1}, |
| {"abbc", "b", 0, 1}, |
| {"abbc", "b", 1, 1}, |
| {"abbc", "b", 2, 2}, |
| {"abbc", "b", 3, -1}, |
| {"abbc", "b", 4, -1}, |
| // The greek letter beta is the two-byte sequence of "\u03b2". |
| {"a\u03b2\u03b2c", "\u03b2", -1, 1}, |
| {"a\u03b2\u03b2c", "\u03b2", 0, 1}, |
| {"a\u03b2\u03b2c", "\u03b2", 1, 1}, |
| {"a\u03b2\u03b2c", "\u03b2", 3, 3}, |
| {"a\u03b2\u03b2c", "\u03b2", 5, -1}, |
| {"a\u03b2\u03b2c", "\u03b2", 6, -1}, |
| } { |
| actual := indexOf(tc.String, tc.Pattern, tc.Position) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func TestLastIndexOf(t *testing.T) { |
| type TestCase struct { |
| String string |
| Pattern string |
| Position int |
| |
| Expected int |
| } |
| |
| for i, tc := range []TestCase{ |
| {"hi world", "world", -1, -1}, |
| {"hi world", "world", 0, -1}, |
| {"hi world", "world", 1, -1}, |
| {"hi world", "world", 2, -1}, |
| {"hi world", "world", 3, -1}, |
| {"hi world", "world", 4, -1}, |
| {"hi world", "world", 5, -1}, |
| {"hi world", "world", 6, -1}, |
| {"hi world", "world", 7, 3}, |
| {"hi world", "world", 8, 3}, |
| {"abbc", "b", -1, -1}, |
| {"abbc", "b", 0, -1}, |
| {"abbc", "b", 1, 1}, |
| {"abbc", "b", 2, 2}, |
| {"abbc", "b", 3, 2}, |
| {"abbc", "b", 4, 2}, |
| // The greek letter beta is the two-byte sequence of "\u03b2". |
| {"a\u03b2\u03b2c", "\u03b2", -1, -1}, |
| {"a\u03b2\u03b2c", "\u03b2", 0, -1}, |
| {"a\u03b2\u03b2c", "\u03b2", 1, 1}, |
| {"a\u03b2\u03b2c", "\u03b2", 3, 3}, |
| {"a\u03b2\u03b2c", "\u03b2", 5, 3}, |
| {"a\u03b2\u03b2c", "\u03b2", 6, 3}, |
| } { |
| actual := lastIndexOf(tc.String, tc.Pattern, tc.Position) |
| assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) |
| } |
| } |
| |
| func BenchmarkDiffMain(bench *testing.B) { |
| s1 := "`Twas brillig, and the slithy toves\nDid gyre and gimble in the wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n" |
| s2 := "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" |
| |
| // Increase the text lengths by 1024 times to ensure a timeout. |
| for x := 0; x < 10; x++ { |
| s1 = s1 + s1 |
| s2 = s2 + s2 |
| } |
| |
| dmp := New() |
| dmp.DiffTimeout = time.Second |
| |
| bench.ResetTimer() |
| |
| for i := 0; i < bench.N; i++ { |
| dmp.DiffMain(s1, s2, true) |
| } |
| } |
| |
| func BenchmarkDiffCommonPrefix(b *testing.B) { |
| s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" |
| |
| dmp := New() |
| |
| for i := 0; i < b.N; i++ { |
| dmp.DiffCommonPrefix(s, s) |
| } |
| } |
| |
| func BenchmarkDiffCommonSuffix(b *testing.B) { |
| s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" |
| |
| dmp := New() |
| |
| b.ResetTimer() |
| |
| for i := 0; i < b.N; i++ { |
| dmp.DiffCommonSuffix(s, s) |
| } |
| } |
| |
| func BenchmarkDiffMainLarge(b *testing.B) { |
| s1, s2 := speedtestTexts() |
| |
| dmp := New() |
| |
| b.ResetTimer() |
| |
| for i := 0; i < b.N; i++ { |
| dmp.DiffMain(s1, s2, true) |
| } |
| } |
| |
| func BenchmarkDiffMainRunesLargeLines(b *testing.B) { |
| s1, s2 := speedtestTexts() |
| |
| dmp := New() |
| |
| b.ResetTimer() |
| |
| for i := 0; i < b.N; i++ { |
| text1, text2, linearray := dmp.DiffLinesToRunes(s1, s2) |
| |
| diffs := dmp.DiffMainRunes(text1, text2, false) |
| diffs = dmp.DiffCharsToLines(diffs, linearray) |
| } |
| } |
| |
| func BenchmarkDiffHalfMatch(b *testing.B) { |
| s1, s2 := speedtestTexts() |
| |
| dmp := New() |
| |
| b.ResetTimer() |
| |
| for i := 0; i < b.N; i++ { |
| dmp.DiffHalfMatch(s1, s2) |
| } |
| } |
| |
| func BenchmarkDiffCleanupSemantic(b *testing.B) { |
| s1, s2 := speedtestTexts() |
| |
| dmp := New() |
| |
| diffs := dmp.DiffMain(s1, s2, false) |
| |
| b.ResetTimer() |
| |
| for i := 0; i < b.N; i++ { |
| dmp.DiffCleanupSemantic(diffs) |
| } |
| } |