from __future__ import absolute_import, division, print_function, with_statement
-from tornado import gen
+from tornado import gen, ioloop
from tornado.testing import AsyncTestCase, gen_test
from tornado.test.util import unittest
+import functools
+import os
+
class AsyncTestCaseTest(AsyncTestCase):
def test_exception_in_callback(self):
yield gen.Task(self.io_loop.add_callback)
self.finished = True
+ def test_timeout(self):
+ # Set a short timeout and exceed it.
+ @gen_test(timeout=0.1)
+ def test(self):
+ yield gen.Task(self.io_loop.add_timeout, self.io_loop.time() + 1)
+
+ with self.assertRaises(ioloop.TimeoutError):
+ test(self)
+
+ self.finished = True
+
+ def test_no_timeout(self):
+ # A test that does not exceed its timeout should succeed.
+ @gen_test(timeout=1)
+ def test(self):
+ yield gen.Task(self.io_loop.add_timeout, self.io_loop.time() + 0.1)
+
+ test(self)
+ self.finished = True
+
+ def test_timeout_environment_variable(self):
+ time = self.io_loop.time
+ add_timeout = self.io_loop.add_timeout
+ old_timeout = os.environ.get('TIMEOUT')
+ try:
+ os.environ['TIMEOUT'] = '0.1'
+
+ @gen_test(timeout=0.5)
+ def test_long_timeout(self):
+ yield gen.Task(add_timeout, time() + 0.25)
+
+ # Uses provided timeout of 0.5 seconds, doesn't time out.
+ self.io_loop.run_sync(
+ functools.partial(test_long_timeout, self))
+
+ @gen_test(timeout=0.01)
+ def test_short_timeout(self):
+ yield gen.Task(add_timeout, time() + 1)
+
+ # Uses environment TIMEOUT of 0.1, times out.
+ with self.assertRaises(ioloop.TimeoutError):
+ test_short_timeout(self)
+
+ self.finished = True
+ finally:
+ if old_timeout is None:
+ del os.environ['TIMEOUT']
+ else:
+ os.environ['TIMEOUT'] = old_timeout
+
if __name__ == '__main__':
unittest.main()
from tornado.stack_context import ExceptionStackContext
from tornado.util import raise_exc_info, basestring_type
import functools
+import inspect
import logging
import os
import re
return 'https'
-def gen_test(f):
+def gen_test(timeout=None):
"""Testing equivalent of ``@gen.coroutine``, to be applied to test methods.
``@gen.coroutine`` cannot be used on tests because the `.IOLoop` is not
def test_something(self):
response = yield gen.Task(self.fetch('/'))
+ By default, ``@gen_test`` times out after 5 seconds. The timeout may be
+ overridden globally with the TIMEOUT environment variable, or for each
+ test with the ``timeout`` parameter:
+
+ class MyTest(AsyncHTTPTestCase):
+ @gen_test(timeout=10)
+ def test_something_slow(self):
+ response = yield gen.Task(self.fetch('/'))
+
+ If both the environment variable and the parameter are set, ``gen_test``
+ uses the maximum of the two.
"""
- f = gen.coroutine(f)
+ try:
+ env_timeout = float(os.environ.get('TIMEOUT'))
+ except (ValueError, TypeError):
+ env_timeout = None
+
+ def wrap(f):
+ f = gen.coroutine(f)
+
+ @functools.wraps(f)
+ def wrapper(self):
+ return self.io_loop.run_sync(
+ functools.partial(f, self), timeout=timeout)
+ return wrapper
+
+ if inspect.isfunction(timeout):
+ # Used like:
+ # @gen_test
+ # def f(self):
+ # pass
+ # The 'timeout' parameter is actually the test function.
+ f = timeout
+ timeout = env_timeout or 5
+ return wrap(f)
+ else:
+ # Used like @gen_test(timeout=10) or @gen_test(10).
+ if env_timeout is not None:
+ timeout = max(float(timeout), env_timeout)
+ else:
+ timeout = float(timeout)
- @functools.wraps(f)
- def wrapper(self):
- return self.io_loop.run_sync(functools.partial(f, self), timeout=5)
- return wrapper
+ return wrap
# Without this attribute, nosetests will try to run gen_test as a test