/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: 2020-05-24 00:39:50 UTC
  • mto: This revision was merged to the branch mainline in revision 7504.
  • Revision ID: jelmer@jelmer.uk-20200524003950-bbc545r76vc5yajg
Add github action.

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
from io import BytesIO
 
18
 
 
19
from .. import (
 
20
    merge3,
 
21
    tests,
 
22
    )
 
23
from ..errors import BinaryFile
 
24
 
 
25
 
 
26
def split_lines(t):
 
27
    return BytesIO(t).readlines()
 
28
 
 
29
 
 
30
############################################################
 
31
# test case data from the gnu diffutils manual
 
32
# common base
 
33
TZU = split_lines(b"""     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(b"""     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(b"""     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(b"""     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
 
 
99
class TestMerge3(tests.TestCase):
 
100
 
 
101
    def test_no_changes(self):
 
102
        """No conflicts because nothing changed"""
 
103
        m3 = merge3.Merge3([b'aaa', b'bbb'],
 
104
                           [b'aaa', b'bbb'],
 
105
                           [b'aaa', b'bbb'])
 
106
 
 
107
        self.assertEqual(m3.find_unconflicted(),
 
108
                         [(0, 2)])
 
109
 
 
110
        self.assertEqual(list(m3.find_sync_regions()),
 
111
                         [(0, 2,
 
112
                           0, 2,
 
113
                           0, 2),
 
114
                          (2, 2, 2, 2, 2, 2)])
 
115
 
 
116
        self.assertEqual(list(m3.merge_regions()),
 
117
                         [('unchanged', 0, 2)])
 
118
 
 
119
        self.assertEqual(list(m3.merge_groups()),
 
120
                         [('unchanged', [b'aaa', b'bbb'])])
 
121
 
 
122
    def test_front_insert(self):
 
123
        m3 = merge3.Merge3([b'zz'],
 
124
                           [b'aaa', b'bbb', b'zz'],
 
125
                           [b'zz'])
 
126
 
 
127
        # todo: should use a sentinal at end as from get_matching_blocks
 
128
        # to match without zz
 
129
        self.assertEqual(list(m3.find_sync_regions()),
 
130
                         [(0, 1, 2, 3, 0, 1),
 
131
                          (1, 1, 3, 3, 1, 1), ])
 
132
 
 
133
        self.assertEqual(list(m3.merge_regions()),
 
134
                         [('a', 0, 2),
 
135
                          ('unchanged', 0, 1)])
 
136
 
 
137
        self.assertEqual(list(m3.merge_groups()),
 
138
                         [('a', [b'aaa', b'bbb']),
 
139
                          ('unchanged', [b'zz'])])
 
140
 
 
141
    def test_null_insert(self):
 
142
        m3 = merge3.Merge3([],
 
143
                           [b'aaa', b'bbb'],
 
144
                           [])
 
145
        # todo: should use a sentinal at end as from get_matching_blocks
 
146
        # to match without zz
 
147
        self.assertEqual(list(m3.find_sync_regions()),
 
148
                         [(0, 0, 2, 2, 0, 0)])
 
149
 
 
150
        self.assertEqual(list(m3.merge_regions()),
 
151
                         [('a', 0, 2)])
 
152
 
 
153
        self.assertEqual(list(m3.merge_lines()),
 
154
                         [b'aaa', b'bbb'])
 
155
 
 
156
    def test_no_conflicts(self):
 
157
        """No conflicts because only one side changed"""
 
158
        m3 = merge3.Merge3([b'aaa', b'bbb'],
 
159
                           [b'aaa', b'111', b'bbb'],
 
160
                           [b'aaa', b'bbb'])
 
161
 
 
162
        self.assertEqual(m3.find_unconflicted(),
 
163
                         [(0, 1), (1, 2)])
 
164
 
 
165
        self.assertEqual(list(m3.find_sync_regions()),
 
166
                         [(0, 1, 0, 1, 0, 1),
 
167
                          (1, 2, 2, 3, 1, 2),
 
168
                          (2, 2, 3, 3, 2, 2), ])
 
169
 
 
170
        self.assertEqual(list(m3.merge_regions()),
 
171
                         [('unchanged', 0, 1),
 
172
                          ('a', 1, 2),
 
173
                          ('unchanged', 1, 2), ])
 
174
 
 
175
    def test_append_a(self):
 
176
        m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
 
177
                           [b'aaa\n', b'bbb\n', b'222\n'],
 
178
                           [b'aaa\n', b'bbb\n'])
 
179
 
 
180
        self.assertEqual(b''.join(m3.merge_lines()),
 
181
                         b'aaa\nbbb\n222\n')
 
182
 
 
183
    def test_append_b(self):
 
184
        m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
 
185
                           [b'aaa\n', b'bbb\n'],
 
186
                           [b'aaa\n', b'bbb\n', b'222\n'])
 
