/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
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
19
from cStringIO import StringIO
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
20
import os
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
21
 
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
22
from bzrlib import (
23
    branch,
24
    builtins,
25
    bzrdir,
26
    debug,
27
    errors,
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
28
    push,
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
29
    tests,
30
    )
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
31
from bzrlib.branch import Branch
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
32
from bzrlib.bzrdir import BzrDir
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
33
from bzrlib.memorytree import MemoryTree
34
from bzrlib.revision import NULL_REVISION
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
35
from bzrlib.smart import client, server
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
36
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
37
from bzrlib.transport import get_transport
2018.5.130 by Robert Collins
Make all branch_implementations tests pass.
38
from bzrlib.transport.local import LocalURLServer
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
39
40
41
class TestPush(TestCaseWithBranch):
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
42
43
    def test_push_convergence_simple(self):
44
        # when revisions are pushed, the left-most accessible parents must 
45
        # become the revision-history.
46
        mine = self.make_branch_and_tree('mine')
47
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
48
        other = mine.bzrdir.sprout('other').open_workingtree()
49
        other.commit('my change', rev_id='M1', allow_pointless=True)
50
        mine.merge_from_branch(other.branch)
51
        mine.commit('merge my change', rev_id='P2')
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
52
        result = mine.branch.push(other.branch)
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
53
        self.assertEqual(['P1', 'P2'], other.branch.revision_history())
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
54
        # result object contains some structured data
55
        self.assertEqual(result.old_revid, 'M1')
56
        self.assertEqual(result.new_revid, 'P2')
57
        # and it can be treated as an integer for compatibility
58
        self.assertEqual(int(result), 0)
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
59
60
    def test_push_merged_indirect(self):
61
        # it should be possible to do a push from one branch into another
62
        # when the tip of the target was merged into the source branch
63
        # via a third branch - so its buried in the ancestry and is not
64
        # directly accessible.
65
        mine = self.make_branch_and_tree('mine')
66
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
67
        target = mine.bzrdir.sprout('target').open_workingtree()
68
        target.commit('my change', rev_id='M1', allow_pointless=True)
69
        other = mine.bzrdir.sprout('other').open_workingtree()
70
        other.merge_from_branch(target.branch)
71
        other.commit('merge my change', rev_id='O2')
72
        mine.merge_from_branch(other.branch)
73
        mine.commit('merge other', rev_id='P2')
74
        mine.branch.push(target.branch)
75
        self.assertEqual(['P1', 'P2'], target.branch.revision_history())
76
77
    def test_push_to_checkout_updates_master(self):
78
        """Pushing into a checkout updates the checkout and the master branch"""
79
        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
80
        checkout = self.make_branch_and_tree('checkout')
81
        try:
82
            checkout.branch.bind(master_tree.branch)
83
        except errors.UpgradeRequired:
84
            # cant bind this format, the test is irrelevant.
85
            return
86
        rev1 = checkout.commit('master')
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
87
88
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
89
        rev2 = other.commit('other commit')
90
        # now push, which should update both checkout and master.
91
        other.branch.push(checkout.branch)
92
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
93
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
94
95
    def test_push_raises_specific_error_on_master_connection_error(self):
96
        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
97
        checkout = self.make_branch_and_tree('checkout')
98
        try:
99
            checkout.branch.bind(master_tree.branch)
100
        except errors.UpgradeRequired:
101
            # cant bind this format, the test is irrelevant.
102
            return
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
103
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
104
        # move the branch out of the way on disk to cause a connection
105
        # error.
106
        os.rename('master', 'master_gone')
107
        # try to push, which should raise a BoundBranchConnectionFailure.
108
        self.assertRaises(errors.BoundBranchConnectionFailure,
109
                other.branch.push, checkout.branch)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
110
2279.1.1 by John Arbash Meinel
Branch.push() only needs a read lock.
111
    def test_push_uses_read_lock(self):
112
        """Push should only need a read lock on the source side."""
113
        source = self.make_branch_and_tree('source')
114
        target = self.make_branch('target')
115
2381.1.3 by Robert Collins
Review feedback.
116
        self.build_tree(['source/a'])
2279.1.1 by John Arbash Meinel
Branch.push() only needs a read lock.
117
        source.add(['a'])
118
        source.commit('a')
119
120
        source.branch.lock_read()
121
        try:
122
            target.lock_write()
123
            try:
