/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/per_merger.py

  • Committer: Jelmer Vernooij
  • Date: 2020-05-06 02:13:25 UTC
  • mfrom: (7490.7.21 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200506021325-awbmmqu1zyorz7sj
Merge 3.1 branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
    TestCaseWithTransport,
29
29
    )
30
30
from .test_merge_core import MergeBuilder
31
 
from ..transform import TreeTransform
32
 
 
33
31
 
34
32
 
35
33
def load_tests(loader, standard_tests, pattern):
47
45
        merger = _mod_merge.Merger.from_revision_ids(
48
46
            target_tree, source_tree.last_revision(),
49
47
            other_branch=source_tree.branch)
50
 
        merger.merge_type=self.merge_type
 
48
        merger.merge_type = self.merge_type
51
49
        for name, value in kwargs.items():
52
50
            setattr(merger, name, value)
53
51
        merger.do_merge()
57
55
        this_tree.lock_write()
58
56
        self.addCleanup(this_tree.unlock)
59
57
        self.build_tree_contents([
60
 
            ('this/file1', 'a\nb\n'),
61
 
            ('this/file2', 'a\nb\n')
 
58
            ('this/file1', b'a\nb\n'),
 
59
            ('this/file2', b'a\nb\n')
62
60
        ])
63
61
        this_tree.add(['file1', 'file2'])
64
62
        this_tree.commit('Added files')
65
63
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
66
64
        self.build_tree_contents([
67
 
            ('other/file1', 'a\nb\nc\n'),
68
 
            ('other/file2', 'a\nb\nc\n')
 
65
            ('other/file1', b'a\nb\nc\n'),
 
66
            ('other/file2', b'a\nb\nc\n')
69
67
        ])
70
68
        other_tree.commit('modified both')
71
69
        self.build_tree_contents([
72
 
            ('this/file1', 'd\na\nb\n'),
73
 
            ('this/file2', 'd\na\nb\n')
 
70
            ('this/file1', b'd\na\nb\n'),
 
71
            ('this/file2', b'd\na\nb\n')
74
72
        ])
75
73
        this_tree.commit('modified both')
76
74
        self.do_merge(this_tree, other_tree, interesting_files=['file1'])
77
 
        self.assertFileEqual('d\na\nb\nc\n', 'this/file1')
78
 
        self.assertFileEqual('d\na\nb\n', 'this/file2')
 
75
        self.assertFileEqual(b'd\na\nb\nc\n', 'this/file1')
 
76
        self.assertFileEqual(b'd\na\nb\n', 'this/file2')
79
77
 
80
78
    def test_merge_move_and_change(self):
81
79
        this_tree = self.make_branch_and_tree('this')
82
80
        this_tree.lock_write()
83
81
        self.addCleanup(this_tree.unlock)
84
82
        self.build_tree_contents([
85
 
            ('this/file1', 'line 1\nline 2\nline 3\nline 4\n'),
 
83
            ('this/file1', b'line 1\nline 2\nline 3\nline 4\n'),
86
84
        ])
87
85
        this_tree.add('file1',)
88
86
        this_tree.commit('Added file')
