~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_merge3.py

  • Committer: Matthäus G. Chajdas
  • Date: 2010-10-09 18:18:19 UTC
  • mto: (5484.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 5485.
  • Revision ID: dev@anteru.net-20101009181819-fah4xnt3sn0e3pc2
Always update PATH in start_bzr.bat and write the proper path during installation.

start_bzr.bat now always sets PATH to point to Bzr, which allows start_bzr to work regardless if the user selected to update PATH or not.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2010 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
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
    errors,
 
20
    merge3,
 
21
    tests,
 
22
    )
 
23
from bzrlib.merge3 import Merge3
 
24
from bzrlib.errors import CantReprocessAndShowBase, BinaryFile
 
25
 
 
26
def split_lines(t):
 
27
    from cStringIO import StringIO
 
28
    return StringIO(t).readlines()
 
29
 
 
30
############################################################
 
31
# test case data from the gnu diffutils manual
 
32
# common base
 
33
TZU = split_lines("""     The Nameless is the origin of Heaven and Earth;
 
34
     The named is the mother of all things.
 
35
 
 
36
     Therefore let there always be non-being,
 
37
       so we may see their subtlety,
 
38
     And let there always be being,
 
39
       so we may see their outcome.
 
40
     The two are the same,
 
41
     But after they are produced,
 
42
       they have different names.
 
43
     They both may be called deep and profound.
 
44
     Deeper and more profound,
 
45
     The door of all subtleties!
 
46
""")
 
47
 
 
48
LAO = split_lines("""     The Way that can be told of is not the eternal Way;
 
49
     The name that can be named is not the eternal name.
 
50
     The Nameless is the origin of Heaven and Earth;
 
51
     The Named is the mother of all things.
 
52
     Therefore let there always be non-being,
 
53
       so we may see their subtlety,
 
54
     And let there always be being,
 
55
       so we may see their outcome.
 
56
     The two are the same,
 
57
     But after they are produced,
 
58
       they have different names.
 
59
""")
 
60
 
 
61
 
 
62
TAO = split_lines("""     The Way that can be told of is not the eternal Way;
 
63
     The name that can be named is not the eternal name.
 
64
     The Nameless is the origin of Heaven and Earth;
 
65
     The named is the mother of all things.
 
66
 
 
67
     Therefore let there always be non-being,
 
68
       so we may see their subtlety,
 
69
     And let there always be being,
 
70
       so we may see their result.
 
71
     The two are the same,
 
72
     But after they are produced,
 
73
       they have different names.
 
74
 
 
75
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
 
76
 
 
77
""")
 
78
 
 
79
MERGED_RESULT = split_lines("""     The Way that can be told of is not the eternal Way;
 
80
     The name that can be named is not the eternal name.
 
81
     The Nameless is the origin of Heaven and Earth;
 
82
     The Named is the mother of all things.
 
83
     Therefore let there always be non-being,
 
84
       so we may see their subtlety,
 
85
     And let there always be being,
 
86
       so we may see their result.
 
87
     The two are the same,
 
88
     But after they are produced,
 
89
       they have different names.
 
90
<<<<<<< LAO
 
91
=======
 
92
 
 
93
       -- The Way of Lao-Tzu, tr. Wing-tsit Chan
 
94
 
 
95
>>>>>>> TAO
 
96
""")
 
97
 
 
98
class TestMerge3(tests.TestCase):
 
99
 
 
100
    def test_no_changes(self):
 
101
        """No conflicts because nothing changed"""
 
102
        m3 = merge3.Merge3(['aaa', 'bbb'],
 
103
                           ['aaa', 'bbb'],
 
104
                           ['aaa', 'bbb'])
 
105
 
 
106
        self.assertEquals(m3.find_unconflicted(),
 
107
                          [(0, 2)])
 
108
 
 
109
        self.assertEquals(list(m3.find_sync_regions()),
 
110
                          [(0, 2,
 
111
                            0, 2,
 
112
                            0, 2),
 
113
                           (2,2, 2,2, 2,2)])
 
114
 
 
115
        self.assertEquals(list(m3.merge_regions()),
 
116
                          [('unchanged', 0, 2)])
 
