~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_bzrdir.py

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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
24
24
import sys
25
25
 
26
26
from bzrlib import (
 
27
    branch,
27
28
    bzrdir,
 
29
    config,
 
30
    controldir,
28
31
    errors,
29
32
    help_topics,
 
33
    lock,
30
34
    repository,
 
35
    revision as _mod_revision,
31
36
    osutils,
32
37
    remote,
 
38
    symbol_versioning,
 
39
    transport as _mod_transport,
33
40
    urlutils,
34
41
    win32utils,
35
 
    workingtree,
 
42
    workingtree_3,
 
43
    workingtree_4,
36
44
    )
37
45
import bzrlib.branch
38
 
from bzrlib.errors import (NotBranchError,
39
 
                           NoColocatedBranchSupport,
40
 
                           UnknownFormatError,
41
 
                           UnsupportedFormatError,
42
 
                           )
 
46
from bzrlib.errors import (
 
47
    NotBranchError,
 
48
    NoColocatedBranchSupport,
 
49
    UnknownFormatError,
 
50
    UnsupportedFormatError,
 
51
    )
43
52
from bzrlib.tests import (
44
53
    TestCase,
45
54
    TestCaseWithMemoryTransport,
52
61
    )
53
62
from bzrlib.tests.test_http import TestWithTransport_pycurl
54
63
from bzrlib.transport import (
55
 
    get_transport,
56
64
    memory,
 
65
    pathfilter,
57
66
    )
58
67
from bzrlib.transport.http._urllib import HttpTransport_urllib
59
68
from bzrlib.transport.nosmart import NoSmartTransportDecorator
60
69
from bzrlib.transport.readonly import ReadonlyTransportDecorator
61
 
from bzrlib.repofmt import knitrepo, weaverepo, pack_repo
 
70
from bzrlib.repofmt import knitrepo, knitpack_repo
62
71
 
63
72
 
64
73
class TestDefaultFormat(TestCase):
65
74
 
66
75
    def test_get_set_default_format(self):
67
76
        old_format = bzrdir.BzrDirFormat.get_default_format()
68
 
        # default is BzrDirFormat6
69
 
        self.failUnless(isinstance(old_format, bzrdir.BzrDirMetaFormat1))
70
 
        bzrdir.BzrDirFormat._set_default_format(SampleBzrDirFormat())
 
77
        # default is BzrDirMetaFormat1
 
78
        self.assertIsInstance(old_format, bzrdir.BzrDirMetaFormat1)
 
79
        controldir.ControlDirFormat._set_default_format(SampleBzrDirFormat())
71
80
        # creating a bzr dir should now create an instrumented dir.
72
81
        try:
73
82
            result = bzrdir.BzrDir.create('memory:///')
74
 
            self.failUnless(isinstance(result, SampleBzrDir))
 
83
            self.assertIsInstance(result, SampleBzrDir)
75
84
        finally:
76
 
            bzrdir.BzrDirFormat._set_default_format(old_format)
 
85
            controldir.ControlDirFormat._set_default_format(old_format)
77
86
        self.assertEqual(old_format, bzrdir.BzrDirFormat.get_default_format())
78
87
 
79
88
 
 
89
class DeprecatedBzrDirFormat(bzrdir.BzrDirFormat):
 
90
    """A deprecated bzr dir format."""
 
91
 
 
92
 
80
93
class TestFormatRegistry(TestCase):
81
94
 
82
95
    def make_format_registry(self):
83
 
        my_format_registry = bzrdir.BzrDirFormatRegistry()
84
 
        my_format_registry.register('weave', bzrdir.BzrDirFormat6,
85
 
            'Pre-0.8 format.  Slower and does not support checkouts or shared'
86
 
            ' repositories', deprecated=True)
87
 
        my_format_registry.register_lazy('lazy', 'bzrlib.bzrdir',
88
 
            'BzrDirFormat6', 'Format registered lazily', deprecated=True)
