~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/per_branch/test_branch.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:56:05 UTC
  • mfrom: (6615.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201195605-o7rl92wf6uyum3fk
(vila) Open trunk again as 2.8b1 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2012, 2016 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
"""Tests for branch implementations - tests a branch format."""
18
18
 
 
19
import contextlib
 
20
 
19
21
from bzrlib import (
20
22
    branch as _mod_branch,
21
 
    bzrdir,
 
23
    controldir,
22
24
    config,
23
25
    delta as _mod_delta,
24
26
    errors,
25
 
    gpg,
26
27
    merge,
 
28
    osutils,
27
29
    urlutils,
28
 
    transactions,
 
30
    transform,
29
31
    transport,
30
32
    remote,
31
33
    repository,
32
34
    revision,
 
35
    shelf,
 
36
    symbol_versioning,
33
37
    tests,
34
38
    )
35
 
from bzrlib.symbol_versioning import deprecated_in
36
39
from bzrlib.tests import (
37
 
    http_server,
38
40
    per_branch,
39
41
    )
40
42
from bzrlib.tests.http_server import HttpServer
77
79
 
78
80
        br = self.get_branch()
79
81
        br.fetch(wt.branch)
80
 
        br.set_revision_history(['rev1', 'rev2', 'rev3'])
81
 
        rh = br.revision_history()
82
 
        self.assertEqual(['rev1', 'rev2', 'rev3'], rh)
83
 
        for revision_id in rh:
 
82
        br.generate_revision_history('rev3')
 
83
        for revision_id in ['rev3', 'rev2', 'rev1']:
84
84
            self.assertIsInstance(revision_id, str)
85
85
        last = br.last_revision()
86
86
        self.assertEqual('rev3', last)
116
116
        tree_a.add('vla', 'file2')
117
117
        tree_a.commit('rev2', rev_id='rev2')
118
118
 
119
 
        delta = tree_a.branch.get_revision_delta(1)
 
119
        delta = self.applyDeprecated(symbol_versioning.deprecated_in(
 
120
            (2, 5, 0)), tree_a.branch.get_revision_delta, 1)
120
121
        self.assertIsInstance(delta, _mod_delta.TreeDelta)
121
122
        self.assertEqual([('foo', 'file1', 'file')], delta.added)
122
 
        delta = tree_a.branch.get_revision_delta(2)
 
123
        delta = self.applyDeprecated(symbol_versioning.deprecated_in(
 
124
            (2, 5, 0)), tree_a.branch.get_revision_delta, 2)
123
125
        self.assertIsInstance(delta, _mod_delta.TreeDelta)
124
126
        self.assertEqual([('vla', 'file2', 'file')], delta.added)
125
127
 
187
189
        self.assertEqual(branch_b.get_parent(), branch_c.get_parent())
188
190
 
189
191
        # We can also set a specific parent, and it should be honored
190
 
        random_parent = 'http://bazaar-vcs.org/path/to/branch'
 
192
        random_parent = 'http://example.com/path/to/branch'
191
193
        branch_b.set_parent(random_parent)
192
194
        repo_d = self.make_repository('d')
193
195
        branch_b.repository.copy_content_into(repo_d)
217
219
    def test_record_initial_ghost(self):
218
220
        """Branches should support having ghosts."""
219
221
        wt = self.make_branch_and_tree('.')
 
222
        if not wt.branch.repository._format.supports_ghosts:
 
223
            raise tests.TestNotApplicable("repository format does not "
 
224
                "support ghosts")
220
225
        wt.set_parent_ids(['non:existent@rev--ision--0--2'],
221
226
            allow_leftmost_as_ghost=True)
222
227
        self.assertEqual(['non:existent@rev--ision--0--2'],
230
235
    def test_record_two_ghosts(self):
231
236
        """Recording with all ghosts works."""
232
237
        wt = self.make_branch_and_tree('.')
 
238
        if not wt.branch.repository._format.supports_ghosts:
 
239
            raise tests.TestNotApplicable("repository format does not "
 
240
                "support ghosts")
233
241
        wt.set_parent_ids([
234
242
                'foo@azkhazan-123123-abcabc',
235
243
                'wibble@fofof--20050401--1928390812',
247
255
                          self.get_branch().repository.get_revision,
248
256
                          None)
249
257
 
250
 
# TODO 20051003 RBC:
251
 
# compare the gpg-to-sign info for a commit with a ghost and
252
 
#     an identical tree without a ghost
253
 
# fetch missing should rewrite the TOC of weaves to list newly available parents.
254
 
 
255
 
    def test_sign_existing_revision(self):
256
 
        wt = self.make_branch_and_tree('.')
257
 
        branch = wt.branch
258
 
        wt.commit("base", allow_pointless=True, rev_id='A')
259
 
        from bzrlib.testament import Testament
260
 
        strategy = gpg.LoopbackGPGStrategy(None)
261
 
        branch.repository.lock_write()
262
 
        branch.repository.start_write_group()
263
 
        branch.repository.sign_revision('A', strategy)
264
 
        branch.repository.commit_write_group()
265
 
        branch.repository.unlock()
266
 
        self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n' +
267
 
                         Testament.from_revision(branch.repository,
268
 
                         'A').as_short_text() +
269
 
                         '-----END PSEUDO-SIGNED CONTENT-----\n',
270
 
                         branch.repository.get_signature_text('A'))
271
 
 
272
 
    def test_store_signature(self):
273
 
        wt = self.make_branch_and_tree('.')
274
 
        branch = wt.branch
275
 
        branch.lock_write()
276
 
        try:
277
 
            branch.repository.start_write_group()
278
 
            try:
279
 
                branch.repository.store_revision_signature(
280
 
                    gpg.LoopbackGPGStrategy(None), 'FOO', 'A')
281
 
            except:
282
 
                branch.repository.abort_write_group()
283
 
                raise
284
 
            else:
285
 
                branch.repository.commit_write_group()
286
 
        finally:
287
 
            branch.unlock()
288
 
        # A signature without a revision should not be accessible.
289
 
        self.assertRaises(errors.NoSuchRevision,
290
 
                          branch.repository.has_signature_for_revision_id,
291
 
                          'A')
292
 
        wt.commit("base", allow_pointless=True, rev_id='A')
293
 
        self.assertEqual('-----BEGIN PSEUDO-SIGNED CONTENT-----\n'
294
 
                         'FOO-----END PSEUDO-SIGNED CONTENT-----\n',
295
 
                         branch.repository.get_signature_text('A'))
296
 
 
297
 
    def test_branch_keeps_signatures(self):
298
 
        wt = self.make_branch_and_tree('source')
299
 
        wt.commit('A', allow_pointless=True, rev_id='A')
300
 
        repo = wt.branch.repository
301
 
        repo.lock_write()
302
 
        repo.start_write_group()
303
 
        repo.sign_revision('A', gpg.LoopbackGPGStrategy(None))
304
 
        repo.commit_write_group()
305
 
        repo.unlock()
306
 
        #FIXME: clone should work to urls,
307
 
        # wt.clone should work to disks.
308
 
        self.build_tree(['target/'])
309
 
        d2 = repo.bzrdir.clone(urlutils.local_path_to_url('target'))
310
 
        self.assertEqual(repo.get_signature_text('A'),
311
 
                         d2.open_repository().get_signature_text('A'))
312
 
 
313
 
    def test_missing_revisions(self):
314
 
        t1 = self.make_branch_and_tree('b1')
315
 
        rev1 = t1.commit('one')
316
 
        t2 = t1.bzrdir.sprout('b2').open_workingtree()
317
 
        rev2 = t1.commit('two')
318
 
        rev3 = t1.commit('three')
319
 
 
320
 
        self.assertEqual([rev2, rev3],
321
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
322
 
            t2.branch.missing_revisions, t1.branch))
323
 
 
324
 
        self.assertEqual([],
325
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
326
 
            t2.branch.missing_revisions, t1.branch, stop_revision=1))