89
87
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
90
88
        self.build_tree_contents([
91
 
            ('other/file1', 'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
 
89
            ('other/file1', b'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
92
90
        ])
93
91
        other_tree.commit('Changed 2 to 2.1')
94
92
        self.build_tree_contents([
95
 
            ('this/file1', 'line 1\nline 3\nline 2\nline 4\n'),
 
93
            ('this/file1', b'line 1\nline 3\nline 2\nline 4\n'),
96
94
        ])
97
95
        this_tree.commit('Swapped 2 & 3')
98
96
        self.do_merge(this_tree, other_tree)
110
108
                '>>>>>>> MERGE-SOURCE\n'
111
109
                'line 4\n', 'this/file1')
112
110
        else:
113
 
            self.assertFileEqual('line 1\n'
 
111
            self.assertFileEqual(
 
112
                'line 1\n'
114
113
                '<<<<<<< TREE\n'
115
114
                'line 3\n'
116
115
                'line 2\n'
126
125
        builder = self.make_branch_builder('test')
127
126
        builder.start_series()
128
127
        builder.build_snapshot(None,
129
 
            [('add', ('', None, 'directory', None)),
130
 
             ('add', ('foo', 'foo-id', 'file', 'a\nb\nc\nd\ne\n')),
131
 
            ], revision_id='BASE-id')
 
128
                               [('add', ('', None, 'directory', None)),
 
129
                                ('add', ('foo', b'foo-id', 'file', b'a\nb\nc\nd\ne\n')),
 
130
                                ], revision_id=b'BASE-id')
132
131
        # Delete 'b\n'
133
 
        builder.build_snapshot(['BASE-id'],
134
 
            [('modify', ('foo-id', 'a\nc\nd\ne\n'))],
135
 
            revision_id='OTHER-id')
 
132
        builder.build_snapshot([b'BASE-id'],
 
133
                               [('modify', ('foo', b'a\nc\nd\ne\n'))],
 
134
                               revision_id=b'OTHER-id')
136
135
        # Modify 'b\n', add 'X\n'
137
 
        builder.build_snapshot(['BASE-id'],
138
 
            [('modify', ('foo-id', 'a\nb2\nc\nd\nX\ne\n'))],
139
 
            revision_id='THIS-id')
 
136
        builder.build_snapshot([b'BASE-id'],
 
137
                               [('modify', ('foo', b'a\nb2\nc\nd\nX\ne\n'))],
 
138
                               revision_id=b'THIS-id')
140
139
        builder.finish_series()
141
140
        branch = builder.get_branch()
142
141
        this_tree = branch.controldir.create_workingtree()
143
142
        this_tree.lock_write()
144
143
        self.addCleanup(this_tree.unlock)
145
 
        other_tree = this_tree.controldir.sprout('other',
146
 
                                             'OTHER-id').open_workingtree()
 
144
        other_tree = this_tree.controldir.sprout(
 
145
            'other', b'OTHER-id').open_workingtree()
147
146
        self.do_merge(this_tree, other_tree)
148
147
        if self.merge_type is _mod_merge.LCAMerger:
149
148
            self.expectFailure("lca merge doesn't track deleted lines",
150
 
                self.assertFileEqual,
151
 
                    'a\n'
152
 
                    '<<<<<<< TREE\n'
153
 
                    'b2\n'
154
 
                    '=======\n'
155
 
                    '>>>>>>> MERGE-SOURCE\n'
156
 
                    'c\n'
157
 
                    'd\n'
158
 
                    'X\n'
159
 
                    'e\n', 'test/foo')
 
149
                               self.assertFileEqual,
 
150
                               'a\n'
 
151
                               '<<<<<<< TREE\n'
 
152
                               'b2\n'
 
153
                               '=======\n'
 
154
                               '>>>>>>> MERGE-SOURCE\n'
 
155
                               'c\n'
 
156
                               'd\n'
 
157
                               'X\n'
 
158
                               'e\n', 'test/foo')
160
159
        else:
161
160
            self.assertFileEqual(
162
 
                'a\n'
163
 
                '<<<<<<< TREE\n'
164
 
                'b2\n'
165
 
                '=======\n'
166
 
                '>>>>>>> MERGE-SOURCE\n'
167
 
                'c\n'
168
 
                'd\n'
169
 
                'X\n'
170
 
                'e\n', 'test/foo')
 
161
                b'a\n'
 
162
                b'<<<<<<< TREE\n'
 
163
                b'b2\n'
 
164
                b'=======\n'
 
165
                b'>>>>>>> MERGE-SOURCE\n'
 
166
                b'c\n'
 
167
                b'd\n'
 
168
                b'X\n'
 
169
                b'e\n', 'test/foo')
171
170
 
172
171
    def get_limbodir_deletiondir(self, wt):
173
 
        transform = TreeTransform(wt)
 
172
        transform = wt.get_transform()
174
173
        limbodir = transform._limbodir
175
174
        deletiondir = transform._deletiondir
176
175
        transform.finalize()
179
178
    def test_merge_with_existing_limbo_empty(self):
180
179
        """Empty limbo dir is just cleaned up - see bug 427773"""
181
180
        wt = self.make_branch_and_tree('this')
