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. 4// 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/ 8 9package diffmatchpatch 10 11import ( 12 "bytes" 13 "fmt" 14 "strconv" 15 "strings" 16 "testing" 17 "time" 18 "unicode/utf8" 19 20 "github.com/stretchr/testify/assert" 21) 22 23func pretty(diffs []Diff) string { 24 var w bytes.Buffer 25 26 for i, diff := range diffs { 27 _, _ = w.WriteString(fmt.Sprintf("%v. ", i)) 28 29 switch diff.Type { 30 case DiffInsert: 31 _, _ = w.WriteString("DiffIns") 32 case DiffDelete: 33 _, _ = w.WriteString("DiffDel") 34 case DiffEqual: 35 _, _ = w.WriteString("DiffEql") 36 default: 37 _, _ = w.WriteString("Unknown") 38 } 39 40 _, _ = w.WriteString(fmt.Sprintf(": %v\n", diff.Text)) 41 } 42 43 return w.String() 44} 45 46func diffRebuildTexts(diffs []Diff) []string { 47 texts := []string{"", ""} 48 49 for _, d := range diffs { 50 if d.Type != DiffInsert { 51 texts[0] += d.Text 52 } 53 if d.Type != DiffDelete { 54 texts[1] += d.Text 55 } 56 } 57 58 return texts 59} 60 61func TestDiffCommonPrefix(t *testing.T) { 62 type TestCase struct { 63 Name string 64 65 Text1 string 66 Text2 string 67 68 Expected int 69 } 70 71 dmp := New() 72 73 for i, tc := range []TestCase{ 74 {"Null", "abc", "xyz", 0}, 75 {"Non-null", "1234abcdef", "1234xyz", 4}, 76 {"Whole", "1234", "1234xyz", 4}, 77 } { 78 actual := dmp.DiffCommonPrefix(tc.Text1, tc.Text2) 79 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 80 } 81} 82 83func BenchmarkDiffCommonPrefix(b *testing.B) { 84 s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" 85 86 dmp := New() 87 88 for i := 0; i < b.N; i++ { 89 dmp.DiffCommonPrefix(s, s) 90 } 91} 92 93func TestCommonPrefixLength(t *testing.T) { 94 type TestCase struct { 95 Text1 string 96 Text2 string 97 98 Expected int 99 } 100 101 for i, tc := range []TestCase{ 102 {"abc", "xyz", 0}, 103 {"1234abcdef", "1234xyz", 4}, 104 {"1234", "1234xyz", 4}, 105 } { 106 actual := commonPrefixLength([]rune(tc.Text1), []rune(tc.Text2)) 107 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 108 } 109} 110 111func TestDiffCommonSuffix(t *testing.T) { 112 type TestCase struct { 113 Name string 114 115 Text1 string 116 Text2 string 117 118 Expected int 119 } 120 121 dmp := New() 122 123 for i, tc := range []TestCase{ 124 {"Null", "abc", "xyz", 0}, 125 {"Non-null", "abcdef1234", "xyz1234", 4}, 126 {"Whole", "1234", "xyz1234", 4}, 127 } { 128 actual := dmp.DiffCommonSuffix(tc.Text1, tc.Text2) 129 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 130 } 131} 132 133func BenchmarkDiffCommonSuffix(b *testing.B) { 134 s := "ABCDEFGHIJKLMNOPQRSTUVWXYZÅÄÖ" 135 136 dmp := New() 137 138 b.ResetTimer() 139 140 for i := 0; i < b.N; i++ { 141 dmp.DiffCommonSuffix(s, s) 142 } 143} 144 145func TestCommonSuffixLength(t *testing.T) { 146 type TestCase struct { 147 Text1 string 148 Text2 string 149 150 Expected int 151 } 152 153 for i, tc := range []TestCase{ 154 {"abc", "xyz", 0}, 155 {"abcdef1234", "xyz1234", 4}, 156 {"1234", "xyz1234", 4}, 157 {"123", "a3", 1}, 158 } { 159 actual := commonSuffixLength([]rune(tc.Text1), []rune(tc.Text2)) 160 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 161 } 162} 163 164func TestDiffCommonOverlap(t *testing.T) { 165 type TestCase struct { 166 Name string 167 168 Text1 string 169 Text2 string 170 171 Expected int 172 } 173 174 dmp := New() 175 176 for i, tc := range []TestCase{ 177 {"Null", "", "abcd", 0}, 178 {"Whole", "abc", "abcd", 3}, 179 {"Null", "123456", "abcd", 0}, 180 {"Null", "123456xxx", "xxxabcd", 3}, 181 // Some overly clever languages (C#) may treat ligatures as equal to their component letters, e.g. U+FB01 == 'fi' 182 {"Unicode", "fi", "\ufb01i", 0}, 183 } { 184 actual := dmp.DiffCommonOverlap(tc.Text1, tc.Text2) 185 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 186 } 187} 188 189func TestDiffHalfMatch(t *testing.T) { 190 type TestCase struct { 191 Text1 string 192 Text2 string 193 194 Expected []string 195 } 196 197 dmp := New() 198 dmp.DiffTimeout = 1 199 200 for i, tc := range []TestCase{ 201 // No match 202 {"1234567890", "abcdef", nil}, 203 {"12345", "23", nil}, 204 205 // Single Match 206 {"1234567890", "a345678z", []string{"12", "90", "a", "z", "345678"}}, 207 {"a345678z", "1234567890", []string{"a", "z", "12", "90", "345678"}}, 208 {"abc56789z", "1234567890", []string{"abc", "z", "1234", "0", "56789"}}, 209 {"a23456xyz", "1234567890", []string{"a", "xyz", "1", "7890", "23456"}}, 210 211 // Multiple Matches 212 {"121231234123451234123121", "a1234123451234z", []string{"12123", "123121", "a", "z", "1234123451234"}}, 213 {"x-=-=-=-=-=-=-=-=-=-=-=-=", "xx-=-=-=-=-=-=-=", []string{"", "-=-=-=-=-=", "x", "", "x-=-=-=-=-=-=-="}}, 214 {"-=-=-=-=-=-=-=-=-=-=-=-=y", "-=-=-=-=-=-=-=yy", []string{"-=-=-=-=-=", "", "", "y", "-=-=-=-=-=-=-=y"}}, 215 216 // Non-optimal halfmatch, ptimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y not -qHillo+x=HelloHe-w+Hulloy 217 {"qHilloHelloHew", "xHelloHeHulloy", []string{"qHillo", "w", "x", "Hulloy", "HelloHe"}}, 218 } { 219 actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2) 220 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 221 } 222 223 dmp.DiffTimeout = 0 224 225 for i, tc := range []TestCase{ 226 // Optimal no halfmatch 227 {"qHilloHelloHew", "xHelloHeHulloy", nil}, 228 } { 229 actual := dmp.DiffHalfMatch(tc.Text1, tc.Text2) 230 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 231 } 232} 233 234func BenchmarkDiffHalfMatch(b *testing.B) { 235 s1, s2 := speedtestTexts() 236 237 dmp := New() 238 239 b.ResetTimer() 240 241 for i := 0; i < b.N; i++ { 242 dmp.DiffHalfMatch(s1, s2) 243 } 244} 245 246func TestDiffBisectSplit(t *testing.T) { 247 type TestCase struct { 248 Text1 string 249 Text2 string 250 } 251 252 dmp := New() 253 254 for _, tc := range []TestCase{ 255 {"STUV\x05WX\x05YZ\x05[", "WĺĻļ\x05YZ\x05ĽľĿŀZ"}, 256 } { 257 diffs := dmp.diffBisectSplit([]rune(tc.Text1), 258 []rune(tc.Text2), 7, 6, time.Now().Add(time.Hour)) 259 260 for _, d := range diffs { 261 assert.True(t, utf8.ValidString(d.Text)) 262 } 263 264 // TODO define the expected outcome 265 } 266} 267 268func TestDiffLinesToChars(t *testing.T) { 269 type TestCase struct { 270 Text1 string 271 Text2 string 272 273 ExpectedChars1 string 274 ExpectedChars2 string 275 ExpectedLines []string 276 } 277 278 dmp := New() 279 280 for i, tc := range []TestCase{ 281 {"", "alpha\r\nbeta\r\n\r\n\r\n", "", "\u0001\u0002\u0003\u0003", []string{"", "alpha\r\n", "beta\r\n", "\r\n"}}, 282 {"a", "b", "\u0001", "\u0002", []string{"", "a", "b"}}, 283 // Omit final newline. 284 {"alpha\nbeta\nalpha", "", "\u0001\u0002\u0003", "", []string{"", "alpha\n", "beta\n", "alpha"}}, 285 } { 286 actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(tc.Text1, tc.Text2) 287 assert.Equal(t, tc.ExpectedChars1, actualChars1, fmt.Sprintf("Test case #%d, %#v", i, tc)) 288 assert.Equal(t, tc.ExpectedChars2, actualChars2, fmt.Sprintf("Test case #%d, %#v", i, tc)) 289 assert.Equal(t, tc.ExpectedLines, actualLines, fmt.Sprintf("Test case #%d, %#v", i, tc)) 290 } 291 292 // More than 256 to reveal any 8-bit limitations. 293 n := 300 294 lineList := []string{ 295 "", // Account for the initial empty element of the lines array. 296 } 297 var charList []rune 298 for x := 1; x < n+1; x++ { 299 lineList = append(lineList, strconv.Itoa(x)+"\n") 300 charList = append(charList, rune(x)) 301 } 302 lines := strings.Join(lineList, "") 303 chars := string(charList) 304 assert.Equal(t, n, utf8.RuneCountInString(chars)) 305 306 actualChars1, actualChars2, actualLines := dmp.DiffLinesToChars(lines, "") 307 assert.Equal(t, chars, actualChars1) 308 assert.Equal(t, "", actualChars2) 309 assert.Equal(t, lineList, actualLines) 310} 311 312func TestDiffCharsToLines(t *testing.T) { 313 type TestCase struct { 314 Diffs []Diff 315 Lines []string 316 317 Expected []Diff 318 } 319 320 dmp := New() 321 322 for i, tc := range []TestCase{ 323 { 324 Diffs: []Diff{ 325 {DiffEqual, "\u0001\u0002\u0001"}, 326 {DiffInsert, "\u0002\u0001\u0002"}, 327 }, 328 Lines: []string{"", "alpha\n", "beta\n"}, 329 330 Expected: []Diff{ 331 {DiffEqual, "alpha\nbeta\nalpha\n"}, 332 {DiffInsert, "beta\nalpha\nbeta\n"}, 333 }, 334 }, 335 } { 336 actual := dmp.DiffCharsToLines(tc.Diffs, tc.Lines) 337 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 338 } 339 340 // More than 256 to reveal any 8-bit limitations. 341 n := 300 342 lineList := []string{ 343 "", // Account for the initial empty element of the lines array. 344 } 345 charList := []rune{} 346 for x := 1; x <= n; x++ { 347 lineList = append(lineList, strconv.Itoa(x)+"\n") 348 charList = append(charList, rune(x)) 349 } 350 assert.Equal(t, n, len(charList)) 351 352 actual := dmp.DiffCharsToLines([]Diff{Diff{DiffDelete, string(charList)}}, lineList) 353 assert.Equal(t, []Diff{Diff{DiffDelete, strings.Join(lineList, "")}}, actual) 354} 355 356func TestDiffCleanupMerge(t *testing.T) { 357 type TestCase struct { 358 Name string 359 360 Diffs []Diff 361 362 Expected []Diff 363 } 364 365 dmp := New() 366 367 for i, tc := range []TestCase{ 368 { 369 "Null case", 370 []Diff{}, 371 []Diff{}, 372 }, 373 { 374 "No Diff case", 375 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, 376 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffInsert, "c"}}, 377 }, 378 { 379 "Merge equalities", 380 []Diff{Diff{DiffEqual, "a"}, Diff{DiffEqual, "b"}, Diff{DiffEqual, "c"}}, 381 []Diff{Diff{DiffEqual, "abc"}}, 382 }, 383 { 384 "Merge deletions", 385 []Diff{Diff{DiffDelete, "a"}, Diff{DiffDelete, "b"}, Diff{DiffDelete, "c"}}, 386 []Diff{Diff{DiffDelete, "abc"}}, 387 }, 388 { 389 "Merge insertions", 390 []Diff{Diff{DiffInsert, "a"}, Diff{DiffInsert, "b"}, Diff{DiffInsert, "c"}}, 391 []Diff{Diff{DiffInsert, "abc"}}, 392 }, 393 { 394 "Merge interweave", 395 []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}, Diff{DiffDelete, "c"}, Diff{DiffInsert, "d"}, Diff{DiffEqual, "e"}, Diff{DiffEqual, "f"}}, 396 []Diff{Diff{DiffDelete, "ac"}, Diff{DiffInsert, "bd"}, Diff{DiffEqual, "ef"}}, 397 }, 398 { 399 "Prefix and suffix detection", 400 []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}}, 401 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "c"}}, 402 }, 403 { 404 "Prefix and suffix detection with equalities", 405 []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "a"}, Diff{DiffInsert, "abc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}}, 406 []Diff{Diff{DiffEqual, "xa"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, 407 }, 408 { 409 "Same test as above but with unicode (\u0101 will appear in diffs with at least 257 unique lines)", 410 []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "\u0101"}, Diff{DiffInsert, "\u0101bc"}, Diff{DiffDelete, "dc"}, Diff{DiffEqual, "y"}}, 411 []Diff{Diff{DiffEqual, "x\u0101"}, Diff{DiffDelete, "d"}, Diff{DiffInsert, "b"}, Diff{DiffEqual, "cy"}}, 412 }, 413 { 414 "Slide edit left", 415 []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "ba"}, Diff{DiffEqual, "c"}}, 416 []Diff{Diff{DiffInsert, "ab"}, Diff{DiffEqual, "ac"}}, 417 }, 418 { 419 "Slide edit right", 420 []Diff{Diff{DiffEqual, "c"}, Diff{DiffInsert, "ab"}, Diff{DiffEqual, "a"}}, 421 []Diff{Diff{DiffEqual, "ca"}, Diff{DiffInsert, "ba"}}, 422 }, 423 { 424 "Slide edit left recursive", 425 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "ac"}, Diff{DiffEqual, "x"}}, 426 []Diff{Diff{DiffDelete, "abc"}, Diff{DiffEqual, "acx"}}, 427 }, 428 { 429 "Slide edit right recursive", 430 []Diff{Diff{DiffEqual, "x"}, Diff{DiffDelete, "ca"}, Diff{DiffEqual, "c"}, Diff{DiffDelete, "b"}, Diff{DiffEqual, "a"}}, 431 []Diff{Diff{DiffEqual, "xca"}, Diff{DiffDelete, "cba"}}, 432 }, 433 } { 434 actual := dmp.DiffCleanupMerge(tc.Diffs) 435 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 436 } 437} 438 439func TestDiffCleanupSemanticLossless(t *testing.T) { 440 type TestCase struct { 441 Name string 442 443 Diffs []Diff 444 445 Expected []Diff 446 } 447 448 dmp := New() 449 450 for i, tc := range []TestCase{ 451 { 452 "Null case", 453 []Diff{}, 454 []Diff{}, 455 }, 456 { 457 "Blank lines", 458 []Diff{ 459 Diff{DiffEqual, "AAA\r\n\r\nBBB"}, 460 Diff{DiffInsert, "\r\nDDD\r\n\r\nBBB"}, 461 Diff{DiffEqual, "\r\nEEE"}, 462 }, 463 []Diff{ 464 Diff{DiffEqual, "AAA\r\n\r\n"}, 465 Diff{DiffInsert, "BBB\r\nDDD\r\n\r\n"}, 466 Diff{DiffEqual, "BBB\r\nEEE"}, 467 }, 468 }, 469 { 470 "Line boundaries", 471 []Diff{ 472 Diff{DiffEqual, "AAA\r\nBBB"}, 473 Diff{DiffInsert, " DDD\r\nBBB"}, 474 Diff{DiffEqual, " EEE"}, 475 }, 476 []Diff{ 477 Diff{DiffEqual, "AAA\r\n"}, 478 Diff{DiffInsert, "BBB DDD\r\n"}, 479 Diff{DiffEqual, "BBB EEE"}, 480 }, 481 }, 482 { 483 "Word boundaries", 484 []Diff{ 485 Diff{DiffEqual, "The c"}, 486 Diff{DiffInsert, "ow and the c"}, 487 Diff{DiffEqual, "at."}, 488 }, 489 []Diff{ 490 Diff{DiffEqual, "The "}, 491 Diff{DiffInsert, "cow and the "}, 492 Diff{DiffEqual, "cat."}, 493 }, 494 }, 495 { 496 "Alphanumeric boundaries", 497 []Diff{ 498 Diff{DiffEqual, "The-c"}, 499 Diff{DiffInsert, "ow-and-the-c"}, 500 Diff{DiffEqual, "at."}, 501 }, 502 []Diff{ 503 Diff{DiffEqual, "The-"}, 504 Diff{DiffInsert, "cow-and-the-"}, 505 Diff{DiffEqual, "cat."}, 506 }, 507 }, 508 { 509 "Hitting the start", 510 []Diff{ 511 Diff{DiffEqual, "a"}, 512 Diff{DiffDelete, "a"}, 513 Diff{DiffEqual, "ax"}, 514 }, 515 []Diff{ 516 Diff{DiffDelete, "a"}, 517 Diff{DiffEqual, "aax"}, 518 }, 519 }, 520 { 521 "Hitting the end", 522 []Diff{ 523 Diff{DiffEqual, "xa"}, 524 Diff{DiffDelete, "a"}, 525 Diff{DiffEqual, "a"}, 526 }, 527 []Diff{ 528 Diff{DiffEqual, "xaa"}, 529 Diff{DiffDelete, "a"}, 530 }, 531 }, 532 { 533 "Sentence boundaries", 534 []Diff{ 535 Diff{DiffEqual, "The xxx. The "}, 536 Diff{DiffInsert, "zzz. The "}, 537 Diff{DiffEqual, "yyy."}, 538 }, 539 []Diff{ 540 Diff{DiffEqual, "The xxx."}, 541 Diff{DiffInsert, " The zzz."}, 542 Diff{DiffEqual, " The yyy."}, 543 }, 544 }, 545 { 546 "UTF-8 strings", 547 []Diff{ 548 Diff{DiffEqual, "The ♕. The "}, 549 Diff{DiffInsert, "♔. The "}, 550 Diff{DiffEqual, "♖."}, 551 }, 552 []Diff{ 553 Diff{DiffEqual, "The ♕."}, 554 Diff{DiffInsert, " The ♔."}, 555 Diff{DiffEqual, " The ♖."}, 556 }, 557 }, 558 { 559 "Rune boundaries", 560 []Diff{ 561 Diff{DiffEqual, "♕♕"}, 562 Diff{DiffInsert, "♔♔"}, 563 Diff{DiffEqual, "♖♖"}, 564 }, 565 []Diff{ 566 Diff{DiffEqual, "♕♕"}, 567 Diff{DiffInsert, "♔♔"}, 568 Diff{DiffEqual, "♖♖"}, 569 }, 570 }, 571 } { 572 actual := dmp.DiffCleanupSemanticLossless(tc.Diffs) 573 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 574 } 575} 576 577func TestDiffCleanupSemantic(t *testing.T) { 578 type TestCase struct { 579 Name string 580 581 Diffs []Diff 582 583 Expected []Diff 584 } 585 586 dmp := New() 587 588 for i, tc := range []TestCase{ 589 { 590 "Null case", 591 []Diff{}, 592 []Diff{}, 593 }, 594 { 595 "No elimination #1", 596 []Diff{ 597 {DiffDelete, "ab"}, 598 {DiffInsert, "cd"}, 599 {DiffEqual, "12"}, 600 {DiffDelete, "e"}, 601 }, 602 []Diff{ 603 {DiffDelete, "ab"}, 604 {DiffInsert, "cd"}, 605 {DiffEqual, "12"}, 606 {DiffDelete, "e"}, 607 }, 608 }, 609 { 610 "No elimination #2", 611 []Diff{ 612 {DiffDelete, "abc"}, 613 {DiffInsert, "ABC"}, 614 {DiffEqual, "1234"}, 615 {DiffDelete, "wxyz"}, 616 }, 617 []Diff{ 618 {DiffDelete, "abc"}, 619 {DiffInsert, "ABC"}, 620 {DiffEqual, "1234"}, 621 {DiffDelete, "wxyz"}, 622 }, 623 }, 624 { 625 "No elimination #3", 626 []Diff{ 627 {DiffEqual, "2016-09-01T03:07:1"}, 628 {DiffInsert, "5.15"}, 629 {DiffEqual, "4"}, 630 {DiffDelete, "."}, 631 {DiffEqual, "80"}, 632 {DiffInsert, "0"}, 633 {DiffEqual, "78"}, 634 {DiffDelete, "3074"}, 635 {DiffEqual, "1Z"}, 636 }, 637 []Diff{ 638 {DiffEqual, "2016-09-01T03:07:1"}, 639 {DiffInsert, "5.15"}, 640 {DiffEqual, "4"}, 641 {DiffDelete, "."}, 642 {DiffEqual, "80"}, 643 {DiffInsert, "0"}, 644 {DiffEqual, "78"}, 645 {DiffDelete, "3074"}, 646 {DiffEqual, "1Z"}, 647 }, 648 }, 649 { 650 "Simple elimination", 651 []Diff{ 652 {DiffDelete, "a"}, 653 {DiffEqual, "b"}, 654 {DiffDelete, "c"}, 655 }, 656 []Diff{ 657 {DiffDelete, "abc"}, 658 {DiffInsert, "b"}, 659 }, 660 }, 661 { 662 "Backpass elimination", 663 []Diff{ 664 {DiffDelete, "ab"}, 665 {DiffEqual, "cd"}, 666 {DiffDelete, "e"}, 667 {DiffEqual, "f"}, 668 {DiffInsert, "g"}, 669 }, 670 []Diff{ 671 {DiffDelete, "abcdef"}, 672 {DiffInsert, "cdfg"}, 673 }, 674 }, 675 { 676 "Multiple eliminations", 677 []Diff{ 678 {DiffInsert, "1"}, 679 {DiffEqual, "A"}, 680 {DiffDelete, "B"}, 681 {DiffInsert, "2"}, 682 {DiffEqual, "_"}, 683 {DiffInsert, "1"}, 684 {DiffEqual, "A"}, 685 {DiffDelete, "B"}, 686 {DiffInsert, "2"}, 687 }, 688 []Diff{ 689 {DiffDelete, "AB_AB"}, 690 {DiffInsert, "1A2_1A2"}, 691 }, 692 }, 693 { 694 "Word boundaries", 695 []Diff{ 696 {DiffEqual, "The c"}, 697 {DiffDelete, "ow and the c"}, 698 {DiffEqual, "at."}, 699 }, 700 []Diff{ 701 {DiffEqual, "The "}, 702 {DiffDelete, "cow and the "}, 703 {DiffEqual, "cat."}, 704 }, 705 }, 706 { 707 "No overlap elimination", 708 []Diff{ 709 {DiffDelete, "abcxx"}, 710 {DiffInsert, "xxdef"}, 711 }, 712 []Diff{ 713 {DiffDelete, "abcxx"}, 714 {DiffInsert, "xxdef"}, 715 }, 716 }, 717 { 718 "Overlap elimination", 719 []Diff{ 720 {DiffDelete, "abcxxx"}, 721 {DiffInsert, "xxxdef"}, 722 }, 723 []Diff{ 724 {DiffDelete, "abc"}, 725 {DiffEqual, "xxx"}, 726 {DiffInsert, "def"}, 727 }, 728 }, 729 { 730 "Reverse overlap elimination", 731 []Diff{ 732 {DiffDelete, "xxxabc"}, 733 {DiffInsert, "defxxx"}, 734 }, 735 []Diff{ 736 {DiffInsert, "def"}, 737 {DiffEqual, "xxx"}, 738 {DiffDelete, "abc"}, 739 }, 740 }, 741 { 742 "Two overlap eliminations", 743 []Diff{ 744 {DiffDelete, "abcd1212"}, 745 {DiffInsert, "1212efghi"}, 746 {DiffEqual, "----"}, 747 {DiffDelete, "A3"}, 748 {DiffInsert, "3BC"}, 749 }, 750 []Diff{ 751 {DiffDelete, "abcd"}, 752 {DiffEqual, "1212"}, 753 {DiffInsert, "efghi"}, 754 {DiffEqual, "----"}, 755 {DiffDelete, "A"}, 756 {DiffEqual, "3"}, 757 {DiffInsert, "BC"}, 758 }, 759 }, 760 { 761 "Test case for adapting DiffCleanupSemantic to be equal to the Python version #19", 762 []Diff{ 763 {DiffEqual, "James McCarthy "}, 764 {DiffDelete, "close to "}, 765 {DiffEqual, "sign"}, 766 {DiffDelete, "ing"}, 767 {DiffInsert, "s"}, 768 {DiffEqual, " new "}, 769 {DiffDelete, "E"}, 770 {DiffInsert, "fi"}, 771 {DiffEqual, "ve"}, 772 {DiffInsert, "-yea"}, 773 {DiffEqual, "r"}, 774 {DiffDelete, "ton"}, 775 {DiffEqual, " deal"}, 776 {DiffInsert, " at Everton"}, 777 }, 778 []Diff{ 779 {DiffEqual, "James McCarthy "}, 780 {DiffDelete, "close to "}, 781 {DiffEqual, "sign"}, 782 {DiffDelete, "ing"}, 783 {DiffInsert, "s"}, 784 {DiffEqual, " new "}, 785 {DiffInsert, "five-year deal at "}, 786 {DiffEqual, "Everton"}, 787 {DiffDelete, " deal"}, 788 }, 789 }, 790 } { 791 actual := dmp.DiffCleanupSemantic(tc.Diffs) 792 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 793 } 794} 795 796func BenchmarkDiffCleanupSemantic(b *testing.B) { 797 s1, s2 := speedtestTexts() 798 799 dmp := New() 800 801 diffs := dmp.DiffMain(s1, s2, false) 802 803 b.ResetTimer() 804 805 for i := 0; i < b.N; i++ { 806 dmp.DiffCleanupSemantic(diffs) 807 } 808} 809 810func TestDiffCleanupEfficiency(t *testing.T) { 811 type TestCase struct { 812 Name string 813 814 Diffs []Diff 815 816 Expected []Diff 817 } 818 819 dmp := New() 820 dmp.DiffEditCost = 4 821 822 for i, tc := range []TestCase{ 823 { 824 "Null case", 825 []Diff{}, 826 []Diff{}, 827 }, 828 { 829 "No elimination", 830 []Diff{ 831 Diff{DiffDelete, "ab"}, 832 Diff{DiffInsert, "12"}, 833 Diff{DiffEqual, "wxyz"}, 834 Diff{DiffDelete, "cd"}, 835 Diff{DiffInsert, "34"}, 836 }, 837 []Diff{ 838 Diff{DiffDelete, "ab"}, 839 Diff{DiffInsert, "12"}, 840 Diff{DiffEqual, "wxyz"}, 841 Diff{DiffDelete, "cd"}, 842 Diff{DiffInsert, "34"}, 843 }, 844 }, 845 { 846 "Four-edit elimination", 847 []Diff{ 848 Diff{DiffDelete, "ab"}, 849 Diff{DiffInsert, "12"}, 850 Diff{DiffEqual, "xyz"}, 851 Diff{DiffDelete, "cd"}, 852 Diff{DiffInsert, "34"}, 853 }, 854 []Diff{ 855 Diff{DiffDelete, "abxyzcd"}, 856 Diff{DiffInsert, "12xyz34"}, 857 }, 858 }, 859 { 860 "Three-edit elimination", 861 []Diff{ 862 Diff{DiffInsert, "12"}, 863 Diff{DiffEqual, "x"}, 864 Diff{DiffDelete, "cd"}, 865 Diff{DiffInsert, "34"}, 866 }, 867 []Diff{ 868 Diff{DiffDelete, "xcd"}, 869 Diff{DiffInsert, "12x34"}, 870 }, 871 }, 872 { 873 "Backpass elimination", 874 []Diff{ 875 Diff{DiffDelete, "ab"}, 876 Diff{DiffInsert, "12"}, 877 Diff{DiffEqual, "xy"}, 878 Diff{DiffInsert, "34"}, 879 Diff{DiffEqual, "z"}, 880 Diff{DiffDelete, "cd"}, 881 Diff{DiffInsert, "56"}, 882 }, 883 []Diff{ 884 Diff{DiffDelete, "abxyzcd"}, 885 Diff{DiffInsert, "12xy34z56"}, 886 }, 887 }, 888 } { 889 actual := dmp.DiffCleanupEfficiency(tc.Diffs) 890 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 891 } 892 893 dmp.DiffEditCost = 5 894 895 for i, tc := range []TestCase{ 896 { 897 "High cost elimination", 898 []Diff{ 899 Diff{DiffDelete, "ab"}, 900 Diff{DiffInsert, "12"}, 901 Diff{DiffEqual, "wxyz"}, 902 Diff{DiffDelete, "cd"}, 903 Diff{DiffInsert, "34"}, 904 }, 905 []Diff{ 906 Diff{DiffDelete, "abwxyzcd"}, 907 Diff{DiffInsert, "12wxyz34"}, 908 }, 909 }, 910 } { 911 actual := dmp.DiffCleanupEfficiency(tc.Diffs) 912 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 913 } 914} 915 916func TestDiffPrettyHtml(t *testing.T) { 917 type TestCase struct { 918 Diffs []Diff 919 920 Expected string 921 } 922 923 dmp := New() 924 925 for i, tc := range []TestCase{ 926 { 927 Diffs: []Diff{ 928 {DiffEqual, "a\n"}, 929 {DiffDelete, "<B>b</B>"}, 930 {DiffInsert, "c&d"}, 931 }, 932 933 Expected: "<span>a¶<br></span><del style=\"background:#ffe6e6;\"><B>b</B></del><ins style=\"background:#e6ffe6;\">c&d</ins>", 934 }, 935 } { 936 actual := dmp.DiffPrettyHtml(tc.Diffs) 937 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 938 } 939} 940 941func TestDiffPrettyText(t *testing.T) { 942 type TestCase struct { 943 Diffs []Diff 944 945 Expected string 946 } 947 948 dmp := New() 949 950 for i, tc := range []TestCase{ 951 { 952 Diffs: []Diff{ 953 {DiffEqual, "a\n"}, 954 {DiffDelete, "<B>b</B>"}, 955 {DiffInsert, "c&d"}, 956 }, 957 958 Expected: "a\n\x1b[31m<B>b</B>\x1b[0m\x1b[32mc&d\x1b[0m", 959 }, 960 } { 961 actual := dmp.DiffPrettyText(tc.Diffs) 962 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 963 } 964} 965 966func TestDiffText(t *testing.T) { 967 type TestCase struct { 968 Diffs []Diff 969 970 ExpectedText1 string 971 ExpectedText2 string 972 } 973 974 dmp := New() 975 976 for i, tc := range []TestCase{ 977 { 978 Diffs: []Diff{ 979 {DiffEqual, "jump"}, 980 {DiffDelete, "s"}, 981 {DiffInsert, "ed"}, 982 {DiffEqual, " over "}, 983 {DiffDelete, "the"}, 984 {DiffInsert, "a"}, 985 {DiffEqual, " lazy"}, 986 }, 987 988 ExpectedText1: "jumps over the lazy", 989 ExpectedText2: "jumped over a lazy", 990 }, 991 } { 992 actualText1 := dmp.DiffText1(tc.Diffs) 993 assert.Equal(t, tc.ExpectedText1, actualText1, fmt.Sprintf("Test case #%d, %#v", i, tc)) 994 995 actualText2 := dmp.DiffText2(tc.Diffs) 996 assert.Equal(t, tc.ExpectedText2, actualText2, fmt.Sprintf("Test case #%d, %#v", i, tc)) 997 } 998} 999 1000func TestDiffDelta(t *testing.T) { 1001 dmp := New() 1002 1003 // Convert a diff into delta string. 1004 diffs := []Diff{ 1005 Diff{DiffEqual, "jump"}, 1006 Diff{DiffDelete, "s"}, 1007 Diff{DiffInsert, "ed"}, 1008 Diff{DiffEqual, " over "}, 1009 Diff{DiffDelete, "the"}, 1010 Diff{DiffInsert, "a"}, 1011 Diff{DiffEqual, " lazy"}, 1012 Diff{DiffInsert, "old dog"}, 1013 } 1014 text1 := dmp.DiffText1(diffs) 1015 assert.Equal(t, "jumps over the lazy", text1) 1016 1017 delta := dmp.DiffToDelta(diffs) 1018 assert.Equal(t, "=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta) 1019 1020 // Convert delta string into a diff. 1021 deltaDiffs, err := dmp.DiffFromDelta(text1, delta) 1022 assert.Equal(t, diffs, deltaDiffs) 1023 1024 // Generates error (19 < 20). 1025 _, err = dmp.DiffFromDelta(text1+"x", delta) 1026 if err == nil { 1027 t.Fatal("Too long.") 1028 } 1029 1030 // Generates error (19 > 18). 1031 _, err = dmp.DiffFromDelta(text1[1:], delta) 1032 if err == nil { 1033 t.Fatal("Too short.") 1034 } 1035 1036 // Generates error (%xy invalid URL escape). 1037 _, err = dmp.DiffFromDelta("", "+%c3%xy") 1038 if err == nil { 1039 assert.Fail(t, "expected Invalid URL escape.") 1040 } 1041 1042 // Generates error (invalid utf8). 1043 _, err = dmp.DiffFromDelta("", "+%c3xy") 1044 if err == nil { 1045 assert.Fail(t, "expected Invalid utf8.") 1046 } 1047 1048 // Test deltas with special characters. 1049 diffs = []Diff{ 1050 Diff{DiffEqual, "\u0680 \x00 \t %"}, 1051 Diff{DiffDelete, "\u0681 \x01 \n ^"}, 1052 Diff{DiffInsert, "\u0682 \x02 \\ |"}, 1053 } 1054 text1 = dmp.DiffText1(diffs) 1055 assert.Equal(t, "\u0680 \x00 \t %\u0681 \x01 \n ^", text1) 1056 1057 // Lowercase, due to UrlEncode uses lower. 1058 delta = dmp.DiffToDelta(diffs) 1059 assert.Equal(t, "=7\t-7\t+%DA%82 %02 %5C %7C", delta) 1060 1061 deltaDiffs, err = dmp.DiffFromDelta(text1, delta) 1062 assert.Equal(t, diffs, deltaDiffs) 1063 assert.Nil(t, err) 1064 1065 // Verify pool of unchanged characters. 1066 diffs = []Diff{ 1067 Diff{DiffInsert, "A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # "}, 1068 } 1069 1070 delta = dmp.DiffToDelta(diffs) 1071 assert.Equal(t, "+A-Z a-z 0-9 - _ . ! ~ * ' ( ) ; / ? : @ & = + $ , # ", delta, "Unchanged characters.") 1072 1073 // Convert delta string into a diff. 1074 deltaDiffs, err = dmp.DiffFromDelta("", delta) 1075 assert.Equal(t, diffs, deltaDiffs) 1076 assert.Nil(t, err) 1077 1078 // Test blank tokens. 1079 _, err = dmp.DiffFromDelta("", "") 1080 assert.Nil(t, err) 1081 1082 // Test invalid diff operation "a" 1083 _, err = dmp.DiffFromDelta("", "a") 1084 if err == nil { 1085 assert.Fail(t, "expected Invalid diff operation.") 1086 } 1087 1088 // Test non-numeric parameter 1089 _, err = dmp.DiffFromDelta("", "-") 1090 if err == nil { 1091 assert.Fail(t, "expected Invalid syntax.") 1092 } 1093 1094 // Test negative parameter 1095 _, err = dmp.DiffFromDelta("", "--1") 1096 if err == nil { 1097 assert.Fail(t, "expected Negative number.") 1098 } 1099 1100} 1101 1102func TestDiffXIndex(t *testing.T) { 1103 type TestCase struct { 1104 Name string 1105 1106 Diffs []Diff 1107 Location int 1108 1109 Expected int 1110 } 1111 1112 dmp := New() 1113 1114 for i, tc := range []TestCase{ 1115 {"Translation on equality", []Diff{{DiffDelete, "a"}, {DiffInsert, "1234"}, {DiffEqual, "xyz"}}, 2, 5}, 1116 {"Translation on deletion", []Diff{{DiffEqual, "a"}, {DiffDelete, "1234"}, {DiffEqual, "xyz"}}, 3, 1}, 1117 } { 1118 actual := dmp.DiffXIndex(tc.Diffs, tc.Location) 1119 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 1120 } 1121} 1122 1123func TestDiffLevenshtein(t *testing.T) { 1124 type TestCase struct { 1125 Name string 1126 1127 Diffs []Diff 1128 1129 Expected int 1130 } 1131 1132 dmp := New() 1133 1134 for i, tc := range []TestCase{ 1135 {"Levenshtein with trailing equality", []Diff{{DiffDelete, "abc"}, {DiffInsert, "1234"}, {DiffEqual, "xyz"}}, 4}, 1136 {"Levenshtein with leading equality", []Diff{{DiffEqual, "xyz"}, {DiffDelete, "abc"}, {DiffInsert, "1234"}}, 4}, 1137 {"Levenshtein with middle equality", []Diff{{DiffDelete, "abc"}, {DiffEqual, "xyz"}, {DiffInsert, "1234"}}, 7}, 1138 } { 1139 actual := dmp.DiffLevenshtein(tc.Diffs) 1140 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 1141 } 1142} 1143 1144func TestDiffBisect(t *testing.T) { 1145 type TestCase struct { 1146 Name string 1147 1148 Time time.Time 1149 1150 Expected []Diff 1151 } 1152 1153 dmp := New() 1154 1155 for i, tc := range []TestCase{ 1156 { 1157 Name: "normal", 1158 Time: time.Date(9999, time.December, 31, 23, 59, 59, 59, time.UTC), 1159 1160 Expected: []Diff{ 1161 {DiffDelete, "c"}, 1162 {DiffInsert, "m"}, 1163 {DiffEqual, "a"}, 1164 {DiffDelete, "t"}, 1165 {DiffInsert, "p"}, 1166 }, 1167 }, 1168 { 1169 Name: "Negative deadlines count as having infinite time", 1170 Time: time.Date(0001, time.January, 01, 00, 00, 00, 00, time.UTC), 1171 1172 Expected: []Diff{ 1173 {DiffDelete, "c"}, 1174 {DiffInsert, "m"}, 1175 {DiffEqual, "a"}, 1176 {DiffDelete, "t"}, 1177 {DiffInsert, "p"}, 1178 }, 1179 }, 1180 { 1181 Name: "Timeout", 1182 Time: time.Now().Add(time.Nanosecond), 1183 1184 Expected: []Diff{ 1185 {DiffDelete, "cat"}, 1186 {DiffInsert, "map"}, 1187 }, 1188 }, 1189 } { 1190 actual := dmp.DiffBisect("cat", "map", tc.Time) 1191 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name)) 1192 } 1193 1194 // Test for invalid UTF-8 sequences 1195 assert.Equal(t, []Diff{ 1196 Diff{DiffEqual, "��"}, 1197 }, dmp.DiffBisect("\xe0\xe5", "\xe0\xe5", time.Now().Add(time.Minute))) 1198} 1199 1200func TestDiffMain(t *testing.T) { 1201 type TestCase struct { 1202 Text1 string 1203 Text2 string 1204 1205 Expected []Diff 1206 } 1207 1208 dmp := New() 1209 1210 // Perform a trivial diff. 1211 for i, tc := range []TestCase{ 1212 { 1213 "", 1214 "", 1215 nil, 1216 }, 1217 { 1218 "abc", 1219 "abc", 1220 []Diff{Diff{DiffEqual, "abc"}}, 1221 }, 1222 { 1223 "abc", 1224 "ab123c", 1225 []Diff{Diff{DiffEqual, "ab"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "c"}}, 1226 }, 1227 { 1228 "a123bc", 1229 "abc", 1230 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "bc"}}, 1231 }, 1232 { 1233 "abc", 1234 "a123b456c", 1235 []Diff{Diff{DiffEqual, "a"}, Diff{DiffInsert, "123"}, Diff{DiffEqual, "b"}, Diff{DiffInsert, "456"}, Diff{DiffEqual, "c"}}, 1236 }, 1237 { 1238 "a123b456c", 1239 "abc", 1240 []Diff{Diff{DiffEqual, "a"}, Diff{DiffDelete, "123"}, Diff{DiffEqual, "b"}, Diff{DiffDelete, "456"}, Diff{DiffEqual, "c"}}, 1241 }, 1242 } { 1243 actual := dmp.DiffMain(tc.Text1, tc.Text2, false) 1244 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 1245 } 1246 1247 // Perform a real diff and switch off the timeout. 1248 dmp.DiffTimeout = 0 1249 1250 for i, tc := range []TestCase{ 1251 { 1252 "a", 1253 "b", 1254 []Diff{Diff{DiffDelete, "a"}, Diff{DiffInsert, "b"}}, 1255 }, 1256 { 1257 "Apples are a fruit.", 1258 "Bananas are also fruit.", 1259 []Diff{ 1260 Diff{DiffDelete, "Apple"}, 1261 Diff{DiffInsert, "Banana"}, 1262 Diff{DiffEqual, "s are a"}, 1263 Diff{DiffInsert, "lso"}, 1264 Diff{DiffEqual, " fruit."}, 1265 }, 1266 }, 1267 { 1268 "ax\t", 1269 "\u0680x\u0000", 1270 []Diff{ 1271 Diff{DiffDelete, "a"}, 1272 Diff{DiffInsert, "\u0680"}, 1273 Diff{DiffEqual, "x"}, 1274 Diff{DiffDelete, "\t"}, 1275 Diff{DiffInsert, "\u0000"}, 1276 }, 1277 }, 1278 { 1279 "1ayb2", 1280 "abxab", 1281 []Diff{ 1282 Diff{DiffDelete, "1"}, 1283 Diff{DiffEqual, "a"}, 1284 Diff{DiffDelete, "y"}, 1285 Diff{DiffEqual, "b"}, 1286 Diff{DiffDelete, "2"}, 1287 Diff{DiffInsert, "xab"}, 1288 }, 1289 }, 1290 { 1291 "abcy", 1292 "xaxcxabc", 1293 []Diff{ 1294 Diff{DiffInsert, "xaxcx"}, 1295 Diff{DiffEqual, "abc"}, Diff{DiffDelete, "y"}, 1296 }, 1297 }, 1298 { 1299 "ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg", 1300 "a-bcd-efghijklmnopqrs", 1301 []Diff{ 1302 Diff{DiffDelete, "ABCD"}, 1303 Diff{DiffEqual, "a"}, 1304 Diff{DiffDelete, "="}, 1305 Diff{DiffInsert, "-"}, 1306 Diff{DiffEqual, "bcd"}, 1307 Diff{DiffDelete, "="}, 1308 Diff{DiffInsert, "-"}, 1309 Diff{DiffEqual, "efghijklmnopqrs"}, 1310 Diff{DiffDelete, "EFGHIJKLMNOefg"}, 1311 }, 1312 }, 1313 { 1314 "a [[Pennsylvania]] and [[New", 1315 " and [[Pennsylvania]]", 1316 []Diff{ 1317 Diff{DiffInsert, " "}, 1318 Diff{DiffEqual, "a"}, 1319 Diff{DiffInsert, "nd"}, 1320 Diff{DiffEqual, " [[Pennsylvania]]"}, 1321 Diff{DiffDelete, " and [[New"}, 1322 }, 1323 }, 1324 } { 1325 actual := dmp.DiffMain(tc.Text1, tc.Text2, false) 1326 assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc)) 1327 } 1328 1329 // Test for invalid UTF-8 sequences 1330 assert.Equal(t, []Diff{ 1331 Diff{DiffDelete, "��"}, 1332 }, dmp.DiffMain("\xe0\xe5", "", false)) 1333} 1334 1335func TestDiffMainWithTimeout(t *testing.T) { 1336 dmp := New() 1337 dmp.DiffTimeout = 200 * time.Millisecond 1338 1339 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" 1340 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" 1341 // Increase the text lengths by 1024 times to ensure a timeout. 1342 for x := 0; x < 13; x++ { 1343 a = a + a 1344 b = b + b 1345 } 1346 1347 startTime := time.Now() 1348 dmp.DiffMain(a, b, true) 1349 endTime := time.Now() 1350 1351 delta := endTime.Sub(startTime) 1352 1353 // Test that we took at least the timeout period. 1354 assert.True(t, delta >= dmp.DiffTimeout, fmt.Sprintf("%v !>= %v", delta, dmp.DiffTimeout)) 1355 1356 // 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. 1357 assert.True(t, delta < (dmp.DiffTimeout*100), fmt.Sprintf("%v !< %v", delta, dmp.DiffTimeout*100)) 1358} 1359 1360func TestDiffMainWithCheckLines(t *testing.T) { 1361 type TestCase struct { 1362 Text1 string 1363 Text2 string 1364 } 1365 1366 dmp := New() 1367 dmp.DiffTimeout = 0 1368 1369 // Test cases must be at least 100 chars long to pass the cutoff. 1370 for i, tc := range []TestCase{ 1371 { 1372 "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", 1373 "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n", 1374 }, 1375 { 1376 "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", 1377 "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij", 1378 }, 1379 { 1380 "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n", 1381 "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n", 1382 }, 1383 } { 1384 resultWithoutCheckLines := dmp.DiffMain(tc.Text1, tc.Text2, false) 1385 resultWithCheckLines := dmp.DiffMain(tc.Text1, tc.Text2, true) 1386 1387 // TODO this fails for the third test case, why? 1388 if i != 2 { 1389 assert.Equal(t, resultWithoutCheckLines, resultWithCheckLines, fmt.Sprintf("Test case #%d, %#v", i, tc)) 1390 } 1391 assert.Equal(t, diffRebuildTexts(resultWithoutCheckLines), diffRebuildTexts(resultWithCheckLines), fmt.Sprintf("Test case #%d, %#v", i, tc)) 1392 } 1393} 1394 1395func BenchmarkDiffMain(bench *testing.B) { 1396 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" 1397 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" 1398 1399 // Increase the text lengths by 1024 times to ensure a timeout. 1400 for x := 0; x < 10; x++ { 1401 s1 = s1 + s1 1402 s2 = s2 + s2 1403 } 1404 1405 dmp := New() 1406 dmp.DiffTimeout = time.Second 1407 1408 bench.ResetTimer() 1409 1410 for i := 0; i < bench.N; i++ { 1411 dmp.DiffMain(s1, s2, true) 1412 } 1413} 1414 1415func BenchmarkDiffMainLarge(b *testing.B) { 1416 s1, s2 := speedtestTexts() 1417 1418 dmp := New() 1419 1420 b.ResetTimer() 1421 1422 for i := 0; i < b.N; i++ { 1423 dmp.DiffMain(s1, s2, true) 1424 } 1425} 1426 1427func BenchmarkDiffMainRunesLargeLines(b *testing.B) { 1428 s1, s2 := speedtestTexts() 1429 1430 dmp := New() 1431 1432 b.ResetTimer() 1433 1434 for i := 0; i < b.N; i++ { 1435 text1, text2, linearray := dmp.DiffLinesToRunes(s1, s2) 1436 1437 diffs := dmp.DiffMainRunes(text1, text2, false) 1438 diffs = dmp.DiffCharsToLines(diffs, linearray) 1439 } 1440} 1441