~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/selftest/testmerge3.py

merge merge tweaks from aaron, which includes latest .dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
2
 
#
 
1
# Copyright (C) 2004, 2005 by Canonical Ltd
 
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
#
 
7
 
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
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
 
    )
22
 
from bzrlib.errors import CantReprocessAndShowBase, BinaryFile
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
from bzrlib.selftest import TestCaseInTempDir, TestCase
 
19
from bzrlib.merge3 import Merge3
23
20
 
24
21
def split_lines(t):
25
22
    from cStringIO import StringIO
30
27
# common base
31
28
TZU = split_lines("""     The Nameless is the origin of Heaven and Earth;
32
29
     The named is the mother of all things.
33
 
 
 
30
     
34
31
     Therefore let there always be non-being,
35
32
       so we may see their subtlety,
36
33
     And let there always be being,
61
58
     The name that can be named is not the eternal name.
62
59
     The Nameless is the origin of Heaven and Earth;
63
60
     The named is the mother of all things.
64
 
 
 
61
     
65
62
     Therefore let there always be non-being,
66
63
       so we may see their subtlety,
67
64
     And let there always be being,
69
66
     The two are the same,
70
67
     But after they are produced,
71
68
       they have different names.
72
 
 
 
69
     
73
70
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
74
71
 
75
72
""")
87
84
       they have different names.
88
85
<<<<<<< LAO
89
86
=======
90
 
 
 
87
     
91
88
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
92
89
 
93
90
>>>>>>> TAO
94
91
""")
95
92
 
96
 
class TestMerge3(tests.TestCase):
 
93
class TestMerge3(TestCase):
97
94
 
98
95
    def test_no_changes(self):
99
96
        """No conflicts because nothing changed"""
100
 
        m3 = merge3.Merge3(['aaa', 'bbb'],
101
 
                           ['aaa', 'bbb'],
102
 
                           ['aaa', 'bbb'])
 
97
        m3 = Merge3(['aaa', 'bbb'],
 
98
                    ['aaa', 'bbb'],
 
99
                    ['aaa', 'bbb'])
103
100
 
104
101
        self.assertEquals(m3.find_unconflicted(),
105
102
                          [(0, 2)])
117
114
                          [('unchanged', ['aaa', 'bbb'])])
118
115
 
119
116
    def test_front_insert(self):
120
 
        m3 = merge3.Merge3(['zz'],
121
 
                           ['aaa', 'bbb', 'zz'],
122
 
                           ['zz'])
 
117
        m3 = Merge3(['zz'],
 
118
                    ['aaa', 'bbb', 'zz'],
 
119
                    ['zz'])
123
120
 
124
121
        # todo: should use a sentinal at end as from get_matching_blocks
125
122
        # to match without zz
134
131
        self.assertEquals(list(m3.merge_groups()),
135
132
                          [('a', ['aaa', 'bbb']),
136
133
                           ('unchanged', ['zz'])])
137
 
 
 
134
        
138
135
    def test_null_insert(self):
139
 
        m3 = merge3.Merge3([],
140
 
                           ['aaa', 'bbb'],
141
 
                           [])
 
136
        m3 = Merge3([],
 
137
                    ['aaa', 'bbb'],
 
138
                    [])
142
139
        # todo: should use a sentinal at end as from get_matching_blocks
143
140
        # to match without zz
144
141
        self.assertEquals(list(m3.find_sync_regions()),
152
149
 
153
150
    def test_no_conflicts(self):
154
151
        """No conflicts because only one side changed"""
155
 
        m3 = merge3.Merge3(['aaa', 'bbb'],
156
 
                           ['aaa', '111', 'bbb'],
157
 
                           ['aaa', 'bbb'])
 
152
        m3 = Merge3(['aaa', 'bbb'],
 
153
                    ['aaa', '111', 'bbb'],
 
154
                    ['aaa', 'bbb'])
158
155
 
159
156
        self.assertEquals(m3.find_unconflicted(),
160
157
                          [(0, 1), (1, 2)])
170
167
                           ('unchanged', 1, 2),])
171
168
 
172
169
    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'])
 
170
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
171
                    ['aaa\n', 'bbb\n', '222\n'],
 
172
                    ['aaa\n', 'bbb\n'])
176
173
 
177
174
        self.assertEquals(''.join(m3.merge_lines()),
178
175
                          'aaa\nbbb\n222\n')
179
176
 
180
177
    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'])
 
178
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
179
                    ['aaa\n', 'bbb\n'],
 
180
                    ['aaa\n', 'bbb\n', '222\n'])
184
181
 
185
182
        self.assertEquals(''.join(m3.merge_lines()),
186
183
                          'aaa\nbbb\n222\n')
187
184
 
188
185
    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'])
 
186
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
187
                    ['aaa\n', 'bbb\n', '222\n'],
 
188
                    ['aaa\n', 'bbb\n', '222\n'])
192
189
 
193
190
        self.assertEquals(''.join(m3.merge_lines()),
194
191
                          'aaa\nbbb\n222\n')
195
192
 
196
193
    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'])
 
194
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
195
                    ['aaa\n', 'bbb\n', '222\n'],
 
196
                    ['aaa\n', 'bbb\n', '333\n'])
200
197
 
201
198
        ml = m3.merge_lines(name_a='a',
202
199
                            name_b='b',
215
212
''')
216
213
 
217
214
    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'])
 
215
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
216
                    ['aaa\n', '222\n', 'bbb\n'],
 
217
                    ['aaa\n', '222\n', 'bbb\n'])
221
218
 
222
219
        ml = m3.merge_lines(name_a='a',
223
220
                            name_b='b',
225
222
                            mid_marker='--',
226
223
                            end_marker='>>')
227
224
        self.assertEquals(''.join(ml), 'aaa\n222\nbbb\n')
228
 
 
 
225
        
229
226
 
230
227
    def test_insert_clash(self):
231
228
        """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'])
 
