2358
2366
class TestOptionConverterMixin(object):
2360
2368
def assertConverted(self, expected, opt, value):
2361
self.assertEquals(expected, opt.convert_from_unicode(value))
2363
def assertWarns(self, opt, value):
2366
warnings.append(args[0] % args[1:])
2367
self.overrideAttr(trace, 'warning', warning)
2368
self.assertEquals(None, opt.convert_from_unicode(value))
2369
self.assertLength(1, warnings)
2371
'Value "%s" is not valid for "%s"' % (value, opt.name),
2374
def assertErrors(self, opt, value):
2375
self.assertRaises(errors.ConfigOptionValueError,
2376
opt.convert_from_unicode, value)
2378
def assertConvertInvalid(self, opt, invalid_value):
2380
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2381
opt.invalid = 'warning'
2382
self.assertWarns(opt, invalid_value)
2383
opt.invalid = 'error'
2384
self.assertErrors(opt, invalid_value)
2387
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2389
def get_option(self):
2390
return config.Option('foo', help='A boolean.',
2391
from_unicode=config.bool_from_store)
2393
def test_convert_invalid(self):
2394
opt = self.get_option()
2395
# A string that is not recognized as a boolean
2396
self.assertConvertInvalid(opt, u'invalid-boolean')
2397
# A list of strings is never recognized as a boolean
2398
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2400
def test_convert_valid(self):
2401
opt = self.get_option()
2402
self.assertConverted(True, opt, u'True')
2403
self.assertConverted(True, opt, u'1')
2404
self.assertConverted(False, opt, u'False')
2407
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2409
def get_option(self):
2410
return config.Option('foo', help='An integer.',
2411
from_unicode=config.int_from_store)
2413
def test_convert_invalid(self):
2414
opt = self.get_option()
2415
# A string that is not recognized as an integer
2416
self.assertConvertInvalid(opt, u'forty-two')
2417
# A list of strings is never recognized as an integer
2418
self.assertConvertInvalid(opt, [u'a', u'list'])
2420
def test_convert_valid(self):
2421
opt = self.get_option()
2422
self.assertConverted(16, opt, u'16')
2425
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2427
def get_option(self):
2428
return config.Option('foo', help='A list.',
2429
from_unicode=config.list_from_store)
2431
def test_convert_invalid(self):
2432
# No string is invalid as all forms can be converted to a list
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
# An empty string is an empty list
2438
self.assertConverted([], opt, '') # Using a bare str() just in case
2439
self.assertConverted([], opt, u'')
2441
self.assertConverted([u'True'], opt, u'True')
2443
self.assertConverted([u'42'], opt, u'42')
2445
self.assertConverted([u'bar'], opt, u'bar')
2446
# A list remains a list (configObj will turn a string containing commas
2447
# into a list, but that's not what we're testing here)
2448
self.assertConverted([u'foo', u'1', u'True'],
2449
opt, [u'foo', u'1', u'True'])
2452
class TestOptionConverterMixin(object):
2454
def assertConverted(self, expected, opt, value):
2455
self.assertEquals(expected, opt.convert_from_unicode(value))
2457
def assertWarns(self, opt, value):
2460
warnings.append(args[0] % args[1:])
2461
self.overrideAttr(trace, 'warning', warning)
2462
self.assertEquals(None, opt.convert_from_unicode(value))
2463
self.assertLength(1, warnings)
2465
'Value "%s" is not valid for "%s"' % (value, opt.name),
2468
def assertErrors(self, opt, value):
2469
self.assertRaises(errors.ConfigOptionValueError,
2470
opt.convert_from_unicode, value)
2472
def assertConvertInvalid(self, opt, invalid_value):
2474
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2475
opt.invalid = 'warning'
2476
self.assertWarns(opt, invalid_value)
2477
opt.invalid = 'error'
2478
self.assertErrors(opt, invalid_value)
2481
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2483
def get_option(self):
2484
return config.Option('foo', help='A boolean.',
2485
from_unicode=config.bool_from_store)
2487
def test_convert_invalid(self):
2488
opt = self.get_option()
2489
# A string that is not recognized as a boolean
2490
self.assertConvertInvalid(opt, u'invalid-boolean')
2491
# A list of strings is never recognized as a boolean
2492
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2494
def test_convert_valid(self):
2495
opt = self.get_option()
2496
self.assertConverted(True, opt, u'True')
2497
self.assertConverted(True, opt, u'1')
2498
self.assertConverted(False, opt, u'False')
2501
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2503
def get_option(self):
2504
return config.Option('foo', help='An integer.',
2505
from_unicode=config.int_from_store)
2507
def test_convert_invalid(self):
2508
opt = self.get_option()
2509
# A string that is not recognized as an integer
2510
self.assertConvertInvalid(opt, u'forty-two')
2511
# A list of strings is never recognized as an integer
2512
self.assertConvertInvalid(opt, [u'a', u'list'])
2514
def test_convert_valid(self):
2515
opt = self.get_option()
2516
self.assertConverted(16, opt, u'16')
2519
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2521
def get_option(self):
2522
return config.Option('foo', help='A list.',
2523
from_unicode=config.list_from_store)
2369
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2371
def assertWarns(self, opt, value):
2374
warnings.append(args[0] % args[1:])
2375
self.overrideAttr(trace, 'warning', warning)
2376
self.assertEquals(None, opt.convert_from_unicode(None, value))
2377
self.assertLength(1, warnings)
2379
'Value "%s" is not valid for "%s"' % (value, opt.name),
2382
def assertErrors(self, opt, value):
2383
self.assertRaises(errors.ConfigOptionValueError,
2384
opt.convert_from_unicode, None, value)
2386
def assertConvertInvalid(self, opt, invalid_value):
2388
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2389
opt.invalid = 'warning'
2390
self.assertWarns(opt, invalid_value)
2391
opt.invalid = 'error'
2392
self.assertErrors(opt, invalid_value)
2395
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2397
def get_option(self):
2398
return config.Option('foo', help='A boolean.',
2399
from_unicode=config.bool_from_store)
2401
def test_convert_invalid(self):
2402
opt = self.get_option()
2403
# A string that is not recognized as a boolean
2404
self.assertConvertInvalid(opt, u'invalid-boolean')
2405
# A list of strings is never recognized as a boolean
2406
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2408
def test_convert_valid(self):
2409
opt = self.get_option()
2410
self.assertConverted(True, opt, u'True')
2411
self.assertConverted(True, opt, u'1')
2412
self.assertConverted(False, opt, u'False')
2415
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2417
def get_option(self):
2418
return config.Option('foo', help='An integer.',
2419
from_unicode=config.int_from_store)
2421
def test_convert_invalid(self):
2422
opt = self.get_option()
2423
# A string that is not recognized as an integer
2424
self.assertConvertInvalid(opt, u'forty-two')
2425
# A list of strings is never recognized as an integer
2426
self.assertConvertInvalid(opt, [u'a', u'list'])
2428
def test_convert_valid(self):
2429
opt = self.get_option()
2430
self.assertConverted(16, opt, u'16')
2433
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2435
def get_option(self):
2436
return config.Option('foo', help='An integer in SI units.',
2437
from_unicode=config.int_SI_from_store)
2439
def test_convert_invalid(self):
2440
opt = self.get_option()
2441
self.assertConvertInvalid(opt, u'not-a-unit')
2442
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2443
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2444
self.assertConvertInvalid(opt, u'1GG')
2445
self.assertConvertInvalid(opt, u'1Mbb')
2446
self.assertConvertInvalid(opt, u'1MM')
2448
def test_convert_valid(self):
2449
opt = self.get_option()
2450
self.assertConverted(int(5e3), opt, u'5kb')
2451
self.assertConverted(int(5e6), opt, u'5M')
2452
self.assertConverted(int(5e6), opt, u'5MB')
2453
self.assertConverted(int(5e9), opt, u'5g')
2454
self.assertConverted(int(5e9), opt, u'5gB')
2455
self.assertConverted(100, opt, u'100')
2458
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2460
def get_option(self):
2461
return config.ListOption('foo', help='A list.')
2525
2463
def test_convert_invalid(self):
2526
2464
opt = self.get_option()
2761
2717
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2720
class TestStoreQuoting(TestStore):
2722
scenarios = [(key, {'get_store': builder}) for key, builder
2723
in config.test_store_builder_registry.iteritems()]
2726
super(TestStoreQuoting, self).setUp()
2727
self.store = self.get_store(self)
2728
# We need a loaded store but any content will do
2729
self.store._load_from_string('')
2731
def assertIdempotent(self, s):
2732
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2734
What matters here is that option values, as they appear in a store, can
2735
be safely round-tripped out of the store and back.
2737
:param s: A string, quoted if required.
2739
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2740
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2742
def test_empty_string(self):
2743
if isinstance(self.store, config.IniFileStore):
2744
# configobj._quote doesn't handle empty values
2745
self.assertRaises(AssertionError,
2746
self.assertIdempotent, '')
2748
self.assertIdempotent('')
2749
# But quoted empty strings are ok
2750
self.assertIdempotent('""')
2752
def test_embedded_spaces(self):
2753
self.assertIdempotent('" a b c "')
2755
def test_embedded_commas(self):
2756
self.assertIdempotent('" a , b c "')
2758
def test_simple_comma(self):
2759
if isinstance(self.store, config.IniFileStore):
2760
# configobj requires that lists are special-cased
2761
self.assertRaises(AssertionError,
2762
self.assertIdempotent, ',')
2764
self.assertIdempotent(',')
2765
# When a single comma is required, quoting is also required
2766
self.assertIdempotent('","')
2768
def test_list(self):
2769
if isinstance(self.store, config.IniFileStore):
2770
# configobj requires that lists are special-cased
2771
self.assertRaises(AssertionError,
2772
self.assertIdempotent, 'a,b')
2774
self.assertIdempotent('a,b')
2777
class TestDictFromStore(tests.TestCase):
2779
def test_unquote_not_string(self):
2780
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2781
value = conf.get('a_section')
2782
# Urgh, despite 'conf' asking for the no-name section, we get the
2783
# content of another section as a dict o_O
2784
self.assertEquals({'a': '1'}, value)
2785
unquoted = conf.store.unquote(value)
2786
# Which cannot be unquoted but shouldn't crash either (the use cases
2787
# are getting the value or displaying it. In the later case, '%s' will
2789
self.assertEquals({'a': '1'}, unquoted)
2790
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2764
2793
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2765
2794
"""Simulate loading a config store with content of various encodings.
2971
3000
self.assertLength(1, calls)
2972
3001
self.assertEquals((store,), calls[0])
3003
def test_set_mark_dirty(self):
3004
stack = config.MemoryStack('')
3005
self.assertLength(0, stack.store.dirty_sections)
3006
stack.set('foo', 'baz')
3007
self.assertLength(1, stack.store.dirty_sections)
3008
self.assertTrue(stack.store._need_saving())
3010
def test_remove_mark_dirty(self):
3011
stack = config.MemoryStack('foo=bar')
3012
self.assertLength(0, stack.store.dirty_sections)
3014
self.assertLength(1, stack.store.dirty_sections)
3015
self.assertTrue(stack.store._need_saving())
3018
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3019
"""Tests that config changes are kept in memory and saved on-demand."""
3022
super(TestStoreSaveChanges, self).setUp()
3023
self.transport = self.get_transport()
3024
# Most of the tests involve two stores pointing to the same persistent
3025
# storage to observe the effects of concurrent changes
3026
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3027
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3030
self.warnings.append(args[0] % args[1:])
3031
self.overrideAttr(trace, 'warning', warning)
3033
def has_store(self, store):
3034
store_basename = urlutils.relative_url(self.transport.external_url(),
3035
store.external_url())
3036
return self.transport.has(store_basename)
3038
def get_stack(self, store):
3039
# Any stack will do as long as it uses the right store, just a single
3040
# no-name section is enough
3041
return config.Stack([store.get_sections], store)
3043
def test_no_changes_no_save(self):
3044
s = self.get_stack(self.st1)
3045
s.store.save_changes()
3046
self.assertEquals(False, self.has_store(self.st1))
3048
def test_unrelated_concurrent_update(self):
3049
s1 = self.get_stack(self.st1)
3050
s2 = self.get_stack(self.st2)
3051
s1.set('foo', 'bar')
3052
s2.set('baz', 'quux')
3054
# Changes don't propagate magically
3055
self.assertEquals(None, s1.get('baz'))
3056
s2.store.save_changes()
3057
self.assertEquals('quux', s2.get('baz'))
3058
# Changes are acquired when saving
3059
self.assertEquals('bar', s2.get('foo'))
3060
# Since there is no overlap, no warnings are emitted
3061
self.assertLength(0, self.warnings)
3063
def test_concurrent_update_modified(self):
3064
s1 = self.get_stack(self.st1)
3065
s2 = self.get_stack(self.st2)
3066
s1.set('foo', 'bar')
3067
s2.set('foo', 'baz')
3070
s2.store.save_changes()
3071
self.assertEquals('baz', s2.get('foo'))
3072
# But the user get a warning
3073
self.assertLength(1, self.warnings)
3074
warning = self.warnings[0]
3075
self.assertStartsWith(warning, 'Option foo in section None')
3076
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3077
' The baz value will be saved.')
3079
def test_concurrent_deletion(self):
3080
self.st1._load_from_string('foo=bar')
3082
s1 = self.get_stack(self.st1)
3083
s2 = self.get_stack(self.st2)
3086
s1.store.save_changes()
3088
self.assertLength(0, self.warnings)
3089
s2.store.save_changes()
3091
self.assertLength(1, self.warnings)
3092
warning = self.warnings[0]
3093
self.assertStartsWith(warning, 'Option foo in section None')
3094
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3095
' The <DELETED> value will be saved.')
3098
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3100
def get_store(self):
3101
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3103
def test_get_quoted_string(self):
3104
store = self.get_store()
3105
store._load_from_string('foo= " abc "')
3106
stack = config.Stack([store.get_sections])
3107
self.assertEquals(' abc ', stack.get('foo'))
3109
def test_set_quoted_string(self):
3110
store = self.get_store()
3111
stack = config.Stack([store.get_sections], store)
3112
stack.set('foo', ' a b c ')
3114
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
2975
3117
class TestTransportIniFileStore(TestStore):
3321
3463
self.assertLength(0, sections)
3324
class TestStackGet(tests.TestCase):
3326
# FIXME: This should be parametrized for all known Stack or dedicated
3327
# paramerized tests created to avoid bloating -- vila 2011-03-31
3329
def overrideOptionRegistry(self):
3466
class TestBaseStackGet(tests.TestCase):
3469
super(TestBaseStackGet, self).setUp()
3330
3470
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3332
def test_single_config_get(self):
3333
conf = dict(foo='bar')
3334
conf_stack = config.Stack([conf])
3335
self.assertEquals('bar', conf_stack.get('foo'))
3472
def test_get_first_definition(self):
3473
store1 = config.IniFileStore()
3474
store1._load_from_string('foo=bar')
3475
store2 = config.IniFileStore()
3476
store2._load_from_string('foo=baz')
3477
conf = config.Stack([store1.get_sections, store2.get_sections])
3478
self.assertEquals('bar', conf.get('foo'))
3337
3480
def test_get_with_registered_default_value(self):
3338
conf_stack = config.Stack([dict()])
3339
opt = config.Option('foo', default='bar')
3340
self.overrideOptionRegistry()
3341
config.option_registry.register('foo', opt)
3481
config.option_registry.register(config.Option('foo', default='bar'))
3482
conf_stack = config.Stack([])
3342
3483
self.assertEquals('bar', conf_stack.get('foo'))
3344
3485
def test_get_without_registered_default_value(self):
3345
conf_stack = config.Stack([dict()])
3346
opt = config.Option('foo')
3347
self.overrideOptionRegistry()
3348
config.option_registry.register('foo', opt)
3486
config.option_registry.register(config.Option('foo'))
3487
conf_stack = config.Stack([])
3349
3488
self.assertEquals(None, conf_stack.get('foo'))
3351
3490
def test_get_without_default_value_for_not_registered(self):
3352
conf_stack = config.Stack([dict()])
3353
opt = config.Option('foo')
3354
self.overrideOptionRegistry()
3491
conf_stack = config.Stack([])
3355
3492
self.assertEquals(None, conf_stack.get('foo'))
3357
def test_get_first_definition(self):
3358
conf1 = dict(foo='bar')
3359
conf2 = dict(foo='baz')
3360
conf_stack = config.Stack([conf1, conf2])
3361
self.assertEquals('bar', conf_stack.get('foo'))
3363
def test_get_embedded_definition(self):
3364
conf1 = dict(yy='12')
3365
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3366
conf_stack = config.Stack([conf1, conf2])
3367
self.assertEquals('baz', conf_stack.get('foo'))
3369
3494
def test_get_for_empty_section_callable(self):
3370
3495
conf_stack = config.Stack([lambda : []])
3371
3496
self.assertEquals(None, conf_stack.get('foo'))
3373
3498
def test_get_for_broken_callable(self):
3374
3499
# Trying to use and invalid callable raises an exception on first use
3375
conf_stack = config.Stack([lambda : object()])
3500
conf_stack = config.Stack([object])
3376
3501
self.assertRaises(TypeError, conf_stack.get, 'foo')
3504
class TestStackWithSimpleStore(tests.TestCase):
3507
super(TestStackWithSimpleStore, self).setUp()
3508
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3509
self.registry = config.option_registry
3511
def get_conf(self, content=None):
3512
return config.MemoryStack(content)
3514
def test_override_value_from_env(self):
3515
self.registry.register(
3516
config.Option('foo', default='bar', override_from_env=['FOO']))
3517
self.overrideEnv('FOO', 'quux')
3518
# Env variable provides a default taking over the option one
3519
conf = self.get_conf('foo=store')
3520
self.assertEquals('quux', conf.get('foo'))
3522
def test_first_override_value_from_env_wins(self):
3523
self.registry.register(
3524
config.Option('foo', default='bar',
3525
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3526
self.overrideEnv('FOO', 'foo')
3527
self.overrideEnv('BAZ', 'baz')
3528
# The first env var set wins
3529
conf = self.get_conf('foo=store')
3530
self.assertEquals('foo', conf.get('foo'))
3533
class TestMemoryStack(tests.TestCase):
3536
conf = config.MemoryStack('foo=bar')
3537
self.assertEquals('bar', conf.get('foo'))
3540
conf = config.MemoryStack('foo=bar')
3541
conf.set('foo', 'baz')
3542
self.assertEquals('baz', conf.get('foo'))
3544
def test_no_content(self):
3545
conf = config.MemoryStack()
3546
# No content means no loading
3547
self.assertFalse(conf.store.is_loaded())
3548
self.assertRaises(NotImplementedError, conf.get, 'foo')
3549
# But a content can still be provided
3550
conf.store._load_from_string('foo=bar')
3551
self.assertEquals('bar', conf.get('foo'))
3379
3554
class TestStackWithTransport(tests.TestCaseWithTransport):
3381
3556
scenarios = [(key, {'get_stack': builder}) for key, builder
3465
3643
def test_get_default_integer_None(self):
3466
3644
self.register_integer_option('foo')
3467
self.assertEquals(None, self.conf.get('foo'))
3645
conf = self.get_conf('')
3646
self.assertEquals(None, conf.get('foo'))
3469
3648
def test_get_default_integer(self):
3470
3649
self.register_integer_option('foo', 42)
3471
self.assertEquals(42, self.conf.get('foo'))
3650
conf = self.get_conf('')
3651
self.assertEquals(42, conf.get('foo'))
3473
3653
def test_get_default_integer_as_string(self):
3474
3654
self.register_integer_option('foo', u'42')
3475
self.assertEquals(42, self.conf.get('foo'))
3655
conf = self.get_conf('')
3656
self.assertEquals(42, conf.get('foo'))
3477
3658
def test_get_default_integer_from_env(self):
3478
3659
self.register_integer_option('foo', default_from_env=['FOO'])
3479
3660
self.overrideEnv('FOO', '18')
3480
self.assertEquals(18, self.conf.get('foo'))
3661
conf = self.get_conf('')
3662
self.assertEquals(18, conf.get('foo'))
3482
3664
def test_get_default_integer_when_conversion_fails(self):
3483
3665
self.register_integer_option('foo', default='12')
3484
self.conf.store._load_from_string('foo=invalid integer')
3485
self.assertEquals(12, self.conf.get('foo'))
3666
conf = self.get_conf('foo=invalid integer')
3667
self.assertEquals(12, conf.get('foo'))
3487
3669
def register_list_option(self, name, default=None, default_from_env=None):
3488
l = config.Option(name, help='A list.',
3489
default=default, default_from_env=default_from_env,
3490
from_unicode=config.list_from_store)
3670
l = config.ListOption(name, help='A list.', default=default,
3671
default_from_env=default_from_env)
3491
3672
self.registry.register(l)
3493
3674
def test_get_default_list_None(self):
3494
3675
self.register_list_option('foo')
3495
self.assertEquals(None, self.conf.get('foo'))
3676
conf = self.get_conf('')
3677
self.assertEquals(None, conf.get('foo'))
3497
3679
def test_get_default_list_empty(self):
3498
3680
self.register_list_option('foo', '')
3499
self.assertEquals([], self.conf.get('foo'))
3681
conf = self.get_conf('')
3682
self.assertEquals([], conf.get('foo'))
3501
3684
def test_get_default_list_from_env(self):
3502
3685
self.register_list_option('foo', default_from_env=['FOO'])
3503
3686
self.overrideEnv('FOO', '')
3504
self.assertEquals([], self.conf.get('foo'))
3687
conf = self.get_conf('')
3688
self.assertEquals([], conf.get('foo'))
3506
3690
def test_get_with_list_converter_no_item(self):
3507
3691
self.register_list_option('foo', None)
3508
self.conf.store._load_from_string('foo=,')
3509
self.assertEquals([], self.conf.get('foo'))
3692
conf = self.get_conf('foo=,')
3693
self.assertEquals([], conf.get('foo'))
3511
3695
def test_get_with_list_converter_many_items(self):
3512
3696
self.register_list_option('foo', None)
3513
self.conf.store._load_from_string('foo=m,o,r,e')
3514
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3697
conf = self.get_conf('foo=m,o,r,e')
3698
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3516
3700
def test_get_with_list_converter_embedded_spaces_many_items(self):
3517
3701
self.register_list_option('foo', None)
3518
self.conf.store._load_from_string('foo=" bar", "baz "')
3519
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3702
conf = self.get_conf('foo=" bar", "baz "')
3703
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3521
3705
def test_get_with_list_converter_stripped_spaces_many_items(self):
3522
3706
self.register_list_option('foo', None)
3523
self.conf.store._load_from_string('foo= bar , baz ')
3524
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3707
conf = self.get_conf('foo= bar , baz ')
3708
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3527
3711
class TestIterOptionRefs(tests.TestCase):