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