229
        m3 = Merge3(['aaa\n', 'bbb\n'],
 
230
                    ['aaa\n', '111\n', 'bbb\n'],
 
231
                    ['aaa\n', '222\n', 'bbb\n'])
235
232
 
236
233
        self.assertEquals(m3.find_unconflicted(),
237
234
                          [(0, 1), (1, 2)])
269
266
 
270
267
    def test_replace_clash(self):
271
268
        """Both try to insert lines in the same place."""
272
 
        m3 = merge3.Merge3(['aaa', '000', 'bbb'],
273
 
                           ['aaa', '111', 'bbb'],
274
 
                           ['aaa', '222', 'bbb'])
 
269
        m3 = Merge3(['aaa', '000', 'bbb'],
 
270
                    ['aaa', '111', 'bbb'],
 
271
                    ['aaa', '222', 'bbb'])
275
272
 
276
273
        self.assertEquals(m3.find_unconflicted(),
277
274
                          [(0, 1), (2, 3)])
283
280
 
284
281
    def test_replace_multi(self):
285
282
        """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'])
 
283
        m3 = Merge3(['aaa', '000', '000', 'bbb'],
 
284
                    ['aaa', '111', '111', '111', 'bbb'],
 
285
                    ['aaa', '222', '222', '222', '222', 'bbb'])
289
286
 
290
287
        self.assertEquals(m3.find_unconflicted(),
291
288
                          [(0, 1), (3, 4)])
298
295
 
299
296
    def test_merge_poem(self):
300
297
        """Test case from diff3 manual"""
301
 
        m3 = merge3.Merge3(TZU, LAO, TAO)
 
298
        m3 = Merge3(TZU, LAO, TAO)
302
299
        ml = list(m3.merge_lines('LAO', 'TAO'))
303
300
        self.log('merge result:')
304
301
        self.log(''.join(ml))
305
302
        self.assertEquals(ml, MERGED_RESULT)
306
 
 
307
 
    def test_minimal_conflicts_common(self):
308
 
        """Reprocessing"""
309
 
        base_text = ("a\n" * 20).splitlines(True)
310
 
        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
311
 
        other_text = ("a\n"*10+"c\n"+"b\n" * 8 + "c\n").splitlines(True)
312
 
        m3 = merge3.Merge3(base_text, other_text, this_text)
313
 
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
314
 
        merged_text = "".join(list(m_lines))
315
 
        optimal_text = ("a\n" * 10 + "<<<<<<< OTHER\nc\n"
316
 
            + 8* "b\n" + "c\n=======\n"
317
 
            + 10*"b\n" + ">>>>>>> THIS\n")
318
 
        self.assertEqualDiff(optimal_text, merged_text)
319
 
 
320
 
    def test_minimal_conflicts_unique(self):
321
 
        def add_newline(s):
322
 
            """Add a newline to each entry in the string"""
323
 
            return [(x+'\n') for x in s]
324
 
 
325
 
        base_text = add_newline("abcdefghijklm")
326
 
        this_text = add_newline("abcdefghijklmNOPQRSTUVWXYZ")
327
 
        other_text = add_newline("abcdefghijklm1OPQRSTUVWXY2")
328
 
        m3 = merge3.Merge3(base_text, other_text, this_text)
329
 
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
330
 
        merged_text = "".join(list(m_lines))
331
 
        optimal_text = ''.join(add_newline("abcdefghijklm")
332
 
            + ["<<<<<<< OTHER\n1\n=======\nN\n>>>>>>> THIS\n"]
333
 
            + add_newline('OPQRSTUVWXY')
334
 
            + ["<<<<<<< OTHER\n2\n=======\nZ\n>>>>>>> THIS\n"]
335
 
            )
336
 
        self.assertEqualDiff(optimal_text, merged_text)
337
 
 
338
 
    def test_minimal_conflicts_nonunique(self):
339
 
        def add_newline(s):
340
 
            """Add a newline to each entry in the string"""
341
 
            return [(x+'\n') for x in s]
342
 
 
343
 
        base_text = add_newline("abacddefgghij")
344
 
        this_text = add_newline("abacddefgghijkalmontfprz")
345
 
        other_text = add_newline("abacddefgghijknlmontfprd")
346
 
        m3 = merge3.Merge3(base_text, other_text, this_text)
347
 
        m_lines = m3.merge_lines('OTHER', 'THIS', reprocess=True)
348
 
        merged_text = "".join(list(m_lines))
349
 
        optimal_text = ''.join(add_newline("abacddefgghijk")
350
 
            + ["<<<<<<< OTHER\nn\n=======\na\n>>>>>>> THIS\n"]
351
 
            + add_newline('lmontfpr')
352
 
            + ["<<<<<<< OTHER\nd\n=======\nz\n>>>>>>> THIS\n"]
353
 
            )
354
 
        self.assertEqualDiff(optimal_text, merged_text)
355
 
 
356
 
    def test_reprocess_and_base(self):
357
 
        """Reprocessing and showing base breaks correctly"""
358
 
        base_text = ("a\n" * 20).splitlines(True)
359
 
        this_text = ("a\n"*10+"b\n" * 10).splitlines(True)
360
 
        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,
363
 
                                 base_marker='|||||||')
364
 
        self.assertRaises(CantReprocessAndShowBase, list, m_lines)
365
 
 
366
 
    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