~bzr-pqm/bzr/bzr.dev

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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
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
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
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,
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
29
    repository,
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
30
    tests,
31
    )
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
32
from bzrlib.branch import Branch
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
33
from bzrlib.bzrdir import BzrDir
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
34
from bzrlib.memorytree import MemoryTree
35
from bzrlib.revision import NULL_REVISION
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
36
from bzrlib.smart import client, server
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
37
from bzrlib.smart.repository import SmartServerRepositoryGetParentMap
4523.1.1 by Martin Pool
Rename tests.branch_implementations to per_branch
38
from bzrlib.tests.per_branch.test_branch import TestCaseWithBranch
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
39
from bzrlib.transport import get_transport
2018.5.130 by Robert Collins
Make all branch_implementations tests pass.
40
from bzrlib.transport.local import LocalURLServer
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
41
42
43
class TestPush(TestCaseWithBranch):
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
44
45
    def test_push_convergence_simple(self):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
46
        # when revisions are pushed, the left-most accessible parents must
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
47
        # become the revision-history.
48
        mine = self.make_branch_and_tree('mine')
49
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
50
        other = mine.bzrdir.sprout('other').open_workingtree()
51
        other.commit('my change', rev_id='M1', allow_pointless=True)
52
        mine.merge_from_branch(other.branch)
53
        mine.commit('merge my change', rev_id='P2')
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
54
        result = mine.branch.push(other.branch)
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
55
        self.assertEqual(['P1', 'P2'], other.branch.revision_history())
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
56
        # result object contains some structured data
57
        self.assertEqual(result.old_revid, 'M1')
58
        self.assertEqual(result.new_revid, 'P2')
59
        # and it can be treated as an integer for compatibility
60
        self.assertEqual(int(result), 0)
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
61
62
    def test_push_merged_indirect(self):
63
        # it should be possible to do a push from one branch into another
64
        # when the tip of the target was merged into the source branch
65
        # via a third branch - so its buried in the ancestry and is not
66
        # directly accessible.
67
        mine = self.make_branch_and_tree('mine')
68
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
69
        target = mine.bzrdir.sprout('target').open_workingtree()
70
        target.commit('my change', rev_id='M1', allow_pointless=True)
71
        other = mine.bzrdir.sprout('other').open_workingtree()
72
        other.merge_from_branch(target.branch)
73
        other.commit('merge my change', rev_id='O2')
74
        mine.merge_from_branch(other.branch)
75
        mine.commit('merge other', rev_id='P2')
76
        mine.branch.push(target.branch)
77
        self.assertEqual(['P1', 'P2'], target.branch.revision_history())
78
79
    def test_push_to_checkout_updates_master(self):
80
        """Pushing into a checkout updates the checkout and the master branch"""
81
        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
82
        checkout = self.make_branch_and_tree('checkout')
83
        try:
84
            checkout.branch.bind(master_tree.branch)
85
        except errors.UpgradeRequired:
86
            # cant bind this format, the test is irrelevant.
87
            return
88
        rev1 = checkout.commit('master')
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
89
90
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
91
        rev2 = other.commit('other commit')
92
        # now push, which should update both checkout and master.
93
        other.branch.push(checkout.branch)
94
        self.assertEqual([rev1, rev2], checkout.branch.revision_history())
95
        self.assertEqual([rev1, rev2], master_tree.branch.revision_history())
96
97
    def test_push_raises_specific_error_on_master_connection_error(self):
98
        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
99
        checkout = self.make_branch_and_tree('checkout')
100
        try:
101
            checkout.branch.bind(master_tree.branch)
102
        except errors.UpgradeRequired:
103
            # cant bind this format, the test is irrelevant.
104
            return
2245.2.1 by Robert Collins
Split branch pushing out of branch pulling.
105
        other = master_tree.branch.bzrdir.sprout('other').open_workingtree()
106
        # move the branch out of the way on disk to cause a connection
107
        # error.
108
        os.rename('master', 'master_gone')
109
        # try to push, which should raise a BoundBranchConnectionFailure.
110
        self.assertRaises(errors.BoundBranchConnectionFailure,
111
                other.branch.push, checkout.branch)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
112
2279.1.1 by John Arbash Meinel
Branch.push() only needs a read lock.
113
    def test_push_uses_read_lock(self):
114
        """Push should only need a read lock on the source side."""