124
                source.branch.push(target, stop_revision=source.last_revision())
125
            finally:
126
                target.unlock()
127
        finally:
128
            source.branch.unlock()
129
2279.1.3 by John Arbash Meinel
Switch the test to being a branch_implementation test.
130
    def test_push_within_repository(self):
131
        """Push from one branch to another inside the same repository."""
132
        try:
133
            repo = self.make_repository('repo', shared=True)
134
        except (errors.IncompatibleFormat, errors.UninitializableFormat):
135
            # This Branch format cannot create shared repositories
136
            return
137
        # This is a little bit trickier because make_branch_and_tree will not
138
        # re-use a shared repository.
139
        a_bzrdir = self.make_bzrdir('repo/tree')
140
        try:
141
            a_branch = self.branch_format.initialize(a_bzrdir)
142
        except (errors.UninitializableFormat):
143
            # Cannot create these branches
144
            return
2018.5.97 by Andrew Bennetts
Fix more tests.
145
        try:
146
            tree = a_branch.bzrdir.create_workingtree()
147
        except errors.NotLocalUrl:
2018.5.130 by Robert Collins
Make all branch_implementations tests pass.
148
            if self.vfs_transport_factory is LocalURLServer:
149
                # the branch is colocated on disk, we cannot create a checkout.
150
                # hopefully callers will expect this.
151
                local_controldir= bzrdir.BzrDir.open(self.get_vfs_only_url('repo/tree'))
152
                tree = local_controldir.create_workingtree()
153
            else:
154
                tree = a_branch.create_checkout('repo/tree', lightweight=True)
2381.1.3 by Robert Collins
Review feedback.
155
        self.build_tree(['repo/tree/a'])
2279.1.3 by John Arbash Meinel
Switch the test to being a branch_implementation test.
156
        tree.add(['a'])
157
        tree.commit('a')
158
159
        to_bzrdir = self.make_bzrdir('repo/branch')
160
        to_branch = self.branch_format.initialize(to_bzrdir)
161
        tree.branch.push(to_branch)
162
163
        self.assertEqual(tree.branch.last_revision(),
164
                         to_branch.last_revision())
165
3449.1.2 by Andrew Bennetts
Add test and NEWS entry.
166
    def test_push_overwrite_of_non_tip_with_stop_revision(self):
167
        """Combining the stop_revision and overwrite options works.
168
        
169
        This was <https://bugs.launchpad.net/bzr/+bug/234229>.
170
        """
171
        source = self.make_branch_and_tree('source')
172
        target = self.make_branch('target')
173
174
        source.commit('1st commit')
175
        source.branch.push(target)
176
        source.commit('2nd commit', rev_id='rev-2')
177
        source.commit('3rd commit')
178
179
        source.branch.push(target, stop_revision='rev-2', overwrite=True)
180
        self.assertEqual('rev-2', target.last_revision())
181
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
182
    def test_push_with_default_stacking_does_not_create_broken_branch(self):
3904.3.7 by Andrew Bennetts
Comment the new tests.
183
        """Pushing a new standalone branch works even when there's a default
184
        stacking policy at the destination.
185
186
        The new branch will preserve the repo format (even if it isn't the
187
        default for the branch), and will be stacked when the repo format
188
        allows (which means that the branch format isn't necessarly preserved).
189
        """
3904.3.6 by Andrew Bennetts
Skip test for two formats, and fix format 5 by avoiding a full history sync with non-format5 branches.
190
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
191
            raise tests.TestNotApplicable('Not a metadir format.')
192
        if isinstance(self.branch_format, branch.BranchReferenceFormat):
3904.3.7 by Andrew Bennetts
Comment the new tests.
193
            # This test could in principle apply to BranchReferenceFormat, but
194
            # make_branch_builder doesn't support it.
3904.3.6 by Andrew Bennetts
Skip test for two formats, and fix format 5 by avoiding a full history sync with non-format5 branches.
195
            raise tests.TestSkipped(
196
                "BranchBuilder can't make reference branches.")
3904.3.7 by Andrew Bennetts
Comment the new tests.
197
        # Make a branch called "local" in a stackable repository
198
        # The branch has 3 revisions:
199
        #   - rev-1, adds a file
200
        #   - rev-2, no changes
201
        #   - rev-3, modifies the file.
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
202
        repo = self.make_repository('repo', shared=True, format='1.6')
203
        builder = self.make_branch_builder('repo/local')
