/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: Breezy landing bot
  • Author(s): Jelmer Vernooij
  • Date: 2018-11-16 07:18:33 UTC
  • mfrom: (7141.3.3 fix1799482)
  • Revision ID: breezy.the.bot@gmail.com-20181116071833-e01b0833f3hkc3et
Report correct paths when running "brz add" in git repositories.

Merged from https://code.launchpad.net/~jelmer/brz/fix1799482/+merge/357734

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2009, 2010, 2011 Canonical Ltd
2
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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""Implementation tests for bzrlib.merge.Merger."""
 
17
"""Implementation tests for breezy.merge.Merger."""
18
18
 
19
19
import os
20
20
 
21
 
from bzrlib.conflicts import TextConflict
22
 
from bzrlib import (
 
21
from ..conflicts import TextConflict
 
22
from .. import (
23
23
    errors,
24
24
    merge as _mod_merge,
25
 
    option,
26
 
    progress,
27
25
    )
28
 
from bzrlib.tests import (
 
26
from . import (
29
27
    multiply_tests,
30
28
    TestCaseWithTransport,
31
29
    )
32
 
from bzrlib.tests.test_merge_core import MergeBuilder
33
 
from bzrlib.transform import TreeTransform
34
 
 
35
 
 
36
 
 
37
 
def load_tests(standard_tests, module, loader):
 
30
from .test_merge_core import MergeBuilder
 
31
from ..transform import TreeTransform
 
32
 
 
33
 
 
34
 
 
35
def load_tests(loader, standard_tests, pattern):
38
36
    """Multiply tests for tranport implementations."""
39
37
    result = loader.suiteClass()
40
38
    scenarios = [
41
39
        (name, {'merge_type': merger})
42
 
        for name, merger in option._merge_type_registry.items()]
 
40
        for name, merger in _mod_merge.merge_type_registry.items()]
43
41
    return multiply_tests(standard_tests, scenarios, result)
44
42
 
45
43
 
46
44
class TestMergeImplementation(TestCaseWithTransport):
47
45
 
48
46
    def do_merge(self, target_tree, source_tree, **kwargs):
49
 
        merger = _mod_merge.Merger.from_revision_ids(None,
 
47
        merger = _mod_merge.Merger.from_revision_ids(
50
48
            target_tree, source_tree.last_revision(),
51
49
            other_branch=source_tree.branch)
52
50
        merger.merge_type=self.merge_type
59
57
        this_tree.lock_write()
60
58
        self.addCleanup(this_tree.unlock)
61
59
        self.build_tree_contents([
62
 
            ('this/file1', 'a\nb\n'),
63
 
            ('this/file2', 'a\nb\n')
 
60
            ('this/file1', b'a\nb\n'),
 
61
            ('this/file2', b'a\nb\n')
64
62
        ])
65
63
        this_tree.add(['file1', 'file2'])
66
64
        this_tree.commit('Added files')
67
 
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
 
65
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
68
66
        self.build_tree_contents([
69
 
            ('other/file1', 'a\nb\nc\n'),
70
 
            ('other/file2', 'a\nb\nc\n')
 
67
            ('other/file1', b'a\nb\nc\n'),
 
68
            ('other/file2', b'a\nb\nc\n')
71
69
        ])
72
70
        other_tree.commit('modified both')
73
71
        self.build_tree_contents([
74
 
            ('this/file1', 'd\na\nb\n'),
75
 
            ('this/file2', 'd\na\nb\n')
 
72
            ('this/file1', b'd\na\nb\n'),
 
73
            ('this/file2', b'd\na\nb\n')
76
74
        ])
77
75
        this_tree.commit('modified both')
78
76
        self.do_merge(this_tree, other_tree, interesting_files=['file1'])
79
 
        self.assertFileEqual('d\na\nb\nc\n', 'this/file1')
80
 
        self.assertFileEqual('d\na\nb\n', 'this/file2')
 
77
        self.assertFileEqual(b'd\na\nb\nc\n', 'this/file1')
 
78
        self.assertFileEqual(b'd\na\nb\n', 'this/file2')
81
79
 
82
80
    def test_merge_move_and_change(self):
83
81
        this_tree = self.make_branch_and_tree('this')
84
82
        this_tree.lock_write()
85
83
        self.addCleanup(this_tree.unlock)
86
84
        self.build_tree_contents([
87
 
            ('this/file1', 'line 1\nline 2\nline 3\nline 4\n'),
 
85
            ('this/file1', b'line 1\nline 2\nline 3\nline 4\n'),
88
86
        ])
89
87
        this_tree.add('file1',)
90
88
        this_tree.commit('Added file')
91
 
        other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
 
89
        other_tree = this_tree.controldir.sprout('other').open_workingtree()
92
90
        self.build_tree_contents([
93
 
            ('other/file1', 'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
 
91
            ('other/file1', b'line 1\nline 2 to 2.1\nline 3\nline 4\n'),
94
92
        ])
95
93
        other_tree.commit('Changed 2 to 2.1')
96
94
        self.build_tree_contents([
97
 
            ('this/file1', 'line 1\nline 3\nline 2\nline 4\n'),
 
95
            ('this/file1', b'line 1\nline 3\nline 2\nline 4\n'),
98
96
        ])
99
97
        this_tree.commit('Swapped 2 & 3')
100
98
        self.do_merge(this_tree, other_tree)
112
110
                '>>>>>>> MERGE-SOURCE\n'
113
111
                'line 4\n', 'this/file1')
114
112
        else:
115
 
            self.assertFileEqual('line 1\n'
 
113
            self.assertFileEqual(
 
114
                'line 1\n'
116
115
                '<<<<<<< TREE\n'
117
116
                'line 3\n'
118
117
                'line 2\n'
127
126
        # the modification should be considered a conflict
128
127
        builder = self.make_branch_builder('test')
129
128
        builder.start_series()
130
 
        builder.build_snapshot('BASE-id', None,
 
129
        builder.build_snapshot(None,
131
130
            [('add', ('', None, 'directory', None)),
132
 
             ('add', ('foo', 'foo-id', 'file', 'a\nb\nc\nd\ne\n')),
133
 
            ])
 
131
             ('add', ('foo', b'foo-id', 'file', b'a\nb\nc\nd\ne\n')),
 
132
            ], revision_id=b'BASE-id')
134
133
        # Delete 'b\n'
135
 
        builder.build_snapshot('OTHER-id', ['BASE-id'],
136
 
            [('modify', ('foo-id', 'a\nc\nd\ne\n'))])
 
134
        builder.build_snapshot([b'BASE-id'],
 
135
            [('modify', ('foo', b'a\nc\nd\ne\n'))],
 
136
            revision_id=b'OTHER-id')
137
137
        # Modify 'b\n', add 'X\n'
138
 
        builder.build_snapshot('THIS-id', ['BASE-id'],
139
 
            [('modify', ('foo-id', 'a\nb2\nc\nd\nX\ne\n'))])
 
138
        builder.build_snapshot([b'BASE-id'],
 
139
            [('modify', ('foo', b'a\nb2\nc\nd\nX\ne\n'))],
 
140
            revision_id=b'THIS-id')
140
141
        builder.finish_series()
141
142
        branch = builder.get_branch()
142
 
        this_tree = branch.bzrdir.create_workingtree()
 
143
        this_tree = branch.controldir.create_workingtree()
143
144
        this_tree.lock_write()
144
145
        self.addCleanup(this_tree.unlock)
145
 
        other_tree = this_tree.bzrdir.sprout('other',
146
 
                                             'OTHER-id').open_workingtree()
 
146
        other_tree = this_tree.controldir.sprout('other', b'OTHER-id').open_workingtree()
147
147
        self.do_merge(this_tree, other_tree)
148
148
        if self.merge_type is _mod_merge.LCAMerger:
149
149
            self.expectFailure("lca merge doesn't track deleted lines",
159
159
                    'e\n', 'test/foo')
160
160
        else:
161
161
            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')
 
162
                b'a\n'
 
163
                b'<<<<<<< TREE\n'
 
164
                b'b2\n'
 
165
                b'=======\n'
 
166
                b'>>>>>>> MERGE-SOURCE\n'
 
167
                b'c\n'
 
168
                b'd\n'
 
169
                b'X\n'
 
170
                b'e\n', 'test/foo')
171
171
 
172
172
    def get_limbodir_deletiondir(self, wt):
173
173
        transform = TreeTransform(wt)
176
176
        transform.finalize()
177
177
        return (limbodir, deletiondir)
178
178
 
179
 
    def test_merge_with_existing_limbo(self):
180
 
        wt = self.make_branch_and_tree('this')
181
 
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
182
 
        os.mkdir(limbodir)
 
179
    def test_merge_with_existing_limbo_empty(self):
 
180
        """Empty limbo dir is just cleaned up - see bug 427773"""
 
181
        wt = self.make_branch_and_tree('this')
 
182
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
183
        os.mkdir(limbodir)
 
184
        self.do_merge(wt, wt)
 
185
 
 
186
    def test_merge_with_existing_limbo_non_empty(self):
 
187
        wt = self.make_branch_and_tree('this')
 
188
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
189
        os.mkdir(limbodir)
 
190
        os.mkdir(os.path.join(limbodir, 'something'))
183
191
        self.assertRaises(errors.ExistingLimbo, self.do_merge, wt, wt)
184
192
        self.assertRaises(errors.LockError, wt.unlock)
185
193
 
186
 
    def test_merge_with_pending_deletion(self):
187
 
        wt = self.make_branch_and_tree('this')
188
 
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
189
 
        os.mkdir(deletiondir)
 
194
    def test_merge_with_pending_deletion_empty(self):
 
195
        wt = self.make_branch_and_tree('this')
 
196
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
197
        os.mkdir(deletiondir)
 
198
        self.do_merge(wt, wt)
 
199
 
 
200
    def test_merge_with_pending_deletion_non_empty(self):
 
201
        """Also see bug 427773"""
 
202
        wt = self.make_branch_and_tree('this')
 
203
        (limbodir, deletiondir) =  self.get_limbodir_deletiondir(wt)
 
204
        os.mkdir(deletiondir)
 
205
        os.mkdir(os.path.join(deletiondir, 'something'))
190
206
        self.assertRaises(errors.ExistingPendingDeletion, self.do_merge, wt, wt)
191
207
        self.assertRaises(errors.LockError, wt.unlock)
192
208
 
195
211
    """Tests that the 'merge_file_content' hook is invoked."""
196
212
 
197
213
    def setUp(self):
198
 
        TestCaseWithTransport.setUp(self)
 
214
        super(TestHookMergeFileContent, self).setUp()
199
215
        self.hook_log = []
200
216
 
201
217
    def install_hook_inactive(self):
223
239
        class HookSuccess(_mod_merge.AbstractPerFileMerger):
224
240
            def merge_contents(self, merge_params):
225
241
                test.hook_log.append(('success',))
226
 
                if merge_params.file_id == '1':
227
 
                    return 'success', ['text-merged-by-hook']
 
242
                if merge_params.file_id == b'1':
 
243
                    return 'success', [b'text-merged-by-hook']
228
244
                return 'not_applicable', None
229
245
        def hook_success_factory(merger):
230
246
            return HookSuccess(merger)
236
252
        class HookConflict(_mod_merge.AbstractPerFileMerger):
237
253
            def merge_contents(self, merge_params):
238
254
                test.hook_log.append(('conflict',))
239
 
                if merge_params.file_id == '1':
 
255
                if merge_params.file_id == b'1':
240
256
                    return ('conflicted',
241
 
                        ['text-with-conflict-markers-from-hook'])
 
257
                        [b'text-with-conflict-markers-from-hook'])
242
258
                return 'not_applicable', None
243
259
        def hook_conflict_factory(merger):
244
260
            return HookConflict(merger)
250
266
        class HookDelete(_mod_merge.AbstractPerFileMerger):
251
267
            def merge_contents(self, merge_params):
252
268
                test.hook_log.append(('delete',))
253
 
                if merge_params.file_id == '1':
 
269
                if merge_params.file_id == b'1':
254
270
                    return 'delete', None
255
271
                return 'not_applicable', None
256
272
        def hook_delete_factory(merger):
284
300
        return builder
285
301
 
286
302
    def create_file_needing_contents_merge(self, builder, file_id):
287
 
        builder.add_file(file_id, builder.tree_root, "name1", "text1", True)
288
 
        builder.change_contents(file_id, other="text4", this="text3")
 
303
        builder.add_file(file_id, builder.tree_root, "name1", b"text1", True)
 
304
        builder.change_contents(file_id, other=b"text4", this=b"text3")
289
305
 
290
306
    def test_change_vs_change(self):
291
307
        """Hook is used for (changed, changed)"""
292
308
        self.install_hook_success()
293
309
        builder = self.make_merge_builder()
294
 
        builder.add_file("1", builder.tree_root, "name1", "text1", True)
295
 
        builder.change_contents("1", other="text4", this="text3")
 
310
        builder.add_file(b"1", builder.tree_root, "name1", b"text1", True)
 
311
        builder.change_contents(b"1", other=b"text4", this=b"text3")
296
312
        conflicts = builder.merge(self.merge_type)
297
313
        self.assertEqual(conflicts, [])
298
 
        self.assertEqual(
299
 
            builder.this.get_file('1').read(), 'text-merged-by-hook')
 
314
        with builder.this.get_file('name1') as f:
 
315
            self.assertEqual(f.read(), b'text-merged-by-hook')
300
316
 
301
317
    def test_change_vs_deleted(self):
302
318
        """Hook is used for (changed, deleted)"""
303
319
        self.install_hook_success()
304
320
        builder = self.make_merge_builder()
305
 
        builder.add_file("1", builder.tree_root, "name1", "text1", True)
306
 
        builder.change_contents("1", this="text2")
307
 
        builder.remove_file("1", other=True)
 
321
        builder.add_file(b"1", builder.tree_root, "name1", b"text1", True)
 
322
        builder.change_contents(b"1", this=b"text2")
 
323
        builder.remove_file(b"1", other=True)
308
324
        conflicts = builder.merge(self.merge_type)
309
325
        self.assertEqual(conflicts, [])
310
 
        self.assertEqual(
311
 
            builder.this.get_file('1').read(), 'text-merged-by-hook')
 
326
        with builder.this.get_file('name1') as f:
 
327
            self.assertEqual(f.read(), b'text-merged-by-hook')
312
328
 
313
329
    def test_result_can_be_delete(self):
314
330
        """A hook's result can be the deletion of a file."""
