~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_merge3.py

Merge bzr.dev (and fix NEWS)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011, 2016 Canonical Ltd
 
1
# Copyright (C) 2004, 2005 by Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
 
18
 
from bzrlib import (
19
 
    merge3,
20
 
    tests,
21
 
    )
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
from bzrlib.tests import TestCaseInTempDir, TestCase
 
19
from bzrlib.merge3 import Merge3
22
20
from bzrlib.errors import CantReprocessAndShowBase, BinaryFile
23
21
 
24
22
def split_lines(t):
30
28
# common base
31
29
TZU = split_lines("""     The Nameless is the origin of Heaven and Earth;
32
30
     The named is the mother of all things.
33
 
 
 
31
     
34
32
     Therefore let there always be non-being,
35
33
       so we may see their subtlety,
36
34
     And let there always be being,
61
59
     The name that can be named is not the eternal name.
62
60
     The Nameless is the origin of Heaven and Earth;
63
61
     The named is the mother of all things.
64
 
 
 
62
     
65
63
     Therefore let there always be non-being,
66
64
       so we may see their subtlety,
67
65
     And let there always be being,
69
67
     The two are the same,
70
68
     But after they are produced,
71
69
       they have different names.
72
 
 
 
70
     
73
71
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
74
72
 
75
73
""")
87
85
       they have different names.
88
86
<<<<<<< LAO
89
87
=======
90
 
 
 
88
     
91
89
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
92
90
 