89
 
        my_format_registry.register_metadir('knit',
 
96
        my_format_registry = controldir.ControlDirFormatRegistry()
 
97
        my_format_registry.register('deprecated', DeprecatedBzrDirFormat,
 
98
            'Some format.  Slower and unawesome and deprecated.',
 
99
            deprecated=True)
 
100
        my_format_registry.register_lazy('lazy', 'bzrlib.tests.test_bzrdir',
 
101
            'DeprecatedBzrDirFormat', 'Format registered lazily',
 
102
            deprecated=True)
 
103
        bzrdir.register_metadir(my_format_registry, 'knit',
90
104
            'bzrlib.repofmt.knitrepo.RepositoryFormatKnit1',
91
105
            'Format using knits',
92
106
            )
93
107
        my_format_registry.set_default('knit')
94
 
        my_format_registry.register_metadir(
 
108
        bzrdir.register_metadir(my_format_registry,
95
109
            'branch6',
96
110
            'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
97
111
            'Experimental successor to knit.  Use at your own risk.',
98
112
            branch_format='bzrlib.branch.BzrBranchFormat6',
99
113
            experimental=True)
100
 
        my_format_registry.register_metadir(
 
114
        bzrdir.register_metadir(my_format_registry,
101
115
            'hidden format',
102
116
            'bzrlib.repofmt.knitrepo.RepositoryFormatKnit3',
103
117
            'Experimental successor to knit.  Use at your own risk.',
104
118
            branch_format='bzrlib.branch.BzrBranchFormat6', hidden=True)
105
 
        my_format_registry.register('hiddenweave', bzrdir.BzrDirFormat6,
106
 
            'Pre-0.8 format.  Slower and does not support checkouts or shared'
107
 
            ' repositories', hidden=True)
108
 
        my_format_registry.register_lazy('hiddenlazy', 'bzrlib.bzrdir',
109
 
            'BzrDirFormat6', 'Format registered lazily', deprecated=True,
110
 
            hidden=True)
 
119
        my_format_registry.register('hiddendeprecated', DeprecatedBzrDirFormat,
 
120
            'Old format.  Slower and does not support things. ', hidden=True)
 
121
        my_format_registry.register_lazy('hiddenlazy', 'bzrlib.tests.test_bzrdir',
 
122
            'DeprecatedBzrDirFormat', 'Format registered lazily',
 
123
            deprecated=True, hidden=True)
111
124
        return my_format_registry
112
125
 
113
126
    def test_format_registry(self):
114
127
        my_format_registry = self.make_format_registry()
115
128
        my_bzrdir = my_format_registry.make_bzrdir('lazy')
116
 
        self.assertIsInstance(my_bzrdir, bzrdir.BzrDirFormat6)
117
 
        my_bzrdir = my_format_registry.make_bzrdir('weave')
118
 
        self.assertIsInstance(my_bzrdir, bzrdir.BzrDirFormat6)
 
129
        self.assertIsInstance(my_bzrdir, DeprecatedBzrDirFormat)
 
130
        my_bzrdir = my_format_registry.make_bzrdir('deprecated')
 
131
        self.assertIsInstance(my_bzrdir, DeprecatedBzrDirFormat)
119
132
        my_bzrdir = my_format_registry.make_bzrdir('default')
120
133
        self.assertIsInstance(my_bzrdir.repository_format,
121
134
            knitrepo.RepositoryFormatKnit1)
134
147
                         my_format_registry.get_help('knit'))
135
148
        self.assertEqual('Format using knits',
136
149
                         my_format_registry.get_help('default'))
137
 
        self.assertEqual('Pre-0.8 format.  Slower and does not support'
138
 
                         ' checkouts or shared repositories',
139
 
                         my_format_registry.get_help('weave'))
 
150
        self.assertEqual('Some format.  Slower and unawesome and deprecated.',
 
151
                         my_format_registry.get_help('deprecated'))
140
152
 
141
153
    def test_help_topic(self):
142
154
        topics = help_topics.HelpTopicRegistry()
166
178
            self.assertIs(bzrdir.format_registry.get('dirstate-with-subtree'),
167
179
                          bzrdir.format_registry.get('default'))
168
180
            self.assertIs(
169
 
                repository.RepositoryFormat.get_default_format().__class__,
 
181
                repository.format_registry.get_default().__class__,
170
182
                knitrepo.RepositoryFormatKnit3)
171
183
        finally:
172
184
            bzrdir.format_registry.set_default_repository(old_default)
173
185
 
174
186
    def test_aliases(self):
175
 
        a_registry = bzrdir.BzrDirFormatRegistry()
176
 
        a_registry.register('weave', bzrdir.BzrDirFormat6,
177
 
            'Pre-0.8 format.  Slower and does not support checkouts or shared'
178
 
            ' repositories', deprecated=True)
179
 
        a_registry.register('weavealias', bzrdir.BzrDirFormat6,
180
 
            'Pre-0.8 format.  Slower and does not support checkouts or shared'
181
 
            ' repositories', deprecated=True, alias=True)
182
 
        self.assertEqual(frozenset(['weavealias']), a_registry.aliases())
 
187
        a_registry = controldir.ControlDirFormatRegistry()
 
188
        a_registry.register('deprecated', DeprecatedBzrDirFormat,
 
189
            'Old format.  Slower and does not support stuff',
 
190
            deprecated=True)
 
191
        a_registry.register('deprecatedalias', DeprecatedBzrDirFormat,
 
192
            'Old format.  Slower and does not support stuff',
 
193
            deprecated=True, alias=True)
 
194
        self.assertEqual(frozenset(['deprecatedalias']), a_registry.aliases())
183
195
 
184
196
 
185
197
class SampleBranch(bzrlib.branch.Branch):
241
253
    def open(self, transport, _found=None):
242
254
        return "opened branch."
243
255
 
 
256
    @classmethod
 
257
    def from_string(cls, format_string):
 
258
        return cls()
 
259
 
 
260
 
 
261
class BzrDirFormatTest1(bzrdir.BzrDirMetaFormat1):
 
262
 
 
263
    @staticmethod
 
264
    def get_format_string():
 
265
        return "Test format 1"
 
266
 
 
267
 
 
268
class BzrDirFormatTest2(bzrdir.BzrDirMetaFormat1):
 
269
 
 
270
    @staticmethod
 
271
    def get_format_string():
 
272
        return "Test format 2"
 
273
 
244
274
 
245
275
class TestBzrDirFormat(TestCaseWithTransport):
246
276
    """Tests for the BzrDirFormat facility."""
248
278
    def test_find_format(self):
249
279
        # is the right format object found for a branch?
250
280
        # create a branch with a few known format objects.
251
 
        # this is not quite the same as
252
 
        t = get_transport(self.get_url())
 
281
        bzrdir.BzrProber.formats.register(BzrDirFormatTest1.get_format_string(),
 
282
            BzrDirFormatTest1())
 
283
        self.addCleanup(bzrdir.BzrProber.formats.remove,
 
284
            BzrDirFormatTest1.get_format_string())
 
285
        bzrdir.BzrProber.formats.register(BzrDirFormatTest2.get_format_string(),
 
286
            BzrDirFormatTest2())
 
287
        self.addCleanup(bzrdir.BzrProber.formats.remove,
 
288
            BzrDirFormatTest2.get_format_string())
 
289
        t = self.get_transport()
253
290
        self.build_tree(["foo/", "bar/"], transport=t)
254
291
        def check_format(format, url):
255
292
            format.initialize(url)
256
 
            t = get_transport(url)
 
293
            t = _mod_transport.get_transport_from_path(url)
257
294
            found_format = bzrdir.BzrDirFormat.find_format(t)
258
 
            self.failUnless(isinstance(found_format, format.__class__))
259
 
        check_format(bzrdir.BzrDirFormat5(), "foo")
260
 
        check_format(bzrdir.BzrDirFormat6(), "bar")
 
295
            self.assertIsInstance(found_format, format.__class__)
 
296
        check_format(BzrDirFormatTest1(), "foo")
 
297
        check_format(BzrDirFormatTest2(), "bar")
261
298
 
262
299
    def test_find_format_nothing_there(self):
263
300
        self.assertRaises(NotBranchError,
264
301
                          bzrdir.BzrDirFormat.find_format,
265
 
                          get_transport('.'))
 
302
                          _mod_transport.get_transport_from_path('.'))
266
303
 
267
304
    def test_find_format_unknown_format(self):
268
 
        t = get_transport(self.get_url())
 
305
        t = self.get_transport()
269
306
        t.mkdir('.bzr')
270
307
        t.put_bytes('.bzr/branch-format', '')
271
308
        self.assertRaises(UnknownFormatError,
272
309
                          bzrdir.BzrDirFormat.find_format,
273
 
                          get_transport('.'))
 
310
                          _mod_transport.get_transport_from_path('.'))
274
311
 
275
312
    def test_register_unregister_format(self):
276
313
        format = SampleBzrDirFormat()
278
315
        # make a bzrdir
279
316
        format.initialize(url)
