367
595
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
598
class TestXDGConfigDir(tests.TestCaseInTempDir):
599
# must be in temp dir because config tests for the existence of the bazaar
600
# subdirectory of $XDG_CONFIG_HOME
603
if sys.platform in ('darwin', 'win32'):
604
raise tests.TestNotApplicable(
605
'XDG config dir not used on this platform')
606
super(TestXDGConfigDir, self).setUp()
607
self.overrideEnv('HOME', self.test_home_dir)
608
# BZR_HOME overrides everything we want to test so unset it.
609
self.overrideEnv('BZR_HOME', None)
611
def test_xdg_config_dir_exists(self):
612
"""When ~/.config/bazaar exists, use it as the config dir."""
613
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
615
self.assertEqual(config.config_dir(), newdir)
617
def test_xdg_config_home(self):
618
"""When XDG_CONFIG_HOME is set, use it."""
619
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
620
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
621
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
623
self.assertEqual(config.config_dir(), newdir)
626
class TestIniConfig(tests.TestCaseInTempDir):
372
628
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
629
conf = config.IniBasedConfig.from_string(s)
630
return conf, conf._get_parser()
378
633
class TestIniConfigBuilding(TestIniConfig):
380
635
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
636
my_config = config.IniBasedConfig()
383
638
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))
639
my_config = config.IniBasedConfig.from_string(sample_config_text)
640
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
642
def test_cached(self):
643
my_config = config.IniBasedConfig.from_string(sample_config_text)
644
parser = my_config._get_parser()
645
self.assertTrue(my_config._get_parser() is parser)
647
def _dummy_chown(self, path, uid, gid):
648
self.path, self.uid, self.gid = path, uid, gid
650
def test_ini_config_ownership(self):
651
"""Ensure that chown is happening during _write_config_file"""
652
self.requireFeature(features.chown_feature)
653
self.overrideAttr(os, 'chown', self._dummy_chown)
654
self.path = self.uid = self.gid = None
655
conf = config.IniBasedConfig(file_name='./foo.conf')
656
conf._write_config_file()
657
self.assertEquals(self.path, './foo.conf')
658
self.assertTrue(isinstance(self.uid, int))
659
self.assertTrue(isinstance(self.gid, int))
661
def test_get_filename_parameter_is_deprecated_(self):
662
conf = self.callDeprecated([
663
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
664
' Use file_name instead.'],
665
config.IniBasedConfig, lambda: 'ini.conf')
666
self.assertEqual('ini.conf', conf.file_name)
668
def test_get_parser_file_parameter_is_deprecated_(self):
391
669
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)
670
conf = config.IniBasedConfig.from_string(sample_config_text)
671
conf = self.callDeprecated([
672
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
673
' Use IniBasedConfig(_content=xxx) instead.'],
674
conf._get_parser, file=config_file)
677
class TestIniConfigSaving(tests.TestCaseInTempDir):
679
def test_cant_save_without_a_file_name(self):
680
conf = config.IniBasedConfig()
681
self.assertRaises(AssertionError, conf._write_config_file)
683
def test_saved_with_content(self):
684
content = 'foo = bar\n'
685
conf = config.IniBasedConfig.from_string(
686
content, file_name='./test.conf', save=True)
687
self.assertFileEqual(content, 'test.conf')
690
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
691
"""What is the default value of expand for config options.
693
This is an opt-in beta feature used to evaluate whether or not option
694
references can appear in dangerous place raising exceptions, disapearing
695
(and as such corrupting data) or if it's safe to activate the option by
698
Note that these tests relies on config._expand_default_value being already
699
overwritten in the parent class setUp.
703
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
707
self.warnings.append(args[0] % args[1:])
708
self.overrideAttr(trace, 'warning', warning)
710
def get_config(self, expand):
711
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
715
def assertExpandIs(self, expected):
716
actual = config._get_expand_default_value()
717
#self.config.get_user_option_as_bool('bzr.config.expand')
718
self.assertEquals(expected, actual)
720
def test_default_is_None(self):
721
self.assertEquals(None, config._expand_default_value)
723
def test_default_is_False_even_if_None(self):
724
self.config = self.get_config(None)
725
self.assertExpandIs(False)
727
def test_default_is_False_even_if_invalid(self):
728
self.config = self.get_config('<your choice>')
729
self.assertExpandIs(False)
731
# Huh ? My choice is False ? Thanks, always happy to hear that :D
732
# Wait, you've been warned !
733
self.assertLength(1, self.warnings)
735
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
738
def test_default_is_True(self):
739
self.config = self.get_config(True)
740
self.assertExpandIs(True)
742
def test_default_is_False(self):
743
self.config = self.get_config(False)
744
self.assertExpandIs(False)
747
class TestIniConfigOptionExpansion(tests.TestCase):
748
"""Test option expansion from the IniConfig level.
750
What we really want here is to test the Config level, but the class being
751
abstract as far as storing values is concerned, this can't be done
754
# FIXME: This should be rewritten when all configs share a storage
755
# implementation -- vila 2011-02-18
757
def get_config(self, string=None):
760
c = config.IniBasedConfig.from_string(string)
763
def assertExpansion(self, expected, conf, string, env=None):
764
self.assertEquals(expected, conf.expand_options(string, env))
766
def test_no_expansion(self):
767
c = self.get_config('')
768
self.assertExpansion('foo', c, 'foo')
770
def test_env_adding_options(self):
771
c = self.get_config('')
772
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
774
def test_env_overriding_options(self):
775
c = self.get_config('foo=baz')
776
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
778
def test_simple_ref(self):
779
c = self.get_config('foo=xxx')
780
self.assertExpansion('xxx', c, '{foo}')
782
def test_unknown_ref(self):
783
c = self.get_config('')
784
self.assertRaises(errors.ExpandingUnknownOption,
785
c.expand_options, '{foo}')
787
def test_indirect_ref(self):
788
c = self.get_config('''
792
self.assertExpansion('xxx', c, '{bar}')
794
def test_embedded_ref(self):
795
c = self.get_config('''
799
self.assertExpansion('xxx', c, '{{bar}}')
801
def test_simple_loop(self):
802
c = self.get_config('foo={foo}')
803
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
805
def test_indirect_loop(self):
806
c = self.get_config('''
810
e = self.assertRaises(errors.OptionExpansionLoop,
811
c.expand_options, '{foo}')
812
self.assertEquals('foo->bar->baz', e.refs)
813
self.assertEquals('{foo}', e.string)
816
conf = self.get_config('''
820
list={foo},{bar},{baz}
822
self.assertEquals(['start', 'middle', 'end'],
823
conf.get_user_option('list', expand=True))
825
def test_cascading_list(self):
826
conf = self.get_config('''
832
self.assertEquals(['start', 'middle', 'end'],
833
conf.get_user_option('list', expand=True))
835
def test_pathological_hidden_list(self):
836
conf = self.get_config('''
842
hidden={start}{middle}{end}
844
# Nope, it's either a string or a list, and the list wins as soon as a
845
# ',' appears, so the string concatenation never occur.
846
self.assertEquals(['{foo', '}', '{', 'bar}'],
847
conf.get_user_option('hidden', expand=True))
850
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
852
def get_config(self, location, string=None):
855
# Since we don't save the config we won't strictly require to inherit
856
# from TestCaseInTempDir, but an error occurs so quickly...
857
c = config.LocationConfig.from_string(string, location)
860
def test_dont_cross_unrelated_section(self):
861
c = self.get_config('/another/branch/path','''
866
[/another/branch/path]
869
self.assertRaises(errors.ExpandingUnknownOption,
870
c.get_user_option, 'bar', expand=True)
872
def test_cross_related_sections(self):
873
c = self.get_config('/project/branch/path','''
877
[/project/branch/path]
880
self.assertEquals('quux', c.get_user_option('bar', expand=True))
883
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
885
def test_cannot_reload_without_name(self):
886
conf = config.IniBasedConfig.from_string(sample_config_text)
887
self.assertRaises(AssertionError, conf.reload)
889
def test_reload_see_new_value(self):
890
c1 = config.IniBasedConfig.from_string('editor=vim\n',
891
file_name='./test/conf')
892
c1._write_config_file()
893
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
894
file_name='./test/conf')
895
c2._write_config_file()
896
self.assertEqual('vim', c1.get_user_option('editor'))
897
self.assertEqual('emacs', c2.get_user_option('editor'))
898
# Make sure we get the Right value
900
self.assertEqual('emacs', c1.get_user_option('editor'))
903
class TestLockableConfig(tests.TestCaseInTempDir):
905
scenarios = lockable_config_scenarios()
910
config_section = None
913
super(TestLockableConfig, self).setUp()
914
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
915
self.config = self.create_config(self._content)
917
def get_existing_config(self):
918
return self.config_class(*self.config_args)
920
def create_config(self, content):
921
kwargs = dict(save=True)
922
c = self.config_class.from_string(content, *self.config_args, **kwargs)
925
def test_simple_read_access(self):
926
self.assertEquals('1', self.config.get_user_option('one'))
928
def test_simple_write_access(self):
929
self.config.set_user_option('one', 'one')
930
self.assertEquals('one', self.config.get_user_option('one'))
932
def test_listen_to_the_last_speaker(self):
934
c2 = self.get_existing_config()
935
c1.set_user_option('one', 'ONE')
936
c2.set_user_option('two', 'TWO')
937
self.assertEquals('ONE', c1.get_user_option('one'))
938
self.assertEquals('TWO', c2.get_user_option('two'))
939
# The second update respect the first one
940
self.assertEquals('ONE', c2.get_user_option('one'))
942
def test_last_speaker_wins(self):
943
# If the same config is not shared, the same variable modified twice
944
# can only see a single result.
946
c2 = self.get_existing_config()
947
c1.set_user_option('one', 'c1')
948
c2.set_user_option('one', 'c2')
949
self.assertEquals('c2', c2._get_user_option('one'))
950
# The first modification is still available until another refresh
952
self.assertEquals('c1', c1._get_user_option('one'))
953
c1.set_user_option('two', 'done')
954
self.assertEquals('c2', c1._get_user_option('one'))
956
def test_writes_are_serialized(self):
958
c2 = self.get_existing_config()
960
# We spawn a thread that will pause *during* the write
961
before_writing = threading.Event()
962
after_writing = threading.Event()
963
writing_done = threading.Event()
964
c1_orig = c1._write_config_file
965
def c1_write_config_file():
968
# The lock is held. We wait for the main thread to decide when to
971
c1._write_config_file = c1_write_config_file
973
c1.set_user_option('one', 'c1')
975
t1 = threading.Thread(target=c1_set_option)
976
# Collect the thread after the test
977
self.addCleanup(t1.join)
978
# Be ready to unblock the thread if the test goes wrong
979
self.addCleanup(after_writing.set)
981
before_writing.wait()
982
self.assertTrue(c1._lock.is_held)
983
self.assertRaises(errors.LockContention,
984
c2.set_user_option, 'one', 'c2')
985
self.assertEquals('c1', c1.get_user_option('one'))
986
# Let the lock be released
989
c2.set_user_option('one', 'c2')
990
self.assertEquals('c2', c2.get_user_option('one'))
992
def test_read_while_writing(self):
994
# We spawn a thread that will pause *during* the write
995
ready_to_write = threading.Event()
996
do_writing = threading.Event()
997
writing_done = threading.Event()
998
c1_orig = c1._write_config_file
999
def c1_write_config_file():
1000
ready_to_write.set()
1001
# The lock is held. We wait for the main thread to decide when to
1006
c1._write_config_file = c1_write_config_file
1007
def c1_set_option():
1008
c1.set_user_option('one', 'c1')
1009
t1 = threading.Thread(target=c1_set_option)
1010
# Collect the thread after the test
1011
self.addCleanup(t1.join)
1012
# Be ready to unblock the thread if the test goes wrong
1013
self.addCleanup(do_writing.set)
1015
# Ensure the thread is ready to write
1016
ready_to_write.wait()
1017
self.assertTrue(c1._lock.is_held)
1018
self.assertEquals('c1', c1.get_user_option('one'))
1019
# If we read during the write, we get the old value
1020
c2 = self.get_existing_config()
1021
self.assertEquals('1', c2.get_user_option('one'))
1022
# Let the writing occur and ensure it occurred
1025
# Now we get the updated value
1026
c3 = self.get_existing_config()
1027
self.assertEquals('c1', c3.get_user_option('one'))
397
1030
class TestGetUserOptionAs(TestIniConfig):
1312
2081
self.assertIs(None, bzrdir_config.get_default_stack_on())
2084
class TestOldConfigHooks(tests.TestCaseWithTransport):
2087
super(TestOldConfigHooks, self).setUp()
2088
create_configs_with_file_option(self)
2090
def assertGetHook(self, conf, name, value):
2094
config.OldConfigHooks.install_named_hook('get', hook, None)
2096
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2097
self.assertLength(0, calls)
2098
actual_value = conf.get_user_option(name)
2099
self.assertEquals(value, actual_value)
2100
self.assertLength(1, calls)
2101
self.assertEquals((conf, name, value), calls[0])
2103
def test_get_hook_bazaar(self):
2104
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2106
def test_get_hook_locations(self):
2107
self.assertGetHook(self.locations_config, 'file', 'locations')
2109
def test_get_hook_branch(self):
2110
# Since locations masks branch, we define a different option
2111
self.branch_config.set_user_option('file2', 'branch')
2112
self.assertGetHook(self.branch_config, 'file2', 'branch')
2114
def assertSetHook(self, conf, name, value):
2118
config.OldConfigHooks.install_named_hook('set', hook, None)
2120
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2121
self.assertLength(0, calls)
2122
conf.set_user_option(name, value)
2123
self.assertLength(1, calls)
2124
# We can't assert the conf object below as different configs use
2125
# different means to implement set_user_option and we care only about
2127
self.assertEquals((name, value), calls[0][1:])
2129
def test_set_hook_bazaar(self):
2130
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2132
def test_set_hook_locations(self):
2133
self.assertSetHook(self.locations_config, 'foo', 'locations')
2135
def test_set_hook_branch(self):
2136
self.assertSetHook(self.branch_config, 'foo', 'branch')
2138
def assertRemoveHook(self, conf, name, section_name=None):
2142
config.OldConfigHooks.install_named_hook('remove', hook, None)
2144
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2145
self.assertLength(0, calls)
2146
conf.remove_user_option(name, section_name)
2147
self.assertLength(1, calls)
2148
# We can't assert the conf object below as different configs use
2149
# different means to implement remove_user_option and we care only about
2151
self.assertEquals((name,), calls[0][1:])
2153
def test_remove_hook_bazaar(self):
2154
self.assertRemoveHook(self.bazaar_config, 'file')
2156
def test_remove_hook_locations(self):
2157
self.assertRemoveHook(self.locations_config, 'file',
2158
self.locations_config.location)
2160
def test_remove_hook_branch(self):
2161
self.assertRemoveHook(self.branch_config, 'file')
2163
def assertLoadHook(self, name, conf_class, *conf_args):
2167
config.OldConfigHooks.install_named_hook('load', hook, None)
2169
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2170
self.assertLength(0, calls)
2172
conf = conf_class(*conf_args)
2173
# Access an option to trigger a load
2174
conf.get_user_option(name)
2175
self.assertLength(1, calls)
2176
# Since we can't assert about conf, we just use the number of calls ;-/
2178
def test_load_hook_bazaar(self):
2179
self.assertLoadHook('file', config.GlobalConfig)
2181
def test_load_hook_locations(self):
2182
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2184
def test_load_hook_branch(self):
2185
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2187
def assertSaveHook(self, conf):
2191
config.OldConfigHooks.install_named_hook('save', hook, None)
2193
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2194
self.assertLength(0, calls)
2195
# Setting an option triggers a save
2196
conf.set_user_option('foo', 'bar')
2197
self.assertLength(1, calls)
2198
# Since we can't assert about conf, we just use the number of calls ;-/
2200
def test_save_hook_bazaar(self):
2201
self.assertSaveHook(self.bazaar_config)
2203
def test_save_hook_locations(self):
2204
self.assertSaveHook(self.locations_config)
2206
def test_save_hook_branch(self):
2207
self.assertSaveHook(self.branch_config)
2210
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2211
"""Tests config hooks for remote configs.
2213
No tests for the remove hook as this is not implemented there.
2217
super(TestOldConfigHooksForRemote, self).setUp()
2218
self.transport_server = test_server.SmartTCPServer_for_testing
2219
create_configs_with_file_option(self)
2221
def assertGetHook(self, conf, name, value):
2225
config.OldConfigHooks.install_named_hook('get', hook, None)
2227
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2228
self.assertLength(0, calls)
2229
actual_value = conf.get_option(name)
2230
self.assertEquals(value, actual_value)
2231
self.assertLength(1, calls)
2232
self.assertEquals((conf, name, value), calls[0])
2234
def test_get_hook_remote_branch(self):
2235
remote_branch = branch.Branch.open(self.get_url('tree'))
2236
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2238
def test_get_hook_remote_bzrdir(self):
2239
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2240
conf = remote_bzrdir._get_config()
2241
conf.set_option('remotedir', 'file')
2242
self.assertGetHook(conf, 'file', 'remotedir')
2244
def assertSetHook(self, conf, name, value):
2248
config.OldConfigHooks.install_named_hook('set', hook, None)
2250
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2251
self.assertLength(0, calls)
2252
conf.set_option(value, name)
2253
self.assertLength(1, calls)
2254
# We can't assert the conf object below as different configs use
2255
# different means to implement set_user_option and we care only about
2257
self.assertEquals((name, value), calls[0][1:])
2259
def test_set_hook_remote_branch(self):
2260
remote_branch = branch.Branch.open(self.get_url('tree'))
2261
self.addCleanup(remote_branch.lock_write().unlock)
2262
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2264
def test_set_hook_remote_bzrdir(self):
2265
remote_branch = branch.Branch.open(self.get_url('tree'))
2266
self.addCleanup(remote_branch.lock_write().unlock)
2267
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2268
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2270
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2274
config.OldConfigHooks.install_named_hook('load', hook, None)
2276
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2277
self.assertLength(0, calls)
2279
conf = conf_class(*conf_args)
2280
# Access an option to trigger a load
2281
conf.get_option(name)
2282
self.assertLength(expected_nb_calls, calls)
2283
# Since we can't assert about conf, we just use the number of calls ;-/
2285
def test_load_hook_remote_branch(self):
2286
remote_branch = branch.Branch.open(self.get_url('tree'))
2287
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2289
def test_load_hook_remote_bzrdir(self):
2290
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2291
# The config file doesn't exist, set an option to force its creation
2292
conf = remote_bzrdir._get_config()
2293
conf.set_option('remotedir', 'file')
2294
# We get one call for the server and one call for the client, this is
2295
# caused by the differences in implementations betwen
2296
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2297
# SmartServerBranchGetConfigFile (in smart/branch.py)
2298
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2300
def assertSaveHook(self, conf):
2304
config.OldConfigHooks.install_named_hook('save', hook, None)
2306
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2307
self.assertLength(0, calls)
2308
# Setting an option triggers a save
2309
conf.set_option('foo', 'bar')
2310
self.assertLength(1, calls)
2311
# Since we can't assert about conf, we just use the number of calls ;-/
2313
def test_save_hook_remote_branch(self):
2314
remote_branch = branch.Branch.open(self.get_url('tree'))
2315
self.addCleanup(remote_branch.lock_write().unlock)
2316
self.assertSaveHook(remote_branch._get_config())
2318
def test_save_hook_remote_bzrdir(self):
2319
remote_branch = branch.Branch.open(self.get_url('tree'))
2320
self.addCleanup(remote_branch.lock_write().unlock)
2321
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2322
self.assertSaveHook(remote_bzrdir._get_config())
2325
class TestOption(tests.TestCase):
2327
def test_default_value(self):
2328
opt = config.Option('foo', default='bar')
2329
self.assertEquals('bar', opt.get_default())
2331
def test_default_value_from_env(self):
2332
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2333
self.overrideEnv('FOO', 'quux')
2334
# Env variable provides a default taking over the option one
2335
self.assertEquals('quux', opt.get_default())
2337
def test_first_default_value_from_env_wins(self):
2338
opt = config.Option('foo', default='bar',
2339
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2340
self.overrideEnv('FOO', 'foo')
2341
self.overrideEnv('BAZ', 'baz')
2342
# The first env var set wins
2343
self.assertEquals('foo', opt.get_default())
2345
def test_not_supported_list_default_value(self):
2346
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2348
def test_not_supported_object_default_value(self):
2349
self.assertRaises(AssertionError, config.Option, 'foo',
2353
class TestOptionConverterMixin(object):
2355
def assertConverted(self, expected, opt, value):
2356
self.assertEquals(expected, opt.convert_from_unicode(value))
2358
def assertWarns(self, opt, value):
2361
warnings.append(args[0] % args[1:])
2362
self.overrideAttr(trace, 'warning', warning)
2363
self.assertEquals(None, opt.convert_from_unicode(value))
2364
self.assertLength(1, warnings)
2366
'Value "%s" is not valid for "%s"' % (value, opt.name),
2369
def assertErrors(self, opt, value):
2370
self.assertRaises(errors.ConfigOptionValueError,
2371
opt.convert_from_unicode, value)
2373
def assertConvertInvalid(self, opt, invalid_value):
2375
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2376
opt.invalid = 'warning'
2377
self.assertWarns(opt, invalid_value)
2378
opt.invalid = 'error'
2379
self.assertErrors(opt, invalid_value)
2382
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2384
def get_option(self):
2385
return config.Option('foo', help='A boolean.',
2386
from_unicode=config.bool_from_store)
2388
def test_convert_invalid(self):
2389
opt = self.get_option()
2390
# A string that is not recognized as a boolean
2391
self.assertConvertInvalid(opt, u'invalid-boolean')
2392
# A list of strings is never recognized as a boolean
2393
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2395
def test_convert_valid(self):
2396
opt = self.get_option()
2397
self.assertConverted(True, opt, u'True')
2398
self.assertConverted(True, opt, u'1')
2399
self.assertConverted(False, opt, u'False')
2402
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2404
def get_option(self):
2405
return config.Option('foo', help='An integer.',
2406
from_unicode=config.int_from_store)
2408
def test_convert_invalid(self):
2409
opt = self.get_option()
2410
# A string that is not recognized as an integer
2411
self.assertConvertInvalid(opt, u'forty-two')
2412
# A list of strings is never recognized as an integer
2413
self.assertConvertInvalid(opt, [u'a', u'list'])
2415
def test_convert_valid(self):
2416
opt = self.get_option()
2417
self.assertConverted(16, opt, u'16')
2419
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2421
def get_option(self):
2422
return config.Option('foo', help='A list.',
2423
from_unicode=config.list_from_store)
2425
def test_convert_invalid(self):
2426
# No string is invalid as all forms can be converted to a list
2429
def test_convert_valid(self):
2430
opt = self.get_option()
2431
# An empty string is an empty list
2432
self.assertConverted([], opt, '') # Using a bare str() just in case
2433
self.assertConverted([], opt, u'')
2435
self.assertConverted([u'True'], opt, u'True')
2437
self.assertConverted([u'42'], opt, u'42')
2439
self.assertConverted([u'bar'], opt, u'bar')
2440
# A list remains a list (configObj will turn a string containing commas
2441
# into a list, but that's not what we're testing here)
2442
self.assertConverted([u'foo', u'1', u'True'],
2443
opt, [u'foo', u'1', u'True'])
2446
class TestOptionConverterMixin(object):
2448
def assertConverted(self, expected, opt, value):
2449
self.assertEquals(expected, opt.convert_from_unicode(value))
2451
def assertWarns(self, opt, value):
2454
warnings.append(args[0] % args[1:])
2455
self.overrideAttr(trace, 'warning', warning)
2456
self.assertEquals(None, opt.convert_from_unicode(value))
2457
self.assertLength(1, warnings)
2459
'Value "%s" is not valid for "%s"' % (value, opt.name),
2462
def assertErrors(self, opt, value):
2463
self.assertRaises(errors.ConfigOptionValueError,
2464
opt.convert_from_unicode, value)
2466
def assertConvertInvalid(self, opt, invalid_value):
2468
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2469
opt.invalid = 'warning'
2470
self.assertWarns(opt, invalid_value)
2471
opt.invalid = 'error'
2472
self.assertErrors(opt, invalid_value)
2475
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2477
def get_option(self):
2478
return config.Option('foo', help='A boolean.',
2479
from_unicode=config.bool_from_store)
2481
def test_convert_invalid(self):
2482
opt = self.get_option()
2483
# A string that is not recognized as a boolean
2484
self.assertConvertInvalid(opt, u'invalid-boolean')
2485
# A list of strings is never recognized as a boolean
2486
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2488
def test_convert_valid(self):
2489
opt = self.get_option()
2490
self.assertConverted(True, opt, u'True')
2491
self.assertConverted(True, opt, u'1')
2492
self.assertConverted(False, opt, u'False')
2495
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2497
def get_option(self):
2498
return config.Option('foo', help='An integer.',
2499
from_unicode=config.int_from_store)
2501
def test_convert_invalid(self):
2502
opt = self.get_option()
2503
# A string that is not recognized as an integer
2504
self.assertConvertInvalid(opt, u'forty-two')
2505
# A list of strings is never recognized as an integer
2506
self.assertConvertInvalid(opt, [u'a', u'list'])
2508
def test_convert_valid(self):
2509
opt = self.get_option()
2510
self.assertConverted(16, opt, u'16')
2513
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2515
def get_option(self):
2516
return config.Option('foo', help='A list.',
2517
from_unicode=config.list_from_store)
2519
def test_convert_invalid(self):
2520
opt = self.get_option()
2521
# We don't even try to convert a list into a list, we only expect
2523
self.assertConvertInvalid(opt, [1])
2524
# No string is invalid as all forms can be converted to a list
2526
def test_convert_valid(self):
2527
opt = self.get_option()
2528
# An empty string is an empty list
2529
self.assertConverted([], opt, '') # Using a bare str() just in case
2530
self.assertConverted([], opt, u'')
2532
self.assertConverted([u'True'], opt, u'True')
2534
self.assertConverted([u'42'], opt, u'42')
2536
self.assertConverted([u'bar'], opt, u'bar')
2539
class TestOptionRegistry(tests.TestCase):
2542
super(TestOptionRegistry, self).setUp()
2543
# Always start with an empty registry
2544
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2545
self.registry = config.option_registry
2547
def test_register(self):
2548
opt = config.Option('foo')
2549
self.registry.register(opt)
2550
self.assertIs(opt, self.registry.get('foo'))
2552
def test_registered_help(self):
2553
opt = config.Option('foo', help='A simple option')
2554
self.registry.register(opt)
2555
self.assertEquals('A simple option', self.registry.get_help('foo'))
2557
lazy_option = config.Option('lazy_foo', help='Lazy help')
2559
def test_register_lazy(self):
2560
self.registry.register_lazy('lazy_foo', self.__module__,
2561
'TestOptionRegistry.lazy_option')
2562
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2564
def test_registered_lazy_help(self):
2565
self.registry.register_lazy('lazy_foo', self.__module__,
2566
'TestOptionRegistry.lazy_option')
2567
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2570
class TestRegisteredOptions(tests.TestCase):
2571
"""All registered options should verify some constraints."""
2573
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2574
in config.option_registry.iteritems()]
2577
super(TestRegisteredOptions, self).setUp()
2578
self.registry = config.option_registry
2580
def test_proper_name(self):
2581
# An option should be registered under its own name, this can't be
2582
# checked at registration time for the lazy ones.
2583
self.assertEquals(self.option_name, self.option.name)
2585
def test_help_is_set(self):
2586
option_help = self.registry.get_help(self.option_name)
2587
self.assertNotEquals(None, option_help)
2588
# Come on, think about the user, he really wants to know what the
2590
self.assertIsNot(None, option_help)
2591
self.assertNotEquals('', option_help)
2594
class TestSection(tests.TestCase):
2596
# FIXME: Parametrize so that all sections produced by Stores run these
2597
# tests -- vila 2011-04-01
2599
def test_get_a_value(self):
2600
a_dict = dict(foo='bar')
2601
section = config.Section('myID', a_dict)
2602
self.assertEquals('bar', section.get('foo'))
2604
def test_get_unknown_option(self):
2606
section = config.Section(None, a_dict)
2607
self.assertEquals('out of thin air',
2608
section.get('foo', 'out of thin air'))
2610
def test_options_is_shared(self):
2612
section = config.Section(None, a_dict)
2613
self.assertIs(a_dict, section.options)
2616
class TestMutableSection(tests.TestCase):
2618
scenarios = [('mutable',
2620
lambda opts: config.MutableSection('myID', opts)},),
2624
a_dict = dict(foo='bar')
2625
section = self.get_section(a_dict)
2626
section.set('foo', 'new_value')
2627
self.assertEquals('new_value', section.get('foo'))
2628
# The change appears in the shared section
2629
self.assertEquals('new_value', a_dict.get('foo'))
2630
# We keep track of the change
2631
self.assertTrue('foo' in section.orig)
2632
self.assertEquals('bar', section.orig.get('foo'))
2634
def test_set_preserve_original_once(self):
2635
a_dict = dict(foo='bar')
2636
section = self.get_section(a_dict)
2637
section.set('foo', 'first_value')
2638
section.set('foo', 'second_value')
2639
# We keep track of the original value
2640
self.assertTrue('foo' in section.orig)
2641
self.assertEquals('bar', section.orig.get('foo'))
2643
def test_remove(self):
2644
a_dict = dict(foo='bar')
2645
section = self.get_section(a_dict)
2646
section.remove('foo')
2647
# We get None for unknown options via the default value
2648
self.assertEquals(None, section.get('foo'))
2649
# Or we just get the default value
2650
self.assertEquals('unknown', section.get('foo', 'unknown'))
2651
self.assertFalse('foo' in section.options)
2652
# We keep track of the deletion
2653
self.assertTrue('foo' in section.orig)
2654
self.assertEquals('bar', section.orig.get('foo'))
2656
def test_remove_new_option(self):
2658
section = self.get_section(a_dict)
2659
section.set('foo', 'bar')
2660
section.remove('foo')
2661
self.assertFalse('foo' in section.options)
2662
# The option didn't exist initially so it we need to keep track of it
2663
# with a special value
2664
self.assertTrue('foo' in section.orig)
2665
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2668
class TestCommandLineStore(tests.TestCase):
2671
super(TestCommandLineStore, self).setUp()
2672
self.store = config.CommandLineStore()
2674
def get_section(self):
2675
"""Get the unique section for the command line overrides."""
2676
sections = list(self.store.get_sections())
2677
self.assertLength(1, sections)
2678
store, section = sections[0]
2679
self.assertEquals(self.store, store)
2682
def test_no_override(self):
2683
self.store._from_cmdline([])
2684
section = self.get_section()
2685
self.assertLength(0, list(section.iter_option_names()))
2687
def test_simple_override(self):
2688
self.store._from_cmdline(['a=b'])
2689
section = self.get_section()
2690
self.assertEqual('b', section.get('a'))
2692
def test_list_override(self):
2693
self.store._from_cmdline(['l=1,2,3'])
2694
val = self.get_section().get('l')
2695
self.assertEqual('1,2,3', val)
2696
# Reminder: lists should be registered as such explicitely, otherwise
2697
# the conversion needs to be done afterwards.
2698
self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2700
def test_multiple_overrides(self):
2701
self.store._from_cmdline(['a=b', 'x=y'])
2702
section = self.get_section()
2703
self.assertEquals('b', section.get('a'))
2704
self.assertEquals('y', section.get('x'))
2706
def test_wrong_syntax(self):
2707
self.assertRaises(errors.BzrCommandError,
2708
self.store._from_cmdline, ['a=b', 'c'])
2711
class TestStore(tests.TestCaseWithTransport):
2713
def assertSectionContent(self, expected, (store, section)):
2714
"""Assert that some options have the proper values in a section."""
2715
expected_name, expected_options = expected
2716
self.assertEquals(expected_name, section.id)
2719
dict([(k, section.get(k)) for k in expected_options.keys()]))
2722
class TestReadonlyStore(TestStore):
2724
scenarios = [(key, {'get_store': builder}) for key, builder
2725
in config.test_store_builder_registry.iteritems()]
2727
def test_building_delays_load(self):
2728
store = self.get_store(self)
2729
self.assertEquals(False, store.is_loaded())
2730
store._load_from_string('')
2731
self.assertEquals(True, store.is_loaded())
2733
def test_get_no_sections_for_empty(self):
2734
store = self.get_store(self)
2735
store._load_from_string('')
2736
self.assertEquals([], list(store.get_sections()))
2738
def test_get_default_section(self):
2739
store = self.get_store(self)
2740
store._load_from_string('foo=bar')
2741
sections = list(store.get_sections())
2742
self.assertLength(1, sections)
2743
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2745
def test_get_named_section(self):
2746
store = self.get_store(self)
2747
store._load_from_string('[baz]\nfoo=bar')
2748
sections = list(store.get_sections())
2749
self.assertLength(1, sections)
2750
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2752
def test_load_from_string_fails_for_non_empty_store(self):
2753
store = self.get_store(self)
2754
store._load_from_string('foo=bar')
2755
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2758
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2759
"""Simulate loading a config store with content of various encodings.
2761
All files produced by bzr are in utf8 content.
2763
Users may modify them manually and end up with a file that can't be
2764
loaded. We need to issue proper error messages in this case.
2767
invalid_utf8_char = '\xff'
2769
def test_load_utf8(self):
2770
"""Ensure we can load an utf8-encoded file."""
2771
t = self.get_transport()
2772
# From http://pad.lv/799212
2773
unicode_user = u'b\N{Euro Sign}ar'
2774
unicode_content = u'user=%s' % (unicode_user,)
2775
utf8_content = unicode_content.encode('utf8')
2776
# Store the raw content in the config file
2777
t.put_bytes('foo.conf', utf8_content)
2778
store = config.TransportIniFileStore(t, 'foo.conf')
2780
stack = config.Stack([store.get_sections], store)
2781
self.assertEquals(unicode_user, stack.get('user'))
2783
def test_load_non_ascii(self):
2784
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2785
t = self.get_transport()
2786
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2787
store = config.TransportIniFileStore(t, 'foo.conf')
2788
self.assertRaises(errors.ConfigContentError, store.load)
2790
def test_load_erroneous_content(self):
2791
"""Ensure we display a proper error on content that can't be parsed."""
2792
t = self.get_transport()
2793
t.put_bytes('foo.conf', '[open_section\n')
2794
store = config.TransportIniFileStore(t, 'foo.conf')
2795
self.assertRaises(errors.ParseConfigError, store.load)
2797
def test_load_permission_denied(self):
2798
"""Ensure we get warned when trying to load an inaccessible file."""
2801
warnings.append(args[0] % args[1:])
2802
self.overrideAttr(trace, 'warning', warning)
2804
t = self.get_transport()
2806
def get_bytes(relpath):
2807
raise errors.PermissionDenied(relpath, "")
2808
t.get_bytes = get_bytes
2809
store = config.TransportIniFileStore(t, 'foo.conf')
2810
self.assertRaises(errors.PermissionDenied, store.load)
2813
[u'Permission denied while trying to load configuration store %s.'
2814
% store.external_url()])
2817
class TestIniConfigContent(tests.TestCaseWithTransport):
2818
"""Simulate loading a IniBasedConfig with content of various encodings.
2820
All files produced by bzr are in utf8 content.
2822
Users may modify them manually and end up with a file that can't be
2823
loaded. We need to issue proper error messages in this case.
2826
invalid_utf8_char = '\xff'
2828
def test_load_utf8(self):
2829
"""Ensure we can load an utf8-encoded file."""
2830
# From http://pad.lv/799212
2831
unicode_user = u'b\N{Euro Sign}ar'
2832
unicode_content = u'user=%s' % (unicode_user,)
2833
utf8_content = unicode_content.encode('utf8')
2834
# Store the raw content in the config file
2835
with open('foo.conf', 'wb') as f:
2836
f.write(utf8_content)
2837
conf = config.IniBasedConfig(file_name='foo.conf')
2838
self.assertEquals(unicode_user, conf.get_user_option('user'))
2840
def test_load_badly_encoded_content(self):
2841
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2842
with open('foo.conf', 'wb') as f:
2843
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2844
conf = config.IniBasedConfig(file_name='foo.conf')
2845
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2847
def test_load_erroneous_content(self):
2848
"""Ensure we display a proper error on content that can't be parsed."""
2849
with open('foo.conf', 'wb') as f:
2850
f.write('[open_section\n')
2851
conf = config.IniBasedConfig(file_name='foo.conf')
2852
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2855
class TestMutableStore(TestStore):
2857
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2858
in config.test_store_builder_registry.iteritems()]
2861
super(TestMutableStore, self).setUp()
2862
self.transport = self.get_transport()
2864
def has_store(self, store):
2865
store_basename = urlutils.relative_url(self.transport.external_url(),
2866
store.external_url())
2867
return self.transport.has(store_basename)
2869
def test_save_empty_creates_no_file(self):
2870
# FIXME: There should be a better way than relying on the test
2871
# parametrization to identify branch.conf -- vila 2011-0526
2872
if self.store_id in ('branch', 'remote_branch'):
2873
raise tests.TestNotApplicable(
2874
'branch.conf is *always* created when a branch is initialized')
2875
store = self.get_store(self)
2877
self.assertEquals(False, self.has_store(store))
2879
def test_save_emptied_succeeds(self):
2880
store = self.get_store(self)
2881
store._load_from_string('foo=bar\n')
2882
section = store.get_mutable_section(None)
2883
section.remove('foo')
2885
self.assertEquals(True, self.has_store(store))
2886
modified_store = self.get_store(self)
2887
sections = list(modified_store.get_sections())
2888
self.assertLength(0, sections)
2890
def test_save_with_content_succeeds(self):
2891
# FIXME: There should be a better way than relying on the test
2892
# parametrization to identify branch.conf -- vila 2011-0526
2893
if self.store_id in ('branch', 'remote_branch'):
2894
raise tests.TestNotApplicable(
2895
'branch.conf is *always* created when a branch is initialized')
2896
store = self.get_store(self)
2897
store._load_from_string('foo=bar\n')
2898
self.assertEquals(False, self.has_store(store))
2900
self.assertEquals(True, self.has_store(store))
2901
modified_store = self.get_store(self)
2902
sections = list(modified_store.get_sections())
2903
self.assertLength(1, sections)
2904
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2906
def test_set_option_in_empty_store(self):
2907
store = self.get_store(self)
2908
section = store.get_mutable_section(None)
2909
section.set('foo', 'bar')
2911
modified_store = self.get_store(self)
2912
sections = list(modified_store.get_sections())
2913
self.assertLength(1, sections)
2914
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2916
def test_set_option_in_default_section(self):
2917
store = self.get_store(self)
2918
store._load_from_string('')
2919
section = store.get_mutable_section(None)
2920
section.set('foo', 'bar')
2922
modified_store = self.get_store(self)
2923
sections = list(modified_store.get_sections())
2924
self.assertLength(1, sections)
2925
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2927
def test_set_option_in_named_section(self):
2928
store = self.get_store(self)
2929
store._load_from_string('')
2930
section = store.get_mutable_section('baz')
2931
section.set('foo', 'bar')
2933
modified_store = self.get_store(self)
2934
sections = list(modified_store.get_sections())
2935
self.assertLength(1, sections)
2936
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2938
def test_load_hook(self):
2939
# We first needs to ensure that the store exists
2940
store = self.get_store(self)
2941
section = store.get_mutable_section('baz')
2942
section.set('foo', 'bar')
2944
# Now we can try to load it
2945
store = self.get_store(self)
2949
config.ConfigHooks.install_named_hook('load', hook, None)
2950
self.assertLength(0, calls)
2952
self.assertLength(1, calls)
2953
self.assertEquals((store,), calls[0])
2955
def test_save_hook(self):
2959
config.ConfigHooks.install_named_hook('save', hook, None)
2960
self.assertLength(0, calls)
2961
store = self.get_store(self)
2962
section = store.get_mutable_section('baz')
2963
section.set('foo', 'bar')
2965
self.assertLength(1, calls)
2966
self.assertEquals((store,), calls[0])
2969
class TestTransportIniFileStore(TestStore):
2971
def test_loading_unknown_file_fails(self):
2972
store = config.TransportIniFileStore(self.get_transport(),
2974
self.assertRaises(errors.NoSuchFile, store.load)
2976
def test_invalid_content(self):
2977
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2978
self.assertEquals(False, store.is_loaded())
2979
exc = self.assertRaises(
2980
errors.ParseConfigError, store._load_from_string,
2981
'this is invalid !')
2982
self.assertEndsWith(exc.filename, 'foo.conf')
2983
# And the load failed
2984
self.assertEquals(False, store.is_loaded())
2986
def test_get_embedded_sections(self):
2987
# A more complicated example (which also shows that section names and
2988
# option names share the same name space...)
2989
# FIXME: This should be fixed by forbidding dicts as values ?
2990
# -- vila 2011-04-05
2991
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2992
store._load_from_string('''
2996
foo_in_DEFAULT=foo_DEFAULT
3004
sections = list(store.get_sections())
3005
self.assertLength(4, sections)
3006
# The default section has no name.
3007
# List values are provided as strings and need to be explicitly
3008
# converted by specifying from_unicode=list_from_store at option
3010
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3012
self.assertSectionContent(
3013
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3014
self.assertSectionContent(
3015
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3016
# sub sections are provided as embedded dicts.
3017
self.assertSectionContent(
3018
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3022
class TestLockableIniFileStore(TestStore):
3024
def test_create_store_in_created_dir(self):
3025
self.assertPathDoesNotExist('dir')
3026
t = self.get_transport('dir/subdir')
3027
store = config.LockableIniFileStore(t, 'foo.conf')
3028
store.get_mutable_section(None).set('foo', 'bar')
3030
self.assertPathExists('dir/subdir')
3033
class TestConcurrentStoreUpdates(TestStore):
3034
"""Test that Stores properly handle conccurent updates.
3036
New Store implementation may fail some of these tests but until such
3037
implementations exist it's hard to properly filter them from the scenarios
3038
applied here. If you encounter such a case, contact the bzr devs.
3041
scenarios = [(key, {'get_stack': builder}) for key, builder
3042
in config.test_stack_builder_registry.iteritems()]
3045
super(TestConcurrentStoreUpdates, self).setUp()
3046
self.stack = self.get_stack(self)
3047
if not isinstance(self.stack, config._CompatibleStack):
3048
raise tests.TestNotApplicable(
3049
'%s is not meant to be compatible with the old config design'
3051
self.stack.set('one', '1')
3052
self.stack.set('two', '2')
3054
self.stack.store.save()
3056
def test_simple_read_access(self):
3057
self.assertEquals('1', self.stack.get('one'))
3059
def test_simple_write_access(self):
3060
self.stack.set('one', 'one')
3061
self.assertEquals('one', self.stack.get('one'))
3063
def test_listen_to_the_last_speaker(self):
3065
c2 = self.get_stack(self)
3066
c1.set('one', 'ONE')
3067
c2.set('two', 'TWO')
3068
self.assertEquals('ONE', c1.get('one'))
3069
self.assertEquals('TWO', c2.get('two'))
3070
# The second update respect the first one
3071
self.assertEquals('ONE', c2.get('one'))
3073
def test_last_speaker_wins(self):
3074
# If the same config is not shared, the same variable modified twice
3075
# can only see a single result.
3077
c2 = self.get_stack(self)
3080
self.assertEquals('c2', c2.get('one'))
3081
# The first modification is still available until another refresh
3083
self.assertEquals('c1', c1.get('one'))
3084
c1.set('two', 'done')
3085
self.assertEquals('c2', c1.get('one'))
3087
def test_writes_are_serialized(self):
3089
c2 = self.get_stack(self)
3091
# We spawn a thread that will pause *during* the config saving.
3092
before_writing = threading.Event()
3093
after_writing = threading.Event()
3094
writing_done = threading.Event()
3095
c1_save_without_locking_orig = c1.store.save_without_locking
3096
def c1_save_without_locking():
3097
before_writing.set()
3098
c1_save_without_locking_orig()
3099
# The lock is held. We wait for the main thread to decide when to
3101
after_writing.wait()
3102
c1.store.save_without_locking = c1_save_without_locking
3106
t1 = threading.Thread(target=c1_set)
3107
# Collect the thread after the test
3108
self.addCleanup(t1.join)
3109
# Be ready to unblock the thread if the test goes wrong
3110
self.addCleanup(after_writing.set)
3112
before_writing.wait()
3113
self.assertRaises(errors.LockContention,
3114
c2.set, 'one', 'c2')
3115
self.assertEquals('c1', c1.get('one'))
3116
# Let the lock be released
3120
self.assertEquals('c2', c2.get('one'))
3122
def test_read_while_writing(self):
3124
# We spawn a thread that will pause *during* the write
3125
ready_to_write = threading.Event()
3126
do_writing = threading.Event()
3127
writing_done = threading.Event()
3128
# We override the _save implementation so we know the store is locked
3129
c1_save_without_locking_orig = c1.store.save_without_locking
3130
def c1_save_without_locking():
3131
ready_to_write.set()
3132
# The lock is held. We wait for the main thread to decide when to
3135
c1_save_without_locking_orig()
3137
c1.store.save_without_locking = c1_save_without_locking
3140
t1 = threading.Thread(target=c1_set)
3141
# Collect the thread after the test
3142
self.addCleanup(t1.join)
3143
# Be ready to unblock the thread if the test goes wrong
3144
self.addCleanup(do_writing.set)
3146
# Ensure the thread is ready to write
3147
ready_to_write.wait()
3148
self.assertEquals('c1', c1.get('one'))
3149
# If we read during the write, we get the old value
3150
c2 = self.get_stack(self)
3151
self.assertEquals('1', c2.get('one'))
3152
# Let the writing occur and ensure it occurred
3155
# Now we get the updated value
3156
c3 = self.get_stack(self)
3157
self.assertEquals('c1', c3.get('one'))
3159
# FIXME: It may be worth looking into removing the lock dir when it's not
3160
# needed anymore and look at possible fallouts for concurrent lockers. This
3161
# will matter if/when we use config files outside of bazaar directories
3162
# (.bazaar or .bzr) -- vila 20110-04-111
3165
class TestSectionMatcher(TestStore):
3167
scenarios = [('location', {'matcher': config.LocationMatcher}),
3168
('id', {'matcher': config.NameMatcher}),]
3171
super(TestSectionMatcher, self).setUp()
3172
# Any simple store is good enough
3173
self.get_store = config.test_store_builder_registry.get('configobj')
3175
def test_no_matches_for_empty_stores(self):
3176
store = self.get_store(self)
3177
store._load_from_string('')
3178
matcher = self.matcher(store, '/bar')
3179
self.assertEquals([], list(matcher.get_sections()))
3181
def test_build_doesnt_load_store(self):
3182
store = self.get_store(self)
3183
matcher = self.matcher(store, '/bar')
3184
self.assertFalse(store.is_loaded())
3187
class TestLocationSection(tests.TestCase):
3189
def get_section(self, options, extra_path):
3190
section = config.Section('foo', options)
3191
# We don't care about the length so we use '0'
3192
return config.LocationSection(section, 0, extra_path)
3194
def test_simple_option(self):
3195
section = self.get_section({'foo': 'bar'}, '')
3196
self.assertEquals('bar', section.get('foo'))
3198
def test_option_with_extra_path(self):
3199
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3201
self.assertEquals('bar/baz', section.get('foo'))
3203
def test_invalid_policy(self):
3204
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3206
# invalid policies are ignored
3207
self.assertEquals('bar', section.get('foo'))
3210
class TestLocationMatcher(TestStore):
3213
super(TestLocationMatcher, self).setUp()
3214
# Any simple store is good enough
3215
self.get_store = config.test_store_builder_registry.get('configobj')
3217
def test_unrelated_section_excluded(self):
3218
store = self.get_store(self)
3219
store._load_from_string('''
3227
section=/foo/bar/baz
3231
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3233
[section.id for _, section in store.get_sections()])
3234
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3235
sections = [section for s, section in matcher.get_sections()]
3236
self.assertEquals([3, 2],
3237
[section.length for section in sections])
3238
self.assertEquals(['/foo/bar', '/foo'],
3239
[section.id for section in sections])
3240
self.assertEquals(['quux', 'bar/quux'],
3241
[section.extra_path for section in sections])
3243
def test_more_specific_sections_first(self):
3244
store = self.get_store(self)
3245
store._load_from_string('''
3251
self.assertEquals(['/foo', '/foo/bar'],
3252
[section.id for _, section in store.get_sections()])
3253
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3254
sections = [section for s, section in matcher.get_sections()]
3255
self.assertEquals([3, 2],
3256
[section.length for section in sections])
3257
self.assertEquals(['/foo/bar', '/foo'],
3258
[section.id for section in sections])
3259
self.assertEquals(['baz', 'bar/baz'],
3260
[section.extra_path for section in sections])
3262
def test_appendpath_in_no_name_section(self):
3263
# It's a bit weird to allow appendpath in a no-name section, but
3264
# someone may found a use for it
3265
store = self.get_store(self)
3266
store._load_from_string('''
3268
foo:policy = appendpath
3270
matcher = config.LocationMatcher(store, 'dir/subdir')
3271
sections = list(matcher.get_sections())
3272
self.assertLength(1, sections)
3273
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3275
def test_file_urls_are_normalized(self):
3276
store = self.get_store(self)
3277
if sys.platform == 'win32':
3278
expected_url = 'file:///C:/dir/subdir'
3279
expected_location = 'C:/dir/subdir'
3281
expected_url = 'file:///dir/subdir'
3282
expected_location = '/dir/subdir'
3283
matcher = config.LocationMatcher(store, expected_url)
3284
self.assertEquals(expected_location, matcher.location)
3287
class TestNameMatcher(TestStore):
3290
super(TestNameMatcher, self).setUp()
3291
self.matcher = config.NameMatcher
3292
# Any simple store is good enough
3293
self.get_store = config.test_store_builder_registry.get('configobj')
3295
def get_matching_sections(self, name):
3296
store = self.get_store(self)
3297
store._load_from_string('''
3305
matcher = self.matcher(store, name)
3306
return list(matcher.get_sections())
3308
def test_matching(self):
3309
sections = self.get_matching_sections('foo')
3310
self.assertLength(1, sections)
3311
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3313
def test_not_matching(self):
3314
sections = self.get_matching_sections('baz')
3315
self.assertLength(0, sections)
3318
class TestStackGet(tests.TestCase):
3320
# FIXME: This should be parametrized for all known Stack or dedicated
3321
# paramerized tests created to avoid bloating -- vila 2011-03-31
3323
def overrideOptionRegistry(self):
3324
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3326
def test_single_config_get(self):
3327
conf = dict(foo='bar')
3328
conf_stack = config.Stack([conf])
3329
self.assertEquals('bar', conf_stack.get('foo'))
3331
def test_get_with_registered_default_value(self):
3332
conf_stack = config.Stack([dict()])
3333
opt = config.Option('foo', default='bar')
3334
self.overrideOptionRegistry()
3335
config.option_registry.register('foo', opt)
3336
self.assertEquals('bar', conf_stack.get('foo'))
3338
def test_get_without_registered_default_value(self):
3339
conf_stack = config.Stack([dict()])
3340
opt = config.Option('foo')
3341
self.overrideOptionRegistry()
3342
config.option_registry.register('foo', opt)
3343
self.assertEquals(None, conf_stack.get('foo'))
3345
def test_get_without_default_value_for_not_registered(self):
3346
conf_stack = config.Stack([dict()])
3347
opt = config.Option('foo')
3348
self.overrideOptionRegistry()
3349
self.assertEquals(None, conf_stack.get('foo'))
3351
def test_get_first_definition(self):
3352
conf1 = dict(foo='bar')
3353
conf2 = dict(foo='baz')
3354
conf_stack = config.Stack([conf1, conf2])
3355
self.assertEquals('bar', conf_stack.get('foo'))
3357
def test_get_embedded_definition(self):
3358
conf1 = dict(yy='12')
3359
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3360
conf_stack = config.Stack([conf1, conf2])
3361
self.assertEquals('baz', conf_stack.get('foo'))
3363
def test_get_for_empty_section_callable(self):
3364
conf_stack = config.Stack([lambda : []])
3365
self.assertEquals(None, conf_stack.get('foo'))
3367
def test_get_for_broken_callable(self):
3368
# Trying to use and invalid callable raises an exception on first use
3369
conf_stack = config.Stack([lambda : object()])
3370
self.assertRaises(TypeError, conf_stack.get, 'foo')
3373
class TestStackWithTransport(tests.TestCaseWithTransport):
3375
scenarios = [(key, {'get_stack': builder}) for key, builder
3376
in config.test_stack_builder_registry.iteritems()]
3379
class TestConcreteStacks(TestStackWithTransport):
3381
def test_build_stack(self):
3382
# Just a smoke test to help debug builders
3383
stack = self.get_stack(self)
3386
class TestStackGet(TestStackWithTransport):
3389
super(TestStackGet, self).setUp()
3390
self.conf = self.get_stack(self)
3392
def test_get_for_empty_stack(self):
3393
self.assertEquals(None, self.conf.get('foo'))
3395
def test_get_hook(self):
3396
self.conf.set('foo', 'bar')
3400
config.ConfigHooks.install_named_hook('get', hook, None)
3401
self.assertLength(0, calls)
3402
value = self.conf.get('foo')
3403
self.assertEquals('bar', value)
3404
self.assertLength(1, calls)
3405
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3408
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3411
super(TestStackGetWithConverter, self).setUp()
3412
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3413
self.registry = config.option_registry
3414
# We just want a simple stack with a simple store so we can inject
3415
# whatever content the tests need without caring about what section
3416
# names are valid for a given store/stack.
3417
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3418
self.conf = config.Stack([store.get_sections], store)
3420
def register_bool_option(self, name, default=None, default_from_env=None):
3421
b = config.Option(name, help='A boolean.',
3422
default=default, default_from_env=default_from_env,
3423
from_unicode=config.bool_from_store)
3424
self.registry.register(b)
3426
def test_get_default_bool_None(self):
3427
self.register_bool_option('foo')
3428
self.assertEquals(None, self.conf.get('foo'))
3430
def test_get_default_bool_True(self):
3431
self.register_bool_option('foo', u'True')
3432
self.assertEquals(True, self.conf.get('foo'))
3434
def test_get_default_bool_False(self):
3435
self.register_bool_option('foo', False)
3436
self.assertEquals(False, self.conf.get('foo'))
3438
def test_get_default_bool_False_as_string(self):
3439
self.register_bool_option('foo', u'False')
3440
self.assertEquals(False, self.conf.get('foo'))
3442
def test_get_default_bool_from_env_converted(self):
3443
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3444
self.overrideEnv('FOO', 'False')
3445
self.assertEquals(False, self.conf.get('foo'))
3447
def test_get_default_bool_when_conversion_fails(self):
3448
self.register_bool_option('foo', default='True')
3449
self.conf.store._load_from_string('foo=invalid boolean')
3450
self.assertEquals(True, self.conf.get('foo'))
3452
def register_integer_option(self, name,
3453
default=None, default_from_env=None):
3454
i = config.Option(name, help='An integer.',
3455
default=default, default_from_env=default_from_env,
3456
from_unicode=config.int_from_store)
3457
self.registry.register(i)
3459
def test_get_default_integer_None(self):
3460
self.register_integer_option('foo')
3461
self.assertEquals(None, self.conf.get('foo'))
3463
def test_get_default_integer(self):
3464
self.register_integer_option('foo', 42)
3465
self.assertEquals(42, self.conf.get('foo'))
3467
def test_get_default_integer_as_string(self):
3468
self.register_integer_option('foo', u'42')
3469
self.assertEquals(42, self.conf.get('foo'))
3471
def test_get_default_integer_from_env(self):
3472
self.register_integer_option('foo', default_from_env=['FOO'])
3473
self.overrideEnv('FOO', '18')
3474
self.assertEquals(18, self.conf.get('foo'))
3476
def test_get_default_integer_when_conversion_fails(self):
3477
self.register_integer_option('foo', default='12')
3478
self.conf.store._load_from_string('foo=invalid integer')
3479
self.assertEquals(12, self.conf.get('foo'))
3481
def register_list_option(self, name, default=None, default_from_env=None):
3482
l = config.Option(name, help='A list.',
3483
default=default, default_from_env=default_from_env,
3484
from_unicode=config.list_from_store)
3485
self.registry.register(l)
3487
def test_get_default_list_None(self):
3488
self.register_list_option('foo')
3489
self.assertEquals(None, self.conf.get('foo'))
3491
def test_get_default_list_empty(self):
3492
self.register_list_option('foo', '')
3493
self.assertEquals([], self.conf.get('foo'))
3495
def test_get_default_list_from_env(self):
3496
self.register_list_option('foo', default_from_env=['FOO'])
3497
self.overrideEnv('FOO', '')
3498
self.assertEquals([], self.conf.get('foo'))
3500
def test_get_with_list_converter_no_item(self):
3501
self.register_list_option('foo', None)
3502
self.conf.store._load_from_string('foo=,')
3503
self.assertEquals([], self.conf.get('foo'))
3505
def test_get_with_list_converter_many_items(self):
3506
self.register_list_option('foo', None)
3507
self.conf.store._load_from_string('foo=m,o,r,e')
3508
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3510
def test_get_with_list_converter_embedded_spaces_many_items(self):
3511
self.register_list_option('foo', None)
3512
self.conf.store._load_from_string('foo=" bar", "baz "')
3513
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3515
def test_get_with_list_converter_stripped_spaces_many_items(self):
3516
self.register_list_option('foo', None)
3517
self.conf.store._load_from_string('foo= bar , baz ')
3518
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3521
class TestIterOptionRefs(tests.TestCase):
3522
"""iter_option_refs is a bit unusual, document some cases."""
3524
def assertRefs(self, expected, string):
3525
self.assertEquals(expected, list(config.iter_option_refs(string)))
3527
def test_empty(self):
3528
self.assertRefs([(False, '')], '')
3530
def test_no_refs(self):
3531
self.assertRefs([(False, 'foo bar')], 'foo bar')
3533
def test_single_ref(self):
3534
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3536
def test_broken_ref(self):
3537
self.assertRefs([(False, '{foo')], '{foo')
3539
def test_embedded_ref(self):
3540
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3543
def test_two_refs(self):
3544
self.assertRefs([(False, ''), (True, '{foo}'),
3545
(False, ''), (True, '{bar}'),
3550
class TestStackExpandOptions(tests.TestCaseWithTransport):
3553
super(TestStackExpandOptions, self).setUp()
3554
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3555
self.registry = config.option_registry
3556
self.conf = build_branch_stack(self)
3558
def assertExpansion(self, expected, string, env=None):
3559
self.assertEquals(expected, self.conf.expand_options(string, env))
3561
def test_no_expansion(self):
3562
self.assertExpansion('foo', 'foo')
3564
def test_expand_default_value(self):
3565
self.conf.store._load_from_string('bar=baz')
3566
self.registry.register(config.Option('foo', default=u'{bar}'))
3567
self.assertEquals('baz', self.conf.get('foo', expand=True))
3569
def test_expand_default_from_env(self):
3570
self.conf.store._load_from_string('bar=baz')
3571
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3572
self.overrideEnv('FOO', '{bar}')
3573
self.assertEquals('baz', self.conf.get('foo', expand=True))
3575
def test_expand_default_on_failed_conversion(self):
3576
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3577
self.registry.register(
3578
config.Option('foo', default=u'{bar}',
3579
from_unicode=config.int_from_store))
3580
self.assertEquals(42, self.conf.get('foo', expand=True))
3582
def test_env_adding_options(self):
3583
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3585
def test_env_overriding_options(self):
3586
self.conf.store._load_from_string('foo=baz')
3587
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3589
def test_simple_ref(self):
3590
self.conf.store._load_from_string('foo=xxx')
3591
self.assertExpansion('xxx', '{foo}')
3593
def test_unknown_ref(self):
3594
self.assertRaises(errors.ExpandingUnknownOption,
3595
self.conf.expand_options, '{foo}')
3597
def test_indirect_ref(self):
3598
self.conf.store._load_from_string('''
3602
self.assertExpansion('xxx', '{bar}')
3604
def test_embedded_ref(self):
3605
self.conf.store._load_from_string('''
3609
self.assertExpansion('xxx', '{{bar}}')
3611
def test_simple_loop(self):
3612
self.conf.store._load_from_string('foo={foo}')
3613
self.assertRaises(errors.OptionExpansionLoop,
3614
self.conf.expand_options, '{foo}')
3616
def test_indirect_loop(self):
3617
self.conf.store._load_from_string('''
3621
e = self.assertRaises(errors.OptionExpansionLoop,
3622
self.conf.expand_options, '{foo}')
3623
self.assertEquals('foo->bar->baz', e.refs)
3624
self.assertEquals('{foo}', e.string)
3626
def test_list(self):
3627
self.conf.store._load_from_string('''
3631
list={foo},{bar},{baz}
3633
self.registry.register(
3634
config.Option('list', from_unicode=config.list_from_store))
3635
self.assertEquals(['start', 'middle', 'end'],
3636
self.conf.get('list', expand=True))
3638
def test_cascading_list(self):
3639
self.conf.store._load_from_string('''
3645
self.registry.register(
3646
config.Option('list', from_unicode=config.list_from_store))
3647
self.assertEquals(['start', 'middle', 'end'],
3648
self.conf.get('list', expand=True))
3650
def test_pathologically_hidden_list(self):
3651
self.conf.store._load_from_string('''
3657
hidden={start}{middle}{end}
3659
# What matters is what the registration says, the conversion happens
3660
# only after all expansions have been performed
3661
self.registry.register(
3662
config.Option('hidden', from_unicode=config.list_from_store))
3663
self.assertEquals(['bin', 'go'],
3664
self.conf.get('hidden', expand=True))
3667
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3670
super(TestStackCrossSectionsExpand, self).setUp()
3672
def get_config(self, location, string):
3675
# Since we don't save the config we won't strictly require to inherit
3676
# from TestCaseInTempDir, but an error occurs so quickly...
3677
c = config.LocationStack(location)
3678
c.store._load_from_string(string)
3681
def test_dont_cross_unrelated_section(self):
3682
c = self.get_config('/another/branch/path','''
3687
[/another/branch/path]
3690
self.assertRaises(errors.ExpandingUnknownOption,
3691
c.get, 'bar', expand=True)
3693
def test_cross_related_sections(self):
3694
c = self.get_config('/project/branch/path','''
3698
[/project/branch/path]
3701
self.assertEquals('quux', c.get('bar', expand=True))
3704
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3706
def test_cross_global_locations(self):
3707
l_store = config.LocationStore()
3708
l_store._load_from_string('''
3714
g_store = config.GlobalStore()
3715
g_store._load_from_string('''
3721
stack = config.LocationStack('/branch')
3722
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3723
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3726
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3728
def test_expand_locals_empty(self):
3729
l_store = config.LocationStore()
3730
l_store._load_from_string('''
3731
[/home/user/project]
3736
stack = config.LocationStack('/home/user/project/')
3737
self.assertEquals('', stack.get('base', expand=True))
3738
self.assertEquals('', stack.get('rel', expand=True))
3740
def test_expand_basename_locally(self):
3741
l_store = config.LocationStore()
3742
l_store._load_from_string('''
3743
[/home/user/project]
3747
stack = config.LocationStack('/home/user/project/branch')
3748
self.assertEquals('branch', stack.get('bfoo', expand=True))
3750
def test_expand_basename_locally_longer_path(self):
3751
l_store = config.LocationStore()
3752
l_store._load_from_string('''
3757
stack = config.LocationStack('/home/user/project/dir/branch')
3758
self.assertEquals('branch', stack.get('bfoo', expand=True))
3760
def test_expand_relpath_locally(self):
3761
l_store = config.LocationStore()
3762
l_store._load_from_string('''
3763
[/home/user/project]
3764
lfoo = loc-foo/{relpath}
3767
stack = config.LocationStack('/home/user/project/branch')
3768
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3770
def test_expand_relpath_unknonw_in_global(self):
3771
g_store = config.GlobalStore()
3772
g_store._load_from_string('''
3777
stack = config.LocationStack('/home/user/project/branch')
3778
self.assertRaises(errors.ExpandingUnknownOption,
3779
stack.get, 'gfoo', expand=True)
3781
def test_expand_local_option_locally(self):
3782
l_store = config.LocationStore()
3783
l_store._load_from_string('''
3784
[/home/user/project]
3785
lfoo = loc-foo/{relpath}
3789
g_store = config.GlobalStore()
3790
g_store._load_from_string('''
3796
stack = config.LocationStack('/home/user/project/branch')
3797
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3798
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3800
def test_locals_dont_leak(self):
3801
"""Make sure we chose the right local in presence of several sections.
3803
l_store = config.LocationStore()
3804
l_store._load_from_string('''
3806
lfoo = loc-foo/{relpath}
3807
[/home/user/project]
3808
lfoo = loc-foo/{relpath}
3811
stack = config.LocationStack('/home/user/project/branch')
3812
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3813
stack = config.LocationStack('/home/user/bar/baz')
3814
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3818
class TestStackSet(TestStackWithTransport):
3820
def test_simple_set(self):
3821
conf = self.get_stack(self)
3822
self.assertEquals(None, conf.get('foo'))
3823
conf.set('foo', 'baz')
3824
# Did we get it back ?
3825
self.assertEquals('baz', conf.get('foo'))
3827
def test_set_creates_a_new_section(self):
3828
conf = self.get_stack(self)
3829
conf.set('foo', 'baz')
3830
self.assertEquals, 'baz', conf.get('foo')
3832
def test_set_hook(self):
3836
config.ConfigHooks.install_named_hook('set', hook, None)
3837
self.assertLength(0, calls)
3838
conf = self.get_stack(self)
3839
conf.set('foo', 'bar')
3840
self.assertLength(1, calls)
3841
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3844
class TestStackRemove(TestStackWithTransport):
3846
def test_remove_existing(self):
3847
conf = self.get_stack(self)
3848
conf.set('foo', 'bar')
3849
self.assertEquals('bar', conf.get('foo'))
3851
# Did we get it back ?
3852
self.assertEquals(None, conf.get('foo'))
3854
def test_remove_unknown(self):
3855
conf = self.get_stack(self)
3856
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3858
def test_remove_hook(self):
3862
config.ConfigHooks.install_named_hook('remove', hook, None)
3863
self.assertLength(0, calls)
3864
conf = self.get_stack(self)
3865
conf.set('foo', 'bar')
3867
self.assertLength(1, calls)
3868
self.assertEquals((conf, 'foo'), calls[0])
3871
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3874
super(TestConfigGetOptions, self).setUp()
3875
create_configs(self)
3877
def test_no_variable(self):
3878
# Using branch should query branch, locations and bazaar
3879
self.assertOptions([], self.branch_config)
3881
def test_option_in_bazaar(self):
3882
self.bazaar_config.set_user_option('file', 'bazaar')
3883
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3886
def test_option_in_locations(self):
3887
self.locations_config.set_user_option('file', 'locations')
3889
[('file', 'locations', self.tree.basedir, 'locations')],
3890
self.locations_config)
3892
def test_option_in_branch(self):
3893
self.branch_config.set_user_option('file', 'branch')
3894
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3897
def test_option_in_bazaar_and_branch(self):
3898
self.bazaar_config.set_user_option('file', 'bazaar')
3899
self.branch_config.set_user_option('file', 'branch')
3900
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3901
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3904
def test_option_in_branch_and_locations(self):
3905
# Hmm, locations override branch :-/
3906
self.locations_config.set_user_option('file', 'locations')
3907
self.branch_config.set_user_option('file', 'branch')
3909
[('file', 'locations', self.tree.basedir, 'locations'),
3910
('file', 'branch', 'DEFAULT', 'branch'),],
3913
def test_option_in_bazaar_locations_and_branch(self):
3914
self.bazaar_config.set_user_option('file', 'bazaar')
3915
self.locations_config.set_user_option('file', 'locations')
3916
self.branch_config.set_user_option('file', 'branch')
3918
[('file', 'locations', self.tree.basedir, 'locations'),
3919
('file', 'branch', 'DEFAULT', 'branch'),
3920
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3924
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3927
super(TestConfigRemoveOption, self).setUp()
3928
create_configs_with_file_option(self)
3930
def test_remove_in_locations(self):
3931
self.locations_config.remove_user_option('file', self.tree.basedir)
3933
[('file', 'branch', 'DEFAULT', 'branch'),
3934
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3937
def test_remove_in_branch(self):
3938
self.branch_config.remove_user_option('file')
3940
[('file', 'locations', self.tree.basedir, 'locations'),
3941
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3944
def test_remove_in_bazaar(self):
3945
self.bazaar_config.remove_user_option('file')
3947
[('file', 'locations', self.tree.basedir, 'locations'),
3948
('file', 'branch', 'DEFAULT', 'branch'),],
3952
class TestConfigGetSections(tests.TestCaseWithTransport):
3955
super(TestConfigGetSections, self).setUp()
3956
create_configs(self)
3958
def assertSectionNames(self, expected, conf, name=None):
3959
"""Check which sections are returned for a given config.
3961
If fallback configurations exist their sections can be included.
3963
:param expected: A list of section names.
3965
:param conf: The configuration that will be queried.
3967
:param name: An optional section name that will be passed to
3970
sections = list(conf._get_sections(name))
3971
self.assertLength(len(expected), sections)
3972
self.assertEqual(expected, [name for name, _, _ in sections])
3974
def test_bazaar_default_section(self):
3975
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3977
def test_locations_default_section(self):
3978
# No sections are defined in an empty file
3979
self.assertSectionNames([], self.locations_config)
3981
def test_locations_named_section(self):
3982
self.locations_config.set_user_option('file', 'locations')
3983
self.assertSectionNames([self.tree.basedir], self.locations_config)
3985
def test_locations_matching_sections(self):
3986
loc_config = self.locations_config
3987
loc_config.set_user_option('file', 'locations')
3988
# We need to cheat a bit here to create an option in sections above and
3989
# below the 'location' one.
3990
parser = loc_config._get_parser()
3991
# locations.cong deals with '/' ignoring native os.sep
3992
location_names = self.tree.basedir.split('/')
3993
parent = '/'.join(location_names[:-1])
3994
child = '/'.join(location_names + ['child'])
3996
parser[parent]['file'] = 'parent'
3998
parser[child]['file'] = 'child'
3999
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4001
def test_branch_data_default_section(self):
4002
self.assertSectionNames([None],
4003
self.branch_config._get_branch_data_config())
4005
def test_branch_default_sections(self):
4006
# No sections are defined in an empty locations file
4007
self.assertSectionNames([None, 'DEFAULT'],
4009
# Unless we define an option
4010
self.branch_config._get_location_config().set_user_option(
4011
'file', 'locations')
4012
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4015
def test_bazaar_named_section(self):
4016
# We need to cheat as the API doesn't give direct access to sections
4017
# other than DEFAULT.
4018
self.bazaar_config.set_alias('bazaar', 'bzr')
4019
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
4022
class TestAuthenticationConfigFile(tests.TestCase):
1316
4023
"""Test the authentication.conf file matching"""