/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/branch_implementations/test_stacking.py

merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008 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 Branch.get_stacked_on_url and set_stacked_on_url."""
 
18
 
 
19
from bzrlib import (
 
20
    branch,
 
21
    bzrdir,
 
22
    errors,
 
23
    )
 
24
from bzrlib.revision import NULL_REVISION
 
25
from bzrlib.smart import server
 
26
from bzrlib.tests import TestNotApplicable, KnownFailure, transport_util
 
27
from bzrlib.tests.branch_implementations import TestCaseWithBranch
 
28
from bzrlib.transport import get_transport
 
29
 
 
30
 
 
31
unstackable_format_errors = (
 
32
    errors.UnstackableBranchFormat,
 
33
    errors.UnstackableRepositoryFormat,
 
34
    )
 
35
 
 
36
 
 
37
class TestStacking(TestCaseWithBranch):
 
38
 
 
39
    def check_lines_added_or_present(self, stacked_branch, revid):
 
40
        # similar to a failure seen in bug 288751 by mbp 20081120
 
41
        stacked_repo = stacked_branch.repository
 
42
        stacked_repo.lock_read()
 
43
        try:
 
44
            list(stacked_repo.inventories.iter_lines_added_or_present_in_keys(
 
45
                    [(revid,)]))
 
46
        finally:
 
47
            stacked_repo.unlock()
 
48
 
 
49
    def test_get_set_stacked_on_url(self):
 
50
        # branches must either:
 
51
        # raise UnstackableBranchFormat or
 
52
        # raise UnstackableRepositoryFormat or
 
53
        # permit stacking to be done and then return the stacked location.
 
54
        branch = self.make_branch('branch')
 
55
        target = self.make_branch('target')
 
56
        try:
 
57
            branch.set_stacked_on_url(target.base)
 
58
        except unstackable_format_errors:
 
59
            # if the set failed, so must the get
 
60
            self.assertRaises(unstackable_format_errors, branch.get_stacked_on_url)
 
61
            self.assertFalse(branch._format.supports_stacking())
 
62
            return
 
63
        self.assertTrue(branch._format.supports_stacking())
 
64
        # now we have a stacked branch:
 
65
        self.assertEqual(target.base, branch.get_stacked_on_url())
 
66
        branch.set_stacked_on_url(None)
 
67
        self.assertRaises(errors.NotStacked, branch.get_stacked_on_url)
 
68
 
 
69
    def test_get_set_stacked_on_relative(self):
 
70
        # Branches can be stacked on other branches using relative paths.
 
71
        branch = self.make_branch('branch')
 
72
        target = self.make_branch('target')
 
73
        try:
 
74
            branch.set_stacked_on_url('../target')
 
75
        except unstackable_format_errors:
 
76
            # if the set failed, so must the get
 
77
            self.assertRaises(unstackable_format_errors, branch.get_stacked_on_url)
 
78
            return
 
79
        self.assertEqual('../target', branch.get_stacked_on_url())
 
80
 
 
81
    def assertRevisionInRepository(self, repo_path, revid):
 
82
        """Check that a revision is in a repository, disregarding stacking."""
 
83
        repo = bzrdir.BzrDir.open(repo_path).open_repository()
 
84
        self.assertTrue(repo.has_revision(revid))
 
85
 
 
86
    def assertRevisionNotInRepository(self, repo_path, revid):
 
87
        """Check that a revision is not in a repository, disregarding stacking."""
 
88
        repo = bzrdir.BzrDir.open(repo_path).open_repository()
 
89
        self.assertFalse(repo.has_revision(revid))
 
90
 
 
91
    def test_get_graph_stacked(self):
 
92
        """A stacked repository shows the graph of its parent."""
 
93
        trunk_tree = self.make_branch_and_tree('mainline')
 
94
        trunk_revid = trunk_tree.commit('mainline')
 
95
        # make a new branch, and stack on the existing one.  we don't use
 
96
        # sprout(stacked=True) here because if that is buggy and copies data
 
97
        # it would cause a false pass of this test.
 
98
        new_branch = self.make_branch('new_branch')
 
99
        try:
 
100
            new_branch.set_stacked_on_url(trunk_tree.branch.base)
 
101
        except unstackable_format_errors, e:
 
102
            raise TestNotApplicable(e)
 
103
        # reading the graph from the stacked branch's repository should see
 
104
        # data from the stacked-on branch
 
105
        new_repo = new_branch.repository
 
106
        new_repo.lock_read()
 
107
        try:
 
108
            self.assertEqual(new_repo.get_parent_map([trunk_revid]),
 
109
                {trunk_revid: (NULL_REVISION, )})
 
110
        finally:
 
111
            new_repo.unlock()
 
112
 
 
113
    def test_sprout_stacked(self):
 
114
        # We have a mainline
 
115
        trunk_tree = self.make_branch_and_tree('mainline')
 
116
        trunk_revid = trunk_tree.commit('mainline')
 
117
        # and make branch from it which is stacked
 
118
        try:
 
119
            new_dir = trunk_tree.bzrdir.sprout('newbranch', stacked=True)
 
120
        except unstackable_format_errors, e:
 
121
            raise TestNotApplicable(e)
 
122
        # stacked repository
 
123
        self.assertRevisionNotInRepository('newbranch', trunk_revid)
 
124
        new_tree = new_dir.open_workingtree()
 
125
        new_branch_revid = new_tree.commit('something local')
 
126
        self.assertRevisionNotInRepository('mainline', new_branch_revid)
 
127
        self.assertRevisionInRepository('newbranch', new_branch_revid)
 
128
 
 
129
    def test_sprout_stacked_from_smart_server(self):
 
130
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
 
131
            raise TestNotApplicable('Branch format 4 is not usable via HPSS.')
 
132
        # We have a mainline
 
133
        trunk_tree = self.make_branch_and_tree('mainline')
 
134
        trunk_revid = trunk_tree.commit('mainline')
 
135
        # Make sure that we can make a stacked branch from it
 
136
        try:
 
137
            trunk_tree.bzrdir.sprout('testbranch', stacked=True)
 
138
        except unstackable_format_errors, e:
 
139
            raise TestNotApplicable(e)
 
140
        # Now serve the original mainline from a smart server
 
141
        remote_transport = self.make_smart_server('mainline')
 
142
        remote_bzrdir = bzrdir.BzrDir.open_from_transport(remote_transport)
 
143
        # and make branch from the smart server which is stacked
 
144
        new_dir = remote_bzrdir.sprout('newbranch', stacked=True)
 
145
        # stacked repository
 
146
        self.assertRevisionNotInRepository('newbranch', trunk_revid)
 
147
        new_tree = new_dir.open_workingtree()
 
148
        new_branch_revid = new_tree.commit('something local')
 
149
        self.assertRevisionNotInRepository('mainline', new_branch_revid)
 
150
        self.assertRevisionInRepository('newbranch', new_branch_revid)
 
151
 
 
152
    def test_unstack_fetches(self):
 
153
        """Removing the stacked-on branch pulls across all data"""
 
154
        # We have a mainline
 
155
        trunk_tree = self.make_branch_and_tree('mainline')
 
156
        trunk_revid = trunk_tree.commit('revision on mainline')
 
157
        # and make branch from it which is stacked
 
158
        try:
 
159
            new_dir = trunk_tree.bzrdir.sprout('newbranch', stacked=True)
 
160
        except unstackable_format_errors, e:
 
161
            raise TestNotApplicable(e)
 
162
        # stacked repository
 
163
        self.assertRevisionNotInRepository('newbranch', trunk_revid)
 
164
        # now when we unstack that should implicitly fetch, to make sure that
 
165
        # the branch will still work
 
166
        new_branch = new_dir.open_branch()
 
167
        new_branch.set_stacked_on_url(None)
 
168
        self.assertRevisionInRepository('newbranch', trunk_revid)
 
169
        # of course it's still in the mainline
 
170
        self.assertRevisionInRepository('mainline', trunk_revid)
 
171
        # and now we're no longer stacked
 
172
        self.assertRaises(errors.NotStacked,
 
173
            new_branch.get_stacked_on_url)
 
174
 
 
175
    def make_stacked_bzrdir(self, in_directory=None):
 
176
        """Create a stacked branch and return its bzrdir.
 
