1312
2082
self.assertIs(None, bzrdir_config.get_default_stack_on())
2085
class TestOldConfigHooks(tests.TestCaseWithTransport):
2088
super(TestOldConfigHooks, self).setUp()
2089
create_configs_with_file_option(self)
2091
def assertGetHook(self, conf, name, value):
2095
config.OldConfigHooks.install_named_hook('get', hook, None)
2097
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2098
self.assertLength(0, calls)
2099
actual_value = conf.get_user_option(name)
2100
self.assertEquals(value, actual_value)
2101
self.assertLength(1, calls)
2102
self.assertEquals((conf, name, value), calls[0])
2104
def test_get_hook_bazaar(self):
2105
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2107
def test_get_hook_locations(self):
2108
self.assertGetHook(self.locations_config, 'file', 'locations')
2110
def test_get_hook_branch(self):
2111
# Since locations masks branch, we define a different option
2112
self.branch_config.set_user_option('file2', 'branch')
2113
self.assertGetHook(self.branch_config, 'file2', 'branch')
2115
def assertSetHook(self, conf, name, value):
2119
config.OldConfigHooks.install_named_hook('set', hook, None)
2121
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2122
self.assertLength(0, calls)
2123
conf.set_user_option(name, value)
2124
self.assertLength(1, calls)
2125
# We can't assert the conf object below as different configs use
2126
# different means to implement set_user_option and we care only about
2128
self.assertEquals((name, value), calls[0][1:])
2130
def test_set_hook_bazaar(self):
2131
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2133
def test_set_hook_locations(self):
2134
self.assertSetHook(self.locations_config, 'foo', 'locations')
2136
def test_set_hook_branch(self):
2137
self.assertSetHook(self.branch_config, 'foo', 'branch')
2139
def assertRemoveHook(self, conf, name, section_name=None):
2143
config.OldConfigHooks.install_named_hook('remove', hook, None)
2145
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2146
self.assertLength(0, calls)
2147
conf.remove_user_option(name, section_name)
2148
self.assertLength(1, calls)
2149
# We can't assert the conf object below as different configs use
2150
# different means to implement remove_user_option and we care only about
2152
self.assertEquals((name,), calls[0][1:])
2154
def test_remove_hook_bazaar(self):
2155
self.assertRemoveHook(self.bazaar_config, 'file')
2157
def test_remove_hook_locations(self):
2158
self.assertRemoveHook(self.locations_config, 'file',
2159
self.locations_config.location)
2161
def test_remove_hook_branch(self):
2162
self.assertRemoveHook(self.branch_config, 'file')
2164
def assertLoadHook(self, name, conf_class, *conf_args):
2168
config.OldConfigHooks.install_named_hook('load', hook, None)
2170
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2171
self.assertLength(0, calls)
2173
conf = conf_class(*conf_args)
2174
# Access an option to trigger a load
2175
conf.get_user_option(name)
2176
self.assertLength(1, calls)
2177
# Since we can't assert about conf, we just use the number of calls ;-/
2179
def test_load_hook_bazaar(self):
2180
self.assertLoadHook('file', config.GlobalConfig)
2182
def test_load_hook_locations(self):
2183
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2185
def test_load_hook_branch(self):
2186
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2188
def assertSaveHook(self, conf):
2192
config.OldConfigHooks.install_named_hook('save', hook, None)
2194
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2195
self.assertLength(0, calls)
2196
# Setting an option triggers a save
2197
conf.set_user_option('foo', 'bar')
2198
self.assertLength(1, calls)
2199
# Since we can't assert about conf, we just use the number of calls ;-/
2201
def test_save_hook_bazaar(self):
2202
self.assertSaveHook(self.bazaar_config)
2204
def test_save_hook_locations(self):
2205
self.assertSaveHook(self.locations_config)
2207
def test_save_hook_branch(self):
2208
self.assertSaveHook(self.branch_config)
2211
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2212
"""Tests config hooks for remote configs.
2214
No tests for the remove hook as this is not implemented there.
2218
super(TestOldConfigHooksForRemote, self).setUp()
2219
self.transport_server = test_server.SmartTCPServer_for_testing
2220
create_configs_with_file_option(self)
2222
def assertGetHook(self, conf, name, value):
2226
config.OldConfigHooks.install_named_hook('get', hook, None)
2228
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2229
self.assertLength(0, calls)
2230
actual_value = conf.get_option(name)
2231
self.assertEquals(value, actual_value)
2232
self.assertLength(1, calls)
2233
self.assertEquals((conf, name, value), calls[0])
2235
def test_get_hook_remote_branch(self):
2236
remote_branch = branch.Branch.open(self.get_url('tree'))
2237
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2239
def test_get_hook_remote_bzrdir(self):
2240
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2241
conf = remote_bzrdir._get_config()
2242
conf.set_option('remotedir', 'file')
2243
self.assertGetHook(conf, 'file', 'remotedir')
2245
def assertSetHook(self, conf, name, value):
2249
config.OldConfigHooks.install_named_hook('set', hook, None)
2251
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2252
self.assertLength(0, calls)
2253
conf.set_option(value, name)
2254
self.assertLength(1, calls)
2255
# We can't assert the conf object below as different configs use
2256
# different means to implement set_user_option and we care only about
2258
self.assertEquals((name, value), calls[0][1:])
2260
def test_set_hook_remote_branch(self):
2261
remote_branch = branch.Branch.open(self.get_url('tree'))
2262
self.addCleanup(remote_branch.lock_write().unlock)
2263
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2265
def test_set_hook_remote_bzrdir(self):
2266
remote_branch = branch.Branch.open(self.get_url('tree'))
2267
self.addCleanup(remote_branch.lock_write().unlock)
2268
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2269
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2271
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2275
config.OldConfigHooks.install_named_hook('load', hook, None)
2277
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2278
self.assertLength(0, calls)
2280
conf = conf_class(*conf_args)
2281
# Access an option to trigger a load
2282
conf.get_option(name)
2283
self.assertLength(expected_nb_calls, calls)
2284
# Since we can't assert about conf, we just use the number of calls ;-/
2286
def test_load_hook_remote_branch(self):
2287
remote_branch = branch.Branch.open(self.get_url('tree'))
2288
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2290
def test_load_hook_remote_bzrdir(self):
2291
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2292
# The config file doesn't exist, set an option to force its creation
2293
conf = remote_bzrdir._get_config()
2294
conf.set_option('remotedir', 'file')
2295
# We get one call for the server and one call for the client, this is
2296
# caused by the differences in implementations betwen
2297
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2298
# SmartServerBranchGetConfigFile (in smart/branch.py)
2299
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2301
def assertSaveHook(self, conf):
2305
config.OldConfigHooks.install_named_hook('save', hook, None)
2307
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2308
self.assertLength(0, calls)
2309
# Setting an option triggers a save
2310
conf.set_option('foo', 'bar')
2311
self.assertLength(1, calls)
2312
# Since we can't assert about conf, we just use the number of calls ;-/
2314
def test_save_hook_remote_branch(self):
2315
remote_branch = branch.Branch.open(self.get_url('tree'))
2316
self.addCleanup(remote_branch.lock_write().unlock)
2317
self.assertSaveHook(remote_branch._get_config())
2319
def test_save_hook_remote_bzrdir(self):
2320
remote_branch = branch.Branch.open(self.get_url('tree'))
2321
self.addCleanup(remote_branch.lock_write().unlock)
2322
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2323
self.assertSaveHook(remote_bzrdir._get_config())
2326
class TestOption(tests.TestCase):
2328
def test_default_value(self):
2329
opt = config.Option('foo', default='bar')
2330
self.assertEquals('bar', opt.get_default())
2332
def test_callable_default_value(self):
2333
def bar_as_unicode():
2335
opt = config.Option('foo', default=bar_as_unicode)
2336
self.assertEquals('bar', opt.get_default())
2338
def test_default_value_from_env(self):
2339
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2340
self.overrideEnv('FOO', 'quux')
2341
# Env variable provides a default taking over the option one
2342
self.assertEquals('quux', opt.get_default())
2344
def test_first_default_value_from_env_wins(self):
2345
opt = config.Option('foo', default='bar',
2346
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2347
self.overrideEnv('FOO', 'foo')
2348
self.overrideEnv('BAZ', 'baz')
2349
# The first env var set wins
2350
self.assertEquals('foo', opt.get_default())
2352
def test_not_supported_list_default_value(self):
2353
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2355
def test_not_supported_object_default_value(self):
2356
self.assertRaises(AssertionError, config.Option, 'foo',
2359
def test_not_supported_callable_default_value_not_unicode(self):
2360
def bar_not_unicode():
2362
opt = config.Option('foo', default=bar_not_unicode)
2363
self.assertRaises(AssertionError, opt.get_default)
2366
class TestOptionConverterMixin(object):
2368
def assertConverted(self, expected, opt, value):
2369
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2371
def assertWarns(self, opt, value):
2374
warnings.append(args[0] % args[1:])
2375
self.overrideAttr(trace, 'warning', warning)
2376
self.assertEquals(None, opt.convert_from_unicode(None, value))
2377
self.assertLength(1, warnings)
2379
'Value "%s" is not valid for "%s"' % (value, opt.name),
2382
def assertErrors(self, opt, value):
2383
self.assertRaises(errors.ConfigOptionValueError,
2384
opt.convert_from_unicode, None, value)
2386
def assertConvertInvalid(self, opt, invalid_value):
2388
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2389
opt.invalid = 'warning'
2390
self.assertWarns(opt, invalid_value)
2391
opt.invalid = 'error'
2392
self.assertErrors(opt, invalid_value)
2395
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2397
def get_option(self):
2398
return config.Option('foo', help='A boolean.',
2399
from_unicode=config.bool_from_store)
2401
def test_convert_invalid(self):
2402
opt = self.get_option()
2403
# A string that is not recognized as a boolean
2404
self.assertConvertInvalid(opt, u'invalid-boolean')
2405
# A list of strings is never recognized as a boolean
2406
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2408
def test_convert_valid(self):
2409
opt = self.get_option()
2410
self.assertConverted(True, opt, u'True')
2411
self.assertConverted(True, opt, u'1')
2412
self.assertConverted(False, opt, u'False')
2415
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2417
def get_option(self):
2418
return config.Option('foo', help='An integer.',
2419
from_unicode=config.int_from_store)
2421
def test_convert_invalid(self):
2422
opt = self.get_option()
2423
# A string that is not recognized as an integer
2424
self.assertConvertInvalid(opt, u'forty-two')
2425
# A list of strings is never recognized as an integer
2426
self.assertConvertInvalid(opt, [u'a', u'list'])
2428
def test_convert_valid(self):
2429
opt = self.get_option()
2430
self.assertConverted(16, opt, u'16')
2433
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2435
def get_option(self):
2436
return config.Option('foo', help='An integer in SI units.',
2437
from_unicode=config.int_SI_from_store)
2439
def test_convert_invalid(self):
2440
opt = self.get_option()
2441
self.assertConvertInvalid(opt, u'not-a-unit')
2442
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2443
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2444
self.assertConvertInvalid(opt, u'1GG')
2445
self.assertConvertInvalid(opt, u'1Mbb')
2446
self.assertConvertInvalid(opt, u'1MM')
2448
def test_convert_valid(self):
2449
opt = self.get_option()
2450
self.assertConverted(int(5e3), opt, u'5kb')
2451
self.assertConverted(int(5e6), opt, u'5M')
2452
self.assertConverted(int(5e6), opt, u'5MB')
2453
self.assertConverted(int(5e9), opt, u'5g')
2454
self.assertConverted(int(5e9), opt, u'5gB')
2455
self.assertConverted(100, opt, u'100')
2458
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2460
def get_option(self):
2461
return config.ListOption('foo', help='A list.')
2463
def test_convert_invalid(self):
2464
opt = self.get_option()
2465
# We don't even try to convert a list into a list, we only expect
2467
self.assertConvertInvalid(opt, [1])
2468
# No string is invalid as all forms can be converted to a list
2470
def test_convert_valid(self):
2471
opt = self.get_option()
2472
# An empty string is an empty list
2473
self.assertConverted([], opt, '') # Using a bare str() just in case
2474
self.assertConverted([], opt, u'')
2476
self.assertConverted([u'True'], opt, u'True')
2478
self.assertConverted([u'42'], opt, u'42')
2480
self.assertConverted([u'bar'], opt, u'bar')
2483
class TestOptionRegistry(tests.TestCase):
2486
super(TestOptionRegistry, self).setUp()
2487
# Always start with an empty registry
2488
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2489
self.registry = config.option_registry
2491
def test_register(self):
2492
opt = config.Option('foo')
2493
self.registry.register(opt)
2494
self.assertIs(opt, self.registry.get('foo'))
2496
def test_registered_help(self):
2497
opt = config.Option('foo', help='A simple option')
2498
self.registry.register(opt)
2499
self.assertEquals('A simple option', self.registry.get_help('foo'))
2501
lazy_option = config.Option('lazy_foo', help='Lazy help')
2503
def test_register_lazy(self):
2504
self.registry.register_lazy('lazy_foo', self.__module__,
2505
'TestOptionRegistry.lazy_option')
2506
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2508
def test_registered_lazy_help(self):
2509
self.registry.register_lazy('lazy_foo', self.__module__,
2510
'TestOptionRegistry.lazy_option')
2511
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2514
class TestRegisteredOptions(tests.TestCase):
2515
"""All registered options should verify some constraints."""
2517
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2518
in config.option_registry.iteritems()]
2521
super(TestRegisteredOptions, self).setUp()
2522
self.registry = config.option_registry
2524
def test_proper_name(self):
2525
# An option should be registered under its own name, this can't be
2526
# checked at registration time for the lazy ones.
2527
self.assertEquals(self.option_name, self.option.name)
2529
def test_help_is_set(self):
2530
option_help = self.registry.get_help(self.option_name)
2531
self.assertNotEquals(None, option_help)
2532
# Come on, think about the user, he really wants to know what the
2534
self.assertIsNot(None, option_help)
2535
self.assertNotEquals('', option_help)
2538
class TestSection(tests.TestCase):
2540
# FIXME: Parametrize so that all sections produced by Stores run these
2541
# tests -- vila 2011-04-01
2543
def test_get_a_value(self):
2544
a_dict = dict(foo='bar')
2545
section = config.Section('myID', a_dict)
2546
self.assertEquals('bar', section.get('foo'))
2548
def test_get_unknown_option(self):
2550
section = config.Section(None, a_dict)
2551
self.assertEquals('out of thin air',
2552
section.get('foo', 'out of thin air'))
2554
def test_options_is_shared(self):
2556
section = config.Section(None, a_dict)
2557
self.assertIs(a_dict, section.options)
2560
class TestMutableSection(tests.TestCase):
2562
scenarios = [('mutable',
2564
lambda opts: config.MutableSection('myID', opts)},),
2568
a_dict = dict(foo='bar')
2569
section = self.get_section(a_dict)
2570
section.set('foo', 'new_value')
2571
self.assertEquals('new_value', section.get('foo'))
2572
# The change appears in the shared section
2573
self.assertEquals('new_value', a_dict.get('foo'))
2574
# We keep track of the change
2575
self.assertTrue('foo' in section.orig)
2576
self.assertEquals('bar', section.orig.get('foo'))
2578
def test_set_preserve_original_once(self):
2579
a_dict = dict(foo='bar')
2580
section = self.get_section(a_dict)
2581
section.set('foo', 'first_value')
2582
section.set('foo', 'second_value')
2583
# We keep track of the original value
2584
self.assertTrue('foo' in section.orig)
2585
self.assertEquals('bar', section.orig.get('foo'))
2587
def test_remove(self):
2588
a_dict = dict(foo='bar')
2589
section = self.get_section(a_dict)
2590
section.remove('foo')
2591
# We get None for unknown options via the default value
2592
self.assertEquals(None, section.get('foo'))
2593
# Or we just get the default value
2594
self.assertEquals('unknown', section.get('foo', 'unknown'))
2595
self.assertFalse('foo' in section.options)
2596
# We keep track of the deletion
2597
self.assertTrue('foo' in section.orig)
2598
self.assertEquals('bar', section.orig.get('foo'))
2600
def test_remove_new_option(self):
2602
section = self.get_section(a_dict)
2603
section.set('foo', 'bar')
2604
section.remove('foo')
2605
self.assertFalse('foo' in section.options)
2606
# The option didn't exist initially so it we need to keep track of it
2607
# with a special value
2608
self.assertTrue('foo' in section.orig)
2609
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2612
class TestCommandLineStore(tests.TestCase):
2615
super(TestCommandLineStore, self).setUp()
2616
self.store = config.CommandLineStore()
2617
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2619
def get_section(self):
2620
"""Get the unique section for the command line overrides."""
2621
sections = list(self.store.get_sections())
2622
self.assertLength(1, sections)
2623
store, section = sections[0]
2624
self.assertEquals(self.store, store)
2627
def test_no_override(self):
2628
self.store._from_cmdline([])
2629
section = self.get_section()
2630
self.assertLength(0, list(section.iter_option_names()))
2632
def test_simple_override(self):
2633
self.store._from_cmdline(['a=b'])
2634
section = self.get_section()
2635
self.assertEqual('b', section.get('a'))
2637
def test_list_override(self):
2638
opt = config.ListOption('l')
2639
config.option_registry.register(opt)
2640
self.store._from_cmdline(['l=1,2,3'])
2641
val = self.get_section().get('l')
2642
self.assertEqual('1,2,3', val)
2643
# Reminder: lists should be registered as such explicitely, otherwise
2644
# the conversion needs to be done afterwards.
2645
self.assertEqual(['1', '2', '3'],
2646
opt.convert_from_unicode(self.store, val))
2648
def test_multiple_overrides(self):
2649
self.store._from_cmdline(['a=b', 'x=y'])
2650
section = self.get_section()
2651
self.assertEquals('b', section.get('a'))
2652
self.assertEquals('y', section.get('x'))
2654
def test_wrong_syntax(self):
2655
self.assertRaises(errors.BzrCommandError,
2656
self.store._from_cmdline, ['a=b', 'c'])
2658
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2660
scenarios = [(key, {'get_store': builder}) for key, builder
2661
in config.test_store_builder_registry.iteritems()] + [
2662
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2665
store = self.get_store(self)
2666
if type(store) == config.TransportIniFileStore:
2667
raise tests.TestNotApplicable(
2668
"%s is not a concrete Store implementation"
2669
" so it doesn't need an id" % (store.__class__.__name__,))
2670
self.assertIsNot(None, store.id)
2673
class TestStore(tests.TestCaseWithTransport):
2675
def assertSectionContent(self, expected, (store, section)):
2676
"""Assert that some options have the proper values in a section."""
2677
expected_name, expected_options = expected
2678
self.assertEquals(expected_name, section.id)
2681
dict([(k, section.get(k)) for k in expected_options.keys()]))
2684
class TestReadonlyStore(TestStore):
2686
scenarios = [(key, {'get_store': builder}) for key, builder
2687
in config.test_store_builder_registry.iteritems()]
2689
def test_building_delays_load(self):
2690
store = self.get_store(self)
2691
self.assertEquals(False, store.is_loaded())
2692
store._load_from_string('')
2693
self.assertEquals(True, store.is_loaded())
2695
def test_get_no_sections_for_empty(self):
2696
store = self.get_store(self)
2697
store._load_from_string('')
2698
self.assertEquals([], list(store.get_sections()))
2700
def test_get_default_section(self):
2701
store = self.get_store(self)
2702
store._load_from_string('foo=bar')
2703
sections = list(store.get_sections())
2704
self.assertLength(1, sections)
2705
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2707
def test_get_named_section(self):
2708
store = self.get_store(self)
2709
store._load_from_string('[baz]\nfoo=bar')
2710
sections = list(store.get_sections())
2711
self.assertLength(1, sections)
2712
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2714
def test_load_from_string_fails_for_non_empty_store(self):
2715
store = self.get_store(self)
2716
store._load_from_string('foo=bar')
2717
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2720
class TestStoreQuoting(TestStore):
2722
scenarios = [(key, {'get_store': builder}) for key, builder
2723
in config.test_store_builder_registry.iteritems()]
2726
super(TestStoreQuoting, self).setUp()
2727
self.store = self.get_store(self)
2728
# We need a loaded store but any content will do
2729
self.store._load_from_string('')
2731
def assertIdempotent(self, s):
2732
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2734
What matters here is that option values, as they appear in a store, can
2735
be safely round-tripped out of the store and back.
2737
:param s: A string, quoted if required.
2739
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2740
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2742
def test_empty_string(self):
2743
if isinstance(self.store, config.IniFileStore):
2744
# configobj._quote doesn't handle empty values
2745
self.assertRaises(AssertionError,
2746
self.assertIdempotent, '')
2748
self.assertIdempotent('')
2749
# But quoted empty strings are ok
2750
self.assertIdempotent('""')
2752
def test_embedded_spaces(self):
2753
self.assertIdempotent('" a b c "')
2755
def test_embedded_commas(self):
2756
self.assertIdempotent('" a , b c "')
2758
def test_simple_comma(self):
2759
if isinstance(self.store, config.IniFileStore):
2760
# configobj requires that lists are special-cased
2761
self.assertRaises(AssertionError,
2762
self.assertIdempotent, ',')
2764
self.assertIdempotent(',')
2765
# When a single comma is required, quoting is also required
2766
self.assertIdempotent('","')
2768
def test_list(self):
2769
if isinstance(self.store, config.IniFileStore):
2770
# configobj requires that lists are special-cased
2771
self.assertRaises(AssertionError,
2772
self.assertIdempotent, 'a,b')
2774
self.assertIdempotent('a,b')
2777
class TestDictFromStore(tests.TestCase):
2779
def test_unquote_not_string(self):
2780
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2781
value = conf.get('a_section')
2782
# Urgh, despite 'conf' asking for the no-name section, we get the
2783
# content of another section as a dict o_O
2784
self.assertEquals({'a': '1'}, value)
2785
unquoted = conf.store.unquote(value)
2786
# Which cannot be unquoted but shouldn't crash either (the use cases
2787
# are getting the value or displaying it. In the later case, '%s' will
2789
self.assertEquals({'a': '1'}, unquoted)
2790
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2793
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2794
"""Simulate loading a config store with content of various encodings.
2796
All files produced by bzr are in utf8 content.
2798
Users may modify them manually and end up with a file that can't be
2799
loaded. We need to issue proper error messages in this case.
2802
invalid_utf8_char = '\xff'
2804
def test_load_utf8(self):
2805
"""Ensure we can load an utf8-encoded file."""
2806
t = self.get_transport()
2807
# From http://pad.lv/799212
2808
unicode_user = u'b\N{Euro Sign}ar'
2809
unicode_content = u'user=%s' % (unicode_user,)
2810
utf8_content = unicode_content.encode('utf8')
2811
# Store the raw content in the config file
2812
t.put_bytes('foo.conf', utf8_content)
2813
store = config.TransportIniFileStore(t, 'foo.conf')
2815
stack = config.Stack([store.get_sections], store)
2816
self.assertEquals(unicode_user, stack.get('user'))
2818
def test_load_non_ascii(self):
2819
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2820
t = self.get_transport()
2821
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2822
store = config.TransportIniFileStore(t, 'foo.conf')
2823
self.assertRaises(errors.ConfigContentError, store.load)
2825
def test_load_erroneous_content(self):
2826
"""Ensure we display a proper error on content that can't be parsed."""
2827
t = self.get_transport()
2828
t.put_bytes('foo.conf', '[open_section\n')
2829
store = config.TransportIniFileStore(t, 'foo.conf')
2830
self.assertRaises(errors.ParseConfigError, store.load)
2832
def test_load_permission_denied(self):
2833
"""Ensure we get warned when trying to load an inaccessible file."""
2836
warnings.append(args[0] % args[1:])
2837
self.overrideAttr(trace, 'warning', warning)
2839
t = self.get_transport()
2841
def get_bytes(relpath):
2842
raise errors.PermissionDenied(relpath, "")
2843
t.get_bytes = get_bytes
2844
store = config.TransportIniFileStore(t, 'foo.conf')
2845
self.assertRaises(errors.PermissionDenied, store.load)
2848
[u'Permission denied while trying to load configuration store %s.'
2849
% store.external_url()])
2852
class TestIniConfigContent(tests.TestCaseWithTransport):
2853
"""Simulate loading a IniBasedConfig with content of various encodings.
2855
All files produced by bzr are in utf8 content.
2857
Users may modify them manually and end up with a file that can't be
2858
loaded. We need to issue proper error messages in this case.
2861
invalid_utf8_char = '\xff'
2863
def test_load_utf8(self):
2864
"""Ensure we can load an utf8-encoded file."""
2865
# From http://pad.lv/799212
2866
unicode_user = u'b\N{Euro Sign}ar'
2867
unicode_content = u'user=%s' % (unicode_user,)
2868
utf8_content = unicode_content.encode('utf8')
2869
# Store the raw content in the config file
2870
with open('foo.conf', 'wb') as f:
2871
f.write(utf8_content)
2872
conf = config.IniBasedConfig(file_name='foo.conf')
2873
self.assertEquals(unicode_user, conf.get_user_option('user'))
2875
def test_load_badly_encoded_content(self):
2876
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2877
with open('foo.conf', 'wb') as f:
2878
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2879
conf = config.IniBasedConfig(file_name='foo.conf')
2880
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2882
def test_load_erroneous_content(self):
2883
"""Ensure we display a proper error on content that can't be parsed."""
2884
with open('foo.conf', 'wb') as f:
2885
f.write('[open_section\n')
2886
conf = config.IniBasedConfig(file_name='foo.conf')
2887
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2890
class TestMutableStore(TestStore):
2892
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2893
in config.test_store_builder_registry.iteritems()]
2896
super(TestMutableStore, self).setUp()
2897
self.transport = self.get_transport()
2899
def has_store(self, store):
2900
store_basename = urlutils.relative_url(self.transport.external_url(),
2901
store.external_url())
2902
return self.transport.has(store_basename)
2904
def test_save_empty_creates_no_file(self):
2905
# FIXME: There should be a better way than relying on the test
2906
# parametrization to identify branch.conf -- vila 2011-0526
2907
if self.store_id in ('branch', 'remote_branch'):
2908
raise tests.TestNotApplicable(
2909
'branch.conf is *always* created when a branch is initialized')
2910
store = self.get_store(self)
2912
self.assertEquals(False, self.has_store(store))
2914
def test_save_emptied_succeeds(self):
2915
store = self.get_store(self)
2916
store._load_from_string('foo=bar\n')
2917
# FIXME: There should be a better way than relying on the test
2918
# parametrization to identify branch.conf -- vila 2011-0526
2919
if self.store_id in ('branch', 'remote_branch'):
2920
# branch stores requires write locked branches
2921
self.addCleanup(store.branch.lock_write().unlock)
2922
section = store.get_mutable_section(None)
2923
section.remove('foo')
2925
self.assertEquals(True, self.has_store(store))
2926
modified_store = self.get_store(self)
2927
sections = list(modified_store.get_sections())
2928
self.assertLength(0, sections)
2930
def test_save_with_content_succeeds(self):
2931
# FIXME: There should be a better way than relying on the test
2932
# parametrization to identify branch.conf -- vila 2011-0526
2933
if self.store_id in ('branch', 'remote_branch'):
2934
raise tests.TestNotApplicable(
2935
'branch.conf is *always* created when a branch is initialized')
2936
store = self.get_store(self)
2937
store._load_from_string('foo=bar\n')
2938
self.assertEquals(False, self.has_store(store))
2940
self.assertEquals(True, self.has_store(store))
2941
modified_store = self.get_store(self)
2942
sections = list(modified_store.get_sections())
2943
self.assertLength(1, sections)
2944
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2946
def test_set_option_in_empty_store(self):
2947
store = self.get_store(self)
2948
# FIXME: There should be a better way than relying on the test
2949
# parametrization to identify branch.conf -- vila 2011-0526
2950
if self.store_id in ('branch', 'remote_branch'):
2951
# branch stores requires write locked branches
2952
self.addCleanup(store.branch.lock_write().unlock)
2953
section = store.get_mutable_section(None)
2954
section.set('foo', 'bar')
2956
modified_store = self.get_store(self)
2957
sections = list(modified_store.get_sections())
2958
self.assertLength(1, sections)
2959
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2961
def test_set_option_in_default_section(self):
2962
store = self.get_store(self)
2963
store._load_from_string('')
2964
# FIXME: There should be a better way than relying on the test
2965
# parametrization to identify branch.conf -- vila 2011-0526
2966
if self.store_id in ('branch', 'remote_branch'):
2967
# branch stores requires write locked branches
2968
self.addCleanup(store.branch.lock_write().unlock)
2969
section = store.get_mutable_section(None)
2970
section.set('foo', 'bar')
2972
modified_store = self.get_store(self)
2973
sections = list(modified_store.get_sections())
2974
self.assertLength(1, sections)
2975
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2977
def test_set_option_in_named_section(self):
2978
store = self.get_store(self)
2979
store._load_from_string('')
2980
# FIXME: There should be a better way than relying on the test
2981
# parametrization to identify branch.conf -- vila 2011-0526
2982
if self.store_id in ('branch', 'remote_branch'):
2983
# branch stores requires write locked branches
2984
self.addCleanup(store.branch.lock_write().unlock)
2985
section = store.get_mutable_section('baz')
2986
section.set('foo', 'bar')
2988
modified_store = self.get_store(self)
2989
sections = list(modified_store.get_sections())
2990
self.assertLength(1, sections)
2991
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2993
def test_load_hook(self):
2994
# First, we need to ensure that the store exists
2995
store = self.get_store(self)
2996
# FIXME: There should be a better way than relying on the test
2997
# parametrization to identify branch.conf -- vila 2011-0526
2998
if self.store_id in ('branch', 'remote_branch'):
2999
# branch stores requires write locked branches
3000
self.addCleanup(store.branch.lock_write().unlock)
3001
section = store.get_mutable_section('baz')
3002
section.set('foo', 'bar')
3004
# Now we can try to load it
3005
store = self.get_store(self)
3009
config.ConfigHooks.install_named_hook('load', hook, None)
3010
self.assertLength(0, calls)
3012
self.assertLength(1, calls)
3013
self.assertEquals((store,), calls[0])
3015
def test_save_hook(self):
3019
config.ConfigHooks.install_named_hook('save', hook, None)
3020
self.assertLength(0, calls)
3021
store = self.get_store(self)
3022
# FIXME: There should be a better way than relying on the test
3023
# parametrization to identify branch.conf -- vila 2011-0526
3024
if self.store_id in ('branch', 'remote_branch'):
3025
# branch stores requires write locked branches
3026
self.addCleanup(store.branch.lock_write().unlock)
3027
section = store.get_mutable_section('baz')
3028
section.set('foo', 'bar')
3030
self.assertLength(1, calls)
3031
self.assertEquals((store,), calls[0])
3033
def test_set_mark_dirty(self):
3034
stack = config.MemoryStack('')
3035
self.assertLength(0, stack.store.dirty_sections)
3036
stack.set('foo', 'baz')
3037
self.assertLength(1, stack.store.dirty_sections)
3038
self.assertTrue(stack.store._need_saving())
3040
def test_remove_mark_dirty(self):
3041
stack = config.MemoryStack('foo=bar')
3042
self.assertLength(0, stack.store.dirty_sections)
3044
self.assertLength(1, stack.store.dirty_sections)
3045
self.assertTrue(stack.store._need_saving())
3048
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3049
"""Tests that config changes are kept in memory and saved on-demand."""
3052
super(TestStoreSaveChanges, self).setUp()
3053
self.transport = self.get_transport()
3054
# Most of the tests involve two stores pointing to the same persistent
3055
# storage to observe the effects of concurrent changes
3056
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3057
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3060
self.warnings.append(args[0] % args[1:])
3061
self.overrideAttr(trace, 'warning', warning)
3063
def has_store(self, store):
3064
store_basename = urlutils.relative_url(self.transport.external_url(),
3065
store.external_url())
3066
return self.transport.has(store_basename)
3068
def get_stack(self, store):
3069
# Any stack will do as long as it uses the right store, just a single
3070
# no-name section is enough
3071
return config.Stack([store.get_sections], store)
3073
def test_no_changes_no_save(self):
3074
s = self.get_stack(self.st1)
3075
s.store.save_changes()
3076
self.assertEquals(False, self.has_store(self.st1))
3078
def test_unrelated_concurrent_update(self):
3079
s1 = self.get_stack(self.st1)
3080
s2 = self.get_stack(self.st2)
3081
s1.set('foo', 'bar')
3082
s2.set('baz', 'quux')
3084
# Changes don't propagate magically
3085
self.assertEquals(None, s1.get('baz'))
3086
s2.store.save_changes()
3087
self.assertEquals('quux', s2.get('baz'))
3088
# Changes are acquired when saving
3089
self.assertEquals('bar', s2.get('foo'))
3090
# Since there is no overlap, no warnings are emitted
3091
self.assertLength(0, self.warnings)
3093
def test_concurrent_update_modified(self):
3094
s1 = self.get_stack(self.st1)
3095
s2 = self.get_stack(self.st2)
3096
s1.set('foo', 'bar')
3097
s2.set('foo', 'baz')
3100
s2.store.save_changes()
3101
self.assertEquals('baz', s2.get('foo'))
3102
# But the user get a warning
3103
self.assertLength(1, self.warnings)
3104
warning = self.warnings[0]
3105
self.assertStartsWith(warning, 'Option foo in section None')
3106
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3107
' The baz value will be saved.')
3109
def test_concurrent_deletion(self):
3110
self.st1._load_from_string('foo=bar')
3112
s1 = self.get_stack(self.st1)
3113
s2 = self.get_stack(self.st2)
3116
s1.store.save_changes()
3118
self.assertLength(0, self.warnings)
3119
s2.store.save_changes()
3121
self.assertLength(1, self.warnings)
3122
warning = self.warnings[0]
3123
self.assertStartsWith(warning, 'Option foo in section None')
3124
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3125
' The <DELETED> value will be saved.')
3128
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3130
def get_store(self):
3131
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3133
def test_get_quoted_string(self):
3134
store = self.get_store()
3135
store._load_from_string('foo= " abc "')
3136
stack = config.Stack([store.get_sections])
3137
self.assertEquals(' abc ', stack.get('foo'))
3139
def test_set_quoted_string(self):
3140
store = self.get_store()
3141
stack = config.Stack([store.get_sections], store)
3142
stack.set('foo', ' a b c ')
3144
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3147
class TestTransportIniFileStore(TestStore):
3149
def test_loading_unknown_file_fails(self):
3150
store = config.TransportIniFileStore(self.get_transport(),
3152
self.assertRaises(errors.NoSuchFile, store.load)
3154
def test_invalid_content(self):
3155
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3156
self.assertEquals(False, store.is_loaded())
3157
exc = self.assertRaises(
3158
errors.ParseConfigError, store._load_from_string,
3159
'this is invalid !')
3160
self.assertEndsWith(exc.filename, 'foo.conf')
3161
# And the load failed
3162
self.assertEquals(False, store.is_loaded())
3164
def test_get_embedded_sections(self):
3165
# A more complicated example (which also shows that section names and
3166
# option names share the same name space...)
3167
# FIXME: This should be fixed by forbidding dicts as values ?
3168
# -- vila 2011-04-05
3169
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3170
store._load_from_string('''
3174
foo_in_DEFAULT=foo_DEFAULT
3182
sections = list(store.get_sections())
3183
self.assertLength(4, sections)
3184
# The default section has no name.
3185
# List values are provided as strings and need to be explicitly
3186
# converted by specifying from_unicode=list_from_store at option
3188
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3190
self.assertSectionContent(
3191
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3192
self.assertSectionContent(
3193
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3194
# sub sections are provided as embedded dicts.
3195
self.assertSectionContent(
3196
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3200
class TestLockableIniFileStore(TestStore):
3202
def test_create_store_in_created_dir(self):
3203
self.assertPathDoesNotExist('dir')
3204
t = self.get_transport('dir/subdir')
3205
store = config.LockableIniFileStore(t, 'foo.conf')
3206
store.get_mutable_section(None).set('foo', 'bar')
3208
self.assertPathExists('dir/subdir')
3211
class TestConcurrentStoreUpdates(TestStore):
3212
"""Test that Stores properly handle conccurent updates.
3214
New Store implementation may fail some of these tests but until such
3215
implementations exist it's hard to properly filter them from the scenarios
3216
applied here. If you encounter such a case, contact the bzr devs.
3219
scenarios = [(key, {'get_stack': builder}) for key, builder
3220
in config.test_stack_builder_registry.iteritems()]
3223
super(TestConcurrentStoreUpdates, self).setUp()
3224
self.stack = self.get_stack(self)
3225
if not isinstance(self.stack, config._CompatibleStack):
3226
raise tests.TestNotApplicable(
3227
'%s is not meant to be compatible with the old config design'
3229
self.stack.set('one', '1')
3230
self.stack.set('two', '2')
3232
self.stack.store.save()
3234
def test_simple_read_access(self):
3235
self.assertEquals('1', self.stack.get('one'))
3237
def test_simple_write_access(self):
3238
self.stack.set('one', 'one')
3239
self.assertEquals('one', self.stack.get('one'))
3241
def test_listen_to_the_last_speaker(self):
3243
c2 = self.get_stack(self)
3244
c1.set('one', 'ONE')
3245
c2.set('two', 'TWO')
3246
self.assertEquals('ONE', c1.get('one'))
3247
self.assertEquals('TWO', c2.get('two'))
3248
# The second update respect the first one
3249
self.assertEquals('ONE', c2.get('one'))
3251
def test_last_speaker_wins(self):
3252
# If the same config is not shared, the same variable modified twice
3253
# can only see a single result.
3255
c2 = self.get_stack(self)
3258
self.assertEquals('c2', c2.get('one'))
3259
# The first modification is still available until another refresh
3261
self.assertEquals('c1', c1.get('one'))
3262
c1.set('two', 'done')
3263
self.assertEquals('c2', c1.get('one'))
3265
def test_writes_are_serialized(self):
3267
c2 = self.get_stack(self)
3269
# We spawn a thread that will pause *during* the config saving.
3270
before_writing = threading.Event()
3271
after_writing = threading.Event()
3272
writing_done = threading.Event()
3273
c1_save_without_locking_orig = c1.store.save_without_locking
3274
def c1_save_without_locking():
3275
before_writing.set()
3276
c1_save_without_locking_orig()
3277
# The lock is held. We wait for the main thread to decide when to
3279
after_writing.wait()
3280
c1.store.save_without_locking = c1_save_without_locking
3284
t1 = threading.Thread(target=c1_set)
3285
# Collect the thread after the test
3286
self.addCleanup(t1.join)
3287
# Be ready to unblock the thread if the test goes wrong
3288
self.addCleanup(after_writing.set)
3290
before_writing.wait()
3291
self.assertRaises(errors.LockContention,
3292
c2.set, 'one', 'c2')
3293
self.assertEquals('c1', c1.get('one'))
3294
# Let the lock be released
3298
self.assertEquals('c2', c2.get('one'))
3300
def test_read_while_writing(self):
3302
# We spawn a thread that will pause *during* the write
3303
ready_to_write = threading.Event()
3304
do_writing = threading.Event()
3305
writing_done = threading.Event()
3306
# We override the _save implementation so we know the store is locked
3307
c1_save_without_locking_orig = c1.store.save_without_locking
3308
def c1_save_without_locking():
3309
ready_to_write.set()
3310
# The lock is held. We wait for the main thread to decide when to
3313
c1_save_without_locking_orig()
3315
c1.store.save_without_locking = c1_save_without_locking
3318
t1 = threading.Thread(target=c1_set)
3319
# Collect the thread after the test
3320
self.addCleanup(t1.join)
3321
# Be ready to unblock the thread if the test goes wrong
3322
self.addCleanup(do_writing.set)
3324
# Ensure the thread is ready to write
3325
ready_to_write.wait()
3326
self.assertEquals('c1', c1.get('one'))
3327
# If we read during the write, we get the old value
3328
c2 = self.get_stack(self)
3329
self.assertEquals('1', c2.get('one'))
3330
# Let the writing occur and ensure it occurred
3333
# Now we get the updated value
3334
c3 = self.get_stack(self)
3335
self.assertEquals('c1', c3.get('one'))
3337
# FIXME: It may be worth looking into removing the lock dir when it's not
3338
# needed anymore and look at possible fallouts for concurrent lockers. This
3339
# will matter if/when we use config files outside of bazaar directories
3340
# (.bazaar or .bzr) -- vila 20110-04-111
3343
class TestSectionMatcher(TestStore):
3345
scenarios = [('location', {'matcher': config.LocationMatcher}),
3346
('id', {'matcher': config.NameMatcher}),]
3349
super(TestSectionMatcher, self).setUp()
3350
# Any simple store is good enough
3351
self.get_store = config.test_store_builder_registry.get('configobj')
3353
def test_no_matches_for_empty_stores(self):
3354
store = self.get_store(self)
3355
store._load_from_string('')
3356
matcher = self.matcher(store, '/bar')
3357
self.assertEquals([], list(matcher.get_sections()))
3359
def test_build_doesnt_load_store(self):
3360
store = self.get_store(self)
3361
matcher = self.matcher(store, '/bar')
3362
self.assertFalse(store.is_loaded())
3365
class TestLocationSection(tests.TestCase):
3367
def get_section(self, options, extra_path):
3368
section = config.Section('foo', options)
3369
# We don't care about the length so we use '0'
3370
return config.LocationSection(section, 0, extra_path)
3372
def test_simple_option(self):
3373
section = self.get_section({'foo': 'bar'}, '')
3374
self.assertEquals('bar', section.get('foo'))
3376
def test_option_with_extra_path(self):
3377
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3379
self.assertEquals('bar/baz', section.get('foo'))
3381
def test_invalid_policy(self):
3382
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3384
# invalid policies are ignored
3385
self.assertEquals('bar', section.get('foo'))
3388
class TestLocationMatcher(TestStore):
3391
super(TestLocationMatcher, self).setUp()
3392
# Any simple store is good enough
3393
self.get_store = config.test_store_builder_registry.get('configobj')
3395
def test_unrelated_section_excluded(self):
3396
store = self.get_store(self)
3397
store._load_from_string('''
3405
section=/foo/bar/baz
3409
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3411
[section.id for _, section in store.get_sections()])
3412
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3413
sections = [section for s, section in matcher.get_sections()]
3414
self.assertEquals([3, 2],
3415
[section.length for section in sections])
3416
self.assertEquals(['/foo/bar', '/foo'],
3417
[section.id for section in sections])
3418
self.assertEquals(['quux', 'bar/quux'],
3419
[section.extra_path for section in sections])
3421
def test_more_specific_sections_first(self):
3422
store = self.get_store(self)
3423
store._load_from_string('''
3429
self.assertEquals(['/foo', '/foo/bar'],
3430
[section.id for _, section in store.get_sections()])
3431
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3432
sections = [section for s, section in matcher.get_sections()]
3433
self.assertEquals([3, 2],
3434
[section.length for section in sections])
3435
self.assertEquals(['/foo/bar', '/foo'],
3436
[section.id for section in sections])
3437
self.assertEquals(['baz', 'bar/baz'],
3438
[section.extra_path for section in sections])
3440
def test_appendpath_in_no_name_section(self):
3441
# It's a bit weird to allow appendpath in a no-name section, but
3442
# someone may found a use for it
3443
store = self.get_store(self)
3444
store._load_from_string('''
3446
foo:policy = appendpath
3448
matcher = config.LocationMatcher(store, 'dir/subdir')
3449
sections = list(matcher.get_sections())
3450
self.assertLength(1, sections)
3451
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3453
def test_file_urls_are_normalized(self):
3454
store = self.get_store(self)
3455
if sys.platform == 'win32':
3456
expected_url = 'file:///C:/dir/subdir'
3457
expected_location = 'C:/dir/subdir'
3459
expected_url = 'file:///dir/subdir'
3460
expected_location = '/dir/subdir'
3461
matcher = config.LocationMatcher(store, expected_url)
3462
self.assertEquals(expected_location, matcher.location)
3465
class TestNameMatcher(TestStore):
3468
super(TestNameMatcher, self).setUp()
3469
self.matcher = config.NameMatcher
3470
# Any simple store is good enough
3471
self.get_store = config.test_store_builder_registry.get('configobj')
3473
def get_matching_sections(self, name):
3474
store = self.get_store(self)
3475
store._load_from_string('''
3483
matcher = self.matcher(store, name)
3484
return list(matcher.get_sections())
3486
def test_matching(self):
3487
sections = self.get_matching_sections('foo')
3488
self.assertLength(1, sections)
3489
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3491
def test_not_matching(self):
3492
sections = self.get_matching_sections('baz')
3493
self.assertLength(0, sections)
3496
class TestBaseStackGet(tests.TestCase):
3499
super(TestBaseStackGet, self).setUp()
3500
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3502
def test_get_first_definition(self):
3503
store1 = config.IniFileStore()
3504
store1._load_from_string('foo=bar')
3505
store2 = config.IniFileStore()
3506
store2._load_from_string('foo=baz')
3507
conf = config.Stack([store1.get_sections, store2.get_sections])
3508
self.assertEquals('bar', conf.get('foo'))
3510
def test_get_with_registered_default_value(self):
3511
config.option_registry.register(config.Option('foo', default='bar'))
3512
conf_stack = config.Stack([])
3513
self.assertEquals('bar', conf_stack.get('foo'))
3515
def test_get_without_registered_default_value(self):
3516
config.option_registry.register(config.Option('foo'))
3517
conf_stack = config.Stack([])
3518
self.assertEquals(None, conf_stack.get('foo'))
3520
def test_get_without_default_value_for_not_registered(self):
3521
conf_stack = config.Stack([])
3522
self.assertEquals(None, conf_stack.get('foo'))
3524
def test_get_for_empty_section_callable(self):
3525
conf_stack = config.Stack([lambda : []])
3526
self.assertEquals(None, conf_stack.get('foo'))
3528
def test_get_for_broken_callable(self):
3529
# Trying to use and invalid callable raises an exception on first use
3530
conf_stack = config.Stack([object])
3531
self.assertRaises(TypeError, conf_stack.get, 'foo')
3534
class TestStackWithSimpleStore(tests.TestCase):
3537
super(TestStackWithSimpleStore, self).setUp()
3538
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3539
self.registry = config.option_registry
3541
def get_conf(self, content=None):
3542
return config.MemoryStack(content)
3544
def test_override_value_from_env(self):
3545
self.registry.register(
3546
config.Option('foo', default='bar', override_from_env=['FOO']))
3547
self.overrideEnv('FOO', 'quux')
3548
# Env variable provides a default taking over the option one
3549
conf = self.get_conf('foo=store')
3550
self.assertEquals('quux', conf.get('foo'))
3552
def test_first_override_value_from_env_wins(self):
3553
self.registry.register(
3554
config.Option('foo', default='bar',
3555
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3556
self.overrideEnv('FOO', 'foo')
3557
self.overrideEnv('BAZ', 'baz')
3558
# The first env var set wins
3559
conf = self.get_conf('foo=store')
3560
self.assertEquals('foo', conf.get('foo'))
3563
class TestMemoryStack(tests.TestCase):
3566
conf = config.MemoryStack('foo=bar')
3567
self.assertEquals('bar', conf.get('foo'))
3570
conf = config.MemoryStack('foo=bar')
3571
conf.set('foo', 'baz')
3572
self.assertEquals('baz', conf.get('foo'))
3574
def test_no_content(self):
3575
conf = config.MemoryStack()
3576
# No content means no loading
3577
self.assertFalse(conf.store.is_loaded())
3578
self.assertRaises(NotImplementedError, conf.get, 'foo')
3579
# But a content can still be provided
3580
conf.store._load_from_string('foo=bar')
3581
self.assertEquals('bar', conf.get('foo'))
3584
class TestStackWithTransport(tests.TestCaseWithTransport):
3586
scenarios = [(key, {'get_stack': builder}) for key, builder
3587
in config.test_stack_builder_registry.iteritems()]
3590
class TestConcreteStacks(TestStackWithTransport):
3592
def test_build_stack(self):
3593
# Just a smoke test to help debug builders
3594
stack = self.get_stack(self)
3597
class TestStackGet(TestStackWithTransport):
3600
super(TestStackGet, self).setUp()
3601
self.conf = self.get_stack(self)
3603
def test_get_for_empty_stack(self):
3604
self.assertEquals(None, self.conf.get('foo'))
3606
def test_get_hook(self):
3607
self.conf.set('foo', 'bar')
3611
config.ConfigHooks.install_named_hook('get', hook, None)
3612
self.assertLength(0, calls)
3613
value = self.conf.get('foo')
3614
self.assertEquals('bar', value)
3615
self.assertLength(1, calls)
3616
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3619
class TestStackGetWithConverter(tests.TestCase):
3622
super(TestStackGetWithConverter, self).setUp()
3623
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3624
self.registry = config.option_registry
3626
def get_conf(self, content=None):
3627
return config.MemoryStack(content)
3629
def register_bool_option(self, name, default=None, default_from_env=None):
3630
b = config.Option(name, help='A boolean.',
3631
default=default, default_from_env=default_from_env,
3632
from_unicode=config.bool_from_store)
3633
self.registry.register(b)
3635
def test_get_default_bool_None(self):
3636
self.register_bool_option('foo')
3637
conf = self.get_conf('')
3638
self.assertEquals(None, conf.get('foo'))
3640
def test_get_default_bool_True(self):
3641
self.register_bool_option('foo', u'True')
3642
conf = self.get_conf('')
3643
self.assertEquals(True, conf.get('foo'))
3645
def test_get_default_bool_False(self):
3646
self.register_bool_option('foo', False)
3647
conf = self.get_conf('')
3648
self.assertEquals(False, conf.get('foo'))
3650
def test_get_default_bool_False_as_string(self):
3651
self.register_bool_option('foo', u'False')
3652
conf = self.get_conf('')
3653
self.assertEquals(False, conf.get('foo'))
3655
def test_get_default_bool_from_env_converted(self):
3656
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3657
self.overrideEnv('FOO', 'False')
3658
conf = self.get_conf('')
3659
self.assertEquals(False, conf.get('foo'))
3661
def test_get_default_bool_when_conversion_fails(self):
3662
self.register_bool_option('foo', default='True')
3663
conf = self.get_conf('foo=invalid boolean')
3664
self.assertEquals(True, conf.get('foo'))
3666
def register_integer_option(self, name,
3667
default=None, default_from_env=None):
3668
i = config.Option(name, help='An integer.',
3669
default=default, default_from_env=default_from_env,
3670
from_unicode=config.int_from_store)
3671
self.registry.register(i)
3673
def test_get_default_integer_None(self):
3674
self.register_integer_option('foo')
3675
conf = self.get_conf('')
3676
self.assertEquals(None, conf.get('foo'))
3678
def test_get_default_integer(self):
3679
self.register_integer_option('foo', 42)
3680
conf = self.get_conf('')
3681
self.assertEquals(42, conf.get('foo'))
3683
def test_get_default_integer_as_string(self):
3684
self.register_integer_option('foo', u'42')
3685
conf = self.get_conf('')
3686
self.assertEquals(42, conf.get('foo'))
3688
def test_get_default_integer_from_env(self):
3689
self.register_integer_option('foo', default_from_env=['FOO'])
3690
self.overrideEnv('FOO', '18')
3691
conf = self.get_conf('')
3692
self.assertEquals(18, conf.get('foo'))
3694
def test_get_default_integer_when_conversion_fails(self):
3695
self.register_integer_option('foo', default='12')
3696
conf = self.get_conf('foo=invalid integer')
3697
self.assertEquals(12, conf.get('foo'))
3699
def register_list_option(self, name, default=None, default_from_env=None):
3700
l = config.ListOption(name, help='A list.', default=default,
3701
default_from_env=default_from_env)
3702
self.registry.register(l)
3704
def test_get_default_list_None(self):
3705
self.register_list_option('foo')
3706
conf = self.get_conf('')
3707
self.assertEquals(None, conf.get('foo'))
3709
def test_get_default_list_empty(self):
3710
self.register_list_option('foo', '')
3711
conf = self.get_conf('')
3712
self.assertEquals([], conf.get('foo'))
3714
def test_get_default_list_from_env(self):
3715
self.register_list_option('foo', default_from_env=['FOO'])
3716
self.overrideEnv('FOO', '')
3717
conf = self.get_conf('')
3718
self.assertEquals([], conf.get('foo'))
3720
def test_get_with_list_converter_no_item(self):
3721
self.register_list_option('foo', None)
3722
conf = self.get_conf('foo=,')
3723
self.assertEquals([], conf.get('foo'))
3725
def test_get_with_list_converter_many_items(self):
3726
self.register_list_option('foo', None)
3727
conf = self.get_conf('foo=m,o,r,e')
3728
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3730
def test_get_with_list_converter_embedded_spaces_many_items(self):
3731
self.register_list_option('foo', None)
3732
conf = self.get_conf('foo=" bar", "baz "')
3733
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3735
def test_get_with_list_converter_stripped_spaces_many_items(self):
3736
self.register_list_option('foo', None)
3737
conf = self.get_conf('foo= bar , baz ')
3738
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3741
class TestIterOptionRefs(tests.TestCase):
3742
"""iter_option_refs is a bit unusual, document some cases."""
3744
def assertRefs(self, expected, string):
3745
self.assertEquals(expected, list(config.iter_option_refs(string)))
3747
def test_empty(self):
3748
self.assertRefs([(False, '')], '')
3750
def test_no_refs(self):
3751
self.assertRefs([(False, 'foo bar')], 'foo bar')
3753
def test_single_ref(self):
3754
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3756
def test_broken_ref(self):
3757
self.assertRefs([(False, '{foo')], '{foo')
3759
def test_embedded_ref(self):
3760
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3763
def test_two_refs(self):
3764
self.assertRefs([(False, ''), (True, '{foo}'),
3765
(False, ''), (True, '{bar}'),
3769
def test_newline_in_refs_are_not_matched(self):
3770
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3773
class TestStackExpandOptions(tests.TestCaseWithTransport):
3776
super(TestStackExpandOptions, self).setUp()
3777
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3778
self.registry = config.option_registry
3779
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3780
self.conf = config.Stack([store.get_sections], store)
3782
def assertExpansion(self, expected, string, env=None):
3783
self.assertEquals(expected, self.conf.expand_options(string, env))
3785
def test_no_expansion(self):
3786
self.assertExpansion('foo', 'foo')
3788
def test_expand_default_value(self):
3789
self.conf.store._load_from_string('bar=baz')
3790
self.registry.register(config.Option('foo', default=u'{bar}'))
3791
self.assertEquals('baz', self.conf.get('foo', expand=True))
3793
def test_expand_default_from_env(self):
3794
self.conf.store._load_from_string('bar=baz')
3795
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3796
self.overrideEnv('FOO', '{bar}')
3797
self.assertEquals('baz', self.conf.get('foo', expand=True))
3799
def test_expand_default_on_failed_conversion(self):
3800
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3801
self.registry.register(
3802
config.Option('foo', default=u'{bar}',
3803
from_unicode=config.int_from_store))
3804
self.assertEquals(42, self.conf.get('foo', expand=True))
3806
def test_env_adding_options(self):
3807
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3809
def test_env_overriding_options(self):
3810
self.conf.store._load_from_string('foo=baz')
3811
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3813
def test_simple_ref(self):
3814
self.conf.store._load_from_string('foo=xxx')
3815
self.assertExpansion('xxx', '{foo}')
3817
def test_unknown_ref(self):
3818
self.assertRaises(errors.ExpandingUnknownOption,
3819
self.conf.expand_options, '{foo}')
3821
def test_indirect_ref(self):
3822
self.conf.store._load_from_string('''
3826
self.assertExpansion('xxx', '{bar}')
3828
def test_embedded_ref(self):
3829
self.conf.store._load_from_string('''
3833
self.assertExpansion('xxx', '{{bar}}')
3835
def test_simple_loop(self):
3836
self.conf.store._load_from_string('foo={foo}')
3837
self.assertRaises(errors.OptionExpansionLoop,
3838
self.conf.expand_options, '{foo}')
3840
def test_indirect_loop(self):
3841
self.conf.store._load_from_string('''
3845
e = self.assertRaises(errors.OptionExpansionLoop,
3846
self.conf.expand_options, '{foo}')
3847
self.assertEquals('foo->bar->baz', e.refs)
3848
self.assertEquals('{foo}', e.string)
3850
def test_list(self):
3851
self.conf.store._load_from_string('''
3855
list={foo},{bar},{baz}
3857
self.registry.register(
3858
config.ListOption('list'))
3859
self.assertEquals(['start', 'middle', 'end'],
3860
self.conf.get('list', expand=True))
3862
def test_cascading_list(self):
3863
self.conf.store._load_from_string('''
3869
self.registry.register(
3870
config.ListOption('list'))
3871
self.assertEquals(['start', 'middle', 'end'],
3872
self.conf.get('list', expand=True))
3874
def test_pathologically_hidden_list(self):
3875
self.conf.store._load_from_string('''
3881
hidden={start}{middle}{end}
3883
# What matters is what the registration says, the conversion happens
3884
# only after all expansions have been performed
3885
self.registry.register(config.ListOption('hidden'))
3886
self.assertEquals(['bin', 'go'],
3887
self.conf.get('hidden', expand=True))
3890
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3893
super(TestStackCrossSectionsExpand, self).setUp()
3895
def get_config(self, location, string):
3898
# Since we don't save the config we won't strictly require to inherit
3899
# from TestCaseInTempDir, but an error occurs so quickly...
3900
c = config.LocationStack(location)
3901
c.store._load_from_string(string)
3904
def test_dont_cross_unrelated_section(self):
3905
c = self.get_config('/another/branch/path','''
3910
[/another/branch/path]
3913
self.assertRaises(errors.ExpandingUnknownOption,
3914
c.get, 'bar', expand=True)
3916
def test_cross_related_sections(self):
3917
c = self.get_config('/project/branch/path','''
3921
[/project/branch/path]
3924
self.assertEquals('quux', c.get('bar', expand=True))
3927
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3929
def test_cross_global_locations(self):
3930
l_store = config.LocationStore()
3931
l_store._load_from_string('''
3937
g_store = config.GlobalStore()
3938
g_store._load_from_string('''
3944
stack = config.LocationStack('/branch')
3945
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3946
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3949
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3951
def test_expand_locals_empty(self):
3952
l_store = config.LocationStore()
3953
l_store._load_from_string('''
3954
[/home/user/project]
3959
stack = config.LocationStack('/home/user/project/')
3960
self.assertEquals('', stack.get('base', expand=True))
3961
self.assertEquals('', stack.get('rel', expand=True))
3963
def test_expand_basename_locally(self):
3964
l_store = config.LocationStore()
3965
l_store._load_from_string('''
3966
[/home/user/project]
3970
stack = config.LocationStack('/home/user/project/branch')
3971
self.assertEquals('branch', stack.get('bfoo', expand=True))
3973
def test_expand_basename_locally_longer_path(self):
3974
l_store = config.LocationStore()
3975
l_store._load_from_string('''
3980
stack = config.LocationStack('/home/user/project/dir/branch')
3981
self.assertEquals('branch', stack.get('bfoo', expand=True))
3983
def test_expand_relpath_locally(self):
3984
l_store = config.LocationStore()
3985
l_store._load_from_string('''
3986
[/home/user/project]
3987
lfoo = loc-foo/{relpath}
3990
stack = config.LocationStack('/home/user/project/branch')
3991
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3993
def test_expand_relpath_unknonw_in_global(self):
3994
g_store = config.GlobalStore()
3995
g_store._load_from_string('''
4000
stack = config.LocationStack('/home/user/project/branch')
4001
self.assertRaises(errors.ExpandingUnknownOption,
4002
stack.get, 'gfoo', expand=True)
4004
def test_expand_local_option_locally(self):
4005
l_store = config.LocationStore()
4006
l_store._load_from_string('''
4007
[/home/user/project]
4008
lfoo = loc-foo/{relpath}
4012
g_store = config.GlobalStore()
4013
g_store._load_from_string('''
4019
stack = config.LocationStack('/home/user/project/branch')
4020
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4021
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4023
def test_locals_dont_leak(self):
4024
"""Make sure we chose the right local in presence of several sections.
4026
l_store = config.LocationStore()
4027
l_store._load_from_string('''
4029
lfoo = loc-foo/{relpath}
4030
[/home/user/project]
4031
lfoo = loc-foo/{relpath}
4034
stack = config.LocationStack('/home/user/project/branch')
4035
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4036
stack = config.LocationStack('/home/user/bar/baz')
4037
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
4041
class TestStackSet(TestStackWithTransport):
4043
def test_simple_set(self):
4044
conf = self.get_stack(self)
4045
self.assertEquals(None, conf.get('foo'))
4046
conf.set('foo', 'baz')
4047
# Did we get it back ?
4048
self.assertEquals('baz', conf.get('foo'))
4050
def test_set_creates_a_new_section(self):
4051
conf = self.get_stack(self)
4052
conf.set('foo', 'baz')
4053
self.assertEquals, 'baz', conf.get('foo')
4055
def test_set_hook(self):
4059
config.ConfigHooks.install_named_hook('set', hook, None)
4060
self.assertLength(0, calls)
4061
conf = self.get_stack(self)
4062
conf.set('foo', 'bar')
4063
self.assertLength(1, calls)
4064
self.assertEquals((conf, 'foo', 'bar'), calls[0])
4067
class TestStackRemove(TestStackWithTransport):
4069
def test_remove_existing(self):
4070
conf = self.get_stack(self)
4071
conf.set('foo', 'bar')
4072
self.assertEquals('bar', conf.get('foo'))
4074
# Did we get it back ?
4075
self.assertEquals(None, conf.get('foo'))
4077
def test_remove_unknown(self):
4078
conf = self.get_stack(self)
4079
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
4081
def test_remove_hook(self):
4085
config.ConfigHooks.install_named_hook('remove', hook, None)
4086
self.assertLength(0, calls)
4087
conf = self.get_stack(self)
4088
conf.set('foo', 'bar')
4090
self.assertLength(1, calls)
4091
self.assertEquals((conf, 'foo'), calls[0])
4094
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
4097
super(TestConfigGetOptions, self).setUp()
4098
create_configs(self)
4100
def test_no_variable(self):
4101
# Using branch should query branch, locations and bazaar
4102
self.assertOptions([], self.branch_config)
4104
def test_option_in_bazaar(self):
4105
self.bazaar_config.set_user_option('file', 'bazaar')
4106
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
4109
def test_option_in_locations(self):
4110
self.locations_config.set_user_option('file', 'locations')
4112
[('file', 'locations', self.tree.basedir, 'locations')],
4113
self.locations_config)
4115
def test_option_in_branch(self):
4116
self.branch_config.set_user_option('file', 'branch')
4117
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
4120
def test_option_in_bazaar_and_branch(self):
4121
self.bazaar_config.set_user_option('file', 'bazaar')
4122
self.branch_config.set_user_option('file', 'branch')
4123
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4124
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4127
def test_option_in_branch_and_locations(self):
4128
# Hmm, locations override branch :-/
4129
self.locations_config.set_user_option('file', 'locations')
4130
self.branch_config.set_user_option('file', 'branch')
4132
[('file', 'locations', self.tree.basedir, 'locations'),
4133
('file', 'branch', 'DEFAULT', 'branch'),],
4136
def test_option_in_bazaar_locations_and_branch(self):
4137
self.bazaar_config.set_user_option('file', 'bazaar')
4138
self.locations_config.set_user_option('file', 'locations')
4139
self.branch_config.set_user_option('file', 'branch')
4141
[('file', 'locations', self.tree.basedir, 'locations'),
4142
('file', 'branch', 'DEFAULT', 'branch'),
4143
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4147
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
4150
super(TestConfigRemoveOption, self).setUp()
4151
create_configs_with_file_option(self)
4153
def test_remove_in_locations(self):
4154
self.locations_config.remove_user_option('file', self.tree.basedir)
4156
[('file', 'branch', 'DEFAULT', 'branch'),
4157
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4160
def test_remove_in_branch(self):
4161
self.branch_config.remove_user_option('file')
4163
[('file', 'locations', self.tree.basedir, 'locations'),
4164
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4167
def test_remove_in_bazaar(self):
4168
self.bazaar_config.remove_user_option('file')
4170
[('file', 'locations', self.tree.basedir, 'locations'),
4171
('file', 'branch', 'DEFAULT', 'branch'),],
4175
class TestConfigGetSections(tests.TestCaseWithTransport):
4178
super(TestConfigGetSections, self).setUp()
4179
create_configs(self)
4181
def assertSectionNames(self, expected, conf, name=None):
4182
"""Check which sections are returned for a given config.
4184
If fallback configurations exist their sections can be included.
4186
:param expected: A list of section names.
4188
:param conf: The configuration that will be queried.
4190
:param name: An optional section name that will be passed to
4193
sections = list(conf._get_sections(name))
4194
self.assertLength(len(expected), sections)
4195
self.assertEqual(expected, [name for name, _, _ in sections])
4197
def test_bazaar_default_section(self):
4198
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4200
def test_locations_default_section(self):
4201
# No sections are defined in an empty file
4202
self.assertSectionNames([], self.locations_config)
4204
def test_locations_named_section(self):
4205
self.locations_config.set_user_option('file', 'locations')
4206
self.assertSectionNames([self.tree.basedir], self.locations_config)
4208
def test_locations_matching_sections(self):
4209
loc_config = self.locations_config
4210
loc_config.set_user_option('file', 'locations')
4211
# We need to cheat a bit here to create an option in sections above and
4212
# below the 'location' one.
4213
parser = loc_config._get_parser()
4214
# locations.cong deals with '/' ignoring native os.sep
4215
location_names = self.tree.basedir.split('/')
4216
parent = '/'.join(location_names[:-1])
4217
child = '/'.join(location_names + ['child'])
4219
parser[parent]['file'] = 'parent'
4221
parser[child]['file'] = 'child'
4222
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4224
def test_branch_data_default_section(self):
4225
self.assertSectionNames([None],
4226
self.branch_config._get_branch_data_config())
4228
def test_branch_default_sections(self):
4229
# No sections are defined in an empty locations file
4230
self.assertSectionNames([None, 'DEFAULT'],
4232
# Unless we define an option
4233
self.branch_config._get_location_config().set_user_option(
4234
'file', 'locations')
4235
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4238
def test_bazaar_named_section(self):
4239
# We need to cheat as the API doesn't give direct access to sections
4240
# other than DEFAULT.
4241
self.bazaar_config.set_alias('bazaar', 'bzr')
4242
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
4245
class TestAuthenticationConfigFile(tests.TestCase):
1316
4246
"""Test the authentication.conf file matching"""