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