315
331
        self.install_hook_delete()
316
332
        builder = self.make_merge_builder()
317
 
        self.create_file_needing_contents_merge(builder, "1")
 
333
        self.create_file_needing_contents_merge(builder, b"1")
318
334
        conflicts = builder.merge(self.merge_type)
319
335
        self.assertEqual(conflicts, [])
320
 
        self.assertRaises(errors.NoSuchId, builder.this.id2path, '1')
 
336
        self.assertRaises(errors.NoSuchId, builder.this.id2path, b'1')
321
337
        self.assertEqual([], list(builder.this.list_files()))
322
338
 
323
339
    def test_result_can_be_conflict(self):
324
340
        """A hook's result can be a conflict."""
325
341
        self.install_hook_conflict()
326
342
        builder = self.make_merge_builder()
327
 
        self.create_file_needing_contents_merge(builder, "1")
 
343
        self.create_file_needing_contents_merge(builder, b"1")
328
344
        conflicts = builder.merge(self.merge_type)
329
 
        self.assertEqual(conflicts, [TextConflict('name1', file_id='1')])
 
345
        self.assertEqual(conflicts, [TextConflict('name1', file_id=b'1')])
330
346
        # The hook still gets to set the file contents in this case, so that it
331
347
        # can insert custom conflict markers.
