1 // Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
2 // https://github.com/sergi/go-diff
3 // See the included LICENSE file for license details.
5 // go-diff is a Go implementation of Google's Diff, Match, and Patch library
6 // Original library is Copyright (c) 2006 Google Inc.
7 // http://code.google.com/p/google-diff-match-patch/
20 "github.com/stretchr/testify/assert"
23 func pretty(diffs []Diff) string {
26 for i, diff := range diffs {
27 _, _ = w.WriteString(fmt.Sprintf("%v. ", i))
31 _, _ = w.WriteString("DiffIns")
33 _, _ = w.WriteString("DiffDel")
35 _, _ = w.WriteString("DiffEql")
37 _, _ = w.WriteString("Unknown")
40 _, _ = w.WriteString(fmt.Sprintf(": %v\n", diff.Text))
46 func diffRebuildTexts(diffs []Diff) []string {
47 texts := []string{"", ""}
49 for _, d := range diffs {
50 if d.Type != DiffInsert {
53 if d.Type != DiffDelete {
61 func TestDiffCommonPrefix(t *testing.T) {
62 type TestCase struct {
73 for i, tc := range []TestCase{
74 {"Null", "abc", "xyz", 0},
75 {"Non-null", "1234abcdef", "1234xyz", 4},
76 {"Whole", "1234", "1234xyz", 4},
78 actual := dmp.DiffCommonPrefix(tc.Text1, tc.Text2)
79 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
83 func BenchmarkDiffCommonPrefix(b *testing.B) {
84 s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ"
88 for i := 0; i < b.N; i++ {
89 dmp.DiffCommonPrefix(s, s)
93 func TestCommonPrefixLength(t *testing.T) {
94 type TestCase struct {
101 for i, tc := range []TestCase{
103 {"1234abcdef", "1234xyz", 4},
104 {"1234", "1234xyz", 4},
106 actual := commonPrefixLength([]rune(tc.Text1), []rune(tc.Text2))
107 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
111 func TestDiffCommonSuffix(t *testing.T) {
112 type TestCase struct {
123 for i, tc := range []TestCase{
124 {"Null", "abc", "xyz", 0},
125 {"Non-null", "abcdef1234", "xyz1234", 4},
126 {"Whole", "1234", "xyz1234", 4},
128 actual := dmp.DiffCommonSuffix(tc.Text1, tc.Text2)
129 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
133 var SinkInt int // exported sink var to avoid compiler optimizations in benchmarks
135 func BenchmarkDiffCommonSuffix(b *testing.B) {
136 s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ"
142 for i := 0; i < b.N; i++ {
143 SinkInt = dmp.DiffCommonSuffix(s, s)
147 func BenchmarkCommonLength(b *testing.B) {
152 {name: "empty", x: nil, y: []rune{}},
153 {name: "short", x: []rune("AABCC"), y: []rune("AA-CC")},
155 x: []rune(strings.Repeat("A", 1000) + "B" + strings.Repeat("C", 1000)),
156 y: []rune(strings.Repeat("A", 1000) + "-" + strings.Repeat("C", 1000)),
159 b.Run("prefix", func(b *testing.B) {
160 for _, d := range data {
161 b.Run(d.name, func(b *testing.B) {
162 for i := 0; i < b.N; i++ {
163 SinkInt = commonPrefixLength(d.x, d.y)
168 b.Run("suffix", func(b *testing.B) {
169 for _, d := range data {
170 b.Run(d.name, func(b *testing.B) {
171 for i := 0; i < b.N; i++ {
172 SinkInt = commonSuffixLength(d.x, d.y)
179 func TestCommonSuffixLength(t *testing.T) {
180 type TestCase struct {
187 for i, tc := range []TestCase{
189 {"abcdef1234", "xyz1234", 4},
190 {"1234", "xyz1234", 4},
193 actual := commonSuffixLength([]rune(tc.Text1), []rune(tc.Text2))
194 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
198 func TestDiffCommonOverlap(t *testing.T) {
199 type TestCase struct {
210 for i, tc := range []TestCase{
211 {"Null", "", "abcd", 0},
212 {"Whole", "abc", "abcd", 3},
213 {"Null", "123456", "abcd", 0},
214 {"Null", "123456xxx", "xxxabcd", 3},
215 // Some overly clever languages (C#) may treat ligatures as equal to their component letters, e.g. U+FB01 == 'fi'
216 {"Unicode", "fi", "\ufb01i", 0},
218 actual := dmp.DiffCommonOverlap(tc.Text1, tc.Text2)
219 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
223 func TestDiffHalfMatch(t *testing.T) {
224 type TestCase struct {
234 for i, tc := range []TestCase{
236 {"1234567890", "abcdef", nil},
237 {"12345", "23", nil},
240 {"1234567890", "a345678z", []string{"12", "90", "a", "z", "345678"}},
241 {"a345678z", "1234567890", []string{"a", "z", "12", "90", "345678"}},
242 {"abc56789z", "1234567890", []string{"abc", "z", "1234", "0", "56789"}},
243 {"a23456xyz", "1234567890", []string{"a", "xyz", "1", "7890", "23456"}},
246 {"121231234123451234123121", "a1234123451234z", []string{"12123", "123121", "a", "z", "1234123451234"}},
247 {"x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=", []string{"", "-=-=-=-=-=", "x", "", "x-=-=-=-=-=-=-="}},
248 {"-=-=-=-=-=-=-=-=-=-=-=-=y", "-=-=-=-=-=-=-=yy", []string{"-=-=-=-=-=", "", "", "y", "-=-=-=-=-=-=-=y"}},
250 // Non-optimal halfmatch, ptimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy
251 {"qHilloHelloHew", "xHelloHeHulloy", []string{"qHillo", "w", "x", "Hulloy", "HelloHe"}},
253 actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2)
254 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
259 for i, tc := range []TestCase{
260 // Optimal no halfmatch
261 {"qHilloHelloHew", "xHelloHeHulloy", nil},
263 actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2)
264 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
268 func BenchmarkDiffHalfMatch(b *testing.B) {
269 s1, s2 := speedtestTexts()
275 for i := 0; i < b.N; i++ {
276 dmp.DiffHalfMatch(s1, s2)
280 func TestDiffBisectSplit(t *testing.T) {
281 type TestCase struct {
288 for _, tc := range []TestCase{
289 {"STUV\x05WX\x05YZ\x05[", "WĺĻļ\x05YZ\x05ĽľĿŀZ"},
291 diffs := dmp.diffBisectSplit([]rune(tc.Text1),
292 []rune(tc.Text2), 7, 6, time.Now().Add(time.Hour))
294 for _, d := range diffs {
295 assert.True(t, utf8.ValidString(d.Text))
298 // TODO define the expected outcome
302 func TestDiffLinesToChars(t *testing.T) {
303 type TestCase struct {
307 ExpectedChars1 string
308 ExpectedChars2 string
309 ExpectedLines []string
314 for i, tc := range []TestCase{
315 {"", "alpha\r\nbeta\r\n\r\n\r\n", "", "\u0001\u0002\u0003\u0003", []string{"", "alpha\r\n", "beta\r\n", "\r\n"}},
316 {"a", "b", "\u0001", "\u0002", []string{"", "a", "b"}},
317 // Omit final newline.
318 {"alpha\nbeta\nalpha", "", "\u0001\u0002\u0003", "", []string{"", "alpha\n", "beta\n", "alpha"}},
320 actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(tc.Text1, tc.Text2)
321 assert.Equal(t, tc.ExpectedChars1, actualChars1, fmt.Sprintf("Test case #%d, %#v", i, tc))
322 assert.Equal(t, tc.ExpectedChars2, actualChars2, fmt.Sprintf("Test case #%d, %#v", i, tc))
323 assert.Equal(t, tc.ExpectedLines, actualLines, fmt.Sprintf("Test case #%d, %#v", i, tc))
326 // More than 256 to reveal any 8-bit limitations.
328 lineList := []string{
329 "", // Account for the initial empty element of the lines array.
332 for x := 1; x < n+1; x++ {
333 lineList = append(lineList, strconv.Itoa(x)+"\n")
334 charList = append(charList, rune(x))
336 lines := strings.Join(lineList, "")
337 chars := string(charList)
338 assert.Equal(t, n, utf8.RuneCountInString(chars))
340 actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(lines, "")
341 assert.Equal(t, chars, actualChars1)
342 assert.Equal(t, "", actualChars2)
343 assert.Equal(t, lineList, actualLines)
346 func TestDiffCharsToLines(t *testing.T) {
347 type TestCase struct {
356 for i, tc := range []TestCase{
359 {DiffEqual, "\u0001\u0002\u0001"},
360 {DiffInsert, "\u0002\u0001\u0002"},
362 Lines: []string{"", "alpha\n", "beta\n"},
365 {DiffEqual, "alpha\nbeta\nalpha\n"},
366 {DiffInsert, "beta\nalpha\nbeta\n"},
370 actual := dmp.DiffCharsToLines(tc.Diffs, tc.Lines)
371 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
374 // More than 256 to reveal any 8-bit limitations.
376 lineList := []string{
377 "", // Account for the initial empty element of the lines array.
380 for x := 1; x <= n; x++ {
381 lineList = append(lineList, strconv.Itoa(x)+"\n")
382 charList = append(charList, rune(x))
384 assert.Equal(t, n, len(charList))
386 actual := dmp.DiffCharsToLines([]Diff{Diff{DiffDelete, string(charList)}}, lineList)
387 assert.Equal(t, []Diff{Diff{DiffDelete, strings.Join(lineList, "")}}, actual)
390 func TestDiffCleanupMerge(t *testing.T) {
391 type TestCase struct {
401 for i, tc := range []TestCase{
409 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}},
410 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}},
414 []Diff{Diff{DiffEqual, "a"}, Diff{DiffEqual, "b"}, Diff{DiffEqual, "c"}},
415 []Diff{Diff{DiffEqual, "abc"}},
419 []Diff{Diff{DiffDelete, "a"}, Diff{DiffDelete, "b"}, Diff{DiffDelete, "c"}},
420 []Diff{Diff{DiffDelete, "abc"}},
424 []Diff{Diff{DiffInsert, "a"}, Diff{DiffInsert, "b"}, Diff{DiffInsert, "c"}},
425 []Diff{Diff{DiffInsert, "abc"}},
429 []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}, Diff{DiffDelete, "c"}, Diff{DiffInsert, "d"}, Diff{DiffEqual, "e"}, Diff{DiffEqual, "f"}},
430 []Diff{Diff{DiffDelete, "ac"}, Diff{DiffInsert, "bd"}, Diff{DiffEqual, "ef"}},
433 "Prefix and suffix detection",
434 []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}},
435 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "c"}},
438 "Prefix and suffix detection with equalities",
439 []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}},
440 []Diff{Diff{DiffEqual, "xa"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}},
443 "Same test as above but with unicode (\u0101 will appear in diffs with at least 257 unique lines)",
444 []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "\u0101"}, Diff{DiffInsert, "\u0101bc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}},
445 []Diff{Diff{DiffEqual, "x\u0101"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}},
449 []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "ba"}, Diff{DiffEqual, "c"}},
450 []Diff{Diff{DiffInsert, "ab"}, Diff{DiffEqual, "ac"}},
454 []Diff{Diff{DiffEqual, "c"}, Diff{DiffInsert, "ab"}, Diff{DiffEqual, "a"}},
455 []Diff{Diff{DiffEqual, "ca"}, Diff{DiffInsert, "ba"}},
458 "Slide edit left recursive",
459 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "ac"}, Diff{DiffEqual, "x"}},
460 []Diff{Diff{DiffDelete, "abc"}, Diff{DiffEqual, "acx"}},
463 "Slide edit right recursive",
464 []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "ca"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "a"}},
465 []Diff{Diff{DiffEqual, "xca"}, Diff{DiffDelete, "cba"}},
468 actual := dmp.DiffCleanupMerge(tc.Diffs)
469 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
473 func TestDiffCleanupSemanticLossless(t *testing.T) {
474 type TestCase struct {
484 for i, tc := range []TestCase{
493 Diff{DiffEqual, "AAA\r\n\r\nBBB"},
494 Diff{DiffInsert, "\r\nDDD\r\n\r\nBBB"},
495 Diff{DiffEqual, "\r\nEEE"},
498 Diff{DiffEqual, "AAA\r\n\r\n"},
499 Diff{DiffInsert, "BBB\r\nDDD\r\n\r\n"},
500 Diff{DiffEqual, "BBB\r\nEEE"},
506 Diff{DiffEqual, "AAA\r\nBBB"},
507 Diff{DiffInsert, " DDD\r\nBBB"},
508 Diff{DiffEqual, " EEE"},
511 Diff{DiffEqual, "AAA\r\n"},
512 Diff{DiffInsert, "BBB DDD\r\n"},
513 Diff{DiffEqual, "BBB EEE"},
519 Diff{DiffEqual, "The c"},
520 Diff{DiffInsert, "ow and the c"},
521 Diff{DiffEqual, "at."},
524 Diff{DiffEqual, "The "},
525 Diff{DiffInsert, "cow and the "},
526 Diff{DiffEqual, "cat."},
530 "Alphanumeric boundaries",
532 Diff{DiffEqual, "The-c"},
533 Diff{DiffInsert, "ow-and-the-c"},
534 Diff{DiffEqual, "at."},
537 Diff{DiffEqual, "The-"},
538 Diff{DiffInsert, "cow-and-the-"},
539 Diff{DiffEqual, "cat."},
545 Diff{DiffEqual, "a"},
546 Diff{DiffDelete, "a"},
547 Diff{DiffEqual, "ax"},
550 Diff{DiffDelete, "a"},
551 Diff{DiffEqual, "aax"},
557 Diff{DiffEqual, "xa"},
558 Diff{DiffDelete, "a"},
559 Diff{DiffEqual, "a"},
562 Diff{DiffEqual, "xaa"},
563 Diff{DiffDelete, "a"},
567 "Sentence boundaries",
569 Diff{DiffEqual, "The xxx. The "},
570 Diff{DiffInsert, "zzz. The "},
571 Diff{DiffEqual, "yyy."},
574 Diff{DiffEqual, "The xxx."},
575 Diff{DiffInsert, " The zzz."},
576 Diff{DiffEqual, " The yyy."},
582 Diff{DiffEqual, "The ♕. The "},
583 Diff{DiffInsert, "♔. The "},
584 Diff{DiffEqual, "♖."},
587 Diff{DiffEqual, "The ♕."},
588 Diff{DiffInsert, " The ♔."},
589 Diff{DiffEqual, " The ♖."},
595 Diff{DiffEqual, "♕♕"},
596 Diff{DiffInsert, "♔♔"},
597 Diff{DiffEqual, "♖♖"},
600 Diff{DiffEqual, "♕♕"},
601 Diff{DiffInsert, "♔♔"},
602 Diff{DiffEqual, "♖♖"},
606 actual := dmp.DiffCleanupSemanticLossless(tc.Diffs)
607 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
611 func TestDiffCleanupSemantic(t *testing.T) {
612 type TestCase struct {
622 for i, tc := range []TestCase{
649 {DiffDelete, "wxyz"},
655 {DiffDelete, "wxyz"},
661 {DiffEqual, "2016-09-01T03:07:1"},
662 {DiffInsert, "5.15"},
668 {DiffDelete, "3074"},
672 {DiffEqual, "2016-09-01T03:07:1"},
673 {DiffInsert, "5.15"},
679 {DiffDelete, "3074"},
684 "Simple elimination",
696 "Backpass elimination",
705 {DiffDelete, "abcdef"},
706 {DiffInsert, "cdfg"},
710 "Multiple eliminations",
723 {DiffDelete, "AB_AB"},
724 {DiffInsert, "1A2_1A2"},
730 {DiffEqual, "The c"},
731 {DiffDelete, "ow and the c"},
736 {DiffDelete, "cow and the "},
741 "No overlap elimination",
743 {DiffDelete, "abcxx"},
744 {DiffInsert, "xxdef"},
747 {DiffDelete, "abcxx"},
748 {DiffInsert, "xxdef"},
752 "Overlap elimination",
754 {DiffDelete, "abcxxx"},
755 {DiffInsert, "xxxdef"},
764 "Reverse overlap elimination",
766 {DiffDelete, "xxxabc"},
767 {DiffInsert, "defxxx"},
776 "Two overlap eliminations",
778 {DiffDelete, "abcd1212"},
779 {DiffInsert, "1212efghi"},
785 {DiffDelete, "abcd"},
787 {DiffInsert, "efghi"},
795 "Test case for adapting DiffCleanupSemantic to be equal to the Python version #19",
797 {DiffEqual, "James McCarthy "},
798 {DiffDelete, "close to "},
802 {DiffEqual, " new "},
806 {DiffInsert, "-yea"},
809 {DiffEqual, " deal"},
810 {DiffInsert, " at Everton"},
813 {DiffEqual, "James McCarthy "},
814 {DiffDelete, "close to "},
818 {DiffEqual, " new "},
819 {DiffInsert, "five-year deal at "},
820 {DiffEqual, "Everton"},
821 {DiffDelete, " deal"},
825 actual := dmp.DiffCleanupSemantic(tc.Diffs)
826 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
830 func BenchmarkDiffCleanupSemantic(b *testing.B) {
831 s1, s2 := speedtestTexts()
835 diffs := dmp.DiffMain(s1, s2, false)
839 for i := 0; i < b.N; i++ {
840 dmp.DiffCleanupSemantic(diffs)
844 func TestDiffCleanupEfficiency(t *testing.T) {
845 type TestCase struct {
856 for i, tc := range []TestCase{
865 Diff{DiffDelete, "ab"},
866 Diff{DiffInsert, "12"},
867 Diff{DiffEqual, "wxyz"},
868 Diff{DiffDelete, "cd"},
869 Diff{DiffInsert, "34"},
872 Diff{DiffDelete, "ab"},
873 Diff{DiffInsert, "12"},
874 Diff{DiffEqual, "wxyz"},
875 Diff{DiffDelete, "cd"},
876 Diff{DiffInsert, "34"},
880 "Four-edit elimination",
882 Diff{DiffDelete, "ab"},
883 Diff{DiffInsert, "12"},
884 Diff{DiffEqual, "xyz"},
885 Diff{DiffDelete, "cd"},
886 Diff{DiffInsert, "34"},
889 Diff{DiffDelete, "abxyzcd"},
890 Diff{DiffInsert, "12xyz34"},
894 "Three-edit elimination",
896 Diff{DiffInsert, "12"},
897 Diff{DiffEqual, "x"},
898 Diff{DiffDelete, "cd"},
899 Diff{DiffInsert, "34"},
902 Diff{DiffDelete, "xcd"},
903 Diff{DiffInsert, "12x34"},
907 "Backpass elimination",
909 Diff{DiffDelete, "ab"},
910 Diff{DiffInsert, "12"},
911 Diff{DiffEqual, "xy"},
912 Diff{DiffInsert, "34"},
913 Diff{DiffEqual, "z"},
914 Diff{DiffDelete, "cd"},
915 Diff{DiffInsert, "56"},
918 Diff{DiffDelete, "abxyzcd"},
919 Diff{DiffInsert, "12xy34z56"},
923 actual := dmp.DiffCleanupEfficiency(tc.Diffs)
924 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
929 for i, tc := range []TestCase{
931 "High cost elimination",
933 Diff{DiffDelete, "ab"},
934 Diff{DiffInsert, "12"},
935 Diff{DiffEqual, "wxyz"},
936 Diff{DiffDelete, "cd"},
937 Diff{DiffInsert, "34"},
940 Diff{DiffDelete, "abwxyzcd"},
941 Diff{DiffInsert, "12wxyz34"},
945 actual := dmp.DiffCleanupEfficiency(tc.Diffs)
946 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
950 func TestDiffPrettyHtml(t *testing.T) {
951 type TestCase struct {
959 for i, tc := range []TestCase{
963 {DiffDelete, "<B>b</B>"},
967 Expected: "<span>a¶<br></span><del style=\"background:#ffe6e6;\"><B>b</B></del><ins style=\"background:#e6ffe6;\">c&d</ins>",
970 actual := dmp.DiffPrettyHtml(tc.Diffs)
971 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
975 func TestDiffPrettyText(t *testing.T) {
976 type TestCase struct {
984 for i, tc := range []TestCase{
988 {DiffDelete, "<B>b</B>"},
992 Expected: "a\n\x1b[31m<B>b</B>\x1b[0m\x1b[32mc&d\x1b[0m",
995 actual := dmp.DiffPrettyText(tc.Diffs)
996 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
1000 func TestDiffText(t *testing.T) {
1001 type TestCase struct {
1004 ExpectedText1 string
1005 ExpectedText2 string
1010 for i, tc := range []TestCase{
1013 {DiffEqual, "jump"},
1016 {DiffEqual, " over "},
1017 {DiffDelete, "the"},
1019 {DiffEqual, " lazy"},
1022 ExpectedText1: "jumps over the lazy",
1023 ExpectedText2: "jumped over a lazy",
1026 actualText1 := dmp.DiffText1(tc.Diffs)
1027 assert.Equal(t, tc.ExpectedText1, actualText1, fmt.Sprintf("Test case #%d, %#v", i, tc))
1029 actualText2 := dmp.DiffText2(tc.Diffs)
1030 assert.Equal(t, tc.ExpectedText2, actualText2, fmt.Sprintf("Test case #%d, %#v", i, tc))
1034 func TestDiffDelta(t *testing.T) {
1035 type TestCase struct {
1041 ErrorMessagePrefix string
1046 for i, tc := range []TestCase{
1047 {"Delta shorter than text", "jumps over the lazyx", "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", "Delta length (19) is different from source text length (20)"},
1048 {"Delta longer than text", "umps over the lazy", "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", "Delta length (19) is different from source text length (18)"},
1049 {"Invalid URL escaping", "", "+%c3%xy", "invalid URL escape \"%xy\""},
1050 {"Invalid UTF-8 sequence", "", "+%c3xy", "invalid UTF-8 token: \"\\xc3xy\""},
1051 {"Invalid diff operation", "", "a", "Invalid diff operation in DiffFromDelta: a"},
1052 {"Invalid diff syntax", "", "-", "strconv.ParseInt: parsing \"\": invalid syntax"},
1053 {"Negative number in delta", "", "--1", "Negative number in DiffFromDelta: -1"},
1054 {"Empty case", "", "", ""},
1056 diffs, err := dmp.DiffFromDelta(tc.Text, tc.Delta)
1057 msg := fmt.Sprintf("Test case #%d, %s", i, tc.Name)
1058 if tc.ErrorMessagePrefix == "" {
1059 assert.Nil(t, err, msg)
1060 assert.Nil(t, diffs, msg)
1063 if strings.HasPrefix(e, tc.ErrorMessagePrefix) {
1064 e = tc.ErrorMessagePrefix
1066 assert.Nil(t, diffs, msg)
1067 assert.Equal(t, tc.ErrorMessagePrefix, e, msg)
1071 // Convert a diff into delta string.
1073 Diff{DiffEqual, "jump"},
1074 Diff{DiffDelete, "s"},
1075 Diff{DiffInsert, "ed"},
1076 Diff{DiffEqual, " over "},
1077 Diff{DiffDelete, "the"},
1078 Diff{DiffInsert, "a"},
1079 Diff{DiffEqual, " lazy"},
1080 Diff{DiffInsert, "old dog"},
1082 text1 := dmp.DiffText1(diffs)
1083 assert.Equal(t, "jumps over the lazy", text1)
1085 delta := dmp.DiffToDelta(diffs)
1086 assert.Equal(t, "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta)
1088 // Convert delta string into a diff.
1089 deltaDiffs, err := dmp.DiffFromDelta(text1, delta)
1090 assert.Equal(t, diffs, deltaDiffs)
1092 // Test deltas with special characters.
1094 Diff{DiffEqual, "\u0680 \x00 \t %"},
1095 Diff{DiffDelete, "\u0681 \x01 \n ^"},
1096 Diff{DiffInsert, "\u0682 \x02 \\ |"},
1098 text1 = dmp.DiffText1(diffs)
1099 assert.Equal(t, "\u0680 \x00 \t %\u0681 \x01 \n ^", text1)
1101 // Lowercase, due to UrlEncode uses lower.
1102 delta = dmp.DiffToDelta(diffs)
1103 assert.Equal(t, "=7\t-7\t+%DA%82 %02 %5C %7C", delta)
1105 deltaDiffs, err = dmp.DiffFromDelta(text1, delta)
1106 assert.Equal(t, diffs, deltaDiffs)
1109 // Verify pool of unchanged characters.
1111 Diff{DiffInsert, "A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # "},
1114 delta = dmp.DiffToDelta(diffs)
1115 assert.Equal(t, "+A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ", delta, "Unchanged characters.")
1117 // Convert delta string into a diff.
1118 deltaDiffs, err = dmp.DiffFromDelta("", delta)
1119 assert.Equal(t, diffs, deltaDiffs)
1123 func TestDiffXIndex(t *testing.T) {
1124 type TestCase struct {
1135 for i, tc := range []TestCase{
1136 {"Translation on equality", []Diff{{DiffDelete, "a"}, {DiffInsert, "1234"}, {DiffEqual, "xyz"}}, 2, 5},
1137 {"Translation on deletion", []Diff{{DiffEqual, "a"}, {DiffDelete, "1234"}, {DiffEqual, "xyz"}}, 3, 1},
1139 actual := dmp.DiffXIndex(tc.Diffs, tc.Location)
1140 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
1144 func TestDiffLevenshtein(t *testing.T) {
1145 type TestCase struct {
1155 for i, tc := range []TestCase{
1156 {"Levenshtein with trailing equality", []Diff{{DiffDelete, "абв"}, {DiffInsert, "1234"}, {DiffEqual, "эюя"}}, 4},
1157 {"Levenshtein with leading equality", []Diff{{DiffEqual, "эюя"}, {DiffDelete, "абв"}, {DiffInsert, "1234"}}, 4},
1158 {"Levenshtein with middle equality", []Diff{{DiffDelete, "абв"}, {DiffEqual, "эюя"}, {DiffInsert, "1234"}}, 7},
1160 actual := dmp.DiffLevenshtein(tc.Diffs)
1161 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
1165 func TestDiffBisect(t *testing.T) {
1166 type TestCase struct {
1176 for i, tc := range []TestCase{
1179 Time: time.Date(9999, time.December, 31, 23, 59, 59, 59, time.UTC),
1190 Name: "Negative deadlines count as having infinite time",
1191 Time: time.Date(0001, time.January, 01, 00, 00, 00, 00, time.UTC),
1203 Time: time.Now().Add(time.Nanosecond),
1206 {DiffDelete, "cat"},
1207 {DiffInsert, "map"},
1211 actual := dmp.DiffBisect("cat", "map", tc.Time)
1212 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
1215 // Test for invalid UTF-8 sequences
1216 assert.Equal(t, []Diff{
1217 Diff{DiffEqual, "��"},
1218 }, dmp.DiffBisect("\xe0\xe5", "\xe0\xe5", time.Now().Add(time.Minute)))
1221 func TestDiffMain(t *testing.T) {
1222 type TestCase struct {
1231 // Perform a trivial diff.
1232 for i, tc := range []TestCase{
1241 []Diff{Diff{DiffEqual, "abc"}},
1246 []Diff{Diff{DiffEqual, "ab"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "c"}},
1251 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "bc"}},
1256 []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "b"}, Diff{DiffInsert, "456"}, Diff{DiffEqual, "c"}},
1261 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "b"}, Diff{DiffDelete, "456"}, Diff{DiffEqual, "c"}},
1264 actual := dmp.DiffMain(tc.Text1, tc.Text2, false)
1265 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
1268 // Perform a real diff and switch off the timeout.
1271 for i, tc := range []TestCase{
1275 []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}},
1278 "Apples are a fruit.",
1279 "Bananas are also fruit.",
1281 Diff{DiffDelete, "Apple"},
1282 Diff{DiffInsert, "Banana"},
1283 Diff{DiffEqual, "s are a"},
1284 Diff{DiffInsert, "lso"},
1285 Diff{DiffEqual, " fruit."},
1292 Diff{DiffDelete, "a"},
1293 Diff{DiffInsert, "\u0680"},
1294 Diff{DiffEqual, "x"},
1295 Diff{DiffDelete, "\t"},
1296 Diff{DiffInsert, "\u0000"},
1303 Diff{DiffDelete, "1"},
1304 Diff{DiffEqual, "a"},
1305 Diff{DiffDelete, "y"},
1306 Diff{DiffEqual, "b"},
1307 Diff{DiffDelete, "2"},
1308 Diff{DiffInsert, "xab"},
1315 Diff{DiffInsert, "xaxcx"},
1316 Diff{DiffEqual, "abc"}, Diff{DiffDelete, "y"},
1320 "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg",
1321 "a-bcd-efghijklmnopqrs",
1323 Diff{DiffDelete, "ABCD"},
1324 Diff{DiffEqual, "a"},
1325 Diff{DiffDelete, "="},
1326 Diff{DiffInsert, "-"},
1327 Diff{DiffEqual, "bcd"},
1328 Diff{DiffDelete, "="},
1329 Diff{DiffInsert, "-"},
1330 Diff{DiffEqual, "efghijklmnopqrs"},
1331 Diff{DiffDelete, "EFGHIJKLMNOefg"},
1335 "a [[Pennsylvania]] and [[New",
1336 " and [[Pennsylvania]]",
1338 Diff{DiffInsert, " "},
1339 Diff{DiffEqual, "a"},
1340 Diff{DiffInsert, "nd"},
1341 Diff{DiffEqual, " [[Pennsylvania]]"},
1342 Diff{DiffDelete, " and [[New"},
1346 actual := dmp.DiffMain(tc.Text1, tc.Text2, false)
1347 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
1350 // Test for invalid UTF-8 sequences
1351 assert.Equal(t, []Diff{
1352 Diff{DiffDelete, "��"},
1353 }, dmp.DiffMain("\xe0\xe5", "", false))
1356 func TestDiffMainWithTimeout(t *testing.T) {
1358 dmp.DiffTimeout = 200 * time.Millisecond
1360 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"
1361 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"
1362 // Increase the text lengths by 1024 times to ensure a timeout.
1363 for x := 0; x < 13; x++ {
1368 startTime := time.Now()
1369 dmp.DiffMain(a, b, true)
1370 endTime := time.Now()
1372 delta := endTime.Sub(startTime)
1374 // Test that we took at least the timeout period.
1375 assert.True(t, delta >= dmp.DiffTimeout, fmt.Sprintf("%v !>= %v", delta, dmp.DiffTimeout))
1377 // 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.
1378 assert.True(t, delta < (dmp.DiffTimeout*100), fmt.Sprintf("%v !< %v", delta, dmp.DiffTimeout*100))
1381 func TestDiffMainWithCheckLines(t *testing.T) {
1382 type TestCase struct {
1390 // Test cases must be at least 100 chars long to pass the cutoff.
1391 for i, tc := range []TestCase{
1393 "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n",
1394 "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n",
1397 "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
1398 "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij",
1401 "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n",
1402 "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n",
1405 resultWithoutCheckLines := dmp.DiffMain(tc.Text1, tc.Text2, false)
1406 resultWithCheckLines := dmp.DiffMain(tc.Text1, tc.Text2, true)
1408 // TODO this fails for the third test case, why?
1410 assert.Equal(t, resultWithoutCheckLines, resultWithCheckLines, fmt.Sprintf("Test case #%d, %#v", i, tc))
1412 assert.Equal(t, diffRebuildTexts(resultWithoutCheckLines), diffRebuildTexts(resultWithCheckLines), fmt.Sprintf("Test case #%d, %#v", i, tc))
1416 func BenchmarkDiffMain(bench *testing.B) {
1417 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"
1418 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"
1420 // Increase the text lengths by 1024 times to ensure a timeout.
1421 for x := 0; x < 10; x++ {
1427 dmp.DiffTimeout = time.Second
1431 for i := 0; i < bench.N; i++ {
1432 dmp.DiffMain(s1, s2, true)
1436 func BenchmarkDiffMainLarge(b *testing.B) {
1437 s1, s2 := speedtestTexts()
1443 for i := 0; i < b.N; i++ {
1444 dmp.DiffMain(s1, s2, true)
1448 func BenchmarkDiffMainRunesLargeLines(b *testing.B) {
1449 s1, s2 := speedtestTexts()
1455 for i := 0; i < b.N; i++ {
1456 text1, text2, linearray := dmp.DiffLinesToRunes(s1, s2)
1458 diffs := dmp.DiffMainRunes(text1, text2, false)
1459 diffs = dmp.DiffCharsToLines(diffs, linearray)