117
 
 
118
        self.assertEquals(list(m3.merge_groups()),
 
119
                          [('unchanged', ['aaa', 'bbb'])])
 
120
 
 
121
    def test_front_insert(self):
 
122
        m3 = merge3.Merge3(['zz'],
 
123
                           ['aaa', 'bbb', 'zz'],
 
124
                           ['zz'])
 
125
 
 
126
        # todo: should use a sentinal at end as from get_matching_blocks
 
127
        # to match without zz
 
128
        self.assertEquals(list(m3.find_sync_regions()),
 
129
                          [(0,1, 2,3, 0,1),
 
130
                           (1,1, 3,3, 1,1),])
 
131
 
 
132
        self.assertEquals(list(m3.merge_regions()),
 
133
                          [('a', 0, 2),
 
134
                           ('unchanged', 0, 1)])
 
135
 
 
136
        self.assertEquals(list(m3.merge_groups()),
 
137
                          [('a', ['aaa', 'bbb']),
 
138
                           ('unchanged', ['zz'])])
 
139
 
 
140
    def test_null_insert(self):
 
141
        m3 = merge3.Merge3([],
 
142
                           ['aaa', 'bbb'],
 
143
                           [])
 
144
        # todo: should use a sentinal at end as from get_matching_blocks
 
145
        # to match without zz
 
146
        self.assertEquals(list(m3.find_sync_regions()),
 
147
                          [(0,0, 2,2, 0,0)])
 
148
 
 
149
        self.assertEquals(list(m3.merge_regions()),
 
150
                          [('a', 0, 2)])
 
151
 
 
152
        self.assertEquals(list(m3.merge_lines()),
 
153
                          ['aaa', 'bbb'])
 
154
 
 
155
    def test_no_conflicts(self):
 
156
        """No conflicts because only one side changed"""
 
157
        m3 = merge3.Merge3(['aaa', 'bbb'],
 
158
                           ['aaa', '111', 'bbb'],
 
159
                           ['aaa', 'bbb'])
 
160
 
 
161
        self.assertEquals(m3.find_unconflicted(),
 
162
                          [(0, 1), (1, 2)])
 
163
 
 
164
        self.assertEquals(list(m3.find_sync_regions()),
 
165
                          [(0,1, 0,1, 0,1),
 
166
                           (1,2, 2,3, 1,2),
 
167
                           (2,2, 3,3, 2,2),])
 
168
 
 
169
        self.assertEquals(list(m3.merge_regions()),
 
170
                          [('unchanged', 0, 1),
 
171
                           ('a', 1, 2),
 
172
                           ('unchanged', 1, 2),])
 
173
 
 
174
    def test_append_a(self):
 
175
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
176
                           ['aaa\n', 'bbb\n', '222\n'],
 
177
                           ['aaa\n', 'bbb\n'])
 
178
 
 
179
        self.assertEquals(''.join(m3.merge_lines()),
 
180
                          'aaa\nbbb\n222\n')
 
181
 
 
182
    def test_append_b(self):
 
183
        m3 = merge3.Merge3(['aaa\n', 'bbb\n'],
 
184
                           ['aaa\n', 'bbb\n'],
 
185
                           ['aaa\n', 'bbb\n', '222\n'])
 
186
 
 
187
        self.assertEquals(''.join(m3.merge_lines()),
 
188
                          'aaa\nbbb\n222\n')
 
189
 
 
190
    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'])
 
194
 
 
195
        self.assertEquals(''.join(m3.merge_lines()),
 
196
                          'aaa\nbbb\n222\n')
 
197
 
 
198
    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'])
 
202
 
 
203
        ml = m3.merge_lines(name_a='a',
 
204
                            name_b='b',
 
205
                            start_marker='<<',
 
206
                            mid_marker='--',
 
207
                            end_marker='>>')
 
208
        self.assertEquals(''.join(ml),
 
209
'''\
 
210
aaa
 
211
bbb
 
212
<< a
 
213
222
 
214
--
 
215
333
 
216
>> b
 
217
''')
 
218
 
 
219
    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'])
 
223
 
 
224
        ml = m3.merge_lines(name_a='a',
 
225
                            name_b='b',
 
226
                            start_marker='<<',
 
227
                            mid_marker='--',
 
228
                            end_marker='>>')
 