332
 
        self.assertEqual(
333
 
            builder.this.get_file('1').read(),
334
 
            'text-with-conflict-markers-from-hook')
 
348
        with builder.this.get_file('name1') as f:
 
349
            self.assertEqual(f.read(), b'text-with-conflict-markers-from-hook')
335
350
 
336
351
    def test_can_access_this_other_and_base_versions(self):
337
352
        """The hook function can call params.merger.get_lines to access the
339
354
        """
340
355
        self.install_hook_log_lines()
341
356
        builder = self.make_merge_builder()
342
 
        builder.add_file("1", builder.tree_root, "name1", "text1", True)
343
 
        builder.change_contents("1", this="text2", other="text3")
 
357
        builder.add_file(b"1", builder.tree_root, "name1", b"text1", True)
 
358
        builder.change_contents(b"1", this=b"text2", other=b"text3")
344
359
        conflicts = builder.merge(self.merge_type)
345
360
        self.assertEqual(
346
 
            [('log_lines', ['text2'], ['text3'], ['text1'])], self.hook_log)
 
361
            [('log_lines', [b'text2'], [b'text3'], [b'text1'])], self.hook_log)
347
362
 
348
363
    def test_chain_when_not_active(self):
349
364
        """When a hook function returns None, merging still works."""
350
365
        self.install_hook_inactive()