327
 
        self.assertEqual([rev2],
328
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
329
 
            t2.branch.missing_revisions, t1.branch, stop_revision=2))
330
 
        self.assertEqual([rev2, rev3],
331
 
            self.applyDeprecated(deprecated_in((1, 6, 0)),
332
 
            t2.branch.missing_revisions, t1.branch, stop_revision=3))
333
 
 
334
 
        self.assertRaises(errors.NoSuchRevision,
335
 
            self.applyDeprecated, deprecated_in((1, 6, 0)),
336
 
            t2.branch.missing_revisions, t1.branch, stop_revision=4)
337
 
 
338
 
        rev4 = t2.commit('four')
339
 
        self.assertRaises(errors.DivergedBranches,
340
 
            self.applyDeprecated, deprecated_in((1, 6, 0)),
341
 
            t2.branch.missing_revisions, t1.branch)
342
 
 
343
 
    def test_nicks(self):
344
 
        """Test explicit and implicit branch nicknames.
 
258
    def test_nicks_bzr(self):
 
259
        """Test the behaviour of branch nicks specific to bzr branches.
345
260
 
346
261
        Nicknames are implicitly the name of the branch's directory, unless an
347
262
        explicit nickname is set.  That is, an explicit nickname always
348
263
        overrides the implicit one.
 
