testing_test.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. from __future__ import absolute_import, division, print_function
  2. from tornado import gen, ioloop
  3. from tornado.log import app_log
  4. from tornado.simple_httpclient import SimpleAsyncHTTPClient, HTTPTimeoutError
  5. from tornado.test.util import unittest, skipBefore35, exec_test, ignore_deprecation
  6. from tornado.testing import AsyncHTTPTestCase, AsyncTestCase, bind_unused_port, gen_test, ExpectLog
  7. from tornado.web import Application
  8. import contextlib
  9. import os
  10. import platform
  11. import traceback
  12. import warnings
  13. try:
  14. import asyncio
  15. except ImportError:
  16. asyncio = None
  17. @contextlib.contextmanager
  18. def set_environ(name, value):
  19. old_value = os.environ.get(name)
  20. os.environ[name] = value
  21. try:
  22. yield
  23. finally:
  24. if old_value is None:
  25. del os.environ[name]
  26. else:
  27. os.environ[name] = old_value
  28. class AsyncTestCaseTest(AsyncTestCase):
  29. def test_exception_in_callback(self):
  30. with ignore_deprecation():
  31. self.io_loop.add_callback(lambda: 1 / 0)
  32. try:
  33. self.wait()
  34. self.fail("did not get expected exception")
  35. except ZeroDivisionError:
  36. pass
  37. def test_wait_timeout(self):
  38. time = self.io_loop.time
  39. # Accept default 5-second timeout, no error
  40. self.io_loop.add_timeout(time() + 0.01, self.stop)
  41. self.wait()
  42. # Timeout passed to wait()
  43. self.io_loop.add_timeout(time() + 1, self.stop)
  44. with self.assertRaises(self.failureException):
  45. self.wait(timeout=0.01)
  46. # Timeout set with environment variable
  47. self.io_loop.add_timeout(time() + 1, self.stop)
  48. with set_environ('ASYNC_TEST_TIMEOUT', '0.01'):
  49. with self.assertRaises(self.failureException):
  50. self.wait()
  51. def test_subsequent_wait_calls(self):
  52. """
  53. This test makes sure that a second call to wait()
  54. clears the first timeout.
  55. """
  56. self.io_loop.add_timeout(self.io_loop.time() + 0.00, self.stop)
  57. self.wait(timeout=0.02)
  58. self.io_loop.add_timeout(self.io_loop.time() + 0.03, self.stop)
  59. self.wait(timeout=0.15)
  60. def test_multiple_errors(self):
  61. with ignore_deprecation():
  62. def fail(message):
  63. raise Exception(message)
  64. self.io_loop.add_callback(lambda: fail("error one"))
  65. self.io_loop.add_callback(lambda: fail("error two"))
  66. # The first error gets raised; the second gets logged.
  67. with ExpectLog(app_log, "multiple unhandled exceptions"):
  68. with self.assertRaises(Exception) as cm:
  69. self.wait()
  70. self.assertEqual(str(cm.exception), "error one")
  71. class AsyncHTTPTestCaseTest(AsyncHTTPTestCase):
  72. @classmethod
  73. def setUpClass(cls):
  74. super(AsyncHTTPTestCaseTest, cls).setUpClass()
  75. # An unused port is bound so we can make requests upon it without
  76. # impacting a real local web server.
  77. cls.external_sock, cls.external_port = bind_unused_port()
  78. def get_app(self):
  79. return Application()
  80. def test_fetch_segment(self):
  81. path = '/path'
  82. response = self.fetch(path)
  83. self.assertEqual(response.request.url, self.get_url(path))
  84. @gen_test
  85. def test_fetch_full_http_url(self):
  86. path = 'http://localhost:%d/path' % self.external_port
  87. with contextlib.closing(SimpleAsyncHTTPClient(force_instance=True)) as client:
  88. with self.assertRaises(HTTPTimeoutError) as cm:
  89. yield client.fetch(path, request_timeout=0.1, raise_error=True)
  90. self.assertEqual(cm.exception.response.request.url, path)
  91. @gen_test
  92. def test_fetch_full_https_url(self):
  93. path = 'https://localhost:%d/path' % self.external_port
  94. with contextlib.closing(SimpleAsyncHTTPClient(force_instance=True)) as client:
  95. with self.assertRaises(HTTPTimeoutError) as cm:
  96. yield client.fetch(path, request_timeout=0.1, raise_error=True)
  97. self.assertEqual(cm.exception.response.request.url, path)
  98. @classmethod
  99. def tearDownClass(cls):
  100. cls.external_sock.close()
  101. super(AsyncHTTPTestCaseTest, cls).tearDownClass()
  102. class AsyncTestCaseWrapperTest(unittest.TestCase):
  103. def test_undecorated_generator(self):
  104. class Test(AsyncTestCase):
  105. def test_gen(self):
  106. yield
  107. test = Test('test_gen')
  108. result = unittest.TestResult()
  109. test.run(result)
  110. self.assertEqual(len(result.errors), 1)
  111. self.assertIn("should be decorated", result.errors[0][1])
  112. @skipBefore35
  113. @unittest.skipIf(platform.python_implementation() == 'PyPy',
  114. 'pypy destructor warnings cannot be silenced')
  115. def test_undecorated_coroutine(self):
  116. namespace = exec_test(globals(), locals(), """
  117. class Test(AsyncTestCase):
  118. async def test_coro(self):
  119. pass
  120. """)
  121. test_class = namespace['Test']
  122. test = test_class('test_coro')
  123. result = unittest.TestResult()
  124. # Silence "RuntimeWarning: coroutine 'test_coro' was never awaited".
  125. with warnings.catch_warnings():
  126. warnings.simplefilter('ignore')
  127. test.run(result)
  128. self.assertEqual(len(result.errors), 1)
  129. self.assertIn("should be decorated", result.errors[0][1])
  130. def test_undecorated_generator_with_skip(self):
  131. class Test(AsyncTestCase):
  132. @unittest.skip("don't run this")
  133. def test_gen(self):
  134. yield
  135. test = Test('test_gen')
  136. result = unittest.TestResult()
  137. test.run(result)
  138. self.assertEqual(len(result.errors), 0)
  139. self.assertEqual(len(result.skipped), 1)
  140. def test_other_return(self):
  141. class Test(AsyncTestCase):
  142. def test_other_return(self):
  143. return 42
  144. test = Test('test_other_return')
  145. result = unittest.TestResult()
  146. test.run(result)
  147. self.assertEqual(len(result.errors), 1)
  148. self.assertIn("Return value from test method ignored", result.errors[0][1])
  149. class SetUpTearDownTest(unittest.TestCase):
  150. def test_set_up_tear_down(self):
  151. """
  152. This test makes sure that AsyncTestCase calls super methods for
  153. setUp and tearDown.
  154. InheritBoth is a subclass of both AsyncTestCase and
  155. SetUpTearDown, with the ordering so that the super of
  156. AsyncTestCase will be SetUpTearDown.
  157. """
  158. events = []
  159. result = unittest.TestResult()
  160. class SetUpTearDown(unittest.TestCase):
  161. def setUp(self):
  162. events.append('setUp')
  163. def tearDown(self):
  164. events.append('tearDown')
  165. class InheritBoth(AsyncTestCase, SetUpTearDown):
  166. def test(self):
  167. events.append('test')
  168. InheritBoth('test').run(result)
  169. expected = ['setUp', 'test', 'tearDown']
  170. self.assertEqual(expected, events)
  171. class GenTest(AsyncTestCase):
  172. def setUp(self):
  173. super(GenTest, self).setUp()
  174. self.finished = False
  175. def tearDown(self):
  176. self.assertTrue(self.finished)
  177. super(GenTest, self).tearDown()
  178. @gen_test
  179. def test_sync(self):
  180. self.finished = True
  181. @gen_test
  182. def test_async(self):
  183. yield gen.moment
  184. self.finished = True
  185. def test_timeout(self):
  186. # Set a short timeout and exceed it.
  187. @gen_test(timeout=0.1)
  188. def test(self):
  189. yield gen.sleep(1)
  190. # This can't use assertRaises because we need to inspect the
  191. # exc_info triple (and not just the exception object)
  192. try:
  193. test(self)
  194. self.fail("did not get expected exception")
  195. except ioloop.TimeoutError:
  196. # The stack trace should blame the add_timeout line, not just
  197. # unrelated IOLoop/testing internals.
  198. self.assertIn(
  199. "gen.sleep(1)",
  200. traceback.format_exc())
  201. self.finished = True
  202. def test_no_timeout(self):
  203. # A test that does not exceed its timeout should succeed.
  204. @gen_test(timeout=1)
  205. def test(self):
  206. yield gen.sleep(0.1)
  207. test(self)
  208. self.finished = True
  209. def test_timeout_environment_variable(self):
  210. @gen_test(timeout=0.5)
  211. def test_long_timeout(self):
  212. yield gen.sleep(0.25)
  213. # Uses provided timeout of 0.5 seconds, doesn't time out.
  214. with set_environ('ASYNC_TEST_TIMEOUT', '0.1'):
  215. test_long_timeout(self)
  216. self.finished = True
  217. def test_no_timeout_environment_variable(self):
  218. @gen_test(timeout=0.01)
  219. def test_short_timeout(self):
  220. yield gen.sleep(1)
  221. # Uses environment-variable timeout of 0.1, times out.
  222. with set_environ('ASYNC_TEST_TIMEOUT', '0.1'):
  223. with self.assertRaises(ioloop.TimeoutError):
  224. test_short_timeout(self)
  225. self.finished = True
  226. def test_with_method_args(self):
  227. @gen_test
  228. def test_with_args(self, *args):
  229. self.assertEqual(args, ('test',))
  230. yield gen.moment
  231. test_with_args(self, 'test')
  232. self.finished = True
  233. def test_with_method_kwargs(self):
  234. @gen_test
  235. def test_with_kwargs(self, **kwargs):
  236. self.assertDictEqual(kwargs, {'test': 'test'})
  237. yield gen.moment
  238. test_with_kwargs(self, test='test')
  239. self.finished = True
  240. @skipBefore35
  241. def test_native_coroutine(self):
  242. namespace = exec_test(globals(), locals(), """
  243. @gen_test
  244. async def test(self):
  245. self.finished = True
  246. """)
  247. namespace['test'](self)
  248. @skipBefore35
  249. def test_native_coroutine_timeout(self):
  250. # Set a short timeout and exceed it.
  251. namespace = exec_test(globals(), locals(), """
  252. @gen_test(timeout=0.1)
  253. async def test(self):
  254. await gen.sleep(1)
  255. """)
  256. try:
  257. namespace['test'](self)
  258. self.fail("did not get expected exception")
  259. except ioloop.TimeoutError:
  260. self.finished = True
  261. @unittest.skipIf(asyncio is None, "asyncio module not present")
  262. class GetNewIOLoopTest(AsyncTestCase):
  263. def get_new_ioloop(self):
  264. # Use the current loop instead of creating a new one here.
  265. return ioloop.IOLoop.current()
  266. def setUp(self):
  267. # This simulates the effect of an asyncio test harness like
  268. # pytest-asyncio.
  269. self.orig_loop = asyncio.get_event_loop()
  270. self.new_loop = asyncio.new_event_loop()
  271. asyncio.set_event_loop(self.new_loop)
  272. super(GetNewIOLoopTest, self).setUp()
  273. def tearDown(self):
  274. super(GetNewIOLoopTest, self).tearDown()
  275. # AsyncTestCase must not affect the existing asyncio loop.
  276. self.assertFalse(asyncio.get_event_loop().is_closed())
  277. asyncio.set_event_loop(self.orig_loop)
  278. self.new_loop.close()
  279. def test_loop(self):
  280. self.assertIs(self.io_loop.asyncio_loop, self.new_loop)
  281. if __name__ == '__main__':
  282. unittest.main()