test_event_system.py 21 KB

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