pytest migration

This commit is contained in:
shimizukawa 2017-01-03 22:24:00 +09:00
parent 54c8c01222
commit 5b7d237db3
34 changed files with 797 additions and 1740 deletions

View File

@ -12,7 +12,7 @@ python:
- "pypy"
env:
global:
- TEST='-v --with-timer --timer-top-n 25'
- TEST='-v --durations 25'
- PYTHONFAULTHANDLER=x
- PYTHONWARNINGS=all
matrix:

View File

@ -72,14 +72,13 @@ reindent:
@$(PYTHON) utils/reindent.py -r -n .
test:
@cd tests; $(PYTHON) run.py -I py35 -d -m '^[tT]est' $(TEST)
@cd tests; $(PYTHON) run.py --ignore py35 -v $(TEST)
test-async:
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' $(TEST)
@cd tests; $(PYTHON) run.py -v $(TEST)
covertest:
@cd tests; $(PYTHON) run.py -d -m '^[tT]est' --with-coverage \
--cover-package=sphinx $(TEST)
@cd tests; $(PYTHON) run.py -v --cov=sphinx --junitxml=.junit.xml $(TEST)
build:
@$(PYTHON) setup.py build

View File

@ -1,6 +1,6 @@
flake8
nose
nose-timer
pytest>=3.0
pytest-cov
mock
six>=1.4
Jinja2>=2.3

231
tests/conftest.py Normal file
View File

@ -0,0 +1,231 @@
# -*- coding: utf-8 -*-
import sys
import subprocess
import pytest
from six import StringIO, string_types
from util import SphinxTestApp, path
@pytest.fixture
def app_params(request):
"""
parameters that is specified by 'pytest.mark.sphinx' for
sphinx.application.Sphinx initialization
"""
markers = request.node.get_marker("sphinx")
pargs = {}
kwargs = {}
if markers is not None:
# to avoid stacking positional args
for info in reversed(list(markers)):
for i, a in enumerate(info.args):
pargs[i] = a
kwargs.update(info.kwargs)
args = [pargs[i] for i in sorted(pargs.keys())]
return args, kwargs
@pytest.fixture
def test_params(request):
"""
test parameters that is specified by 'pytest.mark.testenv'
:param bool build:
If True, 'app' fixture will be build before test function is called.
Default is False.
:param Union[str, bool, None] specific_srcdir:
If True, testroot directory will be copied into
'<TMPDIR>/<TEST FUNCTION NAME>'.
If string is specified, it copied into '<TMPDIR>/<THE STRING>'.
You can used this feature for providing special crafted source
directory. Also you can used for sharing source directory for
parametrized testing and/or inter test functions. Default is None.
:param Union[str, bool, None] shared_result:
If True, app._status and app._warning objects will be shared in the
parametrized test functions. If string is specified, the objects will
be shred in the test functions that have same 'shared_result' value.
If you don't specify specific_srcdir, this option override
specific_srcdir param by 'shared_result' value. Default is None.
"""
env = request.node.get_marker('testenv')
kwargs = env.kwargs if env else {}
result = {
'build': False, # pre build in fixture
'specific_srcdir': None,
'shared_result': None,
}
result.update(kwargs)
if (result['shared_result'] and
not isinstance(result['shared_result'], string_types)):
r = result['shared_result'] = request.node.originalname or request.node.name
if result['shared_result'] and not result['specific_srcdir']:
result['specific_srcdir'] = result['shared_result']
if (result['specific_srcdir'] and
not isinstance(result['specific_srcdir'], string_types)):
result['specific_srcdir'] = request.node.originalname or request.node.name
return result
@pytest.fixture(scope='function')
def app(test_params, app_params, make_app, shared_result):
"""
provides sphinx.application.Sphinx object
"""
args, kwargs = app_params
if test_params['specific_srcdir'] and 'srcdir' not in kwargs:
kwargs['srcdir'] = test_params['specific_srcdir']
if test_params['shared_result']:
restore = shared_result.restore(test_params['shared_result'])
kwargs.update(restore)
app_ = make_app(*args, **kwargs)
if test_params['build']:
# if listdir is not empty, we can use built cache
if not app_.outdir.listdir():
app_.build()
yield app_
if test_params['shared_result']:
shared_result.store(test_params['shared_result'], app_)
@pytest.fixture(scope='function')
def status(app):
"""
compat for testing with previous @with_app decorator
"""
return app._status
@pytest.fixture(scope='function')
def warning(app):
"""
compat for testing with previous @with_app decorator
"""
return app._warning
@pytest.fixture()
def make_app():
"""
provides make_app function to initialize SphinxTestApp instance.
if you want to initialize 'app' in your test function. please use this
instead of using SphinxTestApp class directory.
"""
apps = []
syspath = sys.path[:]
def make(*args, **kwargs):
status, warning = StringIO(), StringIO()
kwargs.setdefault('status', status)
kwargs.setdefault('warning', warning)
app_ = SphinxTestApp(*args, **kwargs)
apps.append(app_)
return app_
yield make
sys.path[:] = syspath
for app_ in apps:
app_.cleanup()
class SharedResult(object):
cache = {}
def store(self, key, app_):
if key in self.cache:
return
data = {
'status': app_._status.getvalue(),
'warning': app_._warning.getvalue(),
}
self.cache[key] = data
def restore(self, key):
if key not in self.cache:
return {}
data = self.cache[key]
return {
'status': StringIO(data['status']),
'warning': StringIO(data['warning']),
}
@pytest.fixture
def shared_result():
return SharedResult()
@pytest.fixture(scope='module', autouse=True)
def _shared_result_cache():
SharedResult.cache.clear()
@pytest.fixture
def if_graphviz_found(app):
"""
The test will be skipped when using 'if_graphviz_found' fixture and graphviz
dot command is not found.
"""
graphviz_dot = getattr(app.config, 'graphviz_dot', '')
try:
if graphviz_dot:
dot = subprocess.Popen([graphviz_dot, '-V'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE) # show version
dot.communicate()
return
except OSError: # No such file or directory
pass
pytest.skip('graphviz "dot" is not available')
@pytest.fixture
def tempdir(tmpdir):
"""
temporary directory that wrapped with `path` class.
this fixture is for compat with old test implementation.
"""
return path(tmpdir)
def pytest_addoption(parser):
"""
the test that have pytest.mark.env('foobar') will be skipped when
'-S foobar' command-line option is provided.
"""
parser.addoption("-S", action="store", metavar="NAME",
help="skip tests matching the environment NAME.")
def pytest_configure(config):
"""
the test that have pytest.mark.env('foobar') will be skipped when
'-S foobar' command-line option is provided.
"""
# register an additional marker
config.addinivalue_line("markers",
"env(name): mark test to run only on named environment")
def pytest_runtest_setup(item):
"""
the test that have pytest.mark.env('foobar') will be skipped when
'-S foobar' command-line option is provided.
"""
envmarker = item.get_marker("env")
if envmarker is not None:
envname = envmarker.args[0]
if envname == item.config.getoption("-S"):
pytest.skip("skip test %r" % envname)

File diff suppressed because it is too large Load Diff

View File

@ -145,7 +145,7 @@ class path(text_type):
mode = 'rU' if PY2 else 'r'
with open(self, mode=mode, encoding=encoding, **kwargs) as f:
text = f.read()
contents = repr_as(text, '<%s contents>' % self.basename())
contents = repr_as(text, '<%s contents: %r>' % (self.basename(), text))
return contents
def bytes(self):

View File

@ -14,7 +14,7 @@
import six
import sys
from util import TestApp, Struct, raises, SkipTest
from nose.tools import with_setup, eq_
import pytest
from six import StringIO
from docutils.statemachine import ViewList
@ -42,6 +42,7 @@ def teardown_module():
directive = options = None
@pytest.fixture
def setup_test():
global options, directive
global processed_docstrings, processed_signatures, _warnings
@ -106,7 +107,7 @@ def skip_member(app, what, name, obj, skip, options):
return True
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)

View File

@ -4,7 +4,7 @@ import os
import sys
sys.path.insert(0, os.path.abspath('.'))
extensions = ['test_enumerable_node']
extensions = ['enumerable_node']
master_doc = 'index'
numfig = True

View File

