2275
2181
opt = config.Option('foo', default='bar')
2276
2182
self.assertEquals('bar', opt.get_default())
2278
def test_default_value_from_env(self):
2279
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2280
self.overrideEnv('FOO', 'quux')
2281
# Env variable provides a default taking over the option one
2282
self.assertEquals('quux', opt.get_default())
2284
def test_first_default_value_from_env_wins(self):
2285
opt = config.Option('foo', default='bar',
2286
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2287
self.overrideEnv('FOO', 'foo')
2288
self.overrideEnv('BAZ', 'baz')
2289
# The first env var set wins
2290
self.assertEquals('foo', opt.get_default())
2292
def test_not_supported_list_default_value(self):
2293
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2295
def test_not_supported_object_default_value(self):
2296
self.assertRaises(AssertionError, config.Option, 'foo',
2300
class TestOptionConverterMixin(object):
2302
def assertConverted(self, expected, opt, value):
2303
self.assertEquals(expected, opt.convert_from_unicode(value))
2305
def assertWarns(self, opt, value):
2308
warnings.append(args[0] % args[1:])
2309
self.overrideAttr(trace, 'warning', warning)
2310
self.assertEquals(None, opt.convert_from_unicode(value))
2311
self.assertLength(1, warnings)
2313
'Value "%s" is not valid for "%s"' % (value, opt.name),
2316
def assertErrors(self, opt, value):
2317
self.assertRaises(errors.ConfigOptionValueError,
2318
opt.convert_from_unicode, value)
2320
def assertConvertInvalid(self, opt, invalid_value):
2322
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2323
opt.invalid = 'warning'
2324
self.assertWarns(opt, invalid_value)
2325
opt.invalid = 'error'
2326
self.assertErrors(opt, invalid_value)
2329
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2331
def get_option(self):
2332
return config.Option('foo', help='A boolean.',
2333
from_unicode=config.bool_from_store)
2335
def test_convert_invalid(self):
2336
opt = self.get_option()
2337
# A string that is not recognized as a boolean
2338
self.assertConvertInvalid(opt, u'invalid-boolean')
2339
# A list of strings is never recognized as a boolean
2340
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2342
def test_convert_valid(self):
2343
opt = self.get_option()
2344
self.assertConverted(True, opt, u'True')
2345
self.assertConverted(True, opt, u'1')
2346
self.assertConverted(False, opt, u'False')
2349
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2351
def get_option(self):
2352
return config.Option('foo', help='An integer.',
2353
from_unicode=config.int_from_store)
2355
def test_convert_invalid(self):
2356
opt = self.get_option()
2357
# A string that is not recognized as an integer
2358
self.assertConvertInvalid(opt, u'forty-two')
2359
# A list of strings is never recognized as an integer
2360
self.assertConvertInvalid(opt, [u'a', u'list'])
2362
def test_convert_valid(self):
2363
opt = self.get_option()
2364
self.assertConverted(16, opt, u'16')
2366
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2368
def get_option(self):
2369
return config.Option('foo', help='A list.',
2370
from_unicode=config.list_from_store)
2372
def test_convert_invalid(self):
2373
# No string is invalid as all forms can be converted to a list
2376
def test_convert_valid(self):
2377
opt = self.get_option()
2378
# An empty string is an empty list
2379
self.assertConverted([], opt, '') # Using a bare str() just in case
2380
self.assertConverted([], opt, u'')
2382
self.assertConverted([u'True'], opt, u'True')
2384
self.assertConverted([u'42'], opt, u'42')
2386
self.assertConverted([u'bar'], opt, u'bar')
2387
# A list remains a list (configObj will turn a string containing commas
2388
# into a list, but that's not what we're testing here)
2389
self.assertConverted([u'foo', u'1', u'True'],
2390
opt, [u'foo', u'1', u'True'])
2393
class TestOptionConverterMixin(object):
2395
def assertConverted(self, expected, opt, value):
2396
self.assertEquals(expected, opt.convert_from_unicode(value))
2398
def assertWarns(self, opt, value):
2401
warnings.append(args[0] % args[1:])
2402
self.overrideAttr(trace, 'warning', warning)
2403
self.assertEquals(None, opt.convert_from_unicode(value))
2404
self.assertLength(1, warnings)
2406
'Value "%s" is not valid for "%s"' % (value, opt.name),
2409
def assertErrors(self, opt, value):
2410
self.assertRaises(errors.ConfigOptionValueError,
2411
opt.convert_from_unicode, value)
2413
def assertConvertInvalid(self, opt, invalid_value):
2415
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2416
opt.invalid = 'warning'
2417
self.assertWarns(opt, invalid_value)
2418
opt.invalid = 'error'
2419
self.assertErrors(opt, invalid_value)
2422
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2424
def get_option(self):
2425
return config.Option('foo', help='A boolean.',
2426
from_unicode=config.bool_from_store)
2428
def test_convert_invalid(self):
2429
opt = self.get_option()
2430
# A string that is not recognized as a boolean
2431
self.assertConvertInvalid(opt, u'invalid-boolean')
2432
# A list of strings is never recognized as a boolean
2433
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
self.assertConverted(True, opt, u'True')
2438
self.assertConverted(True, opt, u'1')
2439
self.assertConverted(False, opt, u'False')
2442
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2444
def get_option(self):
2445
return config.Option('foo', help='An integer.',
2446
from_unicode=config.int_from_store)
2448
def test_convert_invalid(self):
2449
opt = self.get_option()
2450
# A string that is not recognized as an integer
2451
self.assertConvertInvalid(opt, u'forty-two')
2452
# A list of strings is never recognized as an integer
2453
self.assertConvertInvalid(opt, [u'a', u'list'])
2455
def test_convert_valid(self):
2456
opt = self.get_option()
2457
self.assertConverted(16, opt, u'16')
2460
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2462
def get_option(self):
2463
return config.Option('foo', help='A list.',
2464
from_unicode=config.list_from_store)
2466
def test_convert_invalid(self):
2467
opt = self.get_option()
2468
# We don't even try to convert a list into a list, we only expect
2470
self.assertConvertInvalid(opt, [1])
2471
# No string is invalid as all forms can be converted to a list
2473
def test_convert_valid(self):
2474
opt = self.get_option()
2475
# An empty string is an empty list
2476
self.assertConverted([], opt, '') # Using a bare str() just in case
2477
self.assertConverted([], opt, u'')
2479
self.assertConverted([u'True'], opt, u'True')
2481
self.assertConverted([u'42'], opt, u'42')
2483
self.assertConverted([u'bar'], opt, u'bar')
2486
2185
class TestOptionRegistry(tests.TestCase):
2488
2187
def setUp(self):
2489
2188
super(TestOptionRegistry, self).setUp()
2490
2189
# Always start with an empty registry
2491
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2190
self.overrideAttr(config, 'option_registry', registry.Registry())
2492
2191
self.registry = config.option_registry
2494
2193
def test_register(self):
2495
2194
opt = config.Option('foo')
2496
self.registry.register(opt)
2195
self.registry.register('foo', opt)
2497
2196
self.assertIs(opt, self.registry.get('foo'))
2198
lazy_option = config.Option('lazy_foo')
2200
def test_register_lazy(self):
2201
self.registry.register_lazy('foo', self.__module__,
2202
'TestOptionRegistry.lazy_option')
2203
self.assertIs(self.lazy_option, self.registry.get('foo'))
2499
2205
def test_registered_help(self):
2500
opt = config.Option('foo', help='A simple option')
2501
self.registry.register(opt)
2206
opt = config.Option('foo')
2207
self.registry.register('foo', opt, help='A simple option')
2502
2208
self.assertEquals('A simple option', self.registry.get_help('foo'))
2504
lazy_option = config.Option('lazy_foo', help='Lazy help')
2506
def test_register_lazy(self):
2507
self.registry.register_lazy('lazy_foo', self.__module__,
2508
'TestOptionRegistry.lazy_option')
2509
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2511
def test_registered_lazy_help(self):
2512
self.registry.register_lazy('lazy_foo', self.__module__,
2513
'TestOptionRegistry.lazy_option')
2514
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2517
2211
class TestRegisteredOptions(tests.TestCase):
2518
2212
"""All registered options should verify some constraints."""
3333
2897
class TestStackGet(TestStackWithTransport):
3336
super(TestStackGet, self).setUp()
3337
self.conf = self.get_stack(self)
3339
2899
def test_get_for_empty_stack(self):
3340
self.assertEquals(None, self.conf.get('foo'))
2900
conf = self.get_stack(self)
2901
self.assertEquals(None, conf.get('foo'))
3342
2903
def test_get_hook(self):
3343
self.conf.set('foo', 'bar')
2904
conf = self.get_stack(self)
2905
conf.store._load_from_string('foo=bar')
3345
2907
def hook(*args):
3346
2908
calls.append(args)
3347
2909
config.ConfigHooks.install_named_hook('get', hook, None)
3348
2910
self.assertLength(0, calls)
3349
value = self.conf.get('foo')
2911
value = conf.get('foo')
3350
2912
self.assertEquals('bar', value)
3351
2913
self.assertLength(1, calls)
3352
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3355
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3358
super(TestStackGetWithConverter, self).setUp()
3359
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3360
self.registry = config.option_registry
3361
# We just want a simple stack with a simple store so we can inject
3362
# whatever content the tests need without caring about what section
3363
# names are valid for a given store/stack.
3364
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3365
self.conf = config.Stack([store.get_sections], store)
3367
def register_bool_option(self, name, default=None, default_from_env=None):
3368
b = config.Option(name, help='A boolean.',
3369
default=default, default_from_env=default_from_env,
3370
from_unicode=config.bool_from_store)
3371
self.registry.register(b)
3373
def test_get_default_bool_None(self):
3374
self.register_bool_option('foo')
3375
self.assertEquals(None, self.conf.get('foo'))
3377
def test_get_default_bool_True(self):
3378
self.register_bool_option('foo', u'True')
3379
self.assertEquals(True, self.conf.get('foo'))
3381
def test_get_default_bool_False(self):
3382
self.register_bool_option('foo', False)
3383
self.assertEquals(False, self.conf.get('foo'))
3385
def test_get_default_bool_False_as_string(self):
3386
self.register_bool_option('foo', u'False')
3387
self.assertEquals(False, self.conf.get('foo'))
3389
def test_get_default_bool_from_env_converted(self):
3390
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3391
self.overrideEnv('FOO', 'False')
3392
self.assertEquals(False, self.conf.get('foo'))
3394
def test_get_default_bool_when_conversion_fails(self):
3395
self.register_bool_option('foo', default='True')
3396
self.conf.store._load_from_string('foo=invalid boolean')
3397
self.assertEquals(True, self.conf.get('foo'))
3399
def register_integer_option(self, name,
3400
default=None, default_from_env=None):
3401
i = config.Option(name, help='An integer.',
3402
default=default, default_from_env=default_from_env,
3403
from_unicode=config.int_from_store)
3404
self.registry.register(i)
3406
def test_get_default_integer_None(self):
3407
self.register_integer_option('foo')
3408
self.assertEquals(None, self.conf.get('foo'))
3410
def test_get_default_integer(self):
3411
self.register_integer_option('foo', 42)
3412
self.assertEquals(42, self.conf.get('foo'))
3414
def test_get_default_integer_as_string(self):
3415
self.register_integer_option('foo', u'42')
3416
self.assertEquals(42, self.conf.get('foo'))
3418
def test_get_default_integer_from_env(self):
3419
self.register_integer_option('foo', default_from_env=['FOO'])
3420
self.overrideEnv('FOO', '18')
3421
self.assertEquals(18, self.conf.get('foo'))
3423
def test_get_default_integer_when_conversion_fails(self):
3424
self.register_integer_option('foo', default='12')
3425
self.conf.store._load_from_string('foo=invalid integer')
3426
self.assertEquals(12, self.conf.get('foo'))
3428
def register_list_option(self, name, default=None, default_from_env=None):
3429
l = config.Option(name, help='A list.',
3430
default=default, default_from_env=default_from_env,
3431
from_unicode=config.list_from_store)
3432
self.registry.register(l)
3434
def test_get_default_list_None(self):
3435
self.register_list_option('foo')
3436
self.assertEquals(None, self.conf.get('foo'))
3438
def test_get_default_list_empty(self):
3439
self.register_list_option('foo', '')
3440
self.assertEquals([], self.conf.get('foo'))
3442
def test_get_default_list_from_env(self):
3443
self.register_list_option('foo', default_from_env=['FOO'])
3444
self.overrideEnv('FOO', '')
3445
self.assertEquals([], self.conf.get('foo'))
3447
def test_get_with_list_converter_no_item(self):
3448
self.register_list_option('foo', None)
3449
self.conf.store._load_from_string('foo=,')
3450
self.assertEquals([], self.conf.get('foo'))
3452
def test_get_with_list_converter_many_items(self):
3453
self.register_list_option('foo', None)
3454
self.conf.store._load_from_string('foo=m,o,r,e')
3455
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3457
def test_get_with_list_converter_embedded_spaces_many_items(self):
3458
self.register_list_option('foo', None)
3459
self.conf.store._load_from_string('foo=" bar", "baz "')
3460
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3462
def test_get_with_list_converter_stripped_spaces_many_items(self):
3463
self.register_list_option('foo', None)
3464
self.conf.store._load_from_string('foo= bar , baz ')
3465
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3468
class TestIterOptionRefs(tests.TestCase):
3469
"""iter_option_refs is a bit unusual, document some cases."""
3471
def assertRefs(self, expected, string):
3472
self.assertEquals(expected, list(config.iter_option_refs(string)))
3474
def test_empty(self):
3475
self.assertRefs([(False, '')], '')
3477
def test_no_refs(self):
3478
self.assertRefs([(False, 'foo bar')], 'foo bar')
3480
def test_single_ref(self):
3481
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3483
def test_broken_ref(self):
3484
self.assertRefs([(False, '{foo')], '{foo')
3486
def test_embedded_ref(self):
3487
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3490
def test_two_refs(self):
3491
self.assertRefs([(False, ''), (True, '{foo}'),
3492
(False, ''), (True, '{bar}'),
3497
class TestStackExpandOptions(tests.TestCaseWithTransport):
3500
super(TestStackExpandOptions, self).setUp()
3501
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3502
self.registry = config.option_registry
3503
self.conf = build_branch_stack(self)
3505
def assertExpansion(self, expected, string, env=None):
3506
self.assertEquals(expected, self.conf.expand_options(string, env))
3508
def test_no_expansion(self):
3509
self.assertExpansion('foo', 'foo')
3511
def test_expand_default_value(self):
3512
self.conf.store._load_from_string('bar=baz')
3513
self.registry.register(config.Option('foo', default=u'{bar}'))
3514
self.assertEquals('baz', self.conf.get('foo', expand=True))
3516
def test_expand_default_from_env(self):
3517
self.conf.store._load_from_string('bar=baz')
3518
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3519
self.overrideEnv('FOO', '{bar}')
3520
self.assertEquals('baz', self.conf.get('foo', expand=True))
3522
def test_expand_default_on_failed_conversion(self):
3523
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3524
self.registry.register(
3525
config.Option('foo', default=u'{bar}',
3526
from_unicode=config.int_from_store))
3527
self.assertEquals(42, self.conf.get('foo', expand=True))
3529
def test_env_adding_options(self):
3530
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3532
def test_env_overriding_options(self):
3533
self.conf.store._load_from_string('foo=baz')
3534
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3536
def test_simple_ref(self):
3537
self.conf.store._load_from_string('foo=xxx')
3538
self.assertExpansion('xxx', '{foo}')
3540
def test_unknown_ref(self):
3541
self.assertRaises(errors.ExpandingUnknownOption,
3542
self.conf.expand_options, '{foo}')
3544
def test_indirect_ref(self):
3545
self.conf.store._load_from_string('''
3549
self.assertExpansion('xxx', '{bar}')
3551
def test_embedded_ref(self):
3552
self.conf.store._load_from_string('''
3556
self.assertExpansion('xxx', '{{bar}}')
3558
def test_simple_loop(self):
3559
self.conf.store._load_from_string('foo={foo}')
3560
self.assertRaises(errors.OptionExpansionLoop,
3561
self.conf.expand_options, '{foo}')
3563
def test_indirect_loop(self):
3564
self.conf.store._load_from_string('''
3568
e = self.assertRaises(errors.OptionExpansionLoop,
3569
self.conf.expand_options, '{foo}')
3570
self.assertEquals('foo->bar->baz', e.refs)
3571
self.assertEquals('{foo}', e.string)
3573
def test_list(self):
3574
self.conf.store._load_from_string('''
3578
list={foo},{bar},{baz}
3580
self.registry.register(
3581
config.Option('list', from_unicode=config.list_from_store))
3582
self.assertEquals(['start', 'middle', 'end'],
3583
self.conf.get('list', expand=True))
3585
def test_cascading_list(self):
3586
self.conf.store._load_from_string('''
3592
self.registry.register(
3593
config.Option('list', from_unicode=config.list_from_store))
3594
self.assertEquals(['start', 'middle', 'end'],
3595
self.conf.get('list', expand=True))
3597
def test_pathologically_hidden_list(self):
3598
self.conf.store._load_from_string('''
3604
hidden={start}{middle}{end}
3606
# What matters is what the registration says, the conversion happens
3607
# only after all expansions have been performed
3608
self.registry.register(
3609
config.Option('hidden', from_unicode=config.list_from_store))
3610
self.assertEquals(['bin', 'go'],
3611
self.conf.get('hidden', expand=True))
3614
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3617
super(TestStackCrossSectionsExpand, self).setUp()
3619
def get_config(self, location, string):
3622
# Since we don't save the config we won't strictly require to inherit
3623
# from TestCaseInTempDir, but an error occurs so quickly...
3624
c = config.LocationStack(location)
3625
c.store._load_from_string(string)
3628
def test_dont_cross_unrelated_section(self):
3629
c = self.get_config('/another/branch/path','''
3634
[/another/branch/path]
3637
self.assertRaises(errors.ExpandingUnknownOption,
3638
c.get, 'bar', expand=True)
3640
def test_cross_related_sections(self):
3641
c = self.get_config('/project/branch/path','''
3645
[/project/branch/path]
3648
self.assertEquals('quux', c.get('bar', expand=True))
3651
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3653
def test_cross_global_locations(self):
3654
l_store = config.LocationStore()
3655
l_store._load_from_string('''
3661
g_store = config.GlobalStore()
3662
g_store._load_from_string('''
3668
stack = config.LocationStack('/branch')
3669
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3670
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3673
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3675
def test_expand_locals_empty(self):
3676
l_store = config.LocationStore()
3677
l_store._load_from_string('''
3678
[/home/user/project]
3683
stack = config.LocationStack('/home/user/project/')
3684
self.assertEquals('', stack.get('base', expand=True))
3685
self.assertEquals('', stack.get('rel', expand=True))
3687
def test_expand_basename_locally(self):
3688
l_store = config.LocationStore()
3689
l_store._load_from_string('''
3690
[/home/user/project]
3694
stack = config.LocationStack('/home/user/project/branch')
3695
self.assertEquals('branch', stack.get('bfoo', expand=True))
3697
def test_expand_basename_locally_longer_path(self):
3698
l_store = config.LocationStore()
3699
l_store._load_from_string('''
3704
stack = config.LocationStack('/home/user/project/dir/branch')
3705
self.assertEquals('branch', stack.get('bfoo', expand=True))
3707
def test_expand_relpath_locally(self):
3708
l_store = config.LocationStore()
3709
l_store._load_from_string('''
3710
[/home/user/project]
3711
lfoo = loc-foo/{relpath}
3714
stack = config.LocationStack('/home/user/project/branch')
3715
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3717
def test_expand_relpath_unknonw_in_global(self):
3718
g_store = config.GlobalStore()
3719
g_store._load_from_string('''
3724
stack = config.LocationStack('/home/user/project/branch')
3725
self.assertRaises(errors.ExpandingUnknownOption,
3726
stack.get, 'gfoo', expand=True)
3728
def test_expand_local_option_locally(self):
3729
l_store = config.LocationStore()
3730
l_store._load_from_string('''
3731
[/home/user/project]
3732
lfoo = loc-foo/{relpath}
3736
g_store = config.GlobalStore()
3737
g_store._load_from_string('''
3743
stack = config.LocationStack('/home/user/project/branch')
3744
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3745
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3747
def test_locals_dont_leak(self):
3748
"""Make sure we chose the right local in presence of several sections.
3750
l_store = config.LocationStore()
3751
l_store._load_from_string('''
3753
lfoo = loc-foo/{relpath}
3754
[/home/user/project]
3755
lfoo = loc-foo/{relpath}
3758
stack = config.LocationStack('/home/user/project/branch')
3759
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3760
stack = config.LocationStack('/home/user/bar/baz')
3761
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
2914
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3765
2917
class TestStackSet(TestStackWithTransport):
3767
2919
def test_simple_set(self):
3768
2920
conf = self.get_stack(self)
3769
self.assertEquals(None, conf.get('foo'))
2921
conf.store._load_from_string('foo=bar')
2922
self.assertEquals('bar', conf.get('foo'))
3770
2923
conf.set('foo', 'baz')
3771
2924
# Did we get it back ?
3772
2925
self.assertEquals('baz', conf.get('foo'))