264
 
349
265
        """
350
 
        t = transport.get_transport(self.get_url())
 
266
        t = self.get_transport()
351
267
        branch = self.make_branch('bzr.dev')
 
268
        if not isinstance(branch, _mod_branch.BzrBranch):
 
269
            raise tests.TestNotApplicable("not a bzr branch format")
352
270
        # The nick will be 'bzr.dev', because there is no explicit nick set.
353
271
        self.assertEqual(branch.nick, 'bzr.dev')
354
272
        # Move the branch to a different directory, 'bzr.ab'.  Now that branch
360
278
        # config file in the branch.
361
279
        branch.nick = "Aaron's branch"
362
280
        if not isinstance(branch, remote.RemoteBranch):
363
 
            self.failUnless(branch._transport.has("branch.conf"))
 
281
            self.assertTrue(branch._transport.has("branch.conf"))
364
282
        # Because the nick has been set explicitly, the nick is now always
365
283
        # "Aaron's branch", regardless of directory name.
366
284
        self.assertEqual(branch.nick, "Aaron's branch")
370
288
        branch.nick = u"\u1234"
371
289
        self.assertEqual(branch.nick, u"\u1234")
372
290
 
 
291
    def test_nicks(self):
 
292
        """Test explicit and implicit branch nicknames.
 
293
 
 
294
        A nickname is always available, whether set explicitly or not.
 
