pax_global_header00006660000000000000000000000064135132276050014516gustar00rootroot0000000000000052 comment=9f1bdeec3eb5816e0183f975ca65b5f6f29fbfbb pytest-tornasync-master/000077500000000000000000000000001351322760500157215ustar00rootroot00000000000000pytest-tornasync-master/.gitignore000066400000000000000000000001271351322760500177110ustar00rootroot00000000000000.eggs *.egg-info __pycache__ build dist .tox .pytest_cache .coverage.eager.* .coverage pytest-tornasync-master/.travis.yml000066400000000000000000000005051351322760500200320ustar00rootroot00000000000000dist: xenial language: python python: - 3.5 - 3.6 - 3.7 - pypy3.5 cache: directories: - $PWD/wheelhouse env: global: - PIP_FIND_LINKS=$PWD/wheelhouse # command to install dependencies install: - pip install tox . script: - tox -e $(echo py$TRAVIS_PYTHON_VERSION | tr -d . | sed -e 's/pypypy/pypy/') pytest-tornasync-master/CHANGES.rst000066400000000000000000000013561351322760500175300ustar00rootroot00000000000000Changes ======= 0.6.0 (2018-11-19) ------------------ - minor updates to avoid a pytest warning under pytest 4 - repo switch to using a 'src' dir 0.5.0 (2018-05-28) ------------------ - updated to work with Tornado 5, which is now the minimum required version - require pytest >= 3.0 - the `io_loop` fixture always refers to a `tornado.ioloop.IOLoop instance` now - the `io_loop_asyncio` and `io_loop_tornado` fixtures have been removed, since now that Tornado 5 always uses asyncio under Python 3, there would be no difference between the two fixtures, so `io_loop` is all that is needed - tox tests now test more versions of Tornado (5.0.* and latest 5.*), Pytest (3.0.* and latest 3.*), and Python (3.5, 3.6, 3.7, and pypy3). pytest-tornasync-master/LICENSE000066400000000000000000000020641351322760500167300ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 eukaryote Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pytest-tornasync-master/README.rst000066400000000000000000000060531351322760500174140ustar00rootroot00000000000000================ pytest-tornasync ================ .. image:: https://travis-ci.org/eukaryote/pytest-tornasync.svg?branch=master :target: https://travis-ci.org/eukaryote/pytest-tornasync A simple pytest plugin that provides some helpful fixtures for testing Tornado (version 5.0 or newer) apps and easy handling of plain (undecoratored) native coroutine tests (Python 3.5+). Why another Tornado pytest plugin when the excellent ``pytest-tornado`` already exists? The main reason is that I didn't want to have to decorate every test coroutine with ``@pytest.mark.gen_test``. This plugin doesn't have anything like ``gen_test``. Defining a test with ``async def`` and a name that begins with ``test_`` is all that is required. Installation ------------ Install using pip, which must be run with Python 3.5+: .. code-block:: sh pip install pytest-tornasync Usage ----- Define an ``app`` fixture: .. code-block:: python import pytest @pytest.fixture def app(): import yourapp return yourapp.make_app() # a tornado.web.Application Create tests as native coroutines using Python 3.5+ ``async def``: .. code-block:: python async def test_app(http_server_client): resp = await http_server_client.fetch('/') assert resp.code == 200 # ... Fixtures -------- When the plugin is installed, then ``pytest --fixtures`` will show the fixtures that are available: http_server_port Port used by `http_server`. http_server Start a tornado HTTP server that listens on all available interfaces. You must create an `app` fixture, which returns the `tornado.web.Application` to be tested. Raises: FixtureLookupError: tornado application fixture not found http_server_client Create an asynchronous HTTP client that can fetch from `http_server`. http_client Create an asynchronous HTTP client that can fetch from anywhere. io_loop Create a new `tornado.ioloop.IOLoop` for each test case. Examples -------- .. code-block:: python import time import tornado.web import tornado.gen import pytest class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world!") @pytest.fixture def app(): return tornado.web.Application([(r"/", MainHandler)]) async def test_http_server_client(http_server_client): # http_server_client fetches from the `app` fixture and takes path resp = await http_server_client.fetch('/') assert resp.code == 200 assert resp.body == b"Hello, world!" async def test_http_client(http_client): # http_client fetches from anywhere and takes full URL resp = await http_client.fetch('http://httpbin.org/status/204') assert resp.code == 204 async def example_coroutine(period): await tornado.gen.sleep(period) async def test_example(): # no fixtures needed period = 1.0 start = time.time() await example_coroutine(period) elapsed = time.time() - start assert elapsed >= period pytest-tornasync-master/pytest.ini000066400000000000000000000001751351322760500177550ustar00rootroot00000000000000[pytest] addopts = --color yes -s --tb short norecursedirs = .* __pycache__ .tox looponfailroots = src test testpaths = test pytest-tornasync-master/setup.py000066400000000000000000000042231351322760500174340ustar00rootroot00000000000000import sys import os from setuptools import find_packages, setup from setuptools.command.test import test here_dir = os.path.abspath(os.path.dirname(__file__)) # require python-3.5+, since we only support the native coroutine 'async def' # style for tests that were introduced in python 3.5. if sys.version_info < (3, 5): print("pytest-tornasync requires Python 3.5 or newer") sys.exit(1) def read(*filenames): buf = [] for filename in filenames: filepath = os.path.join(here_dir, filename) try: with open(filepath) as f: buf.append(f.read()) except FileNotFoundError: pass return '\n\n'.join(buf) class PyTest(test): def finalize_options(self): test.finalize_options(self) self.test_args = [] self.test_suite = True def run_tests(self): import pytest status = pytest.main(self.test_args) sys.exit(status) _reqs = ['pytest>=3.0', 'tornado>=5.0'] setup( name='pytest-tornasync', version='0.6.0.post2', license='http://www.opensource.org/licenses/mit-license.php', url='https://github.com/eukaryote/pytest-tornasync', description='py.test plugin for testing Python 3.5+ Tornado code', long_description=read('README.rst', 'CHANGES.rst'), keywords='testing py.test tornado', author='Calvin Smith', author_email='sapientdust+pytest-tornasync@gmail.com', packages=find_packages(where="src"), package_dir={"": "src"}, platforms='any', cmdclass={'test': PyTest}, install_requires=_reqs, tests_require=_reqs, test_suite='test', data_files=[("", ["LICENSE"])], entry_points={ 'pytest11': ['tornado = pytest_tornasync.plugin'], }, classifiers=[ 'Programming Language :: Python :: 3 :: Only', 'License :: OSI Approved :: MIT License', 'Environment :: Console', 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', 'Framework :: Pytest', 'Topic :: Software Development :: Testing', ] + [ ("Programming Language :: Python :: %s" % x) for x in "3 3.5 3.6 3.7".split() ] ) pytest-tornasync-master/src/000077500000000000000000000000001351322760500165105ustar00rootroot00000000000000pytest-tornasync-master/src/pytest_tornasync/000077500000000000000000000000001351322760500221405ustar00rootroot00000000000000pytest-tornasync-master/src/pytest_tornasync/__init__.py000066400000000000000000000002661351322760500242550ustar00rootroot00000000000000""" A pytest plugin for testing Tornado apps using plain (undecorated) coroutine tests. """ __version_info__ = (0, 6, 0, 'post2') __version__ = '.'.join(map(str, __version_info__)) pytest-tornasync-master/src/pytest_tornasync/plugin.py000066400000000000000000000074121351322760500240140ustar00rootroot00000000000000from contextlib import closing, contextmanager import inspect from inspect import iscoroutinefunction import tornado.ioloop import tornado.testing import tornado.simple_httpclient import pytest def get_test_timeout(pyfuncitem): timeout = pyfuncitem.config.option.async_test_timeout marker = pyfuncitem.get_closest_marker("timeout") if marker: timeout = marker.kwargs.get("seconds", timeout) return timeout def pytest_addoption(parser): parser.addoption( "--async-test-timeout", type=float, help=("timeout in seconds before failing the test " "(default is no timeout)"), ) parser.addoption( "--app-fixture", default="app", help=("fixture name returning a tornado application " '(default is "app")'), ) @pytest.mark.tryfirst def pytest_pycollect_makeitem(collector, name, obj): if collector.funcnamefilter(name) and iscoroutinefunction(obj): return list(collector._genfunctions(name, obj)) @pytest.mark.tryfirst def pytest_pyfunc_call(pyfuncitem): funcargs = pyfuncitem.funcargs testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} if not iscoroutinefunction(pyfuncitem.obj): pyfuncitem.obj(**testargs) return True try: loop = funcargs["io_loop"] except KeyError: loop = tornado.ioloop.IOLoop.current() loop.run_sync( lambda: pyfuncitem.obj(**testargs), timeout=get_test_timeout(pyfuncitem) ) return True @pytest.fixture def io_loop(): """ Create new io loop for each test, and tear it down after. """ loop = tornado.ioloop.IOLoop() loop.make_current() yield loop loop.clear_current() loop.close(all_fds=True) @pytest.fixture def http_server_port(): """ Port used by `http_server`. """ return tornado.testing.bind_unused_port() @pytest.yield_fixture def http_server(request, io_loop, http_server_port): """Start a tornado HTTP server that listens on all available interfaces. You must create an `app` fixture, which returns the `tornado.web.Application` to be tested. Raises: FixtureLookupError: tornado application fixture not found """ http_app = request.getfixturevalue(request.config.option.app_fixture) server = tornado.httpserver.HTTPServer(http_app) server.add_socket(http_server_port[0]) yield server server.stop() if hasattr(server, "close_all_connections"): io_loop.run_sync( server.close_all_connections, timeout=request.config.option.async_test_timeout, ) class AsyncHTTPServerClient(tornado.simple_httpclient.SimpleAsyncHTTPClient): def initialize(self, *, http_server=None): super().initialize() self._http_server = http_server def fetch(self, path, **kwargs): """ Fetch `path` from test server, passing `kwargs` to the `fetch` of the underlying `tornado.simple_httpclient.SimpleAsyncHTTPClient`. """ return super().fetch(self.get_url(path), **kwargs) def get_protocol(self): return "http" def get_http_port(self): for sock in self._http_server._sockets.values(): return sock.getsockname()[1] def get_url(self, path): return "%s://127.0.0.1:%s%s" % (self.get_protocol(), self.get_http_port(), path) @pytest.fixture def http_server_client(http_server): """ Create an asynchronous HTTP client that can fetch from `http_server`. """ with closing(AsyncHTTPServerClient(http_server=http_server)) as client: yield client @pytest.fixture def http_client(http_server): """ Create an asynchronous HTTP client that can fetch from anywhere. """ with closing(tornado.httpclient.AsyncHTTPClient()) as client: yield client pytest-tornasync-master/test/000077500000000000000000000000001351322760500167005ustar00rootroot00000000000000pytest-tornasync-master/test/__init__.py000066400000000000000000000002401351322760500210050ustar00rootroot00000000000000import tornado.web MESSAGE = "Hello, world!" PAUSE_TIME = 0.05 class MainHandler(tornado.web.RequestHandler): def get(self): self.write(MESSAGE) pytest-tornasync-master/test/conftest.py000066400000000000000000000002661351322760500211030ustar00rootroot00000000000000import pytest import tornado.web from test import MainHandler pytest_plugins = ["pytester"] @pytest.fixture def app(): return tornado.web.Application([(r"/", MainHandler)]) pytest-tornasync-master/test/test_plugin.py000066400000000000000000000041551351322760500216140ustar00rootroot00000000000000import time import tornado.gen import tornado.ioloop import tornado.web import pytest from test import MESSAGE, PAUSE_TIME class ExpectedError(RuntimeError): """ A dedicated error type for raising from tests to verify a fixture was run. """ @pytest.fixture def mynumber(): return 42 class MainHandler(tornado.web.RequestHandler): def get(self): self.write(MESSAGE) def _pause_coro(period): return tornado.gen.sleep(period) async def pause(): await _pause_coro(PAUSE_TIME) def test_plain_function(): # non-coroutine test function without fixtures assert True def test_plain_function_with_fixture(mynumber): # non-coroutine test function that uses a fixture assert mynumber == 42 async def nontest_coroutine(io_loop): # Non-test coroutine function that shouldn't be run assert False def nontest_function(io_loop): # Non-test function that shouldn't be run assert False async def test_pause(io_loop): start = time.time() await pause() elapsed = time.time() - start assert elapsed >= PAUSE_TIME async def test_http_client_fetch(http_client, http_server, http_server_port): url = "http://localhost:%s/" % http_server_port[1] resp = await http_client.fetch(url) assert resp.code == 200 assert resp.body.decode("utf8") == MESSAGE async def test_http_server_client_fetch(http_server_client): resp = await http_server_client.fetch("/") assert resp.code == 200 assert resp.body.decode("utf8") == MESSAGE @pytest.mark.xfail(raises=ExpectedError) def test_expected_noncoroutine_fail(): raise ExpectedError() @pytest.mark.xfail(raises=ExpectedError) async def test_expected_coroutine_fail_no_ioloop(): """A coroutine test without an io_loop param.""" raise ExpectedError() @pytest.mark.xfail(raises=ExpectedError) async def test_expected_coroutine_fail_io_loop(io_loop): """A coroutine test with an io_loop param.""" raise ExpectedError() @pytest.mark.xfail(strict=True, raises=tornado.ioloop.TimeoutError) @pytest.mark.timeout(seconds=0.1) async def test_timeout(io_loop): await _pause_coro(0.15) pytest-tornasync-master/test/test_plugin2.py000066400000000000000000000003301351322760500216650ustar00rootroot00000000000000import asyncio import pytest @pytest.fixture def io_loop(): yield asyncio.SelectorEventLoop() @pytest.mark.xfail(type=TypeError) async def test_bad_io_loop_fixture(io_loop): assert False # won't be run pytest-tornasync-master/tox.ini000066400000000000000000000010031351322760500172260ustar00rootroot00000000000000[tox] envlist = {pypy35,py35,py36,py37}-pytest{3,4}latest-tornado{50,latest} [testenv] deps = pytest3latest: pytest>=3.0,<4.0 pytest4latest: pytest>=4.0,<5.0 tornado50: tornado==5.0 tornado5latest: tornado>5.0.0,<6.0 pytest-pep8 pytest-cov setenv = COV_CORE_SOURCE={toxinidir}/src COV_CORE_CONFIG={toxinidir}/.coveragerc COV_CORE_DATAFILE={toxinidir}/.coverage.eager commands = pytest --disable-pytest-warnings --cov=src --cov-append --cov-report=term-missing -s {posargs} test