367
584
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
587
class TestXDGConfigDir(tests.TestCaseInTempDir):
588
# must be in temp dir because config tests for the existence of the bazaar
589
# subdirectory of $XDG_CONFIG_HOME
592
if sys.platform in ('darwin', 'win32'):
593
raise tests.TestNotApplicable(
594
'XDG config dir not used on this platform')
595
super(TestXDGConfigDir, self).setUp()
596
self.overrideEnv('HOME', self.test_home_dir)
597
# BZR_HOME overrides everything we want to test so unset it.
598
self.overrideEnv('BZR_HOME', None)
600
def test_xdg_config_dir_exists(self):
601
"""When ~/.config/bazaar exists, use it as the config dir."""
602
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
604
self.assertEqual(config.config_dir(), newdir)
606
def test_xdg_config_home(self):
607
"""When XDG_CONFIG_HOME is set, use it."""
608
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
609
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
610
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
612
self.assertEqual(config.config_dir(), newdir)
615
class TestIniConfig(tests.TestCaseInTempDir):
372
617
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
618
conf = config.IniBasedConfig.from_string(s)
619
return conf, conf._get_parser()
378
622
class TestIniConfigBuilding(TestIniConfig):
380
624
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
625
my_config = config.IniBasedConfig()
383
627
def test_from_fp(self):
384
config_file = StringIO(sample_config_text.encode('utf-8'))
385
my_config = config.IniBasedConfig(None)
387
isinstance(my_config._get_parser(file=config_file),
388
configobj.ConfigObj))
628
my_config = config.IniBasedConfig.from_string(sample_config_text)
629
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
631
def test_cached(self):
632
my_config = config.IniBasedConfig.from_string(sample_config_text)
633
parser = my_config._get_parser()
634
self.assertTrue(my_config._get_parser() is parser)
636
def _dummy_chown(self, path, uid, gid):
637
self.path, self.uid, self.gid = path, uid, gid
639
def test_ini_config_ownership(self):
640
"""Ensure that chown is happening during _write_config_file"""
641
self.requireFeature(features.chown_feature)
642
self.overrideAttr(os, 'chown', self._dummy_chown)
643
self.path = self.uid = self.gid = None
644
conf = config.IniBasedConfig(file_name='./foo.conf')
645
conf._write_config_file()
646
self.assertEquals(self.path, './foo.conf')
647
self.assertTrue(isinstance(self.uid, int))
648
self.assertTrue(isinstance(self.gid, int))
650
def test_get_filename_parameter_is_deprecated_(self):
651
conf = self.callDeprecated([
652
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
653
' Use file_name instead.'],
654
config.IniBasedConfig, lambda: 'ini.conf')
655
self.assertEqual('ini.conf', conf.file_name)
657
def test_get_parser_file_parameter_is_deprecated_(self):
391
658
config_file = StringIO(sample_config_text.encode('utf-8'))
392
my_config = config.IniBasedConfig(None)
393
parser = my_config._get_parser(file=config_file)
394
self.failUnless(my_config._get_parser() is parser)
659
conf = config.IniBasedConfig.from_string(sample_config_text)
660
conf = self.callDeprecated([
661
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
662
' Use IniBasedConfig(_content=xxx) instead.'],
663
conf._get_parser, file=config_file)
666
class TestIniConfigSaving(tests.TestCaseInTempDir):
668
def test_cant_save_without_a_file_name(self):
669
conf = config.IniBasedConfig()
670
self.assertRaises(AssertionError, conf._write_config_file)
672
def test_saved_with_content(self):
673
content = 'foo = bar\n'
674
conf = config.IniBasedConfig.from_string(
675
content, file_name='./test.conf', save=True)
676
self.assertFileEqual(content, 'test.conf')
679
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
680
"""What is the default value of expand for config options.
682
This is an opt-in beta feature used to evaluate whether or not option
683
references can appear in dangerous place raising exceptions, disapearing
684
(and as such corrupting data) or if it's safe to activate the option by
687
Note that these tests relies on config._expand_default_value being already
688
overwritten in the parent class setUp.
692
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
696
self.warnings.append(args[0] % args[1:])
697
self.overrideAttr(trace, 'warning', warning)
699
def get_config(self, expand):
700
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
704
def assertExpandIs(self, expected):
705
actual = config._get_expand_default_value()
706
#self.config.get_user_option_as_bool('bzr.config.expand')
707
self.assertEquals(expected, actual)
709
def test_default_is_None(self):
710
self.assertEquals(None, config._expand_default_value)
712
def test_default_is_False_even_if_None(self):
713
self.config = self.get_config(None)
714
self.assertExpandIs(False)
716
def test_default_is_False_even_if_invalid(self):
717
self.config = self.get_config('<your choice>')
718
self.assertExpandIs(False)
720
# Huh ? My choice is False ? Thanks, always happy to hear that :D
721
# Wait, you've been warned !
722
self.assertLength(1, self.warnings)
724
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
727
def test_default_is_True(self):
728
self.config = self.get_config(True)
729
self.assertExpandIs(True)
731
def test_default_is_False(self):
732
self.config = self.get_config(False)
733
self.assertExpandIs(False)
736
class TestIniConfigOptionExpansion(tests.TestCase):
737
"""Test option expansion from the IniConfig level.
739
What we really want here is to test the Config level, but the class being
740
abstract as far as storing values is concerned, this can't be done
743
# FIXME: This should be rewritten when all configs share a storage
744
# implementation -- vila 2011-02-18
746
def get_config(self, string=None):
749
c = config.IniBasedConfig.from_string(string)
752
def assertExpansion(self, expected, conf, string, env=None):
753
self.assertEquals(expected, conf.expand_options(string, env))
755
def test_no_expansion(self):
756
c = self.get_config('')
757
self.assertExpansion('foo', c, 'foo')
759
def test_env_adding_options(self):
760
c = self.get_config('')
761
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
763
def test_env_overriding_options(self):
764
c = self.get_config('foo=baz')
765
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
767
def test_simple_ref(self):
768
c = self.get_config('foo=xxx')
769
self.assertExpansion('xxx', c, '{foo}')
771
def test_unknown_ref(self):
772
c = self.get_config('')
773
self.assertRaises(errors.ExpandingUnknownOption,
774
c.expand_options, '{foo}')
776
def test_indirect_ref(self):
777
c = self.get_config('''
781
self.assertExpansion('xxx', c, '{bar}')
783
def test_embedded_ref(self):
784
c = self.get_config('''
788
self.assertExpansion('xxx', c, '{{bar}}')
790
def test_simple_loop(self):
791
c = self.get_config('foo={foo}')
792
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
794
def test_indirect_loop(self):
795
c = self.get_config('''
799
e = self.assertRaises(errors.OptionExpansionLoop,
800
c.expand_options, '{foo}')
801
self.assertEquals('foo->bar->baz', e.refs)
802
self.assertEquals('{foo}', e.string)
805
conf = self.get_config('''
809
list={foo},{bar},{baz}
811
self.assertEquals(['start', 'middle', 'end'],
812
conf.get_user_option('list', expand=True))
814
def test_cascading_list(self):
815
conf = self.get_config('''
821
self.assertEquals(['start', 'middle', 'end'],
822
conf.get_user_option('list', expand=True))
824
def test_pathological_hidden_list(self):
825
conf = self.get_config('''
831
hidden={start}{middle}{end}
833
# Nope, it's either a string or a list, and the list wins as soon as a
834
# ',' appears, so the string concatenation never occur.
835
self.assertEquals(['{foo', '}', '{', 'bar}'],
836
conf.get_user_option('hidden', expand=True))
838
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
840
def get_config(self, location, string=None):
843
# Since we don't save the config we won't strictly require to inherit
844
# from TestCaseInTempDir, but an error occurs so quickly...
845
c = config.LocationConfig.from_string(string, location)
848
def test_dont_cross_unrelated_section(self):
849
c = self.get_config('/another/branch/path','''
854
[/another/branch/path]
857
self.assertRaises(errors.ExpandingUnknownOption,
858
c.get_user_option, 'bar', expand=True)
860
def test_cross_related_sections(self):
861
c = self.get_config('/project/branch/path','''
865
[/project/branch/path]
868
self.assertEquals('quux', c.get_user_option('bar', expand=True))
871
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
873
def test_cannot_reload_without_name(self):
874
conf = config.IniBasedConfig.from_string(sample_config_text)
875
self.assertRaises(AssertionError, conf.reload)
877
def test_reload_see_new_value(self):
878
c1 = config.IniBasedConfig.from_string('editor=vim\n',
879
file_name='./test/conf')
880
c1._write_config_file()
881
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
882
file_name='./test/conf')
883
c2._write_config_file()
884
self.assertEqual('vim', c1.get_user_option('editor'))
885
self.assertEqual('emacs', c2.get_user_option('editor'))
886
# Make sure we get the Right value
888
self.assertEqual('emacs', c1.get_user_option('editor'))
891
class TestLockableConfig(tests.TestCaseInTempDir):
893
scenarios = lockable_config_scenarios()
898
config_section = None
901
super(TestLockableConfig, self).setUp()
902
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
903
self.config = self.create_config(self._content)
905
def get_existing_config(self):
906
return self.config_class(*self.config_args)
908
def create_config(self, content):
909
kwargs = dict(save=True)
910
c = self.config_class.from_string(content, *self.config_args, **kwargs)
913
def test_simple_read_access(self):
914
self.assertEquals('1', self.config.get_user_option('one'))
916
def test_simple_write_access(self):
917
self.config.set_user_option('one', 'one')
918
self.assertEquals('one', self.config.get_user_option('one'))
920
def test_listen_to_the_last_speaker(self):
922
c2 = self.get_existing_config()
923
c1.set_user_option('one', 'ONE')
924
c2.set_user_option('two', 'TWO')
925
self.assertEquals('ONE', c1.get_user_option('one'))
926
self.assertEquals('TWO', c2.get_user_option('two'))
927
# The second update respect the first one
928
self.assertEquals('ONE', c2.get_user_option('one'))
930
def test_last_speaker_wins(self):
931
# If the same config is not shared, the same variable modified twice
932
# can only see a single result.
934
c2 = self.get_existing_config()
935
c1.set_user_option('one', 'c1')
936
c2.set_user_option('one', 'c2')
937
self.assertEquals('c2', c2._get_user_option('one'))
938
# The first modification is still available until another refresh
940
self.assertEquals('c1', c1._get_user_option('one'))
941
c1.set_user_option('two', 'done')
942
self.assertEquals('c2', c1._get_user_option('one'))
944
def test_writes_are_serialized(self):
946
c2 = self.get_existing_config()
948
# We spawn a thread that will pause *during* the write
949
before_writing = threading.Event()
950
after_writing = threading.Event()
951
writing_done = threading.Event()
952
c1_orig = c1._write_config_file
953
def c1_write_config_file():
956
# The lock is held. We wait for the main thread to decide when to
959
c1._write_config_file = c1_write_config_file
961
c1.set_user_option('one', 'c1')
963
t1 = threading.Thread(target=c1_set_option)
964
# Collect the thread after the test
965
self.addCleanup(t1.join)
966
# Be ready to unblock the thread if the test goes wrong
967
self.addCleanup(after_writing.set)
969
before_writing.wait()
970
self.assertTrue(c1._lock.is_held)
971
self.assertRaises(errors.LockContention,
972
c2.set_user_option, 'one', 'c2')
973
self.assertEquals('c1', c1.get_user_option('one'))
974
# Let the lock be released
977
c2.set_user_option('one', 'c2')
978
self.assertEquals('c2', c2.get_user_option('one'))
980
def test_read_while_writing(self):
982
# We spawn a thread that will pause *during* the write
983
ready_to_write = threading.Event()
984
do_writing = threading.Event()
985
writing_done = threading.Event()
986
c1_orig = c1._write_config_file
987
def c1_write_config_file():
989
# The lock is held. We wait for the main thread to decide when to
994
c1._write_config_file = c1_write_config_file
996
c1.set_user_option('one', 'c1')
997
t1 = threading.Thread(target=c1_set_option)
998
# Collect the thread after the test
999
self.addCleanup(t1.join)
1000
# Be ready to unblock the thread if the test goes wrong
1001
self.addCleanup(do_writing.set)
1003
# Ensure the thread is ready to write
1004
ready_to_write.wait()
1005
self.assertTrue(c1._lock.is_held)
1006
self.assertEquals('c1', c1.get_user_option('one'))
1007
# If we read during the write, we get the old value
1008
c2 = self.get_existing_config()
1009
self.assertEquals('1', c2.get_user_option('one'))
1010
# Let the writing occur and ensure it occurred
1013
# Now we get the updated value
1014
c3 = self.get_existing_config()
1015
self.assertEquals('c1', c3.get_user_option('one'))
397
1018
class TestGetUserOptionAs(TestIniConfig):
1312
2025
self.assertIs(None, bzrdir_config.get_default_stack_on())
2028
class TestOldConfigHooks(tests.TestCaseWithTransport):
2031
super(TestOldConfigHooks, self).setUp()
2032
create_configs_with_file_option(self)
2034
def assertGetHook(self, conf, name, value):
2038
config.OldConfigHooks.install_named_hook('get', hook, None)
2040
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2041
self.assertLength(0, calls)
2042
actual_value = conf.get_user_option(name)
2043
self.assertEquals(value, actual_value)
2044
self.assertLength(1, calls)
2045
self.assertEquals((conf, name, value), calls[0])
2047
def test_get_hook_bazaar(self):
2048
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2050
def test_get_hook_locations(self):
2051
self.assertGetHook(self.locations_config, 'file', 'locations')
2053
def test_get_hook_branch(self):
2054
# Since locations masks branch, we define a different option
2055
self.branch_config.set_user_option('file2', 'branch')
2056
self.assertGetHook(self.branch_config, 'file2', 'branch')
2058
def assertSetHook(self, conf, name, value):
2062
config.OldConfigHooks.install_named_hook('set', hook, None)
2064
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2065
self.assertLength(0, calls)
2066
conf.set_user_option(name, value)
2067
self.assertLength(1, calls)
2068
# We can't assert the conf object below as different configs use
2069
# different means to implement set_user_option and we care only about
2071
self.assertEquals((name, value), calls[0][1:])
2073
def test_set_hook_bazaar(self):
2074
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2076
def test_set_hook_locations(self):
2077
self.assertSetHook(self.locations_config, 'foo', 'locations')
2079
def test_set_hook_branch(self):
2080
self.assertSetHook(self.branch_config, 'foo', 'branch')
2082
def assertRemoveHook(self, conf, name, section_name=None):
2086
config.OldConfigHooks.install_named_hook('remove', hook, None)
2088
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2089
self.assertLength(0, calls)
2090
conf.remove_user_option(name, section_name)
2091
self.assertLength(1, calls)
2092
# We can't assert the conf object below as different configs use
2093
# different means to implement remove_user_option and we care only about
2095
self.assertEquals((name,), calls[0][1:])
2097
def test_remove_hook_bazaar(self):
2098
self.assertRemoveHook(self.bazaar_config, 'file')
2100
def test_remove_hook_locations(self):
2101
self.assertRemoveHook(self.locations_config, 'file',
2102
self.locations_config.location)
2104
def test_remove_hook_branch(self):
2105
self.assertRemoveHook(self.branch_config, 'file')
2107
def assertLoadHook(self, name, conf_class, *conf_args):
2111
config.OldConfigHooks.install_named_hook('load', hook, None)
2113
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2114
self.assertLength(0, calls)
2116
conf = conf_class(*conf_args)
2117
# Access an option to trigger a load
2118
conf.get_user_option(name)
2119
self.assertLength(1, calls)
2120
# Since we can't assert about conf, we just use the number of calls ;-/
2122
def test_load_hook_bazaar(self):
2123
self.assertLoadHook('file', config.GlobalConfig)
2125
def test_load_hook_locations(self):
2126
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2128
def test_load_hook_branch(self):
2129
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2131
def assertSaveHook(self, conf):
2135
config.OldConfigHooks.install_named_hook('save', hook, None)
2137
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2138
self.assertLength(0, calls)
2139
# Setting an option triggers a save
2140
conf.set_user_option('foo', 'bar')
2141
self.assertLength(1, calls)
2142
# Since we can't assert about conf, we just use the number of calls ;-/
2144
def test_save_hook_bazaar(self):
2145
self.assertSaveHook(self.bazaar_config)
2147
def test_save_hook_locations(self):
2148
self.assertSaveHook(self.locations_config)
2150
def test_save_hook_branch(self):
2151
self.assertSaveHook(self.branch_config)
2154
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2155
"""Tests config hooks for remote configs.
2157
No tests for the remove hook as this is not implemented there.
2161
super(TestOldConfigHooksForRemote, self).setUp()
2162
self.transport_server = test_server.SmartTCPServer_for_testing
2163
create_configs_with_file_option(self)
2165
def assertGetHook(self, conf, name, value):
2169
config.OldConfigHooks.install_named_hook('get', hook, None)
2171
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2172
self.assertLength(0, calls)
2173
actual_value = conf.get_option(name)
2174
self.assertEquals(value, actual_value)
2175
self.assertLength(1, calls)
2176
self.assertEquals((conf, name, value), calls[0])
2178
def test_get_hook_remote_branch(self):
2179
remote_branch = branch.Branch.open(self.get_url('tree'))
2180
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2182
def test_get_hook_remote_bzrdir(self):
2183
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2184
conf = remote_bzrdir._get_config()
2185
conf.set_option('remotedir', 'file')
2186
self.assertGetHook(conf, 'file', 'remotedir')
2188
def assertSetHook(self, conf, name, value):
2192
config.OldConfigHooks.install_named_hook('set', hook, None)
2194
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2195
self.assertLength(0, calls)
2196
conf.set_option(value, name)
2197
self.assertLength(1, calls)
2198
# We can't assert the conf object below as different configs use
2199
# different means to implement set_user_option and we care only about
2201
self.assertEquals((name, value), calls[0][1:])
2203
def test_set_hook_remote_branch(self):
2204
remote_branch = branch.Branch.open(self.get_url('tree'))
2205
self.addCleanup(remote_branch.lock_write().unlock)
2206
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2208
def test_set_hook_remote_bzrdir(self):
2209
remote_branch = branch.Branch.open(self.get_url('tree'))
2210
self.addCleanup(remote_branch.lock_write().unlock)
2211
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2212
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2214
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2218
config.OldConfigHooks.install_named_hook('load', hook, None)
2220
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2221
self.assertLength(0, calls)
2223
conf = conf_class(*conf_args)
2224
# Access an option to trigger a load
2225
conf.get_option(name)
2226
self.assertLength(expected_nb_calls, calls)
2227
# Since we can't assert about conf, we just use the number of calls ;-/
2229
def test_load_hook_remote_branch(self):
2230
remote_branch = branch.Branch.open(self.get_url('tree'))
2231
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2233
def test_load_hook_remote_bzrdir(self):
2234
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2235
# The config file doesn't exist, set an option to force its creation
2236
conf = remote_bzrdir._get_config()
2237
conf.set_option('remotedir', 'file')
2238
# We get one call for the server and one call for the client, this is
2239
# caused by the differences in implementations betwen
2240
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2241
# SmartServerBranchGetConfigFile (in smart/branch.py)
2242
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2244
def assertSaveHook(self, conf):
2248
config.OldConfigHooks.install_named_hook('save', hook, None)
2250
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2251
self.assertLength(0, calls)
2252
# Setting an option triggers a save
2253
conf.set_option('foo', 'bar')
2254
self.assertLength(1, calls)
2255
# Since we can't assert about conf, we just use the number of calls ;-/
2257
def test_save_hook_remote_branch(self):
2258
remote_branch = branch.Branch.open(self.get_url('tree'))
2259
self.addCleanup(remote_branch.lock_write().unlock)
2260
self.assertSaveHook(remote_branch._get_config())
2262
def test_save_hook_remote_bzrdir(self):
2263
remote_branch = branch.Branch.open(self.get_url('tree'))
2264
self.addCleanup(remote_branch.lock_write().unlock)
2265
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2266
self.assertSaveHook(remote_bzrdir._get_config())
2269
class TestOption(tests.TestCase):
2271
def test_default_value(self):
2272
opt = config.Option('foo', default='bar')
2273
self.assertEquals('bar', opt.get_default())
2275
def test_default_value_from_env(self):
2276
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2277
self.overrideEnv('FOO', 'quux')
2278
# Env variable provides a default taking over the option one
2279
self.assertEquals('quux', opt.get_default())
2281
def test_first_default_value_from_env_wins(self):
2282
opt = config.Option('foo', default='bar',
2283
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2284
self.overrideEnv('FOO', 'foo')
2285
self.overrideEnv('BAZ', 'baz')
2286
# The first env var set wins
2287
self.assertEquals('foo', opt.get_default())
2289
def test_not_supported_list_default_value(self):
2290
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2292
def test_not_supported_object_default_value(self):
2293
self.assertRaises(AssertionError, config.Option, 'foo',
2297
class TestOptionConverterMixin(object):
2299
def assertConverted(self, expected, opt, value):
2300
self.assertEquals(expected, opt.convert_from_unicode(value))
2302
def assertWarns(self, opt, value):
2305
warnings.append(args[0] % args[1:])
2306
self.overrideAttr(trace, 'warning', warning)
2307
self.assertEquals(None, opt.convert_from_unicode(value))
2308
self.assertLength(1, warnings)
2310
'Value "%s" is not valid for "%s"' % (value, opt.name),
2313
def assertErrors(self, opt, value):
2314
self.assertRaises(errors.ConfigOptionValueError,
2315
opt.convert_from_unicode, value)
2317
def assertConvertInvalid(self, opt, invalid_value):
2319
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2320
opt.invalid = 'warning'
2321
self.assertWarns(opt, invalid_value)
2322
opt.invalid = 'error'
2323
self.assertErrors(opt, invalid_value)
2326
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2328
def get_option(self):
2329
return config.Option('foo', help='A boolean.',
2330
from_unicode=config.bool_from_store)
2332
def test_convert_invalid(self):
2333
opt = self.get_option()
2334
# A string that is not recognized as a boolean
2335
self.assertConvertInvalid(opt, u'invalid-boolean')
2336
# A list of strings is never recognized as a boolean
2337
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2339
def test_convert_valid(self):
2340
opt = self.get_option()
2341
self.assertConverted(True, opt, u'True')
2342
self.assertConverted(True, opt, u'1')
2343
self.assertConverted(False, opt, u'False')
2346
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2348
def get_option(self):
2349
return config.Option('foo', help='An integer.',
2350
from_unicode=config.int_from_store)
2352
def test_convert_invalid(self):
2353
opt = self.get_option()
2354
# A string that is not recognized as an integer
2355
self.assertConvertInvalid(opt, u'forty-two')
2356
# A list of strings is never recognized as an integer
2357
self.assertConvertInvalid(opt, [u'a', u'list'])
2359
def test_convert_valid(self):
2360
opt = self.get_option()
2361
self.assertConverted(16, opt, u'16')
2363
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2365
def get_option(self):
2366
return config.Option('foo', help='A list.',
2367
from_unicode=config.list_from_store)
2369
def test_convert_invalid(self):
2370
# No string is invalid as all forms can be converted to a list
2373
def test_convert_valid(self):
2374
opt = self.get_option()
2375
# An empty string is an empty list
2376
self.assertConverted([], opt, '') # Using a bare str() just in case
2377
self.assertConverted([], opt, u'')
2379
self.assertConverted([u'True'], opt, u'True')
2381
self.assertConverted([u'42'], opt, u'42')
2383
self.assertConverted([u'bar'], opt, u'bar')
2384
# A list remains a list (configObj will turn a string containing commas
2385
# into a list, but that's not what we're testing here)
2386
self.assertConverted([u'foo', u'1', u'True'],
2387
opt, [u'foo', u'1', u'True'])
2390
class TestOptionRegistry(tests.TestCase):
2393
super(TestOptionRegistry, self).setUp()
2394
# Always start with an empty registry
2395
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2396
self.registry = config.option_registry
2398
def test_register(self):
2399
opt = config.Option('foo')
2400
self.registry.register(opt)
2401
self.assertIs(opt, self.registry.get('foo'))
2403
def test_registered_help(self):
2404
opt = config.Option('foo', help='A simple option')
2405
self.registry.register(opt)
2406
self.assertEquals('A simple option', self.registry.get_help('foo'))
2408
lazy_option = config.Option('lazy_foo', help='Lazy help')
2410
def test_register_lazy(self):
2411
self.registry.register_lazy('lazy_foo', self.__module__,
2412
'TestOptionRegistry.lazy_option')
2413
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2415
def test_registered_lazy_help(self):
2416
self.registry.register_lazy('lazy_foo', self.__module__,
2417
'TestOptionRegistry.lazy_option')
2418
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2421
class TestRegisteredOptions(tests.TestCase):
2422
"""All registered options should verify some constraints."""
2424
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2425
in config.option_registry.iteritems()]
2428
super(TestRegisteredOptions, self).setUp()
2429
self.registry = config.option_registry
2431
def test_proper_name(self):
2432
# An option should be registered under its own name, this can't be
2433
# checked at registration time for the lazy ones.
2434
self.assertEquals(self.option_name, self.option.name)
2436
def test_help_is_set(self):
2437
option_help = self.registry.get_help(self.option_name)
2438
self.assertNotEquals(None, option_help)
2439
# Come on, think about the user, he really wants to know what the
2441
self.assertIsNot(None, option_help)
2442
self.assertNotEquals('', option_help)
2445
class TestSection(tests.TestCase):
2447
# FIXME: Parametrize so that all sections produced by Stores run these
2448
# tests -- vila 2011-04-01
2450
def test_get_a_value(self):
2451
a_dict = dict(foo='bar')
2452
section = config.Section('myID', a_dict)
2453
self.assertEquals('bar', section.get('foo'))
2455
def test_get_unknown_option(self):
2457
section = config.Section(None, a_dict)
2458
self.assertEquals('out of thin air',
2459
section.get('foo', 'out of thin air'))
2461
def test_options_is_shared(self):
2463
section = config.Section(None, a_dict)
2464
self.assertIs(a_dict, section.options)
2467
class TestMutableSection(tests.TestCase):
2469
# FIXME: Parametrize so that all sections (including os.environ and the
2470
# ones produced by Stores) run these tests -- vila 2011-04-01
2473
a_dict = dict(foo='bar')
2474
section = config.MutableSection('myID', a_dict)
2475
section.set('foo', 'new_value')
2476
self.assertEquals('new_value', section.get('foo'))
2477
# The change appears in the shared section
2478
self.assertEquals('new_value', a_dict.get('foo'))
2479
# We keep track of the change
2480
self.assertTrue('foo' in section.orig)
2481
self.assertEquals('bar', section.orig.get('foo'))
2483
def test_set_preserve_original_once(self):
2484
a_dict = dict(foo='bar')
2485
section = config.MutableSection('myID', a_dict)
2486
section.set('foo', 'first_value')
2487
section.set('foo', 'second_value')
2488
# We keep track of the original value
2489
self.assertTrue('foo' in section.orig)
2490
self.assertEquals('bar', section.orig.get('foo'))
2492
def test_remove(self):
2493
a_dict = dict(foo='bar')
2494
section = config.MutableSection('myID', a_dict)
2495
section.remove('foo')
2496
# We get None for unknown options via the default value
2497
self.assertEquals(None, section.get('foo'))
2498
# Or we just get the default value
2499
self.assertEquals('unknown', section.get('foo', 'unknown'))
2500
self.assertFalse('foo' in section.options)
2501
# We keep track of the deletion
2502
self.assertTrue('foo' in section.orig)
2503
self.assertEquals('bar', section.orig.get('foo'))
2505
def test_remove_new_option(self):
2507
section = config.MutableSection('myID', a_dict)
2508
section.set('foo', 'bar')
2509
section.remove('foo')
2510
self.assertFalse('foo' in section.options)
2511
# The option didn't exist initially so it we need to keep track of it
2512
# with a special value
2513
self.assertTrue('foo' in section.orig)
2514
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2517
class TestStore(tests.TestCaseWithTransport):
2519
def assertSectionContent(self, expected, section):
2520
"""Assert that some options have the proper values in a section."""
2521
expected_name, expected_options = expected
2522
self.assertEquals(expected_name, section.id)
2525
dict([(k, section.get(k)) for k in expected_options.keys()]))
2528
class TestReadonlyStore(TestStore):
2530
scenarios = [(key, {'get_store': builder}) for key, builder
2531
in config.test_store_builder_registry.iteritems()]
2534
super(TestReadonlyStore, self).setUp()
2536
def test_building_delays_load(self):
2537
store = self.get_store(self)
2538
self.assertEquals(False, store.is_loaded())
2539
store._load_from_string('')
2540
self.assertEquals(True, store.is_loaded())
2542
def test_get_no_sections_for_empty(self):
2543
store = self.get_store(self)
2544
store._load_from_string('')
2545
self.assertEquals([], list(store.get_sections()))
2547
def test_get_default_section(self):
2548
store = self.get_store(self)
2549
store._load_from_string('foo=bar')
2550
sections = list(store.get_sections())
2551
self.assertLength(1, sections)
2552
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2554
def test_get_named_section(self):
2555
store = self.get_store(self)
2556
store._load_from_string('[baz]\nfoo=bar')
2557
sections = list(store.get_sections())
2558
self.assertLength(1, sections)
2559
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2561
def test_load_from_string_fails_for_non_empty_store(self):
2562
store = self.get_store(self)
2563
store._load_from_string('foo=bar')
2564
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2567
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2568
"""Simulate loading a config store without content of various encodings.
2570
All files produced by bzr are in utf8 content.
2572
Users may modify them manually and end up with a file that can't be
2573
loaded. We need to issue proper error messages in this case.
2576
invalid_utf8_char = '\xff'
2578
def test_load_utf8(self):
2579
"""Ensure we can load an utf8-encoded file."""
2580
t = self.get_transport()
2581
# From http://pad.lv/799212
2582
unicode_user = u'b\N{Euro Sign}ar'
2583
unicode_content = u'user=%s' % (unicode_user,)
2584
utf8_content = unicode_content.encode('utf8')
2585
# Store the raw content in the config file
2586
t.put_bytes('foo.conf', utf8_content)
2587
store = config.IniFileStore(t, 'foo.conf')
2589
stack = config.Stack([store.get_sections], store)
2590
self.assertEquals(unicode_user, stack.get('user'))
2592
def test_load_non_ascii(self):
2593
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2594
t = self.get_transport()
2595
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2596
store = config.IniFileStore(t, 'foo.conf')
2597
self.assertRaises(errors.ConfigContentError, store.load)
2599
def test_load_erroneous_content(self):
2600
"""Ensure we display a proper error on content that can't be parsed."""
2601
t = self.get_transport()
2602
t.put_bytes('foo.conf', '[open_section\n')
2603
store = config.IniFileStore(t, 'foo.conf')
2604
self.assertRaises(errors.ParseConfigError, store.load)
2606
def test_load_permission_denied(self):
2607
"""Ensure we get warned when trying to load an inaccessible file."""
2610
warnings.append(args[0] % args[1:])
2611
self.overrideAttr(trace, 'warning', warning)
2613
t = self.get_transport()
2615
def get_bytes(relpath):
2616
raise errors.PermissionDenied(relpath, "")
2617
t.get_bytes = get_bytes
2618
store = config.IniFileStore(t, 'foo.conf')
2619
self.assertRaises(errors.PermissionDenied, store.load)
2622
[u'Permission denied while trying to load configuration store %s.'
2623
% store.external_url()])
2626
class TestIniConfigContent(tests.TestCaseWithTransport):
2627
"""Simulate loading a IniBasedConfig without content of various encodings.
2629
All files produced by bzr are in utf8 content.
2631
Users may modify them manually and end up with a file that can't be
2632
loaded. We need to issue proper error messages in this case.
2635
invalid_utf8_char = '\xff'
2637
def test_load_utf8(self):
2638
"""Ensure we can load an utf8-encoded file."""
2639
# From http://pad.lv/799212
2640
unicode_user = u'b\N{Euro Sign}ar'
2641
unicode_content = u'user=%s' % (unicode_user,)
2642
utf8_content = unicode_content.encode('utf8')
2643
# Store the raw content in the config file
2644
with open('foo.conf', 'wb') as f:
2645
f.write(utf8_content)
2646
conf = config.IniBasedConfig(file_name='foo.conf')
2647
self.assertEquals(unicode_user, conf.get_user_option('user'))
2649
def test_load_badly_encoded_content(self):
2650
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2651
with open('foo.conf', 'wb') as f:
2652
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2653
conf = config.IniBasedConfig(file_name='foo.conf')
2654
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2656
def test_load_erroneous_content(self):
2657
"""Ensure we display a proper error on content that can't be parsed."""
2658
with open('foo.conf', 'wb') as f:
2659
f.write('[open_section\n')
2660
conf = config.IniBasedConfig(file_name='foo.conf')
2661
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2664
class TestMutableStore(TestStore):
2666
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2667
in config.test_store_builder_registry.iteritems()]
2670
super(TestMutableStore, self).setUp()
2671
self.transport = self.get_transport()
2673
def has_store(self, store):
2674
store_basename = urlutils.relative_url(self.transport.external_url(),
2675
store.external_url())
2676
return self.transport.has(store_basename)
2678
def test_save_empty_creates_no_file(self):
2679
# FIXME: There should be a better way than relying on the test
2680
# parametrization to identify branch.conf -- vila 2011-0526
2681
if self.store_id in ('branch', 'remote_branch'):
2682
raise tests.TestNotApplicable(
2683
'branch.conf is *always* created when a branch is initialized')
2684
store = self.get_store(self)
2686
self.assertEquals(False, self.has_store(store))
2688
def test_save_emptied_succeeds(self):
2689
store = self.get_store(self)
2690
store._load_from_string('foo=bar\n')
2691
section = store.get_mutable_section(None)
2692
section.remove('foo')
2694
self.assertEquals(True, self.has_store(store))
2695
modified_store = self.get_store(self)
2696
sections = list(modified_store.get_sections())
2697
self.assertLength(0, sections)
2699
def test_save_with_content_succeeds(self):
2700
# FIXME: There should be a better way than relying on the test
2701
# parametrization to identify branch.conf -- vila 2011-0526
2702
if self.store_id in ('branch', 'remote_branch'):
2703
raise tests.TestNotApplicable(
2704
'branch.conf is *always* created when a branch is initialized')
2705
store = self.get_store(self)
2706
store._load_from_string('foo=bar\n')
2707
self.assertEquals(False, self.has_store(store))
2709
self.assertEquals(True, self.has_store(store))
2710
modified_store = self.get_store(self)
2711
sections = list(modified_store.get_sections())
2712
self.assertLength(1, sections)
2713
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2715
def test_set_option_in_empty_store(self):
2716
store = self.get_store(self)
2717
section = store.get_mutable_section(None)
2718
section.set('foo', 'bar')
2720
modified_store = self.get_store(self)
2721
sections = list(modified_store.get_sections())
2722
self.assertLength(1, sections)
2723
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2725
def test_set_option_in_default_section(self):
2726
store = self.get_store(self)
2727
store._load_from_string('')
2728
section = store.get_mutable_section(None)
2729
section.set('foo', 'bar')
2731
modified_store = self.get_store(self)
2732
sections = list(modified_store.get_sections())
2733
self.assertLength(1, sections)
2734
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2736
def test_set_option_in_named_section(self):
2737
store = self.get_store(self)
2738
store._load_from_string('')
2739
section = store.get_mutable_section('baz')
2740
section.set('foo', 'bar')
2742
modified_store = self.get_store(self)
2743
sections = list(modified_store.get_sections())
2744
self.assertLength(1, sections)
2745
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2747
def test_load_hook(self):
2748
# We first needs to ensure that the store exists
2749
store = self.get_store(self)
2750
section = store.get_mutable_section('baz')
2751
section.set('foo', 'bar')
2753
# Now we can try to load it
2754
store = self.get_store(self)
2758
config.ConfigHooks.install_named_hook('load', hook, None)
2759
self.assertLength(0, calls)
2761
self.assertLength(1, calls)
2762
self.assertEquals((store,), calls[0])
2764
def test_save_hook(self):
2768
config.ConfigHooks.install_named_hook('save', hook, None)
2769
self.assertLength(0, calls)
2770
store = self.get_store(self)
2771
section = store.get_mutable_section('baz')
2772
section.set('foo', 'bar')
2774
self.assertLength(1, calls)
2775
self.assertEquals((store,), calls[0])
2778
class TestIniFileStore(TestStore):
2780
def test_loading_unknown_file_fails(self):
2781
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2782
self.assertRaises(errors.NoSuchFile, store.load)
2784
def test_invalid_content(self):
2785
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2786
self.assertEquals(False, store.is_loaded())
2787
exc = self.assertRaises(
2788
errors.ParseConfigError, store._load_from_string,
2789
'this is invalid !')
2790
self.assertEndsWith(exc.filename, 'foo.conf')
2791
# And the load failed
2792
self.assertEquals(False, store.is_loaded())
2794
def test_get_embedded_sections(self):
2795
# A more complicated example (which also shows that section names and
2796
# option names share the same name space...)
2797
# FIXME: This should be fixed by forbidding dicts as values ?
2798
# -- vila 2011-04-05
2799
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2800
store._load_from_string('''
2804
foo_in_DEFAULT=foo_DEFAULT
2812
sections = list(store.get_sections())
2813
self.assertLength(4, sections)
2814
# The default section has no name.
2815
# List values are provided as lists
2816
self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2818
self.assertSectionContent(
2819
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2820
self.assertSectionContent(
2821
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2822
# sub sections are provided as embedded dicts.
2823
self.assertSectionContent(
2824
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2828
class TestLockableIniFileStore(TestStore):
2830
def test_create_store_in_created_dir(self):
2831
self.assertPathDoesNotExist('dir')
2832
t = self.get_transport('dir/subdir')
2833
store = config.LockableIniFileStore(t, 'foo.conf')
2834
store.get_mutable_section(None).set('foo', 'bar')
2836
self.assertPathExists('dir/subdir')
2839
class TestConcurrentStoreUpdates(TestStore):
2840
"""Test that Stores properly handle conccurent updates.
2842
New Store implementation may fail some of these tests but until such
2843
implementations exist it's hard to properly filter them from the scenarios
2844
applied here. If you encounter such a case, contact the bzr devs.
2847
scenarios = [(key, {'get_stack': builder}) for key, builder
2848
in config.test_stack_builder_registry.iteritems()]
2851
super(TestConcurrentStoreUpdates, self).setUp()
2852
self._content = 'one=1\ntwo=2\n'
2853
self.stack = self.get_stack(self)
2854
if not isinstance(self.stack, config._CompatibleStack):
2855
raise tests.TestNotApplicable(
2856
'%s is not meant to be compatible with the old config design'
2858
self.stack.store._load_from_string(self._content)
2860
self.stack.store.save()
2862
def test_simple_read_access(self):
2863
self.assertEquals('1', self.stack.get('one'))
2865
def test_simple_write_access(self):
2866
self.stack.set('one', 'one')
2867
self.assertEquals('one', self.stack.get('one'))
2869
def test_listen_to_the_last_speaker(self):
2871
c2 = self.get_stack(self)
2872
c1.set('one', 'ONE')
2873
c2.set('two', 'TWO')
2874
self.assertEquals('ONE', c1.get('one'))
2875
self.assertEquals('TWO', c2.get('two'))
2876
# The second update respect the first one
2877
self.assertEquals('ONE', c2.get('one'))
2879
def test_last_speaker_wins(self):
2880
# If the same config is not shared, the same variable modified twice
2881
# can only see a single result.
2883
c2 = self.get_stack(self)
2886
self.assertEquals('c2', c2.get('one'))
2887
# The first modification is still available until another refresh
2889
self.assertEquals('c1', c1.get('one'))
2890
c1.set('two', 'done')
2891
self.assertEquals('c2', c1.get('one'))
2893
def test_writes_are_serialized(self):
2895
c2 = self.get_stack(self)
2897
# We spawn a thread that will pause *during* the config saving.
2898
before_writing = threading.Event()
2899
after_writing = threading.Event()
2900
writing_done = threading.Event()
2901
c1_save_without_locking_orig = c1.store.save_without_locking
2902
def c1_save_without_locking():
2903
before_writing.set()
2904
c1_save_without_locking_orig()
2905
# The lock is held. We wait for the main thread to decide when to
2907
after_writing.wait()
2908
c1.store.save_without_locking = c1_save_without_locking
2912
t1 = threading.Thread(target=c1_set)
2913
# Collect the thread after the test
2914
self.addCleanup(t1.join)
2915
# Be ready to unblock the thread if the test goes wrong
2916
self.addCleanup(after_writing.set)
2918
before_writing.wait()
2919
self.assertRaises(errors.LockContention,
2920
c2.set, 'one', 'c2')
2921
self.assertEquals('c1', c1.get('one'))
2922
# Let the lock be released
2926
self.assertEquals('c2', c2.get('one'))
2928
def test_read_while_writing(self):
2930
# We spawn a thread that will pause *during* the write
2931
ready_to_write = threading.Event()
2932
do_writing = threading.Event()
2933
writing_done = threading.Event()
2934
# We override the _save implementation so we know the store is locked
2935
c1_save_without_locking_orig = c1.store.save_without_locking
2936
def c1_save_without_locking():
2937
ready_to_write.set()
2938
# The lock is held. We wait for the main thread to decide when to
2941
c1_save_without_locking_orig()
2943
c1.store.save_without_locking = c1_save_without_locking
2946
t1 = threading.Thread(target=c1_set)
2947
# Collect the thread after the test
2948
self.addCleanup(t1.join)
2949
# Be ready to unblock the thread if the test goes wrong
2950
self.addCleanup(do_writing.set)
2952
# Ensure the thread is ready to write
2953
ready_to_write.wait()
2954
self.assertEquals('c1', c1.get('one'))
2955
# If we read during the write, we get the old value
2956
c2 = self.get_stack(self)
2957
self.assertEquals('1', c2.get('one'))
2958
# Let the writing occur and ensure it occurred
2961
# Now we get the updated value
2962
c3 = self.get_stack(self)
2963
self.assertEquals('c1', c3.get('one'))
2965
# FIXME: It may be worth looking into removing the lock dir when it's not
2966
# needed anymore and look at possible fallouts for concurrent lockers. This
2967
# will matter if/when we use config files outside of bazaar directories
2968
# (.bazaar or .bzr) -- vila 20110-04-11
2971
class TestSectionMatcher(TestStore):
2973
scenarios = [('location', {'matcher': config.LocationMatcher})]
2975
def get_store(self, file_name):
2976
return config.IniFileStore(self.get_readonly_transport(), file_name)
2978
def test_no_matches_for_empty_stores(self):
2979
store = self.get_store('foo.conf')
2980
store._load_from_string('')
2981
matcher = self.matcher(store, '/bar')
2982
self.assertEquals([], list(matcher.get_sections()))
2984
def test_build_doesnt_load_store(self):
2985
store = self.get_store('foo.conf')
2986
matcher = self.matcher(store, '/bar')
2987
self.assertFalse(store.is_loaded())
2990
class TestLocationSection(tests.TestCase):
2992
def get_section(self, options, extra_path):
2993
section = config.Section('foo', options)
2994
# We don't care about the length so we use '0'
2995
return config.LocationSection(section, 0, extra_path)
2997
def test_simple_option(self):
2998
section = self.get_section({'foo': 'bar'}, '')
2999
self.assertEquals('bar', section.get('foo'))
3001
def test_option_with_extra_path(self):
3002
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3004
self.assertEquals('bar/baz', section.get('foo'))
3006
def test_invalid_policy(self):
3007
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3009
# invalid policies are ignored
3010
self.assertEquals('bar', section.get('foo'))
3013
class TestLocationMatcher(TestStore):
3015
def get_store(self, file_name):
3016
return config.IniFileStore(self.get_readonly_transport(), file_name)
3018
def test_unrelated_section_excluded(self):
3019
store = self.get_store('foo.conf')
3020
store._load_from_string('''
3028
section=/foo/bar/baz
3032
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3034
[section.id for section in store.get_sections()])
3035
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3036
sections = list(matcher.get_sections())
3037
self.assertEquals([3, 2],
3038
[section.length for section in sections])
3039
self.assertEquals(['/foo/bar', '/foo'],
3040
[section.id for section in sections])
3041
self.assertEquals(['quux', 'bar/quux'],
3042
[section.extra_path for section in sections])
3044
def test_more_specific_sections_first(self):
3045
store = self.get_store('foo.conf')
3046
store._load_from_string('''
3052
self.assertEquals(['/foo', '/foo/bar'],
3053
[section.id for section in store.get_sections()])
3054
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3055
sections = list(matcher.get_sections())
3056
self.assertEquals([3, 2],
3057
[section.length for section in sections])
3058
self.assertEquals(['/foo/bar', '/foo'],
3059
[section.id for section in sections])
3060
self.assertEquals(['baz', 'bar/baz'],
3061
[section.extra_path for section in sections])
3063
def test_appendpath_in_no_name_section(self):
3064
# It's a bit weird to allow appendpath in a no-name section, but
3065
# someone may found a use for it
3066
store = self.get_store('foo.conf')
3067
store._load_from_string('''
3069
foo:policy = appendpath
3071
matcher = config.LocationMatcher(store, 'dir/subdir')
3072
sections = list(matcher.get_sections())
3073
self.assertLength(1, sections)
3074
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
3076
def test_file_urls_are_normalized(self):
3077
store = self.get_store('foo.conf')
3078
if sys.platform == 'win32':
3079
expected_url = 'file:///C:/dir/subdir'
3080
expected_location = 'C:/dir/subdir'
3082
expected_url = 'file:///dir/subdir'
3083
expected_location = '/dir/subdir'
3084
matcher = config.LocationMatcher(store, expected_url)
3085
self.assertEquals(expected_location, matcher.location)
3088
class TestStackGet(tests.TestCase):
3090
# FIXME: This should be parametrized for all known Stack or dedicated
3091
# paramerized tests created to avoid bloating -- vila 2011-03-31
3093
def overrideOptionRegistry(self):
3094
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3096
def test_single_config_get(self):
3097
conf = dict(foo='bar')
3098
conf_stack = config.Stack([conf])
3099
self.assertEquals('bar', conf_stack.get('foo'))
3101
def test_get_with_registered_default_value(self):
3102
conf_stack = config.Stack([dict()])
3103
opt = config.Option('foo', default='bar')
3104
self.overrideOptionRegistry()
3105
config.option_registry.register('foo', opt)
3106
self.assertEquals('bar', conf_stack.get('foo'))
3108
def test_get_without_registered_default_value(self):
3109
conf_stack = config.Stack([dict()])
3110
opt = config.Option('foo')
3111
self.overrideOptionRegistry()
3112
config.option_registry.register('foo', opt)
3113
self.assertEquals(None, conf_stack.get('foo'))
3115
def test_get_without_default_value_for_not_registered(self):
3116
conf_stack = config.Stack([dict()])
3117
opt = config.Option('foo')
3118
self.overrideOptionRegistry()
3119
self.assertEquals(None, conf_stack.get('foo'))
3121
def test_get_first_definition(self):
3122
conf1 = dict(foo='bar')
3123
conf2 = dict(foo='baz')
3124
conf_stack = config.Stack([conf1, conf2])
3125
self.assertEquals('bar', conf_stack.get('foo'))
3127
def test_get_embedded_definition(self):
3128
conf1 = dict(yy='12')
3129
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3130
conf_stack = config.Stack([conf1, conf2])
3131
self.assertEquals('baz', conf_stack.get('foo'))
3133
def test_get_for_empty_section_callable(self):
3134
conf_stack = config.Stack([lambda : []])
3135
self.assertEquals(None, conf_stack.get('foo'))
3137
def test_get_for_broken_callable(self):
3138
# Trying to use and invalid callable raises an exception on first use
3139
conf_stack = config.Stack([lambda : object()])
3140
self.assertRaises(TypeError, conf_stack.get, 'foo')
3143
class TestStackWithTransport(tests.TestCaseWithTransport):
3145
scenarios = [(key, {'get_stack': builder}) for key, builder
3146
in config.test_stack_builder_registry.iteritems()]
3149
class TestConcreteStacks(TestStackWithTransport):
3151
def test_build_stack(self):
3152
# Just a smoke test to help debug builders
3153
stack = self.get_stack(self)
3156
class TestStackGet(TestStackWithTransport):
3159
super(TestStackGet, self).setUp()
3160
self.conf = self.get_stack(self)
3162
def test_get_for_empty_stack(self):
3163
self.assertEquals(None, self.conf.get('foo'))
3165
def test_get_hook(self):
3166
self.conf.store._load_from_string('foo=bar')
3170
config.ConfigHooks.install_named_hook('get', hook, None)
3171
self.assertLength(0, calls)
3172
value = self.conf.get('foo')
3173
self.assertEquals('bar', value)
3174
self.assertLength(1, calls)
3175
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3178
class TestStackGetWithConverter(TestStackGet):
3181
super(TestStackGetWithConverter, self).setUp()
3182
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3183
self.registry = config.option_registry
3185
def register_bool_option(self, name, default=None, default_from_env=None):
3186
b = config.Option(name, help='A boolean.',
3187
default=default, default_from_env=default_from_env,
3188
from_unicode=config.bool_from_store)
3189
self.registry.register(b)
3191
def test_get_default_bool_None(self):
3192
self.register_bool_option('foo')
3193
self.assertEquals(None, self.conf.get('foo'))
3195
def test_get_default_bool_True(self):
3196
self.register_bool_option('foo', u'True')
3197
self.assertEquals(True, self.conf.get('foo'))
3199
def test_get_default_bool_False(self):
3200
self.register_bool_option('foo', False)
3201
self.assertEquals(False, self.conf.get('foo'))
3203
def test_get_default_bool_False_as_string(self):
3204
self.register_bool_option('foo', u'False')
3205
self.assertEquals(False, self.conf.get('foo'))
3207
def test_get_default_bool_from_env_converted(self):
3208
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3209
self.overrideEnv('FOO', 'False')
3210
self.assertEquals(False, self.conf.get('foo'))
3212
def test_get_default_bool_when_conversion_fails(self):
3213
self.register_bool_option('foo', default='True')
3214
self.conf.store._load_from_string('foo=invalid boolean')
3215
self.assertEquals(True, self.conf.get('foo'))
3217
def register_integer_option(self, name,
3218
default=None, default_from_env=None):
3219
i = config.Option(name, help='An integer.',
3220
default=default, default_from_env=default_from_env,
3221
from_unicode=config.int_from_store)
3222
self.registry.register(i)
3224
def test_get_default_integer_None(self):
3225
self.register_integer_option('foo')
3226
self.assertEquals(None, self.conf.get('foo'))
3228
def test_get_default_integer(self):
3229
self.register_integer_option('foo', 42)
3230
self.assertEquals(42, self.conf.get('foo'))
3232
def test_get_default_integer_as_string(self):
3233
self.register_integer_option('foo', u'42')
3234
self.assertEquals(42, self.conf.get('foo'))
3236
def test_get_default_integer_from_env(self):
3237
self.register_integer_option('foo', default_from_env=['FOO'])
3238
self.overrideEnv('FOO', '18')
3239
self.assertEquals(18, self.conf.get('foo'))
3241
def test_get_default_integer_when_conversion_fails(self):
3242
self.register_integer_option('foo', default='12')
3243
self.conf.store._load_from_string('foo=invalid integer')
3244
self.assertEquals(12, self.conf.get('foo'))
3246
def register_list_option(self, name, default=None, default_from_env=None):
3247
l = config.Option(name, help='A list.',
3248
default=default, default_from_env=default_from_env,
3249
from_unicode=config.list_from_store)
3250
self.registry.register(l)
3252
def test_get_default_list_None(self):
3253
self.register_list_option('foo')
3254
self.assertEquals(None, self.conf.get('foo'))
3256
def test_get_default_list_empty(self):
3257
self.register_list_option('foo', '')
3258
self.assertEquals([], self.conf.get('foo'))
3260
def test_get_default_list_from_env(self):
3261
self.register_list_option('foo', default_from_env=['FOO'])
3262
self.overrideEnv('FOO', '')
3263
self.assertEquals([], self.conf.get('foo'))
3265
def test_get_with_list_converter_no_item(self):
3266
self.register_list_option('foo', None)
3267
self.conf.store._load_from_string('foo=,')
3268
self.assertEquals([], self.conf.get('foo'))
3270
def test_get_with_list_converter_many_items(self):
3271
self.register_list_option('foo', None)
3272
self.conf.store._load_from_string('foo=m,o,r,e')
3273
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3276
class TestStackSet(TestStackWithTransport):
3278
def test_simple_set(self):
3279
conf = self.get_stack(self)
3280
conf.store._load_from_string('foo=bar')
3281
self.assertEquals('bar', conf.get('foo'))
3282
conf.set('foo', 'baz')
3283
# Did we get it back ?
3284
self.assertEquals('baz', conf.get('foo'))
3286
def test_set_creates_a_new_section(self):
3287
conf = self.get_stack(self)
3288
conf.set('foo', 'baz')
3289
self.assertEquals, 'baz', conf.get('foo')
3291
def test_set_hook(self):
3295
config.ConfigHooks.install_named_hook('set', hook, None)
3296
self.assertLength(0, calls)
3297
conf = self.get_stack(self)
3298
conf.set('foo', 'bar')
3299
self.assertLength(1, calls)
3300
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3303
class TestStackRemove(TestStackWithTransport):
3305
def test_remove_existing(self):
3306
conf = self.get_stack(self)
3307
conf.store._load_from_string('foo=bar')
3308
self.assertEquals('bar', conf.get('foo'))
3310
# Did we get it back ?
3311
self.assertEquals(None, conf.get('foo'))
3313
def test_remove_unknown(self):
3314
conf = self.get_stack(self)
3315
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3317
def test_remove_hook(self):
3321
config.ConfigHooks.install_named_hook('remove', hook, None)
3322
self.assertLength(0, calls)
3323
conf = self.get_stack(self)
3324
conf.store._load_from_string('foo=bar')
3326
self.assertLength(1, calls)
3327
self.assertEquals((conf, 'foo'), calls[0])
3330
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3333
super(TestConfigGetOptions, self).setUp()
3334
create_configs(self)
3336
def test_no_variable(self):
3337
# Using branch should query branch, locations and bazaar
3338
self.assertOptions([], self.branch_config)
3340
def test_option_in_bazaar(self):
3341
self.bazaar_config.set_user_option('file', 'bazaar')
3342
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3345
def test_option_in_locations(self):
3346
self.locations_config.set_user_option('file', 'locations')
3348
[('file', 'locations', self.tree.basedir, 'locations')],
3349
self.locations_config)
3351
def test_option_in_branch(self):
3352
self.branch_config.set_user_option('file', 'branch')
3353
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3356
def test_option_in_bazaar_and_branch(self):
3357
self.bazaar_config.set_user_option('file', 'bazaar')
3358
self.branch_config.set_user_option('file', 'branch')
3359
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3360
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3363
def test_option_in_branch_and_locations(self):
3364
# Hmm, locations override branch :-/
3365
self.locations_config.set_user_option('file', 'locations')
3366
self.branch_config.set_user_option('file', 'branch')
3368
[('file', 'locations', self.tree.basedir, 'locations'),
3369
('file', 'branch', 'DEFAULT', 'branch'),],
3372
def test_option_in_bazaar_locations_and_branch(self):
3373
self.bazaar_config.set_user_option('file', 'bazaar')
3374
self.locations_config.set_user_option('file', 'locations')
3375
self.branch_config.set_user_option('file', 'branch')
3377
[('file', 'locations', self.tree.basedir, 'locations'),
3378
('file', 'branch', 'DEFAULT', 'branch'),
3379
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3383
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3386
super(TestConfigRemoveOption, self).setUp()
3387
create_configs_with_file_option(self)
3389
def test_remove_in_locations(self):
3390
self.locations_config.remove_user_option('file', self.tree.basedir)
3392
[('file', 'branch', 'DEFAULT', 'branch'),
3393
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3396
def test_remove_in_branch(self):
3397
self.branch_config.remove_user_option('file')
3399
[('file', 'locations', self.tree.basedir, 'locations'),
3400
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3403
def test_remove_in_bazaar(self):
3404
self.bazaar_config.remove_user_option('file')
3406
[('file', 'locations', self.tree.basedir, 'locations'),
3407
('file', 'branch', 'DEFAULT', 'branch'),],
3411
class TestConfigGetSections(tests.TestCaseWithTransport):
3414
super(TestConfigGetSections, self).setUp()
3415
create_configs(self)
3417
def assertSectionNames(self, expected, conf, name=None):
3418
"""Check which sections are returned for a given config.
3420
If fallback configurations exist their sections can be included.
3422
:param expected: A list of section names.
3424
:param conf: The configuration that will be queried.
3426
:param name: An optional section name that will be passed to
3429
sections = list(conf._get_sections(name))
3430
self.assertLength(len(expected), sections)
3431
self.assertEqual(expected, [name for name, _, _ in sections])
3433
def test_bazaar_default_section(self):
3434
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3436
def test_locations_default_section(self):
3437
# No sections are defined in an empty file
3438
self.assertSectionNames([], self.locations_config)
3440
def test_locations_named_section(self):
3441
self.locations_config.set_user_option('file', 'locations')
3442
self.assertSectionNames([self.tree.basedir], self.locations_config)
3444
def test_locations_matching_sections(self):
3445
loc_config = self.locations_config
3446
loc_config.set_user_option('file', 'locations')
3447
# We need to cheat a bit here to create an option in sections above and
3448
# below the 'location' one.
3449
parser = loc_config._get_parser()
3450
# locations.cong deals with '/' ignoring native os.sep
3451
location_names = self.tree.basedir.split('/')
3452
parent = '/'.join(location_names[:-1])
3453
child = '/'.join(location_names + ['child'])
3455
parser[parent]['file'] = 'parent'
3457
parser[child]['file'] = 'child'
3458
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3460
def test_branch_data_default_section(self):
3461
self.assertSectionNames([None],
3462
self.branch_config._get_branch_data_config())
3464
def test_branch_default_sections(self):
3465
# No sections are defined in an empty locations file
3466
self.assertSectionNames([None, 'DEFAULT'],
3468
# Unless we define an option
3469
self.branch_config._get_location_config().set_user_option(
3470
'file', 'locations')
3471
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3474
def test_bazaar_named_section(self):
3475
# We need to cheat as the API doesn't give direct access to sections
3476
# other than DEFAULT.
3477
self.bazaar_config.set_alias('bazaar', 'bzr')
3478
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
3481
class TestAuthenticationConfigFile(tests.TestCase):
1316
3482
"""Test the authentication.conf file matching"""