PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/cherrypy/cherrypy/test/test_bus.py

https://github.com/theosp/google_appengine
Python | 263 lines | 183 code | 56 blank | 24 comment | 40 complexity | 3a1b37103413614b0124ae81865d04d6 MD5 | raw file
  1. import threading
  2. import time
  3. import unittest
  4. import cherrypy
  5. from cherrypy._cpcompat import get_daemon, set
  6. from cherrypy.process import wspbus
  7. msg = "Listener %d on channel %s: %s."
  8. class PublishSubscribeTests(unittest.TestCase):
  9. def get_listener(self, channel, index):
  10. def listener(arg=None):
  11. self.responses.append(msg % (index, channel, arg))
  12. return listener
  13. def test_builtin_channels(self):
  14. b = wspbus.Bus()
  15. self.responses, expected = [], []
  16. for channel in b.listeners:
  17. for index, priority in enumerate([100, 50, 0, 51]):
  18. b.subscribe(channel, self.get_listener(channel, index), priority)
  19. for channel in b.listeners:
  20. b.publish(channel)
  21. expected.extend([msg % (i, channel, None) for i in (2, 1, 3, 0)])
  22. b.publish(channel, arg=79347)
  23. expected.extend([msg % (i, channel, 79347) for i in (2, 1, 3, 0)])
  24. self.assertEqual(self.responses, expected)
  25. def test_custom_channels(self):
  26. b = wspbus.Bus()
  27. self.responses, expected = [], []
  28. custom_listeners = ('hugh', 'louis', 'dewey')
  29. for channel in custom_listeners:
  30. for index, priority in enumerate([None, 10, 60, 40]):
  31. b.subscribe(channel, self.get_listener(channel, index), priority)
  32. for channel in custom_listeners:
  33. b.publish(channel, 'ah so')
  34. expected.extend([msg % (i, channel, 'ah so') for i in (1, 3, 0, 2)])
  35. b.publish(channel)
  36. expected.extend([msg % (i, channel, None) for i in (1, 3, 0, 2)])
  37. self.assertEqual(self.responses, expected)
  38. def test_listener_errors(self):
  39. b = wspbus.Bus()
  40. self.responses, expected = [], []
  41. channels = [c for c in b.listeners if c != 'log']
  42. for channel in channels:
  43. b.subscribe(channel, self.get_listener(channel, 1))
  44. # This will break since the lambda takes no args.
  45. b.subscribe(channel, lambda: None, priority=20)
  46. for channel in channels:
  47. self.assertRaises(wspbus.ChannelFailures, b.publish, channel, 123)
  48. expected.append(msg % (1, channel, 123))
  49. self.assertEqual(self.responses, expected)
  50. class BusMethodTests(unittest.TestCase):
  51. def log(self, bus):
  52. self._log_entries = []
  53. def logit(msg, level):
  54. self._log_entries.append(msg)
  55. bus.subscribe('log', logit)
  56. def assertLog(self, entries):
  57. self.assertEqual(self._log_entries, entries)
  58. def get_listener(self, channel, index):
  59. def listener(arg=None):
  60. self.responses.append(msg % (index, channel, arg))
  61. return listener
  62. def test_start(self):
  63. b = wspbus.Bus()
  64. self.log(b)
  65. self.responses = []
  66. num = 3
  67. for index in range(num):
  68. b.subscribe('start', self.get_listener('start', index))
  69. b.start()
  70. try:
  71. # The start method MUST call all 'start' listeners.
  72. self.assertEqual(set(self.responses),
  73. set([msg % (i, 'start', None) for i in range(num)]))
  74. # The start method MUST move the state to STARTED
  75. # (or EXITING, if errors occur)
  76. self.assertEqual(b.state, b.states.STARTED)
  77. # The start method MUST log its states.
  78. self.assertLog(['Bus STARTING', 'Bus STARTED'])
  79. finally:
  80. # Exit so the atexit handler doesn't complain.
  81. b.exit()
  82. def test_stop(self):
  83. b = wspbus.Bus()
  84. self.log(b)
  85. self.responses = []
  86. num = 3
  87. for index in range(num):
  88. b.subscribe('stop', self.get_listener('stop', index))
  89. b.stop()
  90. # The stop method MUST call all 'stop' listeners.
  91. self.assertEqual(set(self.responses),
  92. set([msg % (i, 'stop', None) for i in range(num)]))
  93. # The stop method MUST move the state to STOPPED
  94. self.assertEqual(b.state, b.states.STOPPED)
  95. # The stop method MUST log its states.
  96. self.assertLog(['Bus STOPPING', 'Bus STOPPED'])
  97. def test_graceful(self):
  98. b = wspbus.Bus()
  99. self.log(b)
  100. self.responses = []
  101. num = 3
  102. for index in range(num):
  103. b.subscribe('graceful', self.get_listener('graceful', index))
  104. b.graceful()
  105. # The graceful method MUST call all 'graceful' listeners.
  106. self.assertEqual(set(self.responses),
  107. set([msg % (i, 'graceful', None) for i in range(num)]))
  108. # The graceful method MUST log its states.
  109. self.assertLog(['Bus graceful'])
  110. def test_exit(self):
  111. b = wspbus.Bus()
  112. self.log(b)
  113. self.responses = []
  114. num = 3
  115. for index in range(num):
  116. b.subscribe('stop', self.get_listener('stop', index))
  117. b.subscribe('exit', self.get_listener('exit', index))
  118. b.exit()
  119. # The exit method MUST call all 'stop' listeners,
  120. # and then all 'exit' listeners.
  121. self.assertEqual(set(self.responses),
  122. set([msg % (i, 'stop', None) for i in range(num)] +
  123. [msg % (i, 'exit', None) for i in range(num)]))
  124. # The exit method MUST move the state to EXITING
  125. self.assertEqual(b.state, b.states.EXITING)
  126. # The exit method MUST log its states.
  127. self.assertLog(['Bus STOPPING', 'Bus STOPPED', 'Bus EXITING', 'Bus EXITED'])
  128. def test_wait(self):
  129. b = wspbus.Bus()
  130. def f(method):
  131. time.sleep(0.2)
  132. getattr(b, method)()
  133. for method, states in [('start', [b.states.STARTED]),
  134. ('stop', [b.states.STOPPED]),
  135. ('start', [b.states.STARTING, b.states.STARTED]),
  136. ('exit', [b.states.EXITING]),
  137. ]:
  138. threading.Thread(target=f, args=(method,)).start()
  139. b.wait(states)
  140. # The wait method MUST wait for the given state(s).
  141. if b.state not in states:
  142. self.fail("State %r not in %r" % (b.state, states))
  143. def test_block(self):
  144. b = wspbus.Bus()
  145. self.log(b)
  146. def f():
  147. time.sleep(0.2)
  148. b.exit()
  149. def g():
  150. time.sleep(0.4)
  151. threading.Thread(target=f).start()
  152. threading.Thread(target=g).start()
  153. threads = [t for t in threading.enumerate() if not get_daemon(t)]
  154. self.assertEqual(len(threads), 3)
  155. b.block()
  156. # The block method MUST wait for the EXITING state.
  157. self.assertEqual(b.state, b.states.EXITING)
  158. # The block method MUST wait for ALL non-main, non-daemon threads to finish.
  159. threads = [t for t in threading.enumerate() if not get_daemon(t)]
  160. self.assertEqual(len(threads), 1)
  161. # The last message will mention an indeterminable thread name; ignore it
  162. self.assertEqual(self._log_entries[:-1],
  163. ['Bus STOPPING', 'Bus STOPPED',
  164. 'Bus EXITING', 'Bus EXITED',
  165. 'Waiting for child threads to terminate...'])
  166. def test_start_with_callback(self):
  167. b = wspbus.Bus()
  168. self.log(b)
  169. try:
  170. events = []
  171. def f(*args, **kwargs):
  172. events.append(("f", args, kwargs))
  173. def g():
  174. events.append("g")
  175. b.subscribe("start", g)
  176. b.start_with_callback(f, (1, 3, 5), {"foo": "bar"})
  177. # Give wait() time to run f()
  178. time.sleep(0.2)
  179. # The callback method MUST wait for the STARTED state.
  180. self.assertEqual(b.state, b.states.STARTED)
  181. # The callback method MUST run after all start methods.
  182. self.assertEqual(events, ["g", ("f", (1, 3, 5), {"foo": "bar"})])
  183. finally:
  184. b.exit()
  185. def test_log(self):
  186. b = wspbus.Bus()
  187. self.log(b)
  188. self.assertLog([])
  189. # Try a normal message.
  190. expected = []
  191. for msg in ["O mah darlin'"] * 3 + ["Clementiiiiiiiine"]:
  192. b.log(msg)
  193. expected.append(msg)
  194. self.assertLog(expected)
  195. # Try an error message
  196. try:
  197. foo
  198. except NameError:
  199. b.log("You are lost and gone forever", traceback=True)
  200. lastmsg = self._log_entries[-1]
  201. if "Traceback" not in lastmsg or "NameError" not in lastmsg:
  202. self.fail("Last log message %r did not contain "
  203. "the expected traceback." % lastmsg)
  204. else:
  205. self.fail("NameError was not raised as expected.")
  206. if __name__ == "__main__":
  207. unittest.main()