~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_merge3.py

  • Committer: Benoît Pierre
  • Date: 2011-10-16 10:05:26 UTC
  • mto: This revision was merged to the branch mainline in revision 6218.
  • Revision ID: benoit.pierre@gmail.com-20111016100526-2gicp5g64buevb0q
Add missing news entries for ui.choose related developments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2005-2011 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., 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
 
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
20
23
 
21
24
def split_lines(t):
22
25
    from cStringIO import StringIO
27
30
# common base
28
31
TZU = split_lines("""     The Nameless is the origin of Heaven and Earth;
29
32
     The named is the mother of all things.
30
 
     
 
33
 
31
34
     Therefore let there always be non-being,
32
35
       so we may see their subtlety,
33
36
     And let there always be being,
58
61
     The name that can be named is not the eternal name.
59
62
     The Nameless is the origin of Heaven and Earth;
60
63
     The named is the mother of all things.
61
 
     
 
64
 
62
65
     Therefore let there always be non-being,
63
66
       so we may see their subtlety,
64
67
     And let there always be being,
66
69
     The two are the same,
67
70
     But after they are produced,
68
71
       they have different names.
69
 
     
 
72
 
70
73
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
71
74
 
72
75
""")
84
87
       they have different names.
85
88
<<<<<<< LAO
86
89
=======
87
 
     
 
90
 
88
91
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
89
92
 