295
        """
 
296
        t = self.get_transport()
 
297
        branch = self.make_branch('bzr.dev')
 
298
        # An implicit nick name is set; what it is exactly depends on the
 
299
        # format.
 
300
        self.assertIsInstance(branch.nick, basestring)
 
301
        # Set the branch nick explicitly.
 
302
        branch.nick = "Aaron's branch"
 
303
        # Because the nick has been set explicitly, the nick is now always
 
304
        # "Aaron's branch".
 
305
        self.assertEqual(branch.nick, "Aaron's branch")
 
306
        branch.nick = u"\u1234"
 
307
        self.assertEqual(branch.nick, u"\u1234")
 
308
 
373
309
    def test_commit_nicks(self):
374
310
        """Nicknames are committed to the revision"""
375
311
        wt = self.make_branch_and_tree('bzr.dev')
385
321
            repo = self.make_repository('.', shared=True)
386
322
        except errors.IncompatibleFormat:
387
323
            return
388
 
        self.assertEquals(0, len(repo.bzrdir.list_branches()))
 
324
        if repo.bzrdir._format.colocated_branches:
 
325
            raise tests.TestNotApplicable(
 
326
                "control dir does not support colocated branches")
 
327
        self.assertEqual(0, len(repo.bzrdir.list_branches()))
 
328
        if not self.bzrdir_format.colocated_branches:
 
329
            raise tests.TestNotApplicable("control dir format does not support "
 
330
                "colocated branches")
389
331
        try:
390
332
            child_branch1 = self.branch_format.initialize(repo.bzrdir, 
391
333
                name='branch1')
392
 
        except (errors.UninitializableFormat, errors.NoColocatedBranchSupport):
 
334
        except errors.UninitializableFormat:
393
335
            # branch references are not default init'able and
394
336
            # not all bzrdirs support colocated branches.
395
337
            return
396
 
        self.assertEquals(1, len(repo.bzrdir.list_branches()))
 
338
        self.assertEqual(1, len(repo.bzrdir.list_branches()))
397
339
        self.branch_format.initialize(repo.bzrdir, name='branch2')
398
 
        self.assertEquals(2, len(repo.bzrdir.list_branches()))
 
340
        self.assertEqual(2, len(repo.bzrdir.list_branches()))
 
341
 
 
342
    def test_create_append_revisions_only(self):
 
343
        try:
 
344
            repo = self.make_repository('.', shared=True)
 
345
        except errors.IncompatibleFormat:
 
346
            return
 
347
        for val in (True, False):
 
348
            try:
 
349
                branch = self.branch_format.initialize(repo.bzrdir,
 
350
                    append_revisions_only=True)
 
351
            except (errors.UninitializableFormat, errors.UpgradeRequired):
 
352
                # branch references are not default init'able and
 
353
                # not all branches support append_revisions_only
 
354
                return
 
355
            self.assertEqual(True, branch.get_append_revisions_only())
 
356
            repo.bzrdir.destroy_branch()
 
357
 
 
358
    def test_get_set_append_revisions_only(self):
 
359
        branch = self.make_branch('.')
 
360
        if branch._format.supports_set_append_revisions_only():
 
361
            branch.set_append_revisions_only(True)
 
362
            self.assertTrue(branch.get_append_revisions_only())
 
363
            branch.set_append_revisions_only(False)
 
364
            self.assertFalse(branch.get_append_revisions_only())
 
365
        else:
 
366
            self.assertRaises(errors.UpgradeRequired,
 
367
                branch.set_append_revisions_only, True)
 
368
            self.assertFalse(branch.get_append_revisions_only())
399
369
 
400
370
    def test_create_open_branch_uses_repository(self):
401
371
        try:
402
372
            repo = self.make_repository('.', shared=True)
403
373
        except errors.IncompatibleFormat:
404
 
            return
 
374
            raise tests.TestNotApplicable("requires shared repository support")
405
375
        child_transport = repo.bzrdir.root_transport.clone('child')
406
376
        child_transport.mkdir('.')
407
 
        child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
 
377
        try:
 
378
            child_dir = self.bzrdir_format.initialize_on_transport(child_transport)
 
379
        except errors.UninitializableFormat:
 
380
            raise tests.TestNotApplicable("control dir format not initializable")
408
381
        try:
409
382
            child_branch = self.branch_format.initialize(child_dir)
410
383
        except errors.UninitializableFormat:
419
392
    def test_format_description(self):
420
393
        tree = self.make_branch_and_tree('tree')
421
394
        text = tree.branch._format.get_format_description()
422
 
        self.failUnless(len(text))
 
395
        self.assertTrue(len(text))
423
396
 
424
397
    def test_get_commit_builder(self):
425
398
        branch = self.make_branch(".")
433
406
        """Create a fake revision history easily."""
434
407
        tree = self.make_branch_and_tree('.')
435
408
        rev1 = tree.commit('foo')
436
 
        orig_history = tree.branch.revision_history()
 
409
        tree.lock_write()
 
410
        self.addCleanup(tree.unlock)
 
411
        graph = tree.branch.repository.get_graph()
 
412
        orig_history = list(
 
413
            graph.iter_lefthand_ancestry(
 
414
                tree.branch.last_revision(), [revision.NULL_REVISION]))
437
415
        rev2 = tree.commit('bar', allow_pointless=True)
438
416
        tree.branch.generate_revision_history(rev1)
439
 
        self.assertEqual(orig_history, tree.branch.revision_history())
 
417
        self.assertEqual(orig_history, list(
 
418
            graph.iter_lefthand_ancestry(
 
419
                tree.branch.last_revision(), [revision.NULL_REVISION])))
440
420
 
441
421
    def test_generate_revision_history_NULL_REVISION(self):
442
422
        tree = self.make_branch_and_tree('.')
443
423
        rev1 = tree.commit('foo')
 
424
        tree.lock_write()
 
425
        self.addCleanup(tree.unlock)
444
426
        tree.branch.generate_revision_history(revision.NULL_REVISION)
445
 
        self.assertEqual([], tree.branch.revision_history())
 
427
        self.assertEqual(revision.NULL_REVISION, tree.branch.last_revision())
446
428
 
447
429
    def test_create_checkout(self):
448
430
        tree_a = self.make_branch_and_tree('a')
469
451
        tree_a = self.make_branch_and_tree('a')
470
452
        rev_id = tree_a.commit('put some content in the branch')
471
453
        # open the branch via a readonly transport
472
 
        source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
 
454
        url = self.get_readonly_url(urlutils.basename(tree_a.branch.base))
 
455
        t = transport.get_transport_from_url(url)
 
456
        if not tree_a.branch.bzrdir._format.supports_transport(t):
 
457
            raise tests.TestNotApplicable("format does not support transport")
 
458
        source_branch = _mod_branch.Branch.open(url)
473
459
        # sanity check that the test will be valid
474
460
        self.assertRaises((errors.LockError, errors.TransportNotPossible),
475
461
            source_branch.lock_write)
481
467
        tree_a = self.make_branch_and_tree('a')
482
468
        rev_id = tree_a.commit('put some content in the branch')
483
469
        # open the branch via a readonly transport
484
 
        source_branch = _mod_branch.Branch.open(self.get_readonly_url('a'))
 
470
        url = self.get_readonly_url(
 
471
            osutils.basename(tree_a.branch.base.rstrip('/')))
 
472
        t = transport.get_transport_from_url(url)
 
473
        if not tree_a.branch.bzrdir._format.supports_transport(t):
 
474
            raise tests.TestNotApplicable("format does not support transport")
 
475
        source_branch = _mod_branch.Branch.open(url)
485
476
        # sanity check that the test will be valid
486
477
        self.assertRaises((errors.LockError, errors.TransportNotPossible),
487
478
            source_branch.lock_write)
488
479
        checkout = source_branch.create_checkout('c')
489
480
        self.assertEqual(rev_id, checkout.last_revision())
490
481
 
491
 
    def test_set_revision_history(self):
492
 
        tree = self.make_branch_and_tree('a')
493
 
        tree.commit('a commit', rev_id='rev1')
494
 
        br = tree.branch
495
 
        br.set_revision_history(["rev1"])
496
 
        self.assertEquals(br.revision_history(), ["rev1"])
497
 
        br.set_revision_history([])
498
 
        self.assertEquals(br.revision_history(), [])
 
482
    def test_heads_to_fetch(self):
 
483
        # heads_to_fetch is a method that returns a collection of revids that
 
484
        # need to be fetched to copy this branch into another repo.  At a
 
485
        # minimum this will include the tip.
 
486
        # (In native formats, this is the tip + tags, but other formats may
 
487
        # have other revs needed)
 
488
        tree = self.make_branch_and_tree('a')
 
489
        tree.commit('first commit', rev_id='rev1')
 
490
        tree.commit('second commit', rev_id='rev2')
 
491
        must_fetch, should_fetch = tree.branch.heads_to_fetch()
 
492
        self.assertTrue('rev2' in must_fetch)
 
493
 
 
494
    def test_heads_to_fetch_not_null_revision(self):
 
495
        # NULL_REVISION does not appear in the result of heads_to_fetch, even
 
496
        # for an empty branch.
 
497
        tree = self.make_branch_and_tree('a')
 
498
        must_fetch, should_fetch = tree.branch.heads_to_fetch()
 
499
        self.assertFalse(revision.NULL_REVISION in must_fetch)
 
500
        self.assertFalse(revision.NULL_REVISION in should_fetch)
499
501
 
500
502
 
501
503
class TestBranchFormat(per_branch.TestCaseWithBranch):
519
521
            looked_up_format = registry.get(network_name)
520
522
            self.assertEqual(format.__class__, looked_up_format.__class__)
521
523
 
 
524
    def test_get_config_calls(self):
 
525
        # Smoke test that all branch succeed getting a config
 
526
        br = self.make_branch('.')
 
527
        br.get_config()
 
528
        br.get_config_stack()
 
529
 
522
530
 
523
531
class ChrootedTests(per_branch.TestCaseWithBranch):
524
532
    """A support class that provides readonly urls outside the local namespace.