280
317
        # register a format for it.
281
 
        bzrdir.BzrDirFormat.register_format(format)
 
318
        bzrdir.BzrProber.formats.register(format.get_format_string(), format)
282
319
        # which bzrdir.Open will refuse (not supported)
283
320
        self.assertRaises(UnsupportedFormatError, bzrdir.BzrDir.open, url)
284
321
        # which bzrdir.open_containing will refuse (not supported)
285
322
        self.assertRaises(UnsupportedFormatError, bzrdir.BzrDir.open_containing, url)
286
323
        # but open_downlevel will work
287
 
        t = get_transport(url)
 
324
        t = _mod_transport.get_transport_from_url(url)
288
325
        self.assertEqual(format.open(t), bzrdir.BzrDir.open_unsupported(url))
289
326
        # unregister the format
290
 
        bzrdir.BzrDirFormat.unregister_format(format)
 
327
        bzrdir.BzrProber.formats.remove(format.get_format_string())
291
328
        # now open_downlevel should fail too.
292
329
        self.assertRaises(UnknownFormatError, bzrdir.BzrDir.open_unsupported, url)
293
330
 
470
507
    def test_default_stacking_with_stackable_branch_unstackable_repo(self):
471
508
        # Make stackable source branch with an unstackable repo format.
472
509
        source_bzrdir = self.make_bzrdir('source')
473
 
        pack_repo.RepositoryFormatKnitPack1().initialize(source_bzrdir)
 
510
        knitpack_repo.RepositoryFormatKnitPack1().initialize(source_bzrdir)
474
511
        source_branch = bzrlib.branch.BzrBranchFormat7().initialize(
475
512
            source_bzrdir)
476
513
        # Make a directory with a default stacking policy
480
517
        # Clone source into directory
481
518
        target = source_bzrdir.clone(self.get_url('parent/target'))
482
519
 
 
520
    def test_format_initialize_on_transport_ex_stacked_on(self):
 
521
        # trunk is a stackable format.  Note that its in the same server area
 
522
        # which is what launchpad does, but not sufficient to exercise the
 
523
        # general case.
 
524
        trunk = self.make_branch('trunk', format='1.9')
 
525
        t = self.get_transport('stacked')
 
526
        old_fmt = bzrdir.format_registry.make_bzrdir('pack-0.92')
 
527
        repo_name = old_fmt.repository_format.network_name()
 
528
        # Should end up with a 1.9 format (stackable)
 
529
        repo, control, require_stacking, repo_policy = \
 
530
            old_fmt.initialize_on_transport_ex(t,
 
531
                    repo_format_name=repo_name, stacked_on='../trunk',
 
532
                    stack_on_pwd=t.base)
 
533
        if repo is not None:
 
534
            # Repositories are open write-locked
 
535
            self.assertTrue(repo.is_write_locked())
 
536
            self.addCleanup(repo.unlock)
 
537
        else:
 
538
            repo = control.open_repository()
 
539
        self.assertIsInstance(control, bzrdir.BzrDir)
 
540
        opened = bzrdir.BzrDir.open(t.base)
 
541
        if not isinstance(old_fmt, remote.RemoteBzrDirFormat):
 
542
            self.assertEqual(control._format.network_name(),
 
543
                old_fmt.network_name())
 
544
            self.assertEqual(control._format.network_name(),
 
545
                opened._format.network_name())
 
546
        self.assertEqual(control.__class__, opened.__class__)
 
547
        self.assertLength(1, repo._fallback_repositories)
 
548
 
483
549
    def test_sprout_obeys_stacking_policy(self):
484
550
        child_branch, new_child_transport = self.prepare_default_stacking()
485
551
        new_child = child_branch.bzrdir.sprout(new_child_transport.base)
676
742
        self.assertEqual(relpath, 'baz')
677
743
 
678
744
    def test_open_containing_from_transport(self):
679
 
        self.assertRaises(NotBranchError, bzrdir.BzrDir.open_containing_from_transport,
680
 
                          get_transport(self.get_readonly_url('')))
681
 
        self.assertRaises(NotBranchError, bzrdir.BzrDir.open_containing_from_transport,
682
 
                          get_transport(self.get_readonly_url('g/p/q')))
 
745
        self.assertRaises(NotBranchError,
 
746
            bzrdir.BzrDir.open_containing_from_transport,
 
747
            _mod_transport.get_transport_from_url(self.get_readonly_url('')))
 
748
        self.assertRaises(NotBranchError,
 
749
            bzrdir.BzrDir.open_containing_from_transport,
 
750
            _mod_transport.get_transport_from_url(
 
751
                self.get_readonly_url('g/p/q')))
683
752
        control = bzrdir.BzrDir.create(self.get_url())
684
753
        branch, relpath = bzrdir.BzrDir.open_containing_from_transport(
685
 
            get_transport(self.get_readonly_url('')))
 
754
            _mod_transport.get_transport_from_url(
 
755
                self.get_readonly_url('')))
686
756
        self.assertEqual('', relpath)
687
757
        branch, relpath = bzrdir.BzrDir.open_containing_from_transport(
688
 
            get_transport(self.get_readonly_url('g/p/q')))
 
758
            _mod_transport.get_transport_from_url(
 
759
                self.get_readonly_url('g/p/q')))
689
760
        self.assertEqual('g/p/q', relpath)
690
761
 
691
762
    def test_open_containing_tree_or_branch(self):
735
806
        # transport pointing at bzrdir should give a bzrdir with root transport
736
807
        # set to the given transport
737
808
        control = bzrdir.BzrDir.create(self.get_url())
738
 
        transport = get_transport(self.get_url())
739
 
        opened_bzrdir = bzrdir.BzrDir.open_from_transport(transport)
740
 
        self.assertEqual(transport.base, opened_bzrdir.root_transport.base)
 
809
        t = self.get_transport()
 
810
        opened_bzrdir = bzrdir.BzrDir.open_from_transport(t)
 
811
        self.assertEqual(t.base, opened_bzrdir.root_transport.base)
741
812
        self.assertIsInstance(opened_bzrdir, bzrdir.BzrDir)
742
813
 
743
814
    def test_open_from_transport_no_bzrdir(self):
744
 
        transport = get_transport(self.get_url())
745
 
        self.assertRaises(NotBranchError, bzrdir.BzrDir.open_from_transport,
746
 
                          transport)
 
815
        t = self.get_transport()
 
816
        self.assertRaises(NotBranchError, bzrdir.BzrDir.open_from_transport, t)