187
 
 
188
        self.assertEqual(b''.join(m3.merge_lines()),
 
189
                         b'aaa\nbbb\n222\n')
 
190
 
 
191
    def test_append_agreement(self):
 
192
        m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
 
193
                           [b'aaa\n', b'bbb\n', b'222\n'],
 
194
                           [b'aaa\n', b'bbb\n', b'222\n'])
 
195
 
 
196
        self.assertEqual(b''.join(m3.merge_lines()),
 
197
                         b'aaa\nbbb\n222\n')
 
198
 
 
199
    def test_append_clash(self):
 
200
        m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
 
201
                           [b'aaa\n', b'bbb\n', b'222\n'],
 
202
                           [b'aaa\n', b'bbb\n', b'333\n'])
 
203
 
 
204
        ml = m3.merge_lines(name_a=b'a',
 
205
                            name_b=b'b',
 
206
                            start_marker=b'<<',
 
207
                            mid_marker=b'--',
 
208
                            end_marker=b'>>')
 
209
        self.assertEqual(b''.join(ml),
 
210
                         b'''\
 
211
aaa
 
212
bbb
 
213
<< a
 
214
222
 
215
--
 
216
333
 
217
>> b
 
218
''')
 
219
 
 
220
    def test_insert_agreement(self):
 
221
        m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
 
222
                           [b'aaa\n', b'222\n', b'bbb\n'],
 
223
                           [b'aaa\n', b'222\n', b'bbb\n'])
 
224
 
 
225
        ml = m3.merge_lines(name_a=b'a',
 
226
                            name_b=b'b',
 
227
                            start_marker=b'<<',
 
228
                            mid_marker=b'--',
 
229
                            end_marker=b'>>')
 
230
        self.assertEqual(b''.join(ml), b'aaa\n222\nbbb\n')
 
231
 
 
232
    def test_insert_clash(self):
 
233
        """Both try to insert lines in the same place."""
 
234
        m3 = merge3.Merge3([b'aaa\n', b'bbb\n'],
 
235
                           [b'aaa\n', b'111\n', b'bbb\n'],
 
236
                           [b'aaa\n', b'222\n', b'bbb\n'])
 
237
 
 
238
        self.assertEqual(m3.find_unconflicted(),
 
239
                         [(0, 1), (1, 2)])
 
