~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_interbranch/test_pull.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-05-06 17:31:37 UTC
  • mfrom: (4000.5.23 interbranch-pull)
  • Revision ID: pqm@pqm.ubuntu.com-20090506173137-di6mfn1f3od2khp3
(Jelmer) Add InterBranch.pull.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005, 2007, 2009 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
"""Tests for InterBranch.pull behaviour."""
 
18
 
 
19
import os
 
20
 
 
21
from bzrlib.branch import Branch
 
22
from bzrlib.bzrdir import BzrDir
 
23
from bzrlib import errors
 
24
from bzrlib.memorytree import MemoryTree
 
25
from bzrlib.revision import NULL_REVISION
 
26
from bzrlib.tests.per_interbranch import TestCaseWithInterBranch
 
27
 
 
28
 
 
29
# The tests here are based on the tests in 
 
30
# bzrlib.tests.branch_implementations.test_pull
 
31
 
 
32
 
 
33
class TestPull(TestCaseWithInterBranch):
 
34
 
 
35
    def test_pull_convergence_simple(self):
 
36
        # when revisions are pulled, the left-most accessible parents must
 
37
        # become the revision-history.
 
38
        parent = self.make_from_branch_and_tree('parent')
 
39
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
 
40
        mine = self.sprout_to(parent.bzrdir, 'mine').open_workingtree()
 
41
        mine.commit('my change', rev_id='M1', allow_pointless=True)
 
42
        parent.merge_from_branch(mine.branch)
 
43
        parent.commit('merge my change', rev_id='P2')
 
44
        mine.pull(parent.branch)
 
45
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
 
46
 
 
47
    def test_pull_merged_indirect(self):
 
48
        # it should be possible to do a pull from one branch into another
 
49
        # when the tip of the target was merged into the source branch
 
50
        # via a third branch - so its buried in the ancestry and is not
 
51
        # directly accessible.
 
52
        parent = self.make_from_branch_and_tree('parent')
 
53
        parent.commit('1st post', rev_id='P1', allow_pointless=True)
 
54
        mine = self.sprout_to(parent.bzrdir, 'mine').open_workingtree()
 
55
        mine.commit('my change', rev_id='M1', allow_pointless=True)
 
56
        other = self.sprout_to(parent.bzrdir, 'other').open_workingtree()
 
57
        other.merge_from_branch(mine.branch)
 
58
        other.commit('merge my change', rev_id='O2')
 
59
        parent.merge_from_branch(other.branch)
 
60
        parent.commit('merge other', rev_id='P2')
 
61
        mine.pull(parent.branch)
 
62
        self.assertEqual(['P1', 'P2'], mine.branch.revision_history())
 
63
 
 
64
    def test_pull_updates_checkout_and_master(self):
 
65
        """Pulling into a checkout updates the checkout and the master branch"""
 
66
        master_tree = self.make_from_branch_and_tree('master')
 
67
        rev1 = master_tree.commit('master')
 
68
        checkout = master_tree.branch.create_checkout('checkout')
 
69
        other = self.sprout_to(master_tree.branch.bzrdir, 'other').open_workingtree()
 
70
        rev2 = other.commit('other commit')
 
71
        # now pull, which should update both checkout and master.
 
72
        checkout.branch.pull(other.branch)
 
73
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
 
74
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
 
75
 
 
76
    def test_pull_raises_specific_error_on_master_connection_error(self):
 
77
        master_tree = self.make_from_branch_and_tree('master')
 
78
        checkout = master_tree.branch.create_checkout('checkout')
 
79
        other = self.sprout_to(master_tree.branch.bzrdir, 'other').open_workingtree()
 
80
        # move the branch out of the way on disk to cause a connection
 
81
        # error.
 
82
        os.rename('master', 'master_gone')
 
83
        # try to pull, which should raise a BoundBranchConnectionFailure.
 
84
        self.assertRaises(errors.BoundBranchConnectionFailure,
 
85
                checkout.branch.pull, other.branch)
 
86
 
 
87
    def test_pull_returns_result(self):
 
88
        parent = self.make_from_branch_and_tree('parent')
 
89
        parent.commit('1st post', rev_id='P1')
 
90
        mine = self.sprout_to(parent.bzrdir, 'mine').open_workingtree()
 
91
        mine.commit('my change', rev_id='M1')
 
92
        result = parent.branch.pull(mine.branch)
 
93
        self.assertIsNot(None, result)
 
94
        self.assertIs(mine.branch, result.source_branch)
 
95
        self.assertIs(parent.branch, result.target_branch)
 
96
        self.assertIs(parent.branch, result.master_branch)
 
97
        self.assertIs(None, result.local_branch)
 
98
        self.assertEqual(1, result.old_revno)
 
99
        self.assertEqual('P1', result.old_revid)
 
100
        self.assertEqual(2, result.new_revno)
 
101
        self.assertEqual('M1', result.new_revid)
 
102
        self.assertEqual(None, result.tag_conflicts)
 
103
 
 
104
    def test_pull_overwrite(self):
 
105
        tree_a = self.make_from_branch_and_tree('tree_a')
 
106
        tree_a.commit('message 1')
 
