1471
1979
self.assertIs(None, bzrdir_config.get_default_stack_on())
1982
class TestOldConfigHooks(tests.TestCaseWithTransport):
1985
super(TestOldConfigHooks, self).setUp()
1986
create_configs_with_file_option(self)
1988
def assertGetHook(self, conf, name, value):
1992
config.OldConfigHooks.install_named_hook('get', hook, None)
1994
config.OldConfigHooks.uninstall_named_hook, 'get', None)
1995
self.assertLength(0, calls)
1996
actual_value = conf.get_user_option(name)
1997
self.assertEquals(value, actual_value)
1998
self.assertLength(1, calls)
1999
self.assertEquals((conf, name, value), calls[0])
2001
def test_get_hook_bazaar(self):
2002
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2004
def test_get_hook_locations(self):
2005
self.assertGetHook(self.locations_config, 'file', 'locations')
2007
def test_get_hook_branch(self):
2008
# Since locations masks branch, we define a different option
2009
self.branch_config.set_user_option('file2', 'branch')
2010
self.assertGetHook(self.branch_config, 'file2', 'branch')
2012
def assertSetHook(self, conf, name, value):
2016
config.OldConfigHooks.install_named_hook('set', hook, None)
2018
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2019
self.assertLength(0, calls)
2020
conf.set_user_option(name, value)
2021
self.assertLength(1, calls)
2022
# We can't assert the conf object below as different configs use
2023
# different means to implement set_user_option and we care only about
2025
self.assertEquals((name, value), calls[0][1:])
2027
def test_set_hook_bazaar(self):
2028
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2030
def test_set_hook_locations(self):
2031
self.assertSetHook(self.locations_config, 'foo', 'locations')
2033
def test_set_hook_branch(self):
2034
self.assertSetHook(self.branch_config, 'foo', 'branch')
2036
def assertRemoveHook(self, conf, name, section_name=None):
2040
config.OldConfigHooks.install_named_hook('remove', hook, None)
2042
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2043
self.assertLength(0, calls)
2044
conf.remove_user_option(name, section_name)
2045
self.assertLength(1, calls)
2046
# We can't assert the conf object below as different configs use
2047
# different means to implement remove_user_option and we care only about
2049
self.assertEquals((name,), calls[0][1:])
2051
def test_remove_hook_bazaar(self):
2052
self.assertRemoveHook(self.bazaar_config, 'file')
2054
def test_remove_hook_locations(self):
2055
self.assertRemoveHook(self.locations_config, 'file',
2056
self.locations_config.location)
2058
def test_remove_hook_branch(self):
2059
self.assertRemoveHook(self.branch_config, 'file')
2061
def assertLoadHook(self, name, conf_class, *conf_args):
2065
config.OldConfigHooks.install_named_hook('load', hook, None)
2067
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2068
self.assertLength(0, calls)
2070
conf = conf_class(*conf_args)
2071
# Access an option to trigger a load
2072
conf.get_user_option(name)
2073
self.assertLength(1, calls)
2074
# Since we can't assert about conf, we just use the number of calls ;-/
2076
def test_load_hook_bazaar(self):
2077
self.assertLoadHook('file', config.GlobalConfig)
2079
def test_load_hook_locations(self):
2080
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2082
def test_load_hook_branch(self):
2083
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2085
def assertSaveHook(self, conf):
2089
config.OldConfigHooks.install_named_hook('save', hook, None)
2091
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2092
self.assertLength(0, calls)
2093
# Setting an option triggers a save
2094
conf.set_user_option('foo', 'bar')
2095
self.assertLength(1, calls)
2096
# Since we can't assert about conf, we just use the number of calls ;-/
2098
def test_save_hook_bazaar(self):
2099
self.assertSaveHook(self.bazaar_config)
2101
def test_save_hook_locations(self):
2102
self.assertSaveHook(self.locations_config)
2104
def test_save_hook_branch(self):
2105
self.assertSaveHook(self.branch_config)
2108
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2109
"""Tests config hooks for remote configs.
2111
No tests for the remove hook as this is not implemented there.
2115
super(TestOldConfigHooksForRemote, self).setUp()
2116
self.transport_server = test_server.SmartTCPServer_for_testing
2117
create_configs_with_file_option(self)
2119
def assertGetHook(self, conf, name, value):
2123
config.OldConfigHooks.install_named_hook('get', hook, None)
2125
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2126
self.assertLength(0, calls)
2127
actual_value = conf.get_option(name)
2128
self.assertEquals(value, actual_value)
2129
self.assertLength(1, calls)
2130
self.assertEquals((conf, name, value), calls[0])
2132
def test_get_hook_remote_branch(self):
2133
remote_branch = branch.Branch.open(self.get_url('tree'))
2134
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2136
def test_get_hook_remote_bzrdir(self):
2137
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2138
conf = remote_bzrdir._get_config()
2139
conf.set_option('remotedir', 'file')
2140
self.assertGetHook(conf, 'file', 'remotedir')
2142
def assertSetHook(self, conf, name, value):
2146
config.OldConfigHooks.install_named_hook('set', hook, None)
2148
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2149
self.assertLength(0, calls)
2150
conf.set_option(value, name)
2151
self.assertLength(1, calls)
2152
# We can't assert the conf object below as different configs use
2153
# different means to implement set_user_option and we care only about
2155
self.assertEquals((name, value), calls[0][1:])
2157
def test_set_hook_remote_branch(self):
2158
remote_branch = branch.Branch.open(self.get_url('tree'))
2159
self.addCleanup(remote_branch.lock_write().unlock)
2160
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2162
def test_set_hook_remote_bzrdir(self):
2163
remote_branch = branch.Branch.open(self.get_url('tree'))
2164
self.addCleanup(remote_branch.lock_write().unlock)
2165
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2166
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2168
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2172
config.OldConfigHooks.install_named_hook('load', hook, None)
2174
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2175
self.assertLength(0, calls)
2177
conf = conf_class(*conf_args)
2178
# Access an option to trigger a load
2179
conf.get_option(name)
2180
self.assertLength(expected_nb_calls, calls)
2181
# Since we can't assert about conf, we just use the number of calls ;-/
2183
def test_load_hook_remote_branch(self):
2184
remote_branch = branch.Branch.open(self.get_url('tree'))
2185
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2187
def test_load_hook_remote_bzrdir(self):
2188
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2189
# The config file doesn't exist, set an option to force its creation
2190
conf = remote_bzrdir._get_config()
2191
conf.set_option('remotedir', 'file')
2192
# We get one call for the server and one call for the client, this is
2193
# caused by the differences in implementations betwen
2194
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2195
# SmartServerBranchGetConfigFile (in smart/branch.py)
2196
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2198
def assertSaveHook(self, conf):
2202
config.OldConfigHooks.install_named_hook('save', hook, None)
2204
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2205
self.assertLength(0, calls)
2206
# Setting an option triggers a save
2207
conf.set_option('foo', 'bar')
2208
self.assertLength(1, calls)
2209
# Since we can't assert about conf, we just use the number of calls ;-/
2211
def test_save_hook_remote_branch(self):
2212
remote_branch = branch.Branch.open(self.get_url('tree'))
2213
self.addCleanup(remote_branch.lock_write().unlock)
2214
self.assertSaveHook(remote_branch._get_config())
2216
def test_save_hook_remote_bzrdir(self):
2217
remote_branch = branch.Branch.open(self.get_url('tree'))
2218
self.addCleanup(remote_branch.lock_write().unlock)
2219
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2220
self.assertSaveHook(remote_bzrdir._get_config())
2223
class TestOption(tests.TestCase):
2225
def test_default_value(self):
2226
opt = config.Option('foo', default='bar')
2227
self.assertEquals('bar', opt.get_default())
2229
def test_callable_default_value(self):
2230
def bar_as_unicode():
2232
opt = config.Option('foo', default=bar_as_unicode)
2233
self.assertEquals('bar', opt.get_default())
2235
def test_default_value_from_env(self):
2236
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2237
self.overrideEnv('FOO', 'quux')
2238
# Env variable provides a default taking over the option one
2239
self.assertEquals('quux', opt.get_default())
2241
def test_first_default_value_from_env_wins(self):
2242
opt = config.Option('foo', default='bar',
2243
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2244
self.overrideEnv('FOO', 'foo')
2245
self.overrideEnv('BAZ', 'baz')
2246
# The first env var set wins
2247
self.assertEquals('foo', opt.get_default())
2249
def test_not_supported_list_default_value(self):
2250
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2252
def test_not_supported_object_default_value(self):
2253
self.assertRaises(AssertionError, config.Option, 'foo',
2256
def test_not_supported_callable_default_value_not_unicode(self):
2257
def bar_not_unicode():
2259
opt = config.Option('foo', default=bar_not_unicode)
2260
self.assertRaises(AssertionError, opt.get_default)
2262
def test_get_help_topic(self):
2263
opt = config.Option('foo')
2264
self.assertEquals('foo', opt.get_help_topic())
2267
class TestOptionConverterMixin(object):
2269
def assertConverted(self, expected, opt, value):
2270
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2272
def assertWarns(self, opt, value):
2275
warnings.append(args[0] % args[1:])
2276
self.overrideAttr(trace, 'warning', warning)
2277
self.assertEquals(None, opt.convert_from_unicode(None, value))
2278
self.assertLength(1, warnings)
2280
'Value "%s" is not valid for "%s"' % (value, opt.name),
2283
def assertErrors(self, opt, value):
2284
self.assertRaises(errors.ConfigOptionValueError,
2285
opt.convert_from_unicode, None, value)
2287
def assertConvertInvalid(self, opt, invalid_value):
2289
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2290
opt.invalid = 'warning'
2291
self.assertWarns(opt, invalid_value)
2292
opt.invalid = 'error'
2293
self.assertErrors(opt, invalid_value)
2296
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2298
def get_option(self):
2299
return config.Option('foo', help='A boolean.',
2300
from_unicode=config.bool_from_store)
2302
def test_convert_invalid(self):
2303
opt = self.get_option()
2304
# A string that is not recognized as a boolean
2305
self.assertConvertInvalid(opt, u'invalid-boolean')
2306
# A list of strings is never recognized as a boolean
2307
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2309
def test_convert_valid(self):
2310
opt = self.get_option()
2311
self.assertConverted(True, opt, u'True')
2312
self.assertConverted(True, opt, u'1')
2313
self.assertConverted(False, opt, u'False')
2316
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2318
def get_option(self):
2319
return config.Option('foo', help='An integer.',
2320
from_unicode=config.int_from_store)
2322
def test_convert_invalid(self):
2323
opt = self.get_option()
2324
# A string that is not recognized as an integer
2325
self.assertConvertInvalid(opt, u'forty-two')
2326
# A list of strings is never recognized as an integer
2327
self.assertConvertInvalid(opt, [u'a', u'list'])
2329
def test_convert_valid(self):
2330
opt = self.get_option()
2331
self.assertConverted(16, opt, u'16')
2334
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2336
def get_option(self):
2337
return config.Option('foo', help='An integer in SI units.',
2338
from_unicode=config.int_SI_from_store)
2340
def test_convert_invalid(self):
2341
opt = self.get_option()
2342
self.assertConvertInvalid(opt, u'not-a-unit')
2343
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2344
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2345
self.assertConvertInvalid(opt, u'1GG')
2346
self.assertConvertInvalid(opt, u'1Mbb')
2347
self.assertConvertInvalid(opt, u'1MM')
2349
def test_convert_valid(self):
2350
opt = self.get_option()
2351
self.assertConverted(int(5e3), opt, u'5kb')
2352
self.assertConverted(int(5e6), opt, u'5M')
2353
self.assertConverted(int(5e6), opt, u'5MB')
2354
self.assertConverted(int(5e9), opt, u'5g')
2355
self.assertConverted(int(5e9), opt, u'5gB')
2356
self.assertConverted(100, opt, u'100')
2359
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2361
def get_option(self):
2362
return config.ListOption('foo', help='A list.')
2364
def test_convert_invalid(self):
2365
opt = self.get_option()
2366
# We don't even try to convert a list into a list, we only expect
2368
self.assertConvertInvalid(opt, [1])
2369
# No string is invalid as all forms can be converted to a list
2371
def test_convert_valid(self):
2372
opt = self.get_option()
2373
# An empty string is an empty list
2374
self.assertConverted([], opt, '') # Using a bare str() just in case
2375
self.assertConverted([], opt, u'')
2377
self.assertConverted([u'True'], opt, u'True')
2379
self.assertConverted([u'42'], opt, u'42')
2381
self.assertConverted([u'bar'], opt, u'bar')
2384
class TestRegistryOption(tests.TestCase, TestOptionConverterMixin):
2386
def get_option(self, registry):
2387
return config.RegistryOption('foo', registry,
2388
help='A registry option.')
2390
def test_convert_invalid(self):
2391
registry = _mod_registry.Registry()
2392
opt = self.get_option(registry)
2393
self.assertConvertInvalid(opt, [1])
2394
self.assertConvertInvalid(opt, u"notregistered")
2396
def test_convert_valid(self):
2397
registry = _mod_registry.Registry()
2398
registry.register("someval", 1234)
2399
opt = self.get_option(registry)
2400
# Using a bare str() just in case
2401
self.assertConverted(1234, opt, "someval")
2402
self.assertConverted(1234, opt, u'someval')
2403
self.assertConverted(None, opt, None)
2405
def test_help(self):
2406
registry = _mod_registry.Registry()
2407
registry.register("someval", 1234, help="some option")
2408
registry.register("dunno", 1234, help="some other option")
2409
opt = self.get_option(registry)
2411
'A registry option.\n'
2413
'The following values are supported:\n'
2414
' dunno - some other option\n'
2415
' someval - some option\n',
2418
def test_get_help_text(self):
2419
registry = _mod_registry.Registry()
2420
registry.register("someval", 1234, help="some option")
2421
registry.register("dunno", 1234, help="some other option")
2422
opt = self.get_option(registry)
2424
'A registry option.\n'
2426
'The following values are supported:\n'
2427
' dunno - some other option\n'
2428
' someval - some option\n',
2429
opt.get_help_text())
2432
class TestOptionRegistry(tests.TestCase):
2435
super(TestOptionRegistry, self).setUp()
2436
# Always start with an empty registry
2437
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2438
self.registry = config.option_registry
2440
def test_register(self):
2441
opt = config.Option('foo')
2442
self.registry.register(opt)
2443
self.assertIs(opt, self.registry.get('foo'))
2445
def test_registered_help(self):
2446
opt = config.Option('foo', help='A simple option')
2447
self.registry.register(opt)
2448
self.assertEquals('A simple option', self.registry.get_help('foo'))
2450
lazy_option = config.Option('lazy_foo', help='Lazy help')
2452
def test_register_lazy(self):
2453
self.registry.register_lazy('lazy_foo', self.__module__,
2454
'TestOptionRegistry.lazy_option')
2455
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2457
def test_registered_lazy_help(self):
2458
self.registry.register_lazy('lazy_foo', self.__module__,
2459
'TestOptionRegistry.lazy_option')
2460
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2463
class TestRegisteredOptions(tests.TestCase):
2464
"""All registered options should verify some constraints."""
2466
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2467
in config.option_registry.iteritems()]
2470
super(TestRegisteredOptions, self).setUp()
2471
self.registry = config.option_registry
2473
def test_proper_name(self):
2474
# An option should be registered under its own name, this can't be
2475
# checked at registration time for the lazy ones.
2476
self.assertEquals(self.option_name, self.option.name)
2478
def test_help_is_set(self):
2479
option_help = self.registry.get_help(self.option_name)
2480
# Come on, think about the user, he really wants to know what the
2482
self.assertIsNot(None, option_help)
2483
self.assertNotEquals('', option_help)
2486
class TestSection(tests.TestCase):
2488
# FIXME: Parametrize so that all sections produced by Stores run these
2489
# tests -- vila 2011-04-01
2491
def test_get_a_value(self):
2492
a_dict = dict(foo='bar')
2493
section = config.Section('myID', a_dict)
2494
self.assertEquals('bar', section.get('foo'))
2496
def test_get_unknown_option(self):
2498
section = config.Section(None, a_dict)
2499
self.assertEquals('out of thin air',
2500
section.get('foo', 'out of thin air'))
2502
def test_options_is_shared(self):
2504
section = config.Section(None, a_dict)
2505
self.assertIs(a_dict, section.options)
2508
class TestMutableSection(tests.TestCase):
2510
scenarios = [('mutable',
2512
lambda opts: config.MutableSection('myID', opts)},),
2516
a_dict = dict(foo='bar')
2517
section = self.get_section(a_dict)
2518
section.set('foo', 'new_value')
2519
self.assertEquals('new_value', section.get('foo'))
2520
# The change appears in the shared section
2521
self.assertEquals('new_value', a_dict.get('foo'))
2522
# We keep track of the change
2523
self.assertTrue('foo' in section.orig)
2524
self.assertEquals('bar', section.orig.get('foo'))
2526
def test_set_preserve_original_once(self):
2527
a_dict = dict(foo='bar')
2528
section = self.get_section(a_dict)
2529
section.set('foo', 'first_value')
2530
section.set('foo', 'second_value')
2531
# We keep track of the original value
2532
self.assertTrue('foo' in section.orig)
2533
self.assertEquals('bar', section.orig.get('foo'))
2535
def test_remove(self):
2536
a_dict = dict(foo='bar')
2537
section = self.get_section(a_dict)
2538
section.remove('foo')
2539
# We get None for unknown options via the default value
2540
self.assertEquals(None, section.get('foo'))
2541
# Or we just get the default value
2542
self.assertEquals('unknown', section.get('foo', 'unknown'))
2543
self.assertFalse('foo' in section.options)
2544
# We keep track of the deletion
2545
self.assertTrue('foo' in section.orig)
2546
self.assertEquals('bar', section.orig.get('foo'))
2548
def test_remove_new_option(self):
2550
section = self.get_section(a_dict)
2551
section.set('foo', 'bar')
2552
section.remove('foo')
2553
self.assertFalse('foo' in section.options)
2554
# The option didn't exist initially so it we need to keep track of it
2555
# with a special value
2556
self.assertTrue('foo' in section.orig)
2557
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2560
class TestCommandLineStore(tests.TestCase):
2563
super(TestCommandLineStore, self).setUp()
2564
self.store = config.CommandLineStore()
2565
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2567
def get_section(self):
2568
"""Get the unique section for the command line overrides."""
2569
sections = list(self.store.get_sections())
2570
self.assertLength(1, sections)
2571
store, section = sections[0]
2572
self.assertEquals(self.store, store)
2575
def test_no_override(self):
2576
self.store._from_cmdline([])
2577
section = self.get_section()
2578
self.assertLength(0, list(section.iter_option_names()))
2580
def test_simple_override(self):
2581
self.store._from_cmdline(['a=b'])
2582
section = self.get_section()
2583
self.assertEqual('b', section.get('a'))
2585
def test_list_override(self):
2586
opt = config.ListOption('l')
2587
config.option_registry.register(opt)
2588
self.store._from_cmdline(['l=1,2,3'])
2589
val = self.get_section().get('l')
2590
self.assertEqual('1,2,3', val)
2591
# Reminder: lists should be registered as such explicitely, otherwise
2592
# the conversion needs to be done afterwards.
2593
self.assertEqual(['1', '2', '3'],
2594
opt.convert_from_unicode(self.store, val))
2596
def test_multiple_overrides(self):
2597
self.store._from_cmdline(['a=b', 'x=y'])
2598
section = self.get_section()
2599
self.assertEquals('b', section.get('a'))
2600
self.assertEquals('y', section.get('x'))
2602
def test_wrong_syntax(self):
2603
self.assertRaises(errors.BzrCommandError,
2604
self.store._from_cmdline, ['a=b', 'c'])
2606
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2608
scenarios = [(key, {'get_store': builder}) for key, builder
2609
in config.test_store_builder_registry.iteritems()] + [
2610
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2613
store = self.get_store(self)
2614
if type(store) == config.TransportIniFileStore:
2615
raise tests.TestNotApplicable(
2616
"%s is not a concrete Store implementation"
2617
" so it doesn't need an id" % (store.__class__.__name__,))
2618
self.assertIsNot(None, store.id)
2621
class TestStore(tests.TestCaseWithTransport):
2623
def assertSectionContent(self, expected, (store, section)):
2624
"""Assert that some options have the proper values in a section."""
2625
expected_name, expected_options = expected
2626
self.assertEquals(expected_name, section.id)
2629
dict([(k, section.get(k)) for k in expected_options.keys()]))
2632
class TestReadonlyStore(TestStore):
2634
scenarios = [(key, {'get_store': builder}) for key, builder
2635
in config.test_store_builder_registry.iteritems()]
2637
def test_building_delays_load(self):
2638
store = self.get_store(self)
2639
self.assertEquals(False, store.is_loaded())
2640
store._load_from_string('')
2641
self.assertEquals(True, store.is_loaded())
2643
def test_get_no_sections_for_empty(self):
2644
store = self.get_store(self)
2645
store._load_from_string('')
2646
self.assertEquals([], list(store.get_sections()))
2648
def test_get_default_section(self):
2649
store = self.get_store(self)
2650
store._load_from_string('foo=bar')
2651
sections = list(store.get_sections())
2652
self.assertLength(1, sections)
2653
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2655
def test_get_named_section(self):
2656
store = self.get_store(self)
2657
store._load_from_string('[baz]\nfoo=bar')
2658
sections = list(store.get_sections())
2659
self.assertLength(1, sections)
2660
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2662
def test_load_from_string_fails_for_non_empty_store(self):
2663
store = self.get_store(self)
2664
store._load_from_string('foo=bar')
2665
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2668
class TestStoreQuoting(TestStore):
2670
scenarios = [(key, {'get_store': builder}) for key, builder
2671
in config.test_store_builder_registry.iteritems()]
2674
super(TestStoreQuoting, self).setUp()
2675
self.store = self.get_store(self)
2676
# We need a loaded store but any content will do
2677
self.store._load_from_string('')
2679
def assertIdempotent(self, s):
2680
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2682
What matters here is that option values, as they appear in a store, can
2683
be safely round-tripped out of the store and back.
2685
:param s: A string, quoted if required.
2687
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2688
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2690
def test_empty_string(self):
2691
if isinstance(self.store, config.IniFileStore):
2692
# configobj._quote doesn't handle empty values
2693
self.assertRaises(AssertionError,
2694
self.assertIdempotent, '')
2696
self.assertIdempotent('')
2697
# But quoted empty strings are ok
2698
self.assertIdempotent('""')
2700
def test_embedded_spaces(self):
2701
self.assertIdempotent('" a b c "')
2703
def test_embedded_commas(self):
2704
self.assertIdempotent('" a , b c "')
2706
def test_simple_comma(self):
2707
if isinstance(self.store, config.IniFileStore):
2708
# configobj requires that lists are special-cased
2709
self.assertRaises(AssertionError,
2710
self.assertIdempotent, ',')
2712
self.assertIdempotent(',')
2713
# When a single comma is required, quoting is also required
2714
self.assertIdempotent('","')
2716
def test_list(self):
2717
if isinstance(self.store, config.IniFileStore):
2718
# configobj requires that lists are special-cased
2719
self.assertRaises(AssertionError,
2720
self.assertIdempotent, 'a,b')
2722
self.assertIdempotent('a,b')
2725
class TestDictFromStore(tests.TestCase):
2727
def test_unquote_not_string(self):
2728
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2729
value = conf.get('a_section')
2730
# Urgh, despite 'conf' asking for the no-name section, we get the
2731
# content of another section as a dict o_O
2732
self.assertEquals({'a': '1'}, value)
2733
unquoted = conf.store.unquote(value)
2734
# Which cannot be unquoted but shouldn't crash either (the use cases
2735
# are getting the value or displaying it. In the later case, '%s' will
2737
self.assertEquals({'a': '1'}, unquoted)
2738
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2741
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2742
"""Simulate loading a config store with content of various encodings.
2744
All files produced by bzr are in utf8 content.
2746
Users may modify them manually and end up with a file that can't be
2747
loaded. We need to issue proper error messages in this case.
2750
invalid_utf8_char = '\xff'
2752
def test_load_utf8(self):
2753
"""Ensure we can load an utf8-encoded file."""
2754
t = self.get_transport()
2755
# From http://pad.lv/799212
2756
unicode_user = u'b\N{Euro Sign}ar'
2757
unicode_content = u'user=%s' % (unicode_user,)
2758
utf8_content = unicode_content.encode('utf8')
2759
# Store the raw content in the config file
2760
t.put_bytes('foo.conf', utf8_content)
2761
store = config.TransportIniFileStore(t, 'foo.conf')
2763
stack = config.Stack([store.get_sections], store)
2764
self.assertEquals(unicode_user, stack.get('user'))
2766
def test_load_non_ascii(self):
2767
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2768
t = self.get_transport()
2769
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2770
store = config.TransportIniFileStore(t, 'foo.conf')
2771
self.assertRaises(errors.ConfigContentError, store.load)
2773
def test_load_erroneous_content(self):
2774
"""Ensure we display a proper error on content that can't be parsed."""
2775
t = self.get_transport()
2776
t.put_bytes('foo.conf', '[open_section\n')
2777
store = config.TransportIniFileStore(t, 'foo.conf')
2778
self.assertRaises(errors.ParseConfigError, store.load)
2780
def test_load_permission_denied(self):
2781
"""Ensure we get warned when trying to load an inaccessible file."""
2784
warnings.append(args[0] % args[1:])
2785
self.overrideAttr(trace, 'warning', warning)
2787
t = self.get_transport()
2789
def get_bytes(relpath):
2790
raise errors.PermissionDenied(relpath, "")
2791
t.get_bytes = get_bytes
2792
store = config.TransportIniFileStore(t, 'foo.conf')
2793
self.assertRaises(errors.PermissionDenied, store.load)
2796
[u'Permission denied while trying to load configuration store %s.'
2797
% store.external_url()])
2800
class TestIniConfigContent(tests.TestCaseWithTransport):
2801
"""Simulate loading a IniBasedConfig with content of various encodings.
2803
All files produced by bzr are in utf8 content.
2805
Users may modify them manually and end up with a file that can't be
2806
loaded. We need to issue proper error messages in this case.
2809
invalid_utf8_char = '\xff'
2811
def test_load_utf8(self):
2812
"""Ensure we can load an utf8-encoded file."""
2813
# From http://pad.lv/799212
2814
unicode_user = u'b\N{Euro Sign}ar'
2815
unicode_content = u'user=%s' % (unicode_user,)
2816
utf8_content = unicode_content.encode('utf8')
2817
# Store the raw content in the config file
2818
with open('foo.conf', 'wb') as f:
2819
f.write(utf8_content)
2820
conf = config.IniBasedConfig(file_name='foo.conf')
2821
self.assertEquals(unicode_user, conf.get_user_option('user'))
2823
def test_load_badly_encoded_content(self):
2824
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2825
with open('foo.conf', 'wb') as f:
2826
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2827
conf = config.IniBasedConfig(file_name='foo.conf')
2828
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2830
def test_load_erroneous_content(self):
2831
"""Ensure we display a proper error on content that can't be parsed."""
2832
with open('foo.conf', 'wb') as f:
2833
f.write('[open_section\n')
2834
conf = config.IniBasedConfig(file_name='foo.conf')
2835
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2838
class TestMutableStore(TestStore):
2840
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2841
in config.test_store_builder_registry.iteritems()]
2844
super(TestMutableStore, self).setUp()
2845
self.transport = self.get_transport()
2847
def has_store(self, store):
2848
store_basename = urlutils.relative_url(self.transport.external_url(),
2849
store.external_url())
2850
return self.transport.has(store_basename)
2852
def test_save_empty_creates_no_file(self):
2853
# FIXME: There should be a better way than relying on the test
2854
# parametrization to identify branch.conf -- vila 2011-0526
2855
if self.store_id in ('branch', 'remote_branch'):
2856
raise tests.TestNotApplicable(
2857
'branch.conf is *always* created when a branch is initialized')
2858
store = self.get_store(self)
2860
self.assertEquals(False, self.has_store(store))
2862
def test_mutable_section_shared(self):
2863
store = self.get_store(self)
2864
store._load_from_string('foo=bar\n')
2865
# FIXME: There should be a better way than relying on the test
2866
# parametrization to identify branch.conf -- vila 2011-0526
2867
if self.store_id in ('branch', 'remote_branch'):
2868
# branch stores requires write locked branches
2869
self.addCleanup(store.branch.lock_write().unlock)
2870
section1 = store.get_mutable_section(None)
2871
section2 = store.get_mutable_section(None)
2872
# If we get different sections, different callers won't share the
2874
self.assertIs(section1, section2)
2876
def test_save_emptied_succeeds(self):
2877
store = self.get_store(self)
2878
store._load_from_string('foo=bar\n')
2879
# FIXME: There should be a better way than relying on the test
2880
# parametrization to identify branch.conf -- vila 2011-0526
2881
if self.store_id in ('branch', 'remote_branch'):
2882
# branch stores requires write locked branches
2883
self.addCleanup(store.branch.lock_write().unlock)
2884
section = store.get_mutable_section(None)
2885
section.remove('foo')
2887
self.assertEquals(True, self.has_store(store))
2888
modified_store = self.get_store(self)
2889
sections = list(modified_store.get_sections())
2890
self.assertLength(0, sections)
2892
def test_save_with_content_succeeds(self):
2893
# FIXME: There should be a better way than relying on the test
2894
# parametrization to identify branch.conf -- vila 2011-0526
2895
if self.store_id in ('branch', 'remote_branch'):
2896
raise tests.TestNotApplicable(
2897
'branch.conf is *always* created when a branch is initialized')
2898
store = self.get_store(self)
2899
store._load_from_string('foo=bar\n')
2900
self.assertEquals(False, self.has_store(store))
2902
self.assertEquals(True, self.has_store(store))
2903
modified_store = self.get_store(self)
2904
sections = list(modified_store.get_sections())
2905
self.assertLength(1, sections)
2906
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2908
def test_set_option_in_empty_store(self):
2909
store = self.get_store(self)
2910
# FIXME: There should be a better way than relying on the test
2911
# parametrization to identify branch.conf -- vila 2011-0526
2912
if self.store_id in ('branch', 'remote_branch'):
2913
# branch stores requires write locked branches
2914
self.addCleanup(store.branch.lock_write().unlock)
2915
section = store.get_mutable_section(None)
2916
section.set('foo', 'bar')
2918
modified_store = self.get_store(self)
2919
sections = list(modified_store.get_sections())
2920
self.assertLength(1, sections)
2921
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2923
def test_set_option_in_default_section(self):
2924
store = self.get_store(self)
2925
store._load_from_string('')
2926
# FIXME: There should be a better way than relying on the test
2927
# parametrization to identify branch.conf -- vila 2011-0526
2928
if self.store_id in ('branch', 'remote_branch'):
2929
# branch stores requires write locked branches
2930
self.addCleanup(store.branch.lock_write().unlock)
2931
section = store.get_mutable_section(None)
2932
section.set('foo', 'bar')
2934
modified_store = self.get_store(self)
2935
sections = list(modified_store.get_sections())
2936
self.assertLength(1, sections)
2937
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2939
def test_set_option_in_named_section(self):
2940
store = self.get_store(self)
2941
store._load_from_string('')
2942
# FIXME: There should be a better way than relying on the test
2943
# parametrization to identify branch.conf -- vila 2011-0526
2944
if self.store_id in ('branch', 'remote_branch'):
2945
# branch stores requires write locked branches
2946
self.addCleanup(store.branch.lock_write().unlock)
2947
section = store.get_mutable_section('baz')
2948
section.set('foo', 'bar')
2950
modified_store = self.get_store(self)
2951
sections = list(modified_store.get_sections())
2952
self.assertLength(1, sections)
2953
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2955
def test_load_hook(self):
2956
# First, we need to ensure that the store exists
2957
store = self.get_store(self)
2958
# FIXME: There should be a better way than relying on the test
2959
# parametrization to identify branch.conf -- vila 2011-0526
2960
if self.store_id in ('branch', 'remote_branch'):
2961
# branch stores requires write locked branches
2962
self.addCleanup(store.branch.lock_write().unlock)
2963
section = store.get_mutable_section('baz')
2964
section.set('foo', 'bar')
2966
# Now we can try to load it
2967
store = self.get_store(self)
2971
config.ConfigHooks.install_named_hook('load', hook, None)
2972
self.assertLength(0, calls)
2974
self.assertLength(1, calls)
2975
self.assertEquals((store,), calls[0])
2977
def test_save_hook(self):
2981
config.ConfigHooks.install_named_hook('save', hook, None)
2982
self.assertLength(0, calls)
2983
store = self.get_store(self)
2984
# FIXME: There should be a better way than relying on the test
2985
# parametrization to identify branch.conf -- vila 2011-0526
2986
if self.store_id in ('branch', 'remote_branch'):
2987
# branch stores requires write locked branches
2988
self.addCleanup(store.branch.lock_write().unlock)
2989
section = store.get_mutable_section('baz')
2990
section.set('foo', 'bar')
2992
self.assertLength(1, calls)
2993
self.assertEquals((store,), calls[0])
2995
def test_set_mark_dirty(self):
2996
stack = config.MemoryStack('')
2997
self.assertLength(0, stack.store.dirty_sections)
2998
stack.set('foo', 'baz')
2999
self.assertLength(1, stack.store.dirty_sections)
3000
self.assertTrue(stack.store._need_saving())
3002
def test_remove_mark_dirty(self):
3003
stack = config.MemoryStack('foo=bar')
3004
self.assertLength(0, stack.store.dirty_sections)
3006
self.assertLength(1, stack.store.dirty_sections)
3007
self.assertTrue(stack.store._need_saving())
3010
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3011
"""Tests that config changes are kept in memory and saved on-demand."""
3014
super(TestStoreSaveChanges, self).setUp()
3015
self.transport = self.get_transport()
3016
# Most of the tests involve two stores pointing to the same persistent
3017
# storage to observe the effects of concurrent changes
3018
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3019
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3022
self.warnings.append(args[0] % args[1:])
3023
self.overrideAttr(trace, 'warning', warning)
3025
def has_store(self, store):
3026
store_basename = urlutils.relative_url(self.transport.external_url(),
3027
store.external_url())
3028
return self.transport.has(store_basename)
3030
def get_stack(self, store):
3031
# Any stack will do as long as it uses the right store, just a single
3032
# no-name section is enough
3033
return config.Stack([store.get_sections], store)
3035
def test_no_changes_no_save(self):
3036
s = self.get_stack(self.st1)
3037
s.store.save_changes()
3038
self.assertEquals(False, self.has_store(self.st1))
3040
def test_unrelated_concurrent_update(self):
3041
s1 = self.get_stack(self.st1)
3042
s2 = self.get_stack(self.st2)
3043
s1.set('foo', 'bar')
3044
s2.set('baz', 'quux')
3046
# Changes don't propagate magically
3047
self.assertEquals(None, s1.get('baz'))
3048
s2.store.save_changes()
3049
self.assertEquals('quux', s2.get('baz'))
3050
# Changes are acquired when saving
3051
self.assertEquals('bar', s2.get('foo'))
3052
# Since there is no overlap, no warnings are emitted
3053
self.assertLength(0, self.warnings)
3055
def test_concurrent_update_modified(self):
3056
s1 = self.get_stack(self.st1)
3057
s2 = self.get_stack(self.st2)
3058
s1.set('foo', 'bar')
3059
s2.set('foo', 'baz')
3062
s2.store.save_changes()
3063
self.assertEquals('baz', s2.get('foo'))
3064
# But the user get a warning
3065
self.assertLength(1, self.warnings)
3066
warning = self.warnings[0]
3067
self.assertStartsWith(warning, 'Option foo in section None')
3068
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3069
' The baz value will be saved.')
3071
def test_concurrent_deletion(self):
3072
self.st1._load_from_string('foo=bar')
3074
s1 = self.get_stack(self.st1)
3075
s2 = self.get_stack(self.st2)
3078
s1.store.save_changes()
3080
self.assertLength(0, self.warnings)
3081
s2.store.save_changes()
3083
self.assertLength(1, self.warnings)
3084
warning = self.warnings[0]
3085
self.assertStartsWith(warning, 'Option foo in section None')
3086
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3087
' The <DELETED> value will be saved.')
3090
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3092
def get_store(self):
3093
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3095
def test_get_quoted_string(self):
3096
store = self.get_store()
3097
store._load_from_string('foo= " abc "')
3098
stack = config.Stack([store.get_sections])
3099
self.assertEquals(' abc ', stack.get('foo'))
3101
def test_set_quoted_string(self):
3102
store = self.get_store()
3103
stack = config.Stack([store.get_sections], store)
3104
stack.set('foo', ' a b c ')
3106
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3109
class TestTransportIniFileStore(TestStore):
3111
def test_loading_unknown_file_fails(self):
3112
store = config.TransportIniFileStore(self.get_transport(),
3114
self.assertRaises(errors.NoSuchFile, store.load)
3116
def test_invalid_content(self):
3117
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3118
self.assertEquals(False, store.is_loaded())
3119
exc = self.assertRaises(
3120
errors.ParseConfigError, store._load_from_string,
3121
'this is invalid !')
3122
self.assertEndsWith(exc.filename, 'foo.conf')
3123
# And the load failed
3124
self.assertEquals(False, store.is_loaded())
3126
def test_get_embedded_sections(self):
3127
# A more complicated example (which also shows that section names and
3128
# option names share the same name space...)
3129
# FIXME: This should be fixed by forbidding dicts as values ?
3130
# -- vila 2011-04-05
3131
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3132
store._load_from_string('''
3136
foo_in_DEFAULT=foo_DEFAULT
3144
sections = list(store.get_sections())
3145
self.assertLength(4, sections)
3146
# The default section has no name.
3147
# List values are provided as strings and need to be explicitly
3148
# converted by specifying from_unicode=list_from_store at option
3150
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3152
self.assertSectionContent(
3153
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3154
self.assertSectionContent(
3155
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3156
# sub sections are provided as embedded dicts.
3157
self.assertSectionContent(
3158
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3162
class TestLockableIniFileStore(TestStore):
3164
def test_create_store_in_created_dir(self):
3165
self.assertPathDoesNotExist('dir')
3166
t = self.get_transport('dir/subdir')
3167
store = config.LockableIniFileStore(t, 'foo.conf')
3168
store.get_mutable_section(None).set('foo', 'bar')
3170
self.assertPathExists('dir/subdir')
3173
class TestConcurrentStoreUpdates(TestStore):
3174
"""Test that Stores properly handle conccurent updates.
3176
New Store implementation may fail some of these tests but until such
3177
implementations exist it's hard to properly filter them from the scenarios
3178
applied here. If you encounter such a case, contact the bzr devs.
3181
scenarios = [(key, {'get_stack': builder}) for key, builder
3182
in config.test_stack_builder_registry.iteritems()]
3185
super(TestConcurrentStoreUpdates, self).setUp()
3186
self.stack = self.get_stack(self)
3187
if not isinstance(self.stack, config._CompatibleStack):
3188
raise tests.TestNotApplicable(
3189
'%s is not meant to be compatible with the old config design'
3191
self.stack.set('one', '1')
3192
self.stack.set('two', '2')
3194
self.stack.store.save()
3196
def test_simple_read_access(self):
3197
self.assertEquals('1', self.stack.get('one'))
3199
def test_simple_write_access(self):
3200
self.stack.set('one', 'one')
3201
self.assertEquals('one', self.stack.get('one'))
3203
def test_listen_to_the_last_speaker(self):
3205
c2 = self.get_stack(self)
3206
c1.set('one', 'ONE')
3207
c2.set('two', 'TWO')
3208
self.assertEquals('ONE', c1.get('one'))
3209
self.assertEquals('TWO', c2.get('two'))
3210
# The second update respect the first one
3211
self.assertEquals('ONE', c2.get('one'))
3213
def test_last_speaker_wins(self):
3214
# If the same config is not shared, the same variable modified twice
3215
# can only see a single result.
3217
c2 = self.get_stack(self)
3220
self.assertEquals('c2', c2.get('one'))
3221
# The first modification is still available until another refresh
3223
self.assertEquals('c1', c1.get('one'))
3224
c1.set('two', 'done')
3225
self.assertEquals('c2', c1.get('one'))
3227
def test_writes_are_serialized(self):
3229
c2 = self.get_stack(self)
3231
# We spawn a thread that will pause *during* the config saving.
3232
before_writing = threading.Event()
3233
after_writing = threading.Event()
3234
writing_done = threading.Event()
3235
c1_save_without_locking_orig = c1.store.save_without_locking
3236
def c1_save_without_locking():
3237
before_writing.set()
3238
c1_save_without_locking_orig()
3239
# The lock is held. We wait for the main thread to decide when to
3241
after_writing.wait()
3242
c1.store.save_without_locking = c1_save_without_locking
3246
t1 = threading.Thread(target=c1_set)
3247
# Collect the thread after the test
3248
self.addCleanup(t1.join)
3249
# Be ready to unblock the thread if the test goes wrong
3250
self.addCleanup(after_writing.set)
3252
before_writing.wait()
3253
self.assertRaises(errors.LockContention,
3254
c2.set, 'one', 'c2')
3255
self.assertEquals('c1', c1.get('one'))
3256
# Let the lock be released
3260
self.assertEquals('c2', c2.get('one'))
3262
def test_read_while_writing(self):
3264
# We spawn a thread that will pause *during* the write
3265
ready_to_write = threading.Event()
3266
do_writing = threading.Event()
3267
writing_done = threading.Event()
3268
# We override the _save implementation so we know the store is locked
3269
c1_save_without_locking_orig = c1.store.save_without_locking
3270
def c1_save_without_locking():
3271
ready_to_write.set()
3272
# The lock is held. We wait for the main thread to decide when to
3275
c1_save_without_locking_orig()
3277
c1.store.save_without_locking = c1_save_without_locking
3280
t1 = threading.Thread(target=c1_set)
3281
# Collect the thread after the test
3282
self.addCleanup(t1.join)
3283
# Be ready to unblock the thread if the test goes wrong
3284
self.addCleanup(do_writing.set)
3286
# Ensure the thread is ready to write
3287
ready_to_write.wait()
3288
self.assertEquals('c1', c1.get('one'))
3289
# If we read during the write, we get the old value
3290
c2 = self.get_stack(self)
3291
self.assertEquals('1', c2.get('one'))
3292
# Let the writing occur and ensure it occurred
3295
# Now we get the updated value
3296
c3 = self.get_stack(self)
3297
self.assertEquals('c1', c3.get('one'))
3299
# FIXME: It may be worth looking into removing the lock dir when it's not
3300
# needed anymore and look at possible fallouts for concurrent lockers. This
3301
# will matter if/when we use config files outside of bazaar directories
3302
# (.bazaar or .bzr) -- vila 20110-04-111
3305
class TestSectionMatcher(TestStore):
3307
scenarios = [('location', {'matcher': config.LocationMatcher}),
3308
('id', {'matcher': config.NameMatcher}),]
3311
super(TestSectionMatcher, self).setUp()
3312
# Any simple store is good enough
3313
self.get_store = config.test_store_builder_registry.get('configobj')
3315
def test_no_matches_for_empty_stores(self):
3316
store = self.get_store(self)
3317
store._load_from_string('')
3318
matcher = self.matcher(store, '/bar')
3319
self.assertEquals([], list(matcher.get_sections()))
3321
def test_build_doesnt_load_store(self):
3322
store = self.get_store(self)
3323
matcher = self.matcher(store, '/bar')
3324
self.assertFalse(store.is_loaded())
3327
class TestLocationSection(tests.TestCase):
3329
def get_section(self, options, extra_path):
3330
section = config.Section('foo', options)
3331
return config.LocationSection(section, extra_path)
3333
def test_simple_option(self):
3334
section = self.get_section({'foo': 'bar'}, '')
3335
self.assertEquals('bar', section.get('foo'))
3337
def test_option_with_extra_path(self):
3338
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3340
self.assertEquals('bar/baz', section.get('foo'))
3342
def test_invalid_policy(self):
3343
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3345
# invalid policies are ignored
3346
self.assertEquals('bar', section.get('foo'))
3349
class TestLocationMatcher(TestStore):
3352
super(TestLocationMatcher, self).setUp()
3353
# Any simple store is good enough
3354
self.get_store = config.test_store_builder_registry.get('configobj')
3356
def test_unrelated_section_excluded(self):
3357
store = self.get_store(self)
3358
store._load_from_string('''
3366
section=/foo/bar/baz
3370
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3372
[section.id for _, section in store.get_sections()])
3373
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3374
sections = [section for _, section in matcher.get_sections()]
3375
self.assertEquals(['/foo/bar', '/foo'],
3376
[section.id for section in sections])
3377
self.assertEquals(['quux', 'bar/quux'],
3378
[section.extra_path for section in sections])
3380
def test_more_specific_sections_first(self):
3381
store = self.get_store(self)
3382
store._load_from_string('''
3388
self.assertEquals(['/foo', '/foo/bar'],
3389
[section.id for _, section in store.get_sections()])
3390
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3391
sections = [section for _, section in matcher.get_sections()]
3392
self.assertEquals(['/foo/bar', '/foo'],
3393
[section.id for section in sections])
3394
self.assertEquals(['baz', 'bar/baz'],
3395
[section.extra_path for section in sections])
3397
def test_appendpath_in_no_name_section(self):
3398
# It's a bit weird to allow appendpath in a no-name section, but
3399
# someone may found a use for it
3400
store = self.get_store(self)
3401
store._load_from_string('''
3403
foo:policy = appendpath
3405
matcher = config.LocationMatcher(store, 'dir/subdir')
3406
sections = list(matcher.get_sections())
3407
self.assertLength(1, sections)
3408
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3410
def test_file_urls_are_normalized(self):
3411
store = self.get_store(self)
3412
if sys.platform == 'win32':
3413
expected_url = 'file:///C:/dir/subdir'
3414
expected_location = 'C:/dir/subdir'
3416
expected_url = 'file:///dir/subdir'
3417
expected_location = '/dir/subdir'
3418
matcher = config.LocationMatcher(store, expected_url)
3419
self.assertEquals(expected_location, matcher.location)
3422
class TestStartingPathMatcher(TestStore):
3425
super(TestStartingPathMatcher, self).setUp()
3426
# Any simple store is good enough
3427
self.store = config.IniFileStore()
3429
def assertSectionIDs(self, expected, location, content):
3430
self.store._load_from_string(content)
3431
matcher = config.StartingPathMatcher(self.store, location)
3432
sections = list(matcher.get_sections())
3433
self.assertLength(len(expected), sections)
3434
self.assertEqual(expected, [section.id for _, section in sections])
3437
def test_empty(self):
3438
self.assertSectionIDs([], self.get_url(), '')
3440
def test_url_vs_local_paths(self):
3441
# The matcher location is an url and the section names are local paths
3442
sections = self.assertSectionIDs(['/foo/bar', '/foo'],
3443
'file:///foo/bar/baz', '''\
3448
def test_local_path_vs_url(self):
3449
# The matcher location is a local path and the section names are urls
3450
sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3451
'/foo/bar/baz', '''\
3457
def test_no_name_section_included_when_present(self):
3458
# Note that other tests will cover the case where the no-name section
3459
# is empty and as such, not included.
3460
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3461
'/foo/bar/baz', '''\
3462
option = defined so the no-name section exists
3466
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3467
[s.locals['relpath'] for _, s in sections])
3469
def test_order_reversed(self):
3470
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3475
def test_unrelated_section_excluded(self):
3476
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3482
def test_glob_included(self):
3483
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3484
'/foo/bar/baz', '''\
3490
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3491
# nothing really is... as far using {relpath} to append it to something
3492
# else, this seems good enough though.
3493
self.assertEquals(['', 'baz', 'bar/baz'],
3494
[s.locals['relpath'] for _, s in sections])
3496
def test_respect_order(self):
3497
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3498
'/foo/bar/baz', '''\
3506
class TestNameMatcher(TestStore):
3509
super(TestNameMatcher, self).setUp()
3510
self.matcher = config.NameMatcher
3511
# Any simple store is good enough
3512
self.get_store = config.test_store_builder_registry.get('configobj')
3514
def get_matching_sections(self, name):
3515
store = self.get_store(self)
3516
store._load_from_string('''
3524
matcher = self.matcher(store, name)
3525
return list(matcher.get_sections())
3527
def test_matching(self):
3528
sections = self.get_matching_sections('foo')
3529
self.assertLength(1, sections)
3530
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3532
def test_not_matching(self):
3533
sections = self.get_matching_sections('baz')
3534
self.assertLength(0, sections)
3537
class TestBaseStackGet(tests.TestCase):
3540
super(TestBaseStackGet, self).setUp()
3541
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3543
def test_get_first_definition(self):
3544
store1 = config.IniFileStore()
3545
store1._load_from_string('foo=bar')
3546
store2 = config.IniFileStore()
3547
store2._load_from_string('foo=baz')
3548
conf = config.Stack([store1.get_sections, store2.get_sections])
3549
self.assertEquals('bar', conf.get('foo'))
3551
def test_get_with_registered_default_value(self):
3552
config.option_registry.register(config.Option('foo', default='bar'))
3553
conf_stack = config.Stack([])
3554
self.assertEquals('bar', conf_stack.get('foo'))
3556
def test_get_without_registered_default_value(self):
3557
config.option_registry.register(config.Option('foo'))
3558
conf_stack = config.Stack([])
3559
self.assertEquals(None, conf_stack.get('foo'))
3561
def test_get_without_default_value_for_not_registered(self):
3562
conf_stack = config.Stack([])
3563
self.assertEquals(None, conf_stack.get('foo'))
3565
def test_get_for_empty_section_callable(self):
3566
conf_stack = config.Stack([lambda : []])
3567
self.assertEquals(None, conf_stack.get('foo'))
3569
def test_get_for_broken_callable(self):
3570
# Trying to use and invalid callable raises an exception on first use
3571
conf_stack = config.Stack([object])
3572
self.assertRaises(TypeError, conf_stack.get, 'foo')
3575
class TestStackWithSimpleStore(tests.TestCase):
3578
super(TestStackWithSimpleStore, self).setUp()
3579
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3580
self.registry = config.option_registry
3582
def get_conf(self, content=None):
3583
return config.MemoryStack(content)
3585
def test_override_value_from_env(self):
3586
self.overrideEnv('FOO', None)
3587
self.registry.register(
3588
config.Option('foo', default='bar', override_from_env=['FOO']))
3589
self.overrideEnv('FOO', 'quux')
3590
# Env variable provides a default taking over the option one
3591
conf = self.get_conf('foo=store')
3592
self.assertEquals('quux', conf.get('foo'))
3594
def test_first_override_value_from_env_wins(self):
3595
self.overrideEnv('NO_VALUE', None)
3596
self.overrideEnv('FOO', None)
3597
self.overrideEnv('BAZ', None)
3598
self.registry.register(
3599
config.Option('foo', default='bar',
3600
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3601
self.overrideEnv('FOO', 'foo')
3602
self.overrideEnv('BAZ', 'baz')
3603
# The first env var set wins
3604
conf = self.get_conf('foo=store')
3605
self.assertEquals('foo', conf.get('foo'))
3608
class TestMemoryStack(tests.TestCase):
3611
conf = config.MemoryStack('foo=bar')
3612
self.assertEquals('bar', conf.get('foo'))
3615
conf = config.MemoryStack('foo=bar')
3616
conf.set('foo', 'baz')
3617
self.assertEquals('baz', conf.get('foo'))
3619
def test_no_content(self):
3620
conf = config.MemoryStack()
3621
# No content means no loading
3622
self.assertFalse(conf.store.is_loaded())
3623
self.assertRaises(NotImplementedError, conf.get, 'foo')
3624
# But a content can still be provided
3625
conf.store._load_from_string('foo=bar')
3626
self.assertEquals('bar', conf.get('foo'))
3629
class TestStackIterSections(tests.TestCase):
3631
def test_empty_stack(self):
3632
conf = config.Stack([])
3633
sections = list(conf.iter_sections())
3634
self.assertLength(0, sections)
3636
def test_empty_store(self):
3637
store = config.IniFileStore()
3638
store._load_from_string('')
3639
conf = config.Stack([store.get_sections])
3640
sections = list(conf.iter_sections())
3641
self.assertLength(0, sections)
3643
def test_simple_store(self):
3644
store = config.IniFileStore()
3645
store._load_from_string('foo=bar')
3646
conf = config.Stack([store.get_sections])
3647
tuples = list(conf.iter_sections())
3648
self.assertLength(1, tuples)
3649
(found_store, found_section) = tuples[0]
3650
self.assertIs(store, found_store)
3652
def test_two_stores(self):
3653
store1 = config.IniFileStore()
3654
store1._load_from_string('foo=bar')
3655
store2 = config.IniFileStore()
3656
store2._load_from_string('bar=qux')
3657
conf = config.Stack([store1.get_sections, store2.get_sections])
3658
tuples = list(conf.iter_sections())
3659
self.assertLength(2, tuples)
3660
self.assertIs(store1, tuples[0][0])
3661
self.assertIs(store2, tuples[1][0])
3664
class TestStackWithTransport(tests.TestCaseWithTransport):
3666
scenarios = [(key, {'get_stack': builder}) for key, builder
3667
in config.test_stack_builder_registry.iteritems()]
3670
class TestConcreteStacks(TestStackWithTransport):
3672
def test_build_stack(self):
3673
# Just a smoke test to help debug builders
3674
stack = self.get_stack(self)
3677
class TestStackGet(TestStackWithTransport):
3680
super(TestStackGet, self).setUp()
3681
self.conf = self.get_stack(self)
3683
def test_get_for_empty_stack(self):
3684
self.assertEquals(None, self.conf.get('foo'))
3686
def test_get_hook(self):
3687
self.conf.set('foo', 'bar')
3691
config.ConfigHooks.install_named_hook('get', hook, None)
3692
self.assertLength(0, calls)
3693
value = self.conf.get('foo')
3694
self.assertEquals('bar', value)
3695
self.assertLength(1, calls)
3696
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3699
class TestStackGetWithConverter(tests.TestCase):
3702
super(TestStackGetWithConverter, self).setUp()
3703
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3704
self.registry = config.option_registry
3706
def get_conf(self, content=None):
3707
return config.MemoryStack(content)
3709
def register_bool_option(self, name, default=None, default_from_env=None):
3710
b = config.Option(name, help='A boolean.',
3711
default=default, default_from_env=default_from_env,
3712
from_unicode=config.bool_from_store)
3713
self.registry.register(b)
3715
def test_get_default_bool_None(self):
3716
self.register_bool_option('foo')
3717
conf = self.get_conf('')
3718
self.assertEquals(None, conf.get('foo'))
3720
def test_get_default_bool_True(self):
3721
self.register_bool_option('foo', u'True')
3722
conf = self.get_conf('')
3723
self.assertEquals(True, conf.get('foo'))
3725
def test_get_default_bool_False(self):
3726
self.register_bool_option('foo', False)
3727
conf = self.get_conf('')
3728
self.assertEquals(False, conf.get('foo'))
3730
def test_get_default_bool_False_as_string(self):
3731
self.register_bool_option('foo', u'False')
3732
conf = self.get_conf('')
3733
self.assertEquals(False, conf.get('foo'))
3735
def test_get_default_bool_from_env_converted(self):
3736
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3737
self.overrideEnv('FOO', 'False')
3738
conf = self.get_conf('')
3739
self.assertEquals(False, conf.get('foo'))
3741
def test_get_default_bool_when_conversion_fails(self):
3742
self.register_bool_option('foo', default='True')
3743
conf = self.get_conf('foo=invalid boolean')
3744
self.assertEquals(True, conf.get('foo'))
3746
def register_integer_option(self, name,
3747
default=None, default_from_env=None):
3748
i = config.Option(name, help='An integer.',
3749
default=default, default_from_env=default_from_env,
3750
from_unicode=config.int_from_store)
3751
self.registry.register(i)
3753
def test_get_default_integer_None(self):
3754
self.register_integer_option('foo')
3755
conf = self.get_conf('')
3756
self.assertEquals(None, conf.get('foo'))
3758
def test_get_default_integer(self):
3759
self.register_integer_option('foo', 42)
3760
conf = self.get_conf('')
3761
self.assertEquals(42, conf.get('foo'))
3763
def test_get_default_integer_as_string(self):
3764
self.register_integer_option('foo', u'42')
3765
conf = self.get_conf('')
3766
self.assertEquals(42, conf.get('foo'))
3768
def test_get_default_integer_from_env(self):
3769
self.register_integer_option('foo', default_from_env=['FOO'])
3770
self.overrideEnv('FOO', '18')
3771
conf = self.get_conf('')
3772
self.assertEquals(18, conf.get('foo'))
3774
def test_get_default_integer_when_conversion_fails(self):
3775
self.register_integer_option('foo', default='12')
3776
conf = self.get_conf('foo=invalid integer')
3777
self.assertEquals(12, conf.get('foo'))
3779
def register_list_option(self, name, default=None, default_from_env=None):
3780
l = config.ListOption(name, help='A list.', default=default,
3781
default_from_env=default_from_env)
3782
self.registry.register(l)
3784
def test_get_default_list_None(self):
3785
self.register_list_option('foo')
3786
conf = self.get_conf('')
3787
self.assertEquals(None, conf.get('foo'))
3789
def test_get_default_list_empty(self):
3790
self.register_list_option('foo', '')
3791
conf = self.get_conf('')
3792
self.assertEquals([], conf.get('foo'))
3794
def test_get_default_list_from_env(self):
3795
self.register_list_option('foo', default_from_env=['FOO'])
3796
self.overrideEnv('FOO', '')
3797
conf = self.get_conf('')
3798
self.assertEquals([], conf.get('foo'))
3800
def test_get_with_list_converter_no_item(self):
3801
self.register_list_option('foo', None)
3802
conf = self.get_conf('foo=,')
3803
self.assertEquals([], conf.get('foo'))
3805
def test_get_with_list_converter_many_items(self):
3806
self.register_list_option('foo', None)
3807
conf = self.get_conf('foo=m,o,r,e')
3808
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3810
def test_get_with_list_converter_embedded_spaces_many_items(self):
3811
self.register_list_option('foo', None)
3812
conf = self.get_conf('foo=" bar", "baz "')
3813
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3815
def test_get_with_list_converter_stripped_spaces_many_items(self):
3816
self.register_list_option('foo', None)
3817
conf = self.get_conf('foo= bar , baz ')
3818
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3821
class TestIterOptionRefs(tests.TestCase):
3822
"""iter_option_refs is a bit unusual, document some cases."""
3824
def assertRefs(self, expected, string):
3825
self.assertEquals(expected, list(config.iter_option_refs(string)))
3827
def test_empty(self):
3828
self.assertRefs([(False, '')], '')
3830
def test_no_refs(self):
3831
self.assertRefs([(False, 'foo bar')], 'foo bar')
3833
def test_single_ref(self):
3834
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3836
def test_broken_ref(self):
3837
self.assertRefs([(False, '{foo')], '{foo')
3839
def test_embedded_ref(self):
3840
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3843
def test_two_refs(self):
3844
self.assertRefs([(False, ''), (True, '{foo}'),
3845
(False, ''), (True, '{bar}'),
3849
def test_newline_in_refs_are_not_matched(self):
3850
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3853
class TestStackExpandOptions(tests.TestCaseWithTransport):
3856
super(TestStackExpandOptions, self).setUp()
3857
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3858
self.registry = config.option_registry
3859
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3860
self.conf = config.Stack([store.get_sections], store)
3862
def assertExpansion(self, expected, string, env=None):
3863
self.assertEquals(expected, self.conf.expand_options(string, env))
3865
def test_no_expansion(self):
3866
self.assertExpansion('foo', 'foo')
3868
def test_expand_default_value(self):
3869
self.conf.store._load_from_string('bar=baz')
3870
self.registry.register(config.Option('foo', default=u'{bar}'))
3871
self.assertEquals('baz', self.conf.get('foo', expand=True))
3873
def test_expand_default_from_env(self):
3874
self.conf.store._load_from_string('bar=baz')
3875
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3876
self.overrideEnv('FOO', '{bar}')
3877
self.assertEquals('baz', self.conf.get('foo', expand=True))
3879
def test_expand_default_on_failed_conversion(self):
3880
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3881
self.registry.register(
3882
config.Option('foo', default=u'{bar}',
3883
from_unicode=config.int_from_store))
3884
self.assertEquals(42, self.conf.get('foo', expand=True))
3886
def test_env_adding_options(self):
3887
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3889
def test_env_overriding_options(self):
3890
self.conf.store._load_from_string('foo=baz')
3891
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3893
def test_simple_ref(self):
3894
self.conf.store._load_from_string('foo=xxx')
3895
self.assertExpansion('xxx', '{foo}')
3897
def test_unknown_ref(self):
3898
self.assertRaises(errors.ExpandingUnknownOption,
3899
self.conf.expand_options, '{foo}')
3901
def test_indirect_ref(self):
3902
self.conf.store._load_from_string('''
3906
self.assertExpansion('xxx', '{bar}')
3908
def test_embedded_ref(self):
3909
self.conf.store._load_from_string('''
3913
self.assertExpansion('xxx', '{{bar}}')
3915
def test_simple_loop(self):
3916
self.conf.store._load_from_string('foo={foo}')
3917
self.assertRaises(errors.OptionExpansionLoop,
3918
self.conf.expand_options, '{foo}')
3920
def test_indirect_loop(self):
3921
self.conf.store._load_from_string('''
3925
e = self.assertRaises(errors.OptionExpansionLoop,
3926
self.conf.expand_options, '{foo}')
3927
self.assertEquals('foo->bar->baz', e.refs)
3928
self.assertEquals('{foo}', e.string)
3930
def test_list(self):
3931
self.conf.store._load_from_string('''
3935
list={foo},{bar},{baz}
3937
self.registry.register(
3938
config.ListOption('list'))
3939
self.assertEquals(['start', 'middle', 'end'],
3940
self.conf.get('list', expand=True))
3942
def test_cascading_list(self):
3943
self.conf.store._load_from_string('''
3949
self.registry.register(config.ListOption('list'))
3950
# Register an intermediate option as a list to ensure no conversion
3951
# happen while expanding. Conversion should only occur for the original
3952
# option ('list' here).
3953
self.registry.register(config.ListOption('baz'))
3954
self.assertEquals(['start', 'middle', 'end'],
3955
self.conf.get('list', expand=True))
3957
def test_pathologically_hidden_list(self):
3958
self.conf.store._load_from_string('''
3964
hidden={start}{middle}{end}
3966
# What matters is what the registration says, the conversion happens
3967
# only after all expansions have been performed
3968
self.registry.register(config.ListOption('hidden'))
3969
self.assertEquals(['bin', 'go'],
3970
self.conf.get('hidden', expand=True))
3973
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3976
super(TestStackCrossSectionsExpand, self).setUp()
3978
def get_config(self, location, string):
3981
# Since we don't save the config we won't strictly require to inherit
3982
# from TestCaseInTempDir, but an error occurs so quickly...
3983
c = config.LocationStack(location)
3984
c.store._load_from_string(string)
3987
def test_dont_cross_unrelated_section(self):
3988
c = self.get_config('/another/branch/path','''
3993
[/another/branch/path]
3996
self.assertRaises(errors.ExpandingUnknownOption,
3997
c.get, 'bar', expand=True)
3999
def test_cross_related_sections(self):
4000
c = self.get_config('/project/branch/path','''
4004
[/project/branch/path]
4007
self.assertEquals('quux', c.get('bar', expand=True))
4010
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
4012
def test_cross_global_locations(self):
4013
l_store = config.LocationStore()
4014
l_store._load_from_string('''
4020
g_store = config.GlobalStore()
4021
g_store._load_from_string('''
4027
stack = config.LocationStack('/branch')
4028
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4029
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4032
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4034
def test_expand_locals_empty(self):
4035
l_store = config.LocationStore()
4036
l_store._load_from_string('''
4037
[/home/user/project]
4042
stack = config.LocationStack('/home/user/project/')
4043
self.assertEquals('', stack.get('base', expand=True))
4044
self.assertEquals('', stack.get('rel', expand=True))
4046
def test_expand_basename_locally(self):
4047
l_store = config.LocationStore()
4048
l_store._load_from_string('''
4049
[/home/user/project]
4053
stack = config.LocationStack('/home/user/project/branch')
4054
self.assertEquals('branch', stack.get('bfoo', expand=True))
4056
def test_expand_basename_locally_longer_path(self):
4057
l_store = config.LocationStore()
4058
l_store._load_from_string('''
4063
stack = config.LocationStack('/home/user/project/dir/branch')
4064
self.assertEquals('branch', stack.get('bfoo', expand=True))
4066
def test_expand_relpath_locally(self):
4067
l_store = config.LocationStore()
4068
l_store._load_from_string('''
4069
[/home/user/project]
4070
lfoo = loc-foo/{relpath}
4073
stack = config.LocationStack('/home/user/project/branch')
4074
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4076
def test_expand_relpath_unknonw_in_global(self):
4077
g_store = config.GlobalStore()
4078
g_store._load_from_string('''
4083
stack = config.LocationStack('/home/user/project/branch')
4084
self.assertRaises(errors.ExpandingUnknownOption,
4085
stack.get, 'gfoo', expand=True)
4087
def test_expand_local_option_locally(self):
4088
l_store = config.LocationStore()
4089
l_store._load_from_string('''
4090
[/home/user/project]
4091
lfoo = loc-foo/{relpath}
4095
g_store = config.GlobalStore()
4096
g_store._load_from_string('''
4102
stack = config.LocationStack('/home/user/project/branch')
4103
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4104
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4106
def test_locals_dont_leak(self):
4107
"""Make sure we chose the right local in presence of several sections.
4109
l_store = config.LocationStore()
4110
l_store._load_from_string('''
4112
lfoo = loc-foo/{relpath}
4113
[/home/user/project]
4114
lfoo = loc-foo/{relpath}
4117
stack = config.LocationStack('/home/user/project/branch')
4118
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4119
stack = config.LocationStack('/home/user/bar/baz')
4120
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
4124
class TestStackSet(TestStackWithTransport):
4126
def test_simple_set(self):
4127
conf = self.get_stack(self)
4128
self.assertEquals(None, conf.get('foo'))
4129
conf.set('foo', 'baz')
4130
# Did we get it back ?
4131
self.assertEquals('baz', conf.get('foo'))
4133
def test_set_creates_a_new_section(self):
4134
conf = self.get_stack(self)
4135
conf.set('foo', 'baz')
4136
self.assertEquals, 'baz', conf.get('foo')
4138
def test_set_hook(self):
4142
config.ConfigHooks.install_named_hook('set', hook, None)
4143
self.assertLength(0, calls)
4144
conf = self.get_stack(self)
4145
conf.set('foo', 'bar')
4146
self.assertLength(1, calls)
4147
self.assertEquals((conf, 'foo', 'bar'), calls[0])
4150
class TestStackRemove(TestStackWithTransport):
4152
def test_remove_existing(self):
4153
conf = self.get_stack(self)
4154
conf.set('foo', 'bar')
4155
self.assertEquals('bar', conf.get('foo'))
4157
# Did we get it back ?
4158
self.assertEquals(None, conf.get('foo'))
4160
def test_remove_unknown(self):
4161
conf = self.get_stack(self)
4162
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
4164
def test_remove_hook(self):
4168
config.ConfigHooks.install_named_hook('remove', hook, None)
4169
self.assertLength(0, calls)
4170
conf = self.get_stack(self)
4171
conf.set('foo', 'bar')
4173
self.assertLength(1, calls)
4174
self.assertEquals((conf, 'foo'), calls[0])
4177
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
4180
super(TestConfigGetOptions, self).setUp()
4181
create_configs(self)
4183
def test_no_variable(self):
4184
# Using branch should query branch, locations and bazaar
4185
self.assertOptions([], self.branch_config)
4187
def test_option_in_bazaar(self):
4188
self.bazaar_config.set_user_option('file', 'bazaar')
4189
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
4192
def test_option_in_locations(self):
4193
self.locations_config.set_user_option('file', 'locations')
4195
[('file', 'locations', self.tree.basedir, 'locations')],
4196
self.locations_config)
4198
def test_option_in_branch(self):
4199
self.branch_config.set_user_option('file', 'branch')
4200
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
4203
def test_option_in_bazaar_and_branch(self):
4204
self.bazaar_config.set_user_option('file', 'bazaar')
4205
self.branch_config.set_user_option('file', 'branch')
4206
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4207
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4210
def test_option_in_branch_and_locations(self):
4211
# Hmm, locations override branch :-/
4212
self.locations_config.set_user_option('file', 'locations')
4213
self.branch_config.set_user_option('file', 'branch')
4215
[('file', 'locations', self.tree.basedir, 'locations'),
4216
('file', 'branch', 'DEFAULT', 'branch'),],
4219
def test_option_in_bazaar_locations_and_branch(self):
4220
self.bazaar_config.set_user_option('file', 'bazaar')
4221
self.locations_config.set_user_option('file', 'locations')
4222
self.branch_config.set_user_option('file', 'branch')
4224
[('file', 'locations', self.tree.basedir, 'locations'),
4225
('file', 'branch', 'DEFAULT', 'branch'),
4226
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4230
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
4233
super(TestConfigRemoveOption, self).setUp()
4234
create_configs_with_file_option(self)
4236
def test_remove_in_locations(self):
4237
self.locations_config.remove_user_option('file', self.tree.basedir)
4239
[('file', 'branch', 'DEFAULT', 'branch'),
4240
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4243
def test_remove_in_branch(self):
4244
self.branch_config.remove_user_option('file')
4246
[('file', 'locations', self.tree.basedir, 'locations'),
4247
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4250
def test_remove_in_bazaar(self):
4251
self.bazaar_config.remove_user_option('file')
4253
[('file', 'locations', self.tree.basedir, 'locations'),
4254
('file', 'branch', 'DEFAULT', 'branch'),],
4258
class TestConfigGetSections(tests.TestCaseWithTransport):
4261
super(TestConfigGetSections, self).setUp()
4262
create_configs(self)
4264
def assertSectionNames(self, expected, conf, name=None):
4265
"""Check which sections are returned for a given config.
4267
If fallback configurations exist their sections can be included.
4269
:param expected: A list of section names.
4271
:param conf: The configuration that will be queried.
4273
:param name: An optional section name that will be passed to
4276
sections = list(conf._get_sections(name))
4277
self.assertLength(len(expected), sections)
4278
self.assertEqual(expected, [name for name, _, _ in sections])
4280
def test_bazaar_default_section(self):
4281
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4283
def test_locations_default_section(self):
4284
# No sections are defined in an empty file
4285
self.assertSectionNames([], self.locations_config)
4287
def test_locations_named_section(self):
4288
self.locations_config.set_user_option('file', 'locations')
4289
self.assertSectionNames([self.tree.basedir], self.locations_config)
4291
def test_locations_matching_sections(self):
4292
loc_config = self.locations_config
4293
loc_config.set_user_option('file', 'locations')
4294
# We need to cheat a bit here to create an option in sections above and
4295
# below the 'location' one.
4296
parser = loc_config._get_parser()
4297
# locations.cong deals with '/' ignoring native os.sep
4298
location_names = self.tree.basedir.split('/')
4299
parent = '/'.join(location_names[:-1])
4300
child = '/'.join(location_names + ['child'])
4302
parser[parent]['file'] = 'parent'
4304
parser[child]['file'] = 'child'
4305
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4307
def test_branch_data_default_section(self):
4308
self.assertSectionNames([None],
4309
self.branch_config._get_branch_data_config())
4311
def test_branch_default_sections(self):
4312
# No sections are defined in an empty locations file
4313
self.assertSectionNames([None, 'DEFAULT'],
4315
# Unless we define an option
4316
self.branch_config._get_location_config().set_user_option(
4317
'file', 'locations')
4318
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4321
def test_bazaar_named_section(self):
4322
# We need to cheat as the API doesn't give direct access to sections
4323
# other than DEFAULT.
4324
self.bazaar_config.set_alias('bazaar', 'bzr')
4325
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1474
4328
class TestAuthenticationConfigFile(tests.TestCase):
1475
4329
"""Test the authentication.conf file matching"""