747
817
 
748
818
    def test_open_from_transport_bzrdir_in_parent(self):
749
819
        control = bzrdir.BzrDir.create(self.get_url())
750
 
        transport = get_transport(self.get_url())
751
 
        transport.mkdir('subdir')
752
 
        transport = transport.clone('subdir')
753
 
        self.assertRaises(NotBranchError, bzrdir.BzrDir.open_from_transport,
754
 
                          transport)
 
820
        t = self.get_transport()
 
821
        t.mkdir('subdir')
 
822
        t = t.clone('subdir')
 
823
        self.assertRaises(NotBranchError, bzrdir.BzrDir.open_from_transport, t)
755
824
 
756
825
    def test_sprout_recursive(self):
757
826
        tree = self.make_branch_and_tree('tree1',
766
835
        tree2 = tree.bzrdir.sprout('tree2').open_workingtree()
767
836
        tree2.lock_read()
768
837
        self.addCleanup(tree2.unlock)
769
 
        self.failUnlessExists('tree2/subtree/file')
 
838
        self.assertPathExists('tree2/subtree/file')
770
839
        self.assertEqual('tree-reference', tree2.kind('subtree-root'))
771
840
 
772
841
    def test_cloning_metadir(self):
776
845
        branch = self.make_branch('branch', format='knit')
777
846
        format = branch.bzrdir.cloning_metadir()
778
847
        self.assertIsInstance(format.workingtree_format,
779
 
            workingtree.WorkingTreeFormat3)
 
848
            workingtree_4.WorkingTreeFormat6)
780
849
 
781
850
    def test_sprout_recursive_treeless(self):
782
851
        tree = self.make_branch_and_tree('tree1',
787
856
        self.build_tree(['tree1/subtree/file'])
788
857
        sub_tree.add('file')
789
858
        tree.commit('Initial commit')
 
859
        # The following line force the orhaning to reveal bug #634470
 
860
        tree.branch.get_config().set_user_option(
 
861
            'bzr.transform.orphan_policy', 'move')
790
862
        tree.bzrdir.destroy_workingtree()
 
863
        # FIXME: subtree/.bzr is left here which allows the test to pass (or
 
864
        # fail :-( ) -- vila 20100909
791
865
        repo = self.make_repository('repo', shared=True,
792
866
            format='dirstate-with-subtree')
793
867
        repo.set_make_working_trees(False)
794
 
        tree.bzrdir.sprout('repo/tree2')
795
 
        self.failUnlessExists('repo/tree2/subtree')
796
 
        self.failIfExists('repo/tree2/subtree/file')
 
868
        # FIXME: we just deleted the workingtree and now we want to use it ????
 
869
        # At a minimum, we should use tree.branch below (but this fails too
 
870
        # currently) or stop calling this test 'treeless'. Specifically, I've
 
871
        # turn the line below into an assertRaises when 'subtree/.bzr' is
 
872
        # orphaned and sprout tries to access the branch there (which is left
 
873
        # by bzrdir.BzrDirMeta1.destroy_workingtree when it ignores the
 
874
        # [DeletingParent('Not deleting', u'subtree', None)] conflict). See bug
 
875
        # #634470.  -- vila 20100909
 
876
        self.assertRaises(errors.NotBranchError,
 
877
                          tree.bzrdir.sprout, 'repo/tree2')
 
878
#        self.assertPathExists('repo/tree2/subtree')
 
879
#        self.assertPathDoesNotExist('repo/tree2/subtree/file')
797
880
 
798
881
    def make_foo_bar_baz(self):
799
882
        foo = bzrdir.BzrDir.create_branch_convenience('foo').bzrdir
803
886
 
804
887
    def test_find_bzrdirs(self):
805
888
        foo, bar, baz = self.make_foo_bar_baz()
806
 
        transport = get_transport(self.get_url())
807
 
        self.assertEqualBzrdirs([baz, foo, bar],
808
 
                                bzrdir.BzrDir.find_bzrdirs(transport))
 
889
        t = self.get_transport()
 
890
        self.assertEqualBzrdirs([baz, foo, bar], bzrdir.BzrDir.find_bzrdirs(t))
 
891
 
 
892
    def make_fake_permission_denied_transport(self, transport, paths):
 
893
        """Create a transport that raises PermissionDenied for some paths."""
 
894
        def filter(path):
 
895
            if path in paths:
 
896
                raise errors.PermissionDenied(path)
 
897
            return path
 
898
        path_filter_server = pathfilter.PathFilteringServer(transport, filter)
 
899
        path_filter_server.start_server()
 
900
        self.addCleanup(path_filter_server.stop_server)
 
901
        path_filter_transport = pathfilter.PathFilteringTransport(
 
902
            path_filter_server, '.')
 
903
        return (path_filter_server, path_filter_transport)
 
904
 
 
905
    def assertBranchUrlsEndWith(self, expect_url_suffix, actual_bzrdirs):
 
906
        """Check that each branch url ends with the given suffix."""
 
907
        for actual_bzrdir in actual_bzrdirs:
 
908
            self.assertEndsWith(actual_bzrdir.user_url, expect_url_suffix)
 
909
 
 
910
    def test_find_bzrdirs_permission_denied(self):
 
911
        foo, bar, baz = self.make_foo_bar_baz()
 
912
        t = self.get_transport()
 
913
        path_filter_server, path_filter_transport = \
 
914
            self.make_fake_permission_denied_transport(t, ['foo'])
 
915
        # local transport
 
916
        self.assertBranchUrlsEndWith('/baz/',
 
917
            bzrdir.BzrDir.find_bzrdirs(path_filter_transport))
 
918
        # smart server
 
919
        smart_transport = self.make_smart_server('.',
 
920
            backing_server=path_filter_server)
 
921
        self.assertBranchUrlsEndWith('/baz/',
 
922
            bzrdir.BzrDir.find_bzrdirs(smart_transport))
809
923
 
810
924
    def test_find_bzrdirs_list_current(self):
811
925
        def list_current(transport):
812
926
            return [s for s in transport.list_dir('') if s != 'baz']
813
927
 
814
928
        foo, bar, baz = self.make_foo_bar_baz()
815
 
        transport = get_transport(self.get_url())
816
 
        self.assertEqualBzrdirs([foo, bar],
817
 
                                bzrdir.BzrDir.find_bzrdirs(transport,
818
 
                                    list_current=list_current))
819
 
 
 
929
        t = self.get_transport()
 
930
        self.assertEqualBzrdirs(
 
931
            [foo, bar],
 
932
            bzrdir.BzrDir.find_bzrdirs(t, list_current=list_current))
820
933
 
821
934
    def test_find_bzrdirs_evaluate(self):
822
935
        def evaluate(bzrdir):
823
936
            try:
824
937
                repo = bzrdir.open_repository()
825
 
            except NoRepositoryPresent:
 
938
            except errors.NoRepositoryPresent:
826
939
                return True, bzrdir.root_transport.base
827
940
            else:
828
941
                return False, bzrdir.root_transport.base
829
942
 
830
943
        foo, bar, baz = self.make_foo_bar_baz()
831
 
        transport = get_transport(self.get_url())
 
944
        t = self.get_transport()
832
945
        self.assertEqual([baz.root_transport.base, foo.root_transport.base],
833
 
                         list(bzrdir.BzrDir.find_bzrdirs(transport,
834
 
                                                         evaluate=evaluate)))
 
946
                         list(bzrdir.BzrDir.find_bzrdirs(t, evaluate=evaluate)))