@ -11,7 +11,7 @@ class DummyTestParser(Parser):
pass
extensions = ['test_source_parser']
extensions = ['source_parser']
source_suffix = ['.rst', '.test']
source_parsers = {
'.test': DummyTestParser

View File

@ -11,7 +11,7 @@ class DummyMarkdownParser(Parser):
pass
extensions = ['test_source_parser']
extensions = ['source_parser']
source_suffix = ['.rst', '.md']
source_parsers = {
'.md': DummyMarkdownParser

View File

@ -17,14 +17,14 @@ import warnings
import traceback
from path import path
import nose
import pytest
testroot = os.path.dirname(__file__) or '.'
sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir)))
# check dependencies before testing
print('Checking dependencies...')
for modname in ('nose', 'mock', 'six', 'docutils', 'jinja2', 'pygments',
for modname in ('pytest', 'mock', 'six', 'docutils', 'jinja2', 'pygments',
'snowballstemmer', 'babel', 'html5lib'):
try:
__import__(modname)
@ -50,7 +50,15 @@ print('Running Sphinx test suite (with Python %s)...' % sys.version.split()[0])
sys.stdout.flush()
# filter warnings of test dependencies
warnings.filterwarnings('ignore', category=DeprecationWarning, module='nose.util')
warnings.filterwarnings('ignore', category=DeprecationWarning, module='site') # virtualenv
nose.main(argv=sys.argv)
# exclude 'root' and 'roots' dirs for pytest test collector
ignore_paths = [
os.path.relpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), sub))
for sub in ('root', 'roots')
]
args = sys.argv[1:]
for path in ignore_paths:
args.extend(['--ignore', path])
sys.exit(pytest.main(args))

View File

@ -11,45 +11,60 @@
from __future__ import print_function
import sys
from six import PY2
from collections import namedtuple
from sphinx import apidoc
import pytest
from util import with_tempdir, with_app, rootdir
from sphinx.apidoc import main as apidoc_main
from util import rootdir, remove_unicode_literals
@with_tempdir
def test_simple(tempdir):
codedir = rootdir / 'root'
@pytest.fixture()
def apidoc(tempdir, apidoc_params):
_, kwargs = apidoc_params
coderoot = kwargs.get('coderoot', (rootdir / 'root'))
outdir = tempdir / 'out'
args = ['sphinx-apidoc', '-o', outdir, '-F', codedir]
apidoc.main(args)
args = ['sphinx-apidoc', '-o', outdir, '-F', coderoot] + kwargs.get('options', [])
apidoc_main(args)
return namedtuple('apidoc', 'coderoot,outdir')(coderoot, outdir)
@pytest.fixture
def apidoc_params(request):
markers = request.node.get_marker("apidoc")
pargs = {}
kwargs = {}
if markers is not None:
for info in reversed(list(markers)):
for i, a in enumerate(info.args):
pargs[i] = a
kwargs.update(info.kwargs)
args = [pargs[i] for i in sorted(pargs.keys())]
return args, kwargs
@pytest.mark.apidoc(coderoot=(rootdir / 'root'))
def test_simple(make_app, apidoc):
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'autodoc_fodder.rst').isfile()
assert (outdir / 'index.rst').isfile()
@with_app('text', srcdir=outdir)
def assert_build(app, status, warning):
app.build()
print(status.getvalue())
print(warning.getvalue())
sys.path.append(codedir)
try:
assert_build()
finally:
sys.path.remove(codedir)
app = make_app('text', srcdir=outdir)
app.build()
print(app._status.getvalue())
print(app._warning.getvalue())
@with_tempdir
def test_pep_0420_enabled(tempdir):
codedir = rootdir / 'root' / 'pep_0420'
outdir = tempdir / 'out'
args = ['sphinx-apidoc', '-o', outdir, '-F', codedir, "--implicit-namespaces"]
apidoc.main(args)
@pytest.mark.apidoc(
coderoot=(rootdir / 'root' / 'pep_0420'),
options=["--implicit-namespaces"],
)
def test_pep_0420_enabled(make_app, apidoc):
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'a.b.c.rst').isfile()
assert (outdir / 'a.b.x.rst').isfile()
@ -66,49 +81,28 @@ def test_pep_0420_enabled(tempdir):
assert "automodule:: a.b.x.y\n" in rst
assert "automodule:: a.b.x\n" not in rst
@with_app('text', srcdir=outdir)
def assert_build(app, status, warning):
app.build()
print(status.getvalue())
print(warning.getvalue())
sys.path.append(codedir)
try:
assert_build()
finally:
sys.path.remove(codedir)
app = make_app('text', srcdir=outdir)
app.build()
print(app._status.getvalue())
print(app._warning.getvalue())
@with_tempdir
def test_pep_0420_disabled(tempdir):
codedir = rootdir / 'root' / 'pep_0420'
outdir = tempdir / 'out'
args = ['sphinx-apidoc', '-o', outdir, '-F', codedir]
apidoc.main(args)
@pytest.mark.apidoc(coderoot=(rootdir / 'root' / 'pep_0420'))
def test_pep_0420_disabled(make_app, apidoc):
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert not (outdir / 'a.b.c.rst').exists()
assert not (outdir / 'a.b.x.rst').exists()
@with_app('text', srcdir=outdir)
def assert_build(app, status, warning):
app.build()
print(status.getvalue())
print(warning.getvalue())
app = make_app('text', srcdir=outdir)
app.build()
print(app._status.getvalue())
print(app._warning.getvalue())
sys.path.append(codedir)
try:
assert_build()
finally:
sys.path.remove(codedir)
@with_tempdir
def test_pep_0420_disabled_top_level_verify(tempdir):
codedir = rootdir / 'root' / 'pep_0420' / 'a' / 'b'
outdir = tempdir / 'out'
args = ['sphinx-apidoc', '-o', outdir, '-F', codedir]
apidoc.main(args)
@pytest.mark.apidoc(coderoot=(rootdir / 'root' / 'pep_0420' / 'a' / 'b'))
def test_pep_0420_disabled_top_level_verify(make_app, apidoc):
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'c.rst').isfile()
assert not (outdir / 'x.rst').exists()
@ -119,53 +113,35 @@ def test_pep_0420_disabled_top_level_verify(tempdir):
assert "automodule:: c.d\n" in rst
assert "automodule:: c\n" in rst
@with_app('text', srcdir=outdir)
def assert_build(app, status, warning):
app.build()
print(status.getvalue())
print(warning.getvalue())
app = make_app('text', srcdir=outdir)
app.build()
print(app._status.getvalue())
print(app._warning.getvalue())
sys.path.append(codedir)
try:
assert_build()
finally:
sys.path.remove(codedir)
@with_tempdir
def test_multibyte_parameters(tempdir):
codedir = rootdir / 'root'
outdir = tempdir / 'out'
args = ['sphinx-apidoc', '-o', outdir, '-F', codedir,
'--doc-project', u'プロジェクト名'.encode('utf-8'),
'--doc-author', u'著者名'.encode('utf-8'),
'--doc-version', u'バージョン'.encode('utf-8'),
'--doc-release', u'リリース'.encode('utf-8')]
apidoc.main(args)
@pytest.mark.apidoc(
coderoot=(rootdir / 'root'),
options=[
'--doc-project', u'プロジェクト名'.encode('utf-8'),
'--doc-author', u'著者名'.encode('utf-8'),
'--doc-version', u'バージョン'.encode('utf-8'),
'--doc-release', u'リリース'.encode('utf-8'),
],
)
def test_multibyte_parameters(make_app, apidoc):
outdir = apidoc.outdir
assert (outdir / 'conf.py').isfile()
assert (outdir / 'autodoc_fodder.rst').isfile()
assert (outdir / 'index.rst').isfile()
conf_py = (outdir / 'conf.py').text()
if PY2:
assert u"project = u'プロジェクト名'" in conf_py
assert u"author = u'著者名'" in conf_py
assert u"version = u'バージョン'" in conf_py
assert u"release = u'リリース'" in conf_py
else:
assert u"project = 'プロジェクト名'" in conf_py
assert u"author = '著者名'" in conf_py
assert u"version = 'バージョン'" in conf_py
assert u"release = 'リリース'" in conf_py
conf_py_ = remove_unicode_literals(conf_py)
assert u"project = 'プロジェクト名'" in conf_py_
assert u"author = '著者名'" in conf_py_
assert u"version = 'バージョン'" in conf_py_
assert u"release = 'リリース'" in conf_py_
@with_app('text', srcdir=outdir)
def assert_build(app, status, warning):
app.build()
print(status.getvalue())
print(warning.getvalue())
sys.path.append(codedir)
try:
assert_build()
finally:
sys.path.remove(codedir)
app = make_app('text', srcdir=outdir)
app.build()
print(app._status.getvalue())
print(app._warning.getvalue())

