testing.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 """Support classes for automated testing.
00003 
00004 * `AsyncTestCase` and `AsyncHTTPTestCase`:  Subclasses of unittest.TestCase
00005   with additional support for testing asynchronous (`.IOLoop` based) code.
00006 
00007 * `ExpectLog` and `LogTrapTestCase`: Make test logs less spammy.
00008 
00009 * `main()`: A simple test runner (wrapper around unittest.main()) with support
00010   for the tornado.autoreload module to rerun the tests when code changes.
00011 """
00012 
00013 from __future__ import absolute_import, division, print_function, with_statement
00014 
00015 try:
00016     from tornado import gen
00017     from tornado.httpclient import AsyncHTTPClient
00018     from tornado.httpserver import HTTPServer
00019     from tornado.simple_httpclient import SimpleAsyncHTTPClient
00020     from tornado.ioloop import IOLoop, TimeoutError
00021     from tornado import netutil
00022 except ImportError:
00023     # These modules are not importable on app engine.  Parts of this module
00024     # won't work, but e.g. LogTrapTestCase and main() will.
00025     AsyncHTTPClient = None
00026     gen = None
00027     HTTPServer = None
00028     IOLoop = None
00029     netutil = None
00030     SimpleAsyncHTTPClient = None
00031 from tornado.log import gen_log
00032 from tornado.stack_context import ExceptionStackContext
00033 from tornado.util import raise_exc_info, basestring_type
00034 import functools
00035 import logging
00036 import os
00037 import re
00038 import signal
00039 import socket
00040 import sys
00041 import types
00042 
00043 try:
00044     from cStringIO import StringIO  # py2
00045 except ImportError:
00046     from io import StringIO  # py3
00047 
00048 # Tornado's own test suite requires the updated unittest module
00049 # (either py27+ or unittest2) so tornado.test.util enforces
00050 # this requirement, but for other users of tornado.testing we want
00051 # to allow the older version if unitest2 is not available.
00052 if sys.version_info >= (3,):
00053     # On python 3, mixing unittest2 and unittest (including doctest)
00054     # doesn't seem to work, so always use unittest.
00055     import unittest
00056 else:
00057     # On python 2, prefer unittest2 when available.
00058     try:
00059         import unittest2 as unittest
00060     except ImportError:
00061         import unittest
00062 
00063 _next_port = 10000
00064 
00065 
00066 def get_unused_port():
00067     """Returns a (hopefully) unused port number.
00068 
00069     This function does not guarantee that the port it returns is available,
00070     only that a series of get_unused_port calls in a single process return
00071     distinct ports.
00072 
00073     .. deprecated::
00074        Use bind_unused_port instead, which is guaranteed to find an unused port.
00075     """
00076     global _next_port
00077     port = _next_port
00078     _next_port = _next_port + 1
00079     return port
00080 
00081 
00082 def bind_unused_port():
00083     """Binds a server socket to an available port on localhost.
00084 
00085     Returns a tuple (socket, port).
00086     """
00087     [sock] = netutil.bind_sockets(None, 'localhost', family=socket.AF_INET)
00088     port = sock.getsockname()[1]
00089     return sock, port
00090 
00091 
00092 def get_async_test_timeout():
00093     """Get the global timeout setting for async tests.
00094 
00095     Returns a float, the timeout in seconds.
00096 
00097     .. versionadded:: 3.1
00098     """
00099     try:
00100         return float(os.environ.get('ASYNC_TEST_TIMEOUT'))
00101     except (ValueError, TypeError):
00102         return 5
00103 
00104 
00105 class _TestMethodWrapper(object):
00106     """Wraps a test method to raise an error if it returns a value.
00107 
00108     This is mainly used to detect undecorated generators (if a test
00109     method yields it must use a decorator to consume the generator),
00110     but will also detect other kinds of return values (these are not
00111     necessarily errors, but we alert anyway since there is no good
00112     reason to return a value from a test.
00113     """
00114     def __init__(self, orig_method):
00115         self.orig_method = orig_method
00116 
00117     def __call__(self, *args, **kwargs):
00118         result = self.orig_method(*args, **kwargs)
00119         if isinstance(result, types.GeneratorType):
00120             raise TypeError("Generator test methods should be decorated with "
00121                             "tornado.testing.gen_test")
00122         elif result is not None:
00123             raise ValueError("Return value from test method ignored: %r" %
00124                              result)
00125 
00126     def __getattr__(self, name):
00127         """Proxy all unknown attributes to the original method.
00128 
00129         This is important for some of the decorators in the `unittest`
00130         module, such as `unittest.skipIf`.
00131         """
00132         return getattr(self.orig_method, name)
00133 
00134 
00135 class AsyncTestCase(unittest.TestCase):
00136     """`~unittest.TestCase` subclass for testing `.IOLoop`-based
00137     asynchronous code.
00138 
00139     The unittest framework is synchronous, so the test must be
00140     complete by the time the test method returns.  This means that
00141     asynchronous code cannot be used in quite the same way as usual.
00142     To write test functions that use the same ``yield``-based patterns
00143     used with the `tornado.gen` module, decorate your test methods
00144     with `tornado.testing.gen_test` instead of
00145     `tornado.gen.coroutine`.  This class also provides the `stop()`
00146     and `wait()` methods for a more manual style of testing.  The test
00147     method itself must call ``self.wait()``, and asynchronous
00148     callbacks should call ``self.stop()`` to signal completion.
00149 
00150     By default, a new `.IOLoop` is constructed for each test and is available
00151     as ``self.io_loop``.  This `.IOLoop` should be used in the construction of
00152     HTTP clients/servers, etc.  If the code being tested requires a
00153     global `.IOLoop`, subclasses should override `get_new_ioloop` to return it.
00154 
00155     The `.IOLoop`'s ``start`` and ``stop`` methods should not be
00156     called directly.  Instead, use `self.stop <stop>` and `self.wait
00157     <wait>`.  Arguments passed to ``self.stop`` are returned from
00158     ``self.wait``.  It is possible to have multiple ``wait``/``stop``
00159     cycles in the same test.
00160 
00161     Example::
00162 
00163         # This test uses coroutine style.
00164         class MyTestCase(AsyncTestCase):
00165             @tornado.testing.gen_test
00166             def test_http_fetch(self):
00167                 client = AsyncHTTPClient(self.io_loop)
00168                 response = yield client.fetch("http://www.tornadoweb.org")
00169                 # Test contents of response
00170                 self.assertIn("FriendFeed", response.body)
00171 
00172         # This test uses argument passing between self.stop and self.wait.
00173         class MyTestCase2(AsyncTestCase):
00174             def test_http_fetch(self):
00175                 client = AsyncHTTPClient(self.io_loop)
00176                 client.fetch("http://www.tornadoweb.org/", self.stop)
00177                 response = self.wait()
00178                 # Test contents of response
00179                 self.assertIn("FriendFeed", response.body)
00180 
00181         # This test uses an explicit callback-based style.
00182         class MyTestCase3(AsyncTestCase):
00183             def test_http_fetch(self):
00184                 client = AsyncHTTPClient(self.io_loop)
00185                 client.fetch("http://www.tornadoweb.org/", self.handle_fetch)
00186                 self.wait()
00187 
00188             def handle_fetch(self, response):
00189                 # Test contents of response (failures and exceptions here
00190                 # will cause self.wait() to throw an exception and end the
00191                 # test).
00192                 # Exceptions thrown here are magically propagated to
00193                 # self.wait() in test_http_fetch() via stack_context.
00194                 self.assertIn("FriendFeed", response.body)
00195                 self.stop()
00196     """
00197     def __init__(self, methodName='runTest', **kwargs):
00198         super(AsyncTestCase, self).__init__(methodName, **kwargs)
00199         self.__stopped = False
00200         self.__running = False
00201         self.__failure = None
00202         self.__stop_args = None
00203         self.__timeout = None
00204 
00205         # It's easy to forget the @gen_test decorator, but if you do
00206         # the test will silently be ignored because nothing will consume
00207         # the generator.  Replace the test method with a wrapper that will
00208         # make sure it's not an undecorated generator.
00209         setattr(self, methodName, _TestMethodWrapper(getattr(self, methodName)))
00210 
00211     def setUp(self):
00212         super(AsyncTestCase, self).setUp()
00213         self.io_loop = self.get_new_ioloop()
00214         self.io_loop.make_current()
00215 
00216     def tearDown(self):
00217         self.io_loop.clear_current()
00218         if (not IOLoop.initialized() or
00219                 self.io_loop is not IOLoop.instance()):
00220             # Try to clean up any file descriptors left open in the ioloop.
00221             # This avoids leaks, especially when tests are run repeatedly
00222             # in the same process with autoreload (because curl does not
00223             # set FD_CLOEXEC on its file descriptors)
00224             self.io_loop.close(all_fds=True)
00225         super(AsyncTestCase, self).tearDown()
00226         # In case an exception escaped or the StackContext caught an exception
00227         # when there wasn't a wait() to re-raise it, do so here.
00228         # This is our last chance to raise an exception in a way that the
00229         # unittest machinery understands.
00230         self.__rethrow()
00231 
00232     def get_new_ioloop(self):
00233         """Creates a new `.IOLoop` for this test.  May be overridden in
00234         subclasses for tests that require a specific `.IOLoop` (usually
00235         the singleton `.IOLoop.instance()`).
00236         """
00237         return IOLoop()
00238 
00239     def _handle_exception(self, typ, value, tb):
00240         self.__failure = (typ, value, tb)
00241         self.stop()
00242         return True
00243 
00244     def __rethrow(self):
00245         if self.__failure is not None:
00246             failure = self.__failure
00247             self.__failure = None
00248             raise_exc_info(failure)
00249 
00250     def run(self, result=None):
00251         with ExceptionStackContext(self._handle_exception):
00252             super(AsyncTestCase, self).run(result)
00253         # As a last resort, if an exception escaped super.run() and wasn't
00254         # re-raised in tearDown, raise it here.  This will cause the
00255         # unittest run to fail messily, but that's better than silently
00256         # ignoring an error.
00257         self.__rethrow()
00258 
00259     def stop(self, _arg=None, **kwargs):
00260         """Stops the `.IOLoop`, causing one pending (or future) call to `wait()`
00261         to return.
00262 
00263         Keyword arguments or a single positional argument passed to `stop()` are
00264         saved and will be returned by `wait()`.
00265         """
00266         assert _arg is None or not kwargs
00267         self.__stop_args = kwargs or _arg
00268         if self.__running:
00269             self.io_loop.stop()
00270             self.__running = False
00271         self.__stopped = True
00272 
00273     def wait(self, condition=None, timeout=None):
00274         """Runs the `.IOLoop` until stop is called or timeout has passed.
00275 
00276         In the event of a timeout, an exception will be thrown. The
00277         default timeout is 5 seconds; it may be overridden with a
00278         ``timeout`` keyword argument or globally with the
00279         ``ASYNC_TEST_TIMEOUT`` environment variable.
00280 
00281         If ``condition`` is not None, the `.IOLoop` will be restarted
00282         after `stop()` until ``condition()`` returns true.
00283 
00284         .. versionchanged:: 3.1
00285            Added the ``ASYNC_TEST_TIMEOUT`` environment variable.
00286         """
00287         if timeout is None:
00288             timeout = get_async_test_timeout()
00289 
00290         if not self.__stopped:
00291             if timeout:
00292                 def timeout_func():
00293                     try:
00294                         raise self.failureException(
00295                             'Async operation timed out after %s seconds' %
00296                             timeout)
00297                     except Exception:
00298                         self.__failure = sys.exc_info()
00299                     self.stop()
00300                 self.__timeout = self.io_loop.add_timeout(self.io_loop.time() + timeout, timeout_func)
00301             while True:
00302                 self.__running = True
00303                 self.io_loop.start()
00304                 if (self.__failure is not None or
00305                         condition is None or condition()):
00306                     break
00307             if self.__timeout is not None:
00308                 self.io_loop.remove_timeout(self.__timeout)
00309                 self.__timeout = None
00310         assert self.__stopped
00311         self.__stopped = False
00312         self.__rethrow()
00313         result = self.__stop_args
00314         self.__stop_args = None
00315         return result
00316 
00317 
00318 class AsyncHTTPTestCase(AsyncTestCase):
00319     """A test case that starts up an HTTP server.
00320 
00321     Subclasses must override `get_app()`, which returns the
00322     `tornado.web.Application` (or other `.HTTPServer` callback) to be tested.
00323     Tests will typically use the provided ``self.http_client`` to fetch
00324     URLs from this server.
00325 
00326     Example::
00327 
00328         class MyHTTPTest(AsyncHTTPTestCase):
00329             def get_app(self):
00330                 return Application([('/', MyHandler)...])
00331 
00332             def test_homepage(self):
00333                 # The following two lines are equivalent to
00334                 #   response = self.fetch('/')
00335                 # but are shown in full here to demonstrate explicit use
00336                 # of self.stop and self.wait.
00337                 self.http_client.fetch(self.get_url('/'), self.stop)
00338                 response = self.wait()
00339                 # test contents of response
00340     """
00341     def setUp(self):
00342         super(AsyncHTTPTestCase, self).setUp()
00343         sock, port = bind_unused_port()
00344         self.__port = port
00345 
00346         self.http_client = self.get_http_client()
00347         self._app = self.get_app()
00348         self.http_server = self.get_http_server()
00349         self.http_server.add_sockets([sock])
00350 
00351     def get_http_client(self):
00352         return AsyncHTTPClient(io_loop=self.io_loop)
00353 
00354     def get_http_server(self):
00355         return HTTPServer(self._app, io_loop=self.io_loop,
00356                           **self.get_httpserver_options())
00357 
00358     def get_app(self):
00359         """Should be overridden by subclasses to return a
00360         `tornado.web.Application` or other `.HTTPServer` callback.
00361         """
00362         raise NotImplementedError()
00363 
00364     def fetch(self, path, **kwargs):
00365         """Convenience method to synchronously fetch a url.
00366 
00367         The given path will be appended to the local server's host and
00368         port.  Any additional kwargs will be passed directly to
00369         `.AsyncHTTPClient.fetch` (and so could be used to pass
00370         ``method="POST"``, ``body="..."``, etc).
00371         """
00372         self.http_client.fetch(self.get_url(path), self.stop, **kwargs)
00373         return self.wait()
00374 
00375     def get_httpserver_options(self):
00376         """May be overridden by subclasses to return additional
00377         keyword arguments for the server.
00378         """
00379         return {}
00380 
00381     def get_http_port(self):
00382         """Returns the port used by the server.
00383 
00384         A new port is chosen for each test.
00385         """
00386         return self.__port
00387 
00388     def get_protocol(self):
00389         return 'http'
00390 
00391     def get_url(self, path):
00392         """Returns an absolute url for the given path on the test server."""
00393         return '%s://localhost:%s%s' % (self.get_protocol(),
00394                                         self.get_http_port(), path)
00395 
00396     def tearDown(self):
00397         self.http_server.stop()
00398         self.io_loop.run_sync(self.http_server.close_all_connections)
00399         if (not IOLoop.initialized() or
00400                 self.http_client.io_loop is not IOLoop.instance()):
00401             self.http_client.close()
00402         super(AsyncHTTPTestCase, self).tearDown()
00403 
00404 
00405 class AsyncHTTPSTestCase(AsyncHTTPTestCase):
00406     """A test case that starts an HTTPS server.
00407 
00408     Interface is generally the same as `AsyncHTTPTestCase`.
00409     """
00410     def get_http_client(self):
00411         # Some versions of libcurl have deadlock bugs with ssl,
00412         # so always run these tests with SimpleAsyncHTTPClient.
00413         return SimpleAsyncHTTPClient(io_loop=self.io_loop, force_instance=True,
00414                                      defaults=dict(validate_cert=False))
00415 
00416     def get_httpserver_options(self):
00417         return dict(ssl_options=self.get_ssl_options())
00418 
00419     def get_ssl_options(self):
00420         """May be overridden by subclasses to select SSL options.
00421 
00422         By default includes a self-signed testing certificate.
00423         """
00424         # Testing keys were generated with:
00425         # openssl req -new -keyout tornado/test/test.key -out tornado/test/test.crt -nodes -days 3650 -x509
00426         module_dir = os.path.dirname(__file__)
00427         return dict(
00428             certfile=os.path.join(module_dir, 'test', 'test.crt'),
00429             keyfile=os.path.join(module_dir, 'test', 'test.key'))
00430 
00431     def get_protocol(self):
00432         return 'https'
00433 
00434 
00435 def gen_test(func=None, timeout=None):
00436     """Testing equivalent of ``@gen.coroutine``, to be applied to test methods.
00437 
00438     ``@gen.coroutine`` cannot be used on tests because the `.IOLoop` is not
00439     already running.  ``@gen_test`` should be applied to test methods
00440     on subclasses of `AsyncTestCase`.
00441 
00442     Example::
00443 
00444         class MyTest(AsyncHTTPTestCase):
00445             @gen_test
00446             def test_something(self):
00447                 response = yield gen.Task(self.fetch('/'))
00448 
00449     By default, ``@gen_test`` times out after 5 seconds. The timeout may be
00450     overridden globally with the ``ASYNC_TEST_TIMEOUT`` environment variable,
00451     or for each test with the ``timeout`` keyword argument::
00452 
00453         class MyTest(AsyncHTTPTestCase):
00454             @gen_test(timeout=10)
00455             def test_something_slow(self):
00456                 response = yield gen.Task(self.fetch('/'))
00457 
00458     .. versionadded:: 3.1
00459        The ``timeout`` argument and ``ASYNC_TEST_TIMEOUT`` environment
00460        variable.
00461 
00462     .. versionchanged:: 4.0
00463        The wrapper now passes along ``*args, **kwargs`` so it can be used
00464        on functions with arguments.
00465     """
00466     if timeout is None:
00467         timeout = get_async_test_timeout()
00468 
00469     def wrap(f):
00470         # Stack up several decorators to allow us to access the generator
00471         # object itself.  In the innermost wrapper, we capture the generator
00472         # and save it in an attribute of self.  Next, we run the wrapped
00473         # function through @gen.coroutine.  Finally, the coroutine is
00474         # wrapped again to make it synchronous with run_sync.
00475         #
00476         # This is a good case study arguing for either some sort of
00477         # extensibility in the gen decorators or cancellation support.
00478         @functools.wraps(f)
00479         def pre_coroutine(self, *args, **kwargs):
00480             result = f(self, *args, **kwargs)
00481             if isinstance(result, types.GeneratorType):
00482                 self._test_generator = result
00483             else:
00484                 self._test_generator = None
00485             return result
00486 
00487         coro = gen.coroutine(pre_coroutine)
00488 
00489         @functools.wraps(coro)
00490         def post_coroutine(self, *args, **kwargs):
00491             try:
00492                 return self.io_loop.run_sync(
00493                     functools.partial(coro, self, *args, **kwargs),
00494                     timeout=timeout)
00495             except TimeoutError as e:
00496                 # run_sync raises an error with an unhelpful traceback.
00497                 # If we throw it back into the generator the stack trace
00498                 # will be replaced by the point where the test is stopped.
00499                 self._test_generator.throw(e)
00500                 # In case the test contains an overly broad except clause,
00501                 # we may get back here.  In this case re-raise the original
00502                 # exception, which is better than nothing.
00503                 raise
00504         return post_coroutine
00505 
00506     if func is not None:
00507         # Used like:
00508         #     @gen_test
00509         #     def f(self):
00510         #         pass
00511         return wrap(func)
00512     else:
00513         # Used like @gen_test(timeout=10)
00514         return wrap
00515 
00516 
00517 # Without this attribute, nosetests will try to run gen_test as a test
00518 # anywhere it is imported.
00519 gen_test.__test__ = False
00520 
00521 
00522 class LogTrapTestCase(unittest.TestCase):
00523     """A test case that captures and discards all logging output
00524     if the test passes.
00525 
00526     Some libraries can produce a lot of logging output even when
00527     the test succeeds, so this class can be useful to minimize the noise.
00528     Simply use it as a base class for your test case.  It is safe to combine
00529     with AsyncTestCase via multiple inheritance
00530     (``class MyTestCase(AsyncHTTPTestCase, LogTrapTestCase):``)
00531 
00532     This class assumes that only one log handler is configured and
00533     that it is a `~logging.StreamHandler`.  This is true for both
00534     `logging.basicConfig` and the "pretty logging" configured by
00535     `tornado.options`.  It is not compatible with other log buffering
00536     mechanisms, such as those provided by some test runners.
00537     """
00538     def run(self, result=None):
00539         logger = logging.getLogger()
00540         if not logger.handlers:
00541             logging.basicConfig()
00542         handler = logger.handlers[0]
00543         if (len(logger.handlers) > 1 or
00544                 not isinstance(handler, logging.StreamHandler)):
00545             # Logging has been configured in a way we don't recognize,
00546             # so just leave it alone.
00547             super(LogTrapTestCase, self).run(result)
00548             return
00549         old_stream = handler.stream
00550         try:
00551             handler.stream = StringIO()
00552             gen_log.info("RUNNING TEST: " + str(self))
00553             old_error_count = len(result.failures) + len(result.errors)
00554             super(LogTrapTestCase, self).run(result)
00555             new_error_count = len(result.failures) + len(result.errors)
00556             if new_error_count != old_error_count:
00557                 old_stream.write(handler.stream.getvalue())
00558         finally:
00559             handler.stream = old_stream
00560 
00561 
00562 class ExpectLog(logging.Filter):
00563     """Context manager to capture and suppress expected log output.
00564 
00565     Useful to make tests of error conditions less noisy, while still
00566     leaving unexpected log entries visible.  *Not thread safe.*
00567 
00568     Usage::
00569 
00570         with ExpectLog('tornado.application', "Uncaught exception"):
00571             error_response = self.fetch("/some_page")
00572     """
00573     def __init__(self, logger, regex, required=True):
00574         """Constructs an ExpectLog context manager.
00575 
00576         :param logger: Logger object (or name of logger) to watch.  Pass
00577             an empty string to watch the root logger.
00578         :param regex: Regular expression to match.  Any log entries on
00579             the specified logger that match this regex will be suppressed.
00580         :param required: If true, an exeption will be raised if the end of
00581             the ``with`` statement is reached without matching any log entries.
00582         """
00583         if isinstance(logger, basestring_type):
00584             logger = logging.getLogger(logger)
00585         self.logger = logger
00586         self.regex = re.compile(regex)
00587         self.required = required
00588         self.matched = False
00589 
00590     def filter(self, record):
00591         message = record.getMessage()
00592         if self.regex.match(message):
00593             self.matched = True
00594             return False
00595         return True
00596 
00597     def __enter__(self):
00598         self.logger.addFilter(self)
00599 
00600     def __exit__(self, typ, value, tb):
00601         self.logger.removeFilter(self)
00602         if not typ and self.required and not self.matched:
00603             raise Exception("did not get expected log message")
00604 
00605 
00606 def main(**kwargs):
00607     """A simple test runner.
00608 
00609     This test runner is essentially equivalent to `unittest.main` from
00610     the standard library, but adds support for tornado-style option
00611     parsing and log formatting.
00612 
00613     The easiest way to run a test is via the command line::
00614 
00615         python -m tornado.testing tornado.test.stack_context_test
00616 
00617     See the standard library unittest module for ways in which tests can
00618     be specified.
00619 
00620     Projects with many tests may wish to define a test script like
00621     ``tornado/test/runtests.py``.  This script should define a method
00622     ``all()`` which returns a test suite and then call
00623     `tornado.testing.main()`.  Note that even when a test script is
00624     used, the ``all()`` test suite may be overridden by naming a
00625     single test on the command line::
00626 
00627         # Runs all tests
00628         python -m tornado.test.runtests
00629         # Runs one test
00630         python -m tornado.test.runtests tornado.test.stack_context_test
00631 
00632     Additional keyword arguments passed through to ``unittest.main()``.
00633     For example, use ``tornado.testing.main(verbosity=2)``
00634     to show many test details as they are run.
00635     See http://docs.python.org/library/unittest.html#unittest.main
00636     for full argument list.
00637     """
00638     from tornado.options import define, options, parse_command_line
00639 
00640     define('exception_on_interrupt', type=bool, default=True,
00641            help=("If true (default), ctrl-c raises a KeyboardInterrupt "
00642                  "exception.  This prints a stack trace but cannot interrupt "
00643                  "certain operations.  If false, the process is more reliably "
00644                  "killed, but does not print a stack trace."))
00645 
00646     # support the same options as unittest's command-line interface
00647     define('verbose', type=bool)
00648     define('quiet', type=bool)
00649     define('failfast', type=bool)
00650     define('catch', type=bool)
00651     define('buffer', type=bool)
00652 
00653     argv = [sys.argv[0]] + parse_command_line(sys.argv)
00654 
00655     if not options.exception_on_interrupt:
00656         signal.signal(signal.SIGINT, signal.SIG_DFL)
00657 
00658     if options.verbose is not None:
00659         kwargs['verbosity'] = 2
00660     if options.quiet is not None:
00661         kwargs['verbosity'] = 0
00662     if options.failfast is not None:
00663         kwargs['failfast'] = True
00664     if options.catch is not None:
00665         kwargs['catchbreak'] = True
00666     if options.buffer is not None:
00667         kwargs['buffer'] = True
00668 
00669     if __name__ == '__main__' and len(argv) == 1:
00670         print("No tests specified", file=sys.stderr)
00671         sys.exit(1)
00672     try:
00673         # In order to be able to run tests by their fully-qualified name
00674         # on the command line without importing all tests here,
00675         # module must be set to None.  Python 3.2's unittest.main ignores
00676         # defaultTest if no module is given (it tries to do its own
00677         # test discovery, which is incompatible with auto2to3), so don't
00678         # set module if we're not asking for a specific test.
00679         if len(argv) > 1:
00680             unittest.main(module=None, argv=argv, **kwargs)
00681         else:
00682             unittest.main(defaultTest="all", argv=argv, **kwargs)
00683     except SystemExit as e:
00684         if e.code == 0:
00685             gen_log.info('PASS')
00686         else:
00687             gen_log.error('FAIL')
00688         raise
00689 
00690 if __name__ == '__main__':
00691     main()


rosbridge_tools
Author(s): Jonathan Mace
autogenerated on Sat Dec 27 2014 11:25:59