/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
1
# Copyright (C) 2004, 2005, 2007 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
"""Tests for branch.push behaviour."""
18
19
import os
20
21
from bzrlib.branch import Branch
22
from bzrlib import errors
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
23
from bzrlib.memorytree import MemoryTree
2018.5.97 by Andrew Bennetts
Fix more tests.
24
from bzrlib.remote import RemoteBranch
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
25
from bzrlib.revision import NULL_REVISION
26
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
27
28
29
class TestPush(TestCaseWithBranch):
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
30
31
    def test_push_convergence_simple(self):
32
        # when revisions are pushed, the left-most accessible parents must 
33
        # become the revision-history.
34
        mine = self.make_branch_and_tree('mine')
35
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
36
        other = mine.bzrdir.sprout('other').open_workingtree()
37
        other.commit('my change', rev_id='M1', allow_pointless=True)
38
        mine.merge_from_branch(other.branch)
39
        mine.commit('merge my change', rev_id='P2')
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
40
        result = mine.branch.push(other.branch)
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
41
        self.assertEqual(['P1', 'P2'], other.branch.revision_history())
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
42
        # result object contains some structured data
43
        self.assertEqual(result.old_revid, 'M1')
44
        self.assertEqual(result.new_revid, 'P2')
45
        # and it can be treated as an integer for compatibility
46
        self.assertEqual(int(result), 0)
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
47
48
    def test_push_merged_indirect(self):
49
        # it should be possible to do a push from one branch into another
50
        # when the tip of the target was merged into the source branch
51
        # via a third branch - so its buried in the ancestry and is not
52
        # directly accessible.
53
        mine = self.make_branch_and_tree('mine')
54
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
55
        target = mine.bzrdir.sprout('target').open_workingtree()
56
        target.commit('my change', rev_id='M1', allow_pointless=True)
57
        other = mine.bzrdir.sprout('other').open_workingtree()
58
        other.merge_from_branch(target.branch)
59
        other.commit('merge my change', rev_id='O2')
60
        mine.merge_from_branch(other.branch)
61
        mine.commit('merge other', rev_id='P2')
62
        mine.branch.push(target.branch)
63
        self.assertEqual(['P1', 'P2'], target.branch.revision_history())
64
65
    def test_push_to_checkout_updates_master(self):
66
        """Pushing into a checkout updates the checkout and the master branch"""
67
        master_tree = self.make_branch_and_tree('master')
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
68
        checkout = self.make_branch_and_tree('checkout')
69
        try:
70
            checkout.branch.bind(master_tree.branch)
71
        except errors.UpgradeRequired:
72
            # cant bind this format, the test is irrelevant.
73
            return
74
        rev1 = checkout.commit('master')
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
75
76
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
77
        rev2 = other.commit('other commit')
78
        # now push, which should update both checkout and master.
79
        other.branch.push(checkout.branch)
80
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
81
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
82
83
    def test_push_raises_specific_error_on_master_connection_error(self):
84
        master_tree = self.make_branch_and_tree('master')
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
85
        checkout = self.make_branch_and_tree('checkout')
86
        try:
87
            checkout.branch.bind(master_tree.branch)
88
        except errors.UpgradeRequired:
89
            # cant bind this format, the test is irrelevant.
90
            return
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
91
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
92
        # move the branch out of the way on disk to cause a connection
93
        # error.
94
        os.rename('master', 'master_gone')
95
        # try to push, which should raise a BoundBranchConnectionFailure.
96
        self.assertRaises(errors.BoundBranchConnectionFailure,
97
                other.branch.push, checkout.branch)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
98
2279.1.1 by John Arbash Meinel
Branch.push() only needs a read lock.
99
    def test_push_uses_read_lock(self):
100
        """Push should only need a read lock on the source side."""
101
        source = self.make_branch_and_tree('source')
102
        target = self.make_branch('target')
103
2018.5.97 by Andrew Bennetts
Fix more tests.
104
        self.build_tree(['a'], transport=source.bzrdir.root_transport)
2279.1.1 by John Arbash Meinel
Branch.push() only needs a read lock.
105
        source.add(['a'])
106
        source.commit('a')
107
108
        source.branch.lock_read()
109
        try:
110
            target.lock_write()
111
            try:
112
                source.branch.push(target, stop_revision=source.last_revision())
113
            finally:
114
                target.unlock()
115
        finally:
116
            source.branch.unlock()
117
2279.1.3 by John Arbash Meinel
Switch the test to being a branch_implementation test.
118
    def test_push_within_repository(self):
119
        """Push from one branch to another inside the same repository."""
120
        try:
121
            repo = self.make_repository('repo', shared=True)
122
        except (errors.IncompatibleFormat, errors.UninitializableFormat):
