1312
2083
self.assertIs(None, bzrdir_config.get_default_stack_on())
2086
class TestOldConfigHooks(tests.TestCaseWithTransport):
2089
super(TestOldConfigHooks, self).setUp()
2090
create_configs_with_file_option(self)
2092
def assertGetHook(self, conf, name, value):
2096
config.OldConfigHooks.install_named_hook('get', hook, None)
2098
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2099
self.assertLength(0, calls)
2100
actual_value = conf.get_user_option(name)
2101
self.assertEquals(value, actual_value)
2102
self.assertLength(1, calls)
2103
self.assertEquals((conf, name, value), calls[0])
2105
def test_get_hook_bazaar(self):
2106
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2108
def test_get_hook_locations(self):
2109
self.assertGetHook(self.locations_config, 'file', 'locations')
2111
def test_get_hook_branch(self):
2112
# Since locations masks branch, we define a different option
2113
self.branch_config.set_user_option('file2', 'branch')
2114
self.assertGetHook(self.branch_config, 'file2', 'branch')
2116
def assertSetHook(self, conf, name, value):
2120
config.OldConfigHooks.install_named_hook('set', hook, None)
2122
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2123
self.assertLength(0, calls)
2124
conf.set_user_option(name, value)
2125
self.assertLength(1, calls)
2126
# We can't assert the conf object below as different configs use
2127
# different means to implement set_user_option and we care only about
2129
self.assertEquals((name, value), calls[0][1:])
2131
def test_set_hook_bazaar(self):
2132
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2134
def test_set_hook_locations(self):
2135
self.assertSetHook(self.locations_config, 'foo', 'locations')
2137
def test_set_hook_branch(self):
2138
self.assertSetHook(self.branch_config, 'foo', 'branch')
2140
def assertRemoveHook(self, conf, name, section_name=None):
2144
config.OldConfigHooks.install_named_hook('remove', hook, None)
2146
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2147
self.assertLength(0, calls)
2148
conf.remove_user_option(name, section_name)
2149
self.assertLength(1, calls)
2150
# We can't assert the conf object below as different configs use
2151
# different means to implement remove_user_option and we care only about
2153
self.assertEquals((name,), calls[0][1:])
2155
def test_remove_hook_bazaar(self):
2156
self.assertRemoveHook(self.bazaar_config, 'file')
2158
def test_remove_hook_locations(self):
2159
self.assertRemoveHook(self.locations_config, 'file',
2160
self.locations_config.location)
2162
def test_remove_hook_branch(self):
2163
self.assertRemoveHook(self.branch_config, 'file')
2165
def assertLoadHook(self, name, conf_class, *conf_args):
2169
config.OldConfigHooks.install_named_hook('load', hook, None)
2171
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2172
self.assertLength(0, calls)
2174
conf = conf_class(*conf_args)
2175
# Access an option to trigger a load
2176
conf.get_user_option(name)
2177
self.assertLength(1, calls)
2178
# Since we can't assert about conf, we just use the number of calls ;-/
2180
def test_load_hook_bazaar(self):
2181
self.assertLoadHook('file', config.GlobalConfig)
2183
def test_load_hook_locations(self):
2184
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2186
def test_load_hook_branch(self):
2187
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2189
def assertSaveHook(self, conf):
2193
config.OldConfigHooks.install_named_hook('save', hook, None)
2195
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2196
self.assertLength(0, calls)
2197
# Setting an option triggers a save
2198
conf.set_user_option('foo', 'bar')
2199
self.assertLength(1, calls)
2200
# Since we can't assert about conf, we just use the number of calls ;-/
2202
def test_save_hook_bazaar(self):
2203
self.assertSaveHook(self.bazaar_config)
2205
def test_save_hook_locations(self):
2206
self.assertSaveHook(self.locations_config)
2208
def test_save_hook_branch(self):
2209
self.assertSaveHook(self.branch_config)
2212
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2213
"""Tests config hooks for remote configs.
2215
No tests for the remove hook as this is not implemented there.
2219
super(TestOldConfigHooksForRemote, self).setUp()
2220
self.transport_server = test_server.SmartTCPServer_for_testing
2221
create_configs_with_file_option(self)
2223
def assertGetHook(self, conf, name, value):
2227
config.OldConfigHooks.install_named_hook('get', hook, None)
2229
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2230
self.assertLength(0, calls)
2231
actual_value = conf.get_option(name)
2232
self.assertEquals(value, actual_value)
2233
self.assertLength(1, calls)
2234
self.assertEquals((conf, name, value), calls[0])
2236
def test_get_hook_remote_branch(self):
2237
remote_branch = branch.Branch.open(self.get_url('tree'))
2238
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2240
def test_get_hook_remote_bzrdir(self):
2241
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2242
conf = remote_bzrdir._get_config()
2243
conf.set_option('remotedir', 'file')
2244
self.assertGetHook(conf, 'file', 'remotedir')
2246
def assertSetHook(self, conf, name, value):
2250
config.OldConfigHooks.install_named_hook('set', hook, None)
2252
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2253
self.assertLength(0, calls)
2254
conf.set_option(value, name)
2255
self.assertLength(1, calls)
2256
# We can't assert the conf object below as different configs use
2257
# different means to implement set_user_option and we care only about
2259
self.assertEquals((name, value), calls[0][1:])
2261
def test_set_hook_remote_branch(self):
2262
remote_branch = branch.Branch.open(self.get_url('tree'))
2263
self.addCleanup(remote_branch.lock_write().unlock)
2264
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2266
def test_set_hook_remote_bzrdir(self):
2267
remote_branch = branch.Branch.open(self.get_url('tree'))
2268
self.addCleanup(remote_branch.lock_write().unlock)
2269
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2270
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2272
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2276
config.OldConfigHooks.install_named_hook('load', hook, None)
2278
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2279
self.assertLength(0, calls)
2281
conf = conf_class(*conf_args)
2282
# Access an option to trigger a load
2283
conf.get_option(name)
2284
self.assertLength(expected_nb_calls, calls)
2285
# Since we can't assert about conf, we just use the number of calls ;-/
2287
def test_load_hook_remote_branch(self):
2288
remote_branch = branch.Branch.open(self.get_url('tree'))
2289
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2291
def test_load_hook_remote_bzrdir(self):
2292
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2293
# The config file doesn't exist, set an option to force its creation
2294
conf = remote_bzrdir._get_config()
2295
conf.set_option('remotedir', 'file')
2296
# We get one call for the server and one call for the client, this is
2297
# caused by the differences in implementations betwen
2298
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2299
# SmartServerBranchGetConfigFile (in smart/branch.py)
2300
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2302
def assertSaveHook(self, conf):
2306
config.OldConfigHooks.install_named_hook('save', hook, None)
2308
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2309
self.assertLength(0, calls)
2310
# Setting an option triggers a save
2311
conf.set_option('foo', 'bar')
2312
self.assertLength(1, calls)
2313
# Since we can't assert about conf, we just use the number of calls ;-/
2315
def test_save_hook_remote_branch(self):
2316
remote_branch = branch.Branch.open(self.get_url('tree'))
2317
self.addCleanup(remote_branch.lock_write().unlock)
2318
self.assertSaveHook(remote_branch._get_config())
2320
def test_save_hook_remote_bzrdir(self):
2321
remote_branch = branch.Branch.open(self.get_url('tree'))
2322
self.addCleanup(remote_branch.lock_write().unlock)
2323
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2324
self.assertSaveHook(remote_bzrdir._get_config())
2327
class TestOption(tests.TestCase):
2329
def test_default_value(self):
2330
opt = config.Option('foo', default='bar')
2331
self.assertEquals('bar', opt.get_default())
2333
def test_callable_default_value(self):
2334
def bar_as_unicode():
2336
opt = config.Option('foo', default=bar_as_unicode)
2337
self.assertEquals('bar', opt.get_default())
2339
def test_default_value_from_env(self):
2340
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2341
self.overrideEnv('FOO', 'quux')
2342
# Env variable provides a default taking over the option one
2343
self.assertEquals('quux', opt.get_default())
2345
def test_first_default_value_from_env_wins(self):
2346
opt = config.Option('foo', default='bar',
2347
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2348
self.overrideEnv('FOO', 'foo')
2349
self.overrideEnv('BAZ', 'baz')
2350
# The first env var set wins
2351
self.assertEquals('foo', opt.get_default())
2353
def test_not_supported_list_default_value(self):
2354
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2356
def test_not_supported_object_default_value(self):
2357
self.assertRaises(AssertionError, config.Option, 'foo',
2360
def test_not_supported_callable_default_value_not_unicode(self):
2361
def bar_not_unicode():
2363
opt = config.Option('foo', default=bar_not_unicode)
2364
self.assertRaises(AssertionError, opt.get_default)
2367
class TestOptionConverterMixin(object):
2369
def assertConverted(self, expected, opt, value):
2370
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2372
def assertWarns(self, opt, value):
2375
warnings.append(args[0] % args[1:])
2376
self.overrideAttr(trace, 'warning', warning)
2377
self.assertEquals(None, opt.convert_from_unicode(None, value))
2378
self.assertLength(1, warnings)
2380
'Value "%s" is not valid for "%s"' % (value, opt.name),
2383
def assertErrors(self, opt, value):
2384
self.assertRaises(errors.ConfigOptionValueError,
2385
opt.convert_from_unicode, None, value)
2387
def assertConvertInvalid(self, opt, invalid_value):
2389
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2390
opt.invalid = 'warning'
2391
self.assertWarns(opt, invalid_value)
2392
opt.invalid = 'error'
2393
self.assertErrors(opt, invalid_value)
2396
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2398
def get_option(self):
2399
return config.Option('foo', help='A boolean.',
2400
from_unicode=config.bool_from_store)
2402
def test_convert_invalid(self):
2403
opt = self.get_option()
2404
# A string that is not recognized as a boolean
2405
self.assertConvertInvalid(opt, u'invalid-boolean')
2406
# A list of strings is never recognized as a boolean
2407
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2409
def test_convert_valid(self):
2410
opt = self.get_option()
2411
self.assertConverted(True, opt, u'True')
2412
self.assertConverted(True, opt, u'1')
2413
self.assertConverted(False, opt, u'False')
2416
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2418
def get_option(self):
2419
return config.Option('foo', help='An integer.',
2420
from_unicode=config.int_from_store)
2422
def test_convert_invalid(self):
2423
opt = self.get_option()
2424
# A string that is not recognized as an integer
2425
self.assertConvertInvalid(opt, u'forty-two')
2426
# A list of strings is never recognized as an integer
2427
self.assertConvertInvalid(opt, [u'a', u'list'])
2429
def test_convert_valid(self):
2430
opt = self.get_option()
2431
self.assertConverted(16, opt, u'16')
2434
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2436
def get_option(self):
2437
return config.Option('foo', help='An integer in SI units.',
2438
from_unicode=config.int_SI_from_store)
2440
def test_convert_invalid(self):
2441
opt = self.get_option()
2442
self.assertConvertInvalid(opt, u'not-a-unit')
2443
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2444
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2445
self.assertConvertInvalid(opt, u'1GG')
2446
self.assertConvertInvalid(opt, u'1Mbb')
2447
self.assertConvertInvalid(opt, u'1MM')
2449
def test_convert_valid(self):
2450
opt = self.get_option()
2451
self.assertConverted(int(5e3), opt, u'5kb')
2452
self.assertConverted(int(5e6), opt, u'5M')
2453
self.assertConverted(int(5e6), opt, u'5MB')
2454
self.assertConverted(int(5e9), opt, u'5g')
2455
self.assertConverted(int(5e9), opt, u'5gB')
2456
self.assertConverted(100, opt, u'100')
2459
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2461
def get_option(self):
2462
return config.ListOption('foo', help='A list.')
2464
def test_convert_invalid(self):
2465
opt = self.get_option()
2466
# We don't even try to convert a list into a list, we only expect
2468
self.assertConvertInvalid(opt, [1])
2469
# No string is invalid as all forms can be converted to a list
2471
def test_convert_valid(self):
2472
opt = self.get_option()
2473
# An empty string is an empty list
2474
self.assertConverted([], opt, '') # Using a bare str() just in case
2475
self.assertConverted([], opt, u'')
2477
self.assertConverted([u'True'], opt, u'True')
2479
self.assertConverted([u'42'], opt, u'42')
2481
self.assertConverted([u'bar'], opt, u'bar')
2484
class TestRegistryOption(tests.TestCase, TestOptionConverterMixin):
2486
def get_option(self, registry):
2487
return config.RegistryOption('foo', registry,
2488
help='A registry option.')
2490
def test_convert_invalid(self):
2491
registry = _mod_registry.Registry()
2492
opt = self.get_option(registry)
2493
self.assertConvertInvalid(opt, [1])
2494
self.assertConvertInvalid(opt, u"notregistered")
2496
def test_convert_valid(self):
2497
registry = _mod_registry.Registry()
2498
registry.register("someval", 1234)
2499
opt = self.get_option(registry)
2500
# Using a bare str() just in case
2501
self.assertConverted(1234, opt, "someval")
2502
self.assertConverted(1234, opt, u'someval')
2503
self.assertConverted(None, opt, None)
2505
def test_help(self):
2506
registry = _mod_registry.Registry()
2507
registry.register("someval", 1234, help="some option")
2508
registry.register("dunno", 1234, help="some other option")
2509
opt = self.get_option(registry)
2511
'A registry option.\n'
2513
'The following values are supported:\n'
2514
' dunno - some other option\n'
2515
' someval - some option\n',
2518
def test_get_help_text(self):
2519
registry = _mod_registry.Registry()
2520
registry.register("someval", 1234, help="some option")
2521
registry.register("dunno", 1234, help="some other option")
2522
opt = self.get_option(registry)
2524
'A registry option.\n'
2526
'The following values are supported:\n'
2527
' dunno - some other option\n'
2528
' someval - some option\n',
2529
opt.get_help_text())
2532
class TestOptionRegistry(tests.TestCase):
2535
super(TestOptionRegistry, self).setUp()
2536
# Always start with an empty registry
2537
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2538
self.registry = config.option_registry
2540
def test_register(self):
2541
opt = config.Option('foo')
2542
self.registry.register(opt)
2543
self.assertIs(opt, self.registry.get('foo'))
2545
def test_registered_help(self):
2546
opt = config.Option('foo', help='A simple option')
2547
self.registry.register(opt)
2548
self.assertEquals('A simple option', self.registry.get_help('foo'))
2550
lazy_option = config.Option('lazy_foo', help='Lazy help')
2552
def test_register_lazy(self):
2553
self.registry.register_lazy('lazy_foo', self.__module__,
2554
'TestOptionRegistry.lazy_option')
2555
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2557
def test_registered_lazy_help(self):
2558
self.registry.register_lazy('lazy_foo', self.__module__,
2559
'TestOptionRegistry.lazy_option')
2560
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2563
class TestRegisteredOptions(tests.TestCase):
2564
"""All registered options should verify some constraints."""
2566
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2567
in config.option_registry.iteritems()]
2570
super(TestRegisteredOptions, self).setUp()
2571
self.registry = config.option_registry
2573
def test_proper_name(self):
2574
# An option should be registered under its own name, this can't be
2575
# checked at registration time for the lazy ones.
2576
self.assertEquals(self.option_name, self.option.name)
2578
def test_help_is_set(self):
2579
option_help = self.registry.get_help(self.option_name)
2580
self.assertNotEquals(None, option_help)
2581
# Come on, think about the user, he really wants to know what the
2583
self.assertIsNot(None, option_help)
2584
self.assertNotEquals('', option_help)
2587
class TestSection(tests.TestCase):
2589
# FIXME: Parametrize so that all sections produced by Stores run these
2590
# tests -- vila 2011-04-01
2592
def test_get_a_value(self):
2593
a_dict = dict(foo='bar')
2594
section = config.Section('myID', a_dict)
2595
self.assertEquals('bar', section.get('foo'))
2597
def test_get_unknown_option(self):
2599
section = config.Section(None, a_dict)
2600
self.assertEquals('out of thin air',
2601
section.get('foo', 'out of thin air'))
2603
def test_options_is_shared(self):
2605
section = config.Section(None, a_dict)
2606
self.assertIs(a_dict, section.options)
2609
class TestMutableSection(tests.TestCase):
2611
scenarios = [('mutable',
2613
lambda opts: config.MutableSection('myID', opts)},),
2617
a_dict = dict(foo='bar')
2618
section = self.get_section(a_dict)
2619
section.set('foo', 'new_value')
2620
self.assertEquals('new_value', section.get('foo'))
2621
# The change appears in the shared section
2622
self.assertEquals('new_value', a_dict.get('foo'))
2623
# We keep track of the change
2624
self.assertTrue('foo' in section.orig)
2625
self.assertEquals('bar', section.orig.get('foo'))
2627
def test_set_preserve_original_once(self):
2628
a_dict = dict(foo='bar')
2629
section = self.get_section(a_dict)
2630
section.set('foo', 'first_value')
2631
section.set('foo', 'second_value')
2632
# We keep track of the original value
2633
self.assertTrue('foo' in section.orig)
2634
self.assertEquals('bar', section.orig.get('foo'))
2636
def test_remove(self):
2637
a_dict = dict(foo='bar')
2638
section = self.get_section(a_dict)
2639
section.remove('foo')
2640
# We get None for unknown options via the default value
2641
self.assertEquals(None, section.get('foo'))
2642
# Or we just get the default value
2643
self.assertEquals('unknown', section.get('foo', 'unknown'))
2644
self.assertFalse('foo' in section.options)
2645
# We keep track of the deletion
2646
self.assertTrue('foo' in section.orig)
2647
self.assertEquals('bar', section.orig.get('foo'))
2649
def test_remove_new_option(self):
2651
section = self.get_section(a_dict)
2652
section.set('foo', 'bar')
2653
section.remove('foo')
2654
self.assertFalse('foo' in section.options)
2655
# The option didn't exist initially so it we need to keep track of it
2656
# with a special value
2657
self.assertTrue('foo' in section.orig)
2658
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2661
class TestCommandLineStore(tests.TestCase):
2664
super(TestCommandLineStore, self).setUp()
2665
self.store = config.CommandLineStore()
2666
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2668
def get_section(self):
2669
"""Get the unique section for the command line overrides."""
2670
sections = list(self.store.get_sections())
2671
self.assertLength(1, sections)
2672
store, section = sections[0]
2673
self.assertEquals(self.store, store)
2676
def test_no_override(self):
2677
self.store._from_cmdline([])
2678
section = self.get_section()
2679
self.assertLength(0, list(section.iter_option_names()))
2681
def test_simple_override(self):
2682
self.store._from_cmdline(['a=b'])
2683
section = self.get_section()
2684
self.assertEqual('b', section.get('a'))
2686
def test_list_override(self):
2687
opt = config.ListOption('l')
2688
config.option_registry.register(opt)
2689
self.store._from_cmdline(['l=1,2,3'])
2690
val = self.get_section().get('l')
2691
self.assertEqual('1,2,3', val)
2692
# Reminder: lists should be registered as such explicitely, otherwise
2693
# the conversion needs to be done afterwards.
2694
self.assertEqual(['1', '2', '3'],
2695
opt.convert_from_unicode(self.store, val))
2697
def test_multiple_overrides(self):
2698
self.store._from_cmdline(['a=b', 'x=y'])
2699
section = self.get_section()
2700
self.assertEquals('b', section.get('a'))
2701
self.assertEquals('y', section.get('x'))
2703
def test_wrong_syntax(self):
2704
self.assertRaises(errors.BzrCommandError,
2705
self.store._from_cmdline, ['a=b', 'c'])
2707
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2709
scenarios = [(key, {'get_store': builder}) for key, builder
2710
in config.test_store_builder_registry.iteritems()] + [
2711
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2714
store = self.get_store(self)
2715
if type(store) == config.TransportIniFileStore:
2716
raise tests.TestNotApplicable(
2717
"%s is not a concrete Store implementation"
2718
" so it doesn't need an id" % (store.__class__.__name__,))
2719
self.assertIsNot(None, store.id)
2722
class TestStore(tests.TestCaseWithTransport):
2724
def assertSectionContent(self, expected, (store, section)):
2725
"""Assert that some options have the proper values in a section."""
2726
expected_name, expected_options = expected
2727
self.assertEquals(expected_name, section.id)
2730
dict([(k, section.get(k)) for k in expected_options.keys()]))
2733
class TestReadonlyStore(TestStore):
2735
scenarios = [(key, {'get_store': builder}) for key, builder
2736
in config.test_store_builder_registry.iteritems()]
2738
def test_building_delays_load(self):
2739
store = self.get_store(self)
2740
self.assertEquals(False, store.is_loaded())
2741
store._load_from_string('')
2742
self.assertEquals(True, store.is_loaded())
2744
def test_get_no_sections_for_empty(self):
2745
store = self.get_store(self)
2746
store._load_from_string('')
2747
self.assertEquals([], list(store.get_sections()))
2749
def test_get_default_section(self):
2750
store = self.get_store(self)
2751
store._load_from_string('foo=bar')
2752
sections = list(store.get_sections())
2753
self.assertLength(1, sections)
2754
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2756
def test_get_named_section(self):
2757
store = self.get_store(self)
2758
store._load_from_string('[baz]\nfoo=bar')
2759
sections = list(store.get_sections())
2760
self.assertLength(1, sections)
2761
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2763
def test_load_from_string_fails_for_non_empty_store(self):
2764
store = self.get_store(self)
2765
store._load_from_string('foo=bar')
2766
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2769
class TestStoreQuoting(TestStore):
2771
scenarios = [(key, {'get_store': builder}) for key, builder
2772
in config.test_store_builder_registry.iteritems()]
2775
super(TestStoreQuoting, self).setUp()
2776
self.store = self.get_store(self)
2777
# We need a loaded store but any content will do
2778
self.store._load_from_string('')
2780
def assertIdempotent(self, s):
2781
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2783
What matters here is that option values, as they appear in a store, can
2784
be safely round-tripped out of the store and back.
2786
:param s: A string, quoted if required.
2788
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2789
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2791
def test_empty_string(self):
2792
if isinstance(self.store, config.IniFileStore):
2793
# configobj._quote doesn't handle empty values
2794
self.assertRaises(AssertionError,
2795
self.assertIdempotent, '')
2797
self.assertIdempotent('')
2798
# But quoted empty strings are ok
2799
self.assertIdempotent('""')
2801
def test_embedded_spaces(self):
2802
self.assertIdempotent('" a b c "')
2804
def test_embedded_commas(self):
2805
self.assertIdempotent('" a , b c "')
2807
def test_simple_comma(self):
2808
if isinstance(self.store, config.IniFileStore):
2809
# configobj requires that lists are special-cased
2810
self.assertRaises(AssertionError,
2811
self.assertIdempotent, ',')
2813
self.assertIdempotent(',')
2814
# When a single comma is required, quoting is also required
2815
self.assertIdempotent('","')
2817
def test_list(self):
2818
if isinstance(self.store, config.IniFileStore):
2819
# configobj requires that lists are special-cased
2820
self.assertRaises(AssertionError,
2821
self.assertIdempotent, 'a,b')
2823
self.assertIdempotent('a,b')
2826
class TestDictFromStore(tests.TestCase):
2828
def test_unquote_not_string(self):
2829
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2830
value = conf.get('a_section')
2831
# Urgh, despite 'conf' asking for the no-name section, we get the
2832
# content of another section as a dict o_O
2833
self.assertEquals({'a': '1'}, value)
2834
unquoted = conf.store.unquote(value)
2835
# Which cannot be unquoted but shouldn't crash either (the use cases
2836
# are getting the value or displaying it. In the later case, '%s' will
2838
self.assertEquals({'a': '1'}, unquoted)
2839
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2842
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2843
"""Simulate loading a config store with content of various encodings.
2845
All files produced by bzr are in utf8 content.
2847
Users may modify them manually and end up with a file that can't be
2848
loaded. We need to issue proper error messages in this case.
2851
invalid_utf8_char = '\xff'
2853
def test_load_utf8(self):
2854
"""Ensure we can load an utf8-encoded file."""
2855
t = self.get_transport()
2856
# From http://pad.lv/799212
2857
unicode_user = u'b\N{Euro Sign}ar'
2858
unicode_content = u'user=%s' % (unicode_user,)
2859
utf8_content = unicode_content.encode('utf8')
2860
# Store the raw content in the config file
2861
t.put_bytes('foo.conf', utf8_content)
2862
store = config.TransportIniFileStore(t, 'foo.conf')
2864
stack = config.Stack([store.get_sections], store)
2865
self.assertEquals(unicode_user, stack.get('user'))
2867
def test_load_non_ascii(self):
2868
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2869
t = self.get_transport()
2870
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2871
store = config.TransportIniFileStore(t, 'foo.conf')
2872
self.assertRaises(errors.ConfigContentError, store.load)
2874
def test_load_erroneous_content(self):
2875
"""Ensure we display a proper error on content that can't be parsed."""
2876
t = self.get_transport()
2877
t.put_bytes('foo.conf', '[open_section\n')
2878
store = config.TransportIniFileStore(t, 'foo.conf')
2879
self.assertRaises(errors.ParseConfigError, store.load)
2881
def test_load_permission_denied(self):
2882
"""Ensure we get warned when trying to load an inaccessible file."""
2885
warnings.append(args[0] % args[1:])
2886
self.overrideAttr(trace, 'warning', warning)
2888
t = self.get_transport()
2890
def get_bytes(relpath):
2891
raise errors.PermissionDenied(relpath, "")
2892
t.get_bytes = get_bytes
2893
store = config.TransportIniFileStore(t, 'foo.conf')
2894
self.assertRaises(errors.PermissionDenied, store.load)
2897
[u'Permission denied while trying to load configuration store %s.'
2898
% store.external_url()])
2901
class TestIniConfigContent(tests.TestCaseWithTransport):
2902
"""Simulate loading a IniBasedConfig with content of various encodings.
2904
All files produced by bzr are in utf8 content.
2906
Users may modify them manually and end up with a file that can't be
2907
loaded. We need to issue proper error messages in this case.
2910
invalid_utf8_char = '\xff'
2912
def test_load_utf8(self):
2913
"""Ensure we can load an utf8-encoded file."""
2914
# From http://pad.lv/799212
2915
unicode_user = u'b\N{Euro Sign}ar'
2916
unicode_content = u'user=%s' % (unicode_user,)
2917
utf8_content = unicode_content.encode('utf8')
2918
# Store the raw content in the config file
2919
with open('foo.conf', 'wb') as f:
2920
f.write(utf8_content)
2921
conf = config.IniBasedConfig(file_name='foo.conf')
2922
self.assertEquals(unicode_user, conf.get_user_option('user'))
2924
def test_load_badly_encoded_content(self):
2925
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2926
with open('foo.conf', 'wb') as f:
2927
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2928
conf = config.IniBasedConfig(file_name='foo.conf')
2929
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2931
def test_load_erroneous_content(self):
2932
"""Ensure we display a proper error on content that can't be parsed."""
2933
with open('foo.conf', 'wb') as f:
2934
f.write('[open_section\n')
2935
conf = config.IniBasedConfig(file_name='foo.conf')
2936
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2939
class TestMutableStore(TestStore):
2941
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2942
in config.test_store_builder_registry.iteritems()]
2945
super(TestMutableStore, self).setUp()
2946
self.transport = self.get_transport()
2948
def has_store(self, store):
2949
store_basename = urlutils.relative_url(self.transport.external_url(),
2950
store.external_url())
2951
return self.transport.has(store_basename)
2953
def test_save_empty_creates_no_file(self):
2954
# FIXME: There should be a better way than relying on the test
2955
# parametrization to identify branch.conf -- vila 2011-0526
2956
if self.store_id in ('branch', 'remote_branch'):
2957
raise tests.TestNotApplicable(
2958
'branch.conf is *always* created when a branch is initialized')
2959
store = self.get_store(self)
2961
self.assertEquals(False, self.has_store(store))
2963
def test_save_emptied_succeeds(self):
2964
store = self.get_store(self)
2965
store._load_from_string('foo=bar\n')
2966
section = store.get_mutable_section(None)
2967
section.remove('foo')
2969
self.assertEquals(True, self.has_store(store))
2970
modified_store = self.get_store(self)
2971
sections = list(modified_store.get_sections())
2972
self.assertLength(0, sections)
2974
def test_save_with_content_succeeds(self):
2975
# FIXME: There should be a better way than relying on the test
2976
# parametrization to identify branch.conf -- vila 2011-0526
2977
if self.store_id in ('branch', 'remote_branch'):
2978
raise tests.TestNotApplicable(
2979
'branch.conf is *always* created when a branch is initialized')
2980
store = self.get_store(self)
2981
store._load_from_string('foo=bar\n')
2982
self.assertEquals(False, self.has_store(store))
2984
self.assertEquals(True, self.has_store(store))
2985
modified_store = self.get_store(self)
2986
sections = list(modified_store.get_sections())
2987
self.assertLength(1, sections)
2988
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2990
def test_set_option_in_empty_store(self):
2991
store = self.get_store(self)
2992
section = store.get_mutable_section(None)
2993
section.set('foo', 'bar')
2995
modified_store = self.get_store(self)
2996
sections = list(modified_store.get_sections())
2997
self.assertLength(1, sections)
2998
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
3000
def test_set_option_in_default_section(self):
3001
store = self.get_store(self)
3002
store._load_from_string('')
3003
section = store.get_mutable_section(None)
3004
section.set('foo', 'bar')
3006
modified_store = self.get_store(self)
3007
sections = list(modified_store.get_sections())
3008
self.assertLength(1, sections)
3009
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
3011
def test_set_option_in_named_section(self):
3012
store = self.get_store(self)
3013
store._load_from_string('')
3014
section = store.get_mutable_section('baz')
3015
section.set('foo', 'bar')
3017
modified_store = self.get_store(self)
3018
sections = list(modified_store.get_sections())
3019
self.assertLength(1, sections)
3020
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
3022
def test_load_hook(self):
3023
# We first needs to ensure that the store exists
3024
store = self.get_store(self)
3025
section = store.get_mutable_section('baz')
3026
section.set('foo', 'bar')
3028
# Now we can try to load it
3029
store = self.get_store(self)
3033
config.ConfigHooks.install_named_hook('load', hook, None)
3034
self.assertLength(0, calls)
3036
self.assertLength(1, calls)
3037
self.assertEquals((store,), calls[0])
3039
def test_save_hook(self):
3043
config.ConfigHooks.install_named_hook('save', hook, None)
3044
self.assertLength(0, calls)
3045
store = self.get_store(self)
3046
section = store.get_mutable_section('baz')
3047
section.set('foo', 'bar')
3049
self.assertLength(1, calls)
3050
self.assertEquals((store,), calls[0])
3052
def test_set_mark_dirty(self):
3053
stack = config.MemoryStack('')
3054
self.assertLength(0, stack.store.dirty_sections)
3055
stack.set('foo', 'baz')
3056
self.assertLength(1, stack.store.dirty_sections)
3057
self.assertTrue(stack.store._need_saving())
3059
def test_remove_mark_dirty(self):
3060
stack = config.MemoryStack('foo=bar')
3061
self.assertLength(0, stack.store.dirty_sections)
3063
self.assertLength(1, stack.store.dirty_sections)
3064
self.assertTrue(stack.store._need_saving())
3067
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3068
"""Tests that config changes are kept in memory and saved on-demand."""
3071
super(TestStoreSaveChanges, self).setUp()
3072
self.transport = self.get_transport()
3073
# Most of the tests involve two stores pointing to the same persistent
3074
# storage to observe the effects of concurrent changes
3075
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3076
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3079
self.warnings.append(args[0] % args[1:])
3080
self.overrideAttr(trace, 'warning', warning)
3082
def has_store(self, store):
3083
store_basename = urlutils.relative_url(self.transport.external_url(),
3084
store.external_url())
3085
return self.transport.has(store_basename)
3087
def get_stack(self, store):
3088
# Any stack will do as long as it uses the right store, just a single
3089
# no-name section is enough
3090
return config.Stack([store.get_sections], store)
3092
def test_no_changes_no_save(self):
3093
s = self.get_stack(self.st1)
3094
s.store.save_changes()
3095
self.assertEquals(False, self.has_store(self.st1))
3097
def test_unrelated_concurrent_update(self):
3098
s1 = self.get_stack(self.st1)
3099
s2 = self.get_stack(self.st2)
3100
s1.set('foo', 'bar')
3101
s2.set('baz', 'quux')
3103
# Changes don't propagate magically
3104
self.assertEquals(None, s1.get('baz'))
3105
s2.store.save_changes()
3106
self.assertEquals('quux', s2.get('baz'))
3107
# Changes are acquired when saving
3108
self.assertEquals('bar', s2.get('foo'))
3109
# Since there is no overlap, no warnings are emitted
3110
self.assertLength(0, self.warnings)
3112
def test_concurrent_update_modified(self):
3113
s1 = self.get_stack(self.st1)
3114
s2 = self.get_stack(self.st2)
3115
s1.set('foo', 'bar')
3116
s2.set('foo', 'baz')
3119
s2.store.save_changes()
3120
self.assertEquals('baz', s2.get('foo'))
3121
# But the user get a warning
3122
self.assertLength(1, self.warnings)
3123
warning = self.warnings[0]
3124
self.assertStartsWith(warning, 'Option foo in section None')
3125
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3126
' The baz value will be saved.')
3128
def test_concurrent_deletion(self):
3129
self.st1._load_from_string('foo=bar')
3131
s1 = self.get_stack(self.st1)
3132
s2 = self.get_stack(self.st2)
3135
s1.store.save_changes()
3137
self.assertLength(0, self.warnings)
3138
s2.store.save_changes()
3140
self.assertLength(1, self.warnings)
3141
warning = self.warnings[0]
3142
self.assertStartsWith(warning, 'Option foo in section None')
3143
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3144
' The <DELETED> value will be saved.')
3147
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3149
def get_store(self):
3150
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3152
def test_get_quoted_string(self):
3153
store = self.get_store()
3154
store._load_from_string('foo= " abc "')
3155
stack = config.Stack([store.get_sections])
3156
self.assertEquals(' abc ', stack.get('foo'))
3158
def test_set_quoted_string(self):
3159
store = self.get_store()
3160
stack = config.Stack([store.get_sections], store)
3161
stack.set('foo', ' a b c ')
3163
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3166
class TestTransportIniFileStore(TestStore):
3168
def test_loading_unknown_file_fails(self):
3169
store = config.TransportIniFileStore(self.get_transport(),
3171
self.assertRaises(errors.NoSuchFile, store.load)
3173
def test_invalid_content(self):
3174
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3175
self.assertEquals(False, store.is_loaded())
3176
exc = self.assertRaises(
3177
errors.ParseConfigError, store._load_from_string,
3178
'this is invalid !')
3179
self.assertEndsWith(exc.filename, 'foo.conf')
3180
# And the load failed
3181
self.assertEquals(False, store.is_loaded())
3183
def test_get_embedded_sections(self):
3184
# A more complicated example (which also shows that section names and
3185
# option names share the same name space...)
3186
# FIXME: This should be fixed by forbidding dicts as values ?
3187
# -- vila 2011-04-05
3188
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3189
store._load_from_string('''
3193
foo_in_DEFAULT=foo_DEFAULT
3201
sections = list(store.get_sections())
3202
self.assertLength(4, sections)
3203
# The default section has no name.
3204
# List values are provided as strings and need to be explicitly
3205
# converted by specifying from_unicode=list_from_store at option
3207
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3209
self.assertSectionContent(
3210
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3211
self.assertSectionContent(
3212
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3213
# sub sections are provided as embedded dicts.
3214
self.assertSectionContent(
3215
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3219
class TestLockableIniFileStore(TestStore):
3221
def test_create_store_in_created_dir(self):
3222
self.assertPathDoesNotExist('dir')
3223
t = self.get_transport('dir/subdir')
3224
store = config.LockableIniFileStore(t, 'foo.conf')
3225
store.get_mutable_section(None).set('foo', 'bar')
3227
self.assertPathExists('dir/subdir')
3230
class TestConcurrentStoreUpdates(TestStore):
3231
"""Test that Stores properly handle conccurent updates.
3233
New Store implementation may fail some of these tests but until such
3234
implementations exist it's hard to properly filter them from the scenarios
3235
applied here. If you encounter such a case, contact the bzr devs.
3238
scenarios = [(key, {'get_stack': builder}) for key, builder
3239
in config.test_stack_builder_registry.iteritems()]
3242
super(TestConcurrentStoreUpdates, self).setUp()
3243
self.stack = self.get_stack(self)
3244
if not isinstance(self.stack, config._CompatibleStack):
3245
raise tests.TestNotApplicable(
3246
'%s is not meant to be compatible with the old config design'
3248
self.stack.set('one', '1')
3249
self.stack.set('two', '2')
3251
self.stack.store.save()
3253
def test_simple_read_access(self):
3254
self.assertEquals('1', self.stack.get('one'))
3256
def test_simple_write_access(self):
3257
self.stack.set('one', 'one')
3258
self.assertEquals('one', self.stack.get('one'))
3260
def test_listen_to_the_last_speaker(self):
3262
c2 = self.get_stack(self)
3263
c1.set('one', 'ONE')
3264
c2.set('two', 'TWO')
3265
self.assertEquals('ONE', c1.get('one'))
3266
self.assertEquals('TWO', c2.get('two'))
3267
# The second update respect the first one
3268
self.assertEquals('ONE', c2.get('one'))
3270
def test_last_speaker_wins(self):
3271
# If the same config is not shared, the same variable modified twice
3272
# can only see a single result.
3274
c2 = self.get_stack(self)
3277
self.assertEquals('c2', c2.get('one'))
3278
# The first modification is still available until another refresh
3280
self.assertEquals('c1', c1.get('one'))
3281
c1.set('two', 'done')
3282
self.assertEquals('c2', c1.get('one'))
3284
def test_writes_are_serialized(self):
3286
c2 = self.get_stack(self)
3288
# We spawn a thread that will pause *during* the config saving.
3289
before_writing = threading.Event()
3290
after_writing = threading.Event()
3291
writing_done = threading.Event()
3292
c1_save_without_locking_orig = c1.store.save_without_locking
3293
def c1_save_without_locking():
3294
before_writing.set()
3295
c1_save_without_locking_orig()
3296
# The lock is held. We wait for the main thread to decide when to
3298
after_writing.wait()
3299
c1.store.save_without_locking = c1_save_without_locking
3303
t1 = threading.Thread(target=c1_set)
3304
# Collect the thread after the test
3305
self.addCleanup(t1.join)
3306
# Be ready to unblock the thread if the test goes wrong
3307
self.addCleanup(after_writing.set)
3309
before_writing.wait()
3310
self.assertRaises(errors.LockContention,
3311
c2.set, 'one', 'c2')
3312
self.assertEquals('c1', c1.get('one'))
3313
# Let the lock be released
3317
self.assertEquals('c2', c2.get('one'))
3319
def test_read_while_writing(self):
3321
# We spawn a thread that will pause *during* the write
3322
ready_to_write = threading.Event()
3323
do_writing = threading.Event()
3324
writing_done = threading.Event()
3325
# We override the _save implementation so we know the store is locked
3326
c1_save_without_locking_orig = c1.store.save_without_locking
3327
def c1_save_without_locking():
3328
ready_to_write.set()
3329
# The lock is held. We wait for the main thread to decide when to
3332
c1_save_without_locking_orig()
3334
c1.store.save_without_locking = c1_save_without_locking
3337
t1 = threading.Thread(target=c1_set)
3338
# Collect the thread after the test
3339
self.addCleanup(t1.join)
3340
# Be ready to unblock the thread if the test goes wrong
3341
self.addCleanup(do_writing.set)
3343
# Ensure the thread is ready to write
3344
ready_to_write.wait()
3345
self.assertEquals('c1', c1.get('one'))
3346
# If we read during the write, we get the old value
3347
c2 = self.get_stack(self)
3348
self.assertEquals('1', c2.get('one'))
3349
# Let the writing occur and ensure it occurred
3352
# Now we get the updated value
3353
c3 = self.get_stack(self)
3354
self.assertEquals('c1', c3.get('one'))
3356
# FIXME: It may be worth looking into removing the lock dir when it's not
3357
# needed anymore and look at possible fallouts for concurrent lockers. This
3358
# will matter if/when we use config files outside of bazaar directories
3359
# (.bazaar or .bzr) -- vila 20110-04-111
3362
class TestSectionMatcher(TestStore):
3364
scenarios = [('location', {'matcher': config.LocationMatcher}),
3365
('id', {'matcher': config.NameMatcher}),]
3368
super(TestSectionMatcher, self).setUp()
3369
# Any simple store is good enough
3370
self.get_store = config.test_store_builder_registry.get('configobj')
3372
def test_no_matches_for_empty_stores(self):
3373
store = self.get_store(self)
3374
store._load_from_string('')
3375
matcher = self.matcher(store, '/bar')
3376
self.assertEquals([], list(matcher.get_sections()))
3378
def test_build_doesnt_load_store(self):
3379
store = self.get_store(self)
3380
matcher = self.matcher(store, '/bar')
3381
self.assertFalse(store.is_loaded())
3384
class TestLocationSection(tests.TestCase):
3386
def get_section(self, options, extra_path):
3387
section = config.Section('foo', options)
3388
return config.LocationSection(section, extra_path)
3390
def test_simple_option(self):
3391
section = self.get_section({'foo': 'bar'}, '')
3392
self.assertEquals('bar', section.get('foo'))
3394
def test_option_with_extra_path(self):
3395
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3397
self.assertEquals('bar/baz', section.get('foo'))
3399
def test_invalid_policy(self):
3400
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3402
# invalid policies are ignored
3403
self.assertEquals('bar', section.get('foo'))
3406
class TestLocationMatcher(TestStore):
3409
super(TestLocationMatcher, self).setUp()
3410
# Any simple store is good enough
3411
self.get_store = config.test_store_builder_registry.get('configobj')
3413
def test_unrelated_section_excluded(self):
3414
store = self.get_store(self)
3415
store._load_from_string('''
3423
section=/foo/bar/baz
3427
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3429
[section.id for _, section in store.get_sections()])
3430
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3431
sections = [section for _, section in matcher.get_sections()]
3432
self.assertEquals(['/foo/bar', '/foo'],
3433
[section.id for section in sections])
3434
self.assertEquals(['quux', 'bar/quux'],
3435
[section.extra_path for section in sections])
3437
def test_more_specific_sections_first(self):
3438
store = self.get_store(self)
3439
store._load_from_string('''
3445
self.assertEquals(['/foo', '/foo/bar'],
3446
[section.id for _, section in store.get_sections()])
3447
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3448
sections = [section for _, section in matcher.get_sections()]
3449
self.assertEquals(['/foo/bar', '/foo'],
3450
[section.id for section in sections])
3451
self.assertEquals(['baz', 'bar/baz'],
3452
[section.extra_path for section in sections])
3454
def test_appendpath_in_no_name_section(self):
3455
# It's a bit weird to allow appendpath in a no-name section, but
3456
# someone may found a use for it
3457
store = self.get_store(self)
3458
store._load_from_string('''
3460
foo:policy = appendpath
3462
matcher = config.LocationMatcher(store, 'dir/subdir')
3463
sections = list(matcher.get_sections())
3464
self.assertLength(1, sections)
3465
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3467
def test_file_urls_are_normalized(self):
3468
store = self.get_store(self)
3469
if sys.platform == 'win32':
3470
expected_url = 'file:///C:/dir/subdir'
3471
expected_location = 'C:/dir/subdir'
3473
expected_url = 'file:///dir/subdir'
3474
expected_location = '/dir/subdir'
3475
matcher = config.LocationMatcher(store, expected_url)
3476
self.assertEquals(expected_location, matcher.location)
3479
class TestStartingPathMatcher(TestStore):
3482
super(TestStartingPathMatcher, self).setUp()
3483
# Any simple store is good enough
3484
self.store = config.IniFileStore()
3486
def assertSectionIDs(self, expected, location, content):
3487
self.store._load_from_string(content)
3488
matcher = config.StartingPathMatcher(self.store, location)
3489
sections = list(matcher.get_sections())
3490
self.assertLength(len(expected), sections)
3491
self.assertEqual(expected, [section.id for _, section in sections])
3494
def test_empty(self):
3495
self.assertSectionIDs([], self.get_url(), '')
3497
def test_url_vs_local_paths(self):
3498
# The matcher location is an url and the section names are local paths
3499
sections = self.assertSectionIDs(['/foo/bar', '/foo'],
3500
'file:///foo/bar/baz', '''\
3505
def test_local_path_vs_url(self):
3506
# The matcher location is a local path and the section names are urls
3507
sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3508
'/foo/bar/baz', '''\
3514
def test_no_name_section_included_when_present(self):
3515
# Note that other tests will cover the case where the no-name section
3516
# is empty and as such, not included.
3517
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3518
'/foo/bar/baz', '''\
3519
option = defined so the no-name section exists
3523
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3524
[s.locals['relpath'] for _, s in sections])
3526
def test_order_reversed(self):
3527
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3532
def test_unrelated_section_excluded(self):
3533
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3539
def test_glob_included(self):
3540
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3541
'/foo/bar/baz', '''\
3547
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3548
# nothing really is... as far using {relpath} to append it to something
3549
# else, this seems good enough though.
3550
self.assertEquals(['', 'baz', 'bar/baz'],
3551
[s.locals['relpath'] for _, s in sections])
3553
def test_respect_order(self):
3554
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3555
'/foo/bar/baz', '''\
3563
class TestNameMatcher(TestStore):
3566
super(TestNameMatcher, self).setUp()
3567
self.matcher = config.NameMatcher
3568
# Any simple store is good enough
3569
self.get_store = config.test_store_builder_registry.get('configobj')
3571
def get_matching_sections(self, name):
3572
store = self.get_store(self)
3573
store._load_from_string('''
3581
matcher = self.matcher(store, name)
3582
return list(matcher.get_sections())
3584
def test_matching(self):
3585
sections = self.get_matching_sections('foo')
3586
self.assertLength(1, sections)
3587
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3589
def test_not_matching(self):
3590
sections = self.get_matching_sections('baz')
3591
self.assertLength(0, sections)
3594
class TestBaseStackGet(tests.TestCase):
3597
super(TestBaseStackGet, self).setUp()
3598
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3600
def test_get_first_definition(self):
3601
store1 = config.IniFileStore()
3602
store1._load_from_string('foo=bar')
3603
store2 = config.IniFileStore()
3604
store2._load_from_string('foo=baz')
3605
conf = config.Stack([store1.get_sections, store2.get_sections])
3606
self.assertEquals('bar', conf.get('foo'))
3608
def test_get_with_registered_default_value(self):
3609
config.option_registry.register(config.Option('foo', default='bar'))
3610
conf_stack = config.Stack([])
3611
self.assertEquals('bar', conf_stack.get('foo'))
3613
def test_get_without_registered_default_value(self):
3614
config.option_registry.register(config.Option('foo'))
3615
conf_stack = config.Stack([])
3616
self.assertEquals(None, conf_stack.get('foo'))
3618
def test_get_without_default_value_for_not_registered(self):
3619
conf_stack = config.Stack([])
3620
self.assertEquals(None, conf_stack.get('foo'))
3622
def test_get_for_empty_section_callable(self):
3623
conf_stack = config.Stack([lambda : []])
3624
self.assertEquals(None, conf_stack.get('foo'))
3626
def test_get_for_broken_callable(self):
3627
# Trying to use and invalid callable raises an exception on first use
3628
conf_stack = config.Stack([object])
3629
self.assertRaises(TypeError, conf_stack.get, 'foo')
3632
class TestStackWithSimpleStore(tests.TestCase):
3635
super(TestStackWithSimpleStore, self).setUp()
3636
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3637
self.registry = config.option_registry
3639
def get_conf(self, content=None):
3640
return config.MemoryStack(content)
3642
def test_override_value_from_env(self):
3643
self.registry.register(
3644
config.Option('foo', default='bar', override_from_env=['FOO']))
3645
self.overrideEnv('FOO', 'quux')
3646
# Env variable provides a default taking over the option one
3647
conf = self.get_conf('foo=store')
3648
self.assertEquals('quux', conf.get('foo'))
3650
def test_first_override_value_from_env_wins(self):
3651
self.registry.register(
3652
config.Option('foo', default='bar',
3653
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3654
self.overrideEnv('FOO', 'foo')
3655
self.overrideEnv('BAZ', 'baz')
3656
# The first env var set wins
3657
conf = self.get_conf('foo=store')
3658
self.assertEquals('foo', conf.get('foo'))
3661
class TestMemoryStack(tests.TestCase):
3664
conf = config.MemoryStack('foo=bar')
3665
self.assertEquals('bar', conf.get('foo'))
3668
conf = config.MemoryStack('foo=bar')
3669
conf.set('foo', 'baz')
3670
self.assertEquals('baz', conf.get('foo'))
3672
def test_no_content(self):
3673
conf = config.MemoryStack()
3674
# No content means no loading
3675
self.assertFalse(conf.store.is_loaded())
3676
self.assertRaises(NotImplementedError, conf.get, 'foo')
3677
# But a content can still be provided
3678
conf.store._load_from_string('foo=bar')
3679
self.assertEquals('bar', conf.get('foo'))
3682
class TestStackWithTransport(tests.TestCaseWithTransport):
3684
scenarios = [(key, {'get_stack': builder}) for key, builder
3685
in config.test_stack_builder_registry.iteritems()]
3688
class TestConcreteStacks(TestStackWithTransport):
3690
def test_build_stack(self):
3691
# Just a smoke test to help debug builders
3692
stack = self.get_stack(self)
3695
class TestStackGet(TestStackWithTransport):
3698
super(TestStackGet, self).setUp()
3699
self.conf = self.get_stack(self)
3701
def test_get_for_empty_stack(self):
3702
self.assertEquals(None, self.conf.get('foo'))
3704
def test_get_hook(self):
3705
self.conf.set('foo', 'bar')
3709
config.ConfigHooks.install_named_hook('get', hook, None)
3710
self.assertLength(0, calls)
3711
value = self.conf.get('foo')
3712
self.assertEquals('bar', value)
3713
self.assertLength(1, calls)
3714
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3717
class TestStackGetWithConverter(tests.TestCase):
3720
super(TestStackGetWithConverter, self).setUp()
3721
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3722
self.registry = config.option_registry
3724
def get_conf(self, content=None):
3725
return config.MemoryStack(content)
3727
def register_bool_option(self, name, default=None, default_from_env=None):
3728
b = config.Option(name, help='A boolean.',
3729
default=default, default_from_env=default_from_env,
3730
from_unicode=config.bool_from_store)
3731
self.registry.register(b)
3733
def test_get_default_bool_None(self):
3734
self.register_bool_option('foo')
3735
conf = self.get_conf('')
3736
self.assertEquals(None, conf.get('foo'))
3738
def test_get_default_bool_True(self):
3739
self.register_bool_option('foo', u'True')
3740
conf = self.get_conf('')
3741
self.assertEquals(True, conf.get('foo'))
3743
def test_get_default_bool_False(self):
3744
self.register_bool_option('foo', False)
3745
conf = self.get_conf('')
3746
self.assertEquals(False, conf.get('foo'))
3748
def test_get_default_bool_False_as_string(self):
3749
self.register_bool_option('foo', u'False')
3750
conf = self.get_conf('')
3751
self.assertEquals(False, conf.get('foo'))
3753
def test_get_default_bool_from_env_converted(self):
3754
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3755
self.overrideEnv('FOO', 'False')
3756
conf = self.get_conf('')
3757
self.assertEquals(False, conf.get('foo'))
3759
def test_get_default_bool_when_conversion_fails(self):
3760
self.register_bool_option('foo', default='True')
3761
conf = self.get_conf('foo=invalid boolean')
3762
self.assertEquals(True, conf.get('foo'))
3764
def register_integer_option(self, name,
3765
default=None, default_from_env=None):
3766
i = config.Option(name, help='An integer.',
3767
default=default, default_from_env=default_from_env,
3768
from_unicode=config.int_from_store)
3769
self.registry.register(i)
3771
def test_get_default_integer_None(self):
3772
self.register_integer_option('foo')
3773
conf = self.get_conf('')
3774
self.assertEquals(None, conf.get('foo'))
3776
def test_get_default_integer(self):
3777
self.register_integer_option('foo', 42)
3778
conf = self.get_conf('')
3779
self.assertEquals(42, conf.get('foo'))
3781
def test_get_default_integer_as_string(self):
3782
self.register_integer_option('foo', u'42')
3783
conf = self.get_conf('')
3784
self.assertEquals(42, conf.get('foo'))
3786
def test_get_default_integer_from_env(self):
3787
self.register_integer_option('foo', default_from_env=['FOO'])
3788
self.overrideEnv('FOO', '18')
3789
conf = self.get_conf('')
3790
self.assertEquals(18, conf.get('foo'))
3792
def test_get_default_integer_when_conversion_fails(self):
3793
self.register_integer_option('foo', default='12')
3794
conf = self.get_conf('foo=invalid integer')
3795
self.assertEquals(12, conf.get('foo'))
3797
def register_list_option(self, name, default=None, default_from_env=None):
3798
l = config.ListOption(name, help='A list.', default=default,
3799
default_from_env=default_from_env)
3800
self.registry.register(l)
3802
def test_get_default_list_None(self):
3803
self.register_list_option('foo')
3804
conf = self.get_conf('')
3805
self.assertEquals(None, conf.get('foo'))
3807
def test_get_default_list_empty(self):
3808
self.register_list_option('foo', '')
3809
conf = self.get_conf('')
3810
self.assertEquals([], conf.get('foo'))
3812
def test_get_default_list_from_env(self):
3813
self.register_list_option('foo', default_from_env=['FOO'])
3814
self.overrideEnv('FOO', '')
3815
conf = self.get_conf('')
3816
self.assertEquals([], conf.get('foo'))
3818
def test_get_with_list_converter_no_item(self):
3819
self.register_list_option('foo', None)
3820
conf = self.get_conf('foo=,')
3821
self.assertEquals([], conf.get('foo'))
3823
def test_get_with_list_converter_many_items(self):
3824
self.register_list_option('foo', None)
3825
conf = self.get_conf('foo=m,o,r,e')
3826
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3828
def test_get_with_list_converter_embedded_spaces_many_items(self):
3829
self.register_list_option('foo', None)
3830
conf = self.get_conf('foo=" bar", "baz "')
3831
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3833
def test_get_with_list_converter_stripped_spaces_many_items(self):
3834
self.register_list_option('foo', None)
3835
conf = self.get_conf('foo= bar , baz ')
3836
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3839
class TestIterOptionRefs(tests.TestCase):
3840
"""iter_option_refs is a bit unusual, document some cases."""
3842
def assertRefs(self, expected, string):
3843
self.assertEquals(expected, list(config.iter_option_refs(string)))
3845
def test_empty(self):
3846
self.assertRefs([(False, '')], '')
3848
def test_no_refs(self):
3849
self.assertRefs([(False, 'foo bar')], 'foo bar')
3851
def test_single_ref(self):
3852
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3854
def test_broken_ref(self):
3855
self.assertRefs([(False, '{foo')], '{foo')
3857
def test_embedded_ref(self):
3858
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3861
def test_two_refs(self):
3862
self.assertRefs([(False, ''), (True, '{foo}'),
3863
(False, ''), (True, '{bar}'),
3867
def test_newline_in_refs_are_not_matched(self):
3868
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3871
class TestStackExpandOptions(tests.TestCaseWithTransport):
3874
super(TestStackExpandOptions, self).setUp()
3875
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3876
self.registry = config.option_registry
3877
self.conf = build_branch_stack(self)
3879
def assertExpansion(self, expected, string, env=None):
3880
self.assertEquals(expected, self.conf.expand_options(string, env))
3882
def test_no_expansion(self):
3883
self.assertExpansion('foo', 'foo')
3885
def test_expand_default_value(self):
3886
self.conf.store._load_from_string('bar=baz')
3887
self.registry.register(config.Option('foo', default=u'{bar}'))
3888
self.assertEquals('baz', self.conf.get('foo', expand=True))
3890
def test_expand_default_from_env(self):
3891
self.conf.store._load_from_string('bar=baz')
3892
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3893
self.overrideEnv('FOO', '{bar}')
3894
self.assertEquals('baz', self.conf.get('foo', expand=True))
3896
def test_expand_default_on_failed_conversion(self):
3897
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3898
self.registry.register(
3899
config.Option('foo', default=u'{bar}',
3900
from_unicode=config.int_from_store))
3901
self.assertEquals(42, self.conf.get('foo', expand=True))
3903
def test_env_adding_options(self):
3904
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3906
def test_env_overriding_options(self):
3907
self.conf.store._load_from_string('foo=baz')
3908
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3910
def test_simple_ref(self):
3911
self.conf.store._load_from_string('foo=xxx')
3912
self.assertExpansion('xxx', '{foo}')
3914
def test_unknown_ref(self):
3915
self.assertRaises(errors.ExpandingUnknownOption,
3916
self.conf.expand_options, '{foo}')
3918
def test_indirect_ref(self):
3919
self.conf.store._load_from_string('''
3923
self.assertExpansion('xxx', '{bar}')
3925
def test_embedded_ref(self):
3926
self.conf.store._load_from_string('''
3930
self.assertExpansion('xxx', '{{bar}}')
3932
def test_simple_loop(self):
3933
self.conf.store._load_from_string('foo={foo}')
3934
self.assertRaises(errors.OptionExpansionLoop,
3935
self.conf.expand_options, '{foo}')
3937
def test_indirect_loop(self):
3938
self.conf.store._load_from_string('''
3942
e = self.assertRaises(errors.OptionExpansionLoop,
3943
self.conf.expand_options, '{foo}')
3944
self.assertEquals('foo->bar->baz', e.refs)
3945
self.assertEquals('{foo}', e.string)
3947
def test_list(self):
3948
self.conf.store._load_from_string('''
3952
list={foo},{bar},{baz}
3954
self.registry.register(
3955
config.ListOption('list'))
3956
self.assertEquals(['start', 'middle', 'end'],
3957
self.conf.get('list', expand=True))
3959
def test_cascading_list(self):
3960
self.conf.store._load_from_string('''
3966
self.registry.register(
3967
config.ListOption('list'))
3968
self.assertEquals(['start', 'middle', 'end'],
3969
self.conf.get('list', expand=True))
3971
def test_pathologically_hidden_list(self):
3972
self.conf.store._load_from_string('''
3978
hidden={start}{middle}{end}
3980
# What matters is what the registration says, the conversion happens
3981
# only after all expansions have been performed
3982
self.registry.register(config.ListOption('hidden'))
3983
self.assertEquals(['bin', 'go'],
3984
self.conf.get('hidden', expand=True))
3987
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3990
super(TestStackCrossSectionsExpand, self).setUp()
3992
def get_config(self, location, string):
3995
# Since we don't save the config we won't strictly require to inherit
3996
# from TestCaseInTempDir, but an error occurs so quickly...
3997
c = config.LocationStack(location)
3998
c.store._load_from_string(string)
4001
def test_dont_cross_unrelated_section(self):
4002
c = self.get_config('/another/branch/path','''
4007
[/another/branch/path]
4010
self.assertRaises(errors.ExpandingUnknownOption,
4011
c.get, 'bar', expand=True)
4013
def test_cross_related_sections(self):
4014
c = self.get_config('/project/branch/path','''
4018
[/project/branch/path]
4021
self.assertEquals('quux', c.get('bar', expand=True))
4024
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
4026
def test_cross_global_locations(self):
4027
l_store = config.LocationStore()
4028
l_store._load_from_string('''
4034
g_store = config.GlobalStore()
4035
g_store._load_from_string('''
4041
stack = config.LocationStack('/branch')
4042
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4043
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4046
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4048
def test_expand_locals_empty(self):
4049
l_store = config.LocationStore()
4050
l_store._load_from_string('''
4051
[/home/user/project]
4056
stack = config.LocationStack('/home/user/project/')
4057
self.assertEquals('', stack.get('base', expand=True))
4058
self.assertEquals('', stack.get('rel', expand=True))
4060
def test_expand_basename_locally(self):
4061
l_store = config.LocationStore()
4062
l_store._load_from_string('''
4063
[/home/user/project]
4067
stack = config.LocationStack('/home/user/project/branch')
4068
self.assertEquals('branch', stack.get('bfoo', expand=True))
4070
def test_expand_basename_locally_longer_path(self):
4071
l_store = config.LocationStore()
4072
l_store._load_from_string('''
4077
stack = config.LocationStack('/home/user/project/dir/branch')
4078
self.assertEquals('branch', stack.get('bfoo', expand=True))
4080
def test_expand_relpath_locally(self):
4081
l_store = config.LocationStore()
4082
l_store._load_from_string('''
4083
[/home/user/project]
4084
lfoo = loc-foo/{relpath}
4087
stack = config.LocationStack('/home/user/project/branch')
4088
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4090
def test_expand_relpath_unknonw_in_global(self):
4091
g_store = config.GlobalStore()
4092
g_store._load_from_string('''
4097
stack = config.LocationStack('/home/user/project/branch')
4098
self.assertRaises(errors.ExpandingUnknownOption,
4099
stack.get, 'gfoo', expand=True)
4101
def test_expand_local_option_locally(self):
4102
l_store = config.LocationStore()
4103
l_store._load_from_string('''
4104
[/home/user/project]
4105
lfoo = loc-foo/{relpath}
4109
g_store = config.GlobalStore()
4110
g_store._load_from_string('''
4116
stack = config.LocationStack('/home/user/project/branch')
4117
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4118
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4120
def test_locals_dont_leak(self):
4121
"""Make sure we chose the right local in presence of several sections.
4123
l_store = config.LocationStore()
4124
l_store._load_from_string('''
4126
lfoo = loc-foo/{relpath}
4127
[/home/user/project]
4128
lfoo = loc-foo/{relpath}
4131
stack = config.LocationStack('/home/user/project/branch')
4132
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4133
stack = config.LocationStack('/home/user/bar/baz')
4134
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
4138
class TestStackSet(TestStackWithTransport):
4140
def test_simple_set(self):
4141
conf = self.get_stack(self)
4142
self.assertEquals(None, conf.get('foo'))
4143
conf.set('foo', 'baz')
4144
# Did we get it back ?
4145
self.assertEquals('baz', conf.get('foo'))
4147
def test_set_creates_a_new_section(self):
4148
conf = self.get_stack(self)
4149
conf.set('foo', 'baz')
4150
self.assertEquals, 'baz', conf.get('foo')
4152
def test_set_hook(self):
4156
config.ConfigHooks.install_named_hook('set', hook, None)
4157
self.assertLength(0, calls)
4158
conf = self.get_stack(self)
4159
conf.set('foo', 'bar')
4160
self.assertLength(1, calls)
4161
self.assertEquals((conf, 'foo', 'bar'), calls[0])
4164
class TestStackRemove(TestStackWithTransport):
4166
def test_remove_existing(self):
4167
conf = self.get_stack(self)
4168
conf.set('foo', 'bar')
4169
self.assertEquals('bar', conf.get('foo'))
4171
# Did we get it back ?
4172
self.assertEquals(None, conf.get('foo'))
4174
def test_remove_unknown(self):
4175
conf = self.get_stack(self)
4176
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
4178
def test_remove_hook(self):
4182
config.ConfigHooks.install_named_hook('remove', hook, None)
4183
self.assertLength(0, calls)
4184
conf = self.get_stack(self)
4185
conf.set('foo', 'bar')
4187
self.assertLength(1, calls)
4188
self.assertEquals((conf, 'foo'), calls[0])
4191
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
4194
super(TestConfigGetOptions, self).setUp()
4195
create_configs(self)
4197
def test_no_variable(self):
4198
# Using branch should query branch, locations and bazaar
4199
self.assertOptions([], self.branch_config)
4201
def test_option_in_bazaar(self):
4202
self.bazaar_config.set_user_option('file', 'bazaar')
4203
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
4206
def test_option_in_locations(self):
4207
self.locations_config.set_user_option('file', 'locations')
4209
[('file', 'locations', self.tree.basedir, 'locations')],
4210
self.locations_config)
4212
def test_option_in_branch(self):
4213
self.branch_config.set_user_option('file', 'branch')
4214
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
4217
def test_option_in_bazaar_and_branch(self):
4218
self.bazaar_config.set_user_option('file', 'bazaar')
4219
self.branch_config.set_user_option('file', 'branch')
4220
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4221
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4224
def test_option_in_branch_and_locations(self):
4225
# Hmm, locations override branch :-/
4226
self.locations_config.set_user_option('file', 'locations')
4227
self.branch_config.set_user_option('file', 'branch')
4229
[('file', 'locations', self.tree.basedir, 'locations'),
4230
('file', 'branch', 'DEFAULT', 'branch'),],
4233
def test_option_in_bazaar_locations_and_branch(self):
4234
self.bazaar_config.set_user_option('file', 'bazaar')
4235
self.locations_config.set_user_option('file', 'locations')
4236
self.branch_config.set_user_option('file', 'branch')
4238
[('file', 'locations', self.tree.basedir, 'locations'),
4239
('file', 'branch', 'DEFAULT', 'branch'),
4240
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4244
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
4247
super(TestConfigRemoveOption, self).setUp()
4248
create_configs_with_file_option(self)
4250
def test_remove_in_locations(self):
4251
self.locations_config.remove_user_option('file', self.tree.basedir)
4253
[('file', 'branch', 'DEFAULT', 'branch'),
4254
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4257
def test_remove_in_branch(self):
4258
self.branch_config.remove_user_option('file')
4260
[('file', 'locations', self.tree.basedir, 'locations'),
4261
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4264
def test_remove_in_bazaar(self):
4265
self.bazaar_config.remove_user_option('file')
4267
[('file', 'locations', self.tree.basedir, 'locations'),
4268
('file', 'branch', 'DEFAULT', 'branch'),],
4272
class TestConfigGetSections(tests.TestCaseWithTransport):
4275
super(TestConfigGetSections, self).setUp()
4276
create_configs(self)
4278
def assertSectionNames(self, expected, conf, name=None):
4279
"""Check which sections are returned for a given config.
4281
If fallback configurations exist their sections can be included.
4283
:param expected: A list of section names.
4285
:param conf: The configuration that will be queried.
4287
:param name: An optional section name that will be passed to
4290
sections = list(conf._get_sections(name))
4291
self.assertLength(len(expected), sections)
4292
self.assertEqual(expected, [name for name, _, _ in sections])
4294
def test_bazaar_default_section(self):
4295
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4297
def test_locations_default_section(self):
4298
# No sections are defined in an empty file
4299
self.assertSectionNames([], self.locations_config)
4301
def test_locations_named_section(self):
4302
self.locations_config.set_user_option('file', 'locations')
4303
self.assertSectionNames([self.tree.basedir], self.locations_config)
4305
def test_locations_matching_sections(self):
4306
loc_config = self.locations_config
4307
loc_config.set_user_option('file', 'locations')
4308
# We need to cheat a bit here to create an option in sections above and
4309
# below the 'location' one.
4310
parser = loc_config._get_parser()
4311
# locations.cong deals with '/' ignoring native os.sep
4312
location_names = self.tree.basedir.split('/')
4313
parent = '/'.join(location_names[:-1])
4314
child = '/'.join(location_names + ['child'])
4316
parser[parent]['file'] = 'parent'
4318
parser[child]['file'] = 'child'
4319
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4321
def test_branch_data_default_section(self):
4322
self.assertSectionNames([None],
4323
self.branch_config._get_branch_data_config())
4325
def test_branch_default_sections(self):
4326
# No sections are defined in an empty locations file
4327
self.assertSectionNames([None, 'DEFAULT'],
4329
# Unless we define an option
4330
self.branch_config._get_location_config().set_user_option(
4331
'file', 'locations')
4332
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4335
def test_bazaar_named_section(self):
4336
# We need to cheat as the API doesn't give direct access to sections
4337
# other than DEFAULT.
4338
self.bazaar_config.set_alias('bazaar', 'bzr')
4339
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
4342
class TestAuthenticationConfigFile(tests.TestCase):
1316
4343
"""Test the authentication.conf file matching"""