367
566
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
569
class TestXDGConfigDir(tests.TestCaseInTempDir):
570
# must be in temp dir because config tests for the existence of the bazaar
571
# subdirectory of $XDG_CONFIG_HOME
574
if sys.platform in ('darwin', 'win32'):
575
raise tests.TestNotApplicable(
576
'XDG config dir not used on this platform')
577
super(TestXDGConfigDir, self).setUp()
578
self.overrideEnv('HOME', self.test_home_dir)
579
# BZR_HOME overrides everything we want to test so unset it.
580
self.overrideEnv('BZR_HOME', None)
582
def test_xdg_config_dir_exists(self):
583
"""When ~/.config/bazaar exists, use it as the config dir."""
584
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
586
self.assertEqual(config.config_dir(), newdir)
588
def test_xdg_config_home(self):
589
"""When XDG_CONFIG_HOME is set, use it."""
590
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
591
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
592
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
594
self.assertEqual(config.config_dir(), newdir)
597
class TestIniConfig(tests.TestCaseInTempDir):
372
599
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
600
conf = config.IniBasedConfig.from_string(s)
601
return conf, conf._get_parser()
378
604
class TestIniConfigBuilding(TestIniConfig):
380
606
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
607
my_config = config.IniBasedConfig()
383
609
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))
610
my_config = config.IniBasedConfig.from_string(sample_config_text)
611
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
613
def test_cached(self):
614
my_config = config.IniBasedConfig.from_string(sample_config_text)
615
parser = my_config._get_parser()
616
self.assertTrue(my_config._get_parser() is parser)
618
def _dummy_chown(self, path, uid, gid):
619
self.path, self.uid, self.gid = path, uid, gid
621
def test_ini_config_ownership(self):
622
"""Ensure that chown is happening during _write_config_file"""
623
self.requireFeature(features.chown_feature)
624
self.overrideAttr(os, 'chown', self._dummy_chown)
625
self.path = self.uid = self.gid = None
626
conf = config.IniBasedConfig(file_name='./foo.conf')
627
conf._write_config_file()
628
self.assertEquals(self.path, './foo.conf')
629
self.assertTrue(isinstance(self.uid, int))
630
self.assertTrue(isinstance(self.gid, int))
632
def test_get_filename_parameter_is_deprecated_(self):
633
conf = self.callDeprecated([
634
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
635
' Use file_name instead.'],
636
config.IniBasedConfig, lambda: 'ini.conf')
637
self.assertEqual('ini.conf', conf.file_name)
639
def test_get_parser_file_parameter_is_deprecated_(self):
391
640
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)
641
conf = config.IniBasedConfig.from_string(sample_config_text)
642
conf = self.callDeprecated([
643
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
644
' Use IniBasedConfig(_content=xxx) instead.'],
645
conf._get_parser, file=config_file)
648
class TestIniConfigSaving(tests.TestCaseInTempDir):
650
def test_cant_save_without_a_file_name(self):
651
conf = config.IniBasedConfig()
652
self.assertRaises(AssertionError, conf._write_config_file)
654
def test_saved_with_content(self):
655
content = 'foo = bar\n'
656
conf = config.IniBasedConfig.from_string(
657
content, file_name='./test.conf', save=True)
658
self.assertFileEqual(content, 'test.conf')
661
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
662
"""What is the default value of expand for config options.
664
This is an opt-in beta feature used to evaluate whether or not option
665
references can appear in dangerous place raising exceptions, disapearing
666
(and as such corrupting data) or if it's safe to activate the option by
669
Note that these tests relies on config._expand_default_value being already
670
overwritten in the parent class setUp.
674
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
678
self.warnings.append(args[0] % args[1:])
679
self.overrideAttr(trace, 'warning', warning)
681
def get_config(self, expand):
682
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
686
def assertExpandIs(self, expected):
687
actual = config._get_expand_default_value()
688
#self.config.get_user_option_as_bool('bzr.config.expand')
689
self.assertEquals(expected, actual)
691
def test_default_is_None(self):
692
self.assertEquals(None, config._expand_default_value)
694
def test_default_is_False_even_if_None(self):
695
self.config = self.get_config(None)
696
self.assertExpandIs(False)
698
def test_default_is_False_even_if_invalid(self):
699
self.config = self.get_config('<your choice>')
700
self.assertExpandIs(False)
702
# Huh ? My choice is False ? Thanks, always happy to hear that :D
703
# Wait, you've been warned !
704
self.assertLength(1, self.warnings)
706
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
709
def test_default_is_True(self):
710
self.config = self.get_config(True)
711
self.assertExpandIs(True)
713
def test_default_is_False(self):
714
self.config = self.get_config(False)
715
self.assertExpandIs(False)
718
class TestIniConfigOptionExpansion(tests.TestCase):
719
"""Test option expansion from the IniConfig level.
721
What we really want here is to test the Config level, but the class being
722
abstract as far as storing values is concerned, this can't be done
725
# FIXME: This should be rewritten when all configs share a storage
726
# implementation -- vila 2011-02-18
728
def get_config(self, string=None):
731
c = config.IniBasedConfig.from_string(string)
734
def assertExpansion(self, expected, conf, string, env=None):
735
self.assertEquals(expected, conf.expand_options(string, env))
737
def test_no_expansion(self):
738
c = self.get_config('')
739
self.assertExpansion('foo', c, 'foo')
741
def test_env_adding_options(self):
742
c = self.get_config('')
743
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
745
def test_env_overriding_options(self):
746
c = self.get_config('foo=baz')
747
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
749
def test_simple_ref(self):
750
c = self.get_config('foo=xxx')
751
self.assertExpansion('xxx', c, '{foo}')
753
def test_unknown_ref(self):
754
c = self.get_config('')
755
self.assertRaises(errors.ExpandingUnknownOption,
756
c.expand_options, '{foo}')
758
def test_indirect_ref(self):
759
c = self.get_config('''
763
self.assertExpansion('xxx', c, '{bar}')
765
def test_embedded_ref(self):
766
c = self.get_config('''
770
self.assertExpansion('xxx', c, '{{bar}}')
772
def test_simple_loop(self):
773
c = self.get_config('foo={foo}')
774
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
776
def test_indirect_loop(self):
777
c = self.get_config('''
781
e = self.assertRaises(errors.OptionExpansionLoop,
782
c.expand_options, '{foo}')
783
self.assertEquals('foo->bar->baz', e.refs)
784
self.assertEquals('{foo}', e.string)
787
conf = self.get_config('''
791
list={foo},{bar},{baz}
793
self.assertEquals(['start', 'middle', 'end'],
794
conf.get_user_option('list', expand=True))
796
def test_cascading_list(self):
797
conf = self.get_config('''
803
self.assertEquals(['start', 'middle', 'end'],
804
conf.get_user_option('list', expand=True))
806
def test_pathological_hidden_list(self):
807
conf = self.get_config('''
813
hidden={start}{middle}{end}
815
# Nope, it's either a string or a list, and the list wins as soon as a
816
# ',' appears, so the string concatenation never occur.
817
self.assertEquals(['{foo', '}', '{', 'bar}'],
818
conf.get_user_option('hidden', expand=True))
820
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
822
def get_config(self, location, string=None):
825
# Since we don't save the config we won't strictly require to inherit
826
# from TestCaseInTempDir, but an error occurs so quickly...
827
c = config.LocationConfig.from_string(string, location)
830
def test_dont_cross_unrelated_section(self):
831
c = self.get_config('/another/branch/path','''
836
[/another/branch/path]
839
self.assertRaises(errors.ExpandingUnknownOption,
840
c.get_user_option, 'bar', expand=True)
842
def test_cross_related_sections(self):
843
c = self.get_config('/project/branch/path','''
847
[/project/branch/path]
850
self.assertEquals('quux', c.get_user_option('bar', expand=True))
853
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
855
def test_cannot_reload_without_name(self):
856
conf = config.IniBasedConfig.from_string(sample_config_text)
857
self.assertRaises(AssertionError, conf.reload)
859
def test_reload_see_new_value(self):
860
c1 = config.IniBasedConfig.from_string('editor=vim\n',
861
file_name='./test/conf')
862
c1._write_config_file()
863
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
864
file_name='./test/conf')
865
c2._write_config_file()
866
self.assertEqual('vim', c1.get_user_option('editor'))
867
self.assertEqual('emacs', c2.get_user_option('editor'))
868
# Make sure we get the Right value
870
self.assertEqual('emacs', c1.get_user_option('editor'))
873
class TestLockableConfig(tests.TestCaseInTempDir):
875
scenarios = lockable_config_scenarios()
880
config_section = None
883
super(TestLockableConfig, self).setUp()
884
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
885
self.config = self.create_config(self._content)
887
def get_existing_config(self):
888
return self.config_class(*self.config_args)
890
def create_config(self, content):
891
kwargs = dict(save=True)
892
c = self.config_class.from_string(content, *self.config_args, **kwargs)
895
def test_simple_read_access(self):
896
self.assertEquals('1', self.config.get_user_option('one'))
898
def test_simple_write_access(self):
899
self.config.set_user_option('one', 'one')
900
self.assertEquals('one', self.config.get_user_option('one'))
902
def test_listen_to_the_last_speaker(self):
904
c2 = self.get_existing_config()
905
c1.set_user_option('one', 'ONE')
906
c2.set_user_option('two', 'TWO')
907
self.assertEquals('ONE', c1.get_user_option('one'))
908
self.assertEquals('TWO', c2.get_user_option('two'))
909
# The second update respect the first one
910
self.assertEquals('ONE', c2.get_user_option('one'))
912
def test_last_speaker_wins(self):
913
# If the same config is not shared, the same variable modified twice
914
# can only see a single result.
916
c2 = self.get_existing_config()
917
c1.set_user_option('one', 'c1')
918
c2.set_user_option('one', 'c2')
919
self.assertEquals('c2', c2._get_user_option('one'))
920
# The first modification is still available until another refresh
922
self.assertEquals('c1', c1._get_user_option('one'))
923
c1.set_user_option('two', 'done')
924
self.assertEquals('c2', c1._get_user_option('one'))
926
def test_writes_are_serialized(self):
928
c2 = self.get_existing_config()
930
# We spawn a thread that will pause *during* the write
931
before_writing = threading.Event()
932
after_writing = threading.Event()
933
writing_done = threading.Event()
934
c1_orig = c1._write_config_file
935
def c1_write_config_file():
938
# The lock is held. We wait for the main thread to decide when to
941
c1._write_config_file = c1_write_config_file
943
c1.set_user_option('one', 'c1')
945
t1 = threading.Thread(target=c1_set_option)
946
# Collect the thread after the test
947
self.addCleanup(t1.join)
948
# Be ready to unblock the thread if the test goes wrong
949
self.addCleanup(after_writing.set)
951
before_writing.wait()
952
self.assertTrue(c1._lock.is_held)
953
self.assertRaises(errors.LockContention,
954
c2.set_user_option, 'one', 'c2')
955
self.assertEquals('c1', c1.get_user_option('one'))
956
# Let the lock be released
959
c2.set_user_option('one', 'c2')
960
self.assertEquals('c2', c2.get_user_option('one'))
962
def test_read_while_writing(self):
964
# We spawn a thread that will pause *during* the write
965
ready_to_write = threading.Event()
966
do_writing = threading.Event()
967
writing_done = threading.Event()
968
c1_orig = c1._write_config_file
969
def c1_write_config_file():
971
# The lock is held. We wait for the main thread to decide when to
976
c1._write_config_file = c1_write_config_file
978
c1.set_user_option('one', 'c1')
979
t1 = threading.Thread(target=c1_set_option)
980
# Collect the thread after the test
981
self.addCleanup(t1.join)
982
# Be ready to unblock the thread if the test goes wrong
983
self.addCleanup(do_writing.set)
985
# Ensure the thread is ready to write
986
ready_to_write.wait()
987
self.assertTrue(c1._lock.is_held)
988
self.assertEquals('c1', c1.get_user_option('one'))
989
# If we read during the write, we get the old value
990
c2 = self.get_existing_config()
991
self.assertEquals('1', c2.get_user_option('one'))
992
# Let the writing occur and ensure it occurred
995
# Now we get the updated value
996
c3 = self.get_existing_config()
997
self.assertEquals('c1', c3.get_user_option('one'))
397
1000
class TestGetUserOptionAs(TestIniConfig):
1312
1952
self.assertIs(None, bzrdir_config.get_default_stack_on())
1955
class TestOldConfigHooks(tests.TestCaseWithTransport):
1958
super(TestOldConfigHooks, self).setUp()
1959
create_configs_with_file_option(self)
1961
def assertGetHook(self, conf, name, value):
1965
config.OldConfigHooks.install_named_hook('get', hook, None)
1967
config.OldConfigHooks.uninstall_named_hook, 'get', None)
1968
self.assertLength(0, calls)
1969
actual_value = conf.get_user_option(name)
1970
self.assertEquals(value, actual_value)
1971
self.assertLength(1, calls)
1972
self.assertEquals((conf, name, value), calls[0])
1974
def test_get_hook_bazaar(self):
1975
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
1977
def test_get_hook_locations(self):
1978
self.assertGetHook(self.locations_config, 'file', 'locations')
1980
def test_get_hook_branch(self):
1981
# Since locations masks branch, we define a different option
1982
self.branch_config.set_user_option('file2', 'branch')
1983
self.assertGetHook(self.branch_config, 'file2', 'branch')
1985
def assertSetHook(self, conf, name, value):
1989
config.OldConfigHooks.install_named_hook('set', hook, None)
1991
config.OldConfigHooks.uninstall_named_hook, 'set', None)
1992
self.assertLength(0, calls)
1993
conf.set_user_option(name, value)
1994
self.assertLength(1, calls)
1995
# We can't assert the conf object below as different configs use
1996
# different means to implement set_user_option and we care only about
1998
self.assertEquals((name, value), calls[0][1:])
2000
def test_set_hook_bazaar(self):
2001
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2003
def test_set_hook_locations(self):
2004
self.assertSetHook(self.locations_config, 'foo', 'locations')
2006
def test_set_hook_branch(self):
2007
self.assertSetHook(self.branch_config, 'foo', 'branch')
2009
def assertRemoveHook(self, conf, name, section_name=None):
2013
config.OldConfigHooks.install_named_hook('remove', hook, None)
2015
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2016
self.assertLength(0, calls)
2017
conf.remove_user_option(name, section_name)
2018
self.assertLength(1, calls)
2019
# We can't assert the conf object below as different configs use
2020
# different means to implement remove_user_option and we care only about
2022
self.assertEquals((name,), calls[0][1:])
2024
def test_remove_hook_bazaar(self):
2025
self.assertRemoveHook(self.bazaar_config, 'file')
2027
def test_remove_hook_locations(self):
2028
self.assertRemoveHook(self.locations_config, 'file',
2029
self.locations_config.location)
2031
def test_remove_hook_branch(self):
2032
self.assertRemoveHook(self.branch_config, 'file')
2034
def assertLoadHook(self, name, conf_class, *conf_args):
2038
config.OldConfigHooks.install_named_hook('load', hook, None)
2040
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2041
self.assertLength(0, calls)
2043
conf = conf_class(*conf_args)
2044
# Access an option to trigger a load
2045
conf.get_user_option(name)
2046
self.assertLength(1, calls)
2047
# Since we can't assert about conf, we just use the number of calls ;-/
2049
def test_load_hook_bazaar(self):
2050
self.assertLoadHook('file', config.GlobalConfig)
2052
def test_load_hook_locations(self):
2053
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2055
def test_load_hook_branch(self):
2056
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2058
def assertSaveHook(self, conf):
2062
config.OldConfigHooks.install_named_hook('save', hook, None)
2064
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2065
self.assertLength(0, calls)
2066
# Setting an option triggers a save
2067
conf.set_user_option('foo', 'bar')
2068
self.assertLength(1, calls)
2069
# Since we can't assert about conf, we just use the number of calls ;-/
2071
def test_save_hook_bazaar(self):
2072
self.assertSaveHook(self.bazaar_config)
2074
def test_save_hook_locations(self):
2075
self.assertSaveHook(self.locations_config)
2077
def test_save_hook_branch(self):
2078
self.assertSaveHook(self.branch_config)
2081
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2082
"""Tests config hooks for remote configs.
2084
No tests for the remove hook as this is not implemented there.
2088
super(TestOldConfigHooksForRemote, self).setUp()
2089
self.transport_server = test_server.SmartTCPServer_for_testing
2090
create_configs_with_file_option(self)
2092
def assertGetHook(self, conf, name, value):
2096
config.OldConfigHooks.install_named_hook('get', hook, None)
2098
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2099
self.assertLength(0, calls)
2100
actual_value = conf.get_option(name)
2101
self.assertEquals(value, actual_value)
2102
self.assertLength(1, calls)
2103
self.assertEquals((conf, name, value), calls[0])
2105
def test_get_hook_remote_branch(self):
2106
remote_branch = branch.Branch.open(self.get_url('tree'))
2107
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2109
def test_get_hook_remote_bzrdir(self):
2110
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2111
conf = remote_bzrdir._get_config()
2112
conf.set_option('remotedir', 'file')
2113
self.assertGetHook(conf, 'file', 'remotedir')
2115
def assertSetHook(self, conf, name, value):
2119
config.OldConfigHooks.install_named_hook('set', hook, None)
2121
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2122
self.assertLength(0, calls)
2123
conf.set_option(value, name)
2124
self.assertLength(1, calls)
2125
# We can't assert the conf object below as different configs use
2126
# different means to implement set_user_option and we care only about
2128
self.assertEquals((name, value), calls[0][1:])
2130
def test_set_hook_remote_branch(self):
2131
remote_branch = branch.Branch.open(self.get_url('tree'))
2132
self.addCleanup(remote_branch.lock_write().unlock)
2133
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2135
def test_set_hook_remote_bzrdir(self):
2136
remote_branch = branch.Branch.open(self.get_url('tree'))
2137
self.addCleanup(remote_branch.lock_write().unlock)
2138
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2139
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2141
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2145
config.OldConfigHooks.install_named_hook('load', hook, None)
2147
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2148
self.assertLength(0, calls)
2150
conf = conf_class(*conf_args)
2151
# Access an option to trigger a load
2152
conf.get_option(name)
2153
self.assertLength(expected_nb_calls, calls)
2154
# Since we can't assert about conf, we just use the number of calls ;-/
2156
def test_load_hook_remote_branch(self):
2157
remote_branch = branch.Branch.open(self.get_url('tree'))
2158
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2160
def test_load_hook_remote_bzrdir(self):
2161
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2162
# The config file doesn't exist, set an option to force its creation
2163
conf = remote_bzrdir._get_config()
2164
conf.set_option('remotedir', 'file')
2165
# We get one call for the server and one call for the client, this is
2166
# caused by the differences in implementations betwen
2167
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2168
# SmartServerBranchGetConfigFile (in smart/branch.py)
2169
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2171
def assertSaveHook(self, conf):
2175
config.OldConfigHooks.install_named_hook('save', hook, None)
2177
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2178
self.assertLength(0, calls)
2179
# Setting an option triggers a save
2180
conf.set_option('foo', 'bar')
2181
self.assertLength(1, calls)
2182
# Since we can't assert about conf, we just use the number of calls ;-/
2184
def test_save_hook_remote_branch(self):
2185
remote_branch = branch.Branch.open(self.get_url('tree'))
2186
self.addCleanup(remote_branch.lock_write().unlock)
2187
self.assertSaveHook(remote_branch._get_config())
2189
def test_save_hook_remote_bzrdir(self):
2190
remote_branch = branch.Branch.open(self.get_url('tree'))
2191
self.addCleanup(remote_branch.lock_write().unlock)
2192
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2193
self.assertSaveHook(remote_bzrdir._get_config())
2196
class TestOption(tests.TestCase):
2198
def test_default_value(self):
2199
opt = config.Option('foo', default='bar')
2200
self.assertEquals('bar', opt.get_default())
2203
class TestOptionRegistry(tests.TestCase):
2206
super(TestOptionRegistry, self).setUp()
2207
# Always start with an empty registry
2208
self.overrideAttr(config, 'option_registry', registry.Registry())
2209
self.registry = config.option_registry
2211
def test_register(self):
2212
opt = config.Option('foo')
2213
self.registry.register('foo', opt)
2214
self.assertIs(opt, self.registry.get('foo'))
2216
lazy_option = config.Option('lazy_foo')
2218
def test_register_lazy(self):
2219
self.registry.register_lazy('foo', self.__module__,
2220
'TestOptionRegistry.lazy_option')
2221
self.assertIs(self.lazy_option, self.registry.get('foo'))
2223
def test_registered_help(self):
2224
opt = config.Option('foo')
2225
self.registry.register('foo', opt, help='A simple option')
2226
self.assertEquals('A simple option', self.registry.get_help('foo'))
2229
class TestRegisteredOptions(tests.TestCase):
2230
"""All registered options should verify some constraints."""
2232
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2233
in config.option_registry.iteritems()]
2236
super(TestRegisteredOptions, self).setUp()
2237
self.registry = config.option_registry
2239
def test_proper_name(self):
2240
# An option should be registered under its own name, this can't be
2241
# checked at registration time for the lazy ones.
2242
self.assertEquals(self.option_name, self.option.name)
2244
def test_help_is_set(self):
2245
option_help = self.registry.get_help(self.option_name)
2246
self.assertNotEquals(None, option_help)
2247
# Come on, think about the user, he really wants to know whst the
2249
self.assertNotEquals('', option_help)
2252
class TestSection(tests.TestCase):
2254
# FIXME: Parametrize so that all sections produced by Stores run these
2255
# tests -- vila 2011-04-01
2257
def test_get_a_value(self):
2258
a_dict = dict(foo='bar')
2259
section = config.Section('myID', a_dict)
2260
self.assertEquals('bar', section.get('foo'))
2262
def test_get_unknown_option(self):
2264
section = config.Section(None, a_dict)
2265
self.assertEquals('out of thin air',
2266
section.get('foo', 'out of thin air'))
2268
def test_options_is_shared(self):
2270
section = config.Section(None, a_dict)
2271
self.assertIs(a_dict, section.options)
2274
class TestMutableSection(tests.TestCase):
2276
# FIXME: Parametrize so that all sections (including os.environ and the
2277
# ones produced by Stores) run these tests -- vila 2011-04-01
2280
a_dict = dict(foo='bar')
2281
section = config.MutableSection('myID', a_dict)
2282
section.set('foo', 'new_value')
2283
self.assertEquals('new_value', section.get('foo'))
2284
# The change appears in the shared section
2285
self.assertEquals('new_value', a_dict.get('foo'))
2286
# We keep track of the change
2287
self.assertTrue('foo' in section.orig)
2288
self.assertEquals('bar', section.orig.get('foo'))
2290
def test_set_preserve_original_once(self):
2291
a_dict = dict(foo='bar')
2292
section = config.MutableSection('myID', a_dict)
2293
section.set('foo', 'first_value')
2294
section.set('foo', 'second_value')
2295
# We keep track of the original value
2296
self.assertTrue('foo' in section.orig)
2297
self.assertEquals('bar', section.orig.get('foo'))
2299
def test_remove(self):
2300
a_dict = dict(foo='bar')
2301
section = config.MutableSection('myID', a_dict)
2302
section.remove('foo')
2303
# We get None for unknown options via the default value
2304
self.assertEquals(None, section.get('foo'))
2305
# Or we just get the default value
2306
self.assertEquals('unknown', section.get('foo', 'unknown'))
2307
self.assertFalse('foo' in section.options)
2308
# We keep track of the deletion
2309
self.assertTrue('foo' in section.orig)
2310
self.assertEquals('bar', section.orig.get('foo'))
2312
def test_remove_new_option(self):
2314
section = config.MutableSection('myID', a_dict)
2315
section.set('foo', 'bar')
2316
section.remove('foo')
2317
self.assertFalse('foo' in section.options)
2318
# The option didn't exist initially so it we need to keep track of it
2319
# with a special value
2320
self.assertTrue('foo' in section.orig)
2321
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2324
class TestStore(tests.TestCaseWithTransport):
2326
def assertSectionContent(self, expected, section):
2327
"""Assert that some options have the proper values in a section."""
2328
expected_name, expected_options = expected
2329
self.assertEquals(expected_name, section.id)
2332
dict([(k, section.get(k)) for k in expected_options.keys()]))
2335
class TestReadonlyStore(TestStore):
2337
scenarios = [(key, {'get_store': builder}) for key, builder
2338
in config.test_store_builder_registry.iteritems()]
2341
super(TestReadonlyStore, self).setUp()
2343
def test_building_delays_load(self):
2344
store = self.get_store(self)
2345
self.assertEquals(False, store.is_loaded())
2346
store._load_from_string('')
2347
self.assertEquals(True, store.is_loaded())
2349
def test_get_no_sections_for_empty(self):
2350
store = self.get_store(self)
2351
store._load_from_string('')
2352
self.assertEquals([], list(store.get_sections()))
2354
def test_get_default_section(self):
2355
store = self.get_store(self)
2356
store._load_from_string('foo=bar')
2357
sections = list(store.get_sections())
2358
self.assertLength(1, sections)
2359
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2361
def test_get_named_section(self):
2362
store = self.get_store(self)
2363
store._load_from_string('[baz]\nfoo=bar')
2364
sections = list(store.get_sections())
2365
self.assertLength(1, sections)
2366
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2368
def test_load_from_string_fails_for_non_empty_store(self):
2369
store = self.get_store(self)
2370
store._load_from_string('foo=bar')
2371
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2374
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2375
"""Simulate loading a config store without content of various encodings.
2377
All files produced by bzr are in utf8 content.
2379
Users may modify them manually and end up with a file that can't be
2380
loaded. We need to issue proper error messages in this case.
2383
invalid_utf8_char = '\xff'
2385
def test_load_utf8(self):
2386
"""Ensure we can load an utf8-encoded file."""
2387
t = self.get_transport()
2388
# From http://pad.lv/799212
2389
unicode_user = u'b\N{Euro Sign}ar'
2390
unicode_content = u'user=%s' % (unicode_user,)
2391
utf8_content = unicode_content.encode('utf8')
2392
# Store the raw content in the config file
2393
t.put_bytes('foo.conf', utf8_content)
2394
store = config.IniFileStore(t, 'foo.conf')
2396
stack = config.Stack([store.get_sections], store)
2397
self.assertEquals(unicode_user, stack.get('user'))
2399
def test_load_non_ascii(self):
2400
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2401
t = self.get_transport()
2402
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2403
store = config.IniFileStore(t, 'foo.conf')
2404
self.assertRaises(errors.ConfigContentError, store.load)
2406
def test_load_erroneous_content(self):
2407
"""Ensure we display a proper error on content that can't be parsed."""
2408
t = self.get_transport()
2409
t.put_bytes('foo.conf', '[open_section\n')
2410
store = config.IniFileStore(t, 'foo.conf')
2411
self.assertRaises(errors.ParseConfigError, store.load)
2414
class TestIniConfigContent(tests.TestCaseWithTransport):
2415
"""Simulate loading a IniBasedConfig without content of various encodings.
2417
All files produced by bzr are in utf8 content.
2419
Users may modify them manually and end up with a file that can't be
2420
loaded. We need to issue proper error messages in this case.
2423
invalid_utf8_char = '\xff'
2425
def test_load_utf8(self):
2426
"""Ensure we can load an utf8-encoded file."""
2427
# From http://pad.lv/799212
2428
unicode_user = u'b\N{Euro Sign}ar'
2429
unicode_content = u'user=%s' % (unicode_user,)
2430
utf8_content = unicode_content.encode('utf8')
2431
# Store the raw content in the config file
2432
with open('foo.conf', 'wb') as f:
2433
f.write(utf8_content)
2434
conf = config.IniBasedConfig(file_name='foo.conf')
2435
self.assertEquals(unicode_user, conf.get_user_option('user'))
2437
def test_load_badly_encoded_content(self):
2438
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2439
with open('foo.conf', 'wb') as f:
2440
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2441
conf = config.IniBasedConfig(file_name='foo.conf')
2442
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2444
def test_load_erroneous_content(self):
2445
"""Ensure we display a proper error on content that can't be parsed."""
2446
with open('foo.conf', 'wb') as f:
2447
f.write('[open_section\n')
2448
conf = config.IniBasedConfig(file_name='foo.conf')
2449
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2452
class TestMutableStore(TestStore):
2454
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2455
in config.test_store_builder_registry.iteritems()]
2458
super(TestMutableStore, self).setUp()
2459
self.transport = self.get_transport()
2461
def has_store(self, store):
2462
store_basename = urlutils.relative_url(self.transport.external_url(),
2463
store.external_url())
2464
return self.transport.has(store_basename)
2466
def test_save_empty_creates_no_file(self):
2467
# FIXME: There should be a better way than relying on the test
2468
# parametrization to identify branch.conf -- vila 2011-0526
2469
if self.store_id in ('branch', 'remote_branch'):
2470
raise tests.TestNotApplicable(
2471
'branch.conf is *always* created when a branch is initialized')
2472
store = self.get_store(self)
2474
self.assertEquals(False, self.has_store(store))
2476
def test_save_emptied_succeeds(self):
2477
store = self.get_store(self)
2478
store._load_from_string('foo=bar\n')
2479
section = store.get_mutable_section(None)
2480
section.remove('foo')
2482
self.assertEquals(True, self.has_store(store))
2483
modified_store = self.get_store(self)
2484
sections = list(modified_store.get_sections())
2485
self.assertLength(0, sections)
2487
def test_save_with_content_succeeds(self):
2488
# FIXME: There should be a better way than relying on the test
2489
# parametrization to identify branch.conf -- vila 2011-0526
2490
if self.store_id in ('branch', 'remote_branch'):
2491
raise tests.TestNotApplicable(
2492
'branch.conf is *always* created when a branch is initialized')
2493
store = self.get_store(self)
2494
store._load_from_string('foo=bar\n')
2495
self.assertEquals(False, self.has_store(store))
2497
self.assertEquals(True, self.has_store(store))
2498
modified_store = self.get_store(self)
2499
sections = list(modified_store.get_sections())
2500
self.assertLength(1, sections)
2501
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2503
def test_set_option_in_empty_store(self):
2504
store = self.get_store(self)
2505
section = store.get_mutable_section(None)
2506
section.set('foo', 'bar')
2508
modified_store = self.get_store(self)
2509
sections = list(modified_store.get_sections())
2510
self.assertLength(1, sections)
2511
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2513
def test_set_option_in_default_section(self):
2514
store = self.get_store(self)
2515
store._load_from_string('')
2516
section = store.get_mutable_section(None)
2517
section.set('foo', 'bar')
2519
modified_store = self.get_store(self)
2520
sections = list(modified_store.get_sections())
2521
self.assertLength(1, sections)
2522
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2524
def test_set_option_in_named_section(self):
2525
store = self.get_store(self)
2526
store._load_from_string('')
2527
section = store.get_mutable_section('baz')
2528
section.set('foo', 'bar')
2530
modified_store = self.get_store(self)
2531
sections = list(modified_store.get_sections())
2532
self.assertLength(1, sections)
2533
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2535
def test_load_hook(self):
2536
# We first needs to ensure that the store exists
2537
store = self.get_store(self)
2538
section = store.get_mutable_section('baz')
2539
section.set('foo', 'bar')
2541
# Now we can try to load it
2542
store = self.get_store(self)
2546
config.ConfigHooks.install_named_hook('load', hook, None)
2547
self.assertLength(0, calls)
2549
self.assertLength(1, calls)
2550
self.assertEquals((store,), calls[0])
2552
def test_save_hook(self):
2556
config.ConfigHooks.install_named_hook('save', hook, None)
2557
self.assertLength(0, calls)
2558
store = self.get_store(self)
2559
section = store.get_mutable_section('baz')
2560
section.set('foo', 'bar')
2562
self.assertLength(1, calls)
2563
self.assertEquals((store,), calls[0])
2566
class TestIniFileStore(TestStore):
2568
def test_loading_unknown_file_fails(self):
2569
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2570
self.assertRaises(errors.NoSuchFile, store.load)
2572
def test_invalid_content(self):
2573
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2574
self.assertEquals(False, store.is_loaded())
2575
exc = self.assertRaises(
2576
errors.ParseConfigError, store._load_from_string,
2577
'this is invalid !')
2578
self.assertEndsWith(exc.filename, 'foo.conf')
2579
# And the load failed
2580
self.assertEquals(False, store.is_loaded())
2582
def test_get_embedded_sections(self):
2583
# A more complicated example (which also shows that section names and
2584
# option names share the same name space...)
2585
# FIXME: This should be fixed by forbidding dicts as values ?
2586
# -- vila 2011-04-05
2587
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2588
store._load_from_string('''
2592
foo_in_DEFAULT=foo_DEFAULT
2600
sections = list(store.get_sections())
2601
self.assertLength(4, sections)
2602
# The default section has no name.
2603
# List values are provided as lists
2604
self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2606
self.assertSectionContent(
2607
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2608
self.assertSectionContent(
2609
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2610
# sub sections are provided as embedded dicts.
2611
self.assertSectionContent(
2612
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2616
class TestLockableIniFileStore(TestStore):
2618
def test_create_store_in_created_dir(self):
2619
self.assertPathDoesNotExist('dir')
2620
t = self.get_transport('dir/subdir')
2621
store = config.LockableIniFileStore(t, 'foo.conf')
2622
store.get_mutable_section(None).set('foo', 'bar')
2624
self.assertPathExists('dir/subdir')
2627
class TestConcurrentStoreUpdates(TestStore):
2628
"""Test that Stores properly handle conccurent updates.
2630
New Store implementation may fail some of these tests but until such
2631
implementations exist it's hard to properly filter them from the scenarios
2632
applied here. If you encounter such a case, contact the bzr devs.
2635
scenarios = [(key, {'get_stack': builder}) for key, builder
2636
in config.test_stack_builder_registry.iteritems()]
2639
super(TestConcurrentStoreUpdates, self).setUp()
2640
self._content = 'one=1\ntwo=2\n'
2641
self.stack = self.get_stack(self)
2642
if not isinstance(self.stack, config._CompatibleStack):
2643
raise tests.TestNotApplicable(
2644
'%s is not meant to be compatible with the old config design'
2646
self.stack.store._load_from_string(self._content)
2648
self.stack.store.save()
2650
def test_simple_read_access(self):
2651
self.assertEquals('1', self.stack.get('one'))
2653
def test_simple_write_access(self):
2654
self.stack.set('one', 'one')
2655
self.assertEquals('one', self.stack.get('one'))
2657
def test_listen_to_the_last_speaker(self):
2659
c2 = self.get_stack(self)
2660
c1.set('one', 'ONE')
2661
c2.set('two', 'TWO')
2662
self.assertEquals('ONE', c1.get('one'))
2663
self.assertEquals('TWO', c2.get('two'))
2664
# The second update respect the first one
2665
self.assertEquals('ONE', c2.get('one'))
2667
def test_last_speaker_wins(self):
2668
# If the same config is not shared, the same variable modified twice
2669
# can only see a single result.
2671
c2 = self.get_stack(self)
2674
self.assertEquals('c2', c2.get('one'))
2675
# The first modification is still available until another refresh
2677
self.assertEquals('c1', c1.get('one'))
2678
c1.set('two', 'done')
2679
self.assertEquals('c2', c1.get('one'))
2681
def test_writes_are_serialized(self):
2683
c2 = self.get_stack(self)
2685
# We spawn a thread that will pause *during* the config saving.
2686
before_writing = threading.Event()
2687
after_writing = threading.Event()
2688
writing_done = threading.Event()
2689
c1_save_without_locking_orig = c1.store.save_without_locking
2690
def c1_save_without_locking():
2691
before_writing.set()
2692
c1_save_without_locking_orig()
2693
# The lock is held. We wait for the main thread to decide when to
2695
after_writing.wait()
2696
c1.store.save_without_locking = c1_save_without_locking
2700
t1 = threading.Thread(target=c1_set)
2701
# Collect the thread after the test
2702
self.addCleanup(t1.join)
2703
# Be ready to unblock the thread if the test goes wrong
2704
self.addCleanup(after_writing.set)
2706
before_writing.wait()
2707
self.assertRaises(errors.LockContention,
2708
c2.set, 'one', 'c2')
2709
self.assertEquals('c1', c1.get('one'))
2710
# Let the lock be released
2714
self.assertEquals('c2', c2.get('one'))
2716
def test_read_while_writing(self):
2718
# We spawn a thread that will pause *during* the write
2719
ready_to_write = threading.Event()
2720
do_writing = threading.Event()
2721
writing_done = threading.Event()
2722
# We override the _save implementation so we know the store is locked
2723
c1_save_without_locking_orig = c1.store.save_without_locking
2724
def c1_save_without_locking():
2725
ready_to_write.set()
2726
# The lock is held. We wait for the main thread to decide when to
2729
c1_save_without_locking_orig()
2731
c1.store.save_without_locking = c1_save_without_locking
2734
t1 = threading.Thread(target=c1_set)
2735
# Collect the thread after the test
2736
self.addCleanup(t1.join)
2737
# Be ready to unblock the thread if the test goes wrong
2738
self.addCleanup(do_writing.set)
2740
# Ensure the thread is ready to write
2741
ready_to_write.wait()
2742
self.assertEquals('c1', c1.get('one'))
2743
# If we read during the write, we get the old value
2744
c2 = self.get_stack(self)
2745
self.assertEquals('1', c2.get('one'))
2746
# Let the writing occur and ensure it occurred
2749
# Now we get the updated value
2750
c3 = self.get_stack(self)
2751
self.assertEquals('c1', c3.get('one'))
2753
# FIXME: It may be worth looking into removing the lock dir when it's not
2754
# needed anymore and look at possible fallouts for concurrent lockers. This
2755
# will matter if/when we use config files outside of bazaar directories
2756
# (.bazaar or .bzr) -- vila 20110-04-11
2759
class TestSectionMatcher(TestStore):
2761
scenarios = [('location', {'matcher': config.LocationMatcher})]
2763
def get_store(self, file_name):
2764
return config.IniFileStore(self.get_readonly_transport(), file_name)
2766
def test_no_matches_for_empty_stores(self):
2767
store = self.get_store('foo.conf')
2768
store._load_from_string('')
2769
matcher = self.matcher(store, '/bar')
2770
self.assertEquals([], list(matcher.get_sections()))
2772
def test_build_doesnt_load_store(self):
2773
store = self.get_store('foo.conf')
2774
matcher = self.matcher(store, '/bar')
2775
self.assertFalse(store.is_loaded())
2778
class TestLocationSection(tests.TestCase):
2780
def get_section(self, options, extra_path):
2781
section = config.Section('foo', options)
2782
# We don't care about the length so we use '0'
2783
return config.LocationSection(section, 0, extra_path)
2785
def test_simple_option(self):
2786
section = self.get_section({'foo': 'bar'}, '')
2787
self.assertEquals('bar', section.get('foo'))
2789
def test_option_with_extra_path(self):
2790
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
2792
self.assertEquals('bar/baz', section.get('foo'))
2794
def test_invalid_policy(self):
2795
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
2797
# invalid policies are ignored
2798
self.assertEquals('bar', section.get('foo'))
2801
class TestLocationMatcher(TestStore):
2803
def get_store(self, file_name):
2804
return config.IniFileStore(self.get_readonly_transport(), file_name)
2806
def test_unrelated_section_excluded(self):
2807
store = self.get_store('foo.conf')
2808
store._load_from_string('''
2816
section=/foo/bar/baz
2820
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
2822
[section.id for section in store.get_sections()])
2823
matcher = config.LocationMatcher(store, '/foo/bar/quux')
2824
sections = list(matcher.get_sections())
2825
self.assertEquals([3, 2],
2826
[section.length for section in sections])
2827
self.assertEquals(['/foo/bar', '/foo'],
2828
[section.id for section in sections])
2829
self.assertEquals(['quux', 'bar/quux'],
2830
[section.extra_path for section in sections])
2832
def test_more_specific_sections_first(self):
2833
store = self.get_store('foo.conf')
2834
store._load_from_string('''
2840
self.assertEquals(['/foo', '/foo/bar'],
2841
[section.id for section in store.get_sections()])
2842
matcher = config.LocationMatcher(store, '/foo/bar/baz')
2843
sections = list(matcher.get_sections())
2844
self.assertEquals([3, 2],
2845
[section.length for section in sections])
2846
self.assertEquals(['/foo/bar', '/foo'],
2847
[section.id for section in sections])
2848
self.assertEquals(['baz', 'bar/baz'],
2849
[section.extra_path for section in sections])
2851
def test_appendpath_in_no_name_section(self):
2852
# It's a bit weird to allow appendpath in a no-name section, but
2853
# someone may found a use for it
2854
store = self.get_store('foo.conf')
2855
store._load_from_string('''
2857
foo:policy = appendpath
2859
matcher = config.LocationMatcher(store, 'dir/subdir')
2860
sections = list(matcher.get_sections())
2861
self.assertLength(1, sections)
2862
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
2864
def test_file_urls_are_normalized(self):
2865
store = self.get_store('foo.conf')
2866
if sys.platform == 'win32':
2867
expected_url = 'file:///C:/dir/subdir'
2868
expected_location = 'C:/dir/subdir'
2870
expected_url = 'file:///dir/subdir'
2871
expected_location = '/dir/subdir'
2872
matcher = config.LocationMatcher(store, expected_url)
2873
self.assertEquals(expected_location, matcher.location)
2876
class TestStackGet(tests.TestCase):
2878
# FIXME: This should be parametrized for all known Stack or dedicated
2879
# paramerized tests created to avoid bloating -- vila 2011-03-31
2881
def test_single_config_get(self):
2882
conf = dict(foo='bar')
2883
conf_stack = config.Stack([conf])
2884
self.assertEquals('bar', conf_stack.get('foo'))
2886
def test_get_with_registered_default_value(self):
2887
conf_stack = config.Stack([dict()])
2888
opt = config.Option('foo', default='bar')
2889
self.overrideAttr(config, 'option_registry', registry.Registry())
2890
config.option_registry.register('foo', opt)
2891
self.assertEquals('bar', conf_stack.get('foo'))
2893
def test_get_without_registered_default_value(self):
2894
conf_stack = config.Stack([dict()])
2895
opt = config.Option('foo')
2896
self.overrideAttr(config, 'option_registry', registry.Registry())
2897
config.option_registry.register('foo', opt)
2898
self.assertEquals(None, conf_stack.get('foo'))
2900
def test_get_without_default_value_for_not_registered(self):
2901
conf_stack = config.Stack([dict()])
2902
opt = config.Option('foo')
2903
self.overrideAttr(config, 'option_registry', registry.Registry())
2904
self.assertEquals(None, conf_stack.get('foo'))
2906
def test_get_first_definition(self):
2907
conf1 = dict(foo='bar')
2908
conf2 = dict(foo='baz')
2909
conf_stack = config.Stack([conf1, conf2])
2910
self.assertEquals('bar', conf_stack.get('foo'))
2912
def test_get_embedded_definition(self):
2913
conf1 = dict(yy='12')
2914
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2915
conf_stack = config.Stack([conf1, conf2])
2916
self.assertEquals('baz', conf_stack.get('foo'))
2918
def test_get_for_empty_section_callable(self):
2919
conf_stack = config.Stack([lambda : []])
2920
self.assertEquals(None, conf_stack.get('foo'))
2922
def test_get_for_broken_callable(self):
2923
# Trying to use and invalid callable raises an exception on first use
2924
conf_stack = config.Stack([lambda : object()])
2925
self.assertRaises(TypeError, conf_stack.get, 'foo')
2928
class TestStackWithTransport(tests.TestCaseWithTransport):
2930
scenarios = [(key, {'get_stack': builder}) for key, builder
2931
in config.test_stack_builder_registry.iteritems()]
2934
class TestConcreteStacks(TestStackWithTransport):
2936
def test_build_stack(self):
2937
# Just a smoke test to help debug builders
2938
stack = self.get_stack(self)
2941
class TestStackGet(TestStackWithTransport):
2944
super(TestStackGet, self).setUp()
2945
self.conf = self.get_stack(self)
2947
def test_get_for_empty_stack(self):
2948
self.assertEquals(None, self.conf.get('foo'))
2950
def test_get_hook(self):
2951
self.conf.store._load_from_string('foo=bar')
2955
config.ConfigHooks.install_named_hook('get', hook, None)
2956
self.assertLength(0, calls)
2957
value = self.conf.get('foo')
2958
self.assertEquals('bar', value)
2959
self.assertLength(1, calls)
2960
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
2963
class TestStackGetWithConverter(TestStackGet):
2966
super(TestStackGetWithConverter, self).setUp()
2967
self.overrideAttr(config, 'option_registry', registry.Registry())
2968
self.registry = config.option_registry
2970
def register_bool_option(self, name, default):
2971
b = config.Option(name, default=default,
2972
from_unicode=config.bool_from_store)
2973
self.registry.register(b.name, b, help='A boolean.')
2975
def test_get_with_bool_not_defined_default_true(self):
2976
self.register_bool_option('foo', True)
2977
self.assertEquals(True, self.conf.get('foo'))
2979
def test_get_with_bool_not_defined_default_false(self):
2980
self.register_bool_option('foo', False)
2981
self.assertEquals(False, self.conf.get('foo'))
2983
def test_get_with_bool_converter_not_default(self):
2984
self.register_bool_option('foo', False)
2985
self.conf.store._load_from_string('foo=yes')
2986
self.assertEquals(True, self.conf.get('foo'))
2988
def test_get_with_bool_converter_invalid_string(self):
2989
self.register_bool_option('foo', False)
2990
self.conf.store._load_from_string('foo=not-a-boolean')
2991
self.assertEquals(False, self.conf.get('foo'))
2993
def test_get_with_bool_converter_invalid_list(self):
2994
self.register_bool_option('foo', False)
2995
self.conf.store._load_from_string('foo=not,a,boolean')
2996
self.assertEquals(False, self.conf.get('foo'))
2998
class TestStackSet(TestStackWithTransport):
3000
def test_simple_set(self):
3001
conf = self.get_stack(self)
3002
conf.store._load_from_string('foo=bar')
3003
self.assertEquals('bar', conf.get('foo'))
3004
conf.set('foo', 'baz')
3005
# Did we get it back ?
3006
self.assertEquals('baz', conf.get('foo'))
3008
def test_set_creates_a_new_section(self):
3009
conf = self.get_stack(self)
3010
conf.set('foo', 'baz')
3011
self.assertEquals, 'baz', conf.get('foo')
3013
def test_set_hook(self):
3017
config.ConfigHooks.install_named_hook('set', hook, None)
3018
self.assertLength(0, calls)
3019
conf = self.get_stack(self)
3020
conf.set('foo', 'bar')
3021
self.assertLength(1, calls)
3022
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3025
class TestStackRemove(TestStackWithTransport):
3027
def test_remove_existing(self):
3028
conf = self.get_stack(self)
3029
conf.store._load_from_string('foo=bar')
3030
self.assertEquals('bar', conf.get('foo'))
3032
# Did we get it back ?
3033
self.assertEquals(None, conf.get('foo'))
3035
def test_remove_unknown(self):
3036
conf = self.get_stack(self)
3037
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3039
def test_remove_hook(self):
3043
config.ConfigHooks.install_named_hook('remove', hook, None)
3044
self.assertLength(0, calls)
3045
conf = self.get_stack(self)
3046
conf.store._load_from_string('foo=bar')
3048
self.assertLength(1, calls)
3049
self.assertEquals((conf, 'foo'), calls[0])
3052
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3055
super(TestConfigGetOptions, self).setUp()
3056
create_configs(self)
3058
def test_no_variable(self):
3059
# Using branch should query branch, locations and bazaar
3060
self.assertOptions([], self.branch_config)
3062
def test_option_in_bazaar(self):
3063
self.bazaar_config.set_user_option('file', 'bazaar')
3064
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3067
def test_option_in_locations(self):
3068
self.locations_config.set_user_option('file', 'locations')
3070
[('file', 'locations', self.tree.basedir, 'locations')],
3071
self.locations_config)
3073
def test_option_in_branch(self):
3074
self.branch_config.set_user_option('file', 'branch')
3075
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3078
def test_option_in_bazaar_and_branch(self):
3079
self.bazaar_config.set_user_option('file', 'bazaar')
3080
self.branch_config.set_user_option('file', 'branch')
3081
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3082
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3085
def test_option_in_branch_and_locations(self):
3086
# Hmm, locations override branch :-/
3087
self.locations_config.set_user_option('file', 'locations')
3088
self.branch_config.set_user_option('file', 'branch')
3090
[('file', 'locations', self.tree.basedir, 'locations'),
3091
('file', 'branch', 'DEFAULT', 'branch'),],
3094
def test_option_in_bazaar_locations_and_branch(self):
3095
self.bazaar_config.set_user_option('file', 'bazaar')
3096
self.locations_config.set_user_option('file', 'locations')
3097
self.branch_config.set_user_option('file', 'branch')
3099
[('file', 'locations', self.tree.basedir, 'locations'),
3100
('file', 'branch', 'DEFAULT', 'branch'),
3101
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3105
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3108
super(TestConfigRemoveOption, self).setUp()
3109
create_configs_with_file_option(self)
3111
def test_remove_in_locations(self):
3112
self.locations_config.remove_user_option('file', self.tree.basedir)
3114
[('file', 'branch', 'DEFAULT', 'branch'),
3115
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3118
def test_remove_in_branch(self):
3119
self.branch_config.remove_user_option('file')
3121
[('file', 'locations', self.tree.basedir, 'locations'),
3122
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3125
def test_remove_in_bazaar(self):
3126
self.bazaar_config.remove_user_option('file')
3128
[('file', 'locations', self.tree.basedir, 'locations'),
3129
('file', 'branch', 'DEFAULT', 'branch'),],
3133
class TestConfigGetSections(tests.TestCaseWithTransport):
3136
super(TestConfigGetSections, self).setUp()
3137
create_configs(self)
3139
def assertSectionNames(self, expected, conf, name=None):
3140
"""Check which sections are returned for a given config.
3142
If fallback configurations exist their sections can be included.
3144
:param expected: A list of section names.
3146
:param conf: The configuration that will be queried.
3148
:param name: An optional section name that will be passed to
3151
sections = list(conf._get_sections(name))
3152
self.assertLength(len(expected), sections)
3153
self.assertEqual(expected, [name for name, _, _ in sections])
3155
def test_bazaar_default_section(self):
3156
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3158
def test_locations_default_section(self):
3159
# No sections are defined in an empty file
3160
self.assertSectionNames([], self.locations_config)
3162
def test_locations_named_section(self):
3163
self.locations_config.set_user_option('file', 'locations')
3164
self.assertSectionNames([self.tree.basedir], self.locations_config)
3166
def test_locations_matching_sections(self):
3167
loc_config = self.locations_config
3168
loc_config.set_user_option('file', 'locations')
3169
# We need to cheat a bit here to create an option in sections above and
3170
# below the 'location' one.
3171
parser = loc_config._get_parser()
3172
# locations.cong deals with '/' ignoring native os.sep
3173
location_names = self.tree.basedir.split('/')
3174
parent = '/'.join(location_names[:-1])
3175
child = '/'.join(location_names + ['child'])
3177
parser[parent]['file'] = 'parent'
3179
parser[child]['file'] = 'child'
3180
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3182
def test_branch_data_default_section(self):
3183
self.assertSectionNames([None],
3184
self.branch_config._get_branch_data_config())
3186
def test_branch_default_sections(self):
3187
# No sections are defined in an empty locations file
3188
self.assertSectionNames([None, 'DEFAULT'],
3190
# Unless we define an option
3191
self.branch_config._get_location_config().set_user_option(
3192
'file', 'locations')
3193
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3196
def test_bazaar_named_section(self):
3197
# We need to cheat as the API doesn't give direct access to sections
3198
# other than DEFAULT.
3199
self.bazaar_config.set_alias('bazaar', 'bzr')
3200
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
3203
class TestAuthenticationConfigFile(tests.TestCase):
1316
3204
"""Test the authentication.conf file matching"""