mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
231 lines
6.2 KiB
Python
231 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import print_function
|
|
|
|
import sys
|
|
import subprocess
|
|
from collections import namedtuple
|
|
|
|
import pytest
|
|
from six import StringIO, string_types
|
|
|
|
import util
|
|
|
|
|
|
@pytest.fixture
|
|
def app_params(request, test_params, shared_result):
|
|
"""
|
|
parameters that is specified by 'pytest.mark.sphinx' for
|
|
sphinx.application.Sphinx initialization
|
|
"""
|
|
|
|
# ##### process pytest.mark.sphinx
|
|
|
|
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())]
|
|
|
|
# ##### process pytest.mark.test_params
|
|
|
|
if test_params['shared_result']:
|
|
if 'srcdir' in kwargs:
|
|
raise pytest.Exception('You can not spcify shared_result and '
|
|
'srcdir in same time.')
|
|
kwargs['srcdir'] = test_params['shared_result']
|
|
restore = shared_result.restore(test_params['shared_result'])
|
|
kwargs.update(restore)
|
|
|
|
# ##### prepare Application params
|
|
|
|
if 'srcdir' in kwargs:
|
|
srcdir = util.tempdir / kwargs['srcdir']
|
|
else:
|
|
srcdir = util.tempdir / kwargs.get('testroot', 'root')
|
|
kwargs['srcdir'] = srcdir
|
|
|
|
if kwargs.get('testroot') is None:
|
|
testroot_path = util.rootdir / 'root'
|
|
else:
|
|
testroot_path = util.rootdir / 'roots' / ('test-' + kwargs['testroot'])
|
|
|
|
if not srcdir.exists():
|
|
testroot_path.copytree(srcdir)
|
|
|
|
return namedtuple('app_params', 'args,kwargs')(args, kwargs)
|
|
|
|
|
|
@pytest.fixture
|
|
def test_params(request):
|
|
"""
|
|
test parameters that is specified by 'pytest.mark.test_params'
|
|
|
|
:param Union[str] shared_result:
|
|
If the value is provided, app._status and app._warning objects will be
|
|
shared in the parametrized test functions and/or test functions that
|
|
have same 'shared_result' value.
|
|
**NOTE**: You can not specify shared_result and srcdir in same time.
|
|
"""
|
|
env = request.node.get_marker('test_params')
|
|
kwargs = env.kwargs if env else {}
|
|
result = {
|
|
'shared_result': None,
|
|
}
|
|
result.update(kwargs)
|
|
|
|
if (result['shared_result'] and
|
|
not isinstance(result['shared_result'], string_types)):
|
|
raise pytest.Exception('You can only provide a string type of value '
|
|
'for "shared_result" ')
|
|
return result
|
|
|
|
|
|
class SphinxTestAppWrapperForSkipBuilding(object):
|
|
"""
|
|
This class is a wrapper for SphinxTestApp to speed up the test by skipping
|
|
`app.build` process if it is already built and there is even one output
|
|
file.
|
|
"""
|
|
|
|
def __init__(self, app_):
|
|
self.app = app_
|
|
|
|
def __getattr__(self, name):
|
|
return getattr(self.app, name)
|
|
|
|
def build(self, *args, **kw):
|
|
if not self.app.outdir.listdir():
|
|
# if listdir is empty, do build.
|
|
self.app.build(*args, **kw)
|
|
# otherwise, we can use built cache
|
|
|
|
|
|
@pytest.fixture(scope='function')
|
|
def app(test_params, app_params, make_app, shared_result):
|
|
"""
|
|
provides sphinx.application.Sphinx object
|
|
"""
|
|
args, kwargs = app_params
|
|
app_ = make_app(*args, **kwargs)
|
|
yield app_
|
|
|
|
print('# testroot:', kwargs.get('testroot', 'root'))
|
|
print('# builder:', app_.builder.name)
|
|
print('# srcdir:', app_.srcdir)
|
|
print('# outdir:', app_.outdir)
|
|
print('# status:', '\n' + app_._status.getvalue())
|
|
print('# warning:', '\n' + app_._warning.getvalue())
|
|
|
|
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(test_params):
|
|
"""
|
|
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_ = util.SphinxTestApp(*args, **kwargs)
|
|
apps.append(app_)
|
|
if test_params['shared_result']:
|
|
app_ = SphinxTestAppWrapperForSkipBuilding(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 util.path(tmpdir)
|