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()]
2627
def test_building_delays_load(self):
2628
store = self.get_store(self)
2629
self.assertEquals(False, store.is_loaded())
2630
store._load_from_string('')
2631
self.assertEquals(True, store.is_loaded())
2633
def test_get_no_sections_for_empty(self):
2634
store = self.get_store(self)
2635
store._load_from_string('')
2636
self.assertEquals([], list(store.get_sections()))
2638
def test_get_default_section(self):
2639
store = self.get_store(self)
2640
store._load_from_string('foo=bar')
2641
sections = list(store.get_sections())
2642
self.assertLength(1, sections)
2643
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2645
def test_get_named_section(self):
2646
store = self.get_store(self)
2647
store._load_from_string('[baz]\nfoo=bar')
2648
sections = list(store.get_sections())
2649
self.assertLength(1, sections)
2650
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2652
def test_load_from_string_fails_for_non_empty_store(self):
2653
store = self.get_store(self)
2654
store._load_from_string('foo=bar')
2655
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2658
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2659
"""Simulate loading a config store with content of various encodings.
2661
All files produced by bzr are in utf8 content.
2663
Users may modify them manually and end up with a file that can't be
2664
loaded. We need to issue proper error messages in this case.
2667
invalid_utf8_char = '\xff'
2669
def test_load_utf8(self):
2670
"""Ensure we can load an utf8-encoded file."""
2671
t = self.get_transport()
2672
# From http://pad.lv/799212
2673
unicode_user = u'b\N{Euro Sign}ar'
2674
unicode_content = u'user=%s' % (unicode_user,)
2675
utf8_content = unicode_content.encode('utf8')
2676
# Store the raw content in the config file
2677
t.put_bytes('foo.conf', utf8_content)
2678
store = config.IniFileStore(t, 'foo.conf')
2680
stack = config.Stack([store.get_sections], store)
2681
self.assertEquals(unicode_user, stack.get('user'))
2683
def test_load_non_ascii(self):
2684
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2685
t = self.get_transport()
2686
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2687
store = config.IniFileStore(t, 'foo.conf')
2688
self.assertRaises(errors.ConfigContentError, store.load)
2690
def test_load_erroneous_content(self):
2691
"""Ensure we display a proper error on content that can't be parsed."""
2692
t = self.get_transport()
2693
t.put_bytes('foo.conf', '[open_section\n')
2694
store = config.IniFileStore(t, 'foo.conf')
2695
self.assertRaises(errors.ParseConfigError, store.load)
2697
def test_load_permission_denied(self):
2698
"""Ensure we get warned when trying to load an inaccessible file."""
2701
warnings.append(args[0] % args[1:])
2702
self.overrideAttr(trace, 'warning', warning)
2704
t = self.get_transport()
2706
def get_bytes(relpath):
2707
raise errors.PermissionDenied(relpath, "")
2708
t.get_bytes = get_bytes
2709
store = config.IniFileStore(t, 'foo.conf')
2710
self.assertRaises(errors.PermissionDenied, store.load)
2713
[u'Permission denied while trying to load configuration store %s.'
2714
% store.external_url()])
2717
class TestIniConfigContent(tests.TestCaseWithTransport):
2718
"""Simulate loading a IniBasedConfig with content of various encodings.
2720
All files produced by bzr are in utf8 content.
2722
Users may modify them manually and end up with a file that can't be
2723
loaded. We need to issue proper error messages in this case.
2726
invalid_utf8_char = '\xff'
2728
def test_load_utf8(self):
2729
"""Ensure we can load an utf8-encoded file."""
2730
# From http://pad.lv/799212
2731
unicode_user = u'b\N{Euro Sign}ar'
2732
unicode_content = u'user=%s' % (unicode_user,)
2733
utf8_content = unicode_content.encode('utf8')
2734
# Store the raw content in the config file
2735
with open('foo.conf', 'wb') as f:
2736
f.write(utf8_content)
2737
conf = config.IniBasedConfig(file_name='foo.conf')
2738
self.assertEquals(unicode_user, conf.get_user_option('user'))
2740
def test_load_badly_encoded_content(self):
2741
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2742
with open('foo.conf', 'wb') as f:
2743
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2744
conf = config.IniBasedConfig(file_name='foo.conf')
2745
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2747
def test_load_erroneous_content(self):
2748
"""Ensure we display a proper error on content that can't be parsed."""
2749
with open('foo.conf', 'wb') as f:
2750
f.write('[open_section\n')
2751
conf = config.IniBasedConfig(file_name='foo.conf')
2752
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2755
class TestMutableStore(TestStore):
2757
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2758
in config.test_store_builder_registry.iteritems()]
2761
super(TestMutableStore, self).setUp()
2762
self.transport = self.get_transport()
2764
def has_store(self, store):
2765
store_basename = urlutils.relative_url(self.transport.external_url(),
2766
store.external_url())
2767
return self.transport.has(store_basename)
2769
def test_save_empty_creates_no_file(self):
2770
# FIXME: There should be a better way than relying on the test
2771
# parametrization to identify branch.conf -- vila 2011-0526
2772
if self.store_id in ('branch', 'remote_branch'):
2773
raise tests.TestNotApplicable(
2774
'branch.conf is *always* created when a branch is initialized')
2775
store = self.get_store(self)
2777
self.assertEquals(False, self.has_store(store))
2779
def test_save_emptied_succeeds(self):
2780
store = self.get_store(self)
2781
store._load_from_string('foo=bar\n')
2782
section = store.get_mutable_section(None)
2783
section.remove('foo')
2785
self.assertEquals(True, self.has_store(store))
2786
modified_store = self.get_store(self)
2787
sections = list(modified_store.get_sections())
2788
self.assertLength(0, sections)
2790
def test_save_with_content_succeeds(self):
2791
# FIXME: There should be a better way than relying on the test
2792
# parametrization to identify branch.conf -- vila 2011-0526
2793
if self.store_id in ('branch', 'remote_branch'):
2794
raise tests.TestNotApplicable(
2795
'branch.conf is *always* created when a branch is initialized')
2796
store = self.get_store(self)
2797
store._load_from_string('foo=bar\n')
2798
self.assertEquals(False, self.has_store(store))
2800
self.assertEquals(True, self.has_store(store))
2801
modified_store = self.get_store(self)
2802
sections = list(modified_store.get_sections())
2803
self.assertLength(1, sections)
2804
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2806
def test_set_option_in_empty_store(self):
2807
store = self.get_store(self)
2808
section = store.get_mutable_section(None)
2809
section.set('foo', 'bar')
2811
modified_store = self.get_store(self)
2812
sections = list(modified_store.get_sections())
2813
self.assertLength(1, sections)
2814
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2816
def test_set_option_in_default_section(self):
2817
store = self.get_store(self)
2818
store._load_from_string('')
2819
section = store.get_mutable_section(None)
2820
section.set('foo', 'bar')
2822
modified_store = self.get_store(self)
2823
sections = list(modified_store.get_sections())
2824
self.assertLength(1, sections)
2825
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2827
def test_set_option_in_named_section(self):
2828
store = self.get_store(self)
2829
store._load_from_string('')
2830
section = store.get_mutable_section('baz')
2831
section.set('foo', 'bar')
2833
modified_store = self.get_store(self)
2834
sections = list(modified_store.get_sections())
2835
self.assertLength(1, sections)
2836
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2838
def test_load_hook(self):
2839
# We first needs to ensure that the store exists
2840
store = self.get_store(self)
2841
section = store.get_mutable_section('baz')
2842
section.set('foo', 'bar')
2844
# Now we can try to load it
2845
store = self.get_store(self)
2849
config.ConfigHooks.install_named_hook('load', hook, None)
2850
self.assertLength(0, calls)
2852
self.assertLength(1, calls)
2853
self.assertEquals((store,), calls[0])
2855
def test_save_hook(self):
2859
config.ConfigHooks.install_named_hook('save', hook, None)
2860
self.assertLength(0, calls)
2861
store = self.get_store(self)
2862
section = store.get_mutable_section('baz')
2863
section.set('foo', 'bar')
2865
self.assertLength(1, calls)
2866
self.assertEquals((store,), calls[0])
2869
class TestIniFileStore(TestStore):
2871
def test_loading_unknown_file_fails(self):
2872
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2873
self.assertRaises(errors.NoSuchFile, store.load)
2875
def test_invalid_content(self):
2876
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2877
self.assertEquals(False, store.is_loaded())
2878
exc = self.assertRaises(
2879
errors.ParseConfigError, store._load_from_string,
2880
'this is invalid !')
2881
self.assertEndsWith(exc.filename, 'foo.conf')
2882
# And the load failed
2883
self.assertEquals(False, store.is_loaded())
2885
def test_get_embedded_sections(self):
2886
# A more complicated example (which also shows that section names and
2887
# option names share the same name space...)
2888
# FIXME: This should be fixed by forbidding dicts as values ?
2889
# -- vila 2011-04-05
2890
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2891
store._load_from_string('''
2895
foo_in_DEFAULT=foo_DEFAULT
2903
sections = list(store.get_sections())
2904
self.assertLength(4, sections)
2905
# The default section has no name.
2906
# List values are provided as strings and need to be explicitly
2907
# converted by specifying from_unicode=list_from_store at option
2909
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2911
self.assertSectionContent(
2912
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2913
self.assertSectionContent(
2914
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2915
# sub sections are provided as embedded dicts.
2916
self.assertSectionContent(
2917
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2921
class TestLockableIniFileStore(TestStore):
2923
def test_create_store_in_created_dir(self):
2924
self.assertPathDoesNotExist('dir')
2925
t = self.get_transport('dir/subdir')
2926
store = config.LockableIniFileStore(t, 'foo.conf')
2927
store.get_mutable_section(None).set('foo', 'bar')
2929
self.assertPathExists('dir/subdir')
2932
class TestConcurrentStoreUpdates(TestStore):
2933
"""Test that Stores properly handle conccurent updates.
2935
New Store implementation may fail some of these tests but until such
2936
implementations exist it's hard to properly filter them from the scenarios
2937
applied here. If you encounter such a case, contact the bzr devs.
2940
scenarios = [(key, {'get_stack': builder}) for key, builder
2941
in config.test_stack_builder_registry.iteritems()]
2944
super(TestConcurrentStoreUpdates, self).setUp()
2945
self._content = 'one=1\ntwo=2\n'
2946
self.stack = self.get_stack(self)
2947
if not isinstance(self.stack, config._CompatibleStack):
2948
raise tests.TestNotApplicable(
2949
'%s is not meant to be compatible with the old config design'
2951
self.stack.store._load_from_string(self._content)
2953
self.stack.store.save()
2955
def test_simple_read_access(self):
2956
self.assertEquals('1', self.stack.get('one'))
2958
def test_simple_write_access(self):
2959
self.stack.set('one', 'one')
2960
self.assertEquals('one', self.stack.get('one'))
2962
def test_listen_to_the_last_speaker(self):
2964
c2 = self.get_stack(self)
2965
c1.set('one', 'ONE')
2966
c2.set('two', 'TWO')
2967
self.assertEquals('ONE', c1.get('one'))
2968
self.assertEquals('TWO', c2.get('two'))
2969
# The second update respect the first one
2970
self.assertEquals('ONE', c2.get('one'))
2972
def test_last_speaker_wins(self):
2973
# If the same config is not shared, the same variable modified twice
2974
# can only see a single result.
2976
c2 = self.get_stack(self)
2979
self.assertEquals('c2', c2.get('one'))
2980
# The first modification is still available until another refresh
2982
self.assertEquals('c1', c1.get('one'))
2983
c1.set('two', 'done')
2984
self.assertEquals('c2', c1.get('one'))
2986
def test_writes_are_serialized(self):
2988
c2 = self.get_stack(self)
2990
# We spawn a thread that will pause *during* the config saving.
2991
before_writing = threading.Event()
2992
after_writing = threading.Event()
2993
writing_done = threading.Event()
2994
c1_save_without_locking_orig = c1.store.save_without_locking
2995
def c1_save_without_locking():
2996
before_writing.set()
2997
c1_save_without_locking_orig()
2998
# The lock is held. We wait for the main thread to decide when to
3000
after_writing.wait()
3001
c1.store.save_without_locking = c1_save_without_locking
3005
t1 = threading.Thread(target=c1_set)
3006
# Collect the thread after the test
3007
self.addCleanup(t1.join)
3008
# Be ready to unblock the thread if the test goes wrong
3009
self.addCleanup(after_writing.set)
3011
before_writing.wait()
3012
self.assertRaises(errors.LockContention,
3013
c2.set, 'one', 'c2')
3014
self.assertEquals('c1', c1.get('one'))
3015
# Let the lock be released
3019
self.assertEquals('c2', c2.get('one'))
3021
def test_read_while_writing(self):
3023
# We spawn a thread that will pause *during* the write
3024
ready_to_write = threading.Event()
3025
do_writing = threading.Event()
3026
writing_done = threading.Event()
3027
# We override the _save implementation so we know the store is locked
3028
c1_save_without_locking_orig = c1.store.save_without_locking
3029
def c1_save_without_locking():
3030
ready_to_write.set()
3031
# The lock is held. We wait for the main thread to decide when to
3034
c1_save_without_locking_orig()
3036
c1.store.save_without_locking = c1_save_without_locking
3039
t1 = threading.Thread(target=c1_set)
3040
# Collect the thread after the test
3041
self.addCleanup(t1.join)
3042
# Be ready to unblock the thread if the test goes wrong
3043
self.addCleanup(do_writing.set)
3045
# Ensure the thread is ready to write
3046
ready_to_write.wait()
3047
self.assertEquals('c1', c1.get('one'))
3048
# If we read during the write, we get the old value
3049
c2 = self.get_stack(self)
3050
self.assertEquals('1', c2.get('one'))
3051
# Let the writing occur and ensure it occurred
3054
# Now we get the updated value
3055
c3 = self.get_stack(self)
3056
self.assertEquals('c1', c3.get('one'))
3058
# FIXME: It may be worth looking into removing the lock dir when it's not
3059
# needed anymore and look at possible fallouts for concurrent lockers. This
3060
# will matter if/when we use config files outside of bazaar directories
3061
# (.bazaar or .bzr) -- vila 20110-04-111
3064
class TestSectionMatcher(TestStore):
3066
scenarios = [('location', {'matcher': config.LocationMatcher}),
3067
('id', {'matcher': config.NameMatcher}),]
3069
def get_store(self, file_name):
3070
return config.IniFileStore(self.get_readonly_transport(), file_name)
3072
def test_no_matches_for_empty_stores(self):
3073
store = self.get_store('foo.conf')
3074
store._load_from_string('')
3075
matcher = self.matcher(store, '/bar')
3076
self.assertEquals([], list(matcher.get_sections()))
3078
def test_build_doesnt_load_store(self):
3079
store = self.get_store('foo.conf')
3080
matcher = self.matcher(store, '/bar')
3081
self.assertFalse(store.is_loaded())
3084
class TestLocationSection(tests.TestCase):
3086
def get_section(self, options, extra_path):
3087
section = config.Section('foo', options)
3088
# We don't care about the length so we use '0'
3089
return config.LocationSection(section, 0, extra_path)
3091
def test_simple_option(self):
3092
section = self.get_section({'foo': 'bar'}, '')
3093
self.assertEquals('bar', section.get('foo'))
3095
def test_option_with_extra_path(self):
3096
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3098
self.assertEquals('bar/baz', section.get('foo'))
3100
def test_invalid_policy(self):
3101
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3103
# invalid policies are ignored
3104
self.assertEquals('bar', section.get('foo'))
3107
class TestLocationMatcher(TestStore):
3109
def get_store(self, file_name):
3110
return config.IniFileStore(self.get_readonly_transport(), file_name)
3112
def test_unrelated_section_excluded(self):
3113
store = self.get_store('foo.conf')
3114
store._load_from_string('''
3122
section=/foo/bar/baz
3126
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3128
[section.id for section in store.get_sections()])
3129
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3130
sections = list(matcher.get_sections())
3131
self.assertEquals([3, 2],
3132
[section.length for section in sections])
3133
self.assertEquals(['/foo/bar', '/foo'],
3134
[section.id for section in sections])
3135
self.assertEquals(['quux', 'bar/quux'],
3136
[section.extra_path for section in sections])
3138
def test_more_specific_sections_first(self):
3139
store = self.get_store('foo.conf')
3140
store._load_from_string('''
3146
self.assertEquals(['/foo', '/foo/bar'],
3147
[section.id for section in store.get_sections()])
3148
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3149
sections = list(matcher.get_sections())
3150
self.assertEquals([3, 2],
3151
[section.length for section in sections])
3152
self.assertEquals(['/foo/bar', '/foo'],
3153
[section.id for section in sections])
3154
self.assertEquals(['baz', 'bar/baz'],
3155
[section.extra_path for section in sections])
3157
def test_appendpath_in_no_name_section(self):
3158
# It's a bit weird to allow appendpath in a no-name section, but
3159
# someone may found a use for it
3160
store = self.get_store('foo.conf')
3161
store._load_from_string('''
3163
foo:policy = appendpath
3165
matcher = config.LocationMatcher(store, 'dir/subdir')
3166
sections = list(matcher.get_sections())
3167
self.assertLength(1, sections)
3168
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
3170
def test_file_urls_are_normalized(self):
3171
store = self.get_store('foo.conf')
3172
if sys.platform == 'win32':
3173
expected_url = 'file:///C:/dir/subdir'
3174
expected_location = 'C:/dir/subdir'
3176
expected_url = 'file:///dir/subdir'
3177
expected_location = '/dir/subdir'
3178
matcher = config.LocationMatcher(store, expected_url)
3179
self.assertEquals(expected_location, matcher.location)
3182
class TestNameMatcher(TestStore):
3185
super(TestNameMatcher, self).setUp()
3186
self.store = config.IniFileStore(self.get_readonly_transport(),
3188
self.store._load_from_string('''
3197
def get_matching_sections(self, name):
3198
matcher = config.NameMatcher(self.store, name)
3199
return list(matcher.get_sections())
3201
def test_matching(self):
3202
sections = self.get_matching_sections('foo')
3203
self.assertLength(1, sections)
3204
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3206
def test_not_matching(self):
3207
sections = self.get_matching_sections('baz')
3208
self.assertLength(0, sections)
3211
class TestStackGet(tests.TestCase):
3213
# FIXME: This should be parametrized for all known Stack or dedicated
3214
# paramerized tests created to avoid bloating -- vila 2011-03-31
3216
def overrideOptionRegistry(self):
3217
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3219
def test_single_config_get(self):
3220
conf = dict(foo='bar')
3221
conf_stack = config.Stack([conf])
3222
self.assertEquals('bar', conf_stack.get('foo'))
3224
def test_get_with_registered_default_value(self):
3225
conf_stack = config.Stack([dict()])
3226
opt = config.Option('foo', default='bar')
3227
self.overrideOptionRegistry()
3228
config.option_registry.register('foo', opt)
3229
self.assertEquals('bar', conf_stack.get('foo'))
3231
def test_get_without_registered_default_value(self):
3232
conf_stack = config.Stack([dict()])
3233
opt = config.Option('foo')
3234
self.overrideOptionRegistry()
3235
config.option_registry.register('foo', opt)
3236
self.assertEquals(None, conf_stack.get('foo'))
3238
def test_get_without_default_value_for_not_registered(self):
3239
conf_stack = config.Stack([dict()])
3240
opt = config.Option('foo')
3241
self.overrideOptionRegistry()
3242
self.assertEquals(None, conf_stack.get('foo'))
3244
def test_get_first_definition(self):
3245
conf1 = dict(foo='bar')
3246
conf2 = dict(foo='baz')
3247
conf_stack = config.Stack([conf1, conf2])
3248
self.assertEquals('bar', conf_stack.get('foo'))
3250
def test_get_embedded_definition(self):
3251
conf1 = dict(yy='12')
3252
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3253
conf_stack = config.Stack([conf1, conf2])
3254
self.assertEquals('baz', conf_stack.get('foo'))
3256
def test_get_for_empty_section_callable(self):
3257
conf_stack = config.Stack([lambda : []])
3258
self.assertEquals(None, conf_stack.get('foo'))
3260
def test_get_for_broken_callable(self):
3261
# Trying to use and invalid callable raises an exception on first use
3262
conf_stack = config.Stack([lambda : object()])
3263
self.assertRaises(TypeError, conf_stack.get, 'foo')
3266
class TestStackWithTransport(tests.TestCaseWithTransport):
3268
scenarios = [(key, {'get_stack': builder}) for key, builder
3269
in config.test_stack_builder_registry.iteritems()]
3272
class TestConcreteStacks(TestStackWithTransport):
3274
def test_build_stack(self):
3275
# Just a smoke test to help debug builders
3276
stack = self.get_stack(self)
3279
class TestStackGet(TestStackWithTransport):
3282
super(TestStackGet, self).setUp()
3283
self.conf = self.get_stack(self)
3285
def test_get_for_empty_stack(self):
3286
self.assertEquals(None, self.conf.get('foo'))
3288
def test_get_hook(self):
3289
self.conf.store._load_from_string('foo=bar')
3293
config.ConfigHooks.install_named_hook('get', hook, None)
3294
self.assertLength(0, calls)
3295
value = self.conf.get('foo')
3296
self.assertEquals('bar', value)
3297
self.assertLength(1, calls)
3298
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3301
class TestStackGetWithConverter(TestStackGet):
3304
super(TestStackGetWithConverter, self).setUp()
3305
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3306
self.registry = config.option_registry
3308
def register_bool_option(self, name, default=None, default_from_env=None):
3309
b = config.Option(name, help='A boolean.',
3310
default=default, default_from_env=default_from_env,
3311
from_unicode=config.bool_from_store)
3312
self.registry.register(b)
3314
def test_get_default_bool_None(self):
3315
self.register_bool_option('foo')
3316
self.assertEquals(None, self.conf.get('foo'))
3318
def test_get_default_bool_True(self):
3319
self.register_bool_option('foo', u'True')
3320
self.assertEquals(True, self.conf.get('foo'))
3322
def test_get_default_bool_False(self):
3323
self.register_bool_option('foo', False)
3324
self.assertEquals(False, self.conf.get('foo'))
3326
def test_get_default_bool_False_as_string(self):
3327
self.register_bool_option('foo', u'False')
3328
self.assertEquals(False, self.conf.get('foo'))
3330
def test_get_default_bool_from_env_converted(self):
3331
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3332
self.overrideEnv('FOO', 'False')
3333
self.assertEquals(False, self.conf.get('foo'))
3335
def test_get_default_bool_when_conversion_fails(self):
3336
self.register_bool_option('foo', default='True')
3337
self.conf.store._load_from_string('foo=invalid boolean')
3338
self.assertEquals(True, self.conf.get('foo'))
3340
def register_integer_option(self, name,
3341
default=None, default_from_env=None):
3342
i = config.Option(name, help='An integer.',
3343
default=default, default_from_env=default_from_env,
3344
from_unicode=config.int_from_store)
3345
self.registry.register(i)
3347
def test_get_default_integer_None(self):
3348
self.register_integer_option('foo')
3349
self.assertEquals(None, self.conf.get('foo'))
3351
def test_get_default_integer(self):
3352
self.register_integer_option('foo', 42)
3353
self.assertEquals(42, self.conf.get('foo'))
3355
def test_get_default_integer_as_string(self):
3356
self.register_integer_option('foo', u'42')
3357
self.assertEquals(42, self.conf.get('foo'))
3359
def test_get_default_integer_from_env(self):
3360
self.register_integer_option('foo', default_from_env=['FOO'])
3361
self.overrideEnv('FOO', '18')
3362
self.assertEquals(18, self.conf.get('foo'))
3364
def test_get_default_integer_when_conversion_fails(self):
3365
self.register_integer_option('foo', default='12')
3366
self.conf.store._load_from_string('foo=invalid integer')
3367
self.assertEquals(12, self.conf.get('foo'))
3369
def register_list_option(self, name, default=None, default_from_env=None):
3370
l = config.Option(name, help='A list.',
3371
default=default, default_from_env=default_from_env,
3372
from_unicode=config.list_from_store)
3373
self.registry.register(l)
3375
def test_get_default_list_None(self):
3376
self.register_list_option('foo')
3377
self.assertEquals(None, self.conf.get('foo'))
3379
def test_get_default_list_empty(self):
3380
self.register_list_option('foo', '')
3381
self.assertEquals([], self.conf.get('foo'))
3383
def test_get_default_list_from_env(self):
3384
self.register_list_option('foo', default_from_env=['FOO'])
3385
self.overrideEnv('FOO', '')
3386
self.assertEquals([], self.conf.get('foo'))
3388
def test_get_with_list_converter_no_item(self):
3389
self.register_list_option('foo', None)
3390
self.conf.store._load_from_string('foo=,')
3391
self.assertEquals([], self.conf.get('foo'))
3393
def test_get_with_list_converter_many_items(self):
3394
self.register_list_option('foo', None)
3395
self.conf.store._load_from_string('foo=m,o,r,e')
3396
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3398
def test_get_with_list_converter_embedded_spaces_many_items(self):
3399
self.register_list_option('foo', None)
3400
self.conf.store._load_from_string('foo=" bar", "baz "')
3401
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3403
def test_get_with_list_converter_stripped_spaces_many_items(self):
3404
self.register_list_option('foo', None)
3405
self.conf.store._load_from_string('foo= bar , baz ')
3406
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3409
class TestStackExpandOptions(tests.TestCaseWithTransport):
3412
super(TestStackExpandOptions, self).setUp()
3413
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3414
self.registry = config.option_registry
3415
self.conf = build_branch_stack(self)
3417
def assertExpansion(self, expected, string, env=None):
3418
self.assertEquals(expected, self.conf.expand_options(string, env))
3420
def test_no_expansion(self):
3421
self.assertExpansion('foo', 'foo')
3423
def test_expand_default_value(self):
3424
self.conf.store._load_from_string('bar=baz')
3425
self.registry.register(config.Option('foo', default=u'{bar}'))
3426
self.assertEquals('baz', self.conf.get('foo', expand=True))
3428
def test_expand_default_from_env(self):
3429
self.conf.store._load_from_string('bar=baz')
3430
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3431
self.overrideEnv('FOO', '{bar}')
3432
self.assertEquals('baz', self.conf.get('foo', expand=True))
3434
def test_expand_default_on_failed_conversion(self):
3435
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3436
self.registry.register(
3437
config.Option('foo', default=u'{bar}',
3438
from_unicode=config.int_from_store))
3439
self.assertEquals(42, self.conf.get('foo', expand=True))
3441
def test_env_adding_options(self):
3442
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3444
def test_env_overriding_options(self):
3445
self.conf.store._load_from_string('foo=baz')
3446
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3448
def test_simple_ref(self):
3449
self.conf.store._load_from_string('foo=xxx')
3450
self.assertExpansion('xxx', '{foo}')
3452
def test_unknown_ref(self):
3453
self.assertRaises(errors.ExpandingUnknownOption,
3454
self.conf.expand_options, '{foo}')
3456
def test_indirect_ref(self):
3457
self.conf.store._load_from_string('''
3461
self.assertExpansion('xxx', '{bar}')
3463
def test_embedded_ref(self):
3464
self.conf.store._load_from_string('''
3468
self.assertExpansion('xxx', '{{bar}}')
3470
def test_simple_loop(self):
3471
self.conf.store._load_from_string('foo={foo}')
3472
self.assertRaises(errors.OptionExpansionLoop,
3473
self.conf.expand_options, '{foo}')
3475
def test_indirect_loop(self):
3476
self.conf.store._load_from_string('''
3480
e = self.assertRaises(errors.OptionExpansionLoop,
3481
self.conf.expand_options, '{foo}')
3482
self.assertEquals('foo->bar->baz', e.refs)
3483
self.assertEquals('{foo}', e.string)
3485
def test_list(self):
3486
self.conf.store._load_from_string('''
3490
list={foo},{bar},{baz}
3492
self.registry.register(
3493
config.Option('list', from_unicode=config.list_from_store))
3494
self.assertEquals(['start', 'middle', 'end'],
3495
self.conf.get('list', expand=True))
3497
def test_cascading_list(self):
3498
self.conf.store._load_from_string('''
3504
self.registry.register(
3505
config.Option('list', from_unicode=config.list_from_store))
3506
self.assertEquals(['start', 'middle', 'end'],
3507
self.conf.get('list', expand=True))
3509
def test_pathologically_hidden_list(self):
3510
self.conf.store._load_from_string('''
3516
hidden={start}{middle}{end}
3518
# What matters is what the registration says, the conversion happens
3519
# only after all expansions have been performed
3520
self.registry.register(
3521
config.Option('hidden', from_unicode=config.list_from_store))
3522
self.assertEquals(['bin', 'go'],
3523
self.conf.get('hidden', expand=True))
3526
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3529
super(TestStackCrossSectionsExpand, self).setUp()
3531
def get_config(self, location, string):
3534
# Since we don't save the config we won't strictly require to inherit
3535
# from TestCaseInTempDir, but an error occurs so quickly...
3536
c = config.LocationStack(location)
3537
c.store._load_from_string(string)
3540
def test_dont_cross_unrelated_section(self):
3541
c = self.get_config('/another/branch/path','''
3546
[/another/branch/path]
3549
self.assertRaises(errors.ExpandingUnknownOption,
3550
c.get, 'bar', expand=True)
3552
def test_cross_related_sections(self):
3553
c = self.get_config('/project/branch/path','''
3557
[/project/branch/path]
3560
self.assertEquals('quux', c.get('bar', expand=True))
3563
class TestStackSet(TestStackWithTransport):
3565
def test_simple_set(self):
3566
conf = self.get_stack(self)
3567
conf.store._load_from_string('foo=bar')
3568
self.assertEquals('bar', conf.get('foo'))
3569
conf.set('foo', 'baz')
3570
# Did we get it back ?
3571
self.assertEquals('baz', conf.get('foo'))
3573
def test_set_creates_a_new_section(self):
3574
conf = self.get_stack(self)
3575
conf.set('foo', 'baz')
3576
self.assertEquals, 'baz', conf.get('foo')
3578
def test_set_hook(self):
3582
config.ConfigHooks.install_named_hook('set', hook, None)
3583
self.assertLength(0, calls)
3584
conf = self.get_stack(self)
3585
conf.set('foo', 'bar')
3586
self.assertLength(1, calls)
3587
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3590
class TestStackRemove(TestStackWithTransport):
3592
def test_remove_existing(self):
3593
conf = self.get_stack(self)
3594
conf.store._load_from_string('foo=bar')
3595
self.assertEquals('bar', conf.get('foo'))
3597
# Did we get it back ?
3598
self.assertEquals(None, conf.get('foo'))
3600
def test_remove_unknown(self):
3601
conf = self.get_stack(self)
3602
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3604
def test_remove_hook(self):
3608
config.ConfigHooks.install_named_hook('remove', hook, None)
3609
self.assertLength(0, calls)
3610
conf = self.get_stack(self)
3611
conf.store._load_from_string('foo=bar')
3613
self.assertLength(1, calls)
3614
self.assertEquals((conf, 'foo'), calls[0])
3617
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3620
super(TestConfigGetOptions, self).setUp()
3621
create_configs(self)
3623
def test_no_variable(self):
3624
# Using branch should query branch, locations and bazaar
3625
self.assertOptions([], self.branch_config)
3627
def test_option_in_bazaar(self):
3628
self.bazaar_config.set_user_option('file', 'bazaar')
3629
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3632
def test_option_in_locations(self):
3633
self.locations_config.set_user_option('file', 'locations')
3635
[('file', 'locations', self.tree.basedir, 'locations')],
3636
self.locations_config)
3638
def test_option_in_branch(self):
3639
self.branch_config.set_user_option('file', 'branch')
3640
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3643
def test_option_in_bazaar_and_branch(self):
3644
self.bazaar_config.set_user_option('file', 'bazaar')
3645
self.branch_config.set_user_option('file', 'branch')
3646
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3647
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3650
def test_option_in_branch_and_locations(self):
3651
# Hmm, locations override branch :-/
3652
self.locations_config.set_user_option('file', 'locations')
3653
self.branch_config.set_user_option('file', 'branch')
3655
[('file', 'locations', self.tree.basedir, 'locations'),
3656
('file', 'branch', 'DEFAULT', 'branch'),],
3659
def test_option_in_bazaar_locations_and_branch(self):
3660
self.bazaar_config.set_user_option('file', 'bazaar')
3661
self.locations_config.set_user_option('file', 'locations')
3662
self.branch_config.set_user_option('file', 'branch')
3664
[('file', 'locations', self.tree.basedir, 'locations'),
3665
('file', 'branch', 'DEFAULT', 'branch'),
3666
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3670
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3673
super(TestConfigRemoveOption, self).setUp()
3674
create_configs_with_file_option(self)
3676
def test_remove_in_locations(self):
3677
self.locations_config.remove_user_option('file', self.tree.basedir)
3679
[('file', 'branch', 'DEFAULT', 'branch'),
3680
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3683
def test_remove_in_branch(self):
3684
self.branch_config.remove_user_option('file')
3686
[('file', 'locations', self.tree.basedir, 'locations'),
3687
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3690
def test_remove_in_bazaar(self):
3691
self.bazaar_config.remove_user_option('file')
3693
[('file', 'locations', self.tree.basedir, 'locations'),
3694
('file', 'branch', 'DEFAULT', 'branch'),],
3698
class TestConfigGetSections(tests.TestCaseWithTransport):
3701
super(TestConfigGetSections, self).setUp()
3702
create_configs(self)
3704
def assertSectionNames(self, expected, conf, name=None):
3705
"""Check which sections are returned for a given config.
3707
If fallback configurations exist their sections can be included.
3709
:param expected: A list of section names.
3711
:param conf: The configuration that will be queried.
3713
:param name: An optional section name that will be passed to
3716
sections = list(conf._get_sections(name))
3717
self.assertLength(len(expected), sections)
3718
self.assertEqual(expected, [name for name, _, _ in sections])
3720
def test_bazaar_default_section(self):
3721
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3723
def test_locations_default_section(self):
3724
# No sections are defined in an empty file
3725
self.assertSectionNames([], self.locations_config)
3727
def test_locations_named_section(self):
3728
self.locations_config.set_user_option('file', 'locations')
3729
self.assertSectionNames([self.tree.basedir], self.locations_config)
3731
def test_locations_matching_sections(self):
3732
loc_config = self.locations_config
3733
loc_config.set_user_option('file', 'locations')
3734
# We need to cheat a bit here to create an option in sections above and
3735
# below the 'location' one.
3736
parser = loc_config._get_parser()
3737
# locations.cong deals with '/' ignoring native os.sep
3738
location_names = self.tree.basedir.split('/')
3739
parent = '/'.join(location_names[:-1])
3740
child = '/'.join(location_names + ['child'])
3742
parser[parent]['file'] = 'parent'
3744
parser[child]['file'] = 'child'
3745
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3747
def test_branch_data_default_section(self):
3748
self.assertSectionNames([None],
3749
self.branch_config._get_branch_data_config())
3751
def test_branch_default_sections(self):
3752
# No sections are defined in an empty locations file
3753
self.assertSectionNames([None, 'DEFAULT'],
3755
# Unless we define an option
3756
self.branch_config._get_location_config().set_user_option(
3757
'file', 'locations')
3758
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3761
def test_bazaar_named_section(self):
3762
# We need to cheat as the API doesn't give direct access to sections
3763
# other than DEFAULT.
3764
self.bazaar_config.set_alias('bazaar', 'bzr')
3765
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
3768
class TestAuthenticationConfigFile(tests.TestCase):
1316
3769
"""Test the authentication.conf file matching"""