177
 
 
178
        :param in_directory: If not None, create a directory of this
 
179
            name and create the stacking and stacked-on bzrdirs in
 
180
            this directory.
 
181
        """
 
182
        if in_directory is not None:
 
183
            self.get_transport().mkdir(in_directory)
 
184
            prefix = in_directory + '/'
 
185
        else:
 
186
            prefix = ''
 
187
        tree = self.make_branch_and_tree(prefix + 'stacked-on')
 
188
        tree.commit('Added foo')
 
189
        stacked_bzrdir = tree.branch.bzrdir.sprout(
 
190
            prefix + 'stacked', tree.branch.last_revision(), stacked=True)
 
191
        return stacked_bzrdir
 
192
 
 
193
    def test_clone_from_stacked_branch_preserve_stacking(self):
 
194
        # We can clone from the bzrdir of a stacked branch. If
 
195
        # preserve_stacking is True, the cloned branch is stacked on the
 
196
        # same branch as the original.
 
197
        try:
 
198
            stacked_bzrdir = self.make_stacked_bzrdir()
 
199
        except unstackable_format_errors, e:
 
200
            raise TestNotApplicable(e)
 
201
        cloned_bzrdir = stacked_bzrdir.clone('cloned', preserve_stacking=True)
 
202
        try:
 
203
            self.assertEqual(
 
204
                stacked_bzrdir.open_branch().get_stacked_on_url(),
 
205
                cloned_bzrdir.open_branch().get_stacked_on_url())
 
206
        except unstackable_format_errors, e:
 
207
            pass
 
208
 
 
209
    def test_clone_from_branch_stacked_on_relative_url_preserve_stacking(self):
 
210
        # If a branch's stacked-on url is relative, we can still clone
 
211
        # from it with preserve_stacking True and get a branch stacked
 
212
        # on an appropriately adjusted relative url.
 
213
        try:
 
214
            stacked_bzrdir = self.make_stacked_bzrdir(in_directory='dir')
 
215
        except unstackable_format_errors, e:
 
216
            raise TestNotApplicable(e)
 
217
        stacked_bzrdir.open_branch().set_stacked_on_url('../stacked-on')
 
218
        cloned_bzrdir = stacked_bzrdir.clone('cloned', preserve_stacking=True)
 
219
        self.assertEqual(
 
220
            '../dir/stacked-on',
 
221
            cloned_bzrdir.open_branch().get_stacked_on_url())
 
222
 
 
223
    def test_clone_from_stacked_branch_no_preserve_stacking(self):
 
224
        try:
 
225
            stacked_bzrdir = self.make_stacked_bzrdir()
 
226
        except unstackable_format_errors, e:
 
227
            # not a testable combination.
 
228
            raise TestNotApplicable(e)
 
229
        cloned_unstacked_bzrdir = stacked_bzrdir.clone('cloned-unstacked',
 
230
            preserve_stacking=False)
 
231
        unstacked_branch = cloned_unstacked_bzrdir.open_branch()
 
232
        self.assertRaises((errors.NotStacked, errors.UnstackableBranchFormat),
 
233
                          unstacked_branch.get_stacked_on_url)
 
234
 
 
235
    def test_no_op_preserve_stacking(self):
 
236
        """With no stacking, preserve_stacking should be a no-op."""
 
237
        branch = self.make_branch('source')
 
238
        cloned_bzrdir = branch.bzrdir.clone('cloned', preserve_stacking=True)
 
239
        self.assertRaises((errors.NotStacked, errors.UnstackableBranchFormat),
 
240
                          cloned_bzrdir.open_branch().get_stacked_on_url)
 
241
 
 
242
    def test_sprout_stacking_policy_handling(self):
 
243
        """Obey policy where possible, ignore otherwise."""
 
244
        stack_on = self.make_branch('stack-on')
 
245
        parent_bzrdir = self.make_bzrdir('.', format='default')
 
246
        parent_bzrdir.get_config().set_default_stack_on('stack-on')
 
247
        source = self.make_branch('source')
 
248
        target = source.bzrdir.sprout('target').open_branch()
 
249
        if self.branch_format.supports_stacking():
 
250
            self.assertEqual('../stack-on', target.get_stacked_on_url())
 
251
        else:
 
252
            self.assertRaises(
 
253
                errors.UnstackableBranchFormat, target.get_stacked_on_url)
 
254
 
 
255
    def test_clone_stacking_policy_handling(self):
 
256
        """Obey policy where possible, ignore otherwise."""
 
257
        stack_on = self.make_branch('stack-on')
 
258
        parent_bzrdir = self.make_bzrdir('.', format='default')
 
259
        parent_bzrdir.get_config().set_default_stack_on('stack-on')
 
260
        source = self.make_branch('source')
 
261
        target = source.bzrdir.clone('target').open_branch()
 
262
        if self.branch_format.supports_stacking():
 
263
            self.assertEqual('../stack-on', target.get_stacked_on_url())
 
264
        else:
 
265
            self.assertRaises(
 
266
                errors.UnstackableBranchFormat, target.get_stacked_on_url)
 
267
 
 
268
    def test_sprout_to_smart_server_stacking_policy_handling(self):
 
269
        """Obey policy where possible, ignore otherwise."""
 
270
        if isinstance(self.branch_format, branch.BzrBranchFormat4):
 
271
            raise TestNotApplicable('Branch format 4 is not usable via HPSS.')
 
272
        stack_on = self.make_branch('stack-on')
 
273
        parent_bzrdir = self.make_bzrdir('.', format='default')
 
274
        parent_bzrdir.get_config().set_default_stack_on('stack-on')
 
275
        source = self.make_branch('source')
 
276
        url = self.make_smart_server('target').base
 
277
        target = source.bzrdir.sprout(url).open_branch()
 
278
        if self.branch_format.supports_stacking():
 
279
            self.assertEqual('../stack-on', target.get_stacked_on_url())
 
280
        else:
 
281
            self.assertRaises(
 
282
                errors.UnstackableBranchFormat, target.get_stacked_on_url)
 
283
 
 
284
    def prepare_stacked_on_fetch(self):
 
285
        stack_on = self.make_branch_and_tree('stack-on')
 
286
        stack_on.commit('first commit', rev_id='rev1')
 
287
        try:
 
288
            stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
 
289
        except unstackable_format_errors, e:
 
290
            raise TestNotApplicable('Format does not support stacking.')
 
291
        unstacked = self.make_repository('unstacked')
 
292
        return stacked_dir.open_workingtree(), unstacked
 
293
 
 
294
    def test_fetch_copies_from_stacked_on(self):
 
295
        stacked, unstacked = self.prepare_stacked_on_fetch()
 
296
        unstacked.fetch(stacked.branch.repository, 'rev1')
 
297
        unstacked.get_revision('rev1')
 
298
 
 
299
    def test_fetch_copies_from_stacked_on_and_stacked(self):
 
300
        stacked, unstacked = self.prepare_stacked_on_fetch()
 
301
        stacked.commit('second commit', rev_id='rev2')
 
302
        unstacked.fetch(stacked.branch.repository, 'rev2')
 
303
        unstacked.get_revision('rev1')
 
304
        unstacked.get_revision('rev2')
 
305
        self.check_lines_added_or_present(stacked.branch, 'rev1')
 
306
        self.check_lines_added_or_present(stacked.branch, 'rev2')
 
307
 
 
308
    def test_autopack_when_stacked(self):
 
309
        # in bzr.dev as of 20080730, autopack was reported to fail in stacked
 
310
        # repositories because of problems with text deltas spanning physical
 
311
        # repository boundaries.  however, i didn't actually get this test to
 
312
        # fail on that code. -- mbp
 
313
        # see https://bugs.launchpad.net/bzr/+bug/252821
 
314
        if not self.branch_format.supports_stacking():
 
315
            raise TestNotApplicable("%r does not support stacking"
 
316
                % self.branch_format)
 
317
        stack_on = self.make_branch_and_tree('stack-on')
 
318
        text_lines = ['line %d blah blah blah\n' % i for i in range(20)]
 
319
        self.build_tree_contents([('stack-on/a', ''.join(text_lines))])
 
320
        stack_on.add('a')
 
321
        stack_on.commit('base commit')
 
322
        stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
 
323
        stacked_tree = stacked_dir.open_workingtree()
 
324
        for i in range(20):
 
325
            text_lines[0] = 'changed in %d\n' % i
 
326
            self.build_tree_contents([('stacked/a', ''.join(text_lines))])
 
327
            stacked_tree.commit('commit %d' % i)
 
328
        stacked_tree.branch.repository.pack()
 
329
        stacked_tree.branch.check()
 
330
 
 
331
    def test_pull_delta_when_stacked(self):
 
332
        if not self.branch_format.supports_stacking():
 
333
            raise TestNotApplicable("%r does not support stacking"
 
334
                % self.branch_format)
 
335
        stack_on = self.make_branch_and_tree('stack-on')
 
336
        text_lines = ['line %d blah blah blah\n' % i for i in range(20)]
 
337
        self.build_tree_contents([('stack-on/a', ''.join(text_lines))])
 
338
        stack_on.add('a')
 
339
        stack_on.commit('base commit')
 
340
        # make a stacked branch from the mainline
 
341
        stacked_dir = stack_on.bzrdir.sprout('stacked', stacked=True)
 
342
        stacked_tree = stacked_dir.open_workingtree()
 
343
        # make a second non-stacked branch from the mainline
 
344
        other_dir = stack_on.bzrdir.sprout('other')
 
345
        other_tree = other_dir.open_workingtree()
 
346
        text_lines[9] = 'changed in other\n'
 
347
        self.build_tree_contents([('other/a', ''.join(text_lines))])
 
348
        stacked_revid = other_tree.commit('commit in other')
 
349
        # this should have generated a delta; try to pull that across
 
350
        # bug 252821 caused a RevisionNotPresent here...
 
351
        stacked_tree.pull(other_tree.branch)
 
352
        stacked_tree.branch.repository.pack()
 
353
        stacked_tree.branch.check()
 
354
        self.check_lines_added_or_present(stacked_tree.branch, stacked_revid)
 
355
 
 
356
    def test_fetch_revisions_with_file_changes(self):
 
357
        # Fetching revisions including file changes into a stacked branch
 
358
        # works without error.
 
359
        # Make the source tree.
 
360
        src_tree = self.make_branch_and_tree('src')
 
361
        self.build_tree_contents([('src/a', 'content')])
 
362
        src_tree.add('a')
 
363
        src_tree.commit('first commit')
 
364
 
 
365
        # Make the stacked-on branch.
 
366
        src_tree.bzrdir.sprout('stacked-on')
 
367
 
 
368
        # Make a branch stacked on it.
 
369
        target = self.make_branch('target')
 
370
        try:
 
371
            target.set_stacked_on_url('../stacked-on')
 
372
        except unstackable_format_errors, e:
 
373
            raise TestNotApplicable('Format does not support stacking.')
 
374
 
 
375
        # Change the source branch.
 
376
        self.build_tree_contents([('src/a', 'new content')])
 
377
        src_tree.commit('second commit', rev_id='rev2')
 
378
 
 
379
        # Fetch changes to the target.
 
380
        target.fetch(src_tree.branch)
 
381
        rtree = target.repository.revision_tree('rev2')
 
382
        rtree.lock_read()
 
383
        self.addCleanup(rtree.unlock)
 
384
        self.assertEqual('new content', rtree.get_file_by_path('a').read())
 
385
        self.check_lines_added_or_present(target, 'rev2')
 
386
 
 
387
    def test_transform_fallback_location_hook(self):
 
388
        # The 'transform_fallback_location' branch hook allows us to inspect
 
389
        # and transform the URL of the fallback location for the branch.
 
390
        stack_on = self.make_branch('stack-on')
 
391
        stacked = self.make_branch('stacked')
 
392
        try:
 
393
            stacked.set_stacked_on_url('../stack-on')
 
394
        except unstackable_format_errors, e:
 
395
            raise TestNotApplicable('Format does not support stacking.')
 
396
        self.get_transport().rename('stack-on', 'new-stack-on')
 
397
        hook_calls = []
 
398
        def hook(stacked_branch, url):
 
399
            hook_calls.append(url)
 
400
            return '../new-stack-on'
 
401
        branch.Branch.hooks.install_named_hook(
 
402
            'transform_fallback_location', hook, None)
 
403
        branch.Branch.open('stacked')
 
404
        self.assertEqual(['../stack-on'], hook_calls)
 
405
 
 
406
    def test_stack_on_repository_branch(self):
 
407
        # Stacking should work when the repo isn't co-located with the
 
408
        # stack-on branch.
 
409
        try:
 
410
            repo = self.make_repository('repo', shared=True)
 
411
        except errors.IncompatibleFormat:
 
412
            raise TestNotApplicable()
 
413
        # Avoid make_branch, which produces standalone branches.
 
414
        bzrdir = self.make_bzrdir('repo/stack-on')
 
415
        try:
 
416
            b = bzrdir.create_branch()
 
417
        except errors.UninitializableFormat:
 
418
            raise TestNotApplicable()
 
419
        transport = self.get_transport('stacked')
 
420
        b.bzrdir.clone_on_transport(transport, stacked_on=b.base)
 
421
        # Ensure that opening the branch doesn't raise.
 
422
        branch.Branch.open(transport.base)
 
423
 
 
424
 
 
425
class TestStackingConnections(
 
426
    transport_util.TestCaseWithConnectionHookedTransport):
 
427
 
 
428
    def setUp(self):
 
429
        super(TestStackingConnections, self).setUp()
 
430
        try:
 
431
            base_tree = self.make_branch_and_tree('base',
 
432
                                                  format=self.bzrdir_format)
 
433
        except errors.UninitializableFormat, e:
 
434
            raise TestNotApplicable(e)
 
435
        stacked = self.make_branch('stacked', format=self.bzrdir_format)
 
436
        try:
 
437
            stacked.set_stacked_on_url(base_tree.branch.base)
 
438
        except unstackable_format_errors, e:
 
439
            raise TestNotApplicable(e)
 
440
        base_tree.commit('first', rev_id='rev-base')
 
441
        stacked.set_last_revision_info(1, 'rev-base')
 
442
        stacked_relative = self.make_branch('stacked_relative',
 
443
                                            format=self.bzrdir_format)
 
444
        stacked_relative.set_stacked_on_url('../base')
 
445
        stacked.set_last_revision_info(1, 'rev-base')
 
446
        self.start_logging_connections()
 
447
 
 
448
    def test_open_stacked(self):
 
449
        b = branch.Branch.open(self.get_url('stacked'))
 
450
        rev = b.repository.get_revision('rev-base')
 
451
        self.assertEqual(1, len(self.connections))
 
452
 
 
453
    def test_open_stacked_relative(self):
 
454
        b = branch.Branch.open(self.get_url('stacked_relative'))
 
455
        rev = b.repository.get_revision('rev-base')
 
456
        self.assertEqual(1, len(self.connections))