1
# Copyright (C) 2006, 2008-2012, 2016 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for the Registry classes"""
30
class TestRegistry(tests.TestCase):
32
def register_stuff(self, a_registry):
33
a_registry.register('one', 1)
34
a_registry.register('two', 2)
35
a_registry.register('four', 4)
36
a_registry.register('five', 5)
38
def test_registry(self):
39
a_registry = registry.Registry()
40
self.register_stuff(a_registry)
42
self.assertTrue(a_registry.default_key is None)
44
# test get() (self.default_key is None)
45
self.assertRaises(KeyError, a_registry.get)
46
self.assertRaises(KeyError, a_registry.get, None)
47
self.assertEqual(2, a_registry.get('two'))
48
self.assertRaises(KeyError, a_registry.get, 'three')
50
# test _set_default_key
51
a_registry.default_key = 'five'
52
self.assertTrue(a_registry.default_key == 'five')
53
self.assertEqual(5, a_registry.get())
54
self.assertEqual(5, a_registry.get(None))
55
# If they ask for a specific entry, they should get KeyError
56
# not the default value. They can always pass None if they prefer
57
self.assertRaises(KeyError, a_registry.get, 'six')
58
self.assertRaises(KeyError, a_registry._set_default_key, 'six')
61
self.assertEqual(['five', 'four', 'one', 'two'], a_registry.keys())
63
def test_registry_funcs(self):
64
a_registry = registry.Registry()
65
self.register_stuff(a_registry)
67
self.assertTrue('one' in a_registry)
68
a_registry.remove('one')
69
self.assertFalse('one' in a_registry)
70
self.assertRaises(KeyError, a_registry.get, 'one')
72
a_registry.register('one', 'one')
74
self.assertEqual(['five', 'four', 'one', 'two'],
75
sorted(a_registry.keys()))
76
self.assertEqual([('five', 5), ('four', 4),
77
('one', 'one'), ('two', 2)],
78
sorted(a_registry.iteritems()))
80
def test_register_override(self):
81
a_registry = registry.Registry()
82
a_registry.register('one', 'one')
83
self.assertRaises(KeyError, a_registry.register, 'one', 'two')
84
self.assertRaises(KeyError, a_registry.register, 'one', 'two',
85
override_existing=False)
87
a_registry.register('one', 'two', override_existing=True)
88
self.assertEqual('two', a_registry.get('one'))
90
self.assertRaises(KeyError, a_registry.register_lazy,
91
'one', 'three', 'four')
93
a_registry.register_lazy('one', 'module', 'member',
94
override_existing=True)
96
def test_registry_help(self):
97
a_registry = registry.Registry()
98
a_registry.register('one', 1, help='help text for one')
99
# We should not have to import the module to return the help
101
a_registry.register_lazy('two', 'nonexistent_module', 'member',
102
help='help text for two')
104
# We should be able to handle a callable to get information
106
def generic_help(reg, key):
107
help_calls.append(key)
108
return 'generic help for %s' % (key,)
109
a_registry.register('three', 3, help=generic_help)
110
a_registry.register_lazy('four', 'nonexistent_module', 'member2',
112
a_registry.register('five', 5)
114
def help_from_object(reg, key):
118
class SimpleObj(object):
120
return 'this is my help'
121
a_registry.register('six', SimpleObj(), help=help_from_object)
123
self.assertEqual('help text for one', a_registry.get_help('one'))
124
self.assertEqual('help text for two', a_registry.get_help('two'))
125
self.assertEqual('generic help for three',
126
a_registry.get_help('three'))
127
self.assertEqual(['three'], help_calls)
128
self.assertEqual('generic help for four',
129
a_registry.get_help('four'))
130
self.assertEqual(['three', 'four'], help_calls)
131
self.assertEqual(None, a_registry.get_help('five'))
132
self.assertEqual('this is my help', a_registry.get_help('six'))
134
self.assertRaises(KeyError, a_registry.get_help, None)
135
self.assertRaises(KeyError, a_registry.get_help, 'seven')
137
a_registry.default_key = 'one'
138
self.assertEqual('help text for one', a_registry.get_help(None))
139
self.assertRaises(KeyError, a_registry.get_help, 'seven')
141
self.assertEqual([('five', None),
142
('four', 'generic help for four'),
143
('one', 'help text for one'),
144
('six', 'this is my help'),
145
('three', 'generic help for three'),
146
('two', 'help text for two'),
147
], sorted((key, a_registry.get_help(key))
148
for key in a_registry.keys()))
150
# We don't know what order it was called in, but we should get
151
# 2 more calls to three and four
152
self.assertEqual(['four', 'four', 'three', 'three'],
155
def test_registry_info(self):
156
a_registry = registry.Registry()
157
a_registry.register('one', 1, info='string info')
158
# We should not have to import the module to return the info
159
a_registry.register_lazy('two', 'nonexistent_module', 'member',
162
# We should be able to handle a callable to get information
163
a_registry.register('three', 3, info=['a', 'list'])
165
a_registry.register_lazy('four', 'nonexistent_module', 'member2',
167
a_registry.register('five', 5)
169
self.assertEqual('string info', a_registry.get_info('one'))
170
self.assertEqual(2, a_registry.get_info('two'))
171
self.assertEqual(['a', 'list'], a_registry.get_info('three'))
172
self.assertIs(obj, a_registry.get_info('four'))
173
self.assertIs(None, a_registry.get_info('five'))
175
self.assertRaises(KeyError, a_registry.get_info, None)
176
self.assertRaises(KeyError, a_registry.get_info, 'six')
178
a_registry.default_key = 'one'
179
self.assertEqual('string info', a_registry.get_info(None))
180
self.assertRaises(KeyError, a_registry.get_info, 'six')
182
self.assertEqual([('five', None),
184
('one', 'string info'),
185
('three', ['a', 'list']),
187
], sorted((key, a_registry.get_info(key))
188
for key in a_registry.keys()))
190
def test_get_prefix(self):
191
my_registry = registry.Registry()
192
http_object = object()
193
sftp_object = object()
194
my_registry.register('http:', http_object)
195
my_registry.register('sftp:', sftp_object)
196
found_object, suffix = my_registry.get_prefix('http://foo/bar')
197
self.assertEqual('//foo/bar', suffix)
198
self.assertIs(http_object, found_object)
199
self.assertIsNot(sftp_object, found_object)
200
found_object, suffix = my_registry.get_prefix('sftp://baz/qux')
201
self.assertEqual('//baz/qux', suffix)
202
self.assertIs(sftp_object, found_object)
205
class TestRegistryIter(tests.TestCase):
206
"""Test registry iteration behaviors.
208
There are dark corner cases here when the registered objects trigger
209
addition in the iterated registry.
213
super(TestRegistryIter, self).setUp()
215
# We create a registry with "official" objects and "hidden"
216
# objects. The later represent the side effects that led to bug #277048
218
_registry = registry.Registry()
221
_registry.register('hidden', None)
223
# Avoid closing over self by binding local variable
224
self.registry = _registry
225
self.registry.register('passive', None)
226
self.registry.register('active', register_more)
227
self.registry.register('passive-too', None)
229
class InvasiveGetter(registry._ObjectGetter):
231
def get_obj(inner_self):
232
# Surprise ! Getting a registered object (think lazy loaded
233
# module) register yet another object !
234
_registry.register('more hidden', None)
235
return inner_self._obj
237
self.registry.register('hacky', None)
238
# We peek under the covers because the alternative is to use lazy
239
# registration and create a module that can reference our test registry
240
# it's too much work for such a corner case -- vila 090916
241
self.registry._dict['hacky'] = InvasiveGetter(None)
243
def _iter_them(self, iter_func_name):
244
iter_func = getattr(self.registry, iter_func_name, None)
245
self.assertIsNot(None, iter_func)
247
for name, func in iter_func():
249
self.assertFalse(name in ('hidden', 'more hidden'))
251
# Using an object register another one as a side effect
253
self.assertEqual(4, count)
255
def test_iteritems(self):
256
# the dict is modified during the iteration
257
self.assertRaises(RuntimeError, self._iter_them, 'iteritems')
259
def test_items(self):
260
# we should be able to iterate even if one item modify the dict
261
self._iter_them('items')
264
class TestRegistryWithDirs(tests.TestCaseInTempDir):
265
"""Registry tests that require temporary dirs"""
267
def create_plugin_file(self, contents):
268
"""Create a file to be used as a plugin.
270
This is created in a temporary directory, so that we
271
are sure that it doesn't start in the plugin path.
274
plugin_name = 'bzr_plugin_a_%s' % (osutils.rand_chars(4),)
275
with open('tmp/'+plugin_name+'.py', 'wb') as f: f.write(contents)
278
def create_simple_plugin(self):
279
return self.create_plugin_file(
282
'def function(a,b,c):\n'
285
'class MyClass(object):\n'
286
' def __init__(self, a):\n'
291
def test_lazy_import_registry_foo(self):
292
a_registry = registry.Registry()
293
a_registry.register_lazy('foo', 'bzrlib.branch', 'Branch')
294
a_registry.register_lazy('bar', 'bzrlib.branch', 'Branch.hooks')
295
self.assertEqual(branch.Branch, a_registry.get('foo'))
296
self.assertEqual(branch.Branch.hooks, a_registry.get('bar'))
298
def test_lazy_import_registry(self):
299
plugin_name = self.create_simple_plugin()
300
a_registry = registry.Registry()
301
a_registry.register_lazy('obj', plugin_name, 'object1')
302
a_registry.register_lazy('function', plugin_name, 'function')
303
a_registry.register_lazy('klass', plugin_name, 'MyClass')
304
a_registry.register_lazy('module', plugin_name, None)
306
self.assertEqual(['function', 'klass', 'module', 'obj'],
307
sorted(a_registry.keys()))
308
# The plugin should not be loaded until we grab the first object
309
self.assertFalse(plugin_name in sys.modules)
311
# By default the plugin won't be in the search path
312
self.assertRaises(ImportError, a_registry.get, 'obj')
314
plugin_path = os.getcwd() + '/tmp'
315
sys.path.append(plugin_path)
317
obj = a_registry.get('obj')
318
self.assertEqual('foo', obj)
319
self.assertTrue(plugin_name in sys.modules)
321
# Now grab another object
322
func = a_registry.get('function')
323
self.assertEqual(plugin_name, func.__module__)
324
self.assertEqual('function', func.__name__)
325
self.assertEqual((1, [], '3'), func(1, [], '3'))
327
# And finally a class
328
klass = a_registry.get('klass')
329
self.assertEqual(plugin_name, klass.__module__)
330
self.assertEqual('MyClass', klass.__name__)
333
self.assertIsInstance(inst, klass)
334
self.assertEqual(1, inst.a)
336
module = a_registry.get('module')
337
self.assertIs(obj, module.object1)
338
self.assertIs(func, module.function)
339
self.assertIs(klass, module.MyClass)
341
sys.path.remove(plugin_path)
343
def test_lazy_import_get_module(self):
344
a_registry = registry.Registry()
345
a_registry.register_lazy('obj', "bzrlib.tests.test_registry",
347
self.assertEqual("bzrlib.tests.test_registry",
348
a_registry._get_module("obj"))
350
def test_normal_get_module(self):
351
class AThing(object):
353
a_registry = registry.Registry()
354
a_registry.register("obj", AThing())
355
self.assertEqual("bzrlib.tests.test_registry",
356
a_registry._get_module("obj"))