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