diff_match_patch 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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