115
        source = self.make_branch_and_tree('source')
116
        target = self.make_branch('target')
117
2381.1.3 by Robert Collins
Review feedback.
118
        self.build_tree(['source/a'])
2279.1.1 by John Arbash Meinel
Branch.push() only needs a read lock.
119
        source.add(['a'])
120
        source.commit('a')
121
122
        source.branch.lock_read()
123
        try:
124
            target.lock_write()
125
            try:
126
                source.branch.push(target, stop_revision=source.last_revision())
127
            finally:
128
                target.unlock()
129
        finally:
130
            source.branch.unlock()
131
2279.1.3 by John Arbash Meinel
Switch the test to being a branch_implementation test.
132
    def test_push_within_repository(self):
133
        """Push from one branch to another inside the same repository."""
134
        try:
135
            repo = self.make_repository('repo', shared=True)
136
        except (errors.IncompatibleFormat, errors.UninitializableFormat):
137
            # This Branch format cannot create shared repositories
138
            return
139
        # This is a little bit trickier because make_branch_and_tree will not
140
        # re-use a shared repository.
141
        a_bzrdir = self.make_bzrdir('repo/tree')
142
        try:
143
            a_branch = self.branch_format.initialize(a_bzrdir)
144
        except (errors.UninitializableFormat):
145
            # Cannot create these branches
146
            return
2018.5.97 by Andrew Bennetts
Fix more tests.
147
        try:
148
            tree = a_branch.bzrdir.create_workingtree()
149
        except errors.NotLocalUrl:
2018.5.130 by Robert Collins
Make all branch_implementations tests pass.
150
            if self.vfs_transport_factory is LocalURLServer:
151
                # the branch is colocated on disk, we cannot create a checkout.
152
                # hopefully callers will expect this.
153
                local_controldir= bzrdir.BzrDir.open(self.get_vfs_only_url('repo/tree'))
154
                tree = local_controldir.create_workingtree()
155
            else:
156
                tree = a_branch.create_checkout('repo/tree', lightweight=True)
2381.1.3 by Robert Collins
Review feedback.
157
        self.build_tree(['repo/tree/a'])
2279.1.3 by John Arbash Meinel
Switch the test to being a branch_implementation test.
158
        tree.add(['a'])
159
        tree.commit('a')
160
161
        to_bzrdir = self.make_bzrdir('repo/branch')
162
        to_branch = self.branch_format.initialize(to_bzrdir)
163
        tree.branch.push(to_branch)
164
165
        self.assertEqual(tree.branch.last_revision(),
166
                         to_branch.last_revision())
167
3449.1.2 by Andrew Bennetts
Add test and NEWS entry.
168
    def test_push_overwrite_of_non_tip_with_stop_revision(self):
169
        """Combining the stop_revision and overwrite options works.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
170
3449.1.2 by Andrew Bennetts
Add test and NEWS entry.
171
        This was <https://bugs.launchpad.net/bzr/+bug/234229>.
172
        """
173
        source = self.make_branch_and_tree('source')
174
        target = self.make_branch('target')
175
176
        source.commit('1st commit')
177
        source.branch.push(target)
178
        source.commit('2nd commit', rev_id='rev-2')
179
        source.commit('3rd commit')
180
181
        source.branch.push(target, stop_revision='rev-2', overwrite=True)
182
        self.assertEqual('rev-2', target.last_revision())
183
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
184
    def test_push_with_default_stacking_does_not_create_broken_branch(self):
3904.3.7 by Andrew Bennetts
Comment the new tests.
185
        """Pushing a new standalone branch works even when there's a default
186
        stacking policy at the destination.
187
188
        The new branch will preserve the repo format (even if it isn't the
189
        default for the branch), and will be stacked when the repo format
190
        allows (which means that the branch format isn't necessarly preserved).
191
        """
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.
192
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
193
            raise tests.TestNotApplicable('Not a metadir format.')
194
        if isinstance(self.branch_format, branch.BranchReferenceFormat):
3904.3.7 by Andrew Bennetts
Comment the new tests.
195
            # This test could in principle apply to BranchReferenceFormat, but
196
            # 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.
197
            raise tests.TestSkipped(
198
                "BranchBuilder can't make reference branches.")
3904.3.7 by Andrew Bennetts
Comment the new tests.
199
        # Make a branch called "local" in a stackable repository
