/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 bzrlib/tests/blackbox/test_merge.py

  • Committer: Andrew Bennetts
  • Date: 2008-08-12 14:53:26 UTC
  • mto: This revision was merged to the branch mainline in revision 3624.
  • Revision ID: andrew.bennetts@canonical.com-20080812145326-yx693x2jc4rcovb7
Move the notes on writing tests out of HACKING into a new file, and improve
them.

Many of the testing notes in the HACKING file were in duplicated in two places
in that file!  This change removes that duplication.  It also adds new sections
on “Where should I put a new test?” and “TestCase and its subclasses”, and
others like “Test feature dependencies” have been expanded.  The whole document
has generally been edited to be a bit more coherent. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
#
17
17
# Author: Aaron Bentley <aaron.bentley@utoronto.ca>
18
18
 
21
21
 
22
22
import os
23
23
 
24
 
from bzrlib import (
25
 
    branch,
26
 
    bzrdir,
27
 
    conflicts,
28
 
    errors,
29
 
    merge_directive,
30
 
    osutils,
31
 
    tests,
32
 
    urlutils,
33
 
    workingtree,
34
 
    )
35
 
 
36
 
 
37
 
class TestMerge(tests.TestCaseWithTransport):
 
24
from bzrlib import merge_directive
 
25
from bzrlib.branch import Branch
 
26
from bzrlib.bzrdir import BzrDir
 
27
from bzrlib.conflicts import ConflictList, ContentsConflict
 
28
from bzrlib.osutils import abspath, file_kind, pathjoin
 
29
from bzrlib.tests.blackbox import ExternalBase
 
30
import bzrlib.urlutils as urlutils
 
31
from bzrlib.workingtree import WorkingTree
 
32
 
 
33
 
 
34
class TestMerge(ExternalBase):
38
35
 
39
36
    def example_branch(self, path='.'):
40
37
        tree = self.make_branch_and_tree(path)
41
38
        self.build_tree_contents([
42
 
            (osutils.pathjoin(path, 'hello'), 'foo'),
43
 
            (osutils.pathjoin(path, 'goodbye'), 'baz')])
 
39
            (pathjoin(path, 'hello'), 'foo'),
 
40
            (pathjoin(path, 'goodbye'), 'baz')])
44
41
        tree.add('hello')
45
42
        tree.commit(message='setup')
46
43
        tree.add('goodbye')
47
44
        tree.commit(message='setup')
48
45
        return tree
49
46
 
50
 
    def create_conflicting_branches(self):
51
 
        """Create two branches which have overlapping modifications.
52
 
 
53
 
        :return: (tree, other_branch) Where merging other_branch causes a file
54
 
            conflict.
55
 
        """
56
 
        builder = self.make_branch_builder('branch')
57
 
        builder.build_snapshot('rev1', None,
58
 
            [('add', ('', 'root-id', 'directory', None)),
59
 
             ('add', ('fname', 'f-id', 'file', 'a\nb\nc\n'))])
60
 
        builder.build_snapshot('rev2other', ['rev1'],
61
 
            [('modify', ('f-id', 'a\nB\nD\n'))])
62
 
        other = builder.get_branch().bzrdir.sprout('other').open_branch()
63
 
        builder.build_snapshot('rev2this', ['rev1'],
64
 
            [('modify', ('f-id', 'a\nB\nC\n'))])
65
 
        tree = builder.get_branch().create_checkout('tree', lightweight=True)
66
 
        return tree, other
67
 
 
68
47
    def test_merge_reprocess(self):
69
 
        d = bzrdir.BzrDir.create_standalone_workingtree('.')
 
48
        d = BzrDir.create_standalone_workingtree('.')
70
49
        d.commit('h')
71
50
        self.run_bzr('merge . --reprocess --merge-type weave')
72
51
 
73
52
    def test_merge(self):
 
53
        from bzrlib.branch import Branch
 
54
 
74
55
        a_tree = self.example_branch('a')
75
56
        ancestor = a_tree.branch.revno()
