367
579
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
582
class TestXDGConfigDir(tests.TestCaseInTempDir):
583
# must be in temp dir because config tests for the existence of the bazaar
584
# subdirectory of $XDG_CONFIG_HOME
587
if sys.platform in ('darwin', 'win32'):
588
raise tests.TestNotApplicable(
589
'XDG config dir not used on this platform')
590
super(TestXDGConfigDir, self).setUp()
591
self.overrideEnv('HOME', self.test_home_dir)
592
# BZR_HOME overrides everything we want to test so unset it.
593
self.overrideEnv('BZR_HOME', None)
595
def test_xdg_config_dir_exists(self):
596
"""When ~/.config/bazaar exists, use it as the config dir."""
597
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
599
self.assertEqual(config.config_dir(), newdir)
601
def test_xdg_config_home(self):
602
"""When XDG_CONFIG_HOME is set, use it."""
603
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
604
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
605
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
607
self.assertEqual(config.config_dir(), newdir)
610
class TestIniConfig(tests.TestCaseInTempDir):
372
612
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
613
conf = config.IniBasedConfig.from_string(s)
614
return conf, conf._get_parser()
378
617
class TestIniConfigBuilding(TestIniConfig):
380
619
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
620
my_config = config.IniBasedConfig()
383
622
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))
623
my_config = config.IniBasedConfig.from_string(sample_config_text)
624
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
626
def test_cached(self):
627
my_config = config.IniBasedConfig.from_string(sample_config_text)
628
parser = my_config._get_parser()
629
self.assertTrue(my_config._get_parser() is parser)
631
def _dummy_chown(self, path, uid, gid):
632
self.path, self.uid, self.gid = path, uid, gid
634
def test_ini_config_ownership(self):
635
"""Ensure that chown is happening during _write_config_file"""
636
self.requireFeature(features.chown_feature)
637
self.overrideAttr(os, 'chown', self._dummy_chown)
638
self.path = self.uid = self.gid = None
639
conf = config.IniBasedConfig(file_name='./foo.conf')
640
conf._write_config_file()
641
self.assertEquals(self.path, './foo.conf')
642
self.assertTrue(isinstance(self.uid, int))
643
self.assertTrue(isinstance(self.gid, int))
645
def test_get_filename_parameter_is_deprecated_(self):
646
conf = self.callDeprecated([
647
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
648
' Use file_name instead.'],
649
config.IniBasedConfig, lambda: 'ini.conf')
650
self.assertEqual('ini.conf', conf.file_name)
652
def test_get_parser_file_parameter_is_deprecated_(self):
391
653
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)
654
conf = config.IniBasedConfig.from_string(sample_config_text)
655
conf = self.callDeprecated([
656
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
657
' Use IniBasedConfig(_content=xxx) instead.'],
658
conf._get_parser, file=config_file)
661
class TestIniConfigSaving(tests.TestCaseInTempDir):
663
def test_cant_save_without_a_file_name(self):
664
conf = config.IniBasedConfig()
665
self.assertRaises(AssertionError, conf._write_config_file)
667
def test_saved_with_content(self):
668
content = 'foo = bar\n'
669
conf = config.IniBasedConfig.from_string(
670
content, file_name='./test.conf', save=True)
671
self.assertFileEqual(content, 'test.conf')
674
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
675
"""What is the default value of expand for config options.
677
This is an opt-in beta feature used to evaluate whether or not option
678
references can appear in dangerous place raising exceptions, disapearing
679
(and as such corrupting data) or if it's safe to activate the option by
682
Note that these tests relies on config._expand_default_value being already
683
overwritten in the parent class setUp.
687
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
691
self.warnings.append(args[0] % args[1:])
692
self.overrideAttr(trace, 'warning', warning)
694
def get_config(self, expand):
695
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
699
def assertExpandIs(self, expected):
700
actual = config._get_expand_default_value()
701
#self.config.get_user_option_as_bool('bzr.config.expand')
702
self.assertEquals(expected, actual)
704
def test_default_is_None(self):
705
self.assertEquals(None, config._expand_default_value)
707
def test_default_is_False_even_if_None(self):
708
self.config = self.get_config(None)
709
self.assertExpandIs(False)
711
def test_default_is_False_even_if_invalid(self):
712
self.config = self.get_config('<your choice>')
713
self.assertExpandIs(False)
715
# Huh ? My choice is False ? Thanks, always happy to hear that :D
716
# Wait, you've been warned !
717
self.assertLength(1, self.warnings)
719
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
722
def test_default_is_True(self):
723
self.config = self.get_config(True)
724
self.assertExpandIs(True)
726
def test_default_is_False(self):
727
self.config = self.get_config(False)
728
self.assertExpandIs(False)
731
class TestIniConfigOptionExpansion(tests.TestCase):
732
"""Test option expansion from the IniConfig level.
734
What we really want here is to test the Config level, but the class being
735
abstract as far as storing values is concerned, this can't be done
738
# FIXME: This should be rewritten when all configs share a storage
739
# implementation -- vila 2011-02-18
741
def get_config(self, string=None):
744
c = config.IniBasedConfig.from_string(string)
747
def assertExpansion(self, expected, conf, string, env=None):
748
self.assertEquals(expected, conf.expand_options(string, env))
750
def test_no_expansion(self):
751
c = self.get_config('')
752
self.assertExpansion('foo', c, 'foo')
754
def test_env_adding_options(self):
755
c = self.get_config('')
756
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
758
def test_env_overriding_options(self):
759
c = self.get_config('foo=baz')
760
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
762
def test_simple_ref(self):
763
c = self.get_config('foo=xxx')
764
self.assertExpansion('xxx', c, '{foo}')
766
def test_unknown_ref(self):
767
c = self.get_config('')
768
self.assertRaises(errors.ExpandingUnknownOption,
769
c.expand_options, '{foo}')
771
def test_indirect_ref(self):
772
c = self.get_config('''
776
self.assertExpansion('xxx', c, '{bar}')
778
def test_embedded_ref(self):
779
c = self.get_config('''
783
self.assertExpansion('xxx', c, '{{bar}}')
785
def test_simple_loop(self):
786
c = self.get_config('foo={foo}')
787
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
789
def test_indirect_loop(self):
790
c = self.get_config('''
794
e = self.assertRaises(errors.OptionExpansionLoop,
795
c.expand_options, '{foo}')
796
self.assertEquals('foo->bar->baz', e.refs)
797
self.assertEquals('{foo}', e.string)
800
conf = self.get_config('''
804
list={foo},{bar},{baz}
806
self.assertEquals(['start', 'middle', 'end'],
807
conf.get_user_option('list', expand=True))
809
def test_cascading_list(self):
810
conf = self.get_config('''
816
self.assertEquals(['start', 'middle', 'end'],
817
conf.get_user_option('list', expand=True))
819
def test_pathological_hidden_list(self):
820
conf = self.get_config('''
826
hidden={start}{middle}{end}
828
# Nope, it's either a string or a list, and the list wins as soon as a
829
# ',' appears, so the string concatenation never occur.
830
self.assertEquals(['{foo', '}', '{', 'bar}'],
831
conf.get_user_option('hidden', expand=True))
834
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
836
def get_config(self, location, string=None):
839
# Since we don't save the config we won't strictly require to inherit
840
# from TestCaseInTempDir, but an error occurs so quickly...
841
c = config.LocationConfig.from_string(string, location)
844
def test_dont_cross_unrelated_section(self):
845
c = self.get_config('/another/branch/path','''
850
[/another/branch/path]
853
self.assertRaises(errors.ExpandingUnknownOption,
854
c.get_user_option, 'bar', expand=True)
856
def test_cross_related_sections(self):
857
c = self.get_config('/project/branch/path','''
861
[/project/branch/path]
864
self.assertEquals('quux', c.get_user_option('bar', expand=True))
867
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
869
def test_cannot_reload_without_name(self):
870
conf = config.IniBasedConfig.from_string(sample_config_text)
871
self.assertRaises(AssertionError, conf.reload)
873
def test_reload_see_new_value(self):
874
c1 = config.IniBasedConfig.from_string('editor=vim\n',
875
file_name='./test/conf')
876
c1._write_config_file()
877
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
878
file_name='./test/conf')
879
c2._write_config_file()
880
self.assertEqual('vim', c1.get_user_option('editor'))
881
self.assertEqual('emacs', c2.get_user_option('editor'))
882
# Make sure we get the Right value
884
self.assertEqual('emacs', c1.get_user_option('editor'))
887
class TestLockableConfig(tests.TestCaseInTempDir):
889
scenarios = lockable_config_scenarios()
894
config_section = None
897
super(TestLockableConfig, self).setUp()
898
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
899
self.config = self.create_config(self._content)
901
def get_existing_config(self):
902
return self.config_class(*self.config_args)
904
def create_config(self, content):
905
kwargs = dict(save=True)
906
c = self.config_class.from_string(content, *self.config_args, **kwargs)
909
def test_simple_read_access(self):
910
self.assertEquals('1', self.config.get_user_option('one'))
912
def test_simple_write_access(self):
913
self.config.set_user_option('one', 'one')
914
self.assertEquals('one', self.config.get_user_option('one'))
916
def test_listen_to_the_last_speaker(self):
918
c2 = self.get_existing_config()
919
c1.set_user_option('one', 'ONE')
920
c2.set_user_option('two', 'TWO')
921
self.assertEquals('ONE', c1.get_user_option('one'))
922
self.assertEquals('TWO', c2.get_user_option('two'))
923
# The second update respect the first one
924
self.assertEquals('ONE', c2.get_user_option('one'))
926
def test_last_speaker_wins(self):
927
# If the same config is not shared, the same variable modified twice
928
# can only see a single result.
930
c2 = self.get_existing_config()
931
c1.set_user_option('one', 'c1')
932
c2.set_user_option('one', 'c2')
933
self.assertEquals('c2', c2._get_user_option('one'))
934
# The first modification is still available until another refresh
936
self.assertEquals('c1', c1._get_user_option('one'))
937
c1.set_user_option('two', 'done')
938
self.assertEquals('c2', c1._get_user_option('one'))
940
def test_writes_are_serialized(self):
942
c2 = self.get_existing_config()
944
# We spawn a thread that will pause *during* the write
945
before_writing = threading.Event()
946
after_writing = threading.Event()
947
writing_done = threading.Event()
948
c1_orig = c1._write_config_file
949
def c1_write_config_file():
952
# The lock is held. We wait for the main thread to decide when to
955
c1._write_config_file = c1_write_config_file
957
c1.set_user_option('one', 'c1')
959
t1 = threading.Thread(target=c1_set_option)
960
# Collect the thread after the test
961
self.addCleanup(t1.join)
962
# Be ready to unblock the thread if the test goes wrong
963
self.addCleanup(after_writing.set)
965
before_writing.wait()
966
self.assertTrue(c1._lock.is_held)
967
self.assertRaises(errors.LockContention,
968
c2.set_user_option, 'one', 'c2')
969
self.assertEquals('c1', c1.get_user_option('one'))
970
# Let the lock be released
973
c2.set_user_option('one', 'c2')
974
self.assertEquals('c2', c2.get_user_option('one'))
976
def test_read_while_writing(self):
978
# We spawn a thread that will pause *during* the write
979
ready_to_write = threading.Event()
980
do_writing = threading.Event()
981
writing_done = threading.Event()
982
c1_orig = c1._write_config_file
983
def c1_write_config_file():
985
# The lock is held. We wait for the main thread to decide when to
990
c1._write_config_file = c1_write_config_file
992
c1.set_user_option('one', 'c1')
993
t1 = threading.Thread(target=c1_set_option)
994
# Collect the thread after the test
995
self.addCleanup(t1.join)
996
# Be ready to unblock the thread if the test goes wrong
997
self.addCleanup(do_writing.set)
999
# Ensure the thread is ready to write
1000
ready_to_write.wait()
1001
self.assertTrue(c1._lock.is_held)
1002
self.assertEquals('c1', c1.get_user_option('one'))
1003
# If we read during the write, we get the old value
1004
c2 = self.get_existing_config()
1005
self.assertEquals('1', c2.get_user_option('one'))
1006
# Let the writing occur and ensure it occurred
1009
# Now we get the updated value
1010
c3 = self.get_existing_config()
1011
self.assertEquals('c1', c3.get_user_option('one'))
397
1014
class TestGetUserOptionAs(TestIniConfig):
1312
2012
self.assertIs(None, bzrdir_config.get_default_stack_on())
2015
class TestOldConfigHooks(tests.TestCaseWithTransport):
2018
super(TestOldConfigHooks, self).setUp()
2019
create_configs_with_file_option(self)
2021
def assertGetHook(self, conf, name, value):
2025
config.OldConfigHooks.install_named_hook('get', hook, None)
2027
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2028
self.assertLength(0, calls)
2029
actual_value = conf.get_user_option(name)
2030
self.assertEquals(value, actual_value)
2031
self.assertLength(1, calls)
2032
self.assertEquals((conf, name, value), calls[0])
2034
def test_get_hook_bazaar(self):
2035
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2037
def test_get_hook_locations(self):
2038
self.assertGetHook(self.locations_config, 'file', 'locations')
2040
def test_get_hook_branch(self):
2041
# Since locations masks branch, we define a different option
2042
self.branch_config.set_user_option('file2', 'branch')
2043
self.assertGetHook(self.branch_config, 'file2', 'branch')
2045
def assertSetHook(self, conf, name, value):
2049
config.OldConfigHooks.install_named_hook('set', hook, None)
2051
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2052
self.assertLength(0, calls)
2053
conf.set_user_option(name, value)
2054
self.assertLength(1, calls)
2055
# We can't assert the conf object below as different configs use
2056
# different means to implement set_user_option and we care only about
2058
self.assertEquals((name, value), calls[0][1:])
2060
def test_set_hook_bazaar(self):
2061
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2063
def test_set_hook_locations(self):
2064
self.assertSetHook(self.locations_config, 'foo', 'locations')
2066
def test_set_hook_branch(self):
2067
self.assertSetHook(self.branch_config, 'foo', 'branch')
2069
def assertRemoveHook(self, conf, name, section_name=None):
2073
config.OldConfigHooks.install_named_hook('remove', hook, None)
2075
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2076
self.assertLength(0, calls)
2077
conf.remove_user_option(name, section_name)
2078
self.assertLength(1, calls)
2079
# We can't assert the conf object below as different configs use
2080
# different means to implement remove_user_option and we care only about
2082
self.assertEquals((name,), calls[0][1:])
2084
def test_remove_hook_bazaar(self):
2085
self.assertRemoveHook(self.bazaar_config, 'file')
2087
def test_remove_hook_locations(self):
2088
self.assertRemoveHook(self.locations_config, 'file',
2089
self.locations_config.location)
2091
def test_remove_hook_branch(self):
2092
self.assertRemoveHook(self.branch_config, 'file')
2094
def assertLoadHook(self, name, conf_class, *conf_args):
2098
config.OldConfigHooks.install_named_hook('load', hook, None)
2100
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2101
self.assertLength(0, calls)
2103
conf = conf_class(*conf_args)
2104
# Access an option to trigger a load
2105
conf.get_user_option(name)
2106
self.assertLength(1, calls)
2107
# Since we can't assert about conf, we just use the number of calls ;-/
2109
def test_load_hook_bazaar(self):
2110
self.assertLoadHook('file', config.GlobalConfig)
2112
def test_load_hook_locations(self):
2113
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2115
def test_load_hook_branch(self):
2116
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2118
def assertSaveHook(self, conf):
2122
config.OldConfigHooks.install_named_hook('save', hook, None)
2124
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2125
self.assertLength(0, calls)
2126
# Setting an option triggers a save
2127
conf.set_user_option('foo', 'bar')
2128
self.assertLength(1, calls)
2129
# Since we can't assert about conf, we just use the number of calls ;-/
2131
def test_save_hook_bazaar(self):
2132
self.assertSaveHook(self.bazaar_config)
2134
def test_save_hook_locations(self):
2135
self.assertSaveHook(self.locations_config)
2137
def test_save_hook_branch(self):
2138
self.assertSaveHook(self.branch_config)
2141
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2142
"""Tests config hooks for remote configs.
2144
No tests for the remove hook as this is not implemented there.
2148
super(TestOldConfigHooksForRemote, self).setUp()
2149
self.transport_server = test_server.SmartTCPServer_for_testing
2150
create_configs_with_file_option(self)
2152
def assertGetHook(self, conf, name, value):
2156
config.OldConfigHooks.install_named_hook('get', hook, None)
2158
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2159
self.assertLength(0, calls)
2160
actual_value = conf.get_option(name)
2161
self.assertEquals(value, actual_value)
2162
self.assertLength(1, calls)
2163
self.assertEquals((conf, name, value), calls[0])
2165
def test_get_hook_remote_branch(self):
2166
remote_branch = branch.Branch.open(self.get_url('tree'))
2167
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2169
def test_get_hook_remote_bzrdir(self):
2170
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2171
conf = remote_bzrdir._get_config()
2172
conf.set_option('remotedir', 'file')
2173
self.assertGetHook(conf, 'file', 'remotedir')
2175
def assertSetHook(self, conf, name, value):
2179
config.OldConfigHooks.install_named_hook('set', hook, None)
2181
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2182
self.assertLength(0, calls)
2183
conf.set_option(value, name)
2184
self.assertLength(1, calls)
2185
# We can't assert the conf object below as different configs use
2186
# different means to implement set_user_option and we care only about
2188
self.assertEquals((name, value), calls[0][1:])
2190
def test_set_hook_remote_branch(self):
2191
remote_branch = branch.Branch.open(self.get_url('tree'))
2192
self.addCleanup(remote_branch.lock_write().unlock)
2193
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2195
def test_set_hook_remote_bzrdir(self):
2196
remote_branch = branch.Branch.open(self.get_url('tree'))
2197
self.addCleanup(remote_branch.lock_write().unlock)
2198
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2199
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2201
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2205
config.OldConfigHooks.install_named_hook('load', hook, None)
2207
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2208
self.assertLength(0, calls)
2210
conf = conf_class(*conf_args)
2211
# Access an option to trigger a load
2212
conf.get_option(name)
2213
self.assertLength(expected_nb_calls, calls)
2214
# Since we can't assert about conf, we just use the number of calls ;-/
2216
def test_load_hook_remote_branch(self):
2217
remote_branch = branch.Branch.open(self.get_url('tree'))
2218
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2220
def test_load_hook_remote_bzrdir(self):
2221
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2222
# The config file doesn't exist, set an option to force its creation
2223
conf = remote_bzrdir._get_config()
2224
conf.set_option('remotedir', 'file')
2225
# We get one call for the server and one call for the client, this is
2226
# caused by the differences in implementations betwen
2227
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2228
# SmartServerBranchGetConfigFile (in smart/branch.py)
2229
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2231
def assertSaveHook(self, conf):
2235
config.OldConfigHooks.install_named_hook('save', hook, None)
2237
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2238
self.assertLength(0, calls)
2239
# Setting an option triggers a save
2240
conf.set_option('foo', 'bar')
2241
self.assertLength(1, calls)
2242
# Since we can't assert about conf, we just use the number of calls ;-/
2244
def test_save_hook_remote_branch(self):
2245
remote_branch = branch.Branch.open(self.get_url('tree'))
2246
self.addCleanup(remote_branch.lock_write().unlock)
2247
self.assertSaveHook(remote_branch._get_config())
2249
def test_save_hook_remote_bzrdir(self):
2250
remote_branch = branch.Branch.open(self.get_url('tree'))
2251
self.addCleanup(remote_branch.lock_write().unlock)
2252
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2253
self.assertSaveHook(remote_bzrdir._get_config())
2256
class TestOption(tests.TestCase):
2258
def test_default_value(self):
2259
opt = config.Option('foo', default='bar')
2260
self.assertEquals('bar', opt.get_default())
2262
def test_callable_default_value(self):
2263
def bar_as_unicode():
2265
opt = config.Option('foo', default=bar_as_unicode)
2266
self.assertEquals('bar', opt.get_default())
2268
def test_default_value_from_env(self):
2269
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2270
self.overrideEnv('FOO', 'quux')
2271
# Env variable provides a default taking over the option one
2272
self.assertEquals('quux', opt.get_default())
2274
def test_first_default_value_from_env_wins(self):
2275
opt = config.Option('foo', default='bar',
2276
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2277
self.overrideEnv('FOO', 'foo')
2278
self.overrideEnv('BAZ', 'baz')
2279
# The first env var set wins
2280
self.assertEquals('foo', opt.get_default())
2282
def test_not_supported_list_default_value(self):
2283
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2285
def test_not_supported_object_default_value(self):
2286
self.assertRaises(AssertionError, config.Option, 'foo',
2289
def test_not_supported_callable_default_value_not_unicode(self):
2290
def bar_not_unicode():
2292
opt = config.Option('foo', default=bar_not_unicode)
2293
self.assertRaises(AssertionError, opt.get_default)
2296
class TestOptionConverterMixin(object):
2298
def assertConverted(self, expected, opt, value):
2299
self.assertEquals(expected, opt.convert_from_unicode(value))
2301
def assertWarns(self, opt, value):
2304
warnings.append(args[0] % args[1:])
2305
self.overrideAttr(trace, 'warning', warning)
2306
self.assertEquals(None, opt.convert_from_unicode(value))
2307
self.assertLength(1, warnings)
2309
'Value "%s" is not valid for "%s"' % (value, opt.name),
2312
def assertErrors(self, opt, value):
2313
self.assertRaises(errors.ConfigOptionValueError,
2314
opt.convert_from_unicode, value)
2316
def assertConvertInvalid(self, opt, invalid_value):
2318
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2319
opt.invalid = 'warning'
2320
self.assertWarns(opt, invalid_value)
2321
opt.invalid = 'error'
2322
self.assertErrors(opt, invalid_value)
2325
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2327
def get_option(self):
2328
return config.Option('foo', help='A boolean.',
2329
from_unicode=config.bool_from_store)
2331
def test_convert_invalid(self):
2332
opt = self.get_option()
2333
# A string that is not recognized as a boolean
2334
self.assertConvertInvalid(opt, u'invalid-boolean')
2335
# A list of strings is never recognized as a boolean
2336
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2338
def test_convert_valid(self):
2339
opt = self.get_option()
2340
self.assertConverted(True, opt, u'True')
2341
self.assertConverted(True, opt, u'1')
2342
self.assertConverted(False, opt, u'False')
2345
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2347
def get_option(self):
2348
return config.Option('foo', help='An integer.',
2349
from_unicode=config.int_from_store)
2351
def test_convert_invalid(self):
2352
opt = self.get_option()
2353
# A string that is not recognized as an integer
2354
self.assertConvertInvalid(opt, u'forty-two')
2355
# A list of strings is never recognized as an integer
2356
self.assertConvertInvalid(opt, [u'a', u'list'])
2358
def test_convert_valid(self):
2359
opt = self.get_option()
2360
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 TestOptionConverterMixin(object):
2392
def assertConverted(self, expected, opt, value):
2393
self.assertEquals(expected, opt.convert_from_unicode(value))
2395
def assertWarns(self, opt, value):
2398
warnings.append(args[0] % args[1:])
2399
self.overrideAttr(trace, 'warning', warning)
2400
self.assertEquals(None, opt.convert_from_unicode(value))
2401
self.assertLength(1, warnings)
2403
'Value "%s" is not valid for "%s"' % (value, opt.name),
2406
def assertErrors(self, opt, value):
2407
self.assertRaises(errors.ConfigOptionValueError,
2408
opt.convert_from_unicode, value)
2410
def assertConvertInvalid(self, opt, invalid_value):
2412
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2413
opt.invalid = 'warning'
2414
self.assertWarns(opt, invalid_value)
2415
opt.invalid = 'error'
2416
self.assertErrors(opt, invalid_value)
2419
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2421
def get_option(self):
2422
return config.Option('foo', help='A boolean.',
2423
from_unicode=config.bool_from_store)
2425
def test_convert_invalid(self):
2426
opt = self.get_option()
2427
# A string that is not recognized as a boolean
2428
self.assertConvertInvalid(opt, u'invalid-boolean')
2429
# A list of strings is never recognized as a boolean
2430
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2432
def test_convert_valid(self):
2433
opt = self.get_option()
2434
self.assertConverted(True, opt, u'True')
2435
self.assertConverted(True, opt, u'1')
2436
self.assertConverted(False, opt, u'False')
2439
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2441
def get_option(self):
2442
return config.Option('foo', help='An integer.',
2443
from_unicode=config.int_from_store)
2445
def test_convert_invalid(self):
2446
opt = self.get_option()
2447
# A string that is not recognized as an integer
2448
self.assertConvertInvalid(opt, u'forty-two')
2449
# A list of strings is never recognized as an integer
2450
self.assertConvertInvalid(opt, [u'a', u'list'])
2452
def test_convert_valid(self):
2453
opt = self.get_option()
2454
self.assertConverted(16, opt, u'16')
2457
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2459
def get_option(self):
2460
return config.Option('foo', help='A list.',
2461
from_unicode=config.list_from_store)
2463
def test_convert_invalid(self):
2464
opt = self.get_option()
2465
# We don't even try to convert a list into a list, we only expect
2467
self.assertConvertInvalid(opt, [1])
2468
# No string is invalid as all forms can be converted to a list
2470
def test_convert_valid(self):
2471
opt = self.get_option()
2472
# An empty string is an empty list
2473
self.assertConverted([], opt, '') # Using a bare str() just in case
2474
self.assertConverted([], opt, u'')
2476
self.assertConverted([u'True'], opt, u'True')
2478
self.assertConverted([u'42'], opt, u'42')
2480
self.assertConverted([u'bar'], opt, u'bar')
2483
class TestOptionRegistry(tests.TestCase):
2486
super(TestOptionRegistry, self).setUp()
2487
# Always start with an empty registry
2488
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2489
self.registry = config.option_registry
2491
def test_register(self):
2492
opt = config.Option('foo')
2493
self.registry.register(opt)
2494
self.assertIs(opt, self.registry.get('foo'))
2496
def test_registered_help(self):
2497
opt = config.Option('foo', help='A simple option')
2498
self.registry.register(opt)
2499
self.assertEquals('A simple option', self.registry.get_help('foo'))
2501
lazy_option = config.Option('lazy_foo', help='Lazy help')
2503
def test_register_lazy(self):
2504
self.registry.register_lazy('lazy_foo', self.__module__,
2505
'TestOptionRegistry.lazy_option')
2506
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2508
def test_registered_lazy_help(self):
2509
self.registry.register_lazy('lazy_foo', self.__module__,
2510
'TestOptionRegistry.lazy_option')
2511
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2514
class TestRegisteredOptions(tests.TestCase):
2515
"""All registered options should verify some constraints."""
2517
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2518
in config.option_registry.iteritems()]
2521
super(TestRegisteredOptions, self).setUp()
2522
self.registry = config.option_registry
2524
def test_proper_name(self):
2525
# An option should be registered under its own name, this can't be
2526
# checked at registration time for the lazy ones.
2527
self.assertEquals(self.option_name, self.option.name)
2529
def test_help_is_set(self):
2530
option_help = self.registry.get_help(self.option_name)
2531
self.assertNotEquals(None, option_help)
2532
# Come on, think about the user, he really wants to know what the
2534
self.assertIsNot(None, option_help)
2535
self.assertNotEquals('', option_help)
2538
class TestSection(tests.TestCase):
2540
# FIXME: Parametrize so that all sections produced by Stores run these
2541
# tests -- vila 2011-04-01
2543
def test_get_a_value(self):
2544
a_dict = dict(foo='bar')
2545
section = config.Section('myID', a_dict)
2546
self.assertEquals('bar', section.get('foo'))
2548
def test_get_unknown_option(self):
2550
section = config.Section(None, a_dict)
2551
self.assertEquals('out of thin air',
2552
section.get('foo', 'out of thin air'))
2554
def test_options_is_shared(self):
2556
section = config.Section(None, a_dict)
2557
self.assertIs(a_dict, section.options)
2560
class TestMutableSection(tests.TestCase):
2562
scenarios = [('mutable',
2564
lambda opts: config.MutableSection('myID', opts)},),
2568
a_dict = dict(foo='bar')
2569
section = self.get_section(a_dict)
2570
section.set('foo', 'new_value')
2571
self.assertEquals('new_value', section.get('foo'))
2572
# The change appears in the shared section
2573
self.assertEquals('new_value', a_dict.get('foo'))
2574
# We keep track of the change
2575
self.assertTrue('foo' in section.orig)
2576
self.assertEquals('bar', section.orig.get('foo'))
2578
def test_set_preserve_original_once(self):
2579
a_dict = dict(foo='bar')
2580
section = self.get_section(a_dict)
2581
section.set('foo', 'first_value')
2582
section.set('foo', 'second_value')
2583
# We keep track of the original value
2584
self.assertTrue('foo' in section.orig)
2585
self.assertEquals('bar', section.orig.get('foo'))
2587
def test_remove(self):
2588
a_dict = dict(foo='bar')
2589
section = self.get_section(a_dict)
2590
section.remove('foo')
2591
# We get None for unknown options via the default value
2592
self.assertEquals(None, section.get('foo'))
2593
# Or we just get the default value
2594
self.assertEquals('unknown', section.get('foo', 'unknown'))
2595
self.assertFalse('foo' in section.options)
2596
# We keep track of the deletion
2597
self.assertTrue('foo' in section.orig)
2598
self.assertEquals('bar', section.orig.get('foo'))
2600
def test_remove_new_option(self):
2602
section = self.get_section(a_dict)
2603
section.set('foo', 'bar')
2604
section.remove('foo')
2605
self.assertFalse('foo' in section.options)
2606
# The option didn't exist initially so it we need to keep track of it
2607
# with a special value
2608
self.assertTrue('foo' in section.orig)
2609
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2612
class TestCommandLineStore(tests.TestCase):
2615
super(TestCommandLineStore, self).setUp()
2616
self.store = config.CommandLineStore()
2618
def get_section(self):
2619
"""Get the unique section for the command line overrides."""
2620
sections = list(self.store.get_sections())
2621
self.assertLength(1, sections)
2622
store, section = sections[0]
2623
self.assertEquals(self.store, store)
2626
def test_no_override(self):
2627
self.store._from_cmdline([])
2628
section = self.get_section()
2629
self.assertLength(0, list(section.iter_option_names()))
2631
def test_simple_override(self):
2632
self.store._from_cmdline(['a=b'])
2633
section = self.get_section()
2634
self.assertEqual('b', section.get('a'))
2636
def test_list_override(self):
2637
self.store._from_cmdline(['l=1,2,3'])
2638
val = self.get_section().get('l')
2639
self.assertEqual('1,2,3', val)
2640
# Reminder: lists should be registered as such explicitely, otherwise
2641
# the conversion needs to be done afterwards.
2642
self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2644
def test_multiple_overrides(self):
2645
self.store._from_cmdline(['a=b', 'x=y'])
2646
section = self.get_section()
2647
self.assertEquals('b', section.get('a'))
2648
self.assertEquals('y', section.get('x'))
2650
def test_wrong_syntax(self):
2651
self.assertRaises(errors.BzrCommandError,
2652
self.store._from_cmdline, ['a=b', 'c'])
2655
class TestStore(tests.TestCaseWithTransport):
2657
def assertSectionContent(self, expected, (store, section)):
2658
"""Assert that some options have the proper values in a section."""
2659
expected_name, expected_options = expected
2660
self.assertEquals(expected_name, section.id)
2663
dict([(k, section.get(k)) for k in expected_options.keys()]))
2666
class TestReadonlyStore(TestStore):
2668
scenarios = [(key, {'get_store': builder}) for key, builder
2669
in config.test_store_builder_registry.iteritems()]
2671
def test_building_delays_load(self):
2672
store = self.get_store(self)
2673
self.assertEquals(False, store.is_loaded())
2674
store._load_from_string('')
2675
self.assertEquals(True, store.is_loaded())
2677
def test_get_no_sections_for_empty(self):
2678
store = self.get_store(self)
2679
store._load_from_string('')
2680
self.assertEquals([], list(store.get_sections()))
2682
def test_get_default_section(self):
2683
store = self.get_store(self)
2684
store._load_from_string('foo=bar')
2685
sections = list(store.get_sections())
2686
self.assertLength(1, sections)
2687
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2689
def test_get_named_section(self):
2690
store = self.get_store(self)
2691
store._load_from_string('[baz]\nfoo=bar')
2692
sections = list(store.get_sections())
2693
self.assertLength(1, sections)
2694
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2696
def test_load_from_string_fails_for_non_empty_store(self):
2697
store = self.get_store(self)
2698
store._load_from_string('foo=bar')
2699
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2702
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2703
"""Simulate loading a config store with content of various encodings.
2705
All files produced by bzr are in utf8 content.
2707
Users may modify them manually and end up with a file that can't be
2708
loaded. We need to issue proper error messages in this case.
2711
invalid_utf8_char = '\xff'
2713
def test_load_utf8(self):
2714
"""Ensure we can load an utf8-encoded file."""
2715
t = self.get_transport()
2716
# From http://pad.lv/799212
2717
unicode_user = u'b\N{Euro Sign}ar'
2718
unicode_content = u'user=%s' % (unicode_user,)
2719
utf8_content = unicode_content.encode('utf8')
2720
# Store the raw content in the config file
2721
t.put_bytes('foo.conf', utf8_content)
2722
store = config.TransportIniFileStore(t, 'foo.conf')
2724
stack = config.Stack([store.get_sections], store)
2725
self.assertEquals(unicode_user, stack.get('user'))
2727
def test_load_non_ascii(self):
2728
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2729
t = self.get_transport()
2730
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2731
store = config.TransportIniFileStore(t, 'foo.conf')
2732
self.assertRaises(errors.ConfigContentError, store.load)
2734
def test_load_erroneous_content(self):
2735
"""Ensure we display a proper error on content that can't be parsed."""
2736
t = self.get_transport()
2737
t.put_bytes('foo.conf', '[open_section\n')
2738
store = config.TransportIniFileStore(t, 'foo.conf')
2739
self.assertRaises(errors.ParseConfigError, store.load)
2741
def test_load_permission_denied(self):
2742
"""Ensure we get warned when trying to load an inaccessible file."""
2745
warnings.append(args[0] % args[1:])
2746
self.overrideAttr(trace, 'warning', warning)
2748
t = self.get_transport()
2750
def get_bytes(relpath):
2751
raise errors.PermissionDenied(relpath, "")
2752
t.get_bytes = get_bytes
2753
store = config.TransportIniFileStore(t, 'foo.conf')
2754
self.assertRaises(errors.PermissionDenied, store.load)
2757
[u'Permission denied while trying to load configuration store %s.'
2758
% store.external_url()])
2761
class TestIniConfigContent(tests.TestCaseWithTransport):
2762
"""Simulate loading a IniBasedConfig with content of various encodings.
2764
All files produced by bzr are in utf8 content.
2766
Users may modify them manually and end up with a file that can't be
2767
loaded. We need to issue proper error messages in this case.
2770
invalid_utf8_char = '\xff'
2772
def test_load_utf8(self):
2773
"""Ensure we can load an utf8-encoded file."""
2774
# From http://pad.lv/799212
2775
unicode_user = u'b\N{Euro Sign}ar'
2776
unicode_content = u'user=%s' % (unicode_user,)
2777
utf8_content = unicode_content.encode('utf8')
2778
# Store the raw content in the config file
2779
with open('foo.conf', 'wb') as f:
2780
f.write(utf8_content)
2781
conf = config.IniBasedConfig(file_name='foo.conf')
2782
self.assertEquals(unicode_user, conf.get_user_option('user'))
2784
def test_load_badly_encoded_content(self):
2785
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2786
with open('foo.conf', 'wb') as f:
2787
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2788
conf = config.IniBasedConfig(file_name='foo.conf')
2789
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2791
def test_load_erroneous_content(self):
2792
"""Ensure we display a proper error on content that can't be parsed."""
2793
with open('foo.conf', 'wb') as f:
2794
f.write('[open_section\n')
2795
conf = config.IniBasedConfig(file_name='foo.conf')
2796
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2799
class TestMutableStore(TestStore):
2801
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2802
in config.test_store_builder_registry.iteritems()]
2805
super(TestMutableStore, self).setUp()
2806
self.transport = self.get_transport()
2808
def has_store(self, store):
2809
store_basename = urlutils.relative_url(self.transport.external_url(),
2810
store.external_url())
2811
return self.transport.has(store_basename)
2813
def test_save_empty_creates_no_file(self):
2814
# FIXME: There should be a better way than relying on the test
2815
# parametrization to identify branch.conf -- vila 2011-0526
2816
if self.store_id in ('branch', 'remote_branch'):
2817
raise tests.TestNotApplicable(
2818
'branch.conf is *always* created when a branch is initialized')
2819
store = self.get_store(self)
2821
self.assertEquals(False, self.has_store(store))
2823
def test_save_emptied_succeeds(self):
2824
store = self.get_store(self)
2825
store._load_from_string('foo=bar\n')
2826
section = store.get_mutable_section(None)
2827
section.remove('foo')
2829
self.assertEquals(True, self.has_store(store))
2830
modified_store = self.get_store(self)
2831
sections = list(modified_store.get_sections())
2832
self.assertLength(0, sections)
2834
def test_save_with_content_succeeds(self):
2835
# FIXME: There should be a better way than relying on the test
2836
# parametrization to identify branch.conf -- vila 2011-0526
2837
if self.store_id in ('branch', 'remote_branch'):
2838
raise tests.TestNotApplicable(
2839
'branch.conf is *always* created when a branch is initialized')
2840
store = self.get_store(self)
2841
store._load_from_string('foo=bar\n')
2842
self.assertEquals(False, self.has_store(store))
2844
self.assertEquals(True, self.has_store(store))
2845
modified_store = self.get_store(self)
2846
sections = list(modified_store.get_sections())
2847
self.assertLength(1, sections)
2848
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2850
def test_set_option_in_empty_store(self):
2851
store = self.get_store(self)
2852
section = store.get_mutable_section(None)
2853
section.set('foo', 'bar')
2855
modified_store = self.get_store(self)
2856
sections = list(modified_store.get_sections())
2857
self.assertLength(1, sections)
2858
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2860
def test_set_option_in_default_section(self):
2861
store = self.get_store(self)
2862
store._load_from_string('')
2863
section = store.get_mutable_section(None)
2864
section.set('foo', 'bar')
2866
modified_store = self.get_store(self)
2867
sections = list(modified_store.get_sections())
2868
self.assertLength(1, sections)
2869
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2871
def test_set_option_in_named_section(self):
2872
store = self.get_store(self)
2873
store._load_from_string('')
2874
section = store.get_mutable_section('baz')
2875
section.set('foo', 'bar')
2877
modified_store = self.get_store(self)
2878
sections = list(modified_store.get_sections())
2879
self.assertLength(1, sections)
2880
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2882
def test_load_hook(self):
2883
# We first needs to ensure that the store exists
2884
store = self.get_store(self)
2885
section = store.get_mutable_section('baz')
2886
section.set('foo', 'bar')
2888
# Now we can try to load it
2889
store = self.get_store(self)
2893
config.ConfigHooks.install_named_hook('load', hook, None)
2894
self.assertLength(0, calls)
2896
self.assertLength(1, calls)
2897
self.assertEquals((store,), calls[0])
2899
def test_save_hook(self):
2903
config.ConfigHooks.install_named_hook('save', hook, None)
2904
self.assertLength(0, calls)
2905
store = self.get_store(self)
2906
section = store.get_mutable_section('baz')
2907
section.set('foo', 'bar')
2909
self.assertLength(1, calls)
2910
self.assertEquals((store,), calls[0])
2913
class TestTransportIniFileStore(TestStore):
2915
def test_loading_unknown_file_fails(self):
2916
store = config.TransportIniFileStore(self.get_transport(),
2918
self.assertRaises(errors.NoSuchFile, store.load)
2920
def test_invalid_content(self):
2921
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2922
self.assertEquals(False, store.is_loaded())
2923
exc = self.assertRaises(
2924
errors.ParseConfigError, store._load_from_string,
2925
'this is invalid !')
2926
self.assertEndsWith(exc.filename, 'foo.conf')
2927
# And the load failed
2928
self.assertEquals(False, store.is_loaded())
2930
def test_get_embedded_sections(self):
2931
# A more complicated example (which also shows that section names and
2932
# option names share the same name space...)
2933
# FIXME: This should be fixed by forbidding dicts as values ?
2934
# -- vila 2011-04-05
2935
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2936
store._load_from_string('''
2940
foo_in_DEFAULT=foo_DEFAULT
2948
sections = list(store.get_sections())
2949
self.assertLength(4, sections)
2950
# The default section has no name.
2951
# List values are provided as strings and need to be explicitly
2952
# converted by specifying from_unicode=list_from_store at option
2954
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2956
self.assertSectionContent(
2957
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2958
self.assertSectionContent(
2959
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2960
# sub sections are provided as embedded dicts.
2961
self.assertSectionContent(
2962
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2966
class TestLockableIniFileStore(TestStore):
2968
def test_create_store_in_created_dir(self):
2969
self.assertPathDoesNotExist('dir')
2970
t = self.get_transport('dir/subdir')
2971
store = config.LockableIniFileStore(t, 'foo.conf')
2972
store.get_mutable_section(None).set('foo', 'bar')
2974
self.assertPathExists('dir/subdir')
2977
class TestConcurrentStoreUpdates(TestStore):
2978
"""Test that Stores properly handle conccurent updates.
2980
New Store implementation may fail some of these tests but until such
2981
implementations exist it's hard to properly filter them from the scenarios
2982
applied here. If you encounter such a case, contact the bzr devs.
2985
scenarios = [(key, {'get_stack': builder}) for key, builder
2986
in config.test_stack_builder_registry.iteritems()]
2989
super(TestConcurrentStoreUpdates, self).setUp()
2990
self.stack = self.get_stack(self)
2991
if not isinstance(self.stack, config._CompatibleStack):
2992
raise tests.TestNotApplicable(
2993
'%s is not meant to be compatible with the old config design'
2995
self.stack.set('one', '1')
2996
self.stack.set('two', '2')
2998
self.stack.store.save()
3000
def test_simple_read_access(self):
3001
self.assertEquals('1', self.stack.get('one'))
3003
def test_simple_write_access(self):
3004
self.stack.set('one', 'one')
3005
self.assertEquals('one', self.stack.get('one'))
3007
def test_listen_to_the_last_speaker(self):
3009
c2 = self.get_stack(self)
3010
c1.set('one', 'ONE')
3011
c2.set('two', 'TWO')
3012
self.assertEquals('ONE', c1.get('one'))
3013
self.assertEquals('TWO', c2.get('two'))
3014
# The second update respect the first one
3015
self.assertEquals('ONE', c2.get('one'))
3017
def test_last_speaker_wins(self):
3018
# If the same config is not shared, the same variable modified twice
3019
# can only see a single result.
3021
c2 = self.get_stack(self)
3024
self.assertEquals('c2', c2.get('one'))
3025
# The first modification is still available until another refresh
3027
self.assertEquals('c1', c1.get('one'))
3028
c1.set('two', 'done')
3029
self.assertEquals('c2', c1.get('one'))
3031
def test_writes_are_serialized(self):
3033
c2 = self.get_stack(self)
3035
# We spawn a thread that will pause *during* the config saving.
3036
before_writing = threading.Event()
3037
after_writing = threading.Event()
3038
writing_done = threading.Event()
3039
c1_save_without_locking_orig = c1.store.save_without_locking
3040
def c1_save_without_locking():
3041
before_writing.set()
3042
c1_save_without_locking_orig()
3043
# The lock is held. We wait for the main thread to decide when to
3045
after_writing.wait()
3046
c1.store.save_without_locking = c1_save_without_locking
3050
t1 = threading.Thread(target=c1_set)
3051
# Collect the thread after the test
3052
self.addCleanup(t1.join)
3053
# Be ready to unblock the thread if the test goes wrong
3054
self.addCleanup(after_writing.set)
3056
before_writing.wait()
3057
self.assertRaises(errors.LockContention,
3058
c2.set, 'one', 'c2')
3059
self.assertEquals('c1', c1.get('one'))
3060
# Let the lock be released
3064
self.assertEquals('c2', c2.get('one'))
3066
def test_read_while_writing(self):
3068
# We spawn a thread that will pause *during* the write
3069
ready_to_write = threading.Event()
3070
do_writing = threading.Event()
3071
writing_done = threading.Event()
3072
# We override the _save implementation so we know the store is locked
3073
c1_save_without_locking_orig = c1.store.save_without_locking
3074
def c1_save_without_locking():
3075
ready_to_write.set()
3076
# The lock is held. We wait for the main thread to decide when to
3079
c1_save_without_locking_orig()
3081
c1.store.save_without_locking = c1_save_without_locking
3084
t1 = threading.Thread(target=c1_set)
3085
# Collect the thread after the test
3086
self.addCleanup(t1.join)
3087
# Be ready to unblock the thread if the test goes wrong
3088
self.addCleanup(do_writing.set)
3090
# Ensure the thread is ready to write
3091
ready_to_write.wait()
3092
self.assertEquals('c1', c1.get('one'))
3093
# If we read during the write, we get the old value
3094
c2 = self.get_stack(self)
3095
self.assertEquals('1', c2.get('one'))
3096
# Let the writing occur and ensure it occurred
3099
# Now we get the updated value
3100
c3 = self.get_stack(self)
3101
self.assertEquals('c1', c3.get('one'))
3103
# FIXME: It may be worth looking into removing the lock dir when it's not
3104
# needed anymore and look at possible fallouts for concurrent lockers. This
3105
# will matter if/when we use config files outside of bazaar directories
3106
# (.bazaar or .bzr) -- vila 20110-04-111
3109
class TestSectionMatcher(TestStore):
3111
scenarios = [('location', {'matcher': config.LocationMatcher}),
3112
('id', {'matcher': config.NameMatcher}),]
3115
super(TestSectionMatcher, self).setUp()
3116
# Any simple store is good enough
3117
self.get_store = config.test_store_builder_registry.get('configobj')
3119
def test_no_matches_for_empty_stores(self):
3120
store = self.get_store(self)
3121
store._load_from_string('')
3122
matcher = self.matcher(store, '/bar')
3123
self.assertEquals([], list(matcher.get_sections()))
3125
def test_build_doesnt_load_store(self):
3126
store = self.get_store(self)
3127
matcher = self.matcher(store, '/bar')
3128
self.assertFalse(store.is_loaded())
3131
class TestLocationSection(tests.TestCase):
3133
def get_section(self, options, extra_path):
3134
section = config.Section('foo', options)
3135
# We don't care about the length so we use '0'
3136
return config.LocationSection(section, 0, extra_path)
3138
def test_simple_option(self):
3139
section = self.get_section({'foo': 'bar'}, '')
3140
self.assertEquals('bar', section.get('foo'))
3142
def test_option_with_extra_path(self):
3143
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3145
self.assertEquals('bar/baz', section.get('foo'))
3147
def test_invalid_policy(self):
3148
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3150
# invalid policies are ignored
3151
self.assertEquals('bar', section.get('foo'))
3154
class TestLocationMatcher(TestStore):
3157
super(TestLocationMatcher, self).setUp()
3158
# Any simple store is good enough
3159
self.get_store = config.test_store_builder_registry.get('configobj')
3161
def test_unrelated_section_excluded(self):
3162
store = self.get_store(self)
3163
store._load_from_string('''
3171
section=/foo/bar/baz
3175
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3177
[section.id for _, section in store.get_sections()])
3178
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3179
sections = [section for s, section in matcher.get_sections()]
3180
self.assertEquals([3, 2],
3181
[section.length for section in sections])
3182
self.assertEquals(['/foo/bar', '/foo'],
3183
[section.id for section in sections])
3184
self.assertEquals(['quux', 'bar/quux'],
3185
[section.extra_path for section in sections])
3187
def test_more_specific_sections_first(self):
3188
store = self.get_store(self)
3189
store._load_from_string('''
3195
self.assertEquals(['/foo', '/foo/bar'],
3196
[section.id for _, section in store.get_sections()])
3197
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3198
sections = [section for s, section in matcher.get_sections()]
3199
self.assertEquals([3, 2],
3200
[section.length for section in sections])
3201
self.assertEquals(['/foo/bar', '/foo'],
3202
[section.id for section in sections])
3203
self.assertEquals(['baz', 'bar/baz'],
3204
[section.extra_path for section in sections])
3206
def test_appendpath_in_no_name_section(self):
3207
# It's a bit weird to allow appendpath in a no-name section, but
3208
# someone may found a use for it
3209
store = self.get_store(self)
3210
store._load_from_string('''
3212
foo:policy = appendpath
3214
matcher = config.LocationMatcher(store, 'dir/subdir')
3215
sections = list(matcher.get_sections())
3216
self.assertLength(1, sections)
3217
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3219
def test_file_urls_are_normalized(self):
3220
store = self.get_store(self)
3221
if sys.platform == 'win32':
3222
expected_url = 'file:///C:/dir/subdir'
3223
expected_location = 'C:/dir/subdir'
3225
expected_url = 'file:///dir/subdir'
3226
expected_location = '/dir/subdir'
3227
matcher = config.LocationMatcher(store, expected_url)
3228
self.assertEquals(expected_location, matcher.location)
3231
class TestNameMatcher(TestStore):
3234
super(TestNameMatcher, self).setUp()
3235
self.matcher = config.NameMatcher
3236
# Any simple store is good enough
3237
self.get_store = config.test_store_builder_registry.get('configobj')
3239
def get_matching_sections(self, name):
3240
store = self.get_store(self)
3241
store._load_from_string('''
3249
matcher = self.matcher(store, name)
3250
return list(matcher.get_sections())
3252
def test_matching(self):
3253
sections = self.get_matching_sections('foo')
3254
self.assertLength(1, sections)
3255
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3257
def test_not_matching(self):
3258
sections = self.get_matching_sections('baz')
3259
self.assertLength(0, sections)
3262
class TestStackGet(tests.TestCase):
3264
# FIXME: This should be parametrized for all known Stack or dedicated
3265
# paramerized tests created to avoid bloating -- vila 2011-03-31
3267
def overrideOptionRegistry(self):
3268
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3270
def test_single_config_get(self):
3271
conf = dict(foo='bar')
3272
conf_stack = config.Stack([conf])
3273
self.assertEquals('bar', conf_stack.get('foo'))
3275
def test_get_with_registered_default_value(self):
3276
conf_stack = config.Stack([dict()])
3277
opt = config.Option('foo', default='bar')
3278
self.overrideOptionRegistry()
3279
config.option_registry.register('foo', opt)
3280
self.assertEquals('bar', conf_stack.get('foo'))
3282
def test_get_without_registered_default_value(self):
3283
conf_stack = config.Stack([dict()])
3284
opt = config.Option('foo')
3285
self.overrideOptionRegistry()
3286
config.option_registry.register('foo', opt)
3287
self.assertEquals(None, conf_stack.get('foo'))
3289
def test_get_without_default_value_for_not_registered(self):
3290
conf_stack = config.Stack([dict()])
3291
opt = config.Option('foo')
3292
self.overrideOptionRegistry()
3293
self.assertEquals(None, conf_stack.get('foo'))
3295
def test_get_first_definition(self):
3296
conf1 = dict(foo='bar')
3297
conf2 = dict(foo='baz')
3298
conf_stack = config.Stack([conf1, conf2])
3299
self.assertEquals('bar', conf_stack.get('foo'))
3301
def test_get_embedded_definition(self):
3302
conf1 = dict(yy='12')
3303
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3304
conf_stack = config.Stack([conf1, conf2])
3305
self.assertEquals('baz', conf_stack.get('foo'))
3307
def test_get_for_empty_section_callable(self):
3308
conf_stack = config.Stack([lambda : []])
3309
self.assertEquals(None, conf_stack.get('foo'))
3311
def test_get_for_broken_callable(self):
3312
# Trying to use and invalid callable raises an exception on first use
3313
conf_stack = config.Stack([lambda : object()])
3314
self.assertRaises(TypeError, conf_stack.get, 'foo')
3317
class TestStackWithTransport(tests.TestCaseWithTransport):
3319
scenarios = [(key, {'get_stack': builder}) for key, builder
3320
in config.test_stack_builder_registry.iteritems()]
3323
class TestConcreteStacks(TestStackWithTransport):
3325
def test_build_stack(self):
3326
# Just a smoke test to help debug builders
3327
stack = self.get_stack(self)
3330
class TestStackGet(TestStackWithTransport):
3333
super(TestStackGet, self).setUp()
3334
self.conf = self.get_stack(self)
3336
def test_get_for_empty_stack(self):
3337
self.assertEquals(None, self.conf.get('foo'))
3339
def test_get_hook(self):
3340
self.conf.set('foo', 'bar')
3344
config.ConfigHooks.install_named_hook('get', hook, None)
3345
self.assertLength(0, calls)
3346
value = self.conf.get('foo')
3347
self.assertEquals('bar', value)
3348
self.assertLength(1, calls)
3349
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3352
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3355
super(TestStackGetWithConverter, self).setUp()
3356
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3357
self.registry = config.option_registry
3358
# We just want a simple stack with a simple store so we can inject
3359
# whatever content the tests need without caring about what section
3360
# names are valid for a given store/stack.
3361
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3362
self.conf = config.Stack([store.get_sections], store)
3364
def register_bool_option(self, name, default=None, default_from_env=None):
3365
b = config.Option(name, help='A boolean.',
3366
default=default, default_from_env=default_from_env,
3367
from_unicode=config.bool_from_store)
3368
self.registry.register(b)
3370
def test_get_default_bool_None(self):
3371
self.register_bool_option('foo')
3372
self.assertEquals(None, self.conf.get('foo'))
3374
def test_get_default_bool_True(self):
3375
self.register_bool_option('foo', u'True')
3376
self.assertEquals(True, self.conf.get('foo'))
3378
def test_get_default_bool_False(self):
3379
self.register_bool_option('foo', False)
3380
self.assertEquals(False, self.conf.get('foo'))
3382
def test_get_default_bool_False_as_string(self):
3383
self.register_bool_option('foo', u'False')
3384
self.assertEquals(False, self.conf.get('foo'))
3386
def test_get_default_bool_from_env_converted(self):
3387
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3388
self.overrideEnv('FOO', 'False')
3389
self.assertEquals(False, self.conf.get('foo'))
3391
def test_get_default_bool_when_conversion_fails(self):
3392
self.register_bool_option('foo', default='True')
3393
self.conf.store._load_from_string('foo=invalid boolean')
3394
self.assertEquals(True, self.conf.get('foo'))
3396
def register_integer_option(self, name,
3397
default=None, default_from_env=None):
3398
i = config.Option(name, help='An integer.',
3399
default=default, default_from_env=default_from_env,
3400
from_unicode=config.int_from_store)
3401
self.registry.register(i)
3403
def test_get_default_integer_None(self):
3404
self.register_integer_option('foo')
3405
self.assertEquals(None, self.conf.get('foo'))
3407
def test_get_default_integer(self):
3408
self.register_integer_option('foo', 42)
3409
self.assertEquals(42, self.conf.get('foo'))
3411
def test_get_default_integer_as_string(self):
3412
self.register_integer_option('foo', u'42')
3413
self.assertEquals(42, self.conf.get('foo'))
3415
def test_get_default_integer_from_env(self):
3416
self.register_integer_option('foo', default_from_env=['FOO'])
3417
self.overrideEnv('FOO', '18')
3418
self.assertEquals(18, self.conf.get('foo'))
3420
def test_get_default_integer_when_conversion_fails(self):
3421
self.register_integer_option('foo', default='12')
3422
self.conf.store._load_from_string('foo=invalid integer')
3423
self.assertEquals(12, self.conf.get('foo'))
3425
def register_list_option(self, name, default=None, default_from_env=None):
3426
l = config.Option(name, help='A list.',
3427
default=default, default_from_env=default_from_env,
3428
from_unicode=config.list_from_store)
3429
self.registry.register(l)
3431
def test_get_default_list_None(self):
3432
self.register_list_option('foo')
3433
self.assertEquals(None, self.conf.get('foo'))
3435
def test_get_default_list_empty(self):
3436
self.register_list_option('foo', '')
3437
self.assertEquals([], self.conf.get('foo'))
3439
def test_get_default_list_from_env(self):
3440
self.register_list_option('foo', default_from_env=['FOO'])
3441
self.overrideEnv('FOO', '')
3442
self.assertEquals([], self.conf.get('foo'))
3444
def test_get_with_list_converter_no_item(self):
3445
self.register_list_option('foo', None)
3446
self.conf.store._load_from_string('foo=,')
3447
self.assertEquals([], self.conf.get('foo'))
3449
def test_get_with_list_converter_many_items(self):
3450
self.register_list_option('foo', None)
3451
self.conf.store._load_from_string('foo=m,o,r,e')
3452
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3454
def test_get_with_list_converter_embedded_spaces_many_items(self):
3455
self.register_list_option('foo', None)
3456
self.conf.store._load_from_string('foo=" bar", "baz "')
3457
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3459
def test_get_with_list_converter_stripped_spaces_many_items(self):
3460
self.register_list_option('foo', None)
3461
self.conf.store._load_from_string('foo= bar , baz ')
3462
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3465
class TestIterOptionRefs(tests.TestCase):
3466
"""iter_option_refs is a bit unusual, document some cases."""
3468
def assertRefs(self, expected, string):
3469
self.assertEquals(expected, list(config.iter_option_refs(string)))
3471
def test_empty(self):
3472
self.assertRefs([(False, '')], '')
3474
def test_no_refs(self):
3475
self.assertRefs([(False, 'foo bar')], 'foo bar')
3477
def test_single_ref(self):
3478
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3480
def test_broken_ref(self):
3481
self.assertRefs([(False, '{foo')], '{foo')
3483
def test_embedded_ref(self):
3484
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3487
def test_two_refs(self):
3488
self.assertRefs([(False, ''), (True, '{foo}'),
3489
(False, ''), (True, '{bar}'),
3493
def test_newline_in_refs_are_not_matched(self):
3494
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3497
class TestStackExpandOptions(tests.TestCaseWithTransport):
3500
super(TestStackExpandOptions, self).setUp()
3501
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3502
self.registry = config.option_registry
3503
self.conf = build_branch_stack(self)
3505
def assertExpansion(self, expected, string, env=None):
3506
self.assertEquals(expected, self.conf.expand_options(string, env))
3508
def test_no_expansion(self):
3509
self.assertExpansion('foo', 'foo')
3511
def test_expand_default_value(self):
3512
self.conf.store._load_from_string('bar=baz')
3513
self.registry.register(config.Option('foo', default=u'{bar}'))
3514
self.assertEquals('baz', self.conf.get('foo', expand=True))
3516
def test_expand_default_from_env(self):
3517
self.conf.store._load_from_string('bar=baz')
3518
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3519
self.overrideEnv('FOO', '{bar}')
3520
self.assertEquals('baz', self.conf.get('foo', expand=True))
3522
def test_expand_default_on_failed_conversion(self):
3523
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3524
self.registry.register(
3525
config.Option('foo', default=u'{bar}',
3526
from_unicode=config.int_from_store))
3527
self.assertEquals(42, self.conf.get('foo', expand=True))
3529
def test_env_adding_options(self):
3530
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3532
def test_env_overriding_options(self):
3533
self.conf.store._load_from_string('foo=baz')
3534
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3536
def test_simple_ref(self):
3537
self.conf.store._load_from_string('foo=xxx')
3538
self.assertExpansion('xxx', '{foo}')
3540
def test_unknown_ref(self):
3541
self.assertRaises(errors.ExpandingUnknownOption,
3542
self.conf.expand_options, '{foo}')
3544
def test_indirect_ref(self):
3545
self.conf.store._load_from_string('''
3549
self.assertExpansion('xxx', '{bar}')
3551
def test_embedded_ref(self):
3552
self.conf.store._load_from_string('''
3556
self.assertExpansion('xxx', '{{bar}}')
3558
def test_simple_loop(self):
3559
self.conf.store._load_from_string('foo={foo}')
3560
self.assertRaises(errors.OptionExpansionLoop,
3561
self.conf.expand_options, '{foo}')
3563
def test_indirect_loop(self):
3564
self.conf.store._load_from_string('''
3568
e = self.assertRaises(errors.OptionExpansionLoop,
3569
self.conf.expand_options, '{foo}')
3570
self.assertEquals('foo->bar->baz', e.refs)
3571
self.assertEquals('{foo}', e.string)
3573
def test_list(self):
3574
self.conf.store._load_from_string('''
3578
list={foo},{bar},{baz}
3580
self.registry.register(
3581
config.Option('list', from_unicode=config.list_from_store))
3582
self.assertEquals(['start', 'middle', 'end'],
3583
self.conf.get('list', expand=True))
3585
def test_cascading_list(self):
3586
self.conf.store._load_from_string('''
3592
self.registry.register(
3593
config.Option('list', from_unicode=config.list_from_store))
3594
self.assertEquals(['start', 'middle', 'end'],
3595
self.conf.get('list', expand=True))
3597
def test_pathologically_hidden_list(self):
3598
self.conf.store._load_from_string('''
3604
hidden={start}{middle}{end}
3606
# What matters is what the registration says, the conversion happens
3607
# only after all expansions have been performed
3608
self.registry.register(
3609
config.Option('hidden', from_unicode=config.list_from_store))
3610
self.assertEquals(['bin', 'go'],
3611
self.conf.get('hidden', expand=True))
3614
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3617
super(TestStackCrossSectionsExpand, self).setUp()
3619
def get_config(self, location, string):
3622
# Since we don't save the config we won't strictly require to inherit
3623
# from TestCaseInTempDir, but an error occurs so quickly...
3624
c = config.LocationStack(location)
3625
c.store._load_from_string(string)
3628
def test_dont_cross_unrelated_section(self):
3629
c = self.get_config('/another/branch/path','''
3634
[/another/branch/path]
3637
self.assertRaises(errors.ExpandingUnknownOption,
3638
c.get, 'bar', expand=True)
3640
def test_cross_related_sections(self):
3641
c = self.get_config('/project/branch/path','''
3645
[/project/branch/path]
3648
self.assertEquals('quux', c.get('bar', expand=True))
3651
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3653
def test_cross_global_locations(self):
3654
l_store = config.LocationStore()
3655
l_store._load_from_string('''
3661
g_store = config.GlobalStore()
3662
g_store._load_from_string('''
3668
stack = config.LocationStack('/branch')
3669
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3670
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3673
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3675
def test_expand_locals_empty(self):
3676
l_store = config.LocationStore()
3677
l_store._load_from_string('''
3678
[/home/user/project]
3683
stack = config.LocationStack('/home/user/project/')
3684
self.assertEquals('', stack.get('base', expand=True))
3685
self.assertEquals('', stack.get('rel', expand=True))
3687
def test_expand_basename_locally(self):
3688
l_store = config.LocationStore()
3689
l_store._load_from_string('''
3690
[/home/user/project]
3694
stack = config.LocationStack('/home/user/project/branch')
3695
self.assertEquals('branch', stack.get('bfoo', expand=True))
3697
def test_expand_basename_locally_longer_path(self):
3698
l_store = config.LocationStore()
3699
l_store._load_from_string('''
3704
stack = config.LocationStack('/home/user/project/dir/branch')
3705
self.assertEquals('branch', stack.get('bfoo', expand=True))
3707
def test_expand_relpath_locally(self):
3708
l_store = config.LocationStore()
3709
l_store._load_from_string('''
3710
[/home/user/project]
3711
lfoo = loc-foo/{relpath}
3714
stack = config.LocationStack('/home/user/project/branch')
3715
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3717
def test_expand_relpath_unknonw_in_global(self):
3718
g_store = config.GlobalStore()
3719
g_store._load_from_string('''
3724
stack = config.LocationStack('/home/user/project/branch')
3725
self.assertRaises(errors.ExpandingUnknownOption,
3726
stack.get, 'gfoo', expand=True)
3728
def test_expand_local_option_locally(self):
3729
l_store = config.LocationStore()
3730
l_store._load_from_string('''
3731
[/home/user/project]
3732
lfoo = loc-foo/{relpath}
3736
g_store = config.GlobalStore()
3737
g_store._load_from_string('''
3743
stack = config.LocationStack('/home/user/project/branch')
3744
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3745
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3747
def test_locals_dont_leak(self):
3748
"""Make sure we chose the right local in presence of several sections.
3750
l_store = config.LocationStore()
3751
l_store._load_from_string('''
3753
lfoo = loc-foo/{relpath}
3754
[/home/user/project]
3755
lfoo = loc-foo/{relpath}
3758
stack = config.LocationStack('/home/user/project/branch')
3759
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3760
stack = config.LocationStack('/home/user/bar/baz')
3761
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3765
class TestStackSet(TestStackWithTransport):
3767
def test_simple_set(self):
3768
conf = self.get_stack(self)
3769
self.assertEquals(None, conf.get('foo'))
3770
conf.set('foo', 'baz')
3771
# Did we get it back ?
3772
self.assertEquals('baz', conf.get('foo'))
3774
def test_set_creates_a_new_section(self):
3775
conf = self.get_stack(self)
3776
conf.set('foo', 'baz')
3777
self.assertEquals, 'baz', conf.get('foo')
3779
def test_set_hook(self):
3783
config.ConfigHooks.install_named_hook('set', hook, None)
3784
self.assertLength(0, calls)
3785
conf = self.get_stack(self)
3786
conf.set('foo', 'bar')
3787
self.assertLength(1, calls)
3788
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3791
class TestStackRemove(TestStackWithTransport):
3793
def test_remove_existing(self):
3794
conf = self.get_stack(self)
3795
conf.set('foo', 'bar')
3796
self.assertEquals('bar', conf.get('foo'))
3798
# Did we get it back ?
3799
self.assertEquals(None, conf.get('foo'))
3801
def test_remove_unknown(self):
3802
conf = self.get_stack(self)
3803
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3805
def test_remove_hook(self):
3809
config.ConfigHooks.install_named_hook('remove', hook, None)
3810
self.assertLength(0, calls)
3811
conf = self.get_stack(self)
3812
conf.set('foo', 'bar')
3814
self.assertLength(1, calls)
3815
self.assertEquals((conf, 'foo'), calls[0])
3818
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3821
super(TestConfigGetOptions, self).setUp()
3822
create_configs(self)
3824
def test_no_variable(self):
3825
# Using branch should query branch, locations and bazaar
3826
self.assertOptions([], self.branch_config)
3828
def test_option_in_bazaar(self):
3829
self.bazaar_config.set_user_option('file', 'bazaar')
3830
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3833
def test_option_in_locations(self):
3834
self.locations_config.set_user_option('file', 'locations')
3836
[('file', 'locations', self.tree.basedir, 'locations')],
3837
self.locations_config)
3839
def test_option_in_branch(self):
3840
self.branch_config.set_user_option('file', 'branch')
3841
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3844
def test_option_in_bazaar_and_branch(self):
3845
self.bazaar_config.set_user_option('file', 'bazaar')
3846
self.branch_config.set_user_option('file', 'branch')
3847
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3848
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3851
def test_option_in_branch_and_locations(self):
3852
# Hmm, locations override branch :-/
3853
self.locations_config.set_user_option('file', 'locations')
3854
self.branch_config.set_user_option('file', 'branch')
3856
[('file', 'locations', self.tree.basedir, 'locations'),
3857
('file', 'branch', 'DEFAULT', 'branch'),],
3860
def test_option_in_bazaar_locations_and_branch(self):
3861
self.bazaar_config.set_user_option('file', 'bazaar')
3862
self.locations_config.set_user_option('file', 'locations')
3863
self.branch_config.set_user_option('file', 'branch')
3865
[('file', 'locations', self.tree.basedir, 'locations'),
3866
('file', 'branch', 'DEFAULT', 'branch'),
3867
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3871
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3874
super(TestConfigRemoveOption, self).setUp()
3875
create_configs_with_file_option(self)
3877
def test_remove_in_locations(self):
3878
self.locations_config.remove_user_option('file', self.tree.basedir)
3880
[('file', 'branch', 'DEFAULT', 'branch'),
3881
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3884
def test_remove_in_branch(self):
3885
self.branch_config.remove_user_option('file')
3887
[('file', 'locations', self.tree.basedir, 'locations'),
3888
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3891
def test_remove_in_bazaar(self):
3892
self.bazaar_config.remove_user_option('file')
3894
[('file', 'locations', self.tree.basedir, 'locations'),
3895
('file', 'branch', 'DEFAULT', 'branch'),],
3899
class TestConfigGetSections(tests.TestCaseWithTransport):
3902
super(TestConfigGetSections, self).setUp()
3903
create_configs(self)
3905
def assertSectionNames(self, expected, conf, name=None):
3906
"""Check which sections are returned for a given config.
3908
If fallback configurations exist their sections can be included.
3910
:param expected: A list of section names.
3912
:param conf: The configuration that will be queried.
3914
:param name: An optional section name that will be passed to
3917
sections = list(conf._get_sections(name))
3918
self.assertLength(len(expected), sections)
3919
self.assertEqual(expected, [name for name, _, _ in sections])
3921
def test_bazaar_default_section(self):
3922
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3924
def test_locations_default_section(self):
3925
# No sections are defined in an empty file
3926
self.assertSectionNames([], self.locations_config)
3928
def test_locations_named_section(self):
3929
self.locations_config.set_user_option('file', 'locations')
3930
self.assertSectionNames([self.tree.basedir], self.locations_config)
3932
def test_locations_matching_sections(self):
3933
loc_config = self.locations_config
3934
loc_config.set_user_option('file', 'locations')
3935
# We need to cheat a bit here to create an option in sections above and
3936
# below the 'location' one.
3937
parser = loc_config._get_parser()
3938
# locations.cong deals with '/' ignoring native os.sep
3939
location_names = self.tree.basedir.split('/')
3940
parent = '/'.join(location_names[:-1])
3941
child = '/'.join(location_names + ['child'])
3943
parser[parent]['file'] = 'parent'
3945
parser[child]['file'] = 'child'
3946
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3948
def test_branch_data_default_section(self):
3949
self.assertSectionNames([None],
3950
self.branch_config._get_branch_data_config())
3952
def test_branch_default_sections(self):
3953
# No sections are defined in an empty locations file
3954
self.assertSectionNames([None, 'DEFAULT'],
3956
# Unless we define an option
3957
self.branch_config._get_location_config().set_user_option(
3958
'file', 'locations')
3959
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3962
def test_bazaar_named_section(self):
3963
# We need to cheat as the API doesn't give direct access to sections
3964
# other than DEFAULT.
3965
self.bazaar_config.set_alias('bazaar', 'bzr')
3966
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
3969
class TestAuthenticationConfigFile(tests.TestCase):
1316
3970
"""Test the authentication.conf file matching"""