1471
2065
self.assertIs(None, bzrdir_config.get_default_stack_on())
2068
class TestOldConfigHooks(tests.TestCaseWithTransport):
2071
super(TestOldConfigHooks, self).setUp()
2072
create_configs_with_file_option(self)
2074
def assertGetHook(self, conf, name, value):
2078
config.OldConfigHooks.install_named_hook('get', hook, None)
2080
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2081
self.assertLength(0, calls)
2082
actual_value = conf.get_user_option(name)
2083
self.assertEquals(value, actual_value)
2084
self.assertLength(1, calls)
2085
self.assertEquals((conf, name, value), calls[0])
2087
def test_get_hook_bazaar(self):
2088
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2090
def test_get_hook_locations(self):
2091
self.assertGetHook(self.locations_config, 'file', 'locations')
2093
def test_get_hook_branch(self):
2094
# Since locations masks branch, we define a different option
2095
self.branch_config.set_user_option('file2', 'branch')
2096
self.assertGetHook(self.branch_config, 'file2', 'branch')
2098
def assertSetHook(self, conf, name, value):
2102
config.OldConfigHooks.install_named_hook('set', hook, None)
2104
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2105
self.assertLength(0, calls)
2106
conf.set_user_option(name, value)
2107
self.assertLength(1, calls)
2108
# We can't assert the conf object below as different configs use
2109
# different means to implement set_user_option and we care only about
2111
self.assertEquals((name, value), calls[0][1:])
2113
def test_set_hook_bazaar(self):
2114
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2116
def test_set_hook_locations(self):
2117
self.assertSetHook(self.locations_config, 'foo', 'locations')
2119
def test_set_hook_branch(self):
2120
self.assertSetHook(self.branch_config, 'foo', 'branch')
2122
def assertRemoveHook(self, conf, name, section_name=None):
2126
config.OldConfigHooks.install_named_hook('remove', hook, None)
2128
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2129
self.assertLength(0, calls)
2130
conf.remove_user_option(name, section_name)
2131
self.assertLength(1, calls)
2132
# We can't assert the conf object below as different configs use
2133
# different means to implement remove_user_option and we care only about
2135
self.assertEquals((name,), calls[0][1:])
2137
def test_remove_hook_bazaar(self):
2138
self.assertRemoveHook(self.bazaar_config, 'file')
2140
def test_remove_hook_locations(self):
2141
self.assertRemoveHook(self.locations_config, 'file',
2142
self.locations_config.location)
2144
def test_remove_hook_branch(self):
2145
self.assertRemoveHook(self.branch_config, 'file')
2147
def assertLoadHook(self, name, conf_class, *conf_args):
2151
config.OldConfigHooks.install_named_hook('load', hook, None)
2153
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2154
self.assertLength(0, calls)
2156
conf = conf_class(*conf_args)
2157
# Access an option to trigger a load
2158
conf.get_user_option(name)
2159
self.assertLength(1, calls)
2160
# Since we can't assert about conf, we just use the number of calls ;-/
2162
def test_load_hook_bazaar(self):
2163
self.assertLoadHook('file', config.GlobalConfig)
2165
def test_load_hook_locations(self):
2166
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2168
def test_load_hook_branch(self):
2169
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2171
def assertSaveHook(self, conf):
2175
config.OldConfigHooks.install_named_hook('save', hook, None)
2177
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2178
self.assertLength(0, calls)
2179
# Setting an option triggers a save
2180
conf.set_user_option('foo', 'bar')
2181
self.assertLength(1, calls)
2182
# Since we can't assert about conf, we just use the number of calls ;-/
2184
def test_save_hook_bazaar(self):
2185
self.assertSaveHook(self.bazaar_config)
2187
def test_save_hook_locations(self):
2188
self.assertSaveHook(self.locations_config)
2190
def test_save_hook_branch(self):
2191
self.assertSaveHook(self.branch_config)
2194
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2195
"""Tests config hooks for remote configs.
2197
No tests for the remove hook as this is not implemented there.
2201
super(TestOldConfigHooksForRemote, self).setUp()
2202
self.transport_server = test_server.SmartTCPServer_for_testing
2203
create_configs_with_file_option(self)
2205
def assertGetHook(self, conf, name, value):
2209
config.OldConfigHooks.install_named_hook('get', hook, None)
2211
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2212
self.assertLength(0, calls)
2213
actual_value = conf.get_option(name)
2214
self.assertEquals(value, actual_value)
2215
self.assertLength(1, calls)
2216
self.assertEquals((conf, name, value), calls[0])
2218
def test_get_hook_remote_branch(self):
2219
remote_branch = branch.Branch.open(self.get_url('tree'))
2220
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2222
def test_get_hook_remote_bzrdir(self):
2223
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2224
conf = remote_bzrdir._get_config()
2225
conf.set_option('remotedir', 'file')
2226
self.assertGetHook(conf, 'file', 'remotedir')
2228
def assertSetHook(self, conf, name, value):
2232
config.OldConfigHooks.install_named_hook('set', hook, None)
2234
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2235
self.assertLength(0, calls)
2236
conf.set_option(value, name)
2237
self.assertLength(1, calls)
2238
# We can't assert the conf object below as different configs use
2239
# different means to implement set_user_option and we care only about
2241
self.assertEquals((name, value), calls[0][1:])
2243
def test_set_hook_remote_branch(self):
2244
remote_branch = branch.Branch.open(self.get_url('tree'))
2245
self.addCleanup(remote_branch.lock_write().unlock)
2246
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2248
def test_set_hook_remote_bzrdir(self):
2249
remote_branch = branch.Branch.open(self.get_url('tree'))
2250
self.addCleanup(remote_branch.lock_write().unlock)
2251
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2252
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2254
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2258
config.OldConfigHooks.install_named_hook('load', hook, None)
2260
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2261
self.assertLength(0, calls)
2263
conf = conf_class(*conf_args)
2264
# Access an option to trigger a load
2265
conf.get_option(name)
2266
self.assertLength(expected_nb_calls, calls)
2267
# Since we can't assert about conf, we just use the number of calls ;-/
2269
def test_load_hook_remote_branch(self):
2270
remote_branch = branch.Branch.open(self.get_url('tree'))
2271
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2273
def test_load_hook_remote_bzrdir(self):
2274
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2275
# The config file doesn't exist, set an option to force its creation
2276
conf = remote_bzrdir._get_config()
2277
conf.set_option('remotedir', 'file')
2278
# We get one call for the server and one call for the client, this is
2279
# caused by the differences in implementations betwen
2280
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2281
# SmartServerBranchGetConfigFile (in smart/branch.py)
2282
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2284
def assertSaveHook(self, conf):
2288
config.OldConfigHooks.install_named_hook('save', hook, None)
2290
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2291
self.assertLength(0, calls)
2292
# Setting an option triggers a save
2293
conf.set_option('foo', 'bar')
2294
self.assertLength(1, calls)
2295
# Since we can't assert about conf, we just use the number of calls ;-/
2297
def test_save_hook_remote_branch(self):
2298
remote_branch = branch.Branch.open(self.get_url('tree'))
2299
self.addCleanup(remote_branch.lock_write().unlock)
2300
self.assertSaveHook(remote_branch._get_config())
2302
def test_save_hook_remote_bzrdir(self):
2303
remote_branch = branch.Branch.open(self.get_url('tree'))
2304
self.addCleanup(remote_branch.lock_write().unlock)
2305
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2306
self.assertSaveHook(remote_bzrdir._get_config())
2309
class TestOption(tests.TestCase):
2311
def test_default_value(self):
2312
opt = config.Option('foo', default='bar')
2313
self.assertEquals('bar', opt.get_default())
2315
def test_callable_default_value(self):
2316
def bar_as_unicode():
2318
opt = config.Option('foo', default=bar_as_unicode)
2319
self.assertEquals('bar', opt.get_default())
2321
def test_default_value_from_env(self):
2322
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2323
self.overrideEnv('FOO', 'quux')
2324
# Env variable provides a default taking over the option one
2325
self.assertEquals('quux', opt.get_default())
2327
def test_first_default_value_from_env_wins(self):
2328
opt = config.Option('foo', default='bar',
2329
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2330
self.overrideEnv('FOO', 'foo')
2331
self.overrideEnv('BAZ', 'baz')
2332
# The first env var set wins
2333
self.assertEquals('foo', opt.get_default())
2335
def test_not_supported_list_default_value(self):
2336
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2338
def test_not_supported_object_default_value(self):
2339
self.assertRaises(AssertionError, config.Option, 'foo',
2342
def test_not_supported_callable_default_value_not_unicode(self):
2343
def bar_not_unicode():
2345
opt = config.Option('foo', default=bar_not_unicode)
2346
self.assertRaises(AssertionError, opt.get_default)
2349
class TestOptionConverterMixin(object):
2351
def assertConverted(self, expected, opt, value):
2352
self.assertEquals(expected, opt.convert_from_unicode(value))
2354
def assertWarns(self, opt, value):
2357
warnings.append(args[0] % args[1:])
2358
self.overrideAttr(trace, 'warning', warning)
2359
self.assertEquals(None, opt.convert_from_unicode(value))
2360
self.assertLength(1, warnings)
2362
'Value "%s" is not valid for "%s"' % (value, opt.name),
2365
def assertErrors(self, opt, value):
2366
self.assertRaises(errors.ConfigOptionValueError,
2367
opt.convert_from_unicode, value)
2369
def assertConvertInvalid(self, opt, invalid_value):
2371
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2372
opt.invalid = 'warning'
2373
self.assertWarns(opt, invalid_value)
2374
opt.invalid = 'error'
2375
self.assertErrors(opt, invalid_value)
2378
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2380
def get_option(self):
2381
return config.Option('foo', help='A boolean.',
2382
from_unicode=config.bool_from_store)
2384
def test_convert_invalid(self):
2385
opt = self.get_option()
2386
# A string that is not recognized as a boolean
2387
self.assertConvertInvalid(opt, u'invalid-boolean')
2388
# A list of strings is never recognized as a boolean
2389
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2391
def test_convert_valid(self):
2392
opt = self.get_option()
2393
self.assertConverted(True, opt, u'True')
2394
self.assertConverted(True, opt, u'1')
2395
self.assertConverted(False, opt, u'False')
2398
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2400
def get_option(self):
2401
return config.Option('foo', help='An integer.',
2402
from_unicode=config.int_from_store)
2404
def test_convert_invalid(self):
2405
opt = self.get_option()
2406
# A string that is not recognized as an integer
2407
self.assertConvertInvalid(opt, u'forty-two')
2408
# A list of strings is never recognized as an integer
2409
self.assertConvertInvalid(opt, [u'a', u'list'])
2411
def test_convert_valid(self):
2412
opt = self.get_option()
2413
self.assertConverted(16, opt, u'16')
2416
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2418
def get_option(self):
2419
return config.Option('foo', help='A list.',
2420
from_unicode=config.list_from_store)
2422
def test_convert_invalid(self):
2423
# No string is invalid as all forms can be converted to a list
2426
def test_convert_valid(self):
2427
opt = self.get_option()
2428
# An empty string is an empty list
2429
self.assertConverted([], opt, '') # Using a bare str() just in case
2430
self.assertConverted([], opt, u'')
2432
self.assertConverted([u'True'], opt, u'True')
2434
self.assertConverted([u'42'], opt, u'42')
2436
self.assertConverted([u'bar'], opt, u'bar')
2437
# A list remains a list (configObj will turn a string containing commas
2438
# into a list, but that's not what we're testing here)
2439
self.assertConverted([u'foo', u'1', u'True'],
2440
opt, [u'foo', u'1', u'True'])
2443
class TestOptionConverterMixin(object):
2445
def assertConverted(self, expected, opt, value):
2446
self.assertEquals(expected, opt.convert_from_unicode(value))
2448
def assertWarns(self, opt, value):
2451
warnings.append(args[0] % args[1:])
2452
self.overrideAttr(trace, 'warning', warning)
2453
self.assertEquals(None, opt.convert_from_unicode(value))
2454
self.assertLength(1, warnings)
2456
'Value "%s" is not valid for "%s"' % (value, opt.name),
2459
def assertErrors(self, opt, value):
2460
self.assertRaises(errors.ConfigOptionValueError,
2461
opt.convert_from_unicode, value)
2463
def assertConvertInvalid(self, opt, invalid_value):
2465
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2466
opt.invalid = 'warning'
2467
self.assertWarns(opt, invalid_value)
2468
opt.invalid = 'error'
2469
self.assertErrors(opt, invalid_value)
2472
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2474
def get_option(self):
2475
return config.Option('foo', help='A boolean.',
2476
from_unicode=config.bool_from_store)
2478
def test_convert_invalid(self):
2479
opt = self.get_option()
2480
# A string that is not recognized as a boolean
2481
self.assertConvertInvalid(opt, u'invalid-boolean')
2482
# A list of strings is never recognized as a boolean
2483
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2485
def test_convert_valid(self):
2486
opt = self.get_option()
2487
self.assertConverted(True, opt, u'True')
2488
self.assertConverted(True, opt, u'1')
2489
self.assertConverted(False, opt, u'False')
2492
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2494
def get_option(self):
2495
return config.Option('foo', help='An integer.',
2496
from_unicode=config.int_from_store)
2498
def test_convert_invalid(self):
2499
opt = self.get_option()
2500
# A string that is not recognized as an integer
2501
self.assertConvertInvalid(opt, u'forty-two')
2502
# A list of strings is never recognized as an integer
2503
self.assertConvertInvalid(opt, [u'a', u'list'])
2505
def test_convert_valid(self):
2506
opt = self.get_option()
2507
self.assertConverted(16, opt, u'16')
2510
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2512
def get_option(self):
2513
return config.Option('foo', help='A list.',
2514
from_unicode=config.list_from_store)
2516
def test_convert_invalid(self):
2517
opt = self.get_option()
2518
# We don't even try to convert a list into a list, we only expect
2520
self.assertConvertInvalid(opt, [1])
2521
# No string is invalid as all forms can be converted to a list
2523
def test_convert_valid(self):
2524
opt = self.get_option()
2525
# An empty string is an empty list
2526
self.assertConverted([], opt, '') # Using a bare str() just in case
2527
self.assertConverted([], opt, u'')
2529
self.assertConverted([u'True'], opt, u'True')
2531
self.assertConverted([u'42'], opt, u'42')
2533
self.assertConverted([u'bar'], opt, u'bar')
2536
class TestOptionRegistry(tests.TestCase):
2539
super(TestOptionRegistry, self).setUp()
2540
# Always start with an empty registry
2541
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2542
self.registry = config.option_registry
2544
def test_register(self):
2545
opt = config.Option('foo')
2546
self.registry.register(opt)
2547
self.assertIs(opt, self.registry.get('foo'))
2549
def test_registered_help(self):
2550
opt = config.Option('foo', help='A simple option')
2551
self.registry.register(opt)
2552
self.assertEquals('A simple option', self.registry.get_help('foo'))
2554
lazy_option = config.Option('lazy_foo', help='Lazy help')
2556
def test_register_lazy(self):
2557
self.registry.register_lazy('lazy_foo', self.__module__,
2558
'TestOptionRegistry.lazy_option')
2559
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2561
def test_registered_lazy_help(self):
2562
self.registry.register_lazy('lazy_foo', self.__module__,
2563
'TestOptionRegistry.lazy_option')
2564
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2567
class TestRegisteredOptions(tests.TestCase):
2568
"""All registered options should verify some constraints."""
2570
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2571
in config.option_registry.iteritems()]
2574
super(TestRegisteredOptions, self).setUp()
2575
self.registry = config.option_registry
2577
def test_proper_name(self):
2578
# An option should be registered under its own name, this can't be
2579
# checked at registration time for the lazy ones.
2580
self.assertEquals(self.option_name, self.option.name)
2582
def test_help_is_set(self):
2583
option_help = self.registry.get_help(self.option_name)
2584
self.assertNotEquals(None, option_help)
2585
# Come on, think about the user, he really wants to know what the
2587
self.assertIsNot(None, option_help)
2588
self.assertNotEquals('', option_help)
2591
class TestSection(tests.TestCase):
2593
# FIXME: Parametrize so that all sections produced by Stores run these
2594
# tests -- vila 2011-04-01
2596
def test_get_a_value(self):
2597
a_dict = dict(foo='bar')
2598
section = config.Section('myID', a_dict)
2599
self.assertEquals('bar', section.get('foo'))
2601
def test_get_unknown_option(self):
2603
section = config.Section(None, a_dict)
2604
self.assertEquals('out of thin air',
2605
section.get('foo', 'out of thin air'))
2607
def test_options_is_shared(self):
2609
section = config.Section(None, a_dict)
2610
self.assertIs(a_dict, section.options)
2613
class TestMutableSection(tests.TestCase):
2615
scenarios = [('mutable',
2617
lambda opts: config.MutableSection('myID', opts)},),
2621
a_dict = dict(foo='bar')
2622
section = self.get_section(a_dict)
2623
section.set('foo', 'new_value')
2624
self.assertEquals('new_value', section.get('foo'))
2625
# The change appears in the shared section
2626
self.assertEquals('new_value', a_dict.get('foo'))
2627
# We keep track of the change
2628
self.assertTrue('foo' in section.orig)
2629
self.assertEquals('bar', section.orig.get('foo'))
2631
def test_set_preserve_original_once(self):
2632
a_dict = dict(foo='bar')
2633
section = self.get_section(a_dict)
2634
section.set('foo', 'first_value')
2635
section.set('foo', 'second_value')
2636
# We keep track of the original value
2637
self.assertTrue('foo' in section.orig)
2638
self.assertEquals('bar', section.orig.get('foo'))
2640
def test_remove(self):
2641
a_dict = dict(foo='bar')
2642
section = self.get_section(a_dict)
2643
section.remove('foo')
2644
# We get None for unknown options via the default value
2645
self.assertEquals(None, section.get('foo'))
2646
# Or we just get the default value
2647
self.assertEquals('unknown', section.get('foo', 'unknown'))
2648
self.assertFalse('foo' in section.options)
2649
# We keep track of the deletion
2650
self.assertTrue('foo' in section.orig)
2651
self.assertEquals('bar', section.orig.get('foo'))
2653
def test_remove_new_option(self):
2655
section = self.get_section(a_dict)
2656
section.set('foo', 'bar')
2657
section.remove('foo')
2658
self.assertFalse('foo' in section.options)
2659
# The option didn't exist initially so it we need to keep track of it
2660
# with a special value
2661
self.assertTrue('foo' in section.orig)
2662
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2665
class TestCommandLineStore(tests.TestCase):
2668
super(TestCommandLineStore, self).setUp()
2669
self.store = config.CommandLineStore()
2671
def get_section(self):
2672
"""Get the unique section for the command line overrides."""
2673
sections = list(self.store.get_sections())
2674
self.assertLength(1, sections)
2675
store, section = sections[0]
2676
self.assertEquals(self.store, store)
2679
def test_no_override(self):
2680
self.store._from_cmdline([])
2681
section = self.get_section()
2682
self.assertLength(0, list(section.iter_option_names()))
2684
def test_simple_override(self):
2685
self.store._from_cmdline(['a=b'])
2686
section = self.get_section()
2687
self.assertEqual('b', section.get('a'))
2689
def test_list_override(self):
2690
self.store._from_cmdline(['l=1,2,3'])
2691
val = self.get_section().get('l')
2692
self.assertEqual('1,2,3', val)
2693
# Reminder: lists should be registered as such explicitely, otherwise
2694
# the conversion needs to be done afterwards.
2695
self.assertEqual(['1', '2', '3'], config.list_from_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'])
2708
class TestStore(tests.TestCaseWithTransport):
2710
def assertSectionContent(self, expected, (store, section)):
2711
"""Assert that some options have the proper values in a section."""
2712
expected_name, expected_options = expected
2713
self.assertEquals(expected_name, section.id)
2716
dict([(k, section.get(k)) for k in expected_options.keys()]))
2719
class TestReadonlyStore(TestStore):
2721
scenarios = [(key, {'get_store': builder}) for key, builder
2722
in config.test_store_builder_registry.iteritems()]
2724
def test_building_delays_load(self):
2725
store = self.get_store(self)
2726
self.assertEquals(False, store.is_loaded())
2727
store._load_from_string('')
2728
self.assertEquals(True, store.is_loaded())
2730
def test_get_no_sections_for_empty(self):
2731
store = self.get_store(self)
2732
store._load_from_string('')
2733
self.assertEquals([], list(store.get_sections()))
2735
def test_get_default_section(self):
2736
store = self.get_store(self)
2737
store._load_from_string('foo=bar')
2738
sections = list(store.get_sections())
2739
self.assertLength(1, sections)
2740
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2742
def test_get_named_section(self):
2743
store = self.get_store(self)
2744
store._load_from_string('[baz]\nfoo=bar')
2745
sections = list(store.get_sections())
2746
self.assertLength(1, sections)
2747
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2749
def test_load_from_string_fails_for_non_empty_store(self):
2750
store = self.get_store(self)
2751
store._load_from_string('foo=bar')
2752
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2755
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2756
"""Simulate loading a config store with content of various encodings.
2758
All files produced by bzr are in utf8 content.
2760
Users may modify them manually and end up with a file that can't be
2761
loaded. We need to issue proper error messages in this case.
2764
invalid_utf8_char = '\xff'
2766
def test_load_utf8(self):
2767
"""Ensure we can load an utf8-encoded file."""
2768
t = self.get_transport()
2769
# From http://pad.lv/799212
2770
unicode_user = u'b\N{Euro Sign}ar'
2771
unicode_content = u'user=%s' % (unicode_user,)
2772
utf8_content = unicode_content.encode('utf8')
2773
# Store the raw content in the config file
2774
t.put_bytes('foo.conf', utf8_content)
2775
store = config.TransportIniFileStore(t, 'foo.conf')
2777
stack = config.Stack([store.get_sections], store)
2778
self.assertEquals(unicode_user, stack.get('user'))
2780
def test_load_non_ascii(self):
2781
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2782
t = self.get_transport()
2783
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2784
store = config.TransportIniFileStore(t, 'foo.conf')
2785
self.assertRaises(errors.ConfigContentError, store.load)
2787
def test_load_erroneous_content(self):
2788
"""Ensure we display a proper error on content that can't be parsed."""
2789
t = self.get_transport()
2790
t.put_bytes('foo.conf', '[open_section\n')
2791
store = config.TransportIniFileStore(t, 'foo.conf')
2792
self.assertRaises(errors.ParseConfigError, store.load)
2794
def test_load_permission_denied(self):
2795
"""Ensure we get warned when trying to load an inaccessible file."""
2798
warnings.append(args[0] % args[1:])
2799
self.overrideAttr(trace, 'warning', warning)
2801
t = self.get_transport()
2803
def get_bytes(relpath):
2804
raise errors.PermissionDenied(relpath, "")
2805
t.get_bytes = get_bytes
2806
store = config.TransportIniFileStore(t, 'foo.conf')
2807
self.assertRaises(errors.PermissionDenied, store.load)
2810
[u'Permission denied while trying to load configuration store %s.'
2811
% store.external_url()])
2814
class TestIniConfigContent(tests.TestCaseWithTransport):
2815
"""Simulate loading a IniBasedConfig with content of various encodings.
2817
All files produced by bzr are in utf8 content.
2819
Users may modify them manually and end up with a file that can't be
2820
loaded. We need to issue proper error messages in this case.
2823
invalid_utf8_char = '\xff'
2825
def test_load_utf8(self):
2826
"""Ensure we can load an utf8-encoded file."""
2827
# From http://pad.lv/799212
2828
unicode_user = u'b\N{Euro Sign}ar'
2829
unicode_content = u'user=%s' % (unicode_user,)
2830
utf8_content = unicode_content.encode('utf8')
2831
# Store the raw content in the config file
2832
with open('foo.conf', 'wb') as f:
2833
f.write(utf8_content)
2834
conf = config.IniBasedConfig(file_name='foo.conf')
2835
self.assertEquals(unicode_user, conf.get_user_option('user'))
2837
def test_load_badly_encoded_content(self):
2838
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2839
with open('foo.conf', 'wb') as f:
2840
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2841
conf = config.IniBasedConfig(file_name='foo.conf')
2842
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2844
def test_load_erroneous_content(self):
2845
"""Ensure we display a proper error on content that can't be parsed."""
2846
with open('foo.conf', 'wb') as f:
2847
f.write('[open_section\n')
2848
conf = config.IniBasedConfig(file_name='foo.conf')
2849
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2852
class TestMutableStore(TestStore):
2854
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2855
in config.test_store_builder_registry.iteritems()]
2858
super(TestMutableStore, self).setUp()
2859
self.transport = self.get_transport()
2861
def has_store(self, store):
2862
store_basename = urlutils.relative_url(self.transport.external_url(),
2863
store.external_url())
2864
return self.transport.has(store_basename)
2866
def test_save_empty_creates_no_file(self):
2867
# FIXME: There should be a better way than relying on the test
2868
# parametrization to identify branch.conf -- vila 2011-0526
2869
if self.store_id in ('branch', 'remote_branch'):
2870
raise tests.TestNotApplicable(
2871
'branch.conf is *always* created when a branch is initialized')
2872
store = self.get_store(self)
2874
self.assertEquals(False, self.has_store(store))
2876
def test_save_emptied_succeeds(self):
2877
store = self.get_store(self)
2878
store._load_from_string('foo=bar\n')
2879
section = store.get_mutable_section(None)
2880
section.remove('foo')
2882
self.assertEquals(True, self.has_store(store))
2883
modified_store = self.get_store(self)
2884
sections = list(modified_store.get_sections())
2885
self.assertLength(0, sections)
2887
def test_save_with_content_succeeds(self):
2888
# FIXME: There should be a better way than relying on the test
2889
# parametrization to identify branch.conf -- vila 2011-0526
2890
if self.store_id in ('branch', 'remote_branch'):
2891
raise tests.TestNotApplicable(
2892
'branch.conf is *always* created when a branch is initialized')
2893
store = self.get_store(self)
2894
store._load_from_string('foo=bar\n')
2895
self.assertEquals(False, self.has_store(store))
2897
self.assertEquals(True, self.has_store(store))
2898
modified_store = self.get_store(self)
2899
sections = list(modified_store.get_sections())
2900
self.assertLength(1, sections)
2901
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2903
def test_set_option_in_empty_store(self):
2904
store = self.get_store(self)
2905
section = store.get_mutable_section(None)
2906
section.set('foo', 'bar')
2908
modified_store = self.get_store(self)
2909
sections = list(modified_store.get_sections())
2910
self.assertLength(1, sections)
2911
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2913
def test_set_option_in_default_section(self):
2914
store = self.get_store(self)
2915
store._load_from_string('')
2916
section = store.get_mutable_section(None)
2917
section.set('foo', 'bar')
2919
modified_store = self.get_store(self)
2920
sections = list(modified_store.get_sections())
2921
self.assertLength(1, sections)
2922
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2924
def test_set_option_in_named_section(self):
2925
store = self.get_store(self)
2926
store._load_from_string('')
2927
section = store.get_mutable_section('baz')
2928
section.set('foo', 'bar')
2930
modified_store = self.get_store(self)
2931
sections = list(modified_store.get_sections())
2932
self.assertLength(1, sections)
2933
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2935
def test_load_hook(self):
2936
# We first needs to ensure that the store exists
2937
store = self.get_store(self)
2938
section = store.get_mutable_section('baz')
2939
section.set('foo', 'bar')
2941
# Now we can try to load it
2942
store = self.get_store(self)
2946
config.ConfigHooks.install_named_hook('load', hook, None)
2947
self.assertLength(0, calls)
2949
self.assertLength(1, calls)
2950
self.assertEquals((store,), calls[0])
2952
def test_save_hook(self):
2956
config.ConfigHooks.install_named_hook('save', hook, None)
2957
self.assertLength(0, calls)
2958
store = self.get_store(self)
2959
section = store.get_mutable_section('baz')
2960
section.set('foo', 'bar')
2962
self.assertLength(1, calls)
2963
self.assertEquals((store,), calls[0])
2966
class TestTransportIniFileStore(TestStore):
2968
def test_loading_unknown_file_fails(self):
2969
store = config.TransportIniFileStore(self.get_transport(),
2971
self.assertRaises(errors.NoSuchFile, store.load)
2973
def test_invalid_content(self):
2974
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2975
self.assertEquals(False, store.is_loaded())
2976
exc = self.assertRaises(
2977
errors.ParseConfigError, store._load_from_string,
2978
'this is invalid !')
2979
self.assertEndsWith(exc.filename, 'foo.conf')
2980
# And the load failed
2981
self.assertEquals(False, store.is_loaded())
2983
def test_get_embedded_sections(self):
2984
# A more complicated example (which also shows that section names and
2985
# option names share the same name space...)
2986
# FIXME: This should be fixed by forbidding dicts as values ?
2987
# -- vila 2011-04-05
2988
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2989
store._load_from_string('''
2993
foo_in_DEFAULT=foo_DEFAULT
3001
sections = list(store.get_sections())
3002
self.assertLength(4, sections)
3003
# The default section has no name.
3004
# List values are provided as strings and need to be explicitly
3005
# converted by specifying from_unicode=list_from_store at option
3007
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3009
self.assertSectionContent(
3010
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3011
self.assertSectionContent(
3012
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3013
# sub sections are provided as embedded dicts.
3014
self.assertSectionContent(
3015
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3019
class TestLockableIniFileStore(TestStore):
3021
def test_create_store_in_created_dir(self):
3022
self.assertPathDoesNotExist('dir')
3023
t = self.get_transport('dir/subdir')
3024
store = config.LockableIniFileStore(t, 'foo.conf')
3025
store.get_mutable_section(None).set('foo', 'bar')
3027
self.assertPathExists('dir/subdir')
3030
class TestConcurrentStoreUpdates(TestStore):
3031
"""Test that Stores properly handle conccurent updates.
3033
New Store implementation may fail some of these tests but until such
3034
implementations exist it's hard to properly filter them from the scenarios
3035
applied here. If you encounter such a case, contact the bzr devs.
3038
scenarios = [(key, {'get_stack': builder}) for key, builder
3039
in config.test_stack_builder_registry.iteritems()]
3042
super(TestConcurrentStoreUpdates, self).setUp()
3043
self.stack = self.get_stack(self)
3044
if not isinstance(self.stack, config._CompatibleStack):
3045
raise tests.TestNotApplicable(
3046
'%s is not meant to be compatible with the old config design'
3048
self.stack.set('one', '1')
3049
self.stack.set('two', '2')
3051
self.stack.store.save()
3053
def test_simple_read_access(self):
3054
self.assertEquals('1', self.stack.get('one'))
3056
def test_simple_write_access(self):
3057
self.stack.set('one', 'one')
3058
self.assertEquals('one', self.stack.get('one'))
3060
def test_listen_to_the_last_speaker(self):
3062
c2 = self.get_stack(self)
3063
c1.set('one', 'ONE')
3064
c2.set('two', 'TWO')
3065
self.assertEquals('ONE', c1.get('one'))
3066
self.assertEquals('TWO', c2.get('two'))
3067
# The second update respect the first one
3068
self.assertEquals('ONE', c2.get('one'))
3070
def test_last_speaker_wins(self):
3071
# If the same config is not shared, the same variable modified twice
3072
# can only see a single result.
3074
c2 = self.get_stack(self)
3077
self.assertEquals('c2', c2.get('one'))
3078
# The first modification is still available until another refresh
3080
self.assertEquals('c1', c1.get('one'))
3081
c1.set('two', 'done')
3082
self.assertEquals('c2', c1.get('one'))
3084
def test_writes_are_serialized(self):
3086
c2 = self.get_stack(self)
3088
# We spawn a thread that will pause *during* the config saving.
3089
before_writing = threading.Event()
3090
after_writing = threading.Event()
3091
writing_done = threading.Event()
3092
c1_save_without_locking_orig = c1.store.save_without_locking
3093
def c1_save_without_locking():
3094
before_writing.set()
3095
c1_save_without_locking_orig()
3096
# The lock is held. We wait for the main thread to decide when to
3098
after_writing.wait()
3099
c1.store.save_without_locking = c1_save_without_locking
3103
t1 = threading.Thread(target=c1_set)
3104
# Collect the thread after the test
3105
self.addCleanup(t1.join)
3106
# Be ready to unblock the thread if the test goes wrong
3107
self.addCleanup(after_writing.set)
3109
before_writing.wait()
3110
self.assertRaises(errors.LockContention,
3111
c2.set, 'one', 'c2')
3112
self.assertEquals('c1', c1.get('one'))
3113
# Let the lock be released
3117
self.assertEquals('c2', c2.get('one'))
3119
def test_read_while_writing(self):
3121
# We spawn a thread that will pause *during* the write
3122
ready_to_write = threading.Event()
3123
do_writing = threading.Event()
3124
writing_done = threading.Event()
3125
# We override the _save implementation so we know the store is locked
3126
c1_save_without_locking_orig = c1.store.save_without_locking
3127
def c1_save_without_locking():
3128
ready_to_write.set()
3129
# The lock is held. We wait for the main thread to decide when to
3132
c1_save_without_locking_orig()
3134
c1.store.save_without_locking = c1_save_without_locking
3137
t1 = threading.Thread(target=c1_set)
3138
# Collect the thread after the test
3139
self.addCleanup(t1.join)
3140
# Be ready to unblock the thread if the test goes wrong
3141
self.addCleanup(do_writing.set)
3143
# Ensure the thread is ready to write
3144
ready_to_write.wait()
3145
self.assertEquals('c1', c1.get('one'))
3146
# If we read during the write, we get the old value
3147
c2 = self.get_stack(self)
3148
self.assertEquals('1', c2.get('one'))
3149
# Let the writing occur and ensure it occurred
3152
# Now we get the updated value
3153
c3 = self.get_stack(self)
3154
self.assertEquals('c1', c3.get('one'))
3156
# FIXME: It may be worth looking into removing the lock dir when it's not
3157
# needed anymore and look at possible fallouts for concurrent lockers. This
3158
# will matter if/when we use config files outside of bazaar directories
3159
# (.bazaar or .bzr) -- vila 20110-04-111
3162
class TestSectionMatcher(TestStore):
3164
scenarios = [('location', {'matcher': config.LocationMatcher}),
3165
('id', {'matcher': config.NameMatcher}),]
3168
super(TestSectionMatcher, self).setUp()
3169
# Any simple store is good enough
3170
self.get_store = config.test_store_builder_registry.get('configobj')
3172
def test_no_matches_for_empty_stores(self):
3173
store = self.get_store(self)
3174
store._load_from_string('')
3175
matcher = self.matcher(store, '/bar')
3176
self.assertEquals([], list(matcher.get_sections()))
3178
def test_build_doesnt_load_store(self):
3179
store = self.get_store(self)
3180
matcher = self.matcher(store, '/bar')
3181
self.assertFalse(store.is_loaded())
3184
class TestLocationSection(tests.TestCase):
3186
def get_section(self, options, extra_path):
3187
section = config.Section('foo', options)
3188
# We don't care about the length so we use '0'
3189
return config.LocationSection(section, 0, extra_path)
3191
def test_simple_option(self):
3192
section = self.get_section({'foo': 'bar'}, '')
3193
self.assertEquals('bar', section.get('foo'))
3195
def test_option_with_extra_path(self):
3196
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3198
self.assertEquals('bar/baz', section.get('foo'))
3200
def test_invalid_policy(self):
3201
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3203
# invalid policies are ignored
3204
self.assertEquals('bar', section.get('foo'))
3207
class TestLocationMatcher(TestStore):
3210
super(TestLocationMatcher, self).setUp()
3211
# Any simple store is good enough
3212
self.get_store = config.test_store_builder_registry.get('configobj')
3214
def test_unrelated_section_excluded(self):
3215
store = self.get_store(self)
3216
store._load_from_string('''
3224
section=/foo/bar/baz
3228
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3230
[section.id for _, section in store.get_sections()])
3231
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3232
sections = [section for s, section in matcher.get_sections()]
3233
self.assertEquals([3, 2],
3234
[section.length for section in sections])
3235
self.assertEquals(['/foo/bar', '/foo'],
3236
[section.id for section in sections])
3237
self.assertEquals(['quux', 'bar/quux'],
3238
[section.extra_path for section in sections])
3240
def test_more_specific_sections_first(self):
3241
store = self.get_store(self)
3242
store._load_from_string('''
3248
self.assertEquals(['/foo', '/foo/bar'],
3249
[section.id for _, section in store.get_sections()])
3250
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3251
sections = [section for s, section in matcher.get_sections()]
3252
self.assertEquals([3, 2],
3253
[section.length for section in sections])
3254
self.assertEquals(['/foo/bar', '/foo'],
3255
[section.id for section in sections])
3256
self.assertEquals(['baz', 'bar/baz'],
3257
[section.extra_path for section in sections])
3259
def test_appendpath_in_no_name_section(self):
3260
# It's a bit weird to allow appendpath in a no-name section, but
3261
# someone may found a use for it
3262
store = self.get_store(self)
3263
store._load_from_string('''
3265
foo:policy = appendpath
3267
matcher = config.LocationMatcher(store, 'dir/subdir')
3268
sections = list(matcher.get_sections())
3269
self.assertLength(1, sections)
3270
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3272
def test_file_urls_are_normalized(self):
3273
store = self.get_store(self)
3274
if sys.platform == 'win32':
3275
expected_url = 'file:///C:/dir/subdir'
3276
expected_location = 'C:/dir/subdir'
3278
expected_url = 'file:///dir/subdir'
3279
expected_location = '/dir/subdir'
3280
matcher = config.LocationMatcher(store, expected_url)
3281
self.assertEquals(expected_location, matcher.location)
3284
class TestNameMatcher(TestStore):
3287
super(TestNameMatcher, self).setUp()
3288
self.matcher = config.NameMatcher
3289
# Any simple store is good enough
3290
self.get_store = config.test_store_builder_registry.get('configobj')
3292
def get_matching_sections(self, name):
3293
store = self.get_store(self)
3294
store._load_from_string('''
3302
matcher = self.matcher(store, name)
3303
return list(matcher.get_sections())
3305
def test_matching(self):
3306
sections = self.get_matching_sections('foo')
3307
self.assertLength(1, sections)
3308
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3310
def test_not_matching(self):
3311
sections = self.get_matching_sections('baz')
3312
self.assertLength(0, sections)
3315
class TestStackGet(tests.TestCase):
3317
# FIXME: This should be parametrized for all known Stack or dedicated
3318
# paramerized tests created to avoid bloating -- vila 2011-03-31
3320
def overrideOptionRegistry(self):
3321
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3323
def test_single_config_get(self):
3324
conf = dict(foo='bar')
3325
conf_stack = config.Stack([conf])
3326
self.assertEquals('bar', conf_stack.get('foo'))
3328
def test_get_with_registered_default_value(self):
3329
conf_stack = config.Stack([dict()])
3330
opt = config.Option('foo', default='bar')
3331
self.overrideOptionRegistry()
3332
config.option_registry.register('foo', opt)
3333
self.assertEquals('bar', conf_stack.get('foo'))
3335
def test_get_without_registered_default_value(self):
3336
conf_stack = config.Stack([dict()])
3337
opt = config.Option('foo')
3338
self.overrideOptionRegistry()
3339
config.option_registry.register('foo', opt)
3340
self.assertEquals(None, conf_stack.get('foo'))
3342
def test_get_without_default_value_for_not_registered(self):
3343
conf_stack = config.Stack([dict()])
3344
opt = config.Option('foo')
3345
self.overrideOptionRegistry()
3346
self.assertEquals(None, conf_stack.get('foo'))
3348
def test_get_first_definition(self):
3349
conf1 = dict(foo='bar')
3350
conf2 = dict(foo='baz')
3351
conf_stack = config.Stack([conf1, conf2])
3352
self.assertEquals('bar', conf_stack.get('foo'))
3354
def test_get_embedded_definition(self):
3355
conf1 = dict(yy='12')
3356
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3357
conf_stack = config.Stack([conf1, conf2])
3358
self.assertEquals('baz', conf_stack.get('foo'))
3360
def test_get_for_empty_section_callable(self):
3361
conf_stack = config.Stack([lambda : []])
3362
self.assertEquals(None, conf_stack.get('foo'))
3364
def test_get_for_broken_callable(self):
3365
# Trying to use and invalid callable raises an exception on first use
3366
conf_stack = config.Stack([lambda : object()])
3367
self.assertRaises(TypeError, conf_stack.get, 'foo')
3370
class TestStackWithTransport(tests.TestCaseWithTransport):
3372
scenarios = [(key, {'get_stack': builder}) for key, builder
3373
in config.test_stack_builder_registry.iteritems()]
3376
class TestConcreteStacks(TestStackWithTransport):
3378
def test_build_stack(self):
3379
# Just a smoke test to help debug builders
3380
stack = self.get_stack(self)
3383
class TestStackGet(TestStackWithTransport):
3386
super(TestStackGet, self).setUp()
3387
self.conf = self.get_stack(self)
3389
def test_get_for_empty_stack(self):
3390
self.assertEquals(None, self.conf.get('foo'))
3392
def test_get_hook(self):
3393
self.conf.set('foo', 'bar')
3397
config.ConfigHooks.install_named_hook('get', hook, None)
3398
self.assertLength(0, calls)
3399
value = self.conf.get('foo')
3400
self.assertEquals('bar', value)
3401
self.assertLength(1, calls)
3402
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3405
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3408
super(TestStackGetWithConverter, self).setUp()
3409
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3410
self.registry = config.option_registry
3411
# We just want a simple stack with a simple store so we can inject
3412
# whatever content the tests need without caring about what section
3413
# names are valid for a given store/stack.
3414
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3415
self.conf = config.Stack([store.get_sections], store)
3417
def register_bool_option(self, name, default=None, default_from_env=None):
3418
b = config.Option(name, help='A boolean.',
3419
default=default, default_from_env=default_from_env,
3420
from_unicode=config.bool_from_store)
3421
self.registry.register(b)
3423
def test_get_default_bool_None(self):
3424
self.register_bool_option('foo')
3425
self.assertEquals(None, self.conf.get('foo'))
3427
def test_get_default_bool_True(self):
3428
self.register_bool_option('foo', u'True')
3429
self.assertEquals(True, self.conf.get('foo'))
3431
def test_get_default_bool_False(self):
3432
self.register_bool_option('foo', False)
3433
self.assertEquals(False, self.conf.get('foo'))
3435
def test_get_default_bool_False_as_string(self):
3436
self.register_bool_option('foo', u'False')
3437
self.assertEquals(False, self.conf.get('foo'))
3439
def test_get_default_bool_from_env_converted(self):
3440
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3441
self.overrideEnv('FOO', 'False')
3442
self.assertEquals(False, self.conf.get('foo'))
3444
def test_get_default_bool_when_conversion_fails(self):
3445
self.register_bool_option('foo', default='True')
3446
self.conf.store._load_from_string('foo=invalid boolean')
3447
self.assertEquals(True, self.conf.get('foo'))
3449
def register_integer_option(self, name,
3450
default=None, default_from_env=None):
3451
i = config.Option(name, help='An integer.',
3452
default=default, default_from_env=default_from_env,
3453
from_unicode=config.int_from_store)
3454
self.registry.register(i)
3456
def test_get_default_integer_None(self):
3457
self.register_integer_option('foo')
3458
self.assertEquals(None, self.conf.get('foo'))
3460
def test_get_default_integer(self):
3461
self.register_integer_option('foo', 42)
3462
self.assertEquals(42, self.conf.get('foo'))
3464
def test_get_default_integer_as_string(self):
3465
self.register_integer_option('foo', u'42')
3466
self.assertEquals(42, self.conf.get('foo'))
3468
def test_get_default_integer_from_env(self):
3469
self.register_integer_option('foo', default_from_env=['FOO'])
3470
self.overrideEnv('FOO', '18')
3471
self.assertEquals(18, self.conf.get('foo'))
3473
def test_get_default_integer_when_conversion_fails(self):
3474
self.register_integer_option('foo', default='12')
3475
self.conf.store._load_from_string('foo=invalid integer')
3476
self.assertEquals(12, self.conf.get('foo'))
3478
def register_list_option(self, name, default=None, default_from_env=None):
3479
l = config.Option(name, help='A list.',
3480
default=default, default_from_env=default_from_env,
3481
from_unicode=config.list_from_store)
3482
self.registry.register(l)
3484
def test_get_default_list_None(self):
3485
self.register_list_option('foo')
3486
self.assertEquals(None, self.conf.get('foo'))
3488
def test_get_default_list_empty(self):
3489
self.register_list_option('foo', '')
3490
self.assertEquals([], self.conf.get('foo'))
3492
def test_get_default_list_from_env(self):
3493
self.register_list_option('foo', default_from_env=['FOO'])
3494
self.overrideEnv('FOO', '')
3495
self.assertEquals([], self.conf.get('foo'))
3497
def test_get_with_list_converter_no_item(self):
3498
self.register_list_option('foo', None)
3499
self.conf.store._load_from_string('foo=,')
3500
self.assertEquals([], self.conf.get('foo'))
3502
def test_get_with_list_converter_many_items(self):
3503
self.register_list_option('foo', None)
3504
self.conf.store._load_from_string('foo=m,o,r,e')
3505
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3507
def test_get_with_list_converter_embedded_spaces_many_items(self):
3508
self.register_list_option('foo', None)
3509
self.conf.store._load_from_string('foo=" bar", "baz "')
3510
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3512
def test_get_with_list_converter_stripped_spaces_many_items(self):
3513
self.register_list_option('foo', None)
3514
self.conf.store._load_from_string('foo= bar , baz ')
3515
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3518
class TestIterOptionRefs(tests.TestCase):
3519
"""iter_option_refs is a bit unusual, document some cases."""
3521
def assertRefs(self, expected, string):
3522
self.assertEquals(expected, list(config.iter_option_refs(string)))
3524
def test_empty(self):
3525
self.assertRefs([(False, '')], '')
3527
def test_no_refs(self):
3528
self.assertRefs([(False, 'foo bar')], 'foo bar')
3530
def test_single_ref(self):
3531
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3533
def test_broken_ref(self):
3534
self.assertRefs([(False, '{foo')], '{foo')
3536
def test_embedded_ref(self):
3537
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3540
def test_two_refs(self):
3541
self.assertRefs([(False, ''), (True, '{foo}'),
3542
(False, ''), (True, '{bar}'),
3546
def test_newline_in_refs_are_not_matched(self):
3547
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3550
class TestStackExpandOptions(tests.TestCaseWithTransport):
3553
super(TestStackExpandOptions, self).setUp()
3554
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3555
self.registry = config.option_registry
3556
self.conf = build_branch_stack(self)
3558
def assertExpansion(self, expected, string, env=None):
3559
self.assertEquals(expected, self.conf.expand_options(string, env))
3561
def test_no_expansion(self):
3562
self.assertExpansion('foo', 'foo')
3564
def test_expand_default_value(self):
3565
self.conf.store._load_from_string('bar=baz')
3566
self.registry.register(config.Option('foo', default=u'{bar}'))
3567
self.assertEquals('baz', self.conf.get('foo', expand=True))
3569
def test_expand_default_from_env(self):
3570
self.conf.store._load_from_string('bar=baz')
3571
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3572
self.overrideEnv('FOO', '{bar}')
3573
self.assertEquals('baz', self.conf.get('foo', expand=True))
3575
def test_expand_default_on_failed_conversion(self):
3576
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3577
self.registry.register(
3578
config.Option('foo', default=u'{bar}',
3579
from_unicode=config.int_from_store))
3580
self.assertEquals(42, self.conf.get('foo', expand=True))
3582
def test_env_adding_options(self):
3583
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3585
def test_env_overriding_options(self):
3586
self.conf.store._load_from_string('foo=baz')
3587
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3589
def test_simple_ref(self):
3590
self.conf.store._load_from_string('foo=xxx')
3591
self.assertExpansion('xxx', '{foo}')
3593
def test_unknown_ref(self):
3594
self.assertRaises(errors.ExpandingUnknownOption,
3595
self.conf.expand_options, '{foo}')
3597
def test_indirect_ref(self):
3598
self.conf.store._load_from_string('''
3602
self.assertExpansion('xxx', '{bar}')
3604
def test_embedded_ref(self):
3605
self.conf.store._load_from_string('''
3609
self.assertExpansion('xxx', '{{bar}}')
3611
def test_simple_loop(self):
3612
self.conf.store._load_from_string('foo={foo}')
3613
self.assertRaises(errors.OptionExpansionLoop,
3614
self.conf.expand_options, '{foo}')
3616
def test_indirect_loop(self):
3617
self.conf.store._load_from_string('''
3621
e = self.assertRaises(errors.OptionExpansionLoop,
3622
self.conf.expand_options, '{foo}')
3623
self.assertEquals('foo->bar->baz', e.refs)
3624
self.assertEquals('{foo}', e.string)
3626
def test_list(self):
3627
self.conf.store._load_from_string('''
3631
list={foo},{bar},{baz}
3633
self.registry.register(
3634
config.Option('list', from_unicode=config.list_from_store))
3635
self.assertEquals(['start', 'middle', 'end'],
3636
self.conf.get('list', expand=True))
3638
def test_cascading_list(self):
3639
self.conf.store._load_from_string('''
3645
self.registry.register(
3646
config.Option('list', from_unicode=config.list_from_store))
3647
self.assertEquals(['start', 'middle', 'end'],
3648
self.conf.get('list', expand=True))
3650
def test_pathologically_hidden_list(self):
3651
self.conf.store._load_from_string('''
3657
hidden={start}{middle}{end}
3659
# What matters is what the registration says, the conversion happens
3660
# only after all expansions have been performed
3661
self.registry.register(
3662
config.Option('hidden', from_unicode=config.list_from_store))
3663
self.assertEquals(['bin', 'go'],
3664
self.conf.get('hidden', expand=True))
3667
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3670
super(TestStackCrossSectionsExpand, self).setUp()
3672
def get_config(self, location, string):
3675
# Since we don't save the config we won't strictly require to inherit
3676
# from TestCaseInTempDir, but an error occurs so quickly...
3677
c = config.LocationStack(location)
3678
c.store._load_from_string(string)
3681
def test_dont_cross_unrelated_section(self):
3682
c = self.get_config('/another/branch/path','''
3687
[/another/branch/path]
3690
self.assertRaises(errors.ExpandingUnknownOption,
3691
c.get, 'bar', expand=True)
3693
def test_cross_related_sections(self):
3694
c = self.get_config('/project/branch/path','''
3698
[/project/branch/path]
3701
self.assertEquals('quux', c.get('bar', expand=True))
3704
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3706
def test_cross_global_locations(self):
3707
l_store = config.LocationStore()
3708
l_store._load_from_string('''
3714
g_store = config.GlobalStore()
3715
g_store._load_from_string('''
3721
stack = config.LocationStack('/branch')
3722
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3723
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3726
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3728
def test_expand_locals_empty(self):
3729
l_store = config.LocationStore()
3730
l_store._load_from_string('''
3731
[/home/user/project]
3736
stack = config.LocationStack('/home/user/project/')
3737
self.assertEquals('', stack.get('base', expand=True))
3738
self.assertEquals('', stack.get('rel', expand=True))
3740
def test_expand_basename_locally(self):
3741
l_store = config.LocationStore()
3742
l_store._load_from_string('''
3743
[/home/user/project]
3747
stack = config.LocationStack('/home/user/project/branch')
3748
self.assertEquals('branch', stack.get('bfoo', expand=True))
3750
def test_expand_basename_locally_longer_path(self):
3751
l_store = config.LocationStore()
3752
l_store._load_from_string('''
3757
stack = config.LocationStack('/home/user/project/dir/branch')
3758
self.assertEquals('branch', stack.get('bfoo', expand=True))
3760
def test_expand_relpath_locally(self):
3761
l_store = config.LocationStore()
3762
l_store._load_from_string('''
3763
[/home/user/project]
3764
lfoo = loc-foo/{relpath}
3767
stack = config.LocationStack('/home/user/project/branch')
3768
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3770
def test_expand_relpath_unknonw_in_global(self):
3771
g_store = config.GlobalStore()
3772
g_store._load_from_string('''
3777
stack = config.LocationStack('/home/user/project/branch')
3778
self.assertRaises(errors.ExpandingUnknownOption,
3779
stack.get, 'gfoo', expand=True)
3781
def test_expand_local_option_locally(self):
3782
l_store = config.LocationStore()
3783
l_store._load_from_string('''
3784
[/home/user/project]
3785
lfoo = loc-foo/{relpath}
3789
g_store = config.GlobalStore()
3790
g_store._load_from_string('''
3796
stack = config.LocationStack('/home/user/project/branch')
3797
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3798
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3800
def test_locals_dont_leak(self):
3801
"""Make sure we chose the right local in presence of several sections.
3803
l_store = config.LocationStore()
3804
l_store._load_from_string('''
3806
lfoo = loc-foo/{relpath}
3807
[/home/user/project]
3808
lfoo = loc-foo/{relpath}
3811
stack = config.LocationStack('/home/user/project/branch')
3812
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3813
stack = config.LocationStack('/home/user/bar/baz')
3814
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3818
class TestStackSet(TestStackWithTransport):
3820
def test_simple_set(self):
3821
conf = self.get_stack(self)
3822
self.assertEquals(None, conf.get('foo'))
3823
conf.set('foo', 'baz')
3824
# Did we get it back ?
3825
self.assertEquals('baz', conf.get('foo'))
3827
def test_set_creates_a_new_section(self):
3828
conf = self.get_stack(self)
3829
conf.set('foo', 'baz')
3830
self.assertEquals, 'baz', conf.get('foo')
3832
def test_set_hook(self):
3836
config.ConfigHooks.install_named_hook('set', hook, None)
3837
self.assertLength(0, calls)
3838
conf = self.get_stack(self)
3839
conf.set('foo', 'bar')
3840
self.assertLength(1, calls)
3841
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3844
class TestStackRemove(TestStackWithTransport):
3846
def test_remove_existing(self):
3847
conf = self.get_stack(self)
3848
conf.set('foo', 'bar')
3849
self.assertEquals('bar', conf.get('foo'))
3851
# Did we get it back ?
3852
self.assertEquals(None, conf.get('foo'))
3854
def test_remove_unknown(self):
3855
conf = self.get_stack(self)
3856
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3858
def test_remove_hook(self):
3862
config.ConfigHooks.install_named_hook('remove', hook, None)
3863
self.assertLength(0, calls)
3864
conf = self.get_stack(self)
3865
conf.set('foo', 'bar')
3867
self.assertLength(1, calls)
3868
self.assertEquals((conf, 'foo'), calls[0])
3871
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3874
super(TestConfigGetOptions, self).setUp()
3875
create_configs(self)
3877
def test_no_variable(self):
3878
# Using branch should query branch, locations and bazaar
3879
self.assertOptions([], self.branch_config)
3881
def test_option_in_bazaar(self):
3882
self.bazaar_config.set_user_option('file', 'bazaar')
3883
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3886
def test_option_in_locations(self):
3887
self.locations_config.set_user_option('file', 'locations')
3889
[('file', 'locations', self.tree.basedir, 'locations')],
3890
self.locations_config)
3892
def test_option_in_branch(self):
3893
self.branch_config.set_user_option('file', 'branch')
3894
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3897
def test_option_in_bazaar_and_branch(self):
3898
self.bazaar_config.set_user_option('file', 'bazaar')
3899
self.branch_config.set_user_option('file', 'branch')
3900
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3901
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3904
def test_option_in_branch_and_locations(self):
3905
# Hmm, locations override branch :-/
3906
self.locations_config.set_user_option('file', 'locations')
3907
self.branch_config.set_user_option('file', 'branch')
3909
[('file', 'locations', self.tree.basedir, 'locations'),
3910
('file', 'branch', 'DEFAULT', 'branch'),],
3913
def test_option_in_bazaar_locations_and_branch(self):
3914
self.bazaar_config.set_user_option('file', 'bazaar')
3915
self.locations_config.set_user_option('file', 'locations')
3916
self.branch_config.set_user_option('file', 'branch')
3918
[('file', 'locations', self.tree.basedir, 'locations'),
3919
('file', 'branch', 'DEFAULT', 'branch'),
3920
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3924
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3927
super(TestConfigRemoveOption, self).setUp()
3928
create_configs_with_file_option(self)
3930
def test_remove_in_locations(self):
3931
self.locations_config.remove_user_option('file', self.tree.basedir)
3933
[('file', 'branch', 'DEFAULT', 'branch'),
3934
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3937
def test_remove_in_branch(self):
3938
self.branch_config.remove_user_option('file')
3940
[('file', 'locations', self.tree.basedir, 'locations'),
3941
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3944
def test_remove_in_bazaar(self):
3945
self.bazaar_config.remove_user_option('file')
3947
[('file', 'locations', self.tree.basedir, 'locations'),
3948
('file', 'branch', 'DEFAULT', 'branch'),],
3952
class TestConfigGetSections(tests.TestCaseWithTransport):
3955
super(TestConfigGetSections, self).setUp()
3956
create_configs(self)
3958
def assertSectionNames(self, expected, conf, name=None):
3959
"""Check which sections are returned for a given config.
3961
If fallback configurations exist their sections can be included.
3963
:param expected: A list of section names.
3965
:param conf: The configuration that will be queried.
3967
:param name: An optional section name that will be passed to
3970
sections = list(conf._get_sections(name))
3971
self.assertLength(len(expected), sections)
3972
self.assertEqual(expected, [name for name, _, _ in sections])
3974
def test_bazaar_default_section(self):
3975
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3977
def test_locations_default_section(self):
3978
# No sections are defined in an empty file
3979
self.assertSectionNames([], self.locations_config)
3981
def test_locations_named_section(self):
3982
self.locations_config.set_user_option('file', 'locations')
3983
self.assertSectionNames([self.tree.basedir], self.locations_config)
3985
def test_locations_matching_sections(self):
3986
loc_config = self.locations_config
3987
loc_config.set_user_option('file', 'locations')
3988
# We need to cheat a bit here to create an option in sections above and
3989
# below the 'location' one.
3990
parser = loc_config._get_parser()
3991
# locations.cong deals with '/' ignoring native os.sep
3992
location_names = self.tree.basedir.split('/')
3993
parent = '/'.join(location_names[:-1])
3994
child = '/'.join(location_names + ['child'])
3996
parser[parent]['file'] = 'parent'
3998
parser[child]['file'] = 'child'
3999
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4001
def test_branch_data_default_section(self):
4002
self.assertSectionNames([None],
4003
self.branch_config._get_branch_data_config())
4005
def test_branch_default_sections(self):
4006
# No sections are defined in an empty locations file
4007
self.assertSectionNames([None, 'DEFAULT'],
4009
# Unless we define an option
4010
self.branch_config._get_location_config().set_user_option(
4011
'file', 'locations')
4012
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4015
def test_bazaar_named_section(self):
4016
# We need to cheat as the API doesn't give direct access to sections
4017
# other than DEFAULT.
4018
self.bazaar_config.set_alias('bazaar', 'bzr')
4019
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1474
4022
class TestAuthenticationConfigFile(tests.TestCase):
1475
4023
"""Test the authentication.conf file matching"""