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 /bitbake/lib | |
| 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>
Diffstat (limited to 'bitbake/lib')
| -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"]) | ||
