64
70
load_tests = scenarios.load_tests_apply_scenarios
66
# We need adapters that can build a config store in a test context. Test
67
# classes, based on TestCaseWithTransport, can use the registry to parametrize
68
# themselves. The builder will receive a test instance and should return a
69
# ready-to-use store. Plugins that defines new stores can also register
70
# themselves here to be tested against the tests defined below.
72
# FIXME: plugins should *not* need to import test_config to register their
73
# helpers (or selftest -s xxx will be broken), the following registry should be
74
# moved to bzrlib.config instead so that selftest -s bt.test_config also runs
75
# the plugin specific tests (selftest -s bp.xxx won't, that would be against
76
# the spirit of '-s') -- vila 20110503
77
test_store_builder_registry = registry.Registry()
78
test_store_builder_registry.register(
72
# Register helpers to build stores
73
config.test_store_builder_registry.register(
79
74
'configobj', lambda test: config.IniFileStore(test.get_transport(),
81
test_store_builder_registry.register(
76
config.test_store_builder_registry.register(
82
77
'bazaar', lambda test: config.GlobalStore())
83
test_store_builder_registry.register(
78
config.test_store_builder_registry.register(
84
79
'location', lambda test: config.LocationStore())
85
test_store_builder_registry.register(
86
'branch', lambda test: config.BranchStore(test.branch))
88
# FIXME: Same remark as above for the following registry -- vila 20110503
89
test_stack_builder_registry = registry.Registry()
90
test_stack_builder_registry.register(
82
def build_backing_branch(test, relpath,
83
transport_class=None, server_class=None):
84
"""Test helper to create a backing branch only once.
86
Some tests needs multiple stores/stacks to check concurrent update
87
behaviours. As such, they need to build different branch *objects* even if
88
they share the branch on disk.
90
:param relpath: The relative path to the branch. (Note that the helper
91
should always specify the same relpath).
93
:param transport_class: The Transport class the test needs to use.
95
:param server_class: The server associated with the ``transport_class``
98
Either both or neither of ``transport_class`` and ``server_class`` should
101
if transport_class is not None and server_class is not None:
102
test.transport_class = transport_class
103
test.transport_server = server_class
104
elif not (transport_class is None and server_class is None):
105
raise AssertionError('Specify both ``transport_class`` and '
106
'``server_class`` or neither of them')
107
if getattr(test, 'backing_branch', None) is None:
108
# First call, let's build the branch on disk
109
test.backing_branch = test.make_branch(relpath)
112
def build_branch_store(test):
113
build_backing_branch(test, 'branch')
114
b = branch.Branch.open('branch')
115
return config.BranchStore(b)
116
config.test_store_builder_registry.register('branch', build_branch_store)
119
def build_remote_branch_store(test):
120
# There is only one permutation (but we won't be able to handle more with
121
# this design anyway)
123
server_class) = transport_remote.get_test_permutations()[0]
124
build_backing_branch(test, 'branch', transport_class, server_class)
125
b = branch.Branch.open(test.get_url('branch'))
126
return config.BranchStore(b)
127
config.test_store_builder_registry.register('remote_branch',
128
build_remote_branch_store)
131
config.test_stack_builder_registry.register(
91
132
'bazaar', lambda test: config.GlobalStack())
92
test_stack_builder_registry.register(
133
config.test_stack_builder_registry.register(
93
134
'location', lambda test: config.LocationStack('.'))
94
test_stack_builder_registry.register(
95
'branch', lambda test: config.BranchStack(test.branch))
137
def build_branch_stack(test):
138
build_backing_branch(test, 'branch')
139
b = branch.Branch.open('branch')
140
return config.BranchStack(b)
141
config.test_stack_builder_registry.register('branch', build_branch_stack)
144
def build_remote_branch_stack(test):
145
# There is only one permutation (but we won't be able to handle more with
146
# this design anyway)
148
server_class) = transport_remote.get_test_permutations()[0]
149
build_backing_branch(test, 'branch', transport_class, server_class)
150
b = branch.Branch.open(test.get_url('branch'))
151
return config.BranchStack(b)
152
config.test_stack_builder_registry.register('remote_branch',
153
build_remote_branch_stack)
98
156
sample_long_alias="log -r-15..-1 --line"
1846
1934
self.assertIs(None, bzrdir_config.get_default_stack_on())
1937
class TestOldConfigHooks(tests.TestCaseWithTransport):
1940
super(TestOldConfigHooks, self).setUp()
1941
create_configs_with_file_option(self)
1943
def assertGetHook(self, conf, name, value):
1947
config.OldConfigHooks.install_named_hook('get', hook, None)
1949
config.OldConfigHooks.uninstall_named_hook, 'get', None)
1950
self.assertLength(0, calls)
1951
actual_value = conf.get_user_option(name)
1952
self.assertEquals(value, actual_value)
1953
self.assertLength(1, calls)
1954
self.assertEquals((conf, name, value), calls[0])
1956
def test_get_hook_bazaar(self):
1957
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
1959
def test_get_hook_locations(self):
1960
self.assertGetHook(self.locations_config, 'file', 'locations')
1962
def test_get_hook_branch(self):
1963
# Since locations masks branch, we define a different option
1964
self.branch_config.set_user_option('file2', 'branch')
1965
self.assertGetHook(self.branch_config, 'file2', 'branch')
1967
def assertSetHook(self, conf, name, value):
1971
config.OldConfigHooks.install_named_hook('set', hook, None)
1973
config.OldConfigHooks.uninstall_named_hook, 'set', None)
1974
self.assertLength(0, calls)
1975
conf.set_user_option(name, value)
1976
self.assertLength(1, calls)
1977
# We can't assert the conf object below as different configs use
1978
# different means to implement set_user_option and we care only about
1980
self.assertEquals((name, value), calls[0][1:])
1982
def test_set_hook_bazaar(self):
1983
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
1985
def test_set_hook_locations(self):
1986
self.assertSetHook(self.locations_config, 'foo', 'locations')
1988
def test_set_hook_branch(self):
1989
self.assertSetHook(self.branch_config, 'foo', 'branch')
1991
def assertRemoveHook(self, conf, name, section_name=None):
1995
config.OldConfigHooks.install_named_hook('remove', hook, None)
1997
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
1998
self.assertLength(0, calls)
1999
conf.remove_user_option(name, section_name)
2000
self.assertLength(1, calls)
2001
# We can't assert the conf object below as different configs use
2002
# different means to implement remove_user_option and we care only about
2004
self.assertEquals((name,), calls[0][1:])
2006
def test_remove_hook_bazaar(self):
2007
self.assertRemoveHook(self.bazaar_config, 'file')
2009
def test_remove_hook_locations(self):
2010
self.assertRemoveHook(self.locations_config, 'file',
2011
self.locations_config.location)
2013
def test_remove_hook_branch(self):
2014
self.assertRemoveHook(self.branch_config, 'file')
2016
def assertLoadHook(self, name, conf_class, *conf_args):
2020
config.OldConfigHooks.install_named_hook('load', hook, None)
2022
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2023
self.assertLength(0, calls)
2025
conf = conf_class(*conf_args)
2026
# Access an option to trigger a load
2027
conf.get_user_option(name)
2028
self.assertLength(1, calls)
2029
# Since we can't assert about conf, we just use the number of calls ;-/
2031
def test_load_hook_bazaar(self):
2032
self.assertLoadHook('file', config.GlobalConfig)
2034
def test_load_hook_locations(self):
2035
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2037
def test_load_hook_branch(self):
2038
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2040
def assertSaveHook(self, conf):
2044
config.OldConfigHooks.install_named_hook('save', hook, None)
2046
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2047
self.assertLength(0, calls)
2048
# Setting an option triggers a save
2049
conf.set_user_option('foo', 'bar')
2050
self.assertLength(1, calls)
2051
# Since we can't assert about conf, we just use the number of calls ;-/
2053
def test_save_hook_bazaar(self):
2054
self.assertSaveHook(self.bazaar_config)
2056
def test_save_hook_locations(self):
2057
self.assertSaveHook(self.locations_config)
2059
def test_save_hook_branch(self):
2060
self.assertSaveHook(self.branch_config)
2063
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2064
"""Tests config hooks for remote configs.
2066
No tests for the remove hook as this is not implemented there.
2070
super(TestOldConfigHooksForRemote, self).setUp()
2071
self.transport_server = test_server.SmartTCPServer_for_testing
2072
create_configs_with_file_option(self)
2074
def assertGetHook(self, conf, name, value):
2078
config.OldConfigHooks.install_named_hook('get', hook, None)
2080
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2081
self.assertLength(0, calls)
2082
actual_value = conf.get_option(name)
2083
self.assertEquals(value, actual_value)
2084
self.assertLength(1, calls)
2085
self.assertEquals((conf, name, value), calls[0])
2087
def test_get_hook_remote_branch(self):
2088
remote_branch = branch.Branch.open(self.get_url('tree'))
2089
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2091
def test_get_hook_remote_bzrdir(self):
2092
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2093
conf = remote_bzrdir._get_config()
2094
conf.set_option('remotedir', 'file')
2095
self.assertGetHook(conf, 'file', 'remotedir')
2097
def assertSetHook(self, conf, name, value):
2101
config.OldConfigHooks.install_named_hook('set', hook, None)
2103
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2104
self.assertLength(0, calls)
2105
conf.set_option(value, name)
2106
self.assertLength(1, calls)
2107
# We can't assert the conf object below as different configs use
2108
# different means to implement set_user_option and we care only about
2110
self.assertEquals((name, value), calls[0][1:])
2112
def test_set_hook_remote_branch(self):
2113
remote_branch = branch.Branch.open(self.get_url('tree'))
2114
self.addCleanup(remote_branch.lock_write().unlock)
2115
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2117
def test_set_hook_remote_bzrdir(self):
2118
remote_branch = branch.Branch.open(self.get_url('tree'))
2119
self.addCleanup(remote_branch.lock_write().unlock)
2120
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2121
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2123
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2127
config.OldConfigHooks.install_named_hook('load', hook, None)
2129
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2130
self.assertLength(0, calls)
2132
conf = conf_class(*conf_args)
2133
# Access an option to trigger a load
2134
conf.get_option(name)
2135
self.assertLength(expected_nb_calls, calls)
2136
# Since we can't assert about conf, we just use the number of calls ;-/
2138
def test_load_hook_remote_branch(self):
2139
remote_branch = branch.Branch.open(self.get_url('tree'))
2140
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2142
def test_load_hook_remote_bzrdir(self):
2143
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2144
# The config file doesn't exist, set an option to force its creation
2145
conf = remote_bzrdir._get_config()
2146
conf.set_option('remotedir', 'file')
2147
# We get one call for the server and one call for the client, this is
2148
# caused by the differences in implementations betwen
2149
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2150
# SmartServerBranchGetConfigFile (in smart/branch.py)
2151
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2153
def assertSaveHook(self, conf):
2157
config.OldConfigHooks.install_named_hook('save', hook, None)
2159
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2160
self.assertLength(0, calls)
2161
# Setting an option triggers a save
2162
conf.set_option('foo', 'bar')
2163
self.assertLength(1, calls)
2164
# Since we can't assert about conf, we just use the number of calls ;-/
2166
def test_save_hook_remote_branch(self):
2167
remote_branch = branch.Branch.open(self.get_url('tree'))
2168
self.addCleanup(remote_branch.lock_write().unlock)
2169
self.assertSaveHook(remote_branch._get_config())
2171
def test_save_hook_remote_bzrdir(self):
2172
remote_branch = branch.Branch.open(self.get_url('tree'))
2173
self.addCleanup(remote_branch.lock_write().unlock)
2174
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2175
self.assertSaveHook(remote_bzrdir._get_config())
2178
class TestOption(tests.TestCase):
2180
def test_default_value(self):
2181
opt = config.Option('foo', default='bar')
2182
self.assertEquals('bar', opt.get_default())
2185
class TestOptionRegistry(tests.TestCase):
2188
super(TestOptionRegistry, self).setUp()
2189
# Always start with an empty registry
2190
self.overrideAttr(config, 'option_registry', registry.Registry())
2191
self.registry = config.option_registry
2193
def test_register(self):
2194
opt = config.Option('foo')
2195
self.registry.register('foo', opt)
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'))
2205
def test_registered_help(self):
2206
opt = config.Option('foo')
2207
self.registry.register('foo', opt, help='A simple option')
2208
self.assertEquals('A simple option', self.registry.get_help('foo'))
2211
class TestRegisteredOptions(tests.TestCase):
2212
"""All registered options should verify some constraints."""
2214
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2215
in config.option_registry.iteritems()]
2218
super(TestRegisteredOptions, self).setUp()
2219
self.registry = config.option_registry
2221
def test_proper_name(self):
2222
# An option should be registered under its own name, this can't be
2223
# checked at registration time for the lazy ones.
2224
self.assertEquals(self.option_name, self.option.name)
2226
def test_help_is_set(self):
2227
option_help = self.registry.get_help(self.option_name)
2228
self.assertNotEquals(None, option_help)
2229
# Come on, think about the user, he really wants to know whst the
2231
self.assertNotEquals('', option_help)
1849
2234
class TestSection(tests.TestCase):
1851
2236
# FIXME: Parametrize so that all sections produced by Stores run these
1969
2353
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2356
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2357
"""Simulate loading a config store without content of various encodings.
2359
All files produced by bzr are in utf8 content.
2361
Users may modify them manually and end up with a file that can't be
2362
loaded. We need to issue proper error messages in this case.
2365
invalid_utf8_char = '\xff'
2367
def test_load_utf8(self):
2368
"""Ensure we can load an utf8-encoded file."""
2369
t = self.get_transport()
2370
# From http://pad.lv/799212
2371
unicode_user = u'b\N{Euro Sign}ar'
2372
unicode_content = u'user=%s' % (unicode_user,)
2373
utf8_content = unicode_content.encode('utf8')
2374
# Store the raw content in the config file
2375
t.put_bytes('foo.conf', utf8_content)
2376
store = config.IniFileStore(t, 'foo.conf')
2378
stack = config.Stack([store.get_sections], store)
2379
self.assertEquals(unicode_user, stack.get('user'))
2381
def test_load_non_ascii(self):
2382
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2383
t = self.get_transport()
2384
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2385
store = config.IniFileStore(t, 'foo.conf')
2386
self.assertRaises(errors.ConfigContentError, store.load)
2388
def test_load_erroneous_content(self):
2389
"""Ensure we display a proper error on content that can't be parsed."""
2390
t = self.get_transport()
2391
t.put_bytes('foo.conf', '[open_section\n')
2392
store = config.IniFileStore(t, 'foo.conf')
2393
self.assertRaises(errors.ParseConfigError, store.load)
2396
class TestIniConfigContent(tests.TestCaseWithTransport):
2397
"""Simulate loading a IniBasedConfig without content of various encodings.
2399
All files produced by bzr are in utf8 content.
2401
Users may modify them manually and end up with a file that can't be
2402
loaded. We need to issue proper error messages in this case.
2405
invalid_utf8_char = '\xff'
2407
def test_load_utf8(self):
2408
"""Ensure we can load an utf8-encoded file."""
2409
# From http://pad.lv/799212
2410
unicode_user = u'b\N{Euro Sign}ar'
2411
unicode_content = u'user=%s' % (unicode_user,)
2412
utf8_content = unicode_content.encode('utf8')
2413
# Store the raw content in the config file
2414
with open('foo.conf', 'wb') as f:
2415
f.write(utf8_content)
2416
conf = config.IniBasedConfig(file_name='foo.conf')
2417
self.assertEquals(unicode_user, conf.get_user_option('user'))
2419
def test_load_badly_encoded_content(self):
2420
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2421
with open('foo.conf', 'wb') as f:
2422
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2423
conf = config.IniBasedConfig(file_name='foo.conf')
2424
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2426
def test_load_erroneous_content(self):
2427
"""Ensure we display a proper error on content that can't be parsed."""
2428
with open('foo.conf', 'wb') as f:
2429
f.write('[open_section\n')
2430
conf = config.IniBasedConfig(file_name='foo.conf')
2431
self.assertRaises(errors.ParseConfigError, conf._get_parser)
1972
2434
class TestMutableStore(TestStore):
1974
scenarios = [(key, {'store_id': key, 'get_store': builder})
1975
for key, builder in test_store_builder_registry.iteritems()]
2436
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2437
in config.test_store_builder_registry.iteritems()]
1977
2439
def setUp(self):
1978
2440
super(TestMutableStore, self).setUp()
1979
2441
self.transport = self.get_transport()
1980
self.branch = self.make_branch('branch')
1982
2443
def has_store(self, store):
1983
2444
store_basename = urlutils.relative_url(self.transport.external_url(),
2103
2598
class TestLockableIniFileStore(TestStore):
2105
2600
def test_create_store_in_created_dir(self):
2601
self.assertPathDoesNotExist('dir')
2106
2602
t = self.get_transport('dir/subdir')
2107
2603
store = config.LockableIniFileStore(t, 'foo.conf')
2108
2604
store.get_mutable_section(None).set('foo', 'bar')
2111
# FIXME: We should adapt the tests in TestLockableConfig about concurrent
2112
# writes. Since this requires a clearer rewrite, I'll just rely on using
2113
# the same code in LockableIniFileStore (copied from LockableConfig, but
2114
# trivial enough, the main difference is that we add @needs_write_lock on
2115
# save() instead of set_user_option() and remove_user_option()). The intent
2116
# is to ensure that we always get a valid content for the store even when
2117
# concurrent accesses occur, read/write, write/write. It may be worth
2118
# looking into removing the lock dir when it;s not needed anymore and look
2119
# at possible fallouts for concurrent lockers -- vila 20110-04-06
2606
self.assertPathExists('dir/subdir')
2609
class TestConcurrentStoreUpdates(TestStore):
2610
"""Test that Stores properly handle conccurent updates.
2612
New Store implementation may fail some of these tests but until such
2613
implementations exist it's hard to properly filter them from the scenarios
2614
applied here. If you encounter such a case, contact the bzr devs.
2617
scenarios = [(key, {'get_stack': builder}) for key, builder
2618
in config.test_stack_builder_registry.iteritems()]
2621
super(TestConcurrentStoreUpdates, self).setUp()
2622
self._content = 'one=1\ntwo=2\n'
2623
self.stack = self.get_stack(self)
2624
if not isinstance(self.stack, config._CompatibleStack):
2625
raise tests.TestNotApplicable(
2626
'%s is not meant to be compatible with the old config design'
2628
self.stack.store._load_from_string(self._content)
2630
self.stack.store.save()
2632
def test_simple_read_access(self):
2633
self.assertEquals('1', self.stack.get('one'))
2635
def test_simple_write_access(self):
2636
self.stack.set('one', 'one')
2637
self.assertEquals('one', self.stack.get('one'))
2639
def test_listen_to_the_last_speaker(self):
2641
c2 = self.get_stack(self)
2642
c1.set('one', 'ONE')
2643
c2.set('two', 'TWO')
2644
self.assertEquals('ONE', c1.get('one'))
2645
self.assertEquals('TWO', c2.get('two'))
2646
# The second update respect the first one
2647
self.assertEquals('ONE', c2.get('one'))
2649
def test_last_speaker_wins(self):
2650
# If the same config is not shared, the same variable modified twice
2651
# can only see a single result.
2653
c2 = self.get_stack(self)
2656
self.assertEquals('c2', c2.get('one'))
2657
# The first modification is still available until another refresh
2659
self.assertEquals('c1', c1.get('one'))
2660
c1.set('two', 'done')
2661
self.assertEquals('c2', c1.get('one'))
2663
def test_writes_are_serialized(self):
2665
c2 = self.get_stack(self)
2667
# We spawn a thread that will pause *during* the config saving.
2668
before_writing = threading.Event()
2669
after_writing = threading.Event()
2670
writing_done = threading.Event()
2671
c1_save_without_locking_orig = c1.store.save_without_locking
2672
def c1_save_without_locking():
2673
before_writing.set()
2674
c1_save_without_locking_orig()
2675
# The lock is held. We wait for the main thread to decide when to
2677
after_writing.wait()
2678
c1.store.save_without_locking = c1_save_without_locking
2682
t1 = threading.Thread(target=c1_set)
2683
# Collect the thread after the test
2684
self.addCleanup(t1.join)
2685
# Be ready to unblock the thread if the test goes wrong
2686
self.addCleanup(after_writing.set)
2688
before_writing.wait()
2689
self.assertRaises(errors.LockContention,
2690
c2.set, 'one', 'c2')
2691
self.assertEquals('c1', c1.get('one'))
2692
# Let the lock be released
2696
self.assertEquals('c2', c2.get('one'))
2698
def test_read_while_writing(self):
2700
# We spawn a thread that will pause *during* the write
2701
ready_to_write = threading.Event()
2702
do_writing = threading.Event()
2703
writing_done = threading.Event()
2704
# We override the _save implementation so we know the store is locked
2705
c1_save_without_locking_orig = c1.store.save_without_locking
2706
def c1_save_without_locking():
2707
ready_to_write.set()
2708
# The lock is held. We wait for the main thread to decide when to
2711
c1_save_without_locking_orig()
2713
c1.store.save_without_locking = c1_save_without_locking
2716
t1 = threading.Thread(target=c1_set)
2717
# Collect the thread after the test
2718
self.addCleanup(t1.join)
2719
# Be ready to unblock the thread if the test goes wrong
2720
self.addCleanup(do_writing.set)
2722
# Ensure the thread is ready to write
2723
ready_to_write.wait()
2724
self.assertEquals('c1', c1.get('one'))
2725
# If we read during the write, we get the old value
2726
c2 = self.get_stack(self)
2727
self.assertEquals('1', c2.get('one'))
2728
# Let the writing occur and ensure it occurred
2731
# Now we get the updated value
2732
c3 = self.get_stack(self)
2733
self.assertEquals('c1', c3.get('one'))
2735
# FIXME: It may be worth looking into removing the lock dir when it's not
2736
# needed anymore and look at possible fallouts for concurrent lockers. This
2737
# will matter if/when we use config files outside of bazaar directories
2738
# (.bazaar or .bzr) -- vila 20110-04-11
2122
2741
class TestSectionMatcher(TestStore):