541
549
                          _mod_branch.Branch.open_containing,
542
550
                          self.get_readonly_url('g/p/q'))
543
551
        branch = self.make_branch('.')
 
552
        if not branch.bzrdir._format.supports_transport(
 
553
            transport.get_transport_from_url(self.get_readonly_url('.'))):
 
554
            raise tests.TestNotApplicable("format does not support transport")
544
555
        branch, relpath = _mod_branch.Branch.open_containing(
545
556
            self.get_readonly_url(''))
546
557
        self.assertEqual('', relpath)
632
643
class TestChildSubmitFormats(per_branch.TestCaseWithBranch):
633
644
 
634
645
    def test_get_child_submit_format_default(self):
635
 
        self.assertEqual(None, self.get_branch().get_child_submit_format())
 
646
        submit_format = self.get_branch().get_child_submit_format()
 
647
        self.assertTrue(submit_format is None or
 
648
                        isinstance(submit_format, str))
636
649
 
637
650
    def test_get_child_submit_format(self):
638
651
        branch = self.get_branch()
639
 
        branch.get_config().set_user_option('child_submit_format', '10')
 
652
        branch.get_config_stack().set('child_submit_format', '10')
640
653
        branch = self.get_branch()
641
654
        self.assertEqual('10', branch.get_child_submit_format())
