testing.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. """Support classes for automated testing.
  2. * `AsyncTestCase` and `AsyncHTTPTestCase`: Subclasses of unittest.TestCase
  3. with additional support for testing asynchronous (`.IOLoop`-based) code.
  4. * `ExpectLog`: Make test logs less spammy.
  5. * `main()`: A simple test runner (wrapper around unittest.main()) with support
  6. for the tornado.autoreload module to rerun the tests when code changes.
  7. """
  8. from __future__ import absolute_import, division, print_function
  9. try:
  10. from tornado import gen
  11. from tornado.httpclient import AsyncHTTPClient
  12. from tornado.httpserver import HTTPServer
  13. from tornado.simple_httpclient import SimpleAsyncHTTPClient
  14. from tornado.ioloop import IOLoop, TimeoutError
  15. from tornado import netutil
  16. from tornado.process import Subprocess
  17. except ImportError:
  18. # These modules are not importable on app engine. Parts of this module
  19. # won't work, but e.g. main() will.
  20. AsyncHTTPClient = None # type: ignore
  21. gen = None # type: ignore
  22. HTTPServer = None # type: ignore
  23. IOLoop = None # type: ignore
  24. netutil = None # type: ignore
  25. SimpleAsyncHTTPClient = None # type: ignore
  26. Subprocess = None # type: ignore
  27. from tornado.log import app_log
  28. from tornado.stack_context import ExceptionStackContext
  29. from tornado.util import raise_exc_info, basestring_type, PY3
  30. import functools
  31. import inspect
  32. import logging
  33. import os
  34. import re
  35. import signal
  36. import socket
  37. import sys
  38. try:
  39. import asyncio
  40. except ImportError:
  41. asyncio = None
  42. try:
  43. from collections.abc import Generator as GeneratorType # type: ignore
  44. except ImportError:
  45. from types import GeneratorType # type: ignore
  46. if sys.version_info >= (3, 5):
  47. iscoroutine = inspect.iscoroutine # type: ignore
  48. iscoroutinefunction = inspect.iscoroutinefunction # type: ignore
  49. else:
  50. iscoroutine = iscoroutinefunction = lambda f: False
  51. # Tornado's own test suite requires the updated unittest module
  52. # (either py27+ or unittest2) so tornado.test.util enforces
  53. # this requirement, but for other users of tornado.testing we want
  54. # to allow the older version if unitest2 is not available.
  55. if PY3:
  56. # On python 3, mixing unittest2 and unittest (including doctest)
  57. # doesn't seem to work, so always use unittest.
  58. import unittest
  59. else:
  60. # On python 2, prefer unittest2 when available.
  61. try:
  62. import unittest2 as unittest # type: ignore
  63. except ImportError:
  64. import unittest # type: ignore
  65. if asyncio is None:
  66. _NON_OWNED_IOLOOPS = ()
  67. else:
  68. import tornado.platform.asyncio
  69. _NON_OWNED_IOLOOPS = tornado.platform.asyncio.AsyncIOMainLoop
  70. def bind_unused_port(reuse_port=False):
  71. """Binds a server socket to an available port on localhost.
  72. Returns a tuple (socket, port).
  73. .. versionchanged:: 4.4
  74. Always binds to ``127.0.0.1`` without resolving the name
  75. ``localhost``.
  76. """
  77. sock = netutil.bind_sockets(None, '127.0.0.1', family=socket.AF_INET,
  78. reuse_port=reuse_port)[0]
  79. port = sock.getsockname()[1]
  80. return sock, port
  81. def get_async_test_timeout():
  82. """Get the global timeout setting for async tests.
  83. Returns a float, the timeout in seconds.
  84. .. versionadded:: 3.1
  85. """
  86. try:
  87. return float(os.environ.get('ASYNC_TEST_TIMEOUT'))
  88. except (ValueError, TypeError):
  89. return 5
  90. class _TestMethodWrapper(object):
  91. """Wraps a test method to raise an error if it returns a value.
  92. This is mainly used to detect undecorated generators (if a test
  93. method yields it must use a decorator to consume the generator),
  94. but will also detect other kinds of return values (these are not
  95. necessarily errors, but we alert anyway since there is no good
  96. reason to return a value from a test).
  97. """
  98. def __init__(self, orig_method):
  99. self.orig_method = orig_method
  100. def __call__(self, *args, **kwargs):
  101. result = self.orig_method(*args, **kwargs)
  102. if isinstance(result, GeneratorType) or iscoroutine(result):
  103. raise TypeError("Generator and coroutine test methods should be"
  104. " decorated with tornado.testing.gen_test")
  105. elif result is not None:
  106. raise ValueError("Return value from test method ignored: %r" %
  107. result)
  108. def __getattr__(self, name):
  109. """Proxy all unknown attributes to the original method.
  110. This is important for some of the decorators in the `unittest`
  111. module, such as `unittest.skipIf`.
  112. """
  113. return getattr(self.orig_method, name)
  114. class AsyncTestCase(unittest.TestCase):
  115. """`~unittest.TestCase` subclass for testing `.IOLoop`-based
  116. asynchronous code.
  117. The unittest framework is synchronous, so the test must be
  118. complete by the time the test method returns. This means that
  119. asynchronous code cannot be used in quite the same way as usual
  120. and must be adapted to fit. To write your tests with coroutines,
  121. decorate your test methods with `tornado.testing.gen_test` instead
  122. of `tornado.gen.coroutine`.
  123. This class also provides the (deprecated) `stop()` and `wait()`
  124. methods for a more manual style of testing. The test method itself
  125. must call ``self.wait()``, and asynchronous callbacks should call
  126. ``self.stop()`` to signal completion.
  127. By default, a new `.IOLoop` is constructed for each test and is available
  128. as ``self.io_loop``. If the code being tested requires a
  129. global `.IOLoop`, subclasses should override `get_new_ioloop` to return it.
  130. The `.IOLoop`'s ``start`` and ``stop`` methods should not be
  131. called directly. Instead, use `self.stop <stop>` and `self.wait
  132. <wait>`. Arguments passed to ``self.stop`` are returned from
  133. ``self.wait``. It is possible to have multiple ``wait``/``stop``
  134. cycles in the same test.
  135. Example::
  136. # This test uses coroutine style.
  137. class MyTestCase(AsyncTestCase):
  138. @tornado.testing.gen_test
  139. def test_http_fetch(self):
  140. client = AsyncHTTPClient()
  141. response = yield client.fetch("http://www.tornadoweb.org")
  142. # Test contents of response
  143. self.assertIn("FriendFeed", response.body)
  144. # This test uses argument passing between self.stop and self.wait.
  145. class MyTestCase2(AsyncTestCase):
  146. def test_http_fetch(self):
  147. client = AsyncHTTPClient()
  148. client.fetch("http://www.tornadoweb.org/", self.stop)
  149. response = self.wait()
  150. # Test contents of response
  151. self.assertIn("FriendFeed", response.body)
  152. """
  153. def __init__(self, methodName='runTest'):
  154. super(AsyncTestCase, self).__init__(methodName)
  155. self.__stopped = False
  156. self.__running = False
  157. self.__failure = None
  158. self.__stop_args = None
  159. self.__timeout = None
  160. # It's easy to forget the @gen_test decorator, but if you do
  161. # the test will silently be ignored because nothing will consume
  162. # the generator. Replace the test method with a wrapper that will
  163. # make sure it's not an undecorated generator.
  164. setattr(self, methodName, _TestMethodWrapper(getattr(self, methodName)))
  165. def setUp(self):
  166. super(AsyncTestCase, self).setUp()
  167. self.io_loop = self.get_new_ioloop()
  168. self.io_loop.make_current()
  169. def tearDown(self):
  170. # Clean up Subprocess, so it can be used again with a new ioloop.
  171. Subprocess.uninitialize()
  172. self.io_loop.clear_current()
  173. if not isinstance(self.io_loop, _NON_OWNED_IOLOOPS):
  174. # Try to clean up any file descriptors left open in the ioloop.
  175. # This avoids leaks, especially when tests are run repeatedly
  176. # in the same process with autoreload (because curl does not
  177. # set FD_CLOEXEC on its file descriptors)
  178. self.io_loop.close(all_fds=True)
  179. super(AsyncTestCase, self).tearDown()
  180. # In case an exception escaped or the StackContext caught an exception
  181. # when there wasn't a wait() to re-raise it, do so here.
  182. # This is our last chance to raise an exception in a way that the
  183. # unittest machinery understands.
  184. self.__rethrow()
  185. def get_new_ioloop(self):
  186. """Returns the `.IOLoop` to use for this test.
  187. By default, a new `.IOLoop` is created for each test.
  188. Subclasses may override this method to return
  189. `.IOLoop.current()` if it is not appropriate to use a new
  190. `.IOLoop` in each tests (for example, if there are global
  191. singletons using the default `.IOLoop`) or if a per-test event
  192. loop is being provided by another system (such as
  193. ``pytest-asyncio``).
  194. """
  195. return IOLoop()
  196. def _handle_exception(self, typ, value, tb):
  197. if self.__failure is None:
  198. self.__failure = (typ, value, tb)
  199. else:
  200. app_log.error("multiple unhandled exceptions in test",
  201. exc_info=(typ, value, tb))
  202. self.stop()
  203. return True
  204. def __rethrow(self):
  205. if self.__failure is not None:
  206. failure = self.__failure
  207. self.__failure = None
  208. raise_exc_info(failure)
  209. def run(self, result=None):
  210. with ExceptionStackContext(self._handle_exception, delay_warning=True):
  211. super(AsyncTestCase, self).run(result)
  212. # As a last resort, if an exception escaped super.run() and wasn't
  213. # re-raised in tearDown, raise it here. This will cause the
  214. # unittest run to fail messily, but that's better than silently
  215. # ignoring an error.
  216. self.__rethrow()
  217. def stop(self, _arg=None, **kwargs):
  218. """Stops the `.IOLoop`, causing one pending (or future) call to `wait()`
  219. to return.
  220. Keyword arguments or a single positional argument passed to `stop()` are
  221. saved and will be returned by `wait()`.
  222. .. deprecated:: 5.1
  223. `stop` and `wait` are deprecated; use ``@gen_test`` instead.
  224. """
  225. assert _arg is None or not kwargs
  226. self.__stop_args = kwargs or _arg
  227. if self.__running:
  228. self.io_loop.stop()
  229. self.__running = False
  230. self.__stopped = True
  231. def wait(self, condition=None, timeout=None):
  232. """Runs the `.IOLoop` until stop is called or timeout has passed.
  233. In the event of a timeout, an exception will be thrown. The
  234. default timeout is 5 seconds; it may be overridden with a
  235. ``timeout`` keyword argument or globally with the
  236. ``ASYNC_TEST_TIMEOUT`` environment variable.
  237. If ``condition`` is not None, the `.IOLoop` will be restarted
  238. after `stop()` until ``condition()`` returns true.
  239. .. versionchanged:: 3.1
  240. Added the ``ASYNC_TEST_TIMEOUT`` environment variable.
  241. .. deprecated:: 5.1
  242. `stop` and `wait` are deprecated; use ``@gen_test`` instead.
  243. """
  244. if timeout is None:
  245. timeout = get_async_test_timeout()
  246. if not self.__stopped:
  247. if timeout:
  248. def timeout_func():
  249. try:
  250. raise self.failureException(
  251. 'Async operation timed out after %s seconds' %
  252. timeout)
  253. except Exception:
  254. self.__failure = sys.exc_info()
  255. self.stop()
  256. self.__timeout = self.io_loop.add_timeout(self.io_loop.time() + timeout,
  257. timeout_func)
  258. while True:
  259. self.__running = True
  260. self.io_loop.start()
  261. if (self.__failure is not None or
  262. condition is None or condition()):
  263. break
  264. if self.__timeout is not None:
  265. self.io_loop.remove_timeout(self.__timeout)
  266. self.__timeout = None
  267. assert self.__stopped
  268. self.__stopped = False
  269. self.__rethrow()
  270. result = self.__stop_args
  271. self.__stop_args = None
  272. return result
  273. class AsyncHTTPTestCase(AsyncTestCase):
  274. """A test case that starts up an HTTP server.
  275. Subclasses must override `get_app()`, which returns the
  276. `tornado.web.Application` (or other `.HTTPServer` callback) to be tested.
  277. Tests will typically use the provided ``self.http_client`` to fetch
  278. URLs from this server.
  279. Example, assuming the "Hello, world" example from the user guide is in
  280. ``hello.py``::
  281. import hello
  282. class TestHelloApp(AsyncHTTPTestCase):
  283. def get_app(self):
  284. return hello.make_app()
  285. def test_homepage(self):
  286. response = self.fetch('/')
  287. self.assertEqual(response.code, 200)
  288. self.assertEqual(response.body, 'Hello, world')
  289. That call to ``self.fetch()`` is equivalent to ::
  290. self.http_client.fetch(self.get_url('/'), self.stop)
  291. response = self.wait()
  292. which illustrates how AsyncTestCase can turn an asynchronous operation,
  293. like ``http_client.fetch()``, into a synchronous operation. If you need
  294. to do other asynchronous operations in tests, you'll probably need to use
  295. ``stop()`` and ``wait()`` yourself.
  296. """
  297. def setUp(self):
  298. super(AsyncHTTPTestCase, self).setUp()
  299. sock, port = bind_unused_port()
  300. self.__port = port
  301. self.http_client = self.get_http_client()
  302. self._app = self.get_app()
  303. self.http_server = self.get_http_server()
  304. self.http_server.add_sockets([sock])
  305. def get_http_client(self):
  306. return AsyncHTTPClient()
  307. def get_http_server(self):
  308. return HTTPServer(self._app, **self.get_httpserver_options())
  309. def get_app(self):
  310. """Should be overridden by subclasses to return a
  311. `tornado.web.Application` or other `.HTTPServer` callback.
  312. """
  313. raise NotImplementedError()
  314. def fetch(self, path, raise_error=False, **kwargs):
  315. """Convenience method to synchronously fetch a URL.
  316. The given path will be appended to the local server's host and
  317. port. Any additional kwargs will be passed directly to
  318. `.AsyncHTTPClient.fetch` (and so could be used to pass
  319. ``method="POST"``, ``body="..."``, etc).
  320. If the path begins with http:// or https://, it will be treated as a
  321. full URL and will be fetched as-is.
  322. If ``raise_error`` is True, a `tornado.httpclient.HTTPError` will
  323. be raised if the response code is not 200. This is the same behavior
  324. as the ``raise_error`` argument to `.AsyncHTTPClient.fetch`, but
  325. the default is False here (it's True in `.AsyncHTTPClient`) because
  326. tests often need to deal with non-200 response codes.
  327. .. versionchanged:: 5.0
  328. Added support for absolute URLs.
  329. .. versionchanged:: 5.1
  330. Added the ``raise_error`` argument.
  331. .. deprecated:: 5.1
  332. This method currently turns any exception into an
  333. `.HTTPResponse` with status code 599. In Tornado 6.0,
  334. errors other than `tornado.httpclient.HTTPError` will be
  335. passed through, and ``raise_error=False`` will only
  336. suppress errors that would be raised due to non-200
  337. response codes.
  338. """
  339. if path.lower().startswith(('http://', 'https://')):
  340. url = path
  341. else:
  342. url = self.get_url(path)
  343. return self.io_loop.run_sync(
  344. lambda: self.http_client.fetch(url, raise_error=raise_error, **kwargs),
  345. timeout=get_async_test_timeout())
  346. def get_httpserver_options(self):
  347. """May be overridden by subclasses to return additional
  348. keyword arguments for the server.
  349. """
  350. return {}
  351. def get_http_port(self):
  352. """Returns the port used by the server.
  353. A new port is chosen for each test.
  354. """
  355. return self.__port
  356. def get_protocol(self):
  357. return 'http'
  358. def get_url(self, path):
  359. """Returns an absolute url for the given path on the test server."""
  360. return '%s://127.0.0.1:%s%s' % (self.get_protocol(),
  361. self.get_http_port(), path)
  362. def tearDown(self):
  363. self.http_server.stop()
  364. self.io_loop.run_sync(self.http_server.close_all_connections,
  365. timeout=get_async_test_timeout())
  366. self.http_client.close()
  367. super(AsyncHTTPTestCase, self).tearDown()
  368. class AsyncHTTPSTestCase(AsyncHTTPTestCase):
  369. """A test case that starts an HTTPS server.
  370. Interface is generally the same as `AsyncHTTPTestCase`.
  371. """
  372. def get_http_client(self):
  373. return AsyncHTTPClient(force_instance=True,
  374. defaults=dict(validate_cert=False))
  375. def get_httpserver_options(self):
  376. return dict(ssl_options=self.get_ssl_options())
  377. def get_ssl_options(self):
  378. """May be overridden by subclasses to select SSL options.
  379. By default includes a self-signed testing certificate.
  380. """
  381. # Testing keys were generated with:
  382. # openssl req -new -keyout tornado/test/test.key \
  383. # -out tornado/test/test.crt -nodes -days 3650 -x509
  384. module_dir = os.path.dirname(__file__)
  385. return dict(
  386. certfile=os.path.join(module_dir, 'test', 'test.crt'),
  387. keyfile=os.path.join(module_dir, 'test', 'test.key'))
  388. def get_protocol(self):
  389. return 'https'
  390. def gen_test(func=None, timeout=None):
  391. """Testing equivalent of ``@gen.coroutine``, to be applied to test methods.
  392. ``@gen.coroutine`` cannot be used on tests because the `.IOLoop` is not
  393. already running. ``@gen_test`` should be applied to test methods
  394. on subclasses of `AsyncTestCase`.
  395. Example::
  396. class MyTest(AsyncHTTPTestCase):
  397. @gen_test
  398. def test_something(self):
  399. response = yield self.http_client.fetch(self.get_url('/'))
  400. By default, ``@gen_test`` times out after 5 seconds. The timeout may be
  401. overridden globally with the ``ASYNC_TEST_TIMEOUT`` environment variable,
  402. or for each test with the ``timeout`` keyword argument::
  403. class MyTest(AsyncHTTPTestCase):
  404. @gen_test(timeout=10)
  405. def test_something_slow(self):
  406. response = yield self.http_client.fetch(self.get_url('/'))
  407. Note that ``@gen_test`` is incompatible with `AsyncTestCase.stop`,
  408. `AsyncTestCase.wait`, and `AsyncHTTPTestCase.fetch`. Use ``yield
  409. self.http_client.fetch(self.get_url())`` as shown above instead.
  410. .. versionadded:: 3.1
  411. The ``timeout`` argument and ``ASYNC_TEST_TIMEOUT`` environment
  412. variable.
  413. .. versionchanged:: 4.0
  414. The wrapper now passes along ``*args, **kwargs`` so it can be used
  415. on functions with arguments.
  416. """
  417. if timeout is None:
  418. timeout = get_async_test_timeout()
  419. def wrap(f):
  420. # Stack up several decorators to allow us to access the generator
  421. # object itself. In the innermost wrapper, we capture the generator
  422. # and save it in an attribute of self. Next, we run the wrapped
  423. # function through @gen.coroutine. Finally, the coroutine is
  424. # wrapped again to make it synchronous with run_sync.
  425. #
  426. # This is a good case study arguing for either some sort of
  427. # extensibility in the gen decorators or cancellation support.
  428. @functools.wraps(f)
  429. def pre_coroutine(self, *args, **kwargs):
  430. result = f(self, *args, **kwargs)
  431. if isinstance(result, GeneratorType) or iscoroutine(result):
  432. self._test_generator = result
  433. else:
  434. self._test_generator = None
  435. return result
  436. if iscoroutinefunction(f):
  437. coro = pre_coroutine
  438. else:
  439. coro = gen.coroutine(pre_coroutine)
  440. @functools.wraps(coro)
  441. def post_coroutine(self, *args, **kwargs):
  442. try:
  443. return self.io_loop.run_sync(
  444. functools.partial(coro, self, *args, **kwargs),
  445. timeout=timeout)
  446. except TimeoutError as e:
  447. # run_sync raises an error with an unhelpful traceback.
  448. # If the underlying generator is still running, we can throw the
  449. # exception back into it so the stack trace is replaced by the
  450. # point where the test is stopped. The only reason the generator
  451. # would not be running would be if it were cancelled, which means
  452. # a native coroutine, so we can rely on the cr_running attribute.
  453. if getattr(self._test_generator, 'cr_running', True):
  454. self._test_generator.throw(e)
  455. # In case the test contains an overly broad except
  456. # clause, we may get back here.
  457. # Coroutine was stopped or didn't raise a useful stack trace,
  458. # so re-raise the original exception which is better than nothing.
  459. raise
  460. return post_coroutine
  461. if func is not None:
  462. # Used like:
  463. # @gen_test
  464. # def f(self):
  465. # pass
  466. return wrap(func)
  467. else:
  468. # Used like @gen_test(timeout=10)
  469. return wrap
  470. # Without this attribute, nosetests will try to run gen_test as a test
  471. # anywhere it is imported.
  472. gen_test.__test__ = False # type: ignore
  473. class ExpectLog(logging.Filter):
  474. """Context manager to capture and suppress expected log output.
  475. Useful to make tests of error conditions less noisy, while still
  476. leaving unexpected log entries visible. *Not thread safe.*
  477. The attribute ``logged_stack`` is set to true if any exception
  478. stack trace was logged.
  479. Usage::
  480. with ExpectLog('tornado.application', "Uncaught exception"):
  481. error_response = self.fetch("/some_page")
  482. .. versionchanged:: 4.3
  483. Added the ``logged_stack`` attribute.
  484. """
  485. def __init__(self, logger, regex, required=True):
  486. """Constructs an ExpectLog context manager.
  487. :param logger: Logger object (or name of logger) to watch. Pass
  488. an empty string to watch the root logger.
  489. :param regex: Regular expression to match. Any log entries on
  490. the specified logger that match this regex will be suppressed.
  491. :param required: If true, an exception will be raised if the end of
  492. the ``with`` statement is reached without matching any log entries.
  493. """
  494. if isinstance(logger, basestring_type):
  495. logger = logging.getLogger(logger)
  496. self.logger = logger
  497. self.regex = re.compile(regex)
  498. self.required = required
  499. self.matched = False
  500. self.logged_stack = False
  501. def filter(self, record):
  502. if record.exc_info:
  503. self.logged_stack = True
  504. message = record.getMessage()
  505. if self.regex.match(message):
  506. self.matched = True
  507. return False
  508. return True
  509. def __enter__(self):
  510. self.logger.addFilter(self)
  511. return self
  512. def __exit__(self, typ, value, tb):
  513. self.logger.removeFilter(self)
  514. if not typ and self.required and not self.matched:
  515. raise Exception("did not get expected log message")
  516. def main(**kwargs):
  517. """A simple test runner.
  518. This test runner is essentially equivalent to `unittest.main` from
  519. the standard library, but adds support for tornado-style option
  520. parsing and log formatting. It is *not* necessary to use this
  521. `main` function to run tests using `AsyncTestCase`; these tests
  522. are self-contained and can run with any test runner.
  523. The easiest way to run a test is via the command line::
  524. python -m tornado.testing tornado.test.stack_context_test
  525. See the standard library unittest module for ways in which tests can
  526. be specified.
  527. Projects with many tests may wish to define a test script like
  528. ``tornado/test/runtests.py``. This script should define a method
  529. ``all()`` which returns a test suite and then call
  530. `tornado.testing.main()`. Note that even when a test script is
  531. used, the ``all()`` test suite may be overridden by naming a
  532. single test on the command line::
  533. # Runs all tests
  534. python -m tornado.test.runtests
  535. # Runs one test
  536. python -m tornado.test.runtests tornado.test.stack_context_test
  537. Additional keyword arguments passed through to ``unittest.main()``.
  538. For example, use ``tornado.testing.main(verbosity=2)``
  539. to show many test details as they are run.
  540. See http://docs.python.org/library/unittest.html#unittest.main
  541. for full argument list.
  542. .. versionchanged:: 5.0
  543. This function produces no output of its own; only that produced
  544. by the `unittest` module (Previously it would add a PASS or FAIL
  545. log message).
  546. """
  547. from tornado.options import define, options, parse_command_line
  548. define('exception_on_interrupt', type=bool, default=True,
  549. help=("If true (default), ctrl-c raises a KeyboardInterrupt "
  550. "exception. This prints a stack trace but cannot interrupt "
  551. "certain operations. If false, the process is more reliably "
  552. "killed, but does not print a stack trace."))
  553. # support the same options as unittest's command-line interface
  554. define('verbose', type=bool)
  555. define('quiet', type=bool)
  556. define('failfast', type=bool)
  557. define('catch', type=bool)
  558. define('buffer', type=bool)
  559. argv = [sys.argv[0]] + parse_command_line(sys.argv)
  560. if not options.exception_on_interrupt:
  561. signal.signal(signal.SIGINT, signal.SIG_DFL)
  562. if options.verbose is not None:
  563. kwargs['verbosity'] = 2
  564. if options.quiet is not None:
  565. kwargs['verbosity'] = 0
  566. if options.failfast is not None:
  567. kwargs['failfast'] = True
  568. if options.catch is not None:
  569. kwargs['catchbreak'] = True
  570. if options.buffer is not None:
  571. kwargs['buffer'] = True
  572. if __name__ == '__main__' and len(argv) == 1:
  573. print("No tests specified", file=sys.stderr)
  574. sys.exit(1)
  575. # In order to be able to run tests by their fully-qualified name
  576. # on the command line without importing all tests here,
  577. # module must be set to None. Python 3.2's unittest.main ignores
  578. # defaultTest if no module is given (it tries to do its own
  579. # test discovery, which is incompatible with auto2to3), so don't
  580. # set module if we're not asking for a specific test.
  581. if len(argv) > 1:
  582. unittest.main(module=None, argv=argv, **kwargs)
  583. else:
  584. unittest.main(defaultTest="all", argv=argv, **kwargs)
  585. if __name__ == '__main__':
  586. main()