76
57
        b_tree = a_tree.bzrdir.sprout('b').open_workingtree()
81
62
        # We can't merge when there are in-tree changes
82
63
        os.chdir('a')
83
64
        self.run_bzr('merge ../b', retcode=3)
84
 
        a = workingtree.WorkingTree.open('.')
 
65
        a = WorkingTree.open('.')
85
66
        a_tip = a.commit("Like an epidemic of u's")
86
67
        self.run_bzr('merge ../b -r last:1..last:1 --merge-type blooof',
87
68
                    retcode=3)
100
81
        self.run_bzr('merge ../b -r last:1')
101
82
        self.check_file_contents('goodbye', 'quux')
102
83
        # Merging a branch pulls its revision into the tree
103
 
        b = branch.Branch.open('../b')
 
84
        b = Branch.open('../b')
104
85
        b_tip = b.last_revision()
105
86
        self.failUnless(a.branch.repository.has_revision(b_tip))
106
87
        self.assertEqual([a_tip, b_tip], a.get_parent_ids())
109
90
        self.assertTrue("Not a branch" in err)
110
91
        self.run_bzr('merge -r revno:%d:./..revno:%d:../b'
111
92
                    %(ancestor,b.revno()))
112
 
        self.assertEquals(a.get_parent_ids(),
 
93
        self.assertEquals(a.get_parent_ids(), 
113
94
                          [a.branch.last_revision(), b.last_revision()])
114
95
        self.check_file_contents('goodbye', 'quux')
115
96
        a_tree.revert(backups=False)
120
101
        self.run_bzr('merge ../b -r last:1')
121
102
        self.assertEqual([a_tip], a.get_parent_ids())
122
103
 
123
 
    def test_merge_defaults_to_reprocess(self):
124
 
        tree, other = self.create_conflicting_branches()
125
 
        # The default merge algorithm should enable 'reprocess' because
126
 
        # 'show-base' is not set
127
 
        self.run_bzr('merge ../other', working_dir='tree',
128
 
                     retcode=1)
129
 
        self.assertEqualDiff('a\n'
130
 
                             'B\n'
131
 
                             '<<<<<<< TREE\n'
132
 
                             'C\n'
133
 
                             '=======\n'
134
 
                             'D\n'
135
 
                             '>>>>>>> MERGE-SOURCE\n',
136
 
                             tree.get_file_text('f-id'))
137
 
 
138
 
    def test_merge_explicit_reprocess_show_base(self):
139
 
        tree, other = self.create_conflicting_branches()
140
 
        # Explicitly setting --reprocess, and --show-base is an error
141
 
        self.run_bzr_error(['Cannot do conflict reduction and show base'],
142
 
                           'merge ../other --reprocess --show-base',
143
 
                           working_dir='tree')
144
 
 
145
 
    def test_merge_override_reprocess(self):
146
 
        tree, other = self.create_conflicting_branches()
147
 
        # Explicitly disable reprocess
148
 
        self.run_bzr('merge ../other --no-reprocess', working_dir='tree',
149
 
                     retcode=1)
150
 
        self.assertEqualDiff('a\n'
151
 
                             '<<<<<<< TREE\n'
152
 
                             'B\n'
153
 
                             'C\n'
154
 
                             '=======\n'
155
 
                             'B\n'
156
 
                             'D\n'
157
 
                             '>>>>>>> MERGE-SOURCE\n',
158
 
                             tree.get_file_text('f-id'))
159
 
 
160
 
    def test_merge_override_show_base(self):
161
 
        tree, other = self.create_conflicting_branches()
162
 
        # Setting '--show-base' will auto-disable '--reprocess'
163
 
        self.run_bzr('merge ../other --show-base', working_dir='tree',
164
 
                     retcode=1)
165
 
        self.assertEqualDiff('a\n'
166
 
                             '<<<<<<< TREE\n'
167
 
                             'B\n'
168
 
                             'C\n'
169
 
                             '||||||| BASE-REVISION\n'
170
 
                             'b\n'
171
 
                             'c\n'
172
 
                             '=======\n'
173
 
                             'B\n'
174
 
                             'D\n'
175
 
                             '>>>>>>> MERGE-SOURCE\n',
176
 
                             tree.get_file_text('f-id'))
177
 
 
178
104
    def test_merge_with_missing_file(self):
179
105
        """Merge handles missing file conflicts"""
180
106
        self.build_tree_contents([
211
137
        self.failUnlessExists('sub/a.txt.OTHER')
212
138
        self.failUnlessExists('sub/a.txt.BASE')
213
139
 
214
 
    def test_conflict_leaves_base_this_other_files(self):
215
 
        tree, other = self.create_conflicting_branches()
216
 
        self.run_bzr('merge ../other', working_dir='tree',
217
 
                     retcode=1)
218
 
        self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
219
 
        self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
220
 
        self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
221
 
 
222
 
    def test_weave_conflict_leaves_base_this_other_files(self):
223
 
        tree, other = self.create_conflicting_branches()
224
 
        self.run_bzr('merge ../other --weave', working_dir='tree',
225
 
                     retcode=1)
226
 
        self.assertFileEqual('a\nb\nc\n', 'tree/fname.BASE')
227
 
        self.assertFileEqual('a\nB\nD\n', 'tree/fname.OTHER')
228
 
        self.assertFileEqual('a\nB\nC\n', 'tree/fname.THIS')
229
 
 
230
140
    def test_merge_remember(self):
231
141
        """Merge changes from one branch to another, test submit location."""
232
142
        tree_a = self.make_branch_and_tree('branch_a')
253
163
        out = self.run_bzr('merge', retcode=3)
254
164
        self.assertEquals(out,
255
165
                ('','bzr: ERROR: No location specified or remembered\n'))
256
 
 
257
 
        # test uncommitted changes
 
166
        # test implicit --remember when no parent set, this merge conflicts
258
167
        self.build_tree(['d'])
259
168
        tree_b.add('d')
260
169
        self.run_bzr_error(['Working tree ".*" has uncommitted changes'],
261
 
                           'merge')
262
 
 
263
 
        # merge should now pass and implicitly remember merge location
 
170
                           'merge ../branch_a')
 
171
        self.assertEquals(abspath(branch_b.get_submit_branch()),
 
172
                          abspath(parent))
 
173
        # test implicit --remember after resolving conflict
264
174
        tree_b.commit('commit d')
265
 
        out, err = self.run_bzr('merge ../branch_a')
266
 
 
 
175
        out, err = self.run_bzr('merge')
 
176
        
267
177
        base = urlutils.local_path_from_url(branch_a.base)
 
178
        self.assertStartsWith(err,
 
179
                          'Merging from remembered location %s\n' % (base,))
268
180
        self.assertEndsWith(err, '+N  b\nAll changes applied successfully.\n')
269
 
        self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
270
 
                          osutils.abspath(parent))
271
 
        # test implicit --remember when committing new file
272
 
        self.build_tree(['e'])
273
 
        tree_b.add('e')
274
 
        tree_b.commit('commit e')
275
 
        out, err = self.run_bzr('merge')
276
 
        self.assertStartsWith(err,
277
 
                          'Merging from remembered submit location %s\n' % (base,))
 
181
        self.assertEquals(abspath(branch_b.get_submit_branch()),
 
182
                          abspath(parent))
278
183
        # re-open tree as external run_bzr modified it
279
184
        tree_b = branch_b.bzrdir.open_workingtree()
280
185
        tree_b.commit('merge branch_a')
282
187
        out, err = self.run_bzr('merge ../branch_c --remember')
283
188
        self.assertEquals(out, '')
284
189
        self.assertEquals(err, '+N  c\nAll changes applied successfully.\n')
285
 
        self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
286
 
                          osutils.abspath(branch_c.bzrdir.root_transport.base))
 