182
 
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
181
        (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
183
182
        os.mkdir(limbodir)
184
183
        self.do_merge(wt, wt)
185
184
 
186
185
    def test_merge_with_existing_limbo_non_empty(self):
187
186
        wt = self.make_branch_and_tree('this')
188
 
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
187
        (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
189
188
        os.mkdir(limbodir)
190
189
        os.mkdir(os.path.join(limbodir, 'something'))
191
190
        self.assertRaises(errors.ExistingLimbo, self.do_merge, wt, wt)
193
192
 
194
193
    def test_merge_with_pending_deletion_empty(self):
195
194
        wt = self.make_branch_and_tree('this')
196
 
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
195
        (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
197
196
        os.mkdir(deletiondir)
198
197
        self.do_merge(wt, wt)
199
198
 
200
199
    def test_merge_with_pending_deletion_non_empty(self):
201
200
        """Also see bug 427773"""
202
201
        wt = self.make_branch_and_tree('this')
203
 
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
202
        (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt)
204
203
        os.mkdir(deletiondir)
205
204
        os.mkdir(os.path.join(deletiondir, 'something'))
206
 
        self.assertRaises(errors.ExistingPendingDeletion, self.do_merge, wt, wt)
 
205
        self.assertRaises(errors.ExistingPendingDeletion,
 
206
                          self.do_merge, wt, wt)
207
207
        self.assertRaises(errors.LockError, wt.unlock)
208
208
 
209
209
 
224
224
 
225
225
    def install_hook_noop(self):
226
226
        test = self
 
227
 
227
228
        class HookNA(_mod_merge.AbstractPerFileMerger):
228
229
            def merge_contents(self, merge_params):
229
230
                # This hook unconditionally does nothing.
230
231
                test.hook_log.append(('no-op',))
231
232
                return 'not_applicable', None
 
233
 
232
234
        def hook_na_factory(merger):
233
235
            return HookNA(merger)
234
236
        _mod_merge.Merger.hooks.install_named_hook(
236
238
 
237
239
    def install_hook_success(self):
238
240
        test = self
 
241
 
239
242
        class HookSuccess(_mod_merge.AbstractPerFileMerger):
240
243
            def merge_contents(self, merge_params):
241
244
                test.hook_log.append(('success',))
242
 
                if merge_params.file_id == '1':
243
 
                    return 'success', ['text-merged-by-hook']
 
245
                if merge_params.this_path == 'name1':
 
246
                    return 'success', [b'text-merged-by-hook']
244
247
                return 'not_applicable', None
 
248
 
245
249
        def hook_success_factory(merger):
246
250
            return HookSuccess(merger)
247
251
        _mod_merge.Merger.hooks.install_named_hook(
249
253
 
250
254
    def install_hook_conflict(self):
251
255
        test = self
 
256
 
252
257
        class HookConflict(_mod_merge.AbstractPerFileMerger):
253
258
            def merge_contents(self, merge_params):
254
259
                test.hook_log.append(('conflict',))
255
 
                if merge_params.file_id == '1':
 
260
                if merge_params.this_path == 'name1':
256
261
                    return ('conflicted',
257
 
                        ['text-with-conflict-markers-from-hook'])
 
262
                            [b'text-with-conflict-markers-from-hook'])
258
263
                return 'not_applicable', None
 
264
 
259
265
        def hook_conflict_factory(merger):
260
266
            return HookConflict(merger)
261
267
        _mod_merge.Merger.hooks.install_named_hook(
263
269
 
264
270
    def install_hook_delete(self):
265
271
        test = self
 
272
 
266
273
        class HookDelete(_mod_merge.AbstractPerFileMerger):
267
274
            def merge_contents(self, merge_params):
268
275
                test.hook_log.append(('delete',))
269
 
                if merge_params.file_id == '1':
 
276
                if merge_params.this_path == 'name1':
270
277
                    return 'delete', None
271
278
                return 'not_applicable', None
 
279
 
272
280
        def hook_delete_factory(merger):
273
281
            return HookDelete(merger)
274
282
        _mod_merge.Merger.hooks.install_named_hook(
279
287
        versions of the file.
280
288
        """
281
289
        test = self
 
290
 
282
291
        class HookLogLines(_mod_merge.AbstractPerFileMerger):
283
292
            def merge_contents(self, merge_params):
284
293
                test.hook_log.append((
288
297
                    merge_params.base_lines,
289
298
                    ))
290
299
                return 'not_applicable', None
 
300
 
291
301
        def hook_log_lines_factory(merger):
292
302
            return HookLogLines(merger)
293
303
        _mod_merge.Merger.hooks.install_named_hook(
299
309
        self.addCleanup(builder.cleanup)
300
310
        return builder
301
311
 
302
 
    def create_file_needing_contents_merge(self, builder, file_id):
303
 
        builder.add_file(file_id, builder.tree_root, "name1", "text1", True)
304
 
        builder.change_contents(file_id, other="text4", this="text3")
 
312
    def create_file_needing_contents_merge(self, builder, name):
 
313
        file_id = name.encode('ascii') + b'-id'
 
314
        builder.add_file(file_id, builder.tree_root, name, b"text1", True)
 
315
        builder.change_contents(file_id, other=b"text4", this=b"text3")
305
316
 
306
317
    def test_change_vs_change(self):
307
318
        """Hook is used for (changed, changed)"""
308
319
        self.install_hook_success()
309
320
        builder = self.make_merge_builder()
310
 
        builder.add_file("1", builder.tree_root, "name1", "text1", True)
311
 
        builder.change_contents("1", other="text4", this="text3")
 
321
        builder.add_file(b"1", builder.tree_root, "name1", b"text1", True)
 
322
        builder.change_contents(b"1", other=b"text4", this=b"text3")
312
323
        conflicts = builder.merge(self.merge_type)
313
324
        self.assertEqual(conflicts, [])
314
 
        self.assertEqual(
315
 
            builder.this.get_file('name1').read(), 'text-merged-by-hook')
 
325
        with builder.this.get_file('name1') as f:
 
326
            self.assertEqual(f.read(), b'text-merged-by-hook')
316
327
 
317
328
    def test_change_vs_deleted(self):
318
329
        """Hook is used for (changed, deleted)"""
319
330
        self.install_hook_success()
320
331
        builder = self.make_merge_builder()
321
 
        builder.add_file("1", builder.tree_root, "name1", "text1", True)
322
 
        builder.change_contents("1", this="text2")
323
 
        builder.remove_file("1", other=True)
 
332
        builder.add_file(b"1", builder.tree_root, "name1", b"text1", True)
 
333
        builder.change_contents(b"1", this=b"text2")
 
334
        builder.remove_file(b"1", other=True)
324
335
        conflicts = builder.merge(self.merge_type)
325
336
        self.assertEqual(conflicts, [])
326
 
        self.assertEqual(
327
 
            builder.this.get_file('name1').read(), 'text-merged-by-hook')
 
337
        with builder.this.get_file('name1') as f:
 
338
            self.assertEqual(f.read(), b'text-merged-by-hook')
328
339
 
329
340
    def test_result_can_be_delete(self):
330
341
        """A hook's result can be the deletion of a file."""
331
342
        self.install_hook_delete()
332
343
        builder = self.make_merge_builder()
333
 
        self.create_file_needing_contents_merge(builder, "1")
 
344
        self.create_file_needing_contents_merge(builder, "name1")
334
345
        conflicts = builder.merge(self.merge_type)
335
346
        self.assertEqual(conflicts, [])
336
 
        self.assertRaises(errors.NoSuchId, builder.this.id2path, '1')
 
347
        self.assertFalse(builder.this.is_versioned('name1'))
337
348
        self.assertEqual([], list(builder.this.list_files()))
338
349
 
339
350
    def test_result_can_be_conflict(self):
340
351
        """A hook's result can be a conflict."""
341
352
        self.install_hook_conflict()
342
353
        builder = self.make_merge_builder()
343
 
        self.create_file_needing_contents_merge(builder, "1")
 
354
        self.create_file_needing_contents_merge(builder, "name1")
344
355
        conflicts = builder.merge(self.merge_type)
345
 
        self.assertEqual(conflicts, [TextConflict('name1', file_id='1')])
 
356
        self.assertEqual(conflicts, [TextConflict('name1', file_id=b'name1-id')])
346
357
        # The hook still gets to set the file contents in this case, so that it
347
358
        # can insert custom conflict markers.
348
 
        self.assertEqual(
349
 
            builder.this.get_file('name1').read(),
350
 
            'text-with-conflict-markers-from-hook')
 
359
        with builder.this.get_file('name1') as f:
 
360
            self.assertEqual(f.read(), b'text-with-conflict-markers-from-hook')
351
361
 
352
362
    def test_can_access_this_other_and_base_versions(self):
353
363
        """The hook function can call params.merger.get_lines to access the
355
365
        """
356
366
        self.install_hook_log_lines()
357
367
        builder = self.make_merge_builder()
358
 
        builder.add_file("1", builder.tree_root, "name1", "text1", True)
359
 
        builder.change_contents("1", this="text2", other="text3")
 
368
        builder.add_file(b"1", builder.tree_root, "name1", b"text1", True)
 
369
        builder.change_contents(b"1", this=b"text2", other=b"text3")
360
370
        conflicts = builder.merge(self.merge_type)
361
371
        self.assertEqual(
362
 
            [('log_lines', ['text2'], ['text3'], ['text1'])], self.hook_log)
 
372
            [('log_lines', [b'text2'], [b'text3'], [b'text1'])], self.hook_log)
363
373
 
364
374
    def test_chain_when_not_active(self):
365
375
        """When a hook function returns None, merging still works."""
366
376
        self.install_hook_inactive()
367
377
        self.install_hook_success()
368
378
        builder = self.make_merge_builder()
369
 
        self.create_file_needing_contents_merge(builder, "1")
 
379
        self.create_file_needing_contents_merge(builder, "name1")
370
380
        conflicts = builder.merge(self.merge_type)
371
381
        self.assertEqual(conflicts, [])
372
 
        self.assertEqual(
373
 
            builder.this.get_file('name1').read(), 'text-merged-by-hook')
 
382
        with builder.this.get_file('name1') as f:
 
383
            self.assertEqual(f.read(), b'text-merged-by-hook')
374
384
        self.assertEqual([('inactive',), ('success',)], self.hook_log)
375
385
 
376
386
    def test_chain_when_not_applicable(self):
380
390
        self.install_hook_noop()
381
391
        self.install_hook_success()
382
392
        builder = self.make_merge_builder()
383
 
        self.create_file_needing_contents_merge(builder, "1")
 
393
        self.create_file_needing_contents_merge(builder, "name1")
384
394
        conflicts = builder.merge(self.merge_type)
385
395
        self.assertEqual(conflicts, [])
386
 
        self.assertEqual(
387
 
            builder.this.get_file('name1').read(), 'text-merged-by-hook')
 
396
        with builder.this.get_file('name1') as f:
 
397
            self.assertEqual(f.read(), b'text-merged-by-hook')
388
398
        self.assertEqual([('no-op',), ('success',)], self.hook_log)
389
399
 
390
400
    def test_chain_stops_after_success(self):
393
403
        self.install_hook_success()
394
404
        self.install_hook_noop()
395
405
        builder = self.make_merge_builder()
396
 
        self.create_file_needing_contents_merge(builder, "1")
 
406
        self.create_file_needing_contents_merge(builder, "name1")
397
407
        conflicts = builder.merge(self.merge_type)
398
408
        self.assertEqual([('success',)], self.hook_log)
399
409
 
403
413
        self.install_hook_conflict()
404
414
        self.install_hook_noop()
405
415
        builder = self.make_merge_builder()
406
 
        self.create_file_needing_contents_merge(builder, "1")
 
416
        self.create_file_needing_contents_merge(builder, "name1")
407
417
        conflicts = builder.merge(self.merge_type)
408
418
        self.assertEqual([('conflict',)], self.hook_log)
409
419
 
413
423
        self.install_hook_delete()
414
424
        self.install_hook_noop()
415
425
        builder = self.make_merge_builder()
416
 
        self.create_file_needing_contents_merge(builder, "1")
 
426
        self.create_file_needing_contents_merge(builder, "name1")
417
427
        conflicts = builder.merge(self.merge_type)
418
428
        self.assertEqual([('delete',)], self.hook_log)
419