2655
2655
self.assertRaises(errors.BzrCommandError,
2656
2656
self.store._from_cmdline, ['a=b', 'c'])
2658
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2660
scenarios = [(key, {'get_store': builder}) for key, builder
2661
in config.test_store_builder_registry.iteritems()] + [
2662
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2665
store = self.get_store(self)
2666
if type(store) == config.TransportIniFileStore:
2667
raise tests.TestNotApplicable(
2668
"%s is not a concrete Store implementation"
2669
" so it doesn't need an id" % (store.__class__.__name__,))
2670
self.assertIsNot(None, store.id)
2659
2673
class TestStore(tests.TestCaseWithTransport):
2760
2774
self.assertIdempotent('a,b')
2777
class TestDictFromStore(tests.TestCase):
2779
def test_unquote_not_string(self):
2780
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2781
value = conf.get('a_section')
2782
# Urgh, despite 'conf' asking for the no-name section, we get the
2783
# content of another section as a dict o_O
2784
self.assertEquals({'a': '1'}, value)
2785
unquoted = conf.store.unquote(value)
2786
# Which cannot be unquoted but shouldn't crash either (the use cases
2787
# are getting the value or displaying it. In the later case, '%s' will
2789
self.assertEquals({'a': '1'}, unquoted)
2790
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2763
2793
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2764
2794
"""Simulate loading a config store with content of various encodings.
2970
3000
self.assertLength(1, calls)
2971
3001
self.assertEquals((store,), calls[0])
3003
def test_set_mark_dirty(self):
3004
stack = config.MemoryStack('')
3005
self.assertLength(0, stack.store.dirty_sections)
3006
stack.set('foo', 'baz')
3007
self.assertLength(1, stack.store.dirty_sections)
3008
self.assertTrue(stack.store._need_saving())
3010
def test_remove_mark_dirty(self):
3011
stack = config.MemoryStack('foo=bar')
3012
self.assertLength(0, stack.store.dirty_sections)
3014
self.assertLength(1, stack.store.dirty_sections)
3015
self.assertTrue(stack.store._need_saving())
3018
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3019
"""Tests that config changes are kept in memory and saved on-demand."""
3022
super(TestStoreSaveChanges, self).setUp()
3023
self.transport = self.get_transport()
3024
# Most of the tests involve two stores pointing to the same persistent
3025
# storage to observe the effects of concurrent changes
3026
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3027
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3030
self.warnings.append(args[0] % args[1:])
3031
self.overrideAttr(trace, 'warning', warning)
3033
def has_store(self, store):
3034
store_basename = urlutils.relative_url(self.transport.external_url(),
3035
store.external_url())
3036
return self.transport.has(store_basename)
3038
def get_stack(self, store):
3039
# Any stack will do as long as it uses the right store, just a single
3040
# no-name section is enough
3041
return config.Stack([store.get_sections], store)
3043
def test_no_changes_no_save(self):
3044
s = self.get_stack(self.st1)
3045
s.store.save_changes()
3046
self.assertEquals(False, self.has_store(self.st1))
3048
def test_unrelated_concurrent_update(self):
3049
s1 = self.get_stack(self.st1)
3050
s2 = self.get_stack(self.st2)
3051
s1.set('foo', 'bar')
3052
s2.set('baz', 'quux')
3054
# Changes don't propagate magically
3055
self.assertEquals(None, s1.get('baz'))
3056
s2.store.save_changes()
3057
self.assertEquals('quux', s2.get('baz'))
3058
# Changes are acquired when saving
3059
self.assertEquals('bar', s2.get('foo'))
3060
# Since there is no overlap, no warnings are emitted
3061
self.assertLength(0, self.warnings)
3063
def test_concurrent_update_modified(self):
3064
s1 = self.get_stack(self.st1)
3065
s2 = self.get_stack(self.st2)
3066
s1.set('foo', 'bar')
3067
s2.set('foo', 'baz')
3070
s2.store.save_changes()
3071
self.assertEquals('baz', s2.get('foo'))
3072
# But the user get a warning
3073
self.assertLength(1, self.warnings)
3074
warning = self.warnings[0]
3075
self.assertStartsWith(warning, 'Option foo in section None')
3076
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3077
' The baz value will be saved.')
3079
def test_concurrent_deletion(self):
3080
self.st1._load_from_string('foo=bar')
3082
s1 = self.get_stack(self.st1)
3083
s2 = self.get_stack(self.st2)
3086
s1.store.save_changes()
3088
self.assertLength(0, self.warnings)
3089
s2.store.save_changes()
3091
self.assertLength(1, self.warnings)
3092
warning = self.warnings[0]
3093
self.assertStartsWith(warning, 'Option foo in section None')
3094
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3095
' The <DELETED> value will be saved.')
2974
3098
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3456
3580
self.assertRaises(TypeError, conf_stack.get, 'foo')
3583
class TestStackWithSimpleStore(tests.TestCase):
3586
super(TestStackWithSimpleStore, self).setUp()
3587
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3588
self.registry = config.option_registry
3590
def get_conf(self, content=None):
3591
return config.MemoryStack(content)
3593
def test_override_value_from_env(self):
3594
self.registry.register(
3595
config.Option('foo', default='bar', override_from_env=['FOO']))
3596
self.overrideEnv('FOO', 'quux')
3597
# Env variable provides a default taking over the option one
3598
conf = self.get_conf('foo=store')
3599
self.assertEquals('quux', conf.get('foo'))
3601
def test_first_override_value_from_env_wins(self):
3602
self.registry.register(
3603
config.Option('foo', default='bar',
3604
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3605
self.overrideEnv('FOO', 'foo')
3606
self.overrideEnv('BAZ', 'baz')
3607
# The first env var set wins
3608
conf = self.get_conf('foo=store')
3609
self.assertEquals('foo', conf.get('foo'))
3459
3612
class TestMemoryStack(tests.TestCase):
3461
3614
def test_get(self):
4668
4821
class EmailOptionTests(tests.TestCase):
4670
4823
def test_default_email_uses_BZR_EMAIL(self):
4824
conf = config.MemoryStack('email=jelmer@debian.org')
4671
4825
# BZR_EMAIL takes precedence over EMAIL
4672
4826
self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
4673
4827
self.overrideEnv('EMAIL', 'jelmer@apache.org')
4674
self.assertEquals('jelmer@samba.org', config.default_email())
4828
self.assertEquals('jelmer@samba.org', conf.get('email'))
4676
4830
def test_default_email_uses_EMAIL(self):
4831
conf = config.MemoryStack('')
4677
4832
self.overrideEnv('BZR_EMAIL', None)
4678
4833
self.overrideEnv('EMAIL', 'jelmer@apache.org')
4679
self.assertEquals('jelmer@apache.org', config.default_email())
4834
self.assertEquals('jelmer@apache.org', conf.get('email'))
4681
4836
def test_BZR_EMAIL_overrides(self):
4837
conf = config.MemoryStack('email=jelmer@debian.org')
4682
4838
self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
4683
self.assertEquals('jelmer@apache.org',
4684
config.email_from_store('jelmer@debian.org'))
4839
self.assertEquals('jelmer@apache.org', conf.get('email'))
4685
4840
self.overrideEnv('BZR_EMAIL', None)
4686
4841
self.overrideEnv('EMAIL', 'jelmer@samba.org')
4687
self.assertEquals('jelmer@debian.org',
4688
config.email_from_store('jelmer@debian.org'))
4842
self.assertEquals('jelmer@debian.org', conf.get('email'))