/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_merge3.py

  • Committer: Jelmer Vernooij
  • Date: 2019-08-12 20:24:50 UTC
  • mto: (7290.1.35 work)
  • mto: This revision was merged to the branch mainline in revision 7405.
  • Revision ID: jelmer@jelmer.uk-20190812202450-vdpamxay6sebo93w
Fix path to brz.

Show diffs side-by-side

added added

removed removed

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