diff_match_patch 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ require 'uri'
2
+
3
+ # Class representing one patch operation.
4
+ class PatchObj
5
+ attr_accessor :start1, :start2
6
+ attr_accessor :length1, :length2
7
+ attr_accessor :diffs
8
+
9
+ def initialize
10
+ # Initializes with an empty list of diffs.
11
+ @start1 = nil
12
+ @start2 = nil
13
+ @length1 = 0
14
+ @length2 = 0
15
+ @diffs = []
16
+ end
17
+
18
+ # Emulate GNU diff's format
19
+ # Header: @@ -382,8 +481,9 @@
20
+ # Indices are printed as 1-based, not 0-based.
21
+ def to_s
22
+ if length1 == 0
23
+ coords1 = start1.to_s + ",0"
24
+ elsif length1 == 1
25
+ coords1 = (start1 + 1).to_s
26
+ else
27
+ coords1 = (start1 + 1).to_s + "," + length1.to_s
28
+ end
29
+
30
+ if length2 == 0
31
+ coords2 = start2.to_s + ",0"
32
+ elsif length2 == 1
33
+ coords2 = (start2 + 1).to_s
34
+ else
35
+ coords2 = (start2 + 1).to_s + "," + length2.to_s
36
+ end
37
+
38
+ text = '@@ -' + coords1 + ' +' + coords2 + " @@\n"
39
+
40
+ # Encode the body of the patch with %xx notation.
41
+ text += diffs.map do |op, data|
42
+ op = case op
43
+ when :insert; '+'
44
+ when :delete; '-'
45
+ when :equal ; ' '
46
+ end
47
+ op + URI.encode(data, /[^0-9A-Za-z_.;!~*'(),\/?:@&=+$\#-]/) + "\n"
48
+ end.join.gsub('%20', ' ')
49
+
50
+ return text
51
+ end
52
+ end
@@ -0,0 +1,1208 @@
1
+ require 'test/unit'
2
+ require 'diff_match_patch'
3
+
4
+ class DiffTest < Test::Unit::TestCase
5
+ def setup
6
+ @dmp = DiffMatchPatch.new
7
+ end
8
+
9
+ def test_diff_commonPrefix
10
+ # Detect any common prefix.
11
+ # Null case.
12
+ assert_equal(0, @dmp.diff_commonPrefix('abc', 'xyz'))
13
+
14
+ # Non-null case.
15
+ assert_equal(4, @dmp.diff_commonPrefix('1234abcdef', '1234xyz'))
16
+
17
+ # Whole case.
18
+ assert_equal(4, @dmp.diff_commonPrefix('1234', '1234xyz'))
19
+ end
20
+
21
+ def test_diff_commonSuffix
22
+ # Detect any common suffix.
23
+ # Null case.
24
+ assert_equal(0, @dmp.diff_commonSuffix('abc', 'xyz'))
25
+
26
+ # Non-null case.
27
+ assert_equal(4, @dmp.diff_commonSuffix('abcdef1234', 'xyz1234'))
28
+
29
+ # Whole case.
30
+ assert_equal(4, @dmp.diff_commonSuffix('1234', 'xyz1234'))
31
+ end
32
+
33
+ def test_diff_commonOverlap
34
+ # Detect any suffix/prefix overlap.
35
+ # Null case.
36
+ assert_equal(0, @dmp.diff_commonOverlap('', 'abcd'))
37
+
38
+ # Whole case.
39
+ assert_equal(3, @dmp.diff_commonOverlap('abc', 'abcd'))
40
+
41
+ # No overlap.
42
+ assert_equal(0, @dmp.diff_commonOverlap('123456', 'abcd'))
43
+
44
+ # Overlap.
45
+ assert_equal(3, @dmp.diff_commonOverlap('123456xxx', 'xxxabcd'))
46
+
47
+ # Unicode.
48
+ # Some overly clever languages (C#) may treat ligatures as equal to their
49
+ # component letters. E.g. U+FB01 == 'fi'
50
+ assert_equal(0, @dmp.diff_commonOverlap('fi', '\ufb01i'));
51
+ end
52
+
53
+ def test_diff_halfMatch
54
+ # Detect a halfmatch.
55
+ @dmp.diff_timeout = 1
56
+ # No match.
57
+ assert_equal(nil, @dmp.diff_halfMatch('1234567890', 'abcdef'))
58
+
59
+ assert_equal(nil, @dmp.diff_halfMatch('12345', '23'))
60
+
61
+ # Single Match.
62
+ assert_equal(
63
+ ['12', '90', 'a', 'z', '345678'],
64
+ @dmp.diff_halfMatch('1234567890', 'a345678z')
65
+ )
66
+
67
+ assert_equal(
68
+ ['a', 'z', '12', '90', '345678'],
69
+ @dmp.diff_halfMatch('a345678z', '1234567890')
70
+ )
71
+
72
+ assert_equal(
73
+ ['abc', 'z', '1234', '0', '56789'],
74
+ @dmp.diff_halfMatch('abc56789z', '1234567890')
75
+ )
76
+
77
+ assert_equal(
78
+ ['a', 'xyz', '1', '7890', '23456'],
79
+ @dmp.diff_halfMatch('a23456xyz', '1234567890')
80
+ )
81
+
82
+ # Multiple Matches.
83
+ assert_equal(
84
+ ['12123', '123121', 'a', 'z', '1234123451234'],
85
+ @dmp.diff_halfMatch('121231234123451234123121', 'a1234123451234z')
86
+ )
87
+
88
+ assert_equal(
89
+ ['', '-=-=-=-=-=', 'x', '', 'x-=-=-=-=-=-=-='],
90
+ @dmp.diff_halfMatch('x-=-=-=-=-=-=-=-=-=-=-=-=', 'xx-=-=-=-=-=-=-=')
91
+ )
92
+
93
+ assert_equal(
94
+ ['-=-=-=-=-=', '', '', 'y', '-=-=-=-=-=-=-=y'],
95
+ @dmp.diff_halfMatch('-=-=-=-=-=-=-=-=-=-=-=-=y', '-=-=-=-=-=-=-=yy')
96
+ )
97
+
98
+ # Non-optimal halfmatch.
99
+ # Optimal diff would be -q+x=H-i+e=lloHe+Hu=llo-Hew+y
100
+ # not -qHillo+x=HelloHe-w+Hulloy
101
+ assert_equal(
102
+ ['qHillo', 'w', 'x', 'Hulloy', 'HelloHe'],
103
+ @dmp.diff_halfMatch('qHilloHelloHew', 'xHelloHeHulloy')
104
+ )
105
+
106
+ # Optimal no halfmatch.
107
+ @dmp.diff_timeout = 0
108
+ assert_equal(nil, @dmp.diff_halfMatch('qHilloHelloHew', 'xHelloHeHulloy'))
109
+ end
110
+
111
+ def test_diff_linesToChars
112
+ # Convert lines down to characters.
113
+ assert_equal(
114
+ ["\x01\x02\x01", "\x02\x01\x02", ['', "alpha\n", "beta\n"]],
115
+ @dmp.diff_linesToChars("alpha\nbeta\nalpha\n", "beta\nalpha\nbeta\n")
116
+ )
117
+
118
+ assert_equal(
119
+ ['', "\x01\x02\x03\x03", ['', "alpha\r\n", "beta\r\n", "\r\n"]],
120
+ @dmp.diff_linesToChars('', "alpha\r\nbeta\r\n\r\n\r\n")
121
+ )
122
+
123
+ assert_equal(
124
+ ["\x01", "\x02", ['', 'a', 'b']],
125
+ @dmp.diff_linesToChars('a', 'b')
126
+ )
127
+
128
+ # More than 256 to reveal any 8-bit limitations.
129
+ n = 300
130
+ line_list = (1..n).map {|x| x.to_s + "\n" }
131
+ char_list = (1..n).map {|x| x.chr(Encoding::UTF_8) }
132
+ assert_equal(n, line_list.length)
133
+ lines = line_list.join
134
+ chars = char_list.join
135
+ assert_equal(n, chars.length)
136
+ line_list.unshift('')
137
+ assert_equal([chars, '', line_list], @dmp.diff_linesToChars(lines, ''))
138
+ end
139
+
140
+ def test_diff_charsToLines
141
+ # Convert chars up to lines.
142
+ diffs = [[:equal, "\x01\x02\x01"], [:insert, "\x02\x01\x02"]]
143
+ @dmp.diff_charsToLines(diffs, ['', "alpha\n", "beta\n"])
144
+ assert_equal(
145
+ [[:equal, "alpha\nbeta\nalpha\n"], [:insert, "beta\nalpha\nbeta\n"]],
146
+ diffs
147
+ )
148
+
149
+ # More than 256 to reveal any 8-bit limitations.
150
+ n = 300
151
+ line_list = (1..n).map {|x| x.to_s + "\n" }
152
+ char_list = (1..n).map {|x| x.chr(Encoding::UTF_8) }
153
+ assert_equal(n, line_list.length)
154
+ lines = line_list.join
155
+ chars = char_list.join
156
+ assert_equal(n, chars.length)
157
+ line_list.unshift('')
158
+
159
+ diffs = [[:delete, chars]]
160
+ @dmp.diff_charsToLines(diffs, line_list)
161
+ assert_equal([[:delete, lines]], diffs)
162
+ end
163
+
164
+ def test_diff_cleanupMerge
165
+ # Cleanup a messy diff.
166
+ # Null case.
167
+ diffs = []
168
+ @dmp.diff_cleanupMerge(diffs)
169
+ assert_equal([], diffs)
170
+
171
+ # No change case.
172
+ diffs = [[:equal, 'a'], [:delete, 'b'], [:insert, 'c']]
173
+ @dmp.diff_cleanupMerge(diffs)
174
+ assert_equal([[:equal, 'a'], [:delete, 'b'], [:insert, 'c']], diffs)
175
+
176
+ # Merge equalities.
177
+ diffs = [[:equal, 'a'], [:equal, 'b'], [:equal, 'c']]
178
+ @dmp.diff_cleanupMerge(diffs)
179
+ assert_equal([[:equal, 'abc']], diffs)
180
+
181
+ # Merge deletions.
182
+ diffs = [[:delete, 'a'], [:delete, 'b'], [:delete, 'c']]
183
+ @dmp.diff_cleanupMerge(diffs)
184
+ assert_equal([[:delete, 'abc']], diffs)
185
+
186
+ # Merge insertions.
187
+ diffs = [[:insert, 'a'], [:insert, 'b'], [:insert, 'c']]
188
+ @dmp.diff_cleanupMerge(diffs)
189
+ assert_equal([[:insert, 'abc']], diffs)
190
+
191
+ # Merge interweave.
192
+ diffs = [
193
+ [:delete, 'a'], [:insert, 'b'], [:delete, 'c'],
194
+ [:insert, 'd'], [:equal, 'e'], [:equal, 'f']
195
+ ]
196
+ @dmp.diff_cleanupMerge(diffs)
197
+ assert_equal([[:delete, 'ac'], [:insert, 'bd'], [:equal, 'ef']], diffs)
198
+
199
+ # Prefix and suffix detection.
200
+ diffs = [[:delete, 'a'], [:insert, 'abc'], [:delete, 'dc']]
201
+ @dmp.diff_cleanupMerge(diffs)
202
+ assert_equal(
203
+ [[:equal, 'a'], [:delete, 'd'], [:insert, 'b'],[:equal, 'c']],
204
+ diffs
205
+ )
206
+
207
+ # Prefix and suffix detection with equalities.
208
+ diffs = [
209
+ [:equal, 'x'], [:delete, 'a'], [:insert, 'abc'],
210
+ [:delete, 'dc'], [:equal, 'y']
211
+ ]
212
+ @dmp.diff_cleanupMerge(diffs)
213
+ assert_equal(
214
+ [[:equal, 'xa'], [:delete, 'd'], [:insert, 'b'], [:equal, 'cy']],
215
+ diffs
216
+ )
217
+
218
+ # Slide edit left.
219
+ diffs = [[:equal, 'a'], [:insert, 'ba'], [:equal, 'c']]
220
+ @dmp.diff_cleanupMerge(diffs)
221
+ assert_equal([[:insert, 'ab'], [:equal, 'ac']], diffs)
222
+
223
+ # Slide edit right.
224
+ diffs = [[:equal, 'c'], [:insert, 'ab'], [:equal, 'a']]
225
+ @dmp.diff_cleanupMerge(diffs)
226
+ assert_equal([[:equal, 'ca'], [:insert, 'ba']], diffs)
227
+
228
+ # Slide edit left recursive.
229
+ diffs = [
230
+ [:equal, 'a'], [:delete, 'b'], [:equal, 'c'],
231
+ [:delete, 'ac'], [:equal, 'x']
232
+ ]
233
+ @dmp.diff_cleanupMerge(diffs)
234
+ assert_equal([[:delete, 'abc'], [:equal, 'acx']], diffs)
235
+
236
+ # Slide edit right recursive.
237
+ diffs = [
238
+ [:equal, 'x'], [:delete, 'ca'], [:equal, 'c'],
239
+ [:delete, 'b'], [:equal, 'a']
240
+ ]
241
+ @dmp.diff_cleanupMerge(diffs)
242
+ assert_equal([[:equal, 'xca'], [:delete, 'cba']], diffs)
243
+ end
244
+
245
+ def test_diff_cleanupSemanticLossless
246
+ # Slide diffs to match logical boundaries.
247
+ # Null case.
248
+ diffs = []
249
+ @dmp.diff_cleanupSemanticLossless(diffs)
250
+ assert_equal([], diffs)
251
+
252
+ # Blank lines.
253
+ diffs = [
254
+ [:equal, "AAA\r\n\r\nBBB"],
255
+ [:insert, "\r\nDDD\r\n\r\nBBB"],
256
+ [:equal, "\r\nEEE"]
257
+ ]
258
+ @dmp.diff_cleanupSemanticLossless(diffs)
259
+ assert_equal([
260
+ [:equal, "AAA\r\n\r\n"],
261
+ [:insert, "BBB\r\nDDD\r\n\r\n"],
262
+ [:equal, "BBB\r\nEEE"]
263
+ ],
264
+ diffs
265
+ )
266
+
267
+ # Line boundaries.
268
+ diffs = [[:equal, "AAA\r\nBBB"], [:insert, " DDD\r\nBBB"], [:equal, " EEE"]]
269
+ @dmp.diff_cleanupSemanticLossless(diffs)
270
+ assert_equal(
271
+ [[:equal, "AAA\r\n"], [:insert, "BBB DDD\r\n"], [:equal, "BBB EEE"]],
272
+ diffs
273
+ )
274
+
275
+ # Word boundaries.
276
+ diffs = [[:equal, 'The c'], [:insert, 'ow and the c'], [:equal, 'at.']]
277
+ @dmp.diff_cleanupSemanticLossless(diffs)
278
+ assert_equal(
279
+ [[:equal, 'The '], [:insert, 'cow and the '], [:equal, 'cat.']],
280
+ diffs
281
+ )
282
+
283
+ # Alphanumeric boundaries.
284
+ diffs = [[:equal, 'The-c'], [:insert, 'ow-and-the-c'], [:equal, 'at.']]
285
+ @dmp.diff_cleanupSemanticLossless(diffs)
286
+ assert_equal(
287
+ [[:equal, 'The-'], [:insert, 'cow-and-the-'], [:equal, 'cat.']],
288
+ diffs
289
+ )
290
+
291
+ # Hitting the start.
292
+ diffs = [[:equal, 'a'], [:delete, 'a'], [:equal, 'ax']]
293
+ @dmp.diff_cleanupSemanticLossless(diffs)
294
+ assert_equal([[:delete, 'a'], [:equal, 'aax']], diffs)
295
+
296
+ # Hitting the end.
297
+ diffs = [[:equal, 'xa'], [:delete, 'a'], [:equal, 'a']]
298
+ @dmp.diff_cleanupSemanticLossless(diffs)
299
+ assert_equal([[:equal, 'xaa'], [:delete, 'a']], diffs)
300
+ end
301
+
302
+ def test_diff_cleanupSemantic
303
+ # Cleanup semantically trivial equalities.
304
+ # Null case.
305
+ diffs = []
306
+ @dmp.diff_cleanupSemantic(diffs)
307
+ assert_equal([], diffs)
308
+
309
+ # No elimination #1.
310
+ diffs = [[:delete, 'ab'], [:insert, 'cd'], [:equal, '12'], [:delete, 'e']]
311
+ @dmp.diff_cleanupSemantic(diffs)
312
+ assert_equal(
313
+ [[:delete, 'ab'], [:insert, 'cd'], [:equal, '12'], [:delete, 'e']],
314
+ diffs
315
+ )
316
+
317
+ # No elimination #2.
318
+ diffs = [
319
+ [:delete, 'abc'], [:insert, 'ABC'],
320
+ [:equal, '1234'], [:delete, 'wxyz']
321
+ ]
322
+ @dmp.diff_cleanupSemantic(diffs)
323
+ assert_equal(
324
+ [[:delete, 'abc'], [:insert, 'ABC'], [:equal, '1234'], [:delete, 'wxyz']],
325
+ diffs
326
+ )
327
+
328
+ # Simple elimination.
329
+ diffs = [[:delete, 'a'], [:equal, 'b'], [:delete, 'c']]
330
+ @dmp.diff_cleanupSemantic(diffs)
331
+ assert_equal([[:delete, 'abc'], [:insert, 'b']], diffs)
332
+
333
+ # Backpass elimination.
334
+ diffs = [
335
+ [:delete, 'ab'], [:equal, 'cd'], [:delete, 'e'],
336
+ [:equal, 'f'], [:insert, 'g']
337
+ ]
338
+ @dmp.diff_cleanupSemantic(diffs)
339
+ assert_equal([[:delete, 'abcdef'], [:insert, 'cdfg']], diffs)
340
+
341
+ # Multiple eliminations.
342
+ diffs = [
343
+ [:insert, '1'], [:equal, 'A'], [:delete, 'B'],
344
+ [:insert, '2'], [:equal, '_'], [:insert, '1'],
345
+ [:equal, 'A'], [:delete, 'B'], [:insert, '2']
346
+ ]
347
+ @dmp.diff_cleanupSemantic(diffs)
348
+ assert_equal([[:delete, 'AB_AB'], [:insert, '1A2_1A2']], diffs)
349
+
350
+ # Word boundaries.
351
+ diffs = [[:equal, 'The c'], [:delete, 'ow and the c'], [:equal, 'at.']]
352
+ @dmp.diff_cleanupSemantic(diffs)
353
+ assert_equal(
354
+ [[:equal, 'The '], [:delete, 'cow and the '], [:equal, 'cat.']],
355
+ diffs
356
+ )
357
+
358
+ # No overlap elimination.
359
+ diffs =[[:delete, 'abcxx'],[:insert, 'xxdef']]
360
+ @dmp.diff_cleanupSemantic(diffs)
361
+ assert_equal([[:delete, 'abcxx'], [:insert, 'xxdef']], diffs)
362
+
363
+ # Overlap elimination.
364
+ diffs = [[:delete, 'abcxxx'], [:insert, 'xxxdef']]
365
+ @dmp.diff_cleanupSemantic(diffs)
366
+ assert_equal([[:delete, 'abc'], [:equal, 'xxx'], [:insert, 'def']], diffs)
367
+
368
+ # Two overlap eliminations.
369
+ diffs = [
370
+ [:delete, 'abcd1212'], [:insert, '1212efghi'], [:equal, '----'],
371
+ [:delete, 'A3'], [:insert, '3BC']
372
+ ]
373
+ @dmp.diff_cleanupSemantic(diffs)
374
+ assert_equal([
375
+ [:delete, 'abcd'], [:equal, '1212'], [:insert, 'efghi'],
376
+ [:equal, '----'], [:delete, 'A'], [:equal, '3'], [:insert, 'BC']
377
+ ],
378
+ diffs
379
+ )
380
+ end
381
+
382
+ def test_diff_cleanupEfficiency
383
+ # Cleanup operationally trivial equalities.
384
+ @dmp.diff_editCost = 4
385
+ # Null case.
386
+ diffs = []
387
+ @dmp.diff_cleanupEfficiency(diffs)
388
+ assert_equal([], diffs)
389
+
390
+ # No elimination.
391
+ diffs = [
392
+ [:delete, 'ab'], [:insert, '12'], [:equal, 'wxyz'],
393
+ [:delete, 'cd'], [:insert, '34']
394
+ ]
395
+ @dmp.diff_cleanupEfficiency(diffs)
396
+ assert_equal([
397
+ [:delete, 'ab'], [:insert, '12'], [:equal, 'wxyz'],
398
+ [:delete, 'cd'], [:insert, '34']
399
+ ],
400
+ diffs
401
+ )
402
+
403
+ # Four-edit elimination.
404
+ diffs = [
405
+ [:delete, 'ab'], [:insert, '12'], [:equal, 'xyz'],
406
+ [:delete, 'cd'], [:insert, '34']
407
+ ]
408
+ @dmp.diff_cleanupEfficiency(diffs)
409
+ assert_equal([[:delete, 'abxyzcd'], [:insert, '12xyz34']], diffs)
410
+
411
+ # Three-edit elimination.
412
+ diffs = [[:insert, '12'], [:equal, 'x'], [:delete, 'cd'], [:insert, '34']]
413
+ @dmp.diff_cleanupEfficiency(diffs)
414
+ assert_equal([[:delete, 'xcd'], [:insert, '12x34']], diffs)
415
+
416
+ # Backpass elimination.
417
+ diffs = [
418
+ [:delete, 'ab'], [:insert, '12'], [:equal, 'xy'], [:insert, '34'],
419
+ [:equal, 'z'], [:delete, 'cd'], [:insert, '56']
420
+ ]
421
+ @dmp.diff_cleanupEfficiency(diffs)
422
+ assert_equal([[:delete, 'abxyzcd'], [:insert, '12xy34z56']], diffs)
423
+
424
+ # High cost elimination.
425
+ @dmp.diff_editCost = 5
426
+ diffs = [
427
+ [:delete, 'ab'], [:insert, '12'], [:equal, 'wxyz'],
428
+ [:delete, 'cd'], [:insert, '34']
429
+ ]
430
+ @dmp.diff_cleanupEfficiency(diffs)
431
+ assert_equal([[:delete, 'abwxyzcd'], [:insert, '12wxyz34']], diffs)
432
+ @dmp.diff_editCost = 4
433
+ end
434
+
435
+ def test_diff_prettyHtml
436
+ # Pretty print.
437
+ diffs = [[:equal, 'a\n'], [:delete, '<B>b</B>'], [:insert, 'c&d']]
438
+ assert_equal(
439
+ '<span>a&para;<br></span><del style="background:#ffe6e6;">&lt;B&gt;' +
440
+ 'b&lt;/B&gt;</del><ins style="background:#e6ffe6;">c&amp;d</ins>',
441
+ @dmp.diff_prettyHtml(diffs)
442
+ )
443
+ end
444
+
445
+ def test_diff_text
446
+ # Compute the source and destination texts.
447
+ diffs = [
448
+ [:equal, 'jump'], [:delete, 's'], [:insert, 'ed'], [:equal, ' over '],
449
+ [:delete, 'the'], [:insert, 'a'], [:equal, ' lazy']
450
+ ]
451
+ assert_equal('jumps over the lazy', @dmp.diff_text1(diffs))
452
+ assert_equal('jumped over a lazy', @dmp.diff_text2(diffs))
453
+ end
454
+
455
+ def test_diff_delta
456
+ # Convert a diff into delta string.
457
+ diffs = [
458
+ [:equal, 'jump'], [:delete, 's'], [:insert, 'ed'], [:equal, ' over '],
459
+ [:delete, 'the'], [:insert, 'a'], [:equal, ' lazy'], [:insert, 'old dog']
460
+ ]
461
+ text1 = @dmp.diff_text1(diffs)
462
+ assert_equal('jumps over the lazy', text1)
463
+
464
+ delta = @dmp.diff_toDelta(diffs)
465
+ assert_equal("=4\t-1\t+ed\t=6\t-3\t+a\t=5\t+old dog", delta)
466
+
467
+ # Convert delta string into a diff.
468
+ assert_equal(diffs, @dmp.diff_fromDelta(text1, delta))
469
+
470
+ # Generates error (19 != 20).
471
+ assert_raise ArgumentError do
472
+ @dmp.diff_fromDelta(text1 + 'x', delta)
473
+ end
474
+
475
+ # Generates error (19 != 18).
476
+ assert_raise ArgumentError do
477
+ @dmp.diff_fromDelta(text1[1..-1], delta)
478
+ end
479
+
480
+ # Test deltas with special characters.
481
+ diffs = [
482
+ [:equal, "\u0680 \x00 \t %"],
483
+ [:delete, "\u0681 \x01 \n ^"],
484
+ [:insert, "\u0682 \x02 \\ |"]
485
+ ]
486
+ text1 = @dmp.diff_text1(diffs)
487
+ assert_equal("\u0680 \x00 \t %\u0681 \x01 \n ^", text1)
488
+
489
+ delta = @dmp.diff_toDelta(diffs)
490
+ assert_equal("=7\t-7\t+%DA%82 %02 %5C %7C", delta)
491
+
492
+ # Convert delta string into a diff.
493
+ assert_equal(diffs, @dmp.diff_fromDelta(text1, delta))
494
+
495
+ # Verify pool of unchanged characters.
496
+ diffs = [[:insert, "A-Z a-z 0-9 - _ . ! ~ * \' ( ) / ? : @ & = + $ , # "]]
497
+ text2 = @dmp.diff_text2(diffs)
498
+ assert_equal("A-Z a-z 0-9 - _ . ! ~ * \' ( ) / ? : @ & = + $ , # ", text2)
499
+
500
+ delta = @dmp.diff_toDelta(diffs)
501
+ assert_equal("+A-Z a-z 0-9 - _ . ! ~ * \' ( ) / ? : @ & = + $ , # ", delta)
502
+
503
+ # Convert delta string into a diff.
504
+ assert_equal(diffs, @dmp.diff_fromDelta('', delta))
505
+ end
506
+
507
+ def test_diff_xIndex
508
+ # Translate a location in text1 to text2.
509
+ # Translation on equality.
510
+ diffs = [[:delete, 'a'], [:insert, '1234'], [:equal, 'xyz']]
511
+ assert_equal(5, @dmp.diff_xIndex(diffs, 2))
512
+
513
+ # Translation on deletion.
514
+ diffs = [[:equal, 'a'], [:delete, '1234'], [:equal, 'xyz']]
515
+ assert_equal(1, @dmp.diff_xIndex(diffs, 3))
516
+ end
517
+
518
+ def test_diff_levenshtein
519
+ # Levenshtein with trailing equality.
520
+ diffs = [[:delete, 'abc'], [:insert, '1234'], [:equal, 'xyz']]
521
+ assert_equal(4, @dmp.diff_levenshtein(diffs))
522
+ # Levenshtein with leading equality.
523
+ diffs = [[:equal, 'xyz'], [:delete, 'abc'], [:insert, '1234']]
524
+ assert_equal(4, @dmp.diff_levenshtein(diffs))
525
+ # Levenshtein with middle equality.
526
+ diffs = [[:delete, 'abc'], [:equal, 'xyz'], [:insert, '1234']]
527
+ assert_equal(7, @dmp.diff_levenshtein(diffs))
528
+ end
529
+
530
+ def test_diff_bisect
531
+ # Normal.
532
+ a = 'cat'
533
+ b = 'map'
534
+ # Since the resulting diff hasn't been normalized, it would be ok if
535
+ # the insertion and deletion pairs are swapped.
536
+ # If the order changes, tweak this test as required.
537
+ diffs = [
538
+ [:delete, 'c'], [:insert, 'm'], [:equal, 'a'],
539
+ [:delete, 't'], [:insert, 'p']
540
+ ]
541
+ assert_equal(diffs, @dmp.diff_bisect(a, b, nil))
542
+
543
+ # Timeout.
544
+ assert_equal(
545
+ [[:delete, 'cat'], [:insert, 'map']],
546
+ @dmp.diff_bisect(a, b, Time.now - 1)
547
+ )
548
+ end
549
+
550
+ def test_diff_main
551
+ # Perform a trivial diff.
552
+ # Null case.
553
+ assert_equal([], @dmp.diff_main('', '', false))
554
+
555
+ # Equality.
556
+ assert_equal([[:equal, 'abc']], @dmp.diff_main('abc', 'abc', false))
557
+
558
+ # Simple insertion.
559
+ assert_equal(
560
+ [[:equal, 'ab'], [:insert, '123'], [:equal, 'c']],
561
+ @dmp.diff_main('abc', 'ab123c', false)
562
+ )
563
+
564
+ # Simple deletion.
565
+ assert_equal(
566
+ [[:equal, 'a'], [:delete, '123'], [:equal, 'bc']],
567
+ @dmp.diff_main('a123bc', 'abc', false)
568
+ )
569
+
570
+ # Two insertions.
571
+ assert_equal([
572
+ [:equal, 'a'], [:insert, '123'], [:equal, 'b'],
573
+ [:insert, '456'], [:equal, 'c']
574
+ ],
575
+ @dmp.diff_main('abc', 'a123b456c', false)
576
+ )
577
+
578
+ # Two deletions.
579
+ assert_equal([
580
+ [:equal, 'a'], [:delete, '123'], [:equal, 'b'],
581
+ [:delete, '456'], [:equal, 'c']
582
+ ],
583
+ @dmp.diff_main('a123b456c', 'abc', false)
584
+ )
585
+
586
+ # Perform a real diff.
587
+ # Switch off the timeout.
588
+ @dmp.diff_timeout = 0
589
+ # Simple cases.
590
+ assert_equal(
591
+ [[:delete, 'a'], [:insert, 'b']],
592
+ @dmp.diff_main('a', 'b', false)
593
+ )
594
+
595
+ assert_equal([
596
+ [:delete, 'Apple'], [:insert, 'Banana'], [:equal, 's are a'],
597
+ [:insert, 'lso'], [:equal, ' fruit.']
598
+ ],
599
+ @dmp.diff_main('Apples are a fruit.', 'Bananas are also fruit.', false)
600
+ )
601
+
602
+ assert_equal([
603
+ [:delete, 'a'], [:insert, "\u0680"], [:equal, 'x'],
604
+ [:delete, "\t"], [:insert, "\0"]
605
+ ],
606
+ @dmp.diff_main("ax\t", "\u0680x\0", false)
607
+ )
608
+
609
+ # Overlaps.
610
+ assert_equal([
611
+ [:delete, '1'], [:equal, 'a'], [:delete, 'y'],
612
+ [:equal, 'b'], [:delete, '2'], [:insert, 'xab']
613
+ ],
614
+ @dmp.diff_main('1ayb2', 'abxab', false)
615
+ )
616
+
617
+ assert_equal(
618
+ [[:insert, 'xaxcx'], [:equal, 'abc'], [:delete, 'y']],
619
+ @dmp.diff_main('abcy', 'xaxcxabc', false)
620
+ )
621
+
622
+ assert_equal([
623
+ [:delete, 'ABCD'], [:equal, 'a'], [:delete, '='], [:insert, '-'],
624
+ [:equal, 'bcd'], [:delete, '='], [:insert, '-'],
625
+ [:equal, 'efghijklmnopqrs'], [:delete, 'EFGHIJKLMNOefg']
626
+ ],
627
+ @dmp.diff_main(
628
+ 'ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg',
629
+ 'a-bcd-efghijklmnopqrs',
630
+ false
631
+ )
632
+ )
633
+
634
+ # Large equality.
635
+ assert_equal(
636
+ [
637
+ [:insert, ' '], [:equal, 'a'], [:insert, 'nd'],
638
+ [:equal, ' [[Pennsylvania]]'], [:delete, ' and [[New']
639
+ ],
640
+ @dmp.diff_main(
641
+ 'a [[Pennsylvania]] and [[New', ' and [[Pennsylvania]]', false
642
+ )
643
+ )
644
+
645
+ # Timeout.
646
+ @dmp.diff_timeout = 0.1 # 100ms
647
+ a = "`Twas brillig, and the slithy toves\nDid gyre and gimble in the " +
648
+ "wabe:\nAll mimsy were the borogoves,\nAnd the mome raths outgrabe.\n"
649
+ b = "I am the very model of a modern major general,\nI\'ve information " +
650
+ "vegetable, animal, and mineral,\nI know the kings of England, and " +
651
+ "I quote the fights historical,\nFrom Marathon to Waterloo, in " +
652
+ "order categorical.\n"
653
+ # Increase the text lengths by 1024 times to ensure a timeout.
654
+ a = a * 1024
655
+ b = b * 1024
656
+ start_time = Time.now
657
+ @dmp.diff_main(a, b)
658
+ end_time = Time.now
659
+ # Test that we took at least the timeout period.
660
+ assert_equal(true, @dmp.diff_timeout <= end_time - start_time)
661
+ # Test that we didn't take forever (be forgiving).
662
+ # Theoretically this test could fail very occasionally if the
663
+ # OS task swaps or locks up for a second at the wrong moment.
664
+ assert_equal(true, @dmp.diff_timeout * 1000 * 2 > end_time - start_time)
665
+ @dmp.diff_timeout = 0
666
+
667
+ # Test the linemode speedup.
668
+ # Must be long to pass the 100 char cutoff.
669
+ # Simple line-mode.
670
+ a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" +
671
+ "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" +
672
+ "1234567890\n1234567890\n1234567890\n"
673
+ b = "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n" +
674
+ "abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n" +
675
+ "abcdefghij\nabcdefghij\nabcdefghij\n"
676
+ assert_equal(@dmp.diff_main(a, b, false), @dmp.diff_main(a, b, true))
677
+
678
+ # Single line-mode.
679
+ a = '123456789012345678901234567890123456789012345678901234567890' +
680
+ '123456789012345678901234567890123456789012345678901234567890' +
681
+ '1234567890'
682
+ b = 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij' +
683
+ 'abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij'
684
+ assert_equal(@dmp.diff_main(a, b, false), @dmp.diff_main(a, b, true))
685
+
686
+ # Overlap line-mode.
687
+ a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" +
688
+ "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n" +
689
+ "1234567890\n1234567890\n1234567890\n"
690
+ b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n" +
691
+ "1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n" +
692
+ "1234567890\n1234567890\nabcdefghij\n"
693
+
694
+
695
+ diffs_linemode = @dmp.diff_main(a, b, false)
696
+ diffs_textmode = @dmp.diff_main(a, b, true)
697
+
698
+ assert_equal(
699
+ @dmp.diff_text1(diffs_linemode),
700
+ @dmp.diff_text1(diffs_textmode)
701
+ )
702
+
703
+ assert_equal(
704
+ @dmp.diff_text2(diffs_linemode),
705
+ @dmp.diff_text2(diffs_textmode)
706
+ )
707
+
708
+ # Test null inputs.
709
+ assert_raise ArgumentError do
710
+ @dmp.diff_main(nil, nil)
711
+ end
712
+ end
713
+
714
+ def test_match_alphabet
715
+ # Initialise the bitmasks for Bitap.
716
+ # Unique.
717
+ assert_equal({'a'=>4, 'b'=>2, 'c'=>1}, @dmp.match_alphabet('abc'))
718
+
719
+ # Duplicates.
720
+ assert_equal({'a'=>37, 'b'=>18, 'c'=>8}, @dmp.match_alphabet('abcaba'))
721
+ end
722
+
723
+ def test_match_bitap
724
+ # Bitap algorithm.
725
+ @dmp.match_distance = 100
726
+ @dmp.match_threshold = 0.5
727
+ # Exact matches.
728
+ assert_equal(5, @dmp.match_bitap('abcdefghijk', 'fgh', 5))
729
+
730
+ assert_equal(5, @dmp.match_bitap('abcdefghijk', 'fgh', 0))
731
+
732
+ # Fuzzy matches.
733
+ assert_equal(4, @dmp.match_bitap('abcdefghijk', 'efxhi', 0))
734
+
735
+ assert_equal(2, @dmp.match_bitap('abcdefghijk', 'cdefxyhijk', 5))
736
+
737
+ assert_equal(-1, @dmp.match_bitap('abcdefghijk', 'bxy', 1))
738
+
739
+ # Overflow.
740
+ assert_equal(2, @dmp.match_bitap('123456789xx0', '3456789x0', 2))
741
+
742
+ # Threshold test.
743
+ @dmp.match_threshold = 0.4
744
+ assert_equal(4, @dmp.match_bitap('abcdefghijk', 'efxyhi', 1))
745
+
746
+ @dmp.match_threshold = 0.3
747
+ assert_equal(-1, @dmp.match_bitap('abcdefghijk', 'efxyhi', 1))
748
+
749
+ @dmp.match_threshold = 0.0
750
+ assert_equal(1, @dmp.match_bitap('abcdefghijk', 'bcdef', 1))
751
+ @dmp.match_threshold = 0.5
752
+
753
+ # Multiple select.
754
+ assert_equal(0, @dmp.match_bitap('abcdexyzabcde', 'abccde', 3))
755
+
756
+ assert_equal(8, @dmp.match_bitap('abcdexyzabcde', 'abccde', 5))
757
+
758
+ # Distance test.
759
+ @dmp.match_distance = 10 # Strict location.
760
+ assert_equal(
761
+ -1,
762
+ @dmp.match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 24)
763
+ )
764
+
765
+ assert_equal(
766
+ 0,
767
+ @dmp.match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdxxefg', 1)
768
+ )
769
+
770
+ @dmp.match_distance = 1000 # Loose location.
771
+ assert_equal(
772
+ 0,
773
+ @dmp.match_bitap('abcdefghijklmnopqrstuvwxyz', 'abcdefg', 24)
774
+ )
775
+ end
776
+
777
+ def test_match_main
778
+ # Full match.
779
+ # Shortcut matches.
780
+ assert_equal(0, @dmp.match_main('abcdef', 'abcdef', 1000))
781
+
782
+ assert_equal(-1, @dmp.match_main('', 'abcdef', 1))
783
+
784
+ assert_equal(3, @dmp.match_main('abcdef', '', 3))
785
+
786
+ assert_equal(3, @dmp.match_main('abcdef', 'de', 3))
787
+
788
+ # Beyond end match.
789
+ assert_equal(3, @dmp.match_main("abcdef", "defy", 4))
790
+
791
+ # Oversized pattern.
792
+ assert_equal(0, @dmp.match_main("abcdef", "abcdefy", 0))
793
+
794
+ # Complex match.
795
+ assert_equal(
796
+ 4,
797
+ @dmp.match_main(
798
+ 'I am the very model of a modern major general.',
799
+ ' that berry ',
800
+ 5
801
+ )
802
+ )
803
+
804
+ # Test null inputs.
805
+ assert_raise ArgumentError do
806
+ @dmp.match_main(nil, nil, 0)
807
+ end
808
+ end
809
+
810
+ # Patch tests
811
+
812
+ def test_patch_obj
813
+ # Patch Object.
814
+ p = PatchObj.new
815
+ p.start1 = 20
816
+ p.start2 = 21
817
+ p.length1 = 18
818
+ p.length2 = 17
819
+ p.diffs = [
820
+ [:equal, 'jump'],
821
+ [:delete, 's'],
822
+ [:insert, 'ed'],
823
+ [:equal, ' over '],
824
+ [:delete, 'the'],
825
+ [:insert, 'a'],
826
+ [:equal, "\nlaz"]
827
+ ]
828
+ strp = p.to_s
829
+ assert_equal(
830
+ "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n",
831
+ strp
832
+ )
833
+ end
834
+
835
+ def test_patch_fromText
836
+ assert_equal([], @dmp.patch_fromText(""))
837
+
838
+ [
839
+ "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n",
840
+ "@@ -1 +1 @@\n-a\n+b\n",
841
+ "@@ -1 +1 @@\n-a\n+b\n",
842
+ "@@ -0,0 +1,3 @@\n+abc\n"
843
+ ].each do |strp|
844
+ assert_equal(strp, @dmp.patch_fromText(strp).first.to_s)
845
+ end
846
+
847
+ # Generates error.
848
+ assert_raise ArgumentError do
849
+ @dmp.patch_fromText('Bad\nPatch\n')
850
+ end
851
+ end
852
+
853
+ def test_patch_toText
854
+ [
855
+ "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n",
856
+ "@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n"
857
+ ].each do |strp|
858
+ p = @dmp.patch_fromText(strp)
859
+ assert_equal(strp, @dmp.patch_toText(p))
860
+ end
861
+ end
862
+
863
+ def test_patch_addContext
864
+ @dmp.patch_margin = 4
865
+ p = @dmp.patch_fromText("@@ -21,4 +21,10 @@\n-jump\n+somersault\n").first
866
+ @dmp.patch_addContext(p, 'The quick brown fox jumps over the lazy dog.')
867
+ assert_equal(
868
+ "@@ -17,12 +17,18 @@\n fox \n-jump\n+somersault\n s ov\n",
869
+ p.to_s
870
+ )
871
+
872
+ # Same, but not enough trailing context.
873
+ p = @dmp.patch_fromText("@@ -21,4 +21,10 @@\n-jump\n+somersault\n").first
874
+ @dmp.patch_addContext(p, 'The quick brown fox jumps.')
875
+ assert_equal(
876
+ "@@ -17,10 +17,16 @@\n fox \n-jump\n+somersault\n s.\n",
877
+ p.to_s
878
+ )
879
+
880
+ # Same, but not enough leading context.
881
+ p = @dmp.patch_fromText("@@ -3 +3,2 @@\n-e\n+at\n").first
882
+ @dmp.patch_addContext(p, 'The quick brown fox jumps.')
883
+ assert_equal(
884
+ "@@ -1,7 +1,8 @@\n Th\n-e\n+at\n qui\n",
885
+ p.to_s
886
+ )
887
+
888
+ # Same, but with ambiguity.
889
+ p = @dmp.patch_fromText("@@ -3 +3,2 @@\n-e\n+at\n").first
890
+ @dmp.patch_addContext(
891
+ p,
892
+ 'The quick brown fox jumps. The quick brown fox crashes.'
893
+ );
894
+
895
+ assert_equal(
896
+ "@@ -1,27 +1,28 @@\n Th\n-e\n+at\n quick brown fox jumps. \n",
897
+ p.to_s
898
+ )
899
+ end
900
+
901
+ def test_patch_make
902
+ # Null case.
903
+ patches = @dmp.patch_make('', '')
904
+ assert_equal('', @dmp.patch_toText(patches))
905
+
906
+ text1 = 'The quick brown fox jumps over the lazy dog.'
907
+ text2 = 'That quick brown fox jumped over a lazy dog.'
908
+ # Text2+Text1 inputs.
909
+ expectedPatch = "@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 " +
910
+ "@@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n"
911
+
912
+ # The second patch must be "-21,17 +21,18",
913
+ # not "-22,17 +21,18" due to rolling context
914
+ patches = @dmp.patch_make(text2, text1)
915
+ assert_equal(expectedPatch, @dmp.patch_toText(patches))
916
+
917
+ # Text1+Text2 inputs.
918
+ expectedPatch = "@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18" +
919
+ " +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n"
920
+ patches = @dmp.patch_make(text1, text2)
921
+ assert_equal(expectedPatch, @dmp.patch_toText(patches))
922
+
923
+ # Diff input.
924
+ diffs = @dmp.diff_main(text1, text2, false)
925
+ patches = @dmp.patch_make(diffs)
926
+ assert_equal(expectedPatch, @dmp.patch_toText(patches))
927
+
928
+ # Text1+Diff inputs.
929
+ patches = @dmp.patch_make(text1, diffs)
930
+ assert_equal(expectedPatch, @dmp.patch_toText(patches))
931
+
932
+ # Text1+Text2+Diff inputs (deprecated)
933
+ patches = @dmp.patch_make(text1, text2, diffs)
934
+ assert_equal(expectedPatch, @dmp.patch_toText(patches))
935
+
936
+ # Character encoding.
937
+ patches = @dmp.patch_make(
938
+ '`1234567890-=[]\\;\',./',
939
+ '~!@#$%^&*()_+{}|:"<>?'
940
+ )
941
+ assert_equal(
942
+ "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;\',./\n+~!" +
943
+ "@\#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n",
944
+ @dmp.patch_toText(patches)
945
+ )
946
+
947
+ # Character decoding.
948
+ diffs = [
949
+ [:delete, '`1234567890-=[]\\;\',./'],
950
+ [:insert, '~!@#$%^&*()_+{}|:"<>?']
951
+ ]
952
+ assert_equal(
953
+ diffs,
954
+ @dmp.patch_fromText(
955
+ "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;\',./\n+~!" +
956
+ "@\#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n"
957
+ ).first.diffs
958
+ )
959
+
960
+ # Long string with repeats.
961
+ text1 = 'abcdef' * 100
962
+ text2 = text1 + '123'
963
+ expectedPatch = "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n"
964
+ patches = @dmp.patch_make(text1, text2)
965
+ assert_equal(expectedPatch, @dmp.patch_toText(patches))
966
+
967
+ # Test null inputs.
968
+ assert_raise ArgumentError do
969
+ @dmp.patch_make(nil)
970
+ end
971
+ end
972
+
973
+ def test_patch_splitMax
974
+ # Assumes that dmp.Match_MaxBits is 32.
975
+ patches = @dmp.patch_make(
976
+ 'abcdefghijklmnopqrstuvwxyz01234567890',
977
+ 'XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0'
978
+ )
979
+
980
+ @dmp.patch_splitMax(patches)
981
+ assert_equal(
982
+ "@@ -1,32 +1,46 @@\n+X\n ab\n+X\n cd\n+X\n ef\n+X\n gh\n+X\n "+
983
+ "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 " +
984
+ "wx\n+X\n yz\n+X\n 012345\n@@ -25,13 +39,18 @@\n zX01\n+X\n 23\n+X\n " +
985
+ "45\n+X\n 67\n+X\n 89\n+X\n 0\n",
986
+ @dmp.patch_toText(patches)
987
+ )
988
+
989
+ patches = @dmp.patch_make(
990
+ 'abcdef1234567890123456789012345678901234567890' +
991
+ '123456789012345678901234567890uvwxyz',
992
+ 'abcdefuvwxyz'
993
+ )
994
+
995
+ oldToText = @dmp.patch_toText(patches)
996
+ @dmp.patch_splitMax(patches)
997
+ assert_equal(oldToText, @dmp.patch_toText(patches))
998
+
999
+ patches = @dmp.patch_make(
1000
+ '1234567890123456789012345678901234567890123456789012345678901234567890',
1001
+ 'abc'
1002
+ )
1003
+
1004
+ @dmp.patch_splitMax(patches)
1005
+ assert_equal(
1006
+ "@@ -1,32 +1,4 @@\n-1234567890123456789012345678\n 9012\n" +
1007
+ "@@ -29,32 +1,4 @@\n-9012345678901234567890123456\n 7890\n" +
1008
+ "@@ -57,14 +1,3 @@\n-78901234567890\n+abc\n",
1009
+ @dmp.patch_toText(patches)
1010
+ )
1011
+
1012
+ patches = @dmp.patch_make(
1013
+ 'abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1',
1014
+ 'abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1'
1015
+ )
1016
+
1017
+ @dmp.patch_splitMax(patches)
1018
+ assert_equal(
1019
+ "@@ -2,32 +2,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n" +
1020
+ "@@ -29,32 +29,32 @@\n bcdefghij , h : \n-0\n+1\n , t : 1 abcdef\n",
1021
+ @dmp.patch_toText(patches)
1022
+ )
1023
+ end
1024
+
1025
+ def test_patch_addPadding
1026
+ # Both edges full.
1027
+ patches = @dmp.patch_make('', 'test')
1028
+ assert_equal("@@ -0,0 +1,4 @@\n+test\n", @dmp.patch_toText(patches))
1029
+ @dmp.patch_addPadding(patches)
1030
+ assert_equal(
1031
+ "@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n",
1032
+ @dmp.patch_toText(patches)
1033
+ )
1034
+
1035
+ # Both edges partial.
1036
+ patches = @dmp.patch_make('XY', 'XtestY')
1037
+ assert_equal("@@ -1,2 +1,6 @@\n X\n+test\n Y\n", @dmp.patch_toText(patches))
1038
+ @dmp.patch_addPadding(patches)
1039
+ assert_equal(
1040
+ "@@ -2,8 +2,12 @@\n %02%03%04X\n+test\n Y%01%02%03\n",
1041
+ @dmp.patch_toText(patches)
1042
+ )
1043
+
1044
+ # Both edges none.
1045
+ patches = @dmp.patch_make('XXXXYYYY', 'XXXXtestYYYY')
1046
+ assert_equal(
1047
+ "@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n",
1048
+ @dmp.patch_toText(patches)
1049
+ )
1050
+ @dmp.patch_addPadding(patches)
1051
+ assert_equal(
1052
+ "@@ -5,8 +5,12 @@\n XXXX\n+test\n YYYY\n",
1053
+ @dmp.patch_toText(patches)
1054
+ )
1055
+ end
1056
+
1057
+ def test_patch_apply
1058
+ @dmp.match_distance = 1000
1059
+ @dmp.match_threshold = 0.5
1060
+ @dmp.patch_deleteThreshold = 0.5
1061
+ # Null case.
1062
+ patches = @dmp.patch_make('', '')
1063
+ results = @dmp.patch_apply(patches, 'Hello world.')
1064
+ assert_equal(['Hello world.', []], results)
1065
+
1066
+ # Exact match.
1067
+ patches = @dmp.patch_make(
1068
+ 'The quick brown fox jumps over the lazy dog.',
1069
+ 'That quick brown fox jumped over a lazy dog.'
1070
+ )
1071
+
1072
+ results = @dmp.patch_apply(
1073
+ patches,
1074
+ 'The quick brown fox jumps over the lazy dog.'
1075
+ )
1076
+
1077
+ assert_equal(
1078
+ ['That quick brown fox jumped over a lazy dog.', [true, true]],
1079
+ results
1080
+ )
1081
+
1082
+ # Partial match.
1083
+ results = @dmp.patch_apply(
1084
+ patches,
1085
+ 'The quick red rabbit jumps over the tired tiger.'
1086
+ )
1087
+
1088
+ assert_equal(
1089
+ ['That quick red rabbit jumped over a tired tiger.', [true, true]],
1090
+ results
1091
+ )
1092
+
1093
+ # Failed match.
1094
+ results = @dmp.patch_apply(
1095
+ patches,
1096
+ 'I am the very model of a modern major general.'
1097
+ )
1098
+
1099
+ assert_equal(
1100
+ ['I am the very model of a modern major general.', [false, false]],
1101
+ results
1102
+ )
1103
+
1104
+ # Big delete, small change.
1105
+ patches = @dmp.patch_make(
1106
+ 'x1234567890123456789012345678901234567890123456789012345678901234567890y',
1107
+ 'xabcy'
1108
+ )
1109
+
1110
+ results = @dmp.patch_apply(
1111
+ patches,
1112
+ 'x123456789012345678901234567890-----++++++++++-----' +
1113
+ '123456789012345678901234567890y'
1114
+ )
1115
+
1116
+ assert_equal(['xabcy', [true, true]], results)
1117
+
1118
+ # Big delete, big change 1.
1119
+ patches = @dmp.patch_make(
1120
+ 'x1234567890123456789012345678901234567890123456789012345678901234567890y',
1121
+ 'xabcy'
1122
+ )
1123
+
1124
+ results = @dmp.patch_apply(
1125
+ patches,
1126
+ 'x12345678901234567890---------------++++++++++---------------' +
1127
+ '12345678901234567890y'
1128
+ )
1129
+
1130
+ assert_equal([
1131
+ 'xabc12345678901234567890---------------++++++++++---------------' +
1132
+ '12345678901234567890y',
1133
+ [false, true]
1134
+ ],
1135
+ results
1136
+ )
1137
+
1138
+ # Big delete, big change 2.
1139
+ @dmp.patch_deleteThreshold = 0.6
1140
+ patches = @dmp.patch_make(
1141
+ 'x1234567890123456789012345678901234567890123456789012345678901234567890y',
1142
+ 'xabcy'
1143
+ )
1144
+
1145
+ results = @dmp.patch_apply(
1146
+ patches,
1147
+ 'x12345678901234567890---------------++++++++++---------------' +
1148
+ '12345678901234567890y'
1149
+ )
1150
+
1151
+ assert_equal(['xabcy', [true, true]], results)
1152
+ @dmp.patch_deleteThreshold = 0.5
1153
+
1154
+ # Compensate for failed patch.
1155
+ @dmp.match_threshold = 0.0
1156
+ @dmp.match_distance = 0
1157
+ patches = @dmp.patch_make(
1158
+ 'abcdefghijklmnopqrstuvwxyz--------------------1234567890',
1159
+ 'abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------' +
1160
+ '1234567YYYYYYYYYY890'
1161
+ )
1162
+
1163
+ results = @dmp.patch_apply(
1164
+ patches,
1165
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890'
1166
+ )
1167
+
1168
+ assert_equal([
1169
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890',
1170
+ [false, true]
1171
+ ],
1172
+ results
1173
+ )
1174
+ @dmp.match_threshold = 0.5
1175
+ @dmp.match_distance = 1000
1176
+
1177
+ # No side effects.
1178
+ patches = @dmp.patch_make('', 'test')
1179
+ patchstr = @dmp.patch_toText(patches)
1180
+ @dmp.patch_apply(patches, '')
1181
+ assert_equal(patchstr, @dmp.patch_toText(patches))
1182
+
1183
+ # No side effects with major delete.
1184
+ patches = @dmp.patch_make(
1185
+ 'The quick brown fox jumps over the lazy dog.',
1186
+ 'Woof'
1187
+ )
1188
+
1189
+ patchstr = @dmp.patch_toText(patches)
1190
+ @dmp.patch_apply(patches, 'The quick brown fox jumps over the lazy dog.')
1191
+ assert_equal(patchstr, @dmp.patch_toText(patches))
1192
+
1193
+ # Edge exact match.
1194
+ patches = @dmp.patch_make('', 'test')
1195
+ results = @dmp.patch_apply(patches, '')
1196
+ assert_equal(['test', [true]], results)
1197
+
1198
+ # Near edge exact match.
1199
+ patches = @dmp.patch_make('XY', 'XtestY')
1200
+ results = @dmp.patch_apply(patches, 'XY')
1201
+ assert_equal(['XtestY', [true]], results)
1202
+
1203
+ # Edge partial match.
1204
+ patches = @dmp.patch_make('y', 'y123')
1205
+ results = @dmp.patch_apply(patches, 'x')
1206
+ assert_equal(['x123', [true]], results)
1207
+ end
1208
+ end