100
101
def test_no_changes(self):
101
102
"""No conflicts because nothing changed"""
102
m3 = merge3.Merge3(['aaa', 'bbb'],
103
m3 = merge3.Merge3([b'aaa', b'bbb'],
106
self.assertEquals(m3.find_unconflicted(),
107
self.assertEqual(m3.find_unconflicted(),
109
self.assertEquals(list(m3.find_sync_regions()),
110
self.assertEqual(list(m3.find_sync_regions()),
115
self.assertEquals(list(m3.merge_regions()),
116
self.assertEqual(list(m3.merge_regions()),
116
117
[('unchanged', 0, 2)])
118
self.assertEquals(list(m3.merge_groups()),
119
[('unchanged', ['aaa', 'bbb'])])
119
self.assertEqual(list(m3.merge_groups()),
120
[('unchanged', [b'aaa', b'bbb'])])
121
122
def test_front_insert(self):
122
m3 = merge3.Merge3(['zz'],
123
['aaa', 'bbb', 'zz'],
123
m3 = merge3.Merge3([b'zz'],
124
[b'aaa', b'bbb', b'zz'],
126
127
# todo: should use a sentinal at end as from get_matching_blocks
127
128
# to match without zz
128
self.assertEquals(list(m3.find_sync_regions()),
129
self.assertEqual(list(m3.find_sync_regions()),
131
(1, 1, 3, 3, 1, 1),])
132
self.assertEquals(list(m3.merge_regions()),
133
self.assertEqual(list(m3.merge_regions()),
134
135
('unchanged', 0, 1)])
136
self.assertEquals(list(m3.merge_groups()),
137
[('a', ['aaa', 'bbb']),
138
('unchanged', ['zz'])])
137
self.assertEqual(list(m3.merge_groups()),
138
[('a', [b'aaa', b'bbb']),
139
('unchanged', [b'zz'])])
140
141
def test_null_insert(self):
141
142
m3 = merge3.Merge3([],
144
145
# todo: should use a sentinal at end as from get_matching_blocks
145
146
# to match without zz
146
self.assertEquals(list(m3.find_sync_regions()),
147
self.assertEqual(list(m3.find_sync_regions()),
148
[(0, 0, 2, 2, 0, 0)])
149
self.assertEquals(list(m3.merge_regions()),
150
self.assertEqual(list(m3.merge_regions()),
152
self.assertEquals(list(m3.merge_lines()),
153
self.assertEqual(list(m3.merge_lines()),
155
156
def test_no_conflicts(self):
156
157
"""No conflicts because only one side changed"""
157
m3 = merge3.Merge3(['aaa', 'bbb'],
158
['aaa', '111', 'bbb'],
158
m3 = merge3.Merge3([b'aaa', b'bbb'],
159
[b'aaa', b'111', b'bbb'],
161
self.assertEquals(m3.find_unconflicted(),
162
self.assertEqual(m3.find_unconflicted(),
162
163
[(0, 1), (1, 2)])
164
self.assertEquals(list(m3.find_sync_regions()),
165
self.assertEqual(list(m3.find_sync_regions()),
168
(2, 2, 3, 3, 2, 2),])
169
self.assertEquals(list(m3.merge_regions()),
170
self.assertEqual(list(m3.merge_regions()),
170
171
[('unchanged', 0, 1),
172
173
('unchanged', 1, 2),])
174
175
def test_append_a(self):
175
m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
176
['aaa\n', 'bbb\n', '222\n'],
176
m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
177
[b'aaa\n', b'bbb\n', b'222\n'],
178
[b'aaa\n', b'bbb\n'])
179
self.assertEquals(''.join(m3.merge_lines()),
180
self.assertEqual(b''.join(m3.merge_lines()),
182
183
def test_append_b(self):
183
m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
185
['aaa\n', 'bbb\n', '222\n'])
184
m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
185
[b'aaa\n', b'bbb\n'],
186
[b'aaa\n', b'bbb\n', b'222\n'])
187
self.assertEquals(''.join(m3.merge_lines()),
188
self.assertEqual(b''.join(m3.merge_lines()),
190
191
def test_append_agreement(self):
191
m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
192
['aaa\n', 'bbb\n', '222\n'],
193
['aaa\n', 'bbb\n', '222\n'])
192
m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
193
[b'aaa\n', b'bbb\n', b'222\n'],
194
[b'aaa\n', b'bbb\n', b'222\n'])
195
self.assertEquals(''.join(m3.merge_lines()),
196
self.assertEqual(b''.join(m3.merge_lines()),
198
199
def test_append_clash(self):
199
m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
200
['aaa\n', 'bbb\n', '222\n'],
201
['aaa\n', 'bbb\n', '333\n'])
200
m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
201
[b'aaa\n', b'bbb\n', b'222\n'],
202
[b'aaa\n', b'bbb\n', b'333\n'])
203
ml = m3.merge_lines(name_a='a',
208
self.assertEquals(''.join(ml),
204
ml = m3.merge_lines(name_a=b'a',
209
self.assertEqual(b''.join(ml),
219
220
def test_insert_agreement(self):
220
m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
221
['aaa\n', '222\n', 'bbb\n'],
222
['aaa\n', '222\n', 'bbb\n'])
221
m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
222
[b'aaa\n', b'222\n', b'bbb\n'],
223
[b'aaa\n', b'222\n', b'bbb\n'])
224
ml = m3.merge_lines(name_a='a',
229
self.assertEquals(''.join(ml), 'aaa\n222\nbbb\n')
225
ml = m3.merge_lines(name_a=b'a',
230
self.assertEqual(b''.join(ml), b'aaa\n222\nbbb\n')
232
233
def test_insert_clash(self):
233
234
"""Both try to insert lines in the same place."""
234
m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
235
['aaa\n', '111\n', 'bbb\n'],
236
['aaa\n', '222\n', 'bbb\n'])
235
m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
236
[b'aaa\n', b'111\n', b'bbb\n'],
237
[b'aaa\n', b'222\n', b'bbb\n'])
238
self.assertEquals(m3.find_unconflicted(),
239
self.assertEqual(m3.find_unconflicted(),
239
240
[(0, 1), (1, 2)])
241
self.assertEquals(list(m3.find_sync_regions()),
246
self.assertEquals(list(m3.merge_regions()),
248
('conflict', 1,1, 1,2, 1,2),
251
self.assertEquals(list(m3.merge_groups()),
252
[('unchanged', ['aaa\n']),
253
('conflict', [], ['111\n'], ['222\n']),
254
('unchanged', ['bbb\n']),
242
self.assertEqual(list(m3.find_sync_regions()),
245
(2, 2, 3, 3, 3, 3),])
247
self.assertEqual(list(m3.merge_regions()),
248
[('unchanged', 0, 1),
249
('conflict', 1, 1, 1, 2, 1, 2),
250
('unchanged', 1, 2)])
252
self.assertEqual(list(m3.merge_groups()),
253
[('unchanged', [b'aaa\n']),
254
('conflict', [], [b'111\n'], [b'222\n']),
255
('unchanged', [b'bbb\n']),
257
ml = m3.merge_lines(name_a='a',
262
self.assertEquals(''.join(ml),
258
ml = m3.merge_lines(name_a=b'a',
263
self.assertEqual(b''.join(ml),
272
273
def test_replace_clash(self):
273
274
"""Both try to insert lines in the same place."""
274
m3 = merge3.Merge3(['aaa', '000', 'bbb'],
275
['aaa', '111', 'bbb'],
276
['aaa', '222', 'bbb'])
275
m3 = merge3.Merge3([b'aaa', b'000', b'bbb'],
276
[b'aaa', b'111', b'bbb'],
277
[b'aaa', b'222', b'bbb'])
278
self.assertEquals(m3.find_unconflicted(),
279
self.assertEqual(m3.find_unconflicted(),
279
280
[(0, 1), (2, 3)])
281
self.assertEquals(list(m3.find_sync_regions()),
282
self.assertEqual(list(m3.find_sync_regions()),
285
(3, 3, 3, 3, 3, 3),])
286
287
def test_replace_multi(self):
287
288
"""Replacement with regions of different size."""
288
m3 = merge3.Merge3(['aaa', '000', '000', 'bbb'],
289
['aaa', '111', '111', '111', 'bbb'],
290
['aaa', '222', '222', '222', '222', 'bbb'])
289
m3 = merge3.Merge3([b'aaa', b'000', b'000', b'bbb'],
290
[b'aaa', b'111', b'111', b'111', b'bbb'],
291
[b'aaa', b'222', b'222', b'222', b'222', b'bbb'])
292
self.assertEquals(m3.find_unconflicted(),
293
self.assertEqual(m3.find_unconflicted(),
293
294
[(0, 1), (3, 4)])
296
self.assertEquals(list(m3.find_sync_regions()),
297
self.assertEqual(list(m3.find_sync_regions()),
300
(4, 4, 5, 5, 6, 6),])
301
302
def test_merge_poem(self):
302
303
"""Test case from diff3 manual"""
303
304
m3 = merge3.Merge3(TZU, LAO, TAO)
304
ml = list(m3.merge_lines('LAO', 'TAO'))
305
ml = list(m3.merge_lines(b'LAO', b'TAO'))
305
306
self.log('merge result:')
306
self.log(''.join(ml))
307
self.assertEquals(ml, MERGED_RESULT)
307
self.log(b''.join(ml))
308
self.assertEqual(ml, MERGED_RESULT)
309
310
def test_minimal_conflicts_common(self):
310
311
"""Reprocessing"""
311
base_text = ("a\n" * 20).splitlines(True)
312
this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
313
other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
312
base_text = (b"a\n" * 20).splitlines(True)
313
this_text = (b"a\n"*10+b"b\n" * 10).splitlines(True)
314
other_text = (b"a\n"*10+b"c\n"+b"b\n" * 8 + b"c\n").splitlines(True)
314
315
m3 = merge3.Merge3(base_text, other_text, this_text)
315
m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
316
merged_text = "".join(list(m_lines))
317
optimal_text = ("a\n" * 10 + "<<<<<<< OTHER\nc\n"
318
+ 8* "b\n" + "c\n=======\n"
319
+ 10*"b\n" + ">>>>>>> THIS\n")
316
m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True)
317
merged_text = b"".join(list(m_lines))
318
optimal_text = (b"a\n" * 10 + b"<<<<<<< OTHER\nc\n"
319
+ 8* b"b\n" + b"c\n=======\n"
320
+ 10*b"b\n" + b">>>>>>> THIS\n")
320
321
self.assertEqualDiff(optimal_text, merged_text)
322
323
def test_minimal_conflicts_unique(self):
323
324
def add_newline(s):
324
325
"""Add a newline to each entry in the string"""
325
return [(x+'\n') for x in s]
326
return [(int2byte(x)+b'\n') for x in bytearray(s)]
327
base_text = add_newline("abcdefghijklm")
328
this_text = add_newline("abcdefghijklmNOPQRSTUVWXYZ")
329
other_text = add_newline("abcdefghijklm1OPQRSTUVWXY2")
328
base_text = add_newline(b"abcdefghijklm")
329
this_text = add_newline(b"abcdefghijklmNOPQRSTUVWXYZ")
330
other_text = add_newline(b"abcdefghijklm1OPQRSTUVWXY2")
330
331
m3 = merge3.Merge3(base_text, other_text, this_text)
331
m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
332
merged_text = "".join(list(m_lines))
333
optimal_text = ''.join(add_newline("abcdefghijklm")
334
+ ["<<<<<<< OTHER\n1\n=======\nN\n>>>>>>> THIS\n"]
335
+ add_newline('OPQRSTUVWXY')
336
+ ["<<<<<<< OTHER\n2\n=======\nZ\n>>>>>>> THIS\n"]
332
m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True)
333
merged_text = b"".join(list(m_lines))
334
optimal_text = b''.join(add_newline(b"abcdefghijklm")
335
+ [b"<<<<<<< OTHER\n1\n=======\nN\n>>>>>>> THIS\n"]
336
+ add_newline(b'OPQRSTUVWXY')
337
+ [b"<<<<<<< OTHER\n2\n=======\nZ\n>>>>>>> THIS\n"]
338
339
self.assertEqualDiff(optimal_text, merged_text)
340
341
def test_minimal_conflicts_nonunique(self):
341
342
def add_newline(s):
342
343
"""Add a newline to each entry in the string"""
343
return [(x+'\n') for x in s]
344
return [(int2byte(x)+b'\n') for x in bytearray(s)]
345
base_text = add_newline("abacddefgghij")
346
this_text = add_newline("abacddefgghijkalmontfprz")
347
other_text = add_newline("abacddefgghijknlmontfprd")
346
base_text = add_newline(b"abacddefgghij")
347
this_text = add_newline(b"abacddefgghijkalmontfprz")
348
other_text = add_newline(b"abacddefgghijknlmontfprd")
348
349
m3 = merge3.Merge3(base_text, other_text, this_text)
349
m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
350
merged_text = "".join(list(m_lines))
351
optimal_text = ''.join(add_newline("abacddefgghijk")
352
+ ["<<<<<<< OTHER\nn\n=======\na\n>>>>>>> THIS\n"]
353
+ add_newline('lmontfpr')
354
+ ["<<<<<<< OTHER\nd\n=======\nz\n>>>>>>> THIS\n"]
350
m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True)
351
merged_text = b"".join(list(m_lines))
352
optimal_text = b''.join(add_newline(b"abacddefgghijk")
353
+ [b"<<<<<<< OTHER\nn\n=======\na\n>>>>>>> THIS\n"]
354
+ add_newline(b'lmontfpr')
355
+ [b"<<<<<<< OTHER\nd\n=======\nz\n>>>>>>> THIS\n"]
356
357
self.assertEqualDiff(optimal_text, merged_text)
358
359
def test_reprocess_and_base(self):
359
360
"""Reprocessing and showing base breaks correctly"""
360
base_text = ("a\n" * 20).splitlines(True)
361
this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
362
other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
361
base_text = (b"a\n" * 20).splitlines(True)
362
this_text = (b"a\n"*10+b"b\n" * 10).splitlines(True)
363
other_text = (b"a\n"*10+b"c\n"+b"b\n" * 8 + b"c\n").splitlines(True)
363
364
m3 = merge3.Merge3(base_text, other_text, this_text)
364
m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True,
365
base_marker='|||||||')
366
self.assertRaises(CantReprocessAndShowBase, list, m_lines)
365
m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True,
366
base_marker=b'|||||||')
367
self.assertRaises(merge3.CantReprocessAndShowBase, list, m_lines)
368
369
def test_binary(self):
369
self.assertRaises(BinaryFile, merge3.Merge3, ['\x00'], ['a'], ['b'])
370
self.assertRaises(BinaryFile, merge3.Merge3, [b'\x00'], [b'a'], [b'b'])
371
372
def test_dos_text(self):
375
other_text = b'c\r\n'
375
376
m3 = merge3.Merge3(base_text.splitlines(True),
376
377
other_text.splitlines(True),
377
378
this_text.splitlines(True))
378
m_lines = m3.merge_lines('OTHER', 'THIS')
379
self.assertEqual('<<<<<<< OTHER\r\nc\r\n=======\r\nb\r\n'
380
'>>>>>>> THIS\r\n'.splitlines(True), list(m_lines))
379
m_lines = m3.merge_lines(b'OTHER', b'THIS')
380
self.assertEqual(b'<<<<<<< OTHER\r\nc\r\n=======\r\nb\r\n'
381
b'>>>>>>> THIS\r\n'.splitlines(True), list(m_lines))
382
383
def test_mac_text(self):
386
387
m3 = merge3.Merge3(base_text.splitlines(True),
387
388
other_text.splitlines(True),
388
389
this_text.splitlines(True))
389
m_lines = m3.merge_lines('OTHER', 'THIS')
390
self.assertEqual('<<<<<<< OTHER\rc\r=======\rb\r'
391
'>>>>>>> THIS\r'.splitlines(True), list(m_lines))
390
m_lines = m3.merge_lines(b'OTHER', b'THIS')
391
self.assertEqual(b'<<<<<<< OTHER\rc\r=======\rb\r'
392
b'>>>>>>> THIS\r'.splitlines(True), list(m_lines))
393
394
def test_merge3_cherrypick(self):
396
other_text = "a\nb\nc\n"
395
base_text = b"a\nb\n"
397
other_text = b"a\nb\nc\n"
397
398
# When cherrypicking, lines in base are not part of the conflict
398
399
m3 = merge3.Merge3(base_text.splitlines(True),
399
400
this_text.splitlines(True),
400
401
other_text.splitlines(True), is_cherrypick=True)
401
402
m_lines = m3.merge_lines()
402
self.assertEqualDiff('a\n<<<<<<<\n=======\nc\n>>>>>>>\n',
403
self.assertEqualDiff(b'a\n<<<<<<<\n=======\nc\n>>>>>>>\n',
405
406
# This is not symmetric
406
407
m3 = merge3.Merge3(base_text.splitlines(True),
407
408
other_text.splitlines(True),
408
409
this_text.splitlines(True), is_cherrypick=True)
409
410
m_lines = m3.merge_lines()
410
self.assertEqualDiff('a\n<<<<<<<\nb\nc\n=======\n>>>>>>>\n',
411
self.assertEqualDiff(b'a\n<<<<<<<\nb\nc\n=======\n>>>>>>>\n',
413
414
def test_merge3_cherrypick_w_mixed(self):
414
base_text = 'a\nb\nc\nd\ne\n'
415
this_text = 'a\nb\nq\n'
416
other_text = 'a\nb\nc\nd\nf\ne\ng\n'
415
base_text = b'a\nb\nc\nd\ne\n'
416
this_text = b'a\nb\nq\n'
417
other_text = b'a\nb\nc\nd\nf\ne\ng\n'
417
418
# When cherrypicking, lines in base are not part of the conflict
418
419
m3 = merge3.Merge3(base_text.splitlines(True),
419
420
this_text.splitlines(True),
420
421
other_text.splitlines(True), is_cherrypick=True)
421
422
m_lines = m3.merge_lines()
422
self.assertEqualDiff('a\n'
423
self.assertEqualDiff(b'a\n'
435
436
def test_allow_objects(self):
436
437
"""Objects other than strs may be used with Merge3 when