229
        self.assertEquals(''.join(ml), 'aaa\n222\nbbb\n')
 
230
 
 
231
 
 
232
    def test_insert_clash(self):
 
233
        """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'])
 
237
 
 
238
        self.assertEquals(m3.find_unconflicted(),
 
239
                          [(0, 1), (1, 2)])
 
240
 
 
241
        self.assertEquals(list(m3.find_sync_regions()),
 
242
                          [(0,1, 0,1, 0,1),
 
243
                           (1,2, 2,3, 2,3),
 
244
                           (2,2, 3,3, 3,3),])
 
245
 
 
246
        self.assertEquals(list(m3.merge_regions()),
 
247
                          [('unchanged', 0,1),
 
248
                           ('conflict', 1,1, 1,2, 1,2),
 
249
                           ('unchanged', 1,2)])
 
250
 
 
251
        self.assertEquals(list(m3.merge_groups()),
 
252
                          [('unchanged', ['aaa\n']),
 
253
                           ('conflict', [], ['111\n'], ['222\n']),
 
254
                           ('unchanged', ['bbb\n']),
 
255
                           ])
 
256
 
 
257
        ml = m3.merge_lines(name_a='a',
 
258
                            name_b='b',
 
259
                            start_marker='<<',
 
260
                            mid_marker='--',
 
261
                            end_marker='>>')
 
262
        self.assertEquals(''.join(ml),
 
263
'''aaa
 
264
<< a
 
265
111
 
266
--
 
267
222
 
268
>> b
 
269
bbb
 
270
''')
 
271
 
 
272
    def test_replace_clash(self):
 
273
        """Both try to insert lines in the same place."""
 
274
        m3 = merge3.Merge3(['aaa', '000', 'bbb'],
 
275
                           ['aaa', '111', 'bbb'],
 
276
                           ['aaa', '222', 'bbb'])
 
277
 
 
278
        self.assertEquals(m3.find_unconflicted(),
 
279
                          [(0, 1), (2, 3)])
 
280
 
 
281
        self.assertEquals(list(m3.find_sync_regions()),
 
282
                          [(0,1, 0,1, 0,1),
 
283
                           (2,3, 2,3, 2,3),
 
284
                           (3,3, 3,3, 3,3),])
 
285
 
 
286
    def test_replace_multi(self):
 
287
        """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'])
 
291
 
 
292
        self.assertEquals(m3.find_unconflicted(),
 
293
                          [(0, 1), (3, 4)])
 
294
 
 
295
 
 
296
        self.assertEquals(list(m3.find_sync_regions()),
 
297
                          [(0,1, 0,1, 0,1),
 
298
                           (3,4, 4,5, 5,6),
 
299
                           (4,4, 5,5, 6,6),])
 
300
 
 
301
    def test_merge_poem(self):
 
302
        """Test case from diff3 manual"""
 
303
        m3 = merge3.Merge3(TZU, LAO, TAO)
 
304
        ml = list(m3.merge_lines('LAO', 'TAO'))
 
305
        self.log('merge result:')
 
306
        self.log(''.join(ml))
 
307
        self.assertEquals(ml, MERGED_RESULT)
 
308
 
 
309
    def test_minimal_conflicts_common(self):
 
310
        """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)
 
314
        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")
 
320
        self.assertEqualDiff(optimal_text, merged_text)
 
321
 
 
322
    def test_minimal_conflicts_unique(self):
 
323
        def add_newline(s):
 
324
            """Add a newline to each entry in the string"""
 
325
            return [(x+'\n') for x in s]
 
326
 
 
327
        base_text = add_newline("abcdefghijklm")
 
328
        this_text = add_newline("abcdefghijklmNOPQRSTUVWXYZ")
 
329
        other_text = add_newline("abcdefghijklm1OPQRSTUVWXY2")
 
330
        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"]
 
337
            )
 
338
        self.assertEqualDiff(optimal_text, merged_text)
 
339
 
 
340
    def test_minimal_conflicts_nonunique(self):
 
341
        def add_newline(s):
 
342
            """Add a newline to each entry in the string"""
 
343
            return [(x+'\n') for x in s]
 
344
 
 
345
        base_text = add_newline("abacddefgghij")
 
346
        this_text = add_newline("abacddefgghijkalmontfprz")
 
347
        other_text = add_newline("abacddefgghijknlmontfprd")
 
348
        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"]
 
355
            )
 
356
        self.assertEqualDiff(optimal_text, merged_text)
 
357
 
 
358
    def test_reprocess_and_base(self):
 
359
        """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)
 
