1312
2089
self.assertIs(None, bzrdir_config.get_default_stack_on())
2092
class TestOldConfigHooks(tests.TestCaseWithTransport):
2095
super(TestOldConfigHooks, self).setUp()
2096
create_configs_with_file_option(self)
2098
def assertGetHook(self, conf, name, value):
2102
config.OldConfigHooks.install_named_hook('get', hook, None)
2104
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2105
self.assertLength(0, calls)
2106
actual_value = conf.get_user_option(name)
2107
self.assertEquals(value, actual_value)
2108
self.assertLength(1, calls)
2109
self.assertEquals((conf, name, value), calls[0])
2111
def test_get_hook_bazaar(self):
2112
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2114
def test_get_hook_locations(self):
2115
self.assertGetHook(self.locations_config, 'file', 'locations')
2117
def test_get_hook_branch(self):
2118
# Since locations masks branch, we define a different option
2119
self.branch_config.set_user_option('file2', 'branch')
2120
self.assertGetHook(self.branch_config, 'file2', 'branch')
2122
def assertSetHook(self, conf, name, value):
2126
config.OldConfigHooks.install_named_hook('set', hook, None)
2128
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2129
self.assertLength(0, calls)
2130
conf.set_user_option(name, value)
2131
self.assertLength(1, calls)
2132
# We can't assert the conf object below as different configs use
2133
# different means to implement set_user_option and we care only about
2135
self.assertEquals((name, value), calls[0][1:])
2137
def test_set_hook_bazaar(self):
2138
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2140
def test_set_hook_locations(self):
2141
self.assertSetHook(self.locations_config, 'foo', 'locations')
2143
def test_set_hook_branch(self):
2144
self.assertSetHook(self.branch_config, 'foo', 'branch')
2146
def assertRemoveHook(self, conf, name, section_name=None):
2150
config.OldConfigHooks.install_named_hook('remove', hook, None)
2152
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2153
self.assertLength(0, calls)
2154
conf.remove_user_option(name, section_name)
2155
self.assertLength(1, calls)
2156
# We can't assert the conf object below as different configs use
2157
# different means to implement remove_user_option and we care only about
2159
self.assertEquals((name,), calls[0][1:])
2161
def test_remove_hook_bazaar(self):
2162
self.assertRemoveHook(self.bazaar_config, 'file')
2164
def test_remove_hook_locations(self):
2165
self.assertRemoveHook(self.locations_config, 'file',
2166
self.locations_config.location)
2168
def test_remove_hook_branch(self):
2169
self.assertRemoveHook(self.branch_config, 'file')
2171
def assertLoadHook(self, name, conf_class, *conf_args):
2175
config.OldConfigHooks.install_named_hook('load', hook, None)
2177
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2178
self.assertLength(0, calls)
2180
conf = conf_class(*conf_args)
2181
# Access an option to trigger a load
2182
conf.get_user_option(name)
2183
self.assertLength(1, calls)
2184
# Since we can't assert about conf, we just use the number of calls ;-/
2186
def test_load_hook_bazaar(self):
2187
self.assertLoadHook('file', config.GlobalConfig)
2189
def test_load_hook_locations(self):
2190
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2192
def test_load_hook_branch(self):
2193
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2195
def assertSaveHook(self, conf):
2199
config.OldConfigHooks.install_named_hook('save', hook, None)
2201
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2202
self.assertLength(0, calls)
2203
# Setting an option triggers a save
2204
conf.set_user_option('foo', 'bar')
2205
self.assertLength(1, calls)
2206
# Since we can't assert about conf, we just use the number of calls ;-/
2208
def test_save_hook_bazaar(self):
2209
self.assertSaveHook(self.bazaar_config)
2211
def test_save_hook_locations(self):
2212
self.assertSaveHook(self.locations_config)
2214
def test_save_hook_branch(self):
2215
self.assertSaveHook(self.branch_config)
2218
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2219
"""Tests config hooks for remote configs.
2221
No tests for the remove hook as this is not implemented there.
2225
super(TestOldConfigHooksForRemote, self).setUp()
2226
self.transport_server = test_server.SmartTCPServer_for_testing
2227
create_configs_with_file_option(self)
2229
def assertGetHook(self, conf, name, value):
2233
config.OldConfigHooks.install_named_hook('get', hook, None)
2235
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2236
self.assertLength(0, calls)
2237
actual_value = conf.get_option(name)
2238
self.assertEquals(value, actual_value)
2239
self.assertLength(1, calls)
2240
self.assertEquals((conf, name, value), calls[0])
2242
def test_get_hook_remote_branch(self):
2243
remote_branch = branch.Branch.open(self.get_url('tree'))
2244
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2246
def test_get_hook_remote_bzrdir(self):
2247
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2248
conf = remote_bzrdir._get_config()
2249
conf.set_option('remotedir', 'file')
2250
self.assertGetHook(conf, 'file', 'remotedir')
2252
def assertSetHook(self, conf, name, value):
2256
config.OldConfigHooks.install_named_hook('set', hook, None)
2258
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2259
self.assertLength(0, calls)
2260
conf.set_option(value, name)
2261
self.assertLength(1, calls)
2262
# We can't assert the conf object below as different configs use
2263
# different means to implement set_user_option and we care only about
2265
self.assertEquals((name, value), calls[0][1:])
2267
def test_set_hook_remote_branch(self):
2268
remote_branch = branch.Branch.open(self.get_url('tree'))
2269
self.addCleanup(remote_branch.lock_write().unlock)
2270
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2272
def test_set_hook_remote_bzrdir(self):
2273
remote_branch = branch.Branch.open(self.get_url('tree'))
2274
self.addCleanup(remote_branch.lock_write().unlock)
2275
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2276
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2278
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2282
config.OldConfigHooks.install_named_hook('load', hook, None)
2284
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2285
self.assertLength(0, calls)
2287
conf = conf_class(*conf_args)
2288
# Access an option to trigger a load
2289
conf.get_option(name)
2290
self.assertLength(expected_nb_calls, calls)
2291
# Since we can't assert about conf, we just use the number of calls ;-/
2293
def test_load_hook_remote_branch(self):
2294
remote_branch = branch.Branch.open(self.get_url('tree'))
2295
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2297
def test_load_hook_remote_bzrdir(self):
2298
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2299
# The config file doesn't exist, set an option to force its creation
2300
conf = remote_bzrdir._get_config()
2301
conf.set_option('remotedir', 'file')
2302
# We get one call for the server and one call for the client, this is
2303
# caused by the differences in implementations betwen
2304
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2305
# SmartServerBranchGetConfigFile (in smart/branch.py)
2306
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2308
def assertSaveHook(self, conf):
2312
config.OldConfigHooks.install_named_hook('save', hook, None)
2314
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2315
self.assertLength(0, calls)
2316
# Setting an option triggers a save
2317
conf.set_option('foo', 'bar')
2318
self.assertLength(1, calls)
2319
# Since we can't assert about conf, we just use the number of calls ;-/
2321
def test_save_hook_remote_branch(self):
2322
remote_branch = branch.Branch.open(self.get_url('tree'))
2323
self.addCleanup(remote_branch.lock_write().unlock)
2324
self.assertSaveHook(remote_branch._get_config())
2326
def test_save_hook_remote_bzrdir(self):
2327
remote_branch = branch.Branch.open(self.get_url('tree'))
2328
self.addCleanup(remote_branch.lock_write().unlock)
2329
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2330
self.assertSaveHook(remote_bzrdir._get_config())
2333
class TestOption(tests.TestCase):
2335
def test_default_value(self):
2336
opt = config.Option('foo', default='bar')
2337
self.assertEquals('bar', opt.get_default())
2339
def test_callable_default_value(self):
2340
def bar_as_unicode():
2342
opt = config.Option('foo', default=bar_as_unicode)
2343
self.assertEquals('bar', opt.get_default())
2345
def test_default_value_from_env(self):
2346
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2347
self.overrideEnv('FOO', 'quux')
2348
# Env variable provides a default taking over the option one
2349
self.assertEquals('quux', opt.get_default())
2351
def test_first_default_value_from_env_wins(self):
2352
opt = config.Option('foo', default='bar',
2353
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2354
self.overrideEnv('FOO', 'foo')
2355
self.overrideEnv('BAZ', 'baz')
2356
# The first env var set wins
2357
self.assertEquals('foo', opt.get_default())
2359
def test_not_supported_list_default_value(self):
2360
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2362
def test_not_supported_object_default_value(self):
2363
self.assertRaises(AssertionError, config.Option, 'foo',
2366
def test_not_supported_callable_default_value_not_unicode(self):
2367
def bar_not_unicode():
2369
opt = config.Option('foo', default=bar_not_unicode)
2370
self.assertRaises(AssertionError, opt.get_default)
2372
def test_get_help_topic(self):
2373
opt = config.Option('foo')
2374
self.assertEquals('foo', opt.get_help_topic())
2377
class TestOptionConverterMixin(object):
2379
def assertConverted(self, expected, opt, value):
2380
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2382
def assertWarns(self, opt, value):
2385
warnings.append(args[0] % args[1:])
2386
self.overrideAttr(trace, 'warning', warning)
2387
self.assertEquals(None, opt.convert_from_unicode(None, value))
2388
self.assertLength(1, warnings)
2390
'Value "%s" is not valid for "%s"' % (value, opt.name),
2393
def assertErrors(self, opt, value):
2394
self.assertRaises(errors.ConfigOptionValueError,
2395
opt.convert_from_unicode, None, value)
2397
def assertConvertInvalid(self, opt, invalid_value):
2399
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2400
opt.invalid = 'warning'
2401
self.assertWarns(opt, invalid_value)
2402
opt.invalid = 'error'
2403
self.assertErrors(opt, invalid_value)
2406
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2408
def get_option(self):
2409
return config.Option('foo', help='A boolean.',
2410
from_unicode=config.bool_from_store)
2412
def test_convert_invalid(self):
2413
opt = self.get_option()
2414
# A string that is not recognized as a boolean
2415
self.assertConvertInvalid(opt, u'invalid-boolean')
2416
# A list of strings is never recognized as a boolean
2417
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2419
def test_convert_valid(self):
2420
opt = self.get_option()
2421
self.assertConverted(True, opt, u'True')
2422
self.assertConverted(True, opt, u'1')
2423
self.assertConverted(False, opt, u'False')
2426
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2428
def get_option(self):
2429
return config.Option('foo', help='An integer.',
2430
from_unicode=config.int_from_store)
2432
def test_convert_invalid(self):
2433
opt = self.get_option()
2434
# A string that is not recognized as an integer
2435
self.assertConvertInvalid(opt, u'forty-two')
2436
# A list of strings is never recognized as an integer
2437
self.assertConvertInvalid(opt, [u'a', u'list'])
2439
def test_convert_valid(self):
2440
opt = self.get_option()
2441
self.assertConverted(16, opt, u'16')
2444
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2446
def get_option(self):
2447
return config.Option('foo', help='An integer in SI units.',
2448
from_unicode=config.int_SI_from_store)
2450
def test_convert_invalid(self):
2451
opt = self.get_option()
2452
self.assertConvertInvalid(opt, u'not-a-unit')
2453
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2454
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2455
self.assertConvertInvalid(opt, u'1GG')
2456
self.assertConvertInvalid(opt, u'1Mbb')
2457
self.assertConvertInvalid(opt, u'1MM')
2459
def test_convert_valid(self):
2460
opt = self.get_option()
2461
self.assertConverted(int(5e3), opt, u'5kb')
2462
self.assertConverted(int(5e6), opt, u'5M')
2463
self.assertConverted(int(5e6), opt, u'5MB')
2464
self.assertConverted(int(5e9), opt, u'5g')
2465
self.assertConverted(int(5e9), opt, u'5gB')
2466
self.assertConverted(100, opt, u'100')
2469
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2471
def get_option(self):
2472
return config.ListOption('foo', help='A list.')
2474
def test_convert_invalid(self):
2475
opt = self.get_option()
2476
# We don't even try to convert a list into a list, we only expect
2478
self.assertConvertInvalid(opt, [1])
2479
# No string is invalid as all forms can be converted to a list
2481
def test_convert_valid(self):
2482
opt = self.get_option()
2483
# An empty string is an empty list
2484
self.assertConverted([], opt, '') # Using a bare str() just in case
2485
self.assertConverted([], opt, u'')
2487
self.assertConverted([u'True'], opt, u'True')
2489
self.assertConverted([u'42'], opt, u'42')
2491
self.assertConverted([u'bar'], opt, u'bar')
2494
class TestOptionRegistry(tests.TestCase):
2497
super(TestOptionRegistry, self).setUp()
2498
# Always start with an empty registry
2499
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2500
self.registry = config.option_registry
2502
def test_register(self):
2503
opt = config.Option('foo')
2504
self.registry.register(opt)
2505
self.assertIs(opt, self.registry.get('foo'))
2507
def test_registered_help(self):
2508
opt = config.Option('foo', help='A simple option')
2509
self.registry.register(opt)
2510
self.assertEquals('A simple option', self.registry.get_help('foo'))
2512
lazy_option = config.Option('lazy_foo', help='Lazy help')
2514
def test_register_lazy(self):
2515
self.registry.register_lazy('lazy_foo', self.__module__,
2516
'TestOptionRegistry.lazy_option')
2517
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2519
def test_registered_lazy_help(self):
2520
self.registry.register_lazy('lazy_foo', self.__module__,
2521
'TestOptionRegistry.lazy_option')
2522
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2525
class TestRegisteredOptions(tests.TestCase):
2526
"""All registered options should verify some constraints."""
2528
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2529
in config.option_registry.iteritems()]
2532
super(TestRegisteredOptions, self).setUp()
2533
self.registry = config.option_registry
2535
def test_proper_name(self):
2536
# An option should be registered under its own name, this can't be
2537
# checked at registration time for the lazy ones.
2538
self.assertEquals(self.option_name, self.option.name)
2540
def test_help_is_set(self):
2541
option_help = self.registry.get_help(self.option_name)
2542
self.assertNotEquals(None, option_help)
2543
# Come on, think about the user, he really wants to know what the
2545
self.assertIsNot(None, option_help)
2546
self.assertNotEquals('', option_help)
2549
class TestSection(tests.TestCase):
2551
# FIXME: Parametrize so that all sections produced by Stores run these
2552
# tests -- vila 2011-04-01
2554
def test_get_a_value(self):
2555
a_dict = dict(foo='bar')
2556
section = config.Section('myID', a_dict)
2557
self.assertEquals('bar', section.get('foo'))
2559
def test_get_unknown_option(self):
2561
section = config.Section(None, a_dict)
2562
self.assertEquals('out of thin air',
2563
section.get('foo', 'out of thin air'))
2565
def test_options_is_shared(self):
2567
section = config.Section(None, a_dict)
2568
self.assertIs(a_dict, section.options)
2571
class TestMutableSection(tests.TestCase):
2573
scenarios = [('mutable',
2575
lambda opts: config.MutableSection('myID', opts)},),
2579
a_dict = dict(foo='bar')
2580
section = self.get_section(a_dict)
2581
section.set('foo', 'new_value')
2582
self.assertEquals('new_value', section.get('foo'))
2583
# The change appears in the shared section
2584
self.assertEquals('new_value', a_dict.get('foo'))
2585
# We keep track of the change
2586
self.assertTrue('foo' in section.orig)
2587
self.assertEquals('bar', section.orig.get('foo'))
2589
def test_set_preserve_original_once(self):
2590
a_dict = dict(foo='bar')
2591
section = self.get_section(a_dict)
2592
section.set('foo', 'first_value')
2593
section.set('foo', 'second_value')
2594
# We keep track of the original value
2595
self.assertTrue('foo' in section.orig)
2596
self.assertEquals('bar', section.orig.get('foo'))
2598
def test_remove(self):
2599
a_dict = dict(foo='bar')
2600
section = self.get_section(a_dict)
2601
section.remove('foo')
2602
# We get None for unknown options via the default value
2603
self.assertEquals(None, section.get('foo'))
2604
# Or we just get the default value
2605
self.assertEquals('unknown', section.get('foo', 'unknown'))
2606
self.assertFalse('foo' in section.options)
2607
# We keep track of the deletion
2608
self.assertTrue('foo' in section.orig)
2609
self.assertEquals('bar', section.orig.get('foo'))
2611
def test_remove_new_option(self):
2613
section = self.get_section(a_dict)
2614
section.set('foo', 'bar')
2615
section.remove('foo')
2616
self.assertFalse('foo' in section.options)
2617
# The option didn't exist initially so it we need to keep track of it
2618
# with a special value
2619
self.assertTrue('foo' in section.orig)
2620
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2623
class TestCommandLineStore(tests.TestCase):
2626
super(TestCommandLineStore, self).setUp()
2627
self.store = config.CommandLineStore()
2628
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2630
def get_section(self):
2631
"""Get the unique section for the command line overrides."""
2632
sections = list(self.store.get_sections())
2633
self.assertLength(1, sections)
2634
store, section = sections[0]
2635
self.assertEquals(self.store, store)
2638
def test_no_override(self):
2639
self.store._from_cmdline([])
2640
section = self.get_section()
2641
self.assertLength(0, list(section.iter_option_names()))
2643
def test_simple_override(self):
2644
self.store._from_cmdline(['a=b'])
2645
section = self.get_section()
2646
self.assertEqual('b', section.get('a'))
2648
def test_list_override(self):
2649
opt = config.ListOption('l')
2650
config.option_registry.register(opt)
2651
self.store._from_cmdline(['l=1,2,3'])
2652
val = self.get_section().get('l')
2653
self.assertEqual('1,2,3', val)
2654
# Reminder: lists should be registered as such explicitely, otherwise
2655
# the conversion needs to be done afterwards.
2656
self.assertEqual(['1', '2', '3'],
2657
opt.convert_from_unicode(self.store, val))
2659
def test_multiple_overrides(self):
2660
self.store._from_cmdline(['a=b', 'x=y'])
2661
section = self.get_section()
2662
self.assertEquals('b', section.get('a'))
2663
self.assertEquals('y', section.get('x'))
2665
def test_wrong_syntax(self):
2666
self.assertRaises(errors.BzrCommandError,
2667
self.store._from_cmdline, ['a=b', 'c'])
2669
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2671
scenarios = [(key, {'get_store': builder}) for key, builder
2672
in config.test_store_builder_registry.iteritems()] + [
2673
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2676
store = self.get_store(self)
2677
if type(store) == config.TransportIniFileStore:
2678
raise tests.TestNotApplicable(
2679
"%s is not a concrete Store implementation"
2680
" so it doesn't need an id" % (store.__class__.__name__,))
2681
self.assertIsNot(None, store.id)
2684
class TestStore(tests.TestCaseWithTransport):
2686
def assertSectionContent(self, expected, (store, section)):
2687
"""Assert that some options have the proper values in a section."""
2688
expected_name, expected_options = expected
2689
self.assertEquals(expected_name, section.id)
2692
dict([(k, section.get(k)) for k in expected_options.keys()]))
2695
class TestReadonlyStore(TestStore):
2697
scenarios = [(key, {'get_store': builder}) for key, builder
2698
in config.test_store_builder_registry.iteritems()]
2700
def test_building_delays_load(self):
2701
store = self.get_store(self)
2702
self.assertEquals(False, store.is_loaded())
2703
store._load_from_string('')
2704
self.assertEquals(True, store.is_loaded())
2706
def test_get_no_sections_for_empty(self):
2707
store = self.get_store(self)
2708
store._load_from_string('')
2709
self.assertEquals([], list(store.get_sections()))
2711
def test_get_default_section(self):
2712
store = self.get_store(self)
2713
store._load_from_string('foo=bar')
2714
sections = list(store.get_sections())
2715
self.assertLength(1, sections)
2716
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2718
def test_get_named_section(self):
2719
store = self.get_store(self)
2720
store._load_from_string('[baz]\nfoo=bar')
2721
sections = list(store.get_sections())
2722
self.assertLength(1, sections)
2723
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2725
def test_load_from_string_fails_for_non_empty_store(self):
2726
store = self.get_store(self)
2727
store._load_from_string('foo=bar')
2728
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2731
class TestStoreQuoting(TestStore):
2733
scenarios = [(key, {'get_store': builder}) for key, builder
2734
in config.test_store_builder_registry.iteritems()]
2737
super(TestStoreQuoting, self).setUp()
2738
self.store = self.get_store(self)
2739
# We need a loaded store but any content will do
2740
self.store._load_from_string('')
2742
def assertIdempotent(self, s):
2743
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2745
What matters here is that option values, as they appear in a store, can
2746
be safely round-tripped out of the store and back.
2748
:param s: A string, quoted if required.
2750
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2751
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2753
def test_empty_string(self):
2754
if isinstance(self.store, config.IniFileStore):
2755
# configobj._quote doesn't handle empty values
2756
self.assertRaises(AssertionError,
2757
self.assertIdempotent, '')
2759
self.assertIdempotent('')
2760
# But quoted empty strings are ok
2761
self.assertIdempotent('""')
2763
def test_embedded_spaces(self):
2764
self.assertIdempotent('" a b c "')
2766
def test_embedded_commas(self):
2767
self.assertIdempotent('" a , b c "')
2769
def test_simple_comma(self):
2770
if isinstance(self.store, config.IniFileStore):
2771
# configobj requires that lists are special-cased
2772
self.assertRaises(AssertionError,
2773
self.assertIdempotent, ',')
2775
self.assertIdempotent(',')
2776
# When a single comma is required, quoting is also required
2777
self.assertIdempotent('","')
2779
def test_list(self):
2780
if isinstance(self.store, config.IniFileStore):
2781
# configobj requires that lists are special-cased
2782
self.assertRaises(AssertionError,
2783
self.assertIdempotent, 'a,b')
2785
self.assertIdempotent('a,b')
2788
class TestDictFromStore(tests.TestCase):
2790
def test_unquote_not_string(self):
2791
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2792
value = conf.get('a_section')
2793
# Urgh, despite 'conf' asking for the no-name section, we get the
2794
# content of another section as a dict o_O
2795
self.assertEquals({'a': '1'}, value)
2796
unquoted = conf.store.unquote(value)
2797
# Which cannot be unquoted but shouldn't crash either (the use cases
2798
# are getting the value or displaying it. In the later case, '%s' will
2800
self.assertEquals({'a': '1'}, unquoted)
2801
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2804
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2805
"""Simulate loading a config store with content of various encodings.
2807
All files produced by bzr are in utf8 content.
2809
Users may modify them manually and end up with a file that can't be
2810
loaded. We need to issue proper error messages in this case.
2813
invalid_utf8_char = '\xff'
2815
def test_load_utf8(self):
2816
"""Ensure we can load an utf8-encoded file."""
2817
t = self.get_transport()
2818
# From http://pad.lv/799212
2819
unicode_user = u'b\N{Euro Sign}ar'
2820
unicode_content = u'user=%s' % (unicode_user,)
2821
utf8_content = unicode_content.encode('utf8')
2822
# Store the raw content in the config file
2823
t.put_bytes('foo.conf', utf8_content)
2824
store = config.TransportIniFileStore(t, 'foo.conf')
2826
stack = config.Stack([store.get_sections], store)
2827
self.assertEquals(unicode_user, stack.get('user'))
2829
def test_load_non_ascii(self):
2830
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2831
t = self.get_transport()
2832
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2833
store = config.TransportIniFileStore(t, 'foo.conf')
2834
self.assertRaises(errors.ConfigContentError, store.load)
2836
def test_load_erroneous_content(self):
2837
"""Ensure we display a proper error on content that can't be parsed."""
2838
t = self.get_transport()
2839
t.put_bytes('foo.conf', '[open_section\n')
2840
store = config.TransportIniFileStore(t, 'foo.conf')
2841
self.assertRaises(errors.ParseConfigError, store.load)
2843
def test_load_permission_denied(self):
2844
"""Ensure we get warned when trying to load an inaccessible file."""
2847
warnings.append(args[0] % args[1:])
2848
self.overrideAttr(trace, 'warning', warning)
2850
t = self.get_transport()
2852
def get_bytes(relpath):
2853
raise errors.PermissionDenied(relpath, "")
2854
t.get_bytes = get_bytes
2855
store = config.TransportIniFileStore(t, 'foo.conf')
2856
self.assertRaises(errors.PermissionDenied, store.load)
2859
[u'Permission denied while trying to load configuration store %s.'
2860
% store.external_url()])
2863
class TestIniConfigContent(tests.TestCaseWithTransport):
2864
"""Simulate loading a IniBasedConfig with content of various encodings.
2866
All files produced by bzr are in utf8 content.
2868
Users may modify them manually and end up with a file that can't be
2869
loaded. We need to issue proper error messages in this case.
2872
invalid_utf8_char = '\xff'
2874
def test_load_utf8(self):
2875
"""Ensure we can load an utf8-encoded file."""
2876
# From http://pad.lv/799212
2877
unicode_user = u'b\N{Euro Sign}ar'
2878
unicode_content = u'user=%s' % (unicode_user,)
2879
utf8_content = unicode_content.encode('utf8')
2880
# Store the raw content in the config file
2881
with open('foo.conf', 'wb') as f:
2882
f.write(utf8_content)
2883
conf = config.IniBasedConfig(file_name='foo.conf')
2884
self.assertEquals(unicode_user, conf.get_user_option('user'))
2886
def test_load_badly_encoded_content(self):
2887
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2888
with open('foo.conf', 'wb') as f:
2889
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2890
conf = config.IniBasedConfig(file_name='foo.conf')
2891
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2893
def test_load_erroneous_content(self):
2894
"""Ensure we display a proper error on content that can't be parsed."""
2895
with open('foo.conf', 'wb') as f:
2896
f.write('[open_section\n')
2897
conf = config.IniBasedConfig(file_name='foo.conf')
2898
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2901
class TestMutableStore(TestStore):
2903
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2904
in config.test_store_builder_registry.iteritems()]
2907
super(TestMutableStore, self).setUp()
2908
self.transport = self.get_transport()
2910
def has_store(self, store):
2911
store_basename = urlutils.relative_url(self.transport.external_url(),
2912
store.external_url())
2913
return self.transport.has(store_basename)
2915
def test_save_empty_creates_no_file(self):
2916
# FIXME: There should be a better way than relying on the test
2917
# parametrization to identify branch.conf -- vila 2011-0526
2918
if self.store_id in ('branch', 'remote_branch'):
2919
raise tests.TestNotApplicable(
2920
'branch.conf is *always* created when a branch is initialized')
2921
store = self.get_store(self)
2923
self.assertEquals(False, self.has_store(store))
2925
def test_save_emptied_succeeds(self):
2926
store = self.get_store(self)
2927
store._load_from_string('foo=bar\n')
2928
section = store.get_mutable_section(None)
2929
section.remove('foo')
2931
self.assertEquals(True, self.has_store(store))
2932
modified_store = self.get_store(self)
2933
sections = list(modified_store.get_sections())
2934
self.assertLength(0, sections)
2936
def test_save_with_content_succeeds(self):
2937
# FIXME: There should be a better way than relying on the test
2938
# parametrization to identify branch.conf -- vila 2011-0526
2939
if self.store_id in ('branch', 'remote_branch'):
2940
raise tests.TestNotApplicable(
2941
'branch.conf is *always* created when a branch is initialized')
2942
store = self.get_store(self)
2943
store._load_from_string('foo=bar\n')
2944
self.assertEquals(False, self.has_store(store))
2946
self.assertEquals(True, self.has_store(store))
2947
modified_store = self.get_store(self)
2948
sections = list(modified_store.get_sections())
2949
self.assertLength(1, sections)
2950
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2952
def test_set_option_in_empty_store(self):
2953
store = self.get_store(self)
2954
section = store.get_mutable_section(None)
2955
section.set('foo', 'bar')
2957
modified_store = self.get_store(self)
2958
sections = list(modified_store.get_sections())
2959
self.assertLength(1, sections)
2960
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2962
def test_set_option_in_default_section(self):
2963
store = self.get_store(self)
2964
store._load_from_string('')
2965
section = store.get_mutable_section(None)
2966
section.set('foo', 'bar')
2968
modified_store = self.get_store(self)
2969
sections = list(modified_store.get_sections())
2970
self.assertLength(1, sections)
2971
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2973
def test_set_option_in_named_section(self):
2974
store = self.get_store(self)
2975
store._load_from_string('')
2976
section = store.get_mutable_section('baz')
2977
section.set('foo', 'bar')
2979
modified_store = self.get_store(self)
2980
sections = list(modified_store.get_sections())
2981
self.assertLength(1, sections)
2982
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2984
def test_load_hook(self):
2985
# We first needs to ensure that the store exists
2986
store = self.get_store(self)
2987
section = store.get_mutable_section('baz')
2988
section.set('foo', 'bar')
2990
# Now we can try to load it
2991
store = self.get_store(self)
2995
config.ConfigHooks.install_named_hook('load', hook, None)
2996
self.assertLength(0, calls)
2998
self.assertLength(1, calls)
2999
self.assertEquals((store,), calls[0])
3001
def test_save_hook(self):
3005
config.ConfigHooks.install_named_hook('save', hook, None)
3006
self.assertLength(0, calls)
3007
store = self.get_store(self)
3008
section = store.get_mutable_section('baz')
3009
section.set('foo', 'bar')
3011
self.assertLength(1, calls)
3012
self.assertEquals((store,), calls[0])
3014
def test_set_mark_dirty(self):
3015
stack = config.MemoryStack('')
3016
self.assertLength(0, stack.store.dirty_sections)
3017
stack.set('foo', 'baz')
3018
self.assertLength(1, stack.store.dirty_sections)
3019
self.assertTrue(stack.store._need_saving())
3021
def test_remove_mark_dirty(self):
3022
stack = config.MemoryStack('foo=bar')
3023
self.assertLength(0, stack.store.dirty_sections)
3025
self.assertLength(1, stack.store.dirty_sections)
3026
self.assertTrue(stack.store._need_saving())
3029
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3030
"""Tests that config changes are kept in memory and saved on-demand."""
3033
super(TestStoreSaveChanges, self).setUp()
3034
self.transport = self.get_transport()
3035
# Most of the tests involve two stores pointing to the same persistent
3036
# storage to observe the effects of concurrent changes
3037
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3038
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3041
self.warnings.append(args[0] % args[1:])
3042
self.overrideAttr(trace, 'warning', warning)
3044
def has_store(self, store):
3045
store_basename = urlutils.relative_url(self.transport.external_url(),
3046
store.external_url())
3047
return self.transport.has(store_basename)
3049
def get_stack(self, store):
3050
# Any stack will do as long as it uses the right store, just a single
3051
# no-name section is enough
3052
return config.Stack([store.get_sections], store)
3054
def test_no_changes_no_save(self):
3055
s = self.get_stack(self.st1)
3056
s.store.save_changes()
3057
self.assertEquals(False, self.has_store(self.st1))
3059
def test_unrelated_concurrent_update(self):
3060
s1 = self.get_stack(self.st1)
3061
s2 = self.get_stack(self.st2)
3062
s1.set('foo', 'bar')
3063
s2.set('baz', 'quux')
3065
# Changes don't propagate magically
3066
self.assertEquals(None, s1.get('baz'))
3067
s2.store.save_changes()
3068
self.assertEquals('quux', s2.get('baz'))
3069
# Changes are acquired when saving
3070
self.assertEquals('bar', s2.get('foo'))
3071
# Since there is no overlap, no warnings are emitted
3072
self.assertLength(0, self.warnings)
3074
def test_concurrent_update_modified(self):
3075
s1 = self.get_stack(self.st1)
3076
s2 = self.get_stack(self.st2)
3077
s1.set('foo', 'bar')
3078
s2.set('foo', 'baz')
3081
s2.store.save_changes()
3082
self.assertEquals('baz', s2.get('foo'))
3083
# But the user get a warning
3084
self.assertLength(1, self.warnings)
3085
warning = self.warnings[0]
3086
self.assertStartsWith(warning, 'Option foo in section None')
3087
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3088
' The baz value will be saved.')
3090
def test_concurrent_deletion(self):
3091
self.st1._load_from_string('foo=bar')
3093
s1 = self.get_stack(self.st1)
3094
s2 = self.get_stack(self.st2)
3097
s1.store.save_changes()
3099
self.assertLength(0, self.warnings)
3100
s2.store.save_changes()
3102
self.assertLength(1, self.warnings)
3103
warning = self.warnings[0]
3104
self.assertStartsWith(warning, 'Option foo in section None')
3105
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3106
' The <DELETED> value will be saved.')
3109
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3111
def get_store(self):
3112
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3114
def test_get_quoted_string(self):
3115
store = self.get_store()
3116
store._load_from_string('foo= " abc "')
3117
stack = config.Stack([store.get_sections])
3118
self.assertEquals(' abc ', stack.get('foo'))
3120
def test_set_quoted_string(self):
3121
store = self.get_store()
3122
stack = config.Stack([store.get_sections], store)
3123
stack.set('foo', ' a b c ')
3125
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3128
class TestTransportIniFileStore(TestStore):
3130
def test_loading_unknown_file_fails(self):
3131
store = config.TransportIniFileStore(self.get_transport(),
3133
self.assertRaises(errors.NoSuchFile, store.load)
3135
def test_invalid_content(self):
3136
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3137
self.assertEquals(False, store.is_loaded())
3138
exc = self.assertRaises(
3139
errors.ParseConfigError, store._load_from_string,
3140
'this is invalid !')
3141
self.assertEndsWith(exc.filename, 'foo.conf')
3142
# And the load failed
3143
self.assertEquals(False, store.is_loaded())
3145
def test_get_embedded_sections(self):
3146
# A more complicated example (which also shows that section names and
3147
# option names share the same name space...)
3148
# FIXME: This should be fixed by forbidding dicts as values ?
3149
# -- vila 2011-04-05
3150
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3151
store._load_from_string('''
3155
foo_in_DEFAULT=foo_DEFAULT
3163
sections = list(store.get_sections())
3164
self.assertLength(4, sections)
3165
# The default section has no name.
3166
# List values are provided as strings and need to be explicitly
3167
# converted by specifying from_unicode=list_from_store at option
3169
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3171
self.assertSectionContent(
3172
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3173
self.assertSectionContent(
3174
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3175
# sub sections are provided as embedded dicts.
3176
self.assertSectionContent(
3177
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3181
class TestLockableIniFileStore(TestStore):
3183
def test_create_store_in_created_dir(self):
3184
self.assertPathDoesNotExist('dir')
3185
t = self.get_transport('dir/subdir')
3186
store = config.LockableIniFileStore(t, 'foo.conf')
3187
store.get_mutable_section(None).set('foo', 'bar')
3189
self.assertPathExists('dir/subdir')
3192
class TestConcurrentStoreUpdates(TestStore):
3193
"""Test that Stores properly handle conccurent updates.
3195
New Store implementation may fail some of these tests but until such
3196
implementations exist it's hard to properly filter them from the scenarios
3197
applied here. If you encounter such a case, contact the bzr devs.
3200
scenarios = [(key, {'get_stack': builder}) for key, builder
3201
in config.test_stack_builder_registry.iteritems()]
3204
super(TestConcurrentStoreUpdates, self).setUp()
3205
self.stack = self.get_stack(self)
3206
if not isinstance(self.stack, config._CompatibleStack):
3207
raise tests.TestNotApplicable(
3208
'%s is not meant to be compatible with the old config design'
3210
self.stack.set('one', '1')
3211
self.stack.set('two', '2')
3213
self.stack.store.save()
3215
def test_simple_read_access(self):
3216
self.assertEquals('1', self.stack.get('one'))
3218
def test_simple_write_access(self):
3219
self.stack.set('one', 'one')
3220
self.assertEquals('one', self.stack.get('one'))
3222
def test_listen_to_the_last_speaker(self):
3224
c2 = self.get_stack(self)
3225
c1.set('one', 'ONE')
3226
c2.set('two', 'TWO')
3227
self.assertEquals('ONE', c1.get('one'))
3228
self.assertEquals('TWO', c2.get('two'))
3229
# The second update respect the first one
3230
self.assertEquals('ONE', c2.get('one'))
3232
def test_last_speaker_wins(self):
3233
# If the same config is not shared, the same variable modified twice
3234
# can only see a single result.
3236
c2 = self.get_stack(self)
3239
self.assertEquals('c2', c2.get('one'))
3240
# The first modification is still available until another refresh
3242
self.assertEquals('c1', c1.get('one'))
3243
c1.set('two', 'done')
3244
self.assertEquals('c2', c1.get('one'))
3246
def test_writes_are_serialized(self):
3248
c2 = self.get_stack(self)
3250
# We spawn a thread that will pause *during* the config saving.
3251
before_writing = threading.Event()
3252
after_writing = threading.Event()
3253
writing_done = threading.Event()
3254
c1_save_without_locking_orig = c1.store.save_without_locking
3255
def c1_save_without_locking():
3256
before_writing.set()
3257
c1_save_without_locking_orig()
3258
# The lock is held. We wait for the main thread to decide when to
3260
after_writing.wait()
3261
c1.store.save_without_locking = c1_save_without_locking
3265
t1 = threading.Thread(target=c1_set)
3266
# Collect the thread after the test
3267
self.addCleanup(t1.join)
3268
# Be ready to unblock the thread if the test goes wrong
3269
self.addCleanup(after_writing.set)
3271
before_writing.wait()
3272
self.assertRaises(errors.LockContention,
3273
c2.set, 'one', 'c2')
3274
self.assertEquals('c1', c1.get('one'))
3275
# Let the lock be released
3279
self.assertEquals('c2', c2.get('one'))
3281
def test_read_while_writing(self):
3283
# We spawn a thread that will pause *during* the write
3284
ready_to_write = threading.Event()
3285
do_writing = threading.Event()
3286
writing_done = threading.Event()
3287
# We override the _save implementation so we know the store is locked
3288
c1_save_without_locking_orig = c1.store.save_without_locking
3289
def c1_save_without_locking():
3290
ready_to_write.set()
3291
# The lock is held. We wait for the main thread to decide when to
3294
c1_save_without_locking_orig()
3296
c1.store.save_without_locking = c1_save_without_locking
3299
t1 = threading.Thread(target=c1_set)
3300
# Collect the thread after the test
3301
self.addCleanup(t1.join)
3302
# Be ready to unblock the thread if the test goes wrong
3303
self.addCleanup(do_writing.set)
3305
# Ensure the thread is ready to write
3306
ready_to_write.wait()
3307
self.assertEquals('c1', c1.get('one'))
3308
# If we read during the write, we get the old value
3309
c2 = self.get_stack(self)
3310
self.assertEquals('1', c2.get('one'))
3311
# Let the writing occur and ensure it occurred
3314
# Now we get the updated value
3315
c3 = self.get_stack(self)
3316
self.assertEquals('c1', c3.get('one'))
3318
# FIXME: It may be worth looking into removing the lock dir when it's not
3319
# needed anymore and look at possible fallouts for concurrent lockers. This
3320
# will matter if/when we use config files outside of bazaar directories
3321
# (.bazaar or .bzr) -- vila 20110-04-111
3324
class TestSectionMatcher(TestStore):
3326
scenarios = [('location', {'matcher': config.LocationMatcher}),
3327
('id', {'matcher': config.NameMatcher}),]
3330
super(TestSectionMatcher, self).setUp()
3331
# Any simple store is good enough
3332
self.get_store = config.test_store_builder_registry.get('configobj')
3334
def test_no_matches_for_empty_stores(self):
3335
store = self.get_store(self)
3336
store._load_from_string('')
3337
matcher = self.matcher(store, '/bar')
3338
self.assertEquals([], list(matcher.get_sections()))
3340
def test_build_doesnt_load_store(self):
3341
store = self.get_store(self)
3342
matcher = self.matcher(store, '/bar')
3343
self.assertFalse(store.is_loaded())
3346
class TestLocationSection(tests.TestCase):
3348
def get_section(self, options, extra_path):
3349
section = config.Section('foo', options)
3350
return config.LocationSection(section, extra_path)
3352
def test_simple_option(self):
3353
section = self.get_section({'foo': 'bar'}, '')
3354
self.assertEquals('bar', section.get('foo'))
3356
def test_option_with_extra_path(self):
3357
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3359
self.assertEquals('bar/baz', section.get('foo'))
3361
def test_invalid_policy(self):
3362
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3364
# invalid policies are ignored
3365
self.assertEquals('bar', section.get('foo'))
3368
class TestLocationMatcher(TestStore):
3371
super(TestLocationMatcher, self).setUp()
3372
# Any simple store is good enough
3373
self.get_store = config.test_store_builder_registry.get('configobj')
3375
def test_unrelated_section_excluded(self):
3376
store = self.get_store(self)
3377
store._load_from_string('''
3385
section=/foo/bar/baz
3389
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3391
[section.id for _, section in store.get_sections()])
3392
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3393
sections = [section for _, section in matcher.get_sections()]
3394
self.assertEquals(['/foo/bar', '/foo'],
3395
[section.id for section in sections])
3396
self.assertEquals(['quux', 'bar/quux'],
3397
[section.extra_path for section in sections])
3399
def test_more_specific_sections_first(self):
3400
store = self.get_store(self)
3401
store._load_from_string('''
3407
self.assertEquals(['/foo', '/foo/bar'],
3408
[section.id for _, section in store.get_sections()])
3409
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3410
sections = [section for _, section in matcher.get_sections()]
3411
self.assertEquals(['/foo/bar', '/foo'],
3412
[section.id for section in sections])
3413
self.assertEquals(['baz', 'bar/baz'],
3414
[section.extra_path for section in sections])
3416
def test_appendpath_in_no_name_section(self):
3417
# It's a bit weird to allow appendpath in a no-name section, but
3418
# someone may found a use for it
3419
store = self.get_store(self)
3420
store._load_from_string('''
3422
foo:policy = appendpath
3424
matcher = config.LocationMatcher(store, 'dir/subdir')
3425
sections = list(matcher.get_sections())
3426
self.assertLength(1, sections)
3427
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3429
def test_file_urls_are_normalized(self):
3430
store = self.get_store(self)
3431
if sys.platform == 'win32':
3432
expected_url = 'file:///C:/dir/subdir'
3433
expected_location = 'C:/dir/subdir'
3435
expected_url = 'file:///dir/subdir'
3436
expected_location = '/dir/subdir'
3437
matcher = config.LocationMatcher(store, expected_url)
3438
self.assertEquals(expected_location, matcher.location)
3441
class TestStartingPathMatcher(TestStore):
3444
super(TestStartingPathMatcher, self).setUp()
3445
# Any simple store is good enough
3446
self.store = config.IniFileStore()
3448
def assertSectionIDs(self, expected, location, content):
3449
self.store._load_from_string(content)
3450
matcher = config.StartingPathMatcher(self.store, location)
3451
sections = list(matcher.get_sections())
3452
self.assertLength(len(expected), sections)
3453
self.assertEqual(expected, [section.id for _, section in sections])
3456
def test_empty(self):
3457
self.assertSectionIDs([], self.get_url(), '')
3459
def test_url_vs_local_paths(self):
3460
# The matcher location is an url and the section names are local paths
3461
sections = self.assertSectionIDs(['/foo/bar', '/foo'],
3462
'file:///foo/bar/baz', '''\
3467
def test_local_path_vs_url(self):
3468
# The matcher location is a local path and the section names are urls
3469
sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3470
'/foo/bar/baz', '''\
3476
def test_no_name_section_included_when_present(self):
3477
# Note that other tests will cover the case where the no-name section
3478
# is empty and as such, not included.
3479
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3480
'/foo/bar/baz', '''\
3481
option = defined so the no-name section exists
3485
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3486
[s.locals['relpath'] for _, s in sections])
3488
def test_order_reversed(self):
3489
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3494
def test_unrelated_section_excluded(self):
3495
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3501
def test_glob_included(self):
3502
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3503
'/foo/bar/baz', '''\
3509
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3510
# nothing really is... as far using {relpath} to append it to something
3511
# else, this seems good enough though.
3512
self.assertEquals(['', 'baz', 'bar/baz'],
3513
[s.locals['relpath'] for _, s in sections])
3515
def test_respect_order(self):
3516
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3517
'/foo/bar/baz', '''\
3525
class TestNameMatcher(TestStore):
3528
super(TestNameMatcher, self).setUp()
3529
self.matcher = config.NameMatcher
3530
# Any simple store is good enough
3531
self.get_store = config.test_store_builder_registry.get('configobj')
3533
def get_matching_sections(self, name):
3534
store = self.get_store(self)
3535
store._load_from_string('''
3543
matcher = self.matcher(store, name)
3544
return list(matcher.get_sections())
3546
def test_matching(self):
3547
sections = self.get_matching_sections('foo')
3548
self.assertLength(1, sections)
3549
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3551
def test_not_matching(self):
3552
sections = self.get_matching_sections('baz')
3553
self.assertLength(0, sections)
3556
class TestBaseStackGet(tests.TestCase):
3559
super(TestBaseStackGet, self).setUp()
3560
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3562
def test_get_first_definition(self):
3563
store1 = config.IniFileStore()
3564
store1._load_from_string('foo=bar')
3565
store2 = config.IniFileStore()
3566
store2._load_from_string('foo=baz')
3567
conf = config.Stack([store1.get_sections, store2.get_sections])
3568
self.assertEquals('bar', conf.get('foo'))
3570
def test_get_with_registered_default_value(self):
3571
config.option_registry.register(config.Option('foo', default='bar'))
3572
conf_stack = config.Stack([])
3573
self.assertEquals('bar', conf_stack.get('foo'))
3575
def test_get_without_registered_default_value(self):
3576
config.option_registry.register(config.Option('foo'))
3577
conf_stack = config.Stack([])
3578
self.assertEquals(None, conf_stack.get('foo'))
3580
def test_get_without_default_value_for_not_registered(self):
3581
conf_stack = config.Stack([])
3582
self.assertEquals(None, conf_stack.get('foo'))
3584
def test_get_for_empty_section_callable(self):
3585
conf_stack = config.Stack([lambda : []])
3586
self.assertEquals(None, conf_stack.get('foo'))
3588
def test_get_for_broken_callable(self):
3589
# Trying to use and invalid callable raises an exception on first use
3590
conf_stack = config.Stack([object])
3591
self.assertRaises(TypeError, conf_stack.get, 'foo')
3594
class TestStackWithSimpleStore(tests.TestCase):
3597
super(TestStackWithSimpleStore, self).setUp()
3598
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3599
self.registry = config.option_registry
3601
def get_conf(self, content=None):
3602
return config.MemoryStack(content)
3604
def test_override_value_from_env(self):
3605
self.registry.register(
3606
config.Option('foo', default='bar', override_from_env=['FOO']))
3607
self.overrideEnv('FOO', 'quux')
3608
# Env variable provides a default taking over the option one
3609
conf = self.get_conf('foo=store')
3610
self.assertEquals('quux', conf.get('foo'))
3612
def test_first_override_value_from_env_wins(self):
3613
self.registry.register(
3614
config.Option('foo', default='bar',
3615
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3616
self.overrideEnv('FOO', 'foo')
3617
self.overrideEnv('BAZ', 'baz')
3618
# The first env var set wins
3619
conf = self.get_conf('foo=store')
3620
self.assertEquals('foo', conf.get('foo'))
3623
class TestMemoryStack(tests.TestCase):
3626
conf = config.MemoryStack('foo=bar')
3627
self.assertEquals('bar', conf.get('foo'))
3630
conf = config.MemoryStack('foo=bar')
3631
conf.set('foo', 'baz')
3632
self.assertEquals('baz', conf.get('foo'))
3634
def test_no_content(self):
3635
conf = config.MemoryStack()
3636
# No content means no loading
3637
self.assertFalse(conf.store.is_loaded())
3638
self.assertRaises(NotImplementedError, conf.get, 'foo')
3639
# But a content can still be provided
3640
conf.store._load_from_string('foo=bar')
3641
self.assertEquals('bar', conf.get('foo'))
3644
class TestStackWithTransport(tests.TestCaseWithTransport):
3646
scenarios = [(key, {'get_stack': builder}) for key, builder
3647
in config.test_stack_builder_registry.iteritems()]
3650
class TestConcreteStacks(TestStackWithTransport):
3652
def test_build_stack(self):
3653
# Just a smoke test to help debug builders
3654
stack = self.get_stack(self)
3657
class TestStackGet(TestStackWithTransport):
3660
super(TestStackGet, self).setUp()
3661
self.conf = self.get_stack(self)
3663
def test_get_for_empty_stack(self):
3664
self.assertEquals(None, self.conf.get('foo'))
3666
def test_get_hook(self):
3667
self.conf.set('foo', 'bar')
3671
config.ConfigHooks.install_named_hook('get', hook, None)
3672
self.assertLength(0, calls)
3673
value = self.conf.get('foo')
3674
self.assertEquals('bar', value)
3675
self.assertLength(1, calls)
3676
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3679
class TestStackGetWithConverter(tests.TestCase):
3682
super(TestStackGetWithConverter, self).setUp()
3683
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3684
self.registry = config.option_registry
3686
def get_conf(self, content=None):
3687
return config.MemoryStack(content)
3689
def register_bool_option(self, name, default=None, default_from_env=None):
3690
b = config.Option(name, help='A boolean.',
3691
default=default, default_from_env=default_from_env,
3692
from_unicode=config.bool_from_store)
3693
self.registry.register(b)
3695
def test_get_default_bool_None(self):
3696
self.register_bool_option('foo')
3697
conf = self.get_conf('')
3698
self.assertEquals(None, conf.get('foo'))
3700
def test_get_default_bool_True(self):
3701
self.register_bool_option('foo', u'True')
3702
conf = self.get_conf('')
3703
self.assertEquals(True, conf.get('foo'))
3705
def test_get_default_bool_False(self):
3706
self.register_bool_option('foo', False)
3707
conf = self.get_conf('')
3708
self.assertEquals(False, conf.get('foo'))
3710
def test_get_default_bool_False_as_string(self):
3711
self.register_bool_option('foo', u'False')
3712
conf = self.get_conf('')
3713
self.assertEquals(False, conf.get('foo'))
3715
def test_get_default_bool_from_env_converted(self):
3716
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3717
self.overrideEnv('FOO', 'False')
3718
conf = self.get_conf('')
3719
self.assertEquals(False, conf.get('foo'))
3721
def test_get_default_bool_when_conversion_fails(self):
3722
self.register_bool_option('foo', default='True')
3723
conf = self.get_conf('foo=invalid boolean')
3724
self.assertEquals(True, conf.get('foo'))
3726
def register_integer_option(self, name,
3727
default=None, default_from_env=None):
3728
i = config.Option(name, help='An integer.',
3729
default=default, default_from_env=default_from_env,
3730
from_unicode=config.int_from_store)
3731
self.registry.register(i)
3733
def test_get_default_integer_None(self):
3734
self.register_integer_option('foo')
3735
conf = self.get_conf('')
3736
self.assertEquals(None, conf.get('foo'))
3738
def test_get_default_integer(self):
3739
self.register_integer_option('foo', 42)
3740
conf = self.get_conf('')
3741
self.assertEquals(42, conf.get('foo'))
3743
def test_get_default_integer_as_string(self):
3744
self.register_integer_option('foo', u'42')
3745
conf = self.get_conf('')
3746
self.assertEquals(42, conf.get('foo'))
3748
def test_get_default_integer_from_env(self):
3749
self.register_integer_option('foo', default_from_env=['FOO'])
3750
self.overrideEnv('FOO', '18')
3751
conf = self.get_conf('')
3752
self.assertEquals(18, conf.get('foo'))
3754
def test_get_default_integer_when_conversion_fails(self):
3755
self.register_integer_option('foo', default='12')
3756
conf = self.get_conf('foo=invalid integer')
3757
self.assertEquals(12, conf.get('foo'))
3759
def register_list_option(self, name, default=None, default_from_env=None):
3760
l = config.ListOption(name, help='A list.', default=default,
3761
default_from_env=default_from_env)
3762
self.registry.register(l)
3764
def test_get_default_list_None(self):
3765
self.register_list_option('foo')
3766
conf = self.get_conf('')
3767
self.assertEquals(None, conf.get('foo'))
3769
def test_get_default_list_empty(self):
3770
self.register_list_option('foo', '')
3771
conf = self.get_conf('')
3772
self.assertEquals([], conf.get('foo'))
3774
def test_get_default_list_from_env(self):
3775
self.register_list_option('foo', default_from_env=['FOO'])
3776
self.overrideEnv('FOO', '')
3777
conf = self.get_conf('')
3778
self.assertEquals([], conf.get('foo'))
3780
def test_get_with_list_converter_no_item(self):
3781
self.register_list_option('foo', None)
3782
conf = self.get_conf('foo=,')
3783
self.assertEquals([], conf.get('foo'))
3785
def test_get_with_list_converter_many_items(self):
3786
self.register_list_option('foo', None)
3787
conf = self.get_conf('foo=m,o,r,e')
3788
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3790
def test_get_with_list_converter_embedded_spaces_many_items(self):
3791
self.register_list_option('foo', None)
3792
conf = self.get_conf('foo=" bar", "baz "')
3793
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3795
def test_get_with_list_converter_stripped_spaces_many_items(self):
3796
self.register_list_option('foo', None)
3797
conf = self.get_conf('foo= bar , baz ')
3798
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3801
class TestIterOptionRefs(tests.TestCase):
3802
"""iter_option_refs is a bit unusual, document some cases."""
3804
def assertRefs(self, expected, string):
3805
self.assertEquals(expected, list(config.iter_option_refs(string)))
3807
def test_empty(self):
3808
self.assertRefs([(False, '')], '')
3810
def test_no_refs(self):
3811
self.assertRefs([(False, 'foo bar')], 'foo bar')
3813
def test_single_ref(self):
3814
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3816
def test_broken_ref(self):
3817
self.assertRefs([(False, '{foo')], '{foo')
3819
def test_embedded_ref(self):
3820
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3823
def test_two_refs(self):
3824
self.assertRefs([(False, ''), (True, '{foo}'),
3825
(False, ''), (True, '{bar}'),
3829
def test_newline_in_refs_are_not_matched(self):
3830
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3833
class TestStackExpandOptions(tests.TestCaseWithTransport):
3836
super(TestStackExpandOptions, self).setUp()
3837
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3838
self.registry = config.option_registry
3839
self.conf = build_branch_stack(self)
3841
def assertExpansion(self, expected, string, env=None):
3842
self.assertEquals(expected, self.conf.expand_options(string, env))
3844
def test_no_expansion(self):
3845
self.assertExpansion('foo', 'foo')
3847
def test_expand_default_value(self):
3848
self.conf.store._load_from_string('bar=baz')
3849
self.registry.register(config.Option('foo', default=u'{bar}'))
3850
self.assertEquals('baz', self.conf.get('foo', expand=True))
3852
def test_expand_default_from_env(self):
3853
self.conf.store._load_from_string('bar=baz')
3854
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3855
self.overrideEnv('FOO', '{bar}')
3856
self.assertEquals('baz', self.conf.get('foo', expand=True))
3858
def test_expand_default_on_failed_conversion(self):
3859
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3860
self.registry.register(
3861
config.Option('foo', default=u'{bar}',
3862
from_unicode=config.int_from_store))
3863
self.assertEquals(42, self.conf.get('foo', expand=True))
3865
def test_env_adding_options(self):
3866
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3868
def test_env_overriding_options(self):
3869
self.conf.store._load_from_string('foo=baz')
3870
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3872
def test_simple_ref(self):
3873
self.conf.store._load_from_string('foo=xxx')
3874
self.assertExpansion('xxx', '{foo}')
3876
def test_unknown_ref(self):
3877
self.assertRaises(errors.ExpandingUnknownOption,
3878
self.conf.expand_options, '{foo}')
3880
def test_indirect_ref(self):
3881
self.conf.store._load_from_string('''
3885
self.assertExpansion('xxx', '{bar}')
3887
def test_embedded_ref(self):
3888
self.conf.store._load_from_string('''
3892
self.assertExpansion('xxx', '{{bar}}')
3894
def test_simple_loop(self):
3895
self.conf.store._load_from_string('foo={foo}')
3896
self.assertRaises(errors.OptionExpansionLoop,
3897
self.conf.expand_options, '{foo}')
3899
def test_indirect_loop(self):
3900
self.conf.store._load_from_string('''
3904
e = self.assertRaises(errors.OptionExpansionLoop,
3905
self.conf.expand_options, '{foo}')
3906
self.assertEquals('foo->bar->baz', e.refs)
3907
self.assertEquals('{foo}', e.string)
3909
def test_list(self):
3910
self.conf.store._load_from_string('''
3914
list={foo},{bar},{baz}
3916
self.registry.register(
3917
config.ListOption('list'))
3918
self.assertEquals(['start', 'middle', 'end'],
3919
self.conf.get('list', expand=True))
3921
def test_cascading_list(self):
3922
self.conf.store._load_from_string('''
3928
self.registry.register(
3929
config.ListOption('list'))
3930
self.assertEquals(['start', 'middle', 'end'],
3931
self.conf.get('list', expand=True))
3933
def test_pathologically_hidden_list(self):
3934
self.conf.store._load_from_string('''
3940
hidden={start}{middle}{end}
3942
# What matters is what the registration says, the conversion happens
3943
# only after all expansions have been performed
3944
self.registry.register(config.ListOption('hidden'))
3945
self.assertEquals(['bin', 'go'],
3946
self.conf.get('hidden', expand=True))
3949
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3952
super(TestStackCrossSectionsExpand, self).setUp()
3954
def get_config(self, location, string):
3957
# Since we don't save the config we won't strictly require to inherit
3958
# from TestCaseInTempDir, but an error occurs so quickly...
3959
c = config.LocationStack(location)
3960
c.store._load_from_string(string)
3963
def test_dont_cross_unrelated_section(self):
3964
c = self.get_config('/another/branch/path','''
3969
[/another/branch/path]
3972
self.assertRaises(errors.ExpandingUnknownOption,
3973
c.get, 'bar', expand=True)
3975
def test_cross_related_sections(self):
3976
c = self.get_config('/project/branch/path','''
3980
[/project/branch/path]
3983
self.assertEquals('quux', c.get('bar', expand=True))
3986
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3988
def test_cross_global_locations(self):
3989
l_store = config.LocationStore()
3990
l_store._load_from_string('''
3996
g_store = config.GlobalStore()
3997
g_store._load_from_string('''
4003
stack = config.LocationStack('/branch')
4004
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4005
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4008
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4010
def test_expand_locals_empty(self):
4011
l_store = config.LocationStore()
4012
l_store._load_from_string('''
4013
[/home/user/project]
4018
stack = config.LocationStack('/home/user/project/')
4019
self.assertEquals('', stack.get('base', expand=True))
4020
self.assertEquals('', stack.get('rel', expand=True))
4022
def test_expand_basename_locally(self):
4023
l_store = config.LocationStore()
4024
l_store._load_from_string('''
4025
[/home/user/project]
4029
stack = config.LocationStack('/home/user/project/branch')
4030
self.assertEquals('branch', stack.get('bfoo', expand=True))
4032
def test_expand_basename_locally_longer_path(self):
4033
l_store = config.LocationStore()
4034
l_store._load_from_string('''
4039
stack = config.LocationStack('/home/user/project/dir/branch')
4040
self.assertEquals('branch', stack.get('bfoo', expand=True))
4042
def test_expand_relpath_locally(self):
4043
l_store = config.LocationStore()
4044
l_store._load_from_string('''
4045
[/home/user/project]
4046
lfoo = loc-foo/{relpath}
4049
stack = config.LocationStack('/home/user/project/branch')
4050
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4052
def test_expand_relpath_unknonw_in_global(self):
4053
g_store = config.GlobalStore()
4054
g_store._load_from_string('''
4059
stack = config.LocationStack('/home/user/project/branch')
4060
self.assertRaises(errors.ExpandingUnknownOption,
4061
stack.get, 'gfoo', expand=True)
4063
def test_expand_local_option_locally(self):
4064
l_store = config.LocationStore()
4065
l_store._load_from_string('''
4066
[/home/user/project]
4067
lfoo = loc-foo/{relpath}
4071
g_store = config.GlobalStore()
4072
g_store._load_from_string('''
4078
stack = config.LocationStack('/home/user/project/branch')
4079
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4080
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4082
def test_locals_dont_leak(self):
4083
"""Make sure we chose the right local in presence of several sections.
4085
l_store = config.LocationStore()
4086
l_store._load_from_string('''
4088
lfoo = loc-foo/{relpath}
4089
[/home/user/project]
4090
lfoo = loc-foo/{relpath}
4093
stack = config.LocationStack('/home/user/project/branch')
4094
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4095
stack = config.LocationStack('/home/user/bar/baz')
4096
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
4100
class TestStackSet(TestStackWithTransport):
4102
def test_simple_set(self):
4103
conf = self.get_stack(self)
4104
self.assertEquals(None, conf.get('foo'))
4105
conf.set('foo', 'baz')
4106
# Did we get it back ?
4107
self.assertEquals('baz', conf.get('foo'))
4109
def test_set_creates_a_new_section(self):
4110
conf = self.get_stack(self)
4111
conf.set('foo', 'baz')
4112
self.assertEquals, 'baz', conf.get('foo')
4114
def test_set_hook(self):
4118
config.ConfigHooks.install_named_hook('set', hook, None)
4119
self.assertLength(0, calls)
4120
conf = self.get_stack(self)
4121
conf.set('foo', 'bar')
4122
self.assertLength(1, calls)
4123
self.assertEquals((conf, 'foo', 'bar'), calls[0])
4126
class TestStackRemove(TestStackWithTransport):
4128
def test_remove_existing(self):
4129
conf = self.get_stack(self)
4130
conf.set('foo', 'bar')
4131
self.assertEquals('bar', conf.get('foo'))
4133
# Did we get it back ?
4134
self.assertEquals(None, conf.get('foo'))
4136
def test_remove_unknown(self):
4137
conf = self.get_stack(self)
4138
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
4140
def test_remove_hook(self):
4144
config.ConfigHooks.install_named_hook('remove', hook, None)
4145
self.assertLength(0, calls)
4146
conf = self.get_stack(self)
4147
conf.set('foo', 'bar')
4149
self.assertLength(1, calls)
4150
self.assertEquals((conf, 'foo'), calls[0])
4153
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
4156
super(TestConfigGetOptions, self).setUp()
4157
create_configs(self)
4159
def test_no_variable(self):
4160
# Using branch should query branch, locations and bazaar
4161
self.assertOptions([], self.branch_config)
4163
def test_option_in_bazaar(self):
4164
self.bazaar_config.set_user_option('file', 'bazaar')
4165
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
4168
def test_option_in_locations(self):
4169
self.locations_config.set_user_option('file', 'locations')
4171
[('file', 'locations', self.tree.basedir, 'locations')],
4172
self.locations_config)
4174
def test_option_in_branch(self):
4175
self.branch_config.set_user_option('file', 'branch')
4176
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
4179
def test_option_in_bazaar_and_branch(self):
4180
self.bazaar_config.set_user_option('file', 'bazaar')
4181
self.branch_config.set_user_option('file', 'branch')
4182
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4183
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4186
def test_option_in_branch_and_locations(self):
4187
# Hmm, locations override branch :-/
4188
self.locations_config.set_user_option('file', 'locations')
4189
self.branch_config.set_user_option('file', 'branch')
4191
[('file', 'locations', self.tree.basedir, 'locations'),
4192
('file', 'branch', 'DEFAULT', 'branch'),],
4195
def test_option_in_bazaar_locations_and_branch(self):
4196
self.bazaar_config.set_user_option('file', 'bazaar')
4197
self.locations_config.set_user_option('file', 'locations')
4198
self.branch_config.set_user_option('file', 'branch')
4200
[('file', 'locations', self.tree.basedir, 'locations'),
4201
('file', 'branch', 'DEFAULT', 'branch'),
4202
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4206
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
4209
super(TestConfigRemoveOption, self).setUp()
4210
create_configs_with_file_option(self)
4212
def test_remove_in_locations(self):
4213
self.locations_config.remove_user_option('file', self.tree.basedir)
4215
[('file', 'branch', 'DEFAULT', 'branch'),
4216
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4219
def test_remove_in_branch(self):
4220
self.branch_config.remove_user_option('file')
4222
[('file', 'locations', self.tree.basedir, 'locations'),
4223
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4226
def test_remove_in_bazaar(self):
4227
self.bazaar_config.remove_user_option('file')
4229
[('file', 'locations', self.tree.basedir, 'locations'),
4230
('file', 'branch', 'DEFAULT', 'branch'),],
4234
class TestConfigGetSections(tests.TestCaseWithTransport):
4237
super(TestConfigGetSections, self).setUp()
4238
create_configs(self)
4240
def assertSectionNames(self, expected, conf, name=None):
4241
"""Check which sections are returned for a given config.
4243
If fallback configurations exist their sections can be included.
4245
:param expected: A list of section names.
4247
:param conf: The configuration that will be queried.
4249
:param name: An optional section name that will be passed to
4252
sections = list(conf._get_sections(name))
4253
self.assertLength(len(expected), sections)
4254
self.assertEqual(expected, [name for name, _, _ in sections])
4256
def test_bazaar_default_section(self):
4257
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4259
def test_locations_default_section(self):
4260
# No sections are defined in an empty file
4261
self.assertSectionNames([], self.locations_config)
4263
def test_locations_named_section(self):
4264
self.locations_config.set_user_option('file', 'locations')
4265
self.assertSectionNames([self.tree.basedir], self.locations_config)
4267
def test_locations_matching_sections(self):
4268
loc_config = self.locations_config
4269
loc_config.set_user_option('file', 'locations')
4270
# We need to cheat a bit here to create an option in sections above and
4271
# below the 'location' one.
4272
parser = loc_config._get_parser()
4273
# locations.cong deals with '/' ignoring native os.sep
4274
location_names = self.tree.basedir.split('/')
4275
parent = '/'.join(location_names[:-1])
4276
child = '/'.join(location_names + ['child'])
4278
parser[parent]['file'] = 'parent'
4280
parser[child]['file'] = 'child'
4281
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4283
def test_branch_data_default_section(self):
4284
self.assertSectionNames([None],
4285
self.branch_config._get_branch_data_config())
4287
def test_branch_default_sections(self):
4288
# No sections are defined in an empty locations file
4289
self.assertSectionNames([None, 'DEFAULT'],
4291
# Unless we define an option
4292
self.branch_config._get_location_config().set_user_option(
4293
'file', 'locations')
4294
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4297
def test_bazaar_named_section(self):
4298
# We need to cheat as the API doesn't give direct access to sections
4299
# other than DEFAULT.
4300
self.bazaar_config.set_alias('bazaar', 'bzr')
4301
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
4304
class TestAuthenticationConfigFile(tests.TestCase):
1316
4305
"""Test the authentication.conf file matching"""