39
|
1 # -*- coding: utf-8 -*-
|
|
2 #
|
|
3 # Copyright (C) 2005 Edgewall Software
|
|
4 # Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
|
|
5 # All rights reserved.
|
|
6 #
|
|
7 # This software is licensed as described in the file COPYING, which
|
|
8 # you should have received as part of this distribution. The terms
|
|
9 # are also available at http://trac.edgewall.com/license.html.
|
|
10 #
|
|
11 # This software consists of voluntary contributions made by many
|
|
12 # individuals. For the exact contribution history, see the revision
|
|
13 # history and logs, available at http://projects.edgewall.com/trac/.
|
|
14 #
|
|
15 # Author: Christopher Lenz <cmlenz@gmx.de>
|
|
16
|
|
17 from trac.core import *
|
|
18
|
|
19 import unittest
|
|
20
|
|
21
|
|
22 class ITest(Interface):
|
|
23 def test():
|
|
24 """Dummy function."""
|
|
25
|
|
26
|
|
27 class ComponentTestCase(unittest.TestCase):
|
|
28
|
|
29 def setUp(self):
|
|
30 from trac.core import ComponentManager, ComponentMeta
|
|
31 self.compmgr = ComponentManager()
|
|
32
|
|
33 # Make sure we have no external components hanging around in the
|
|
34 # component registry
|
|
35 self.old_registry = ComponentMeta._registry
|
|
36 ComponentMeta._registry = {}
|
|
37
|
|
38 def tearDown(self):
|
|
39 # Restore the original component registry
|
|
40 from trac.core import ComponentMeta
|
|
41 ComponentMeta._registry = self.old_registry
|
|
42
|
|
43 def test_base_class_not_registered(self):
|
|
44 """
|
|
45 Make sure that the Component base class does not appear in the component
|
|
46 registry.
|
|
47 """
|
|
48 from trac.core import ComponentMeta
|
|
49 assert Component not in ComponentMeta._components
|
|
50 self.assertRaises(TracError, self.compmgr.__getitem__, Component)
|
|
51
|
|
52 def test_abstract_component_not_registered(self):
|
|
53 """
|
|
54 Make sure that a Component class marked as abstract does not appear in
|
|
55 the component registry.
|
|
56 """
|
|
57 from trac.core import ComponentMeta
|
|
58 class AbstractComponent(Component):
|
|
59 abstract = True
|
|
60 assert AbstractComponent not in ComponentMeta._components
|
|
61 self.assertRaises(TracError, self.compmgr.__getitem__,
|
|
62 AbstractComponent)
|
|
63
|
|
64 def test_unregistered_component(self):
|
|
65 """
|
|
66 Make sure the component manager refuses to manage classes not derived
|
|
67 from `Component`.
|
|
68 """
|
|
69 class NoComponent(object): pass
|
|
70 self.assertRaises(TracError, self.compmgr.__getitem__, NoComponent)
|
|
71
|
|
72 def test_component_registration(self):
|
|
73 """
|
|
74 Verify that classes derived from `Component` are managed by the
|
|
75 component manager.
|
|
76 """
|
|
77 class ComponentA(Component):
|
|
78 pass
|
|
79 assert self.compmgr[ComponentA]
|
|
80 assert ComponentA(self.compmgr)
|
|
81
|
|
82 def test_component_identity(self):
|
|
83 """
|
|
84 Make sure instantiating a component multiple times just returns the
|
|
85 same instance again.
|
|
86 """
|
|
87 class ComponentA(Component):
|
|
88 pass
|
|
89 c1 = ComponentA(self.compmgr)
|
|
90 c2 = ComponentA(self.compmgr)
|
|
91 assert c1 is c2, 'Expected same component instance'
|
|
92 c2 = self.compmgr[ComponentA]
|
|
93 assert c1 is c2, 'Expected same component instance'
|
|
94
|
|
95 def test_component_initializer(self):
|
|
96 """
|
|
97 Makes sure that a components' `__init__` method gets called.
|
|
98 """
|
|
99 class ComponentA(Component):
|
|
100 def __init__(self):
|
|
101 self.data = 'test'
|
|
102 self.assertEqual('test', ComponentA(self.compmgr).data)
|
|
103 ComponentA(self.compmgr).data = 'newtest'
|
|
104 self.assertEqual('newtest', ComponentA(self.compmgr).data)
|
|
105
|
|
106 def test_inherited_component_initializer(self):
|
|
107 """
|
|
108 Makes sure that a the `__init__` method of a components' super-class
|
|
109 gets called if the component doesn't override it.
|
|
110 """
|
|
111 class ComponentA(Component):
|
|
112 def __init__(self):
|
|
113 self.data = 'foo'
|
|
114 class ComponentB(ComponentA):
|
|
115 def __init__(self):
|
|
116 self.data = 'bar'
|
|
117 class ComponentC(ComponentB):
|
|
118 pass
|
|
119 self.assertEqual('bar', ComponentC(self.compmgr).data)
|
|
120 ComponentC(self.compmgr).data = 'baz'
|
|
121 self.assertEqual('baz', ComponentC(self.compmgr).data)
|
|
122
|
|
123 def test_implements_called_outside_classdef(self):
|
|
124 """
|
|
125 Verify that calling implements() outside a class definition raises an
|
|
126 `AssertionError`.
|
|
127 """
|
|
128 try:
|
|
129 implements()
|
|
130 self.fail('Expected AssertionError')
|
|
131 except AssertionError:
|
|
132 pass
|
|
133
|
|
134 def test_implements_called_twice(self):
|
|
135 """
|
|
136 Verify that calling implements() twice in a class definition raises an
|
|
137 `AssertionError`.
|
|
138 """
|
|
139 try:
|
|
140 class ComponentA(Component):
|
|
141 implements()
|
|
142 implements()
|
|
143 self.fail('Expected AssertionError')
|
|
144 except AssertionError:
|
|
145 pass
|
|
146
|
|
147 def test_attribute_access(self):
|
|
148 """
|
|
149 Verify that accessing undefined attributes on components raises an
|
|
150 `AttributeError`.
|
|
151 """
|
|
152 class ComponentA(Component):
|
|
153 pass
|
|
154 comp = ComponentA(self.compmgr)
|
|
155 try:
|
|
156 comp.foo
|
|
157 self.fail('Expected AttributeError')
|
|
158 except AttributeError:
|
|
159 pass
|
|
160
|
|
161 def test_nonconforming_extender(self):
|
|
162 """
|
|
163 Verify that accessing a method of a declared extension point interface
|
|
164 raises a normal `AttributeError` if the component does not implement
|
|
165 the method.
|
|
166 """
|
|
167 class ComponentA(Component):
|
|
168 tests = ExtensionPoint(ITest)
|
|
169 class ComponentB(Component):
|
|
170 implements(ITest)
|
|
171 tests = iter(ComponentA(self.compmgr).tests)
|
|
172 try:
|
|
173 tests.next().test()
|
|
174 self.fail('Expected AttributeError')
|
|
175 except AttributeError:
|
|
176 pass
|
|
177
|
|
178 def test_extension_point_with_no_extension(self):
|
|
179 """
|
|
180 Verify that accessing an extension point with no extenders returns an
|
|
181 empty list.
|
|
182 """
|
|
183 class ComponentA(Component):
|
|
184 tests = ExtensionPoint(ITest)
|
|
185 tests = iter(ComponentA(self.compmgr).tests)
|
|
186 self.assertRaises(StopIteration, tests.next)
|
|
187
|
|
188 def test_extension_point_with_one_extension(self):
|
|
189 """
|
|
190 Verify that a single component extending an extension point can be
|
|
191 accessed through the extension point attribute of the declaring
|
|
192 component.
|
|
193 """
|
|
194 class ComponentA(Component):
|
|
195 tests = ExtensionPoint(ITest)
|
|
196 class ComponentB(Component):
|
|
197 implements(ITest)
|
|
198 def test(self): return 'x'
|
|
199 tests = iter(ComponentA(self.compmgr).tests)
|
|
200 self.assertEquals('x', tests.next().test())
|
|
201 self.assertRaises(StopIteration, tests.next)
|
|
202
|
|
203 def test_extension_point_with_two_extensions(self):
|
|
204 """
|
|
205 Verify that two components extending an extension point can be accessed
|
|
206 through the extension point attribute of the declaring component.
|
|
207 """
|
|
208 class ComponentA(Component):
|
|
209 tests = ExtensionPoint(ITest)
|
|
210 class ComponentB(Component):
|
|
211 implements(ITest)
|
|
212 def test(self): return 'x'
|
|
213 class ComponentC(Component):
|
|
214 implements(ITest)
|
|
215 def test(self): return 'y'
|
|
216 tests = iter(ComponentA(self.compmgr).tests)
|
|
217 self.assertEquals('x', tests.next().test())
|
|
218 self.assertEquals('y', tests.next().test())
|
|
219 self.assertRaises(StopIteration, tests.next)
|
|
220
|
|
221 def test_inherited_extension_point(self):
|
|
222 """
|
|
223 Verify that extension points are inherited to sub-classes.
|
|
224 """
|
|
225 class BaseComponent(Component):
|
|
226 tests = ExtensionPoint(ITest)
|
|
227 class ConcreteComponent(BaseComponent):
|
|
228 pass
|
|
229 class ExtendingComponent(Component):
|
|
230 implements(ITest)
|
|
231 def test(self): return 'x'
|
|
232 tests = iter(ConcreteComponent(self.compmgr).tests)
|
|
233 self.assertEquals('x', tests.next().test())
|
|
234 self.assertRaises(StopIteration, tests.next)
|
|
235
|
|
236 def test_inherited_implements(self):
|
|
237 """
|
|
238 Verify that a component with a super-class implementing an extension
|
|
239 point interface is also registered as implementing that interface.
|
|
240 """
|
|
241 class BaseComponent(Component):
|
|
242 implements(ITest)
|
|
243 abstract = True
|
|
244 class ConcreteComponent(BaseComponent):
|
|
245 pass
|
|
246 from trac.core import ComponentMeta
|
|
247 assert ConcreteComponent in ComponentMeta._registry[ITest]
|
|
248
|
|
249 def test_component_manager_component(self):
|
|
250 """
|
|
251 Verify that a component manager can itself be a component with its own
|
|
252 extension points.
|
|
253 """
|
|
254 from trac.core import ComponentManager
|
|
255 class ManagerComponent(ComponentManager, Component):
|
|
256 tests = ExtensionPoint(ITest)
|
|
257 def __init__(self, foo, bar):
|
|
258 ComponentManager.__init__(self)
|
|
259 self.foo, self.bar = foo, bar
|
|
260 class Extender(Component):
|
|
261 implements(ITest)
|
|
262 def test(self): return 'x'
|
|
263 mgr = ManagerComponent('Test', 42)
|
|
264 assert id(mgr) == id(mgr[ManagerComponent])
|
|
265 tests = iter(mgr.tests)
|
|
266 self.assertEquals('x', tests.next().test())
|
|
267 self.assertRaises(StopIteration, tests.next)
|
|
268
|
|
269 def test_instantiation_doesnt_enable(self):
|
|
270 """
|
|
271 Make sure that a component disabled by the ComponentManager is not
|
|
272 implicitly enabled by instantiating it directly.
|
|
273 """
|
|
274 from trac.core import ComponentManager
|
|
275 class DisablingComponentManager(ComponentManager):
|
|
276 def is_component_enabled(self, cls):
|
|
277 return False
|
|
278 class ComponentA(Component):
|
|
279 pass
|
|
280 mgr = DisablingComponentManager()
|
|
281 instance = ComponentA(mgr)
|
|
282 self.assertEqual(None, mgr[ComponentA])
|
|
283
|
|
284
|
|
285 def suite():
|
|
286 return unittest.makeSuite(ComponentTestCase, 'test')
|
|
287
|
|
288 if __name__ == '__main__':
|
|
289 unittest.main()
|