240
 
 
241
        self.assertEqual(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.assertEqual(list(m3.merge_regions()),
 
247
                         [('unchanged', 0, 1),
 
248
                          ('conflict', 1, 1, 1, 2, 1, 2),
 
249
                          ('unchanged', 1, 2)])
 
250
 
 
251
        self.assertEqual(list(m3.merge_groups()),
 
252
                         [('unchanged', [b'aaa\n']),
 
253
                          ('conflict', [], [b'111\n'], [b'222\n']),
 
254
                          ('unchanged', [b'bbb\n']),
 
255
                          ])
 
256
 
 
257
        ml = m3.merge_lines(name_a=b'a',
 
258
                            name_b=b'b',
 
259
                            start_marker=b'<<',
 
260
                            mid_marker=b'--',
 
261
                            end_marker=b'>>')
 
262
        self.assertEqual(b''.join(ml),
 
263
                         b'''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([b'aaa', b'000', b'bbb'],
 
275
                           [b'aaa', b'111', b'bbb'],
 
276
                           [b'aaa', b'222', b'bbb'])
 
277
 
 
278
        self.assertEqual(m3.find_unconflicted(),
 
279
                         [(0, 1), (2, 3)])
 
280
 
 
281
        self.assertEqual(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([b'aaa', b'000', b'000', b'bbb'],
 
289
                           [b'aaa', b'111', b'111', b'111', b'bbb'],
 
290
                           [b'aaa', b'222', b'222', b'222', b'222', b'bbb'])
 
291
 
 
292
        self.assertEqual(m3.find_unconflicted(),
 
293
                         [(0, 1), (3, 4)])
 
294
 
 
295
        self.assertEqual(list(m3.find_sync_regions()),
 
296
                         [(0, 1, 0, 1, 0, 1),
 
297
                          (3, 4, 4, 5, 5, 6),
 
298
                          (4, 4, 5, 5, 6, 6), ])
 
299
 
 
300
    def test_merge_poem(self):
 
301
        """Test case from diff3 manual"""
 
302
        m3 = merge3.Merge3(TZU, LAO, TAO)
 
303
        ml = list(m3.merge_lines(b'LAO', b'TAO'))
 
304
        self.log('merge result:')
 
305
        self.log(b''.join(ml))
 
306
        self.assertEqual(ml, MERGED_RESULT)
 
307
 
 
308
    def test_minimal_conflicts_common(self):
 
309
        """Reprocessing"""
 
310
        base_text = (b"a\n" * 20).splitlines(True)
 
311
        this_text = (b"a\n" * 10 + b"b\n" * 10).splitlines(True)
 
312
        other_text = (b"a\n" * 10 + b"c\n" + b"b\n" *
 
313
                      8 + b"c\n").splitlines(True)
 
314
        m3 = merge3.Merge3(base_text, other_text, this_text)
 
315
        m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True)
 
316
        merged_text = b"".join(list(m_lines))
 
317
        optimal_text = (b"a\n" * 10 + b"<<<<<<< OTHER\nc\n"
 
318
                        + 8 * b"b\n" + b"c\n=======\n"
 
319
                        + 10 * b"b\n" + b">>>>>>> 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 [(bytes([x]) + b'\n') for x in bytearray(s)]
 
326
 
 
327
        base_text = add_newline(b"abcdefghijklm")
 
328
        this_text = add_newline(b"abcdefghijklmNOPQRSTUVWXYZ")
 
329
        other_text = add_newline(b"abcdefghijklm1OPQRSTUVWXY2")
 
330
        m3 = merge3.Merge3(base_text, other_text, this_text)
 
331
        m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True)
 
332
        merged_text = b"".join(list(m_lines))
 
333
        optimal_text = b''.join(add_newline(b"abcdefghijklm")
 
334
                                + [b"<<<<<<< OTHER\n1\n=======\nN\n>>>>>>> THIS\n"]
 
335
                                + add_newline(b'OPQRSTUVWXY')
 
336
                                + [b"<<<<<<< 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 [(bytes([x]) + b'\n') for x in bytearray(s)]
 
344
 
 
345
        base_text = add_newline(b"abacddefgghij")
 
346
        this_text = add_newline(b"abacddefgghijkalmontfprz")
 
347
        other_text = add_newline(b"abacddefgghijknlmontfprd")
 
348
        m3 = merge3.Merge3(base_text, other_text, this_text)
 
349
        m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True)
 
350
        merged_text = b"".join(list(m_lines))
 
351
        optimal_text = b''.join(add_newline(b"abacddefgghijk")
 
352
                                + [b"<<<<<<< OTHER\nn\n=======\na\n>>>>>>> THIS\n"]
 
353
                                + add_newline(b'lmontfpr')
 
354
                                + [b"<<<<<<< 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 = (b"a\n" * 20).splitlines(True)
 
361
        this_text = (b"a\n" * 10 + b"b\n" * 10).splitlines(True)
 
362
        other_text = (b"a\n" * 10 + b"c\n" + b"b\n" *
 
363
                      8 + b"c\n").splitlines(True)
 
364
        m3 = merge3.Merge3(base_text, other_text, this_text)
 
365
        m_lines = m3.merge_lines(b'OTHER', b'THIS', reprocess=True,
 
366
                                 base_marker=b'|||||||')
 
367
        self.assertRaises(merge3.CantReprocessAndShowBase, list, m_lines)
 
368
 
 
369
    def test_binary(self):
 
370
        self.assertRaises(BinaryFile, merge3.Merge3, [b'\x00'], [b'a'], [b'b'])
 
371
 
 
372
    def test_dos_text(self):
 
373
        base_text = b'a\r\n'
 
374
        this_text = b'b\r\n'
 
375
        other_text = b'c\r\n'
 
376
        m3 = merge3.Merge3(base_text.splitlines(True),
 
377
                           other_text.splitlines(True),
 
378
                           this_text.splitlines(True))
 
379
        m_lines = m3.merge_lines(b'OTHER', b'THIS')
 
380
        self.assertEqual(b'<<<<<<< OTHER\r\nc\r\n=======\r\nb\r\n'
 
381
                         b'>>>>>>> THIS\r\n'.splitlines(True), list(m_lines))
 
382
 
 
383
    def test_mac_text(self):
 
384
        base_text = b'a\r'
 
385
        this_text = b'b\r'
 
386
        other_text = b'c\r'
 
387
        m3 = merge3.Merge3(base_text.splitlines(True),
 
388
                           other_text.splitlines(True),
 
389
                           this_text.splitlines(True))
 
390
        m_lines = m3.merge_lines(b'OTHER', b'THIS')
 
391
        self.assertEqual(b'<<<<<<< OTHER\rc\r=======\rb\r'
 
392
                         b'>>>>>>> THIS\r'.splitlines(True), list(m_lines))
 
393
 
 
394
    def test_merge3_cherrypick(self):
 
395
        base_text = b"a\nb\n"
 
396
        this_text = b"a\n"
 
397
        other_text = b"a\nb\nc\n"
 
398
        # When cherrypicking, lines in base are not part of the conflict
 
399
        m3 = merge3.Merge3(base_text.splitlines(True),
 
400
                           this_text.splitlines(True),
 
401
                           other_text.splitlines(True), is_cherrypick=True)
 
402
        m_lines = m3.merge_lines()
 
403
        self.assertEqualDiff(b'a\n<<<<<<<\n=======\nc\n>>>>>>>\n',
 
404
                             b''.join(m_lines))
 
405
 
 
406
        # This is not symmetric
 
407
        m3 = merge3.Merge3(base_text.splitlines(True),
 
408
                           other_text.splitlines(True),
 
409
                           this_text.splitlines(True), is_cherrypick=True)
 
410
        m_lines = m3.merge_lines()
 
411
        self.assertEqualDiff(b'a\n<<<<<<<\nb\nc\n=======\n>>>>>>>\n',
 
412
                             b''.join(m_lines))
 
413
 
 
414
    def test_merge3_cherrypick_w_mixed(self):
 
415
        base_text = b'a\nb\nc\nd\ne\n'
 
416
        this_text = b'a\nb\nq\n'
 
417
        other_text = b'a\nb\nc\nd\nf\ne\ng\n'
 
418
        # When cherrypicking, lines in base are not part of the conflict
 
419
        m3 = merge3.Merge3(base_text.splitlines(True),
 
420
                           this_text.splitlines(True),
 
421
                           other_text.splitlines(True), is_cherrypick=True)
 
422
        m_lines = m3.merge_lines()
 
423
        self.assertEqualDiff(b'a\n'
 
424
                             b'b\n'
 
425
                             b'<<<<<<<\n'
 
426
                             b'q\n'
 
427
                             b'=======\n'
 
428
                             b'f\n'
 
429
                             b'>>>>>>>\n'
 
430
                             b'<<<<<<<\n'
 
431
                             b'=======\n'
 
432
                             b'g\n'
 
433
                             b'>>>>>>>\n',
 
434
                             b''.join(m_lines))
 
435
 
 
436
    def test_allow_objects(self):
 
437
        """Objects other than strs may be used with Merge3 when
 
438
        allow_objects=True.
 
439
 
 
440
        merge_groups and merge_regions work with non-str input.  Methods that
 
441
        return lines like merge_lines fail.
 
442
        """
 
443
        base = [(x, x) for x in 'abcde']
 
444
        a = [(x, x) for x in 'abcdef']
 
445
        b = [(x, x) for x in 'Zabcde']
 
446
        m3 = merge3.Merge3(base, a, b, allow_objects=True)
 
447
        self.assertEqual(
 
448
            [('b', 0, 1),
 
449
             ('unchanged', 0, 5),
 
450
             ('a', 5, 6)],
 
451
            list(m3.merge_regions()))
 
452
        self.assertEqual(
 
453
            [('b', [('Z', 'Z')]),
 
454
             ('unchanged', [(x, x) for x in 'abcde']),
 
455
             ('a', [('f', 'f')])],
 
456
            list(m3.merge_groups()))