3904.3.4 by Andrew Bennetts
First cut of a branch_implementations test. It fails.
204
        builder.start_series()
205
        builder.build_snapshot('rev-1', None, [
206
            ('add', ('', 'root-id', 'directory', '')),
207
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
208
        builder.build_snapshot('rev-2', ['rev-1'], [])
209
        builder.build_snapshot('rev-3', ['rev-2'],
210
            [('modify', ('f-id', 'new-content\n'))])
211
        builder.finish_series()
3904.3.6 by Andrew Bennetts
Skip test for two formats, and fix format 5 by avoiding a full history sync with non-format5 branches.
212
        trunk = builder.get_branch()
3904.3.7 by Andrew Bennetts
Comment the new tests.
213
        # Sprout rev-1 to "trunk", so that we can stack on it.
3904.3.6 by Andrew Bennetts
Skip test for two formats, and fix format 5 by avoiding a full history sync with non-format5 branches.
214
        trunk.bzrdir.sprout(self.get_url('trunk'), revision_id='rev-1')
3904.3.7 by Andrew Bennetts
Comment the new tests.
215
        # Set a default stacking policy so that new branches will automatically
216
        # stack on trunk.
3904.3.4 by Andrew Bennetts
First cut of a branch_implementations test. It fails.
217
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
3904.3.7 by Andrew Bennetts
Comment the new tests.
218
        # Push rev-2 to a new branch "remote".  It will be stacked on "trunk".
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
219
        output = StringIO()
3904.3.6 by Andrew Bennetts
Skip test for two formats, and fix format 5 by avoiding a full history sync with non-format5 branches.
220
        push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
3904.3.7 by Andrew Bennetts
Comment the new tests.
221
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
222
        # fulltext record for f-id @ rev-1, then this will fail.
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
223
        remote_branch = Branch.open(self.get_url('remote'))
3904.3.6 by Andrew Bennetts
Skip test for two formats, and fix format 5 by avoiding a full history sync with non-format5 branches.
224
        trunk.push(remote_branch)
225
        remote_branch.check()
3904.3.4 by Andrew Bennetts
First cut of a branch_implementations test. It fails.
226
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
227
228
class TestPushHook(TestCaseWithBranch):
229
230
    def setUp(self):
231
        self.hook_calls = []
232
        TestCaseWithBranch.setUp(self)
233
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
234
    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
235
        """Capture post push hook calls to self.hook_calls.
236
        
237
        The call is logged, as is some state of the two branches.
238
        """
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
239
        if result.local_branch:
240
            local_locked = result.local_branch.is_locked()
241
            local_base = result.local_branch.base
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
242
        else:
243
            local_locked = None
244
            local_base = None
245
        self.hook_calls.append(
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
246
            ('post_push', result.source_branch, local_base,
247
             result.master_branch.base,
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
248
             result.old_revno, result.old_revid,
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
249
             result.new_revno, result.new_revid,
250
             result.source_branch.is_locked(), local_locked,
251
             result.master_branch.is_locked()))
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
252
253
    def test_post_push_empty_history(self):
254
        target = self.make_branch('target')
255
        source = self.make_branch('source')
3256.2.14 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.branch_implementation.test_push.
256
        Branch.hooks.install_named_hook('post_push',
257
                                        self.capture_post_push_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
258
        source.push(target)
259
        # with nothing there we should still get a notification, and
260
        # have both branches locked at the notification time.
261
        self.assertEqual([
262
            ('post_push', source, None, target.base, 0, NULL_REVISION,
263
             0, NULL_REVISION, True, None, True)
264
            ],
265
            self.hook_calls)
266
267
    def test_post_push_bound_branch(self):
268
        # pushing to a bound branch should pass in the master branch to the
269
        # hook, allowing the correct number of emails to be sent, while still
270
        # allowing hooks that want to modify the target to do so to both 
271
        # instances.
272
        target = self.make_branch('target')
273
        local = self.make_branch('local')
274
        try:
275
            local.bind(target)
276
        except errors.UpgradeRequired:
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
277
            # We can't bind this format to itself- typically it is the local
278
            # branch that doesn't support binding.  As of May 2007
279
            # remotebranches can't be bound.  Let's instead make a new local
280
            # branch of the default type, which does allow binding.
281
            # See https://bugs.launchpad.net/bzr/+bug/112020
2477.1.9 by Martin Pool
Review cleanups from John, mostly docs
282
            local = BzrDir.create_branch_convenience('local2')
283
            local.bind(target)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
284
        source = self.make_branch('source')
3256.2.14 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.branch_implementation.test_push.
285
        Branch.hooks.install_named_hook('post_push',
286
                                        self.capture_post_push_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
287
        source.push(local)
288
        # with nothing there we should still get a notification, and
289
        # have both branches locked at the notification time.
290
        self.assertEqual([
291
            ('post_push', source, local.base, target.base, 0, NULL_REVISION,
292
             0, NULL_REVISION, True, True, True)
293
            ],
294
            self.hook_calls)
295
296
    def test_post_push_nonempty_history(self):
297
        target = self.make_branch_and_memory_tree('target')
298
        target.lock_write()
299
        target.add('')
300
        rev1 = target.commit('rev 1')
301
        target.unlock()
302
        sourcedir = target.bzrdir.clone(self.get_url('source'))
303
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
304
        rev2 = source.commit('rev 2')
3256.2.14 by Daniel Watkins
Updated uses of Hooks.install_hook to Hooks.install_named_hook in tests.branch_implementation.test_push.
305
        Branch.hooks.install_named_hook('post_push',
306
                                        self.capture_post_push_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
307
        source.branch.push(target.branch)
308
        # with nothing there we should still get a notification, and
309
        # have both branches locked at the notification time.
310
        self.assertEqual([
311
            ('post_push', source.branch, None, target.branch.base, 1, rev1,
312
             2, rev2, True, None, True)
313
            ],
314
            self.hook_calls)
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
315
316
317
class EmptyPushSmartEffortTests(TestCaseWithBranch):
318
    """Tests that a push of 0 revisions should make a limited number of smart
319
    protocol RPCs.
320
    """
321
322
    def setUp(self):
323
        # Skip some scenarios that don't apply to these tests.
324
        if (self.transport_server is not None and
325
            issubclass(self.transport_server, server.SmartTCPServer)):
326
            raise tests.TestNotApplicable(
327
                'Does not apply when remote backing branch is also '
328
                'a smart branch')
329
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
330
            raise tests.TestNotApplicable(
331
                'Branch format 4 is not usable via HPSS.')
332
        super(EmptyPushSmartEffortTests, self).setUp()
333
        # Create a smart server that publishes whatever the backing VFS server
334
        # does.
335
        self.smart_server = server.SmartTCPServer_for_testing()
336
        self.smart_server.setUp(self.get_server())
337
        self.addCleanup(self.smart_server.tearDown)
338
        # Make two empty branches, 'empty' and 'target'.
339
        self.empty_branch = self.make_branch('empty')
340
        self.make_branch('target')
341
        # Log all HPSS calls into self.hpss_calls.
342
        client._SmartClient.hooks.install_named_hook(
343
            'call', self.capture_hpss_call, None)
344
        self.hpss_calls = []
345
346
    def capture_hpss_call(self, params):
347
        self.hpss_calls.append(params.method)
348
349
    def test_empty_branch_api(self):
350
        """The branch_obj.push API should make a limited number of HPSS calls.
351
        """
352
        transport = get_transport(self.smart_server.get_url()).clone('target')
353
        target = Branch.open_from_transport(transport)
354
        self.empty_branch.push(target)
355
        self.assertEqual(
356
            ['BzrDir.open',
357
             'BzrDir.open_branch',
358
             'BzrDir.find_repositoryV2',
359
             'Branch.get_stacked_on_url',
360
             'Branch.lock_write',
361
             'Branch.last_revision_info',
362
             'Branch.unlock'],
363
            self.hpss_calls)
364
365
    def test_empty_branch_command(self):
366
        """The 'bzr push' command should make a limited number of HPSS calls.
367
        """
368
        cmd = builtins.cmd_push()
369
        cmd.outf = tests.StringIOWrapper()
370
        cmd.run(
371
            directory=self.get_url() + 'empty',
372
            location=self.smart_server.get_url() + 'target')
373
        # HPSS calls as of 2008/09/22:
374
        # [BzrDir.open, BzrDir.open_branch, BzrDir.find_repositoryV2,
375
        # Branch.get_stacked_on_url, get, get, Branch.lock_write,
376
        # Branch.last_revision_info, Branch.unlock]
377
        self.assertTrue(len(self.hpss_calls) <= 9, self.hpss_calls)
378
379