2220
2189
def test_save_hook_remote_bzrdir(self):
2221
2190
remote_branch = branch.Branch.open(self.get_url('tree'))
2222
2191
self.addCleanup(remote_branch.lock_write().unlock)
2223
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2192
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2224
2193
self.assertSaveHook(remote_bzrdir._get_config())
2227
class TestOptionNames(tests.TestCase):
2229
def is_valid(self, name):
2230
return config._option_ref_re.match('{%s}' % name) is not None
2232
def test_valid_names(self):
2233
self.assertTrue(self.is_valid('foo'))
2234
self.assertTrue(self.is_valid('foo.bar'))
2235
self.assertTrue(self.is_valid('f1'))
2236
self.assertTrue(self.is_valid('_'))
2237
self.assertTrue(self.is_valid('__bar__'))
2238
self.assertTrue(self.is_valid('a_'))
2239
self.assertTrue(self.is_valid('a1'))
2241
def test_invalid_names(self):
2242
self.assertFalse(self.is_valid(' foo'))
2243
self.assertFalse(self.is_valid('foo '))
2244
self.assertFalse(self.is_valid('1'))
2245
self.assertFalse(self.is_valid('1,2'))
2246
self.assertFalse(self.is_valid('foo$'))
2247
self.assertFalse(self.is_valid('!foo'))
2248
self.assertFalse(self.is_valid('foo.'))
2249
self.assertFalse(self.is_valid('foo..bar'))
2250
self.assertFalse(self.is_valid('{}'))
2251
self.assertFalse(self.is_valid('{a}'))
2252
self.assertFalse(self.is_valid('a\n'))
2254
def assertSingleGroup(self, reference):
2255
# the regexp is used with split and as such should match the reference
2256
# *only*, if more groups needs to be defined, (?:...) should be used.
2257
m = config._option_ref_re.match('{a}')
2258
self.assertLength(1, m.groups())
2260
def test_valid_references(self):
2261
self.assertSingleGroup('{a}')
2262
self.assertSingleGroup('{{a}}')
2265
2196
class TestOption(tests.TestCase):
2267
2198
def test_default_value(self):
2268
2199
opt = config.Option('foo', default='bar')
2269
2200
self.assertEquals('bar', opt.get_default())
2271
def test_callable_default_value(self):
2272
def bar_as_unicode():
2274
opt = config.Option('foo', default=bar_as_unicode)
2275
self.assertEquals('bar', opt.get_default())
2277
def test_default_value_from_env(self):
2278
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2279
self.overrideEnv('FOO', 'quux')
2280
# Env variable provides a default taking over the option one
2281
self.assertEquals('quux', opt.get_default())
2283
def test_first_default_value_from_env_wins(self):
2284
opt = config.Option('foo', default='bar',
2285
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2286
self.overrideEnv('FOO', 'foo')
2287
self.overrideEnv('BAZ', 'baz')
2288
# The first env var set wins
2289
self.assertEquals('foo', opt.get_default())
2291
def test_not_supported_list_default_value(self):
2292
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2294
def test_not_supported_object_default_value(self):
2295
self.assertRaises(AssertionError, config.Option, 'foo',
2298
def test_not_supported_callable_default_value_not_unicode(self):
2299
def bar_not_unicode():
2301
opt = config.Option('foo', default=bar_not_unicode)
2302
self.assertRaises(AssertionError, opt.get_default)
2304
def test_get_help_topic(self):
2305
opt = config.Option('foo')
2306
self.assertEquals('foo', opt.get_help_topic())
2309
class TestOptionConverterMixin(object):
2311
def assertConverted(self, expected, opt, value):
2312
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2314
def assertWarns(self, opt, value):
2317
warnings.append(args[0] % args[1:])
2318
self.overrideAttr(trace, 'warning', warning)
2319
self.assertEquals(None, opt.convert_from_unicode(None, value))
2320
self.assertLength(1, warnings)
2322
'Value "%s" is not valid for "%s"' % (value, opt.name),
2325
def assertErrors(self, opt, value):
2326
self.assertRaises(errors.ConfigOptionValueError,
2327
opt.convert_from_unicode, None, value)
2329
def assertConvertInvalid(self, opt, invalid_value):
2331
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2332
opt.invalid = 'warning'
2333
self.assertWarns(opt, invalid_value)
2334
opt.invalid = 'error'
2335
self.assertErrors(opt, invalid_value)
2338
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2340
def get_option(self):
2341
return config.Option('foo', help='A boolean.',
2342
from_unicode=config.bool_from_store)
2344
def test_convert_invalid(self):
2345
opt = self.get_option()
2346
# A string that is not recognized as a boolean
2347
self.assertConvertInvalid(opt, u'invalid-boolean')
2348
# A list of strings is never recognized as a boolean
2349
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2351
def test_convert_valid(self):
2352
opt = self.get_option()
2353
self.assertConverted(True, opt, u'True')
2354
self.assertConverted(True, opt, u'1')
2355
self.assertConverted(False, opt, u'False')
2358
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2360
def get_option(self):
2361
return config.Option('foo', help='An integer.',
2362
from_unicode=config.int_from_store)
2364
def test_convert_invalid(self):
2365
opt = self.get_option()
2366
# A string that is not recognized as an integer
2367
self.assertConvertInvalid(opt, u'forty-two')
2368
# A list of strings is never recognized as an integer
2369
self.assertConvertInvalid(opt, [u'a', u'list'])
2371
def test_convert_valid(self):
2372
opt = self.get_option()
2373
self.assertConverted(16, opt, u'16')
2376
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2378
def get_option(self):
2379
return config.Option('foo', help='An integer in SI units.',
2380
from_unicode=config.int_SI_from_store)
2382
def test_convert_invalid(self):
2383
opt = self.get_option()
2384
self.assertConvertInvalid(opt, u'not-a-unit')
2385
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2386
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2387
self.assertConvertInvalid(opt, u'1GG')
2388
self.assertConvertInvalid(opt, u'1Mbb')
2389
self.assertConvertInvalid(opt, u'1MM')
2391
def test_convert_valid(self):
2392
opt = self.get_option()
2393
self.assertConverted(int(5e3), opt, u'5kb')
2394
self.assertConverted(int(5e6), opt, u'5M')
2395
self.assertConverted(int(5e6), opt, u'5MB')
2396
self.assertConverted(int(5e9), opt, u'5g')
2397
self.assertConverted(int(5e9), opt, u'5gB')
2398
self.assertConverted(100, opt, u'100')
2401
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2403
def get_option(self):
2404
return config.ListOption('foo', help='A list.')
2406
def test_convert_invalid(self):
2407
opt = self.get_option()
2408
# We don't even try to convert a list into a list, we only expect
2410
self.assertConvertInvalid(opt, [1])
2411
# No string is invalid as all forms can be converted to a list
2413
def test_convert_valid(self):
2414
opt = self.get_option()
2415
# An empty string is an empty list
2416
self.assertConverted([], opt, '') # Using a bare str() just in case
2417
self.assertConverted([], opt, u'')
2419
self.assertConverted([u'True'], opt, u'True')
2421
self.assertConverted([u'42'], opt, u'42')
2423
self.assertConverted([u'bar'], opt, u'bar')
2426
class TestRegistryOption(tests.TestCase, TestOptionConverterMixin):
2428
def get_option(self, registry):
2429
return config.RegistryOption('foo', registry,
2430
help='A registry option.')
2432
def test_convert_invalid(self):
2433
registry = _mod_registry.Registry()
2434
opt = self.get_option(registry)
2435
self.assertConvertInvalid(opt, [1])
2436
self.assertConvertInvalid(opt, u"notregistered")
2438
def test_convert_valid(self):
2439
registry = _mod_registry.Registry()
2440
registry.register("someval", 1234)
2441
opt = self.get_option(registry)
2442
# Using a bare str() just in case
2443
self.assertConverted(1234, opt, "someval")
2444
self.assertConverted(1234, opt, u'someval')
2445
self.assertConverted(None, opt, None)
2447
def test_help(self):
2448
registry = _mod_registry.Registry()
2449
registry.register("someval", 1234, help="some option")
2450
registry.register("dunno", 1234, help="some other option")
2451
opt = self.get_option(registry)
2453
'A registry option.\n'
2455
'The following values are supported:\n'
2456
' dunno - some other option\n'
2457
' someval - some option\n',
2460
def test_get_help_text(self):
2461
registry = _mod_registry.Registry()
2462
registry.register("someval", 1234, help="some option")
2463
registry.register("dunno", 1234, help="some other option")
2464
opt = self.get_option(registry)
2466
'A registry option.\n'
2468
'The following values are supported:\n'
2469
' dunno - some other option\n'
2470
' someval - some option\n',
2471
opt.get_help_text())
2474
2203
class TestOptionRegistry(tests.TestCase):
2476
2205
def setUp(self):
2477
2206
super(TestOptionRegistry, self).setUp()
2478
2207
# Always start with an empty registry
2479
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2208
self.overrideAttr(config, 'option_registry', registry.Registry())
2480
2209
self.registry = config.option_registry
2482
2211
def test_register(self):
2483
2212
opt = config.Option('foo')
2484
self.registry.register(opt)
2213
self.registry.register('foo', opt)
2485
2214
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'))
2487
2223
def test_registered_help(self):
2488
opt = config.Option('foo', help='A simple option')
2489
self.registry.register(opt)
2224
opt = config.Option('foo')
2225
self.registry.register('foo', opt, help='A simple option')
2490
2226
self.assertEquals('A simple option', self.registry.get_help('foo'))
2492
def test_dont_register_illegal_name(self):
2493
self.assertRaises(errors.IllegalOptionName,
2494
self.registry.register, config.Option(' foo'))
2495
self.assertRaises(errors.IllegalOptionName,
2496
self.registry.register, config.Option('bar,'))
2498
lazy_option = config.Option('lazy_foo', help='Lazy help')
2500
def test_register_lazy(self):
2501
self.registry.register_lazy('lazy_foo', self.__module__,
2502
'TestOptionRegistry.lazy_option')
2503
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2505
def test_registered_lazy_help(self):
2506
self.registry.register_lazy('lazy_foo', self.__module__,
2507
'TestOptionRegistry.lazy_option')
2508
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2510
def test_dont_lazy_register_illegal_name(self):
2511
# This is where the root cause of http://pad.lv/1235099 is better
2512
# understood: 'register_lazy' doc string mentions that key should match
2513
# the option name which indirectly requires that the option name is a
2514
# valid python identifier. We violate that rule here (using a key that
2515
# doesn't match the option name) to test the option name checking.
2516
self.assertRaises(errors.IllegalOptionName,
2517
self.registry.register_lazy, ' foo', self.__module__,
2518
'TestOptionRegistry.lazy_option')
2519
self.assertRaises(errors.IllegalOptionName,
2520
self.registry.register_lazy, '1,2', self.__module__,
2521
'TestOptionRegistry.lazy_option')
2524
2229
class TestRegisteredOptions(tests.TestCase):
2525
2230
"""All registered options should verify some constraints."""
2726
2371
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2729
class TestStoreQuoting(TestStore):
2731
scenarios = [(key, {'get_store': builder}) for key, builder
2732
in config.test_store_builder_registry.iteritems()]
2735
super(TestStoreQuoting, self).setUp()
2736
self.store = self.get_store(self)
2737
# We need a loaded store but any content will do
2738
self.store._load_from_string('')
2740
def assertIdempotent(self, s):
2741
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2743
What matters here is that option values, as they appear in a store, can
2744
be safely round-tripped out of the store and back.
2746
:param s: A string, quoted if required.
2748
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2749
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2751
def test_empty_string(self):
2752
if isinstance(self.store, config.IniFileStore):
2753
# configobj._quote doesn't handle empty values
2754
self.assertRaises(AssertionError,
2755
self.assertIdempotent, '')
2757
self.assertIdempotent('')
2758
# But quoted empty strings are ok
2759
self.assertIdempotent('""')
2761
def test_embedded_spaces(self):
2762
self.assertIdempotent('" a b c "')
2764
def test_embedded_commas(self):
2765
self.assertIdempotent('" a , b c "')
2767
def test_simple_comma(self):
2768
if isinstance(self.store, config.IniFileStore):
2769
# configobj requires that lists are special-cased
2770
self.assertRaises(AssertionError,
2771
self.assertIdempotent, ',')
2773
self.assertIdempotent(',')
2774
# When a single comma is required, quoting is also required
2775
self.assertIdempotent('","')
2777
def test_list(self):
2778
if isinstance(self.store, config.IniFileStore):
2779
# configobj requires that lists are special-cased
2780
self.assertRaises(AssertionError,
2781
self.assertIdempotent, 'a,b')
2783
self.assertIdempotent('a,b')
2786
class TestDictFromStore(tests.TestCase):
2788
def test_unquote_not_string(self):
2789
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2790
value = conf.get('a_section')
2791
# Urgh, despite 'conf' asking for the no-name section, we get the
2792
# content of another section as a dict o_O
2793
self.assertEquals({'a': '1'}, value)
2794
unquoted = conf.store.unquote(value)
2795
# Which cannot be unquoted but shouldn't crash either (the use cases
2796
# are getting the value or displaying it. In the later case, '%s' will
2798
self.assertEquals({'a': '1'}, unquoted)
2799
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2802
2374
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2803
"""Simulate loading a config store with content of various encodings.
2375
"""Simulate loading a config store without content of various encodings.
2805
2377
All files produced by bzr are in utf8 content.
3042
2556
config.ConfigHooks.install_named_hook('save', hook, None)
3043
2557
self.assertLength(0, calls)
3044
2558
store = self.get_store(self)
3045
# FIXME: There should be a better way than relying on the test
3046
# parametrization to identify branch.conf -- vila 2011-0526
3047
if self.store_id in ('branch', 'remote_branch'):
3048
# branch stores requires write locked branches
3049
self.addCleanup(store.branch.lock_write().unlock)
3050
2559
section = store.get_mutable_section('baz')
3051
2560
section.set('foo', 'bar')
3053
2562
self.assertLength(1, calls)
3054
2563
self.assertEquals((store,), calls[0])
3056
def test_set_mark_dirty(self):
3057
stack = config.MemoryStack('')
3058
self.assertLength(0, stack.store.dirty_sections)
3059
stack.set('foo', 'baz')
3060
self.assertLength(1, stack.store.dirty_sections)
3061
self.assertTrue(stack.store._need_saving())
3063
def test_remove_mark_dirty(self):
3064
stack = config.MemoryStack('foo=bar')
3065
self.assertLength(0, stack.store.dirty_sections)
3067
self.assertLength(1, stack.store.dirty_sections)
3068
self.assertTrue(stack.store._need_saving())
3071
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3072
"""Tests that config changes are kept in memory and saved on-demand."""
3075
super(TestStoreSaveChanges, self).setUp()
3076
self.transport = self.get_transport()
3077
# Most of the tests involve two stores pointing to the same persistent
3078
# storage to observe the effects of concurrent changes
3079
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3080
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3083
self.warnings.append(args[0] % args[1:])
3084
self.overrideAttr(trace, 'warning', warning)
3086
def has_store(self, store):
3087
store_basename = urlutils.relative_url(self.transport.external_url(),
3088
store.external_url())
3089
return self.transport.has(store_basename)
3091
def get_stack(self, store):
3092
# Any stack will do as long as it uses the right store, just a single
3093
# no-name section is enough
3094
return config.Stack([store.get_sections], store)
3096
def test_no_changes_no_save(self):
3097
s = self.get_stack(self.st1)
3098
s.store.save_changes()
3099
self.assertEquals(False, self.has_store(self.st1))
3101
def test_unrelated_concurrent_update(self):
3102
s1 = self.get_stack(self.st1)
3103
s2 = self.get_stack(self.st2)
3104
s1.set('foo', 'bar')
3105
s2.set('baz', 'quux')
3107
# Changes don't propagate magically
3108
self.assertEquals(None, s1.get('baz'))
3109
s2.store.save_changes()
3110
self.assertEquals('quux', s2.get('baz'))
3111
# Changes are acquired when saving
3112
self.assertEquals('bar', s2.get('foo'))
3113
# Since there is no overlap, no warnings are emitted
3114
self.assertLength(0, self.warnings)
3116
def test_concurrent_update_modified(self):
3117
s1 = self.get_stack(self.st1)
3118
s2 = self.get_stack(self.st2)
3119
s1.set('foo', 'bar')
3120
s2.set('foo', 'baz')
3123
s2.store.save_changes()
3124
self.assertEquals('baz', s2.get('foo'))
3125
# But the user get a warning
3126
self.assertLength(1, self.warnings)
3127
warning = self.warnings[0]
3128
self.assertStartsWith(warning, 'Option foo in section None')
3129
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3130
' The baz value will be saved.')
3132
def test_concurrent_deletion(self):
3133
self.st1._load_from_string('foo=bar')
3135
s1 = self.get_stack(self.st1)
3136
s2 = self.get_stack(self.st2)
3139
s1.store.save_changes()
3141
self.assertLength(0, self.warnings)
3142
s2.store.save_changes()
3144
self.assertLength(1, self.warnings)
3145
warning = self.warnings[0]
3146
self.assertStartsWith(warning, 'Option foo in section None')
3147
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3148
' The <DELETED> value will be saved.')
3151
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3153
def get_store(self):
3154
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3156
def test_get_quoted_string(self):
3157
store = self.get_store()
3158
store._load_from_string('foo= " abc "')
3159
stack = config.Stack([store.get_sections])
3160
self.assertEquals(' abc ', stack.get('foo'))
3162
def test_set_quoted_string(self):
3163
store = self.get_store()
3164
stack = config.Stack([store.get_sections], store)
3165
stack.set('foo', ' a b c ')
3167
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3170
class TestTransportIniFileStore(TestStore):
2566
class TestIniFileStore(TestStore):
3172
2568
def test_loading_unknown_file_fails(self):
3173
store = config.TransportIniFileStore(self.get_transport(),
2569
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
3175
2570
self.assertRaises(errors.NoSuchFile, store.load)
3177
2572
def test_invalid_content(self):
3178
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2573
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
3179
2574
self.assertEquals(False, store.is_loaded())
3180
2575
exc = self.assertRaises(
3181
2576
errors.ParseConfigError, store._load_from_string,
3479
2846
matcher = config.LocationMatcher(store, expected_url)
3480
2847
self.assertEquals(expected_location, matcher.location)
3482
def test_branch_name_colo(self):
3483
store = self.get_store(self)
3484
store._load_from_string(dedent("""\
3486
push_location=my{branchname}
3488
matcher = config.LocationMatcher(store, 'file:///,branch=example%3c')
3489
self.assertEqual('example<', matcher.branch_name)
3490
((_, section),) = matcher.get_sections()
3491
self.assertEqual('example<', section.locals['branchname'])
3493
def test_branch_name_basename(self):
3494
store = self.get_store(self)
3495
store._load_from_string(dedent("""\
3497
push_location=my{branchname}
3499
matcher = config.LocationMatcher(store, 'file:///parent/example%3c')
3500
self.assertEqual('example<', matcher.branch_name)
3501
((_, section),) = matcher.get_sections()
3502
self.assertEqual('example<', section.locals['branchname'])
3505
class TestStartingPathMatcher(TestStore):
3508
super(TestStartingPathMatcher, self).setUp()
3509
# Any simple store is good enough
3510
self.store = config.IniFileStore()
3512
def assertSectionIDs(self, expected, location, content):
3513
self.store._load_from_string(content)
3514
matcher = config.StartingPathMatcher(self.store, location)
3515
sections = list(matcher.get_sections())
3516
self.assertLength(len(expected), sections)
3517
self.assertEqual(expected, [section.id for _, section in sections])
3520
def test_empty(self):
3521
self.assertSectionIDs([], self.get_url(), '')
3523
def test_url_vs_local_paths(self):
3524
# The matcher location is an url and the section names are local paths
3525
self.assertSectionIDs(['/foo/bar', '/foo'],
3526
'file:///foo/bar/baz', '''\
3531
def test_local_path_vs_url(self):
3532
# The matcher location is a local path and the section names are urls
3533
self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3534
'/foo/bar/baz', '''\
3540
def test_no_name_section_included_when_present(self):
3541
# Note that other tests will cover the case where the no-name section
3542
# is empty and as such, not included.
3543
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3544
'/foo/bar/baz', '''\
3545
option = defined so the no-name section exists
3549
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3550
[s.locals['relpath'] for _, s in sections])
3552
def test_order_reversed(self):
3553
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3558
def test_unrelated_section_excluded(self):
3559
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3565
def test_glob_included(self):
3566
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3567
'/foo/bar/baz', '''\
3573
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3574
# nothing really is... as far using {relpath} to append it to something
3575
# else, this seems good enough though.
3576
self.assertEquals(['', 'baz', 'bar/baz'],
3577
[s.locals['relpath'] for _, s in sections])
3579
def test_respect_order(self):
3580
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3581
'/foo/bar/baz', '''\
3589
class TestNameMatcher(TestStore):
3592
super(TestNameMatcher, self).setUp()
3593
self.matcher = config.NameMatcher
3594
# Any simple store is good enough
3595
self.get_store = config.test_store_builder_registry.get('configobj')
3597
def get_matching_sections(self, name):
3598
store = self.get_store(self)
3599
store._load_from_string('''
3607
matcher = self.matcher(store, name)
3608
return list(matcher.get_sections())
3610
def test_matching(self):
3611
sections = self.get_matching_sections('foo')
3612
self.assertLength(1, sections)
3613
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3615
def test_not_matching(self):
3616
sections = self.get_matching_sections('baz')
3617
self.assertLength(0, sections)
3620
class TestBaseStackGet(tests.TestCase):
3623
super(TestBaseStackGet, self).setUp()
3624
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3626
def test_get_first_definition(self):
3627
store1 = config.IniFileStore()
3628
store1._load_from_string('foo=bar')
3629
store2 = config.IniFileStore()
3630
store2._load_from_string('foo=baz')
3631
conf = config.Stack([store1.get_sections, store2.get_sections])
3632
self.assertEquals('bar', conf.get('foo'))
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'))
3634
2860
def test_get_with_registered_default_value(self):
3635
config.option_registry.register(config.Option('foo', default='bar'))
3636
conf_stack = config.Stack([])
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)
3637
2865
self.assertEquals('bar', conf_stack.get('foo'))
3639
2867
def test_get_without_registered_default_value(self):
3640
config.option_registry.register(config.Option('foo'))
3641
conf_stack = config.Stack([])
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)
3642
2872
self.assertEquals(None, conf_stack.get('foo'))
3644
2874
def test_get_without_default_value_for_not_registered(self):
3645
conf_stack = config.Stack([])
2875
conf_stack = config.Stack([dict()])
2876
opt = config.Option('foo')
2877
self.overrideAttr(config, 'option_registry', registry.Registry())
3646
2878
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'))
3648
2892
def test_get_for_empty_section_callable(self):
3649
2893
conf_stack = config.Stack([lambda : []])
3650
2894
self.assertEquals(None, conf_stack.get('foo'))
3652
2896
def test_get_for_broken_callable(self):
3653
2897
# Trying to use and invalid callable raises an exception on first use
3654
conf_stack = config.Stack([object])
2898
conf_stack = config.Stack([lambda : object()])
3655
2899
self.assertRaises(TypeError, conf_stack.get, 'foo')
3658
class TestStackWithSimpleStore(tests.TestCase):
3661
super(TestStackWithSimpleStore, self).setUp()
3662
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3663
self.registry = config.option_registry
3665
def get_conf(self, content=None):
3666
return config.MemoryStack(content)
3668
def test_override_value_from_env(self):
3669
self.overrideEnv('FOO', None)
3670
self.registry.register(
3671
config.Option('foo', default='bar', override_from_env=['FOO']))
3672
self.overrideEnv('FOO', 'quux')
3673
# Env variable provides a default taking over the option one
3674
conf = self.get_conf('foo=store')
3675
self.assertEquals('quux', conf.get('foo'))
3677
def test_first_override_value_from_env_wins(self):
3678
self.overrideEnv('NO_VALUE', None)
3679
self.overrideEnv('FOO', None)
3680
self.overrideEnv('BAZ', None)
3681
self.registry.register(
3682
config.Option('foo', default='bar',
3683
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3684
self.overrideEnv('FOO', 'foo')
3685
self.overrideEnv('BAZ', 'baz')
3686
# The first env var set wins
3687
conf = self.get_conf('foo=store')
3688
self.assertEquals('foo', conf.get('foo'))
3691
class TestMemoryStack(tests.TestCase):
3694
conf = config.MemoryStack('foo=bar')
3695
self.assertEquals('bar', conf.get('foo'))
3698
conf = config.MemoryStack('foo=bar')
3699
conf.set('foo', 'baz')
3700
self.assertEquals('baz', conf.get('foo'))
3702
def test_no_content(self):
3703
conf = config.MemoryStack()
3704
# No content means no loading
3705
self.assertFalse(conf.store.is_loaded())
3706
self.assertRaises(NotImplementedError, conf.get, 'foo')
3707
# But a content can still be provided
3708
conf.store._load_from_string('foo=bar')
3709
self.assertEquals('bar', conf.get('foo'))
3712
class TestStackIterSections(tests.TestCase):
3714
def test_empty_stack(self):
3715
conf = config.Stack([])
3716
sections = list(conf.iter_sections())
3717
self.assertLength(0, sections)
3719
def test_empty_store(self):
3720
store = config.IniFileStore()
3721
store._load_from_string('')
3722
conf = config.Stack([store.get_sections])
3723
sections = list(conf.iter_sections())
3724
self.assertLength(0, sections)
3726
def test_simple_store(self):
3727
store = config.IniFileStore()
3728
store._load_from_string('foo=bar')
3729
conf = config.Stack([store.get_sections])
3730
tuples = list(conf.iter_sections())
3731
self.assertLength(1, tuples)
3732
(found_store, found_section) = tuples[0]
3733
self.assertIs(store, found_store)
3735
def test_two_stores(self):
3736
store1 = config.IniFileStore()
3737
store1._load_from_string('foo=bar')
3738
store2 = config.IniFileStore()
3739
store2._load_from_string('bar=qux')
3740
conf = config.Stack([store1.get_sections, store2.get_sections])
3741
tuples = list(conf.iter_sections())
3742
self.assertLength(2, tuples)
3743
self.assertIs(store1, tuples[0][0])
3744
self.assertIs(store2, tuples[1][0])
3747
2902
class TestStackWithTransport(tests.TestCaseWithTransport):
3749
2904
scenarios = [(key, {'get_stack': builder}) for key, builder
3755
2910
def test_build_stack(self):
3756
2911
# Just a smoke test to help debug builders
3757
self.get_stack(self)
2912
stack = self.get_stack(self)
3760
2915
class TestStackGet(TestStackWithTransport):
3763
super(TestStackGet, self).setUp()
3764
self.conf = self.get_stack(self)
3766
2917
def test_get_for_empty_stack(self):
3767
self.assertEquals(None, self.conf.get('foo'))
2918
conf = self.get_stack(self)
2919
self.assertEquals(None, conf.get('foo'))
3769
2921
def test_get_hook(self):
3770
self.conf.set('foo', 'bar')
2922
conf = self.get_stack(self)
2923
conf.store._load_from_string('foo=bar')
3772
2925
def hook(*args):
3773
2926
calls.append(args)
3774
2927
config.ConfigHooks.install_named_hook('get', hook, None)
3775
2928
self.assertLength(0, calls)
3776
value = self.conf.get('foo')
2929
value = conf.get('foo')
3777
2930
self.assertEquals('bar', value)
3778
2931
self.assertLength(1, calls)
3779
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3782
class TestStackGetWithConverter(tests.TestCase):
3785
super(TestStackGetWithConverter, self).setUp()
3786
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3787
self.registry = config.option_registry
3789
def get_conf(self, content=None):
3790
return config.MemoryStack(content)
3792
def register_bool_option(self, name, default=None, default_from_env=None):
3793
b = config.Option(name, help='A boolean.',
3794
default=default, default_from_env=default_from_env,
3795
from_unicode=config.bool_from_store)
3796
self.registry.register(b)
3798
def test_get_default_bool_None(self):
3799
self.register_bool_option('foo')
3800
conf = self.get_conf('')
3801
self.assertEquals(None, conf.get('foo'))
3803
def test_get_default_bool_True(self):
3804
self.register_bool_option('foo', u'True')
3805
conf = self.get_conf('')
3806
self.assertEquals(True, conf.get('foo'))
3808
def test_get_default_bool_False(self):
3809
self.register_bool_option('foo', False)
3810
conf = self.get_conf('')
3811
self.assertEquals(False, conf.get('foo'))
3813
def test_get_default_bool_False_as_string(self):
3814
self.register_bool_option('foo', u'False')
3815
conf = self.get_conf('')
3816
self.assertEquals(False, conf.get('foo'))
3818
def test_get_default_bool_from_env_converted(self):
3819
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3820
self.overrideEnv('FOO', 'False')
3821
conf = self.get_conf('')
3822
self.assertEquals(False, conf.get('foo'))
3824
def test_get_default_bool_when_conversion_fails(self):
3825
self.register_bool_option('foo', default='True')
3826
conf = self.get_conf('foo=invalid boolean')
3827
self.assertEquals(True, conf.get('foo'))
3829
def register_integer_option(self, name,
3830
default=None, default_from_env=None):
3831
i = config.Option(name, help='An integer.',
3832
default=default, default_from_env=default_from_env,
3833
from_unicode=config.int_from_store)
3834
self.registry.register(i)
3836
def test_get_default_integer_None(self):
3837
self.register_integer_option('foo')
3838
conf = self.get_conf('')
3839
self.assertEquals(None, conf.get('foo'))
3841
def test_get_default_integer(self):
3842
self.register_integer_option('foo', 42)
3843
conf = self.get_conf('')
3844
self.assertEquals(42, conf.get('foo'))
3846
def test_get_default_integer_as_string(self):
3847
self.register_integer_option('foo', u'42')
3848
conf = self.get_conf('')
3849
self.assertEquals(42, conf.get('foo'))
3851
def test_get_default_integer_from_env(self):
3852
self.register_integer_option('foo', default_from_env=['FOO'])
3853
self.overrideEnv('FOO', '18')
3854
conf = self.get_conf('')
3855
self.assertEquals(18, conf.get('foo'))
3857
def test_get_default_integer_when_conversion_fails(self):
3858
self.register_integer_option('foo', default='12')
3859
conf = self.get_conf('foo=invalid integer')
3860
self.assertEquals(12, conf.get('foo'))
3862
def register_list_option(self, name, default=None, default_from_env=None):
3863
l = config.ListOption(name, help='A list.', default=default,
3864
default_from_env=default_from_env)
3865
self.registry.register(l)
3867
def test_get_default_list_None(self):
3868
self.register_list_option('foo')
3869
conf = self.get_conf('')
3870
self.assertEquals(None, conf.get('foo'))
3872
def test_get_default_list_empty(self):
3873
self.register_list_option('foo', '')
3874
conf = self.get_conf('')
3875
self.assertEquals([], conf.get('foo'))
3877
def test_get_default_list_from_env(self):
3878
self.register_list_option('foo', default_from_env=['FOO'])
3879
self.overrideEnv('FOO', '')
3880
conf = self.get_conf('')
3881
self.assertEquals([], conf.get('foo'))
3883
def test_get_with_list_converter_no_item(self):
3884
self.register_list_option('foo', None)
3885
conf = self.get_conf('foo=,')
3886
self.assertEquals([], conf.get('foo'))
3888
def test_get_with_list_converter_many_items(self):
3889
self.register_list_option('foo', None)
3890
conf = self.get_conf('foo=m,o,r,e')
3891
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3893
def test_get_with_list_converter_embedded_spaces_many_items(self):
3894
self.register_list_option('foo', None)
3895
conf = self.get_conf('foo=" bar", "baz "')
3896
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3898
def test_get_with_list_converter_stripped_spaces_many_items(self):
3899
self.register_list_option('foo', None)
3900
conf = self.get_conf('foo= bar , baz ')
3901
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3904
class TestIterOptionRefs(tests.TestCase):
3905
"""iter_option_refs is a bit unusual, document some cases."""
3907
def assertRefs(self, expected, string):
3908
self.assertEquals(expected, list(config.iter_option_refs(string)))
3910
def test_empty(self):
3911
self.assertRefs([(False, '')], '')
3913
def test_no_refs(self):
3914
self.assertRefs([(False, 'foo bar')], 'foo bar')
3916
def test_single_ref(self):
3917
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3919
def test_broken_ref(self):
3920
self.assertRefs([(False, '{foo')], '{foo')
3922
def test_embedded_ref(self):
3923
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3926
def test_two_refs(self):
3927
self.assertRefs([(False, ''), (True, '{foo}'),
3928
(False, ''), (True, '{bar}'),
3932
def test_newline_in_refs_are_not_matched(self):
3933
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3936
class TestStackExpandOptions(tests.TestCaseWithTransport):
3939
super(TestStackExpandOptions, self).setUp()
3940
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3941
self.registry = config.option_registry
3942
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3943
self.conf = config.Stack([store.get_sections], store)
3945
def assertExpansion(self, expected, string, env=None):
3946
self.assertEquals(expected, self.conf.expand_options(string, env))
3948
def test_no_expansion(self):
3949
self.assertExpansion('foo', 'foo')
3951
def test_expand_default_value(self):
3952
self.conf.store._load_from_string('bar=baz')
3953
self.registry.register(config.Option('foo', default=u'{bar}'))
3954
self.assertEquals('baz', self.conf.get('foo', expand=True))
3956
def test_expand_default_from_env(self):
3957
self.conf.store._load_from_string('bar=baz')
3958
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3959
self.overrideEnv('FOO', '{bar}')
3960
self.assertEquals('baz', self.conf.get('foo', expand=True))
3962
def test_expand_default_on_failed_conversion(self):
3963
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3964
self.registry.register(
3965
config.Option('foo', default=u'{bar}',
3966
from_unicode=config.int_from_store))
3967
self.assertEquals(42, self.conf.get('foo', expand=True))
3969
def test_env_adding_options(self):
3970
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3972
def test_env_overriding_options(self):
3973
self.conf.store._load_from_string('foo=baz')
3974
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3976
def test_simple_ref(self):
3977
self.conf.store._load_from_string('foo=xxx')
3978
self.assertExpansion('xxx', '{foo}')
3980
def test_unknown_ref(self):
3981
self.assertRaises(errors.ExpandingUnknownOption,
3982
self.conf.expand_options, '{foo}')
3984
def test_illegal_def_is_ignored(self):
3985
self.assertExpansion('{1,2}', '{1,2}')
3986
self.assertExpansion('{ }', '{ }')
3987
self.assertExpansion('${Foo,f}', '${Foo,f}')
3989
def test_indirect_ref(self):
3990
self.conf.store._load_from_string('''
3994
self.assertExpansion('xxx', '{bar}')
3996
def test_embedded_ref(self):
3997
self.conf.store._load_from_string('''
4001
self.assertExpansion('xxx', '{{bar}}')
4003
def test_simple_loop(self):
4004
self.conf.store._load_from_string('foo={foo}')
4005
self.assertRaises(errors.OptionExpansionLoop,
4006
self.conf.expand_options, '{foo}')
4008
def test_indirect_loop(self):
4009
self.conf.store._load_from_string('''
4013
e = self.assertRaises(errors.OptionExpansionLoop,
4014
self.conf.expand_options, '{foo}')
4015
self.assertEquals('foo->bar->baz', e.refs)
4016
self.assertEquals('{foo}', e.string)
4018
def test_list(self):
4019
self.conf.store._load_from_string('''
4023
list={foo},{bar},{baz}
4025
self.registry.register(
4026
config.ListOption('list'))
4027
self.assertEquals(['start', 'middle', 'end'],
4028
self.conf.get('list', expand=True))
4030
def test_cascading_list(self):
4031
self.conf.store._load_from_string('''
4037
self.registry.register(config.ListOption('list'))
4038
# Register an intermediate option as a list to ensure no conversion
4039
# happen while expanding. Conversion should only occur for the original
4040
# option ('list' here).
4041
self.registry.register(config.ListOption('baz'))
4042
self.assertEquals(['start', 'middle', 'end'],
4043
self.conf.get('list', expand=True))
4045
def test_pathologically_hidden_list(self):
4046
self.conf.store._load_from_string('''
4052
hidden={start}{middle}{end}
4054
# What matters is what the registration says, the conversion happens
4055
# only after all expansions have been performed
4056
self.registry.register(config.ListOption('hidden'))
4057
self.assertEquals(['bin', 'go'],
4058
self.conf.get('hidden', expand=True))
4061
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
4064
super(TestStackCrossSectionsExpand, self).setUp()
4066
def get_config(self, location, string):
4069
# Since we don't save the config we won't strictly require to inherit
4070
# from TestCaseInTempDir, but an error occurs so quickly...
4071
c = config.LocationStack(location)
4072
c.store._load_from_string(string)
4075
def test_dont_cross_unrelated_section(self):
4076
c = self.get_config('/another/branch/path','''
4081
[/another/branch/path]
4084
self.assertRaises(errors.ExpandingUnknownOption,
4085
c.get, 'bar', expand=True)
4087
def test_cross_related_sections(self):
4088
c = self.get_config('/project/branch/path','''
4092
[/project/branch/path]
4095
self.assertEquals('quux', c.get('bar', expand=True))
4098
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
4100
def test_cross_global_locations(self):
4101
l_store = config.LocationStore()
4102
l_store._load_from_string('''
4108
g_store = config.GlobalStore()
4109
g_store._load_from_string('''
4115
stack = config.LocationStack('/branch')
4116
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4117
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4120
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4122
def test_expand_locals_empty(self):
4123
l_store = config.LocationStore()
4124
l_store._load_from_string('''
4125
[/home/user/project]
4130
stack = config.LocationStack('/home/user/project/')
4131
self.assertEquals('', stack.get('base', expand=True))
4132
self.assertEquals('', stack.get('rel', expand=True))
4134
def test_expand_basename_locally(self):
4135
l_store = config.LocationStore()
4136
l_store._load_from_string('''
4137
[/home/user/project]
4141
stack = config.LocationStack('/home/user/project/branch')
4142
self.assertEquals('branch', stack.get('bfoo', expand=True))
4144
def test_expand_basename_locally_longer_path(self):
4145
l_store = config.LocationStore()
4146
l_store._load_from_string('''
4151
stack = config.LocationStack('/home/user/project/dir/branch')
4152
self.assertEquals('branch', stack.get('bfoo', expand=True))
4154
def test_expand_relpath_locally(self):
4155
l_store = config.LocationStore()
4156
l_store._load_from_string('''
4157
[/home/user/project]
4158
lfoo = loc-foo/{relpath}
4161
stack = config.LocationStack('/home/user/project/branch')
4162
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4164
def test_expand_relpath_unknonw_in_global(self):
4165
g_store = config.GlobalStore()
4166
g_store._load_from_string('''
4171
stack = config.LocationStack('/home/user/project/branch')
4172
self.assertRaises(errors.ExpandingUnknownOption,
4173
stack.get, 'gfoo', expand=True)
4175
def test_expand_local_option_locally(self):
4176
l_store = config.LocationStore()
4177
l_store._load_from_string('''
4178
[/home/user/project]
4179
lfoo = loc-foo/{relpath}
4183
g_store = config.GlobalStore()
4184
g_store._load_from_string('''
4190
stack = config.LocationStack('/home/user/project/branch')
4191
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4192
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4194
def test_locals_dont_leak(self):
4195
"""Make sure we chose the right local in presence of several sections.
4197
l_store = config.LocationStore()
4198
l_store._load_from_string('''
4200
lfoo = loc-foo/{relpath}
4201
[/home/user/project]
4202
lfoo = loc-foo/{relpath}
4205
stack = config.LocationStack('/home/user/project/branch')
4206
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4207
stack = config.LocationStack('/home/user/bar/baz')
4208
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
2932
self.assertEquals((conf, 'foo', 'bar'), calls[0])
4212
2935
class TestStackSet(TestStackWithTransport):
4214
2937
def test_simple_set(self):
4215
2938
conf = self.get_stack(self)
4216
self.assertEquals(None, conf.get('foo'))
2939
conf.store._load_from_string('foo=bar')
2940
self.assertEquals('bar', conf.get('foo'))
4217
2941
conf.set('foo', 'baz')
4218
2942
# Did we get it back ?
4219
2943
self.assertEquals('baz', conf.get('foo'))
4959
3665
self.assertEquals((None, None), (realname, address))
4962
class TestDefaultMailDomain(tests.TestCaseInTempDir):
4963
"""Test retrieving default domain from mailname file"""
4965
def test_default_mail_domain_simple(self):
4966
f = file('simple', 'w')
4968
f.write("domainname.com\n")
4971
r = config._get_default_mail_domain('simple')
4972
self.assertEquals('domainname.com', r)
4974
def test_default_mail_domain_no_eol(self):
4975
f = file('no_eol', 'w')
4977
f.write("domainname.com")
4980
r = config._get_default_mail_domain('no_eol')
4981
self.assertEquals('domainname.com', r)
4983
def test_default_mail_domain_multiple_lines(self):
4984
f = file('multiple_lines', 'w')
4986
f.write("domainname.com\nsome other text\n")
4989
r = config._get_default_mail_domain('multiple_lines')
4990
self.assertEquals('domainname.com', r)
4993
class EmailOptionTests(tests.TestCase):
4995
def test_default_email_uses_BZR_EMAIL(self):
4996
conf = config.MemoryStack('email=jelmer@debian.org')
4997
# BZR_EMAIL takes precedence over EMAIL
4998
self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
4999
self.overrideEnv('EMAIL', 'jelmer@apache.org')
5000
self.assertEquals('jelmer@samba.org', conf.get('email'))
5002
def test_default_email_uses_EMAIL(self):
5003
conf = config.MemoryStack('')
5004
self.overrideEnv('BZR_EMAIL', None)
5005
self.overrideEnv('EMAIL', 'jelmer@apache.org')
5006
self.assertEquals('jelmer@apache.org', conf.get('email'))
5008
def test_BZR_EMAIL_overrides(self):
5009
conf = config.MemoryStack('email=jelmer@debian.org')
5010
self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
5011
self.assertEquals('jelmer@apache.org', conf.get('email'))
5012
self.overrideEnv('BZR_EMAIL', None)
5013
self.overrideEnv('EMAIL', 'jelmer@samba.org')
5014
self.assertEquals('jelmer@debian.org', conf.get('email'))
5017
class MailClientOptionTests(tests.TestCase):
5019
def test_default(self):
5020
conf = config.MemoryStack('')
5021
client = conf.get('mail_client')
5022
self.assertIs(client, mail_client.DefaultMail)
5024
def test_evolution(self):
5025
conf = config.MemoryStack('mail_client=evolution')
5026
client = conf.get('mail_client')
5027
self.assertIs(client, mail_client.Evolution)
5029
def test_kmail(self):
5030
conf = config.MemoryStack('mail_client=kmail')
5031
client = conf.get('mail_client')
5032
self.assertIs(client, mail_client.KMail)
5034
def test_mutt(self):
5035
conf = config.MemoryStack('mail_client=mutt')
5036
client = conf.get('mail_client')
5037
self.assertIs(client, mail_client.Mutt)
5039
def test_thunderbird(self):
5040
conf = config.MemoryStack('mail_client=thunderbird')
5041
client = conf.get('mail_client')
5042
self.assertIs(client, mail_client.Thunderbird)
5044
def test_explicit_default(self):
5045
conf = config.MemoryStack('mail_client=default')
5046
client = conf.get('mail_client')
5047
self.assertIs(client, mail_client.DefaultMail)
5049
def test_editor(self):
5050
conf = config.MemoryStack('mail_client=editor')
5051
client = conf.get('mail_client')
5052
self.assertIs(client, mail_client.Editor)
5054
def test_mapi(self):
5055
conf = config.MemoryStack('mail_client=mapi')
5056
client = conf.get('mail_client')
5057
self.assertIs(client, mail_client.MAPIClient)
5059
def test_xdg_email(self):
5060
conf = config.MemoryStack('mail_client=xdg-email')
5061
client = conf.get('mail_client')
5062
self.assertIs(client, mail_client.XDGEmail)
5064
def test_unknown(self):
5065
conf = config.MemoryStack('mail_client=firebird')
5066
self.assertRaises(errors.ConfigOptionValueError, conf.get,