190
        self.assertEquals(abspath(branch_b.get_submit_branch()),
 
191
                          abspath(branch_c.bzrdir.root_transport.base))
287
192
        # re-open tree as external run_bzr modified it
288
193
        tree_b = branch_b.bzrdir.open_workingtree()
289
194
        tree_b.commit('merge branch_c')
311
216
                                              tree_b.get_parent_ids()[0])
312
217
        self.assertEqualDiff(testament_a.as_text(),
313
218
                         testament_b.as_text())
314
 
        tree_a.set_conflicts(conflicts.ConflictList())
 
219
        tree_a.set_conflicts(ConflictList())
315
220
        tree_a.commit('message')
316
221
        # it is legal to attempt to merge an already-merged bundle
317
222
        output = self.run_bzr('merge ../bundle')[1]
366
271
        os.chdir('a')
367
272
        (out, err) = self.run_bzr('merge --pull ../b')
368
273
        self.assertContainsRe(out, 'Now on revision 2\\.')
369
 
        tree_a = workingtree.WorkingTree.open('.')
 
274
        tree_a = WorkingTree.open('.')
370
275
        self.assertEqual([self.id2], tree_a.get_parent_ids())
371
276
 
372
277
    def test_merge_kind_change(self):
380
285
        tree_a.commit('changed file to directory')