123
            # This Branch format cannot create shared repositories
124
            return
125
        # This is a little bit trickier because make_branch_and_tree will not
126
        # re-use a shared repository.
127
        a_bzrdir = self.make_bzrdir('repo/tree')
128
        try:
129
            a_branch = self.branch_format.initialize(a_bzrdir)
130
        except (errors.UninitializableFormat):
131
            # Cannot create these branches
132
            return
2018.5.97 by Andrew Bennetts
Fix more tests.
133
        try:
134
            tree = a_branch.bzrdir.create_workingtree()
135
        except errors.NotLocalUrl:
136
            tree = a_branch.create_checkout('checkout', lightweight=True)
137
        self.build_tree(['a'], transport=tree.bzrdir.root_transport)
2279.1.3 by John Arbash Meinel
Switch the test to being a branch_implementation test.
138
        tree.add(['a'])
139
        tree.commit('a')
140
141
        to_bzrdir = self.make_bzrdir('repo/branch')
142
        to_branch = self.branch_format.initialize(to_bzrdir)
143
        tree.branch.push(to_branch)
144
145
        self.assertEqual(tree.branch.last_revision(),
146
                         to_branch.last_revision())
147
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
148
149
class TestPushHook(TestCaseWithBranch):
150
151
    def setUp(self):
152
        self.hook_calls = []
153
        TestCaseWithBranch.setUp(self)
154
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
155
    def capture_post_push_hook(self, result):
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
156
        """Capture post push hook calls to self.hook_calls.
157
        
158
        The call is logged, as is some state of the two branches.
159
        """
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
160
        if result.local_branch:
161
            local_locked = result.local_branch.is_locked()
162
            local_base = result.local_branch.base
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
163
        else:
164
            local_locked = None
165
            local_base = None
166
        self.hook_calls.append(
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
167
            ('post_push', result.source_branch, local_base,
168
             result.master_branch.base,
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
169
             result.old_revno, result.old_revid,
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
170
             result.new_revno, result.new_revid,
171
             result.source_branch.is_locked(), local_locked,
172
             result.master_branch.is_locked()))
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
173
174
    def test_post_push_empty_history(self):
175
        target = self.make_branch('target')
176
        source = self.make_branch('source')
177
        Branch.hooks.install_hook('post_push', self.capture_post_push_hook)
178
        source.push(target)
179
        # with nothing there we should still get a notification, and
180
        # have both branches locked at the notification time.
2018.5.97 by Andrew Bennetts
Fix more tests.
181
        if isinstance(source, RemoteBranch):
182
            # XXX: at the moment, push on remote branches is just delegated to
183
            # the file-level branch object, so we adjust the expected result
184
            # accordingly.  In the future, when RemoteBranch implements push
185
            # directly, this should be unnecessary.
186
            source = source._real_branch
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
187
        self.assertEqual([
188
            ('post_push', source, None, target.base, 0, NULL_REVISION,
189
             0, NULL_REVISION, True, None, True)
190
            ],
191
            self.hook_calls)
192
193
    def test_post_push_bound_branch(self):
194
        # pushing to a bound branch should pass in the master branch to the
195
        # hook, allowing the correct number of emails to be sent, while still
196
        # allowing hooks that want to modify the target to do so to both 
197
        # instances.
198
        target = self.make_branch('target')
199
        local = self.make_branch('local')
200
        try:
201
            local.bind(target)
202
        except errors.UpgradeRequired:
203
            # cant bind this format, the test is irrelevant.
204
            return
205
        source = self.make_branch('source')
206
        Branch.hooks.install_hook('post_push', self.capture_post_push_hook)
207
        source.push(local)
208
        # with nothing there we should still get a notification, and
209
        # have both branches locked at the notification time.
210
        self.assertEqual([
211
            ('post_push', source, local.base, target.base, 0, NULL_REVISION,
212
             0, NULL_REVISION, True, True, True)
213
            ],
214
            self.hook_calls)
215
216
    def test_post_push_nonempty_history(self):
217
        target = self.make_branch_and_memory_tree('target')
218
        target.lock_write()
219
        target.add('')
220
        rev1 = target.commit('rev 1')
221
        target.unlock()
222
        sourcedir = target.bzrdir.clone(self.get_url('source'))
223
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
224
        rev2 = source.commit('rev 2')
225
        Branch.hooks.install_hook('post_push', self.capture_post_push_hook)
226
        source.branch.push(target.branch)
227
        # with nothing there we should still get a notification, and
228
        # have both branches locked at the notification time.
229
        self.assertEqual([
230
            ('post_push', source.branch, None, target.branch.base, 1, rev1,
231
             2, rev2, True, None, True)
232
            ],
233
            self.hook_calls)