test_event_system.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. import unittest
  2. from cloudbridge.cloud.base.events import SimpleEventDispatcher
  3. from cloudbridge.cloud.interfaces.events import EventHandler
  4. from cloudbridge.cloud.interfaces.exceptions import HandlerException
  5. class EventSystemTestCase(unittest.TestCase):
  6. def test_emit_event_no_handlers(self):
  7. dispatcher = SimpleEventDispatcher()
  8. result = dispatcher.dispatch(self, "event.hello.world")
  9. self.assertIsNone(result, "Result should be none as there are no"
  10. "registered handlers")
  11. def test_emit_event_observing_handler(self):
  12. EVENT_NAME = "event.hello.world"
  13. callback_tracker = ['']
  14. def my_callback(event_args, *args, **kwargs):
  15. self.assertDictEqual(event_args,
  16. {'sender': self,
  17. 'event': EVENT_NAME})
  18. self.assertSequenceEqual(args, ['first_pos_arg'])
  19. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  20. callback_tracker[0] += 'obs'
  21. return "hello"
  22. dispatcher = SimpleEventDispatcher()
  23. handler = dispatcher.observe(event_pattern=EVENT_NAME, priority=1000,
  24. callback=my_callback)
  25. self.assertIsInstance(handler, EventHandler)
  26. result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
  27. a_keyword_arg='another_thing')
  28. self.assertEqual(
  29. callback_tracker[0], "obs", "callback should have been invoked"
  30. "once and contain value `obs` but tracker value is {0}".format(
  31. callback_tracker[0]))
  32. self.assertIsNone(result, "Result should be none as this is an"
  33. " observing handler")
  34. def test_emit_event_intercepting_handler(self):
  35. EVENT_NAME = "event.hello.world"
  36. callback_tracker = ['']
  37. def my_callback(event_args, *args, **kwargs):
  38. self.assertDictEqual(event_args,
  39. {'sender': self,
  40. 'event': EVENT_NAME,
  41. 'next_handler': None})
  42. self.assertSequenceEqual(args, ['first_pos_arg'])
  43. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  44. callback_tracker[0] += "intcpt"
  45. return "world"
  46. dispatcher = SimpleEventDispatcher()
  47. handler = dispatcher.intercept(event_pattern=EVENT_NAME, priority=1000,
  48. callback=my_callback)
  49. self.assertIsInstance(handler, EventHandler)
  50. result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
  51. a_keyword_arg='another_thing')
  52. self.assertEqual(
  53. callback_tracker[0], "intcpt", "callback should have been invoked"
  54. "once and contain value `intcpt` but tracker value is {0}".format(
  55. callback_tracker[0]))
  56. self.assertEqual(result, "world", "Result should be `world` as this"
  57. " is an intercepting handler")
  58. def test_emit_event_implementing_handler(self):
  59. EVENT_NAME = "event.hello.world"
  60. callback_tracker = ['']
  61. def my_callback(*args, **kwargs):
  62. self.assertSequenceEqual(args, ['first_pos_arg'])
  63. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  64. callback_tracker[0] += "impl"
  65. return "world"
  66. dispatcher = SimpleEventDispatcher()
  67. handler = dispatcher.implement(event_pattern=EVENT_NAME, priority=1000,
  68. callback=my_callback)
  69. self.assertIsInstance(handler, EventHandler)
  70. result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
  71. a_keyword_arg='another_thing')
  72. self.assertEqual(
  73. callback_tracker[0], "impl", "callback should have been invoked"
  74. "once and contain value `intcpt` but tracker value is {0}".format(
  75. callback_tracker[0]))
  76. self.assertEqual(result, "world", "Result should be `world` as this"
  77. " is an implementing handler")
  78. def test_emit_event_observe_precedes_intercept(self):
  79. EVENT_NAME = "event.hello.world"
  80. callback_tracker = ['']
  81. def my_callback_obs(event_args, *args, **kwargs):
  82. self.assertDictEqual(event_args,
  83. {'sender': self,
  84. 'event': EVENT_NAME})
  85. self.assertSequenceEqual(args, ['first_pos_arg'])
  86. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  87. callback_tracker[0] += "obs_"
  88. return "hello"
  89. def my_callback_intcpt(event_args, *args, **kwargs):
  90. self.assertDictEqual(event_args,
  91. {'sender': self,
  92. 'event': EVENT_NAME,
  93. 'next_handler': None})
  94. self.assertSequenceEqual(args, ['first_pos_arg'])
  95. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  96. callback_tracker[0] += "intcpt_"
  97. return "world"
  98. dispatcher = SimpleEventDispatcher()
  99. dispatcher.observe(EVENT_NAME, 1000, my_callback_obs)
  100. dispatcher.intercept(EVENT_NAME, 1001, my_callback_intcpt)
  101. result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
  102. a_keyword_arg='another_thing')
  103. self.assertEqual(
  104. callback_tracker[0], "obs_intcpt_", "callback was not invoked in "
  105. "expected order. Should have been obs_intcpt_ but is {0}".format(
  106. callback_tracker[0]))
  107. self.assertEqual(result, "world", "Result should be `world` as this"
  108. " is the return value of the intercepting handler")
  109. def test_emit_event_observe_follows_intercept(self):
  110. EVENT_NAME = "event.hello.world"
  111. callback_tracker = ['']
  112. def my_callback_intcpt(event_args, *args, **kwargs):
  113. self.assertSequenceEqual(args, ['first_pos_arg'])
  114. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  115. self.assertEqual(event_args.get('sender'), self)
  116. self.assertEqual(event_args.get('next_handler').priority, 1001)
  117. self.assertEqual(event_args.get('next_handler').callback.__name__,
  118. "my_callback_obs")
  119. callback_tracker[0] += "intcpt_"
  120. # invoke next handler
  121. next_handler = event_args.get('next_handler')
  122. assert next_handler.priority == 1001
  123. assert next_handler.event_pattern == EVENT_NAME
  124. assert next_handler.callback == my_callback_obs
  125. retval = next_handler.invoke(event_args, *args, **kwargs)
  126. self.assertIsNone(retval, "Return values of observable handlers"
  127. " should not be propagated.")
  128. return "world"
  129. def my_callback_obs(event_args, *args, **kwargs):
  130. self.assertSequenceEqual(args, ['first_pos_arg'])
  131. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  132. self.assertDictEqual(event_args,
  133. {'sender': self,
  134. 'event': EVENT_NAME})
  135. callback_tracker[0] += "obs_"
  136. return "hello"
  137. dispatcher = SimpleEventDispatcher()
  138. # register priorities out of order to test that too
  139. dispatcher.observe(EVENT_NAME, 1001, my_callback_obs)
  140. dispatcher.intercept(EVENT_NAME, 1000, my_callback_intcpt)
  141. result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
  142. a_keyword_arg='another_thing')
  143. self.assertEqual(
  144. callback_tracker[0], "intcpt_obs_", "callback was not invoked in "
  145. "expected order. Should have been intcpt_obs_ but is {0}".format(
  146. callback_tracker[0]))
  147. self.assertEqual(result, "world", "Result should be `world` as this"
  148. " is the return value of the intercepting handler")
  149. def test_emit_event_intercept_follows_intercept(self):
  150. EVENT_NAME = "event.hello.world"
  151. callback_tracker = ['']
  152. def my_callback_intcpt1(event_args, *args, **kwargs):
  153. self.assertEqual(event_args.get('sender'), self)
  154. next_handler = event_args.get('next_handler')
  155. self.assertEqual(next_handler.priority, 2020)
  156. self.assertEqual(next_handler.callback.__name__,
  157. "my_callback_intcpt2")
  158. callback_tracker[0] += "intcpt1_"
  159. # invoke next handler but ignore return value
  160. return "hello" + next_handler.invoke(event_args, *args, **kwargs)
  161. def my_callback_intcpt2(event_args, *args, **kwargs):
  162. self.assertDictEqual(event_args,
  163. {'sender': self,
  164. 'event': EVENT_NAME,
  165. 'next_handler': None})
  166. callback_tracker[0] += "intcpt2_"
  167. return "world"
  168. dispatcher = SimpleEventDispatcher()
  169. dispatcher.intercept(EVENT_NAME, 2000, my_callback_intcpt1)
  170. dispatcher.intercept(EVENT_NAME, 2020, my_callback_intcpt2)
  171. result = dispatcher.dispatch(self, EVENT_NAME)
  172. self.assertEqual(
  173. callback_tracker[0], "intcpt1_intcpt2_", "callback was not invoked"
  174. " in expected order. Should have been intcpt1_intcpt2_ but is"
  175. " {0}".format(callback_tracker[0]))
  176. self.assertEqual(result, "helloworld", "Result should be `helloworld` "
  177. "as this is the expected return value from the chain")
  178. def test_emit_event_implement_follows_intercept(self):
  179. EVENT_NAME = "event.hello.world"
  180. callback_tracker = ['']
  181. def my_callback_intcpt(event_args, *args, **kwargs):
  182. self.assertEqual(event_args.get('sender'), self)
  183. next_handler = event_args.get('next_handler')
  184. self.assertEqual(next_handler.priority, 2020)
  185. self.assertEqual(next_handler.callback.__name__,
  186. "my_callback_impl")
  187. callback_tracker[0] += "intcpt_"
  188. # invoke next handler but ignore return value
  189. return "hello" + next_handler.invoke(event_args, *args, **kwargs)
  190. def my_callback_impl(*args, **kwargs):
  191. self.assertSequenceEqual(args, ['first_pos_arg'])
  192. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  193. callback_tracker[0] += "impl_"
  194. return "world"
  195. dispatcher = SimpleEventDispatcher()
  196. dispatcher.intercept(EVENT_NAME, 2000, my_callback_intcpt)
  197. dispatcher.implement(EVENT_NAME, 2020, my_callback_impl)
  198. result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
  199. a_keyword_arg='another_thing')
  200. self.assertEqual(
  201. callback_tracker[0], "intcpt_impl_", "callback was not invoked"
  202. " in expected order. Should have been intcpt_impl_ but is"
  203. " {0}".format(callback_tracker[0]))
  204. self.assertEqual(result, "helloworld", "Result should be `helloworld` "
  205. "as this is the expected return value from the chain")
  206. def test_emit_event_implement_precedes_intercept(self):
  207. EVENT_NAME = "event.hello.world"
  208. callback_tracker = ['']
  209. def my_callback_intcpt(event_args, *args, **kwargs):
  210. # Impl result should be accessible to intercepts that follow
  211. self.assertDictEqual(event_args,
  212. {'sender': self,
  213. 'event': EVENT_NAME,
  214. 'result': 'world',
  215. 'next_handler': None})
  216. self.assertSequenceEqual(args, ['first_pos_arg'])
  217. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  218. callback_tracker[0] += "intcpt_"
  219. return "hello"
  220. def my_callback_impl(*args, **kwargs):
  221. self.assertSequenceEqual(args, ['first_pos_arg'])
  222. self.assertDictEqual(kwargs, {'a_keyword_arg': 'another_thing'})
  223. callback_tracker[0] += "impl_"
  224. return "world"
  225. dispatcher = SimpleEventDispatcher()
  226. dispatcher.implement(EVENT_NAME, 2000, my_callback_impl)
  227. dispatcher.intercept(EVENT_NAME, 2020, my_callback_intcpt)
  228. result = dispatcher.dispatch(self, EVENT_NAME, 'first_pos_arg',
  229. a_keyword_arg='another_thing')
  230. self.assertEqual(
  231. callback_tracker[0], "impl_intcpt_", "callback was not invoked"
  232. " in expected order. Should have been intcpt_intcpt_ but is"
  233. " {0}".format(callback_tracker[0]))
  234. self.assertEqual(result, "world", "Result should be `world` "
  235. "as this is the expected return value from the chain")
  236. def test_subscribe_event_duplicate_priority(self):
  237. def my_callback(event_args, *args, **kwargs):
  238. pass
  239. dispatcher = SimpleEventDispatcher()
  240. dispatcher.intercept("event.hello.world", 1000, my_callback)
  241. dispatcher.intercept("event.hello.world", 1000, my_callback)
  242. with self.assertRaises(HandlerException):
  243. dispatcher.dispatch(self, "event.hello.world")
  244. def test_subscribe_event_duplicate_wildcard_priority(self):
  245. def my_callback(event_args, *args, **kwargs):
  246. pass
  247. dispatcher = SimpleEventDispatcher()
  248. dispatcher.intercept("event.hello.world", 1000, my_callback)
  249. dispatcher.intercept("event.hello.*", 1000, my_callback)
  250. with self.assertRaises(HandlerException):
  251. dispatcher.dispatch(self, "event.hello.world")
  252. def test_subscribe_event_duplicate_wildcard_priority_allowed(self):
  253. # duplicate priorities for different wildcard namespaces allowed
  254. def my_callback(event_args, *args, **kwargs):
  255. pass
  256. dispatcher = SimpleEventDispatcher()
  257. dispatcher.intercept("event.hello.world", 1000, my_callback)
  258. dispatcher.intercept("someevent.hello.*", 1000, my_callback)
  259. # emit should work fine in this case with no exceptions
  260. dispatcher.dispatch(self, "event.hello.world")
  261. def test_subscribe_multiple_events(self):
  262. EVENT_NAME = "event.hello.world"
  263. callback_tracker = ['']
  264. def my_callback1(event_args, *args, **kwargs):
  265. self.assertDictEqual(event_args, {'sender': self,
  266. 'event': EVENT_NAME})
  267. callback_tracker[0] += "event1_"
  268. return "hello"
  269. def my_callback2(event_args, *args, **kwargs):
  270. self.assertDictEqual(event_args,
  271. {'sender': self,
  272. 'event': "event.hello.anotherworld"})
  273. callback_tracker[0] += "event2_"
  274. return "another"
  275. def my_callback3(event_args, *args, **kwargs):
  276. self.assertDictEqual(event_args,
  277. {'sender': self,
  278. 'event': "event.hello.anotherworld",
  279. 'next_handler': None})
  280. callback_tracker[0] += "event3_"
  281. return "world"
  282. dispatcher = SimpleEventDispatcher()
  283. dispatcher.observe(EVENT_NAME, 2000, my_callback1)
  284. # register to a different event with the same priority
  285. dispatcher.observe("event.hello.anotherworld", 2000, my_callback2)
  286. dispatcher.intercept("event.hello.anotherworld", 2020, my_callback3)
  287. result = dispatcher.dispatch(self, EVENT_NAME)
  288. self.assertEqual(
  289. callback_tracker[0], "event1_", "only `event.hello.world` handlers"
  290. " should have been triggered but received {0}".format(
  291. callback_tracker[0]))
  292. self.assertEqual(result, None, "Result should be `helloworld` "
  293. "as this is the expected return value from the chain")
  294. result = dispatcher.dispatch(self, "event.hello.anotherworld")
  295. self.assertEqual(
  296. callback_tracker[0], "event1_event2_event3_", "only handlers for"
  297. " event `event.hello.anotherworld` should have been triggered"
  298. " but received {0}".format(callback_tracker[0]))
  299. self.assertEqual(result, "world", "Result should be `world` "
  300. "as this is the expected return value from the chain")
  301. def test_subscribe_wildcard(self):
  302. callback_tracker = ['']
  303. def my_callback1(event_args, *args, **kwargs):
  304. callback_tracker[0] += "event1_"
  305. next_handler = event_args.get('next_handler')
  306. return "hello" + next_handler.invoke(event_args, *args, **kwargs)
  307. def my_callback2(event_args, *args, **kwargs):
  308. callback_tracker[0] += "event2_"
  309. next_handler = event_args.get('next_handler')
  310. return "some" + next_handler.invoke(event_args, *args, **kwargs)
  311. def my_callback3(event_args, *args, **kwargs):
  312. callback_tracker[0] += "event3_"
  313. next_handler = event_args.get('next_handler')
  314. return "other" + next_handler.invoke(event_args, *args, **kwargs)
  315. def my_callback4(*args, **kwargs):
  316. callback_tracker[0] += "event4_"
  317. return "world"
  318. dispatcher = SimpleEventDispatcher()
  319. dispatcher.intercept("event.*", 2000, my_callback1)
  320. # register to a different event with the same priority
  321. dispatcher.intercept("event.hello.*", 2010, my_callback2)
  322. dispatcher.intercept("event.hello.there", 2030, my_callback4)
  323. dispatcher.intercept("event.*.there", 2020, my_callback3)
  324. dispatcher.intercept("event.*.world", 2020, my_callback4)
  325. dispatcher.intercept("someevent.hello.there", 2030, my_callback3)
  326. # emit a series of events
  327. result = dispatcher.dispatch(self, "event.hello.there")
  328. self.assertEqual(
  329. callback_tracker[0], "event1_event2_event3_event4_",
  330. "Event handlers executed in unexpected order {0}".format(
  331. callback_tracker[0]))
  332. self.assertEqual(result, "hellosomeotherworld")
  333. result = dispatcher.dispatch(self, "event.test.hello.world")
  334. self.assertEqual(
  335. callback_tracker[0], "event1_event2_event3_event4_event1_event4_",
  336. "Event handlers executed in unexpected order {0}".format(
  337. callback_tracker[0]))
  338. self.assertEqual(result, "helloworld")
  339. # make sure cache gets invalidated when subscribing after emit
  340. def test_subscribe_after_emit(self):
  341. callback_tracker = ['']
  342. def my_callback1(event_args, *args, **kwargs):
  343. callback_tracker[0] += "event1_"
  344. next_handler = event_args.get('next_handler')
  345. if next_handler:
  346. return "hello" + next_handler.invoke(
  347. event_args, *args, **kwargs)
  348. else:
  349. return "hello"
  350. def my_callback2(*args, **kwargs):
  351. callback_tracker[0] += "event2_"
  352. return "some"
  353. dispatcher = SimpleEventDispatcher()
  354. dispatcher.intercept("event.hello.world", 1000, my_callback1)
  355. dispatcher.dispatch(self, "event.hello.world")
  356. dispatcher.intercept("event.hello.*", 1001, my_callback2)
  357. result = dispatcher.dispatch(self, "event.hello.world")
  358. self.assertEqual(
  359. callback_tracker[0], "event1_event1_event2_",
  360. "Event handlers executed in unexpected order {0}".format(
  361. callback_tracker[0]))
  362. self.assertEqual(result, "hellosome")
  363. def test_unubscribe(self):
  364. EVENT_NAME = "event.hello.world"
  365. callback_tracker = ['']
  366. def my_callback1(event_args, *args, **kwargs):
  367. callback_tracker[0] += "event1_"
  368. next_handler = event_args.get('next_handler')
  369. if next_handler:
  370. return "hello" + next_handler.invoke(
  371. event_args, *args, **kwargs)
  372. else:
  373. return "hello"
  374. def my_callback2(*args, **kwargs):
  375. callback_tracker[0] += "event2_"
  376. return "some"
  377. dispatcher = SimpleEventDispatcher()
  378. hndlr1 = dispatcher.intercept(EVENT_NAME, 1000, my_callback1)
  379. dispatcher.dispatch(self, EVENT_NAME)
  380. hndlr2 = dispatcher.intercept("event.hello.*", 1001, my_callback2)
  381. # Both handlers should be registered
  382. self.assertListEqual(
  383. [my_callback1, my_callback2],
  384. [handler.callback for handler in
  385. dispatcher.get_handlers_for_event(EVENT_NAME)])
  386. hndlr1.unsubscribe()
  387. # Only my_callback2 should be registered after unsubscribe
  388. self.assertListEqual(
  389. [my_callback2],
  390. [handler.callback for handler in
  391. dispatcher.get_handlers_for_event(EVENT_NAME)])
  392. result = dispatcher.dispatch(self, EVENT_NAME)
  393. self.assertEqual(
  394. callback_tracker[0], "event1_event2_",
  395. "Event handlers executed in unexpected order {0}".format(
  396. callback_tracker[0]))
  397. self.assertEqual(result, "some")
  398. hndlr2.unsubscribe()
  399. result = dispatcher.dispatch(self, "event.hello.world")
  400. self.assertEqual(result, None)