642
655
 
682
695
            # they may not be initializable.
683
696
            return
684
697
        # supported formats must be able to init and open
685
 
        t = transport.get_transport(self.get_url())
686
 
        readonly_t = transport.get_transport(self.get_readonly_url())
 
698
        t = self.get_transport()
 
699
        readonly_t = transport.get_transport_from_url(self.get_readonly_url())
687
700
        made_branch = self.make_branch('.')
688
 
        self.failUnless(isinstance(made_branch, _mod_branch.Branch))
 
701
        self.assertIsInstance(made_branch, _mod_branch.Branch)
689
702
 
690
703
        # find it via bzrdir opening:
691
 
        opened_control = bzrdir.BzrDir.open(readonly_t.base)
 
704
        opened_control = controldir.ControlDir.open(readonly_t.base)
692
705
        direct_opened_branch = opened_control.open_branch()
693
706
        self.assertEqual(direct_opened_branch.__class__, made_branch.__class__)
694
707
        self.assertEqual(opened_control, direct_opened_branch.bzrdir)
695
 
        self.failUnless(isinstance(direct_opened_branch._format,
696
 
                        self.branch_format.__class__))
 
708
        self.assertIsInstance(direct_opened_branch._format,
 
709
            self.branch_format.__class__)
697
710
 
698
711
        # find it via Branch.open
699
712
        opened_branch = _mod_branch.Branch.open(readonly_t.base)
700
 
        self.failUnless(isinstance(opened_branch, made_branch.__class__))
 
713
        self.assertIsInstance(opened_branch, made_branch.__class__)
701
714
        self.assertEqual(made_branch._format.__class__,
702
715
                         opened_branch._format.__class__)
703
716
        # if it has a unique id string, can we probe for it ?
746
759
        except errors.UpgradeRequired:
747
760
            raise tests.TestNotApplicable('Format does not support binding')
748
761
 
 
762
    def test_unbind_clears_cached_master_branch(self):
 
763
        """b.unbind clears any cached value of b.get_master_branch."""
 
764
        master = self.make_branch('master')
 
765
        branch = self.make_branch('branch')
 
766
        try:
 
767
            branch.bind(master)
 
768
        except errors.UpgradeRequired:
 
769
            raise tests.TestNotApplicable('Format does not support binding')
 
770
        self.addCleanup(branch.lock_write().unlock)
 
771
        self.assertNotEqual(None, branch.get_master_branch())
 
772
        branch.unbind()
 
773
        self.assertEqual(None, branch.get_master_branch())
 
774
 
 
775
    def test_bind_clears_cached_master_branch(self):
 
776
        """b.bind clears any cached value of b.get_master_branch."""
 
777
        master1 = self.make_branch('master1')
 
778
        master2 = self.make_branch('master2')
 
779
        branch = self.make_branch('branch')
 
780
        try:
 
781
            branch.bind(master1)
 
782
        except errors.UpgradeRequired:
 
