367
584
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
587
class TestXDGConfigDir(tests.TestCaseInTempDir):
588
# must be in temp dir because config tests for the existence of the bazaar
589
# subdirectory of $XDG_CONFIG_HOME
592
if sys.platform in ('darwin', 'win32'):
593
raise tests.TestNotApplicable(
594
'XDG config dir not used on this platform')
595
super(TestXDGConfigDir, self).setUp()
596
self.overrideEnv('HOME', self.test_home_dir)
597
# BZR_HOME overrides everything we want to test so unset it.
598
self.overrideEnv('BZR_HOME', None)
600
def test_xdg_config_dir_exists(self):
601
"""When ~/.config/bazaar exists, use it as the config dir."""
602
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
604
self.assertEqual(config.config_dir(), newdir)
606
def test_xdg_config_home(self):
607
"""When XDG_CONFIG_HOME is set, use it."""
608
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
609
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
610
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
612
self.assertEqual(config.config_dir(), newdir)
615
class TestIniConfig(tests.TestCaseInTempDir):
372
617
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
618
conf = config.IniBasedConfig.from_string(s)
619
return conf, conf._get_parser()
378
622
class TestIniConfigBuilding(TestIniConfig):
380
624
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
625
my_config = config.IniBasedConfig()
383
627
def test_from_fp(self):
384
config_file = StringIO(sample_config_text.encode('utf-8'))
385
my_config = config.IniBasedConfig(None)
387
isinstance(my_config._get_parser(file=config_file),
388
configobj.ConfigObj))
628
my_config = config.IniBasedConfig.from_string(sample_config_text)
629
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
631
def test_cached(self):
632
my_config = config.IniBasedConfig.from_string(sample_config_text)
633
parser = my_config._get_parser()
634
self.assertTrue(my_config._get_parser() is parser)
636
def _dummy_chown(self, path, uid, gid):
637
self.path, self.uid, self.gid = path, uid, gid
639
def test_ini_config_ownership(self):
640
"""Ensure that chown is happening during _write_config_file"""
641
self.requireFeature(features.chown_feature)
642
self.overrideAttr(os, 'chown', self._dummy_chown)
643
self.path = self.uid = self.gid = None
644
conf = config.IniBasedConfig(file_name='./foo.conf')
645
conf._write_config_file()
646
self.assertEquals(self.path, './foo.conf')
647
self.assertTrue(isinstance(self.uid, int))
648
self.assertTrue(isinstance(self.gid, int))
650
def test_get_filename_parameter_is_deprecated_(self):
651
conf = self.callDeprecated([
652
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
653
' Use file_name instead.'],
654
config.IniBasedConfig, lambda: 'ini.conf')
655
self.assertEqual('ini.conf', conf.file_name)
657
def test_get_parser_file_parameter_is_deprecated_(self):
391
658
config_file = StringIO(sample_config_text.encode('utf-8'))
392
my_config = config.IniBasedConfig(None)
393
parser = my_config._get_parser(file=config_file)
394
self.failUnless(my_config._get_parser() is parser)
659
conf = config.IniBasedConfig.from_string(sample_config_text)
660
conf = self.callDeprecated([
661
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
662
' Use IniBasedConfig(_content=xxx) instead.'],
663
conf._get_parser, file=config_file)
666
class TestIniConfigSaving(tests.TestCaseInTempDir):
668
def test_cant_save_without_a_file_name(self):
669
conf = config.IniBasedConfig()
670
self.assertRaises(AssertionError, conf._write_config_file)
672
def test_saved_with_content(self):
673
content = 'foo = bar\n'
674
conf = config.IniBasedConfig.from_string(
675
content, file_name='./test.conf', save=True)
676
self.assertFileEqual(content, 'test.conf')
679
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
680
"""What is the default value of expand for config options.
682
This is an opt-in beta feature used to evaluate whether or not option
683
references can appear in dangerous place raising exceptions, disapearing
684
(and as such corrupting data) or if it's safe to activate the option by
687
Note that these tests relies on config._expand_default_value being already
688
overwritten in the parent class setUp.
692
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
696
self.warnings.append(args[0] % args[1:])
697
self.overrideAttr(trace, 'warning', warning)
699
def get_config(self, expand):
700
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
704
def assertExpandIs(self, expected):
705
actual = config._get_expand_default_value()
706
#self.config.get_user_option_as_bool('bzr.config.expand')
707
self.assertEquals(expected, actual)
709
def test_default_is_None(self):
710
self.assertEquals(None, config._expand_default_value)
712
def test_default_is_False_even_if_None(self):
713
self.config = self.get_config(None)
714
self.assertExpandIs(False)
716
def test_default_is_False_even_if_invalid(self):
717
self.config = self.get_config('<your choice>')
718
self.assertExpandIs(False)
720
# Huh ? My choice is False ? Thanks, always happy to hear that :D
721
# Wait, you've been warned !
722
self.assertLength(1, self.warnings)
724
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
727
def test_default_is_True(self):
728
self.config = self.get_config(True)
729
self.assertExpandIs(True)
731
def test_default_is_False(self):
732
self.config = self.get_config(False)
733
self.assertExpandIs(False)
736
class TestIniConfigOptionExpansion(tests.TestCase):
737
"""Test option expansion from the IniConfig level.
739
What we really want here is to test the Config level, but the class being
740
abstract as far as storing values is concerned, this can't be done
743
# FIXME: This should be rewritten when all configs share a storage
744
# implementation -- vila 2011-02-18
746
def get_config(self, string=None):
749
c = config.IniBasedConfig.from_string(string)
752
def assertExpansion(self, expected, conf, string, env=None):
753
self.assertEquals(expected, conf.expand_options(string, env))
755
def test_no_expansion(self):
756
c = self.get_config('')
757
self.assertExpansion('foo', c, 'foo')
759
def test_env_adding_options(self):
760
c = self.get_config('')
761
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
763
def test_env_overriding_options(self):
764
c = self.get_config('foo=baz')
765
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
767
def test_simple_ref(self):
768
c = self.get_config('foo=xxx')
769
self.assertExpansion('xxx', c, '{foo}')
771
def test_unknown_ref(self):
772
c = self.get_config('')
773
self.assertRaises(errors.ExpandingUnknownOption,
774
c.expand_options, '{foo}')
776
def test_indirect_ref(self):
777
c = self.get_config('''
781
self.assertExpansion('xxx', c, '{bar}')
783
def test_embedded_ref(self):
784
c = self.get_config('''
788
self.assertExpansion('xxx', c, '{{bar}}')
790
def test_simple_loop(self):
791
c = self.get_config('foo={foo}')
792
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
794
def test_indirect_loop(self):
795
c = self.get_config('''
799
e = self.assertRaises(errors.OptionExpansionLoop,
800
c.expand_options, '{foo}')
801
self.assertEquals('foo->bar->baz', e.refs)
802
self.assertEquals('{foo}', e.string)
805
conf = self.get_config('''
809
list={foo},{bar},{baz}
811
self.assertEquals(['start', 'middle', 'end'],
812
conf.get_user_option('list', expand=True))
814
def test_cascading_list(self):
815
conf = self.get_config('''
821
self.assertEquals(['start', 'middle', 'end'],
822
conf.get_user_option('list', expand=True))
824
def test_pathological_hidden_list(self):
825
conf = self.get_config('''
831
hidden={start}{middle}{end}
833
# Nope, it's either a string or a list, and the list wins as soon as a
834
# ',' appears, so the string concatenation never occur.
835
self.assertEquals(['{foo', '}', '{', 'bar}'],
836
conf.get_user_option('hidden', expand=True))
839
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
841
def get_config(self, location, string=None):
844
# Since we don't save the config we won't strictly require to inherit
845
# from TestCaseInTempDir, but an error occurs so quickly...
846
c = config.LocationConfig.from_string(string, location)
849
def test_dont_cross_unrelated_section(self):
850
c = self.get_config('/another/branch/path','''
855
[/another/branch/path]
858
self.assertRaises(errors.ExpandingUnknownOption,
859
c.get_user_option, 'bar', expand=True)
861
def test_cross_related_sections(self):
862
c = self.get_config('/project/branch/path','''
866
[/project/branch/path]
869
self.assertEquals('quux', c.get_user_option('bar', expand=True))
872
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
874
def test_cannot_reload_without_name(self):
875
conf = config.IniBasedConfig.from_string(sample_config_text)
876
self.assertRaises(AssertionError, conf.reload)
878
def test_reload_see_new_value(self):
879
c1 = config.IniBasedConfig.from_string('editor=vim\n',
880
file_name='./test/conf')
881
c1._write_config_file()
882
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
883
file_name='./test/conf')
884
c2._write_config_file()
885
self.assertEqual('vim', c1.get_user_option('editor'))
886
self.assertEqual('emacs', c2.get_user_option('editor'))
887
# Make sure we get the Right value
889
self.assertEqual('emacs', c1.get_user_option('editor'))
892
class TestLockableConfig(tests.TestCaseInTempDir):
894
scenarios = lockable_config_scenarios()
899
config_section = None
902
super(TestLockableConfig, self).setUp()
903
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
904
self.config = self.create_config(self._content)
906
def get_existing_config(self):
907
return self.config_class(*self.config_args)
909
def create_config(self, content):
910
kwargs = dict(save=True)
911
c = self.config_class.from_string(content, *self.config_args, **kwargs)
914
def test_simple_read_access(self):
915
self.assertEquals('1', self.config.get_user_option('one'))
917
def test_simple_write_access(self):
918
self.config.set_user_option('one', 'one')
919
self.assertEquals('one', self.config.get_user_option('one'))
921
def test_listen_to_the_last_speaker(self):
923
c2 = self.get_existing_config()
924
c1.set_user_option('one', 'ONE')
925
c2.set_user_option('two', 'TWO')
926
self.assertEquals('ONE', c1.get_user_option('one'))
927
self.assertEquals('TWO', c2.get_user_option('two'))
928
# The second update respect the first one
929
self.assertEquals('ONE', c2.get_user_option('one'))
931
def test_last_speaker_wins(self):
932
# If the same config is not shared, the same variable modified twice
933
# can only see a single result.
935
c2 = self.get_existing_config()
936
c1.set_user_option('one', 'c1')
937
c2.set_user_option('one', 'c2')
938
self.assertEquals('c2', c2._get_user_option('one'))
939
# The first modification is still available until another refresh
941
self.assertEquals('c1', c1._get_user_option('one'))
942
c1.set_user_option('two', 'done')
943
self.assertEquals('c2', c1._get_user_option('one'))
945
def test_writes_are_serialized(self):
947
c2 = self.get_existing_config()
949
# We spawn a thread that will pause *during* the write
950
before_writing = threading.Event()
951
after_writing = threading.Event()
952
writing_done = threading.Event()
953
c1_orig = c1._write_config_file
954
def c1_write_config_file():
957
# The lock is held. We wait for the main thread to decide when to
960
c1._write_config_file = c1_write_config_file
962
c1.set_user_option('one', 'c1')
964
t1 = threading.Thread(target=c1_set_option)
965
# Collect the thread after the test
966
self.addCleanup(t1.join)
967
# Be ready to unblock the thread if the test goes wrong
968
self.addCleanup(after_writing.set)
970
before_writing.wait()
971
self.assertTrue(c1._lock.is_held)
972
self.assertRaises(errors.LockContention,
973
c2.set_user_option, 'one', 'c2')
974
self.assertEquals('c1', c1.get_user_option('one'))
975
# Let the lock be released
978
c2.set_user_option('one', 'c2')
979
self.assertEquals('c2', c2.get_user_option('one'))
981
def test_read_while_writing(self):
983
# We spawn a thread that will pause *during* the write
984
ready_to_write = threading.Event()
985
do_writing = threading.Event()
986
writing_done = threading.Event()
987
c1_orig = c1._write_config_file
988
def c1_write_config_file():
990
# The lock is held. We wait for the main thread to decide when to
995
c1._write_config_file = c1_write_config_file
997
c1.set_user_option('one', 'c1')
998
t1 = threading.Thread(target=c1_set_option)
999
# Collect the thread after the test
1000
self.addCleanup(t1.join)
1001
# Be ready to unblock the thread if the test goes wrong
1002
self.addCleanup(do_writing.set)
1004
# Ensure the thread is ready to write
1005
ready_to_write.wait()
1006
self.assertTrue(c1._lock.is_held)
1007
self.assertEquals('c1', c1.get_user_option('one'))
1008
# If we read during the write, we get the old value
1009
c2 = self.get_existing_config()
1010
self.assertEquals('1', c2.get_user_option('one'))
1011
# Let the writing occur and ensure it occurred
1014
# Now we get the updated value
1015
c3 = self.get_existing_config()
1016
self.assertEquals('c1', c3.get_user_option('one'))
397
1019
class TestGetUserOptionAs(TestIniConfig):
1312
2026
self.assertIs(None, bzrdir_config.get_default_stack_on())
2029
class TestOldConfigHooks(tests.TestCaseWithTransport):
2032
super(TestOldConfigHooks, self).setUp()
2033
create_configs_with_file_option(self)
2035
def assertGetHook(self, conf, name, value):
2039
config.OldConfigHooks.install_named_hook('get', hook, None)
2041
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2042
self.assertLength(0, calls)
2043
actual_value = conf.get_user_option(name)
2044
self.assertEquals(value, actual_value)
2045
self.assertLength(1, calls)
2046
self.assertEquals((conf, name, value), calls[0])
2048
def test_get_hook_bazaar(self):
2049
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2051
def test_get_hook_locations(self):
2052
self.assertGetHook(self.locations_config, 'file', 'locations')
2054
def test_get_hook_branch(self):
2055
# Since locations masks branch, we define a different option
2056
self.branch_config.set_user_option('file2', 'branch')
2057
self.assertGetHook(self.branch_config, 'file2', 'branch')
2059
def assertSetHook(self, conf, name, value):
2063
config.OldConfigHooks.install_named_hook('set', hook, None)
2065
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2066
self.assertLength(0, calls)
2067
conf.set_user_option(name, value)
2068
self.assertLength(1, calls)
2069
# We can't assert the conf object below as different configs use
2070
# different means to implement set_user_option and we care only about
2072
self.assertEquals((name, value), calls[0][1:])
2074
def test_set_hook_bazaar(self):
2075
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2077
def test_set_hook_locations(self):
2078
self.assertSetHook(self.locations_config, 'foo', 'locations')
2080
def test_set_hook_branch(self):
2081
self.assertSetHook(self.branch_config, 'foo', 'branch')
2083
def assertRemoveHook(self, conf, name, section_name=None):
2087
config.OldConfigHooks.install_named_hook('remove', hook, None)
2089
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2090
self.assertLength(0, calls)
2091
conf.remove_user_option(name, section_name)
2092
self.assertLength(1, calls)
2093
# We can't assert the conf object below as different configs use
2094
# different means to implement remove_user_option and we care only about
2096
self.assertEquals((name,), calls[0][1:])
2098
def test_remove_hook_bazaar(self):
2099
self.assertRemoveHook(self.bazaar_config, 'file')
2101
def test_remove_hook_locations(self):
2102
self.assertRemoveHook(self.locations_config, 'file',
2103
self.locations_config.location)
2105
def test_remove_hook_branch(self):
2106
self.assertRemoveHook(self.branch_config, 'file')
2108
def assertLoadHook(self, name, conf_class, *conf_args):
2112
config.OldConfigHooks.install_named_hook('load', hook, None)
2114
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2115
self.assertLength(0, calls)
2117
conf = conf_class(*conf_args)
2118
# Access an option to trigger a load
2119
conf.get_user_option(name)
2120
self.assertLength(1, calls)
2121
# Since we can't assert about conf, we just use the number of calls ;-/
2123
def test_load_hook_bazaar(self):
2124
self.assertLoadHook('file', config.GlobalConfig)
2126
def test_load_hook_locations(self):
2127
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2129
def test_load_hook_branch(self):
2130
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2132
def assertSaveHook(self, conf):
2136
config.OldConfigHooks.install_named_hook('save', hook, None)
2138
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2139
self.assertLength(0, calls)
2140
# Setting an option triggers a save
2141
conf.set_user_option('foo', 'bar')
2142
self.assertLength(1, calls)
2143
# Since we can't assert about conf, we just use the number of calls ;-/
2145
def test_save_hook_bazaar(self):
2146
self.assertSaveHook(self.bazaar_config)
2148
def test_save_hook_locations(self):
2149
self.assertSaveHook(self.locations_config)
2151
def test_save_hook_branch(self):
2152
self.assertSaveHook(self.branch_config)
2155
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2156
"""Tests config hooks for remote configs.
2158
No tests for the remove hook as this is not implemented there.
2162
super(TestOldConfigHooksForRemote, self).setUp()
2163
self.transport_server = test_server.SmartTCPServer_for_testing
2164
create_configs_with_file_option(self)
2166
def assertGetHook(self, conf, name, value):
2170
config.OldConfigHooks.install_named_hook('get', hook, None)
2172
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2173
self.assertLength(0, calls)
2174
actual_value = conf.get_option(name)
2175
self.assertEquals(value, actual_value)
2176
self.assertLength(1, calls)
2177
self.assertEquals((conf, name, value), calls[0])
2179
def test_get_hook_remote_branch(self):
2180
remote_branch = branch.Branch.open(self.get_url('tree'))
2181
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2183
def test_get_hook_remote_bzrdir(self):
2184
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2185
conf = remote_bzrdir._get_config()
2186
conf.set_option('remotedir', 'file')
2187
self.assertGetHook(conf, 'file', 'remotedir')
2189
def assertSetHook(self, conf, name, value):
2193
config.OldConfigHooks.install_named_hook('set', hook, None)
2195
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2196
self.assertLength(0, calls)
2197
conf.set_option(value, name)
2198
self.assertLength(1, calls)
2199
# We can't assert the conf object below as different configs use
2200
# different means to implement set_user_option and we care only about
2202
self.assertEquals((name, value), calls[0][1:])
2204
def test_set_hook_remote_branch(self):
2205
remote_branch = branch.Branch.open(self.get_url('tree'))
2206
self.addCleanup(remote_branch.lock_write().unlock)
2207
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2209
def test_set_hook_remote_bzrdir(self):
2210
remote_branch = branch.Branch.open(self.get_url('tree'))
2211
self.addCleanup(remote_branch.lock_write().unlock)
2212
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2213
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2215
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2219
config.OldConfigHooks.install_named_hook('load', hook, None)
2221
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2222
self.assertLength(0, calls)
2224
conf = conf_class(*conf_args)
2225
# Access an option to trigger a load
2226
conf.get_option(name)
2227
self.assertLength(expected_nb_calls, calls)
2228
# Since we can't assert about conf, we just use the number of calls ;-/
2230
def test_load_hook_remote_branch(self):
2231
remote_branch = branch.Branch.open(self.get_url('tree'))
2232
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2234
def test_load_hook_remote_bzrdir(self):
2235
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2236
# The config file doesn't exist, set an option to force its creation
2237
conf = remote_bzrdir._get_config()
2238
conf.set_option('remotedir', 'file')
2239
# We get one call for the server and one call for the client, this is
2240
# caused by the differences in implementations betwen
2241
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2242
# SmartServerBranchGetConfigFile (in smart/branch.py)
2243
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2245
def assertSaveHook(self, conf):
2249
config.OldConfigHooks.install_named_hook('save', hook, None)
2251
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2252
self.assertLength(0, calls)
2253
# Setting an option triggers a save
2254
conf.set_option('foo', 'bar')
2255
self.assertLength(1, calls)
2256
# Since we can't assert about conf, we just use the number of calls ;-/
2258
def test_save_hook_remote_branch(self):
2259
remote_branch = branch.Branch.open(self.get_url('tree'))
2260
self.addCleanup(remote_branch.lock_write().unlock)
2261
self.assertSaveHook(remote_branch._get_config())
2263
def test_save_hook_remote_bzrdir(self):
2264
remote_branch = branch.Branch.open(self.get_url('tree'))
2265
self.addCleanup(remote_branch.lock_write().unlock)
2266
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2267
self.assertSaveHook(remote_bzrdir._get_config())
2270
class TestOption(tests.TestCase):
2272
def test_default_value(self):
2273
opt = config.Option('foo', default='bar')
2274
self.assertEquals('bar', opt.get_default())
2276
def test_default_value_from_env(self):
2277
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2278
self.overrideEnv('FOO', 'quux')
2279
# Env variable provides a default taking over the option one
2280
self.assertEquals('quux', opt.get_default())
2282
def test_first_default_value_from_env_wins(self):
2283
opt = config.Option('foo', default='bar',
2284
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2285
self.overrideEnv('FOO', 'foo')
2286
self.overrideEnv('BAZ', 'baz')
2287
# The first env var set wins
2288
self.assertEquals('foo', opt.get_default())
2290
def test_not_supported_list_default_value(self):
2291
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2293
def test_not_supported_object_default_value(self):
2294
self.assertRaises(AssertionError, config.Option, 'foo',
2298
class TestOptionConverterMixin(object):
2300
def assertConverted(self, expected, opt, value):
2301
self.assertEquals(expected, opt.convert_from_unicode(value))
2303
def assertWarns(self, opt, value):
2306
warnings.append(args[0] % args[1:])
2307
self.overrideAttr(trace, 'warning', warning)
2308
self.assertEquals(None, opt.convert_from_unicode(value))
2309
self.assertLength(1, warnings)
2311
'Value "%s" is not valid for "%s"' % (value, opt.name),
2314
def assertErrors(self, opt, value):
2315
self.assertRaises(errors.ConfigOptionValueError,
2316
opt.convert_from_unicode, value)
2318
def assertConvertInvalid(self, opt, invalid_value):
2320
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2321
opt.invalid = 'warning'
2322
self.assertWarns(opt, invalid_value)
2323
opt.invalid = 'error'
2324
self.assertErrors(opt, invalid_value)
2327
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2329
def get_option(self):
2330
return config.Option('foo', help='A boolean.',
2331
from_unicode=config.bool_from_store)
2333
def test_convert_invalid(self):
2334
opt = self.get_option()
2335
# A string that is not recognized as a boolean
2336
self.assertConvertInvalid(opt, u'invalid-boolean')
2337
# A list of strings is never recognized as a boolean
2338
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2340
def test_convert_valid(self):
2341
opt = self.get_option()
2342
self.assertConverted(True, opt, u'True')
2343
self.assertConverted(True, opt, u'1')
2344
self.assertConverted(False, opt, u'False')
2347
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2349
def get_option(self):
2350
return config.Option('foo', help='An integer.',
2351
from_unicode=config.int_from_store)
2353
def test_convert_invalid(self):
2354
opt = self.get_option()
2355
# A string that is not recognized as an integer
2356
self.assertConvertInvalid(opt, u'forty-two')
2357
# A list of strings is never recognized as an integer
2358
self.assertConvertInvalid(opt, [u'a', u'list'])
2360
def test_convert_valid(self):
2361
opt = self.get_option()
2362
self.assertConverted(16, opt, u'16')
2364
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2366
def get_option(self):
2367
return config.Option('foo', help='A list.',
2368
from_unicode=config.list_from_store)
2370
def test_convert_invalid(self):
2371
# No string is invalid as all forms can be converted to a list
2374
def test_convert_valid(self):
2375
opt = self.get_option()
2376
# An empty string is an empty list
2377
self.assertConverted([], opt, '') # Using a bare str() just in case
2378
self.assertConverted([], opt, u'')
2380
self.assertConverted([u'True'], opt, u'True')
2382
self.assertConverted([u'42'], opt, u'42')
2384
self.assertConverted([u'bar'], opt, u'bar')
2385
# A list remains a list (configObj will turn a string containing commas
2386
# into a list, but that's not what we're testing here)
2387
self.assertConverted([u'foo', u'1', u'True'],
2388
opt, [u'foo', u'1', u'True'])
2391
class TestOptionConverterMixin(object):
2393
def assertConverted(self, expected, opt, value):
2394
self.assertEquals(expected, opt.convert_from_unicode(value))
2396
def assertWarns(self, opt, value):
2399
warnings.append(args[0] % args[1:])
2400
self.overrideAttr(trace, 'warning', warning)
2401
self.assertEquals(None, opt.convert_from_unicode(value))
2402
self.assertLength(1, warnings)
2404
'Value "%s" is not valid for "%s"' % (value, opt.name),
2407
def assertErrors(self, opt, value):
2408
self.assertRaises(errors.ConfigOptionValueError,
2409
opt.convert_from_unicode, value)
2411
def assertConvertInvalid(self, opt, invalid_value):
2413
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2414
opt.invalid = 'warning'
2415
self.assertWarns(opt, invalid_value)
2416
opt.invalid = 'error'
2417
self.assertErrors(opt, invalid_value)
2420
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2422
def get_option(self):
2423
return config.Option('foo', help='A boolean.',
2424
from_unicode=config.bool_from_store)
2426
def test_convert_invalid(self):
2427
opt = self.get_option()
2428
# A string that is not recognized as a boolean
2429
self.assertConvertInvalid(opt, u'invalid-boolean')
2430
# A list of strings is never recognized as a boolean
2431
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2433
def test_convert_valid(self):
2434
opt = self.get_option()
2435
self.assertConverted(True, opt, u'True')
2436
self.assertConverted(True, opt, u'1')
2437
self.assertConverted(False, opt, u'False')
2440
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2442
def get_option(self):
2443
return config.Option('foo', help='An integer.',
2444
from_unicode=config.int_from_store)
2446
def test_convert_invalid(self):
2447
opt = self.get_option()
2448
# A string that is not recognized as an integer
2449
self.assertConvertInvalid(opt, u'forty-two')
2450
# A list of strings is never recognized as an integer
2451
self.assertConvertInvalid(opt, [u'a', u'list'])
2453
def test_convert_valid(self):
2454
opt = self.get_option()
2455
self.assertConverted(16, opt, u'16')
2458
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2460
def get_option(self):
2461
return config.Option('foo', help='A list.',
2462
from_unicode=config.list_from_store)
2464
def test_convert_invalid(self):
2465
opt = self.get_option()
2466
# We don't even try to convert a list into a list, we only expect
2468
self.assertConvertInvalid(opt, [1])
2469
# No string is invalid as all forms can be converted to a list
2471
def test_convert_valid(self):
2472
opt = self.get_option()
2473
# An empty string is an empty list
2474
self.assertConverted([], opt, '') # Using a bare str() just in case
2475
self.assertConverted([], opt, u'')
2477
self.assertConverted([u'True'], opt, u'True')
2479
self.assertConverted([u'42'], opt, u'42')
2481
self.assertConverted([u'bar'], opt, u'bar')
2484
class TestOptionRegistry(tests.TestCase):
2487
super(TestOptionRegistry, self).setUp()
2488
# Always start with an empty registry
2489
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2490
self.registry = config.option_registry
2492
def test_register(self):
2493
opt = config.Option('foo')
2494
self.registry.register(opt)
2495
self.assertIs(opt, self.registry.get('foo'))
2497
def test_registered_help(self):
2498
opt = config.Option('foo', help='A simple option')
2499
self.registry.register(opt)
2500
self.assertEquals('A simple option', self.registry.get_help('foo'))
2502
lazy_option = config.Option('lazy_foo', help='Lazy help')
2504
def test_register_lazy(self):
2505
self.registry.register_lazy('lazy_foo', self.__module__,
2506
'TestOptionRegistry.lazy_option')
2507
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2509
def test_registered_lazy_help(self):
2510
self.registry.register_lazy('lazy_foo', self.__module__,
2511
'TestOptionRegistry.lazy_option')
2512
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2515
class TestRegisteredOptions(tests.TestCase):
2516
"""All registered options should verify some constraints."""
2518
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2519
in config.option_registry.iteritems()]
2522
super(TestRegisteredOptions, self).setUp()
2523
self.registry = config.option_registry
2525
def test_proper_name(self):
2526
# An option should be registered under its own name, this can't be
2527
# checked at registration time for the lazy ones.
2528
self.assertEquals(self.option_name, self.option.name)
2530
def test_help_is_set(self):
2531
option_help = self.registry.get_help(self.option_name)
2532
self.assertNotEquals(None, option_help)
2533
# Come on, think about the user, he really wants to know what the
2535
self.assertIsNot(None, option_help)
2536
self.assertNotEquals('', option_help)
2539
class TestSection(tests.TestCase):
2541
# FIXME: Parametrize so that all sections produced by Stores run these
2542
# tests -- vila 2011-04-01
2544
def test_get_a_value(self):
2545
a_dict = dict(foo='bar')
2546
section = config.Section('myID', a_dict)
2547
self.assertEquals('bar', section.get('foo'))
2549
def test_get_unknown_option(self):
2551
section = config.Section(None, a_dict)
2552
self.assertEquals('out of thin air',
2553
section.get('foo', 'out of thin air'))
2555
def test_options_is_shared(self):
2557
section = config.Section(None, a_dict)
2558
self.assertIs(a_dict, section.options)
2561
class TestMutableSection(tests.TestCase):
2563
# FIXME: Parametrize so that all sections (including os.environ and the
2564
# ones produced by Stores) run these tests -- vila 2011-04-01
2567
a_dict = dict(foo='bar')
2568
section = config.MutableSection('myID', a_dict)
2569
section.set('foo', 'new_value')
2570
self.assertEquals('new_value', section.get('foo'))
2571
# The change appears in the shared section
2572
self.assertEquals('new_value', a_dict.get('foo'))
2573
# We keep track of the change
2574
self.assertTrue('foo' in section.orig)
2575
self.assertEquals('bar', section.orig.get('foo'))
2577
def test_set_preserve_original_once(self):
2578
a_dict = dict(foo='bar')
2579
section = config.MutableSection('myID', a_dict)
2580
section.set('foo', 'first_value')
2581
section.set('foo', 'second_value')
2582
# We keep track of the original value
2583
self.assertTrue('foo' in section.orig)
2584
self.assertEquals('bar', section.orig.get('foo'))
2586
def test_remove(self):
2587
a_dict = dict(foo='bar')
2588
section = config.MutableSection('myID', a_dict)
2589
section.remove('foo')
2590
# We get None for unknown options via the default value
2591
self.assertEquals(None, section.get('foo'))
2592
# Or we just get the default value
2593
self.assertEquals('unknown', section.get('foo', 'unknown'))
2594
self.assertFalse('foo' in section.options)
2595
# We keep track of the deletion
2596
self.assertTrue('foo' in section.orig)
2597
self.assertEquals('bar', section.orig.get('foo'))
2599
def test_remove_new_option(self):
2601
section = config.MutableSection('myID', a_dict)
2602
section.set('foo', 'bar')
2603
section.remove('foo')
2604
self.assertFalse('foo' in section.options)
2605
# The option didn't exist initially so it we need to keep track of it
2606
# with a special value
2607
self.assertTrue('foo' in section.orig)
2608
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2611
class TestStore(tests.TestCaseWithTransport):
2613
def assertSectionContent(self, expected, section):
2614
"""Assert that some options have the proper values in a section."""
2615
expected_name, expected_options = expected
2616
self.assertEquals(expected_name, section.id)
2619
dict([(k, section.get(k)) for k in expected_options.keys()]))
2622
class TestReadonlyStore(TestStore):
2624
scenarios = [(key, {'get_store': builder}) for key, builder
2625
in config.test_store_builder_registry.iteritems()]
2628
super(TestReadonlyStore, self).setUp()
2630
def test_building_delays_load(self):
2631
store = self.get_store(self)
2632
self.assertEquals(False, store.is_loaded())
2633
store._load_from_string('')
2634
self.assertEquals(True, store.is_loaded())
2636
def test_get_no_sections_for_empty(self):
2637
store = self.get_store(self)
2638
store._load_from_string('')
2639
self.assertEquals([], list(store.get_sections()))
2641
def test_get_default_section(self):
2642
store = self.get_store(self)
2643
store._load_from_string('foo=bar')
2644
sections = list(store.get_sections())
2645
self.assertLength(1, sections)
2646
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2648
def test_get_named_section(self):
2649
store = self.get_store(self)
2650
store._load_from_string('[baz]\nfoo=bar')
2651
sections = list(store.get_sections())
2652
self.assertLength(1, sections)
2653
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2655
def test_load_from_string_fails_for_non_empty_store(self):
2656
store = self.get_store(self)
2657
store._load_from_string('foo=bar')
2658
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2661
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2662
"""Simulate loading a config store with content of various encodings.
2664
All files produced by bzr are in utf8 content.
2666
Users may modify them manually and end up with a file that can't be
2667
loaded. We need to issue proper error messages in this case.
2670
invalid_utf8_char = '\xff'
2672
def test_load_utf8(self):
2673
"""Ensure we can load an utf8-encoded file."""
2674
t = self.get_transport()
2675
# From http://pad.lv/799212
2676
unicode_user = u'b\N{Euro Sign}ar'
2677
unicode_content = u'user=%s' % (unicode_user,)
2678
utf8_content = unicode_content.encode('utf8')
2679
# Store the raw content in the config file
2680
t.put_bytes('foo.conf', utf8_content)
2681
store = config.IniFileStore(t, 'foo.conf')
2683
stack = config.Stack([store.get_sections], store)
2684
self.assertEquals(unicode_user, stack.get('user'))
2686
def test_load_non_ascii(self):
2687
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2688
t = self.get_transport()
2689
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2690
store = config.IniFileStore(t, 'foo.conf')
2691
self.assertRaises(errors.ConfigContentError, store.load)
2693
def test_load_erroneous_content(self):
2694
"""Ensure we display a proper error on content that can't be parsed."""
2695
t = self.get_transport()
2696
t.put_bytes('foo.conf', '[open_section\n')
2697
store = config.IniFileStore(t, 'foo.conf')
2698
self.assertRaises(errors.ParseConfigError, store.load)
2700
def test_load_permission_denied(self):
2701
"""Ensure we get warned when trying to load an inaccessible file."""
2704
warnings.append(args[0] % args[1:])
2705
self.overrideAttr(trace, 'warning', warning)
2707
t = self.get_transport()
2709
def get_bytes(relpath):
2710
raise errors.PermissionDenied(relpath, "")
2711
t.get_bytes = get_bytes
2712
store = config.IniFileStore(t, 'foo.conf')
2713
self.assertRaises(errors.PermissionDenied, store.load)
2716
[u'Permission denied while trying to load configuration store %s.'
2717
% store.external_url()])
2720
class TestIniConfigContent(tests.TestCaseWithTransport):
2721
"""Simulate loading a IniBasedConfig with content of various encodings.
2723
All files produced by bzr are in utf8 content.
2725
Users may modify them manually and end up with a file that can't be
2726
loaded. We need to issue proper error messages in this case.
2729
invalid_utf8_char = '\xff'
2731
def test_load_utf8(self):
2732
"""Ensure we can load an utf8-encoded file."""
2733
# From http://pad.lv/799212
2734
unicode_user = u'b\N{Euro Sign}ar'
2735
unicode_content = u'user=%s' % (unicode_user,)
2736
utf8_content = unicode_content.encode('utf8')
2737
# Store the raw content in the config file
2738
with open('foo.conf', 'wb') as f:
2739
f.write(utf8_content)
2740
conf = config.IniBasedConfig(file_name='foo.conf')
2741
self.assertEquals(unicode_user, conf.get_user_option('user'))
2743
def test_load_badly_encoded_content(self):
2744
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2745
with open('foo.conf', 'wb') as f:
2746
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2747
conf = config.IniBasedConfig(file_name='foo.conf')
2748
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2750
def test_load_erroneous_content(self):
2751
"""Ensure we display a proper error on content that can't be parsed."""
2752
with open('foo.conf', 'wb') as f:
2753
f.write('[open_section\n')
2754
conf = config.IniBasedConfig(file_name='foo.conf')
2755
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2758
class TestMutableStore(TestStore):
2760
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2761
in config.test_store_builder_registry.iteritems()]
2764
super(TestMutableStore, self).setUp()
2765
self.transport = self.get_transport()
2767
def has_store(self, store):
2768
store_basename = urlutils.relative_url(self.transport.external_url(),
2769
store.external_url())
2770
return self.transport.has(store_basename)
2772
def test_save_empty_creates_no_file(self):
2773
# FIXME: There should be a better way than relying on the test
2774
# parametrization to identify branch.conf -- vila 2011-0526
2775
if self.store_id in ('branch', 'remote_branch'):
2776
raise tests.TestNotApplicable(
2777
'branch.conf is *always* created when a branch is initialized')
2778
store = self.get_store(self)
2780
self.assertEquals(False, self.has_store(store))
2782
def test_save_emptied_succeeds(self):
2783
store = self.get_store(self)
2784
store._load_from_string('foo=bar\n')
2785
section = store.get_mutable_section(None)
2786
section.remove('foo')
2788
self.assertEquals(True, self.has_store(store))
2789
modified_store = self.get_store(self)
2790
sections = list(modified_store.get_sections())
2791
self.assertLength(0, sections)
2793
def test_save_with_content_succeeds(self):
2794
# FIXME: There should be a better way than relying on the test
2795
# parametrization to identify branch.conf -- vila 2011-0526
2796
if self.store_id in ('branch', 'remote_branch'):
2797
raise tests.TestNotApplicable(
2798
'branch.conf is *always* created when a branch is initialized')
2799
store = self.get_store(self)
2800
store._load_from_string('foo=bar\n')
2801
self.assertEquals(False, self.has_store(store))
2803
self.assertEquals(True, self.has_store(store))
2804
modified_store = self.get_store(self)
2805
sections = list(modified_store.get_sections())
2806
self.assertLength(1, sections)
2807
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2809
def test_set_option_in_empty_store(self):
2810
store = self.get_store(self)
2811
section = store.get_mutable_section(None)
2812
section.set('foo', 'bar')
2814
modified_store = self.get_store(self)
2815
sections = list(modified_store.get_sections())
2816
self.assertLength(1, sections)
2817
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2819
def test_set_option_in_default_section(self):
2820
store = self.get_store(self)
2821
store._load_from_string('')
2822
section = store.get_mutable_section(None)
2823
section.set('foo', 'bar')
2825
modified_store = self.get_store(self)
2826
sections = list(modified_store.get_sections())
2827
self.assertLength(1, sections)
2828
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2830
def test_set_option_in_named_section(self):
2831
store = self.get_store(self)
2832
store._load_from_string('')
2833
section = store.get_mutable_section('baz')
2834
section.set('foo', 'bar')
2836
modified_store = self.get_store(self)
2837
sections = list(modified_store.get_sections())
2838
self.assertLength(1, sections)
2839
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2841
def test_load_hook(self):
2842
# We first needs to ensure that the store exists
2843
store = self.get_store(self)
2844
section = store.get_mutable_section('baz')
2845
section.set('foo', 'bar')
2847
# Now we can try to load it
2848
store = self.get_store(self)
2852
config.ConfigHooks.install_named_hook('load', hook, None)
2853
self.assertLength(0, calls)
2855
self.assertLength(1, calls)
2856
self.assertEquals((store,), calls[0])
2858
def test_save_hook(self):
2862
config.ConfigHooks.install_named_hook('save', hook, None)
2863
self.assertLength(0, calls)
2864
store = self.get_store(self)
2865
section = store.get_mutable_section('baz')
2866
section.set('foo', 'bar')
2868
self.assertLength(1, calls)
2869
self.assertEquals((store,), calls[0])
2872
class TestIniFileStore(TestStore):
2874
def test_loading_unknown_file_fails(self):
2875
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2876
self.assertRaises(errors.NoSuchFile, store.load)
2878
def test_invalid_content(self):
2879
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2880
self.assertEquals(False, store.is_loaded())
2881
exc = self.assertRaises(
2882
errors.ParseConfigError, store._load_from_string,
2883
'this is invalid !')
2884
self.assertEndsWith(exc.filename, 'foo.conf')
2885
# And the load failed
2886
self.assertEquals(False, store.is_loaded())
2888
def test_get_embedded_sections(self):
2889
# A more complicated example (which also shows that section names and
2890
# option names share the same name space...)
2891
# FIXME: This should be fixed by forbidding dicts as values ?
2892
# -- vila 2011-04-05
2893
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2894
store._load_from_string('''
2898
foo_in_DEFAULT=foo_DEFAULT
2906
sections = list(store.get_sections())
2907
self.assertLength(4, sections)
2908
# The default section has no name.
2909
# List values are provided as strings and need to be explicitly
2910
# converted by specifying from_unicode=list_from_store at option
2912
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2914
self.assertSectionContent(
2915
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2916
self.assertSectionContent(
2917
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2918
# sub sections are provided as embedded dicts.
2919
self.assertSectionContent(
2920
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2924
class TestLockableIniFileStore(TestStore):
2926
def test_create_store_in_created_dir(self):
2927
self.assertPathDoesNotExist('dir')
2928
t = self.get_transport('dir/subdir')
2929
store = config.LockableIniFileStore(t, 'foo.conf')
2930
store.get_mutable_section(None).set('foo', 'bar')
2932
self.assertPathExists('dir/subdir')
2935
class TestConcurrentStoreUpdates(TestStore):
2936
"""Test that Stores properly handle conccurent updates.
2938
New Store implementation may fail some of these tests but until such
2939
implementations exist it's hard to properly filter them from the scenarios
2940
applied here. If you encounter such a case, contact the bzr devs.
2943
scenarios = [(key, {'get_stack': builder}) for key, builder
2944
in config.test_stack_builder_registry.iteritems()]
2947
super(TestConcurrentStoreUpdates, self).setUp()
2948
self._content = 'one=1\ntwo=2\n'
2949
self.stack = self.get_stack(self)
2950
if not isinstance(self.stack, config._CompatibleStack):
2951
raise tests.TestNotApplicable(
2952
'%s is not meant to be compatible with the old config design'
2954
self.stack.store._load_from_string(self._content)
2956
self.stack.store.save()
2958
def test_simple_read_access(self):
2959
self.assertEquals('1', self.stack.get('one'))
2961
def test_simple_write_access(self):
2962
self.stack.set('one', 'one')
2963
self.assertEquals('one', self.stack.get('one'))
2965
def test_listen_to_the_last_speaker(self):
2967
c2 = self.get_stack(self)
2968
c1.set('one', 'ONE')
2969
c2.set('two', 'TWO')
2970
self.assertEquals('ONE', c1.get('one'))
2971
self.assertEquals('TWO', c2.get('two'))
2972
# The second update respect the first one
2973
self.assertEquals('ONE', c2.get('one'))
2975
def test_last_speaker_wins(self):
2976
# If the same config is not shared, the same variable modified twice
2977
# can only see a single result.
2979
c2 = self.get_stack(self)
2982
self.assertEquals('c2', c2.get('one'))
2983
# The first modification is still available until another refresh
2985
self.assertEquals('c1', c1.get('one'))
2986
c1.set('two', 'done')
2987
self.assertEquals('c2', c1.get('one'))
2989
def test_writes_are_serialized(self):
2991
c2 = self.get_stack(self)
2993
# We spawn a thread that will pause *during* the config saving.
2994
before_writing = threading.Event()
2995
after_writing = threading.Event()
2996
writing_done = threading.Event()
2997
c1_save_without_locking_orig = c1.store.save_without_locking
2998
def c1_save_without_locking():
2999
before_writing.set()
3000
c1_save_without_locking_orig()
3001
# The lock is held. We wait for the main thread to decide when to
3003
after_writing.wait()
3004
c1.store.save_without_locking = c1_save_without_locking
3008
t1 = threading.Thread(target=c1_set)
3009
# Collect the thread after the test
3010
self.addCleanup(t1.join)
3011
# Be ready to unblock the thread if the test goes wrong
3012
self.addCleanup(after_writing.set)
3014
before_writing.wait()
3015
self.assertRaises(errors.LockContention,
3016
c2.set, 'one', 'c2')
3017
self.assertEquals('c1', c1.get('one'))
3018
# Let the lock be released
3022
self.assertEquals('c2', c2.get('one'))
3024
def test_read_while_writing(self):
3026
# We spawn a thread that will pause *during* the write
3027
ready_to_write = threading.Event()
3028
do_writing = threading.Event()
3029
writing_done = threading.Event()
3030
# We override the _save implementation so we know the store is locked
3031
c1_save_without_locking_orig = c1.store.save_without_locking
3032
def c1_save_without_locking():
3033
ready_to_write.set()
3034
# The lock is held. We wait for the main thread to decide when to
3037
c1_save_without_locking_orig()
3039
c1.store.save_without_locking = c1_save_without_locking
3042
t1 = threading.Thread(target=c1_set)
3043
# Collect the thread after the test
3044
self.addCleanup(t1.join)
3045
# Be ready to unblock the thread if the test goes wrong
3046
self.addCleanup(do_writing.set)
3048
# Ensure the thread is ready to write
3049
ready_to_write.wait()
3050
self.assertEquals('c1', c1.get('one'))
3051
# If we read during the write, we get the old value
3052
c2 = self.get_stack(self)
3053
self.assertEquals('1', c2.get('one'))
3054
# Let the writing occur and ensure it occurred
3057
# Now we get the updated value
3058
c3 = self.get_stack(self)
3059
self.assertEquals('c1', c3.get('one'))
3061
# FIXME: It may be worth looking into removing the lock dir when it's not
3062
# needed anymore and look at possible fallouts for concurrent lockers. This
3063
# will matter if/when we use config files outside of bazaar directories
3064
# (.bazaar or .bzr) -- vila 20110-04-11
3067
class TestSectionMatcher(TestStore):
3069
scenarios = [('location', {'matcher': config.LocationMatcher})]
3071
def get_store(self, file_name):
3072
return config.IniFileStore(self.get_readonly_transport(), file_name)
3074
def test_no_matches_for_empty_stores(self):
3075
store = self.get_store('foo.conf')
3076
store._load_from_string('')
3077
matcher = self.matcher(store, '/bar')
3078
self.assertEquals([], list(matcher.get_sections()))
3080
def test_build_doesnt_load_store(self):
3081
store = self.get_store('foo.conf')
3082
matcher = self.matcher(store, '/bar')
3083
self.assertFalse(store.is_loaded())
3086
class TestLocationSection(tests.TestCase):
3088
def get_section(self, options, extra_path):
3089
section = config.Section('foo', options)
3090
# We don't care about the length so we use '0'
3091
return config.LocationSection(section, 0, extra_path)
3093
def test_simple_option(self):
3094
section = self.get_section({'foo': 'bar'}, '')
3095
self.assertEquals('bar', section.get('foo'))
3097
def test_option_with_extra_path(self):
3098
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3100
self.assertEquals('bar/baz', section.get('foo'))
3102
def test_invalid_policy(self):
3103
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3105
# invalid policies are ignored
3106
self.assertEquals('bar', section.get('foo'))
3109
class TestLocationMatcher(TestStore):
3111
def get_store(self, file_name):
3112
return config.IniFileStore(self.get_readonly_transport(), file_name)
3114
def test_unrelated_section_excluded(self):
3115
store = self.get_store('foo.conf')
3116
store._load_from_string('''
3124
section=/foo/bar/baz
3128
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3130
[section.id for section in store.get_sections()])
3131
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3132
sections = list(matcher.get_sections())
3133
self.assertEquals([3, 2],
3134
[section.length for section in sections])
3135
self.assertEquals(['/foo/bar', '/foo'],
3136
[section.id for section in sections])
3137
self.assertEquals(['quux', 'bar/quux'],
3138
[section.extra_path for section in sections])
3140
def test_more_specific_sections_first(self):
3141
store = self.get_store('foo.conf')
3142
store._load_from_string('''
3148
self.assertEquals(['/foo', '/foo/bar'],
3149
[section.id for section in store.get_sections()])
3150
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3151
sections = list(matcher.get_sections())
3152
self.assertEquals([3, 2],
3153
[section.length for section in sections])
3154
self.assertEquals(['/foo/bar', '/foo'],
3155
[section.id for section in sections])
3156
self.assertEquals(['baz', 'bar/baz'],
3157
[section.extra_path for section in sections])
3159
def test_appendpath_in_no_name_section(self):
3160
# It's a bit weird to allow appendpath in a no-name section, but
3161
# someone may found a use for it
3162
store = self.get_store('foo.conf')
3163
store._load_from_string('''
3165
foo:policy = appendpath
3167
matcher = config.LocationMatcher(store, 'dir/subdir')
3168
sections = list(matcher.get_sections())
3169
self.assertLength(1, sections)
3170
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
3172
def test_file_urls_are_normalized(self):
3173
store = self.get_store('foo.conf')
3174
if sys.platform == 'win32':
3175
expected_url = 'file:///C:/dir/subdir'
3176
expected_location = 'C:/dir/subdir'
3178
expected_url = 'file:///dir/subdir'
3179
expected_location = '/dir/subdir'
3180
matcher = config.LocationMatcher(store, expected_url)
3181
self.assertEquals(expected_location, matcher.location)
3184
class TestStackGet(tests.TestCase):
3186
# FIXME: This should be parametrized for all known Stack or dedicated
3187
# paramerized tests created to avoid bloating -- vila 2011-03-31
3189
def overrideOptionRegistry(self):
3190
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3192
def test_single_config_get(self):
3193
conf = dict(foo='bar')
3194
conf_stack = config.Stack([conf])
3195
self.assertEquals('bar', conf_stack.get('foo'))
3197
def test_get_with_registered_default_value(self):
3198
conf_stack = config.Stack([dict()])
3199
opt = config.Option('foo', default='bar')
3200
self.overrideOptionRegistry()
3201
config.option_registry.register('foo', opt)
3202
self.assertEquals('bar', conf_stack.get('foo'))
3204
def test_get_without_registered_default_value(self):
3205
conf_stack = config.Stack([dict()])
3206
opt = config.Option('foo')
3207
self.overrideOptionRegistry()
3208
config.option_registry.register('foo', opt)
3209
self.assertEquals(None, conf_stack.get('foo'))
3211
def test_get_without_default_value_for_not_registered(self):
3212
conf_stack = config.Stack([dict()])
3213
opt = config.Option('foo')
3214
self.overrideOptionRegistry()
3215
self.assertEquals(None, conf_stack.get('foo'))
3217
def test_get_first_definition(self):
3218
conf1 = dict(foo='bar')
3219
conf2 = dict(foo='baz')
3220
conf_stack = config.Stack([conf1, conf2])
3221
self.assertEquals('bar', conf_stack.get('foo'))
3223
def test_get_embedded_definition(self):
3224
conf1 = dict(yy='12')
3225
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3226
conf_stack = config.Stack([conf1, conf2])
3227
self.assertEquals('baz', conf_stack.get('foo'))
3229
def test_get_for_empty_section_callable(self):
3230
conf_stack = config.Stack([lambda : []])
3231
self.assertEquals(None, conf_stack.get('foo'))
3233
def test_get_for_broken_callable(self):
3234
# Trying to use and invalid callable raises an exception on first use
3235
conf_stack = config.Stack([lambda : object()])
3236
self.assertRaises(TypeError, conf_stack.get, 'foo')
3239
class TestStackWithTransport(tests.TestCaseWithTransport):
3241
scenarios = [(key, {'get_stack': builder}) for key, builder
3242
in config.test_stack_builder_registry.iteritems()]
3245
class TestConcreteStacks(TestStackWithTransport):
3247
def test_build_stack(self):
3248
# Just a smoke test to help debug builders
3249
stack = self.get_stack(self)
3252
class TestStackGet(TestStackWithTransport):
3255
super(TestStackGet, self).setUp()
3256
self.conf = self.get_stack(self)
3258
def test_get_for_empty_stack(self):
3259
self.assertEquals(None, self.conf.get('foo'))
3261
def test_get_hook(self):
3262
self.conf.store._load_from_string('foo=bar')
3266
config.ConfigHooks.install_named_hook('get', hook, None)
3267
self.assertLength(0, calls)
3268
value = self.conf.get('foo')
3269
self.assertEquals('bar', value)
3270
self.assertLength(1, calls)
3271
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3274
class TestStackGetWithConverter(TestStackGet):
3277
super(TestStackGetWithConverter, self).setUp()
3278
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3279
self.registry = config.option_registry
3281
def register_bool_option(self, name, default=None, default_from_env=None):
3282
b = config.Option(name, help='A boolean.',
3283
default=default, default_from_env=default_from_env,
3284
from_unicode=config.bool_from_store)
3285
self.registry.register(b)
3287
def test_get_default_bool_None(self):
3288
self.register_bool_option('foo')
3289
self.assertEquals(None, self.conf.get('foo'))
3291
def test_get_default_bool_True(self):
3292
self.register_bool_option('foo', u'True')
3293
self.assertEquals(True, self.conf.get('foo'))
3295
def test_get_default_bool_False(self):
3296
self.register_bool_option('foo', False)
3297
self.assertEquals(False, self.conf.get('foo'))
3299
def test_get_default_bool_False_as_string(self):
3300
self.register_bool_option('foo', u'False')
3301
self.assertEquals(False, self.conf.get('foo'))
3303
def test_get_default_bool_from_env_converted(self):
3304
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3305
self.overrideEnv('FOO', 'False')
3306
self.assertEquals(False, self.conf.get('foo'))
3308
def test_get_default_bool_when_conversion_fails(self):
3309
self.register_bool_option('foo', default='True')
3310
self.conf.store._load_from_string('foo=invalid boolean')
3311
self.assertEquals(True, self.conf.get('foo'))
3313
def register_integer_option(self, name,
3314
default=None, default_from_env=None):
3315
i = config.Option(name, help='An integer.',
3316
default=default, default_from_env=default_from_env,
3317
from_unicode=config.int_from_store)
3318
self.registry.register(i)
3320
def test_get_default_integer_None(self):
3321
self.register_integer_option('foo')
3322
self.assertEquals(None, self.conf.get('foo'))
3324
def test_get_default_integer(self):
3325
self.register_integer_option('foo', 42)
3326
self.assertEquals(42, self.conf.get('foo'))
3328
def test_get_default_integer_as_string(self):
3329
self.register_integer_option('foo', u'42')
3330
self.assertEquals(42, self.conf.get('foo'))
3332
def test_get_default_integer_from_env(self):
3333
self.register_integer_option('foo', default_from_env=['FOO'])
3334
self.overrideEnv('FOO', '18')
3335
self.assertEquals(18, self.conf.get('foo'))
3337
def test_get_default_integer_when_conversion_fails(self):
3338
self.register_integer_option('foo', default='12')
3339
self.conf.store._load_from_string('foo=invalid integer')
3340
self.assertEquals(12, self.conf.get('foo'))
3342
def register_list_option(self, name, default=None, default_from_env=None):
3343
l = config.Option(name, help='A list.',
3344
default=default, default_from_env=default_from_env,
3345
from_unicode=config.list_from_store)
3346
self.registry.register(l)
3348
def test_get_default_list_None(self):
3349
self.register_list_option('foo')
3350
self.assertEquals(None, self.conf.get('foo'))
3352
def test_get_default_list_empty(self):
3353
self.register_list_option('foo', '')
3354
self.assertEquals([], self.conf.get('foo'))
3356
def test_get_default_list_from_env(self):
3357
self.register_list_option('foo', default_from_env=['FOO'])
3358
self.overrideEnv('FOO', '')
3359
self.assertEquals([], self.conf.get('foo'))
3361
def test_get_with_list_converter_no_item(self):
3362
self.register_list_option('foo', None)
3363
self.conf.store._load_from_string('foo=,')
3364
self.assertEquals([], self.conf.get('foo'))
3366
def test_get_with_list_converter_many_items(self):
3367
self.register_list_option('foo', None)
3368
self.conf.store._load_from_string('foo=m,o,r,e')
3369
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3371
def test_get_with_list_converter_embedded_spaces_many_items(self):
3372
self.register_list_option('foo', None)
3373
self.conf.store._load_from_string('foo=" bar", "baz "')
3374
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3376
def test_get_with_list_converter_stripped_spaces_many_items(self):
3377
self.register_list_option('foo', None)
3378
self.conf.store._load_from_string('foo= bar , baz ')
3379
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3382
class TestStackExpandOptions(tests.TestCaseWithTransport):
3385
super(TestStackExpandOptions, self).setUp()
3386
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3387
self.registry = config.option_registry
3388
self.conf = build_branch_stack(self)
3390
def assertExpansion(self, expected, string, env=None):
3391
self.assertEquals(expected, self.conf.expand_options(string, env))
3393
def test_no_expansion(self):
3394
self.assertExpansion('foo', 'foo')
3396
def test_expand_default_value(self):
3397
self.conf.store._load_from_string('bar=baz')
3398
self.registry.register(config.Option('foo', default=u'{bar}'))
3399
self.assertEquals('baz', self.conf.get('foo', expand=True))
3401
def test_expand_default_from_env(self):
3402
self.conf.store._load_from_string('bar=baz')
3403
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3404
self.overrideEnv('FOO', '{bar}')
3405
self.assertEquals('baz', self.conf.get('foo', expand=True))
3407
def test_expand_default_on_failed_conversion(self):
3408
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3409
self.registry.register(
3410
config.Option('foo', default=u'{bar}',
3411
from_unicode=config.int_from_store))
3412
self.assertEquals(42, self.conf.get('foo', expand=True))
3414
def test_env_adding_options(self):
3415
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3417
def test_env_overriding_options(self):
3418
self.conf.store._load_from_string('foo=baz')
3419
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3421
def test_simple_ref(self):
3422
self.conf.store._load_from_string('foo=xxx')
3423
self.assertExpansion('xxx', '{foo}')
3425
def test_unknown_ref(self):
3426
self.assertRaises(errors.ExpandingUnknownOption,
3427
self.conf.expand_options, '{foo}')
3429
def test_indirect_ref(self):
3430
self.conf.store._load_from_string('''
3434
self.assertExpansion('xxx', '{bar}')
3436
def test_embedded_ref(self):
3437
self.conf.store._load_from_string('''
3441
self.assertExpansion('xxx', '{{bar}}')
3443
def test_simple_loop(self):
3444
self.conf.store._load_from_string('foo={foo}')
3445
self.assertRaises(errors.OptionExpansionLoop,
3446
self.conf.expand_options, '{foo}')
3448
def test_indirect_loop(self):
3449
self.conf.store._load_from_string('''
3453
e = self.assertRaises(errors.OptionExpansionLoop,
3454
self.conf.expand_options, '{foo}')
3455
self.assertEquals('foo->bar->baz', e.refs)
3456
self.assertEquals('{foo}', e.string)
3458
def test_list(self):
3459
self.conf.store._load_from_string('''
3463
list={foo},{bar},{baz}
3465
self.registry.register(
3466
config.Option('list', from_unicode=config.list_from_store))
3467
self.assertEquals(['start', 'middle', 'end'],
3468
self.conf.get('list', expand=True))
3470
def test_cascading_list(self):
3471
self.conf.store._load_from_string('''
3477
self.registry.register(
3478
config.Option('list', from_unicode=config.list_from_store))
3479
self.assertEquals(['start', 'middle', 'end'],
3480
self.conf.get('list', expand=True))
3482
def test_pathologically_hidden_list(self):
3483
self.conf.store._load_from_string('''
3489
hidden={start}{middle}{end}
3491
# What matters is what the registration says, the conversion happens
3492
# only after all expansions have been performed
3493
self.registry.register(
3494
config.Option('hidden', from_unicode=config.list_from_store))
3495
self.assertEquals(['bin', 'go'],
3496
self.conf.get('hidden', expand=True))
3499
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3502
super(TestStackCrossSectionsExpand, self).setUp()
3504
def get_config(self, location, string):
3507
# Since we don't save the config we won't strictly require to inherit
3508
# from TestCaseInTempDir, but an error occurs so quickly...
3509
c = config.LocationStack(location)
3510
c.store._load_from_string(string)
3513
def test_dont_cross_unrelated_section(self):
3514
c = self.get_config('/another/branch/path','''
3519
[/another/branch/path]
3522
self.assertRaises(errors.ExpandingUnknownOption,
3523
c.get, 'bar', expand=True)
3525
def test_cross_related_sections(self):
3526
c = self.get_config('/project/branch/path','''
3530
[/project/branch/path]
3533
self.assertEquals('quux', c.get('bar', expand=True))
3536
class TestStackSet(TestStackWithTransport):
3538
def test_simple_set(self):
3539
conf = self.get_stack(self)
3540
conf.store._load_from_string('foo=bar')
3541
self.assertEquals('bar', conf.get('foo'))
3542
conf.set('foo', 'baz')
3543
# Did we get it back ?
3544
self.assertEquals('baz', conf.get('foo'))
3546
def test_set_creates_a_new_section(self):
3547
conf = self.get_stack(self)
3548
conf.set('foo', 'baz')
3549
self.assertEquals, 'baz', conf.get('foo')
3551
def test_set_hook(self):
3555
config.ConfigHooks.install_named_hook('set', hook, None)
3556
self.assertLength(0, calls)
3557
conf = self.get_stack(self)
3558
conf.set('foo', 'bar')
3559
self.assertLength(1, calls)
3560
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3563
class TestStackRemove(TestStackWithTransport):
3565
def test_remove_existing(self):
3566
conf = self.get_stack(self)
3567
conf.store._load_from_string('foo=bar')
3568
self.assertEquals('bar', conf.get('foo'))
3570
# Did we get it back ?
3571
self.assertEquals(None, conf.get('foo'))
3573
def test_remove_unknown(self):
3574
conf = self.get_stack(self)
3575
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3577
def test_remove_hook(self):
3581
config.ConfigHooks.install_named_hook('remove', hook, None)
3582
self.assertLength(0, calls)
3583
conf = self.get_stack(self)
3584
conf.store._load_from_string('foo=bar')
3586
self.assertLength(1, calls)
3587
self.assertEquals((conf, 'foo'), calls[0])
3590
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3593
super(TestConfigGetOptions, self).setUp()
3594
create_configs(self)
3596
def test_no_variable(self):
3597
# Using branch should query branch, locations and bazaar
3598
self.assertOptions([], self.branch_config)
3600
def test_option_in_bazaar(self):
3601
self.bazaar_config.set_user_option('file', 'bazaar')
3602
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3605
def test_option_in_locations(self):
3606
self.locations_config.set_user_option('file', 'locations')
3608
[('file', 'locations', self.tree.basedir, 'locations')],
3609
self.locations_config)
3611
def test_option_in_branch(self):
3612
self.branch_config.set_user_option('file', 'branch')
3613
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3616
def test_option_in_bazaar_and_branch(self):
3617
self.bazaar_config.set_user_option('file', 'bazaar')
3618
self.branch_config.set_user_option('file', 'branch')
3619
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3620
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3623
def test_option_in_branch_and_locations(self):
3624
# Hmm, locations override branch :-/
3625
self.locations_config.set_user_option('file', 'locations')
3626
self.branch_config.set_user_option('file', 'branch')
3628
[('file', 'locations', self.tree.basedir, 'locations'),
3629
('file', 'branch', 'DEFAULT', 'branch'),],
3632
def test_option_in_bazaar_locations_and_branch(self):
3633
self.bazaar_config.set_user_option('file', 'bazaar')
3634
self.locations_config.set_user_option('file', 'locations')
3635
self.branch_config.set_user_option('file', 'branch')
3637
[('file', 'locations', self.tree.basedir, 'locations'),
3638
('file', 'branch', 'DEFAULT', 'branch'),
3639
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3643
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3646
super(TestConfigRemoveOption, self).setUp()
3647
create_configs_with_file_option(self)
3649
def test_remove_in_locations(self):
3650
self.locations_config.remove_user_option('file', self.tree.basedir)
3652
[('file', 'branch', 'DEFAULT', 'branch'),
3653
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3656
def test_remove_in_branch(self):
3657
self.branch_config.remove_user_option('file')
3659
[('file', 'locations', self.tree.basedir, 'locations'),
3660
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3663
def test_remove_in_bazaar(self):
3664
self.bazaar_config.remove_user_option('file')
3666
[('file', 'locations', self.tree.basedir, 'locations'),
3667
('file', 'branch', 'DEFAULT', 'branch'),],
3671
class TestConfigGetSections(tests.TestCaseWithTransport):
3674
super(TestConfigGetSections, self).setUp()
3675
create_configs(self)
3677
def assertSectionNames(self, expected, conf, name=None):
3678
"""Check which sections are returned for a given config.
3680
If fallback configurations exist their sections can be included.
3682
:param expected: A list of section names.
3684
:param conf: The configuration that will be queried.
3686
:param name: An optional section name that will be passed to
3689
sections = list(conf._get_sections(name))
3690
self.assertLength(len(expected), sections)
3691
self.assertEqual(expected, [name for name, _, _ in sections])
3693
def test_bazaar_default_section(self):
3694
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3696
def test_locations_default_section(self):
3697
# No sections are defined in an empty file
3698
self.assertSectionNames([], self.locations_config)
3700
def test_locations_named_section(self):
3701
self.locations_config.set_user_option('file', 'locations')
3702
self.assertSectionNames([self.tree.basedir], self.locations_config)
3704
def test_locations_matching_sections(self):
3705
loc_config = self.locations_config
3706
loc_config.set_user_option('file', 'locations')
3707
# We need to cheat a bit here to create an option in sections above and
3708
# below the 'location' one.
3709
parser = loc_config._get_parser()
3710
# locations.cong deals with '/' ignoring native os.sep
3711
location_names = self.tree.basedir.split('/')
3712
parent = '/'.join(location_names[:-1])
3713
child = '/'.join(location_names + ['child'])
3715
parser[parent]['file'] = 'parent'
3717
parser[child]['file'] = 'child'
3718
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3720
def test_branch_data_default_section(self):
3721
self.assertSectionNames([None],
3722
self.branch_config._get_branch_data_config())
3724
def test_branch_default_sections(self):
3725
# No sections are defined in an empty locations file
3726
self.assertSectionNames([None, 'DEFAULT'],
3728
# Unless we define an option
3729
self.branch_config._get_location_config().set_user_option(
3730
'file', 'locations')
3731
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3734
def test_bazaar_named_section(self):
3735
# We need to cheat as the API doesn't give direct access to sections
3736
# other than DEFAULT.
3737
self.bazaar_config.set_alias('bazaar', 'bzr')
3738
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
3741
class TestAuthenticationConfigFile(tests.TestCase):
1316
3742
"""Test the authentication.conf file matching"""