381
286
        os.chdir('tree_b')
382
287
        self.run_bzr('merge ../tree_a')
383
 
        self.assertEqual('directory', osutils.file_kind('file'))
 
288
        self.assertEqual('directory', file_kind('file'))
384
289
        tree_b.revert()
385
 
        self.assertEqual('file', osutils.file_kind('file'))
 
290
        self.assertEqual('file', file_kind('file'))
386
291
        self.build_tree_contents([('file', 'content_2')])
387
292
        tree_b.commit('content change')
388
293
        self.run_bzr('merge ../tree_a', retcode=1)
389
294
        self.assertEqual(tree_b.conflicts(),
390
 
                         [conflicts.ContentsConflict('file',
391
 
                                                     file_id='file-id')])
 
295
                         [ContentsConflict('file', file_id='file-id')])
392
296
 
393
297
    def test_directive_cherrypick(self):
394
298
        source = self.make_branch_and_tree('source')
395
 
        source.commit("nothing")
396
 
        # see https://bugs.edge.launchpad.net/bzr/+bug/409688 - trying to
397
 
        # cherrypick from one branch into another unrelated branch with a
398
 
        # different root id will give shape conflicts.  as a workaround we
399
 
        # make sure they share the same root id.
400
 
        target = source.bzrdir.sprout('target').open_workingtree()
401
299
        self.build_tree(['source/a'])
402
300
        source.add('a')
403
301
        source.commit('Added a', rev_id='rev1')
404
302
        self.build_tree(['source/b'])
405
303
        source.add('b')
406
304
        source.commit('Added b', rev_id='rev2')
 
305
        target = self.make_branch_and_tree('target')
407
306
        target.commit('empty commit')
408
307
        self.write_directive('directive', source.branch, 'target', 'rev2',
409
308
                             'rev1')
466
365
 
467
366
    def assertDirectoryContent(self, directory, entries, message=''):
468
367
        """Assert whether entries (file or directories) exist in a directory.
469
 
 
 
368
        
470
369
        It also checks that there are no extra entries.
471
370
        """
472
371
        ondisk = os.listdir(directory)
519
418
        tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
520
419
        tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
521
420
        out, err = self.run_bzr(['merge', '-d', 'c'])
522
 
        self.assertContainsRe(err, 'Merging from remembered parent location .*a\/')
 
421
        self.assertContainsRe(err, 'Merging from remembered location .*a\/')
523
422
        tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
524
423
        out, err = self.run_bzr(['merge', '-d', 'c'])
525
 
        self.assertContainsRe(err, 'Merging from remembered submit location .*b\/')
 
424
        self.assertContainsRe(err, 'Merging from remembered location .*b\/')
526
425
 
527
426
    def test_remember_sets_submit(self):
528
427
        tree_a = self.make_branch_and_tree('a')