90
93
>>>>>>> TAO
91
94
""")
92
95
 
93
 
class TestMerge3(TestCase):
 
96
class TestMerge3(tests.TestCase):
94
97
 
95
98
    def test_no_changes(self):
96
99
        """No conflicts because nothing changed"""
97
 
        m3 = Merge3(['aaa', 'bbb'],
98
 
                    ['aaa', 'bbb'],
99
 
                    ['aaa', 'bbb'])
 
100
        m3 = merge3.Merge3(['aaa', 'bbb'],
 
101
                           ['aaa', 'bbb'],
 
102
                           ['aaa', 'bbb'])
100
103
 
101
104
        self.assertEquals(m3.find_unconflicted(),
102
105
                          [(0, 2)])
114
117
                          [('unchanged', ['aaa', 'bbb'])])
115
118
 
116
119
    def test_front_insert(self):
117
 
        m3 = Merge3(['zz'],
118
 
                    ['aaa', 'bbb', 'zz'],
119
 
                    ['zz'])
 
120
        m3 = merge3.Merge3(['zz'],
 
121
                           ['aaa', 'bbb', 'zz'],
 
122
                           ['zz'])
120
123
 
121
124
        # todo: should use a sentinal at end as from get_matching_blocks
122
125
        # to match without zz
131
134
        self.assertEquals(list(m3.merge_groups()),
132
135
                          [('a', ['aaa', 'bbb']),
133
136
                           ('unchanged', ['zz'])])
134
 
        
 
137
 
135
138
    def test_null_insert(self):
136
 
        m3 = Merge3([],
137
 
                    ['aaa', 'bbb'],
138
 
                    [])
 
139
        m3 = merge3.Merge3([],
 
140
                           ['aaa', 'bbb'],
 
141
                           [])
139
142
        # todo: should use a sentinal at end as from get_matching_blocks
140
143
        # to match without zz
141
144
        self.assertEquals(list(m3.find_sync_regions()),
149
152
 
150
153
    def test_no_conflicts(self):
151
154
        """No conflicts because only one side changed"""
152
 
        m3 = Merge3(['aaa', 'bbb'],
153
 
                    ['aaa', '111', 'bbb'],
154
 
                    ['aaa', 'bbb'])
 
155
        m3 = merge3.Merge3(['aaa', 'bbb'],
 
156
                           ['aaa', '111', 'bbb'],
 
157
                           ['aaa', 'bbb'])
155
158
 
156
159
        self.assertEquals(m3.find_unconflicted(),
157
160
                          [(0, 1), (1, 2)])
167
170
                           ('unchanged', 1, 2),])
168
171
 
169
172
    def test_append_a(self):
170
 
        m3 = Merge3(['aaa\n', 'bbb\n'],
171
 
                    ['aaa\n', 'bbb\n', '222\n'],
172
 
                    ['aaa\n', 'bbb\n'])
 
173
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
174
                           ['aaa\n', 'bbb\n', '222\n'],
 
175
                           ['aaa\n', 'bbb\n'])
173
176
 
174
177
        self.assertEquals(''.join(m3.merge_lines()),
175
178
                          'aaa\nbbb\n222\n')
176
179
 
177
180
    def test_append_b(self):
178
 
        m3 = Merge3(['aaa\n', 'bbb\n'],
179
 
                    ['aaa\n', 'bbb\n'],
180
 
                    ['aaa\n', 'bbb\n', '222\n'])
 
181
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
182
                           ['aaa\n', 'bbb\n'],
 
183
                           ['aaa\n', 'bbb\n', '222\n'])
181
184
 
182
185
        self.assertEquals(''.join(m3.merge_lines()),
183
186
                          'aaa\nbbb\n222\n')
184
187
 
185
188
    def test_append_agreement(self):
186
 
        m3 = Merge3(['aaa\n', 'bbb\n'],
187
 
                    ['aaa\n', 'bbb\n', '222\n'],
188
 
                    ['aaa\n', 'bbb\n', '222\n'])
 
189
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
190
                           ['aaa\n', 'bbb\n', '222\n'],
 
191
                           ['aaa\n', 'bbb\n', '222\n'])
189
192
 
190
193
        self.assertEquals(''.join(m3.merge_lines()),
191
194
                          'aaa\nbbb\n222\n')
192
195
 
193
196
    def test_append_clash(self):
194
 
        m3 = Merge3(['aaa\n', 'bbb\n'],
195
 
                    ['aaa\n', 'bbb\n', '222\n'],
196
 
                    ['aaa\n', 'bbb\n', '333\n'])
 
197
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
198
                           ['aaa\n', 'bbb\n', '222\n'],
 
199
                           ['aaa\n', 'bbb\n', '333\n'])
197
200
 
198
201
        ml = m3.merge_lines(name_a='a',
199
202
                            name_b='b',
212
215
''')
213
216
 
214
217
    def test_insert_agreement(self):
215
 
        m3 = Merge3(['aaa\n', 'bbb\n'],
216
 
                    ['aaa\n', '222\n', 'bbb\n'],
217
 
                    ['aaa\n', '222\n', 'bbb\n'])
 
218
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
219
                           ['aaa\n', '222\n', 'bbb\n'],
 
220
                           ['aaa\n', '222\n', 'bbb\n'])
218
221
 
219
222
        ml = m3.merge_lines(name_a='a',
220
223
                            name_b='b',
222
225
                            mid_marker='--',
223
226
                            end_marker='>>')
224
227
        self.assertEquals(''.join(ml), 'aaa\n222\nbbb\n')
225
 
        
 
228
 
226
229
 
227
230
    def test_insert_clash(self):
228
231
        """Both try to insert lines in the same place."""
229
 
        m3 = Merge3(['aaa\n', 'bbb\n'],
230
 
                    ['aaa\n', '111\n', 'bbb\n'],
231
 
                    ['aaa\n', '222\n', 'bbb\n'])
 
232
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
233
                           ['aaa\n', '111\n', 'bbb\n'],
 
234
                           ['aaa\n', '222\n', 'bbb\n'])
232
235
 
233
236
        self.assertEquals(m3.find_unconflicted(),
234
237
                          [(0, 1), (1, 2)])
266
269
 
267
270
    def test_replace_clash(self):
268
271
        """Both try to insert lines in the same place."""
269
 
        m3 = Merge3(['aaa', '000', 'bbb'],
270
 
                    ['aaa', '111', 'bbb'],
271
 
                    ['aaa', '222', 'bbb'])
 
272
        m3 = merge3.Merge3(['aaa', '000', 'bbb'],
 
273
                           ['aaa', '111', 'bbb'],
 
274
                           ['aaa', '222', 'bbb'])
272
275
 
273
276
        self.assertEquals(m3.find_unconflicted(),
274
277
                          [(0, 1), (2, 3)])
280
283
 
281
284
    def test_replace_multi(self):
282
285
        """Replacement with regions of different size."""
283
 
        m3 = Merge3(['aaa', '000', '000', 'bbb'],
284
 
                    ['aaa', '111', '111', '111', 'bbb'],
285
 
                    ['aaa', '222', '222', '222', '222', 'bbb'])
 
286
        m3 = merge3.Merge3(['aaa', '000', '000', 'bbb'],
 
287
                           ['aaa', '111', '111', '111', 'bbb'],
 
288
                           ['aaa', '222', '222', '222', '222', 'bbb'])
286
289
 
287
290
        self.assertEquals(m3.find_unconflicted(),
288
291
                          [(0, 1), (3, 4)])
295
298
 
296
299
    def test_merge_poem(self):
297
300
        """Test case from diff3 manual"""
298
 
        m3 = Merge3(TZU, LAO, TAO)
 
301
        m3 = merge3.Merge3(TZU, LAO, TAO)
299
302
        ml = list(m3.merge_lines('LAO', 'TAO'))
300
303
        self.log('merge result:')
301
304
        self.log(''.join(ml))
302
305
        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