107
        tree_b = self.sprout_to(tree_a.bzrdir, 'tree_b').open_workingtree()
 
108
        tree_a.commit('message 2', rev_id='rev2a')
 
109
        tree_b.commit('message 2', rev_id='rev2b')
 
110
        self.assertRaises(errors.DivergedBranches, tree_a.pull, tree_b.branch)
 
111
        self.assertRaises(errors.DivergedBranches,
 
112
                          tree_a.branch.pull, tree_b.branch,
 
113
                          overwrite=False, stop_revision='rev2b')
 
114
        # It should not have updated the branch tip, but it should have fetched
 
115
        # the revision
 
116
        self.assertEqual('rev2a', tree_a.branch.last_revision())
 
117
        self.assertTrue(tree_a.branch.repository.has_revision('rev2b'))
 
118
        tree_a.branch.pull(tree_b.branch, overwrite=True,
 
119
                           stop_revision='rev2b')
 
120
        self.assertEqual('rev2b', tree_a.branch.last_revision())
 
121
        self.assertEqual(tree_b.branch.revision_history(),
 
122
                         tree_a.branch.revision_history())
 
123
 
 
124
 
 
125
class TestPullHook(TestCaseWithInterBranch):
 
126
 
 
127
    def setUp(self):
 
128
        self.hook_calls = []
 
129
        TestCaseWithInterBranch.setUp(self)
 
130
 
 
131
    def capture_post_pull_hook(self, result):
 
132
        """Capture post pull hook calls to self.hook_calls.
 
133
 
 
134
        The call is logged, as is some state of the two branches.
 
135
        """
 
136
        if result.local_branch:
 
137
            local_locked = result.local_branch.is_locked()
 
138
            local_base = result.local_branch.base
 
139
        else:
 
140
            local_locked = None
 
141
            local_base = None
 
142
        self.hook_calls.append(
 
143
            ('post_pull', result.source_branch, local_base,
 
144
             result.master_branch.base, result.old_revno,
 
145
             result.old_revid,
 
146
             result.new_revno, result.new_revid,
 
147
             result.source_branch.is_locked(), local_locked,
 
148
             result.master_branch.is_locked()))
 
149
 
 
150
    def test_post_pull_empty_history(self):
 
151
        target = self.make_to_branch('target')
 
152
        source = self.make_from_branch('source')
 
153
        Branch.hooks.install_named_hook('post_pull',
 
154
            self.capture_post_pull_hook, None)
 
155
        target.pull(source)
 
156
        # with nothing there we should still get a notification, and
 
157
        # have both branches locked at the notification time.
 
158
        self.assertEqual([
 
159
            ('post_pull', source, None, target.base, 0, NULL_REVISION,
 
160
             0, NULL_REVISION, True, None, True)
 
161
            ],
 
162
            self.hook_calls)
 
163
 
 
164
    def test_post_pull_bound_branch(self):
 
165
        # pulling to a bound branch should pass in the master branch to the
 
166
        # hook, allowing the correct number of emails to be sent, while still
 
167
        # allowing hooks that want to modify the target to do so to both
 
168
        # instances.
 
169
        target = self.make_to_branch('target')
 
170
        local = self.make_from_branch('local')
 
171
        try:
 
172
            local.bind(target)
 
173
        except errors.UpgradeRequired:
 
174
            # We can't bind this format to itself- typically it is the local
 
175
            # branch that doesn't support binding.  As of May 2007
 
176
            # remotebranches can't be bound.  Let's instead make a new local
 
177
            # branch of the default type, which does allow binding.
 
178
            # See https://bugs.launchpad.net/bzr/+bug/112020
 
179
            local = BzrDir.create_branch_convenience('local2')
 
180
            local.bind(target)
 
181
        source = self.make_from_branch('source')
 
182
        Branch.hooks.install_named_hook('post_pull',
 
183
            self.capture_post_pull_hook, None)
 
184
        local.pull(source)
 
185
        # with nothing there we should still get a notification, and
 
186
        # have both branches locked at the notification time.
 
187
        self.assertEqual([
 
188
            ('post_pull', source, local.base, target.base, 0, NULL_REVISION,
 
189
             0, NULL_REVISION, True, True, True)
 
190
            ],
 
191
            self.hook_calls)
 
192
 
 
193
    def test_post_pull_nonempty_history(self):
 
194
        target = self.make_to_branch_and_memory_tree('target')
 
195
        target.lock_write()
 
196
        target.add('')
 
197
        rev1 = target.commit('rev 1')
 
198
        target.unlock()
 
199
        sourcedir = target.bzrdir.clone(self.get_url('source'))
 
200
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
 
201
        rev2 = source.commit('rev 2')
 
202
        Branch.hooks.install_named_hook('post_pull',
 
203
            self.capture_post_pull_hook, None)
 
204
        target.branch.pull(source.branch)
 
205
        # with nothing there we should still get a notification, and
 
206
        # have both branches locked at the notification time.
 
207
        self.assertEqual([
 
208
            ('post_pull', source.branch, None, target.branch.base, 1, rev1,
 
209
             2, rev2, True, None, True)
 
210
            ],
 
211
            self.hook_calls)