2267
2356
# The first env var set wins
2268
2357
self.assertEquals('foo', opt.get_default())
2359
def test_not_supported_list_default_value(self):
2360
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2362
def test_not_supported_object_default_value(self):
2363
self.assertRaises(AssertionError, config.Option, 'foo',
2366
def test_not_supported_callable_default_value_not_unicode(self):
2367
def bar_not_unicode():
2369
opt = config.Option('foo', default=bar_not_unicode)
2370
self.assertRaises(AssertionError, opt.get_default)
2373
class TestOptionConverterMixin(object):
2375
def assertConverted(self, expected, opt, value):
2376
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2378
def assertWarns(self, opt, value):
2381
warnings.append(args[0] % args[1:])
2382
self.overrideAttr(trace, 'warning', warning)
2383
self.assertEquals(None, opt.convert_from_unicode(None, value))
2384
self.assertLength(1, warnings)
2386
'Value "%s" is not valid for "%s"' % (value, opt.name),
2389
def assertErrors(self, opt, value):
2390
self.assertRaises(errors.ConfigOptionValueError,
2391
opt.convert_from_unicode, None, value)
2393
def assertConvertInvalid(self, opt, invalid_value):
2395
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2396
opt.invalid = 'warning'
2397
self.assertWarns(opt, invalid_value)
2398
opt.invalid = 'error'
2399
self.assertErrors(opt, invalid_value)
2402
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2404
def get_option(self):
2405
return config.Option('foo', help='A boolean.',
2406
from_unicode=config.bool_from_store)
2408
def test_convert_invalid(self):
2409
opt = self.get_option()
2410
# A string that is not recognized as a boolean
2411
self.assertConvertInvalid(opt, u'invalid-boolean')
2412
# A list of strings is never recognized as a boolean
2413
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2415
def test_convert_valid(self):
2416
opt = self.get_option()
2417
self.assertConverted(True, opt, u'True')
2418
self.assertConverted(True, opt, u'1')
2419
self.assertConverted(False, opt, u'False')
2422
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2424
def get_option(self):
2425
return config.Option('foo', help='An integer.',
2426
from_unicode=config.int_from_store)
2428
def test_convert_invalid(self):
2429
opt = self.get_option()
2430
# A string that is not recognized as an integer
2431
self.assertConvertInvalid(opt, u'forty-two')
2432
# A list of strings is never recognized as an integer
2433
self.assertConvertInvalid(opt, [u'a', u'list'])
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
self.assertConverted(16, opt, u'16')
2440
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2442
def get_option(self):
2443
return config.Option('foo', help='An integer in SI units.',
2444
from_unicode=config.int_SI_from_store)
2446
def test_convert_invalid(self):
2447
opt = self.get_option()
2448
self.assertConvertInvalid(opt, u'not-a-unit')
2449
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2450
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2451
self.assertConvertInvalid(opt, u'1GG')
2452
self.assertConvertInvalid(opt, u'1Mbb')
2453
self.assertConvertInvalid(opt, u'1MM')
2455
def test_convert_valid(self):
2456
opt = self.get_option()
2457
self.assertConverted(int(5e3), opt, u'5kb')
2458
self.assertConverted(int(5e6), opt, u'5M')
2459
self.assertConverted(int(5e6), opt, u'5MB')
2460
self.assertConverted(int(5e9), opt, u'5g')
2461
self.assertConverted(int(5e9), opt, u'5gB')
2462
self.assertConverted(100, opt, u'100')
2465
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2467
def get_option(self):
2468
return config.ListOption('foo', help='A list.')
2470
def test_convert_invalid(self):
2471
opt = self.get_option()
2472
# We don't even try to convert a list into a list, we only expect
2474
self.assertConvertInvalid(opt, [1])
2475
# No string is invalid as all forms can be converted to a list
2477
def test_convert_valid(self):
2478
opt = self.get_option()
2479
# An empty string is an empty list
2480
self.assertConverted([], opt, '') # Using a bare str() just in case
2481
self.assertConverted([], opt, u'')
2483
self.assertConverted([u'True'], opt, u'True')
2485
self.assertConverted([u'42'], opt, u'42')
2487
self.assertConverted([u'bar'], opt, u'bar')
2271
2490
class TestOptionRegistry(tests.TestCase):
2636
3007
self.assertLength(1, calls)
2637
3008
self.assertEquals((store,), calls[0])
2640
class TestIniFileStore(TestStore):
3010
def test_set_mark_dirty(self):
3011
stack = config.MemoryStack('')
3012
self.assertLength(0, stack.store.dirty_sections)
3013
stack.set('foo', 'baz')
3014
self.assertLength(1, stack.store.dirty_sections)
3015
self.assertTrue(stack.store._need_saving())
3017
def test_remove_mark_dirty(self):
3018
stack = config.MemoryStack('foo=bar')
3019
self.assertLength(0, stack.store.dirty_sections)
3021
self.assertLength(1, stack.store.dirty_sections)
3022
self.assertTrue(stack.store._need_saving())
3025
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3026
"""Tests that config changes are kept in memory and saved on-demand."""
3029
super(TestStoreSaveChanges, self).setUp()
3030
self.transport = self.get_transport()
3031
# Most of the tests involve two stores pointing to the same persistent
3032
# storage to observe the effects of concurrent changes
3033
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3034
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3037
self.warnings.append(args[0] % args[1:])
3038
self.overrideAttr(trace, 'warning', warning)
3040
def has_store(self, store):
3041
store_basename = urlutils.relative_url(self.transport.external_url(),
3042
store.external_url())
3043
return self.transport.has(store_basename)
3045
def get_stack(self, store):
3046
# Any stack will do as long as it uses the right store, just a single
3047
# no-name section is enough
3048
return config.Stack([store.get_sections], store)
3050
def test_no_changes_no_save(self):
3051
s = self.get_stack(self.st1)
3052
s.store.save_changes()
3053
self.assertEquals(False, self.has_store(self.st1))
3055
def test_unrelated_concurrent_update(self):
3056
s1 = self.get_stack(self.st1)
3057
s2 = self.get_stack(self.st2)
3058
s1.set('foo', 'bar')
3059
s2.set('baz', 'quux')
3061
# Changes don't propagate magically
3062
self.assertEquals(None, s1.get('baz'))
3063
s2.store.save_changes()
3064
self.assertEquals('quux', s2.get('baz'))
3065
# Changes are acquired when saving
3066
self.assertEquals('bar', s2.get('foo'))
3067
# Since there is no overlap, no warnings are emitted
3068
self.assertLength(0, self.warnings)
3070
def test_concurrent_update_modified(self):
3071
s1 = self.get_stack(self.st1)
3072
s2 = self.get_stack(self.st2)
3073
s1.set('foo', 'bar')
3074
s2.set('foo', 'baz')
3077
s2.store.save_changes()
3078
self.assertEquals('baz', s2.get('foo'))
3079
# But the user get a warning
3080
self.assertLength(1, self.warnings)
3081
warning = self.warnings[0]
3082
self.assertStartsWith(warning, 'Option foo in section None')
3083
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3084
' The baz value will be saved.')
3086
def test_concurrent_deletion(self):
3087
self.st1._load_from_string('foo=bar')
3089
s1 = self.get_stack(self.st1)
3090
s2 = self.get_stack(self.st2)
3093
s1.store.save_changes()
3095
self.assertLength(0, self.warnings)
3096
s2.store.save_changes()
3098
self.assertLength(1, self.warnings)
3099
warning = self.warnings[0]
3100
self.assertStartsWith(warning, 'Option foo in section None')
3101
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3102
' The <DELETED> value will be saved.')
3105
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3107
def get_store(self):
3108
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3110
def test_get_quoted_string(self):
3111
store = self.get_store()
3112
store._load_from_string('foo= " abc "')
3113
stack = config.Stack([store.get_sections])
3114
self.assertEquals(' abc ', stack.get('foo'))
3116
def test_set_quoted_string(self):
3117
store = self.get_store()
3118
stack = config.Stack([store.get_sections], store)
3119
stack.set('foo', ' a b c ')
3121
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3124
class TestTransportIniFileStore(TestStore):
2642
3126
def test_loading_unknown_file_fails(self):
2643
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
3127
store = config.TransportIniFileStore(self.get_transport(),
2644
3129
self.assertRaises(errors.NoSuchFile, store.load)
2646
3131
def test_invalid_content(self):
2647
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
3132
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2648
3133
self.assertEquals(False, store.is_loaded())
2649
3134
exc = self.assertRaises(
2650
3135
errors.ParseConfigError, store._load_from_string,
2947
3434
self.assertEquals(expected_location, matcher.location)
2950
class TestStackGet(tests.TestCase):
2952
# FIXME: This should be parametrized for all known Stack or dedicated
2953
# paramerized tests created to avoid bloating -- vila 2011-03-31
2955
def overrideOptionRegistry(self):
3437
class TestStartingPathMatcher(TestStore):
3440
super(TestStartingPathMatcher, self).setUp()
3441
# Any simple store is good enough
3442
self.store = config.IniFileStore()
3444
def assertSectionIDs(self, expected, location, content):
3445
self.store._load_from_string(content)
3446
matcher = config.StartingPathMatcher(self.store, location)
3447
sections = list(matcher.get_sections())
3448
self.assertLength(len(expected), sections)
3449
self.assertEqual(expected, [section.id for _, section in sections])
3452
def test_empty(self):
3453
self.assertSectionIDs([], self.get_url(), '')
3455
def test_url_vs_local_paths(self):
3456
# The matcher location is an url and the section names are local paths
3457
sections = self.assertSectionIDs(['/foo/bar', '/foo'],
3458
'file:///foo/bar/baz', '''\
3463
def test_local_path_vs_url(self):
3464
# The matcher location is a local path and the section names are urls
3465
sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3466
'/foo/bar/baz', '''\
3472
def test_no_name_section_included_when_present(self):
3473
# Note that other tests will cover the case where the no-name section
3474
# is empty and as such, not included.
3475
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3476
'/foo/bar/baz', '''\
3477
option = defined so the no-name section exists
3481
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3482
[s.locals['relpath'] for _, s in sections])
3484
def test_order_reversed(self):
3485
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3490
def test_unrelated_section_excluded(self):
3491
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3497
def test_glob_included(self):
3498
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3499
'/foo/bar/baz', '''\
3505
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3506
# nothing really is... as far using {relpath} to append it to something
3507
# else, this seems good enough though.
3508
self.assertEquals(['', 'baz', 'bar/baz'],
3509
[s.locals['relpath'] for _, s in sections])
3511
def test_respect_order(self):
3512
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3513
'/foo/bar/baz', '''\
3521
class TestNameMatcher(TestStore):
3524
super(TestNameMatcher, self).setUp()
3525
self.matcher = config.NameMatcher
3526
# Any simple store is good enough
3527
self.get_store = config.test_store_builder_registry.get('configobj')
3529
def get_matching_sections(self, name):
3530
store = self.get_store(self)
3531
store._load_from_string('''
3539
matcher = self.matcher(store, name)
3540
return list(matcher.get_sections())
3542
def test_matching(self):
3543
sections = self.get_matching_sections('foo')
3544
self.assertLength(1, sections)
3545
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3547
def test_not_matching(self):
3548
sections = self.get_matching_sections('baz')
3549
self.assertLength(0, sections)
3552
class TestBaseStackGet(tests.TestCase):
3555
super(TestBaseStackGet, self).setUp()
2956
3556
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2958
def test_single_config_get(self):
2959
conf = dict(foo='bar')
2960
conf_stack = config.Stack([conf])
2961
self.assertEquals('bar', conf_stack.get('foo'))
3558
def test_get_first_definition(self):
3559
store1 = config.IniFileStore()
3560
store1._load_from_string('foo=bar')
3561
store2 = config.IniFileStore()
3562
store2._load_from_string('foo=baz')
3563
conf = config.Stack([store1.get_sections, store2.get_sections])
3564
self.assertEquals('bar', conf.get('foo'))
2963
3566
def test_get_with_registered_default_value(self):
2964
conf_stack = config.Stack([dict()])
2965
opt = config.Option('foo', default='bar')
2966
self.overrideOptionRegistry()
2967
config.option_registry.register('foo', opt)
3567
config.option_registry.register(config.Option('foo', default='bar'))
3568
conf_stack = config.Stack([])
2968
3569
self.assertEquals('bar', conf_stack.get('foo'))
2970
3571
def test_get_without_registered_default_value(self):
2971
conf_stack = config.Stack([dict()])
2972
opt = config.Option('foo')
2973
self.overrideOptionRegistry()
2974
config.option_registry.register('foo', opt)
3572
config.option_registry.register(config.Option('foo'))
3573
conf_stack = config.Stack([])
2975
3574
self.assertEquals(None, conf_stack.get('foo'))
2977
3576
def test_get_without_default_value_for_not_registered(self):
2978
conf_stack = config.Stack([dict()])
2979
opt = config.Option('foo')
2980
self.overrideOptionRegistry()
3577
conf_stack = config.Stack([])
2981
3578
self.assertEquals(None, conf_stack.get('foo'))
2983
def test_get_first_definition(self):
2984
conf1 = dict(foo='bar')
2985
conf2 = dict(foo='baz')
2986
conf_stack = config.Stack([conf1, conf2])
2987
self.assertEquals('bar', conf_stack.get('foo'))
2989
def test_get_embedded_definition(self):
2990
conf1 = dict(yy='12')
2991
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2992
conf_stack = config.Stack([conf1, conf2])
2993
self.assertEquals('baz', conf_stack.get('foo'))
2995
3580
def test_get_for_empty_section_callable(self):
2996
3581
conf_stack = config.Stack([lambda : []])
2997
3582
self.assertEquals(None, conf_stack.get('foo'))
2999
3584
def test_get_for_broken_callable(self):
3000
3585
# Trying to use and invalid callable raises an exception on first use
3001
conf_stack = config.Stack([lambda : object()])
3586
conf_stack = config.Stack([object])
3002
3587
self.assertRaises(TypeError, conf_stack.get, 'foo')
3590
class TestStackWithSimpleStore(tests.TestCase):
3593
super(TestStackWithSimpleStore, self).setUp()
3594
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3595
self.registry = config.option_registry
3597
def get_conf(self, content=None):
3598
return config.MemoryStack(content)
3600
def test_override_value_from_env(self):
3601
self.registry.register(
3602
config.Option('foo', default='bar', override_from_env=['FOO']))
3603
self.overrideEnv('FOO', 'quux')
3604
# Env variable provides a default taking over the option one
3605
conf = self.get_conf('foo=store')
3606
self.assertEquals('quux', conf.get('foo'))
3608
def test_first_override_value_from_env_wins(self):
3609
self.registry.register(
3610
config.Option('foo', default='bar',
3611
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3612
self.overrideEnv('FOO', 'foo')
3613
self.overrideEnv('BAZ', 'baz')
3614
# The first env var set wins
3615
conf = self.get_conf('foo=store')
3616
self.assertEquals('foo', conf.get('foo'))
3619
class TestMemoryStack(tests.TestCase):
3622
conf = config.MemoryStack('foo=bar')
3623
self.assertEquals('bar', conf.get('foo'))
3626
conf = config.MemoryStack('foo=bar')
3627
conf.set('foo', 'baz')
3628
self.assertEquals('baz', conf.get('foo'))
3630
def test_no_content(self):
3631
conf = config.MemoryStack()
3632
# No content means no loading
3633
self.assertFalse(conf.store.is_loaded())
3634
self.assertRaises(NotImplementedError, conf.get, 'foo')
3635
# But a content can still be provided
3636
conf.store._load_from_string('foo=bar')
3637
self.assertEquals('bar', conf.get('foo'))
3005
3640
class TestStackWithTransport(tests.TestCaseWithTransport):
3007
3642
scenarios = [(key, {'get_stack': builder}) for key, builder
3037
3672
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3040
class TestStackGetWithConverter(TestStackGet):
3675
class TestStackGetWithConverter(tests.TestCase):
3042
3677
def setUp(self):
3043
3678
super(TestStackGetWithConverter, self).setUp()
3044
3679
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3045
3680
self.registry = config.option_registry
3047
def register_bool_option(self, name, default, invalid=None):
3048
b = config.Option(name, default=default, help='A boolean.',
3049
from_unicode=config.bool_from_store,
3682
def get_conf(self, content=None):
3683
return config.MemoryStack(content)
3685
def register_bool_option(self, name, default=None, default_from_env=None):
3686
b = config.Option(name, help='A boolean.',
3687
default=default, default_from_env=default_from_env,
3688
from_unicode=config.bool_from_store)
3051
3689
self.registry.register(b)
3053
def test_get_with_bool_not_defined_default_true(self):
3054
self.register_bool_option('foo', True)
3055
self.assertEquals(True, self.conf.get('foo'))
3057
def test_get_with_bool_not_defined_default_false(self):
3058
self.register_bool_option('foo', False)
3059
self.assertEquals(False, self.conf.get('foo'))
3061
def test_get_with_bool_converter_not_default(self):
3062
self.register_bool_option('foo', False)
3063
self.conf.store._load_from_string('foo=yes')
3064
self.assertEquals(True, self.conf.get('foo'))
3066
def test_get_with_bool_converter_invalid_string(self):
3067
self.register_bool_option('foo', False)
3068
self.conf.store._load_from_string('foo=not-a-boolean')
3069
self.assertEquals(False, self.conf.get('foo'))
3071
def test_get_with_bool_converter_invalid_list(self):
3072
self.register_bool_option('foo', False)
3073
self.conf.store._load_from_string('foo=not,a,boolean')
3074
self.assertEquals(False, self.conf.get('foo'))
3076
def test_get_invalid_warns(self):
3077
self.register_bool_option('foo', False, invalid='warning')
3078
self.conf.store._load_from_string('foo=not-a-boolean')
3081
warnings.append(args[0] % args[1:])
3082
self.overrideAttr(trace, 'warning', warning)
3083
self.assertEquals(False, self.conf.get('foo'))
3084
self.assertLength(1, warnings)
3085
self.assertEquals('Value "not-a-boolean" is not valid for "foo"',
3088
def test_get_invalid_errors(self):
3089
self.register_bool_option('foo', False, invalid='error')
3090
self.conf.store._load_from_string('foo=not-a-boolean')
3091
self.assertRaises(errors.ConfigOptionValueError, self.conf.get, 'foo')
3093
def register_integer_option(self, name, default):
3094
i = config.Option(name, default=default, help='An integer.',
3691
def test_get_default_bool_None(self):
3692
self.register_bool_option('foo')
3693
conf = self.get_conf('')
3694
self.assertEquals(None, conf.get('foo'))
3696
def test_get_default_bool_True(self):
3697
self.register_bool_option('foo', u'True')
3698
conf = self.get_conf('')
3699
self.assertEquals(True, conf.get('foo'))
3701
def test_get_default_bool_False(self):
3702
self.register_bool_option('foo', False)
3703
conf = self.get_conf('')
3704
self.assertEquals(False, conf.get('foo'))
3706
def test_get_default_bool_False_as_string(self):
3707
self.register_bool_option('foo', u'False')
3708
conf = self.get_conf('')
3709
self.assertEquals(False, conf.get('foo'))
3711
def test_get_default_bool_from_env_converted(self):
3712
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3713
self.overrideEnv('FOO', 'False')
3714
conf = self.get_conf('')
3715
self.assertEquals(False, conf.get('foo'))
3717
def test_get_default_bool_when_conversion_fails(self):
3718
self.register_bool_option('foo', default='True')
3719
conf = self.get_conf('foo=invalid boolean')
3720
self.assertEquals(True, conf.get('foo'))
3722
def register_integer_option(self, name,
3723
default=None, default_from_env=None):
3724
i = config.Option(name, help='An integer.',
3725
default=default, default_from_env=default_from_env,
3095
3726
from_unicode=config.int_from_store)
3096
3727
self.registry.register(i)
3098
def test_get_with_integer_not_defined_returns_default(self):
3099
self.register_integer_option('foo', 42)
3100
self.assertEquals(42, self.conf.get('foo'))
3102
def test_get_with_integer_converter_not_default(self):
3103
self.register_integer_option('foo', 42)
3104
self.conf.store._load_from_string('foo=16')
3105
self.assertEquals(16, self.conf.get('foo'))
3107
def test_get_with_integer_converter_invalid_string(self):
3108
# We don't set a default value
3109
self.register_integer_option('foo', None)
3110
self.conf.store._load_from_string('foo=forty-two')
3111
# No default value, so we should get None
3112
self.assertEquals(None, self.conf.get('foo'))
3114
def test_get_with_integer_converter_invalid_list(self):
3115
# We don't set a default value
3116
self.register_integer_option('foo', None)
3117
self.conf.store._load_from_string('foo=a,list')
3118
# No default value, so we should get None
3119
self.assertEquals(None, self.conf.get('foo'))
3121
def register_list_option(self, name, default):
3122
l = config.Option(name, default=default, help='A list.',
3123
from_unicode=config.list_from_store)
3729
def test_get_default_integer_None(self):
3730
self.register_integer_option('foo')
3731
conf = self.get_conf('')
3732
self.assertEquals(None, conf.get('foo'))
3734
def test_get_default_integer(self):
3735
self.register_integer_option('foo', 42)
3736
conf = self.get_conf('')
3737
self.assertEquals(42, conf.get('foo'))
3739
def test_get_default_integer_as_string(self):
3740
self.register_integer_option('foo', u'42')
3741
conf = self.get_conf('')
3742
self.assertEquals(42, conf.get('foo'))
3744
def test_get_default_integer_from_env(self):
3745
self.register_integer_option('foo', default_from_env=['FOO'])
3746
self.overrideEnv('FOO', '18')
3747
conf = self.get_conf('')
3748
self.assertEquals(18, conf.get('foo'))
3750
def test_get_default_integer_when_conversion_fails(self):
3751
self.register_integer_option('foo', default='12')
3752
conf = self.get_conf('foo=invalid integer')
3753
self.assertEquals(12, conf.get('foo'))
3755
def register_list_option(self, name, default=None, default_from_env=None):
3756
l = config.ListOption(name, help='A list.', default=default,
3757
default_from_env=default_from_env)
3124
3758
self.registry.register(l)
3126
def test_get_with_list_not_defined_returns_default(self):
3127
self.register_list_option('foo', [])
3128
self.assertEquals([], self.conf.get('foo'))
3130
def test_get_with_list_converter_nothing(self):
3131
self.register_list_option('foo', [1])
3132
self.conf.store._load_from_string('foo=')
3133
self.assertEquals([], self.conf.get('foo'))
3760
def test_get_default_list_None(self):
3761
self.register_list_option('foo')
3762
conf = self.get_conf('')
3763
self.assertEquals(None, conf.get('foo'))
3765
def test_get_default_list_empty(self):
3766
self.register_list_option('foo', '')
3767
conf = self.get_conf('')
3768
self.assertEquals([], conf.get('foo'))
3770
def test_get_default_list_from_env(self):
3771
self.register_list_option('foo', default_from_env=['FOO'])
3772
self.overrideEnv('FOO', '')
3773
conf = self.get_conf('')
3774
self.assertEquals([], conf.get('foo'))
3135
3776
def test_get_with_list_converter_no_item(self):
3136
self.register_list_option('foo', [1])
3137
self.conf.store._load_from_string('foo=,')
3138
self.assertEquals([], self.conf.get('foo'))
3140
def test_get_with_list_converter_one_boolean(self):
3141
self.register_list_option('foo', [1])
3142
self.conf.store._load_from_string('foo=True')
3143
# We get a list of one string
3144
self.assertEquals(['True'], self.conf.get('foo'))
3146
def test_get_with_list_converter_one_integer(self):
3147
self.register_list_option('foo', [1])
3148
self.conf.store._load_from_string('foo=2')
3149
# We get a list of one string
3150
self.assertEquals(['2'], self.conf.get('foo'))
3152
def test_get_with_list_converter_one_string(self):
3153
self.register_list_option('foo', ['foo'])
3154
self.conf.store._load_from_string('foo=bar')
3155
# We get a list of one string
3156
self.assertEquals(['bar'], self.conf.get('foo'))
3777
self.register_list_option('foo', None)
3778
conf = self.get_conf('foo=,')
3779
self.assertEquals([], conf.get('foo'))
3158
3781
def test_get_with_list_converter_many_items(self):
3159
self.register_list_option('foo', [1])
3160
self.conf.store._load_from_string('foo=m,o,r,e')
3161
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3782
self.register_list_option('foo', None)
3783
conf = self.get_conf('foo=m,o,r,e')
3784
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3786
def test_get_with_list_converter_embedded_spaces_many_items(self):
3787
self.register_list_option('foo', None)
3788
conf = self.get_conf('foo=" bar", "baz "')
3789
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3791
def test_get_with_list_converter_stripped_spaces_many_items(self):
3792
self.register_list_option('foo', None)
3793
conf = self.get_conf('foo= bar , baz ')
3794
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3797
class TestIterOptionRefs(tests.TestCase):
3798
"""iter_option_refs is a bit unusual, document some cases."""
3800
def assertRefs(self, expected, string):
3801
self.assertEquals(expected, list(config.iter_option_refs(string)))
3803
def test_empty(self):
3804
self.assertRefs([(False, '')], '')
3806
def test_no_refs(self):
3807
self.assertRefs([(False, 'foo bar')], 'foo bar')
3809
def test_single_ref(self):
3810
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3812
def test_broken_ref(self):
3813
self.assertRefs([(False, '{foo')], '{foo')
3815
def test_embedded_ref(self):
3816
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3819
def test_two_refs(self):
3820
self.assertRefs([(False, ''), (True, '{foo}'),
3821
(False, ''), (True, '{bar}'),
3825
def test_newline_in_refs_are_not_matched(self):
3826
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3829
class TestStackExpandOptions(tests.TestCaseWithTransport):
3832
super(TestStackExpandOptions, self).setUp()
3833
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3834
self.registry = config.option_registry
3835
self.conf = build_branch_stack(self)
3837
def assertExpansion(self, expected, string, env=None):
3838
self.assertEquals(expected, self.conf.expand_options(string, env))
3840
def test_no_expansion(self):
3841
self.assertExpansion('foo', 'foo')
3843
def test_expand_default_value(self):
3844
self.conf.store._load_from_string('bar=baz')
3845
self.registry.register(config.Option('foo', default=u'{bar}'))
3846
self.assertEquals('baz', self.conf.get('foo', expand=True))
3848
def test_expand_default_from_env(self):
3849
self.conf.store._load_from_string('bar=baz')
3850
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3851
self.overrideEnv('FOO', '{bar}')
3852
self.assertEquals('baz', self.conf.get('foo', expand=True))
3854
def test_expand_default_on_failed_conversion(self):
3855
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3856
self.registry.register(
3857
config.Option('foo', default=u'{bar}',
3858
from_unicode=config.int_from_store))
3859
self.assertEquals(42, self.conf.get('foo', expand=True))
3861
def test_env_adding_options(self):
3862
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3864
def test_env_overriding_options(self):
3865
self.conf.store._load_from_string('foo=baz')
3866
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3868
def test_simple_ref(self):
3869
self.conf.store._load_from_string('foo=xxx')
3870
self.assertExpansion('xxx', '{foo}')
3872
def test_unknown_ref(self):
3873
self.assertRaises(errors.ExpandingUnknownOption,
3874
self.conf.expand_options, '{foo}')
3876
def test_indirect_ref(self):
3877
self.conf.store._load_from_string('''
3881
self.assertExpansion('xxx', '{bar}')
3883
def test_embedded_ref(self):
3884
self.conf.store._load_from_string('''
3888
self.assertExpansion('xxx', '{{bar}}')
3890
def test_simple_loop(self):
3891
self.conf.store._load_from_string('foo={foo}')
3892
self.assertRaises(errors.OptionExpansionLoop,
3893
self.conf.expand_options, '{foo}')
3895
def test_indirect_loop(self):
3896
self.conf.store._load_from_string('''
3900
e = self.assertRaises(errors.OptionExpansionLoop,
3901
self.conf.expand_options, '{foo}')
3902
self.assertEquals('foo->bar->baz', e.refs)
3903
self.assertEquals('{foo}', e.string)
3905
def test_list(self):
3906
self.conf.store._load_from_string('''
3910
list={foo},{bar},{baz}
3912
self.registry.register(
3913
config.ListOption('list'))
3914
self.assertEquals(['start', 'middle', 'end'],
3915
self.conf.get('list', expand=True))
3917
def test_cascading_list(self):
3918
self.conf.store._load_from_string('''
3924
self.registry.register(
3925
config.ListOption('list'))
3926
self.assertEquals(['start', 'middle', 'end'],
3927
self.conf.get('list', expand=True))
3929
def test_pathologically_hidden_list(self):
3930
self.conf.store._load_from_string('''
3936
hidden={start}{middle}{end}
3938
# What matters is what the registration says, the conversion happens
3939
# only after all expansions have been performed
3940
self.registry.register(config.ListOption('hidden'))
3941
self.assertEquals(['bin', 'go'],
3942
self.conf.get('hidden', expand=True))
3945
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3948
super(TestStackCrossSectionsExpand, self).setUp()
3950
def get_config(self, location, string):
3953
# Since we don't save the config we won't strictly require to inherit
3954
# from TestCaseInTempDir, but an error occurs so quickly...
3955
c = config.LocationStack(location)
3956
c.store._load_from_string(string)
3959
def test_dont_cross_unrelated_section(self):
3960
c = self.get_config('/another/branch/path','''
3965
[/another/branch/path]
3968
self.assertRaises(errors.ExpandingUnknownOption,
3969
c.get, 'bar', expand=True)
3971
def test_cross_related_sections(self):
3972
c = self.get_config('/project/branch/path','''
3976
[/project/branch/path]
3979
self.assertEquals('quux', c.get('bar', expand=True))
3982
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3984
def test_cross_global_locations(self):
3985
l_store = config.LocationStore()
3986
l_store._load_from_string('''
3992
g_store = config.GlobalStore()
3993
g_store._load_from_string('''
3999
stack = config.LocationStack('/branch')
4000
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4001
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4004
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4006
def test_expand_locals_empty(self):
4007
l_store = config.LocationStore()
4008
l_store._load_from_string('''
4009
[/home/user/project]
4014
stack = config.LocationStack('/home/user/project/')
4015
self.assertEquals('', stack.get('base', expand=True))
4016
self.assertEquals('', stack.get('rel', expand=True))
4018
def test_expand_basename_locally(self):
4019
l_store = config.LocationStore()
4020
l_store._load_from_string('''
4021
[/home/user/project]
4025
stack = config.LocationStack('/home/user/project/branch')
4026
self.assertEquals('branch', stack.get('bfoo', expand=True))
4028
def test_expand_basename_locally_longer_path(self):
4029
l_store = config.LocationStore()
4030
l_store._load_from_string('''
4035
stack = config.LocationStack('/home/user/project/dir/branch')
4036
self.assertEquals('branch', stack.get('bfoo', expand=True))
4038
def test_expand_relpath_locally(self):
4039
l_store = config.LocationStore()
4040
l_store._load_from_string('''
4041
[/home/user/project]
4042
lfoo = loc-foo/{relpath}
4045
stack = config.LocationStack('/home/user/project/branch')
4046
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4048
def test_expand_relpath_unknonw_in_global(self):
4049
g_store = config.GlobalStore()
4050
g_store._load_from_string('''
4055
stack = config.LocationStack('/home/user/project/branch')
4056
self.assertRaises(errors.ExpandingUnknownOption,
4057
stack.get, 'gfoo', expand=True)
4059
def test_expand_local_option_locally(self):
4060
l_store = config.LocationStore()
4061
l_store._load_from_string('''
4062
[/home/user/project]
4063
lfoo = loc-foo/{relpath}
4067
g_store = config.GlobalStore()
4068
g_store._load_from_string('''
4074
stack = config.LocationStack('/home/user/project/branch')
4075
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4076
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4078
def test_locals_dont_leak(self):
4079
"""Make sure we chose the right local in presence of several sections.
4081
l_store = config.LocationStore()
4082
l_store._load_from_string('''
4084
lfoo = loc-foo/{relpath}
4085
[/home/user/project]
4086
lfoo = loc-foo/{relpath}
4089
stack = config.LocationStack('/home/user/project/branch')
4090
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4091
stack = config.LocationStack('/home/user/bar/baz')
4092
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3164
4096
class TestStackSet(TestStackWithTransport):
3166
4098
def test_simple_set(self):
3167
4099
conf = self.get_stack(self)
3168
conf.store._load_from_string('foo=bar')
3169
self.assertEquals('bar', conf.get('foo'))
4100
self.assertEquals(None, conf.get('foo'))
3170
4101
conf.set('foo', 'baz')
3171
4102
# Did we get it back ?
3172
4103
self.assertEquals('baz', conf.get('foo'))