1// Copyright (c) 2012-2016 The go-diff authors. All rights reserved.
2// https://github.com/sergi/go-diff
3// See the included LICENSE file for license details.
4//
5// go-diff is a Go implementation of Google's Diff, Match, and Patch library
6// Original library is Copyright (c) 2006 Google Inc.
7// http://code.google.com/p/google-diff-match-patch/
8
9package diffmatchpatch
10
11import (
12	"fmt"
13	"strings"
14	"testing"
15
16	"github.com/stretchr/testify/assert"
17)
18
19func TestPatchString(t *testing.T) {
20	type TestCase struct {
21		Patch Patch
22
23		Expected string
24	}
25
26	for i, tc := range []TestCase{
27		{
28			Patch: Patch{
29				Start1:  20,
30				Start2:  21,
31				Length1: 18,
32				Length2: 17,
33
34				diffs: []Diff{
35					{DiffEqual, "jump"},
36					{DiffDelete, "s"},
37					{DiffInsert, "ed"},
38					{DiffEqual, " over "},
39					{DiffDelete, "the"},
40					{DiffInsert, "a"},
41					{DiffEqual, "\nlaz"},
42				},
43			},
44
45			Expected: "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n %0Alaz\n",
46		},
47	} {
48		actual := tc.Patch.String()
49		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
50	}
51}
52
53func TestPatchFromText(t *testing.T) {
54	type TestCase struct {
55		Patch string
56
57		ErrorMessagePrefix string
58	}
59
60	dmp := New()
61
62	for i, tc := range []TestCase{
63		{"", ""},
64		{"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n %0Alaz\n", ""},
65		{"@@ -1 +1 @@\n-a\n+b\n", ""},
66		{"@@ -1,3 +0,0 @@\n-abc\n", ""},
67		{"@@ -0,0 +1,3 @@\n+abc\n", ""},
68		{"@@ _0,0 +0,0 @@\n+abc\n", "Invalid patch string: @@ _0,0 +0,0 @@"},
69		{"Bad\nPatch\n", "Invalid patch string"},
70	} {
71		patches, err := dmp.PatchFromText(tc.Patch)
72		if tc.ErrorMessagePrefix == "" {
73			assert.Nil(t, err)
74
75			if tc.Patch == "" {
76				assert.Equal(t, []Patch{}, patches, fmt.Sprintf("Test case #%d, %#v", i, tc))
77			} else {
78				assert.Equal(t, tc.Patch, patches[0].String(), fmt.Sprintf("Test case #%d, %#v", i, tc))
79			}
80		} else {
81			e := err.Error()
82			if strings.HasPrefix(e, tc.ErrorMessagePrefix) {
83				e = tc.ErrorMessagePrefix
84			}
85			assert.Equal(t, tc.ErrorMessagePrefix, e)
86		}
87	}
88
89	diffs := []Diff{
90		{DiffDelete, "`1234567890-=[]\\;',./"},
91		{DiffInsert, "~!@#$%^&*()_+{}|:\"<>?"},
92	}
93
94	patches, err := dmp.PatchFromText("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n")
95	assert.Len(t, patches, 1)
96	assert.Equal(t, diffs,
97		patches[0].diffs,
98	)
99	assert.Nil(t, err)
100}
101
102func TestPatchToText(t *testing.T) {
103	type TestCase struct {
104		Patch string
105	}
106
107	dmp := New()
108
109	for i, tc := range []TestCase{
110		{"@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
111		{"@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n  tes\n"},
112	} {
113		patches, err := dmp.PatchFromText(tc.Patch)
114		assert.Nil(t, err)
115
116		actual := dmp.PatchToText(patches)
117		assert.Equal(t, tc.Patch, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
118	}
119}
120
121func TestPatchAddContext(t *testing.T) {
122	type TestCase struct {
123		Name string
124
125		Patch string
126		Text  string
127
128		Expected string
129	}
130
131	dmp := New()
132	dmp.PatchMargin = 4
133
134	for i, tc := range []TestCase{
135		{"Simple case", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps over the lazy dog.", "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n"},
136		{"Not enough trailing context", "@@ -21,4 +21,10 @@\n-jump\n+somersault\n", "The quick brown fox jumps.", "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n"},
137		{"Not enough leading context", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.", "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n  qui\n"},
138		{"Ambiguity", "@@ -3 +3,2 @@\n-e\n+at\n", "The quick brown fox jumps.  The quick brown fox crashes.", "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n  quick brown fox jumps. \n"},
139	} {
140		patches, err := dmp.PatchFromText(tc.Patch)
141		assert.Nil(t, err)
142
143		actual := dmp.PatchAddContext(patches[0], tc.Text)
144		assert.Equal(t, tc.Expected, actual.String(), fmt.Sprintf("Test case #%d, %s", i, tc.Name))
145	}
146}
147
148func TestPatchMakeAndPatchToText(t *testing.T) {
149	type TestCase struct {
150		Name string
151
152		Input1 interface{}
153		Input2 interface{}
154		Input3 interface{}
155
156		Expected string
157	}
158
159	dmp := New()
160
161	text1 := "The quick brown fox jumps over the lazy dog."
162	text2 := "That quick brown fox jumped over a lazy dog."
163
164	for i, tc := range []TestCase{
165		{"Null case", "", "", nil, ""},
166		{"Text2+Text1 inputs", text2, text1, nil, "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n  qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n  over \n-a\n+the\n  laz\n"},
167		{"Text1+Text2 inputs", text1, text2, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
168		{"Diff input", dmp.DiffMain(text1, text2, false), nil, nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
169		{"Text1+Diff inputs", text1, dmp.DiffMain(text1, text2, false), nil, "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
170		{"Text1+Text2+Diff inputs (deprecated)", text1, text2, dmp.DiffMain(text1, text2, false), "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n  quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n  over \n-the\n+a\n  laz\n"},
171		{"Character encoding", "`1234567890-=[]\\;',./", "~!@#$%^&*()_+{}|:\"<>?", nil, "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n"},
172		{"Long string with repeats", strings.Repeat("abcdef", 100), strings.Repeat("abcdef", 100) + "123", nil, "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"},
173		{"Corner case of #31 fixed by #32", "2016-09-01T03:07:14.807830741Z", "2016-09-01T03:07:15.154800781Z", nil, "@@ -15,16 +15,16 @@\n 07:1\n+5.15\n 4\n-.\n 80\n+0\n 78\n-3074\n 1Z\n"},
174	} {
175		var patches []Patch
176		if tc.Input3 != nil {
177			patches = dmp.PatchMake(tc.Input1, tc.Input2, tc.Input3)
178		} else if tc.Input2 != nil {
179			patches = dmp.PatchMake(tc.Input1, tc.Input2)
180		} else if ps, ok := tc.Input1.([]Patch); ok {
181			patches = ps
182		} else {
183			patches = dmp.PatchMake(tc.Input1)
184		}
185
186		actual := dmp.PatchToText(patches)
187		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
188	}
189
190	// Corner case of #28 wrong patch with timeout of 0
191	dmp.DiffTimeout = 0
192
193	text1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum libero vel."
194	text2 = "Lorem a ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ut risus et enim consectetur convallis a non ipsum. Sed nec nibh cursus, interdum liberovel."
195
196	diffs := dmp.DiffMain(text1, text2, true)
197	// Additional check that the diff texts are equal to the originals even if we are using DiffMain with checklines=true #29
198	assert.Equal(t, text1, dmp.DiffText1(diffs))
199	assert.Equal(t, text2, dmp.DiffText2(diffs))
200
201	patches := dmp.PatchMake(text1, diffs)
202
203	actual := dmp.PatchToText(patches)
204	assert.Equal(t, "@@ -1,14 +1,16 @@\n Lorem \n+a \n ipsum do\n@@ -148,13 +148,12 @@\n m libero\n- \n vel.\n", actual)
205
206	// Check that empty Patch array is returned for no parameter call
207	patches = dmp.PatchMake()
208	assert.Equal(t, []Patch{}, patches)
209}
210
211func TestPatchSplitMax(t *testing.T) {
212	type TestCase struct {
213		Text1 string
214		Text2 string
215
216		Expected string
217	}
218
219	dmp := New()
220
221	for i, tc := range []TestCase{
222		{"abcdefghijklmnopqrstuvwxyz01234567890", "XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0", "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n ij\n+X\n kl\n+X\n mn\n+X\n op\n+X\n qr\n+X\n st\n+X\n uv\n+X\n wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n 45\n+X\n 67\n+X\n 89\n+X\n 0\n"},
223		{"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz", "abcdefuvwxyz", "@@ -3,78 +3,8 @@\n cdef\n-1234567890123456789012345678901234567890123456789012345678901234567890\n uvwx\n"},
224		{"1234567890123456789012345678901234567890123456789012345678901234567890", "abc", "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n"},
225		{"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1", "abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1", "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n  , t : 1 abcdef\n@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n  , t : 1 abcdef\n"},
226	} {
227		patches := dmp.PatchMake(tc.Text1, tc.Text2)
228		patches = dmp.PatchSplitMax(patches)
229
230		actual := dmp.PatchToText(patches)
231		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %#v", i, tc))
232	}
233}
234
235func TestPatchAddPadding(t *testing.T) {
236	type TestCase struct {
237		Name string
238
239		Text1 string
240		Text2 string
241
242		Expected            string
243		ExpectedWithPadding string
244	}
245
246	dmp := New()
247
248	for i, tc := range []TestCase{
249		{"Both edges full", "", "test", "@@ -0,0 +1,4 @@\n+test\n", "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n"},
250		{"Both edges partial", "XY", "XtestY", "@@ -1,2 +1,6 @@\n X\n+test\n Y\n", "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n"},
251		{"Both edges none", "XXXXYYYY", "XXXXtestYYYY", "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n", "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n"},
252	} {
253		patches := dmp.PatchMake(tc.Text1, tc.Text2)
254
255		actual := dmp.PatchToText(patches)
256		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
257
258		dmp.PatchAddPadding(patches)
259
260		actualWithPadding := dmp.PatchToText(patches)
261		assert.Equal(t, tc.ExpectedWithPadding, actualWithPadding, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
262	}
263}
264
265func TestPatchApply(t *testing.T) {
266	type TestCase struct {
267		Name string
268
269		Text1    string
270		Text2    string
271		TextBase string
272
273		Expected        string
274		ExpectedApplies []bool
275	}
276
277	dmp := New()
278	dmp.MatchDistance = 1000
279	dmp.MatchThreshold = 0.5
280	dmp.PatchDeleteThreshold = 0.5
281
282	for i, tc := range []TestCase{
283		{"Null case", "", "", "Hello world.", "Hello world.", []bool{}},
284		{"Exact match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", []bool{true, true}},
285		{"Partial match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "The quick red rabbit jumps over the tired tiger.", "That quick red rabbit jumped over a tired tiger.", []bool{true, true}},
286		{"Failed match", "The quick brown fox jumps over the lazy dog.", "That quick brown fox jumped over a lazy dog.", "I am the very model of a modern major general.", "I am the very model of a modern major general.", []bool{false, false}},
287		{"Big delete, small Diff", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y", "xabcy", []bool{true, true}},
288		{"Big delete, big Diff 1", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y", []bool{false, true}},
289	} {
290		patches := dmp.PatchMake(tc.Text1, tc.Text2)
291
292		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
293		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
294		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
295	}
296
297	dmp.PatchDeleteThreshold = 0.6
298
299	for i, tc := range []TestCase{
300		{"Big delete, big Diff 2", "x1234567890123456789012345678901234567890123456789012345678901234567890y", "xabcy", "x12345678901234567890---------------++++++++++---------------12345678901234567890y", "xabcy", []bool{true, true}},
301	} {
302		patches := dmp.PatchMake(tc.Text1, tc.Text2)
303
304		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
305		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
306		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
307	}
308
309	dmp.MatchDistance = 0
310	dmp.MatchThreshold = 0.0
311	dmp.PatchDeleteThreshold = 0.5
312
313	for i, tc := range []TestCase{
314		{"Compensate for failed patch", "abcdefghijklmnopqrstuvwxyz--------------------1234567890", "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890", "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890", []bool{false, true}},
315	} {
316		patches := dmp.PatchMake(tc.Text1, tc.Text2)
317
318		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
319		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
320		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
321	}
322
323	dmp.MatchThreshold = 0.5
324	dmp.MatchDistance = 1000
325
326	for i, tc := range []TestCase{
327		{"No side effects", "", "test", "", "test", []bool{true}},
328		{"No side effects with major delete", "The quick brown fox jumps over the lazy dog.", "Woof", "The quick brown fox jumps over the lazy dog.", "Woof", []bool{true, true}},
329		{"Edge exact match", "", "test", "", "test", []bool{true}},
330		{"Near edge exact match", "XY", "XtestY", "XY", "XtestY", []bool{true}},
331		{"Edge partial match", "y", "y123", "x", "x123", []bool{true}},
332	} {
333		patches := dmp.PatchMake(tc.Text1, tc.Text2)
334
335		actual, actualApplies := dmp.PatchApply(patches, tc.TextBase)
336		assert.Equal(t, tc.Expected, actual, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
337		assert.Equal(t, tc.ExpectedApplies, actualApplies, fmt.Sprintf("Test case #%d, %s", i, tc.Name))
338	}
339}
340