diff --git a/CHANGES b/CHANGES
index 87631a1f4..84269ba02 100644
--- a/CHANGES
+++ b/CHANGES
@@ -82,6 +82,7 @@ Bugs fixed
* #3268: Sphinx crashes with requests package from Debian jessie
* #3284: Sphinx crashes on parallel build with an extension which raises
unserializable exception
+* #3315: Bibliography crashes on latex build with docclass 'memoir'
Release 1.5.1 (released Dec 13, 2016)
diff --git a/Makefile b/Makefile
index 3d2439145..09a497da2 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ PYTHON ?= python
DONT_CHECK = -i build -i dist -i sphinx/style/jquery.js \
-i sphinx/pycode/pgen2 -i sphinx/util/smartypants.py \
-i .ropeproject -i doc/_build -i tests/path.py \
- -i tests/coverage.py -i utils/convert.py \
+ -i utils/convert.py \
-i tests/typing_test_data.py \
-i tests/test_autodoc_py35.py \
-i tests/roots/test-warnings/undecodable.rst \
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index c351a15af..9869f71aa 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -712,13 +712,13 @@ class StandardDomain(Domain):
else:
title = env.config.numfig_format.get(figtype, '')
- if figname is None and '%{name}' in title:
+ if figname is None and '{name}' in title:
logger.warning('the link has no caption: %s', title, location=node)
return contnode
else:
fignum = '.'.join(map(str, fignumber))
if '{name}' in title or 'number' in title:
- # new style format (cf. "Fig.%{number}")
+ # new style format (cf. "Fig.{number}")
if figname:
newtitle = title.format(name=figname, number=fignum)
else:
diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty
index 249e9ed9d..cc85ef935 100644
--- a/sphinx/texinputs/sphinx.sty
+++ b/sphinx/texinputs/sphinx.sty
@@ -1082,8 +1082,14 @@
% make commands known to non-Sphinx document classes
\providecommand*{\sphinxtableofcontents}{\tableofcontents}
-\providecommand*{\sphinxthebibliography}{\thebibliography}
-\providecommand*{\sphinxtheindex}{\theindex}
+\spx@ifundefined{sphinxthebibliography}
+ {\newenvironment
+ {sphinxthebibliography}{\begin{thebibliography}}{\end{thebibliography}}%
+ }
+ {}% else clause of ifundefined
+\spx@ifundefined{sphinxtheindex}
+ {\newenvironment{sphinxtheindex}{\begin{theindex}}{\end{theindex}}}%
+ {}% else clause of ifundefined
% remove LaTeX's cap on nesting depth if 'maxlistdepth' key used.
% This is a hack, which works with the standard classes: it assumes \@toodeep
diff --git a/test-reqs.txt b/test-reqs.txt
index 4475cec9a..e06513a01 100644
--- a/test-reqs.txt
+++ b/test-reqs.txt
@@ -3,7 +3,7 @@ pytest>=3.0
pytest-cov
mock
six>=1.4
-Jinja2>=2.3,<2.9
+Jinja2>=2.3
Pygments>=2.0
docutils>=0.11
snowballstemmer>=1.1
diff --git a/tests/conftest.py b/tests/conftest.py
index c83c3f94b..6fab88b12 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,19 +1,25 @@
# -*- coding: utf-8 -*-
+from __future__ import print_function
+
import sys
import subprocess
+from collections import namedtuple
import pytest
-from six import StringIO
+from six import StringIO, string_types
-from util import SphinxTestApp, path
+import util
@pytest.fixture
-def app_params(request):
+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 = {}
@@ -26,17 +32,99 @@ def app_params(request):
kwargs.update(info.kwargs)
args = [pargs[i] for i in sorted(pargs.keys())]
- return args, kwargs
+
+ # ##### 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(app_params, make_app):
+def app(test_params, app_params, make_app, shared_result):
"""
provides sphinx.application.Sphinx object
"""
args, kwargs = app_params
app_ = make_app(*args, **kwargs)
- return app_
+ yield app_
+
+ print('# testroot:', kwargs.get('testroot', 'root'))
+ print('# builder:', app_.buildername)
+ 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')
@@ -56,7 +144,7 @@ def warning(app):
@pytest.fixture()
-def make_app():
+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
@@ -69,8 +157,10 @@ def make_app():
status, warning = StringIO(), StringIO()
kwargs.setdefault('status', status)
kwargs.setdefault('warning', warning)
- app_ = SphinxTestApp(*args, **kwargs)
+ app_ = util.SphinxTestApp(*args, **kwargs)
apps.append(app_)
+ if test_params['shared_result']:
+ app_ = SphinxTestAppWrapperForSkipBuilding(app_)
return app_
yield make
@@ -79,6 +169,38 @@ def make_app():
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):
"""
@@ -105,4 +227,4 @@ def tempdir(tmpdir):
temporary directory that wrapped with `path` class.
this fixture is for compat with old test implementation.
"""
- return path(tmpdir)
+ return util.path(tmpdir)
diff --git a/tests/py35/test_autodoc_py35.py b/tests/py35/test_autodoc_py35.py
index 6049f7521..0a4fc872a 100644
--- a/tests/py35/test_autodoc_py35.py
+++ b/tests/py35/test_autodoc_py35.py
@@ -13,7 +13,7 @@
# "raises" imported for usage by autodoc
import six
import sys
-from util import TestApp, Struct, raises, SkipTest
+from util import SphinxTestApp, Struct
import pytest
from six import StringIO
@@ -27,7 +27,7 @@ app = None
def setup_module():
global app
- app = TestApp()
+ app = SphinxTestApp()
app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy'
app.connect('autodoc-process-docstring', process_docstring)
@@ -185,7 +185,7 @@ def test_generate():
'Class.meth', more_content=add_content)
# test check_module
- inst = FunctionDocumenter(directive, 'raises')
+ inst = FunctionDocumenter(directive, 'add_documenter')
inst.generate(check_module=True)
assert len(directive.result) == 0
diff --git a/tests/run.py b/tests/run.py
index 72a668541..a7afbb8b6 100755
--- a/tests/run.py
+++ b/tests/run.py
@@ -17,11 +17,15 @@ import warnings
import traceback
from path import path
-import pytest
testroot = os.path.dirname(__file__) or '.'
sys.path.insert(0, os.path.abspath(os.path.join(testroot, os.path.pardir)))
+# filter warnings of test dependencies
+warnings.filterwarnings('ignore', category=DeprecationWarning, module='site') # virtualenv
+warnings.filterwarnings('ignore', category=ImportWarning, module='backports')
+warnings.filterwarnings('ignore', category=PendingDeprecationWarning, module=r'_pytest\..*')
+
# check dependencies before testing
print('Checking dependencies...')
for modname in ('pytest', 'mock', 'six', 'docutils', 'jinja2', 'pygments',
@@ -49,9 +53,6 @@ tempdir.makedirs()
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='site') # virtualenv
-
# 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))
@@ -61,4 +62,5 @@ args = sys.argv[1:]
for path in ignore_paths:
args.extend(['--ignore', path])
+import pytest
sys.exit(pytest.main(args))
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 22eb02b34..06a3faf43 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -10,8 +10,7 @@
:license: BSD, see LICENSE for details.
"""
-# "raises" imported for usage by autodoc
-from util import TestApp, Struct, raises, SkipTest # NOQA
+from util import SphinxTestApp, Struct # NOQA
import pytest
import enum
@@ -26,7 +25,7 @@ app = None
def setup_module():
global app
- app = TestApp()
+ app = SphinxTestApp()
app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy'
app.connect('autodoc-process-docstring', process_docstring)
@@ -125,26 +124,27 @@ def test_parse_name():
del _warnings[:]
# for functions/classes
- verify('function', 'util.raises', ('util', ['raises'], None, None))
- verify('function', 'util.raises(exc) -> None',
- ('util', ['raises'], 'exc', 'None'))
- directive.env.temp_data['autodoc:module'] = 'util'
- verify('function', 'raises', ('util', ['raises'], None, None))
+ verify('function', 'test_autodoc.raises',
+ ('test_autodoc', ['raises'], None, None))
+ verify('function', 'test_autodoc.raises(exc) -> None',
+ ('test_autodoc', ['raises'], 'exc', 'None'))
+ directive.env.temp_data['autodoc:module'] = 'test_autodoc'
+ verify('function', 'raises', ('test_autodoc', ['raises'], None, None))
del directive.env.temp_data['autodoc:module']
- directive.env.ref_context['py:module'] = 'util'
- verify('function', 'raises', ('util', ['raises'], None, None))
- verify('class', 'TestApp', ('util', ['TestApp'], None, None))
+ directive.env.ref_context['py:module'] = 'test_autodoc'
+ verify('function', 'raises', ('test_autodoc', ['raises'], None, None))
+ verify('class', 'Base', ('test_autodoc', ['Base'], None, None))
# for members
directive.env.ref_context['py:module'] = 'foo'
- verify('method', 'util.TestApp.cleanup',
- ('util', ['TestApp', 'cleanup'], None, None))
+ verify('method', 'util.SphinxTestApp.cleanup',
+ ('util', ['SphinxTestApp', 'cleanup'], None, None))
directive.env.ref_context['py:module'] = 'util'
directive.env.ref_context['py:class'] = 'Foo'
- directive.env.temp_data['autodoc:class'] = 'TestApp'
- verify('method', 'cleanup', ('util', ['TestApp', 'cleanup'], None, None))
- verify('method', 'TestApp.cleanup',
- ('util', ['TestApp', 'cleanup'], None, None))
+ directive.env.temp_data['autodoc:class'] = 'SphinxTestApp'
+ verify('method', 'cleanup', ('util', ['SphinxTestApp', 'cleanup'], None, None))
+ verify('method', 'SphinxTestApp.cleanup',
+ ('util', ['SphinxTestApp', 'cleanup'], None, None))
# and clean up
del directive.env.ref_context['py:module']
@@ -658,7 +658,7 @@ def test_generate():
'Class.meth', more_content=add_content)
# test check_module
- inst = FunctionDocumenter(directive, 'raises')
+ inst = FunctionDocumenter(directive, 'add_documenter')
inst.generate(check_module=True)
assert len(directive.result) == 0
@@ -878,6 +878,11 @@ __all__ = ['Class']
integer = 1
+def raises(exc, func, *args, **kwds):
+ """Raise AssertionError if ``func(*args, **kwds)`` does not raise *exc*."""
+ pass
+
+
class CustomEx(Exception):
"""My custom exception."""
@@ -1086,7 +1091,7 @@ def test_type_hints():
try:
from typing_test_data import f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11
except (ImportError, SyntaxError):
- raise SkipTest('Cannot import Python code with function annotations')
+ pytest.skip('Cannot import Python code with function annotations')
def verify_arg_spec(f, expected):
assert formatargspec(f, *getargspec(f)) == expected
diff --git a/tests/test_build.py b/tests/test_build.py
index d623d9b88..e067809d1 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -15,14 +15,8 @@ import mock
import pytest
from textwrap import dedent
from sphinx.errors import SphinxError
-import sphinx.builders.linkcheck
-from util import rootdir, tempdir, SkipTest, TestApp, path
-
-try:
- from docutils.writers.manpage import Writer as ManWriter
-except ImportError:
- ManWriter = None
+from util import rootdir, tempdir, path
def request_session_head(url, **kwargs):
@@ -32,24 +26,17 @@ def request_session_head(url, **kwargs):
return response
-def verify_build(buildername, srcdir):
- if buildername == 'man' and ManWriter is None:
- raise SkipTest('man writer is not available')
- app = TestApp(buildername=buildername, srcdir=srcdir)
- try:
- app.builder.build_all()
- finally:
- app.cleanup()
-
-
-def test_build_all():
+@pytest.fixture
+def nonascii_srcdir(request):
# If supported, build in a non-ASCII source dir
test_name = u'\u65e5\u672c\u8a9e'
+ basedir = tempdir / request.node.originalname
try:
- srcdir = tempdir / test_name
- (rootdir / 'root').copytree(tempdir / test_name)
+ srcdir = basedir / test_name
+ if not srcdir.exists():
+ (rootdir / 'root').copytree(srcdir)
except UnicodeEncodeError:
- srcdir = tempdir / 'all'
+ srcdir = basedir / 'all'
else:
# add a doc with a non-ASCII file name to the source dir
(srcdir / (test_name + '.txt')).write_text(dedent("""
@@ -63,31 +50,33 @@ def test_build_all():
%(test_name)s/%(test_name)s
""" % {'test_name': test_name})
- )
+ )
+ return srcdir
- with mock.patch('sphinx.builders.linkcheck.requests') as requests:
- requests.head = request_session_head
+@pytest.mark.parametrize(
+ "buildername",
+ [
# note: no 'html' - if it's ok with dirhtml it's ok with html
- for buildername in ['dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle',
- 'json', 'text', 'htmlhelp', 'qthelp', 'epub2', 'epub',
- 'applehelp', 'changes', 'xml', 'pseudoxml', 'man',
- 'linkcheck']:
- yield verify_build, buildername, srcdir
+ 'dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle', 'json', 'text',
+ 'htmlhelp', 'qthelp', 'epub2', 'epub', 'applehelp', 'changes', 'xml',
+ 'pseudoxml', 'man', 'linkcheck',
+ ],
+)
+@mock.patch('sphinx.builders.linkcheck.requests.head',
+ side_effect=request_session_head)
+def test_build_all(requests_head, make_app, nonascii_srcdir, buildername):
+ app = make_app(buildername, srcdir=nonascii_srcdir)
+ app.build()
-def test_master_doc_not_found(tempdir):
+def test_master_doc_not_found(tempdir, make_app):
(tempdir / 'conf.py').write_text('master_doc = "index"')
assert tempdir.listdir() == ['conf.py']
- try:
- app = TestApp(buildername='dummy', srcdir=tempdir)
+ app = make_app('dummy', srcdir=tempdir)
+ with pytest.raises(SphinxError):
app.builder.build_all()
- assert False # SphinxError not raised
- except Exception as exc:
- assert isinstance(exc, SphinxError)
- finally:
- app.cleanup()
@pytest.mark.sphinx(buildername='text', testroot='circular')
diff --git a/tests/test_build_gettext.py b/tests/test_build_gettext.py
index cbc17ab80..6354360d7 100644
--- a/tests/test_build_gettext.py
+++ b/tests/test_build_gettext.py
@@ -17,36 +17,36 @@ from subprocess import Popen, PIPE
import pytest
-from util import (
- gen_with_app, SkipTest, assert_in, assert_true, assert_equal
-)
+from sphinx.util.osutil import cd
-@gen_with_app('gettext', srcdir='root-gettext')
-def test_all(app, status, warning):
+@pytest.mark.sphinx('gettext', srcdir='root-gettext')
+def test_build_gettext(app):
# Generic build; should fail only when the builder is horribly broken.
app.builder.build_all()
# Do messages end up in the correct location?
# top-level documents end up in a message catalog
- yield assert_true, (app.outdir / 'extapi.pot').isfile()
+ assert (app.outdir / 'extapi.pot').isfile()
# directory items are grouped into sections
- yield assert_true, (app.outdir / 'subdir.pot').isfile()
+ assert (app.outdir / 'subdir.pot').isfile()
# regression test for issue #960
catalog = (app.outdir / 'markup.pot').text(encoding='utf-8')
- yield assert_in, 'msgid "something, something else, something more"', catalog
+ assert 'msgid "something, something else, something more"' in catalog
+
+@pytest.mark.sphinx('gettext', srcdir='root-gettext')
+def test_msgfmt(app):
+ app.builder.build_all()
(app.outdir / 'en' / 'LC_MESSAGES').makedirs()
- cwd = os.getcwd()
- os.chdir(app.outdir)
- try:
+ with cd(app.outdir):
try:
p = Popen(['msginit', '--no-translator', '-i', 'markup.pot',
'--locale', 'en_US'],
stdout=PIPE, stderr=PIPE)
except OSError:
- raise SkipTest # most likely msginit was not found
+ pytest.skip() # most likely msginit was not found
else:
stdout, stderr = p.communicate()
if p.returncode != 0:
@@ -54,13 +54,13 @@ def test_all(app, status, warning):
print(stderr)
assert False, 'msginit exited with return code %s' % \
p.returncode
- yield assert_true, (app.outdir / 'en_US.po').isfile(), 'msginit failed'
+ assert (app.outdir / 'en_US.po').isfile(), 'msginit failed'
try:
p = Popen(['msgfmt', 'en_US.po', '-o',
os.path.join('en', 'LC_MESSAGES', 'test_root.mo')],
stdout=PIPE, stderr=PIPE)
except OSError:
- raise SkipTest # most likely msgfmt was not found
+ pytest.skip() # most likely msgfmt was not found
else:
stdout, stderr = p.communicate()
if p.returncode != 0:
@@ -68,20 +68,17 @@ def test_all(app, status, warning):
print(stderr)
assert False, 'msgfmt exited with return code %s' % \
p.returncode
- yield (assert_true,
- (app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo').isfile(),
- 'msgfmt failed')
- finally:
- os.chdir(cwd)
+ mo = app.outdir / 'en' / 'LC_MESSAGES' / 'test_root.mo'
+ assert mo.isfile(), 'msgfmt failed'
_ = gettext.translation('test_root', app.outdir, languages=['en']).gettext
- yield assert_equal, _("Testing various markup"), u"Testing various markup"
+ assert _("Testing various markup") == u"Testing various markup"
@pytest.mark.sphinx(
'gettext', testroot='intl', srcdir='gettext',
confoverrides={'gettext_compact': False})
-def test_gettext_index_entries(app, status, warning):
+def test_gettext_index_entries(app):
# regression test for #976
app.builder.build(['index_entries'])
@@ -128,8 +125,9 @@ def test_gettext_index_entries(app, status, warning):
@pytest.mark.sphinx(
'gettext', testroot='intl', srcdir='gettext',
- confoverrides={'gettext_compact': False, 'gettext_additional_targets': []})
-def test_gettext_disable_index_entries(app, status, warning):
+ confoverrides={'gettext_compact': False,
+ 'gettext_additional_targets': []})
+def test_gettext_disable_index_entries(app):
# regression test for #976
app.builder.build(['index_entries'])
@@ -160,7 +158,7 @@ def test_gettext_disable_index_entries(app, status, warning):
@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext')
-def test_gettext_template(app, status, warning):
+def test_gettext_template(app):
app.builder.build_all()
assert (app.outdir / 'sphinx.pot').isfile()
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index a9948fcfa..98a11d1fc 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -11,11 +11,12 @@
import os
import re
+from itertools import cycle, chain
-from six import PY3, iteritems
+from six import PY3
from sphinx import __display_version__
-from util import remove_unicode_literals, gen_with_app, strip_escseq
+from util import remove_unicode_literals, strip_escseq
from etree13 import ElementTree
from html5lib import getTreeBuilder, HTMLParser
import pytest
@@ -49,6 +50,31 @@ if PY3:
HTML_WARNINGS = remove_unicode_literals(HTML_WARNINGS)
+etree_cache = {}
+
+@pytest.fixture(scope='module')
+def cached_etree_parse():
+ def parse(fname):
+ if fname in etree_cache:
+ return etree_cache[fname]
+ with (fname).open('rb') as fp:
+ etree = HTML_PARSER.parse(fp)
+ etree_cache.clear()
+ etree_cache[fname] = etree
+ return etree
+ yield parse
+ etree_cache.clear()
+
+
+def flat_dict(d):
+ return chain.from_iterable(
+ [
+ zip(cycle([fname]), values)
+ for fname, values in d.items()
+ ]
+ )
+
+
def tail_check(check):
rex = re.compile(check)
@@ -60,274 +86,6 @@ def tail_check(check):
return checker
-HTML_XPATH = {
- 'images.html': [
- (".//img[@src='_images/img.png']", ''),
- (".//img[@src='_images/img1.png']", ''),
- (".//img[@src='_images/simg.png']", ''),
- (".//img[@src='_images/svgimg.svg']", ''),
- (".//a[@href='_sources/images.txt']", ''),
- ],
- 'subdir/images.html': [
- (".//img[@src='../_images/img1.png']", ''),
- (".//img[@src='../_images/rimg.png']", ''),
- ],
- 'subdir/includes.html': [
- (".//a[@href='../_downloads/img.png']", ''),
- (".//img[@src='../_images/img.png']", ''),
- (".//p", 'This is an include file.'),
- (".//pre/span", 'line 1'),
- (".//pre/span", 'line 2'),
- ],
- 'includes.html': [
- (".//pre", u'Max Strauß'),
- (".//a[@href='_downloads/img.png']", ''),
- (".//a[@href='_downloads/img1.png']", ''),
- (".//pre/span", u'"quotes"'),
- (".//pre/span", u"'included'"),
- (".//pre/span[@class='s2']", u'üöä'),
- (".//div[@class='inc-pyobj1 highlight-text']//pre",
- r'^class Foo:\n pass\n\s*$'),
- (".//div[@class='inc-pyobj2 highlight-text']//pre",
- r'^ def baz\(\):\n pass\n\s*$'),
- (".//div[@class='inc-lines highlight-text']//pre",
- r'^class Foo:\n pass\nclass Bar:\n$'),
- (".//div[@class='inc-startend highlight-text']//pre",
- u'^foo = "Including Unicode characters: üöä"\\n$'),
- (".//div[@class='inc-preappend highlight-text']//pre",
- r'(?m)^START CODE$'),
- (".//div[@class='inc-pyobj-dedent highlight-python']//span",
- r'def'),
- (".//div[@class='inc-tab3 highlight-text']//pre",
- r'-| |-'),
- (".//div[@class='inc-tab8 highlight-python']//pre/span",
- r'-| |-'),
- ],
- 'autodoc.html': [
- (".//dt[@id='test_autodoc.Class']", ''),
- (".//dt[@id='test_autodoc.function']/em", r'\*\*kwds'),
- (".//dd/p", r'Return spam\.'),
- ],
- 'extapi.html': [
- (".//strong", 'from function: Foo'),
- (".//strong", 'from class: Bar'),
- ],
- 'markup.html': [
- (".//title", 'set by title directive'),
- (".//p/em", 'Section author: Georg Brandl'),
- (".//p/em", 'Module author: Georg Brandl'),
- # created by the meta directive
- (".//meta[@name='author'][@content='Me']", ''),
- (".//meta[@name='keywords'][@content='docs, sphinx']", ''),
- # a label created by ``.. _label:``
- (".//div[@id='label']", ''),
- # code with standard code blocks
- (".//pre", '^some code$'),
- # an option list
- (".//span[@class='option']", '--help'),
- # admonitions
- (".//p[@class='first admonition-title']", 'My Admonition'),
- (".//p[@class='last']", 'Note text.'),
- (".//p[@class='last']", 'Warning text.'),
- # inline markup
- (".//li/strong", r'^command\\n$'),
- (".//li/strong", r'^program\\n$'),
- (".//li/em", r'^dfn\\n$'),
- (".//li/code/span[@class='pre']", r'^kbd\\n$'),
- (".//li/span", u'File \N{TRIANGULAR BULLET} Close'),
- (".//li/code/span[@class='pre']", '^a/$'),
- (".//li/code/em/span[@class='pre']", '^varpart$'),
- (".//li/code/em/span[@class='pre']", '^i$'),
- (".//a[@href='https://www.python.org/dev/peps/pep-0008']"
- "[@class='pep reference external']/strong", 'PEP 8'),
- (".//a[@href='https://www.python.org/dev/peps/pep-0008']"
- "[@class='pep reference external']/strong",
- 'Python Enhancement Proposal #8'),
- (".//a[@href='https://tools.ietf.org/html/rfc1.html']"
- "[@class='rfc reference external']/strong", 'RFC 1'),
- (".//a[@href='https://tools.ietf.org/html/rfc1.html']"
- "[@class='rfc reference external']/strong", 'Request for Comments #1'),
- (".//a[@href='objects.html#envvar-HOME']"
- "[@class='reference internal']/code/span[@class='pre']", 'HOME'),
- (".//a[@href='#with']"
- "[@class='reference internal']/code/span[@class='pre']", '^with$'),
- (".//a[@href='#grammar-token-try_stmt']"
- "[@class='reference internal']/code/span", '^statement$'),
- (".//a[@href='#some-label'][@class='reference internal']/span", '^here$'),
- (".//a[@href='#some-label'][@class='reference internal']/span", '^there$'),
- (".//a[@href='subdir/includes.html']"
- "[@class='reference internal']/span", 'Including in subdir'),
- (".//a[@href='objects.html#cmdoption-python-c']"
- "[@class='reference internal']/code/span[@class='pre']", '-c'),
- # abbreviations
- (".//abbr[@title='abbreviation']", '^abbr$'),
- # version stuff
- (".//div[@class='versionadded']/p/span", 'New in version 0.6: '),
- (".//div[@class='versionadded']/p/span",
- tail_check('First paragraph of versionadded')),
- (".//div[@class='versionchanged']/p/span",
- tail_check('First paragraph of versionchanged')),
- (".//div[@class='versionchanged']/p",
- 'Second paragraph of versionchanged'),
- # footnote reference
- (".//a[@class='footnote-reference']", r'\[1\]'),
- # created by reference lookup
- (".//a[@href='contents.html#ref1']", ''),
- # ``seealso`` directive
- (".//div/p[@class='first admonition-title']", 'See also'),
- # a ``hlist`` directive
- (".//table[@class='hlist']/tbody/tr/td/ul/li", '^This$'),
- # a ``centered`` directive
- (".//p[@class='centered']/strong", 'LICENSE'),
- # a glossary
- (".//dl/dt[@id='term-boson']", 'boson'),
- # a production list
- (".//pre/strong", 'try_stmt'),
- (".//pre/a[@href='#grammar-token-try1_stmt']/code/span", 'try1_stmt'),
- # tests for ``only`` directive
- (".//p", 'A global substitution.'),
- (".//p", 'In HTML.'),
- (".//p", 'In both.'),
- (".//p", 'Always present'),
- # tests for ``any`` role
- (".//a[@href='#with']/span", 'headings'),
- (".//a[@href='objects.html#func_without_body']/code/span", 'objects'),
- ],
- 'objects.html': [
- (".//dt[@id='mod.Cls.meth1']", ''),
- (".//dt[@id='errmod.Error']", ''),
- (".//dt/code", r'long\(parameter,\s* list\)'),
- (".//dt/code", 'another one'),
- (".//a[@href='#mod.Cls'][@class='reference internal']", ''),
- (".//dl[@class='userdesc']", ''),
- (".//dt[@id='userdesc-myobj']", ''),
- (".//a[@href='#userdesc-myobj'][@class='reference internal']", ''),
- # docfields
- (".//a[@class='reference internal'][@href='#TimeInt']/em", 'TimeInt'),
- (".//a[@class='reference internal'][@href='#Time']", 'Time'),
- (".//a[@class='reference internal'][@href='#errmod.Error']/strong", 'Error'),
- # C references
- (".//span[@class='pre']", 'CFunction()'),
- (".//a[@href='#c.Sphinx_DoSomething']", ''),
- (".//a[@href='#c.SphinxStruct.member']", ''),
- (".//a[@href='#c.SPHINX_USE_PYTHON']", ''),
- (".//a[@href='#c.SphinxType']", ''),
- (".//a[@href='#c.sphinx_global']", ''),
- # test global TOC created by toctree()
- (".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='#']",
- 'Testing object descriptions'),
- (".//li[@class='toctree-l1']/a[@href='markup.html']",
- 'Testing various markup'),
- # test unknown field names
- (".//th[@class='field-name']", 'Field_name:'),
- (".//th[@class='field-name']", 'Field_name all lower:'),
- (".//th[@class='field-name']", 'FIELD_NAME:'),
- (".//th[@class='field-name']", 'FIELD_NAME ALL CAPS:'),
- (".//th[@class='field-name']", 'Field_Name:'),
- (".//th[@class='field-name']", 'Field_Name All Word Caps:'),
- (".//th[@class='field-name']", 'Field_name:'),
- (".//th[@class='field-name']", 'Field_name First word cap:'),
- (".//th[@class='field-name']", 'FIELd_name:'),
- (".//th[@class='field-name']", 'FIELd_name PARTial caps:'),
- # custom sidebar
- (".//h4", 'Custom sidebar'),
- # docfields
- (".//td[@class='field-body']/strong", '^moo$'),
- (".//td[@class='field-body']/strong", tail_check(r'\(Moo\) .* Moo')),
- (".//td[@class='field-body']/ul/li/strong", '^hour$'),
- (".//td[@class='field-body']/ul/li/em", '^DuplicateType$'),
- (".//td[@class='field-body']/ul/li/em", tail_check(r'.* Some parameter')),
- # others
- (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
- 'perl'),
- (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
- '\+p'),
- (".//a[@class='reference internal'][@href='#cmdoption-perl-plugin-option']/code/span",
- '--plugin.option'),
- (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']"
- "/code/span",
- 'create-auth-token'),
- (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-arg']/code/span",
- 'arg'),
- (".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
- 'hg'),
- (".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
- 'commit'),
- (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
- 'git'),
- (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
- 'commit'),
- (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
- '-p'),
- ],
- 'contents.html': [
- (".//meta[@name='hc'][@content='hcval']", ''),
- (".//meta[@name='hc_co'][@content='hcval_co']", ''),
- (".//meta[@name='testopt'][@content='testoverride']", ''),
- (".//td[@class='label']", r'\[Ref1\]'),
- (".//td[@class='label']", ''),
- (".//li[@class='toctree-l1']/a", 'Testing various markup'),
- (".//li[@class='toctree-l2']/a", 'Inline markup'),
- (".//title", 'Sphinx '),
- (".//div[@class='footer']", 'Georg Brandl & Team'),
- (".//a[@href='http://python.org/']"
- "[@class='reference external']", ''),
- (".//li/a[@href='genindex.html']/span", 'Index'),
- (".//li/a[@href='py-modindex.html']/span", 'Module Index'),
- (".//li/a[@href='search.html']/span", 'Search Page'),
- # custom sidebar only for contents
- (".//h4", 'Contents sidebar'),
- # custom JavaScript
- (".//script[@src='file://moo.js']", ''),
- # URL in contents
- (".//a[@class='reference external'][@href='http://sphinx-doc.org/']",
- 'http://sphinx-doc.org/'),
- (".//a[@class='reference external'][@href='http://sphinx-doc.org/latest/']",
- 'Latest reference'),
- # Indirect hyperlink targets across files
- (".//a[@href='markup.html#some-label'][@class='reference internal']/span",
- '^indirect hyperref$'),
- ],
- 'bom.html': [
- (".//title", " File with UTF-8 BOM"),
- ],
- 'extensions.html': [
- (".//a[@href='http://python.org/dev/']", "http://python.org/dev/"),
- (".//a[@href='http://bugs.python.org/issue1000']", "issue 1000"),
- (".//a[@href='http://bugs.python.org/issue1042']", "explicit caption"),
- ],
- '_static/statictmpl.html': [
- (".//project", 'Sphinx '),
- ],
- 'genindex.html': [
- # index entries
- (".//a/strong", "Main"),
- (".//a/strong", "[1]"),
- (".//a/strong", "Other"),
- (".//a", "entry"),
- (".//li/a", "double"),
- ],
- 'footnote.html': [
- (".//a[@class='footnote-reference'][@href='#id7'][@id='id1']", r"\[1\]"),
- (".//a[@class='footnote-reference'][@href='#id8'][@id='id2']", r"\[2\]"),
- (".//a[@class='footnote-reference'][@href='#foo'][@id='id3']", r"\[3\]"),
- (".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"),
- (".//a[@class='footnote-reference'][@href='#id9'][@id='id5']", r"\[4\]"),
- (".//a[@class='footnote-reference'][@href='#id10'][@id='id6']", r"\[5\]"),
- (".//a[@class='fn-backref'][@href='#id1']", r"\[1\]"),
- (".//a[@class='fn-backref'][@href='#id2']", r"\[2\]"),
- (".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"),
- (".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"),
- (".//a[@class='fn-backref'][@href='#id5']", r"\[4\]"),
- (".//a[@class='fn-backref'][@href='#id6']", r"\[5\]"),
- ],
- 'otherext.html': [
- (".//h1", "Generated section"),
- (".//a[@href='_sources/otherext.foo.txt']", ''),
- ]
-}
-
-
def check_xpath(etree, fname, path, check, be_found=True):
nodes = list(etree.findall(path))
if check is None:
@@ -385,9 +143,9 @@ def check_extra_entries(outdir):
assert (outdir / 'robots.txt').isfile()
-@pytest.mark.sphinx('html', testroot='warnings', freshenv=True)
-def test_html_warnings(app, status, warning):
- app.builder.build_all()
+@pytest.mark.sphinx('html', testroot='warnings')
+def test_html_warnings(app, warning):
+ app.build()
html_warnings = strip_escseq(warning.getvalue().replace(os.sep, '/'))
html_warnings_exp = HTML_WARNINGS % {
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
@@ -397,168 +155,427 @@ def test_html_warnings(app, status, warning):
'--- Got:\n' + html_warnings
-@gen_with_app(buildername='html', tags=['testtag'],
- confoverrides={'html_context.hckey_co': 'hcval_co'})
-def test_html_output(app, status, warning):
- app.builder.build_all()
- for fname, paths in iteritems(HTML_XPATH):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
- for path, check in paths:
- yield check_xpath, etree, fname, path, check
-
+@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={
+ 'html_context.hckey_co': 'hcval_co'})
+@pytest.mark.test_params(shared_result='test_build_html_output')
+def test_static_output(app):
+ app.build()
check_static_entries(app.builder.outdir)
check_extra_entries(app.builder.outdir)
-@gen_with_app(buildername='html', testroot='tocdepth')
-def test_tocdepth(app, status, warning):
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'images.html': [
+ (".//img[@src='_images/img.png']", ''),
+ (".//img[@src='_images/img1.png']", ''),
+ (".//img[@src='_images/simg.png']", ''),
+ (".//img[@src='_images/svgimg.svg']", ''),
+ (".//a[@href='_sources/images.txt']", ''),
+ ],
+ 'subdir/images.html': [
+ (".//img[@src='../_images/img1.png']", ''),
+ (".//img[@src='../_images/rimg.png']", ''),
+ ],
+ 'subdir/includes.html': [
+ (".//a[@href='../_downloads/img.png']", ''),
+ (".//img[@src='../_images/img.png']", ''),
+ (".//p", 'This is an include file.'),
+ (".//pre/span", 'line 1'),
+ (".//pre/span", 'line 2'),
+ ],
+ 'includes.html': [
+ (".//pre", u'Max Strauß'),
+ (".//a[@href='_downloads/img.png']", ''),
+ (".//a[@href='_downloads/img1.png']", ''),
+ (".//pre/span", u'"quotes"'),
+ (".//pre/span", u"'included'"),
+ (".//pre/span[@class='s2']", u'üöä'),
+ (".//div[@class='inc-pyobj1 highlight-text']//pre",
+ r'^class Foo:\n pass\n\s*$'),
+ (".//div[@class='inc-pyobj2 highlight-text']//pre",
+ r'^ def baz\(\):\n pass\n\s*$'),
+ (".//div[@class='inc-lines highlight-text']//pre",
+ r'^class Foo:\n pass\nclass Bar:\n$'),
+ (".//div[@class='inc-startend highlight-text']//pre",
+ u'^foo = "Including Unicode characters: üöä"\\n$'),
+ (".//div[@class='inc-preappend highlight-text']//pre",
+ r'(?m)^START CODE$'),
+ (".//div[@class='inc-pyobj-dedent highlight-python']//span",
+ r'def'),
+ (".//div[@class='inc-tab3 highlight-text']//pre",
+ r'-| |-'),
+ (".//div[@class='inc-tab8 highlight-python']//pre/span",
+ r'-| |-'),
+ ],
+ 'autodoc.html': [
+ (".//dt[@id='test_autodoc.Class']", ''),
+ (".//dt[@id='test_autodoc.function']/em", r'\*\*kwds'),
+ (".//dd/p", r'Return spam\.'),
+ ],
+ 'extapi.html': [
+ (".//strong", 'from function: Foo'),
+ (".//strong", 'from class: Bar'),
+ ],
+ 'markup.html': [
+ (".//title", 'set by title directive'),
+ (".//p/em", 'Section author: Georg Brandl'),
+ (".//p/em", 'Module author: Georg Brandl'),
+ # created by the meta directive
+ (".//meta[@name='author'][@content='Me']", ''),
+ (".//meta[@name='keywords'][@content='docs, sphinx']", ''),
+ # a label created by ``.. _label:``
+ (".//div[@id='label']", ''),
+ # code with standard code blocks
+ (".//pre", '^some code$'),
+ # an option list
+ (".//span[@class='option']", '--help'),
+ # admonitions
+ (".//p[@class='first admonition-title']", 'My Admonition'),
+ (".//p[@class='last']", 'Note text.'),
+ (".//p[@class='last']", 'Warning text.'),
+ # inline markup
+ (".//li/strong", r'^command\\n$'),
+ (".//li/strong", r'^program\\n$'),
+ (".//li/em", r'^dfn\\n$'),
+ (".//li/code/span[@class='pre']", r'^kbd\\n$'),
+ (".//li/span", u'File \N{TRIANGULAR BULLET} Close'),
+ (".//li/code/span[@class='pre']", '^a/$'),
+ (".//li/code/em/span[@class='pre']", '^varpart$'),
+ (".//li/code/em/span[@class='pre']", '^i$'),
+ (".//a[@href='https://www.python.org/dev/peps/pep-0008']"
+ "[@class='pep reference external']/strong", 'PEP 8'),
+ (".//a[@href='https://www.python.org/dev/peps/pep-0008']"
+ "[@class='pep reference external']/strong",
+ 'Python Enhancement Proposal #8'),
+ (".//a[@href='https://tools.ietf.org/html/rfc1.html']"
+ "[@class='rfc reference external']/strong", 'RFC 1'),
+ (".//a[@href='https://tools.ietf.org/html/rfc1.html']"
+ "[@class='rfc reference external']/strong", 'Request for Comments #1'),
+ (".//a[@href='objects.html#envvar-HOME']"
+ "[@class='reference internal']/code/span[@class='pre']", 'HOME'),
+ (".//a[@href='#with']"
+ "[@class='reference internal']/code/span[@class='pre']", '^with$'),
+ (".//a[@href='#grammar-token-try_stmt']"
+ "[@class='reference internal']/code/span", '^statement$'),
+ (".//a[@href='#some-label'][@class='reference internal']/span", '^here$'),
+ (".//a[@href='#some-label'][@class='reference internal']/span", '^there$'),
+ (".//a[@href='subdir/includes.html']"
+ "[@class='reference internal']/span", 'Including in subdir'),
+ (".//a[@href='objects.html#cmdoption-python-c']"
+ "[@class='reference internal']/code/span[@class='pre']", '-c'),
+ # abbreviations
+ (".//abbr[@title='abbreviation']", '^abbr$'),
+ # version stuff
+ (".//div[@class='versionadded']/p/span", 'New in version 0.6: '),
+ (".//div[@class='versionadded']/p/span",
+ tail_check('First paragraph of versionadded')),
+ (".//div[@class='versionchanged']/p/span",
+ tail_check('First paragraph of versionchanged')),
+ (".//div[@class='versionchanged']/p",
+ 'Second paragraph of versionchanged'),
+ # footnote reference
+ (".//a[@class='footnote-reference']", r'\[1\]'),
+ # created by reference lookup
+ (".//a[@href='contents.html#ref1']", ''),
+ # ``seealso`` directive
+ (".//div/p[@class='first admonition-title']", 'See also'),
+ # a ``hlist`` directive
+ (".//table[@class='hlist']/tbody/tr/td/ul/li", '^This$'),
+ # a ``centered`` directive
+ (".//p[@class='centered']/strong", 'LICENSE'),
+ # a glossary
+ (".//dl/dt[@id='term-boson']", 'boson'),
+ # a production list
+ (".//pre/strong", 'try_stmt'),
+ (".//pre/a[@href='#grammar-token-try1_stmt']/code/span", 'try1_stmt'),
+ # tests for ``only`` directive
+ (".//p", 'A global substitution.'),
+ (".//p", 'In HTML.'),
+ (".//p", 'In both.'),
+ (".//p", 'Always present'),
+ # tests for ``any`` role
+ (".//a[@href='#with']/span", 'headings'),
+ (".//a[@href='objects.html#func_without_body']/code/span", 'objects'),
+ ],
+ 'objects.html': [
+ (".//dt[@id='mod.Cls.meth1']", ''),
+ (".//dt[@id='errmod.Error']", ''),
+ (".//dt/code", r'long\(parameter,\s* list\)'),
+ (".//dt/code", 'another one'),
+ (".//a[@href='#mod.Cls'][@class='reference internal']", ''),
+ (".//dl[@class='userdesc']", ''),
+ (".//dt[@id='userdesc-myobj']", ''),
+ (".//a[@href='#userdesc-myobj'][@class='reference internal']", ''),
+ # docfields
+ (".//a[@class='reference internal'][@href='#TimeInt']/em", 'TimeInt'),
+ (".//a[@class='reference internal'][@href='#Time']", 'Time'),
+ (".//a[@class='reference internal'][@href='#errmod.Error']/strong", 'Error'),
+ # C references
+ (".//span[@class='pre']", 'CFunction()'),
+ (".//a[@href='#c.Sphinx_DoSomething']", ''),
+ (".//a[@href='#c.SphinxStruct.member']", ''),
+ (".//a[@href='#c.SPHINX_USE_PYTHON']", ''),
+ (".//a[@href='#c.SphinxType']", ''),
+ (".//a[@href='#c.sphinx_global']", ''),
+ # test global TOC created by toctree()
+ (".//ul[@class='current']/li[@class='toctree-l1 current']/a[@href='#']",
+ 'Testing object descriptions'),
+ (".//li[@class='toctree-l1']/a[@href='markup.html']",
+ 'Testing various markup'),
+ # test unknown field names
+ (".//th[@class='field-name']", 'Field_name:'),
+ (".//th[@class='field-name']", 'Field_name all lower:'),
+ (".//th[@class='field-name']", 'FIELD_NAME:'),
+ (".//th[@class='field-name']", 'FIELD_NAME ALL CAPS:'),
+ (".//th[@class='field-name']", 'Field_Name:'),
+ (".//th[@class='field-name']", 'Field_Name All Word Caps:'),
+ (".//th[@class='field-name']", 'Field_name:'),
+ (".//th[@class='field-name']", 'Field_name First word cap:'),
+ (".//th[@class='field-name']", 'FIELd_name:'),
+ (".//th[@class='field-name']", 'FIELd_name PARTial caps:'),
+ # custom sidebar
+ (".//h4", 'Custom sidebar'),
+ # docfields
+ (".//td[@class='field-body']/strong", '^moo$'),
+ (".//td[@class='field-body']/strong", tail_check(r'\(Moo\) .* Moo')),
+ (".//td[@class='field-body']/ul/li/strong", '^hour$'),
+ (".//td[@class='field-body']/ul/li/em", '^DuplicateType$'),
+ (".//td[@class='field-body']/ul/li/em", tail_check(r'.* Some parameter')),
+ # others
+ (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
+ 'perl'),
+ (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
+ '\+p'),
+ (".//a[@class='reference internal'][@href='#cmdoption-perl-plugin-option']/code/span",
+ '--plugin.option'),
+ (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']"
+ "/code/span",
+ 'create-auth-token'),
+ (".//a[@class='reference internal'][@href='#cmdoption-perl-arg-arg']/code/span",
+ 'arg'),
+ (".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
+ 'hg'),
+ (".//a[@class='reference internal'][@href='#cmdoption-hg-arg-commit']/code/span",
+ 'commit'),
+ (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
+ 'git'),
+ (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
+ 'commit'),
+ (".//a[@class='reference internal'][@href='#cmdoption-git-commit-p']/code/span",
+ '-p'),
+ ],
+ 'contents.html': [
+ (".//meta[@name='hc'][@content='hcval']", ''),
+ (".//meta[@name='hc_co'][@content='hcval_co']", ''),
+ (".//meta[@name='testopt'][@content='testoverride']", ''),
+ (".//td[@class='label']", r'\[Ref1\]'),
+ (".//td[@class='label']", ''),
+ (".//li[@class='toctree-l1']/a", 'Testing various markup'),
+ (".//li[@class='toctree-l2']/a", 'Inline markup'),
+ (".//title", 'Sphinx '),
+ (".//div[@class='footer']", 'Georg Brandl & Team'),
+ (".//a[@href='http://python.org/']"
+ "[@class='reference external']", ''),
+ (".//li/a[@href='genindex.html']/span", 'Index'),
+ (".//li/a[@href='py-modindex.html']/span", 'Module Index'),
+ (".//li/a[@href='search.html']/span", 'Search Page'),
+ # custom sidebar only for contents
+ (".//h4", 'Contents sidebar'),
+ # custom JavaScript
+ (".//script[@src='file://moo.js']", ''),
+ # URL in contents
+ (".//a[@class='reference external'][@href='http://sphinx-doc.org/']",
+ 'http://sphinx-doc.org/'),
+ (".//a[@class='reference external'][@href='http://sphinx-doc.org/latest/']",
+ 'Latest reference'),
+ # Indirect hyperlink targets across files
+ (".//a[@href='markup.html#some-label'][@class='reference internal']/span",
+ '^indirect hyperref$'),
+ ],
+ 'bom.html': [
+ (".//title", " File with UTF-8 BOM"),
+ ],
+ 'extensions.html': [
+ (".//a[@href='http://python.org/dev/']", "http://python.org/dev/"),
+ (".//a[@href='http://bugs.python.org/issue1000']", "issue 1000"),
+ (".//a[@href='http://bugs.python.org/issue1042']", "explicit caption"),
+ ],
+ '_static/statictmpl.html': [
+ (".//project", 'Sphinx '),
+ ],
+ 'genindex.html': [
+ # index entries
+ (".//a/strong", "Main"),
+ (".//a/strong", "[1]"),
+ (".//a/strong", "Other"),
+ (".//a", "entry"),
+ (".//li/a", "double"),
+ ],
+ 'footnote.html': [
+ (".//a[@class='footnote-reference'][@href='#id7'][@id='id1']", r"\[1\]"),
+ (".//a[@class='footnote-reference'][@href='#id8'][@id='id2']", r"\[2\]"),
+ (".//a[@class='footnote-reference'][@href='#foo'][@id='id3']", r"\[3\]"),
+ (".//a[@class='reference internal'][@href='#bar'][@id='id4']", r"\[bar\]"),
+ (".//a[@class='footnote-reference'][@href='#id9'][@id='id5']", r"\[4\]"),
+ (".//a[@class='footnote-reference'][@href='#id10'][@id='id6']", r"\[5\]"),
+ (".//a[@class='fn-backref'][@href='#id1']", r"\[1\]"),
+ (".//a[@class='fn-backref'][@href='#id2']", r"\[2\]"),
+ (".//a[@class='fn-backref'][@href='#id3']", r"\[3\]"),
+ (".//a[@class='fn-backref'][@href='#id4']", r"\[bar\]"),
+ (".//a[@class='fn-backref'][@href='#id5']", r"\[4\]"),
+ (".//a[@class='fn-backref'][@href='#id6']", r"\[5\]"),
+ ],
+ 'otherext.html': [
+ (".//h1", "Generated section"),
+ (".//a[@href='_sources/otherext.foo.txt']", ''),
+ ]
+}))
+@pytest.mark.sphinx('html', tags=['testtag'], confoverrides={
+ 'html_context.hckey_co': 'hcval_co'})
+@pytest.mark.test_params(shared_result='test_build_html_output')
+def test_html_output(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
+
+
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//li[@class='toctree-l3']/a", '1.1.1. Foo A1', True),
+ (".//li[@class='toctree-l3']/a", '1.2.1. Foo B1', True),
+ (".//li[@class='toctree-l3']/a", '2.1.1. Bar A1', False),
+ (".//li[@class='toctree-l3']/a", '2.2.1. Bar B1', False),
+ ],
+ 'foo.html': [
+ (".//h1", '1. Foo', True),
+ (".//h2", '1.1. Foo A', True),
+ (".//h3", '1.1.1. Foo A1', True),
+ (".//h2", '1.2. Foo B', True),
+ (".//h3", '1.2.1. Foo B1', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '1.1. Foo A', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '1.1.1. Foo A1', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '1.2. Foo B', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '1.2.1. Foo B1', True),
+ ],
+ 'bar.html': [
+ (".//h1", '2. Bar', True),
+ (".//h2", '2.1. Bar A', True),
+ (".//h2", '2.2. Bar B', True),
+ (".//h3", '2.2.1. Bar B1', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '2. Bar', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '2.1. Bar A', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '2.2. Bar B', True),
+ (".//div[@class='sphinxsidebarwrapper']//li/a", '2.2.1. Bar B1', False),
+ ],
+ 'baz.html': [
+ (".//h1", '2.1.1. Baz A', True),
+ ],
+}))
+@pytest.mark.sphinx('html', testroot='tocdepth')
+@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
+def test_tocdepth(app, cached_etree_parse, fname, expect):
+ app.build()
# issue #1251
- app.builder.build_all()
-
- expects = {
- 'index.html': [
- (".//li[@class='toctree-l3']/a", '1.1.1. Foo A1', True),
- (".//li[@class='toctree-l3']/a", '1.2.1. Foo B1', True),
- (".//li[@class='toctree-l3']/a", '2.1.1. Bar A1', False),
- (".//li[@class='toctree-l3']/a", '2.2.1. Bar B1', False),
- ],
- 'foo.html': [
- (".//h1", '1. Foo', True),
- (".//h2", '1.1. Foo A', True),
- (".//h3", '1.1.1. Foo A1', True),
- (".//h2", '1.2. Foo B', True),
- (".//h3", '1.2.1. Foo B1', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '1.1. Foo A', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '1.1.1. Foo A1', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '1.2. Foo B', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '1.2.1. Foo B1', True),
- ],
- 'bar.html': [
- (".//h1", '2. Bar', True),
- (".//h2", '2.1. Bar A', True),
- (".//h2", '2.2. Bar B', True),
- (".//h3", '2.2.1. Bar B1', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '2. Bar', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '2.1. Bar A', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '2.2. Bar B', True),
- (".//div[@class='sphinxsidebarwrapper']//li/a", '2.2.1. Bar B1', False),
- ],
- 'baz.html': [
- (".//h1", '2.1.1. Baz A', True),
- ],
- }
-
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@gen_with_app(buildername='singlehtml', testroot='tocdepth')
-def test_tocdepth_singlehtml(app, status, warning):
- app.builder.build_all()
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//li[@class='toctree-l3']/a", '1.1.1. Foo A1', True),
+ (".//li[@class='toctree-l3']/a", '1.2.1. Foo B1', True),
+ (".//li[@class='toctree-l3']/a", '2.1.1. Bar A1', False),
+ (".//li[@class='toctree-l3']/a", '2.2.1. Bar B1', False),
- expects = {
- 'index.html': [
- (".//li[@class='toctree-l3']/a", '1.1.1. Foo A1', True),
- (".//li[@class='toctree-l3']/a", '1.2.1. Foo B1', True),
- (".//li[@class='toctree-l3']/a", '2.1.1. Bar A1', False),
- (".//li[@class='toctree-l3']/a", '2.2.1. Bar B1', False),
+ # index.rst
+ (".//h1", 'test-tocdepth', True),
- # index.rst
- (".//h1", 'test-tocdepth', True),
+ # foo.rst
+ (".//h2", '1. Foo', True),
+ (".//h3", '1.1. Foo A', True),
+ (".//h4", '1.1.1. Foo A1', True),
+ (".//h3", '1.2. Foo B', True),
+ (".//h4", '1.2.1. Foo B1', True),
- # foo.rst
- (".//h2", '1. Foo', True),
- (".//h3", '1.1. Foo A', True),
- (".//h4", '1.1.1. Foo A1', True),
- (".//h3", '1.2. Foo B', True),
- (".//h4", '1.2.1. Foo B1', True),
+ # bar.rst
+ (".//h2", '2. Bar', True),
+ (".//h3", '2.1. Bar A', True),
+ (".//h3", '2.2. Bar B', True),
+ (".//h4", '2.2.1. Bar B1', True),
- # bar.rst
- (".//h2", '2. Bar', True),
- (".//h3", '2.1. Bar A', True),
- (".//h3", '2.2. Bar B', True),
- (".//h4", '2.2.1. Bar B1', True),
-
- # baz.rst
- (".//h4", '2.1.1. Baz A', True),
- ],
- }
-
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+ # baz.rst
+ (".//h4", '2.1.1. Baz A', True),
+ ],
+}))
+@pytest.mark.sphinx('singlehtml', testroot='tocdepth')
+@pytest.mark.test_params(shared_result='test_build_html_tocdepth')
+def test_tocdepth_singlehtml(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@gen_with_app(buildername='html', testroot='numfig')
-def test_numfig_disabled(app, status, warning):
- app.builder.build_all()
-
+@pytest.mark.sphinx('html', testroot='numfig')
+@pytest.mark.test_params(shared_result='test_build_html_numfig')
+def test_numfig_disabled_warn(app, warning):
+ app.build()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' not in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' not in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' not in warnings
- expects = {
- 'index.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", None, True),
- (".//table/caption/span[@class='caption-number']", None, True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", None, True),
- (".//li/code/span", '^fig1$', True),
- (".//li/code/span", '^Figure%s$', True),
- (".//li/code/span", '^table-1$', True),
- (".//li/code/span", '^Table:%s$', True),
- (".//li/code/span", '^CODE_1$', True),
- (".//li/code/span", '^Code-%s$', True),
- (".//li/code/span", '^foo$', True),
- (".//li/code/span", '^bar_a$', True),
- (".//li/code/span", '^Fig.{number}$', True),
- (".//li/code/span", '^Sect.{number}$', True),
- ],
- 'foo.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", None, True),
- (".//table/caption/span[@class='caption-number']", None, True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", None, True),
- ],
- 'bar.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", None, True),
- (".//table/caption/span[@class='caption-number']", None, True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", None, True),
- ],
- 'baz.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", None, True),
- (".//table/caption/span[@class='caption-number']", None, True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", None, True),
- ],
- }
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", None, True),
+ (".//table/caption/span[@class='caption-number']", None, True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", None, True),
+ (".//li/code/span", '^fig1$', True),
+ (".//li/code/span", '^Figure%s$', True),
+ (".//li/code/span", '^table-1$', True),
+ (".//li/code/span", '^Table:%s$', True),
+ (".//li/code/span", '^CODE_1$', True),
+ (".//li/code/span", '^Code-%s$', True),
+ (".//li/code/span", '^foo$', True),
+ (".//li/code/span", '^bar_a$', True),
+ (".//li/code/span", '^Fig.{number}$', True),
+ (".//li/code/span", '^Sect.{number}$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", None, True),
+ (".//table/caption/span[@class='caption-number']", None, True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", None, True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", None, True),
+ (".//table/caption/span[@class='caption-number']", None, True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", None, True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", None, True),
+ (".//table/caption/span[@class='caption-number']", None, True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", None, True),
+ ],
+}))
+@pytest.mark.sphinx('html', testroot='numfig')
+@pytest.mark.test_params(shared_result='test_build_html_numfig')
+def test_numfig_disabled(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@gen_with_app(buildername='html', testroot='numfig', freshenv=True,
- confoverrides={'numfig': True})
-def test_numfig_without_numbered_toctree(app, status, warning):
+@pytest.mark.sphinx(
+ 'html', testroot='numfig',
+ srcdir='test_numfig_without_numbered_toctree_warn',
+ confoverrides={'numfig': True})
+def test_numfig_without_numbered_toctree_warn(app, warning):
+ app.build()
# remove :numbered: option
index = (app.srcdir / 'index.rst').text()
index = re.sub(':numbered:.*', '', index, re.MULTILINE)
@@ -571,522 +588,518 @@ def test_numfig_without_numbered_toctree(app, status, warning):
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
- expects = {
- 'index.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 9 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 10 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 9 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 10 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 9 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 10 $', True),
- (".//li/a/span", '^Fig. 9$', True),
- (".//li/a/span", '^Figure6$', True),
- (".//li/a/span", '^Table 9$', True),
- (".//li/a/span", '^Table:6$', True),
- (".//li/a/span", '^Listing 9$', True),
- (".//li/a/span", '^Code-6$', True),
- (".//li/code/span", '^foo$', True),
- (".//li/code/span", '^bar_a$', True),
- (".//li/a/span", '^Fig.9 should be Fig.1$', True),
- (".//li/code/span", '^Sect.{number}$', True),
- ],
- 'foo.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 4 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 4 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 4 $', True),
- ],
- 'bar.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 5 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 7 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 8 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 5 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 7 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 8 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 5 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 7 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 8 $', True),
- ],
- 'baz.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 6 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 6 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 6 $', True),
- ],
- }
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 9 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 10 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 9 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 10 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 9 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 10 $', True),
+ (".//li/a/span", '^Fig. 9$', True),
+ (".//li/a/span", '^Figure6$', True),
+ (".//li/a/span", '^Table 9$', True),
+ (".//li/a/span", '^Table:6$', True),
+ (".//li/a/span", '^Listing 9$', True),
+ (".//li/a/span", '^Code-6$', True),
+ (".//li/code/span", '^foo$', True),
+ (".//li/code/span", '^bar_a$', True),
+ (".//li/a/span", '^Fig.9 should be Fig.1$', True),
+ (".//li/code/span", '^Sect.{number}$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 4 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 4 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 4 $', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 5 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 7 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 8 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 5 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 7 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 8 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 5 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 7 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 8 $', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 6 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 6 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 6 $', True),
+ ],
+}))
+@pytest.mark.sphinx(
+ 'html', testroot='numfig',
+ srcdir='test_numfig_without_numbered_toctree',
+ confoverrides={'numfig': True})
+def test_numfig_without_numbered_toctree(app, cached_etree_parse, fname, expect):
+ # remove :numbered: option
+ index = (app.srcdir / 'index.rst').text()
+ index = re.sub(':numbered:.*', '', index, re.MULTILINE)
+ (app.srcdir / 'index.rst').write_text(index, encoding='utf-8')
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+ if not app.outdir.listdir():
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@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()
-
+@pytest.mark.sphinx('html', testroot='numfig', confoverrides={'numfig': True})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
+def test_numfig_with_numbered_toctree_warn(app, warning):
+ app.build()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
- expects = {
- 'index.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2 $', True),
- (".//li/a/span", '^Fig. 1$', True),
- (".//li/a/span", '^Figure2.2$', True),
- (".//li/a/span", '^Table 1$', True),
- (".//li/a/span", '^Table:2.2$', True),
- (".//li/a/span", '^Listing 1$', True),
- (".//li/a/span", '^Code-2.2$', True),
- (".//li/a/span", '^Section.1$', True),
- (".//li/a/span", '^Section.2.1$', True),
- (".//li/a/span", '^Fig.1 should be Fig.1$', True),
- (".//li/a/span", '^Sect.1 Foo$', True),
- ],
- 'foo.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.2 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.4 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.4 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.4 $', True),
- ],
- 'bar.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.4 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.4 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.4 $', True),
- ],
- 'baz.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.2 $', True),
- ],
- }
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2 $', True),
+ (".//li/a/span", '^Fig. 1$', True),
+ (".//li/a/span", '^Figure2.2$', True),
+ (".//li/a/span", '^Table 1$', True),
+ (".//li/a/span", '^Table:2.2$', True),
+ (".//li/a/span", '^Listing 1$', True),
+ (".//li/a/span", '^Code-2.2$', True),
+ (".//li/a/span", '^Section.1$', True),
+ (".//li/a/span", '^Section.2.1$', True),
+ (".//li/a/span", '^Fig.1 should be Fig.1$', True),
+ (".//li/a/span", '^Sect.1 Foo$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.2 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.4 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.4 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.4 $', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.4 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.4 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.4 $', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.2 $', True),
+ ],
+}))
+@pytest.mark.sphinx('html', testroot='numfig', confoverrides={'numfig': True})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
+def test_numfig_with_numbered_toctree(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@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',
- 'code-block': 'Code-%s',
- 'section': 'SECTION-%s'}})
-def test_numfig_with_prefix(app, status, warning):
- app.builder.build_all()
-
+@pytest.mark.sphinx('html', testroot='numfig', confoverrides={
+ 'numfig': True,
+ 'numfig_format': {'figure': 'Figure:%s',
+ 'table': 'Tab_%s',
+ 'code-block': 'Code-%s',
+ 'section': 'SECTION-%s'}})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_format_warn')
+def test_numfig_with_prefix_warn(app, warning):
+ app.build()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
- expects = {
- 'index.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-2 $', True),
- (".//li/a/span", '^Figure:1$', True),
- (".//li/a/span", '^Figure2.2$', True),
- (".//li/a/span", '^Tab_1$', True),
- (".//li/a/span", '^Table:2.2$', True),
- (".//li/a/span", '^Code-1$', True),
- (".//li/a/span", '^Code-2.2$', True),
- (".//li/a/span", '^SECTION-1$', True),
- (".//li/a/span", '^SECTION-2.1$', True),
- (".//li/a/span", '^Fig.1 should be Fig.1$', True),
- (".//li/a/span", '^Sect.1 Foo$', True),
- ],
- 'foo.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:1.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:1.2 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:1.3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:1.4 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_1.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_1.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_1.3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_1.4 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-1.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-1.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-1.3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-1.4 $', True),
- ],
- 'bar.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:2.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:2.3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:2.4 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_2.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_2.3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_2.4 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-2.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-2.3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-2.4 $', True),
- ],
- 'baz.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Figure:2.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Tab_2.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Code-2.2 $', True),
- ],
- }
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-2 $', True),
+ (".//li/a/span", '^Figure:1$', True),
+ (".//li/a/span", '^Figure2.2$', True),
+ (".//li/a/span", '^Tab_1$', True),
+ (".//li/a/span", '^Table:2.2$', True),
+ (".//li/a/span", '^Code-1$', True),
+ (".//li/a/span", '^Code-2.2$', True),
+ (".//li/a/span", '^SECTION-1$', True),
+ (".//li/a/span", '^SECTION-2.1$', True),
+ (".//li/a/span", '^Fig.1 should be Fig.1$', True),
+ (".//li/a/span", '^Sect.1 Foo$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:1.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:1.2 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:1.3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:1.4 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_1.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_1.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_1.3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_1.4 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-1.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-1.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-1.3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-1.4 $', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:2.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:2.3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:2.4 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_2.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_2.3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_2.4 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-2.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-2.3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-2.4 $', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Figure:2.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Tab_2.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Code-2.2 $', True),
+ ],
+}))
+@pytest.mark.sphinx('html', testroot='numfig', confoverrides={
+ 'numfig': True,
+ 'numfig_format': {'figure': 'Figure:%s',
+ 'table': 'Tab_%s',
+ 'code-block': 'Code-%s',
+ 'section': 'SECTION-%s'}})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_format_warn')
+def test_numfig_with_prefix(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@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()
-
+@pytest.mark.sphinx('html', testroot='numfig', confoverrides={
+ 'numfig': True, 'numfig_secnum_depth': 2})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_depth_2')
+def test_numfig_with_secnum_depth_warn(app, warning):
+ app.build()
warnings = warning.getvalue()
assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
- expects = {
- 'index.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2 $', True),
- (".//li/a/span", '^Fig. 1$', True),
- (".//li/a/span", '^Figure2.1.2$', True),
- (".//li/a/span", '^Table 1$', True),
- (".//li/a/span", '^Table:2.1.2$', True),
- (".//li/a/span", '^Listing 1$', True),
- (".//li/a/span", '^Code-2.1.2$', True),
- (".//li/a/span", '^Section.1$', True),
- (".//li/a/span", '^Section.2.1$', True),
- (".//li/a/span", '^Fig.1 should be Fig.1$', True),
- (".//li/a/span", '^Sect.1 Foo$', True),
- ],
- 'foo.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.1.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.1.2 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.2.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.1.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.1.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.2.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.1.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.1.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.2.1 $', True),
- ],
- 'bar.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.1.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.1.3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.2.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.1.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.1.3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.2.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.1.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.1.3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.2.1 $', True),
- ],
- 'baz.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.1.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.1.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.1.2 $', True),
- ],
- }
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2 $', True),
+ (".//li/a/span", '^Fig. 1$', True),
+ (".//li/a/span", '^Figure2.1.2$', True),
+ (".//li/a/span", '^Table 1$', True),
+ (".//li/a/span", '^Table:2.1.2$', True),
+ (".//li/a/span", '^Listing 1$', True),
+ (".//li/a/span", '^Code-2.1.2$', True),
+ (".//li/a/span", '^Section.1$', True),
+ (".//li/a/span", '^Section.2.1$', True),
+ (".//li/a/span", '^Fig.1 should be Fig.1$', True),
+ (".//li/a/span", '^Sect.1 Foo$', True),
+ ],
+ 'foo.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.1.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.1.2 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.2.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.1.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.1.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.2.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.1.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.1.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.2.1 $', True),
+ ],
+ 'bar.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.1.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.1.3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.2.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.1.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.1.3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.2.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.1.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.1.3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.2.1 $', True),
+ ],
+ 'baz.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.1.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.1.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.1.2 $', True),
+ ],
+}))
+@pytest.mark.sphinx('html', testroot='numfig', confoverrides={
+ 'numfig': True, 'numfig_secnum_depth': 2})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_depth_2')
+def test_numfig_with_secnum_depth(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@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()
-
- expects = {
- 'index.html': [
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2 $', True),
- (".//li/a/span", '^Fig. 1$', True),
- (".//li/a/span", '^Figure2.2$', True),
- (".//li/a/span", '^Table 1$', True),
- (".//li/a/span", '^Table:2.2$', True),
- (".//li/a/span", '^Listing 1$', True),
- (".//li/a/span", '^Code-2.2$', True),
- (".//li/a/span", '^Section.1$', True),
- (".//li/a/span", '^Section.2.1$', True),
- (".//li/a/span", '^Fig.1 should be Fig.1$', True),
- (".//li/a/span", '^Sect.1 Foo$', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.2 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 1.4 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 1.4 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 1.4 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.1 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.3 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.4 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.1 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.3 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.4 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.1 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.3 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.4 $', True),
- (".//div[@class='figure']/p[@class='caption']/"
- "span[@class='caption-number']", '^Fig. 2.2 $', True),
- (".//table/caption/span[@class='caption-number']",
- '^Table 2.2 $', True),
- (".//div[@class='code-block-caption']/"
- "span[@class='caption-number']", '^Listing 2.2 $', True),
- ],
- }
-
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2 $', True),
+ (".//li/a/span", '^Fig. 1$', True),
+ (".//li/a/span", '^Figure2.2$', True),
+ (".//li/a/span", '^Table 1$', True),
+ (".//li/a/span", '^Table:2.2$', True),
+ (".//li/a/span", '^Listing 1$', True),
+ (".//li/a/span", '^Code-2.2$', True),
+ (".//li/a/span", '^Section.1$', True),
+ (".//li/a/span", '^Section.2.1$', True),
+ (".//li/a/span", '^Fig.1 should be Fig.1$', True),
+ (".//li/a/span", '^Sect.1 Foo$', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.2 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 1.4 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 1.4 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 1.4 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.1 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.3 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.4 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.1 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.3 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.4 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.1 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.3 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.4 $', True),
+ (".//div[@class='figure']/p[@class='caption']/"
+ "span[@class='caption-number']", '^Fig. 2.2 $', True),
+ (".//table/caption/span[@class='caption-number']",
+ '^Table 2.2 $', True),
+ (".//div[@class='code-block-caption']/"
+ "span[@class='caption-number']", '^Listing 2.2 $', True),
+ ],
+}))
+@pytest.mark.sphinx('singlehtml', testroot='numfig', confoverrides={
+ 'numfig': True})
+@pytest.mark.test_params(shared_result='test_build_html_numfig_on')
+def test_numfig_with_singlehtml(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
-@gen_with_app(buildername='html', testroot='add_enumerable_node')
-def test_enumerable_node(app, status, warning):
- app.builder.build_all()
-
- expects = {
- 'index.html': [
- (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
- "Fig. 1", True),
- (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
- "Fig. 2", True),
- (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
- "Fig. 3", True),
- (".//div//span[@class='caption-number']", "No.1 ", True),
- (".//div//span[@class='caption-number']", "No.2 ", True),
- (".//li/a/span", 'Fig. 1', True),
- (".//li/a/span", 'Fig. 2', True),
- (".//li/a/span", 'Fig. 3', True),
- (".//li/a/span", 'No.1', True),
- (".//li/a/span", 'No.2', True),
- ],
- }
-
- for fname, paths in iteritems(expects):
- with (app.outdir / fname).open('rb') as fp:
- etree = HTML_PARSER.parse(fp)
-
- for xpath, check, be_found in paths:
- yield check_xpath, etree, fname, xpath, check, be_found
+@pytest.mark.parametrize("fname,expect", flat_dict({
+ 'index.html': [
+ (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
+ "Fig. 1", True),
+ (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
+ "Fig. 2", True),
+ (".//div[@class='figure']/p[@class='caption']/span[@class='caption-number']",
+ "Fig. 3", True),
+ (".//div//span[@class='caption-number']", "No.1 ", True),
+ (".//div//span[@class='caption-number']", "No.2 ", True),
+ (".//li/a/span", 'Fig. 1', True),
+ (".//li/a/span", 'Fig. 2', True),
+ (".//li/a/span", 'Fig. 3', True),
+ (".//li/a/span", 'No.1', True),
+ (".//li/a/span", 'No.2', True),
+ ],
+}))
+@pytest.mark.sphinx(
+ 'html', testroot='add_enumerable_node',
+ srcdir='test_enumerable_node',
+)
+def test_enumerable_node(app, cached_etree_parse, fname, expect):
+ app.build()
+ check_xpath(cached_etree_parse(app.outdir / fname), fname, *expect)
@pytest.mark.sphinx('html', testroot='html_assets')
-def test_html_assets(app, status, warning):
+def test_html_assets(app):
app.builder.build_all()
# html_static_path
@@ -1114,7 +1127,7 @@ def test_html_assets(app, status, warning):
@pytest.mark.sphinx('html', confoverrides={'html_sourcelink_suffix': ''})
-def test_html_sourcelink_suffix(app, status, warning):
+def test_html_sourcelink_suffix(app):
app.builder.build_all()
content_otherext = (app.outdir / 'otherext.html').text()
content_images = (app.outdir / 'images.html').text()
diff --git a/tests/test_config.py b/tests/test_config.py
index 379b59dfc..75de8d57c 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -13,9 +13,6 @@ from six import PY3, iteritems
import pytest
import mock
-from util import TestApp, gen_with_app, \
- assert_in, assert_not_in
-
import sphinx
from sphinx.config import Config
from sphinx.errors import ExtensionError, ConfigError, VersionRequirementError
@@ -76,7 +73,6 @@ def test_core_config(app, status, warning):
assert cfg['project'] == cfg.project == 'Sphinx Tests'
-@pytest.mark.sphinx()
def test_extension_values(app, status, warning):
cfg = app.config
@@ -125,39 +121,39 @@ def test_errors_warnings(logger, tempdir):
assert logger.warning.called is True
-def test_errors_if_setup_is_not_callable(tempdir):
+def test_errors_if_setup_is_not_callable(tempdir, make_app):
# test the error to call setup() in the config file
(tempdir / 'conf.py').write_text(u'setup = 1')
with pytest.raises(ConfigError) as excinfo:
- TestApp(srcdir=tempdir)
+ make_app(srcdir=tempdir)
assert 'callable' in str(excinfo.value)
@mock.patch.object(sphinx, '__display_version__', '1.3.4')
-def test_needs_sphinx():
+def test_needs_sphinx(make_app):
# micro version
- app = TestApp(confoverrides={'needs_sphinx': '1.3.3'}) # OK: less
+ app = make_app(confoverrides={'needs_sphinx': '1.3.3'}) # OK: less
app.cleanup()
- app = TestApp(confoverrides={'needs_sphinx': '1.3.4'}) # OK: equals
+ app = make_app(confoverrides={'needs_sphinx': '1.3.4'}) # OK: equals
app.cleanup()
with pytest.raises(VersionRequirementError):
- TestApp(confoverrides={'needs_sphinx': '1.3.5'}) # NG: greater
+ make_app(confoverrides={'needs_sphinx': '1.3.5'}) # NG: greater
# minor version
- app = TestApp(confoverrides={'needs_sphinx': '1.2'}) # OK: less
+ app = make_app(confoverrides={'needs_sphinx': '1.2'}) # OK: less
app.cleanup()
- app = TestApp(confoverrides={'needs_sphinx': '1.3'}) # OK: equals
+ app = make_app(confoverrides={'needs_sphinx': '1.3'}) # OK: equals
app.cleanup()
with pytest.raises(VersionRequirementError):
- TestApp(confoverrides={'needs_sphinx': '1.4'}) # NG: greater
+ make_app(confoverrides={'needs_sphinx': '1.4'}) # NG: greater
# major version
- app = TestApp(confoverrides={'needs_sphinx': '0'}) # OK: less
+ app = make_app(confoverrides={'needs_sphinx': '0'}) # OK: less
app.cleanup()
- app = TestApp(confoverrides={'needs_sphinx': '1'}) # OK: equals
+ app = make_app(confoverrides={'needs_sphinx': '1'}) # OK: equals
app.cleanup()
with pytest.raises(VersionRequirementError):
- TestApp(confoverrides={'needs_sphinx': '2'}) # NG: greater
+ make_app(confoverrides={'needs_sphinx': '2'}) # NG: greater
@mock.patch("sphinx.config.logger")
@@ -177,12 +173,14 @@ def test_config_eol(logger, tempdir):
'primary_domain': None})
def test_builtin_conf(app, status, warning):
warnings = warning.getvalue()
- assert_in('master_doc', warnings,
- 'override on builtin "master_doc" should raise a type warning')
- assert_not_in('language', warnings, 'explicitly permitted '
- 'override on builtin "language" should NOT raise a type warning')
- assert_not_in('primary_domain', warnings, 'override to None on builtin '
- '"primary_domain" should NOT raise a type warning')
+ assert 'master_doc' in warnings, (
+ 'override on builtin "master_doc" should raise a type warning')
+ assert 'language' not in warnings, (
+ 'explicitly permitted override on builtin "language" should NOT raise '
+ 'a type warning')
+ assert 'primary_domain' not in warnings, (
+ 'override to None on builtin "primary_domain" should NOT raise a type '
+ 'warning')
# See roots/test-config/conf.py.
@@ -197,7 +195,7 @@ TYPECHECK_WARNINGS = {
'value8': False,
'value9': False,
'value10': False,
- 'value11': True,
+ 'value11': False if PY3 else True,
'value12': False,
'value13': False,
'value14': False,
@@ -206,15 +204,17 @@ TYPECHECK_WARNINGS = {
}
-@gen_with_app(testroot='config')
-def test_gen_check_types(app, status, warning):
- if PY3:
- TYPECHECK_WARNINGS['value11'] = False
-
- for key, should in iteritems(TYPECHECK_WARNINGS):
- yield assert_in if should else assert_not_in, key, warning.getvalue(), (
- 'override on "%s" should%s raise a type warning' %
- (key, '' if should else ' NOT')
+@pytest.mark.parametrize("key,should", iteritems(TYPECHECK_WARNINGS))
+@pytest.mark.sphinx(testroot='config')
+def test_check_types(warning, key, should):
+ warn = warning.getvalue()
+ if should:
+ assert key in warn, (
+ 'override on "%s" should raise a type warning' % key
+ )
+ else:
+ assert key not in warn, (
+ 'override on "%s" should NOT raise a type warning' % key
)
diff --git a/tests/test_correct_year.py b/tests/test_correct_year.py
index 2c2f0d812..574adb6f0 100644
--- a/tests/test_correct_year.py
+++ b/tests/test_correct_year.py
@@ -8,42 +8,30 @@
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-import os
-
-from util import TestApp
+import pytest
-def test_correct_year():
- try:
- # save current value of SOURCE_DATE_EPOCH
- sde = os.environ.pop('SOURCE_DATE_EPOCH', None)
-
+@pytest.fixture(
+ params=[
# test with SOURCE_DATE_EPOCH unset: no modification
- app = TestApp(buildername='html', testroot='correct-year')
- app.builder.build_all()
- content = (app.outdir / 'contents.html').text()
- app.cleanup()
- assert '2006-2009' in content
+ (None, '2006-2009'),
+ # test with SOURCE_DATE_EPOCH set: copyright year should be updated
+ ('1293840000', '2006-2011'),
+ ('1293839999', '2006-2010'),
+ ],
- # test with SOURCE_DATE_EPOCH set: copyright year should be
- # updated
- os.environ['SOURCE_DATE_EPOCH'] = "1293840000"
- app = TestApp(buildername='html', testroot='correct-year')
- app.builder.build_all()
- content = (app.outdir / 'contents.html').text()
- app.cleanup()
- assert '2006-2011' in content
+)
+def expect_date(request, monkeypatch):
+ sde, expect = request.param
+ if sde:
+ monkeypatch.setenv('SOURCE_DATE_EPOCH', sde)
+ else:
+ monkeypatch.delenv('SOURCE_DATE_EPOCH', raising=False)
+ yield expect
- os.environ['SOURCE_DATE_EPOCH'] = "1293839999"
- app = TestApp(buildername='html', testroot='correct-year')
- app.builder.build_all()
- content = (app.outdir / 'contents.html').text()
- app.cleanup()
- assert '2006-2010' in content
- finally:
- # Restores SOURCE_DATE_EPOCH
- if sde is None:
- os.environ.pop('SOURCE_DATE_EPOCH', None)
- else:
- os.environ['SOURCE_DATE_EPOCH'] = sde
+@pytest.mark.sphinx('html', testroot='correct-year')
+def test_correct_year(expect_date, app):
+ app.build()
+ content = (app.outdir / 'contents.html').text()
+ assert expect_date in content
diff --git a/tests/test_environment.py b/tests/test_environment.py
index 11de4a93b..a0e438494 100644
--- a/tests/test_environment.py
+++ b/tests/test_environment.py
@@ -9,9 +9,7 @@
:license: BSD, see LICENSE for details.
"""
-from six import StringIO
-
-from util import TestApp, path
+from util import SphinxTestApp, path
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.builders.latex import LaTeXBuilder
@@ -21,7 +19,7 @@ app = env = None
def setup_module():
global app, env
- app = TestApp(srcdir='root-envtest', warning=StringIO())
+ app = SphinxTestApp(srcdir='root-envtest')
env = app.env
diff --git a/tests/test_environment_toctree.py b/tests/test_environment_toctree.py
index 037109126..595f922ae 100644
--- a/tests/test_environment_toctree.py
+++ b/tests/test_environment_toctree.py
@@ -16,23 +16,13 @@ from sphinx.addnodes import compact_paragraph, only
from sphinx.builders.html import StandaloneHTMLBuilder
import pytest
-from util import gen_with_app, assert_node
+from util import assert_node
-@gen_with_app('xml', testroot='toctree')
-def test_basic(app, status, warning):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_process_doc(app):
app.build()
- yield _test_process_doc, app
- yield _test_get_toc_for, app
- yield _test_get_toc_for_only, app
- yield _test_get_toc_for_tocdepth, app
- yield _test_get_toctree_for, app
- yield _test_get_toctree_for_collapse, app
- yield _test_get_toctree_for_maxdepth, app
- yield _test_get_toctree_for_includehidden, app
-
-
-def _test_process_doc(app):
# tocs
toctree = app.env.tocs['index']
assert_node(toctree,
@@ -99,7 +89,7 @@ def _test_process_doc(app):
@pytest.mark.sphinx('dummy', testroot='toctree-glob')
-def test_glob(app, status, warning):
+def test_glob(app):
includefiles = ['foo', 'bar/index', 'bar/bar_1', 'bar/bar_2',
'bar/bar_3', 'baz', 'qux/index']
@@ -144,7 +134,10 @@ def test_glob(app, status, warning):
assert app.env.numbered_toctrees == set()
-def _test_get_toc_for(app):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_get_toc_for(app):
+ app.build()
toctree = app.env.get_toc_for('index', app.builder)
assert_node(toctree,
@@ -167,7 +160,10 @@ def _test_get_toc_for(app):
[compact_paragraph, reference, "Indices and tables"])
-def _test_get_toc_for_only(app):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_get_toc_for_only(app):
+ app.build()
builder = StandaloneHTMLBuilder(app)
toctree = app.env.get_toc_for('index', builder)
@@ -194,7 +190,10 @@ def _test_get_toc_for_only(app):
[compact_paragraph, reference, "Indices and tables"])
-def _test_get_toc_for_tocdepth(app):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_get_toc_for_tocdepth(app):
+ app.build()
toctree = app.env.get_toc_for('tocdepth', app.builder)
assert_node(toctree,
@@ -206,7 +205,10 @@ def _test_get_toc_for_tocdepth(app):
[bullet_list, list_item, compact_paragraph, reference, "level 2"])
-def _test_get_toctree_for(app):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_get_toctree_for(app):
+ app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=False)
assert_node(toctree,
[compact_paragraph, ([caption, "Table of Contents"],
@@ -240,7 +242,10 @@ def _test_get_toctree_for(app):
assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/")
-def _test_get_toctree_for_collapse(app):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_get_toctree_for_collapse(app):
+ app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=True)
assert_node(toctree,
[compact_paragraph, ([caption, "Table of Contents"],
@@ -265,7 +270,10 @@ def _test_get_toctree_for_collapse(app):
assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/")
-def _test_get_toctree_for_maxdepth(app):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_get_toctree_for_maxdepth(app):
+ app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=False, maxdepth=3)
assert_node(toctree,
[compact_paragraph, ([caption, "Table of Contents"],
@@ -304,7 +312,10 @@ def _test_get_toctree_for_maxdepth(app):
assert_node(toctree[3][1][0][0], reference, refuri="http://python.org/")
-def _test_get_toctree_for_includehidden(app):
+@pytest.mark.sphinx('xml', testroot='toctree')
+@pytest.mark.test_params(shared_result='test_environment_toctree_basic')
+def test_get_toctree_for_includehidden(app):
+ app.build()
toctree = app.env.get_toctree_for('index', app.builder, collapse=False,
includehidden=False)
assert_node(toctree,
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 904fd7c5c..5fb3cea24 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -15,31 +15,22 @@ import os
import re
import pickle
from docutils import nodes
-from subprocess import Popen, PIPE
-from babel.messages import pofile
+from babel.messages import pofile, mofile
from six import string_types
import pytest
-from util import tempdir, rootdir, path, gen_with_app, SkipTest, \
- assert_re_search, assert_not_re_search, assert_in, assert_not_in, \
- assert_startswith, assert_node, etree_parse, strip_escseq, assert_equal
+from util import tempdir, rootdir, path, assert_re_search, \
+ assert_not_re_search, assert_startswith, assert_node, etree_parse
-root = tempdir / 'test-intl'
-
-
-def gen_with_intl_app(builder, confoverrides={}, *args, **kw):
- default_kw = {
- 'testroot': 'intl',
- 'confoverrides': {
- 'language': 'xx', 'locale_dirs': ['.'],
- 'gettext_compact': False,
- },
- }
- default_kw.update(kw)
- default_kw['confoverrides'].update(confoverrides)
- return gen_with_app(builder, *args, **default_kw)
+sphinx_intl = pytest.mark.sphinx(
+ testroot='intl',
+ confoverrides={
+ 'language': 'xx', 'locale_dirs': ['.'],
+ 'gettext_compact': False,
+ },
+)
def read_po(pathname):
@@ -47,32 +38,41 @@ def read_po(pathname):
return pofile.read_po(f)
-def setup_module():
- if not root.exists():
- (rootdir / 'roots' / 'test-intl').copytree(root)
- # Delete remnants left over after failed build
- # Compile all required catalogs into binary format (*.mo).
- for dirpath, dirs, files in os.walk(root):
- dirpath = path(dirpath)
- for f in [f for f in files if f.endswith('.po')]:
- po = dirpath / f
- mo = root / 'xx' / 'LC_MESSAGES' / (
- os.path.relpath(po[:-3], root) + '.mo')
- if not mo.parent.exists():
- mo.parent.makedirs()
- try:
- p = Popen(['msgfmt', po, '-o', mo],
- stdout=PIPE, stderr=PIPE)
- except OSError:
- raise SkipTest # most likely msgfmt was not found
- else:
- stdout, stderr = p.communicate()
- if p.returncode != 0:
- print(stdout)
- print(stderr)
- assert False, \
- 'msgfmt exited with return code %s' % p.returncode
- assert mo.isfile(), 'msgfmt failed'
+def write_mo(pathname, po):
+ with pathname.open('wb') as f:
+ return mofile.write_mo(f, po)
+
+
+@pytest.fixture
+def build_mo():
+ def builder(srcdir):
+ """
+ :param str srcdir: app.srcdir
+ """
+ srcdir = path(srcdir)
+ for dirpath, dirs, files in os.walk(srcdir):
+ dirpath = path(dirpath)
+ for f in [f for f in files if f.endswith('.po')]:
+ po = dirpath / f
+ mo = srcdir / 'xx' / 'LC_MESSAGES' / (
+ os.path.relpath(po[:-3], srcdir) + '.mo')
+ if not mo.parent.exists():
+ mo.parent.makedirs()
+
+ write_mo(mo, read_po(po))
+ return builder
+
+
+@pytest.fixture(autouse=True)
+def setup_intl(app_params, build_mo):
+ build_mo(app_params.kwargs['srcdir'])
+
+
+@pytest.fixture(autouse=True)
+def _info(app):
+ yield
+ print('# language:', app.config.language)
+ print('# locale_dirs:', app.config.locale_dirs)
def elem_gettexts(elem):
@@ -110,46 +110,72 @@ def assert_elem(elem, texts=None, refs=None, names=None):
def assert_count(expected_expr, result, count):
find_pair = (expected_expr, result)
- return assert_equal, len(re.findall(*find_pair)), count, find_pair
+ assert len(re.findall(*find_pair)) == count, find_pair
-@gen_with_intl_app('text', freshenv=True)
-def test_text_builder(app, status, warning):
- app.builder.build_all()
-
- # --- toctree
-
+@sphinx_intl
+@pytest.mark.sphinx('text')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_text_toctree(app):
+ app.build()
result = (app.outdir / 'contents.txt').text(encoding='utf-8')
- yield assert_startswith, result, u"CONTENTS\n********\n\nTABLE OF CONTENTS\n"
+ assert_startswith(result, u"CONTENTS\n********\n\nTABLE OF CONTENTS\n")
- # --- warnings in translation
+@sphinx_intl
+@pytest.mark.sphinx('text')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_text_emit_warnings(app, warning):
+ app.build()
+ # test warnings in translation
warnings = getwarning(warning)
warning_expr = u'.*/warnings.txt:4: ' \
u'WARNING: Inline literal start-string without end-string.\n'
- yield assert_re_search, warning_expr, warnings
+ assert_re_search(warning_expr, warnings)
+
+@sphinx_intl
+@pytest.mark.sphinx('text')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_text_warning_node(app):
+ app.build()
+ # test warnings in translation
result = (app.outdir / 'warnings.txt').text(encoding='utf-8')
expect = (u"I18N WITH REST WARNINGS"
u"\n***********************\n"
u"\nLINE OF >>``<reference')
- yield assert_equal, len(re.findall(expected_expr, result)), 2
+ assert len(re.findall(expected_expr, result)) == 2
expected_expr = ('reference')
- yield assert_equal, len(re.findall(expected_expr, result)), 0
+ assert len(re.findall(expected_expr, result)) == 0
expected_expr = ('I18N WITH '
'REFS INCONSISTENCY')
- yield assert_equal, len(re.findall(expected_expr, result)), 1
+ assert len(re.findall(expected_expr, result)) == 1
+
+@sphinx_intl
+@pytest.mark.sphinx('html')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_html_index_entries(app):
+ app.build()
# --- index entries: regression test for #976
-
result = (app.outdir / 'genindex.html').text(encoding='utf-8')
def wrap(tag, keyword):
@@ -440,10 +581,15 @@ def test_html_builder(app, status, warning):
wrap('a', 'BUILTIN'),
]
for expr in expected_exprs:
- yield assert_re_search, expr, result, re.M
+ assert_re_search(expr, result, re.M)
+
+@sphinx_intl
+@pytest.mark.sphinx('html')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_html_versionchanges(app):
+ app.build()
# --- versionchanges
-
result = (app.outdir / 'versionchange.html').text(encoding='utf-8')
def get_content(result, name):
@@ -459,88 +605,108 @@ def test_html_builder(app, status, warning):
u"""THIS IS THE FIRST PARAGRAPH OF DEPRECATED.
\n"""
u"""THIS IS THE SECOND PARAGRAPH OF DEPRECATED.
\n""")
matched_content = get_content(result, "deprecated")
- yield assert_equal, expect1, matched_content
+ assert expect1 == matched_content
expect2 = (
u"""New in version 1.0: """
u"""THIS IS THE FIRST PARAGRAPH OF VERSIONADDED.
\n""")
matched_content = get_content(result, "versionadded")
- yield assert_equal, expect2, matched_content
+ assert expect2 == matched_content
expect3 = (
u"""Changed in version 1.0: """
u"""THIS IS THE FIRST PARAGRAPH OF VERSIONCHANGED.
\n""")
matched_content = get_content(result, "versionchanged")
- yield assert_equal, expect3, matched_content
+ assert expect3 == matched_content
+
+@sphinx_intl
+@pytest.mark.sphinx('html')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_html_docfields(app):
+ app.build()
# --- docfields
-
# expect no error by build
(app.outdir / 'docfields.html').text(encoding='utf-8')
+
+@sphinx_intl
+@pytest.mark.sphinx('html')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_html_template(app):
+ app.build()
# --- gettext template
-
result = (app.outdir / 'index.html').text(encoding='utf-8')
- yield assert_in, "WELCOME", result
- yield assert_in, "SPHINX 2013.120", result
+ assert "WELCOME" in result
+ assert "SPHINX 2013.120" in result
+
+@sphinx_intl
+@pytest.mark.sphinx('html')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_html_rebuild_mo(app):
+ app.build()
# --- rebuild by .mo mtime
-
app.builder.build_update()
updated = app.env.update(app.config, app.srcdir, app.doctreedir, app)
- yield assert_equal, len(updated), 0
+ assert len(updated) == 0
(app.srcdir / 'xx' / 'LC_MESSAGES' / 'bom.mo').utime(None)
updated = app.env.update(app.config, app.srcdir, app.doctreedir, app)
- yield assert_equal, len(updated), 1
+ assert len(updated) == 1
-@gen_with_intl_app('xml', freshenv=True)
-def test_xml_builder(app, status, warning):
- app.builder.build_all()
-
+@sphinx_intl
+@pytest.mark.sphinx('xml')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_xml_footnotes(app, warning):
+ app.build()
# --- footnotes: regression test for fix #955, #1176
-
et = etree_parse(app.outdir / 'footnote.xml')
secs = et.findall('section')
para0 = secs[0].findall('paragraph')
- yield (assert_elem,
- para0[0],
- ['I18N WITH FOOTNOTE', 'INCLUDE THIS CONTENTS',
- '2', '[ref]', '1', '100', '.'],
- ['i18n-with-footnote', 'ref'])
+ assert_elem(
+ para0[0],
+ ['I18N WITH FOOTNOTE', 'INCLUDE THIS CONTENTS',
+ '2', '[ref]', '1', '100', '.'],
+ ['i18n-with-footnote', 'ref'])
footnote0 = secs[0].findall('footnote')
- yield (assert_elem,
- footnote0[0],
- ['1', 'THIS IS A AUTO NUMBERED FOOTNOTE.'],
- None,
- ['1'])
- yield (assert_elem,
- footnote0[1],
- ['100', 'THIS IS A NUMBERED FOOTNOTE.'],
- None,
- ['100'])
- yield (assert_elem,
- footnote0[2],
- ['2', 'THIS IS A AUTO NUMBERED NAMED FOOTNOTE.'],
- None,
- ['named'])
+ assert_elem(
+ footnote0[0],
+ ['1', 'THIS IS A AUTO NUMBERED FOOTNOTE.'],
+ None,
+ ['1'])
+ assert_elem(
+ footnote0[1],
+ ['100', 'THIS IS A NUMBERED FOOTNOTE.'],
+ None,
+ ['100'])
+ assert_elem(
+ footnote0[2],
+ ['2', 'THIS IS A AUTO NUMBERED NAMED FOOTNOTE.'],
+ None,
+ ['named'])
citation0 = secs[0].findall('citation')
- yield (assert_elem,
- citation0[0],
- ['ref', 'THIS IS A NAMED FOOTNOTE.'],
- None,
- ['ref'])
+ assert_elem(
+ citation0[0],
+ ['ref', 'THIS IS A NAMED FOOTNOTE.'],
+ None,
+ ['ref'])
warnings = getwarning(warning)
warning_expr = u'.*/footnote.xml:\\d*: SEVERE: Duplicate ID: ".*".\n'
- yield assert_not_re_search, warning_expr, warnings
+ assert_not_re_search(warning_expr, warnings)
+
+@sphinx_intl
+@pytest.mark.sphinx('xml')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_xml_footnote_backlinks(app):
+ app.build()
# --- footnote backlinks: i18n test for #1058
-
et = etree_parse(app.outdir / 'footnote.xml')
secs = et.findall('section')
@@ -553,207 +719,234 @@ def test_xml_builder(app, status, warning):
for footnote in footnote0:
ids = footnote.attrib.get('ids')
backrefs = footnote.attrib.get('backrefs')
- yield assert_equal, refid2id[ids], backrefs
+ assert refid2id[ids] == backrefs
+
+@sphinx_intl
+@pytest.mark.sphinx('xml')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_xml_refs_in_python_domain(app):
+ app.build()
# --- refs in the Python domain
-
et = etree_parse(app.outdir / 'refs_python_domain.xml')
secs = et.findall('section')
# regression test for fix #1363
para0 = secs[0].findall('paragraph')
- yield (assert_elem,
- para0[0],
- ['SEE THIS DECORATOR:', 'sensitive_variables()', '.'],
- ['sensitive.sensitive_variables'])
+ assert_elem(
+ para0[0],
+ ['SEE THIS DECORATOR:', 'sensitive_variables()', '.'],
+ ['sensitive.sensitive_variables'])
+
+@sphinx_intl
+@pytest.mark.sphinx('xml')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_xml_keep_external_links(app):
+ app.build()
# --- keep external links: regression test for #1044
-
et = etree_parse(app.outdir / 'external_links.xml')
secs = et.findall('section')
para0 = secs[0].findall('paragraph')
# external link check
- yield (assert_elem,
- para0[0],
- ['EXTERNAL LINK TO', 'Python', '.'],
- ['http://python.org/index.html'])
+ assert_elem(
+ para0[0],
+ ['EXTERNAL LINK TO', 'Python', '.'],
+ ['http://python.org/index.html'])
# internal link check
- yield (assert_elem,
- para0[1],
- ['EXTERNAL LINKS', 'IS INTERNAL LINK.'],
- ['i18n-with-external-links'])
+ assert_elem(
+ para0[1],
+ ['EXTERNAL LINKS', 'IS INTERNAL LINK.'],
+ ['i18n-with-external-links'])
# inline link check
- yield (assert_elem,
- para0[2],
- ['INLINE LINK BY', 'THE SPHINX SITE', '.'],
- ['http://sphinx-doc.org'])
+ assert_elem(
+ para0[2],
+ ['INLINE LINK BY', 'THE SPHINX SITE', '.'],
+ ['http://sphinx-doc.org'])
# unnamed link check
- yield (assert_elem,
- para0[3],
- ['UNNAMED', 'LINK', '.'],
- ['http://google.com'])
+ assert_elem(
+ para0[3],
+ ['UNNAMED', 'LINK', '.'],
+ ['http://google.com'])
# link target swapped translation
para1 = secs[1].findall('paragraph')
- yield (assert_elem,
- para1[0],
- ['LINK TO', 'external2', 'AND', 'external1', '.'],
- ['http://example.com/external2',
- 'http://example.com/external1'])
- yield (assert_elem,
+ assert_elem(
+ para1[0],
+ ['LINK TO', 'external2', 'AND', 'external1', '.'],
+ ['http://example.com/external2',
+ 'http://example.com/external1'])
+ assert_elem(
para1[1],
['LINK TO', 'THE PYTHON SITE', 'AND', 'THE SPHINX SITE', '.'],
['http://python.org', 'http://sphinx-doc.org'])
# multiple references in the same line
para2 = secs[2].findall('paragraph')
- yield (assert_elem,
- para2[0],
- ['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',',
- 'THE SPHINX SITE', ',', 'UNNAMED', 'AND',
- 'THE PYTHON SITE', '.'],
- ['i18n-with-external-links', 'http://python.org/index.html',
- 'http://sphinx-doc.org', 'http://google.com',
- 'http://python.org'])
+ assert_elem(
+ para2[0],
+ ['LINK TO', 'EXTERNAL LINKS', ',', 'Python', ',',
+ 'THE SPHINX SITE', ',', 'UNNAMED', 'AND',
+ 'THE PYTHON SITE', '.'],
+ ['i18n-with-external-links', 'http://python.org/index.html',
+ 'http://sphinx-doc.org', 'http://google.com',
+ 'http://python.org'])
+
+@sphinx_intl
+@pytest.mark.sphinx('xml')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_xml_role_xref(app):
+ app.build()
# --- role xref: regression test for #1090, #1193
-
et = etree_parse(app.outdir / 'role_xref.xml')
sec1, sec2 = et.findall('section')
para1, = sec1.findall('paragraph')
- yield (assert_elem,
- para1,
- ['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',',
- 'SOME NEW TERM', '.'],
- ['i18n-role-xref', 'contents',
- 'glossary_terms#term-some-term'])
+ assert_elem(
+ para1,
+ ['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',',
+ 'SOME NEW TERM', '.'],
+ ['i18n-role-xref', 'contents',
+ 'glossary_terms#term-some-term'])
para2 = sec2.findall('paragraph')
- yield (assert_elem,
- para2[0],
- ['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM', '.'],
- ['glossary_terms#term-some-other-term',
- 'glossary_terms#term-some-term'])
- yield(assert_elem,
- para2[1],
- ['LINK TO', 'SAME TYPE LINKS', 'AND',
- "I18N ROCK'N ROLE XREF", '.'],
- ['same-type-links', 'i18n-role-xref'])
- yield (assert_elem,
- para2[2],
- ['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'],
- ['glossary_terms', 'contents'])
- yield (assert_elem,
- para2[3],
- ['LINK TO', '--module', 'AND', '-m', '.'],
- ['cmdoption-module', 'cmdoption-m'])
- yield (assert_elem,
- para2[4],
- ['LINK TO', 'env2', 'AND', 'env1', '.'],
- ['envvar-env2', 'envvar-env1'])
- yield (assert_elem,
- para2[5],
- ['LINK TO', 'token2', 'AND', 'token1', '.'],
- []) # TODO: how do I link token role to productionlist?
- yield (assert_elem,
- para2[6],
- ['LINK TO', 'same-type-links', 'AND', "i18n-role-xref", '.'],
- ['same-type-links', 'i18n-role-xref'])
+ assert_elem(
+ para2[0],
+ ['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM', '.'],
+ ['glossary_terms#term-some-other-term',
+ 'glossary_terms#term-some-term'])
+ assert_elem(
+ para2[1],
+ ['LINK TO', 'SAME TYPE LINKS', 'AND',
+ "I18N ROCK'N ROLE XREF", '.'],
+ ['same-type-links', 'i18n-role-xref'])
+ assert_elem(
+ para2[2],
+ ['LINK TO', 'I18N WITH GLOSSARY TERMS', 'AND', 'CONTENTS', '.'],
+ ['glossary_terms', 'contents'])
+ assert_elem(
+ para2[3],
+ ['LINK TO', '--module', 'AND', '-m', '.'],
+ ['cmdoption-module', 'cmdoption-m'])
+ assert_elem(
+ para2[4],
+ ['LINK TO', 'env2', 'AND', 'env1', '.'],
+ ['envvar-env2', 'envvar-env1'])
+ assert_elem(
+ para2[5],
+ ['LINK TO', 'token2', 'AND', 'token1', '.'],
+ []) # TODO: how do I link token role to productionlist?
+ assert_elem(
+ para2[6],
+ ['LINK TO', 'same-type-links', 'AND', "i18n-role-xref", '.'],
+ ['same-type-links', 'i18n-role-xref'])
+
+@sphinx_intl
+@pytest.mark.sphinx('xml')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_xml_warnings(app, warning):
+ app.build()
# warnings
warnings = getwarning(warning)
- yield assert_not_in, 'term not in glossary', warnings
- yield assert_not_in, 'undefined label', warnings
- yield assert_not_in, 'unknown document', warnings
+ assert 'term not in glossary' not in warnings
+ assert 'undefined label' not in warnings
+ assert 'unknown document' not in warnings
+
+@sphinx_intl
+@pytest.mark.sphinx('xml')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_xml_label_targets(app):
+ app.build()
# --- label targets: regression test for #1193, #1265
-
et = etree_parse(app.outdir / 'label_target.xml')
secs = et.findall('section')
para0 = secs[0].findall('paragraph')
- yield (assert_elem,
- para0[0],
- ['X SECTION AND LABEL', 'POINT TO', 'implicit-target', 'AND',
- 'X SECTION AND LABEL', 'POINT TO', 'section-and-label', '.'],
- ['implicit-target', 'section-and-label'])
+ assert_elem(
+ para0[0],
+ ['X SECTION AND LABEL', 'POINT TO', 'implicit-target', 'AND',
+ 'X SECTION AND LABEL', 'POINT TO', 'section-and-label', '.'],
+ ['implicit-target', 'section-and-label'])
para1 = secs[1].findall('paragraph')
- yield (assert_elem,
- para1[0],
- ['X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'AND',
- 'X EXPLICIT-TARGET', 'POINT TO DUPLICATED ID LIKE', 'id1',
- '.'],
- ['explicit-target', 'id1'])
+ assert_elem(
+ para1[0],
+ ['X EXPLICIT-TARGET', 'POINT TO', 'explicit-target', 'AND',
+ 'X EXPLICIT-TARGET', 'POINT TO DUPLICATED ID LIKE', 'id1',
+ '.'],
+ ['explicit-target', 'id1'])
para2 = secs[2].findall('paragraph')
- yield (assert_elem,
- para2[0],
- ['X IMPLICIT SECTION NAME', 'POINT TO',
- 'implicit-section-name', '.'],
- ['implicit-section-name'])
+ assert_elem(
+ para2[0],
+ ['X IMPLICIT SECTION NAME', 'POINT TO',
+ 'implicit-section-name', '.'],
+ ['implicit-section-name'])
sec2 = secs[2].findall('section')
para2_0 = sec2[0].findall('paragraph')
- yield (assert_elem,
- para2_0[0],
- ['`X DUPLICATED SUB SECTION`_', 'IS BROKEN LINK.'],
- [])
+ assert_elem(
+ para2_0[0],
+ ['`X DUPLICATED SUB SECTION`_', 'IS BROKEN LINK.'],
+ [])
para3 = secs[3].findall('paragraph')
- yield (assert_elem,
- para3[0],
- ['X', 'bridge label',
- 'IS NOT TRANSLATABLE BUT LINKED TO TRANSLATED ' +
- 'SECTION TITLE.'],
- ['label-bridged-target-section'])
- yield (assert_elem,
- para3[1],
- ['X', 'bridge label', 'POINT TO',
- 'LABEL BRIDGED TARGET SECTION', 'AND', 'bridge label2',
- 'POINT TO', 'SECTION AND LABEL', '. THE SECOND APPEARED',
- 'bridge label2', 'POINT TO CORRECT TARGET.'],
- ['label-bridged-target-section',
- 'section-and-label',
- 'section-and-label'])
+ assert_elem(
+ para3[0],
+ ['X', 'bridge label',
+ 'IS NOT TRANSLATABLE BUT LINKED TO TRANSLATED ' +
+ 'SECTION TITLE.'],
+ ['label-bridged-target-section'])
+ assert_elem(
+ para3[1],
+ ['X', 'bridge label', 'POINT TO',
+ 'LABEL BRIDGED TARGET SECTION', 'AND', 'bridge label2',
+ 'POINT TO', 'SECTION AND LABEL', '. THE SECOND APPEARED',
+ 'bridge label2', 'POINT TO CORRECT TARGET.'],
+ ['label-bridged-target-section',
+ 'section-and-label',
+ 'section-and-label'])
-@gen_with_intl_app('html', freshenv=True)
-def test_additional_targets_should_not_be_translated(app, status, warning):
- app.builder.build_all()
-
+@sphinx_intl
+@pytest.mark.sphinx('html')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_additional_targets_should_not_be_translated(app):
+ app.build()
# [literalblock.txt]
result = (app.outdir / 'literalblock.html').text(encoding='utf-8')
# title should be translated
expected_expr = 'CODE-BLOCKS'
- yield assert_count(expected_expr, result, 2)
+ assert_count(expected_expr, result, 2)
# ruby code block should not be translated but be highlighted
expected_expr = """'result'"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# C code block without lang should not be translated and *ruby* highlighted
expected_expr = """#include <stdlib.h>"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# C code block with lang should not be translated but be *C* highlighted
expected_expr = ("""#include """
"""<stdio.h>""")
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# doctest block should not be translated but be highlighted
expected_expr = (
""">>> """
"""import sys """
"""# sys importing""")
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# [raw.txt]
@@ -761,7 +954,7 @@ def test_additional_targets_should_not_be_translated(app, status, warning):
# raw block should not be translated
expected_expr = """"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# [figure.txt]
@@ -769,52 +962,57 @@ def test_additional_targets_should_not_be_translated(app, status, warning):
# alt and src for image block should not be translated
expected_expr = """
"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# alt and src for figure block should not be translated
expected_expr = """
"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
-@gen_with_intl_app('html', freshenv=True,
- confoverrides={
- 'gettext_additional_targets': [
- 'index',
- 'literal-block',
- 'doctest-block',
- 'raw',
- 'image',
- ],
- })
-def test_additional_targets_should_be_translated(app, status, warning):
- app.builder.build_all()
-
+@sphinx_intl
+@pytest.mark.sphinx(
+ 'html',
+ srcdir='test_additional_targets_should_be_translated',
+ confoverrides={
+ 'language': 'xx', 'locale_dirs': ['.'],
+ 'gettext_compact': False,
+ 'gettext_additional_targets': [
+ 'index',
+ 'literal-block',
+ 'doctest-block',
+ 'raw',
+ 'image',
+ ],
+ }
+)
+def test_additional_targets_should_be_translated(app):
+ app.build()
# [literalblock.txt]
result = (app.outdir / 'literalblock.html').text(encoding='utf-8')
# title should be translated
expected_expr = 'CODE-BLOCKS'
- yield assert_count(expected_expr, result, 2)
+ assert_count(expected_expr, result, 2)
# ruby code block should be translated and be highlighted
expected_expr = """'RESULT'"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# C code block without lang should be translated and *ruby* highlighted
expected_expr = """#include <STDLIB.H>"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# C code block with lang should be translated and be *C* highlighted
expected_expr = ("""#include """
"""<STDIO.H>""")
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# doctest block should not be translated but be highlighted
expected_expr = (
""">>> """
"""import sys """
"""# SYS IMPORTING""")
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# [raw.txt]
@@ -822,7 +1020,7 @@ def test_additional_targets_should_be_translated(app, status, warning):
# raw block should be translated
expected_expr = """"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# [figure.txt]
@@ -830,26 +1028,31 @@ def test_additional_targets_should_be_translated(app, status, warning):
# alt and src for image block should be translated
expected_expr = """
"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
# alt and src for figure block should be translated
expected_expr = """
"""
- yield assert_count(expected_expr, result, 1)
+ assert_count(expected_expr, result, 1)
-@gen_with_intl_app('text', freshenv=True)
-def test_references(app, status, warning):
+@sphinx_intl
+@pytest.mark.sphinx('text')
+@pytest.mark.test_params(shared_result='test_intl_basic')
+def test_text_references(app, warning):
app.builder.build_specific([app.srcdir / 'refs.txt'])
warnings = warning.getvalue().replace(os.sep, '/')
warning_expr = u'refs.txt:\\d+: ERROR: Unknown target name:'
- yield assert_count(warning_expr, warnings, 0)
+ assert_count(warning_expr, warnings, 0)
-@pytest.mark.sphinx('dummy', testroot='image-glob', confoverrides={'language': 'xx'})
-def test_image_glob_intl(app, status, warning):
- app.builder.build_all()
-
+@pytest.mark.sphinx(
+ 'dummy', testroot='image-glob',
+ srcdir='test_intl_image_glob',
+ confoverrides={'language': 'xx'}
+)
+def test_image_glob_intl(app):
+ app.build()
# index.rst
doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes())
@@ -887,12 +1090,16 @@ def test_image_glob_intl(app, status, warning):
'image/svg+xml': 'subdir/svgimg.xx.svg'})
-@pytest.mark.sphinx('dummy', testroot='image-glob',
- confoverrides={'language': 'xx',
- 'figure_language_filename': u'{root}{ext}.{language}'})
-def test_image_glob_intl_using_figure_language_filename(app, status, warning):
- app.builder.build_all()
-
+@pytest.mark.sphinx(
+ 'dummy', testroot='image-glob',
+ srcdir='test_intl_image_glob',
+ confoverrides={
+ 'language': 'xx',
+ 'figure_language_filename': u'{root}{ext}.{language}',
+ }
+)
+def test_image_glob_intl_using_figure_language_filename(app):
+ app.build()
# index.rst
doctree = pickle.loads((app.doctreedir / 'index.doctree').bytes())
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 9bfb44e25..3e190a4d6 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -13,7 +13,7 @@ import re
import pickle
from docutils import frontend, utils, nodes
-from docutils.parsers import rst
+from docutils.parsers.rst import Parser as RstParser
from sphinx import addnodes
from sphinx.util import texescape
@@ -22,31 +22,37 @@ from sphinx.writers.html import HTMLWriter, SmartyPantsHTMLTranslator
from sphinx.writers.latex import LaTeXWriter, LaTeXTranslator
import pytest
-from util import TestApp, assert_node
+from util import assert_node
-app = settings = parser = domain_context = None
-
-
-def setup_module():
- global app, settings, parser, domain_context
+@pytest.fixture
+def settings(app):
texescape.init() # otherwise done by the latex builder
- app = TestApp()
optparser = frontend.OptionParser(
- components=(rst.Parser, HTMLWriter, LaTeXWriter))
+ components=(RstParser, HTMLWriter, LaTeXWriter))
settings = optparser.get_default_values()
settings.env = app.builder.env
settings.env.temp_data['docname'] = 'dummy'
- parser = rst.Parser()
domain_context = sphinx_domains(settings.env)
domain_context.enable()
-
-
-def teardown_module():
- app.cleanup()
+ yield settings
domain_context.disable()
+@pytest.fixture
+def parse(settings):
+ def parse_(rst):
+ document = utils.new_document(b'test data', settings)
+ document['file'] = 'dummy'
+ parser = RstParser()
+ parser.parse(rst, document)
+ for msg in document.traverse(nodes.system_message):
+ if msg['level'] == 1:
+ msg.replace_self([])
+ return document
+ return parse_
+
+
# since we're not resolving the markup afterwards, these nodes may remain
class ForgivingTranslator:
def visit_pending_xref(self, node):
@@ -64,93 +70,158 @@ class ForgivingLaTeXTranslator(LaTeXTranslator, ForgivingTranslator):
pass
-def verify_re(rst, html_expected, latex_expected):
- document = utils.new_document(b'test data', settings)
- document['file'] = 'dummy'
- parser.parse(rst, document)
- for msg in document.traverse(nodes.system_message):
- if msg['level'] == 1:
- msg.replace_self([])
-
- if html_expected:
+@pytest.fixture
+def verify_re_html(app, parse):
+ def verify(rst, html_expected):
+ document = parse(rst)
html_translator = ForgivingHTMLTranslator(app.builder, document)
document.walkabout(html_translator)
html_translated = ''.join(html_translator.fragment).strip()
assert re.match(html_expected, html_translated), 'from ' + rst
+ return verify
- if latex_expected:
+
+@pytest.fixture
+def verify_re_latex(app, parse):
+ def verify(rst, latex_expected):
+ document = parse(rst)
latex_translator = ForgivingLaTeXTranslator(document, app.builder)
latex_translator.first_document = -1 # don't write \begin{document}
document.walkabout(latex_translator)
latex_translated = ''.join(latex_translator.body).strip()
assert re.match(latex_expected, latex_translated), 'from ' + repr(rst)
+ return verify
-def verify(rst, html_expected, latex_expected):
- if html_expected:
- html_expected = re.escape(html_expected) + '$'
- if latex_expected:
- latex_expected = re.escape(latex_expected) + '$'
- verify_re(rst, html_expected, latex_expected)
+@pytest.fixture
+def verify_re(verify_re_html, verify_re_latex):
+ def verify_re_(rst, html_expected, latex_expected):
+ if html_expected:
+ return verify_re_html(rst, html_expected)
+ if latex_expected:
+ return verify_re_latex(rst, latex_expected)
+ return verify_re_
-def test_inline():
- # correct interpretation of code with whitespace
- _html = (''
- 'code sample
')
- yield verify_re, '``code sample``', _html, r'\\sphinxcode{code sample}'
- yield verify_re, ':samp:`code sample`', _html, r'\\sphinxcode{code sample}'
-
- # interpolation of braces in samp and file roles (HTML only)
- yield (verify, ':samp:`a{b}c`',
- 'a'
- 'b'
- 'c
',
- '\\sphinxcode{a\\sphinxstyleemphasis{b}c}')
-
- # interpolation of arrows in menuselection
- yield (verify, ':menuselection:`a --> b`',
- u'',
- '\\sphinxmenuselection{a \\(\\rightarrow\\) b}')
-
- # interpolation of ampersands in guilabel/menuselection
- yield (verify, ':guilabel:`&Foo -&&- &Bar`',
- u'Foo '
- '-&- Bar
',
- r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}')
-
- # non-interpolation of dashes in option role
- yield (verify_re, ':option:`--with-option`',
- ''
- '--with-option
$',
- r'\\sphinxcode{-{-}with-option}$')
-
- # verify smarty-pants quotes
- yield verify, '"John"', '“John”
', "``John''"
- # ... but not in literal text
- yield (verify, '``"John"``',
- ''
- '"John"
',
- '\\sphinxcode{"John"}')
-
- # verify classes for inline roles
- yield (verify, ':manpage:`mp(1)`',
- 'mp(1)
',
- '\\sphinxstyleliteralemphasis{mp(1)}')
+@pytest.fixture
+def verify(verify_re_html, verify_re_latex):
+ def verify_(rst, html_expected, latex_expected):
+ if html_expected:
+ return verify_re_html(rst, re.escape(html_expected) + '$')
+ if latex_expected:
+ return verify_re_latex(rst, re.escape(latex_expected) + '$')
+ return verify_
-def test_latex_escaping():
- # correct escaping in normal mode
- yield (verify, u'Γ\\\\∞$', None,
- r'\(\Gamma\)\textbackslash{}\(\infty\)\$')
- # in verbatim code fragments
- yield (verify, u'::\n\n @Γ\\∞${}', None,
- u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
- u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
- u'\\end{sphinxVerbatim}')
- # in URIs
- yield (verify_re, u'`test `_', None,
- r'\\href{http://example.com/~me/}{test}.*')
+@pytest.fixture
+def get_verifier(verify, verify_re):
+ v = {
+ 'verify': verify,
+ 'verify_re': verify_re,
+ }
+ def get(name):
+ return v[name]
+ return get
+
+
+@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
+ (
+ # correct interpretation of code with whitespace
+ 'verify_re',
+ '``code sample``',
+ (''
+ 'code sample
'),
+ r'\\sphinxcode{code sample}',
+ ),
+ (
+ # correct interpretation of code with whitespace
+ 'verify_re',
+ ':samp:`code sample`',
+ (''
+ 'code sample
'),
+ r'\\sphinxcode{code sample}',
+ ),
+ (
+ # interpolation of braces in samp and file roles (HTML only)
+ 'verify',
+ ':samp:`a{b}c`',
+ ('a'
+ 'b'
+ 'c
'),
+ '\\sphinxcode{a\\sphinxstyleemphasis{b}c}',
+ ),
+ (
+ # interpolation of arrows in menuselection
+ 'verify',
+ ':menuselection:`a --> b`',
+ (u''),
+ '\\sphinxmenuselection{a \\(\\rightarrow\\) b}',
+ ),
+ (
+ # interpolation of ampersands in guilabel/menuselection
+ 'verify',
+ ':guilabel:`&Foo -&&- &Bar`',
+ (u'Foo '
+ '-&- Bar
'),
+ r'\sphinxmenuselection{\sphinxaccelerator{F}oo -\&- \sphinxaccelerator{B}ar}',
+ ),
+ (
+ # non-interpolation of dashes in option role
+ 'verify_re',
+ ':option:`--with-option`',
+ (''
+ '--with-option
$'),
+ r'\\sphinxcode{-{-}with-option}$',
+ ),
+ (
+ # verify smarty-pants quotes
+ 'verify',
+ '"John"',
+ '“John”
',
+ "``John''",
+ ),
+ (
+ # ... but not in literal text
+ 'verify',
+ '``"John"``',
+ (''
+ '"John"
'),
+ '\\sphinxcode{"John"}',
+ ),
+ (
+ # verify classes for inline roles
+ 'verify',
+ ':manpage:`mp(1)`',
+ 'mp(1)
',
+ '\\sphinxstyleliteralemphasis{mp(1)}',
+ ),
+ (
+ # correct escaping in normal mode
+ 'verify',
+ u'Γ\\\\∞$',
+ None,
+ r'\(\Gamma\)\textbackslash{}\(\infty\)\$',
+ ),
+ (
+ # in verbatim code fragments
+ 'verify',
+ u'::\n\n @Γ\\∞${}',
+ None,
+ (u'\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
+ u'@\\(\\Gamma\\)\\PYGZbs{}\\(\\infty\\)\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
+ u'\\end{sphinxVerbatim}'),
+ ),
+ (
+ # in URIs
+ 'verify_re',
+ u'`test `_',
+ None,
+ r'\\href{http://example.com/~me/}{test}.*',
+ ),
+])
+def test_inline(get_verifier, type, rst, html_expected, latex_expected):
+ verifier = get_verifier(type)
+ verifier(rst, html_expected, latex_expected)
@pytest.mark.sphinx('dummy', testroot='prolog')
diff --git a/tests/test_util_i18n.py b/tests/test_util_i18n.py
index a8fb8b0b3..d5a0f8f64 100644
--- a/tests/test_util_i18n.py
+++ b/tests/test_util_i18n.py
@@ -12,30 +12,27 @@ from __future__ import print_function
import os
import datetime
-from os import path
+import pytest
from babel.messages.mofile import read_mo
from sphinx.util import i18n
from sphinx.errors import SphinxError
-import pytest
-
-from util import TestApp
def test_catalog_info_for_file_and_path():
cat = i18n.CatalogInfo('path', 'domain', 'utf-8')
assert cat.po_file == 'domain.po'
assert cat.mo_file == 'domain.mo'
- assert cat.po_path == path.join('path', 'domain.po')
- assert cat.mo_path == path.join('path', 'domain.mo')
+ assert cat.po_path == os.path.join('path', 'domain.po')
+ assert cat.mo_path == os.path.join('path', 'domain.mo')
def test_catalog_info_for_sub_domain_file_and_path():
cat = i18n.CatalogInfo('path', 'sub/domain', 'utf-8')
assert cat.po_file == 'sub/domain.po'
assert cat.mo_file == 'sub/domain.mo'
- assert cat.po_path == path.join('path', 'sub/domain.po')
- assert cat.mo_path == path.join('path', 'sub/domain.mo')
+ assert cat.po_path == os.path.join('path', 'sub/domain.po')
+ assert cat.mo_path == os.path.join('path', 'sub/domain.mo')
def test_catalog_outdated(tempdir):
@@ -55,7 +52,7 @@ 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)
+ assert os.path.exists(cat.mo_path)
with open(cat.mo_path, 'rb') as f:
assert read_mo(f) is not None
@@ -189,9 +186,7 @@ def test_format_date():
assert i18n.format_date(format, date=date) == 'Feb 7, 2016'
-def test_get_filename_for_language():
- app = TestApp()
-
+def test_get_filename_for_language(app):
# language is None
app.env.config.language = None
assert app.env.config.language is None
diff --git a/tests/test_util_nodes.py b/tests/test_util_nodes.py
index ac8c3645d..7aded8a4b 100644
--- a/tests/test_util_nodes.py
+++ b/tests/test_util_nodes.py
@@ -17,6 +17,7 @@ from docutils import frontend
from sphinx.util.nodes import extract_messages, clean_astext
from sphinx.transforms import ApplySourceWorkaround
+import pytest
def _transform(doctree):
@@ -49,84 +50,63 @@ def assert_node_count(messages, node_type, expect_count):
% (node_type, node_list, count, expect_count))
-def test_extract_messages():
- text = dedent(
- """
- .. admonition:: admonition title
+@pytest.mark.parametrize(
+ 'rst,node_cls,count',
+ [
+ (
+ """
+ .. admonition:: admonition title
- admonition body
- """
- )
- yield (
- assert_node_count,
- extract_messages(_get_doctree(text)),
- nodes.title, 1,
- )
+ admonition body
+ """,
+ nodes.title, 1
+ ),
+ (
+ """
+ .. figure:: foo.jpg
- text = dedent(
- """
- .. figure:: foo.jpg
+ this is title
+ """,
+ nodes.caption, 1,
+ ),
+ (
+ """
+ .. rubric:: spam
+ """,
+ nodes.rubric, 1,
+ ),
+ (
+ """
+ | spam
+ | egg
+ """,
+ nodes.line, 2,
+ ),
+ (
+ """
+ section
+ =======
- this is title
- """
- )
- yield (
- assert_node_count,
- extract_messages(_get_doctree(text)),
- nodes.caption, 1,
- )
+ +----------------+
+ | | **Title 1** |
+ | | Message 1 |
+ +----------------+
+ """,
+ nodes.line, 2,
+ ),
+ (
+ """
+ * | **Title 1**
+ | Message 1
+ """,
+ nodes.line, 2,
- text = dedent(
- """
- .. rubric:: spam
- """
- )
- yield (
- assert_node_count,
- extract_messages(_get_doctree(text)),
- nodes.rubric, 1,
- )
-
- text = dedent(
- """
- | spam
- | egg
- """
- )
- yield (
- assert_node_count,
- extract_messages(_get_doctree(text)),
- nodes.line, 2,
- )
-
- text = dedent(
- """
- section
- =======
-
- +----------------+
- | | **Title 1** |
- | | Message 1 |
- +----------------+
- """
- )
- yield (
- assert_node_count,
- extract_messages(_get_doctree(text)),
- nodes.line, 2,
- )
-
- text = dedent(
- """
- * | **Title 1**
- | Message 1
- """
- )
- yield (
- assert_node_count,
- extract_messages(_get_doctree(text)),
- nodes.line, 2,
- )
+ ),
+ ]
+)
+def test_extract_messages(rst, node_cls, count):
+ msg = extract_messages(_get_doctree(dedent(rst)))
+ assert_node_count(msg, node_cls, count)
def test_extract_messages_without_rawsource():
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index f5b5057d7..4b9ebefcb 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -16,7 +16,7 @@ from docutils.parsers.rst.directives.html import MetaBody
from sphinx import addnodes
from sphinx.versioning import add_uids, merge_doctrees, get_ratio
-from util import TestApp
+from util import SphinxTestApp
app = original = original_uids = None
@@ -24,7 +24,7 @@ app = original = original_uids = None
def setup_module():
global app, original, original_uids
- app = TestApp(testroot='versioning')
+ app = SphinxTestApp(testroot='versioning')
app.builder.env.app = app
app.connect('doctree-resolved', on_doctree_resolved)
app.build()
diff --git a/tests/test_websupport.py b/tests/test_websupport.py
index 3592dd5ec..bded050d3 100644
--- a/tests/test_websupport.py
+++ b/tests/test_websupport.py
@@ -23,7 +23,7 @@ except ImportError:
sqlalchemy_missing = True
import pytest
-from util import rootdir, tempdir, skip_if
+from util import rootdir, tempdir
@pytest.fixture
@@ -57,13 +57,13 @@ def test_no_srcdir(support):
support.build()
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_build(support):
support.build()
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_get_document(support):
with pytest.raises(DocumentNotFoundError):
@@ -74,7 +74,7 @@ def test_get_document(support):
and contents['sidebar'] and contents['relbar']
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_comments(support):
session = Session()
@@ -123,7 +123,7 @@ def test_comments(support):
assert children[0]['text'] == 'Child test comment
\n'
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_user_delete_comments(support):
def get_comment():
@@ -152,7 +152,7 @@ def moderation_callback(comment):
called = True
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support(moderation_callback=moderation_callback)
def test_moderation(support):
session = Session()
@@ -178,7 +178,7 @@ def test_moderation(support):
assert len(comments) == 1
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_moderator_delete_comments(support):
def get_comment():
@@ -194,7 +194,7 @@ def test_moderator_delete_comments(support):
get_comment()
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_update_username(support):
support.update_username('user_two', 'new_user_two')
@@ -213,7 +213,7 @@ def test_update_username(support):
assert len(votes) == 0
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_proposals(support):
session = Session()
@@ -229,7 +229,7 @@ def test_proposals(support):
proposal=proposal)
-@skip_if(sqlalchemy_missing, 'needs sqlalchemy')
+@pytest.mark.skipif(sqlalchemy_missing, reason='needs sqlalchemy')
@with_support()
def test_voting(support):
session = Session()
diff --git a/tests/util.py b/tests/util.py
index 8f2d1ff9a..a756b6006 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -207,7 +207,6 @@ def strip_escseq(text):
# #############################################
# DEPRECATED implementations
-import tempfile
from six import StringIO