835
947
 
836
948
    def assertEqualBzrdirs(self, first, second):
837
949
        first = list(first)
844
956
        root = self.make_repository('', shared=True)
845
957
        foo, bar, baz = self.make_foo_bar_baz()
846
958
        qux = self.make_bzrdir('foo/qux')
847
 
        transport = get_transport(self.get_url())
848
 
        branches = bzrdir.BzrDir.find_branches(transport)
 
959
        t = self.get_transport()
 
960
        branches = bzrdir.BzrDir.find_branches(t)
849
961
        self.assertEqual(baz.root_transport.base, branches[0].base)
850
962
        self.assertEqual(foo.root_transport.base, branches[1].base)
851
963
        self.assertEqual(bar.root_transport.base, branches[2].base)
852
964
 
853
965
        # ensure this works without a top-level repo
854
 
        branches = bzrdir.BzrDir.find_branches(transport.clone('foo'))
 
966
        branches = bzrdir.BzrDir.find_branches(t.clone('foo'))
855
967
        self.assertEqual(foo.root_transport.base, branches[0].base)
856
968
        self.assertEqual(bar.root_transport.base, branches[1].base)
857
969
 
858
970
 
 
971
class TestMissingRepoBranchesSkipped(TestCaseWithMemoryTransport):
 
972
 
 
973
    def test_find_bzrdirs_missing_repo(self):
 
974
        t = self.get_transport()
 
975
        arepo = self.make_repository('arepo', shared=True)
 
976
        abranch_url = arepo.user_url + '/abranch'
 
977
        abranch = bzrdir.BzrDir.create(abranch_url).create_branch()
 
978
        t.delete_tree('arepo/.bzr')
 
979
        self.assertRaises(errors.NoRepositoryPresent,
 
980
            branch.Branch.open, abranch_url)
 
981
        self.make_branch('baz')
 
982
        for actual_bzrdir in bzrdir.BzrDir.find_branches(t):
 
983
            self.assertEndsWith(actual_bzrdir.user_url, '/baz/')
 
984
 
 
985
 
859
986
class TestMeta1DirFormat(TestCaseWithTransport):
860
987
    """Tests specific to the meta1 dir format."""
861
988
 
868
995
                         dir.get_branch_transport(bzrlib.branch.BzrBranchFormat5()).base)
869
996
        repository_base = t.clone('repository').base
870
997
        self.assertEqual(repository_base, dir.get_repository_transport(None).base)
 
998
        repository_format = repository.format_registry.get_default()
871
999
        self.assertEqual(repository_base,
872
 
                         dir.get_repository_transport(weaverepo.RepositoryFormat7()).base)
 
1000
                         dir.get_repository_transport(repository_format).base)
873
1001
        checkout_base = t.clone('checkout').base
874
1002
        self.assertEqual(checkout_base, dir.get_workingtree_transport(None).base)
875
1003
        self.assertEqual(checkout_base,
876
 
                         dir.get_workingtree_transport(workingtree.WorkingTreeFormat3()).base)
 
1004
                         dir.get_workingtree_transport(workingtree_3.WorkingTreeFormat3()).base)
877
1005
 
878
1006
    def test_meta1dir_uses_lockdir(self):
879
1007
        """Meta1 format uses a LockDir to guard the whole directory, not a file."""
897
1025
        self.assertNotEqual(otherdir2, mydir)
898
1026
        self.assertFalse(otherdir2 == mydir)
899
1027
 
 
1028
    def test_with_features(self):
 
1029
        tree = self.make_branch_and_tree('tree', format='2a')
 
1030
        tree.bzrdir.update_feature_flags({"bar": "required"})
 
1031
        self.assertRaises(errors.MissingFeature, bzrdir.BzrDir.open, 'tree')
 
1032
        bzrdir.BzrDirMetaFormat1.register_feature('bar')
 
1033
        self.addCleanup(bzrdir.BzrDirMetaFormat1.unregister_feature, 'bar')
 
1034
        dir = bzrdir.BzrDir.open('tree')
 
1035
        self.assertEquals("required", dir._format.features.get("bar"))
 
1036
        tree.bzrdir.update_feature_flags({"bar": None, "nonexistant": None})
 
1037
        dir = bzrdir.BzrDir.open('tree')
 
1038
        self.assertEquals({}, dir._format.features)
 
1039
 
900
1040
    def test_needs_conversion_different_working_tree(self):
901
1041
        # meta1dirs need an conversion if any element is not the default.
902
1042
        new_format = bzrdir.format_registry.make_bzrdir('dirstate')
921
1061
        self.assertEqual(2, rpc_count)
922
1062
 
923
1063
 
924
 
class TestFormat5(TestCaseWithTransport):
925
 
    """Tests specific to the version 5 bzrdir format."""
926
 
 
927
 
    def test_same_lockfiles_between_tree_repo_branch(self):
928
 
        # this checks that only a single lockfiles instance is created
929
 
        # for format 5 objects
930
 
        dir = bzrdir.BzrDirFormat5().initialize(self.get_url())
931
 
        def check_dir_components_use_same_lock(dir):
932
 
            ctrl_1 = dir.open_repository().control_files
933
 
            ctrl_2 = dir.open_branch().control_files
934
 
            ctrl_3 = dir.open_workingtree()._control_files
935
 
            self.assertTrue(ctrl_1 is ctrl_2)
936
 
            self.assertTrue(ctrl_2 is ctrl_3)
937
 
        check_dir_components_use_same_lock(dir)