View File

@ -12,7 +12,7 @@
# "raises" imported for usage by autodoc
from util import TestApp, Struct, raises, SkipTest # NOQA
from nose.tools import with_setup, eq_
import pytest
import enum
from six import StringIO, add_metaclass
@ -41,6 +41,7 @@ def teardown_module():
directive = options = None
@pytest.fixture
def setup_test():
global options, directive
global processed_docstrings, processed_signatures, _warnings
@ -74,6 +75,10 @@ def setup_test():
processed_signatures = []
_warnings = []
yield
AutoDirective._special_attrgetters.clear()
_warnings = []
processed_docstrings = []
@ -105,7 +110,7 @@ def skip_member(app, what, name, obj, skip, options):
return True
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_parse_name():
def verify(objtype, name, result):
inst = AutoDirective._registry[objtype](directive, name)
@ -147,7 +152,7 @@ def test_parse_name():
del directive.env.temp_data['autodoc:class']
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_format_signature():
def formatsig(objtype, name, obj, args, retann):
inst = AutoDirective._registry[objtype](directive, name)
@ -253,7 +258,7 @@ def test_format_signature():
'(b, c=42, *d, **e)'
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_get_doc():
def getdocl(objtype, obj, encoding=None):
inst = AutoDirective._registry[objtype](directive, 'tmp')
@ -423,7 +428,7 @@ def test_get_doc():
assert getdocl('class', I) == ['Class docstring', '', 'New docstring']
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_docstring_processing():
def process(objtype, name, obj):
inst = AutoDirective._registry[objtype](directive, name)
@ -478,7 +483,7 @@ def test_docstring_processing():
app.disconnect(lid)
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_docstring_property_processing():
def genarate_docstring(objtype, name, **kw):
del processed_docstrings[:]
@ -515,7 +520,7 @@ def test_docstring_property_processing():
assert 'Second line of docstring' in docstrings
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_new_documenter():
class MyDocumenter(ModuleLevelDocumenter):
objtype = 'integer'
@ -543,7 +548,7 @@ def test_new_documenter():
assert_result_contains('.. py:data:: integer', 'module', 'test_autodoc')
@with_setup(setup_test, AutoDirective._special_attrgetters.clear)
@pytest.mark.usefixtures('setup_test')
def test_attrgetter_using():
def assert_getter_works(objtype, name, obj, attrs=[], **kw):
getattr_spy = []
@ -575,7 +580,7 @@ def test_attrgetter_using():
assert_getter_works('class', 'test_autodoc.Class', Class, ['meth', 'inheritedmeth'])
@with_setup(setup_test)
@pytest.mark.usefixtures('setup_test')
def test_generate():
def assert_warns(warn_str, objtype, name, **kw):
inst = AutoDirective._registry[objtype](directive, name)
@ -1084,7 +1089,7 @@ def test_type_hints():
raise SkipTest('Cannot import Python code with function annotations')
def verify_arg_spec(f, expected):
eq_(formatargspec(f, *getargspec(f)), expected)
assert formatargspec(f, *getargspec(f)) == expected
# Class annotations
verify_arg_spec(f0, '(x: int, y: numbers.Integral) -> None')

View File

@ -78,12 +78,12 @@ def test_build_all():
@with_tempdir
def test_master_doc_not_found(tmpdir):
(tmpdir / 'conf.py').write_text('master_doc = "index"')
assert tmpdir.listdir() == ['conf.py']
def test_master_doc_not_found(tempdir):
(tempdir / 'conf.py').write_text('master_doc = "index"')
assert tempdir.listdir() == ['conf.py']
try:
app = TestApp(buildername='dummy', srcdir=tmpdir)
app = TestApp(buildername='dummy', srcdir=tempdir)
app.builder.build_all()
assert False # SphinxError not raised
except Exception as exc:

View File