351
366
        self.install_hook_success()
352
367
        builder = self.make_merge_builder()
353
 
        self.create_file_needing_contents_merge(builder, "1")
 
368
        self.create_file_needing_contents_merge(builder, b"1")
354
369
        conflicts = builder.merge(self.merge_type)
355
370
        self.assertEqual(conflicts, [])
356
 
        self.assertEqual(
357
 
            builder.this.get_file('1').read(), 'text-merged-by-hook')
 
371
        with builder.this.get_file('name1') as f:
 
372
            self.assertEqual(f.read(), b'text-merged-by-hook')
358
373
        self.assertEqual([('inactive',), ('success',)], self.hook_log)
359
374
 
360
375
    def test_chain_when_not_applicable(self):
364
379
        self.install_hook_noop()
365
380
        self.install_hook_success()
366
381
        builder = self.make_merge_builder()
367
 
        self.create_file_needing_contents_merge(builder, "1")
 
382
        self.create_file_needing_contents_merge(builder, b"1")
368
383
        conflicts = builder.merge(self.merge_type)
369
384
        self.assertEqual(conflicts, [])
370
 
        self.assertEqual(
371
 
            builder.this.get_file('1').read(), 'text-merged-by-hook')
 
385
        with builder.this.get_file('name1') as f:
 