938
 
        # and if we open it normally.
939
 
        dir = bzrdir.BzrDir.open(self.get_url())
940
 
        check_dir_components_use_same_lock(dir)
941
 
 
942
 
    def test_can_convert(self):
943
 
        # format 5 dirs are convertable
944
 
        dir = bzrdir.BzrDirFormat5().initialize(self.get_url())
945
 
        self.assertTrue(dir.can_convert_format())
946
 
 
947
 
    def test_needs_conversion(self):
948
 
        # format 5 dirs need a conversion if they are not the default,
949
 
        # and they aren't
950
 
        dir = bzrdir.BzrDirFormat5().initialize(self.get_url())
951
 
        # don't need to convert it to itself
952
 
        self.assertFalse(dir.needs_format_conversion(bzrdir.BzrDirFormat5()))
953
 
        # do need to convert it to the current default
954
 
        self.assertTrue(dir.needs_format_conversion(
955
 
            bzrdir.BzrDirFormat.get_default_format()))
956
 
 
957
 
 
958
 
class TestFormat6(TestCaseWithTransport):
959
 
    """Tests specific to the version 6 bzrdir format."""
960
 
 
961
 
    def test_same_lockfiles_between_tree_repo_branch(self):
962
 
        # this checks that only a single lockfiles instance is created
963
 
        # for format 6 objects
964
 
        dir = bzrdir.BzrDirFormat6().initialize(self.get_url())
965
 
        def check_dir_components_use_same_lock(dir):
966
 
            ctrl_1 = dir.open_repository().control_files
967
 
            ctrl_2 = dir.open_branch().control_files
968
 
            ctrl_3 = dir.open_workingtree()._control_files
969
 
            self.assertTrue(ctrl_1 is ctrl_2)
970
 
            self.assertTrue(ctrl_2 is ctrl_3)
971
 
        check_dir_components_use_same_lock(dir)
972
 
        # and if we open it normally.
973
 
        dir = bzrdir.BzrDir.open(self.get_url())
974
 
        check_dir_components_use_same_lock(dir)
975
 
 
976
 
    def test_can_convert(self):
977
 
        # format 6 dirs are convertable
978
 
        dir = bzrdir.BzrDirFormat6().initialize(self.get_url())
979
 
        self.assertTrue(dir.can_convert_format())
980
 
 
981
 
    def test_needs_conversion(self):
982
 
        # format 6 dirs need an conversion if they are not the default.
983
 
        dir = bzrdir.BzrDirFormat6().initialize(self.get_url())
984
 
        self.assertTrue(dir.needs_format_conversion(
985
 
            bzrdir.BzrDirFormat.get_default_format()))
986
 
 
987
 
 
988
 
class NotBzrDir(bzrlib.bzrdir.BzrDir):
989
 
    """A non .bzr based control directory."""
990
 
 
991
 
    def __init__(self, transport, format):
992
 
        self._format = format
993
 
        self.root_transport = transport
994
 
        self.transport = transport.clone('.not')
995
 
 
996
 
 
997
 
class NotBzrDirFormat(bzrlib.bzrdir.BzrDirFormat):
998
 
    """A test class representing any non-.bzr based disk format."""
999
 
 
1000
 
    def initialize_on_transport(self, transport):
1001
 
        """Initialize a new .not dir in the base directory of a Transport."""
1002
 
        transport.mkdir('.not')
1003
 
        return self.open(transport)
1004
 
 
1005
 
    def open(self, transport):
1006
 
        """Open this directory."""
1007
 
        return NotBzrDir(transport, self)
1008
 
 
1009
 
    @classmethod
1010
 
    def _known_formats(self):
1011
 
        return set([NotBzrDirFormat()])
1012
 
 
1013
 
    @classmethod
1014
 
    def probe_transport(self, transport):
1015
 
        """Our format is present if the transport ends in '.not/'."""
1016
 
        if transport.has('.not'):
1017
 
            return NotBzrDirFormat()
1018
 
 
1019
 
 
1020
 
class TestNotBzrDir(TestCaseWithTransport):
1021
 
    """Tests for using the bzrdir api with a non .bzr based disk format.
1022
 
 
1023
 
    If/when one of these is in the core, we can let the implementation tests
1024
 
    verify this works.
1025
 
    """
1026
 
 
1027
 
    def test_create_and_find_format(self):
1028
 
        # create a .notbzr dir
1029
 
        format = NotBzrDirFormat()
1030
 
        dir = format.initialize(self.get_url())
1031
 
        self.assertIsInstance(dir, NotBzrDir)
1032
 
        # now probe for it.
1033
 
        bzrlib.bzrdir.BzrDirFormat.register_control_format(format)
1034
 
        try:
1035
 
            found = bzrlib.bzrdir.BzrDirFormat.find_format(
1036
 
                get_transport(self.get_url()))
1037
 
            self.assertIsInstance(found, NotBzrDirFormat)
1038
 
        finally:
1039
 
            bzrlib.bzrdir.BzrDirFormat.unregister_control_format(format)
1040
 
 
1041
 
    def test_included_in_known_formats(self):
1042
 
        bzrlib.bzrdir.BzrDirFormat.register_control_format(NotBzrDirFormat)
1043
 
        try:
1044
 
            formats = bzrlib.bzrdir.BzrDirFormat.known_formats()
1045
 
            for format in formats:
1046
 
                if isinstance(format, NotBzrDirFormat):
1047
 
                    return
1048
 
            self.fail("No NotBzrDirFormat in %s" % formats)
1049
 
        finally:
1050
 
            bzrlib.bzrdir.BzrDirFormat.unregister_control_format(NotBzrDirFormat)
1051
 
 
1052
 
 
1053
1064
class NonLocalTests(TestCaseWithTransport):
1054
1065
    """Tests for bzrdir static behaviour on non local paths."""
1055
1066
 
1074
1085
            self.get_url('foo'),
1075
1086
            force_new_tree=True,
1076
1087
            format=format)
1077
 
        t = get_transport(self.get_url('.'))
 
1088
        t = self.get_transport()
1078
1089
        self.assertFalse(t.has('foo'))
1079
1090
 
1080
1091
    def test_clone(self):
1096
1107
        my_bzrdir = bzrdir.BzrDir.open(self.get_url('branch-knit2'))
1097
1108
        checkout_format = my_bzrdir.checkout_metadir()
1098
1109
        self.assertIsInstance(checkout_format.workingtree_format,
1099
 
                              workingtree.WorkingTreeFormat3)
 
