Update tornado

This commit is contained in:
Ruud
2012-07-07 09:29:32 +02:00
parent 57547fbd7c
commit 1018a7dd32
33 changed files with 1505 additions and 802 deletions
Regular → Executable
+85 -21
View File
@@ -18,12 +18,13 @@ inheritance. See the docstrings for each class/function below for more
information.
"""
from __future__ import with_statement
from __future__ import absolute_import, division, with_statement
from cStringIO import StringIO
try:
from tornado.httpclient import AsyncHTTPClient
from tornado.httpserver import HTTPServer
from tornado.simple_httpclient import SimpleAsyncHTTPClient
from tornado.ioloop import IOLoop
except ImportError:
# These modules are not importable on app engine. Parts of this module
@@ -31,15 +32,20 @@ except ImportError:
AsyncHTTPClient = None
HTTPServer = None
IOLoop = None
SimpleAsyncHTTPClient = None
from tornado.stack_context import StackContext, NullContext
from tornado.util import raise_exc_info
import contextlib
import logging
import os
import signal
import sys
import time
import unittest
_next_port = 10000
def get_unused_port():
"""Returns a (hopefully) unused port number."""
global _next_port
@@ -47,6 +53,7 @@ def get_unused_port():
_next_port = _next_port + 1
return port
class AsyncTestCase(unittest.TestCase):
"""TestCase subclass for testing IOLoop-based asynchronous code.
@@ -104,6 +111,7 @@ class AsyncTestCase(unittest.TestCase):
self.__running = False
self.__failure = None
self.__stop_args = None
self.__timeout = None
def setUp(self):
super(AsyncTestCase, self).setUp()
@@ -134,9 +142,18 @@ class AsyncTestCase(unittest.TestCase):
self.__failure = sys.exc_info()
self.stop()
def __rethrow(self):
if self.__failure is not None:
failure = self.__failure
self.__failure = None
raise_exc_info(failure)
def run(self, result=None):
with StackContext(self._stack_context):
super(AsyncTestCase, self).run(result)
# In case an exception escaped super.run or the StackContext caught
# an exception when there wasn't a wait() to re-raise it, do so here.
self.__rethrow()
def stop(self, _arg=None, **kwargs):
'''Stops the ioloop, causing one pending (or future) call to wait()
@@ -165,12 +182,14 @@ class AsyncTestCase(unittest.TestCase):
def timeout_func():
try:
raise self.failureException(
'Async operation timed out after %d seconds' %
'Async operation timed out after %s seconds' %
timeout)
except Exception:
self.__failure = sys.exc_info()
self.stop()
self.io_loop.add_timeout(time.time() + timeout, timeout_func)
if self.__timeout is not None:
self.io_loop.remove_timeout(self.__timeout)
self.__timeout = self.io_loop.add_timeout(time.time() + timeout, timeout_func)
while True:
self.__running = True
with NullContext():
@@ -183,13 +202,7 @@ class AsyncTestCase(unittest.TestCase):
break
assert self.__stopped
self.__stopped = False
if self.__failure is not None:
# 2to3 isn't smart enough to convert three-argument raise
# statements correctly in some cases.
if isinstance(self.__failure[1], self.__failure[0]):
raise self.__failure[1], None, self.__failure[2]
else:
raise self.__failure[0], self.__failure[1], self.__failure[2]
self.__rethrow()
result = self.__stop_args
self.__stop_args = None
return result
@@ -222,12 +235,19 @@ class AsyncHTTPTestCase(AsyncTestCase):
super(AsyncHTTPTestCase, self).setUp()
self.__port = None
self.http_client = AsyncHTTPClient(io_loop=self.io_loop)
self.http_client = self.get_http_client()
self._app = self.get_app()
self.http_server = HTTPServer(self._app, io_loop=self.io_loop,
**self.get_httpserver_options())
self.http_server = self.get_http_server()
self.http_server.listen(self.get_http_port(), address="127.0.0.1")
def get_http_client(self):
return AsyncHTTPClient(io_loop=self.io_loop)
def get_http_server(self):
return HTTPServer(self._app, io_loop=self.io_loop,
**self.get_httpserver_options())
def get_app(self):
"""Should be overridden by subclasses to return a
tornado.web.Application or other HTTPServer callback.
@@ -247,12 +267,12 @@ class AsyncHTTPTestCase(AsyncTestCase):
def get_httpserver_options(self):
"""May be overridden by subclasses to return additional
keyword arguments for HTTPServer.
keyword arguments for the server.
"""
return {}
def get_http_port(self):
"""Returns the port used by the HTTPServer.
"""Returns the port used by the server.
A new port is chosen for each test.
"""
@@ -260,15 +280,53 @@ class AsyncHTTPTestCase(AsyncTestCase):
self.__port = get_unused_port()
return self.__port
def get_protocol(self):
return 'http'
def get_url(self, path):
"""Returns an absolute url for the given path on the test server."""
return 'http://localhost:%s%s' % (self.get_http_port(), path)
return '%s://localhost:%s%s' % (self.get_protocol(),
self.get_http_port(), path)
def tearDown(self):
self.http_server.stop()
self.http_client.close()
super(AsyncHTTPTestCase, self).tearDown()
class AsyncHTTPSTestCase(AsyncHTTPTestCase):
"""A test case that starts an HTTPS server.
Interface is generally the same as `AsyncHTTPTestCase`.
"""
def get_http_client(self):
# Some versions of libcurl have deadlock bugs with ssl,
# so always run these tests with SimpleAsyncHTTPClient.
return SimpleAsyncHTTPClient(io_loop=self.io_loop, force_instance=True)
def get_httpserver_options(self):
return dict(ssl_options=self.get_ssl_options())
def get_ssl_options(self):
"""May be overridden by subclasses to select SSL options.
By default includes a self-signed testing certificate.
"""
# Testing keys were generated with:
# openssl req -new -keyout tornado/test/test.key -out tornado/test/test.crt -nodes -days 3650 -x509
module_dir = os.path.dirname(__file__)
return dict(
certfile=os.path.join(module_dir, 'test', 'test.crt'),
keyfile=os.path.join(module_dir, 'test', 'test.key'))
def get_protocol(self):
return 'https'
def fetch(self, path, **kwargs):
return AsyncHTTPTestCase.fetch(self, path, validate_cert=False,
**kwargs)
class LogTrapTestCase(unittest.TestCase):
"""A test case that captures and discards all logging output
if the test passes.
@@ -308,7 +366,8 @@ class LogTrapTestCase(unittest.TestCase):
finally:
handler.stream = old_stream
def main():
def main(**kwargs):
"""A simple test runner.
This test runner is essentially equivalent to `unittest.main` from
@@ -329,10 +388,15 @@ def main():
be overridden by naming a single test on the command line::
# Runs all tests
tornado/test/runtests.py
python -m tornado.test.runtests
# Runs one test
tornado/test/runtests.py tornado.test.stack_context_test
python -m tornado.test.runtests tornado.test.stack_context_test
Additional keyword arguments passed through to ``unittest.main()``.
For example, use ``tornado.testing.main(verbosity=2)``
to show many test details as they are run.
See http://docs.python.org/library/unittest.html#unittest.main
for full argument list.
"""
from tornado.options import define, options, parse_command_line
@@ -364,9 +428,9 @@ def main():
# test discovery, which is incompatible with auto2to3), so don't
# set module if we're not asking for a specific test.
if len(argv) > 1:
unittest.main(module=None, argv=argv)
unittest.main(module=None, argv=argv, **kwargs)
else:
unittest.main(defaultTest="all", argv=argv)
unittest.main(defaultTest="all", argv=argv, **kwargs)
except SystemExit, e:
if e.code == 0:
logging.info('PASS')