go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/diff/main_test.go (about)

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