386
            self.assertEqual(f.read(), b'text-merged-by-hook')
372
387
        self.assertEqual([('no-op',), ('success',)], self.hook_log)
373
388
 
374
389
    def test_chain_stops_after_success(self):
377
392
        self.install_hook_success()
378
393
        self.install_hook_noop()
379
394
        builder = self.make_merge_builder()
380
 
        self.create_file_needing_contents_merge(builder, "1")
 
395
        self.create_file_needing_contents_merge(builder, b"1")
381
396
        conflicts = builder.merge(self.merge_type)
382
397
        self.assertEqual([('success',)], self.hook_log)
383
398
 
387
402
        self.install_hook_conflict()
388
403
        self.install_hook_noop()
389
404
        builder = self.make_merge_builder()
390
 
        self.create_file_needing_contents_merge(builder, "1")
 
405
        self.create_file_needing_contents_merge(builder, b"1")
391
406
        conflicts = builder.merge(self.merge_type)
392
407
        self.assertEqual([('conflict',)], self.hook_log)
393
408
 
397
412
        self.install_hook_delete()
398
413
        self.install_hook_noop()
399
414
        builder = self.make_merge_builder()
400
 
        self.create_file_needing_contents_merge(builder, "1")
 
415
        self.create_file_needing_contents_merge(builder, b"1")
401
416
        conflicts = builder.merge(self.merge_type)
402
417
        self.assertEqual([('delete',)], self.hook_log)
403
418