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&para;<br></span><del style=\"background:#ffe6e6;\">&lt;B&gt;b&lt;/B&gt;</del><ins style=\"background:#e6ffe6;\">c&amp;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  }