1110
                              workingtree_4.WorkingTreeFormat4)
1100
1111
 
1101
1112
 
1102
1113
class TestHTTPRedirections(object):
1111
1122
    """
1112
1123
 
1113
1124
    def create_transport_readonly_server(self):
 
1125
        # We don't set the http protocol version, relying on the default
1114
1126
        return http_utils.HTTPServerRedirecting()
1115
1127
 
1116
1128
    def create_transport_secondary_server(self):
 
1129
        # We don't set the http protocol version, relying on the default
1117
1130
        return http_utils.HTTPServerRedirecting()
1118
1131
 
1119
1132
    def setUp(self):
1244
1257
 
1245
1258
    def __init__(self, *args, **kwargs):
1246
1259
        super(_TestBzrDir, self).__init__(*args, **kwargs)
1247
 
        self.test_branch = _TestBranch()
 
1260
        self.test_branch = _TestBranch(self.transport)
1248
1261
        self.test_branch.repository = self.create_repository()
1249
1262
 
1250
 
    def open_branch(self, unsupported=False):
 
1263
    def open_branch(self, unsupported=False, possible_transports=None):
1251
1264
        return self.test_branch
1252
1265
 
1253
1266
    def cloning_metadir(self, require_stacking=False):
1261
1274
class _TestBranch(bzrlib.branch.Branch):
1262
1275
    """Test Branch implementation for TestBzrDirSprout."""
1263
1276
 
1264
 
    def __init__(self, *args, **kwargs):
 
1277
    def __init__(self, transport, *args, **kwargs):
1265
1278
        self._format = _TestBranchFormat()
 
1279
        self._transport = transport
 
1280
        self.base = transport.base
1266
1281
        super(_TestBranch, self).__init__(*args, **kwargs)
1267
1282
        self.calls = []
1268
1283
        self._parent = None
1269
1284
 
1270
1285
    def sprout(self, *args, **kwargs):
1271
1286
        self.calls.append('sprout')
1272
 
        return _TestBranch()
 
1287
        return _TestBranch(self._transport)
1273
1288
 
1274
1289
    def copy_content_into(self, destination, revision_id=None):
1275
1290
        self.calls.append('copy_content_into')
1276
1291
 
 
1292
    def last_revision(self):
 
1293
        return _mod_revision.NULL_REVISION
 
1294
 
1277
1295
    def get_parent(self):
1278
1296
        return self._parent
1279
1297
 
 
1298
    def _get_config(self):
 
1299
        return config.TransportConfig(self._transport, 'branch.conf')
 
1300
 
 
1301
    def _get_config_store(self):
 
1302
        return config.BranchStore(self)
 
1303
 
1280
1304
    def set_parent(self, parent):
1281
1305
        self._parent = parent
1282
1306
 
 
1307
    def lock_read(self):
 
1308
        return lock.LogicalLockResult(self.unlock)
 
1309
 
 
1310
    def unlock(self):
 
1311
        return
 
1312
 
1283
1313
 
1284
1314
class TestBzrDirSprout(TestCaseWithMemoryTransport):
1285
1315
 
1341
1371
        self.assertEqual('fail', err._preformatted_string)
1342
1372
 
1343
1373
    def test_post_repo_init(self):
1344
 
        from bzrlib.bzrdir import RepoInitHookParams
 
1374
        from bzrlib.controldir import RepoInitHookParams
1345
1375
        calls = []
1346
1376
        bzrdir.BzrDir.hooks.install_named_hook('post_repo_init',
1347
1377
            calls.append, None)
1351
1381
        self.assertIsInstance(params, RepoInitHookParams)
1352
1382
        self.assertTrue(hasattr(params, 'bzrdir'))
1353
1383
        self.assertTrue(hasattr(params, 'repository'))
 
1384
 
 
1385
    def test_post_repo_init_hook_repr(self):
 
1386
        param_reprs = []
 
1387
        bzrdir.BzrDir.hooks.install_named_hook('post_repo_init',
 
1388
            lambda params: param_reprs.append(repr(params)), None)
 
1389
        self.make_repository('foo')
 
1390
        self.assertLength(1, param_reprs)
 
1391
        param_repr = param_reprs[0]
 
1392
        self.assertStartsWith(param_repr, '<RepoInitHookParams for ')
 
1393
 
 
1394
 
 
1395
class TestGenerateBackupName(TestCaseWithMemoryTransport):
 
1396
    # FIXME: This may need to be unified with test_osutils.TestBackupNames or
 
1397
    # moved to per_bzrdir or per_transport for better coverage ?
 
1398
    # -- vila 20100909
 
1399
 
 
1400
    def setUp(self):
 
1401
        super(TestGenerateBackupName, self).setUp()
 
1402
        self._transport = self.get_transport()
 
1403
        bzrdir.BzrDir.create(self.get_url(),
 
1404
            possible_transports=[self._transport])
 
1405
        self._bzrdir = bzrdir.BzrDir.open_from_transport(self._transport)
 
1406
 
 
1407
    def test_deprecated_generate_backup_name(self):
 
1408
        res = self.applyDeprecated(
 
1409
                symbol_versioning.deprecated_in((2, 3, 0)),
 
1410
                self._bzrdir.generate_backup_name, 'whatever')
 
1411
 
 
1412
    def test_new(self):
 
1413
        self.assertEqual("a.~1~", self._bzrdir._available_backup_name("a"))
 
1414
 
 
1415
    def test_exiting(self):
 
1416
        self._transport.put_bytes("a.~1~", "some content")
 
1417
        self.assertEqual("a.~2~", self._bzrdir._available_backup_name("a"))
 
1418
 
 
1419
 
 
1420
class TestMeta1DirColoFormat(TestCaseWithTransport):
 
1421
    """Tests specific to the meta1 dir with colocated branches format."""
 
1422
 
 
1423
    def test_supports_colo(self):
 
1424
        format = bzrdir.BzrDirMetaFormat1Colo()
 
1425
        self.assertTrue(format.colocated_branches)
 
1426
 
 
1427
    def test_upgrade_from_2a(self):
 
1428
        tree = self.make_branch_and_tree('.', format='2a')
 
1429
        format = bzrdir.BzrDirMetaFormat1Colo()
 
1430
        self.assertTrue(tree.bzrdir.needs_format_conversion(format))
 
1431
        converter = tree.bzrdir._format.get_converter(format)
 
1432
        result = converter.convert(tree.bzrdir, None)
 
1433
        self.assertIsInstance(result._format, bzrdir.BzrDirMetaFormat1Colo)
 
1434
        self.assertFalse(result.needs_format_conversion(format))
 
1435
 
 
1436
    def test_downgrade_to_2a(self):
 
1437
        tree = self.make_branch_and_tree('.', format='development-colo')
 
1438
        format = bzrdir.BzrDirMetaFormat1()
 
1439
        self.assertTrue(tree.bzrdir.needs_format_conversion(format))
 
1440
        converter = tree.bzrdir._format.get_converter(format)
 
1441
        result = converter.convert(tree.bzrdir, None)
 
1442
        self.assertIsInstance(result._format, bzrdir.BzrDirMetaFormat1)
 
1443
        self.assertFalse(result.needs_format_conversion(format))
 
1444
 
 
1445
    def test_downgrade_to_2a_too_many_branches(self):
 
1446
        tree = self.make_branch_and_tree('.', format='development-colo')
 
1447
        tree.bzrdir.create_branch(name="another-colocated-branch")
 
1448
        converter = tree.bzrdir._format.get_converter(
 
1449
            bzrdir.BzrDirMetaFormat1())
 
1450
        self.assertRaises(errors.BzrError, converter.convert, tree.bzrdir,
 
1451
            None)
 
1452
 
 
1453
 
 
1454
class SampleBzrFormat(bzrdir.BzrFormat):
 
1455
 
 
1456
    @classmethod
 
1457
    def get_format_string(cls):
 
1458
        return "First line\n"
 
1459
 
 
1460
 
 
1461
class TestBzrFormat(TestCase):
 
1462
    """Tests for BzrFormat."""
 
1463
 
 
1464
    def test_as_string(self):
 
1465
        format = SampleBzrFormat()
 
1466
        format.features = {"foo": "required"}
 
1467
        self.assertEquals(format.as_string(),
 
1468
            "First line\n"
 
1469
            "required foo\n")
 
1470
        format.features["another"] = "optional"
 
1471
        self.assertEquals(format.as_string(),
 
1472
            "First line\n"
 
1473
            "required foo\n"
 
1474
            "optional another\n")
 
1475
 
 
1476
    def test_network_name(self):
 
1477
        # The network string should include the feature info
 
1478
        format = SampleBzrFormat()
 
1479
        format.features = {"foo": "required"}
 
1480
        self.assertEquals(
 
1481
            "First line\nrequired foo\n",
 
1482
            format.network_name())
 
1483
 
 
1484
    def test_from_string_no_features(self):
 
1485
        # No features
 
1486
        format = SampleBzrFormat.from_string(
 
1487
            "First line\n")
 
1488
        self.assertEquals({}, format.features)
 
1489
 
 
1490
    def test_from_string_with_feature(self):
 
1491
        # Proper feature
 
1492
        format = SampleBzrFormat.from_string(
 
1493
            "First line\nrequired foo\n")
 
1494
        self.assertEquals("required", format.features.get("foo"))
 
1495
 
 
1496
    def test_from_string_format_string_mismatch(self):
 
1497
        # The first line has to match the format string
 
1498
        self.assertRaises(AssertionError, SampleBzrFormat.from_string,
 
1499
            "Second line\nrequired foo\n")
 
1500
 
 
1501
    def test_from_string_missing_space(self):
 
1502
        # At least one space is required in the feature lines
 
1503
        self.assertRaises(errors.ParseFormatError, SampleBzrFormat.from_string,
 
1504
            "First line\nfoo\n")
 
1505
 
 
1506
    def test_from_string_with_spaces(self):
 
1507
        # Feature with spaces (in case we add stuff like this in the future)
 
1508
        format = SampleBzrFormat.from_string(
 
1509
            "First line\nrequired foo with spaces\n")
 
1510
        self.assertEquals("required", format.features.get("foo with spaces"))
 
1511
 
 
1512
    def test_eq(self):
 
1513
        format1 = SampleBzrFormat()
 
1514
        format1.features = {"nested-trees": "optional"}
 
1515
        format2 = SampleBzrFormat()
 
1516
        format2.features = {"nested-trees": "optional"}
 
1517
        self.assertEquals(format1, format1)
 
1518
        self.assertEquals(format1, format2)
 
1519
        format3 = SampleBzrFormat()
 
1520
        self.assertNotEquals(format1, format3)
 
1521
 
 
1522
    def test_check_support_status_optional(self):
 
1523
        # Optional, so silently ignore
 
1524
        format = SampleBzrFormat()
 
1525
        format.features = {"nested-trees": "optional"}
 
1526
        format.check_support_status(True)
 
1527
        self.addCleanup(SampleBzrFormat.unregister_feature, "nested-trees")
 
1528
        SampleBzrFormat.register_feature("nested-trees")
 
1529
        format.check_support_status(True)
 
1530
 
 
1531
    def test_check_support_status_required(self):
 
1532
        # Optional, so trigger an exception
 
1533
        format = SampleBzrFormat()
 
1534
        format.features = {"nested-trees": "required"}
 
1535
        self.assertRaises(errors.MissingFeature, format.check_support_status,
 
1536
            True)
 
1537
        self.addCleanup(SampleBzrFormat.unregister_feature, "nested-trees")
 
1538
        SampleBzrFormat.register_feature("nested-trees")
 
1539
        format.check_support_status(True)
 
1540
 
 
1541
    def test_check_support_status_unknown(self):
 
1542
        # treat unknown necessity as required
 
1543
        format = SampleBzrFormat()
 
1544
        format.features = {"nested-trees": "unknown"}
 
1545
        self.assertRaises(errors.MissingFeature, format.check_support_status,
 
1546
            True)
 
1547
        self.addCleanup(SampleBzrFormat.unregister_feature, "nested-trees")
 
1548
        SampleBzrFormat.register_feature("nested-trees")
 
1549
        format.check_support_status(True)
 
1550
 
 
1551
    def test_feature_already_registered(self):
 
1552
        # a feature can only be registered once
 
1553
        self.addCleanup(SampleBzrFormat.unregister_feature, "nested-trees")
 
1554
        SampleBzrFormat.register_feature("nested-trees")
 
1555
        self.assertRaises(errors.FeatureAlreadyRegistered,
 
1556
            SampleBzrFormat.register_feature, "nested-trees")
 
1557
 
 
1558
    def test_feature_with_space(self):
 
1559
        # spaces are not allowed in feature names
 
1560
        self.assertRaises(ValueError, SampleBzrFormat.register_feature,
 
1561
            "nested trees")