/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2009, 2010 Canonical Ltd
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
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 branch.push behaviour."""
18
5317.1.1 by Robert Collins
Make the interbranch test_no_get_parent_map_after_insert_stream test work for looms - a little ugly, will want a generic hook-out to foreign formats in some form, at some point.
19
from testtools.matchers import (
20
    Equals,
21
    MatchesAny,
22
    )
23
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
24
from ... import (
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
25
    branch,
4332.3.35 by Robert Collins
Fix failing tests.
26
    check,
6472.2.2 by Jelmer Vernooij
Use controldir rather than bzrdir in a couple more places.
27
    controldir,
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
28
    errors,
29
    push,
30
    tests,
6670.4.1 by Jelmer Vernooij
Update imports.
31
    )
32
from ...bzr import (
33
    branch as bzrbranch,
5863.4.2 by Jelmer Vernooij
Fix test.
34
    vf_repository,
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
35
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
36
from ...branch import Branch
37
from ...controldir import ControlDir
38
from ...memorytree import MemoryTree
39
from ...revision import NULL_REVISION
40
from ...sixish import (
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
41
    BytesIO,
42
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
43
from ...smart.repository import SmartServerRepositoryGetParentMap
44
from . import (
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
45
    TestCaseWithInterBranch,
46
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
47
from .. import test_server
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
48
49
4211.1.6 by Jelmer Vernooij
Review from Ian.
50
# These tests are based on similar tests in 
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
51
# breezy.tests.per_branch.test_push.
4211.1.6 by Jelmer Vernooij
Review from Ian.
52
53
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
54
class TestPush(TestCaseWithInterBranch):
55
56
    def test_push_convergence_simple(self):
57
        # when revisions are pushed, the left-most accessible parents must
58
        # become the revision-history.
59
        mine = self.make_from_branch_and_tree('mine')
60
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
61
        other = self.sprout_to(mine.bzrdir, 'other').open_workingtree()
62
        other.commit('my change', rev_id='M1', allow_pointless=True)
63
        mine.merge_from_branch(other.branch)
64
        mine.commit('merge my change', rev_id='P2')
65
        result = mine.branch.push(other.branch)
6165.4.6 by Jelmer Vernooij
Avoid more uses of revision_history.
66
        self.assertEqual('P2', other.branch.last_revision())
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
67
        # result object contains some structured data
68
        self.assertEqual(result.old_revid, 'M1')
69
        self.assertEqual(result.new_revid, 'P2')
70
71
    def test_push_merged_indirect(self):
72
        # it should be possible to do a push from one branch into another
73
        # when the tip of the target was merged into the source branch
74
        # via a third branch - so its buried in the ancestry and is not
75
        # directly accessible.
76
        mine = self.make_from_branch_and_tree('mine')
77
        mine.commit('1st post', rev_id='P1', allow_pointless=True)
78
        target = self.sprout_to(mine.bzrdir, 'target').open_workingtree()
79
        target.commit('my change', rev_id='M1', allow_pointless=True)
80
        other = self.sprout_to(mine.bzrdir, 'other').open_workingtree()
81
        other.merge_from_branch(target.branch)
82
        other.commit('merge my change', rev_id='O2')
83
        mine.merge_from_branch(other.branch)
84
        mine.commit('merge other', rev_id='P2')
85
        mine.branch.push(target.branch)
6165.4.6 by Jelmer Vernooij
Avoid more uses of revision_history.
86
        self.assertEqual('P2', target.branch.last_revision())
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
87
88
    def test_push_to_checkout_updates_master(self):
89
        """Pushing into a checkout updates the checkout and the master branch"""
90
        master_tree = self.make_to_branch_and_tree('master')
91
        checkout = self.make_to_branch_and_tree('checkout')
92
        try:
93
            checkout.branch.bind(master_tree.branch)
94
        except errors.UpgradeRequired:
95
            # cant bind this format, the test is irrelevant.
96
            return
97
        rev1 = checkout.commit('master')
98
4211.1.5 by Jelmer Vernooij
Fix copyright year, number of columns used.
99
        other_bzrdir = self.sprout_from(master_tree.branch.bzrdir, 'other')
100
        other = other_bzrdir.open_workingtree()
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
101
        rev2 = other.commit('other commit')
102
        # now push, which should update both checkout and master.
103
        other.branch.push(checkout.branch)
6165.4.6 by Jelmer Vernooij
Avoid more uses of revision_history.
104
        self.assertEqual(rev2, checkout.branch.last_revision())
105
        self.assertEqual(rev2, master_tree.branch.last_revision())
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
106
107
    def test_push_raises_specific_error_on_master_connection_error(self):
108
        master_tree = self.make_to_branch_and_tree('master')
109
        checkout = self.make_to_branch_and_tree('checkout')
110
        try:
111
            checkout.branch.bind(master_tree.branch)
112
        except errors.UpgradeRequired:
113
            # cant bind this format, the test is irrelevant.
114
            return
4211.1.5 by Jelmer Vernooij
Fix copyright year, number of columns used.
115
        other_bzrdir = self.sprout_from(master_tree.branch.bzrdir, 'other')
116
        other = other_bzrdir.open_workingtree()
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
117
        # move the branch out of the way on disk to cause a connection
118
        # error.
6162.5.3 by Jelmer Vernooij
Use destroy_branch, as some branches don't actually exist on disk.
119
        master_tree.bzrdir.destroy_branch()
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
120
        # try to push, which should raise a BoundBranchConnectionFailure.
121
        self.assertRaises(errors.BoundBranchConnectionFailure,
122
                other.branch.push, checkout.branch)
123
124
    def test_push_uses_read_lock(self):
125
        """Push should only need a read lock on the source side."""
126
        source = self.make_from_branch_and_tree('source')
127
        target = self.make_to_branch('target')
128
129
        self.build_tree(['source/a'])
130
        source.add(['a'])
131
        source.commit('a')
132
133
        source.branch.lock_read()
134
        try:
135
            target.lock_write()
136
            try:
137
                source.branch.push(target, stop_revision=source.last_revision())
138
            finally:
139
                target.unlock()
140
        finally:
141
            source.branch.unlock()
142
143
    def test_push_within_repository(self):
144
        """Push from one branch to another inside the same repository."""
145
        try:
146
            repo = self.make_repository('repo', shared=True)
147
        except (errors.IncompatibleFormat, errors.UninitializableFormat):
148
            # This Branch format cannot create shared repositories
149
            return
5297.2.2 by Robert Collins
Fixup tests in per_interbranch not being strict about making the from format the configured one.
150
        # This is a little bit trickier because make_from_branch_and_tree will not
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
151
        # re-use a shared repository.
152
        try:
153
            a_branch = self.make_from_branch('repo/tree')
154
        except (errors.UninitializableFormat):
155
            # Cannot create these branches
156
            return
157
        try:
158
            tree = a_branch.bzrdir.create_workingtree()
6127.1.9 by Jelmer Vernooij
Add lightweight option to _get_checkout_format().
159
        except errors.UnsupportedOperation:
160
            self.assertFalse(a_branch.bzrdir._format.supports_workingtrees)
161
            tree = a_branch.create_checkout('repo/tree', lightweight=True)
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
162
        except errors.NotLocalUrl:
5017.3.41 by Vincent Ladeuil
-s bt.per_interbranch passing
163
            if self.vfs_transport_factory is test_server.LocalURLServer:
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
164
                # the branch is colocated on disk, we cannot create a checkout.
165
                # hopefully callers will expect this.
6472.2.2 by Jelmer Vernooij
Use controldir rather than bzrdir in a couple more places.
166
                local_controldir = controldir.ControlDir.open(self.get_vfs_only_url('repo/tree'))
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
167
                tree = local_controldir.create_workingtree()
168
            else:
169
                tree = a_branch.create_checkout('repo/tree', lightweight=True)
170
        self.build_tree(['repo/tree/a'])
171
        tree.add(['a'])
172
        tree.commit('a')
173
174
        to_branch = self.make_to_branch('repo/branch')
175
        tree.branch.push(to_branch)
176
177
        self.assertEqual(tree.branch.last_revision(),
178
                         to_branch.last_revision())
179
180
    def test_push_overwrite_of_non_tip_with_stop_revision(self):
181
        """Combining the stop_revision and overwrite options works.