783
            raise tests.TestNotApplicable('Format does not support binding')
 
784
        self.addCleanup(branch.lock_write().unlock)
 
785
        self.assertNotEqual(None, branch.get_master_branch())
 
786
        branch.bind(master2)
 
787
        self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
 
788
                branch.get_master_branch().base))
 
789
 
 
790
    def test_set_bound_location_clears_cached_master_branch(self):
 
791
        """b.set_bound_location clears any cached value of b.get_master_branch.
 
792
        """
 
793
        master1 = self.make_branch('master1')
 
794
        master2 = self.make_branch('master2')
 
795
        branch = self.make_branch('branch')
 
796
        try:
 
797
            branch.bind(master1)
 
798
        except errors.UpgradeRequired:
 
799
            raise tests.TestNotApplicable('Format does not support binding')
 
800
        self.addCleanup(branch.lock_write().unlock)
 
801
        self.assertNotEqual(None, branch.get_master_branch())
 
802
        branch.set_bound_location(self.get_url('master2'))
 
803
        self.assertEqual('.', urlutils.relative_url(self.get_url('master2'),
 
804
                branch.get_master_branch().base))
 
805
 
749
806
 
750
807
class TestStrict(per_branch.TestCaseWithBranch):
751
808
 
785
842
    def test_fallbacks_not_opened(self):
786
843
        stacked = self.make_branch_with_fallback()
787
844
        self.get_transport('').rename('fallback', 'moved')
788
 
        reopened = stacked.bzrdir.open_branch(ignore_fallbacks=True)
 
845
        reopened_dir = controldir.ControlDir.open(stacked.base)
 
846
        reopened = reopened_dir.open_branch(ignore_fallbacks=True)
789
847
        self.assertEqual([], reopened.repository._fallback_repositories)
790
848
 
791
849
    def test_fallbacks_are_opened(self):
792
850
        stacked = self.make_branch_with_fallback()
793
 
        reopened = stacked.bzrdir.open_branch(ignore_fallbacks=False)
 
851
        reopened_dir = controldir.ControlDir.open(stacked.base)
 
852
        reopened = reopened_dir.open_branch(ignore_fallbacks=False)
794
853
        self.assertLength(1, reopened.repository._fallback_repositories)
795
854
 
796
855
 
804
863
            tree.add_reference(subtree)
805
864
        except errors.UnsupportedOperation:
806
865
            raise tests.TestNotApplicable('Tree cannot hold references.')
807
 
        reference_parent = tree.branch.reference_parent('subtree-id',
808
 
                                                        'subtree')
 
866
        reference_parent = tree.branch.reference_parent(
 
867
            'subtree-id',
 
868
            urlutils.relative_url(tree.branch.user_url, subtree.branch.user_url))
809
869
        self.assertEqual(subtree.branch.base, reference_parent.base)
810
870
 
811
871
    def test_reference_parent_accepts_possible_transports(self):
817
877
        except errors.UnsupportedOperation:
818
878
            raise tests.TestNotApplicable('Tree cannot hold references.')
819
879
        reference_parent = tree.branch.reference_parent('subtree-id',
820
 
            'subtree', possible_transports=[subtree.bzrdir.root_transport])
 
880
            urlutils.relative_url(
 
881
                tree.branch.user_url, subtree.branch.user_url),
 
882
            possible_transports=[subtree.bzrdir.root_transport])
821
883
 
822
884
    def test_get_reference_info(self):
823
885
        branch = self.make_branch('branch')
990
1052
 
991
1053
class TestBranchControlComponent(per_branch.TestCaseWithBranch):
992
1054
    """Branch implementations adequately implement ControlComponent."""
993
 
    
 
1055
 
994
1056
    def test_urls(self):
995
1057
        br = self.make_branch('branch')
996
1058
        self.assertIsInstance(br.user_url, str)
999
1061
        # above the control dir but we might need to relax that?
1000
1062
        self.assertEqual(br.control_url.find(br.user_url), 0)
1001
1063
        self.assertEqual(br.control_url, br.control_transport.base)
 
1064
 
 
1065
 
 
1066
class FakeShelfCreator(object):
 
1067
 
 
1068
    def __init__(self, branch):
 