@ -15,9 +15,9 @@ import re
import gettext
from subprocess import Popen, PIPE
from nose.tools import assert_true, assert_equal
from util import with_app, gen_with_app, SkipTest, assert_in
from util import (
with_app, gen_with_app, SkipTest, assert_in, assert_true, assert_equal
)
@gen_with_app('gettext', srcdir='root-gettext')
@ -76,7 +76,7 @@ def test_all(app, status, warning):
yield assert_equal, _("Testing various markup"), u"Testing various markup"
@with_app('gettext', testroot='intl',
@with_app('gettext', testroot='intl', srcdir='gettext',
confoverrides={'gettext_compact': False})
def test_gettext_index_entries(app, status, warning):
# regression test for #976
@ -123,7 +123,7 @@ def test_gettext_index_entries(app, status, warning):
assert msgids == []
@with_app('gettext', testroot='intl',
@with_app('gettext', testroot='intl', srcdir='gettext',
confoverrides={'gettext_compact': False, 'gettext_additional_targets': []})
def test_gettext_disable_index_entries(app, status, warning):
# regression test for #976
@ -155,7 +155,7 @@ def test_gettext_disable_index_entries(app, status, warning):
assert msgids == []
@with_app(buildername='gettext', testroot='intl')
@with_app(buildername='gettext', testroot='intl', srcdir='gettext')
def test_gettext_template(app, status, warning):
app.builder.build_all()
assert (app.outdir / 'sphinx.pot').isfile()

View File

@ -24,10 +24,7 @@ TREE_BUILDER = getTreeBuilder('etree', implementation=ElementTree)
HTML_PARSER = HTMLParser(TREE_BUILDER, namespaceHTMLElements=False)
ENV_WARNINGS = """\
(%(root)s/autodoc_fodder.py:docstring of autodoc_fodder.MarkupError:\\d+: \
WARNING: duplicate object description of autodoc_fodder.MarkupError, other \
instance in %(root)s/autodoc.rst, use :noindex: for one of them
)?%(root)s/autodoc_fodder.py:docstring of autodoc_fodder.MarkupError:\\d+: \
%(root)s/autodoc_fodder.py:docstring of autodoc_fodder.MarkupError:\\d+: \
WARNING: Explicit markup ends without a blank line; unexpected unindent.
%(root)s/index.rst:\\d+: WARNING: Encoding 'utf-8-sig' used for reading included \
file u'%(root)s/wrongenc.inc' seems to be wrong, try giving an :encoding: option
@ -36,7 +33,7 @@ file u'%(root)s/wrongenc.inc' seems to be wrong, try giving an :encoding: option
%(root)s/index.rst:\\d+: WARNING: download file not readable: %(root)s/nonexisting.png
%(root)s/index.rst:\\d+: WARNING: invalid single index entry u''
%(root)s/undecodable.rst:\\d+: WARNING: undecodable source characters, replacing \
with "\\?": b?'here: >>>(\\\\|/)xbb<<<'
with "\\?": b?'here: >>>(\\\\|/)xbb<<<((\\\\|/)r)?'
"""
HTML_WARNINGS = ENV_WARNINGS + """\
@ -662,7 +659,7 @@ def test_numfig_without_numbered_toctree(app, status, warning):
yield check_xpath, etree, fname, xpath, check, be_found
@gen_with_app(buildername='html', testroot='numfig',
@gen_with_app(buildername='html', testroot='numfig', srcdir='test_build_html_numfig_on',
confoverrides={'numfig': True})
def test_numfig_with_numbered_toctree(app, status, warning):
app.builder.build_all()
@ -763,6 +760,7 @@ def test_numfig_with_numbered_toctree(app, status, warning):
@gen_with_app(buildername='html', testroot='numfig',
srcdir='test_build_html_numfig_format_warn',
confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s',
'table': 'Tab_%s',
@ -867,6 +865,7 @@ def test_numfig_with_prefix(app, status, warning):
@gen_with_app(buildername='html', testroot='numfig',
srcdir='test_build_html_numfig_depth_2',
confoverrides={'numfig': True, 'numfig_secnum_depth': 2})
def test_numfig_with_secnum_depth(app, status, warning):
app.builder.build_all()
@ -967,6 +966,7 @@ def test_numfig_with_secnum_depth(app, status, warning):
@gen_with_app(buildername='singlehtml', testroot='numfig',
srcdir='test_build_html_numfig_on',
confoverrides={'numfig': True})
def test_numfig_with_singlehtml(app, status, warning):
app.builder.build_all()

View File

@ -12,11 +12,11 @@ from __future__ import print_function
import os
import re
from functools import wraps
from itertools import product
from subprocess import Popen, PIPE
from six import PY3
import pytest
from sphinx.errors import SphinxError
from sphinx.util.osutil import cd, ensuredir
@ -90,14 +90,13 @@ def skip_if_stylefiles_notfound(testfunc):
return testfunc
def test_latex():
for engine, docclass in product(LATEX_ENGINES, DOCCLASSES):
yield build_latex_doc, engine, docclass
@skip_if_stylefiles_notfound
@with_app(buildername='latex')
def build_latex_doc(app, status, warning, engine, docclass):
@pytest.mark.parametrize(
"engine,docclass",
product(LATEX_ENGINES, DOCCLASSES),
)
@pytest.mark.sphinx('latex')
def test_build_latex_doc(app, status, warning, engine, docclass):
app.config.latex_engine = engine
app.config.latex_documents[0] = app.config.latex_documents[0][:4] + (docclass,)

View File

@ -10,7 +10,7 @@
"""
import shutil
from nose.tools import with_setup
import pytest
from util import with_app, find_files, rootdir, tempdir
@ -19,6 +19,7 @@ build_dir = root / '_build'
locale_dir = build_dir / 'locale'
@pytest.fixture
def setup_test():
# delete remnants left over after failed build
root.rmtree(True)
@ -30,12 +31,12 @@ def setup_test():
copy_po.parent.makedirs()
shutil.copy(root / po, copy_po)
yield
def teardown_test():
build_dir.rmtree(True)
@with_setup(setup_test, teardown_test)
@pytest.mark.usefixtures('setup_test')
@with_app(buildername='html', testroot='intl',
confoverrides={'language': 'en', 'locale_dirs': [locale_dir]})
def test_compile_all_catalogs(app, status, warning):
@ -51,7 +52,7 @@ def test_compile_all_catalogs(app, status, warning):
assert actual == expect
@with_setup(setup_test, teardown_test)
@pytest.mark.usefixtures('setup_test')
@with_app(buildername='html', testroot='intl',
confoverrides={'language': 'en', 'locale_dirs': [locale_dir]})
def test_compile_specific_catalogs(app, status, warning):
@ -66,7 +67,7 @@ def test_compile_specific_catalogs(app, status, warning):
assert actual == set(['admonitions.mo'])
@with_setup(setup_test, teardown_test)
@pytest.mark.usefixtures('setup_test')
@with_app(buildername='html', testroot='intl',
confoverrides={'language': 'en', 'locale_dirs': [locale_dir]})
def test_compile_update_catalogs(app, status, warning):

View File

@ -87,16 +87,16 @@ def test_extension_values(app, status, warning):
@with_tempdir
def test_errors_warnings(dir):
def test_errors_warnings(tempdir):
# test the error for syntax errors in the config file
(dir / 'conf.py').write_text(u'project = \n', encoding='ascii')
raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None)
(tempdir / 'conf.py').write_text(u'project = \n', encoding='ascii')
raises_msg(ConfigError, 'conf.py', Config, tempdir, 'conf.py', {}, None)
# test the automatic conversion of 2.x only code in configs
(dir / 'conf.py').write_text(
(tempdir / 'conf.py').write_text(
u'# -*- coding: utf-8\n\nproject = u"Jägermeister"\n',
encoding='utf-8')
cfg = Config(dir, 'conf.py', {}, None)
cfg = Config(tempdir, 'conf.py', {}, None)
cfg.init_values(lambda warning: 1/0)
assert cfg.project == u'Jägermeister'
@ -105,9 +105,9 @@ def test_errors_warnings(dir):
# skip the test there
if PY3:
return
(dir / 'conf.py').write_text(
(tempdir / 'conf.py').write_text(
u'# -*- coding: latin-1\nproject = "fooä"\n', encoding='latin-1')
cfg = Config(dir, 'conf.py', {}, None)
cfg = Config(tempdir, 'conf.py', {}, None)
warned = [False]
def warn(msg):
@ -118,10 +118,10 @@ def test_errors_warnings(dir):
@with_tempdir
def test_errors_if_setup_is_not_callable(dir):
def test_errors_if_setup_is_not_callable(tempdir):
# test the error to call setup() in the config file
(dir / 'conf.py').write_text(u'setup = 1')
raises_msg(ConfigError, 'callable', TestApp, srcdir=dir)
(tempdir / 'conf.py').write_text(u'setup = 1')
raises_msg(ConfigError, 'callable', TestApp, srcdir=tempdir)
@mock.patch.object(sphinx, '__display_version__', '1.3.4')
@ -152,12 +152,12 @@ def test_needs_sphinx():
@with_tempdir
def test_config_eol(tmpdir):
def test_config_eol(tempdir):
# test config file's eol patterns: LF, CRLF
configfile = tmpdir / 'conf.py'
configfile = tempdir / 'conf.py'
for eol in (b'\n', b'\r\n'):
configfile.write_bytes(b'project = "spam"' + eol)
cfg = Config(tmpdir, 'conf.py', {}, None)
cfg = Config(tempdir, 'conf.py', {}, None)
cfg.init_values(lambda warning: 1/0)
assert cfg.project == u'spam'

View File

@ -54,7 +54,6 @@ def test_images():
'http://www.python.org/logo.png')
tree = env.get_doctree('images')
app._warning.reset()
htmlbuilder = StandaloneHTMLBuilder(app)
htmlbuilder.imgpath = 'dummy'
htmlbuilder.post_process_images(tree)
@ -64,7 +63,6 @@ def test_images():
assert set(htmlbuilder.images.values()) == \
set(['img.png', 'img1.png', 'simg.png', 'svgimg.svg', 'img.foo.png'])
app._warning.reset()
latexbuilder = LaTeXBuilder(app)
latexbuilder.post_process_images(tree)
assert set(latexbuilder.images.keys()) == \

View File

@ -10,37 +10,14 @@
"""
import re
import subprocess
from functools import wraps
import pytest
from util import with_app, SkipTest
def skip_if_graphviz_not_found(fn):
@wraps(fn)
def decorator(app, *args, **kwargs):
found = False
graphviz_dot = getattr(app.config, 'graphviz_dot', '')
try:
if graphviz_dot:
dot = subprocess.Popen([graphviz_dot, '-V'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE) # show version
dot.communicate()
found = True
except OSError: # No such file or directory
pass
if not found:
raise SkipTest('graphviz "dot" is not available')
return fn(app, *args, **kwargs)
return decorator
@with_app('html', testroot='ext-graphviz')
@skip_if_graphviz_not_found
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_html(app, status, warning):
app.builder.build_all()
@ -61,7 +38,7 @@ def test_graphviz_html(app, status, warning):
@with_app('latex', testroot='ext-graphviz')
@skip_if_graphviz_not_found
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_latex(app, status, warning):
app.builder.build_all()
@ -81,7 +58,7 @@ def test_graphviz_latex(app, status, warning):
@with_app('html', testroot='ext-graphviz', confoverrides={'language': 'xx'})
@skip_if_graphviz_not_found
@pytest.mark.usefixtures('if_graphviz_found')
def test_graphviz_i18n(app, status, warning):
app.builder.build_all()

View File

@ -12,12 +12,12 @@
import re
import sys
from util import with_app, rootdir, raises
from test_ext_graphviz import skip_if_graphviz_not_found
from sphinx.ext.inheritance_diagram import InheritanceException, import_classes
import pytest
@with_app('html', testroot='ext-inheritance_diagram')
@skip_if_graphviz_not_found
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_html(app, status, warning):
app.builder.build_all()
@ -32,7 +32,7 @@ def test_inheritance_diagram_html(app, status, warning):
@with_app('latex', testroot='ext-inheritance_diagram')
@skip_if_graphviz_not_found
@pytest.mark.usefixtures('if_graphviz_found')
def test_inheritance_diagram_latex(app, status, warning):
app.builder.build_all()

View File

@ -85,7 +85,7 @@ def test_read_inventory_v2():
@with_app()
@mock.patch('sphinx.ext.intersphinx.read_inventory')
@mock.patch('sphinx.ext.intersphinx._read_from_url')
def test_fetch_inventory_redirection(app, status, warning, _read_from_url, read_inventory):
def test_fetch_inventory_redirection(_read_from_url, read_inventory, app, status, warning):
intersphinx_setup(app)
_read_from_url().readline.return_value = '# Sphinx inventory version 2'.encode('utf-8')

View File

@ -18,12 +18,11 @@ from docutils import nodes
from subprocess import Popen, PIPE
from babel.messages import pofile
from nose.tools import assert_equal
from six import string_types
from util import tempdir, rootdir, path, gen_with_app, with_app, SkipTest, \
assert_re_search, assert_not_re_search, assert_in, assert_not_in, \
assert_startswith, assert_node, repr_as, etree_parse
assert_startswith, assert_node, repr_as, etree_parse, assert_equal
root = tempdir / 'test-intl'

View File

@ -14,8 +14,6 @@
from util import with_app
from nose.tools import assert_equal
@with_app('pseudoxml')
def test_docinfo(app, status, warning):
@ -53,8 +51,4 @@ def test_docinfo(app, status, warning):
'orphan': u'',
'nocomments': u'',
}
# I like this way of comparing dicts - easier to see the error.
for key in exampledocinfo:
yield assert_equal, exampledocinfo.get(key), expecteddocinfo.get(key)
# but then we still have to check for missing keys
yield assert_equal, set(expecteddocinfo.keys()), set(exampledocinfo.keys())
assert exampledocinfo == expecteddocinfo

View File

@ -12,12 +12,13 @@
import os
import sys
import subprocess
from functools import wraps
import tempfile
from collections import namedtuple
import sphinx
from util import rootdir, tempdir, SkipTest
from path import path
import pytest
from sphinx.util.osutil import cd
from util import rootdir, tempdir
from textwrap import dedent
root = tempdir / 'test-setup'
@ -28,57 +29,52 @@ def setup_module():
(rootdir / 'roots' / 'test-setup').copytree(root)
def with_setup_command(root, *args, **kwds):
@pytest.fixture
def setup_command(request, tempdir):
"""
Run `setup.py build_sphinx` with args and kwargs,
pass it to the test and clean up properly.
"""
def generator(func):
@wraps(func)
def deco(*args2, **kwargs2):
tempdir = path(tempfile.mkdtemp())
pkgrootdir = (tempdir / 'root')
root.copytree(pkgrootdir)
cwd = os.getcwd()
os.chdir(pkgrootdir)
pythonpath = os.path.dirname(os.path.dirname(sphinx.__file__))
if os.getenv('PYTHONPATH'):
pythonpath = os.getenv('PYTHONPATH') + os.pathsep + pythonpath
command = [sys.executable, 'setup.py', 'build_sphinx']
command.extend(args)
try:
proc = subprocess.Popen(
command,
env=dict(os.environ, PYTHONPATH=pythonpath),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
func(pkgrootdir, proc)
finally:
tempdir.rmtree(ignore_errors=True)
os.chdir(cwd)
return deco
return generator
marker = request.node.get_marker('setup_command')
args = marker.args if marker else []
pkgrootdir = tempdir / 'root'
root.copytree(pkgrootdir)
with cd(pkgrootdir):
pythonpath = os.path.dirname(os.path.dirname(sphinx.__file__))
if os.getenv('PYTHONPATH'):
pythonpath = os.getenv('PYTHONPATH') + os.pathsep + pythonpath
command = [sys.executable, 'setup.py', 'build_sphinx']
command.extend(args)
proc = subprocess.Popen(
command,
env=dict(os.environ, PYTHONPATH=pythonpath),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
yield namedtuple('setup', 'pkgroot,proc')(pkgrootdir, proc)
@with_setup_command(root)
def test_build_sphinx(pkgroot, proc):
def test_build_sphinx(setup_command):
proc = setup_command.proc
out, err = proc.communicate()
print(out)
print(err)
assert proc.returncode == 0
@with_setup_command(root)
def test_build_sphinx_with_nonascii_path(pkgroot, proc):
@pytest.fixture
def nonascii_srcdir(request, setup_command):
mb_name = u'\u65e5\u672c\u8a9e'
srcdir = (pkgroot / 'doc')
srcdir = (setup_command.pkgroot / 'doc')
try:
(srcdir / mb_name).makedirs()
except UnicodeEncodeError:
from path import FILESYSTEMENCODING
raise SkipTest(
pytest.skip(
'non-ASCII filename not supported on this filesystem encoding: '
'%s', FILESYSTEMENCODING)
'%s' % FILESYSTEMENCODING)
(srcdir / mb_name / (mb_name + '.txt')).write_text(dedent("""
multi byte file name page
@ -91,41 +87,46 @@ def test_build_sphinx_with_nonascii_path(pkgroot, proc):
%(mb_name)s/%(mb_name)s
""" % locals())
).encode('utf-8'))
).encode('utf-8'))
def test_build_sphinx_with_nonascii_path(setup_command, nonascii_srcdir):
proc = setup_command.proc
out, err = proc.communicate()
print(out)
print(err)
assert proc.returncode == 0
@with_setup_command(root, '-b', 'linkcheck')
def test_build_sphinx_return_nonzero_status(pkgroot, proc):
srcdir = (pkgroot / 'doc')
@pytest.mark.setup_command('-b', 'linkcheck')
def test_build_sphinx_return_nonzero_status(setup_command):
srcdir = (setup_command.pkgroot / 'doc')
(srcdir / 'contents.txt').write_text(
'http://localhost.unexistentdomain/index.html')
proc = setup_command.proc
out, err = proc.communicate()
print(out)
print(err)
assert proc.returncode != 0, 'expect non-zero status for setup.py'
@with_setup_command(root)
def test_build_sphinx_warning_return_zero_status(pkgroot, proc):
srcdir = (pkgroot / 'doc')
def test_build_sphinx_warning_return_zero_status(setup_command):
srcdir = (setup_command.pkgroot / 'doc')
(srcdir / 'contents.txt').write_text(
'See :ref:`unexisting-reference-label`')
proc = setup_command.proc
out, err = proc.communicate()
print(out)
print(err)
assert proc.returncode == 0
@with_setup_command(root, '--warning-is-error')
def test_build_sphinx_warning_is_error_return_nonzero_status(pkgroot, proc):
srcdir = (pkgroot / 'doc')
@pytest.mark.setup_command('--warning-is-error')
def test_build_sphinx_warning_is_error_return_nonzero_status(setup_command):
srcdir = (setup_command.pkgroot / 'doc')
(srcdir / 'contents.txt').write_text(
'See :ref:`unexisting-reference-label`')
proc = setup_command.proc
out, err = proc.communicate()
print(out)
print(err)

View File

@ -83,7 +83,8 @@ def test_js_source(app, status, warning):
assert 'Underscore.js {v}'.format(v=v) in underscore_src, msg
def test_double_inheriting_theme():
@with_app(testroot='double-inheriting-theme')
def test_double_inheriting_theme(make_app, app_params):
from sphinx.theming import load_theme_plugins # load original before patching
def load_themes():
@ -92,8 +93,6 @@ def test_double_inheriting_theme():
for t in load_theme_plugins():
yield t
@mock.patch('sphinx.theming.load_theme_plugins', side_effect=load_themes)
@with_app(testroot='double-inheriting-theme')
def test_double_inheriting_theme_(app, status, warning, m_):
pass
yield test_double_inheriting_theme_
with mock.patch('sphinx.theming.load_theme_plugins', side_effect=load_themes):
args, kwargs = app_params
make_app(*args, **kwargs)

View File

@ -25,32 +25,32 @@ class DummyTemplateLoader(BuiltinTemplateLoader):
@with_tempdir
def test_copy_asset_file(tmpdir):
def test_copy_asset_file(tempdir):
renderer = DummyTemplateLoader()
# copy normal file
src = (tmpdir / 'asset.txt')
src = (tempdir / 'asset.txt')
src.write_text('# test data')
dest = (tmpdir / 'output.txt')
dest = (tempdir / 'output.txt')
copy_asset_file(src, dest)
assert dest.exists()
assert src.text() == dest.text()
# copy template file
src = (tmpdir / 'asset.txt_t')
src = (tempdir / 'asset.txt_t')
src.write_text('# {{var1}} data')
dest = (tmpdir / 'output.txt_t')
dest = (tempdir / 'output.txt_t')
copy_asset_file(src, dest, {'var1': 'template'}, renderer)
assert not dest.exists()
assert (tmpdir / 'output.txt').exists()
assert (tmpdir / 'output.txt').text() == '# template data'
assert (tempdir / 'output.txt').exists()
assert (tempdir / 'output.txt').text() == '# template data'
# copy template file to subdir
src = (tmpdir / 'asset.txt_t')
src = (tempdir / 'asset.txt_t')
src.write_text('# {{var1}} data')
subdir1 = (tmpdir / 'subdir')
subdir1 = (tempdir / 'subdir')
subdir1.makedirs()
copy_asset_file(src, subdir1, {'var1': 'template'}, renderer)
@ -58,8 +58,8 @@ def test_copy_asset_file(tmpdir):
assert (subdir1 / 'asset.txt').text() == '# template data'
# copy template file without context
src = (tmpdir / 'asset.txt_t')
subdir2 = (tmpdir / 'subdir2')
src = (tempdir / 'asset.txt_t')
subdir2 = (tempdir / 'subdir2')
subdir2.makedirs()
copy_asset_file(src, subdir2)
@ -69,11 +69,11 @@ def test_copy_asset_file(tmpdir):
@with_tempdir
def test_copy_asset(tmpdir):
def test_copy_asset(tempdir):
renderer = DummyTemplateLoader()
# prepare source files
source = (tmpdir / 'source')
source = (tempdir / 'source')
source.makedirs()
(source / 'index.rst').write_text('index.rst')
(source / 'foo.rst_t').write_text('{{var1}}.rst')
@ -84,13 +84,13 @@ def test_copy_asset(tmpdir):
(source / '_templates' / 'sidebar.html_t').write_text('sidebar: {{var2}}')
# copy a single file
assert not (tmpdir / 'test1').exists()
copy_asset(source / 'index.rst', tmpdir / 'test1')
assert (tmpdir / 'test1').exists()
assert (tmpdir / 'test1/index.rst').exists()
assert not (tempdir / 'test1').exists()
copy_asset(source / 'index.rst', tempdir / 'test1')
assert (tempdir / 'test1').exists()
assert (tempdir / 'test1/index.rst').exists()
# copy directories
destdir = tmpdir / 'test2'
destdir = tempdir / 'test2'
copy_asset(source, destdir, context=dict(var1='bar', var2='baz'), renderer=renderer)
assert (destdir / 'index.rst').exists()
assert (destdir / 'foo.rst').exists()
@ -104,7 +104,7 @@ def test_copy_asset(tmpdir):
def excluded(path):
return ('sidebar.html' in path or 'basic.css' in path)
destdir = tmpdir / 'test3'
destdir = tempdir / 'test3'
copy_asset(source, destdir, excluded,
context=dict(var1='bar', var2='baz'), renderer=renderer)
assert (destdir / 'index.rst').exists()

View File

@ -38,12 +38,12 @@ def test_catalog_info_for_sub_domain_file_and_path():
@with_tempdir
def test_catalog_outdated(dir):
(dir / 'test.po').write_text('#')
cat = i18n.CatalogInfo(dir, 'test', 'utf-8')
def test_catalog_outdated(tempdir):
(tempdir / 'test.po').write_text('#')
cat = i18n.CatalogInfo(tempdir, 'test', 'utf-8')
assert cat.is_outdated() # if mo is not exist
mo_file = (dir / 'test.mo')
mo_file = (tempdir / 'test.mo')
mo_file.write_text('#')
assert not cat.is_outdated() # if mo is exist and newer than po
@ -52,9 +52,9 @@ def test_catalog_outdated(dir):
@with_tempdir
def test_catalog_write_mo(dir):
(dir / 'test.po').write_text('#')
cat = i18n.CatalogInfo(dir, 'test', 'utf-8')
def test_catalog_write_mo(tempdir):
(tempdir / 'test.po').write_text('#')
cat = i18n.CatalogInfo(tempdir, 'test', 'utf-8')
cat.write_mo('en')
assert path.exists(cat.mo_path)
with open(cat.mo_path, 'rb') as f:
@ -62,20 +62,20 @@ def test_catalog_write_mo(dir):
@with_tempdir
def test_get_catalogs_for_xx(dir):
(dir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test3.pot').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.po').write_text('#')
(dir / 'loc1' / 'en' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'en' / 'LC_MESSAGES' / 'test6.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_ALL').makedirs()
(dir / 'loc1' / 'xx' / 'LC_ALL' / 'test7.po').write_text('#')
def test_get_catalogs_for_xx(tempdir):
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test3.pot').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.po').write_text('#')
(tempdir / 'loc1' / 'en' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc1' / 'en' / 'LC_MESSAGES' / 'test6.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_ALL').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_ALL' / 'test7.po').write_text('#')
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', force_all=False)
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1'], 'xx', force_all=False)
domains = set(c.domain for c in catalogs)
assert domains == set([
'test1',
@ -86,23 +86,23 @@ def test_get_catalogs_for_xx(dir):
@with_tempdir
def test_get_catalogs_for_en(dir):
(dir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'xx_dom.po').write_text('#')
(dir / 'loc1' / 'en' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'en' / 'LC_MESSAGES' / 'en_dom.po').write_text('#')
def test_get_catalogs_for_en(tempdir):
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'xx_dom.po').write_text('#')
(tempdir / 'loc1' / 'en' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc1' / 'en' / 'LC_MESSAGES' / 'en_dom.po').write_text('#')
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'en', force_all=False)
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1'], 'en', force_all=False)
domains = set(c.domain for c in catalogs)
assert domains == set(['en_dom'])
@with_tempdir
def test_get_catalogs_with_non_existent_locale(dir):
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx')
def test_get_catalogs_with_non_existent_locale(tempdir):
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1'], 'xx')
assert not catalogs
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], None)
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1'], None)
assert not catalogs
@ -112,24 +112,24 @@ def test_get_catalogs_with_non_existent_locale_dirs():
@with_tempdir
def test_get_catalogs_for_xx_without_outdated(dir):
(dir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.mo').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.mo').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test3.pot').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test3.mo').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.mo').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.mo').write_text('#')
def test_get_catalogs_for_xx_without_outdated(tempdir):
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.mo').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.mo').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test3.pot').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test3.mo').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.mo').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test5.mo').write_text('#')
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', force_all=False)
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1'], 'xx', force_all=False)
assert not catalogs
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', force_all=True)
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1'], 'xx', force_all=True)
domains = set(c.domain for c in catalogs)
assert domains == set([
'test1',
@ -140,28 +140,28 @@ def test_get_catalogs_for_xx_without_outdated(dir):
@with_tempdir
def test_get_catalogs_from_multiple_locale_dirs(dir):
(dir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(dir / 'loc2' / 'xx' / 'LC_MESSAGES').makedirs()
(dir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(dir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
def test_get_catalogs_from_multiple_locale_dirs(tempdir):
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(tempdir / 'loc2' / 'xx' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(tempdir / 'loc2' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
catalogs = i18n.find_catalog_source_files([dir / 'loc1', dir / 'loc2'], 'xx')
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1', tempdir / 'loc2'], 'xx')
domains = sorted(c.domain for c in catalogs)
assert domains == ['test1', 'test1', 'test2']
@with_tempdir
def test_get_catalogs_with_compact(dir):
(dir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').makedirs()
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test3.po').write_text('#')
(dir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#')
def test_get_catalogs_with_compact(tempdir):
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test1.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'test2.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub').makedirs()
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test3.po').write_text('#')
(tempdir / 'loc1' / 'xx' / 'LC_MESSAGES' / 'sub' / 'test4.po').write_text('#')
catalogs = i18n.find_catalog_source_files([dir / 'loc1'], 'xx', gettext_compact=True)
catalogs = i18n.find_catalog_source_files([tempdir / 'loc1'], 'xx', gettext_compact=True)
domains = set(c.domain for c in catalogs)
assert domains == set(['test1', 'test2', 'sub'])

View File

@ -9,10 +9,6 @@
:license: BSD, see LICENSE for details.
"""
from functools import wraps
from six import StringIO
from sphinx.websupport import WebSupport
from sphinx.websupport.errors import DocumentNotFoundError, \
CommentNotAllowedError, UserNotAuthorizedError
@ -26,26 +22,28 @@ try:
except ImportError:
sqlalchemy_missing = True
import pytest
from util import rootdir, tempdir, raises, skip_if
default_settings = {'builddir': tempdir / 'websupport',
'status': StringIO(),
'warning': StringIO()}
@pytest.fixture
def support(request):
settings = {
'srcdir': rootdir / 'root',
# to use same directory for 'builddir' in each 'support' fixture, using
# 'tempdir' (static) value instead of 'tempdir' fixture value.
# each test expect result of db value at previous test case.
'builddir': tempdir / 'websupport'
}
marker = request.node.get_marker('support')
if marker:
settings.update(marker.kwargs)
support = WebSupport(**settings)
yield support
def with_support(*args, **kwargs):
"""Make a WebSupport object and pass it the test."""
settings = default_settings.copy()
settings.update(kwargs)
def generator(func):
@wraps(func)
def new_func(*args2, **kwargs2):
support = WebSupport(**settings)
func(support, *args2, **kwargs2)
return new_func
return generator
with_support = pytest.mark.support
class NullStorage(StorageBackend):
@ -59,7 +57,7 @@ def test_no_srcdir(support):
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support(srcdir=rootdir / 'root')
@with_support()
def test_build(support):
support.build()
@ -123,56 +121,6 @@ def test_comments(support):
assert children[0]['text'] == '<p>Child test comment</p>\n'
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support()
def test_voting(support):
session = Session()
nodes = session.query(Node).all()
node = nodes[0]
comment = support.get_data(node.id)['comments'][0]
def check_rating(val):
data = support.get_data(node.id)
comment = data['comments'][0]
assert comment['rating'] == val, '%s != %s' % (comment['rating'], val)
support.process_vote(comment['id'], 'user_one', '1')
support.process_vote(comment['id'], 'user_two', '1')
support.process_vote(comment['id'], 'user_three', '1')
check_rating(3)
support.process_vote(comment['id'], 'user_one', '-1')
check_rating(1)
support.process_vote(comment['id'], 'user_one', '0')
check_rating(2)
# Make sure a vote with value > 1 or < -1 can't be cast.
raises(ValueError, support.process_vote, comment['id'], 'user_one', '2')
raises(ValueError, support.process_vote, comment['id'], 'user_one', '-2')
# Make sure past voting data is associated with comments when they are
# fetched.
data = support.get_data(str(node.id), username='user_two')
comment = data['comments'][0]
assert comment['vote'] == 1, '%s != 1' % comment['vote']
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support()
def test_proposals(support):
session = Session()
node = session.query(Node).first()
data = support.get_data(node.id)
source = data['source']
proposal = source[:5] + source[10:15] + 'asdf' + source[15:]
support.add_comment('Proposal comment',
node_id=node.id,
proposal=proposal)
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support()
def test_user_delete_comments(support):
@ -194,6 +142,38 @@ def test_user_delete_comments(support):
assert comment['text'] == '[deleted]'
called = False
def moderation_callback(comment):
global called
called = True
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support(moderation_callback=moderation_callback)
def test_moderation(support):
session = Session()
nodes = session.query(Node).all()
node = nodes[7]
session.close()
accepted = support.add_comment('Accepted Comment', node_id=node.id,
displayed=False)
deleted = support.add_comment('Comment to delete', node_id=node.id,
displayed=False)
# Make sure the moderation_callback is called.
assert called
# Make sure the user must be a moderator.
raises(UserNotAuthorizedError, support.accept_comment, accepted['id'])
raises(UserNotAuthorizedError, support.delete_comment, deleted['id'])
support.accept_comment(accepted['id'], moderator=True)
support.delete_comment(deleted['id'], moderator=True)
comments = support.get_data(node.id)['comments']
assert len(comments) == 1
comments = support.get_data(node.id, moderator=True)['comments']
assert len(comments) == 1
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support()
def test_moderator_delete_comments(support):
@ -228,36 +208,54 @@ def test_update_username(support):
assert len(votes) == 0
called = False
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support()
def test_proposals(support):
session = Session()
node = session.query(Node).first()
data = support.get_data(node.id)
def moderation_callback(comment):
global called
called = True
source = data['source']
proposal = source[:5] + source[10:15] + 'asdf' + source[15:]
support.add_comment('Proposal comment',
node_id=node.id,
proposal=proposal)
@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
@with_support(moderation_callback=moderation_callback)
def test_moderation(support):
@with_support()
def test_voting(support):
session = Session()
nodes = session.query(Node).all()
node = nodes[7]
session.close()
accepted = support.add_comment('Accepted Comment', node_id=node.id,
displayed=False)
deleted = support.add_comment('Comment to delete', node_id=node.id,
displayed=False)
# Make sure the moderation_callback is called.
assert called
# Make sure the user must be a moderator.
raises(UserNotAuthorizedError, support.accept_comment, accepted['id'])
raises(UserNotAuthorizedError, support.delete_comment, deleted['id'])
support.accept_comment(accepted['id'], moderator=True)
support.delete_comment(deleted['id'], moderator=True)
comments = support.get_data(node.id)['comments']
assert len(comments) == 1
comments = support.get_data(node.id, moderator=True)['comments']
assert len(comments) == 1
node = nodes[0]
comment = support.get_data(node.id)['comments'][0]
def check_rating(val):
data = support.get_data(node.id)
comment = data['comments'][0]
assert comment['rating'] == val, '%s != %s' % (comment['rating'], val)
support.process_vote(comment['id'], 'user_one', '1')
support.process_vote(comment['id'], 'user_two', '1')
support.process_vote(comment['id'], 'user_three', '1')
check_rating(3)
support.process_vote(comment['id'], 'user_one', '-1')
check_rating(1)
support.process_vote(comment['id'], 'user_one', '0')
check_rating(2)
# Make sure a vote with value > 1 or < -1 can't be cast.
raises(ValueError, support.process_vote, comment['id'], 'user_one', '2')
raises(ValueError, support.process_vote, comment['id'], 'user_one', '-2')
# Make sure past voting data is associated with comments when they are
# fetched.
data = support.get_data(str(node.id), username='user_two')
comment = data['comments'][0]
assert comment['vote'] == 1, '%s != 1' % comment['vote']
def test_differ():

View File

@ -10,14 +10,13 @@
import os
import re
import sys
import tempfile
import warnings
from functools import wraps
from xml.etree import ElementTree
from six import StringIO, string_types
from six import string_types
from nose import tools, SkipTest
import pytest
from docutils import nodes
from docutils.parsers.rst import directives, roles
@ -27,16 +26,17 @@ from sphinx.builders.latex import LaTeXBuilder
from sphinx.theming import Theme
from sphinx.ext.autodoc import AutoDirective
from sphinx.pycode import ModuleAnalyzer
from sphinx.deprecation import RemovedInSphinx17Warning
from path import path, repr_as # NOQA
__all__ = [
'rootdir', 'tempdir', 'raises', 'raises_msg',
'skip_if', 'skip_unless', 'skip_unless_importable', 'Struct',
'ListOutput', 'TestApp', 'with_app', 'gen_with_app',
'path', 'with_tempdir',
'sprint', 'remove_unicode_literals',
'rootdir', 'tempdir',
'skip_unless_importable', 'Struct',
'SphinxTestApp',
'path',
'remove_unicode_literals',
]
@ -44,36 +44,6 @@ rootdir = path(os.path.dirname(__file__) or '.').abspath()
tempdir = path(os.environ['SPHINX_TEST_TEMPDIR']).abspath()
def _excstr(exc):
if type(exc) is tuple:
return str(tuple(map(_excstr, exc)))
return exc.__name__
def raises(exc, func, *args, **kwds):
"""Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
try:
func(*args, **kwds)
except exc:
pass
else:
raise AssertionError('%s did not raise %s' %
(func.__name__, _excstr(exc)))
def raises_msg(exc, msg, func, *args, **kwds):
"""Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*,
and check if the message contains *msg*.
"""
try:
func(*args, **kwds)
except exc as err:
assert msg in str(err), "\"%s\" not in \"%s\"" % (msg, err)
else:
raise AssertionError('%s did not raise %s' %
(func.__name__, _excstr(exc)))
def assert_re_search(regex, text, flags=0):
if not re.search(regex, text, flags):
assert False, '%r did not match %r' % (regex, text)
@ -118,43 +88,14 @@ def assert_node(node, cls=None, xpath="", **kwargs):
'The node%s[%s] is not %r: %r' % (xpath, key, value, node[key])
try:
from nose.tools import assert_in, assert_not_in
except ImportError:
def assert_in(x, thing, msg=''):
if x not in thing:
assert False, msg or '%r is not in %r' % (x, thing)
def assert_not_in(x, thing, msg=''):
if x in thing:
assert False, msg or '%r is in %r' % (x, thing)
def skip_if(condition, msg=None):
"""Decorator to skip test if condition is true."""
def deco(test):
@tools.make_decorator(test)
def skipper(*args, **kwds):
if condition:
raise SkipTest(msg or 'conditional skip')
return test(*args, **kwds)
return skipper
return deco
def skip_unless(condition, msg=None):
"""Decorator to skip test if condition is false."""
return skip_if(not condition, msg)
def skip_unless_importable(module, msg=None):
"""Decorator to skip test if module is not importable."""
try:
__import__(module)
except ImportError:
return skip_if(True, msg)
return pytest.mark.skipif(True, reason=(msg or 'conditional skip'))
else:
return skip_if(False, msg)
return pytest.mark.skipif(False, reason=(msg or 'conditional skip'))
def etree_parse(path):
@ -168,22 +109,7 @@ class Struct(object):
self.__dict__.update(kwds)
class ListOutput(object):
"""
File-like object that collects written text in a list.
"""
def __init__(self, name):
self.name = name
self.content = []
def reset(self):
del self.content[:]
def write(self, text):
self.content.append(text)
class TestApp(application.Sphinx):
class SphinxTestApp(application.Sphinx):
"""
A subclass of :class:`Sphinx` that runs on the test root, with some
better default values for the initialization parameters.
@ -222,10 +148,6 @@ class TestApp(application.Sphinx):
doctreedir.makedirs()
if confoverrides is None:
confoverrides = {}
if status is None:
status = StringIO()
if warning is None:
warning = ListOutput('stderr')
# if warningiserror is None:
warningiserror = False
@ -263,59 +185,6 @@ class TestApp(application.Sphinx):
return '<%s buildername=%r>' % (self.__class__.__name__, self.builder.name)
def with_app(*args, **kwargs):
"""
Make a TestApp with args and kwargs, pass it to the test and clean up
properly.
"""
def generator(func):
@wraps(func)
def deco(*args2, **kwargs2):
status, warning = StringIO(), StringIO()
kwargs['status'] = status
kwargs['warning'] = warning
app = TestApp(*args, **kwargs)
try:
func(app, status, warning, *args2, **kwargs2)
finally:
app.cleanup()
return deco
return generator
def gen_with_app(*args, **kwargs):
"""
Decorate a test generator to pass a TestApp as the first argument to the
test generator when it's executed.
"""
def generator(func):
@wraps(func)
def deco(*args2, **kwargs2):
status, warning = StringIO(), StringIO()
kwargs['status'] = status
kwargs['warning'] = warning
app = TestApp(*args, **kwargs)
try:
for item in func(app, status, warning, *args2, **kwargs2):
yield item
finally:
app.cleanup()
return deco
return generator
def with_tempdir(func):
def new_func(*args, **kwds):
new_tempdir = path(tempfile.mkdtemp(dir=tempdir))
func(new_tempdir, *args, **kwds)
new_func.__name__ = func.__name__
return new_func
def sprint(*args):
sys.stderr.write(' '.join(map(str, args)) + '\n')
_unicode_literals_re = re.compile(r'u(".*?")|u(\'.*?\')')
@ -333,3 +202,163 @@ def find_files(root, suffix=None):
def strip_escseq(text):
return re.sub('\x1b.*?m', '', text)
# #############################################
# DEPRECATED implementations
import tempfile
from six import StringIO
def gen_with_app(*args, **kwargs):
"""
**DEPRECATED**: use pytest.mark.parametrize instead.
Decorate a test generator to pass a SphinxTestApp as the first argument to
the test generator when it's executed.
"""
def generator(func):
@wraps(func)
def deco(*args2, **kwargs2):
status, warning = StringIO(), StringIO()
kwargs['status'] = status
kwargs['warning'] = warning
app = SphinxTestApp(*args, **kwargs)
try:
for item in func(app, status, warning, *args2, **kwargs2):
yield item
finally:
app.cleanup()
return deco
return generator
def skip_if(condition, msg=None):
"""
**DEPRECATED**: use pytest.mark.skipif instead.
Decorator to skip test if condition is true.
"""
return pytest.mark.skipif(condition, reason=(msg or 'conditional skip'))
def skip_unless(condition, msg=None):
"""
**DEPRECATED**: use pytest.mark.skipif instead.
Decorator to skip test if condition is false.
"""
return pytest.mark.skipif(not condition, reason=(msg or 'conditional skip'))
def with_tempdir(func):
"""
**DEPRECATED**: use tempdir fixture instead.
"""
return func
def raises(exc, func, *args, **kwds):
"""
**DEPRECATED**: use pytest.raises instead.
Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*.
"""
with pytest.raises(exc):
func(*args, **kwds)
def raises_msg(exc, msg, func, *args, **kwds):
"""
**DEPRECATED**: use pytest.raises instead.
Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*,
and check if the message contains *msg*.
"""
with pytest.raises(exc) as excinfo:
func(*args, **kwds)
assert msg in str(excinfo.value)
def assert_true(v1, msg=''):
"""
**DEPRECATED**: use assert instead.
"""
assert v1, msg
def assert_equal(v1, v2, msg=''):
"""
**DEPRECATED**: use assert instead.
"""
assert v1 == v2, msg
def assert_in(x, thing, msg=''):
"""
**DEPRECATED**: use assert instead.
"""
if x not in thing:
assert False, msg or '%r is not in %r' % (x, thing)
def assert_not_in(x, thing, msg=''):
"""
**DEPRECATED**: use assert instead.
"""
if x in thing:
assert False, msg or '%r is in %r' % (x, thing)
class ListOutput(object):
"""
File-like object that collects written text in a list.
"""
def __init__(self, name):
self.name = name
self.content = []
def reset(self):
del self.content[:]
def write(self, text):
self.content.append(text)
# **DEPRECATED**: use pytest.skip instead.
SkipTest = pytest.skip.Exception
class _DeprecationWrapper(object):
def __init__(self, mod, deprecated):
self._mod = mod
self._deprecated = deprecated
def __getattr__(self, attr):
if attr in self._deprecated:
obj, instead = self._deprecated[attr]
warnings.warn("tests/util.py::%s is deprecated and will be "
"removed in Sphinx 1.7, please use %s instead."
% (attr, instead),
RemovedInSphinx17Warning, stacklevel=2)
return obj
return getattr(self._mod, attr)
sys.modules[__name__] = _DeprecationWrapper(sys.modules[__name__], dict( # type: ignore
with_app=(pytest.mark.sphinx, 'pytest.mark.sphinx'),
TestApp=(SphinxTestApp, 'SphinxTestApp'),
gen_with_app=(gen_with_app, 'pytest.mark.parametrize'),
skip_if=(skip_if, 'pytest.skipif'),
skip_unless=(skip_unless, 'pytest.skipif'),
with_tempdir=(with_tempdir, 'tmpdir pytest fixture'),
raises=(raises, 'pytest.raises'),
raises_msg=(raises_msg, 'pytest.raises'),
assert_true=(assert_true, 'assert'),
assert_equal=(assert_equal, 'assert'),
assert_in=(assert_in, 'assert'),
assert_not_in=(assert_not_in, 'assert'),
ListOutput=(ListOutput, 'StringIO'),
SkipTest=(SkipTest, 'pytest.skip'),
))