367
588
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
591
class TestXDGConfigDir(tests.TestCaseInTempDir):
592
# must be in temp dir because config tests for the existence of the bazaar
593
# subdirectory of $XDG_CONFIG_HOME
596
if sys.platform in ('darwin', 'win32'):
597
raise tests.TestNotApplicable(
598
'XDG config dir not used on this platform')
599
super(TestXDGConfigDir, self).setUp()
600
self.overrideEnv('HOME', self.test_home_dir)
601
# BZR_HOME overrides everything we want to test so unset it.
602
self.overrideEnv('BZR_HOME', None)
604
def test_xdg_config_dir_exists(self):
605
"""When ~/.config/bazaar exists, use it as the config dir."""
606
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
608
self.assertEqual(config.config_dir(), newdir)
610
def test_xdg_config_home(self):
611
"""When XDG_CONFIG_HOME is set, use it."""
612
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
613
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
614
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
616
self.assertEqual(config.config_dir(), newdir)
619
class TestIniConfig(tests.TestCaseInTempDir):
372
621
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
622
conf = config.IniBasedConfig.from_string(s)
623
return conf, conf._get_parser()
378
626
class TestIniConfigBuilding(TestIniConfig):
380
628
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
629
my_config = config.IniBasedConfig()
383
631
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))
632
my_config = config.IniBasedConfig.from_string(sample_config_text)
633
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
635
def test_cached(self):
636
my_config = config.IniBasedConfig.from_string(sample_config_text)
637
parser = my_config._get_parser()
638
self.assertTrue(my_config._get_parser() is parser)
640
def _dummy_chown(self, path, uid, gid):
641
self.path, self.uid, self.gid = path, uid, gid
643
def test_ini_config_ownership(self):
644
"""Ensure that chown is happening during _write_config_file"""
645
self.requireFeature(features.chown_feature)
646
self.overrideAttr(os, 'chown', self._dummy_chown)
647
self.path = self.uid = self.gid = None
648
conf = config.IniBasedConfig(file_name='./foo.conf')
649
conf._write_config_file()
650
self.assertEquals(self.path, './foo.conf')
651
self.assertTrue(isinstance(self.uid, int))
652
self.assertTrue(isinstance(self.gid, int))
654
def test_get_filename_parameter_is_deprecated_(self):
655
conf = self.callDeprecated([
656
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
657
' Use file_name instead.'],
658
config.IniBasedConfig, lambda: 'ini.conf')
659
self.assertEqual('ini.conf', conf.file_name)
661
def test_get_parser_file_parameter_is_deprecated_(self):
391
662
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)
663
conf = config.IniBasedConfig.from_string(sample_config_text)
664
conf = self.callDeprecated([
665
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
666
' Use IniBasedConfig(_content=xxx) instead.'],
667
conf._get_parser, file=config_file)
670
class TestIniConfigSaving(tests.TestCaseInTempDir):
672
def test_cant_save_without_a_file_name(self):
673
conf = config.IniBasedConfig()
674
self.assertRaises(AssertionError, conf._write_config_file)
676
def test_saved_with_content(self):
677
content = 'foo = bar\n'
678
conf = config.IniBasedConfig.from_string(
679
content, file_name='./test.conf', save=True)
680
self.assertFileEqual(content, 'test.conf')
683
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
684
"""What is the default value of expand for config options.
686
This is an opt-in beta feature used to evaluate whether or not option
687
references can appear in dangerous place raising exceptions, disapearing
688
(and as such corrupting data) or if it's safe to activate the option by
691
Note that these tests relies on config._expand_default_value being already
692
overwritten in the parent class setUp.
696
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
700
self.warnings.append(args[0] % args[1:])
701
self.overrideAttr(trace, 'warning', warning)
703
def get_config(self, expand):
704
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
708
def assertExpandIs(self, expected):
709
actual = config._get_expand_default_value()
710
#self.config.get_user_option_as_bool('bzr.config.expand')
711
self.assertEquals(expected, actual)
713
def test_default_is_None(self):
714
self.assertEquals(None, config._expand_default_value)
716
def test_default_is_False_even_if_None(self):
717
self.config = self.get_config(None)
718
self.assertExpandIs(False)
720
def test_default_is_False_even_if_invalid(self):
721
self.config = self.get_config('<your choice>')
722
self.assertExpandIs(False)
724
# Huh ? My choice is False ? Thanks, always happy to hear that :D
725
# Wait, you've been warned !
726
self.assertLength(1, self.warnings)
728
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
731
def test_default_is_True(self):
732
self.config = self.get_config(True)
733
self.assertExpandIs(True)
735
def test_default_is_False(self):
736
self.config = self.get_config(False)
737
self.assertExpandIs(False)
740
class TestIniConfigOptionExpansion(tests.TestCase):
741
"""Test option expansion from the IniConfig level.
743
What we really want here is to test the Config level, but the class being
744
abstract as far as storing values is concerned, this can't be done
747
# FIXME: This should be rewritten when all configs share a storage
748
# implementation -- vila 2011-02-18
750
def get_config(self, string=None):
753
c = config.IniBasedConfig.from_string(string)
756
def assertExpansion(self, expected, conf, string, env=None):
757
self.assertEquals(expected, conf.expand_options(string, env))
759
def test_no_expansion(self):
760
c = self.get_config('')
761
self.assertExpansion('foo', c, 'foo')
763
def test_env_adding_options(self):
764
c = self.get_config('')
765
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
767
def test_env_overriding_options(self):
768
c = self.get_config('foo=baz')
769
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
771
def test_simple_ref(self):
772
c = self.get_config('foo=xxx')
773
self.assertExpansion('xxx', c, '{foo}')
775
def test_unknown_ref(self):
776
c = self.get_config('')
777
self.assertRaises(errors.ExpandingUnknownOption,
778
c.expand_options, '{foo}')
780
def test_indirect_ref(self):
781
c = self.get_config('''
785
self.assertExpansion('xxx', c, '{bar}')
787
def test_embedded_ref(self):
788
c = self.get_config('''
792
self.assertExpansion('xxx', c, '{{bar}}')
794
def test_simple_loop(self):
795
c = self.get_config('foo={foo}')
796
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
798
def test_indirect_loop(self):
799
c = self.get_config('''
803
e = self.assertRaises(errors.OptionExpansionLoop,
804
c.expand_options, '{foo}')
805
self.assertEquals('foo->bar->baz', e.refs)
806
self.assertEquals('{foo}', e.string)
809
conf = self.get_config('''
813
list={foo},{bar},{baz}
815
self.assertEquals(['start', 'middle', 'end'],
816
conf.get_user_option('list', expand=True))
818
def test_cascading_list(self):
819
conf = self.get_config('''
825
self.assertEquals(['start', 'middle', 'end'],
826
conf.get_user_option('list', expand=True))
828
def test_pathological_hidden_list(self):
829
conf = self.get_config('''
835
hidden={start}{middle}{end}
837
# Nope, it's either a string or a list, and the list wins as soon as a
838
# ',' appears, so the string concatenation never occur.
839
self.assertEquals(['{foo', '}', '{', 'bar}'],
840
conf.get_user_option('hidden', expand=True))
842
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
844
def get_config(self, location, string=None):
847
# Since we don't save the config we won't strictly require to inherit
848
# from TestCaseInTempDir, but an error occurs so quickly...
849
c = config.LocationConfig.from_string(string, location)
852
def test_dont_cross_unrelated_section(self):
853
c = self.get_config('/another/branch/path','''
858
[/another/branch/path]
861
self.assertRaises(errors.ExpandingUnknownOption,
862
c.get_user_option, 'bar', expand=True)
864
def test_cross_related_sections(self):
865
c = self.get_config('/project/branch/path','''
869
[/project/branch/path]
872
self.assertEquals('quux', c.get_user_option('bar', expand=True))
875
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
877
def test_cannot_reload_without_name(self):
878
conf = config.IniBasedConfig.from_string(sample_config_text)
879
self.assertRaises(AssertionError, conf.reload)
881
def test_reload_see_new_value(self):
882
c1 = config.IniBasedConfig.from_string('editor=vim\n',
883
file_name='./test/conf')
884
c1._write_config_file()
885
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
886
file_name='./test/conf')
887
c2._write_config_file()
888
self.assertEqual('vim', c1.get_user_option('editor'))
889
self.assertEqual('emacs', c2.get_user_option('editor'))
890
# Make sure we get the Right value
892
self.assertEqual('emacs', c1.get_user_option('editor'))
895
class TestLockableConfig(tests.TestCaseInTempDir):
897
scenarios = lockable_config_scenarios()
902
config_section = None
905
super(TestLockableConfig, self).setUp()
906
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
907
self.config = self.create_config(self._content)
909
def get_existing_config(self):
910
return self.config_class(*self.config_args)
912
def create_config(self, content):
913
kwargs = dict(save=True)
914
c = self.config_class.from_string(content, *self.config_args, **kwargs)
917
def test_simple_read_access(self):
918
self.assertEquals('1', self.config.get_user_option('one'))
920
def test_simple_write_access(self):
921
self.config.set_user_option('one', 'one')
922
self.assertEquals('one', self.config.get_user_option('one'))
924
def test_listen_to_the_last_speaker(self):
926
c2 = self.get_existing_config()
927
c1.set_user_option('one', 'ONE')
928
c2.set_user_option('two', 'TWO')
929
self.assertEquals('ONE', c1.get_user_option('one'))
930
self.assertEquals('TWO', c2.get_user_option('two'))
931
# The second update respect the first one
932
self.assertEquals('ONE', c2.get_user_option('one'))
934
def test_last_speaker_wins(self):
935
# If the same config is not shared, the same variable modified twice
936
# can only see a single result.
938
c2 = self.get_existing_config()
939
c1.set_user_option('one', 'c1')
940
c2.set_user_option('one', 'c2')
941
self.assertEquals('c2', c2._get_user_option('one'))
942
# The first modification is still available until another refresh
944
self.assertEquals('c1', c1._get_user_option('one'))
945
c1.set_user_option('two', 'done')
946
self.assertEquals('c2', c1._get_user_option('one'))
948
def test_writes_are_serialized(self):
950
c2 = self.get_existing_config()
952
# We spawn a thread that will pause *during* the write
953
before_writing = threading.Event()
954
after_writing = threading.Event()
955
writing_done = threading.Event()
956
c1_orig = c1._write_config_file
957
def c1_write_config_file():
960
# The lock is held. We wait for the main thread to decide when to
963
c1._write_config_file = c1_write_config_file
965
c1.set_user_option('one', 'c1')
967
t1 = threading.Thread(target=c1_set_option)
968
# Collect the thread after the test
969
self.addCleanup(t1.join)
970
# Be ready to unblock the thread if the test goes wrong
971
self.addCleanup(after_writing.set)
973
before_writing.wait()
974
self.assertTrue(c1._lock.is_held)
975
self.assertRaises(errors.LockContention,
976
c2.set_user_option, 'one', 'c2')
977
self.assertEquals('c1', c1.get_user_option('one'))
978
# Let the lock be released
981
c2.set_user_option('one', 'c2')
982
self.assertEquals('c2', c2.get_user_option('one'))
984
def test_read_while_writing(self):
986
# We spawn a thread that will pause *during* the write
987
ready_to_write = threading.Event()
988
do_writing = threading.Event()
989
writing_done = threading.Event()
990
c1_orig = c1._write_config_file
991
def c1_write_config_file():
993
# The lock is held. We wait for the main thread to decide when to
998
c1._write_config_file = c1_write_config_file
1000
c1.set_user_option('one', 'c1')
1001
t1 = threading.Thread(target=c1_set_option)
1002
# Collect the thread after the test
1003
self.addCleanup(t1.join)
1004
# Be ready to unblock the thread if the test goes wrong
1005
self.addCleanup(do_writing.set)
1007
# Ensure the thread is ready to write
1008
ready_to_write.wait()
1009
self.assertTrue(c1._lock.is_held)
1010
self.assertEquals('c1', c1.get_user_option('one'))
1011
# If we read during the write, we get the old value
1012
c2 = self.get_existing_config()
1013
self.assertEquals('1', c2.get_user_option('one'))
1014
# Let the writing occur and ensure it occurred
1017
# Now we get the updated value
1018
c3 = self.get_existing_config()
1019
self.assertEquals('c1', c3.get_user_option('one'))
397
1022
class TestGetUserOptionAs(TestIniConfig):
1312
2006
self.assertIs(None, bzrdir_config.get_default_stack_on())
2009
class TestOldConfigHooks(tests.TestCaseWithTransport):
2012
super(TestOldConfigHooks, self).setUp()
2013
create_configs_with_file_option(self)
2015
def assertGetHook(self, conf, name, value):
2019
config.OldConfigHooks.install_named_hook('get', hook, None)
2021
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2022
self.assertLength(0, calls)
2023
actual_value = conf.get_user_option(name)
2024
self.assertEquals(value, actual_value)
2025
self.assertLength(1, calls)
2026
self.assertEquals((conf, name, value), calls[0])
2028
def test_get_hook_bazaar(self):
2029
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2031
def test_get_hook_locations(self):
2032
self.assertGetHook(self.locations_config, 'file', 'locations')
2034
def test_get_hook_branch(self):
2035
# Since locations masks branch, we define a different option
2036
self.branch_config.set_user_option('file2', 'branch')
2037
self.assertGetHook(self.branch_config, 'file2', 'branch')
2039
def assertSetHook(self, conf, name, value):
2043
config.OldConfigHooks.install_named_hook('set', hook, None)
2045
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2046
self.assertLength(0, calls)
2047
conf.set_user_option(name, value)
2048
self.assertLength(1, calls)
2049
# We can't assert the conf object below as different configs use
2050
# different means to implement set_user_option and we care only about
2052
self.assertEquals((name, value), calls[0][1:])
2054
def test_set_hook_bazaar(self):
2055
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2057
def test_set_hook_locations(self):
2058
self.assertSetHook(self.locations_config, 'foo', 'locations')
2060
def test_set_hook_branch(self):
2061
self.assertSetHook(self.branch_config, 'foo', 'branch')
2063
def assertRemoveHook(self, conf, name, section_name=None):
2067
config.OldConfigHooks.install_named_hook('remove', hook, None)
2069
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2070
self.assertLength(0, calls)
2071
conf.remove_user_option(name, section_name)
2072
self.assertLength(1, calls)
2073
# We can't assert the conf object below as different configs use
2074
# different means to implement remove_user_option and we care only about
2076
self.assertEquals((name,), calls[0][1:])
2078
def test_remove_hook_bazaar(self):
2079
self.assertRemoveHook(self.bazaar_config, 'file')
2081
def test_remove_hook_locations(self):
2082
self.assertRemoveHook(self.locations_config, 'file',
2083
self.locations_config.location)
2085
def test_remove_hook_branch(self):
2086
self.assertRemoveHook(self.branch_config, 'file')
2088
def assertLoadHook(self, name, conf_class, *conf_args):
2092
config.OldConfigHooks.install_named_hook('load', hook, None)
2094
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2095
self.assertLength(0, calls)
2097
conf = conf_class(*conf_args)
2098
# Access an option to trigger a load
2099
conf.get_user_option(name)
2100
self.assertLength(1, calls)
2101
# Since we can't assert about conf, we just use the number of calls ;-/
2103
def test_load_hook_bazaar(self):
2104
self.assertLoadHook('file', config.GlobalConfig)
2106
def test_load_hook_locations(self):
2107
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2109
def test_load_hook_branch(self):
2110
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2112
def assertSaveHook(self, conf):
2116
config.OldConfigHooks.install_named_hook('save', hook, None)
2118
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2119
self.assertLength(0, calls)
2120
# Setting an option triggers a save
2121
conf.set_user_option('foo', 'bar')
2122
self.assertLength(1, calls)
2123
# Since we can't assert about conf, we just use the number of calls ;-/
2125
def test_save_hook_bazaar(self):
2126
self.assertSaveHook(self.bazaar_config)
2128
def test_save_hook_locations(self):
2129
self.assertSaveHook(self.locations_config)
2131
def test_save_hook_branch(self):
2132
self.assertSaveHook(self.branch_config)
2135
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2136
"""Tests config hooks for remote configs.
2138
No tests for the remove hook as this is not implemented there.
2142
super(TestOldConfigHooksForRemote, self).setUp()
2143
self.transport_server = test_server.SmartTCPServer_for_testing
2144
create_configs_with_file_option(self)
2146
def assertGetHook(self, conf, name, value):
2150
config.OldConfigHooks.install_named_hook('get', hook, None)
2152
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2153
self.assertLength(0, calls)
2154
actual_value = conf.get_option(name)
2155
self.assertEquals(value, actual_value)
2156
self.assertLength(1, calls)
2157
self.assertEquals((conf, name, value), calls[0])
2159
def test_get_hook_remote_branch(self):
2160
remote_branch = branch.Branch.open(self.get_url('tree'))
2161
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2163
def test_get_hook_remote_bzrdir(self):
2164
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2165
conf = remote_bzrdir._get_config()
2166
conf.set_option('remotedir', 'file')
2167
self.assertGetHook(conf, 'file', 'remotedir')
2169
def assertSetHook(self, conf, name, value):
2173
config.OldConfigHooks.install_named_hook('set', hook, None)
2175
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2176
self.assertLength(0, calls)
2177
conf.set_option(value, name)
2178
self.assertLength(1, calls)
2179
# We can't assert the conf object below as different configs use
2180
# different means to implement set_user_option and we care only about
2182
self.assertEquals((name, value), calls[0][1:])
2184
def test_set_hook_remote_branch(self):
2185
remote_branch = branch.Branch.open(self.get_url('tree'))
2186
self.addCleanup(remote_branch.lock_write().unlock)
2187
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2189
def test_set_hook_remote_bzrdir(self):
2190
remote_branch = branch.Branch.open(self.get_url('tree'))
2191
self.addCleanup(remote_branch.lock_write().unlock)
2192
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2193
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2195
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2199
config.OldConfigHooks.install_named_hook('load', hook, None)
2201
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2202
self.assertLength(0, calls)
2204
conf = conf_class(*conf_args)
2205
# Access an option to trigger a load
2206
conf.get_option(name)
2207
self.assertLength(expected_nb_calls, calls)
2208
# Since we can't assert about conf, we just use the number of calls ;-/
2210
def test_load_hook_remote_branch(self):
2211
remote_branch = branch.Branch.open(self.get_url('tree'))
2212
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2214
def test_load_hook_remote_bzrdir(self):
2215
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2216
# The config file doesn't exist, set an option to force its creation
2217
conf = remote_bzrdir._get_config()
2218
conf.set_option('remotedir', 'file')
2219
# We get one call for the server and one call for the client, this is
2220
# caused by the differences in implementations betwen
2221
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2222
# SmartServerBranchGetConfigFile (in smart/branch.py)
2223
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2225
def assertSaveHook(self, conf):
2229
config.OldConfigHooks.install_named_hook('save', hook, None)
2231
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2232
self.assertLength(0, calls)
2233
# Setting an option triggers a save
2234
conf.set_option('foo', 'bar')
2235
self.assertLength(1, calls)
2236
# Since we can't assert about conf, we just use the number of calls ;-/
2238
def test_save_hook_remote_branch(self):
2239
remote_branch = branch.Branch.open(self.get_url('tree'))
2240
self.addCleanup(remote_branch.lock_write().unlock)
2241
self.assertSaveHook(remote_branch._get_config())
2243
def test_save_hook_remote_bzrdir(self):
2244
remote_branch = branch.Branch.open(self.get_url('tree'))
2245
self.addCleanup(remote_branch.lock_write().unlock)
2246
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2247
self.assertSaveHook(remote_bzrdir._get_config())
2250
class TestOption(tests.TestCase):
2252
def test_default_value(self):
2253
opt = config.Option('foo', default='bar')
2254
self.assertEquals('bar', opt.get_default())
2256
def test_default_value_from_env(self):
2257
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2258
self.overrideEnv('FOO', 'quux')
2259
# Env variable provides a default taking over the option one
2260
self.assertEquals('quux', opt.get_default())
2262
def test_first_default_value_from_env_wins(self):
2263
opt = config.Option('foo', default='bar',
2264
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2265
self.overrideEnv('FOO', 'foo')
2266
self.overrideEnv('BAZ', 'baz')
2267
# The first env var set wins
2268
self.assertEquals('foo', opt.get_default())
2271
class TestOptionRegistry(tests.TestCase):
2274
super(TestOptionRegistry, self).setUp()
2275
# Always start with an empty registry
2276
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2277
self.registry = config.option_registry
2279
def test_register(self):
2280
opt = config.Option('foo')
2281
self.registry.register(opt)
2282
self.assertIs(opt, self.registry.get('foo'))
2284
def test_registered_help(self):
2285
opt = config.Option('foo', help='A simple option')
2286
self.registry.register(opt)
2287
self.assertEquals('A simple option', self.registry.get_help('foo'))
2289
lazy_option = config.Option('lazy_foo', help='Lazy help')
2291
def test_register_lazy(self):
2292
self.registry.register_lazy('lazy_foo', self.__module__,
2293
'TestOptionRegistry.lazy_option')
2294
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2296
def test_registered_lazy_help(self):
2297
self.registry.register_lazy('lazy_foo', self.__module__,
2298
'TestOptionRegistry.lazy_option')
2299
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2302
class TestRegisteredOptions(tests.TestCase):
2303
"""All registered options should verify some constraints."""
2305
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2306
in config.option_registry.iteritems()]
2309
super(TestRegisteredOptions, self).setUp()
2310
self.registry = config.option_registry
2312
def test_proper_name(self):
2313
# An option should be registered under its own name, this can't be
2314
# checked at registration time for the lazy ones.
2315
self.assertEquals(self.option_name, self.option.name)
2317
def test_help_is_set(self):
2318
option_help = self.registry.get_help(self.option_name)
2319
self.assertNotEquals(None, option_help)
2320
# Come on, think about the user, he really wants to know what the
2322
self.assertIsNot(None, option_help)
2323
self.assertNotEquals('', option_help)
2326
class TestSection(tests.TestCase):
2328
# FIXME: Parametrize so that all sections produced by Stores run these
2329
# tests -- vila 2011-04-01
2331
def test_get_a_value(self):
2332
a_dict = dict(foo='bar')
2333
section = config.Section('myID', a_dict)
2334
self.assertEquals('bar', section.get('foo'))
2336
def test_get_unknown_option(self):
2338
section = config.Section(None, a_dict)
2339
self.assertEquals('out of thin air',
2340
section.get('foo', 'out of thin air'))
2342
def test_options_is_shared(self):
2344
section = config.Section(None, a_dict)
2345
self.assertIs(a_dict, section.options)
2348
class TestMutableSection(tests.TestCase):
2350
# FIXME: Parametrize so that all sections (including os.environ and the
2351
# ones produced by Stores) run these tests -- vila 2011-04-01
2354
a_dict = dict(foo='bar')
2355
section = config.MutableSection('myID', a_dict)
2356
section.set('foo', 'new_value')
2357
self.assertEquals('new_value', section.get('foo'))
2358
# The change appears in the shared section
2359
self.assertEquals('new_value', a_dict.get('foo'))
2360
# We keep track of the change
2361
self.assertTrue('foo' in section.orig)
2362
self.assertEquals('bar', section.orig.get('foo'))
2364
def test_set_preserve_original_once(self):
2365
a_dict = dict(foo='bar')
2366
section = config.MutableSection('myID', a_dict)
2367
section.set('foo', 'first_value')
2368
section.set('foo', 'second_value')
2369
# We keep track of the original value
2370
self.assertTrue('foo' in section.orig)
2371
self.assertEquals('bar', section.orig.get('foo'))
2373
def test_remove(self):
2374
a_dict = dict(foo='bar')
2375
section = config.MutableSection('myID', a_dict)
2376
section.remove('foo')
2377
# We get None for unknown options via the default value
2378
self.assertEquals(None, section.get('foo'))
2379
# Or we just get the default value
2380
self.assertEquals('unknown', section.get('foo', 'unknown'))
2381
self.assertFalse('foo' in section.options)
2382
# We keep track of the deletion
2383
self.assertTrue('foo' in section.orig)
2384
self.assertEquals('bar', section.orig.get('foo'))
2386
def test_remove_new_option(self):
2388
section = config.MutableSection('myID', a_dict)
2389
section.set('foo', 'bar')
2390
section.remove('foo')
2391
self.assertFalse('foo' in section.options)
2392
# The option didn't exist initially so it we need to keep track of it
2393
# with a special value
2394
self.assertTrue('foo' in section.orig)
2395
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2398
class TestStore(tests.TestCaseWithTransport):
2400
def assertSectionContent(self, expected, section):
2401
"""Assert that some options have the proper values in a section."""
2402
expected_name, expected_options = expected
2403
self.assertEquals(expected_name, section.id)
2406
dict([(k, section.get(k)) for k in expected_options.keys()]))
2409
class TestReadonlyStore(TestStore):
2411
scenarios = [(key, {'get_store': builder}) for key, builder
2412
in config.test_store_builder_registry.iteritems()]
2415
super(TestReadonlyStore, self).setUp()
2417
def test_building_delays_load(self):
2418
store = self.get_store(self)
2419
self.assertEquals(False, store.is_loaded())
2420
store._load_from_string('')
2421
self.assertEquals(True, store.is_loaded())
2423
def test_get_no_sections_for_empty(self):
2424
store = self.get_store(self)
2425
store._load_from_string('')
2426
self.assertEquals([], list(store.get_sections()))
2428
def test_get_default_section(self):
2429
store = self.get_store(self)
2430
store._load_from_string('foo=bar')
2431
sections = list(store.get_sections())
2432
self.assertLength(1, sections)
2433
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2435
def test_get_named_section(self):
2436
store = self.get_store(self)
2437
store._load_from_string('[baz]\nfoo=bar')
2438
sections = list(store.get_sections())
2439
self.assertLength(1, sections)
2440
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2442
def test_load_from_string_fails_for_non_empty_store(self):
2443
store = self.get_store(self)
2444
store._load_from_string('foo=bar')
2445
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2448
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2449
"""Simulate loading a config store without content of various encodings.
2451
All files produced by bzr are in utf8 content.
2453
Users may modify them manually and end up with a file that can't be
2454
loaded. We need to issue proper error messages in this case.
2457
invalid_utf8_char = '\xff'
2459
def test_load_utf8(self):
2460
"""Ensure we can load an utf8-encoded file."""
2461
t = self.get_transport()
2462
# From http://pad.lv/799212
2463
unicode_user = u'b\N{Euro Sign}ar'
2464
unicode_content = u'user=%s' % (unicode_user,)
2465
utf8_content = unicode_content.encode('utf8')
2466
# Store the raw content in the config file
2467
t.put_bytes('foo.conf', utf8_content)
2468
store = config.IniFileStore(t, 'foo.conf')
2470
stack = config.Stack([store.get_sections], store)
2471
self.assertEquals(unicode_user, stack.get('user'))
2473
def test_load_non_ascii(self):
2474
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2475
t = self.get_transport()
2476
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2477
store = config.IniFileStore(t, 'foo.conf')
2478
self.assertRaises(errors.ConfigContentError, store.load)
2480
def test_load_erroneous_content(self):
2481
"""Ensure we display a proper error on content that can't be parsed."""
2482
t = self.get_transport()
2483
t.put_bytes('foo.conf', '[open_section\n')
2484
store = config.IniFileStore(t, 'foo.conf')
2485
self.assertRaises(errors.ParseConfigError, store.load)
2488
class TestIniConfigContent(tests.TestCaseWithTransport):
2489
"""Simulate loading a IniBasedConfig without content of various encodings.
2491
All files produced by bzr are in utf8 content.
2493
Users may modify them manually and end up with a file that can't be
2494
loaded. We need to issue proper error messages in this case.
2497
invalid_utf8_char = '\xff'
2499
def test_load_utf8(self):
2500
"""Ensure we can load an utf8-encoded file."""
2501
# From http://pad.lv/799212
2502
unicode_user = u'b\N{Euro Sign}ar'
2503
unicode_content = u'user=%s' % (unicode_user,)
2504
utf8_content = unicode_content.encode('utf8')
2505
# Store the raw content in the config file
2506
with open('foo.conf', 'wb') as f:
2507
f.write(utf8_content)
2508
conf = config.IniBasedConfig(file_name='foo.conf')
2509
self.assertEquals(unicode_user, conf.get_user_option('user'))
2511
def test_load_badly_encoded_content(self):
2512
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2513
with open('foo.conf', 'wb') as f:
2514
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2515
conf = config.IniBasedConfig(file_name='foo.conf')
2516
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2518
def test_load_erroneous_content(self):
2519
"""Ensure we display a proper error on content that can't be parsed."""
2520
with open('foo.conf', 'wb') as f:
2521
f.write('[open_section\n')
2522
conf = config.IniBasedConfig(file_name='foo.conf')
2523
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2526
class TestMutableStore(TestStore):
2528
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2529
in config.test_store_builder_registry.iteritems()]
2532
super(TestMutableStore, self).setUp()
2533
self.transport = self.get_transport()
2535
def has_store(self, store):
2536
store_basename = urlutils.relative_url(self.transport.external_url(),
2537
store.external_url())
2538
return self.transport.has(store_basename)
2540
def test_save_empty_creates_no_file(self):
2541
# FIXME: There should be a better way than relying on the test
2542
# parametrization to identify branch.conf -- vila 2011-0526
2543
if self.store_id in ('branch', 'remote_branch'):
2544
raise tests.TestNotApplicable(
2545
'branch.conf is *always* created when a branch is initialized')
2546
store = self.get_store(self)
2548
self.assertEquals(False, self.has_store(store))
2550
def test_save_emptied_succeeds(self):
2551
store = self.get_store(self)
2552
store._load_from_string('foo=bar\n')
2553
section = store.get_mutable_section(None)
2554
section.remove('foo')
2556
self.assertEquals(True, self.has_store(store))
2557
modified_store = self.get_store(self)
2558
sections = list(modified_store.get_sections())
2559
self.assertLength(0, sections)
2561
def test_save_with_content_succeeds(self):
2562
# FIXME: There should be a better way than relying on the test
2563
# parametrization to identify branch.conf -- vila 2011-0526
2564
if self.store_id in ('branch', 'remote_branch'):
2565
raise tests.TestNotApplicable(
2566
'branch.conf is *always* created when a branch is initialized')
2567
store = self.get_store(self)
2568
store._load_from_string('foo=bar\n')
2569
self.assertEquals(False, self.has_store(store))
2571
self.assertEquals(True, self.has_store(store))
2572
modified_store = self.get_store(self)
2573
sections = list(modified_store.get_sections())
2574
self.assertLength(1, sections)
2575
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2577
def test_set_option_in_empty_store(self):
2578
store = self.get_store(self)
2579
section = store.get_mutable_section(None)
2580
section.set('foo', 'bar')
2582
modified_store = self.get_store(self)
2583
sections = list(modified_store.get_sections())
2584
self.assertLength(1, sections)
2585
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2587
def test_set_option_in_default_section(self):
2588
store = self.get_store(self)
2589
store._load_from_string('')
2590
section = store.get_mutable_section(None)
2591
section.set('foo', 'bar')
2593
modified_store = self.get_store(self)
2594
sections = list(modified_store.get_sections())
2595
self.assertLength(1, sections)
2596
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2598
def test_set_option_in_named_section(self):
2599
store = self.get_store(self)
2600
store._load_from_string('')
2601
section = store.get_mutable_section('baz')
2602
section.set('foo', 'bar')
2604
modified_store = self.get_store(self)
2605
sections = list(modified_store.get_sections())
2606
self.assertLength(1, sections)
2607
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2609
def test_load_hook(self):
2610
# We first needs to ensure that the store exists
2611
store = self.get_store(self)
2612
section = store.get_mutable_section('baz')
2613
section.set('foo', 'bar')
2615
# Now we can try to load it
2616
store = self.get_store(self)
2620
config.ConfigHooks.install_named_hook('load', hook, None)
2621
self.assertLength(0, calls)
2623
self.assertLength(1, calls)
2624
self.assertEquals((store,), calls[0])
2626
def test_save_hook(self):
2630
config.ConfigHooks.install_named_hook('save', hook, None)
2631
self.assertLength(0, calls)
2632
store = self.get_store(self)
2633
section = store.get_mutable_section('baz')
2634
section.set('foo', 'bar')
2636
self.assertLength(1, calls)
2637
self.assertEquals((store,), calls[0])
2640
class TestIniFileStore(TestStore):
2642
def test_loading_unknown_file_fails(self):
2643
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2644
self.assertRaises(errors.NoSuchFile, store.load)
2646
def test_invalid_content(self):
2647
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2648
self.assertEquals(False, store.is_loaded())
2649
exc = self.assertRaises(
2650
errors.ParseConfigError, store._load_from_string,
2651
'this is invalid !')
2652
self.assertEndsWith(exc.filename, 'foo.conf')
2653
# And the load failed
2654
self.assertEquals(False, store.is_loaded())
2656
def test_get_embedded_sections(self):
2657
# A more complicated example (which also shows that section names and
2658
# option names share the same name space...)
2659
# FIXME: This should be fixed by forbidding dicts as values ?
2660
# -- vila 2011-04-05
2661
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2662
store._load_from_string('''
2666
foo_in_DEFAULT=foo_DEFAULT
2674
sections = list(store.get_sections())
2675
self.assertLength(4, sections)
2676
# The default section has no name.
2677
# List values are provided as lists
2678
self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2680
self.assertSectionContent(
2681
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2682
self.assertSectionContent(
2683
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2684
# sub sections are provided as embedded dicts.
2685
self.assertSectionContent(
2686
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2690
class TestLockableIniFileStore(TestStore):
2692
def test_create_store_in_created_dir(self):
2693
self.assertPathDoesNotExist('dir')
2694
t = self.get_transport('dir/subdir')
2695
store = config.LockableIniFileStore(t, 'foo.conf')
2696
store.get_mutable_section(None).set('foo', 'bar')
2698
self.assertPathExists('dir/subdir')
2701
class TestConcurrentStoreUpdates(TestStore):
2702
"""Test that Stores properly handle conccurent updates.
2704
New Store implementation may fail some of these tests but until such
2705
implementations exist it's hard to properly filter them from the scenarios
2706
applied here. If you encounter such a case, contact the bzr devs.
2709
scenarios = [(key, {'get_stack': builder}) for key, builder
2710
in config.test_stack_builder_registry.iteritems()]
2713
super(TestConcurrentStoreUpdates, self).setUp()
2714
self._content = 'one=1\ntwo=2\n'
2715
self.stack = self.get_stack(self)
2716
if not isinstance(self.stack, config._CompatibleStack):
2717
raise tests.TestNotApplicable(
2718
'%s is not meant to be compatible with the old config design'
2720
self.stack.store._load_from_string(self._content)
2722
self.stack.store.save()
2724
def test_simple_read_access(self):
2725
self.assertEquals('1', self.stack.get('one'))
2727
def test_simple_write_access(self):
2728
self.stack.set('one', 'one')
2729
self.assertEquals('one', self.stack.get('one'))
2731
def test_listen_to_the_last_speaker(self):
2733
c2 = self.get_stack(self)
2734
c1.set('one', 'ONE')
2735
c2.set('two', 'TWO')
2736
self.assertEquals('ONE', c1.get('one'))
2737
self.assertEquals('TWO', c2.get('two'))
2738
# The second update respect the first one
2739
self.assertEquals('ONE', c2.get('one'))
2741
def test_last_speaker_wins(self):
2742
# If the same config is not shared, the same variable modified twice
2743
# can only see a single result.
2745
c2 = self.get_stack(self)
2748
self.assertEquals('c2', c2.get('one'))
2749
# The first modification is still available until another refresh
2751
self.assertEquals('c1', c1.get('one'))
2752
c1.set('two', 'done')
2753
self.assertEquals('c2', c1.get('one'))
2755
def test_writes_are_serialized(self):
2757
c2 = self.get_stack(self)
2759
# We spawn a thread that will pause *during* the config saving.
2760
before_writing = threading.Event()
2761
after_writing = threading.Event()
2762
writing_done = threading.Event()
2763
c1_save_without_locking_orig = c1.store.save_without_locking
2764
def c1_save_without_locking():
2765
before_writing.set()
2766
c1_save_without_locking_orig()
2767
# The lock is held. We wait for the main thread to decide when to
2769
after_writing.wait()
2770
c1.store.save_without_locking = c1_save_without_locking
2774
t1 = threading.Thread(target=c1_set)
2775
# Collect the thread after the test
2776
self.addCleanup(t1.join)
2777
# Be ready to unblock the thread if the test goes wrong
2778
self.addCleanup(after_writing.set)
2780
before_writing.wait()
2781
self.assertRaises(errors.LockContention,
2782
c2.set, 'one', 'c2')
2783
self.assertEquals('c1', c1.get('one'))
2784
# Let the lock be released
2788
self.assertEquals('c2', c2.get('one'))
2790
def test_read_while_writing(self):
2792
# We spawn a thread that will pause *during* the write
2793
ready_to_write = threading.Event()
2794
do_writing = threading.Event()
2795
writing_done = threading.Event()
2796
# We override the _save implementation so we know the store is locked
2797
c1_save_without_locking_orig = c1.store.save_without_locking
2798
def c1_save_without_locking():
2799
ready_to_write.set()
2800
# The lock is held. We wait for the main thread to decide when to
2803
c1_save_without_locking_orig()
2805
c1.store.save_without_locking = c1_save_without_locking
2808
t1 = threading.Thread(target=c1_set)
2809
# Collect the thread after the test
2810
self.addCleanup(t1.join)
2811
# Be ready to unblock the thread if the test goes wrong
2812
self.addCleanup(do_writing.set)
2814
# Ensure the thread is ready to write
2815
ready_to_write.wait()
2816
self.assertEquals('c1', c1.get('one'))
2817
# If we read during the write, we get the old value
2818
c2 = self.get_stack(self)
2819
self.assertEquals('1', c2.get('one'))
2820
# Let the writing occur and ensure it occurred
2823
# Now we get the updated value
2824
c3 = self.get_stack(self)
2825
self.assertEquals('c1', c3.get('one'))
2827
# FIXME: It may be worth looking into removing the lock dir when it's not
2828
# needed anymore and look at possible fallouts for concurrent lockers. This
2829
# will matter if/when we use config files outside of bazaar directories
2830
# (.bazaar or .bzr) -- vila 20110-04-11
2833
class TestSectionMatcher(TestStore):
2835
scenarios = [('location', {'matcher': config.LocationMatcher})]
2837
def get_store(self, file_name):
2838
return config.IniFileStore(self.get_readonly_transport(), file_name)
2840
def test_no_matches_for_empty_stores(self):
2841
store = self.get_store('foo.conf')
2842
store._load_from_string('')
2843
matcher = self.matcher(store, '/bar')
2844
self.assertEquals([], list(matcher.get_sections()))
2846
def test_build_doesnt_load_store(self):
2847
store = self.get_store('foo.conf')
2848
matcher = self.matcher(store, '/bar')
2849
self.assertFalse(store.is_loaded())
2852
class TestLocationSection(tests.TestCase):
2854
def get_section(self, options, extra_path):
2855
section = config.Section('foo', options)
2856
# We don't care about the length so we use '0'
2857
return config.LocationSection(section, 0, extra_path)
2859
def test_simple_option(self):
2860
section = self.get_section({'foo': 'bar'}, '')
2861
self.assertEquals('bar', section.get('foo'))
2863
def test_option_with_extra_path(self):
2864
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
2866
self.assertEquals('bar/baz', section.get('foo'))
2868
def test_invalid_policy(self):
2869
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
2871
# invalid policies are ignored
2872
self.assertEquals('bar', section.get('foo'))
2875
class TestLocationMatcher(TestStore):
2877
def get_store(self, file_name):
2878
return config.IniFileStore(self.get_readonly_transport(), file_name)
2880
def test_unrelated_section_excluded(self):
2881
store = self.get_store('foo.conf')
2882
store._load_from_string('''
2890
section=/foo/bar/baz
2894
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
2896
[section.id for section in store.get_sections()])
2897
matcher = config.LocationMatcher(store, '/foo/bar/quux')
2898
sections = list(matcher.get_sections())
2899
self.assertEquals([3, 2],
2900
[section.length for section in sections])
2901
self.assertEquals(['/foo/bar', '/foo'],
2902
[section.id for section in sections])
2903
self.assertEquals(['quux', 'bar/quux'],
2904
[section.extra_path for section in sections])
2906
def test_more_specific_sections_first(self):
2907
store = self.get_store('foo.conf')
2908
store._load_from_string('''
2914
self.assertEquals(['/foo', '/foo/bar'],
2915
[section.id for section in store.get_sections()])
2916
matcher = config.LocationMatcher(store, '/foo/bar/baz')
2917
sections = list(matcher.get_sections())
2918
self.assertEquals([3, 2],
2919
[section.length for section in sections])
2920
self.assertEquals(['/foo/bar', '/foo'],
2921
[section.id for section in sections])
2922
self.assertEquals(['baz', 'bar/baz'],
2923
[section.extra_path for section in sections])
2925
def test_appendpath_in_no_name_section(self):
2926
# It's a bit weird to allow appendpath in a no-name section, but
2927
# someone may found a use for it
2928
store = self.get_store('foo.conf')
2929
store._load_from_string('''
2931
foo:policy = appendpath
2933
matcher = config.LocationMatcher(store, 'dir/subdir')
2934
sections = list(matcher.get_sections())
2935
self.assertLength(1, sections)
2936
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
2938
def test_file_urls_are_normalized(self):
2939
store = self.get_store('foo.conf')
2940
if sys.platform == 'win32':
2941
expected_url = 'file:///C:/dir/subdir'
2942
expected_location = 'C:/dir/subdir'
2944
expected_url = 'file:///dir/subdir'
2945
expected_location = '/dir/subdir'
2946
matcher = config.LocationMatcher(store, expected_url)
2947
self.assertEquals(expected_location, matcher.location)
2950
class TestStackGet(tests.TestCase):
2952
# FIXME: This should be parametrized for all known Stack or dedicated
2953
# paramerized tests created to avoid bloating -- vila 2011-03-31
2955
def overrideOptionRegistry(self):
2956
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2958
def test_single_config_get(self):
2959
conf = dict(foo='bar')
2960
conf_stack = config.Stack([conf])
2961
self.assertEquals('bar', conf_stack.get('foo'))
2963
def test_get_with_registered_default_value(self):
2964
conf_stack = config.Stack([dict()])
2965
opt = config.Option('foo', default='bar')
2966
self.overrideOptionRegistry()
2967
config.option_registry.register('foo', opt)
2968
self.assertEquals('bar', conf_stack.get('foo'))
2970
def test_get_without_registered_default_value(self):
2971
conf_stack = config.Stack([dict()])
2972
opt = config.Option('foo')
2973
self.overrideOptionRegistry()
2974
config.option_registry.register('foo', opt)
2975
self.assertEquals(None, conf_stack.get('foo'))
2977
def test_get_without_default_value_for_not_registered(self):
2978
conf_stack = config.Stack([dict()])
2979
opt = config.Option('foo')
2980
self.overrideOptionRegistry()
2981
self.assertEquals(None, conf_stack.get('foo'))
2983
def test_get_first_definition(self):
2984
conf1 = dict(foo='bar')
2985
conf2 = dict(foo='baz')
2986
conf_stack = config.Stack([conf1, conf2])
2987
self.assertEquals('bar', conf_stack.get('foo'))
2989
def test_get_embedded_definition(self):
2990
conf1 = dict(yy='12')
2991
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2992
conf_stack = config.Stack([conf1, conf2])
2993
self.assertEquals('baz', conf_stack.get('foo'))
2995
def test_get_for_empty_section_callable(self):
2996
conf_stack = config.Stack([lambda : []])
2997
self.assertEquals(None, conf_stack.get('foo'))
2999
def test_get_for_broken_callable(self):
3000
# Trying to use and invalid callable raises an exception on first use
3001
conf_stack = config.Stack([lambda : object()])
3002
self.assertRaises(TypeError, conf_stack.get, 'foo')
3005
class TestStackWithTransport(tests.TestCaseWithTransport):
3007
scenarios = [(key, {'get_stack': builder}) for key, builder
3008
in config.test_stack_builder_registry.iteritems()]
3011
class TestConcreteStacks(TestStackWithTransport):
3013
def test_build_stack(self):
3014
# Just a smoke test to help debug builders
3015
stack = self.get_stack(self)
3018
class TestStackGet(TestStackWithTransport):
3021
super(TestStackGet, self).setUp()
3022
self.conf = self.get_stack(self)
3024
def test_get_for_empty_stack(self):
3025
self.assertEquals(None, self.conf.get('foo'))
3027
def test_get_hook(self):
3028
self.conf.store._load_from_string('foo=bar')
3032
config.ConfigHooks.install_named_hook('get', hook, None)
3033
self.assertLength(0, calls)
3034
value = self.conf.get('foo')
3035
self.assertEquals('bar', value)
3036
self.assertLength(1, calls)
3037
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3040
class TestStackGetWithConverter(TestStackGet):
3043
super(TestStackGetWithConverter, self).setUp()
3044
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3045
self.registry = config.option_registry
3047
def register_bool_option(self, name, default, invalid=None):
3048
b = config.Option(name, default=default, help='A boolean.',
3049
from_unicode=config.bool_from_store,
3051
self.registry.register(b)
3053
def test_get_with_bool_not_defined_default_true(self):
3054
self.register_bool_option('foo', True)
3055
self.assertEquals(True, self.conf.get('foo'))
3057
def test_get_with_bool_not_defined_default_false(self):
3058
self.register_bool_option('foo', False)
3059
self.assertEquals(False, self.conf.get('foo'))
3061
def test_get_with_bool_converter_not_default(self):
3062
self.register_bool_option('foo', False)
3063
self.conf.store._load_from_string('foo=yes')
3064
self.assertEquals(True, self.conf.get('foo'))
3066
def test_get_with_bool_converter_invalid_string(self):
3067
self.register_bool_option('foo', False)
3068
self.conf.store._load_from_string('foo=not-a-boolean')
3069
self.assertEquals(False, self.conf.get('foo'))
3071
def test_get_with_bool_converter_invalid_list(self):
3072
self.register_bool_option('foo', False)
3073
self.conf.store._load_from_string('foo=not,a,boolean')
3074
self.assertEquals(False, self.conf.get('foo'))
3076
def test_get_invalid_warns(self):
3077
self.register_bool_option('foo', False, invalid='warning')
3078
self.conf.store._load_from_string('foo=not-a-boolean')
3081
warnings.append(args[0] % args[1:])
3082
self.overrideAttr(trace, 'warning', warning)
3083
self.assertEquals(False, self.conf.get('foo'))
3084
self.assertLength(1, warnings)
3085
self.assertEquals('Value "not-a-boolean" is not valid for "foo"',
3088
def test_get_invalid_errors(self):
3089
self.register_bool_option('foo', False, invalid='error')
3090
self.conf.store._load_from_string('foo=not-a-boolean')
3091
self.assertRaises(errors.ConfigOptionValueError, self.conf.get, 'foo')
3093
def register_integer_option(self, name, default):
3094
i = config.Option(name, default=default, help='An integer.',
3095
from_unicode=config.int_from_store)
3096
self.registry.register(i)
3098
def test_get_with_integer_not_defined_returns_default(self):
3099
self.register_integer_option('foo', 42)
3100
self.assertEquals(42, self.conf.get('foo'))
3102
def test_get_with_integer_converter_not_default(self):
3103
self.register_integer_option('foo', 42)
3104
self.conf.store._load_from_string('foo=16')
3105
self.assertEquals(16, self.conf.get('foo'))
3107
def test_get_with_integer_converter_invalid_string(self):
3108
# We don't set a default value
3109
self.register_integer_option('foo', None)
3110
self.conf.store._load_from_string('foo=forty-two')
3111
# No default value, so we should get None
3112
self.assertEquals(None, self.conf.get('foo'))
3114
def test_get_with_integer_converter_invalid_list(self):
3115
# We don't set a default value
3116
self.register_integer_option('foo', None)
3117
self.conf.store._load_from_string('foo=a,list')
3118
# No default value, so we should get None
3119
self.assertEquals(None, self.conf.get('foo'))
3121
def register_list_option(self, name, default):
3122
l = config.Option(name, default=default, help='A list.',
3123
from_unicode=config.list_from_store)
3124
self.registry.register(l)
3126
def test_get_with_list_not_defined_returns_default(self):
3127
self.register_list_option('foo', [])
3128
self.assertEquals([], self.conf.get('foo'))
3130
def test_get_with_list_converter_nothing(self):
3131
self.register_list_option('foo', [1])
3132
self.conf.store._load_from_string('foo=')
3133
self.assertEquals([], self.conf.get('foo'))
3135
def test_get_with_list_converter_no_item(self):
3136
self.register_list_option('foo', [1])
3137
self.conf.store._load_from_string('foo=,')
3138
self.assertEquals([], self.conf.get('foo'))
3140
def test_get_with_list_converter_one_boolean(self):
3141
self.register_list_option('foo', [1])
3142
self.conf.store._load_from_string('foo=True')
3143
# We get a list of one string
3144
self.assertEquals(['True'], self.conf.get('foo'))
3146
def test_get_with_list_converter_one_integer(self):
3147
self.register_list_option('foo', [1])
3148
self.conf.store._load_from_string('foo=2')
3149
# We get a list of one string
3150
self.assertEquals(['2'], self.conf.get('foo'))
3152
def test_get_with_list_converter_one_string(self):
3153
self.register_list_option('foo', ['foo'])
3154
self.conf.store._load_from_string('foo=bar')
3155
# We get a list of one string
3156
self.assertEquals(['bar'], self.conf.get('foo'))
3158
def test_get_with_list_converter_many_items(self):
3159
self.register_list_option('foo', [1])
3160
self.conf.store._load_from_string('foo=m,o,r,e')
3161
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3164
class TestStackSet(TestStackWithTransport):
3166
def test_simple_set(self):
3167
conf = self.get_stack(self)
3168
conf.store._load_from_string('foo=bar')
3169
self.assertEquals('bar', conf.get('foo'))
3170
conf.set('foo', 'baz')
3171
# Did we get it back ?
3172
self.assertEquals('baz', conf.get('foo'))
3174
def test_set_creates_a_new_section(self):
3175
conf = self.get_stack(self)
3176
conf.set('foo', 'baz')
3177
self.assertEquals, 'baz', conf.get('foo')
3179
def test_set_hook(self):
3183
config.ConfigHooks.install_named_hook('set', hook, None)
3184
self.assertLength(0, calls)
3185
conf = self.get_stack(self)
3186
conf.set('foo', 'bar')
3187
self.assertLength(1, calls)
3188
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3191
class TestStackRemove(TestStackWithTransport):
3193
def test_remove_existing(self):
3194
conf = self.get_stack(self)
3195
conf.store._load_from_string('foo=bar')
3196
self.assertEquals('bar', conf.get('foo'))
3198
# Did we get it back ?
3199
self.assertEquals(None, conf.get('foo'))
3201
def test_remove_unknown(self):
3202
conf = self.get_stack(self)
3203
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3205
def test_remove_hook(self):
3209
config.ConfigHooks.install_named_hook('remove', hook, None)
3210
self.assertLength(0, calls)
3211
conf = self.get_stack(self)
3212
conf.store._load_from_string('foo=bar')
3214
self.assertLength(1, calls)
3215
self.assertEquals((conf, 'foo'), calls[0])
3218
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3221
super(TestConfigGetOptions, self).setUp()
3222
create_configs(self)
3224
def test_no_variable(self):
3225
# Using branch should query branch, locations and bazaar
3226
self.assertOptions([], self.branch_config)
3228
def test_option_in_bazaar(self):
3229
self.bazaar_config.set_user_option('file', 'bazaar')
3230
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3233
def test_option_in_locations(self):
3234
self.locations_config.set_user_option('file', 'locations')
3236
[('file', 'locations', self.tree.basedir, 'locations')],
3237
self.locations_config)
3239
def test_option_in_branch(self):
3240
self.branch_config.set_user_option('file', 'branch')
3241
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3244
def test_option_in_bazaar_and_branch(self):
3245
self.bazaar_config.set_user_option('file', 'bazaar')
3246
self.branch_config.set_user_option('file', 'branch')
3247
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3248
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3251
def test_option_in_branch_and_locations(self):
3252
# Hmm, locations override branch :-/
3253
self.locations_config.set_user_option('file', 'locations')
3254
self.branch_config.set_user_option('file', 'branch')
3256
[('file', 'locations', self.tree.basedir, 'locations'),
3257
('file', 'branch', 'DEFAULT', 'branch'),],
3260
def test_option_in_bazaar_locations_and_branch(self):
3261
self.bazaar_config.set_user_option('file', 'bazaar')
3262
self.locations_config.set_user_option('file', 'locations')
3263
self.branch_config.set_user_option('file', 'branch')
3265
[('file', 'locations', self.tree.basedir, 'locations'),
3266
('file', 'branch', 'DEFAULT', 'branch'),
3267
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3271
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3274
super(TestConfigRemoveOption, self).setUp()
3275
create_configs_with_file_option(self)
3277
def test_remove_in_locations(self):
3278
self.locations_config.remove_user_option('file', self.tree.basedir)
3280
[('file', 'branch', 'DEFAULT', 'branch'),
3281
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3284
def test_remove_in_branch(self):
3285
self.branch_config.remove_user_option('file')
3287
[('file', 'locations', self.tree.basedir, 'locations'),
3288
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3291
def test_remove_in_bazaar(self):
3292
self.bazaar_config.remove_user_option('file')
3294
[('file', 'locations', self.tree.basedir, 'locations'),
3295
('file', 'branch', 'DEFAULT', 'branch'),],
3299
class TestConfigGetSections(tests.TestCaseWithTransport):
3302
super(TestConfigGetSections, self).setUp()
3303
create_configs(self)
3305
def assertSectionNames(self, expected, conf, name=None):
3306
"""Check which sections are returned for a given config.
3308
If fallback configurations exist their sections can be included.
3310
:param expected: A list of section names.
3312
:param conf: The configuration that will be queried.
3314
:param name: An optional section name that will be passed to
3317
sections = list(conf._get_sections(name))
3318
self.assertLength(len(expected), sections)
3319
self.assertEqual(expected, [name for name, _, _ in sections])
3321
def test_bazaar_default_section(self):
3322
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3324
def test_locations_default_section(self):
3325
# No sections are defined in an empty file
3326
self.assertSectionNames([], self.locations_config)
3328
def test_locations_named_section(self):
3329
self.locations_config.set_user_option('file', 'locations')
3330
self.assertSectionNames([self.tree.basedir], self.locations_config)
3332
def test_locations_matching_sections(self):
3333
loc_config = self.locations_config
3334
loc_config.set_user_option('file', 'locations')
3335
# We need to cheat a bit here to create an option in sections above and
3336
# below the 'location' one.
3337
parser = loc_config._get_parser()
3338
# locations.cong deals with '/' ignoring native os.sep
3339
location_names = self.tree.basedir.split('/')
3340
parent = '/'.join(location_names[:-1])
3341
child = '/'.join(location_names + ['child'])
3343
parser[parent]['file'] = 'parent'
3345
parser[child]['file'] = 'child'
3346
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3348
def test_branch_data_default_section(self):
3349
self.assertSectionNames([None],
3350
self.branch_config._get_branch_data_config())
3352
def test_branch_default_sections(self):
3353
# No sections are defined in an empty locations file
3354
self.assertSectionNames([None, 'DEFAULT'],
3356
# Unless we define an option
3357
self.branch_config._get_location_config().set_user_option(
3358
'file', 'locations')
3359
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3362
def test_bazaar_named_section(self):
3363
# We need to cheat as the API doesn't give direct access to sections
3364
# other than DEFAULT.
3365
self.bazaar_config.set_alias('bazaar', 'bzr')
3366
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
3369
class TestAuthenticationConfigFile(tests.TestCase):
1316
3370
"""Test the authentication.conf file matching"""