1069
        self.branch = branch
 
1070
 
 
1071
    def write_shelf(self, shelf_file, message=None):
 
1072
        tree = self.branch.repository.revision_tree(revision.NULL_REVISION)
 
1073
        with transform.TransformPreview(tree) as tt:
 
1074
            shelf.ShelfCreator._write_shelf(
 
1075
                shelf_file, tt, revision.NULL_REVISION)
 
1076
 
 
1077
 
 
1078
@contextlib.contextmanager
 
1079
def skip_if_storing_uncommitted_unsupported():
 
1080
    try:
 
1081
        yield
 
1082
    except errors.StoringUncommittedNotSupported:
 
1083
        raise tests.TestNotApplicable('Cannot store uncommitted changes.')
 
1084
 
 
1085
 
 
1086
class TestUncommittedChanges(per_branch.TestCaseWithBranch):
 
1087
 
 
1088
    def bind(self, branch, master):
 
1089
        try:
 
1090
            branch.bind(master)
 
1091
        except errors.UpgradeRequired:
 
1092
            raise tests.TestNotApplicable('Branch cannot be bound.')
 
1093
 
 
1094
    def test_store_uncommitted(self):
 
1095
        tree = self.make_branch_and_tree('b')
 
1096
        branch = tree.branch
 
1097
        creator = FakeShelfCreator(branch)
 
1098
        with skip_if_storing_uncommitted_unsupported():
 
1099
            self.assertIs(None, branch.get_unshelver(tree))
 
1100
        branch.store_uncommitted(creator)
 
1101
        self.assertIsNot(None, branch.get_unshelver(tree))
 
1102
 
 
1103
    def test_store_uncommitted_bound(self):
 
1104
        tree = self.make_branch_and_tree('b')
 
1105
        branch = tree.branch
 
1106
        master = self.make_branch('master')
 
1107
        self.bind(branch, master)
 
1108
        creator = FakeShelfCreator(tree.branch)
 
1109
        self.assertIs(None, tree.branch.get_unshelver(tree))
 
1110
        self.assertIs(None, master.get_unshelver(tree))
 
1111
        tree.branch.store_uncommitted(creator)
 
1112
        self.assertIsNot(None, master.get_unshelver(tree))
 
1113
 
 
1114
    def test_store_uncommitted_already_stored(self):
 
1115
        branch = self.make_branch('b')
 
1116
        with skip_if_storing_uncommitted_unsupported():
 
1117
            branch.store_uncommitted(FakeShelfCreator(branch))
 
1118
        self.assertRaises(errors.ChangesAlreadyStored,
 
1119
                          branch.store_uncommitted, FakeShelfCreator(branch))
 
1120
 
 
1121
    def test_store_uncommitted_none(self):
 
1122
        branch = self.make_branch('b')
 
1123
        with skip_if_storing_uncommitted_unsupported():
 
1124
            branch.store_uncommitted(FakeShelfCreator(branch))
 
1125
        branch.store_uncommitted(None)
 
1126
        self.assertIs(None, branch.get_unshelver(None))
 
1127
 
 
1128
    def test_get_unshelver(self):
 
1129
        tree = self.make_branch_and_tree('tree')
 
1130
        tree.commit('')
 
1131
        self.build_tree_contents([('tree/file', 'contents1')])
 
1132
        tree.add('file')
 
1133
        with skip_if_storing_uncommitted_unsupported():
 
1134
            tree.store_uncommitted()
 
1135
        unshelver = tree.branch.get_unshelver(tree)
 
1136
        self.assertIsNot(None, unshelver)
 
1137
 
 
1138
    def test_get_unshelver_bound(self):
 
1139
        tree = self.make_branch_and_tree('tree')
 
1140
        tree.commit('')
 
1141
        self.build_tree_contents([('tree/file', 'contents1')])
 
1142
        tree.add('file')
 
1143
        with skip_if_storing_uncommitted_unsupported():
 
1144
            tree.store_uncommitted()
 
1145
        branch = self.make_branch('branch')
 
1146
        self.bind(branch, tree.branch)
 
1147
        unshelver = branch.get_unshelver(tree)
 
1148
        self.assertIsNot(None, unshelver)