593
353
'/home/bogus/.cache')
596
class TestXDGConfigDir(tests.TestCaseInTempDir):
597
# must be in temp dir because config tests for the existence of the bazaar
598
# subdirectory of $XDG_CONFIG_HOME
601
if sys.platform in ('darwin', 'win32'):
602
raise tests.TestNotApplicable(
603
'XDG config dir not used on this platform')
604
super(TestXDGConfigDir, self).setUp()
605
self.overrideEnv('HOME', self.test_home_dir)
606
# BZR_HOME overrides everything we want to test so unset it.
607
self.overrideEnv('BZR_HOME', None)
609
def test_xdg_config_dir_exists(self):
610
"""When ~/.config/bazaar exists, use it as the config dir."""
611
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
613
self.assertEqual(config.config_dir(), newdir)
615
def test_xdg_config_home(self):
616
"""When XDG_CONFIG_HOME is set, use it."""
617
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
618
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
619
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
621
self.assertEqual(config.config_dir(), newdir)
624
class TestIniConfig(tests.TestCaseInTempDir):
626
def make_config_parser(self, s):
627
conf = config.IniBasedConfig.from_string(s)
628
return conf, conf._get_parser()
631
class TestIniConfigBuilding(TestIniConfig):
356
class TestIniConfig(tests.TestCase):
633
358
def test_contructs(self):
634
my_config = config.IniBasedConfig()
359
my_config = config.IniBasedConfig("nothing")
636
361
def test_from_fp(self):
637
my_config = config.IniBasedConfig.from_string(sample_config_text)
638
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
362
config_file = StringIO(sample_config_text.encode('utf-8'))
363
my_config = config.IniBasedConfig(None)
365
isinstance(my_config._get_parser(file=config_file),
366
configobj.ConfigObj))
640
368
def test_cached(self):
641
my_config = config.IniBasedConfig.from_string(sample_config_text)
642
parser = my_config._get_parser()
643
self.assertTrue(my_config._get_parser() is parser)
645
def _dummy_chown(self, path, uid, gid):
646
self.path, self.uid, self.gid = path, uid, gid
648
def test_ini_config_ownership(self):
649
"""Ensure that chown is happening during _write_config_file"""
650
self.requireFeature(features.chown_feature)
651
self.overrideAttr(os, 'chown', self._dummy_chown)
652
self.path = self.uid = self.gid = None
653
conf = config.IniBasedConfig(file_name='./foo.conf')
654
conf._write_config_file()
655
self.assertEquals(self.path, './foo.conf')
656
self.assertTrue(isinstance(self.uid, int))
657
self.assertTrue(isinstance(self.gid, int))
659
def test_get_filename_parameter_is_deprecated_(self):
660
conf = self.callDeprecated([
661
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
662
' Use file_name instead.'],
663
config.IniBasedConfig, lambda: 'ini.conf')
664
self.assertEqual('ini.conf', conf.file_name)
666
def test_get_parser_file_parameter_is_deprecated_(self):
667
369
config_file = StringIO(sample_config_text.encode('utf-8'))
668
conf = config.IniBasedConfig.from_string(sample_config_text)
669
conf = self.callDeprecated([
670
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
671
' Use IniBasedConfig(_content=xxx) instead.'],
672
conf._get_parser, file=config_file)
675
class TestIniConfigSaving(tests.TestCaseInTempDir):
677
def test_cant_save_without_a_file_name(self):
678
conf = config.IniBasedConfig()
679
self.assertRaises(AssertionError, conf._write_config_file)
681
def test_saved_with_content(self):
682
content = 'foo = bar\n'
683
conf = config.IniBasedConfig.from_string(
684
content, file_name='./test.conf', save=True)
685
self.assertFileEqual(content, 'test.conf')
688
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
689
"""What is the default value of expand for config options.
691
This is an opt-in beta feature used to evaluate whether or not option
692
references can appear in dangerous place raising exceptions, disapearing
693
(and as such corrupting data) or if it's safe to activate the option by
696
Note that these tests relies on config._expand_default_value being already
697
overwritten in the parent class setUp.
701
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
705
self.warnings.append(args[0] % args[1:])
706
self.overrideAttr(trace, 'warning', warning)
708
def get_config(self, expand):
709
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
713
def assertExpandIs(self, expected):
714
actual = config._get_expand_default_value()
715
#self.config.get_user_option_as_bool('bzr.config.expand')
716
self.assertEquals(expected, actual)
718
def test_default_is_None(self):
719
self.assertEquals(None, config._expand_default_value)
721
def test_default_is_False_even_if_None(self):
722
self.config = self.get_config(None)
723
self.assertExpandIs(False)
725
def test_default_is_False_even_if_invalid(self):
726
self.config = self.get_config('<your choice>')
727
self.assertExpandIs(False)
729
# Huh ? My choice is False ? Thanks, always happy to hear that :D
730
# Wait, you've been warned !
731
self.assertLength(1, self.warnings)
733
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
736
def test_default_is_True(self):
737
self.config = self.get_config(True)
738
self.assertExpandIs(True)
740
def test_default_is_False(self):
741
self.config = self.get_config(False)
742
self.assertExpandIs(False)
745
class TestIniConfigOptionExpansion(tests.TestCase):
746
"""Test option expansion from the IniConfig level.
748
What we really want here is to test the Config level, but the class being
749
abstract as far as storing values is concerned, this can't be done
752
# FIXME: This should be rewritten when all configs share a storage
753
# implementation -- vila 2011-02-18
755
def get_config(self, string=None):
758
c = config.IniBasedConfig.from_string(string)
761
def assertExpansion(self, expected, conf, string, env=None):
762
self.assertEquals(expected, conf.expand_options(string, env))
764
def test_no_expansion(self):
765
c = self.get_config('')
766
self.assertExpansion('foo', c, 'foo')
768
def test_env_adding_options(self):
769
c = self.get_config('')
770
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
772
def test_env_overriding_options(self):
773
c = self.get_config('foo=baz')
774
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
776
def test_simple_ref(self):
777
c = self.get_config('foo=xxx')
778
self.assertExpansion('xxx', c, '{foo}')
780
def test_unknown_ref(self):
781
c = self.get_config('')
782
self.assertRaises(errors.ExpandingUnknownOption,
783
c.expand_options, '{foo}')
785
def test_indirect_ref(self):
786
c = self.get_config('''
790
self.assertExpansion('xxx', c, '{bar}')
792
def test_embedded_ref(self):
793
c = self.get_config('''
797
self.assertExpansion('xxx', c, '{{bar}}')
799
def test_simple_loop(self):
800
c = self.get_config('foo={foo}')
801
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
803
def test_indirect_loop(self):
804
c = self.get_config('''
808
e = self.assertRaises(errors.OptionExpansionLoop,
809
c.expand_options, '{foo}')
810
self.assertEquals('foo->bar->baz', e.refs)
811
self.assertEquals('{foo}', e.string)
814
conf = self.get_config('''
818
list={foo},{bar},{baz}
820
self.assertEquals(['start', 'middle', 'end'],
821
conf.get_user_option('list', expand=True))
823
def test_cascading_list(self):
824
conf = self.get_config('''
830
self.assertEquals(['start', 'middle', 'end'],
831
conf.get_user_option('list', expand=True))
833
def test_pathological_hidden_list(self):
834
conf = self.get_config('''
840
hidden={start}{middle}{end}
842
# Nope, it's either a string or a list, and the list wins as soon as a
843
# ',' appears, so the string concatenation never occur.
844
self.assertEquals(['{foo', '}', '{', 'bar}'],
845
conf.get_user_option('hidden', expand=True))
848
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
850
def get_config(self, location, string=None):
853
# Since we don't save the config we won't strictly require to inherit
854
# from TestCaseInTempDir, but an error occurs so quickly...
855
c = config.LocationConfig.from_string(string, location)
858
def test_dont_cross_unrelated_section(self):
859
c = self.get_config('/another/branch/path','''
864
[/another/branch/path]
867
self.assertRaises(errors.ExpandingUnknownOption,
868
c.get_user_option, 'bar', expand=True)
870
def test_cross_related_sections(self):
871
c = self.get_config('/project/branch/path','''
875
[/project/branch/path]
878
self.assertEquals('quux', c.get_user_option('bar', expand=True))
881
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
883
def test_cannot_reload_without_name(self):
884
conf = config.IniBasedConfig.from_string(sample_config_text)
885
self.assertRaises(AssertionError, conf.reload)
887
def test_reload_see_new_value(self):
888
c1 = config.IniBasedConfig.from_string('editor=vim\n',
889
file_name='./test/conf')
890
c1._write_config_file()
891
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
892
file_name='./test/conf')
893
c2._write_config_file()
894
self.assertEqual('vim', c1.get_user_option('editor'))
895
self.assertEqual('emacs', c2.get_user_option('editor'))
896
# Make sure we get the Right value
898
self.assertEqual('emacs', c1.get_user_option('editor'))
901
class TestLockableConfig(tests.TestCaseInTempDir):
903
scenarios = lockable_config_scenarios()
908
config_section = None
911
super(TestLockableConfig, self).setUp()
912
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
913
self.config = self.create_config(self._content)
915
def get_existing_config(self):
916
return self.config_class(*self.config_args)
918
def create_config(self, content):
919
kwargs = dict(save=True)
920
c = self.config_class.from_string(content, *self.config_args, **kwargs)
923
def test_simple_read_access(self):
924
self.assertEquals('1', self.config.get_user_option('one'))
926
def test_simple_write_access(self):
927
self.config.set_user_option('one', 'one')
928
self.assertEquals('one', self.config.get_user_option('one'))
930
def test_listen_to_the_last_speaker(self):
932
c2 = self.get_existing_config()
933
c1.set_user_option('one', 'ONE')
934
c2.set_user_option('two', 'TWO')
935
self.assertEquals('ONE', c1.get_user_option('one'))
936
self.assertEquals('TWO', c2.get_user_option('two'))
937
# The second update respect the first one
938
self.assertEquals('ONE', c2.get_user_option('one'))
940
def test_last_speaker_wins(self):
941
# If the same config is not shared, the same variable modified twice
942
# can only see a single result.
944
c2 = self.get_existing_config()
945
c1.set_user_option('one', 'c1')
946
c2.set_user_option('one', 'c2')
947
self.assertEquals('c2', c2._get_user_option('one'))
948
# The first modification is still available until another refresh
950
self.assertEquals('c1', c1._get_user_option('one'))
951
c1.set_user_option('two', 'done')
952
self.assertEquals('c2', c1._get_user_option('one'))
954
def test_writes_are_serialized(self):
956
c2 = self.get_existing_config()
958
# We spawn a thread that will pause *during* the write
959
before_writing = threading.Event()
960
after_writing = threading.Event()
961
writing_done = threading.Event()
962
c1_orig = c1._write_config_file
963
def c1_write_config_file():
966
# The lock is held. We wait for the main thread to decide when to
969
c1._write_config_file = c1_write_config_file
971
c1.set_user_option('one', 'c1')
973
t1 = threading.Thread(target=c1_set_option)
974
# Collect the thread after the test
975
self.addCleanup(t1.join)
976
# Be ready to unblock the thread if the test goes wrong
977
self.addCleanup(after_writing.set)
979
before_writing.wait()
980
self.assertTrue(c1._lock.is_held)
981
self.assertRaises(errors.LockContention,
982
c2.set_user_option, 'one', 'c2')
983
self.assertEquals('c1', c1.get_user_option('one'))
984
# Let the lock be released
987
c2.set_user_option('one', 'c2')
988
self.assertEquals('c2', c2.get_user_option('one'))
990
def test_read_while_writing(self):
992
# We spawn a thread that will pause *during* the write
993
ready_to_write = threading.Event()
994
do_writing = threading.Event()
995
writing_done = threading.Event()
996
c1_orig = c1._write_config_file
997
def c1_write_config_file():
999
# The lock is held. We wait for the main thread to decide when to
1004
c1._write_config_file = c1_write_config_file
1005
def c1_set_option():
1006
c1.set_user_option('one', 'c1')
1007
t1 = threading.Thread(target=c1_set_option)
1008
# Collect the thread after the test
1009
self.addCleanup(t1.join)
1010
# Be ready to unblock the thread if the test goes wrong
1011
self.addCleanup(do_writing.set)
1013
# Ensure the thread is ready to write
1014
ready_to_write.wait()
1015
self.assertTrue(c1._lock.is_held)
1016
self.assertEquals('c1', c1.get_user_option('one'))
1017
# If we read during the write, we get the old value
1018
c2 = self.get_existing_config()
1019
self.assertEquals('1', c2.get_user_option('one'))
1020
# Let the writing occur and ensure it occurred
1023
# Now we get the updated value
1024
c3 = self.get_existing_config()
1025
self.assertEquals('c1', c3.get_user_option('one'))
1028
class TestGetUserOptionAs(TestIniConfig):
370
my_config = config.IniBasedConfig(None)
371
parser = my_config._get_parser(file=config_file)
372
self.failUnless(my_config._get_parser() is parser)
1030
374
def test_get_user_option_as_bool(self):
1031
conf, parser = self.make_config_parser("""
375
config_file = StringIO("""
1032
376
a_true_bool = true
1033
377
a_false_bool = 0
1034
378
an_invalid_bool = maybe
1035
a_list = hmm, who knows ? # This is interpreted as a list !
1037
get_bool = conf.get_user_option_as_bool
1038
self.assertEqual(True, get_bool('a_true_bool'))
1039
self.assertEqual(False, get_bool('a_false_bool'))
1042
warnings.append(args[0] % args[1:])
1043
self.overrideAttr(trace, 'warning', warning)
1044
msg = 'Value "%s" is not a boolean for "%s"'
1045
self.assertIs(None, get_bool('an_invalid_bool'))
1046
self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
1048
self.assertIs(None, get_bool('not_defined_in_this_config'))
1049
self.assertEquals([], warnings)
1051
def test_get_user_option_as_list(self):
1052
conf, parser = self.make_config_parser("""
1057
get_list = conf.get_user_option_as_list
1058
self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
1059
self.assertEqual(['1'], get_list('length_1'))
1060
self.assertEqual('x', conf.get_user_option('one_item'))
1061
# automatically cast to list
1062
self.assertEqual(['x'], get_list('one_item'))
1064
def test_get_user_option_as_int_from_SI(self):
1065
conf, parser = self.make_config_parser("""
1074
def get_si(s, default=None):
1075
return self.applyDeprecated(
1076
deprecated_in((2, 5, 0)),
1077
conf.get_user_option_as_int_from_SI, s, default)
1078
self.assertEqual(100, get_si('plain'))
1079
self.assertEqual(5000, get_si('si_k'))
1080
self.assertEqual(5000, get_si('si_kb'))
1081
self.assertEqual(5000000, get_si('si_m'))
1082
self.assertEqual(5000000, get_si('si_mb'))
1083
self.assertEqual(5000000000, get_si('si_g'))
1084
self.assertEqual(5000000000, get_si('si_gb'))
1085
self.assertEqual(None, get_si('non-exist'))
1086
self.assertEqual(42, get_si('non-exist-with-default', 42))
1089
class TestSupressWarning(TestIniConfig):
1091
def make_warnings_config(self, s):
1092
conf, parser = self.make_config_parser(s)
1093
return conf.suppress_warning
1095
def test_suppress_warning_unknown(self):
1096
suppress_warning = self.make_warnings_config('')
1097
self.assertEqual(False, suppress_warning('unknown_warning'))
1099
def test_suppress_warning_known(self):
1100
suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
1101
self.assertEqual(False, suppress_warning('c'))
1102
self.assertEqual(True, suppress_warning('a'))
1103
self.assertEqual(True, suppress_warning('b'))
379
a_list = hmm, who knows ? # This interpreted as a list !
381
my_config = config.IniBasedConfig(None)
382
parser = my_config._get_parser(file=config_file)
383
get_option = my_config.get_user_option_as_bool
384
self.assertEqual(True, get_option('a_true_bool'))
385
self.assertEqual(False, get_option('a_false_bool'))
386
self.assertIs(None, get_option('an_invalid_bool'))
387
self.assertIs(None, get_option('not_defined_in_this_config'))
1106
389
class TestGetConfig(tests.TestCase):
2082
1238
self.assertIs(None, bzrdir_config.get_default_stack_on())
2085
class TestOldConfigHooks(tests.TestCaseWithTransport):
2088
super(TestOldConfigHooks, self).setUp()
2089
create_configs_with_file_option(self)
2091
def assertGetHook(self, conf, name, value):
2095
config.OldConfigHooks.install_named_hook('get', hook, None)
2097
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2098
self.assertLength(0, calls)
2099
actual_value = conf.get_user_option(name)
2100
self.assertEquals(value, actual_value)
2101
self.assertLength(1, calls)
2102
self.assertEquals((conf, name, value), calls[0])
2104
def test_get_hook_bazaar(self):
2105
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2107
def test_get_hook_locations(self):
2108
self.assertGetHook(self.locations_config, 'file', 'locations')
2110
def test_get_hook_branch(self):
2111
# Since locations masks branch, we define a different option
2112
self.branch_config.set_user_option('file2', 'branch')
2113
self.assertGetHook(self.branch_config, 'file2', 'branch')
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_user_option(name, value)
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_bazaar(self):
2131
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2133
def test_set_hook_locations(self):
2134
self.assertSetHook(self.locations_config, 'foo', 'locations')
2136
def test_set_hook_branch(self):
2137
self.assertSetHook(self.branch_config, 'foo', 'branch')
2139
def assertRemoveHook(self, conf, name, section_name=None):
2143
config.OldConfigHooks.install_named_hook('remove', hook, None)
2145
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2146
self.assertLength(0, calls)
2147
conf.remove_user_option(name, section_name)
2148
self.assertLength(1, calls)
2149
# We can't assert the conf object below as different configs use
2150
# different means to implement remove_user_option and we care only about
2152
self.assertEquals((name,), calls[0][1:])
2154
def test_remove_hook_bazaar(self):
2155
self.assertRemoveHook(self.bazaar_config, 'file')
2157
def test_remove_hook_locations(self):
2158
self.assertRemoveHook(self.locations_config, 'file',
2159
self.locations_config.location)
2161
def test_remove_hook_branch(self):
2162
self.assertRemoveHook(self.branch_config, 'file')
2164
def assertLoadHook(self, name, conf_class, *conf_args):
2168
config.OldConfigHooks.install_named_hook('load', hook, None)
2170
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2171
self.assertLength(0, calls)
2173
conf = conf_class(*conf_args)
2174
# Access an option to trigger a load
2175
conf.get_user_option(name)
2176
self.assertLength(1, calls)
2177
# Since we can't assert about conf, we just use the number of calls ;-/
2179
def test_load_hook_bazaar(self):
2180
self.assertLoadHook('file', config.GlobalConfig)
2182
def test_load_hook_locations(self):
2183
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2185
def test_load_hook_branch(self):
2186
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2188
def assertSaveHook(self, conf):
2192
config.OldConfigHooks.install_named_hook('save', hook, None)
2194
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2195
self.assertLength(0, calls)
2196
# Setting an option triggers a save
2197
conf.set_user_option('foo', 'bar')
2198
self.assertLength(1, calls)
2199
# Since we can't assert about conf, we just use the number of calls ;-/
2201
def test_save_hook_bazaar(self):
2202
self.assertSaveHook(self.bazaar_config)
2204
def test_save_hook_locations(self):
2205
self.assertSaveHook(self.locations_config)
2207
def test_save_hook_branch(self):
2208
self.assertSaveHook(self.branch_config)
2211
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2212
"""Tests config hooks for remote configs.
2214
No tests for the remove hook as this is not implemented there.
2218
super(TestOldConfigHooksForRemote, self).setUp()
2219
self.transport_server = test_server.SmartTCPServer_for_testing
2220
create_configs_with_file_option(self)
2222
def assertGetHook(self, conf, name, value):
2226
config.OldConfigHooks.install_named_hook('get', hook, None)
2228
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2229
self.assertLength(0, calls)
2230
actual_value = conf.get_option(name)
2231
self.assertEquals(value, actual_value)
2232
self.assertLength(1, calls)
2233
self.assertEquals((conf, name, value), calls[0])
2235
def test_get_hook_remote_branch(self):
2236
remote_branch = branch.Branch.open(self.get_url('tree'))
2237
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2239
def test_get_hook_remote_bzrdir(self):
2240
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2241
conf = remote_bzrdir._get_config()
2242
conf.set_option('remotedir', 'file')
2243
self.assertGetHook(conf, 'file', 'remotedir')
2245
def assertSetHook(self, conf, name, value):
2249
config.OldConfigHooks.install_named_hook('set', hook, None)
2251
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2252
self.assertLength(0, calls)
2253
conf.set_option(value, name)
2254
self.assertLength(1, calls)
2255
# We can't assert the conf object below as different configs use
2256
# different means to implement set_user_option and we care only about
2258
self.assertEquals((name, value), calls[0][1:])
2260
def test_set_hook_remote_branch(self):
2261
remote_branch = branch.Branch.open(self.get_url('tree'))
2262
self.addCleanup(remote_branch.lock_write().unlock)
2263
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2265
def test_set_hook_remote_bzrdir(self):
2266
remote_branch = branch.Branch.open(self.get_url('tree'))
2267
self.addCleanup(remote_branch.lock_write().unlock)
2268
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2269
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2271
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2275
config.OldConfigHooks.install_named_hook('load', hook, None)
2277
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2278
self.assertLength(0, calls)
2280
conf = conf_class(*conf_args)
2281
# Access an option to trigger a load
2282
conf.get_option(name)
2283
self.assertLength(expected_nb_calls, calls)
2284
# Since we can't assert about conf, we just use the number of calls ;-/
2286
def test_load_hook_remote_branch(self):
2287
remote_branch = branch.Branch.open(self.get_url('tree'))
2288
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2290
def test_load_hook_remote_bzrdir(self):
2291
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2292
# The config file doesn't exist, set an option to force its creation
2293
conf = remote_bzrdir._get_config()
2294
conf.set_option('remotedir', 'file')
2295
# We get one call for the server and one call for the client, this is
2296
# caused by the differences in implementations betwen
2297
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2298
# SmartServerBranchGetConfigFile (in smart/branch.py)
2299
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2301
def assertSaveHook(self, conf):
2305
config.OldConfigHooks.install_named_hook('save', hook, None)
2307
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2308
self.assertLength(0, calls)
2309
# Setting an option triggers a save
2310
conf.set_option('foo', 'bar')
2311
self.assertLength(1, calls)
2312
# Since we can't assert about conf, we just use the number of calls ;-/
2314
def test_save_hook_remote_branch(self):
2315
remote_branch = branch.Branch.open(self.get_url('tree'))
2316
self.addCleanup(remote_branch.lock_write().unlock)
2317
self.assertSaveHook(remote_branch._get_config())
2319
def test_save_hook_remote_bzrdir(self):
2320
remote_branch = branch.Branch.open(self.get_url('tree'))
2321
self.addCleanup(remote_branch.lock_write().unlock)
2322
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2323
self.assertSaveHook(remote_bzrdir._get_config())
2326
class TestOption(tests.TestCase):
2328
def test_default_value(self):
2329
opt = config.Option('foo', default='bar')
2330
self.assertEquals('bar', opt.get_default())
2332
def test_callable_default_value(self):
2333
def bar_as_unicode():
2335
opt = config.Option('foo', default=bar_as_unicode)
2336
self.assertEquals('bar', opt.get_default())
2338
def test_default_value_from_env(self):
2339
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2340
self.overrideEnv('FOO', 'quux')
2341
# Env variable provides a default taking over the option one
2342
self.assertEquals('quux', opt.get_default())
2344
def test_first_default_value_from_env_wins(self):
2345
opt = config.Option('foo', default='bar',
2346
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2347
self.overrideEnv('FOO', 'foo')
2348
self.overrideEnv('BAZ', 'baz')
2349
# The first env var set wins
2350
self.assertEquals('foo', opt.get_default())
2352
def test_not_supported_list_default_value(self):
2353
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2355
def test_not_supported_object_default_value(self):
2356
self.assertRaises(AssertionError, config.Option, 'foo',
2359
def test_not_supported_callable_default_value_not_unicode(self):
2360
def bar_not_unicode():
2362
opt = config.Option('foo', default=bar_not_unicode)
2363
self.assertRaises(AssertionError, opt.get_default)
2366
class TestOptionConverterMixin(object):
2368
def assertConverted(self, expected, opt, value):
2369
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2371
def assertWarns(self, opt, value):
2374
warnings.append(args[0] % args[1:])
2375
self.overrideAttr(trace, 'warning', warning)
2376
self.assertEquals(None, opt.convert_from_unicode(None, value))
2377
self.assertLength(1, warnings)
2379
'Value "%s" is not valid for "%s"' % (value, opt.name),
2382
def assertErrors(self, opt, value):
2383
self.assertRaises(errors.ConfigOptionValueError,
2384
opt.convert_from_unicode, None, value)
2386
def assertConvertInvalid(self, opt, invalid_value):
2388
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2389
opt.invalid = 'warning'
2390
self.assertWarns(opt, invalid_value)
2391
opt.invalid = 'error'
2392
self.assertErrors(opt, invalid_value)
2395
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2397
def get_option(self):
2398
return config.Option('foo', help='A boolean.',
2399
from_unicode=config.bool_from_store)
2401
def test_convert_invalid(self):
2402
opt = self.get_option()
2403
# A string that is not recognized as a boolean
2404
self.assertConvertInvalid(opt, u'invalid-boolean')
2405
# A list of strings is never recognized as a boolean
2406
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2408
def test_convert_valid(self):
2409
opt = self.get_option()
2410
self.assertConverted(True, opt, u'True')
2411
self.assertConverted(True, opt, u'1')
2412
self.assertConverted(False, opt, u'False')
2415
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2417
def get_option(self):
2418
return config.Option('foo', help='An integer.',
2419
from_unicode=config.int_from_store)
2421
def test_convert_invalid(self):
2422
opt = self.get_option()
2423
# A string that is not recognized as an integer
2424
self.assertConvertInvalid(opt, u'forty-two')
2425
# A list of strings is never recognized as an integer
2426
self.assertConvertInvalid(opt, [u'a', u'list'])
2428
def test_convert_valid(self):
2429
opt = self.get_option()
2430
self.assertConverted(16, opt, u'16')
2433
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2435
def get_option(self):
2436
return config.Option('foo', help='An integer in SI units.',
2437
from_unicode=config.int_SI_from_store)
2439
def test_convert_invalid(self):
2440
opt = self.get_option()
2441
self.assertConvertInvalid(opt, u'not-a-unit')
2442
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2443
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2444
self.assertConvertInvalid(opt, u'1GG')
2445
self.assertConvertInvalid(opt, u'1Mbb')
2446
self.assertConvertInvalid(opt, u'1MM')
2448
def test_convert_valid(self):
2449
opt = self.get_option()
2450
self.assertConverted(int(5e3), opt, u'5kb')
2451
self.assertConverted(int(5e6), opt, u'5M')
2452
self.assertConverted(int(5e6), opt, u'5MB')
2453
self.assertConverted(int(5e9), opt, u'5g')
2454
self.assertConverted(int(5e9), opt, u'5gB')
2455
self.assertConverted(100, opt, u'100')
2458
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2460
def get_option(self):
2461
return config.ListOption('foo', help='A list.')
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()
2617
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2619
def get_section(self):
2620
"""Get the unique section for the command line overrides."""
2621
sections = list(self.store.get_sections())
2622
self.assertLength(1, sections)
2623
store, section = sections[0]
2624
self.assertEquals(self.store, store)
2627
def test_no_override(self):
2628
self.store._from_cmdline([])
2629
section = self.get_section()
2630
self.assertLength(0, list(section.iter_option_names()))
2632
def test_simple_override(self):
2633
self.store._from_cmdline(['a=b'])
2634
section = self.get_section()
2635
self.assertEqual('b', section.get('a'))
2637
def test_list_override(self):
2638
opt = config.ListOption('l')
2639
config.option_registry.register(opt)
2640
self.store._from_cmdline(['l=1,2,3'])
2641
val = self.get_section().get('l')
2642
self.assertEqual('1,2,3', val)
2643
# Reminder: lists should be registered as such explicitely, otherwise
2644
# the conversion needs to be done afterwards.
2645
self.assertEqual(['1', '2', '3'],
2646
opt.convert_from_unicode(self.store, val))
2648
def test_multiple_overrides(self):
2649
self.store._from_cmdline(['a=b', 'x=y'])
2650
section = self.get_section()
2651
self.assertEquals('b', section.get('a'))
2652
self.assertEquals('y', section.get('x'))
2654
def test_wrong_syntax(self):
2655
self.assertRaises(errors.BzrCommandError,
2656
self.store._from_cmdline, ['a=b', 'c'])
2659
class TestStore(tests.TestCaseWithTransport):
2661
def assertSectionContent(self, expected, (store, section)):
2662
"""Assert that some options have the proper values in a section."""
2663
expected_name, expected_options = expected
2664
self.assertEquals(expected_name, section.id)
2667
dict([(k, section.get(k)) for k in expected_options.keys()]))
2670
class TestReadonlyStore(TestStore):
2672
scenarios = [(key, {'get_store': builder}) for key, builder
2673
in config.test_store_builder_registry.iteritems()]
2675
def test_building_delays_load(self):
2676
store = self.get_store(self)
2677
self.assertEquals(False, store.is_loaded())
2678
store._load_from_string('')
2679
self.assertEquals(True, store.is_loaded())
2681
def test_get_no_sections_for_empty(self):
2682
store = self.get_store(self)
2683
store._load_from_string('')
2684
self.assertEquals([], list(store.get_sections()))
2686
def test_get_default_section(self):
2687
store = self.get_store(self)
2688
store._load_from_string('foo=bar')
2689
sections = list(store.get_sections())
2690
self.assertLength(1, sections)
2691
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2693
def test_get_named_section(self):
2694
store = self.get_store(self)
2695
store._load_from_string('[baz]\nfoo=bar')
2696
sections = list(store.get_sections())
2697
self.assertLength(1, sections)
2698
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2700
def test_load_from_string_fails_for_non_empty_store(self):
2701
store = self.get_store(self)
2702
store._load_from_string('foo=bar')
2703
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2706
class TestStoreQuoting(TestStore):
2708
scenarios = [(key, {'get_store': builder}) for key, builder
2709
in config.test_store_builder_registry.iteritems()]
2712
super(TestStoreQuoting, self).setUp()
2713
self.store = self.get_store(self)
2714
# We need a loaded store but any content will do
2715
self.store._load_from_string('')
2717
def assertIdempotent(self, s):
2718
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2720
What matters here is that option values, as they appear in a store, can
2721
be safely round-tripped out of the store and back.
2723
:param s: A string, quoted if required.
2725
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2726
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2728
def test_empty_string(self):
2729
if isinstance(self.store, config.IniFileStore):
2730
# configobj._quote doesn't handle empty values
2731
self.assertRaises(AssertionError,
2732
self.assertIdempotent, '')
2734
self.assertIdempotent('')
2735
# But quoted empty strings are ok
2736
self.assertIdempotent('""')
2738
def test_embedded_spaces(self):
2739
self.assertIdempotent('" a b c "')
2741
def test_embedded_commas(self):
2742
self.assertIdempotent('" a , b c "')
2744
def test_simple_comma(self):
2745
if isinstance(self.store, config.IniFileStore):
2746
# configobj requires that lists are special-cased
2747
self.assertRaises(AssertionError,
2748
self.assertIdempotent, ',')
2750
self.assertIdempotent(',')
2751
# When a single comma is required, quoting is also required
2752
self.assertIdempotent('","')
2754
def test_list(self):
2755
if isinstance(self.store, config.IniFileStore):
2756
# configobj requires that lists are special-cased
2757
self.assertRaises(AssertionError,
2758
self.assertIdempotent, 'a,b')
2760
self.assertIdempotent('a,b')
2763
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2764
"""Simulate loading a config store with content of various encodings.
2766
All files produced by bzr are in utf8 content.
2768
Users may modify them manually and end up with a file that can't be
2769
loaded. We need to issue proper error messages in this case.
2772
invalid_utf8_char = '\xff'
2774
def test_load_utf8(self):
2775
"""Ensure we can load an utf8-encoded file."""
2776
t = self.get_transport()
2777
# From http://pad.lv/799212
2778
unicode_user = u'b\N{Euro Sign}ar'
2779
unicode_content = u'user=%s' % (unicode_user,)
2780
utf8_content = unicode_content.encode('utf8')
2781
# Store the raw content in the config file
2782
t.put_bytes('foo.conf', utf8_content)
2783
store = config.TransportIniFileStore(t, 'foo.conf')
2785
stack = config.Stack([store.get_sections], store)
2786
self.assertEquals(unicode_user, stack.get('user'))
2788
def test_load_non_ascii(self):
2789
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2790
t = self.get_transport()
2791
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2792
store = config.TransportIniFileStore(t, 'foo.conf')
2793
self.assertRaises(errors.ConfigContentError, store.load)
2795
def test_load_erroneous_content(self):
2796
"""Ensure we display a proper error on content that can't be parsed."""
2797
t = self.get_transport()
2798
t.put_bytes('foo.conf', '[open_section\n')
2799
store = config.TransportIniFileStore(t, 'foo.conf')
2800
self.assertRaises(errors.ParseConfigError, store.load)
2802
def test_load_permission_denied(self):
2803
"""Ensure we get warned when trying to load an inaccessible file."""
2806
warnings.append(args[0] % args[1:])
2807
self.overrideAttr(trace, 'warning', warning)
2809
t = self.get_transport()
2811
def get_bytes(relpath):
2812
raise errors.PermissionDenied(relpath, "")
2813
t.get_bytes = get_bytes
2814
store = config.TransportIniFileStore(t, 'foo.conf')
2815
self.assertRaises(errors.PermissionDenied, store.load)
2818
[u'Permission denied while trying to load configuration store %s.'
2819
% store.external_url()])
2822
class TestIniConfigContent(tests.TestCaseWithTransport):
2823
"""Simulate loading a IniBasedConfig with content of various encodings.
2825
All files produced by bzr are in utf8 content.
2827
Users may modify them manually and end up with a file that can't be
2828
loaded. We need to issue proper error messages in this case.
2831
invalid_utf8_char = '\xff'
2833
def test_load_utf8(self):
2834
"""Ensure we can load an utf8-encoded file."""
2835
# From http://pad.lv/799212
2836
unicode_user = u'b\N{Euro Sign}ar'
2837
unicode_content = u'user=%s' % (unicode_user,)
2838
utf8_content = unicode_content.encode('utf8')
2839
# Store the raw content in the config file
2840
with open('foo.conf', 'wb') as f:
2841
f.write(utf8_content)
2842
conf = config.IniBasedConfig(file_name='foo.conf')
2843
self.assertEquals(unicode_user, conf.get_user_option('user'))
2845
def test_load_badly_encoded_content(self):
2846
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2847
with open('foo.conf', 'wb') as f:
2848
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2849
conf = config.IniBasedConfig(file_name='foo.conf')
2850
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2852
def test_load_erroneous_content(self):
2853
"""Ensure we display a proper error on content that can't be parsed."""
2854
with open('foo.conf', 'wb') as f:
2855
f.write('[open_section\n')
2856
conf = config.IniBasedConfig(file_name='foo.conf')
2857
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2860
class TestMutableStore(TestStore):
2862
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2863
in config.test_store_builder_registry.iteritems()]
2866
super(TestMutableStore, self).setUp()
2867
self.transport = self.get_transport()
2869
def has_store(self, store):
2870
store_basename = urlutils.relative_url(self.transport.external_url(),
2871
store.external_url())
2872
return self.transport.has(store_basename)
2874
def test_save_empty_creates_no_file(self):
2875
# FIXME: There should be a better way than relying on the test
2876
# parametrization to identify branch.conf -- vila 2011-0526
2877
if self.store_id in ('branch', 'remote_branch'):
2878
raise tests.TestNotApplicable(
2879
'branch.conf is *always* created when a branch is initialized')
2880
store = self.get_store(self)
2882
self.assertEquals(False, self.has_store(store))
2884
def test_save_emptied_succeeds(self):
2885
store = self.get_store(self)
2886
store._load_from_string('foo=bar\n')
2887
section = store.get_mutable_section(None)
2888
section.remove('foo')
2890
self.assertEquals(True, self.has_store(store))
2891
modified_store = self.get_store(self)
2892
sections = list(modified_store.get_sections())
2893
self.assertLength(0, sections)
2895
def test_save_with_content_succeeds(self):
2896
# FIXME: There should be a better way than relying on the test
2897
# parametrization to identify branch.conf -- vila 2011-0526
2898
if self.store_id in ('branch', 'remote_branch'):
2899
raise tests.TestNotApplicable(
2900
'branch.conf is *always* created when a branch is initialized')
2901
store = self.get_store(self)
2902
store._load_from_string('foo=bar\n')
2903
self.assertEquals(False, self.has_store(store))
2905
self.assertEquals(True, self.has_store(store))
2906
modified_store = self.get_store(self)
2907
sections = list(modified_store.get_sections())
2908
self.assertLength(1, sections)
2909
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2911
def test_set_option_in_empty_store(self):
2912
store = self.get_store(self)
2913
section = store.get_mutable_section(None)
2914
section.set('foo', 'bar')
2916
modified_store = self.get_store(self)
2917
sections = list(modified_store.get_sections())
2918
self.assertLength(1, sections)
2919
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2921
def test_set_option_in_default_section(self):
2922
store = self.get_store(self)
2923
store._load_from_string('')
2924
section = store.get_mutable_section(None)
2925
section.set('foo', 'bar')
2927
modified_store = self.get_store(self)
2928
sections = list(modified_store.get_sections())
2929
self.assertLength(1, sections)
2930
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2932
def test_set_option_in_named_section(self):
2933
store = self.get_store(self)
2934
store._load_from_string('')
2935
section = store.get_mutable_section('baz')
2936
section.set('foo', 'bar')
2938
modified_store = self.get_store(self)
2939
sections = list(modified_store.get_sections())
2940
self.assertLength(1, sections)
2941
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2943
def test_load_hook(self):
2944
# We first needs to ensure that the store exists
2945
store = self.get_store(self)
2946
section = store.get_mutable_section('baz')
2947
section.set('foo', 'bar')
2949
# Now we can try to load it
2950
store = self.get_store(self)
2954
config.ConfigHooks.install_named_hook('load', hook, None)
2955
self.assertLength(0, calls)
2957
self.assertLength(1, calls)
2958
self.assertEquals((store,), calls[0])
2960
def test_save_hook(self):
2964
config.ConfigHooks.install_named_hook('save', hook, None)
2965
self.assertLength(0, calls)
2966
store = self.get_store(self)
2967
section = store.get_mutable_section('baz')
2968
section.set('foo', 'bar')
2970
self.assertLength(1, calls)
2971
self.assertEquals((store,), calls[0])
2974
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
2976
def get_store(self):
2977
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2979
def test_get_quoted_string(self):
2980
store = self.get_store()
2981
store._load_from_string('foo= " abc "')
2982
stack = config.Stack([store.get_sections])
2983
self.assertEquals(' abc ', stack.get('foo'))
2985
def test_set_quoted_string(self):
2986
store = self.get_store()
2987
stack = config.Stack([store.get_sections], store)
2988
stack.set('foo', ' a b c ')
2990
self.assertFileEqual('foo = " a b c "\n', 'foo.conf')
2993
class TestTransportIniFileStore(TestStore):
2995
def test_loading_unknown_file_fails(self):
2996
store = config.TransportIniFileStore(self.get_transport(),
2998
self.assertRaises(errors.NoSuchFile, store.load)
3000
def test_invalid_content(self):
3001
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3002
self.assertEquals(False, store.is_loaded())
3003
exc = self.assertRaises(
3004
errors.ParseConfigError, store._load_from_string,
3005
'this is invalid !')
3006
self.assertEndsWith(exc.filename, 'foo.conf')
3007
# And the load failed
3008
self.assertEquals(False, store.is_loaded())
3010
def test_get_embedded_sections(self):
3011
# A more complicated example (which also shows that section names and
3012
# option names share the same name space...)
3013
# FIXME: This should be fixed by forbidding dicts as values ?
3014
# -- vila 2011-04-05
3015
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3016
store._load_from_string('''
3020
foo_in_DEFAULT=foo_DEFAULT
3028
sections = list(store.get_sections())
3029
self.assertLength(4, sections)
3030
# The default section has no name.
3031
# List values are provided as strings and need to be explicitly
3032
# converted by specifying from_unicode=list_from_store at option
3034
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3036
self.assertSectionContent(
3037
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3038
self.assertSectionContent(
3039
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3040
# sub sections are provided as embedded dicts.
3041
self.assertSectionContent(
3042
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3046
class TestLockableIniFileStore(TestStore):
3048
def test_create_store_in_created_dir(self):
3049
self.assertPathDoesNotExist('dir')
3050
t = self.get_transport('dir/subdir')
3051
store = config.LockableIniFileStore(t, 'foo.conf')
3052
store.get_mutable_section(None).set('foo', 'bar')
3054
self.assertPathExists('dir/subdir')
3057
class TestConcurrentStoreUpdates(TestStore):
3058
"""Test that Stores properly handle conccurent updates.
3060
New Store implementation may fail some of these tests but until such
3061
implementations exist it's hard to properly filter them from the scenarios
3062
applied here. If you encounter such a case, contact the bzr devs.
3065
scenarios = [(key, {'get_stack': builder}) for key, builder
3066
in config.test_stack_builder_registry.iteritems()]
3069
super(TestConcurrentStoreUpdates, self).setUp()
3070
self.stack = self.get_stack(self)
3071
if not isinstance(self.stack, config._CompatibleStack):
3072
raise tests.TestNotApplicable(
3073
'%s is not meant to be compatible with the old config design'
3075
self.stack.set('one', '1')
3076
self.stack.set('two', '2')
3078
self.stack.store.save()
3080
def test_simple_read_access(self):
3081
self.assertEquals('1', self.stack.get('one'))
3083
def test_simple_write_access(self):
3084
self.stack.set('one', 'one')
3085
self.assertEquals('one', self.stack.get('one'))
3087
def test_listen_to_the_last_speaker(self):
3089
c2 = self.get_stack(self)
3090
c1.set('one', 'ONE')
3091
c2.set('two', 'TWO')
3092
self.assertEquals('ONE', c1.get('one'))
3093
self.assertEquals('TWO', c2.get('two'))
3094
# The second update respect the first one
3095
self.assertEquals('ONE', c2.get('one'))
3097
def test_last_speaker_wins(self):
3098
# If the same config is not shared, the same variable modified twice
3099
# can only see a single result.
3101
c2 = self.get_stack(self)
3104
self.assertEquals('c2', c2.get('one'))
3105
# The first modification is still available until another refresh
3107
self.assertEquals('c1', c1.get('one'))
3108
c1.set('two', 'done')
3109
self.assertEquals('c2', c1.get('one'))
3111
def test_writes_are_serialized(self):
3113
c2 = self.get_stack(self)
3115
# We spawn a thread that will pause *during* the config saving.
3116
before_writing = threading.Event()
3117
after_writing = threading.Event()
3118
writing_done = threading.Event()
3119
c1_save_without_locking_orig = c1.store.save_without_locking
3120
def c1_save_without_locking():
3121
before_writing.set()
3122
c1_save_without_locking_orig()
3123
# The lock is held. We wait for the main thread to decide when to
3125
after_writing.wait()
3126
c1.store.save_without_locking = c1_save_without_locking
3130
t1 = threading.Thread(target=c1_set)
3131
# Collect the thread after the test
3132
self.addCleanup(t1.join)
3133
# Be ready to unblock the thread if the test goes wrong
3134
self.addCleanup(after_writing.set)
3136
before_writing.wait()
3137
self.assertRaises(errors.LockContention,
3138
c2.set, 'one', 'c2')
3139
self.assertEquals('c1', c1.get('one'))
3140
# Let the lock be released
3144
self.assertEquals('c2', c2.get('one'))
3146
def test_read_while_writing(self):
3148
# We spawn a thread that will pause *during* the write
3149
ready_to_write = threading.Event()
3150
do_writing = threading.Event()
3151
writing_done = threading.Event()
3152
# We override the _save implementation so we know the store is locked
3153
c1_save_without_locking_orig = c1.store.save_without_locking
3154
def c1_save_without_locking():
3155
ready_to_write.set()
3156
# The lock is held. We wait for the main thread to decide when to
3159
c1_save_without_locking_orig()
3161
c1.store.save_without_locking = c1_save_without_locking
3164
t1 = threading.Thread(target=c1_set)
3165
# Collect the thread after the test
3166
self.addCleanup(t1.join)
3167
# Be ready to unblock the thread if the test goes wrong
3168
self.addCleanup(do_writing.set)
3170
# Ensure the thread is ready to write
3171
ready_to_write.wait()
3172
self.assertEquals('c1', c1.get('one'))
3173
# If we read during the write, we get the old value
3174
c2 = self.get_stack(self)
3175
self.assertEquals('1', c2.get('one'))
3176
# Let the writing occur and ensure it occurred
3179
# Now we get the updated value
3180
c3 = self.get_stack(self)
3181
self.assertEquals('c1', c3.get('one'))
3183
# FIXME: It may be worth looking into removing the lock dir when it's not
3184
# needed anymore and look at possible fallouts for concurrent lockers. This
3185
# will matter if/when we use config files outside of bazaar directories
3186
# (.bazaar or .bzr) -- vila 20110-04-111
3189
class TestSectionMatcher(TestStore):
3191
scenarios = [('location', {'matcher': config.LocationMatcher}),
3192
('id', {'matcher': config.NameMatcher}),]
3195
super(TestSectionMatcher, self).setUp()
3196
# Any simple store is good enough
3197
self.get_store = config.test_store_builder_registry.get('configobj')
3199
def test_no_matches_for_empty_stores(self):
3200
store = self.get_store(self)
3201
store._load_from_string('')
3202
matcher = self.matcher(store, '/bar')
3203
self.assertEquals([], list(matcher.get_sections()))
3205
def test_build_doesnt_load_store(self):
3206
store = self.get_store(self)
3207
matcher = self.matcher(store, '/bar')
3208
self.assertFalse(store.is_loaded())
3211
class TestLocationSection(tests.TestCase):
3213
def get_section(self, options, extra_path):
3214
section = config.Section('foo', options)
3215
return config.LocationSection(section, extra_path)
3217
def test_simple_option(self):
3218
section = self.get_section({'foo': 'bar'}, '')
3219
self.assertEquals('bar', section.get('foo'))
3221
def test_option_with_extra_path(self):
3222
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3224
self.assertEquals('bar/baz', section.get('foo'))
3226
def test_invalid_policy(self):
3227
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3229
# invalid policies are ignored
3230
self.assertEquals('bar', section.get('foo'))
3233
class TestLocationMatcher(TestStore):
3236
super(TestLocationMatcher, self).setUp()
3237
# Any simple store is good enough
3238
self.get_store = config.test_store_builder_registry.get('configobj')
3240
def test_unrelated_section_excluded(self):
3241
store = self.get_store(self)
3242
store._load_from_string('''
3250
section=/foo/bar/baz
3254
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3256
[section.id for _, section in store.get_sections()])
3257
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3258
sections = [section for _, section in matcher.get_sections()]
3259
self.assertEquals(['/foo/bar', '/foo'],
3260
[section.id for section in sections])
3261
self.assertEquals(['quux', 'bar/quux'],
3262
[section.extra_path for section in sections])
3264
def test_more_specific_sections_first(self):
3265
store = self.get_store(self)
3266
store._load_from_string('''
3272
self.assertEquals(['/foo', '/foo/bar'],
3273
[section.id for _, section in store.get_sections()])
3274
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3275
sections = [section for _, section in matcher.get_sections()]
3276
self.assertEquals(['/foo/bar', '/foo'],
3277
[section.id for section in sections])
3278
self.assertEquals(['baz', 'bar/baz'],
3279
[section.extra_path for section in sections])
3281
def test_appendpath_in_no_name_section(self):
3282
# It's a bit weird to allow appendpath in a no-name section, but
3283
# someone may found a use for it
3284
store = self.get_store(self)
3285
store._load_from_string('''
3287
foo:policy = appendpath
3289
matcher = config.LocationMatcher(store, 'dir/subdir')
3290
sections = list(matcher.get_sections())
3291
self.assertLength(1, sections)
3292
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3294
def test_file_urls_are_normalized(self):
3295
store = self.get_store(self)
3296
if sys.platform == 'win32':
3297
expected_url = 'file:///C:/dir/subdir'
3298
expected_location = 'C:/dir/subdir'
3300
expected_url = 'file:///dir/subdir'
3301
expected_location = '/dir/subdir'
3302
matcher = config.LocationMatcher(store, expected_url)
3303
self.assertEquals(expected_location, matcher.location)
3306
class TestStartingPathMatcher(TestStore):
3309
super(TestStartingPathMatcher, self).setUp()
3310
# Any simple store is good enough
3311
self.store = config.IniFileStore()
3313
def assertSectionIDs(self, expected, location, content):
3314
self.store._load_from_string(content)
3315
matcher = config.StartingPathMatcher(self.store, location)
3316
sections = list(matcher.get_sections())
3317
self.assertLength(len(expected), sections)
3318
self.assertEqual(expected, [section.id for _, section in sections])
3321
def test_empty(self):
3322
self.assertSectionIDs([], self.get_url(), '')
3324
def test_url_vs_local_paths(self):
3325
# The matcher location is an url and the section names are local paths
3326
sections = self.assertSectionIDs(['/foo/bar', '/foo'],
3327
'file:///foo/bar/baz', '''\
3332
def test_local_path_vs_url(self):
3333
# The matcher location is a local path and the section names are urls
3334
sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3335
'/foo/bar/baz', '''\
3341
def test_no_name_section_included_when_present(self):
3342
# Note that other tests will cover the case where the no-name section
3343
# is empty and as such, not included.
3344
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3345
'/foo/bar/baz', '''\
3346
option = defined so the no-name section exists
3350
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3351
[s.locals['relpath'] for _, s in sections])
3353
def test_order_reversed(self):
3354
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3359
def test_unrelated_section_excluded(self):
3360
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3366
def test_glob_included(self):
3367
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3368
'/foo/bar/baz', '''\
3374
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3375
# nothing really is... as far using {relpath} to append it to something
3376
# else, this seems good enough though.
3377
self.assertEquals(['', 'baz', 'bar/baz'],
3378
[s.locals['relpath'] for _, s in sections])
3380
def test_respect_order(self):
3381
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3382
'/foo/bar/baz', '''\
3390
class TestNameMatcher(TestStore):
3393
super(TestNameMatcher, self).setUp()
3394
self.matcher = config.NameMatcher
3395
# Any simple store is good enough
3396
self.get_store = config.test_store_builder_registry.get('configobj')
3398
def get_matching_sections(self, name):
3399
store = self.get_store(self)
3400
store._load_from_string('''
3408
matcher = self.matcher(store, name)
3409
return list(matcher.get_sections())
3411
def test_matching(self):
3412
sections = self.get_matching_sections('foo')
3413
self.assertLength(1, sections)
3414
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3416
def test_not_matching(self):
3417
sections = self.get_matching_sections('baz')
3418
self.assertLength(0, sections)
3421
class TestBaseStackGet(tests.TestCase):
3424
super(TestBaseStackGet, self).setUp()
3425
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3427
def test_get_first_definition(self):
3428
store1 = config.IniFileStore()
3429
store1._load_from_string('foo=bar')
3430
store2 = config.IniFileStore()
3431
store2._load_from_string('foo=baz')
3432
conf = config.Stack([store1.get_sections, store2.get_sections])
3433
self.assertEquals('bar', conf.get('foo'))
3435
def test_get_with_registered_default_value(self):
3436
config.option_registry.register(config.Option('foo', default='bar'))
3437
conf_stack = config.Stack([])
3438
self.assertEquals('bar', conf_stack.get('foo'))
3440
def test_get_without_registered_default_value(self):
3441
config.option_registry.register(config.Option('foo'))
3442
conf_stack = config.Stack([])
3443
self.assertEquals(None, conf_stack.get('foo'))
3445
def test_get_without_default_value_for_not_registered(self):
3446
conf_stack = config.Stack([])
3447
self.assertEquals(None, conf_stack.get('foo'))
3449
def test_get_for_empty_section_callable(self):
3450
conf_stack = config.Stack([lambda : []])
3451
self.assertEquals(None, conf_stack.get('foo'))
3453
def test_get_for_broken_callable(self):
3454
# Trying to use and invalid callable raises an exception on first use
3455
conf_stack = config.Stack([object])
3456
self.assertRaises(TypeError, conf_stack.get, 'foo')
3459
class TestMemoryStack(tests.TestCase):
3462
conf = config.MemoryStack('foo=bar')
3463
self.assertEquals('bar', conf.get('foo'))
3466
conf = config.MemoryStack('foo=bar')
3467
conf.set('foo', 'baz')
3468
self.assertEquals('baz', conf.get('foo'))
3470
def test_no_content(self):
3471
conf = config.MemoryStack()
3472
# No content means no loading
3473
self.assertFalse(conf.store.is_loaded())
3474
self.assertRaises(NotImplementedError, conf.get, 'foo')
3475
# But a content can still be provided
3476
conf.store._load_from_string('foo=bar')
3477
self.assertEquals('bar', conf.get('foo'))
3480
class TestStackWithTransport(tests.TestCaseWithTransport):
3482
scenarios = [(key, {'get_stack': builder}) for key, builder
3483
in config.test_stack_builder_registry.iteritems()]
3486
class TestConcreteStacks(TestStackWithTransport):
3488
def test_build_stack(self):
3489
# Just a smoke test to help debug builders
3490
stack = self.get_stack(self)
3493
class TestStackGet(TestStackWithTransport):
3496
super(TestStackGet, self).setUp()
3497
self.conf = self.get_stack(self)
3499
def test_get_for_empty_stack(self):
3500
self.assertEquals(None, self.conf.get('foo'))
3502
def test_get_hook(self):
3503
self.conf.set('foo', 'bar')
3507
config.ConfigHooks.install_named_hook('get', hook, None)
3508
self.assertLength(0, calls)
3509
value = self.conf.get('foo')
3510
self.assertEquals('bar', value)
3511
self.assertLength(1, calls)
3512
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3515
class TestStackGetWithConverter(tests.TestCase):
3518
super(TestStackGetWithConverter, self).setUp()
3519
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3520
self.registry = config.option_registry
3522
def get_conf(self, content=None):
3523
return config.MemoryStack(content)
3525
def register_bool_option(self, name, default=None, default_from_env=None):
3526
b = config.Option(name, help='A boolean.',
3527
default=default, default_from_env=default_from_env,
3528
from_unicode=config.bool_from_store)
3529
self.registry.register(b)
3531
def test_get_default_bool_None(self):
3532
self.register_bool_option('foo')
3533
conf = self.get_conf('')
3534
self.assertEquals(None, conf.get('foo'))
3536
def test_get_default_bool_True(self):
3537
self.register_bool_option('foo', u'True')
3538
conf = self.get_conf('')
3539
self.assertEquals(True, conf.get('foo'))
3541
def test_get_default_bool_False(self):
3542
self.register_bool_option('foo', False)
3543
conf = self.get_conf('')
3544
self.assertEquals(False, conf.get('foo'))
3546
def test_get_default_bool_False_as_string(self):
3547
self.register_bool_option('foo', u'False')
3548
conf = self.get_conf('')
3549
self.assertEquals(False, conf.get('foo'))
3551
def test_get_default_bool_from_env_converted(self):
3552
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3553
self.overrideEnv('FOO', 'False')
3554
conf = self.get_conf('')
3555
self.assertEquals(False, conf.get('foo'))
3557
def test_get_default_bool_when_conversion_fails(self):
3558
self.register_bool_option('foo', default='True')
3559
conf = self.get_conf('foo=invalid boolean')
3560
self.assertEquals(True, conf.get('foo'))
3562
def register_integer_option(self, name,
3563
default=None, default_from_env=None):
3564
i = config.Option(name, help='An integer.',
3565
default=default, default_from_env=default_from_env,
3566
from_unicode=config.int_from_store)
3567
self.registry.register(i)
3569
def test_get_default_integer_None(self):
3570
self.register_integer_option('foo')
3571
conf = self.get_conf('')
3572
self.assertEquals(None, conf.get('foo'))
3574
def test_get_default_integer(self):
3575
self.register_integer_option('foo', 42)
3576
conf = self.get_conf('')
3577
self.assertEquals(42, conf.get('foo'))
3579
def test_get_default_integer_as_string(self):
3580
self.register_integer_option('foo', u'42')
3581
conf = self.get_conf('')
3582
self.assertEquals(42, conf.get('foo'))
3584
def test_get_default_integer_from_env(self):
3585
self.register_integer_option('foo', default_from_env=['FOO'])
3586
self.overrideEnv('FOO', '18')
3587
conf = self.get_conf('')
3588
self.assertEquals(18, conf.get('foo'))
3590
def test_get_default_integer_when_conversion_fails(self):
3591
self.register_integer_option('foo', default='12')
3592
conf = self.get_conf('foo=invalid integer')
3593
self.assertEquals(12, conf.get('foo'))
3595
def register_list_option(self, name, default=None, default_from_env=None):
3596
l = config.ListOption(name, help='A list.', default=default,
3597
default_from_env=default_from_env)
3598
self.registry.register(l)
3600
def test_get_default_list_None(self):
3601
self.register_list_option('foo')
3602
conf = self.get_conf('')
3603
self.assertEquals(None, conf.get('foo'))
3605
def test_get_default_list_empty(self):
3606
self.register_list_option('foo', '')
3607
conf = self.get_conf('')
3608
self.assertEquals([], conf.get('foo'))
3610
def test_get_default_list_from_env(self):
3611
self.register_list_option('foo', default_from_env=['FOO'])
3612
self.overrideEnv('FOO', '')
3613
conf = self.get_conf('')
3614
self.assertEquals([], conf.get('foo'))
3616
def test_get_with_list_converter_no_item(self):
3617
self.register_list_option('foo', None)
3618
conf = self.get_conf('foo=,')
3619
self.assertEquals([], conf.get('foo'))
3621
def test_get_with_list_converter_many_items(self):
3622
self.register_list_option('foo', None)
3623
conf = self.get_conf('foo=m,o,r,e')
3624
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3626
def test_get_with_list_converter_embedded_spaces_many_items(self):
3627
self.register_list_option('foo', None)
3628
conf = self.get_conf('foo=" bar", "baz "')
3629
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3631
def test_get_with_list_converter_stripped_spaces_many_items(self):
3632
self.register_list_option('foo', None)
3633
conf = self.get_conf('foo= bar , baz ')
3634
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3637
class TestIterOptionRefs(tests.TestCase):
3638
"""iter_option_refs is a bit unusual, document some cases."""
3640
def assertRefs(self, expected, string):
3641
self.assertEquals(expected, list(config.iter_option_refs(string)))
3643
def test_empty(self):
3644
self.assertRefs([(False, '')], '')
3646
def test_no_refs(self):
3647
self.assertRefs([(False, 'foo bar')], 'foo bar')
3649
def test_single_ref(self):
3650
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3652
def test_broken_ref(self):
3653
self.assertRefs([(False, '{foo')], '{foo')
3655
def test_embedded_ref(self):
3656
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3659
def test_two_refs(self):
3660
self.assertRefs([(False, ''), (True, '{foo}'),
3661
(False, ''), (True, '{bar}'),
3665
def test_newline_in_refs_are_not_matched(self):
3666
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3669
class TestStackExpandOptions(tests.TestCaseWithTransport):
3672
super(TestStackExpandOptions, self).setUp()
3673
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3674
self.registry = config.option_registry
3675
self.conf = build_branch_stack(self)
3677
def assertExpansion(self, expected, string, env=None):
3678
self.assertEquals(expected, self.conf.expand_options(string, env))
3680
def test_no_expansion(self):
3681
self.assertExpansion('foo', 'foo')
3683
def test_expand_default_value(self):
3684
self.conf.store._load_from_string('bar=baz')
3685
self.registry.register(config.Option('foo', default=u'{bar}'))
3686
self.assertEquals('baz', self.conf.get('foo', expand=True))
3688
def test_expand_default_from_env(self):
3689
self.conf.store._load_from_string('bar=baz')
3690
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3691
self.overrideEnv('FOO', '{bar}')
3692
self.assertEquals('baz', self.conf.get('foo', expand=True))
3694
def test_expand_default_on_failed_conversion(self):
3695
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3696
self.registry.register(
3697
config.Option('foo', default=u'{bar}',
3698
from_unicode=config.int_from_store))
3699
self.assertEquals(42, self.conf.get('foo', expand=True))
3701
def test_env_adding_options(self):
3702
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3704
def test_env_overriding_options(self):
3705
self.conf.store._load_from_string('foo=baz')
3706
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3708
def test_simple_ref(self):
3709
self.conf.store._load_from_string('foo=xxx')
3710
self.assertExpansion('xxx', '{foo}')
3712
def test_unknown_ref(self):
3713
self.assertRaises(errors.ExpandingUnknownOption,
3714
self.conf.expand_options, '{foo}')
3716
def test_indirect_ref(self):
3717
self.conf.store._load_from_string('''
3721
self.assertExpansion('xxx', '{bar}')
3723
def test_embedded_ref(self):
3724
self.conf.store._load_from_string('''
3728
self.assertExpansion('xxx', '{{bar}}')
3730
def test_simple_loop(self):
3731
self.conf.store._load_from_string('foo={foo}')
3732
self.assertRaises(errors.OptionExpansionLoop,
3733
self.conf.expand_options, '{foo}')
3735
def test_indirect_loop(self):
3736
self.conf.store._load_from_string('''
3740
e = self.assertRaises(errors.OptionExpansionLoop,
3741
self.conf.expand_options, '{foo}')
3742
self.assertEquals('foo->bar->baz', e.refs)
3743
self.assertEquals('{foo}', e.string)
3745
def test_list(self):
3746
self.conf.store._load_from_string('''
3750
list={foo},{bar},{baz}
3752
self.registry.register(
3753
config.ListOption('list'))
3754
self.assertEquals(['start', 'middle', 'end'],
3755
self.conf.get('list', expand=True))
3757
def test_cascading_list(self):
3758
self.conf.store._load_from_string('''
3764
self.registry.register(
3765
config.ListOption('list'))
3766
self.assertEquals(['start', 'middle', 'end'],
3767
self.conf.get('list', expand=True))
3769
def test_pathologically_hidden_list(self):
3770
self.conf.store._load_from_string('''
3776
hidden={start}{middle}{end}
3778
# What matters is what the registration says, the conversion happens
3779
# only after all expansions have been performed
3780
self.registry.register(config.ListOption('hidden'))
3781
self.assertEquals(['bin', 'go'],
3782
self.conf.get('hidden', expand=True))
3785
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3788
super(TestStackCrossSectionsExpand, self).setUp()
3790
def get_config(self, location, string):
3793
# Since we don't save the config we won't strictly require to inherit
3794
# from TestCaseInTempDir, but an error occurs so quickly...
3795
c = config.LocationStack(location)
3796
c.store._load_from_string(string)
3799
def test_dont_cross_unrelated_section(self):
3800
c = self.get_config('/another/branch/path','''
3805
[/another/branch/path]
3808
self.assertRaises(errors.ExpandingUnknownOption,
3809
c.get, 'bar', expand=True)
3811
def test_cross_related_sections(self):
3812
c = self.get_config('/project/branch/path','''
3816
[/project/branch/path]
3819
self.assertEquals('quux', c.get('bar', expand=True))
3822
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3824
def test_cross_global_locations(self):
3825
l_store = config.LocationStore()
3826
l_store._load_from_string('''
3832
g_store = config.GlobalStore()
3833
g_store._load_from_string('''
3839
stack = config.LocationStack('/branch')
3840
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3841
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3844
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3846
def test_expand_locals_empty(self):
3847
l_store = config.LocationStore()
3848
l_store._load_from_string('''
3849
[/home/user/project]
3854
stack = config.LocationStack('/home/user/project/')
3855
self.assertEquals('', stack.get('base', expand=True))
3856
self.assertEquals('', stack.get('rel', expand=True))
3858
def test_expand_basename_locally(self):
3859
l_store = config.LocationStore()
3860
l_store._load_from_string('''
3861
[/home/user/project]
3865
stack = config.LocationStack('/home/user/project/branch')
3866
self.assertEquals('branch', stack.get('bfoo', expand=True))
3868
def test_expand_basename_locally_longer_path(self):
3869
l_store = config.LocationStore()
3870
l_store._load_from_string('''
3875
stack = config.LocationStack('/home/user/project/dir/branch')
3876
self.assertEquals('branch', stack.get('bfoo', expand=True))
3878
def test_expand_relpath_locally(self):
3879
l_store = config.LocationStore()
3880
l_store._load_from_string('''
3881
[/home/user/project]
3882
lfoo = loc-foo/{relpath}
3885
stack = config.LocationStack('/home/user/project/branch')
3886
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3888
def test_expand_relpath_unknonw_in_global(self):
3889
g_store = config.GlobalStore()
3890
g_store._load_from_string('''
3895
stack = config.LocationStack('/home/user/project/branch')
3896
self.assertRaises(errors.ExpandingUnknownOption,
3897
stack.get, 'gfoo', expand=True)
3899
def test_expand_local_option_locally(self):
3900
l_store = config.LocationStore()
3901
l_store._load_from_string('''
3902
[/home/user/project]
3903
lfoo = loc-foo/{relpath}
3907
g_store = config.GlobalStore()
3908
g_store._load_from_string('''
3914
stack = config.LocationStack('/home/user/project/branch')
3915
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3916
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3918
def test_locals_dont_leak(self):
3919
"""Make sure we chose the right local in presence of several sections.
3921
l_store = config.LocationStore()
3922
l_store._load_from_string('''
3924
lfoo = loc-foo/{relpath}
3925
[/home/user/project]
3926
lfoo = loc-foo/{relpath}
3929
stack = config.LocationStack('/home/user/project/branch')
3930
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3931
stack = config.LocationStack('/home/user/bar/baz')
3932
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3936
class TestStackSet(TestStackWithTransport):
3938
def test_simple_set(self):
3939
conf = self.get_stack(self)
3940
self.assertEquals(None, conf.get('foo'))
3941
conf.set('foo', 'baz')
3942
# Did we get it back ?
3943
self.assertEquals('baz', conf.get('foo'))
3945
def test_set_creates_a_new_section(self):
3946
conf = self.get_stack(self)
3947
conf.set('foo', 'baz')
3948
self.assertEquals, 'baz', conf.get('foo')
3950
def test_set_hook(self):
3954
config.ConfigHooks.install_named_hook('set', hook, None)
3955
self.assertLength(0, calls)
3956
conf = self.get_stack(self)
3957
conf.set('foo', 'bar')
3958
self.assertLength(1, calls)
3959
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3962
class TestStackRemove(TestStackWithTransport):
3964
def test_remove_existing(self):
3965
conf = self.get_stack(self)
3966
conf.set('foo', 'bar')
3967
self.assertEquals('bar', conf.get('foo'))
3969
# Did we get it back ?
3970
self.assertEquals(None, conf.get('foo'))
3972
def test_remove_unknown(self):
3973
conf = self.get_stack(self)
3974
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3976
def test_remove_hook(self):
3980
config.ConfigHooks.install_named_hook('remove', hook, None)
3981
self.assertLength(0, calls)
3982
conf = self.get_stack(self)
3983
conf.set('foo', 'bar')
3985
self.assertLength(1, calls)
3986
self.assertEquals((conf, 'foo'), calls[0])
3989
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3992
super(TestConfigGetOptions, self).setUp()
3993
create_configs(self)
3995
def test_no_variable(self):
3996
# Using branch should query branch, locations and bazaar
3997
self.assertOptions([], self.branch_config)
3999
def test_option_in_bazaar(self):
4000
self.bazaar_config.set_user_option('file', 'bazaar')
4001
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
4004
def test_option_in_locations(self):
4005
self.locations_config.set_user_option('file', 'locations')
4007
[('file', 'locations', self.tree.basedir, 'locations')],
4008
self.locations_config)
4010
def test_option_in_branch(self):
4011
self.branch_config.set_user_option('file', 'branch')
4012
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
4015
def test_option_in_bazaar_and_branch(self):
4016
self.bazaar_config.set_user_option('file', 'bazaar')
4017
self.branch_config.set_user_option('file', 'branch')
4018
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4019
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4022
def test_option_in_branch_and_locations(self):
4023
# Hmm, locations override branch :-/
4024
self.locations_config.set_user_option('file', 'locations')
4025
self.branch_config.set_user_option('file', 'branch')
4027
[('file', 'locations', self.tree.basedir, 'locations'),
4028
('file', 'branch', 'DEFAULT', 'branch'),],
4031
def test_option_in_bazaar_locations_and_branch(self):
4032
self.bazaar_config.set_user_option('file', 'bazaar')
4033
self.locations_config.set_user_option('file', 'locations')
4034
self.branch_config.set_user_option('file', 'branch')
4036
[('file', 'locations', self.tree.basedir, 'locations'),
4037
('file', 'branch', 'DEFAULT', 'branch'),
4038
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4042
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
4045
super(TestConfigRemoveOption, self).setUp()
4046
create_configs_with_file_option(self)
4048
def test_remove_in_locations(self):
4049
self.locations_config.remove_user_option('file', self.tree.basedir)
4051
[('file', 'branch', 'DEFAULT', 'branch'),
4052
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4055
def test_remove_in_branch(self):
4056
self.branch_config.remove_user_option('file')
4058
[('file', 'locations', self.tree.basedir, 'locations'),
4059
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4062
def test_remove_in_bazaar(self):
4063
self.bazaar_config.remove_user_option('file')
4065
[('file', 'locations', self.tree.basedir, 'locations'),
4066
('file', 'branch', 'DEFAULT', 'branch'),],
4070
class TestConfigGetSections(tests.TestCaseWithTransport):
4073
super(TestConfigGetSections, self).setUp()
4074
create_configs(self)
4076
def assertSectionNames(self, expected, conf, name=None):
4077
"""Check which sections are returned for a given config.
4079
If fallback configurations exist their sections can be included.
4081
:param expected: A list of section names.
4083
:param conf: The configuration that will be queried.
4085
:param name: An optional section name that will be passed to
4088
sections = list(conf._get_sections(name))
4089
self.assertLength(len(expected), sections)
4090
self.assertEqual(expected, [name for name, _, _ in sections])
4092
def test_bazaar_default_section(self):
4093
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4095
def test_locations_default_section(self):
4096
# No sections are defined in an empty file
4097
self.assertSectionNames([], self.locations_config)
4099
def test_locations_named_section(self):
4100
self.locations_config.set_user_option('file', 'locations')
4101
self.assertSectionNames([self.tree.basedir], self.locations_config)
4103
def test_locations_matching_sections(self):
4104
loc_config = self.locations_config
4105
loc_config.set_user_option('file', 'locations')
4106
# We need to cheat a bit here to create an option in sections above and
4107
# below the 'location' one.
4108
parser = loc_config._get_parser()
4109
# locations.cong deals with '/' ignoring native os.sep
4110
location_names = self.tree.basedir.split('/')
4111
parent = '/'.join(location_names[:-1])
4112
child = '/'.join(location_names + ['child'])
4114
parser[parent]['file'] = 'parent'
4116
parser[child]['file'] = 'child'
4117
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4119
def test_branch_data_default_section(self):
4120
self.assertSectionNames([None],
4121
self.branch_config._get_branch_data_config())
4123
def test_branch_default_sections(self):
4124
# No sections are defined in an empty locations file
4125
self.assertSectionNames([None, 'DEFAULT'],
4127
# Unless we define an option
4128
self.branch_config._get_location_config().set_user_option(
4129
'file', 'locations')
4130
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4133
def test_bazaar_named_section(self):
4134
# We need to cheat as the API doesn't give direct access to sections
4135
# other than DEFAULT.
4136
self.bazaar_config.set_alias('bazaar', 'bzr')
4137
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
4140
1241
class TestAuthenticationConfigFile(tests.TestCase):
4141
1242
"""Test the authentication.conf file matching"""