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)
2373
class TestOptionConverterMixin(object):
2375
def assertConverted(self, expected, opt, value):
2376
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2378
def assertWarns(self, opt, value):
2381
warnings.append(args[0] % args[1:])
2382
self.overrideAttr(trace, 'warning', warning)
2383
self.assertEquals(None, opt.convert_from_unicode(None, value))
2384
self.assertLength(1, warnings)
2386
'Value "%s" is not valid for "%s"' % (value, opt.name),
2389
def assertErrors(self, opt, value):
2390
self.assertRaises(errors.ConfigOptionValueError,
2391
opt.convert_from_unicode, None, value)
2393
def assertConvertInvalid(self, opt, invalid_value):
2395
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2396
opt.invalid = 'warning'
2397
self.assertWarns(opt, invalid_value)
2398
opt.invalid = 'error'
2399
self.assertErrors(opt, invalid_value)
2402
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2404
def get_option(self):
2405
return config.Option('foo', help='A boolean.',
2406
from_unicode=config.bool_from_store)
2408
def test_convert_invalid(self):
2409
opt = self.get_option()
2410
# A string that is not recognized as a boolean
2411
self.assertConvertInvalid(opt, u'invalid-boolean')
2412
# A list of strings is never recognized as a boolean
2413
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2415
def test_convert_valid(self):
2416
opt = self.get_option()
2417
self.assertConverted(True, opt, u'True')
2418
self.assertConverted(True, opt, u'1')
2419
self.assertConverted(False, opt, u'False')
2422
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2424
def get_option(self):
2425
return config.Option('foo', help='An integer.',
2426
from_unicode=config.int_from_store)
2428
def test_convert_invalid(self):
2429
opt = self.get_option()
2430
# A string that is not recognized as an integer
2431
self.assertConvertInvalid(opt, u'forty-two')
2432
# A list of strings is never recognized as an integer
2433
self.assertConvertInvalid(opt, [u'a', u'list'])
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
self.assertConverted(16, opt, u'16')
2440
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2442
def get_option(self):
2443
return config.Option('foo', help='An integer in SI units.',
2444
from_unicode=config.int_SI_from_store)
2446
def test_convert_invalid(self):
2447
opt = self.get_option()
2448
self.assertConvertInvalid(opt, u'not-a-unit')
2449
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2450
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2451
self.assertConvertInvalid(opt, u'1GG')
2452
self.assertConvertInvalid(opt, u'1Mbb')
2453
self.assertConvertInvalid(opt, u'1MM')
2455
def test_convert_valid(self):
2456
opt = self.get_option()
2457
self.assertConverted(int(5e3), opt, u'5kb')
2458
self.assertConverted(int(5e6), opt, u'5M')
2459
self.assertConverted(int(5e6), opt, u'5MB')
2460
self.assertConverted(int(5e9), opt, u'5g')
2461
self.assertConverted(int(5e9), opt, u'5gB')
2462
self.assertConverted(100, opt, u'100')
2465
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2467
def get_option(self):
2468
return config.ListOption('foo', help='A list.')
2470
def test_convert_invalid(self):
2471
opt = self.get_option()
2472
# We don't even try to convert a list into a list, we only expect
2474
self.assertConvertInvalid(opt, [1])
2475
# No string is invalid as all forms can be converted to a list
2477
def test_convert_valid(self):
2478
opt = self.get_option()
2479
# An empty string is an empty list
2480
self.assertConverted([], opt, '') # Using a bare str() just in case
2481
self.assertConverted([], opt, u'')
2483
self.assertConverted([u'True'], opt, u'True')
2485
self.assertConverted([u'42'], opt, u'42')
2487
self.assertConverted([u'bar'], opt, u'bar')
2490
class TestOptionRegistry(tests.TestCase):
2493
super(TestOptionRegistry, self).setUp()
2494
# Always start with an empty registry
2495
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2496
self.registry = config.option_registry
2498
def test_register(self):
2499
opt = config.Option('foo')
2500
self.registry.register(opt)
2501
self.assertIs(opt, self.registry.get('foo'))
2503
def test_registered_help(self):
2504
opt = config.Option('foo', help='A simple option')
2505
self.registry.register(opt)
2506
self.assertEquals('A simple option', self.registry.get_help('foo'))
2508
lazy_option = config.Option('lazy_foo', help='Lazy help')
2510
def test_register_lazy(self):
2511
self.registry.register_lazy('lazy_foo', self.__module__,
2512
'TestOptionRegistry.lazy_option')
2513
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2515
def test_registered_lazy_help(self):
2516
self.registry.register_lazy('lazy_foo', self.__module__,
2517
'TestOptionRegistry.lazy_option')
2518
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2521
class TestRegisteredOptions(tests.TestCase):
2522
"""All registered options should verify some constraints."""
2524
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2525
in config.option_registry.iteritems()]
2528
super(TestRegisteredOptions, self).setUp()
2529
self.registry = config.option_registry
2531
def test_proper_name(self):
2532
# An option should be registered under its own name, this can't be
2533
# checked at registration time for the lazy ones.
2534
self.assertEquals(self.option_name, self.option.name)
2536
def test_help_is_set(self):
2537
option_help = self.registry.get_help(self.option_name)
2538
self.assertNotEquals(None, option_help)
2539
# Come on, think about the user, he really wants to know what the
2541
self.assertIsNot(None, option_help)
2542
self.assertNotEquals('', option_help)
2545
class TestSection(tests.TestCase):
2547
# FIXME: Parametrize so that all sections produced by Stores run these
2548
# tests -- vila 2011-04-01
2550
def test_get_a_value(self):
2551
a_dict = dict(foo='bar')
2552
section = config.Section('myID', a_dict)
2553
self.assertEquals('bar', section.get('foo'))
2555
def test_get_unknown_option(self):
2557
section = config.Section(None, a_dict)
2558
self.assertEquals('out of thin air',
2559
section.get('foo', 'out of thin air'))
2561
def test_options_is_shared(self):
2563
section = config.Section(None, a_dict)
2564
self.assertIs(a_dict, section.options)
2567
class TestMutableSection(tests.TestCase):
2569
scenarios = [('mutable',
2571
lambda opts: config.MutableSection('myID', opts)},),
2575
a_dict = dict(foo='bar')
2576
section = self.get_section(a_dict)
2577
section.set('foo', 'new_value')
2578
self.assertEquals('new_value', section.get('foo'))
2579
# The change appears in the shared section
2580
self.assertEquals('new_value', a_dict.get('foo'))
2581
# We keep track of the change
2582
self.assertTrue('foo' in section.orig)
2583
self.assertEquals('bar', section.orig.get('foo'))
2585
def test_set_preserve_original_once(self):
2586
a_dict = dict(foo='bar')
2587
section = self.get_section(a_dict)
2588
section.set('foo', 'first_value')
2589
section.set('foo', 'second_value')
2590
# We keep track of the original value
2591
self.assertTrue('foo' in section.orig)
2592
self.assertEquals('bar', section.orig.get('foo'))
2594
def test_remove(self):
2595
a_dict = dict(foo='bar')
2596
section = self.get_section(a_dict)
2597
section.remove('foo')
2598
# We get None for unknown options via the default value
2599
self.assertEquals(None, section.get('foo'))
2600
# Or we just get the default value
2601
self.assertEquals('unknown', section.get('foo', 'unknown'))
2602
self.assertFalse('foo' in section.options)
2603
# We keep track of the deletion
2604
self.assertTrue('foo' in section.orig)
2605
self.assertEquals('bar', section.orig.get('foo'))
2607
def test_remove_new_option(self):
2609
section = self.get_section(a_dict)
2610
section.set('foo', 'bar')
2611
section.remove('foo')
2612
self.assertFalse('foo' in section.options)
2613
# The option didn't exist initially so it we need to keep track of it
2614
# with a special value
2615
self.assertTrue('foo' in section.orig)
2616
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2619
class TestCommandLineStore(tests.TestCase):
2622
super(TestCommandLineStore, self).setUp()
2623
self.store = config.CommandLineStore()
2624
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2626
def get_section(self):
2627
"""Get the unique section for the command line overrides."""
2628
sections = list(self.store.get_sections())
2629
self.assertLength(1, sections)
2630
store, section = sections[0]
2631
self.assertEquals(self.store, store)
2634
def test_no_override(self):
2635
self.store._from_cmdline([])
2636
section = self.get_section()
2637
self.assertLength(0, list(section.iter_option_names()))
2639
def test_simple_override(self):
2640
self.store._from_cmdline(['a=b'])
2641
section = self.get_section()
2642
self.assertEqual('b', section.get('a'))
2644
def test_list_override(self):
2645
opt = config.ListOption('l')
2646
config.option_registry.register(opt)
2647
self.store._from_cmdline(['l=1,2,3'])
2648
val = self.get_section().get('l')
2649
self.assertEqual('1,2,3', val)
2650
# Reminder: lists should be registered as such explicitely, otherwise
2651
# the conversion needs to be done afterwards.
2652
self.assertEqual(['1', '2', '3'],
2653
opt.convert_from_unicode(self.store, val))
2655
def test_multiple_overrides(self):
2656
self.store._from_cmdline(['a=b', 'x=y'])
2657
section = self.get_section()
2658
self.assertEquals('b', section.get('a'))
2659
self.assertEquals('y', section.get('x'))
2661
def test_wrong_syntax(self):
2662
self.assertRaises(errors.BzrCommandError,
2663
self.store._from_cmdline, ['a=b', 'c'])
2665
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2667
scenarios = [(key, {'get_store': builder}) for key, builder
2668
in config.test_store_builder_registry.iteritems()] + [
2669
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2672
store = self.get_store(self)
2673
if type(store) == config.TransportIniFileStore:
2674
raise tests.TestNotApplicable(
2675
"%s is not a concrete Store implementation"
2676
" so it doesn't need an id" % (store.__class__.__name__,))
2677
self.assertIsNot(None, store.id)
2680
class TestStore(tests.TestCaseWithTransport):
2682
def assertSectionContent(self, expected, (store, section)):
2683
"""Assert that some options have the proper values in a section."""
2684
expected_name, expected_options = expected
2685
self.assertEquals(expected_name, section.id)
2688
dict([(k, section.get(k)) for k in expected_options.keys()]))
2691
class TestReadonlyStore(TestStore):
2693
scenarios = [(key, {'get_store': builder}) for key, builder
2694
in config.test_store_builder_registry.iteritems()]
2696
def test_building_delays_load(self):
2697
store = self.get_store(self)
2698
self.assertEquals(False, store.is_loaded())
2699
store._load_from_string('')
2700
self.assertEquals(True, store.is_loaded())
2702
def test_get_no_sections_for_empty(self):
2703
store = self.get_store(self)
2704
store._load_from_string('')
2705
self.assertEquals([], list(store.get_sections()))
2707
def test_get_default_section(self):
2708
store = self.get_store(self)
2709
store._load_from_string('foo=bar')
2710
sections = list(store.get_sections())
2711
self.assertLength(1, sections)
2712
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2714
def test_get_named_section(self):
2715
store = self.get_store(self)
2716
store._load_from_string('[baz]\nfoo=bar')
2717
sections = list(store.get_sections())
2718
self.assertLength(1, sections)
2719
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2721
def test_load_from_string_fails_for_non_empty_store(self):
2722
store = self.get_store(self)
2723
store._load_from_string('foo=bar')
2724
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2727
class TestStoreQuoting(TestStore):
2729
scenarios = [(key, {'get_store': builder}) for key, builder
2730
in config.test_store_builder_registry.iteritems()]
2733
super(TestStoreQuoting, self).setUp()
2734
self.store = self.get_store(self)
2735
# We need a loaded store but any content will do
2736
self.store._load_from_string('')
2738
def assertIdempotent(self, s):
2739
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2741
What matters here is that option values, as they appear in a store, can
2742
be safely round-tripped out of the store and back.
2744
:param s: A string, quoted if required.
2746
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2747
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2749
def test_empty_string(self):
2750
if isinstance(self.store, config.IniFileStore):
2751
# configobj._quote doesn't handle empty values
2752
self.assertRaises(AssertionError,
2753
self.assertIdempotent, '')
2755
self.assertIdempotent('')
2756
# But quoted empty strings are ok
2757
self.assertIdempotent('""')
2759
def test_embedded_spaces(self):
2760
self.assertIdempotent('" a b c "')
2762
def test_embedded_commas(self):
2763
self.assertIdempotent('" a , b c "')
2765
def test_simple_comma(self):
2766
if isinstance(self.store, config.IniFileStore):
2767
# configobj requires that lists are special-cased
2768
self.assertRaises(AssertionError,
2769
self.assertIdempotent, ',')
2771
self.assertIdempotent(',')
2772
# When a single comma is required, quoting is also required
2773
self.assertIdempotent('","')
2775
def test_list(self):
2776
if isinstance(self.store, config.IniFileStore):
2777
# configobj requires that lists are special-cased
2778
self.assertRaises(AssertionError,
2779
self.assertIdempotent, 'a,b')
2781
self.assertIdempotent('a,b')
2784
class TestDictFromStore(tests.TestCase):
2786
def test_unquote_not_string(self):
2787
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2788
value = conf.get('a_section')
2789
# Urgh, despite 'conf' asking for the no-name section, we get the
2790
# content of another section as a dict o_O
2791
self.assertEquals({'a': '1'}, value)
2792
unquoted = conf.store.unquote(value)
2793
# Which cannot be unquoted but shouldn't crash either (the use cases
2794
# are getting the value or displaying it. In the later case, '%s' will
2796
self.assertEquals({'a': '1'}, unquoted)
2797
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2800
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2801
"""Simulate loading a config store 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
t = self.get_transport()
2814
# From http://pad.lv/799212
2815
unicode_user = u'b\N{Euro Sign}ar'
2816
unicode_content = u'user=%s' % (unicode_user,)
2817
utf8_content = unicode_content.encode('utf8')
2818
# Store the raw content in the config file
2819
t.put_bytes('foo.conf', utf8_content)
2820
store = config.TransportIniFileStore(t, 'foo.conf')
2822
stack = config.Stack([store.get_sections], store)
2823
self.assertEquals(unicode_user, stack.get('user'))
2825
def test_load_non_ascii(self):
2826
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2827
t = self.get_transport()
2828
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2829
store = config.TransportIniFileStore(t, 'foo.conf')
2830
self.assertRaises(errors.ConfigContentError, store.load)
2832
def test_load_erroneous_content(self):
2833
"""Ensure we display a proper error on content that can't be parsed."""
2834
t = self.get_transport()
2835
t.put_bytes('foo.conf', '[open_section\n')
2836
store = config.TransportIniFileStore(t, 'foo.conf')
2837
self.assertRaises(errors.ParseConfigError, store.load)
2839
def test_load_permission_denied(self):
2840
"""Ensure we get warned when trying to load an inaccessible file."""
2843
warnings.append(args[0] % args[1:])
2844
self.overrideAttr(trace, 'warning', warning)
2846
t = self.get_transport()
2848
def get_bytes(relpath):
2849
raise errors.PermissionDenied(relpath, "")
2850
t.get_bytes = get_bytes
2851
store = config.TransportIniFileStore(t, 'foo.conf')
2852
self.assertRaises(errors.PermissionDenied, store.load)
2855
[u'Permission denied while trying to load configuration store %s.'
2856
% store.external_url()])
2859
class TestIniConfigContent(tests.TestCaseWithTransport):
2860
"""Simulate loading a IniBasedConfig with content of various encodings.
2862
All files produced by bzr are in utf8 content.
2864
Users may modify them manually and end up with a file that can't be
2865
loaded. We need to issue proper error messages in this case.
2868
invalid_utf8_char = '\xff'
2870
def test_load_utf8(self):
2871
"""Ensure we can load an utf8-encoded file."""
2872
# From http://pad.lv/799212
2873
unicode_user = u'b\N{Euro Sign}ar'
2874
unicode_content = u'user=%s' % (unicode_user,)
2875
utf8_content = unicode_content.encode('utf8')
2876
# Store the raw content in the config file
2877
with open('foo.conf', 'wb') as f:
2878
f.write(utf8_content)
2879
conf = config.IniBasedConfig(file_name='foo.conf')
2880
self.assertEquals(unicode_user, conf.get_user_option('user'))
2882
def test_load_badly_encoded_content(self):
2883
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2884
with open('foo.conf', 'wb') as f:
2885
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2886
conf = config.IniBasedConfig(file_name='foo.conf')
2887
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2889
def test_load_erroneous_content(self):
2890
"""Ensure we display a proper error on content that can't be parsed."""
2891
with open('foo.conf', 'wb') as f:
2892
f.write('[open_section\n')
2893
conf = config.IniBasedConfig(file_name='foo.conf')
2894
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2897
class TestMutableStore(TestStore):
2899
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2900
in config.test_store_builder_registry.iteritems()]
2903
super(TestMutableStore, self).setUp()
2904
self.transport = self.get_transport()
2906
def has_store(self, store):
2907
store_basename = urlutils.relative_url(self.transport.external_url(),
2908
store.external_url())
2909
return self.transport.has(store_basename)
2911
def test_save_empty_creates_no_file(self):
2912
# FIXME: There should be a better way than relying on the test
2913
# parametrization to identify branch.conf -- vila 2011-0526
2914
if self.store_id in ('branch', 'remote_branch'):
2915
raise tests.TestNotApplicable(
2916
'branch.conf is *always* created when a branch is initialized')
2917
store = self.get_store(self)
2919
self.assertEquals(False, self.has_store(store))
2921
def test_save_emptied_succeeds(self):
2922
store = self.get_store(self)
2923
store._load_from_string('foo=bar\n')
2924
section = store.get_mutable_section(None)
2925
section.remove('foo')
2927
self.assertEquals(True, self.has_store(store))
2928
modified_store = self.get_store(self)
2929
sections = list(modified_store.get_sections())
2930
self.assertLength(0, sections)
2932
def test_save_with_content_succeeds(self):
2933
# FIXME: There should be a better way than relying on the test
2934
# parametrization to identify branch.conf -- vila 2011-0526
2935
if self.store_id in ('branch', 'remote_branch'):
2936
raise tests.TestNotApplicable(
2937
'branch.conf is *always* created when a branch is initialized')
2938
store = self.get_store(self)
2939
store._load_from_string('foo=bar\n')
2940
self.assertEquals(False, self.has_store(store))
2942
self.assertEquals(True, self.has_store(store))
2943
modified_store = self.get_store(self)
2944
sections = list(modified_store.get_sections())
2945
self.assertLength(1, sections)
2946
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2948
def test_set_option_in_empty_store(self):
2949
store = self.get_store(self)
2950
section = store.get_mutable_section(None)
2951
section.set('foo', 'bar')
2953
modified_store = self.get_store(self)
2954
sections = list(modified_store.get_sections())
2955
self.assertLength(1, sections)
2956
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2958
def test_set_option_in_default_section(self):
2959
store = self.get_store(self)
2960
store._load_from_string('')
2961
section = store.get_mutable_section(None)
2962
section.set('foo', 'bar')
2964
modified_store = self.get_store(self)
2965
sections = list(modified_store.get_sections())
2966
self.assertLength(1, sections)
2967
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2969
def test_set_option_in_named_section(self):
2970
store = self.get_store(self)
2971
store._load_from_string('')
2972
section = store.get_mutable_section('baz')
2973
section.set('foo', 'bar')
2975
modified_store = self.get_store(self)
2976
sections = list(modified_store.get_sections())
2977
self.assertLength(1, sections)
2978
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2980
def test_load_hook(self):
2981
# We first needs to ensure that the store exists
2982
store = self.get_store(self)
2983
section = store.get_mutable_section('baz')
2984
section.set('foo', 'bar')
2986
# Now we can try to load it
2987
store = self.get_store(self)
2991
config.ConfigHooks.install_named_hook('load', hook, None)
2992
self.assertLength(0, calls)
2994
self.assertLength(1, calls)
2995
self.assertEquals((store,), calls[0])
2997
def test_save_hook(self):
3001
config.ConfigHooks.install_named_hook('save', hook, None)
3002
self.assertLength(0, calls)
3003
store = self.get_store(self)
3004
section = store.get_mutable_section('baz')
3005
section.set('foo', 'bar')
3007
self.assertLength(1, calls)
3008
self.assertEquals((store,), calls[0])
3010
def test_set_mark_dirty(self):
3011
stack = config.MemoryStack('')
3012
self.assertLength(0, stack.store.dirty_sections)
3013
stack.set('foo', 'baz')
3014
self.assertLength(1, stack.store.dirty_sections)
3015
self.assertTrue(stack.store._need_saving())
3017
def test_remove_mark_dirty(self):
3018
stack = config.MemoryStack('foo=bar')
3019
self.assertLength(0, stack.store.dirty_sections)
3021
self.assertLength(1, stack.store.dirty_sections)
3022
self.assertTrue(stack.store._need_saving())
3025
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3026
"""Tests that config changes are kept in memory and saved on-demand."""
3029
super(TestStoreSaveChanges, self).setUp()
3030
self.transport = self.get_transport()
3031
# Most of the tests involve two stores pointing to the same persistent
3032
# storage to observe the effects of concurrent changes
3033
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3034
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3037
self.warnings.append(args[0] % args[1:])
3038
self.overrideAttr(trace, 'warning', warning)
3040
def has_store(self, store):
3041
store_basename = urlutils.relative_url(self.transport.external_url(),
3042
store.external_url())
3043
return self.transport.has(store_basename)
3045
def get_stack(self, store):
3046
# Any stack will do as long as it uses the right store, just a single
3047
# no-name section is enough
3048
return config.Stack([store.get_sections], store)
3050
def test_no_changes_no_save(self):
3051
s = self.get_stack(self.st1)
3052
s.store.save_changes()
3053
self.assertEquals(False, self.has_store(self.st1))
3055
def test_unrelated_concurrent_update(self):
3056
s1 = self.get_stack(self.st1)
3057
s2 = self.get_stack(self.st2)
3058
s1.set('foo', 'bar')
3059
s2.set('baz', 'quux')
3061
# Changes don't propagate magically
3062
self.assertEquals(None, s1.get('baz'))
3063
s2.store.save_changes()
3064
self.assertEquals('quux', s2.get('baz'))
3065
# Changes are acquired when saving
3066
self.assertEquals('bar', s2.get('foo'))
3067
# Since there is no overlap, no warnings are emitted
3068
self.assertLength(0, self.warnings)
3070
def test_concurrent_update_modified(self):
3071
s1 = self.get_stack(self.st1)
3072
s2 = self.get_stack(self.st2)
3073
s1.set('foo', 'bar')
3074
s2.set('foo', 'baz')
3077
s2.store.save_changes()
3078
self.assertEquals('baz', s2.get('foo'))
3079
# But the user get a warning
3080
self.assertLength(1, self.warnings)
3081
warning = self.warnings[0]
3082
self.assertStartsWith(warning, 'Option foo in section None')
3083
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3084
' The baz value will be saved.')
3086
def test_concurrent_deletion(self):
3087
self.st1._load_from_string('foo=bar')
3089
s1 = self.get_stack(self.st1)
3090
s2 = self.get_stack(self.st2)
3093
s1.store.save_changes()
3095
self.assertLength(0, self.warnings)
3096
s2.store.save_changes()
3098
self.assertLength(1, self.warnings)
3099
warning = self.warnings[0]
3100
self.assertStartsWith(warning, 'Option foo in section None')
3101
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3102
' The <DELETED> value will be saved.')
3105
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3107
def get_store(self):
3108
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3110
def test_get_quoted_string(self):
3111
store = self.get_store()
3112
store._load_from_string('foo= " abc "')
3113
stack = config.Stack([store.get_sections])
3114
self.assertEquals(' abc ', stack.get('foo'))
3116
def test_set_quoted_string(self):
3117
store = self.get_store()
3118
stack = config.Stack([store.get_sections], store)
3119
stack.set('foo', ' a b c ')
3121
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3124
class TestTransportIniFileStore(TestStore):
3126
def test_loading_unknown_file_fails(self):
3127
store = config.TransportIniFileStore(self.get_transport(),
3129
self.assertRaises(errors.NoSuchFile, store.load)
3131
def test_invalid_content(self):
3132
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3133
self.assertEquals(False, store.is_loaded())
3134
exc = self.assertRaises(
3135
errors.ParseConfigError, store._load_from_string,
3136
'this is invalid !')
3137
self.assertEndsWith(exc.filename, 'foo.conf')
3138
# And the load failed
3139
self.assertEquals(False, store.is_loaded())
3141
def test_get_embedded_sections(self):
3142
# A more complicated example (which also shows that section names and
3143
# option names share the same name space...)
3144
# FIXME: This should be fixed by forbidding dicts as values ?
3145
# -- vila 2011-04-05
3146
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3147
store._load_from_string('''
3151
foo_in_DEFAULT=foo_DEFAULT
3159
sections = list(store.get_sections())
3160
self.assertLength(4, sections)
3161
# The default section has no name.
3162
# List values are provided as strings and need to be explicitly
3163
# converted by specifying from_unicode=list_from_store at option
3165
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3167
self.assertSectionContent(
3168
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3169
self.assertSectionContent(
3170
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3171
# sub sections are provided as embedded dicts.
3172
self.assertSectionContent(
3173
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3177
class TestLockableIniFileStore(TestStore):
3179
def test_create_store_in_created_dir(self):
3180
self.assertPathDoesNotExist('dir')
3181
t = self.get_transport('dir/subdir')
3182
store = config.LockableIniFileStore(t, 'foo.conf')
3183
store.get_mutable_section(None).set('foo', 'bar')
3185
self.assertPathExists('dir/subdir')
3188
class TestConcurrentStoreUpdates(TestStore):
3189
"""Test that Stores properly handle conccurent updates.
3191
New Store implementation may fail some of these tests but until such
3192
implementations exist it's hard to properly filter them from the scenarios
3193
applied here. If you encounter such a case, contact the bzr devs.
3196
scenarios = [(key, {'get_stack': builder}) for key, builder
3197
in config.test_stack_builder_registry.iteritems()]
3200
super(TestConcurrentStoreUpdates, self).setUp()
3201
self.stack = self.get_stack(self)
3202
if not isinstance(self.stack, config._CompatibleStack):
3203
raise tests.TestNotApplicable(
3204
'%s is not meant to be compatible with the old config design'
3206
self.stack.set('one', '1')
3207
self.stack.set('two', '2')
3209
self.stack.store.save()
3211
def test_simple_read_access(self):
3212
self.assertEquals('1', self.stack.get('one'))
3214
def test_simple_write_access(self):
3215
self.stack.set('one', 'one')
3216
self.assertEquals('one', self.stack.get('one'))
3218
def test_listen_to_the_last_speaker(self):
3220
c2 = self.get_stack(self)
3221
c1.set('one', 'ONE')
3222
c2.set('two', 'TWO')
3223
self.assertEquals('ONE', c1.get('one'))
3224
self.assertEquals('TWO', c2.get('two'))
3225
# The second update respect the first one
3226
self.assertEquals('ONE', c2.get('one'))
3228
def test_last_speaker_wins(self):
3229
# If the same config is not shared, the same variable modified twice
3230
# can only see a single result.
3232
c2 = self.get_stack(self)
3235
self.assertEquals('c2', c2.get('one'))
3236
# The first modification is still available until another refresh
3238
self.assertEquals('c1', c1.get('one'))
3239
c1.set('two', 'done')
3240
self.assertEquals('c2', c1.get('one'))
3242
def test_writes_are_serialized(self):
3244
c2 = self.get_stack(self)
3246
# We spawn a thread that will pause *during* the config saving.
3247
before_writing = threading.Event()
3248
after_writing = threading.Event()
3249
writing_done = threading.Event()
3250
c1_save_without_locking_orig = c1.store.save_without_locking
3251
def c1_save_without_locking():
3252
before_writing.set()
3253
c1_save_without_locking_orig()
3254
# The lock is held. We wait for the main thread to decide when to
3256
after_writing.wait()
3257
c1.store.save_without_locking = c1_save_without_locking
3261
t1 = threading.Thread(target=c1_set)
3262
# Collect the thread after the test
3263
self.addCleanup(t1.join)
3264
# Be ready to unblock the thread if the test goes wrong
3265
self.addCleanup(after_writing.set)
3267
before_writing.wait()
3268
self.assertRaises(errors.LockContention,
3269
c2.set, 'one', 'c2')
3270
self.assertEquals('c1', c1.get('one'))
3271
# Let the lock be released
3275
self.assertEquals('c2', c2.get('one'))
3277
def test_read_while_writing(self):
3279
# We spawn a thread that will pause *during* the write
3280
ready_to_write = threading.Event()
3281
do_writing = threading.Event()
3282
writing_done = threading.Event()
3283
# We override the _save implementation so we know the store is locked
3284
c1_save_without_locking_orig = c1.store.save_without_locking
3285
def c1_save_without_locking():
3286
ready_to_write.set()
3287
# The lock is held. We wait for the main thread to decide when to
3290
c1_save_without_locking_orig()
3292
c1.store.save_without_locking = c1_save_without_locking
3295
t1 = threading.Thread(target=c1_set)
3296
# Collect the thread after the test
3297
self.addCleanup(t1.join)
3298
# Be ready to unblock the thread if the test goes wrong
3299
self.addCleanup(do_writing.set)
3301
# Ensure the thread is ready to write
3302
ready_to_write.wait()
3303
self.assertEquals('c1', c1.get('one'))
3304
# If we read during the write, we get the old value
3305
c2 = self.get_stack(self)
3306
self.assertEquals('1', c2.get('one'))
3307
# Let the writing occur and ensure it occurred
3310
# Now we get the updated value
3311
c3 = self.get_stack(self)
3312
self.assertEquals('c1', c3.get('one'))
3314
# FIXME: It may be worth looking into removing the lock dir when it's not
3315
# needed anymore and look at possible fallouts for concurrent lockers. This
3316
# will matter if/when we use config files outside of bazaar directories
3317
# (.bazaar or .bzr) -- vila 20110-04-111
3320
class TestSectionMatcher(TestStore):
3322
scenarios = [('location', {'matcher': config.LocationMatcher}),
3323
('id', {'matcher': config.NameMatcher}),]
3326
super(TestSectionMatcher, self).setUp()
3327
# Any simple store is good enough
3328
self.get_store = config.test_store_builder_registry.get('configobj')
3330
def test_no_matches_for_empty_stores(self):
3331
store = self.get_store(self)
3332
store._load_from_string('')
3333
matcher = self.matcher(store, '/bar')
3334
self.assertEquals([], list(matcher.get_sections()))
3336
def test_build_doesnt_load_store(self):
3337
store = self.get_store(self)
3338
matcher = self.matcher(store, '/bar')
3339
self.assertFalse(store.is_loaded())
3342
class TestLocationSection(tests.TestCase):
3344
def get_section(self, options, extra_path):
3345
section = config.Section('foo', options)
3346
return config.LocationSection(section, extra_path)
3348
def test_simple_option(self):
3349
section = self.get_section({'foo': 'bar'}, '')
3350
self.assertEquals('bar', section.get('foo'))
3352
def test_option_with_extra_path(self):
3353
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3355
self.assertEquals('bar/baz', section.get('foo'))
3357
def test_invalid_policy(self):
3358
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3360
# invalid policies are ignored
3361
self.assertEquals('bar', section.get('foo'))
3364
class TestLocationMatcher(TestStore):
3367
super(TestLocationMatcher, self).setUp()
3368
# Any simple store is good enough
3369
self.get_store = config.test_store_builder_registry.get('configobj')
3371
def test_unrelated_section_excluded(self):
3372
store = self.get_store(self)
3373
store._load_from_string('''
3381
section=/foo/bar/baz
3385
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3387
[section.id for _, section in store.get_sections()])
3388
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3389
sections = [section for _, section in matcher.get_sections()]
3390
self.assertEquals(['/foo/bar', '/foo'],
3391
[section.id for section in sections])
3392
self.assertEquals(['quux', 'bar/quux'],
3393
[section.extra_path for section in sections])
3395
def test_more_specific_sections_first(self):
3396
store = self.get_store(self)
3397
store._load_from_string('''
3403
self.assertEquals(['/foo', '/foo/bar'],
3404
[section.id for _, section in store.get_sections()])
3405
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3406
sections = [section for _, section in matcher.get_sections()]
3407
self.assertEquals(['/foo/bar', '/foo'],
3408
[section.id for section in sections])
3409
self.assertEquals(['baz', 'bar/baz'],
3410
[section.extra_path for section in sections])
3412
def test_appendpath_in_no_name_section(self):
3413
# It's a bit weird to allow appendpath in a no-name section, but
3414
# someone may found a use for it
3415
store = self.get_store(self)
3416
store._load_from_string('''
3418
foo:policy = appendpath
3420
matcher = config.LocationMatcher(store, 'dir/subdir')
3421
sections = list(matcher.get_sections())
3422
self.assertLength(1, sections)
3423
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3425
def test_file_urls_are_normalized(self):
3426
store = self.get_store(self)
3427
if sys.platform == 'win32':
3428
expected_url = 'file:///C:/dir/subdir'
3429
expected_location = 'C:/dir/subdir'
3431
expected_url = 'file:///dir/subdir'
3432
expected_location = '/dir/subdir'
3433
matcher = config.LocationMatcher(store, expected_url)
3434
self.assertEquals(expected_location, matcher.location)
3437
class TestStartingPathMatcher(TestStore):
3440
super(TestStartingPathMatcher, self).setUp()
3441
# Any simple store is good enough
3442
self.store = config.IniFileStore()
3444
def assertSectionIDs(self, expected, location, content):
3445
self.store._load_from_string(content)
3446
matcher = config.StartingPathMatcher(self.store, location)
3447
sections = list(matcher.get_sections())
3448
self.assertLength(len(expected), sections)
3449
self.assertEqual(expected, [section.id for _, section in sections])
3452
def test_empty(self):
3453
self.assertSectionIDs([], self.get_url(), '')
3455
def test_url_vs_local_paths(self):
3456
# The matcher location is an url and the section names are local paths
3457
sections = self.assertSectionIDs(['/foo/bar', '/foo'],
3458
'file:///foo/bar/baz', '''\
3463
def test_local_path_vs_url(self):
3464
# The matcher location is a local path and the section names are urls
3465
sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3466
'/foo/bar/baz', '''\
3472
def test_no_name_section_included_when_present(self):
3473
# Note that other tests will cover the case where the no-name section
3474
# is empty and as such, not included.
3475
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3476
'/foo/bar/baz', '''\
3477
option = defined so the no-name section exists
3481
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3482
[s.locals['relpath'] for _, s in sections])
3484
def test_order_reversed(self):
3485
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3490
def test_unrelated_section_excluded(self):
3491
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3497
def test_glob_included(self):
3498
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3499
'/foo/bar/baz', '''\
3505
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3506
# nothing really is... as far using {relpath} to append it to something
3507
# else, this seems good enough though.
3508
self.assertEquals(['', 'baz', 'bar/baz'],
3509
[s.locals['relpath'] for _, s in sections])
3511
def test_respect_order(self):
3512
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3513
'/foo/bar/baz', '''\
3521
class TestNameMatcher(TestStore):
3524
super(TestNameMatcher, self).setUp()
3525
self.matcher = config.NameMatcher
3526
# Any simple store is good enough
3527
self.get_store = config.test_store_builder_registry.get('configobj')
3529
def get_matching_sections(self, name):
3530
store = self.get_store(self)
3531
store._load_from_string('''
3539
matcher = self.matcher(store, name)
3540
return list(matcher.get_sections())
3542
def test_matching(self):
3543
sections = self.get_matching_sections('foo')
3544
self.assertLength(1, sections)
3545
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3547
def test_not_matching(self):
3548
sections = self.get_matching_sections('baz')
3549
self.assertLength(0, sections)
3552
class TestBaseStackGet(tests.TestCase):
3555
super(TestBaseStackGet, self).setUp()
3556
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3558
def test_get_first_definition(self):
3559
store1 = config.IniFileStore()
3560
store1._load_from_string('foo=bar')
3561
store2 = config.IniFileStore()
3562
store2._load_from_string('foo=baz')
3563
conf = config.Stack([store1.get_sections, store2.get_sections])
3564
self.assertEquals('bar', conf.get('foo'))
3566
def test_get_with_registered_default_value(self):
3567
config.option_registry.register(config.Option('foo', default='bar'))
3568
conf_stack = config.Stack([])
3569
self.assertEquals('bar', conf_stack.get('foo'))
3571
def test_get_without_registered_default_value(self):
3572
config.option_registry.register(config.Option('foo'))
3573
conf_stack = config.Stack([])
3574
self.assertEquals(None, conf_stack.get('foo'))
3576
def test_get_without_default_value_for_not_registered(self):
3577
conf_stack = config.Stack([])
3578
self.assertEquals(None, conf_stack.get('foo'))
3580
def test_get_for_empty_section_callable(self):
3581
conf_stack = config.Stack([lambda : []])
3582
self.assertEquals(None, conf_stack.get('foo'))
3584
def test_get_for_broken_callable(self):
3585
# Trying to use and invalid callable raises an exception on first use
3586
conf_stack = config.Stack([object])
3587
self.assertRaises(TypeError, conf_stack.get, 'foo')
3590
class TestStackWithSimpleStore(tests.TestCase):
3593
super(TestStackWithSimpleStore, self).setUp()
3594
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3595
self.registry = config.option_registry
3597
def get_conf(self, content=None):
3598
return config.MemoryStack(content)
3600
def test_override_value_from_env(self):
3601
self.registry.register(
3602
config.Option('foo', default='bar', override_from_env=['FOO']))
3603
self.overrideEnv('FOO', 'quux')
3604
# Env variable provides a default taking over the option one
3605
conf = self.get_conf('foo=store')
3606
self.assertEquals('quux', conf.get('foo'))
3608
def test_first_override_value_from_env_wins(self):
3609
self.registry.register(
3610
config.Option('foo', default='bar',
3611
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3612
self.overrideEnv('FOO', 'foo')
3613
self.overrideEnv('BAZ', 'baz')
3614
# The first env var set wins
3615
conf = self.get_conf('foo=store')
3616
self.assertEquals('foo', conf.get('foo'))
3619
class TestMemoryStack(tests.TestCase):
3622
conf = config.MemoryStack('foo=bar')
3623
self.assertEquals('bar', conf.get('foo'))
3626
conf = config.MemoryStack('foo=bar')
3627
conf.set('foo', 'baz')
3628
self.assertEquals('baz', conf.get('foo'))
3630
def test_no_content(self):
3631
conf = config.MemoryStack()
3632
# No content means no loading
3633
self.assertFalse(conf.store.is_loaded())
3634
self.assertRaises(NotImplementedError, conf.get, 'foo')
3635
# But a content can still be provided
3636
conf.store._load_from_string('foo=bar')
3637
self.assertEquals('bar', conf.get('foo'))
3640
class TestStackWithTransport(tests.TestCaseWithTransport):
3642
scenarios = [(key, {'get_stack': builder}) for key, builder
3643
in config.test_stack_builder_registry.iteritems()]
3646
class TestConcreteStacks(TestStackWithTransport):
3648
def test_build_stack(self):
3649
# Just a smoke test to help debug builders
3650
stack = self.get_stack(self)
3653
class TestStackGet(TestStackWithTransport):
3656
super(TestStackGet, self).setUp()
3657
self.conf = self.get_stack(self)
3659
def test_get_for_empty_stack(self):
3660
self.assertEquals(None, self.conf.get('foo'))
3662
def test_get_hook(self):
3663
self.conf.set('foo', 'bar')
3667
config.ConfigHooks.install_named_hook('get', hook, None)
3668
self.assertLength(0, calls)
3669
value = self.conf.get('foo')
3670
self.assertEquals('bar', value)
3671
self.assertLength(1, calls)
3672
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3675
class TestStackGetWithConverter(tests.TestCase):
3678
super(TestStackGetWithConverter, self).setUp()
3679
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3680
self.registry = config.option_registry
3682
def get_conf(self, content=None):
3683
return config.MemoryStack(content)
3685
def register_bool_option(self, name, default=None, default_from_env=None):
3686
b = config.Option(name, help='A boolean.',
3687
default=default, default_from_env=default_from_env,
3688
from_unicode=config.bool_from_store)
3689
self.registry.register(b)
3691
def test_get_default_bool_None(self):
3692
self.register_bool_option('foo')
3693
conf = self.get_conf('')
3694
self.assertEquals(None, conf.get('foo'))
3696
def test_get_default_bool_True(self):
3697
self.register_bool_option('foo', u'True')
3698
conf = self.get_conf('')
3699
self.assertEquals(True, conf.get('foo'))
3701
def test_get_default_bool_False(self):
3702
self.register_bool_option('foo', False)
3703
conf = self.get_conf('')
3704
self.assertEquals(False, conf.get('foo'))
3706
def test_get_default_bool_False_as_string(self):
3707
self.register_bool_option('foo', u'False')
3708
conf = self.get_conf('')
3709
self.assertEquals(False, conf.get('foo'))
3711
def test_get_default_bool_from_env_converted(self):
3712
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3713
self.overrideEnv('FOO', 'False')
3714
conf = self.get_conf('')
3715
self.assertEquals(False, conf.get('foo'))
3717
def test_get_default_bool_when_conversion_fails(self):
3718
self.register_bool_option('foo', default='True')
3719
conf = self.get_conf('foo=invalid boolean')
3720
self.assertEquals(True, conf.get('foo'))
3722
def register_integer_option(self, name,
3723
default=None, default_from_env=None):
3724
i = config.Option(name, help='An integer.',
3725
default=default, default_from_env=default_from_env,
3726
from_unicode=config.int_from_store)
3727
self.registry.register(i)
3729
def test_get_default_integer_None(self):
3730
self.register_integer_option('foo')
3731
conf = self.get_conf('')
3732
self.assertEquals(None, conf.get('foo'))
3734
def test_get_default_integer(self):
3735
self.register_integer_option('foo', 42)
3736
conf = self.get_conf('')
3737
self.assertEquals(42, conf.get('foo'))
3739
def test_get_default_integer_as_string(self):
3740
self.register_integer_option('foo', u'42')
3741
conf = self.get_conf('')
3742
self.assertEquals(42, conf.get('foo'))
3744
def test_get_default_integer_from_env(self):
3745
self.register_integer_option('foo', default_from_env=['FOO'])
3746
self.overrideEnv('FOO', '18')
3747
conf = self.get_conf('')
3748
self.assertEquals(18, conf.get('foo'))
3750
def test_get_default_integer_when_conversion_fails(self):
3751
self.register_integer_option('foo', default='12')
3752
conf = self.get_conf('foo=invalid integer')
3753
self.assertEquals(12, conf.get('foo'))
3755
def register_list_option(self, name, default=None, default_from_env=None):
3756
l = config.ListOption(name, help='A list.', default=default,
3757
default_from_env=default_from_env)
3758
self.registry.register(l)
3760
def test_get_default_list_None(self):
3761
self.register_list_option('foo')
3762
conf = self.get_conf('')
3763
self.assertEquals(None, conf.get('foo'))
3765
def test_get_default_list_empty(self):
3766
self.register_list_option('foo', '')
3767
conf = self.get_conf('')
3768
self.assertEquals([], conf.get('foo'))
3770
def test_get_default_list_from_env(self):
3771
self.register_list_option('foo', default_from_env=['FOO'])
3772
self.overrideEnv('FOO', '')
3773
conf = self.get_conf('')
3774
self.assertEquals([], conf.get('foo'))
3776
def test_get_with_list_converter_no_item(self):
3777
self.register_list_option('foo', None)
3778
conf = self.get_conf('foo=,')
3779
self.assertEquals([], conf.get('foo'))
3781
def test_get_with_list_converter_many_items(self):
3782
self.register_list_option('foo', None)
3783
conf = self.get_conf('foo=m,o,r,e')
3784
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3786
def test_get_with_list_converter_embedded_spaces_many_items(self):
3787
self.register_list_option('foo', None)
3788
conf = self.get_conf('foo=" bar", "baz "')
3789
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3791
def test_get_with_list_converter_stripped_spaces_many_items(self):
3792
self.register_list_option('foo', None)
3793
conf = self.get_conf('foo= bar , baz ')
3794
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3797
class TestIterOptionRefs(tests.TestCase):
3798
"""iter_option_refs is a bit unusual, document some cases."""
3800
def assertRefs(self, expected, string):
3801
self.assertEquals(expected, list(config.iter_option_refs(string)))
3803
def test_empty(self):
3804
self.assertRefs([(False, '')], '')
3806
def test_no_refs(self):
3807
self.assertRefs([(False, 'foo bar')], 'foo bar')
3809
def test_single_ref(self):
3810
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3812
def test_broken_ref(self):
3813
self.assertRefs([(False, '{foo')], '{foo')
3815
def test_embedded_ref(self):
3816
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3819
def test_two_refs(self):
3820
self.assertRefs([(False, ''), (True, '{foo}'),
3821
(False, ''), (True, '{bar}'),
3825
def test_newline_in_refs_are_not_matched(self):
3826
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3829
class TestStackExpandOptions(tests.TestCaseWithTransport):
3832
super(TestStackExpandOptions, self).setUp()
3833
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3834
self.registry = config.option_registry
3835
self.conf = build_branch_stack(self)
3837
def assertExpansion(self, expected, string, env=None):
3838
self.assertEquals(expected, self.conf.expand_options(string, env))
3840
def test_no_expansion(self):
3841
self.assertExpansion('foo', 'foo')
3843
def test_expand_default_value(self):
3844
self.conf.store._load_from_string('bar=baz')
3845
self.registry.register(config.Option('foo', default=u'{bar}'))
3846
self.assertEquals('baz', self.conf.get('foo', expand=True))
3848
def test_expand_default_from_env(self):
3849
self.conf.store._load_from_string('bar=baz')
3850
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3851
self.overrideEnv('FOO', '{bar}')
3852
self.assertEquals('baz', self.conf.get('foo', expand=True))
3854
def test_expand_default_on_failed_conversion(self):
3855
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3856
self.registry.register(
3857
config.Option('foo', default=u'{bar}',
3858
from_unicode=config.int_from_store))
3859
self.assertEquals(42, self.conf.get('foo', expand=True))
3861
def test_env_adding_options(self):
3862
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3864
def test_env_overriding_options(self):
3865
self.conf.store._load_from_string('foo=baz')
3866
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3868
def test_simple_ref(self):
3869
self.conf.store._load_from_string('foo=xxx')
3870
self.assertExpansion('xxx', '{foo}')
3872
def test_unknown_ref(self):
3873
self.assertRaises(errors.ExpandingUnknownOption,
3874
self.conf.expand_options, '{foo}')
3876
def test_indirect_ref(self):
3877
self.conf.store._load_from_string('''
3881
self.assertExpansion('xxx', '{bar}')
3883
def test_embedded_ref(self):
3884
self.conf.store._load_from_string('''
3888
self.assertExpansion('xxx', '{{bar}}')
3890
def test_simple_loop(self):
3891
self.conf.store._load_from_string('foo={foo}')
3892
self.assertRaises(errors.OptionExpansionLoop,
3893
self.conf.expand_options, '{foo}')
3895
def test_indirect_loop(self):
3896
self.conf.store._load_from_string('''
3900
e = self.assertRaises(errors.OptionExpansionLoop,
3901
self.conf.expand_options, '{foo}')
3902
self.assertEquals('foo->bar->baz', e.refs)
3903
self.assertEquals('{foo}', e.string)
3905
def test_list(self):
3906
self.conf.store._load_from_string('''
3910
list={foo},{bar},{baz}
3912
self.registry.register(
3913
config.ListOption('list'))
3914
self.assertEquals(['start', 'middle', 'end'],
3915
self.conf.get('list', expand=True))
3917
def test_cascading_list(self):
3918
self.conf.store._load_from_string('''
3924
self.registry.register(
3925
config.ListOption('list'))
3926
self.assertEquals(['start', 'middle', 'end'],
3927
self.conf.get('list', expand=True))
3929
def test_pathologically_hidden_list(self):
3930
self.conf.store._load_from_string('''
3936
hidden={start}{middle}{end}
3938
# What matters is what the registration says, the conversion happens
3939
# only after all expansions have been performed
3940
self.registry.register(config.ListOption('hidden'))
3941
self.assertEquals(['bin', 'go'],
3942
self.conf.get('hidden', expand=True))
3945
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3948
super(TestStackCrossSectionsExpand, self).setUp()
3950
def get_config(self, location, string):
3953
# Since we don't save the config we won't strictly require to inherit
3954
# from TestCaseInTempDir, but an error occurs so quickly...
3955
c = config.LocationStack(location)
3956
c.store._load_from_string(string)
3959
def test_dont_cross_unrelated_section(self):
3960
c = self.get_config('/another/branch/path','''
3965
[/another/branch/path]
3968
self.assertRaises(errors.ExpandingUnknownOption,
3969
c.get, 'bar', expand=True)
3971
def test_cross_related_sections(self):
3972
c = self.get_config('/project/branch/path','''
3976
[/project/branch/path]
3979
self.assertEquals('quux', c.get('bar', expand=True))
3982
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3984
def test_cross_global_locations(self):
3985
l_store = config.LocationStore()
3986
l_store._load_from_string('''
3992
g_store = config.GlobalStore()
3993
g_store._load_from_string('''
3999
stack = config.LocationStack('/branch')
4000
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4001
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4004
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4006
def test_expand_locals_empty(self):
4007
l_store = config.LocationStore()
4008
l_store._load_from_string('''
4009
[/home/user/project]
4014
stack = config.LocationStack('/home/user/project/')
4015
self.assertEquals('', stack.get('base', expand=True))
4016
self.assertEquals('', stack.get('rel', expand=True))
4018
def test_expand_basename_locally(self):
4019
l_store = config.LocationStore()
4020
l_store._load_from_string('''
4021
[/home/user/project]
4025
stack = config.LocationStack('/home/user/project/branch')
4026
self.assertEquals('branch', stack.get('bfoo', expand=True))
4028
def test_expand_basename_locally_longer_path(self):
4029
l_store = config.LocationStore()
4030
l_store._load_from_string('''
4035
stack = config.LocationStack('/home/user/project/dir/branch')
4036
self.assertEquals('branch', stack.get('bfoo', expand=True))
4038
def test_expand_relpath_locally(self):
4039
l_store = config.LocationStore()
4040
l_store._load_from_string('''
4041
[/home/user/project]
4042
lfoo = loc-foo/{relpath}
4045
stack = config.LocationStack('/home/user/project/branch')
4046
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4048
def test_expand_relpath_unknonw_in_global(self):
4049
g_store = config.GlobalStore()
4050
g_store._load_from_string('''
4055
stack = config.LocationStack('/home/user/project/branch')
4056
self.assertRaises(errors.ExpandingUnknownOption,
4057
stack.get, 'gfoo', expand=True)
4059
def test_expand_local_option_locally(self):
4060
l_store = config.LocationStore()
4061
l_store._load_from_string('''
4062
[/home/user/project]
4063
lfoo = loc-foo/{relpath}
4067
g_store = config.GlobalStore()
4068
g_store._load_from_string('''
4074
stack = config.LocationStack('/home/user/project/branch')
4075
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4076
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4078
def test_locals_dont_leak(self):
4079
"""Make sure we chose the right local in presence of several sections.
4081
l_store = config.LocationStore()
4082
l_store._load_from_string('''
4084
lfoo = loc-foo/{relpath}
4085
[/home/user/project]
4086
lfoo = loc-foo/{relpath}
4089
stack = config.LocationStack('/home/user/project/branch')
4090
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4091
stack = config.LocationStack('/home/user/bar/baz')
4092
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
4096
class TestStackSet(TestStackWithTransport):
4098
def test_simple_set(self):
4099
conf = self.get_stack(self)
4100
self.assertEquals(None, conf.get('foo'))
4101
conf.set('foo', 'baz')
4102
# Did we get it back ?
4103
self.assertEquals('baz', conf.get('foo'))
4105
def test_set_creates_a_new_section(self):
4106
conf = self.get_stack(self)
4107
conf.set('foo', 'baz')
4108
self.assertEquals, 'baz', conf.get('foo')
4110
def test_set_hook(self):
4114
config.ConfigHooks.install_named_hook('set', hook, None)
4115
self.assertLength(0, calls)
4116
conf = self.get_stack(self)
4117
conf.set('foo', 'bar')
4118
self.assertLength(1, calls)
4119
self.assertEquals((conf, 'foo', 'bar'), calls[0])
4122
class TestStackRemove(TestStackWithTransport):
4124
def test_remove_existing(self):
4125
conf = self.get_stack(self)
4126
conf.set('foo', 'bar')
4127
self.assertEquals('bar', conf.get('foo'))
4129
# Did we get it back ?
4130
self.assertEquals(None, conf.get('foo'))
4132
def test_remove_unknown(self):
4133
conf = self.get_stack(self)
4134
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
4136
def test_remove_hook(self):
4140
config.ConfigHooks.install_named_hook('remove', hook, None)
4141
self.assertLength(0, calls)
4142
conf = self.get_stack(self)
4143
conf.set('foo', 'bar')
4145
self.assertLength(1, calls)
4146
self.assertEquals((conf, 'foo'), calls[0])
4149
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
4152
super(TestConfigGetOptions, self).setUp()
4153
create_configs(self)
4155
def test_no_variable(self):
4156
# Using branch should query branch, locations and bazaar
4157
self.assertOptions([], self.branch_config)
4159
def test_option_in_bazaar(self):
4160
self.bazaar_config.set_user_option('file', 'bazaar')
4161
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
4164
def test_option_in_locations(self):
4165
self.locations_config.set_user_option('file', 'locations')
4167
[('file', 'locations', self.tree.basedir, 'locations')],
4168
self.locations_config)
4170
def test_option_in_branch(self):
4171
self.branch_config.set_user_option('file', 'branch')
4172
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
4175
def test_option_in_bazaar_and_branch(self):
4176
self.bazaar_config.set_user_option('file', 'bazaar')
4177
self.branch_config.set_user_option('file', 'branch')
4178
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4179
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4182
def test_option_in_branch_and_locations(self):
4183
# Hmm, locations override branch :-/
4184
self.locations_config.set_user_option('file', 'locations')
4185
self.branch_config.set_user_option('file', 'branch')
4187
[('file', 'locations', self.tree.basedir, 'locations'),
4188
('file', 'branch', 'DEFAULT', 'branch'),],
4191
def test_option_in_bazaar_locations_and_branch(self):
4192
self.bazaar_config.set_user_option('file', 'bazaar')
4193
self.locations_config.set_user_option('file', 'locations')
4194
self.branch_config.set_user_option('file', 'branch')
4196
[('file', 'locations', self.tree.basedir, 'locations'),
4197
('file', 'branch', 'DEFAULT', 'branch'),
4198
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4202
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
4205
super(TestConfigRemoveOption, self).setUp()
4206
create_configs_with_file_option(self)
4208
def test_remove_in_locations(self):
4209
self.locations_config.remove_user_option('file', self.tree.basedir)
4211
[('file', 'branch', 'DEFAULT', 'branch'),
4212
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4215
def test_remove_in_branch(self):
4216
self.branch_config.remove_user_option('file')
4218
[('file', 'locations', self.tree.basedir, 'locations'),
4219
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4222
def test_remove_in_bazaar(self):
4223
self.bazaar_config.remove_user_option('file')
4225
[('file', 'locations', self.tree.basedir, 'locations'),
4226
('file', 'branch', 'DEFAULT', 'branch'),],
4230
class TestConfigGetSections(tests.TestCaseWithTransport):
4233
super(TestConfigGetSections, self).setUp()
4234
create_configs(self)
4236
def assertSectionNames(self, expected, conf, name=None):
4237
"""Check which sections are returned for a given config.
4239
If fallback configurations exist their sections can be included.
4241
:param expected: A list of section names.
4243
:param conf: The configuration that will be queried.
4245
:param name: An optional section name that will be passed to
4248
sections = list(conf._get_sections(name))
4249
self.assertLength(len(expected), sections)
4250
self.assertEqual(expected, [name for name, _, _ in sections])
4252
def test_bazaar_default_section(self):
4253
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4255
def test_locations_default_section(self):
4256
# No sections are defined in an empty file
4257
self.assertSectionNames([], self.locations_config)
4259
def test_locations_named_section(self):
4260
self.locations_config.set_user_option('file', 'locations')
4261
self.assertSectionNames([self.tree.basedir], self.locations_config)
4263
def test_locations_matching_sections(self):
4264
loc_config = self.locations_config
4265
loc_config.set_user_option('file', 'locations')
4266
# We need to cheat a bit here to create an option in sections above and
4267
# below the 'location' one.
4268
parser = loc_config._get_parser()
4269
# locations.cong deals with '/' ignoring native os.sep
4270
location_names = self.tree.basedir.split('/')
4271
parent = '/'.join(location_names[:-1])
4272
child = '/'.join(location_names + ['child'])
4274
parser[parent]['file'] = 'parent'
4276
parser[child]['file'] = 'child'
4277
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4279
def test_branch_data_default_section(self):
4280
self.assertSectionNames([None],
4281
self.branch_config._get_branch_data_config())
4283
def test_branch_default_sections(self):
4284
# No sections are defined in an empty locations file
4285
self.assertSectionNames([None, 'DEFAULT'],
4287
# Unless we define an option
4288
self.branch_config._get_location_config().set_user_option(
4289
'file', 'locations')
4290
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4293
def test_bazaar_named_section(self):
4294
# We need to cheat as the API doesn't give direct access to sections
4295
# other than DEFAULT.
4296
self.bazaar_config.set_alias('bazaar', 'bzr')
4297
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
4300
class TestAuthenticationConfigFile(tests.TestCase):
1316
4301
"""Test the authentication.conf file matching"""