93
91
>>>>>>> TAO
94
92
""")
95
93
 
96
 
class TestMerge3(tests.TestCase):
 
94
class TestMerge3(TestCase):
97
95
 
98
96
    def test_no_changes(self):
99
97
        """No conflicts because nothing changed"""
100
 
        m3 = merge3.Merge3(['aaa', 'bbb'],
101
 
                           ['aaa', 'bbb'],
102
 
                           ['aaa', 'bbb'])
 
98
        m3 = Merge3(['aaa', 'bbb'],
 
99
                    ['aaa', 'bbb'],
 
100
                    ['aaa', 'bbb'])
103
101
 
104
 
        self.assertEqual(m3.find_unconflicted(),
 
102
        self.assertEquals(m3.find_unconflicted(),
105
103
                          [(0, 2)])
106
104
 
107
 
        self.assertEqual(list(m3.find_sync_regions()),
 
105
        self.assertEquals(list(m3.find_sync_regions()),
108
106
                          [(0, 2,
109
107
                            0, 2,
110
108
                            0, 2),
111
109
                           (2,2, 2,2, 2,2)])
112
110
 
113
 
        self.assertEqual(list(m3.merge_regions()),
 
111
        self.assertEquals(list(m3.merge_regions()),
114
112
                          [('unchanged', 0, 2)])
115
113
 
116
 
        self.assertEqual(list(m3.merge_groups()),
 
114
        self.assertEquals(list(m3.merge_groups()),
117
115
                          [('unchanged', ['aaa', 'bbb'])])
118
116
 
119
117
    def test_front_insert(self):
120
 
        m3 = merge3.Merge3(['zz'],
121
 
                           ['aaa', 'bbb', 'zz'],
122
 
                           ['zz'])
 
118
        m3 = Merge3(['zz'],
 
119
                    ['aaa', 'bbb', 'zz'],
 
120
                    ['zz'])
123
121
 
124
122
        # todo: should use a sentinal at end as from get_matching_blocks
125
123
        # to match without zz
126
 
        self.assertEqual(list(m3.find_sync_regions()),
 
124
        self.assertEquals(list(m3.find_sync_regions()),
127
125
                          [(0,1, 2,3, 0,1),
128
126
                           (1,1, 3,3, 1,1),])
129
127
 
130
 
        self.assertEqual(list(m3.merge_regions()),
 
128
        self.assertEquals(list(m3.merge_regions()),
131
129
                          [('a', 0, 2),
132
130
                           ('unchanged', 0, 1)])
133
131
 
134
 
        self.assertEqual(list(m3.merge_groups()),
 
132
        self.assertEquals(list(m3.merge_groups()),
135
133
                          [('a', ['aaa', 'bbb']),
136
134
                           ('unchanged', ['zz'])])
137
 
 
 
135
        
138
136
    def test_null_insert(self):
139
 
        m3 = merge3.Merge3([],
140
 
                           ['aaa', 'bbb'],
141
 
                           [])
 
137
        m3 = Merge3([],
 
138
                    ['aaa', 'bbb'],
 
139
                    [])
142
140
        # todo: should use a sentinal at end as from get_matching_blocks
143
141
        # to match without zz
144
 
        self.assertEqual(list(m3.find_sync_regions()),
 
142
        self.assertEquals(list(m3.find_sync_regions()),
145
143
                          [(0,0, 2,2, 0,0)])
146
144
 
147
 
        self.assertEqual(list(m3.merge_regions()),
 
145
        self.assertEquals(list(m3.merge_regions()),
148
146
                          [('a', 0, 2)])
149
147
 
150
 
        self.assertEqual(list(m3.merge_lines()),
 
148
        self.assertEquals(list(m3.merge_lines()),
151
149
                          ['aaa', 'bbb'])
152
150
 
153
151
    def test_no_conflicts(self):
154
152
        """No conflicts because only one side changed"""
155
 
        m3 = merge3.Merge3(['aaa', 'bbb'],
156
 
                           ['aaa', '111', 'bbb'],
157
 
                           ['aaa', 'bbb'])
 
153
        m3 = Merge3(['aaa', 'bbb'],
 
154
                    ['aaa', '111', 'bbb'],
 
155
                    ['aaa', 'bbb'])
158
156
 
159
 
        self.assertEqual(m3.find_unconflicted(),
 
157
        self.assertEquals(m3.find_unconflicted(),
160
158
                          [(0, 1), (1, 2)])
161
159
 
162
 
        self.assertEqual(list(m3.find_sync_regions()),
 
160
        self.assertEquals(list(m3.find_sync_regions()),
163
161
                          [(0,1, 0,1, 0,1),
164
162
                           (1,2, 2,3, 1,2),
165
163
                           (2,2, 3,3, 2,2),])
166
164
 
167
 
        self.assertEqual(list(m3.merge_regions()),
 
165
        self.assertEquals(list(m3.merge_regions()),
168
166
                          [('unchanged', 0, 1),
169
167
                           ('a', 1, 2),
170
168
                           ('unchanged', 1, 2),])
171
169
 
172
170
    def test_append_a(self):
173
 
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
174
 
                           ['aaa\n', 'bbb\n', '222\n'],
175
 
                           ['aaa\n', 'bbb\n'])
 
171
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
172
                    ['aaa\n', 'bbb\n', '222\n'],
 
173
                    ['aaa\n', 'bbb\n'])
176
174
 
177
 
        self.assertEqual(''.join(m3.merge_lines()),
 
175
        self.assertEquals(''.join(m3.merge_lines()),
178
176
                          'aaa\nbbb\n222\n')
179
177
 
180
178
    def test_append_b(self):
181
 
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
182
 
                           ['aaa\n', 'bbb\n'],
183
 
                           ['aaa\n', 'bbb\n', '222\n'])
 
179
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
180
                    ['aaa\n', 'bbb\n'],
 
181
                    ['aaa\n', 'bbb\n', '222\n'])
184
182
 
185
 
        self.assertEqual(''.join(m3.merge_lines()),
 
183
        self.assertEquals(''.join(m3.merge_lines()),
186
184
                          'aaa\nbbb\n222\n')
187
185
 
188
186
    def test_append_agreement(self):
189
 
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
190
 
                           ['aaa\n', 'bbb\n', '222\n'],
191
 
                           ['aaa\n', 'bbb\n', '222\n'])
 
187
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
188
                    ['aaa\n', 'bbb\n', '222\n'],
 
189
                    ['aaa\n', 'bbb\n', '222\n'])
192
190
 
193
 
        self.assertEqual(''.join(m3.merge_lines()),
 
191
        self.assertEquals(''.join(m3.merge_lines()),
194
192
                          'aaa\nbbb\n222\n')
195
193
 
196
194
    def test_append_clash(self):
197
 
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
198
 
                           ['aaa\n', 'bbb\n', '222\n'],
199
 
                           ['aaa\n', 'bbb\n', '333\n'])
 
195
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
196
                    ['aaa\n', 'bbb\n', '222\n'],
 
197
                    ['aaa\n', 'bbb\n', '333\n'])
200
198
 
201
199
        ml = m3.merge_lines(name_a='a',
202
200
                            name_b='b',
203
201
                            start_marker='<<',
204
202
                            mid_marker='--',
205
203
                            end_marker='>>')
206
 
        self.assertEqual(''.join(ml),
 
204
        self.assertEquals(''.join(ml),
207
205
'''\
208
206
aaa
209
207
bbb
215
213
''')
216
214
 
217
215
    def test_insert_agreement(self):
218
 
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
219
 
                           ['aaa\n', '222\n', 'bbb\n'],
220
 
                           ['aaa\n', '222\n', 'bbb\n'])
 
216
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
217
                    ['aaa\n', '222\n', 'bbb\n'],
 
218
                    ['aaa\n', '222\n', 'bbb\n'])
221
219
 
222
220
        ml = m3.merge_lines(name_a='a',
223
221
                            name_b='b',
224
222
                            start_marker='<<',
225
223
                            mid_marker='--',
226
224
                            end_marker='>>')
227
 
        self.assertEqual(''.join(ml), 'aaa\n222\nbbb\n')
228
 
 
 
225
        self.assertEquals(''.join(ml), 'aaa\n222\nbbb\n')
 
226
        
229
227
 
230
228
    def test_insert_clash(self):
231
229
        """Both try to insert lines in the same place."""
232
 
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
233
 
                           ['aaa\n', '111\n', 'bbb\n'],
234
 
                           ['aaa\n', '222\n', 'bbb\n'])
 
230
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
231
                    ['aaa\n', '111\n', 'bbb\n'],
 
232
                    ['aaa\n', '222\n', 'bbb\n'])
235
233
 
236
 
        self.assertEqual(m3.find_unconflicted(),
 
234
        self.assertEquals(m3.find_unconflicted(),
237
235
                          [(0, 1), (1, 2)])
238
236
 
239
 
        self.assertEqual(list(m3.find_sync_regions()),
 
237
        self.assertEquals(list(m3.find_sync_regions()),
240
238
                          [(0,1, 0,1, 0,1),
241
239
                           (1,2, 2,3, 2,3),
242
240
                           (2,2, 3,3, 3,3),])
243
241
 
244
 
        self.assertEqual(list(m3.merge_regions()),
 
242
        self.assertEquals(list(m3.merge_regions()),
245
243
                          [('unchanged', 0,1),
246
244
                           ('conflict', 1,1, 1,2, 1,2),
247
245
                           ('unchanged', 1,2)])
248
246
 
249
 
        self.assertEqual(list(m3.merge_groups()),
 
247
        self.assertEquals(list(m3.merge_groups()),
250
248
                          [('unchanged', ['aaa\n']),
251
249
                           ('conflict', [], ['111\n'], ['222\n']),
252
250
                           ('unchanged', ['bbb\n']),
257
255
                            start_marker='<<',
258
256
                            mid_marker='--',
259
257
                            end_marker='>>')
260
 
        self.assertEqual(''.join(ml),
 
258
        self.assertEquals(''.join(ml),
261
259
'''aaa
262
260
<< a
263
261
111
269
267
 
270
268
    def test_replace_clash(self):
271
269
        """Both try to insert lines in the same place."""
272
 
        m3 = merge3.Merge3(['aaa', '000', 'bbb'],
273
 
                           ['aaa', '111', 'bbb'],
274
 
                           ['aaa', '222', 'bbb'])
 
270
        m3 = Merge3(['aaa', '000', 'bbb'],
 
271
                    ['aaa', '111', 'bbb'],
 
272
                    ['aaa', '222', 'bbb'])
275
273
 
276
 
        self.assertEqual(m3.find_unconflicted(),
 
274
        self.assertEquals(m3.find_unconflicted(),
277
275
                          [(0, 1), (2, 3)])
278
276
 
279
 
        self.assertEqual(list(m3.find_sync_regions()),
 
277
        self.assertEquals(list(m3.find_sync_regions()),
280
278
                          [(0,1, 0,1, 0,1),
281
279
                           (2,3, 2,3, 2,3),
282
280
                           (3,3, 3,3, 3,3),])
283
281
 
284
282
    def test_replace_multi(self):
285
283
        """Replacement with regions of different size."""
286
 
        m3 = merge3.Merge3(['aaa', '000', '000', 'bbb'],
287
 
                           ['aaa', '111', '111', '111', 'bbb'],
288
 
                           ['aaa', '222', '222', '222', '222', 'bbb'])
 
284
        m3 = Merge3(['aaa', '000', '000', 'bbb'],
 
285
                    ['aaa', '111', '111', '111', 'bbb'],
 
286
                    ['aaa', '222', '222', '222', '222', 'bbb'])
289
287
 
290
 
        self.assertEqual(m3.find_unconflicted(),
 
288
        self.assertEquals(m3.find_unconflicted(),
291
289
                          [(0, 1), (3, 4)])
292
290
 
293
291
 
294
 
        self.assertEqual(list(m3.find_sync_regions()),
 
292
        self.assertEquals(list(m3.find_sync_regions()),
295
293
                          [(0,1, 0,1, 0,1),
296
294
                           (3,4, 4,5, 5,6),
297
295
                           (4,4, 5,5, 6,6),])
298
296
 
299
297
    def test_merge_poem(self):
300
298
        """Test case from diff3 manual"""
301
 
        m3 = merge3.Merge3(TZU, LAO, TAO)
 
299
        m3 = Merge3(TZU, LAO, TAO)
302
300
        ml = list(m3.merge_lines('LAO', 'TAO'))
303
301
        self.log('merge result:')
304
302
        self.log(''.join(ml))
305
 
        self.assertEqual(ml, MERGED_RESULT)
 
303
        self.assertEquals(ml, MERGED_RESULT)
306
304
 
307
305
    def test_minimal_conflicts_common(self):
308
306
        """Reprocessing"""
309
307
        base_text = ("a\n" * 20).splitlines(True)
310
308
        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
311
309
        other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
312
 
        m3 = merge3.Merge3(base_text, other_text, this_text)
 
310
        m3 = Merge3(base_text, other_text, this_text)
313
311
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
314
312
        merged_text = "".join(list(m_lines))
315
313
        optimal_text = ("a\n" * 10 + "<<<<<<< OTHER\nc\n"
325
323
        base_text = add_newline("abcdefghijklm")
326
324
        this_text = add_newline("abcdefghijklmNOPQRSTUVWXYZ")
327
325
        other_text = add_newline("abcdefghijklm1OPQRSTUVWXY2")
328
 
        m3 = merge3.Merge3(base_text, other_text, this_text)
 
326
        m3 = Merge3(base_text, other_text, this_text)
329
327
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
330
328
        merged_text = "".join(list(m_lines))
331
329
        optimal_text = ''.join(add_newline("abcdefghijklm")
343
341
        base_text = add_newline("abacddefgghij")
344
342
        this_text = add_newline("abacddefgghijkalmontfprz")
345
343
        other_text = add_newline("abacddefgghijknlmontfprd")
346
 
        m3 = merge3.Merge3(base_text, other_text, this_text)
 
344
        m3 = Merge3(base_text, other_text, this_text)
347
345
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
348
346
        merged_text = "".join(list(m_lines))
349
347
        optimal_text = ''.join(add_newline("abacddefgghijk")
358
356
        base_text = ("a\n" * 20).splitlines(True)
359
357
        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
360
358
        other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
361
 
        m3 = merge3.Merge3(base_text, other_text, this_text)
362
 
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True,
 
359
        m3 = Merge3(base_text, other_text, this_text)
 
360
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True, 
363
361
                                 base_marker='|||||||')
364
362
        self.assertRaises(CantReprocessAndShowBase, list, m_lines)
365
363
 
366
364
    def test_binary(self):
367
 
        self.assertRaises(BinaryFile, merge3.Merge3, ['\x00'], ['a'], ['b'])
368
 
 
369
 
    def test_dos_text(self):
370
 
        base_text = 'a\r\n'
371
 
        this_text = 'b\r\n'
372
 
        other_text = 'c\r\n'
373
 
        m3 = merge3.Merge3(base_text.splitlines(True),
374
 
                           other_text.splitlines(True),
375
 
                           this_text.splitlines(True))
376
 
        m_lines = m3.merge_lines('OTHER', 'THIS')
377
 
        self.assertEqual('<<<<<<< OTHER\r\nc\r\n=======\r\nb\r\n'
378
 
            '>>>>>>> THIS\r\n'.splitlines(True), list(m_lines))
379
 
 
380
 
    def test_mac_text(self):
381
 
        base_text = 'a\r'
382
 
        this_text = 'b\r'
383
 
        other_text = 'c\r'
384
 
        m3 = merge3.Merge3(base_text.splitlines(True),
385
 
                           other_text.splitlines(True),
386
 
                           this_text.splitlines(True))
387
 
        m_lines = m3.merge_lines('OTHER', 'THIS')
388
 
        self.assertEqual('<<<<<<< OTHER\rc\r=======\rb\r'
389
 
            '>>>>>>> THIS\r'.splitlines(True), list(m_lines))
390
 
 
391
 
    def test_merge3_cherrypick(self):
392
 
        base_text = "a\nb\n"
393
 
        this_text = "a\n"
394
 
        other_text = "a\nb\nc\n"
395
 
        # When cherrypicking, lines in base are not part of the conflict
396
 
        m3 = merge3.Merge3(base_text.splitlines(True),
397
 
                           this_text.splitlines(True),
398
 
                           other_text.splitlines(True), is_cherrypick=True)
399
 
        m_lines = m3.merge_lines()
400
 
        self.assertEqualDiff('a\n<<<<<<<\n=======\nc\n>>>>>>>\n',
401
 
                             ''.join(m_lines))
402
 
 
403
 
        # This is not symmetric
404
 
        m3 = merge3.Merge3(base_text.splitlines(True),
405
 
                           other_text.splitlines(True),
406
 
                           this_text.splitlines(True), is_cherrypick=True)
407
 
        m_lines = m3.merge_lines()
408
 
        self.assertEqualDiff('a\n<<<<<<<\nb\nc\n=======\n>>>>>>>\n',
409
 
                             ''.join(m_lines))
410
 
 
411
 
    def test_merge3_cherrypick_w_mixed(self):
412
 
        base_text = 'a\nb\nc\nd\ne\n'
413
 
        this_text = 'a\nb\nq\n'
414
 
        other_text = 'a\nb\nc\nd\nf\ne\ng\n'
415
 
        # When cherrypicking, lines in base are not part of the conflict
416
 
        m3 = merge3.Merge3(base_text.splitlines(True),
417
 
                           this_text.splitlines(True),
418
 
                           other_text.splitlines(True), is_cherrypick=True)
419
 
        m_lines = m3.merge_lines()
420
 
        self.assertEqualDiff('a\n'
421
 
                             'b\n'
422
 
                             '<<<<<<<\n'
423
 
                             'q\n'
424
 
                             '=======\n'
425
 
                             'f\n'
426
 
                             '>>>>>>>\n'
427
 
                             '<<<<<<<\n'
428
 
                             '=======\n'
429
 
                             'g\n'
430
 
                             '>>>>>>>\n',
431
 
                             ''.join(m_lines))
432
 
 
433
 
    def test_allow_objects(self):
434
 
        """Objects other than strs may be used with Merge3 when
435
 
        allow_objects=True.
436
 
        
437
 
        merge_groups and merge_regions work with non-str input.  Methods that
438
 
        return lines like merge_lines fail.
439
 
        """
440
 
        base = [(x,x) for x in 'abcde']
441
 
        a = [(x,x) for x in 'abcdef']
442
 
        b = [(x,x) for x in 'Zabcde']
443
 
        m3 = merge3.Merge3(base, a, b, allow_objects=True)
444
 
        self.assertEqual(
445
 
            [('b', 0, 1),
446
 
             ('unchanged', 0, 5),
447
 
             ('a', 5, 6)],
448
 
            list(m3.merge_regions()))
449
 
        self.assertEqual(
450
 
            [('b', [('Z', 'Z')]),
451
 
             ('unchanged', [(x,x) for x in 'abcde']),
452
 
             ('a', [('f', 'f')])],
453
 
            list(m3.merge_groups()))
454
 
 
 
365
        self.assertRaises(BinaryFile, Merge3, ['\x00'], ['a'], ['b'])