200
        # The branch has 3 revisions:
201
        #   - rev-1, adds a file
202
        #   - rev-2, no changes
203
        #   - rev-3, modifies the file.
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
204
        repo = self.make_repository('repo', shared=True, format='1.6')
205
        builder = self.make_branch_builder('repo/local')
3904.3.4 by Andrew Bennetts
First cut of a branch_implementations test. It fails.
206
        builder.start_series()
207
        builder.build_snapshot('rev-1', None, [
208
            ('add', ('', 'root-id', 'directory', '')),
209
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
210
        builder.build_snapshot('rev-2', ['rev-1'], [])
211
        builder.build_snapshot('rev-3', ['rev-2'],
212
            [('modify', ('f-id', 'new-content\n'))])
213
        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.
214
        trunk = builder.get_branch()
3904.3.7 by Andrew Bennetts
Comment the new tests.
215
        # 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.
216
        trunk.bzrdir.sprout(self.get_url('trunk'), revision_id='rev-1')
3904.3.7 by Andrew Bennetts
Comment the new tests.
217
        # Set a default stacking policy so that new branches will automatically
218
        # stack on trunk.
3904.3.4 by Andrew Bennetts
First cut of a branch_implementations test. It fails.
219
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
3904.3.7 by Andrew Bennetts
Comment the new tests.
220
        # 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.
221
        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.
222
        push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
3904.3.7 by Andrew Bennetts
Comment the new tests.
223
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
224
        # fulltext record for f-id @ rev-1, then this will fail.
3904.3.5 by Andrew Bennetts
Improve the test; now 4/7 passing.
225
        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.
226
        trunk.push(remote_branch)
227
        remote_branch.check()
3904.3.4 by Andrew Bennetts
First cut of a branch_implementations test. It fails.
228
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
229
    def test_no_get_parent_map_after_insert_stream(self):
230
        # Effort test for bug 331823
231
        self.setup_smart_server_with_call_log()
232
        # Make a local branch with four revisions.  Four revisions because:
233
        # one to push, one there for _walk_to_common_revisions to find, one we
234
        # don't want to access, one for luck :)
4035.2.2 by Robert Collins
Minor tweaks to fix failing tests.
235
        if isinstance(self.branch_format, branch.BranchReferenceFormat):
236
            # This test could in principle apply to BranchReferenceFormat, but
237
            # make_branch_builder doesn't support it.
238
            raise tests.TestSkipped(
239
                "BranchBuilder can't make reference branches.")
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
240
        try:
241
            builder = self.make_branch_builder('local')
242
        except (errors.TransportNotPossible, errors.UninitializableFormat):
243
            raise tests.TestNotApplicable('format not directly constructable')
244
        builder.start_series()
245
        builder.build_snapshot('first', None, [
246
            ('add', ('', 'root-id', 'directory', ''))])
247
        builder.build_snapshot('second', ['first'], [])
248
        builder.build_snapshot('third', ['second'], [])
249
        builder.build_snapshot('fourth', ['third'], [])
250
        builder.finish_series()
251
        local = builder.get_branch()
252
        local = branch.Branch.open(self.get_vfs_only_url('local'))
253
        # Initial push of three revisions
254
        remote_bzrdir = local.bzrdir.sprout(
255
            self.get_url('remote'), revision_id='third')
256
        remote = remote_bzrdir.open_branch()
257
        # Push fourth revision
258
        self.reset_smart_call_log()
259
        self.disableOptimisticGetParentMap()
260
        self.assertFalse(local.is_locked())
261
        local.push(remote)
4070.2.6 by Robert Collins
Missed one test.
262
        hpss_call_names = [item.call.method for item in self.hpss_calls]
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
263
        self.assertTrue('Repository.insert_stream' in hpss_call_names)
264
        insert_stream_idx = hpss_call_names.index('Repository.insert_stream')
265
        calls_after_insert_stream = hpss_call_names[insert_stream_idx:]
266
        # After inserting the stream the client has no reason to query the
267
        # remote graph any further.
268
        self.assertEqual(
269
            ['Repository.insert_stream', 'Repository.insert_stream', 'get',
4032.3.8 by Robert Collins
Merge bzr.dev, adjust effort tests reducing the effort in line with the write_group avoidance.
270
             'Branch.set_last_revision_info', 'Branch.unlock'],
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
271
            calls_after_insert_stream)
272
273
    def disableOptimisticGetParentMap(self):
274
        # Tweak some class variables to stop remote get_parent_map calls asking
275
        # for or receiving more data than the caller asked for.
276
        old_flag = SmartServerRepositoryGetParentMap.no_extra_results
4144.3.8 by Andrew Bennetts
Fix disableOptimisticGetParentMap for removed InterOtherToRemote.
277
        inter_class = repository.InterRepository
278
        old_batch_size = inter_class._walk_to_common_revisions_batch_size
279
        inter_class._walk_to_common_revisions_batch_size = 1
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
280
        SmartServerRepositoryGetParentMap.no_extra_results = True
281
        def reset_values():
282
            SmartServerRepositoryGetParentMap.no_extra_results = old_flag
4144.3.8 by Andrew Bennetts
Fix disableOptimisticGetParentMap for removed InterOtherToRemote.
283
            inter_class._walk_to_common_revisions_batch_size = old_batch_size
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
284
        self.addCleanup(reset_values)
285
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
286
287
class TestPushHook(TestCaseWithBranch):
288
289
    def setUp(self):
290
        self.hook_calls = []
291
        TestCaseWithBranch.setUp(self)
292
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
293
    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
294
        """Capture post push hook calls to self.hook_calls.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
295
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
296
        The call is logged, as is some state of the two branches.
297
        """
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
298
        if result.local_branch:
299
            local_locked = result.local_branch.is_locked()
300
            local_base = result.local_branch.base
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
301
        else:
302
            local_locked = None
303
            local_base = None
304
        self.hook_calls.append(
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
305
            ('post_push', result.source_branch, local_base,
306
             result.master_branch.base,
2297.1.4 by Martin Pool
Push now returns a PushResult rather than just an integer.
307
             result.old_revno, result.old_revid,
2297.1.6 by Martin Pool
Add docs for Results, give some members cleaner names
308
             result.new_revno, result.new_revid,
309
             result.source_branch.is_locked(), local_locked,
310
             result.master_branch.is_locked()))
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
311
312
    def test_post_push_empty_history(self):
313
        target = self.make_branch('target')
314
        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.
315
        Branch.hooks.install_named_hook('post_push',
316
                                        self.capture_post_push_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
317
        source.push(target)
318
        # with nothing there we should still get a notification, and
319
        # have both branches locked at the notification time.
320
        self.assertEqual([
321
            ('post_push', source, None, target.base, 0, NULL_REVISION,
322
             0, NULL_REVISION, True, None, True)
323
            ],
324
            self.hook_calls)
325
326
    def test_post_push_bound_branch(self):
327
        # pushing to a bound branch should pass in the master branch to the
328
        # hook, allowing the correct number of emails to be sent, while still
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
329
        # allowing hooks that want to modify the target to do so to both
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
330
        # instances.
331
        target = self.make_branch('target')
332
        local = self.make_branch('local')
333
        try:
334
            local.bind(target)
335
        except errors.UpgradeRequired:
2477.1.2 by Martin Pool
Rename push/pull back to 'run_hooks' (jameinel)
336
            # We can't bind this format to itself- typically it is the local
337
            # branch that doesn't support binding.  As of May 2007
338
            # remotebranches can't be bound.  Let's instead make a new local
339
            # branch of the default type, which does allow binding.
340
            # See https://bugs.launchpad.net/bzr/+bug/112020
2477.1.9 by Martin Pool
Review cleanups from John, mostly docs
341
            local = BzrDir.create_branch_convenience('local2')
342
            local.bind(target)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
343
        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.
344
        Branch.hooks.install_named_hook('post_push',
345
                                        self.capture_post_push_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
346
        source.push(local)
347
        # with nothing there we should still get a notification, and
348
        # have both branches locked at the notification time.
349
        self.assertEqual([
350
            ('post_push', source, local.base, target.base, 0, NULL_REVISION,
351
             0, NULL_REVISION, True, True, True)
352
            ],
353
            self.hook_calls)
354
355
    def test_post_push_nonempty_history(self):
356
        target = self.make_branch_and_memory_tree('target')
357
        target.lock_write()
358
        target.add('')
359
        rev1 = target.commit('rev 1')
360
        target.unlock()
361
        sourcedir = target.bzrdir.clone(self.get_url('source'))
362
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
363
        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.
364
        Branch.hooks.install_named_hook('post_push',
365
                                        self.capture_post_push_hook, None)
2246.1.3 by Robert Collins
New branch hooks: post_push, post_pull, post_commit, post_uncommit. These
366
        source.branch.push(target.branch)
367
        # with nothing there we should still get a notification, and
368
        # have both branches locked at the notification time.
369
        self.assertEqual([
370
            ('post_push', source.branch, None, target.branch.base, 1, rev1,
371
             2, rev2, True, None, True)
372
            ],
373
            self.hook_calls)
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
374
375
376
class EmptyPushSmartEffortTests(TestCaseWithBranch):
377
    """Tests that a push of 0 revisions should make a limited number of smart
378
    protocol RPCs.
379
    """
380
381
    def setUp(self):
382
        # Skip some scenarios that don't apply to these tests.
383
        if (self.transport_server is not None and
384
            issubclass(self.transport_server, server.SmartTCPServer)):
385
            raise tests.TestNotApplicable(
386
                'Does not apply when remote backing branch is also '
387
                'a smart branch')
388
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
389
            raise tests.TestNotApplicable(
390
                'Branch format 4 is not usable via HPSS.')
391
        super(EmptyPushSmartEffortTests, self).setUp()
392
        # Create a smart server that publishes whatever the backing VFS server
393
        # does.
394
        self.smart_server = server.SmartTCPServer_for_testing()
395
        self.smart_server.setUp(self.get_server())
396
        self.addCleanup(self.smart_server.tearDown)
397
        # Make two empty branches, 'empty' and 'target'.
398
        self.empty_branch = self.make_branch('empty')
399
        self.make_branch('target')
400
        # Log all HPSS calls into self.hpss_calls.
401
        client._SmartClient.hooks.install_named_hook(
402
            'call', self.capture_hpss_call, None)
403
        self.hpss_calls = []
404
405
    def capture_hpss_call(self, params):
406
        self.hpss_calls.append(params.method)
407
408
    def test_empty_branch_api(self):
409
        """The branch_obj.push API should make a limited number of HPSS calls.
410
        """
411
        transport = get_transport(self.smart_server.get_url()).clone('target')
412
        target = Branch.open_from_transport(transport)
413
        self.empty_branch.push(target)
414
        self.assertEqual(
415
            ['BzrDir.open',
4084.2.1 by Robert Collins
Make accessing a branch.tags.get_tag_dict use a smart[er] method rather than VFS calls and real objects.
416
             'BzrDir.open_branchV2',
4053.1.4 by Robert Collins
Move the fetch control attributes from Repository to RepositoryFormat.
417
             'BzrDir.find_repositoryV3',
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
418
             'Branch.get_stacked_on_url',
419
             'Branch.lock_write',
420
             'Branch.last_revision_info',
421
             'Branch.unlock'],
422
            self.hpss_calls)
423
424
    def test_empty_branch_command(self):
425
        """The 'bzr push' command should make a limited number of HPSS calls.
426
        """
427
        cmd = builtins.cmd_push()
428
        cmd.outf = tests.StringIOWrapper()
429
        cmd.run(
4420.1.2 by Vincent Ladeuil
Fix bug #284038 by adding a --strict option to push.
430
            directory=self.get_url('empty'),
4420.1.4 by Vincent Ladeuil
Cleanup.
431
            location=self.smart_server.get_url() + 'target')
3703.3.7 by Andrew Bennetts
Move empty push effort tests to branch_implementations.
432
        # HPSS calls as of 2008/09/22:
433
        # [BzrDir.open, BzrDir.open_branch, BzrDir.find_repositoryV2,
434
        # Branch.get_stacked_on_url, get, get, Branch.lock_write,
435
        # Branch.last_revision_info, Branch.unlock]
436
        self.assertTrue(len(self.hpss_calls) <= 9, self.hpss_calls)
437
438
4347.3.2 by Jelmer Vernooij
Add some basic tests for lossy_push.
439
class TestLossyPush(TestCaseWithBranch):
440
441
    def setUp(self):
442
        self.hook_calls = []
443
        TestCaseWithBranch.setUp(self)
444
445
    def test_lossy_push_raises_same_vcs(self):
446
        target = self.make_branch('target')
447
        source = self.make_branch('source')
448
        self.assertRaises(errors.LossyPushToSameVCS, source.lossy_push, target)