diff options
author | Jair Gonzalez <jair.de.jesus.gonzalez.plascencia@linux.intel.com> | 2017-01-20 15:59:09 -0600 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-06-23 14:14:17 +0100 |
commit | df051ba9a2a187e25b821ca2cda6459ad0d251ce (patch) | |
tree | 7400c9f9c1b414fc236fc85e49140971c2667802 | |
parent | d591b00af06abd197904902baed3a6d2fcb5dbc6 (diff) | |
download | poky-df051ba9a2a187e25b821ca2cda6459ad0d251ce.tar.gz |
bitbake: tests: create unit tests for event module
This change adds a new unit test module (bb.tests.event)
for bitbake event.
It includes the following items:
- Client and server stubs setup
- Testing the module's main functions including:
- get_class_handlers
- set_class_handlers
- clean_class_handlers
- enable_threadlock
- disable_threadlock
- get_handlers
- set_handlers
- execute_handler
- fire_class_handlers
- print_ui_queue
- fire_ui_handlers
- fire
- fire_from_worker
- register
- remove
- register_UIHhandler
- unregister_UIHhandler
- Testing event handling using:
- class Event(object)
- class OperationStarted(Event)
- class OperationCompleted(Event)
- class OperationProgress(Event)
- class ConfigParsed(Event)
[YOCTO #10368]
(Bitbake rev: 0be3ad391adc73cc0dff81bd0ed7874f2c6a00be)
Signed-off-by: Jair Gonzalez <jair.de.jesus.gonzalez.plascencia@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | bitbake/lib/bb/tests/event.py | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/bitbake/lib/bb/tests/event.py b/bitbake/lib/bb/tests/event.py new file mode 100644 index 0000000000..c7eb1fe44c --- /dev/null +++ b/bitbake/lib/bb/tests/event.py | |||
@@ -0,0 +1,377 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # BitBake Tests for the Event implementation (event.py) | ||
5 | # | ||
6 | # Copyright (C) 2017 Intel Corporation | ||
7 | # | ||
8 | # This program is free software; you can redistribute it and/or modify | ||
9 | # it under the terms of the GNU General Public License version 2 as | ||
10 | # published by the Free Software Foundation. | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, | ||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | # GNU General Public License for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License along | ||
18 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | # | ||
21 | |||
22 | import unittest | ||
23 | import bb | ||
24 | import logging | ||
25 | import bb.compat | ||
26 | import bb.event | ||
27 | import importlib | ||
28 | import threading | ||
29 | import time | ||
30 | import pickle | ||
31 | from unittest.mock import Mock | ||
32 | from unittest.mock import call | ||
33 | |||
34 | |||
35 | class EventQueueStub(): | ||
36 | """ Class used as specification for UI event handler queue stub objects """ | ||
37 | def __init__(self): | ||
38 | return | ||
39 | |||
40 | def send(self, event): | ||
41 | return | ||
42 | |||
43 | |||
44 | class PickleEventQueueStub(): | ||
45 | """ Class used as specification for UI event handler queue stub objects | ||
46 | with sendpickle method """ | ||
47 | def __init__(self): | ||
48 | return | ||
49 | |||
50 | def sendpickle(self, pickled_event): | ||
51 | return | ||
52 | |||
53 | |||
54 | class UIClientStub(): | ||
55 | """ Class used as specification for UI event handler stub objects """ | ||
56 | def __init__(self): | ||
57 | self.event = None | ||
58 | |||
59 | |||
60 | class EventHandlingTest(unittest.TestCase): | ||
61 | """ Event handling test class """ | ||
62 | _threadlock_test_calls = [] | ||
63 | |||
64 | def setUp(self): | ||
65 | self._test_process = Mock() | ||
66 | ui_client1 = UIClientStub() | ||
67 | ui_client2 = UIClientStub() | ||
68 | self._test_ui1 = Mock(wraps=ui_client1) | ||
69 | self._test_ui2 = Mock(wraps=ui_client2) | ||
70 | importlib.reload(bb.event) | ||
71 | |||
72 | def _create_test_handlers(self): | ||
73 | """ Method used to create a test handler ordered dictionary """ | ||
74 | test_handlers = bb.compat.OrderedDict() | ||
75 | test_handlers["handler1"] = self._test_process.handler1 | ||
76 | test_handlers["handler2"] = self._test_process.handler2 | ||
77 | return test_handlers | ||
78 | |||
79 | def test_class_handlers(self): | ||
80 | """ Test set_class_handlers and get_class_handlers methods """ | ||
81 | test_handlers = self._create_test_handlers() | ||
82 | bb.event.set_class_handlers(test_handlers) | ||
83 | self.assertEqual(test_handlers, | ||
84 | bb.event.get_class_handlers()) | ||
85 | |||
86 | def test_handlers(self): | ||
87 | """ Test set_handlers and get_handlers """ | ||
88 | test_handlers = self._create_test_handlers() | ||
89 | bb.event.set_handlers(test_handlers) | ||
90 | self.assertEqual(test_handlers, | ||
91 | bb.event.get_handlers()) | ||
92 | |||
93 | def test_clean_class_handlers(self): | ||
94 | """ Test clean_class_handlers method """ | ||
95 | cleanDict = bb.compat.OrderedDict() | ||
96 | self.assertEqual(cleanDict, | ||
97 | bb.event.clean_class_handlers()) | ||
98 | |||
99 | def test_register(self): | ||
100 | """ Test register method for class handlers """ | ||
101 | result = bb.event.register("handler", self._test_process.handler) | ||
102 | self.assertEqual(result, bb.event.Registered) | ||
103 | handlers_dict = bb.event.get_class_handlers() | ||
104 | self.assertIn("handler", handlers_dict) | ||
105 | |||
106 | def test_already_registered(self): | ||
107 | """ Test detection of an already registed class handler """ | ||
108 | bb.event.register("handler", self._test_process.handler) | ||
109 | handlers_dict = bb.event.get_class_handlers() | ||
110 | self.assertIn("handler", handlers_dict) | ||
111 | result = bb.event.register("handler", self._test_process.handler) | ||
112 | self.assertEqual(result, bb.event.AlreadyRegistered) | ||
113 | |||
114 | def test_register_from_string(self): | ||
115 | """ Test register method receiving code in string """ | ||
116 | result = bb.event.register("string_handler", " return True") | ||
117 | self.assertEqual(result, bb.event.Registered) | ||
118 | handlers_dict = bb.event.get_class_handlers() | ||
119 | self.assertIn("string_handler", handlers_dict) | ||
120 | |||
121 | def test_register_with_mask(self): | ||
122 | """ Test register method with event masking """ | ||
123 | mask = ["bb.event.OperationStarted", | ||
124 | "bb.event.OperationCompleted"] | ||
125 | result = bb.event.register("event_handler", | ||
126 | self._test_process.event_handler, | ||
127 | mask) | ||
128 | self.assertEqual(result, bb.event.Registered) | ||
129 | handlers_dict = bb.event.get_class_handlers() | ||
130 | self.assertIn("event_handler", handlers_dict) | ||
131 | |||
132 | def test_remove(self): | ||
133 | """ Test remove method for class handlers """ | ||
134 | test_handlers = self._create_test_handlers() | ||
135 | bb.event.set_class_handlers(test_handlers) | ||
136 | count = len(test_handlers) | ||
137 | bb.event.remove("handler1", None) | ||
138 | test_handlers = bb.event.get_class_handlers() | ||
139 | self.assertEqual(len(test_handlers), count - 1) | ||
140 | with self.assertRaises(KeyError): | ||
141 | bb.event.remove("handler1", None) | ||
142 | |||
143 | def test_execute_handler(self): | ||
144 | """ Test execute_handler method for class handlers """ | ||
145 | mask = ["bb.event.OperationProgress"] | ||
146 | result = bb.event.register("event_handler", | ||
147 | self._test_process.event_handler, | ||
148 | mask) | ||
149 | self.assertEqual(result, bb.event.Registered) | ||
150 | event = bb.event.OperationProgress(current=10, total=100) | ||
151 | bb.event.execute_handler("event_handler", | ||
152 | self._test_process.event_handler, | ||
153 | event, | ||
154 | None) | ||
155 | self._test_process.event_handler.assert_called_once_with(event) | ||
156 | |||
157 | def test_fire_class_handlers(self): | ||
158 | """ Test fire_class_handlers method """ | ||
159 | mask = ["bb.event.OperationStarted"] | ||
160 | result = bb.event.register("event_handler1", | ||
161 | self._test_process.event_handler1, | ||
162 | mask) | ||
163 | self.assertEqual(result, bb.event.Registered) | ||
164 | result = bb.event.register("event_handler2", | ||
165 | self._test_process.event_handler2, | ||
166 | "*") | ||
167 | self.assertEqual(result, bb.event.Registered) | ||
168 | event1 = bb.event.OperationStarted() | ||
169 | event2 = bb.event.OperationCompleted(total=123) | ||
170 | bb.event.fire_class_handlers(event1, None) | ||
171 | bb.event.fire_class_handlers(event2, None) | ||
172 | bb.event.fire_class_handlers(event2, None) | ||
173 | expected_event_handler1 = [call(event1)] | ||
174 | expected_event_handler2 = [call(event1), | ||
175 | call(event2), | ||
176 | call(event2)] | ||
177 | self.assertEqual(self._test_process.event_handler1.call_args_list, | ||
178 | expected_event_handler1) | ||
179 | self.assertEqual(self._test_process.event_handler2.call_args_list, | ||
180 | expected_event_handler2) | ||
181 | |||
182 | def test_change_handler_event_mapping(self): | ||
183 | """ Test changing the event mapping for class handlers """ | ||
184 | event1 = bb.event.OperationStarted() | ||
185 | event2 = bb.event.OperationCompleted(total=123) | ||
186 | |||
187 | # register handler for all events | ||
188 | result = bb.event.register("event_handler1", | ||
189 | self._test_process.event_handler1, | ||
190 | "*") | ||
191 | self.assertEqual(result, bb.event.Registered) | ||
192 | bb.event.fire_class_handlers(event1, None) | ||
193 | bb.event.fire_class_handlers(event2, None) | ||
194 | expected = [call(event1), call(event2)] | ||
195 | self.assertEqual(self._test_process.event_handler1.call_args_list, | ||
196 | expected) | ||
197 | |||
198 | # unregister handler and register it only for OperationStarted | ||
199 | result = bb.event.remove("event_handler1", | ||
200 | self._test_process.event_handler1) | ||
201 | mask = ["bb.event.OperationStarted"] | ||
202 | result = bb.event.register("event_handler1", | ||
203 | self._test_process.event_handler1, | ||
204 | mask) | ||
205 | self.assertEqual(result, bb.event.Registered) | ||
206 | bb.event.fire_class_handlers(event1, None) | ||
207 | bb.event.fire_class_handlers(event2, None) | ||
208 | expected = [call(event1), call(event2), call(event1)] | ||
209 | self.assertEqual(self._test_process.event_handler1.call_args_list, | ||
210 | expected) | ||
211 | |||
212 | # unregister handler and register it only for OperationCompleted | ||
213 | result = bb.event.remove("event_handler1", | ||
214 | self._test_process.event_handler1) | ||
215 | mask = ["bb.event.OperationCompleted"] | ||
216 | result = bb.event.register("event_handler1", | ||
217 | self._test_process.event_handler1, | ||
218 | mask) | ||
219 | self.assertEqual(result, bb.event.Registered) | ||
220 | bb.event.fire_class_handlers(event1, None) | ||
221 | bb.event.fire_class_handlers(event2, None) | ||
222 | expected = [call(event1), call(event2), call(event1), call(event2)] | ||
223 | self.assertEqual(self._test_process.event_handler1.call_args_list, | ||
224 | expected) | ||
225 | |||
226 | def test_register_UIHhandler(self): | ||
227 | """ Test register_UIHhandler method """ | ||
228 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
229 | self.assertEqual(result, 1) | ||
230 | |||
231 | def test_UIHhandler_already_registered(self): | ||
232 | """ Test registering an UIHhandler already existing """ | ||
233 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
234 | self.assertEqual(result, 1) | ||
235 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
236 | self.assertEqual(result, 2) | ||
237 | |||
238 | def test_unregister_UIHhandler(self): | ||
239 | """ Test unregister_UIHhandler method """ | ||
240 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
241 | self.assertEqual(result, 1) | ||
242 | result = bb.event.unregister_UIHhandler(1) | ||
243 | self.assertIs(result, None) | ||
244 | |||
245 | def test_fire_ui_handlers(self): | ||
246 | """ Test fire_ui_handlers method """ | ||
247 | self._test_ui1.event = Mock(spec_set=EventQueueStub) | ||
248 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
249 | self.assertEqual(result, 1) | ||
250 | self._test_ui2.event = Mock(spec_set=PickleEventQueueStub) | ||
251 | result = bb.event.register_UIHhandler(self._test_ui2, mainui=True) | ||
252 | self.assertEqual(result, 2) | ||
253 | event1 = bb.event.OperationStarted() | ||
254 | bb.event.fire_ui_handlers(event1, None) | ||
255 | expected = [call(event1)] | ||
256 | self.assertEqual(self._test_ui1.event.send.call_args_list, | ||
257 | expected) | ||
258 | expected = [call(pickle.dumps(event1))] | ||
259 | self.assertEqual(self._test_ui2.event.sendpickle.call_args_list, | ||
260 | expected) | ||
261 | |||
262 | def test_fire(self): | ||
263 | """ Test fire method used to trigger class and ui event handlers """ | ||
264 | mask = ["bb.event.ConfigParsed"] | ||
265 | result = bb.event.register("event_handler1", | ||
266 | self._test_process.event_handler1, | ||
267 | mask) | ||
268 | |||
269 | self._test_ui1.event = Mock(spec_set=EventQueueStub) | ||
270 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
271 | self.assertEqual(result, 1) | ||
272 | |||
273 | event1 = bb.event.ConfigParsed() | ||
274 | bb.event.fire(event1, None) | ||
275 | expected = [call(event1)] | ||
276 | self.assertEqual(self._test_process.event_handler1.call_args_list, | ||
277 | expected) | ||
278 | self.assertEqual(self._test_ui1.event.send.call_args_list, | ||
279 | expected) | ||
280 | |||
281 | def test_fire_from_worker(self): | ||
282 | """ Test fire_from_worker method """ | ||
283 | self._test_ui1.event = Mock(spec_set=EventQueueStub) | ||
284 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
285 | self.assertEqual(result, 1) | ||
286 | event1 = bb.event.ConfigParsed() | ||
287 | bb.event.fire_from_worker(event1, None) | ||
288 | expected = [call(event1)] | ||
289 | self.assertEqual(self._test_ui1.event.send.call_args_list, | ||
290 | expected) | ||
291 | |||
292 | def test_print_ui_queue(self): | ||
293 | """ Test print_ui_queue method """ | ||
294 | event1 = bb.event.OperationStarted() | ||
295 | event2 = bb.event.OperationCompleted(total=123) | ||
296 | bb.event.fire(event1, None) | ||
297 | bb.event.fire(event2, None) | ||
298 | logger = logging.getLogger("BitBake") | ||
299 | logger.addHandler(bb.event.LogHandler()) | ||
300 | logger.info("Test info LogRecord") | ||
301 | logger.warning("Test warning LogRecord") | ||
302 | with self.assertLogs("BitBake", level="INFO") as cm: | ||
303 | bb.event.print_ui_queue() | ||
304 | self.assertEqual(cm.output, | ||
305 | ["INFO:BitBake:Test info LogRecord", | ||
306 | "WARNING:BitBake:Test warning LogRecord"]) | ||
307 | |||
308 | def _set_threadlock_test_mockups(self): | ||
309 | """ Create UI event handler mockups used in enable and disable | ||
310 | threadlock tests """ | ||
311 | def ui1_event_send(event): | ||
312 | if type(event) is bb.event.ConfigParsed: | ||
313 | self._threadlock_test_calls.append("w1_ui1") | ||
314 | if type(event) is bb.event.OperationStarted: | ||
315 | self._threadlock_test_calls.append("w2_ui1") | ||
316 | time.sleep(2) | ||
317 | |||
318 | def ui2_event_send(event): | ||
319 | if type(event) is bb.event.ConfigParsed: | ||
320 | self._threadlock_test_calls.append("w1_ui2") | ||
321 | if type(event) is bb.event.OperationStarted: | ||
322 | self._threadlock_test_calls.append("w2_ui2") | ||
323 | time.sleep(2) | ||
324 | |||
325 | self._threadlock_test_calls = [] | ||
326 | self._test_ui1.event = EventQueueStub() | ||
327 | self._test_ui1.event.send = ui1_event_send | ||
328 | result = bb.event.register_UIHhandler(self._test_ui1, mainui=True) | ||
329 | self.assertEqual(result, 1) | ||
330 | self._test_ui2.event = EventQueueStub() | ||
331 | self._test_ui2.event.send = ui2_event_send | ||
332 | result = bb.event.register_UIHhandler(self._test_ui2, mainui=True) | ||
333 | self.assertEqual(result, 2) | ||
334 | |||
335 | def _set_and_run_threadlock_test_workers(self): | ||
336 | """ Create and run the workers used to trigger events in enable and | ||
337 | disable threadlock tests """ | ||
338 | worker1 = threading.Thread(target=self._thread_lock_test_worker1) | ||
339 | worker2 = threading.Thread(target=self._thread_lock_test_worker2) | ||
340 | worker1.start() | ||
341 | time.sleep(1) | ||
342 | worker2.start() | ||
343 | worker1.join() | ||
344 | worker2.join() | ||
345 | |||
346 | def _thread_lock_test_worker1(self): | ||
347 | """ First worker used to fire the ConfigParsed event for enable and | ||
348 | disable threadlocks tests """ | ||
349 | bb.event.fire(bb.event.ConfigParsed(), None) | ||
350 | |||
351 | def _thread_lock_test_worker2(self): | ||
352 | """ Second worker used to fire the OperationStarted event for enable | ||
353 | and disable threadlocks tests """ | ||
354 | bb.event.fire(bb.event.OperationStarted(), None) | ||
355 | |||
356 | def test_enable_threadlock(self): | ||
357 | """ Test enable_threadlock method """ | ||
358 | self._set_threadlock_test_mockups() | ||
359 | bb.event.enable_threadlock() | ||
360 | self._set_and_run_threadlock_test_workers() | ||
361 | # Calls to UI handlers should be in order as all the registered | ||
362 | # handlers for the event coming from the first worker should be | ||
363 | # called before processing the event from the second worker. | ||
364 | self.assertEqual(self._threadlock_test_calls, | ||
365 | ["w1_ui1", "w1_ui2", "w2_ui1", "w2_ui2"]) | ||
366 | |||
367 | def test_disable_threadlock(self): | ||
368 | """ Test disable_threadlock method """ | ||
369 | self._set_threadlock_test_mockups() | ||
370 | bb.event.disable_threadlock() | ||
371 | self._set_and_run_threadlock_test_workers() | ||
372 | # Calls to UI handlers should be intertwined together. Thanks to the | ||
373 | # delay in the registered handlers for the event coming from the first | ||
374 | # worker, the event coming from the second worker starts being | ||
375 | # processed before finishing handling the first worker event. | ||
376 | self.assertEqual(self._threadlock_test_calls, | ||
377 | ["w1_ui1", "w2_ui1", "w1_ui2", "w2_ui2"]) | ||