367
585
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
588
class TestXDGConfigDir(tests.TestCaseInTempDir):
589
# must be in temp dir because config tests for the existence of the bazaar
590
# subdirectory of $XDG_CONFIG_HOME
593
if sys.platform in ('darwin', 'win32'):
594
raise tests.TestNotApplicable(
595
'XDG config dir not used on this platform')
596
super(TestXDGConfigDir, self).setUp()
597
self.overrideEnv('HOME', self.test_home_dir)
598
# BZR_HOME overrides everything we want to test so unset it.
599
self.overrideEnv('BZR_HOME', None)
601
def test_xdg_config_dir_exists(self):
602
"""When ~/.config/bazaar exists, use it as the config dir."""
603
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
605
self.assertEqual(config.config_dir(), newdir)
607
def test_xdg_config_home(self):
608
"""When XDG_CONFIG_HOME is set, use it."""
609
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
610
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
611
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
613
self.assertEqual(config.config_dir(), newdir)
616
class TestIniConfig(tests.TestCaseInTempDir):
372
618
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
619
conf = config.IniBasedConfig.from_string(s)
620
return conf, conf._get_parser()
378
623
class TestIniConfigBuilding(TestIniConfig):
380
625
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
626
my_config = config.IniBasedConfig()
383
628
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))
629
my_config = config.IniBasedConfig.from_string(sample_config_text)
630
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
632
def test_cached(self):
633
my_config = config.IniBasedConfig.from_string(sample_config_text)
634
parser = my_config._get_parser()
635
self.assertTrue(my_config._get_parser() is parser)
637
def _dummy_chown(self, path, uid, gid):
638
self.path, self.uid, self.gid = path, uid, gid
640
def test_ini_config_ownership(self):
641
"""Ensure that chown is happening during _write_config_file"""
642
self.requireFeature(features.chown_feature)
643
self.overrideAttr(os, 'chown', self._dummy_chown)
644
self.path = self.uid = self.gid = None
645
conf = config.IniBasedConfig(file_name='./foo.conf')
646
conf._write_config_file()
647
self.assertEquals(self.path, './foo.conf')
648
self.assertTrue(isinstance(self.uid, int))
649
self.assertTrue(isinstance(self.gid, int))
651
def test_get_filename_parameter_is_deprecated_(self):
652
conf = self.callDeprecated([
653
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
654
' Use file_name instead.'],
655
config.IniBasedConfig, lambda: 'ini.conf')
656
self.assertEqual('ini.conf', conf.file_name)
658
def test_get_parser_file_parameter_is_deprecated_(self):
391
659
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)
660
conf = config.IniBasedConfig.from_string(sample_config_text)
661
conf = self.callDeprecated([
662
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
663
' Use IniBasedConfig(_content=xxx) instead.'],
664
conf._get_parser, file=config_file)
667
class TestIniConfigSaving(tests.TestCaseInTempDir):
669
def test_cant_save_without_a_file_name(self):
670
conf = config.IniBasedConfig()
671
self.assertRaises(AssertionError, conf._write_config_file)
673
def test_saved_with_content(self):
674
content = 'foo = bar\n'
675
conf = config.IniBasedConfig.from_string(
676
content, file_name='./test.conf', save=True)
677
self.assertFileEqual(content, 'test.conf')
680
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
681
"""What is the default value of expand for config options.
683
This is an opt-in beta feature used to evaluate whether or not option
684
references can appear in dangerous place raising exceptions, disapearing
685
(and as such corrupting data) or if it's safe to activate the option by
688
Note that these tests relies on config._expand_default_value being already
689
overwritten in the parent class setUp.
693
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
697
self.warnings.append(args[0] % args[1:])
698
self.overrideAttr(trace, 'warning', warning)
700
def get_config(self, expand):
701
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
705
def assertExpandIs(self, expected):
706
actual = config._get_expand_default_value()
707
#self.config.get_user_option_as_bool('bzr.config.expand')
708
self.assertEquals(expected, actual)
710
def test_default_is_None(self):
711
self.assertEquals(None, config._expand_default_value)
713
def test_default_is_False_even_if_None(self):
714
self.config = self.get_config(None)
715
self.assertExpandIs(False)
717
def test_default_is_False_even_if_invalid(self):
718
self.config = self.get_config('<your choice>')
719
self.assertExpandIs(False)
721
# Huh ? My choice is False ? Thanks, always happy to hear that :D
722
# Wait, you've been warned !
723
self.assertLength(1, self.warnings)
725
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
728
def test_default_is_True(self):
729
self.config = self.get_config(True)
730
self.assertExpandIs(True)
732
def test_default_is_False(self):
733
self.config = self.get_config(False)
734
self.assertExpandIs(False)
737
class TestIniConfigOptionExpansion(tests.TestCase):
738
"""Test option expansion from the IniConfig level.
740
What we really want here is to test the Config level, but the class being
741
abstract as far as storing values is concerned, this can't be done
744
# FIXME: This should be rewritten when all configs share a storage
745
# implementation -- vila 2011-02-18
747
def get_config(self, string=None):
750
c = config.IniBasedConfig.from_string(string)
753
def assertExpansion(self, expected, conf, string, env=None):
754
self.assertEquals(expected, conf.expand_options(string, env))
756
def test_no_expansion(self):
757
c = self.get_config('')
758
self.assertExpansion('foo', c, 'foo')
760
def test_env_adding_options(self):
761
c = self.get_config('')
762
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
764
def test_env_overriding_options(self):
765
c = self.get_config('foo=baz')
766
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
768
def test_simple_ref(self):
769
c = self.get_config('foo=xxx')
770
self.assertExpansion('xxx', c, '{foo}')
772
def test_unknown_ref(self):
773
c = self.get_config('')
774
self.assertRaises(errors.ExpandingUnknownOption,
775
c.expand_options, '{foo}')
777
def test_indirect_ref(self):
778
c = self.get_config('''
782
self.assertExpansion('xxx', c, '{bar}')
784
def test_embedded_ref(self):
785
c = self.get_config('''
789
self.assertExpansion('xxx', c, '{{bar}}')
791
def test_simple_loop(self):
792
c = self.get_config('foo={foo}')
793
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
795
def test_indirect_loop(self):
796
c = self.get_config('''
800
e = self.assertRaises(errors.OptionExpansionLoop,
801
c.expand_options, '{foo}')
802
self.assertEquals('foo->bar->baz', e.refs)
803
self.assertEquals('{foo}', e.string)
806
conf = self.get_config('''
810
list={foo},{bar},{baz}
812
self.assertEquals(['start', 'middle', 'end'],
813
conf.get_user_option('list', expand=True))
815
def test_cascading_list(self):
816
conf = self.get_config('''
822
self.assertEquals(['start', 'middle', 'end'],
823
conf.get_user_option('list', expand=True))
825
def test_pathological_hidden_list(self):
826
conf = self.get_config('''
832
hidden={start}{middle}{end}
834
# Nope, it's either a string or a list, and the list wins as soon as a
835
# ',' appears, so the string concatenation never occur.
836
self.assertEquals(['{foo', '}', '{', 'bar}'],
837
conf.get_user_option('hidden', expand=True))
840
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
842
def get_config(self, location, string=None):
845
# Since we don't save the config we won't strictly require to inherit
846
# from TestCaseInTempDir, but an error occurs so quickly...
847
c = config.LocationConfig.from_string(string, location)
850
def test_dont_cross_unrelated_section(self):
851
c = self.get_config('/another/branch/path','''
856
[/another/branch/path]
859
self.assertRaises(errors.ExpandingUnknownOption,
860
c.get_user_option, 'bar', expand=True)
862
def test_cross_related_sections(self):
863
c = self.get_config('/project/branch/path','''
867
[/project/branch/path]
870
self.assertEquals('quux', c.get_user_option('bar', expand=True))
873
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
875
def test_cannot_reload_without_name(self):
876
conf = config.IniBasedConfig.from_string(sample_config_text)
877
self.assertRaises(AssertionError, conf.reload)
879
def test_reload_see_new_value(self):
880
c1 = config.IniBasedConfig.from_string('editor=vim\n',
881
file_name='./test/conf')
882
c1._write_config_file()
883
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
884
file_name='./test/conf')
885
c2._write_config_file()
886
self.assertEqual('vim', c1.get_user_option('editor'))
887
self.assertEqual('emacs', c2.get_user_option('editor'))
888
# Make sure we get the Right value
890
self.assertEqual('emacs', c1.get_user_option('editor'))
893
class TestLockableConfig(tests.TestCaseInTempDir):
895
scenarios = lockable_config_scenarios()
900
config_section = None
903
super(TestLockableConfig, self).setUp()
904
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
905
self.config = self.create_config(self._content)
907
def get_existing_config(self):
908
return self.config_class(*self.config_args)
910
def create_config(self, content):
911
kwargs = dict(save=True)
912
c = self.config_class.from_string(content, *self.config_args, **kwargs)
915
def test_simple_read_access(self):
916
self.assertEquals('1', self.config.get_user_option('one'))
918
def test_simple_write_access(self):
919
self.config.set_user_option('one', 'one')
920
self.assertEquals('one', self.config.get_user_option('one'))
922
def test_listen_to_the_last_speaker(self):
924
c2 = self.get_existing_config()
925
c1.set_user_option('one', 'ONE')
926
c2.set_user_option('two', 'TWO')
927
self.assertEquals('ONE', c1.get_user_option('one'))
928
self.assertEquals('TWO', c2.get_user_option('two'))
929
# The second update respect the first one
930
self.assertEquals('ONE', c2.get_user_option('one'))
932
def test_last_speaker_wins(self):
933
# If the same config is not shared, the same variable modified twice
934
# can only see a single result.
936
c2 = self.get_existing_config()
937
c1.set_user_option('one', 'c1')
938
c2.set_user_option('one', 'c2')
939
self.assertEquals('c2', c2._get_user_option('one'))
940
# The first modification is still available until another refresh
942
self.assertEquals('c1', c1._get_user_option('one'))
943
c1.set_user_option('two', 'done')
944
self.assertEquals('c2', c1._get_user_option('one'))
946
def test_writes_are_serialized(self):
948
c2 = self.get_existing_config()
950
# We spawn a thread that will pause *during* the write
951
before_writing = threading.Event()
952
after_writing = threading.Event()
953
writing_done = threading.Event()
954
c1_orig = c1._write_config_file
955
def c1_write_config_file():
958
# The lock is held. We wait for the main thread to decide when to
961
c1._write_config_file = c1_write_config_file
963
c1.set_user_option('one', 'c1')
965
t1 = threading.Thread(target=c1_set_option)
966
# Collect the thread after the test
967
self.addCleanup(t1.join)
968
# Be ready to unblock the thread if the test goes wrong
969
self.addCleanup(after_writing.set)
971
before_writing.wait()
972
self.assertTrue(c1._lock.is_held)
973
self.assertRaises(errors.LockContention,
974
c2.set_user_option, 'one', 'c2')
975
self.assertEquals('c1', c1.get_user_option('one'))
976
# Let the lock be released
979
c2.set_user_option('one', 'c2')
980
self.assertEquals('c2', c2.get_user_option('one'))
982
def test_read_while_writing(self):
984
# We spawn a thread that will pause *during* the write
985
ready_to_write = threading.Event()
986
do_writing = threading.Event()
987
writing_done = threading.Event()
988
c1_orig = c1._write_config_file
989
def c1_write_config_file():
991
# The lock is held. We wait for the main thread to decide when to
996
c1._write_config_file = c1_write_config_file
998
c1.set_user_option('one', 'c1')
999
t1 = threading.Thread(target=c1_set_option)
1000
# Collect the thread after the test
1001
self.addCleanup(t1.join)
1002
# Be ready to unblock the thread if the test goes wrong
1003
self.addCleanup(do_writing.set)
1005
# Ensure the thread is ready to write
1006
ready_to_write.wait()
1007
self.assertTrue(c1._lock.is_held)
1008
self.assertEquals('c1', c1.get_user_option('one'))
1009
# If we read during the write, we get the old value
1010
c2 = self.get_existing_config()
1011
self.assertEquals('1', c2.get_user_option('one'))
1012
# Let the writing occur and ensure it occurred
1015
# Now we get the updated value
1016
c3 = self.get_existing_config()
1017
self.assertEquals('c1', c3.get_user_option('one'))
397
1020
class TestGetUserOptionAs(TestIniConfig):
1312
2028
self.assertIs(None, bzrdir_config.get_default_stack_on())
2031
class TestOldConfigHooks(tests.TestCaseWithTransport):
2034
super(TestOldConfigHooks, self).setUp()
2035
create_configs_with_file_option(self)
2037
def assertGetHook(self, conf, name, value):
2041
config.OldConfigHooks.install_named_hook('get', hook, None)
2043
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2044
self.assertLength(0, calls)
2045
actual_value = conf.get_user_option(name)
2046
self.assertEquals(value, actual_value)
2047
self.assertLength(1, calls)
2048
self.assertEquals((conf, name, value), calls[0])
2050
def test_get_hook_bazaar(self):
2051
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2053
def test_get_hook_locations(self):
2054
self.assertGetHook(self.locations_config, 'file', 'locations')
2056
def test_get_hook_branch(self):
2057
# Since locations masks branch, we define a different option
2058
self.branch_config.set_user_option('file2', 'branch')
2059
self.assertGetHook(self.branch_config, 'file2', 'branch')
2061
def assertSetHook(self, conf, name, value):
2065
config.OldConfigHooks.install_named_hook('set', hook, None)
2067
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2068
self.assertLength(0, calls)
2069
conf.set_user_option(name, value)
2070
self.assertLength(1, calls)
2071
# We can't assert the conf object below as different configs use
2072
# different means to implement set_user_option and we care only about
2074
self.assertEquals((name, value), calls[0][1:])
2076
def test_set_hook_bazaar(self):
2077
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2079
def test_set_hook_locations(self):
2080
self.assertSetHook(self.locations_config, 'foo', 'locations')
2082
def test_set_hook_branch(self):
2083
self.assertSetHook(self.branch_config, 'foo', 'branch')
2085
def assertRemoveHook(self, conf, name, section_name=None):
2089
config.OldConfigHooks.install_named_hook('remove', hook, None)
2091
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2092
self.assertLength(0, calls)
2093
conf.remove_user_option(name, section_name)
2094
self.assertLength(1, calls)
2095
# We can't assert the conf object below as different configs use
2096
# different means to implement remove_user_option and we care only about
2098
self.assertEquals((name,), calls[0][1:])
2100
def test_remove_hook_bazaar(self):
2101
self.assertRemoveHook(self.bazaar_config, 'file')
2103
def test_remove_hook_locations(self):
2104
self.assertRemoveHook(self.locations_config, 'file',
2105
self.locations_config.location)
2107
def test_remove_hook_branch(self):
2108
self.assertRemoveHook(self.branch_config, 'file')
2110
def assertLoadHook(self, name, conf_class, *conf_args):
2114
config.OldConfigHooks.install_named_hook('load', hook, None)
2116
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2117
self.assertLength(0, calls)
2119
conf = conf_class(*conf_args)
2120
# Access an option to trigger a load
2121
conf.get_user_option(name)
2122
self.assertLength(1, calls)
2123
# Since we can't assert about conf, we just use the number of calls ;-/
2125
def test_load_hook_bazaar(self):
2126
self.assertLoadHook('file', config.GlobalConfig)
2128
def test_load_hook_locations(self):
2129
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2131
def test_load_hook_branch(self):
2132
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2134
def assertSaveHook(self, conf):
2138
config.OldConfigHooks.install_named_hook('save', hook, None)
2140
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2141
self.assertLength(0, calls)
2142
# Setting an option triggers a save
2143
conf.set_user_option('foo', 'bar')
2144
self.assertLength(1, calls)
2145
# Since we can't assert about conf, we just use the number of calls ;-/
2147
def test_save_hook_bazaar(self):
2148
self.assertSaveHook(self.bazaar_config)
2150
def test_save_hook_locations(self):
2151
self.assertSaveHook(self.locations_config)
2153
def test_save_hook_branch(self):
2154
self.assertSaveHook(self.branch_config)
2157
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2158
"""Tests config hooks for remote configs.
2160
No tests for the remove hook as this is not implemented there.
2164
super(TestOldConfigHooksForRemote, self).setUp()
2165
self.transport_server = test_server.SmartTCPServer_for_testing
2166
create_configs_with_file_option(self)
2168
def assertGetHook(self, conf, name, value):
2172
config.OldConfigHooks.install_named_hook('get', hook, None)
2174
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2175
self.assertLength(0, calls)
2176
actual_value = conf.get_option(name)
2177
self.assertEquals(value, actual_value)
2178
self.assertLength(1, calls)
2179
self.assertEquals((conf, name, value), calls[0])
2181
def test_get_hook_remote_branch(self):
2182
remote_branch = branch.Branch.open(self.get_url('tree'))
2183
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2185
def test_get_hook_remote_bzrdir(self):
2186
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2187
conf = remote_bzrdir._get_config()
2188
conf.set_option('remotedir', 'file')
2189
self.assertGetHook(conf, 'file', 'remotedir')
2191
def assertSetHook(self, conf, name, value):
2195
config.OldConfigHooks.install_named_hook('set', hook, None)
2197
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2198
self.assertLength(0, calls)
2199
conf.set_option(value, name)
2200
self.assertLength(1, calls)
2201
# We can't assert the conf object below as different configs use
2202
# different means to implement set_user_option and we care only about
2204
self.assertEquals((name, value), calls[0][1:])
2206
def test_set_hook_remote_branch(self):
2207
remote_branch = branch.Branch.open(self.get_url('tree'))
2208
self.addCleanup(remote_branch.lock_write().unlock)
2209
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2211
def test_set_hook_remote_bzrdir(self):
2212
remote_branch = branch.Branch.open(self.get_url('tree'))
2213
self.addCleanup(remote_branch.lock_write().unlock)
2214
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2215
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2217
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2221
config.OldConfigHooks.install_named_hook('load', hook, None)
2223
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2224
self.assertLength(0, calls)
2226
conf = conf_class(*conf_args)
2227
# Access an option to trigger a load
2228
conf.get_option(name)
2229
self.assertLength(expected_nb_calls, calls)
2230
# Since we can't assert about conf, we just use the number of calls ;-/
2232
def test_load_hook_remote_branch(self):
2233
remote_branch = branch.Branch.open(self.get_url('tree'))
2234
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2236
def test_load_hook_remote_bzrdir(self):
2237
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2238
# The config file doesn't exist, set an option to force its creation
2239
conf = remote_bzrdir._get_config()
2240
conf.set_option('remotedir', 'file')
2241
# We get one call for the server and one call for the client, this is
2242
# caused by the differences in implementations betwen
2243
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2244
# SmartServerBranchGetConfigFile (in smart/branch.py)
2245
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2247
def assertSaveHook(self, conf):
2251
config.OldConfigHooks.install_named_hook('save', hook, None)
2253
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2254
self.assertLength(0, calls)
2255
# Setting an option triggers a save
2256
conf.set_option('foo', 'bar')
2257
self.assertLength(1, calls)
2258
# Since we can't assert about conf, we just use the number of calls ;-/
2260
def test_save_hook_remote_branch(self):
2261
remote_branch = branch.Branch.open(self.get_url('tree'))
2262
self.addCleanup(remote_branch.lock_write().unlock)
2263
self.assertSaveHook(remote_branch._get_config())
2265
def test_save_hook_remote_bzrdir(self):
2266
remote_branch = branch.Branch.open(self.get_url('tree'))
2267
self.addCleanup(remote_branch.lock_write().unlock)
2268
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2269
self.assertSaveHook(remote_bzrdir._get_config())
2272
class TestOption(tests.TestCase):
2274
def test_default_value(self):
2275
opt = config.Option('foo', default='bar')
2276
self.assertEquals('bar', opt.get_default())
2278
def test_default_value_from_env(self):
2279
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2280
self.overrideEnv('FOO', 'quux')
2281
# Env variable provides a default taking over the option one
2282
self.assertEquals('quux', opt.get_default())
2284
def test_first_default_value_from_env_wins(self):
2285
opt = config.Option('foo', default='bar',
2286
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2287
self.overrideEnv('FOO', 'foo')
2288
self.overrideEnv('BAZ', 'baz')
2289
# The first env var set wins
2290
self.assertEquals('foo', opt.get_default())
2292
def test_not_supported_list_default_value(self):
2293
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2295
def test_not_supported_object_default_value(self):
2296
self.assertRaises(AssertionError, config.Option, 'foo',
2300
class TestOptionConverterMixin(object):
2302
def assertConverted(self, expected, opt, value):
2303
self.assertEquals(expected, opt.convert_from_unicode(value))
2305
def assertWarns(self, opt, value):
2308
warnings.append(args[0] % args[1:])
2309
self.overrideAttr(trace, 'warning', warning)
2310
self.assertEquals(None, opt.convert_from_unicode(value))
2311
self.assertLength(1, warnings)
2313
'Value "%s" is not valid for "%s"' % (value, opt.name),
2316
def assertErrors(self, opt, value):
2317
self.assertRaises(errors.ConfigOptionValueError,
2318
opt.convert_from_unicode, value)
2320
def assertConvertInvalid(self, opt, invalid_value):
2322
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2323
opt.invalid = 'warning'
2324
self.assertWarns(opt, invalid_value)
2325
opt.invalid = 'error'
2326
self.assertErrors(opt, invalid_value)
2329
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2331
def get_option(self):
2332
return config.Option('foo', help='A boolean.',
2333
from_unicode=config.bool_from_store)
2335
def test_convert_invalid(self):
2336
opt = self.get_option()
2337
# A string that is not recognized as a boolean
2338
self.assertConvertInvalid(opt, u'invalid-boolean')
2339
# A list of strings is never recognized as a boolean
2340
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2342
def test_convert_valid(self):
2343
opt = self.get_option()
2344
self.assertConverted(True, opt, u'True')
2345
self.assertConverted(True, opt, u'1')
2346
self.assertConverted(False, opt, u'False')
2349
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2351
def get_option(self):
2352
return config.Option('foo', help='An integer.',
2353
from_unicode=config.int_from_store)
2355
def test_convert_invalid(self):
2356
opt = self.get_option()
2357
# A string that is not recognized as an integer
2358
self.assertConvertInvalid(opt, u'forty-two')
2359
# A list of strings is never recognized as an integer
2360
self.assertConvertInvalid(opt, [u'a', u'list'])
2362
def test_convert_valid(self):
2363
opt = self.get_option()
2364
self.assertConverted(16, opt, u'16')
2366
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2368
def get_option(self):
2369
return config.Option('foo', help='A list.',
2370
from_unicode=config.list_from_store)
2372
def test_convert_invalid(self):
2373
# No string is invalid as all forms can be converted to a list
2376
def test_convert_valid(self):
2377
opt = self.get_option()
2378
# An empty string is an empty list
2379
self.assertConverted([], opt, '') # Using a bare str() just in case
2380
self.assertConverted([], opt, u'')
2382
self.assertConverted([u'True'], opt, u'True')
2384
self.assertConverted([u'42'], opt, u'42')
2386
self.assertConverted([u'bar'], opt, u'bar')
2387
# A list remains a list (configObj will turn a string containing commas
2388
# into a list, but that's not what we're testing here)
2389
self.assertConverted([u'foo', u'1', u'True'],
2390
opt, [u'foo', u'1', u'True'])
2393
class TestOptionConverterMixin(object):
2395
def assertConverted(self, expected, opt, value):
2396
self.assertEquals(expected, opt.convert_from_unicode(value))
2398
def assertWarns(self, opt, value):
2401
warnings.append(args[0] % args[1:])
2402
self.overrideAttr(trace, 'warning', warning)
2403
self.assertEquals(None, opt.convert_from_unicode(value))
2404
self.assertLength(1, warnings)
2406
'Value "%s" is not valid for "%s"' % (value, opt.name),
2409
def assertErrors(self, opt, value):
2410
self.assertRaises(errors.ConfigOptionValueError,
2411
opt.convert_from_unicode, value)
2413
def assertConvertInvalid(self, opt, invalid_value):
2415
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2416
opt.invalid = 'warning'
2417
self.assertWarns(opt, invalid_value)
2418
opt.invalid = 'error'
2419
self.assertErrors(opt, invalid_value)
2422
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2424
def get_option(self):
2425
return config.Option('foo', help='A boolean.',
2426
from_unicode=config.bool_from_store)
2428
def test_convert_invalid(self):
2429
opt = self.get_option()
2430
# A string that is not recognized as a boolean
2431
self.assertConvertInvalid(opt, u'invalid-boolean')
2432
# A list of strings is never recognized as a boolean
2433
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
self.assertConverted(True, opt, u'True')
2438
self.assertConverted(True, opt, u'1')
2439
self.assertConverted(False, opt, u'False')
2442
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2444
def get_option(self):
2445
return config.Option('foo', help='An integer.',
2446
from_unicode=config.int_from_store)
2448
def test_convert_invalid(self):
2449
opt = self.get_option()
2450
# A string that is not recognized as an integer
2451
self.assertConvertInvalid(opt, u'forty-two')
2452
# A list of strings is never recognized as an integer
2453
self.assertConvertInvalid(opt, [u'a', u'list'])
2455
def test_convert_valid(self):
2456
opt = self.get_option()
2457
self.assertConverted(16, opt, u'16')
2460
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2462
def get_option(self):
2463
return config.Option('foo', help='A list.',
2464
from_unicode=config.list_from_store)
2466
def test_convert_invalid(self):
2467
opt = self.get_option()
2468
# We don't even try to convert a list into a list, we only expect
2470
self.assertConvertInvalid(opt, [1])
2471
# No string is invalid as all forms can be converted to a list
2473
def test_convert_valid(self):
2474
opt = self.get_option()
2475
# An empty string is an empty list
2476
self.assertConverted([], opt, '') # Using a bare str() just in case
2477
self.assertConverted([], opt, u'')
2479
self.assertConverted([u'True'], opt, u'True')
2481
self.assertConverted([u'42'], opt, u'42')
2483
self.assertConverted([u'bar'], opt, u'bar')
2486
class TestOptionRegistry(tests.TestCase):
2489
super(TestOptionRegistry, self).setUp()
2490
# Always start with an empty registry
2491
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2492
self.registry = config.option_registry
2494
def test_register(self):
2495
opt = config.Option('foo')
2496
self.registry.register(opt)
2497
self.assertIs(opt, self.registry.get('foo'))
2499
def test_registered_help(self):
2500
opt = config.Option('foo', help='A simple option')
2501
self.registry.register(opt)
2502
self.assertEquals('A simple option', self.registry.get_help('foo'))
2504
lazy_option = config.Option('lazy_foo', help='Lazy help')
2506
def test_register_lazy(self):
2507
self.registry.register_lazy('lazy_foo', self.__module__,
2508
'TestOptionRegistry.lazy_option')
2509
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2511
def test_registered_lazy_help(self):
2512
self.registry.register_lazy('lazy_foo', self.__module__,
2513
'TestOptionRegistry.lazy_option')
2514
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2517
class TestRegisteredOptions(tests.TestCase):
2518
"""All registered options should verify some constraints."""
2520
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2521
in config.option_registry.iteritems()]
2524
super(TestRegisteredOptions, self).setUp()
2525
self.registry = config.option_registry
2527
def test_proper_name(self):
2528
# An option should be registered under its own name, this can't be
2529
# checked at registration time for the lazy ones.
2530
self.assertEquals(self.option_name, self.option.name)
2532
def test_help_is_set(self):
2533
option_help = self.registry.get_help(self.option_name)
2534
self.assertNotEquals(None, option_help)
2535
# Come on, think about the user, he really wants to know what the
2537
self.assertIsNot(None, option_help)
2538
self.assertNotEquals('', option_help)
2541
class TestSection(tests.TestCase):
2543
# FIXME: Parametrize so that all sections produced by Stores run these
2544
# tests -- vila 2011-04-01
2546
def test_get_a_value(self):
2547
a_dict = dict(foo='bar')
2548
section = config.Section('myID', a_dict)
2549
self.assertEquals('bar', section.get('foo'))
2551
def test_get_unknown_option(self):
2553
section = config.Section(None, a_dict)
2554
self.assertEquals('out of thin air',
2555
section.get('foo', 'out of thin air'))
2557
def test_options_is_shared(self):
2559
section = config.Section(None, a_dict)
2560
self.assertIs(a_dict, section.options)
2563
class TestMutableSection(tests.TestCase):
2565
scenarios = [('mutable',
2567
lambda opts: config.MutableSection('myID', opts)},),
2571
a_dict = dict(foo='bar')
2572
section = self.get_section(a_dict)
2573
section.set('foo', 'new_value')
2574
self.assertEquals('new_value', section.get('foo'))
2575
# The change appears in the shared section
2576
self.assertEquals('new_value', a_dict.get('foo'))
2577
# We keep track of the change
2578
self.assertTrue('foo' in section.orig)
2579
self.assertEquals('bar', section.orig.get('foo'))
2581
def test_set_preserve_original_once(self):
2582
a_dict = dict(foo='bar')
2583
section = self.get_section(a_dict)
2584
section.set('foo', 'first_value')
2585
section.set('foo', 'second_value')
2586
# We keep track of the original value
2587
self.assertTrue('foo' in section.orig)
2588
self.assertEquals('bar', section.orig.get('foo'))
2590
def test_remove(self):
2591
a_dict = dict(foo='bar')
2592
section = self.get_section(a_dict)
2593
section.remove('foo')
2594
# We get None for unknown options via the default value
2595
self.assertEquals(None, section.get('foo'))
2596
# Or we just get the default value
2597
self.assertEquals('unknown', section.get('foo', 'unknown'))
2598
self.assertFalse('foo' in section.options)
2599
# We keep track of the deletion
2600
self.assertTrue('foo' in section.orig)
2601
self.assertEquals('bar', section.orig.get('foo'))
2603
def test_remove_new_option(self):
2605
section = self.get_section(a_dict)
2606
section.set('foo', 'bar')
2607
section.remove('foo')
2608
self.assertFalse('foo' in section.options)
2609
# The option didn't exist initially so it we need to keep track of it
2610
# with a special value
2611
self.assertTrue('foo' in section.orig)
2612
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2615
class TestCommandLineStore(tests.TestCase):
2618
super(TestCommandLineStore, self).setUp()
2619
self.store = config.CommandLineStore()
2621
def get_section(self):
2622
"""Get the unique section for the command line overrides."""
2623
sections = list(self.store.get_sections())
2624
self.assertLength(1, sections)
2625
store, section = sections[0]
2626
self.assertEquals(self.store, store)
2629
def test_no_override(self):
2630
self.store._from_cmdline([])
2631
section = self.get_section()
2632
self.assertLength(0, list(section.iter_option_names()))
2634
def test_simple_override(self):
2635
self.store._from_cmdline(['a=b'])
2636
section = self.get_section()
2637
self.assertEqual('b', section.get('a'))
2639
def test_list_override(self):
2640
self.store._from_cmdline(['l=1,2,3'])
2641
val = self.get_section().get('l')
2642
self.assertEqual('1,2,3', val)
2643
# Reminder: lists should be registered as such explicitely, otherwise
2644
# the conversion needs to be done afterwards.
2645
self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2647
def test_multiple_overrides(self):
2648
self.store._from_cmdline(['a=b', 'x=y'])
2649
section = self.get_section()
2650
self.assertEquals('b', section.get('a'))
2651
self.assertEquals('y', section.get('x'))
2653
def test_wrong_syntax(self):
2654
self.assertRaises(errors.BzrCommandError,
2655
self.store._from_cmdline, ['a=b', 'c'])
2658
class TestStore(tests.TestCaseWithTransport):
2660
def assertSectionContent(self, expected, (store, section)):
2661
"""Assert that some options have the proper values in a section."""
2662
expected_name, expected_options = expected
2663
self.assertEquals(expected_name, section.id)
2666
dict([(k, section.get(k)) for k in expected_options.keys()]))
2669
class TestReadonlyStore(TestStore):
2671
scenarios = [(key, {'get_store': builder}) for key, builder
2672
in config.test_store_builder_registry.iteritems()]
2674
def test_building_delays_load(self):
2675
store = self.get_store(self)
2676
self.assertEquals(False, store.is_loaded())
2677
store._load_from_string('')
2678
self.assertEquals(True, store.is_loaded())
2680
def test_get_no_sections_for_empty(self):
2681
store = self.get_store(self)
2682
store._load_from_string('')
2683
self.assertEquals([], list(store.get_sections()))
2685
def test_get_default_section(self):
2686
store = self.get_store(self)
2687
store._load_from_string('foo=bar')
2688
sections = list(store.get_sections())
2689
self.assertLength(1, sections)
2690
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2692
def test_get_named_section(self):
2693
store = self.get_store(self)
2694
store._load_from_string('[baz]\nfoo=bar')
2695
sections = list(store.get_sections())
2696
self.assertLength(1, sections)
2697
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2699
def test_load_from_string_fails_for_non_empty_store(self):
2700
store = self.get_store(self)
2701
store._load_from_string('foo=bar')
2702
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2705
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2706
"""Simulate loading a config store with content of various encodings.
2708
All files produced by bzr are in utf8 content.
2710
Users may modify them manually and end up with a file that can't be
2711
loaded. We need to issue proper error messages in this case.
2714
invalid_utf8_char = '\xff'
2716
def test_load_utf8(self):
2717
"""Ensure we can load an utf8-encoded file."""
2718
t = self.get_transport()
2719
# From http://pad.lv/799212
2720
unicode_user = u'b\N{Euro Sign}ar'
2721
unicode_content = u'user=%s' % (unicode_user,)
2722
utf8_content = unicode_content.encode('utf8')
2723
# Store the raw content in the config file
2724
t.put_bytes('foo.conf', utf8_content)
2725
store = config.IniFileStore(t, 'foo.conf')
2727
stack = config.Stack([store.get_sections], store)
2728
self.assertEquals(unicode_user, stack.get('user'))
2730
def test_load_non_ascii(self):
2731
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2732
t = self.get_transport()
2733
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2734
store = config.IniFileStore(t, 'foo.conf')
2735
self.assertRaises(errors.ConfigContentError, store.load)
2737
def test_load_erroneous_content(self):
2738
"""Ensure we display a proper error on content that can't be parsed."""
2739
t = self.get_transport()
2740
t.put_bytes('foo.conf', '[open_section\n')
2741
store = config.IniFileStore(t, 'foo.conf')
2742
self.assertRaises(errors.ParseConfigError, store.load)
2744
def test_load_permission_denied(self):
2745
"""Ensure we get warned when trying to load an inaccessible file."""
2748
warnings.append(args[0] % args[1:])
2749
self.overrideAttr(trace, 'warning', warning)
2751
t = self.get_transport()
2753
def get_bytes(relpath):
2754
raise errors.PermissionDenied(relpath, "")
2755
t.get_bytes = get_bytes
2756
store = config.IniFileStore(t, 'foo.conf')
2757
self.assertRaises(errors.PermissionDenied, store.load)
2760
[u'Permission denied while trying to load configuration store %s.'
2761
% store.external_url()])
2764
class TestIniConfigContent(tests.TestCaseWithTransport):
2765
"""Simulate loading a IniBasedConfig with content of various encodings.
2767
All files produced by bzr are in utf8 content.
2769
Users may modify them manually and end up with a file that can't be
2770
loaded. We need to issue proper error messages in this case.
2773
invalid_utf8_char = '\xff'
2775
def test_load_utf8(self):
2776
"""Ensure we can load an utf8-encoded file."""
2777
# From http://pad.lv/799212
2778
unicode_user = u'b\N{Euro Sign}ar'
2779
unicode_content = u'user=%s' % (unicode_user,)
2780
utf8_content = unicode_content.encode('utf8')
2781
# Store the raw content in the config file
2782
with open('foo.conf', 'wb') as f:
2783
f.write(utf8_content)
2784
conf = config.IniBasedConfig(file_name='foo.conf')
2785
self.assertEquals(unicode_user, conf.get_user_option('user'))
2787
def test_load_badly_encoded_content(self):
2788
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2789
with open('foo.conf', 'wb') as f:
2790
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2791
conf = config.IniBasedConfig(file_name='foo.conf')
2792
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2794
def test_load_erroneous_content(self):
2795
"""Ensure we display a proper error on content that can't be parsed."""
2796
with open('foo.conf', 'wb') as f:
2797
f.write('[open_section\n')
2798
conf = config.IniBasedConfig(file_name='foo.conf')
2799
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2802
class TestMutableStore(TestStore):
2804
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2805
in config.test_store_builder_registry.iteritems()]
2808
super(TestMutableStore, self).setUp()
2809
self.transport = self.get_transport()
2811
def has_store(self, store):
2812
store_basename = urlutils.relative_url(self.transport.external_url(),
2813
store.external_url())
2814
return self.transport.has(store_basename)
2816
def test_save_empty_creates_no_file(self):
2817
# FIXME: There should be a better way than relying on the test
2818
# parametrization to identify branch.conf -- vila 2011-0526
2819
if self.store_id in ('branch', 'remote_branch'):
2820
raise tests.TestNotApplicable(
2821
'branch.conf is *always* created when a branch is initialized')
2822
store = self.get_store(self)
2824
self.assertEquals(False, self.has_store(store))
2826
def test_save_emptied_succeeds(self):
2827
store = self.get_store(self)
2828
store._load_from_string('foo=bar\n')
2829
section = store.get_mutable_section(None)
2830
section.remove('foo')
2832
self.assertEquals(True, self.has_store(store))
2833
modified_store = self.get_store(self)
2834
sections = list(modified_store.get_sections())
2835
self.assertLength(0, sections)
2837
def test_save_with_content_succeeds(self):
2838
# FIXME: There should be a better way than relying on the test
2839
# parametrization to identify branch.conf -- vila 2011-0526
2840
if self.store_id in ('branch', 'remote_branch'):
2841
raise tests.TestNotApplicable(
2842
'branch.conf is *always* created when a branch is initialized')
2843
store = self.get_store(self)
2844
store._load_from_string('foo=bar\n')
2845
self.assertEquals(False, self.has_store(store))
2847
self.assertEquals(True, self.has_store(store))
2848
modified_store = self.get_store(self)
2849
sections = list(modified_store.get_sections())
2850
self.assertLength(1, sections)
2851
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2853
def test_set_option_in_empty_store(self):
2854
store = self.get_store(self)
2855
section = store.get_mutable_section(None)
2856
section.set('foo', 'bar')
2858
modified_store = self.get_store(self)
2859
sections = list(modified_store.get_sections())
2860
self.assertLength(1, sections)
2861
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2863
def test_set_option_in_default_section(self):
2864
store = self.get_store(self)
2865
store._load_from_string('')
2866
section = store.get_mutable_section(None)
2867
section.set('foo', 'bar')
2869
modified_store = self.get_store(self)
2870
sections = list(modified_store.get_sections())
2871
self.assertLength(1, sections)
2872
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2874
def test_set_option_in_named_section(self):
2875
store = self.get_store(self)
2876
store._load_from_string('')
2877
section = store.get_mutable_section('baz')
2878
section.set('foo', 'bar')
2880
modified_store = self.get_store(self)
2881
sections = list(modified_store.get_sections())
2882
self.assertLength(1, sections)
2883
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2885
def test_load_hook(self):
2886
# We first needs to ensure that the store exists
2887
store = self.get_store(self)
2888
section = store.get_mutable_section('baz')
2889
section.set('foo', 'bar')
2891
# Now we can try to load it
2892
store = self.get_store(self)
2896
config.ConfigHooks.install_named_hook('load', hook, None)
2897
self.assertLength(0, calls)
2899
self.assertLength(1, calls)
2900
self.assertEquals((store,), calls[0])
2902
def test_save_hook(self):
2906
config.ConfigHooks.install_named_hook('save', hook, None)
2907
self.assertLength(0, calls)
2908
store = self.get_store(self)
2909
section = store.get_mutable_section('baz')
2910
section.set('foo', 'bar')
2912
self.assertLength(1, calls)
2913
self.assertEquals((store,), calls[0])
2916
class TestIniFileStore(TestStore):
2918
def test_loading_unknown_file_fails(self):
2919
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2920
self.assertRaises(errors.NoSuchFile, store.load)
2922
def test_invalid_content(self):
2923
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2924
self.assertEquals(False, store.is_loaded())
2925
exc = self.assertRaises(
2926
errors.ParseConfigError, store._load_from_string,
2927
'this is invalid !')
2928
self.assertEndsWith(exc.filename, 'foo.conf')
2929
# And the load failed
2930
self.assertEquals(False, store.is_loaded())
2932
def test_get_embedded_sections(self):
2933
# A more complicated example (which also shows that section names and
2934
# option names share the same name space...)
2935
# FIXME: This should be fixed by forbidding dicts as values ?
2936
# -- vila 2011-04-05
2937
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2938
store._load_from_string('''
2942
foo_in_DEFAULT=foo_DEFAULT
2950
sections = list(store.get_sections())
2951
self.assertLength(4, sections)
2952
# The default section has no name.
2953
# List values are provided as strings and need to be explicitly
2954
# converted by specifying from_unicode=list_from_store at option
2956
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2958
self.assertSectionContent(
2959
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2960
self.assertSectionContent(
2961
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2962
# sub sections are provided as embedded dicts.
2963
self.assertSectionContent(
2964
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2968
class TestLockableIniFileStore(TestStore):
2970
def test_create_store_in_created_dir(self):
2971
self.assertPathDoesNotExist('dir')
2972
t = self.get_transport('dir/subdir')
2973
store = config.LockableIniFileStore(t, 'foo.conf')
2974
store.get_mutable_section(None).set('foo', 'bar')
2976
self.assertPathExists('dir/subdir')
2979
class TestConcurrentStoreUpdates(TestStore):
2980
"""Test that Stores properly handle conccurent updates.
2982
New Store implementation may fail some of these tests but until such
2983
implementations exist it's hard to properly filter them from the scenarios
2984
applied here. If you encounter such a case, contact the bzr devs.
2987
scenarios = [(key, {'get_stack': builder}) for key, builder
2988
in config.test_stack_builder_registry.iteritems()]
2991
super(TestConcurrentStoreUpdates, self).setUp()
2992
self.stack = self.get_stack(self)
2993
if not isinstance(self.stack, config._CompatibleStack):
2994
raise tests.TestNotApplicable(
2995
'%s is not meant to be compatible with the old config design'
2997
self.stack.set('one', '1')
2998
self.stack.set('two', '2')
3000
self.stack.store.save()
3002
def test_simple_read_access(self):
3003
self.assertEquals('1', self.stack.get('one'))
3005
def test_simple_write_access(self):
3006
self.stack.set('one', 'one')
3007
self.assertEquals('one', self.stack.get('one'))
3009
def test_listen_to_the_last_speaker(self):
3011
c2 = self.get_stack(self)
3012
c1.set('one', 'ONE')
3013
c2.set('two', 'TWO')
3014
self.assertEquals('ONE', c1.get('one'))
3015
self.assertEquals('TWO', c2.get('two'))
3016
# The second update respect the first one
3017
self.assertEquals('ONE', c2.get('one'))
3019
def test_last_speaker_wins(self):
3020
# If the same config is not shared, the same variable modified twice
3021
# can only see a single result.
3023
c2 = self.get_stack(self)
3026
self.assertEquals('c2', c2.get('one'))
3027
# The first modification is still available until another refresh
3029
self.assertEquals('c1', c1.get('one'))
3030
c1.set('two', 'done')
3031
self.assertEquals('c2', c1.get('one'))
3033
def test_writes_are_serialized(self):
3035
c2 = self.get_stack(self)
3037
# We spawn a thread that will pause *during* the config saving.
3038
before_writing = threading.Event()
3039
after_writing = threading.Event()
3040
writing_done = threading.Event()
3041
c1_save_without_locking_orig = c1.store.save_without_locking
3042
def c1_save_without_locking():
3043
before_writing.set()
3044
c1_save_without_locking_orig()
3045
# The lock is held. We wait for the main thread to decide when to
3047
after_writing.wait()
3048
c1.store.save_without_locking = c1_save_without_locking
3052
t1 = threading.Thread(target=c1_set)
3053
# Collect the thread after the test
3054
self.addCleanup(t1.join)
3055
# Be ready to unblock the thread if the test goes wrong
3056
self.addCleanup(after_writing.set)
3058
before_writing.wait()
3059
self.assertRaises(errors.LockContention,
3060
c2.set, 'one', 'c2')
3061
self.assertEquals('c1', c1.get('one'))
3062
# Let the lock be released
3066
self.assertEquals('c2', c2.get('one'))
3068
def test_read_while_writing(self):
3070
# We spawn a thread that will pause *during* the write
3071
ready_to_write = threading.Event()
3072
do_writing = threading.Event()
3073
writing_done = threading.Event()
3074
# We override the _save implementation so we know the store is locked
3075
c1_save_without_locking_orig = c1.store.save_without_locking
3076
def c1_save_without_locking():
3077
ready_to_write.set()
3078
# The lock is held. We wait for the main thread to decide when to
3081
c1_save_without_locking_orig()
3083
c1.store.save_without_locking = c1_save_without_locking
3086
t1 = threading.Thread(target=c1_set)
3087
# Collect the thread after the test
3088
self.addCleanup(t1.join)
3089
# Be ready to unblock the thread if the test goes wrong
3090
self.addCleanup(do_writing.set)
3092
# Ensure the thread is ready to write
3093
ready_to_write.wait()
3094
self.assertEquals('c1', c1.get('one'))
3095
# If we read during the write, we get the old value
3096
c2 = self.get_stack(self)
3097
self.assertEquals('1', c2.get('one'))
3098
# Let the writing occur and ensure it occurred
3101
# Now we get the updated value
3102
c3 = self.get_stack(self)
3103
self.assertEquals('c1', c3.get('one'))
3105
# FIXME: It may be worth looking into removing the lock dir when it's not
3106
# needed anymore and look at possible fallouts for concurrent lockers. This
3107
# will matter if/when we use config files outside of bazaar directories
3108
# (.bazaar or .bzr) -- vila 20110-04-111
3111
class TestSectionMatcher(TestStore):
3113
scenarios = [('location', {'matcher': config.LocationMatcher}),
3114
('id', {'matcher': config.NameMatcher}),]
3117
super(TestSectionMatcher, self).setUp()
3118
# Any simple store is good enough
3119
self.get_store = config.test_store_builder_registry.get('configobj')
3121
def test_no_matches_for_empty_stores(self):
3122
store = self.get_store(self)
3123
store._load_from_string('')
3124
matcher = self.matcher(store, '/bar')
3125
self.assertEquals([], list(matcher.get_sections()))
3127
def test_build_doesnt_load_store(self):
3128
store = self.get_store(self)
3129
matcher = self.matcher(store, '/bar')
3130
self.assertFalse(store.is_loaded())
3133
class TestLocationSection(tests.TestCase):
3135
def get_section(self, options, extra_path):
3136
section = config.Section('foo', options)
3137
# We don't care about the length so we use '0'
3138
return config.LocationSection(section, 0, extra_path)
3140
def test_simple_option(self):
3141
section = self.get_section({'foo': 'bar'}, '')
3142
self.assertEquals('bar', section.get('foo'))
3144
def test_option_with_extra_path(self):
3145
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3147
self.assertEquals('bar/baz', section.get('foo'))
3149
def test_invalid_policy(self):
3150
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3152
# invalid policies are ignored
3153
self.assertEquals('bar', section.get('foo'))
3156
class TestLocationMatcher(TestStore):
3159
super(TestLocationMatcher, self).setUp()
3160
# Any simple store is good enough
3161
self.get_store = config.test_store_builder_registry.get('configobj')
3163
def test_unrelated_section_excluded(self):
3164
store = self.get_store(self)
3165
store._load_from_string('''
3173
section=/foo/bar/baz
3177
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3179
[section.id for _, section in store.get_sections()])
3180
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3181
sections = [section for s, section in matcher.get_sections()]
3182
self.assertEquals([3, 2],
3183
[section.length for section in sections])
3184
self.assertEquals(['/foo/bar', '/foo'],
3185
[section.id for section in sections])
3186
self.assertEquals(['quux', 'bar/quux'],
3187
[section.extra_path for section in sections])
3189
def test_more_specific_sections_first(self):
3190
store = self.get_store(self)
3191
store._load_from_string('''
3197
self.assertEquals(['/foo', '/foo/bar'],
3198
[section.id for _, section in store.get_sections()])
3199
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3200
sections = [section for s, section in matcher.get_sections()]
3201
self.assertEquals([3, 2],
3202
[section.length for section in sections])
3203
self.assertEquals(['/foo/bar', '/foo'],
3204
[section.id for section in sections])
3205
self.assertEquals(['baz', 'bar/baz'],
3206
[section.extra_path for section in sections])
3208
def test_appendpath_in_no_name_section(self):
3209
# It's a bit weird to allow appendpath in a no-name section, but
3210
# someone may found a use for it
3211
store = self.get_store(self)
3212
store._load_from_string('''
3214
foo:policy = appendpath
3216
matcher = config.LocationMatcher(store, 'dir/subdir')
3217
sections = list(matcher.get_sections())
3218
self.assertLength(1, sections)
3219
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3221
def test_file_urls_are_normalized(self):
3222
store = self.get_store(self)
3223
if sys.platform == 'win32':
3224
expected_url = 'file:///C:/dir/subdir'
3225
expected_location = 'C:/dir/subdir'
3227
expected_url = 'file:///dir/subdir'
3228
expected_location = '/dir/subdir'
3229
matcher = config.LocationMatcher(store, expected_url)
3230
self.assertEquals(expected_location, matcher.location)
3233
class TestNameMatcher(TestStore):
3236
super(TestNameMatcher, self).setUp()
3237
self.matcher = config.NameMatcher
3238
# Any simple store is good enough
3239
self.get_store = config.test_store_builder_registry.get('configobj')
3241
def get_matching_sections(self, name):
3242
store = self.get_store(self)
3243
store._load_from_string('''
3251
matcher = self.matcher(store, name)
3252
return list(matcher.get_sections())
3254
def test_matching(self):
3255
sections = self.get_matching_sections('foo')
3256
self.assertLength(1, sections)
3257
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3259
def test_not_matching(self):
3260
sections = self.get_matching_sections('baz')
3261
self.assertLength(0, sections)
3264
class TestStackGet(tests.TestCase):
3266
# FIXME: This should be parametrized for all known Stack or dedicated
3267
# paramerized tests created to avoid bloating -- vila 2011-03-31
3269
def overrideOptionRegistry(self):
3270
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3272
def test_single_config_get(self):
3273
conf = dict(foo='bar')
3274
conf_stack = config.Stack([conf])
3275
self.assertEquals('bar', conf_stack.get('foo'))
3277
def test_get_with_registered_default_value(self):
3278
conf_stack = config.Stack([dict()])
3279
opt = config.Option('foo', default='bar')
3280
self.overrideOptionRegistry()
3281
config.option_registry.register('foo', opt)
3282
self.assertEquals('bar', conf_stack.get('foo'))
3284
def test_get_without_registered_default_value(self):
3285
conf_stack = config.Stack([dict()])
3286
opt = config.Option('foo')
3287
self.overrideOptionRegistry()
3288
config.option_registry.register('foo', opt)
3289
self.assertEquals(None, conf_stack.get('foo'))
3291
def test_get_without_default_value_for_not_registered(self):
3292
conf_stack = config.Stack([dict()])
3293
opt = config.Option('foo')
3294
self.overrideOptionRegistry()
3295
self.assertEquals(None, conf_stack.get('foo'))
3297
def test_get_first_definition(self):
3298
conf1 = dict(foo='bar')
3299
conf2 = dict(foo='baz')
3300
conf_stack = config.Stack([conf1, conf2])
3301
self.assertEquals('bar', conf_stack.get('foo'))
3303
def test_get_embedded_definition(self):
3304
conf1 = dict(yy='12')
3305
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3306
conf_stack = config.Stack([conf1, conf2])
3307
self.assertEquals('baz', conf_stack.get('foo'))
3309
def test_get_for_empty_section_callable(self):
3310
conf_stack = config.Stack([lambda : []])
3311
self.assertEquals(None, conf_stack.get('foo'))
3313
def test_get_for_broken_callable(self):
3314
# Trying to use and invalid callable raises an exception on first use
3315
conf_stack = config.Stack([lambda : object()])
3316
self.assertRaises(TypeError, conf_stack.get, 'foo')
3319
class TestStackWithTransport(tests.TestCaseWithTransport):
3321
scenarios = [(key, {'get_stack': builder}) for key, builder
3322
in config.test_stack_builder_registry.iteritems()]
3325
class TestConcreteStacks(TestStackWithTransport):
3327
def test_build_stack(self):
3328
# Just a smoke test to help debug builders
3329
stack = self.get_stack(self)
3332
class TestStackGet(TestStackWithTransport):
3335
super(TestStackGet, self).setUp()
3336
self.conf = self.get_stack(self)
3338
def test_get_for_empty_stack(self):
3339
self.assertEquals(None, self.conf.get('foo'))
3341
def test_get_hook(self):
3342
self.conf.set('foo', 'bar')
3346
config.ConfigHooks.install_named_hook('get', hook, None)
3347
self.assertLength(0, calls)
3348
value = self.conf.get('foo')
3349
self.assertEquals('bar', value)
3350
self.assertLength(1, calls)
3351
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3354
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3357
super(TestStackGetWithConverter, self).setUp()
3358
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3359
self.registry = config.option_registry
3360
# We just want a simple stack with a simple store so we can inject
3361
# whatever content the tests need without caring about what section
3362
# names are valid for a given store/stack.
3363
store = config.IniFileStore(self.get_transport(), 'foo.conf')
3364
self.conf = config.Stack([store.get_sections], store)
3366
def register_bool_option(self, name, default=None, default_from_env=None):
3367
b = config.Option(name, help='A boolean.',
3368
default=default, default_from_env=default_from_env,
3369
from_unicode=config.bool_from_store)
3370
self.registry.register(b)
3372
def test_get_default_bool_None(self):
3373
self.register_bool_option('foo')
3374
self.assertEquals(None, self.conf.get('foo'))
3376
def test_get_default_bool_True(self):
3377
self.register_bool_option('foo', u'True')
3378
self.assertEquals(True, self.conf.get('foo'))
3380
def test_get_default_bool_False(self):
3381
self.register_bool_option('foo', False)
3382
self.assertEquals(False, self.conf.get('foo'))
3384
def test_get_default_bool_False_as_string(self):
3385
self.register_bool_option('foo', u'False')
3386
self.assertEquals(False, self.conf.get('foo'))
3388
def test_get_default_bool_from_env_converted(self):
3389
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3390
self.overrideEnv('FOO', 'False')
3391
self.assertEquals(False, self.conf.get('foo'))
3393
def test_get_default_bool_when_conversion_fails(self):
3394
self.register_bool_option('foo', default='True')
3395
self.conf.store._load_from_string('foo=invalid boolean')
3396
self.assertEquals(True, self.conf.get('foo'))
3398
def register_integer_option(self, name,
3399
default=None, default_from_env=None):
3400
i = config.Option(name, help='An integer.',
3401
default=default, default_from_env=default_from_env,
3402
from_unicode=config.int_from_store)
3403
self.registry.register(i)
3405
def test_get_default_integer_None(self):
3406
self.register_integer_option('foo')
3407
self.assertEquals(None, self.conf.get('foo'))
3409
def test_get_default_integer(self):
3410
self.register_integer_option('foo', 42)
3411
self.assertEquals(42, self.conf.get('foo'))
3413
def test_get_default_integer_as_string(self):
3414
self.register_integer_option('foo', u'42')
3415
self.assertEquals(42, self.conf.get('foo'))
3417
def test_get_default_integer_from_env(self):
3418
self.register_integer_option('foo', default_from_env=['FOO'])
3419
self.overrideEnv('FOO', '18')
3420
self.assertEquals(18, self.conf.get('foo'))
3422
def test_get_default_integer_when_conversion_fails(self):
3423
self.register_integer_option('foo', default='12')
3424
self.conf.store._load_from_string('foo=invalid integer')
3425
self.assertEquals(12, self.conf.get('foo'))
3427
def register_list_option(self, name, default=None, default_from_env=None):
3428
l = config.Option(name, help='A list.',
3429
default=default, default_from_env=default_from_env,
3430
from_unicode=config.list_from_store)
3431
self.registry.register(l)
3433
def test_get_default_list_None(self):
3434
self.register_list_option('foo')
3435
self.assertEquals(None, self.conf.get('foo'))
3437
def test_get_default_list_empty(self):
3438
self.register_list_option('foo', '')
3439
self.assertEquals([], self.conf.get('foo'))
3441
def test_get_default_list_from_env(self):
3442
self.register_list_option('foo', default_from_env=['FOO'])
3443
self.overrideEnv('FOO', '')
3444
self.assertEquals([], self.conf.get('foo'))
3446
def test_get_with_list_converter_no_item(self):
3447
self.register_list_option('foo', None)
3448
self.conf.store._load_from_string('foo=,')
3449
self.assertEquals([], self.conf.get('foo'))
3451
def test_get_with_list_converter_many_items(self):
3452
self.register_list_option('foo', None)
3453
self.conf.store._load_from_string('foo=m,o,r,e')
3454
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3456
def test_get_with_list_converter_embedded_spaces_many_items(self):
3457
self.register_list_option('foo', None)
3458
self.conf.store._load_from_string('foo=" bar", "baz "')
3459
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3461
def test_get_with_list_converter_stripped_spaces_many_items(self):
3462
self.register_list_option('foo', None)
3463
self.conf.store._load_from_string('foo= bar , baz ')
3464
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3467
class TestIterOptionRefs(tests.TestCase):
3468
"""iter_option_refs is a bit unusual, document some cases."""
3470
def assertRefs(self, expected, string):
3471
self.assertEquals(expected, list(config.iter_option_refs(string)))
3473
def test_empty(self):
3474
self.assertRefs([(False, '')], '')
3476
def test_no_refs(self):
3477
self.assertRefs([(False, 'foo bar')], 'foo bar')
3479
def test_single_ref(self):
3480
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3482
def test_broken_ref(self):
3483
self.assertRefs([(False, '{foo')], '{foo')
3485
def test_embedded_ref(self):
3486
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3489
def test_two_refs(self):
3490
self.assertRefs([(False, ''), (True, '{foo}'),
3491
(False, ''), (True, '{bar}'),
3496
class TestStackExpandOptions(tests.TestCaseWithTransport):
3499
super(TestStackExpandOptions, self).setUp()
3500
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3501
self.registry = config.option_registry
3502
self.conf = build_branch_stack(self)
3504
def assertExpansion(self, expected, string, env=None):
3505
self.assertEquals(expected, self.conf.expand_options(string, env))
3507
def test_no_expansion(self):
3508
self.assertExpansion('foo', 'foo')
3510
def test_expand_default_value(self):
3511
self.conf.store._load_from_string('bar=baz')
3512
self.registry.register(config.Option('foo', default=u'{bar}'))
3513
self.assertEquals('baz', self.conf.get('foo', expand=True))
3515
def test_expand_default_from_env(self):
3516
self.conf.store._load_from_string('bar=baz')
3517
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3518
self.overrideEnv('FOO', '{bar}')
3519
self.assertEquals('baz', self.conf.get('foo', expand=True))
3521
def test_expand_default_on_failed_conversion(self):
3522
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3523
self.registry.register(
3524
config.Option('foo', default=u'{bar}',
3525
from_unicode=config.int_from_store))
3526
self.assertEquals(42, self.conf.get('foo', expand=True))
3528
def test_env_adding_options(self):
3529
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3531
def test_env_overriding_options(self):
3532
self.conf.store._load_from_string('foo=baz')
3533
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3535
def test_simple_ref(self):
3536
self.conf.store._load_from_string('foo=xxx')
3537
self.assertExpansion('xxx', '{foo}')
3539
def test_unknown_ref(self):
3540
self.assertRaises(errors.ExpandingUnknownOption,
3541
self.conf.expand_options, '{foo}')
3543
def test_indirect_ref(self):
3544
self.conf.store._load_from_string('''
3548
self.assertExpansion('xxx', '{bar}')
3550
def test_embedded_ref(self):
3551
self.conf.store._load_from_string('''
3555
self.assertExpansion('xxx', '{{bar}}')
3557
def test_simple_loop(self):
3558
self.conf.store._load_from_string('foo={foo}')
3559
self.assertRaises(errors.OptionExpansionLoop,
3560
self.conf.expand_options, '{foo}')
3562
def test_indirect_loop(self):
3563
self.conf.store._load_from_string('''
3567
e = self.assertRaises(errors.OptionExpansionLoop,
3568
self.conf.expand_options, '{foo}')
3569
self.assertEquals('foo->bar->baz', e.refs)
3570
self.assertEquals('{foo}', e.string)
3572
def test_list(self):
3573
self.conf.store._load_from_string('''
3577
list={foo},{bar},{baz}
3579
self.registry.register(
3580
config.Option('list', from_unicode=config.list_from_store))
3581
self.assertEquals(['start', 'middle', 'end'],
3582
self.conf.get('list', expand=True))
3584
def test_cascading_list(self):
3585
self.conf.store._load_from_string('''
3591
self.registry.register(
3592
config.Option('list', from_unicode=config.list_from_store))
3593
self.assertEquals(['start', 'middle', 'end'],
3594
self.conf.get('list', expand=True))
3596
def test_pathologically_hidden_list(self):
3597
self.conf.store._load_from_string('''
3603
hidden={start}{middle}{end}
3605
# What matters is what the registration says, the conversion happens
3606
# only after all expansions have been performed
3607
self.registry.register(
3608
config.Option('hidden', from_unicode=config.list_from_store))
3609
self.assertEquals(['bin', 'go'],
3610
self.conf.get('hidden', expand=True))
3613
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3616
super(TestStackCrossSectionsExpand, self).setUp()
3618
def get_config(self, location, string):
3621
# Since we don't save the config we won't strictly require to inherit
3622
# from TestCaseInTempDir, but an error occurs so quickly...
3623
c = config.LocationStack(location)
3624
c.store._load_from_string(string)
3627
def test_dont_cross_unrelated_section(self):
3628
c = self.get_config('/another/branch/path','''
3633
[/another/branch/path]
3636
self.assertRaises(errors.ExpandingUnknownOption,
3637
c.get, 'bar', expand=True)
3639
def test_cross_related_sections(self):
3640
c = self.get_config('/project/branch/path','''
3644
[/project/branch/path]
3647
self.assertEquals('quux', c.get('bar', expand=True))
3650
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3652
def test_cross_global_locations(self):
3653
l_store = config.LocationStore()
3654
l_store._load_from_string('''
3660
g_store = config.GlobalStore()
3661
g_store._load_from_string('''
3667
stack = config.LocationStack('/branch')
3668
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3669
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3672
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3674
def test_expand_relpath_locally(self):
3675
l_store = config.LocationStore()
3676
l_store._load_from_string('''
3677
[/home/user/project]
3678
lfoo = loc-foo/{relpath}
3681
stack = config.LocationStack('/home/user/project/branch')
3682
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3684
def test_expand_relpath_unknonw_in_global(self):
3685
g_store = config.GlobalStore()
3686
g_store._load_from_string('''
3691
stack = config.LocationStack('/home/user/project/branch')
3692
self.assertRaises(errors.ExpandingUnknownOption,
3693
stack.get, 'gfoo', expand=True)
3695
def test_expand_local_option_locally(self):
3696
l_store = config.LocationStore()
3697
l_store._load_from_string('''
3698
[/home/user/project]
3699
lfoo = loc-foo/{relpath}
3703
g_store = config.GlobalStore()
3704
g_store._load_from_string('''
3710
stack = config.LocationStack('/home/user/project/branch')
3711
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3712
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3714
def test_locals_dont_leak(self):
3715
"""Make sure we chose the right local in presence of several sections.
3717
l_store = config.LocationStore()
3718
l_store._load_from_string('''
3720
lfoo = loc-foo/{relpath}
3721
[/home/user/project]
3722
lfoo = loc-foo/{relpath}
3725
stack = config.LocationStack('/home/user/project/branch')
3726
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3727
stack = config.LocationStack('/home/user/bar/baz')
3728
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3732
class TestStackSet(TestStackWithTransport):
3734
def test_simple_set(self):
3735
conf = self.get_stack(self)
3736
self.assertEquals(None, conf.get('foo'))
3737
conf.set('foo', 'baz')
3738
# Did we get it back ?
3739
self.assertEquals('baz', conf.get('foo'))
3741
def test_set_creates_a_new_section(self):
3742
conf = self.get_stack(self)
3743
conf.set('foo', 'baz')
3744
self.assertEquals, 'baz', conf.get('foo')
3746
def test_set_hook(self):
3750
config.ConfigHooks.install_named_hook('set', hook, None)
3751
self.assertLength(0, calls)
3752
conf = self.get_stack(self)
3753
conf.set('foo', 'bar')
3754
self.assertLength(1, calls)
3755
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3758
class TestStackRemove(TestStackWithTransport):
3760
def test_remove_existing(self):
3761
conf = self.get_stack(self)
3762
conf.set('foo', 'bar')
3763
self.assertEquals('bar', conf.get('foo'))
3765
# Did we get it back ?
3766
self.assertEquals(None, conf.get('foo'))
3768
def test_remove_unknown(self):
3769
conf = self.get_stack(self)
3770
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3772
def test_remove_hook(self):
3776
config.ConfigHooks.install_named_hook('remove', hook, None)
3777
self.assertLength(0, calls)
3778
conf = self.get_stack(self)
3779
conf.set('foo', 'bar')
3781
self.assertLength(1, calls)
3782
self.assertEquals((conf, 'foo'), calls[0])
3785
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3788
super(TestConfigGetOptions, self).setUp()
3789
create_configs(self)
3791
def test_no_variable(self):
3792
# Using branch should query branch, locations and bazaar
3793
self.assertOptions([], self.branch_config)
3795
def test_option_in_bazaar(self):
3796
self.bazaar_config.set_user_option('file', 'bazaar')
3797
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3800
def test_option_in_locations(self):
3801
self.locations_config.set_user_option('file', 'locations')
3803
[('file', 'locations', self.tree.basedir, 'locations')],
3804
self.locations_config)
3806
def test_option_in_branch(self):
3807
self.branch_config.set_user_option('file', 'branch')
3808
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3811
def test_option_in_bazaar_and_branch(self):
3812
self.bazaar_config.set_user_option('file', 'bazaar')
3813
self.branch_config.set_user_option('file', 'branch')
3814
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3815
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3818
def test_option_in_branch_and_locations(self):
3819
# Hmm, locations override branch :-/
3820
self.locations_config.set_user_option('file', 'locations')
3821
self.branch_config.set_user_option('file', 'branch')
3823
[('file', 'locations', self.tree.basedir, 'locations'),
3824
('file', 'branch', 'DEFAULT', 'branch'),],
3827
def test_option_in_bazaar_locations_and_branch(self):
3828
self.bazaar_config.set_user_option('file', 'bazaar')
3829
self.locations_config.set_user_option('file', 'locations')
3830
self.branch_config.set_user_option('file', 'branch')
3832
[('file', 'locations', self.tree.basedir, 'locations'),
3833
('file', 'branch', 'DEFAULT', 'branch'),
3834
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3838
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3841
super(TestConfigRemoveOption, self).setUp()
3842
create_configs_with_file_option(self)
3844
def test_remove_in_locations(self):
3845
self.locations_config.remove_user_option('file', self.tree.basedir)
3847
[('file', 'branch', 'DEFAULT', 'branch'),
3848
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3851
def test_remove_in_branch(self):
3852
self.branch_config.remove_user_option('file')
3854
[('file', 'locations', self.tree.basedir, 'locations'),
3855
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3858
def test_remove_in_bazaar(self):
3859
self.bazaar_config.remove_user_option('file')
3861
[('file', 'locations', self.tree.basedir, 'locations'),
3862
('file', 'branch', 'DEFAULT', 'branch'),],
3866
class TestConfigGetSections(tests.TestCaseWithTransport):
3869
super(TestConfigGetSections, self).setUp()
3870
create_configs(self)
3872
def assertSectionNames(self, expected, conf, name=None):
3873
"""Check which sections are returned for a given config.
3875
If fallback configurations exist their sections can be included.
3877
:param expected: A list of section names.
3879
:param conf: The configuration that will be queried.
3881
:param name: An optional section name that will be passed to
3884
sections = list(conf._get_sections(name))
3885
self.assertLength(len(expected), sections)
3886
self.assertEqual(expected, [name for name, _, _ in sections])
3888
def test_bazaar_default_section(self):
3889
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3891
def test_locations_default_section(self):
3892
# No sections are defined in an empty file
3893
self.assertSectionNames([], self.locations_config)
3895
def test_locations_named_section(self):
3896
self.locations_config.set_user_option('file', 'locations')
3897
self.assertSectionNames([self.tree.basedir], self.locations_config)
3899
def test_locations_matching_sections(self):
3900
loc_config = self.locations_config
3901
loc_config.set_user_option('file', 'locations')
3902
# We need to cheat a bit here to create an option in sections above and
3903
# below the 'location' one.
3904
parser = loc_config._get_parser()
3905
# locations.cong deals with '/' ignoring native os.sep
3906
location_names = self.tree.basedir.split('/')
3907
parent = '/'.join(location_names[:-1])
3908
child = '/'.join(location_names + ['child'])
3910
parser[parent]['file'] = 'parent'
3912
parser[child]['file'] = 'child'
3913
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3915
def test_branch_data_default_section(self):
3916
self.assertSectionNames([None],
3917
self.branch_config._get_branch_data_config())
3919
def test_branch_default_sections(self):
3920
# No sections are defined in an empty locations file
3921
self.assertSectionNames([None, 'DEFAULT'],
3923
# Unless we define an option
3924
self.branch_config._get_location_config().set_user_option(
3925
'file', 'locations')
3926
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3929
def test_bazaar_named_section(self):
3930
# We need to cheat as the API doesn't give direct access to sections
3931
# other than DEFAULT.
3932
self.bazaar_config.set_alias('bazaar', 'bzr')
3933
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
3936
class TestAuthenticationConfigFile(tests.TestCase):
1316
3937
"""Test the authentication.conf file matching"""