1493
1524
self.get_branch_config('http://www.example.com',
1494
1525
global_config=sample_ignore_signatures)
1495
1526
self.assertEqual(config.CHECK_ALWAYS,
1496
self.my_config.signature_checking())
1527
self.applyDeprecated(deprecated_in((2, 5, 0)),
1528
self.my_config.signature_checking))
1497
1529
self.assertEqual(config.SIGN_NEVER,
1498
self.my_config.signing_policy())
1530
self.applyDeprecated(deprecated_in((2, 5, 0)),
1531
self.my_config.signing_policy))
1500
1533
def test_signatures_never(self):
1501
1534
self.get_branch_config('/a/c')
1502
1535
self.assertEqual(config.CHECK_NEVER,
1503
self.my_config.signature_checking())
1536
self.applyDeprecated(deprecated_in((2, 5, 0)),
1537
self.my_config.signature_checking))
1505
1539
def test_signatures_when_available(self):
1506
1540
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1507
1541
self.assertEqual(config.CHECK_IF_POSSIBLE,
1508
self.my_config.signature_checking())
1542
self.applyDeprecated(deprecated_in((2, 5, 0)),
1543
self.my_config.signature_checking))
1510
1545
def test_signatures_always(self):
1511
1546
self.get_branch_config('/b')
1512
1547
self.assertEqual(config.CHECK_ALWAYS,
1513
self.my_config.signature_checking())
1548
self.applyDeprecated(deprecated_in((2, 5, 0)),
1549
self.my_config.signature_checking))
1515
1551
def test_gpg_signing_command(self):
1516
1552
self.get_branch_config('/b')
1517
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1553
self.assertEqual("gnome-gpg",
1554
self.applyDeprecated(deprecated_in((2, 5, 0)),
1555
self.my_config.gpg_signing_command))
1519
1557
def test_gpg_signing_command_missing(self):
1520
1558
self.get_branch_config('/a')
1521
self.assertEqual("false", self.my_config.gpg_signing_command())
1559
self.assertEqual("false",
1560
self.applyDeprecated(deprecated_in((2, 5, 0)),
1561
self.my_config.gpg_signing_command))
1563
def test_gpg_signing_key(self):
1564
self.get_branch_config('/b')
1565
self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
1566
self.my_config.gpg_signing_key))
1568
def test_gpg_signing_key_default(self):
1569
self.get_branch_config('/a')
1570
self.assertEqual("erik@bagfors.nu",
1571
self.applyDeprecated(deprecated_in((2, 5, 0)),
1572
self.my_config.gpg_signing_key))
1523
1574
def test_get_user_option_global(self):
1524
1575
self.get_branch_config('/a')
2199
2238
opt = config.Option('foo', default='bar')
2200
2239
self.assertEquals('bar', opt.get_default())
2241
def test_callable_default_value(self):
2242
def bar_as_unicode():
2244
opt = config.Option('foo', default=bar_as_unicode)
2245
self.assertEquals('bar', opt.get_default())
2247
def test_default_value_from_env(self):
2248
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2249
self.overrideEnv('FOO', 'quux')
2250
# Env variable provides a default taking over the option one
2251
self.assertEquals('quux', opt.get_default())
2253
def test_first_default_value_from_env_wins(self):
2254
opt = config.Option('foo', default='bar',
2255
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2256
self.overrideEnv('FOO', 'foo')
2257
self.overrideEnv('BAZ', 'baz')
2258
# The first env var set wins
2259
self.assertEquals('foo', opt.get_default())
2261
def test_not_supported_list_default_value(self):
2262
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2264
def test_not_supported_object_default_value(self):
2265
self.assertRaises(AssertionError, config.Option, 'foo',
2268
def test_not_supported_callable_default_value_not_unicode(self):
2269
def bar_not_unicode():
2271
opt = config.Option('foo', default=bar_not_unicode)
2272
self.assertRaises(AssertionError, opt.get_default)
2274
def test_get_help_topic(self):
2275
opt = config.Option('foo')
2276
self.assertEquals('foo', opt.get_help_topic())
2279
class TestOptionConverterMixin(object):
2281
def assertConverted(self, expected, opt, value):
2282
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2284
def assertWarns(self, opt, value):
2287
warnings.append(args[0] % args[1:])
2288
self.overrideAttr(trace, 'warning', warning)
2289
self.assertEquals(None, opt.convert_from_unicode(None, value))
2290
self.assertLength(1, warnings)
2292
'Value "%s" is not valid for "%s"' % (value, opt.name),
2295
def assertErrors(self, opt, value):
2296
self.assertRaises(errors.ConfigOptionValueError,
2297
opt.convert_from_unicode, None, value)
2299
def assertConvertInvalid(self, opt, invalid_value):
2301
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2302
opt.invalid = 'warning'
2303
self.assertWarns(opt, invalid_value)
2304
opt.invalid = 'error'
2305
self.assertErrors(opt, invalid_value)
2308
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2310
def get_option(self):
2311
return config.Option('foo', help='A boolean.',
2312
from_unicode=config.bool_from_store)
2314
def test_convert_invalid(self):
2315
opt = self.get_option()
2316
# A string that is not recognized as a boolean
2317
self.assertConvertInvalid(opt, u'invalid-boolean')
2318
# A list of strings is never recognized as a boolean
2319
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2321
def test_convert_valid(self):
2322
opt = self.get_option()
2323
self.assertConverted(True, opt, u'True')
2324
self.assertConverted(True, opt, u'1')
2325
self.assertConverted(False, opt, u'False')
2328
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2330
def get_option(self):
2331
return config.Option('foo', help='An integer.',
2332
from_unicode=config.int_from_store)
2334
def test_convert_invalid(self):
2335
opt = self.get_option()
2336
# A string that is not recognized as an integer
2337
self.assertConvertInvalid(opt, u'forty-two')
2338
# A list of strings is never recognized as an integer
2339
self.assertConvertInvalid(opt, [u'a', u'list'])
2341
def test_convert_valid(self):
2342
opt = self.get_option()
2343
self.assertConverted(16, opt, u'16')
2346
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2348
def get_option(self):
2349
return config.Option('foo', help='An integer in SI units.',
2350
from_unicode=config.int_SI_from_store)
2352
def test_convert_invalid(self):
2353
opt = self.get_option()
2354
self.assertConvertInvalid(opt, u'not-a-unit')
2355
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2356
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2357
self.assertConvertInvalid(opt, u'1GG')
2358
self.assertConvertInvalid(opt, u'1Mbb')
2359
self.assertConvertInvalid(opt, u'1MM')
2361
def test_convert_valid(self):
2362
opt = self.get_option()
2363
self.assertConverted(int(5e3), opt, u'5kb')
2364
self.assertConverted(int(5e6), opt, u'5M')
2365
self.assertConverted(int(5e6), opt, u'5MB')
2366
self.assertConverted(int(5e9), opt, u'5g')
2367
self.assertConverted(int(5e9), opt, u'5gB')
2368
self.assertConverted(100, opt, u'100')
2371
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2373
def get_option(self):
2374
return config.ListOption('foo', help='A list.')
2376
def test_convert_invalid(self):
2377
opt = self.get_option()
2378
# We don't even try to convert a list into a list, we only expect
2380
self.assertConvertInvalid(opt, [1])
2381
# No string is invalid as all forms can be converted to a list
2383
def test_convert_valid(self):
2384
opt = self.get_option()
2385
# An empty string is an empty list
2386
self.assertConverted([], opt, '') # Using a bare str() just in case
2387
self.assertConverted([], opt, u'')
2389
self.assertConverted([u'True'], opt, u'True')
2391
self.assertConverted([u'42'], opt, u'42')
2393
self.assertConverted([u'bar'], opt, u'bar')
2396
class TestRegistryOption(tests.TestCase, TestOptionConverterMixin):
2398
def get_option(self, registry):
2399
return config.RegistryOption('foo', registry,
2400
help='A registry option.')
2402
def test_convert_invalid(self):
2403
registry = _mod_registry.Registry()
2404
opt = self.get_option(registry)
2405
self.assertConvertInvalid(opt, [1])
2406
self.assertConvertInvalid(opt, u"notregistered")
2408
def test_convert_valid(self):
2409
registry = _mod_registry.Registry()
2410
registry.register("someval", 1234)
2411
opt = self.get_option(registry)
2412
# Using a bare str() just in case
2413
self.assertConverted(1234, opt, "someval")
2414
self.assertConverted(1234, opt, u'someval')
2415
self.assertConverted(None, opt, None)
2417
def test_help(self):
2418
registry = _mod_registry.Registry()
2419
registry.register("someval", 1234, help="some option")
2420
registry.register("dunno", 1234, help="some other option")
2421
opt = self.get_option(registry)
2423
'A registry option.\n'
2425
'The following values are supported:\n'
2426
' dunno - some other option\n'
2427
' someval - some option\n',
2430
def test_get_help_text(self):
2431
registry = _mod_registry.Registry()
2432
registry.register("someval", 1234, help="some option")
2433
registry.register("dunno", 1234, help="some other option")
2434
opt = self.get_option(registry)
2436
'A registry option.\n'
2438
'The following values are supported:\n'
2439
' dunno - some other option\n'
2440
' someval - some option\n',
2441
opt.get_help_text())
2203
2444
class TestOptionRegistry(tests.TestCase):
2205
2446
def setUp(self):
2206
2447
super(TestOptionRegistry, self).setUp()
2207
2448
# Always start with an empty registry
2208
self.overrideAttr(config, 'option_registry', registry.Registry())
2449
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2209
2450
self.registry = config.option_registry
2211
2452
def test_register(self):
2212
2453
opt = config.Option('foo')
2213
self.registry.register('foo', opt)
2454
self.registry.register(opt)
2214
2455
self.assertIs(opt, self.registry.get('foo'))
2216
lazy_option = config.Option('lazy_foo')
2218
def test_register_lazy(self):
2219
self.registry.register_lazy('foo', self.__module__,
2220
'TestOptionRegistry.lazy_option')
2221
self.assertIs(self.lazy_option, self.registry.get('foo'))
2223
2457
def test_registered_help(self):
2224
opt = config.Option('foo')
2225
self.registry.register('foo', opt, help='A simple option')
2458
opt = config.Option('foo', help='A simple option')
2459
self.registry.register(opt)
2226
2460
self.assertEquals('A simple option', self.registry.get_help('foo'))
2462
lazy_option = config.Option('lazy_foo', help='Lazy help')
2464
def test_register_lazy(self):
2465
self.registry.register_lazy('lazy_foo', self.__module__,
2466
'TestOptionRegistry.lazy_option')
2467
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2469
def test_registered_lazy_help(self):
2470
self.registry.register_lazy('lazy_foo', self.__module__,
2471
'TestOptionRegistry.lazy_option')
2472
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2229
2475
class TestRegisteredOptions(tests.TestCase):
2230
2476
"""All registered options should verify some constraints."""
2321
2570
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2573
class TestCommandLineStore(tests.TestCase):
2576
super(TestCommandLineStore, self).setUp()
2577
self.store = config.CommandLineStore()
2578
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2580
def get_section(self):
2581
"""Get the unique section for the command line overrides."""
2582
sections = list(self.store.get_sections())
2583
self.assertLength(1, sections)
2584
store, section = sections[0]
2585
self.assertEquals(self.store, store)
2588
def test_no_override(self):
2589
self.store._from_cmdline([])
2590
section = self.get_section()
2591
self.assertLength(0, list(section.iter_option_names()))
2593
def test_simple_override(self):
2594
self.store._from_cmdline(['a=b'])
2595
section = self.get_section()
2596
self.assertEqual('b', section.get('a'))
2598
def test_list_override(self):
2599
opt = config.ListOption('l')
2600
config.option_registry.register(opt)
2601
self.store._from_cmdline(['l=1,2,3'])
2602
val = self.get_section().get('l')
2603
self.assertEqual('1,2,3', val)
2604
# Reminder: lists should be registered as such explicitely, otherwise
2605
# the conversion needs to be done afterwards.
2606
self.assertEqual(['1', '2', '3'],
2607
opt.convert_from_unicode(self.store, val))
2609
def test_multiple_overrides(self):
2610
self.store._from_cmdline(['a=b', 'x=y'])
2611
section = self.get_section()
2612
self.assertEquals('b', section.get('a'))
2613
self.assertEquals('y', section.get('x'))
2615
def test_wrong_syntax(self):
2616
self.assertRaises(errors.BzrCommandError,
2617
self.store._from_cmdline, ['a=b', 'c'])
2619
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2621
scenarios = [(key, {'get_store': builder}) for key, builder
2622
in config.test_store_builder_registry.iteritems()] + [
2623
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2626
store = self.get_store(self)
2627
if type(store) == config.TransportIniFileStore:
2628
raise tests.TestNotApplicable(
2629
"%s is not a concrete Store implementation"
2630
" so it doesn't need an id" % (store.__class__.__name__,))
2631
self.assertIsNot(None, store.id)
2324
2634
class TestStore(tests.TestCaseWithTransport):
2326
def assertSectionContent(self, expected, section):
2636
def assertSectionContent(self, expected, (store, section)):
2327
2637
"""Assert that some options have the proper values in a section."""
2328
2638
expected_name, expected_options = expected
2329
2639
self.assertEquals(expected_name, section.id)
2371
2678
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2681
class TestStoreQuoting(TestStore):
2683
scenarios = [(key, {'get_store': builder}) for key, builder
2684
in config.test_store_builder_registry.iteritems()]
2687
super(TestStoreQuoting, self).setUp()
2688
self.store = self.get_store(self)
2689
# We need a loaded store but any content will do
2690
self.store._load_from_string('')
2692
def assertIdempotent(self, s):
2693
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2695
What matters here is that option values, as they appear in a store, can
2696
be safely round-tripped out of the store and back.
2698
:param s: A string, quoted if required.
2700
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2701
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2703
def test_empty_string(self):
2704
if isinstance(self.store, config.IniFileStore):
2705
# configobj._quote doesn't handle empty values
2706
self.assertRaises(AssertionError,
2707
self.assertIdempotent, '')
2709
self.assertIdempotent('')
2710
# But quoted empty strings are ok
2711
self.assertIdempotent('""')
2713
def test_embedded_spaces(self):
2714
self.assertIdempotent('" a b c "')
2716
def test_embedded_commas(self):
2717
self.assertIdempotent('" a , b c "')
2719
def test_simple_comma(self):
2720
if isinstance(self.store, config.IniFileStore):
2721
# configobj requires that lists are special-cased
2722
self.assertRaises(AssertionError,
2723
self.assertIdempotent, ',')
2725
self.assertIdempotent(',')
2726
# When a single comma is required, quoting is also required
2727
self.assertIdempotent('","')
2729
def test_list(self):
2730
if isinstance(self.store, config.IniFileStore):
2731
# configobj requires that lists are special-cased
2732
self.assertRaises(AssertionError,
2733
self.assertIdempotent, 'a,b')
2735
self.assertIdempotent('a,b')
2738
class TestDictFromStore(tests.TestCase):
2740
def test_unquote_not_string(self):
2741
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2742
value = conf.get('a_section')
2743
# Urgh, despite 'conf' asking for the no-name section, we get the
2744
# content of another section as a dict o_O
2745
self.assertEquals({'a': '1'}, value)
2746
unquoted = conf.store.unquote(value)
2747
# Which cannot be unquoted but shouldn't crash either (the use cases
2748
# are getting the value or displaying it. In the later case, '%s' will
2750
self.assertEquals({'a': '1'}, unquoted)
2751
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2374
2754
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2375
"""Simulate loading a config store without content of various encodings.
2755
"""Simulate loading a config store with content of various encodings.
2377
2757
All files produced by bzr are in utf8 content.
2556
2980
config.ConfigHooks.install_named_hook('save', hook, None)
2557
2981
self.assertLength(0, calls)
2558
2982
store = self.get_store(self)
2983
# FIXME: There should be a better way than relying on the test
2984
# parametrization to identify branch.conf -- vila 2011-0526
2985
if self.store_id in ('branch', 'remote_branch'):
2986
# branch stores requires write locked branches
2987
self.addCleanup(store.branch.lock_write().unlock)
2559
2988
section = store.get_mutable_section('baz')
2560
2989
section.set('foo', 'bar')
2562
2991
self.assertLength(1, calls)
2563
2992
self.assertEquals((store,), calls[0])
2566
class TestIniFileStore(TestStore):
2994
def test_set_mark_dirty(self):
2995
stack = config.MemoryStack('')
2996
self.assertLength(0, stack.store.dirty_sections)
2997
stack.set('foo', 'baz')
2998
self.assertLength(1, stack.store.dirty_sections)
2999
self.assertTrue(stack.store._need_saving())
3001
def test_remove_mark_dirty(self):
3002
stack = config.MemoryStack('foo=bar')
3003
self.assertLength(0, stack.store.dirty_sections)
3005
self.assertLength(1, stack.store.dirty_sections)
3006
self.assertTrue(stack.store._need_saving())
3009
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3010
"""Tests that config changes are kept in memory and saved on-demand."""
3013
super(TestStoreSaveChanges, self).setUp()
3014
self.transport = self.get_transport()
3015
# Most of the tests involve two stores pointing to the same persistent
3016
# storage to observe the effects of concurrent changes
3017
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3018
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3021
self.warnings.append(args[0] % args[1:])
3022
self.overrideAttr(trace, 'warning', warning)
3024
def has_store(self, store):
3025
store_basename = urlutils.relative_url(self.transport.external_url(),
3026
store.external_url())
3027
return self.transport.has(store_basename)
3029
def get_stack(self, store):
3030
# Any stack will do as long as it uses the right store, just a single
3031
# no-name section is enough
3032
return config.Stack([store.get_sections], store)
3034
def test_no_changes_no_save(self):
3035
s = self.get_stack(self.st1)
3036
s.store.save_changes()
3037
self.assertEquals(False, self.has_store(self.st1))
3039
def test_unrelated_concurrent_update(self):
3040
s1 = self.get_stack(self.st1)
3041
s2 = self.get_stack(self.st2)
3042
s1.set('foo', 'bar')
3043
s2.set('baz', 'quux')
3045
# Changes don't propagate magically
3046
self.assertEquals(None, s1.get('baz'))
3047
s2.store.save_changes()
3048
self.assertEquals('quux', s2.get('baz'))
3049
# Changes are acquired when saving
3050
self.assertEquals('bar', s2.get('foo'))
3051
# Since there is no overlap, no warnings are emitted
3052
self.assertLength(0, self.warnings)
3054
def test_concurrent_update_modified(self):
3055
s1 = self.get_stack(self.st1)
3056
s2 = self.get_stack(self.st2)
3057
s1.set('foo', 'bar')
3058
s2.set('foo', 'baz')
3061
s2.store.save_changes()
3062
self.assertEquals('baz', s2.get('foo'))
3063
# But the user get a warning
3064
self.assertLength(1, self.warnings)
3065
warning = self.warnings[0]
3066
self.assertStartsWith(warning, 'Option foo in section None')
3067
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3068
' The baz value will be saved.')
3070
def test_concurrent_deletion(self):
3071
self.st1._load_from_string('foo=bar')
3073
s1 = self.get_stack(self.st1)
3074
s2 = self.get_stack(self.st2)
3077
s1.store.save_changes()
3079
self.assertLength(0, self.warnings)
3080
s2.store.save_changes()
3082
self.assertLength(1, self.warnings)
3083
warning = self.warnings[0]
3084
self.assertStartsWith(warning, 'Option foo in section None')
3085
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3086
' The <DELETED> value will be saved.')
3089
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3091
def get_store(self):
3092
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3094
def test_get_quoted_string(self):
3095
store = self.get_store()
3096
store._load_from_string('foo= " abc "')
3097
stack = config.Stack([store.get_sections])
3098
self.assertEquals(' abc ', stack.get('foo'))
3100
def test_set_quoted_string(self):
3101
store = self.get_store()
3102
stack = config.Stack([store.get_sections], store)
3103
stack.set('foo', ' a b c ')
3105
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3108
class TestTransportIniFileStore(TestStore):
2568
3110
def test_loading_unknown_file_fails(self):
2569
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
3111
store = config.TransportIniFileStore(self.get_transport(),
2570
3113
self.assertRaises(errors.NoSuchFile, store.load)
2572
3115
def test_invalid_content(self):
2573
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
3116
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2574
3117
self.assertEquals(False, store.is_loaded())
2575
3118
exc = self.assertRaises(
2576
3119
errors.ParseConfigError, store._load_from_string,
2847
3418
self.assertEquals(expected_location, matcher.location)
2850
class TestStackGet(tests.TestCase):
2852
# FIXME: This should be parametrized for all known Stack or dedicated
2853
# paramerized tests created to avoid bloating -- vila 2011-03-31
2855
def test_single_config_get(self):
2856
conf = dict(foo='bar')
2857
conf_stack = config.Stack([conf])
2858
self.assertEquals('bar', conf_stack.get('foo'))
3421
class TestStartingPathMatcher(TestStore):
3424
super(TestStartingPathMatcher, self).setUp()
3425
# Any simple store is good enough
3426
self.store = config.IniFileStore()
3428
def assertSectionIDs(self, expected, location, content):
3429
self.store._load_from_string(content)
3430
matcher = config.StartingPathMatcher(self.store, location)
3431
sections = list(matcher.get_sections())
3432
self.assertLength(len(expected), sections)
3433
self.assertEqual(expected, [section.id for _, section in sections])
3436
def test_empty(self):
3437
self.assertSectionIDs([], self.get_url(), '')
3439
def test_url_vs_local_paths(self):
3440
# The matcher location is an url and the section names are local paths
3441
sections = self.assertSectionIDs(['/foo/bar', '/foo'],
3442
'file:///foo/bar/baz', '''\
3447
def test_local_path_vs_url(self):
3448
# The matcher location is a local path and the section names are urls
3449
sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3450
'/foo/bar/baz', '''\
3456
def test_no_name_section_included_when_present(self):
3457
# Note that other tests will cover the case where the no-name section
3458
# is empty and as such, not included.
3459
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3460
'/foo/bar/baz', '''\
3461
option = defined so the no-name section exists
3465
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3466
[s.locals['relpath'] for _, s in sections])
3468
def test_order_reversed(self):
3469
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3474
def test_unrelated_section_excluded(self):
3475
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3481
def test_glob_included(self):
3482
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3483
'/foo/bar/baz', '''\
3489
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3490
# nothing really is... as far using {relpath} to append it to something
3491
# else, this seems good enough though.
3492
self.assertEquals(['', 'baz', 'bar/baz'],
3493
[s.locals['relpath'] for _, s in sections])
3495
def test_respect_order(self):
3496
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3497
'/foo/bar/baz', '''\
3505
class TestNameMatcher(TestStore):
3508
super(TestNameMatcher, self).setUp()
3509
self.matcher = config.NameMatcher
3510
# Any simple store is good enough
3511
self.get_store = config.test_store_builder_registry.get('configobj')
3513
def get_matching_sections(self, name):
3514
store = self.get_store(self)
3515
store._load_from_string('''
3523
matcher = self.matcher(store, name)
3524
return list(matcher.get_sections())
3526
def test_matching(self):
3527
sections = self.get_matching_sections('foo')
3528
self.assertLength(1, sections)
3529
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3531
def test_not_matching(self):
3532
sections = self.get_matching_sections('baz')
3533
self.assertLength(0, sections)
3536
class TestBaseStackGet(tests.TestCase):
3539
super(TestBaseStackGet, self).setUp()
3540
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3542
def test_get_first_definition(self):
3543
store1 = config.IniFileStore()
3544
store1._load_from_string('foo=bar')
3545
store2 = config.IniFileStore()
3546
store2._load_from_string('foo=baz')
3547
conf = config.Stack([store1.get_sections, store2.get_sections])
3548
self.assertEquals('bar', conf.get('foo'))
2860
3550
def test_get_with_registered_default_value(self):
2861
conf_stack = config.Stack([dict()])
2862
opt = config.Option('foo', default='bar')
2863
self.overrideAttr(config, 'option_registry', registry.Registry())
2864
config.option_registry.register('foo', opt)
3551
config.option_registry.register(config.Option('foo', default='bar'))
3552
conf_stack = config.Stack([])
2865
3553
self.assertEquals('bar', conf_stack.get('foo'))
2867
3555
def test_get_without_registered_default_value(self):
2868
conf_stack = config.Stack([dict()])
2869
opt = config.Option('foo')
2870
self.overrideAttr(config, 'option_registry', registry.Registry())
2871
config.option_registry.register('foo', opt)
3556
config.option_registry.register(config.Option('foo'))
3557
conf_stack = config.Stack([])
2872
3558
self.assertEquals(None, conf_stack.get('foo'))
2874
3560
def test_get_without_default_value_for_not_registered(self):
2875
conf_stack = config.Stack([dict()])
2876
opt = config.Option('foo')
2877
self.overrideAttr(config, 'option_registry', registry.Registry())
3561
conf_stack = config.Stack([])
2878
3562
self.assertEquals(None, conf_stack.get('foo'))
2880
def test_get_first_definition(self):
2881
conf1 = dict(foo='bar')
2882
conf2 = dict(foo='baz')
2883
conf_stack = config.Stack([conf1, conf2])
2884
self.assertEquals('bar', conf_stack.get('foo'))
2886
def test_get_embedded_definition(self):
2887
conf1 = dict(yy='12')
2888
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2889
conf_stack = config.Stack([conf1, conf2])
2890
self.assertEquals('baz', conf_stack.get('foo'))
2892
3564
def test_get_for_empty_section_callable(self):
2893
3565
conf_stack = config.Stack([lambda : []])
2894
3566
self.assertEquals(None, conf_stack.get('foo'))
2896
3568
def test_get_for_broken_callable(self):
2897
3569
# Trying to use and invalid callable raises an exception on first use
2898
conf_stack = config.Stack([lambda : object()])
3570
conf_stack = config.Stack([object])
2899
3571
self.assertRaises(TypeError, conf_stack.get, 'foo')
3574
class TestStackWithSimpleStore(tests.TestCase):
3577
super(TestStackWithSimpleStore, self).setUp()
3578
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3579
self.registry = config.option_registry
3581
def get_conf(self, content=None):
3582
return config.MemoryStack(content)
3584
def test_override_value_from_env(self):
3585
self.registry.register(
3586
config.Option('foo', default='bar', override_from_env=['FOO']))
3587
self.overrideEnv('FOO', 'quux')
3588
# Env variable provides a default taking over the option one
3589
conf = self.get_conf('foo=store')
3590
self.assertEquals('quux', conf.get('foo'))
3592
def test_first_override_value_from_env_wins(self):
3593
self.registry.register(
3594
config.Option('foo', default='bar',
3595
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3596
self.overrideEnv('FOO', 'foo')
3597
self.overrideEnv('BAZ', 'baz')
3598
# The first env var set wins
3599
conf = self.get_conf('foo=store')
3600
self.assertEquals('foo', conf.get('foo'))
3603
class TestMemoryStack(tests.TestCase):
3606
conf = config.MemoryStack('foo=bar')
3607
self.assertEquals('bar', conf.get('foo'))
3610
conf = config.MemoryStack('foo=bar')
3611
conf.set('foo', 'baz')
3612
self.assertEquals('baz', conf.get('foo'))
3614
def test_no_content(self):
3615
conf = config.MemoryStack()
3616
# No content means no loading
3617
self.assertFalse(conf.store.is_loaded())
3618
self.assertRaises(NotImplementedError, conf.get, 'foo')
3619
# But a content can still be provided
3620
conf.store._load_from_string('foo=bar')
3621
self.assertEquals('bar', conf.get('foo'))
3624
class TestStackIterSections(tests.TestCase):
3626
def test_empty_stack(self):
3627
conf = config.Stack([])
3628
sections = list(conf.iter_sections())
3629
self.assertLength(0, sections)
3631
def test_empty_store(self):
3632
store = config.IniFileStore()
3633
store._load_from_string('')
3634
conf = config.Stack([store.get_sections])
3635
sections = list(conf.iter_sections())
3636
self.assertLength(0, sections)
3638
def test_simple_store(self):
3639
store = config.IniFileStore()
3640
store._load_from_string('foo=bar')
3641
conf = config.Stack([store.get_sections])
3642
tuples = list(conf.iter_sections())
3643
self.assertLength(1, tuples)
3644
(found_store, found_section) = tuples[0]
3645
self.assertIs(store, found_store)
3647
def test_two_stores(self):
3648
store1 = config.IniFileStore()
3649
store1._load_from_string('foo=bar')
3650
store2 = config.IniFileStore()
3651
store2._load_from_string('bar=qux')
3652
conf = config.Stack([store1.get_sections, store2.get_sections])
3653
tuples = list(conf.iter_sections())
3654
self.assertLength(2, tuples)
3655
self.assertIs(store1, tuples[0][0])
3656
self.assertIs(store2, tuples[1][0])
2902
3659
class TestStackWithTransport(tests.TestCaseWithTransport):
2904
3661
scenarios = [(key, {'get_stack': builder}) for key, builder
2915
3672
class TestStackGet(TestStackWithTransport):
3675
super(TestStackGet, self).setUp()
3676
self.conf = self.get_stack(self)
2917
3678
def test_get_for_empty_stack(self):
2918
conf = self.get_stack(self)
2919
self.assertEquals(None, conf.get('foo'))
3679
self.assertEquals(None, self.conf.get('foo'))
2921
3681
def test_get_hook(self):
2922
conf = self.get_stack(self)
2923
conf.store._load_from_string('foo=bar')
3682
self.conf.set('foo', 'bar')
2925
3684
def hook(*args):
2926
3685
calls.append(args)
2927
3686
config.ConfigHooks.install_named_hook('get', hook, None)
2928
3687
self.assertLength(0, calls)
2929
value = conf.get('foo')
3688
value = self.conf.get('foo')
2930
3689
self.assertEquals('bar', value)
2931
3690
self.assertLength(1, calls)
2932
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3691
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3694
class TestStackGetWithConverter(tests.TestCase):
3697
super(TestStackGetWithConverter, self).setUp()
3698
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3699
self.registry = config.option_registry
3701
def get_conf(self, content=None):
3702
return config.MemoryStack(content)
3704
def register_bool_option(self, name, default=None, default_from_env=None):
3705
b = config.Option(name, help='A boolean.',
3706
default=default, default_from_env=default_from_env,
3707
from_unicode=config.bool_from_store)
3708
self.registry.register(b)
3710
def test_get_default_bool_None(self):
3711
self.register_bool_option('foo')
3712
conf = self.get_conf('')
3713
self.assertEquals(None, conf.get('foo'))
3715
def test_get_default_bool_True(self):
3716
self.register_bool_option('foo', u'True')
3717
conf = self.get_conf('')
3718
self.assertEquals(True, conf.get('foo'))
3720
def test_get_default_bool_False(self):
3721
self.register_bool_option('foo', False)
3722
conf = self.get_conf('')
3723
self.assertEquals(False, conf.get('foo'))
3725
def test_get_default_bool_False_as_string(self):
3726
self.register_bool_option('foo', u'False')
3727
conf = self.get_conf('')
3728
self.assertEquals(False, conf.get('foo'))
3730
def test_get_default_bool_from_env_converted(self):
3731
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3732
self.overrideEnv('FOO', 'False')
3733
conf = self.get_conf('')
3734
self.assertEquals(False, conf.get('foo'))
3736
def test_get_default_bool_when_conversion_fails(self):
3737
self.register_bool_option('foo', default='True')
3738
conf = self.get_conf('foo=invalid boolean')
3739
self.assertEquals(True, conf.get('foo'))
3741
def register_integer_option(self, name,
3742
default=None, default_from_env=None):
3743
i = config.Option(name, help='An integer.',
3744
default=default, default_from_env=default_from_env,
3745
from_unicode=config.int_from_store)
3746
self.registry.register(i)
3748
def test_get_default_integer_None(self):
3749
self.register_integer_option('foo')
3750
conf = self.get_conf('')
3751
self.assertEquals(None, conf.get('foo'))
3753
def test_get_default_integer(self):
3754
self.register_integer_option('foo', 42)
3755
conf = self.get_conf('')
3756
self.assertEquals(42, conf.get('foo'))
3758
def test_get_default_integer_as_string(self):
3759
self.register_integer_option('foo', u'42')
3760
conf = self.get_conf('')
3761
self.assertEquals(42, conf.get('foo'))
3763
def test_get_default_integer_from_env(self):
3764
self.register_integer_option('foo', default_from_env=['FOO'])
3765
self.overrideEnv('FOO', '18')
3766
conf = self.get_conf('')
3767
self.assertEquals(18, conf.get('foo'))
3769
def test_get_default_integer_when_conversion_fails(self):
3770
self.register_integer_option('foo', default='12')
3771
conf = self.get_conf('foo=invalid integer')
3772
self.assertEquals(12, conf.get('foo'))
3774
def register_list_option(self, name, default=None, default_from_env=None):
3775
l = config.ListOption(name, help='A list.', default=default,
3776
default_from_env=default_from_env)
3777
self.registry.register(l)
3779
def test_get_default_list_None(self):
3780
self.register_list_option('foo')
3781
conf = self.get_conf('')
3782
self.assertEquals(None, conf.get('foo'))
3784
def test_get_default_list_empty(self):
3785
self.register_list_option('foo', '')
3786
conf = self.get_conf('')
3787
self.assertEquals([], conf.get('foo'))
3789
def test_get_default_list_from_env(self):
3790
self.register_list_option('foo', default_from_env=['FOO'])
3791
self.overrideEnv('FOO', '')
3792
conf = self.get_conf('')
3793
self.assertEquals([], conf.get('foo'))
3795
def test_get_with_list_converter_no_item(self):
3796
self.register_list_option('foo', None)
3797
conf = self.get_conf('foo=,')
3798
self.assertEquals([], conf.get('foo'))
3800
def test_get_with_list_converter_many_items(self):
3801
self.register_list_option('foo', None)
3802
conf = self.get_conf('foo=m,o,r,e')
3803
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3805
def test_get_with_list_converter_embedded_spaces_many_items(self):
3806
self.register_list_option('foo', None)
3807
conf = self.get_conf('foo=" bar", "baz "')
3808
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3810
def test_get_with_list_converter_stripped_spaces_many_items(self):
3811
self.register_list_option('foo', None)
3812
conf = self.get_conf('foo= bar , baz ')
3813
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3816
class TestIterOptionRefs(tests.TestCase):
3817
"""iter_option_refs is a bit unusual, document some cases."""
3819
def assertRefs(self, expected, string):
3820
self.assertEquals(expected, list(config.iter_option_refs(string)))
3822
def test_empty(self):
3823
self.assertRefs([(False, '')], '')
3825
def test_no_refs(self):
3826
self.assertRefs([(False, 'foo bar')], 'foo bar')
3828
def test_single_ref(self):
3829
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3831
def test_broken_ref(self):
3832
self.assertRefs([(False, '{foo')], '{foo')
3834
def test_embedded_ref(self):
3835
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3838
def test_two_refs(self):
3839
self.assertRefs([(False, ''), (True, '{foo}'),
3840
(False, ''), (True, '{bar}'),
3844
def test_newline_in_refs_are_not_matched(self):
3845
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3848
class TestStackExpandOptions(tests.TestCaseWithTransport):
3851
super(TestStackExpandOptions, self).setUp()
3852
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3853
self.registry = config.option_registry
3854
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3855
self.conf = config.Stack([store.get_sections], store)
3857
def assertExpansion(self, expected, string, env=None):
3858
self.assertEquals(expected, self.conf.expand_options(string, env))
3860
def test_no_expansion(self):
3861
self.assertExpansion('foo', 'foo')
3863
def test_expand_default_value(self):
3864
self.conf.store._load_from_string('bar=baz')
3865
self.registry.register(config.Option('foo', default=u'{bar}'))
3866
self.assertEquals('baz', self.conf.get('foo', expand=True))
3868
def test_expand_default_from_env(self):
3869
self.conf.store._load_from_string('bar=baz')
3870
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3871
self.overrideEnv('FOO', '{bar}')
3872
self.assertEquals('baz', self.conf.get('foo', expand=True))
3874
def test_expand_default_on_failed_conversion(self):
3875
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3876
self.registry.register(
3877
config.Option('foo', default=u'{bar}',
3878
from_unicode=config.int_from_store))
3879
self.assertEquals(42, self.conf.get('foo', expand=True))
3881
def test_env_adding_options(self):
3882
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3884
def test_env_overriding_options(self):
3885
self.conf.store._load_from_string('foo=baz')
3886
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3888
def test_simple_ref(self):
3889
self.conf.store._load_from_string('foo=xxx')
3890
self.assertExpansion('xxx', '{foo}')
3892
def test_unknown_ref(self):
3893
self.assertRaises(errors.ExpandingUnknownOption,
3894
self.conf.expand_options, '{foo}')
3896
def test_indirect_ref(self):
3897
self.conf.store._load_from_string('''
3901
self.assertExpansion('xxx', '{bar}')
3903
def test_embedded_ref(self):
3904
self.conf.store._load_from_string('''
3908
self.assertExpansion('xxx', '{{bar}}')
3910
def test_simple_loop(self):
3911
self.conf.store._load_from_string('foo={foo}')
3912
self.assertRaises(errors.OptionExpansionLoop,
3913
self.conf.expand_options, '{foo}')
3915
def test_indirect_loop(self):
3916
self.conf.store._load_from_string('''
3920
e = self.assertRaises(errors.OptionExpansionLoop,
3921
self.conf.expand_options, '{foo}')
3922
self.assertEquals('foo->bar->baz', e.refs)
3923
self.assertEquals('{foo}', e.string)
3925
def test_list(self):
3926
self.conf.store._load_from_string('''
3930
list={foo},{bar},{baz}
3932
self.registry.register(
3933
config.ListOption('list'))
3934
self.assertEquals(['start', 'middle', 'end'],
3935
self.conf.get('list', expand=True))
3937
def test_cascading_list(self):
3938
self.conf.store._load_from_string('''
3944
self.registry.register(config.ListOption('list'))
3945
# Register an intermediate option as a list to ensure no conversion
3946
# happen while expanding. Conversion should only occur for the original
3947
# option ('list' here).
3948
self.registry.register(config.ListOption('baz'))
3949
self.assertEquals(['start', 'middle', 'end'],
3950
self.conf.get('list', expand=True))
3952
def test_pathologically_hidden_list(self):
3953
self.conf.store._load_from_string('''
3959
hidden={start}{middle}{end}
3961
# What matters is what the registration says, the conversion happens
3962
# only after all expansions have been performed
3963
self.registry.register(config.ListOption('hidden'))
3964
self.assertEquals(['bin', 'go'],
3965
self.conf.get('hidden', expand=True))
3968
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3971
super(TestStackCrossSectionsExpand, self).setUp()
3973
def get_config(self, location, string):
3976
# Since we don't save the config we won't strictly require to inherit
3977
# from TestCaseInTempDir, but an error occurs so quickly...
3978
c = config.LocationStack(location)
3979
c.store._load_from_string(string)
3982
def test_dont_cross_unrelated_section(self):
3983
c = self.get_config('/another/branch/path','''
3988
[/another/branch/path]
3991
self.assertRaises(errors.ExpandingUnknownOption,
3992
c.get, 'bar', expand=True)
3994
def test_cross_related_sections(self):
3995
c = self.get_config('/project/branch/path','''
3999
[/project/branch/path]
4002
self.assertEquals('quux', c.get('bar', expand=True))
4005
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
4007
def test_cross_global_locations(self):
4008
l_store = config.LocationStore()
4009
l_store._load_from_string('''
4015
g_store = config.GlobalStore()
4016
g_store._load_from_string('''
4022
stack = config.LocationStack('/branch')
4023
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4024
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4027
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4029
def test_expand_locals_empty(self):
4030
l_store = config.LocationStore()
4031
l_store._load_from_string('''
4032
[/home/user/project]
4037
stack = config.LocationStack('/home/user/project/')
4038
self.assertEquals('', stack.get('base', expand=True))
4039
self.assertEquals('', stack.get('rel', expand=True))
4041
def test_expand_basename_locally(self):
4042
l_store = config.LocationStore()
4043
l_store._load_from_string('''
4044
[/home/user/project]
4048
stack = config.LocationStack('/home/user/project/branch')
4049
self.assertEquals('branch', stack.get('bfoo', expand=True))
4051
def test_expand_basename_locally_longer_path(self):
4052
l_store = config.LocationStore()
4053
l_store._load_from_string('''
4058
stack = config.LocationStack('/home/user/project/dir/branch')
4059
self.assertEquals('branch', stack.get('bfoo', expand=True))
4061
def test_expand_relpath_locally(self):
4062
l_store = config.LocationStore()
4063
l_store._load_from_string('''
4064
[/home/user/project]
4065
lfoo = loc-foo/{relpath}
4068
stack = config.LocationStack('/home/user/project/branch')
4069
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4071
def test_expand_relpath_unknonw_in_global(self):
4072
g_store = config.GlobalStore()
4073
g_store._load_from_string('''
4078
stack = config.LocationStack('/home/user/project/branch')
4079
self.assertRaises(errors.ExpandingUnknownOption,
4080
stack.get, 'gfoo', expand=True)
4082
def test_expand_local_option_locally(self):
4083
l_store = config.LocationStore()
4084
l_store._load_from_string('''
4085
[/home/user/project]
4086
lfoo = loc-foo/{relpath}
4090
g_store = config.GlobalStore()
4091
g_store._load_from_string('''
4097
stack = config.LocationStack('/home/user/project/branch')
4098
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4099
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4101
def test_locals_dont_leak(self):
4102
"""Make sure we chose the right local in presence of several sections.
4104
l_store = config.LocationStore()
4105
l_store._load_from_string('''
4107
lfoo = loc-foo/{relpath}
4108
[/home/user/project]
4109
lfoo = loc-foo/{relpath}
4112
stack = config.LocationStack('/home/user/project/branch')
4113
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4114
stack = config.LocationStack('/home/user/bar/baz')
4115
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
2935
4119
class TestStackSet(TestStackWithTransport):
2937
4121
def test_simple_set(self):
2938
4122
conf = self.get_stack(self)
2939
conf.store._load_from_string('foo=bar')
2940
self.assertEquals('bar', conf.get('foo'))
4123
self.assertEquals(None, conf.get('foo'))
2941
4124
conf.set('foo', 'baz')
2942
4125
# Did we get it back ?
2943
4126
self.assertEquals('baz', conf.get('foo'))