182
183
        This was <https://bugs.launchpad.net/bzr/+bug/234229>.
184
        """
185
        source = self.make_from_branch_and_tree('source')
186
        target = self.make_to_branch('target')
187
188
        source.commit('1st commit')
189
        source.branch.push(target)
190
        source.commit('2nd commit', rev_id='rev-2')
191
        source.commit('3rd commit')
192
193
        source.branch.push(target, stop_revision='rev-2', overwrite=True)
194
        self.assertEqual('rev-2', target.last_revision())
195
196
    def test_push_with_default_stacking_does_not_create_broken_branch(self):
197
        """Pushing a new standalone branch works even when there's a default
198
        stacking policy at the destination.
199
200
        The new branch will preserve the repo format (even if it isn't the
201
        default for the branch), and will be stacked when the repo format
202
        allows (which means that the branch format isn't necessarly preserved).
203
        """
6653.1.1 by Jelmer Vernooij
Split bzr branch code out into breezy.bzrbranch.
204
        if isinstance(self.branch_format_from, bzrbranch.BranchReferenceFormat):
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
205
            # This test could in principle apply to BranchReferenceFormat, but
206
            # make_branch_builder doesn't support it.
207
            raise tests.TestSkipped(
208
                "BranchBuilder can't make reference branches.")
209
        # Make a branch called "local" in a stackable repository
210
        # The branch has 3 revisions:
211
        #   - rev-1, adds a file
212
        #   - rev-2, no changes
213
        #   - rev-3, modifies the file.
214
        repo = self.make_repository('repo', shared=True, format='1.6')
215
        builder = self.make_from_branch_builder('repo/local')
216
        builder.start_series()
217
        builder.build_snapshot('rev-1', None, [
218
            ('add', ('', 'root-id', 'directory', '')),
219
            ('add', ('filename', 'f-id', 'file', 'content\n'))])
220
        builder.build_snapshot('rev-2', ['rev-1'], [])
221
        builder.build_snapshot('rev-3', ['rev-2'],
222
            [('modify', ('f-id', 'new-content\n'))])
223
        builder.finish_series()
224
        trunk = builder.get_branch()
225
        # Sprout rev-1 to "trunk", so that we can stack on it.
226
        trunk.bzrdir.sprout(self.get_url('trunk'), revision_id='rev-1')
227
        # Set a default stacking policy so that new branches will automatically
228
        # stack on trunk.
229
        self.make_bzrdir('.').get_config().set_default_stack_on('trunk')
230
        # Push rev-2 to a new branch "remote".  It will be stacked on "trunk".
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
231
        output = BytesIO()
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
232
        push._show_push_branch(trunk, 'rev-2', self.get_url('remote'), output)
233
        # Push rev-3 onto "remote".  If "remote" not stacked and is missing the
234
        # fulltext record for f-id @ rev-1, then this will fail.
235
        remote_branch = Branch.open(self.get_url('remote'))
236
        trunk.push(remote_branch)
4332.3.35 by Robert Collins
Fix failing tests.
237
        check.check_dwim(remote_branch.base, False, True, True)
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
238
239
    def test_no_get_parent_map_after_insert_stream(self):
240
        # Effort test for bug 331823
241
        self.setup_smart_server_with_call_log()
242
        # Make a local branch with four revisions.  Four revisions because:
243
        # one to push, one there for _walk_to_common_revisions to find, one we
244
        # don't want to access, one for luck :)
6653.1.1 by Jelmer Vernooij
Split bzr branch code out into breezy.bzrbranch.
245
        if isinstance(self.branch_format_from, bzrbranch.BranchReferenceFormat):
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
246
            # This test could in principle apply to BranchReferenceFormat, but
247
            # make_branch_builder doesn't support it.
248
            raise tests.TestSkipped(
249
                "BranchBuilder can't make reference branches.")
250
        try:
251
            builder = self.make_from_branch_builder('local')
252
        except (errors.TransportNotPossible, errors.UninitializableFormat):
253
            raise tests.TestNotApplicable('format not directly constructable')
254
        builder.start_series()
255
        builder.build_snapshot('first', None, [
256
            ('add', ('', 'root-id', 'directory', ''))])
257
        builder.build_snapshot('second', ['first'], [])
258
        builder.build_snapshot('third', ['second'], [])
259
        builder.build_snapshot('fourth', ['third'], [])
260
        builder.finish_series()
261
        local = branch.Branch.open(self.get_vfs_only_url('local'))
262
        # Initial push of three revisions
263
        remote_bzrdir = local.bzrdir.sprout(
264
            self.get_url('remote'), revision_id='third')
265
        remote = remote_bzrdir.open_branch()
266
        # Push fourth revision
267
        self.reset_smart_call_log()
268
        self.disableOptimisticGetParentMap()
269
        self.assertFalse(local.is_locked())
270
        local.push(remote)
271
        hpss_call_names = [item.call.method for item in self.hpss_calls]
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
272
        self.assertTrue('Repository.insert_stream_1.19' in hpss_call_names)
4476.3.18 by Andrew Bennetts
Update some tests that were expecting the pre-1.17 insert_stream verb.
273
        insert_stream_idx = hpss_call_names.index(
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
274
            'Repository.insert_stream_1.19')
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
275
        calls_after_insert_stream = hpss_call_names[insert_stream_idx:]
276
        # After inserting the stream the client has no reason to query the
277
        # remote graph any further.
5317.1.1 by Robert Collins
Make the interbranch test_no_get_parent_map_after_insert_stream test work for looms - a little ugly, will want a generic hook-out to foreign formats in some form, at some point.
278
        bzr_core_trace = Equals(
279
            ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19',
6282.6.33 by Jelmer Vernooij
Fix some tests.
280
             'Branch.set_last_revision_info', 'Branch.unlock'])
5317.1.1 by Robert Collins
Make the interbranch test_no_get_parent_map_after_insert_stream test work for looms - a little ugly, will want a generic hook-out to foreign formats in some form, at some point.
281
        bzr_loom_trace = Equals(
282
            ['Repository.insert_stream_1.19', 'Repository.insert_stream_1.19',
6282.6.33 by Jelmer Vernooij
Fix some tests.
283
             'Branch.set_last_revision_info', 'get', 'Branch.unlock'])
5317.1.1 by Robert Collins
Make the interbranch test_no_get_parent_map_after_insert_stream test work for looms - a little ugly, will want a generic hook-out to foreign formats in some form, at some point.
284
        self.assertThat(calls_after_insert_stream,
285
            MatchesAny(bzr_core_trace, bzr_loom_trace))
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
286
287
    def disableOptimisticGetParentMap(self):
288
        # Tweak some class variables to stop remote get_parent_map calls asking
289
        # for or receiving more data than the caller asked for.
5863.4.2 by Jelmer Vernooij
Fix test.
290
        self.overrideAttr(vf_repository.InterVersionedFileRepository,
4985.1.5 by Vincent Ladeuil
Deploying the new overrideAttr facility further reduces the complexity
291
                          '_walk_to_common_revisions_batch_size', 1)
292
        self.overrideAttr(SmartServerRepositoryGetParentMap,
293
                            'no_extra_results', True)
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
294
295
296
class TestPushHook(TestCaseWithInterBranch):
297
298
    def setUp(self):
299
        self.hook_calls = []
6552.1.4 by Vincent Ladeuil
Remaining tests matching setup(self) that can be rewritten with super().
300
        super(TestPushHook, self).setUp()
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
301
302
    def capture_post_push_hook(self, result):
303
        """Capture post push hook calls to self.hook_calls.
304
305
        The call is logged, as is some state of the two branches.
306
        """
307
        if result.local_branch:
308
            local_locked = result.local_branch.is_locked()
309
            local_base = result.local_branch.base
310
        else:
311
            local_locked = None
312
            local_base = None
313
        self.hook_calls.append(
314
            ('post_push', result.source_branch, local_base,
315
             result.master_branch.base,
316
             result.old_revno, result.old_revid,
317
             result.new_revno, result.new_revid,
318
             result.source_branch.is_locked(), local_locked,
319
             result.master_branch.is_locked()))
320
321
    def test_post_push_empty_history(self):
322
        target = self.make_to_branch('target')
323
        source = self.make_from_branch('source')
324
        Branch.hooks.install_named_hook('post_push',
325
                                        self.capture_post_push_hook, None)
326
        source.push(target)
327
        # with nothing there we should still get a notification, and
328
        # have both branches locked at the notification time.
329
        self.assertEqual([
330
            ('post_push', source, None, target.base, 0, NULL_REVISION,
331
             0, NULL_REVISION, True, None, True)
332
            ],
333
            self.hook_calls)
334
335
    def test_post_push_bound_branch(self):
336
        # pushing to a bound branch should pass in the master branch to the
337
        # hook, allowing the correct number of emails to be sent, while still
338
        # allowing hooks that want to modify the target to do so to both
339
        # instances.
340
        target = self.make_to_branch('target')
341
        local = self.make_from_branch('local')
342
        try:
343
            local.bind(target)
344
        except errors.UpgradeRequired:
345
            # We can't bind this format to itself- typically it is the local
346
            # branch that doesn't support binding.  As of May 2007
347
            # remotebranches can't be bound.  Let's instead make a new local
348
            # branch of the default type, which does allow binding.
349
            # See https://bugs.launchpad.net/bzr/+bug/112020
6472.2.2 by Jelmer Vernooij
Use controldir rather than bzrdir in a couple more places.
350
            local = ControlDir.create_branch_convenience('local2')
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
351
            local.bind(target)
352
        source = self.make_from_branch('source')
353
        Branch.hooks.install_named_hook('post_push',
354
                                        self.capture_post_push_hook, None)
355
        source.push(local)
356
        # with nothing there we should still get a notification, and
357
        # have both branches locked at the notification time.
358
        self.assertEqual([
359
            ('post_push', source, local.base, target.base, 0, NULL_REVISION,
360
             0, NULL_REVISION, True, True, True)
361
            ],
362
            self.hook_calls)
363
364
    def test_post_push_nonempty_history(self):
365
        target = self.make_to_branch_and_tree('target')
366
        target.lock_write()
367
        target.add('')
368
        rev1 = target.commit('rev 1')
369
        target.unlock()
6217.3.3 by Jelmer Vernooij
Fix test when workingtree and branch are not colocated.
370
        sourcedir = target.branch.bzrdir.clone(self.get_url('source'))
4211.1.4 by Jelmer Vernooij
add InterBranch.push() tests.
371
        source = MemoryTree.create_on_branch(sourcedir.open_branch())
372
        rev2 = source.commit('rev 2')
373
        Branch.hooks.install_named_hook('post_push',
374
                                        self.capture_post_push_hook, None)
375
        source.branch.push(target.branch)
376
        # with nothing there we should still get a notification, and
377
        # have both branches locked at the notification time.
378
        self.assertEqual([
379
            ('post_push', source.branch, None, target.branch.base, 1, rev1,
380
             2, rev2, True, None, True)
381
            ],
382
            self.hook_calls)