363
        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)
 
367
 
 
368
    def test_binary(self):
 
369
        self.assertRaises(BinaryFile, merge3.Merge3, ['\x00'], ['a'], ['b'])
 
370
 
 
371
    def test_dos_text(self):
 
372
        base_text = 'a\r\n'
 
373
        this_text = 'b\r\n'
 
374
        other_text = 'c\r\n'
 
375
        m3 = merge3.Merge3(base_text.splitlines(True),
 
376
                           other_text.splitlines(True),
 
377
                           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))
 
381
 
 
382
    def test_mac_text(self):
 
383
        base_text = 'a\r'
 
384
        this_text = 'b\r'
 
385
        other_text = 'c\r'
 
386
        m3 = merge3.Merge3(base_text.splitlines(True),
 
387
                           other_text.splitlines(True),
 
388
                           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))
 
392
 
 
393
    def test_merge3_cherrypick(self):
 
394
        base_text = "a\nb\n"
 
395
        this_text = "a\n"
 
396
        other_text = "a\nb\nc\n"
 
397
        # When cherrypicking, lines in base are not part of the conflict
 
398
        m3 = merge3.Merge3(base_text.splitlines(True),
 
399
                           this_text.splitlines(True),
 
400
                           other_text.splitlines(True), is_cherrypick=True)
 
401
        m_lines = m3.merge_lines()
 
402
        self.assertEqualDiff('a\n<<<<<<<\n=======\nc\n>>>>>>>\n',
 
403
                             ''.join(m_lines))
 
404
 
 
405
        # This is not symmetric
 
406
        m3 = merge3.Merge3(base_text.splitlines(True),
 
407
                           other_text.splitlines(True),
 
408
                           this_text.splitlines(True), is_cherrypick=True)
 
409
        m_lines = m3.merge_lines()
 
410
        self.assertEqualDiff('a\n<<<<<<<\nb\nc\n=======\n>>>>>>>\n',
 
411
                             ''.join(m_lines))
 
412
 
 
413
    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'
 
417
        # When cherrypicking, lines in base are not part of the conflict
 
418
        m3 = merge3.Merge3(base_text.splitlines(True),
 
419
                           this_text.splitlines(True),
 
420
                           other_text.splitlines(True), is_cherrypick=True)
 
421
        m_lines = m3.merge_lines()
 
422
        self.assertEqualDiff('a\n'
 
423
                             'b\n'
 
424
                             '<<<<<<<\n'
 
425
                             'q\n'
 
426
                             '=======\n'
 
427
                             'f\n'
 
428
                             '>>>>>>>\n'
 
429
                             '<<<<<<<\n'
 
430
                             '=======\n'
 
431
                             'g\n'
 
432
                             '>>>>>>>\n',
 
433
                             ''.join(m_lines))
 
434
 
 
435
    def test_allow_objects(self):
 
436
        """Objects other than strs may be used with Merge3 when
 
437
        allow_objects=True.
 
438
        
 
439
        merge_groups and merge_regions work with non-str input.  Methods that
 
440
        return lines like merge_lines fail.
 
441
        """
 
442
        base = [(x,x) for x in 'abcde']
 
443
        a = [(x,x) for x in 'abcdef']
 
444
        b = [(x,x) for x in 'Zabcde']
 
445
        m3 = merge3.Merge3(base, a, b, allow_objects=True)
 
446
        self.assertEqual(
 
447
            [('b', 0, 1),
 
448
             ('unchanged', 0, 5),
 
449
             ('a', 5, 6)],
 
450
            list(m3.merge_regions()))
 
451
        self.assertEqual(
 
452
            [('b', [('Z', 'Z')]),
 
453
             ('unchanged', [(x,x) for x in 'abcde']),
 
454
             ('a', [('f', 'f')])],
 
455
            list(m3.merge_groups()))
 
456