566
465
        tree_a.merge_from_branch(tree_b.branch)
567
466
        self.build_tree_contents([('a/file',
568
467
                                   'base-contents\nthis-contents\n')])
569
 
        tree_a.set_conflicts(conflicts.ConflictList())
 
468
        tree_a.set_conflicts(ConflictList())
570
469
        tree_b.merge_from_branch(tree_a.branch)
571
470
        self.build_tree_contents([('b/file',
572
471
                                   'base-contents\nother-contents\n')])
573
 
        tree_b.set_conflicts(conflicts.ConflictList())
 
472
        tree_b.set_conflicts(ConflictList())
574
473
        tree_a.commit('', rev_id='rev3a')
575
474
        tree_b.commit('', rev_id='rev3b')
576
475
        out, err = self.run_bzr(['merge', '-d', 'a', 'b', '--lca'], retcode=1)
593
492
        self.addCleanup(this_tree.unlock)
594
493
        self.assertEqual([],
595
494
                         list(this_tree.iter_changes(this_tree.basis_tree())))
596
 
 
597
 
    def test_merge_missing_second_revision_spec(self):
598
 
        """Merge uses branch basis when the second revision is unspecified."""
599
 
        this = self.make_branch_and_tree('this')
600
 
        this.commit('rev1')
601
 
        other = self.make_branch_and_tree('other')
602
 
        self.build_tree(['other/other_file'])
603
 
        other.add('other_file')
604
 
        other.commit('rev1b')
605
 
        self.run_bzr('merge -d this other -r0..')
606
 
        self.failUnlessExists('this/other_file')
607
 
 
608
 
    def test_merge_interactive_unlocks_branch(self):
609
 
        this = self.make_branch_and_tree('this')
610
 
        other = self.make_branch_and_tree('other')
611
 
        other.commit('empty commit')
612
 
        self.run_bzr('merge -i -d this other')
613
 
        this.lock_write()
614
 
        this.unlock()
615
 
 
616
 
    def test_merge_reversed_revision_range(self):
617
 
        tree = self.make_branch_and_tree(".")
618
 
        for f in ("a", "b"):
619
 
            self.build_tree([f])
620
 
            tree.add(f)
621
 
            tree.commit("added "+f)
622
 
        for context in (".", "", "a"):
623
 
            self.run_bzr("merge -r 1..0 " + context)
624
 
            self.failIfExists("a")
625
 
            tree.revert()
626
 
            self.failUnlessExists("a")
627
 
 
628
 
 
629
 
class TestMergeForce(tests.TestCaseWithTransport):
630
 
 
631
 
    def setUp(self):
632
 
        super(TestMergeForce, self).setUp()
633
 
        self.tree_a = self.make_branch_and_tree('a')
634
 
        self.build_tree(['a/foo'])
635
 
        self.tree_a.add(['foo'])
636
 
        self.tree_a.commit('add file')
637
 
        self.tree_b = self.tree_a.bzrdir.sprout('b').open_workingtree()
638
 
        self.build_tree_contents([('a/foo', 'change 1')])
639
 
        self.tree_a.commit('change file')
640
 
        self.tree_b.merge_from_branch(self.tree_a.branch)
641
 
 
642
 
    def test_merge_force(self):
643
 
        self.tree_a.commit('empty change to allow merge to run')
644
 
        # Second merge on top of the uncommitted one
645
 
        self.run_bzr(['merge', '../a', '--force'], working_dir='b')
646
 
 
647
 
 
648
 
    def test_merge_with_uncommitted_changes(self):
649
 
        self.run_bzr_error(['Working tree .* has uncommitted changes'],
650
 
                           ['merge', '../a'], working_dir='b')
651
 
 
652
 
    def test_merge_with_pending_merges(self):
653
 
        # Revert the changes keeping the pending merge
654
 
        self.run_bzr(['revert', 'b'])
655
 
        self.run_bzr_error(['Working tree .* has uncommitted changes'],
656
 
                           ['merge', '../a'], working_dir='b')