Skip to content

Commit

Permalink
Organise tests into directories
Browse files Browse the repository at this point in the history
  • Loading branch information
AA-Turner committed Jan 17, 2024
1 parent 841f2bd commit 462404c
Show file tree
Hide file tree
Showing 121 changed files with 467 additions and 442 deletions.
9 changes: 7 additions & 2 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,8 @@ select = [
]

# these tests need old ``typing`` generic aliases
"tests/test_util_typing.py" = ["UP006", "UP035"]
"tests/typing_test_data.py" = ["FA100", "UP006", "UP035"]
"tests/test_util/test_util_typing.py" = ["UP006", "UP035"]
"tests/test_util/typing_test_data.py" = ["FA100", "UP006", "UP035"]

"utils/*" = [
"T201", # whitelist ``print`` for stdout messages
Expand All @@ -520,3 +520,8 @@ select = [

[lint.flake8-quotes]
inline-quotes = "single"

[lint.isort]
forced-separate = [
"tests",
]
4 changes: 2 additions & 2 deletions sphinx/testing/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@

DEFAULT_ENABLED_MARKERS = [
(
'sphinx(builder, testroot=None, freshenv=False, confoverrides=None, tags=None,'
' docutilsconf=None, parallel=0): arguments to initialize the sphinx test application.'
'sphinx(builder, testroot=None, freshenv=False, confoverrides=None, tags=None, '
'docutils_conf=None, parallel=0): arguments to initialize the sphinx test application.'
),
'test_params(shared_result=...): test parameters.',
]
Expand Down
47 changes: 21 additions & 26 deletions sphinx/testing/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@
from xml.etree import ElementTree

from docutils import nodes
from docutils.parsers.rst import directives, roles

from sphinx import application, locale
from sphinx.pycode import ModuleAnalyzer
from sphinx import application

if TYPE_CHECKING:
from io import StringIO
Expand Down Expand Up @@ -89,14 +87,14 @@ def __init__(
status: IO | None = None,
warning: IO | None = None,
tags: list[str] | None = None,
docutilsconf: str | None = None,
docutils_conf: str | None = None,
parallel: int = 0,
) -> None:
assert srcdir is not None

self.docutils_conf_path = srcdir / 'docutils.conf'
if docutilsconf is not None:
self.docutils_conf_path.write_text(docutilsconf, encoding='utf8')
if docutils_conf is not None:
self.docutils_conf_path.write_text(docutils_conf, encoding='utf8')

if builddir is None:
builddir = srcdir / '_build'
Expand All @@ -108,35 +106,32 @@ def __init__(
doctreedir.mkdir(parents=True, exist_ok=True)
if confoverrides is None:
confoverrides = {}
warningiserror = False

self._saved_path = sys.path.copy()
self._saved_directives = directives._directives.copy() # type: ignore[attr-defined]
self._saved_roles = roles._roles.copy() # type: ignore[attr-defined]

self._saved_nodeclasses = {v for v in dir(nodes.GenericNodeVisitor)
if v.startswith('visit_')}

try:
super().__init__(srcdir, confdir, outdir, doctreedir,
buildername, confoverrides, status, warning,
freshenv, warningiserror, tags, parallel=parallel)
super().__init__(
srcdir, confdir, outdir, doctreedir,
buildername, confoverrides, status, warning, freshenv,
warningiserror=False, tags=tags, parallel=parallel,
)
except Exception:
self.cleanup()
raise

def cleanup(self, doctrees: bool = False) -> None:
ModuleAnalyzer.cache.clear()
locale.translators.clear()
sys.path[:] = self._saved_path
sys.modules.pop('autodoc_fodder', None)
directives._directives = self._saved_directives # type: ignore[attr-defined]
roles._roles = self._saved_roles # type: ignore[attr-defined]
for method in dir(nodes.GenericNodeVisitor):
if method.startswith('visit_') and \
method not in self._saved_nodeclasses:
delattr(nodes.GenericNodeVisitor, 'visit_' + method[6:])
delattr(nodes.GenericNodeVisitor, 'depart_' + method[6:])
# ModuleAnalyzer.cache.clear()
# locale.translators.clear()
# sys.path[:] = self._saved_path
# sys.modules.pop('autodoc_fodder', None)
# directives._directives.clear() # type: ignore[attr-defined]
# roles._roles.clear() # type: ignore[attr-defined]
# for node in additional_nodes:
# delattr(nodes.GenericNodeVisitor, f'visit_{node.__name__}')
# delattr(nodes.GenericNodeVisitor, f'depart_{node.__name__}')
# delattr(nodes.SparseNodeVisitor, f'visit_{node.__name__}')
# delattr(nodes.SparseNodeVisitor, f'depart_{node.__name__}')
# additional_nodes.clear()
with contextlib.suppress(FileNotFoundError):
os.remove(self.docutils_conf_path)

Expand Down
29 changes: 28 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import os
import sys
from pathlib import Path

import docutils
import pytest
from docutils import nodes
from docutils.parsers.rst import directives, roles

import sphinx
import sphinx.locale
import sphinx.pycode
from sphinx.util.docutils import additional_nodes


def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'):
Expand All @@ -30,11 +35,33 @@ def _init_console(locale_dir=sphinx.locale._LOCALE_DIR, catalog='sphinx'):

@pytest.fixture(scope='session')
def rootdir():
return Path(__file__).parent.absolute() / 'roots'
return Path(__file__).parent.resolve() / 'roots'


def pytest_report_header(config):
header = f"libraries: Sphinx-{sphinx.__display_version__}, docutils-{docutils.__version__}"
if hasattr(config, '_tmp_path_factory'):
header += f"\nbase tmp_path: {config._tmp_path_factory.getbasetemp()}"
return header


@pytest.fixture(autouse=True)
def _cleanup_docutils():
saved_path = sys.path
yield # run the test
sys.path[:] = saved_path

# clean up Docutils global state
directives._directives.clear() # type: ignore[attr-defined]
roles._roles.clear() # type: ignore[attr-defined]
for node in additional_nodes:
delattr(nodes.GenericNodeVisitor, f'visit_{node.__name__}')
delattr(nodes.GenericNodeVisitor, f'depart_{node.__name__}')
delattr(nodes.SparseNodeVisitor, f'visit_{node.__name__}')
delattr(nodes.SparseNodeVisitor, f'depart_{node.__name__}')
additional_nodes.clear()

# clean up Sphinx global state
sphinx.locale.translators.clear()
sphinx.pycode.ModuleAnalyzer.cache.clear()
sys.modules.pop('autodoc_fodder', None)
2 changes: 1 addition & 1 deletion tests/roots/test-ext-doctest-skipif/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
exclude_patterns = ['_build']

doctest_global_setup = '''
from tests.test_ext_doctest import record
from tests.test_extensions.test_ext_doctest import record
record('doctest_global_setup', 'body', True)
'''
Expand Down
2 changes: 1 addition & 1 deletion tests/roots/test-ext-doctest/doctest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Special directives

.. testcleanup:: *

from tests import test_ext_doctest
from tests.test_extensions import test_ext_doctest
test_ext_doctest.cleanup_call()

non-ASCII result
Expand Down
Empty file added tests/test_builders/__init__.py
Empty file.
13 changes: 7 additions & 6 deletions tests/test_build.py → tests/test_builders/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import os
import shutil
from contextlib import contextmanager
from pathlib import Path
from unittest import mock

import pytest
Expand All @@ -12,6 +11,8 @@
from sphinx.cmd.build import build_main
from sphinx.errors import SphinxError

from tests.utils import TESTS_ROOT


def request_session_head(url, **kwargs):
response = mock.Mock()
Expand Down Expand Up @@ -63,12 +64,12 @@ def test_root_doc_not_found(tmp_path, make_app):

app = make_app('dummy', srcdir=tmp_path)
with pytest.raises(SphinxError):
app.builder.build_all() # no index.rst
app.build(force_all=True) # no index.rst


@pytest.mark.sphinx(buildername='text', testroot='circular')
def test_circular_toctree(app, status, warning):
app.builder.build_all()
app.build(force_all=True)
warnings = warning.getvalue()
assert (
'circular toctree references detected, ignoring: '
Expand All @@ -80,7 +81,7 @@ def test_circular_toctree(app, status, warning):

@pytest.mark.sphinx(buildername='text', testroot='numbered-circular')
def test_numbered_circular_toctree(app, status, warning):
app.builder.build_all()
app.build(force_all=True)
warnings = warning.getvalue()
assert (
'circular toctree references detected, ignoring: '
Expand All @@ -92,7 +93,7 @@ def test_numbered_circular_toctree(app, status, warning):

@pytest.mark.sphinx(buildername='dummy', testroot='images')
def test_image_glob(app, status, warning):
app.builder.build_all()
app.build(force_all=True)

# index.rst
doctree = app.env.get_doctree('index')
Expand Down Expand Up @@ -155,7 +156,7 @@ def force_colors():
def test_log_no_ansi_colors(tmp_path):
with force_colors():
wfile = tmp_path / 'warnings.txt'
srcdir = Path(__file__).parent / 'roots/test-nitpicky-warnings'
srcdir = TESTS_ROOT / 'roots' / 'test-nitpicky-warnings'
argv = list(map(str, ['-b', 'html', srcdir, tmp_path, '-n', '-w', wfile]))
retcode = build_main(argv)
assert retcode == 0
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __iter__(self):

@pytest.mark.sphinx('epub', testroot='basic')
def test_build_epub(app):
app.builder.build_all()
app.build(force_all=True)
assert (app.outdir / 'mimetype').read_text(encoding='utf8') == 'application/epub+zip'
assert (app.outdir / 'META-INF' / 'container.xml').exists()

Expand Down Expand Up @@ -278,7 +278,7 @@ def navinfo(elem):
@pytest.mark.sphinx('epub', testroot='basic')
def test_epub_writing_mode(app):
# horizontal (default)
app.builder.build_all()
app.build(force_all=True)

# horizontal / page-progression-direction
opf = EPUBElementTree.fromstring((app.outdir / 'content.opf').read_text(encoding='utf8'))
Expand Down Expand Up @@ -324,7 +324,7 @@ def test_epub_anchor_id(app):

@pytest.mark.sphinx('epub', testroot='html_assets')
def test_epub_assets(app):
app.builder.build_all()
app.build(force_all=True)

# epub_sytlesheets (same as html_css_files)
content = (app.outdir / 'index.xhtml').read_text(encoding='utf8')
Expand All @@ -337,7 +337,7 @@ def test_epub_assets(app):
@pytest.mark.sphinx('epub', testroot='html_assets',
confoverrides={'epub_css_files': ['css/epub.css']})
def test_epub_css_files(app):
app.builder.build_all()
app.build(force_all=True)

# epub_css_files
content = (app.outdir / 'index.xhtml').read_text(encoding='utf8')
Expand Down Expand Up @@ -368,7 +368,7 @@ def test_html_download_role(app, status, warning):

@pytest.mark.sphinx('epub', testroot='toctree-duplicated')
def test_duplicated_toctree_entry(app, status, warning):
app.builder.build_all()
app.build(force_all=True)
assert 'WARNING: duplicated ToC entry found: foo.xhtml' in warning.getvalue()


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test_Catalog_duplicated_message():
@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()
app.build(force_all=True)

# Do messages end up in the correct location?
# top-level documents end up in a message catalog
Expand All @@ -62,7 +62,7 @@ def test_build_gettext(app):

@pytest.mark.sphinx('gettext', srcdir='root-gettext')
def test_msgfmt(app):
app.builder.build_all()
app.build(force_all=True)

(app.outdir / 'en' / 'LC_MESSAGES').mkdir(parents=True, exist_ok=True)
with chdir(app.outdir):
Expand Down Expand Up @@ -102,7 +102,7 @@ def test_msgfmt(app):
confoverrides={'gettext_compact': False})
def test_gettext_index_entries(app):
# regression test for #976
app.builder.build(['index_entries'])
app.build(filenames=[app.srcdir / 'index_entries.txt'])

pot = (app.outdir / 'index_entries.pot').read_text(encoding='utf8')
msg_ids = list(filter(None, map(msgid_getter, pot.splitlines())))
Expand Down Expand Up @@ -131,7 +131,7 @@ def test_gettext_index_entries(app):
def test_gettext_disable_index_entries(app):
# regression test for #976
app.env._pickled_doctree_cache.clear() # clear cache
app.builder.build(['index_entries'])
app.build(filenames=[app.srcdir / 'index_entries.txt'])

pot = (app.outdir / 'index_entries.pot').read_text(encoding='utf8')
msg_ids = list(filter(None, map(msgid_getter, pot.splitlines())))
Expand All @@ -147,7 +147,7 @@ def test_gettext_disable_index_entries(app):

@pytest.mark.sphinx('gettext', testroot='intl', srcdir='gettext')
def test_gettext_template(app):
app.builder.build_all()
app.build(force_all=True)

assert (app.outdir / 'sphinx.pot').is_file()

Expand All @@ -158,7 +158,7 @@ def test_gettext_template(app):

@pytest.mark.sphinx('gettext', testroot='gettext-template')
def test_gettext_template_msgid_order_in_sphinxpot(app):
app.builder.build_all()
app.build(force_all=True)
assert (app.outdir / 'sphinx.pot').is_file()

result = (app.outdir / 'sphinx.pot').read_text(encoding='utf8')
Expand All @@ -175,7 +175,7 @@ def test_gettext_template_msgid_order_in_sphinxpot(app):
'gettext', srcdir='root-gettext',
confoverrides={'gettext_compact': 'documentation'})
def test_build_single_pot(app):
app.builder.build_all()
app.build(force_all=True)

assert (app.outdir / 'documentation.pot').is_file()

Expand All @@ -196,7 +196,7 @@ def test_build_single_pot(app):
confoverrides={'gettext_compact': False,
'gettext_additional_targets': ['image']})
def test_gettext_prolog_epilog_substitution(app):
app.builder.build_all()
app.build(force_all=True)

assert (app.outdir / 'prolog_epilog_substitution.pot').is_file()
pot = (app.outdir / 'prolog_epilog_substitution.pot').read_text(encoding='utf8')
Expand All @@ -223,7 +223,7 @@ def test_gettext_prolog_epilog_substitution(app):
'gettext_additional_targets': ['image']})
def test_gettext_prolog_epilog_substitution_excluded(app):
# regression test for #9428
app.builder.build_all()
app.build(force_all=True)

assert (app.outdir / 'prolog_epilog_substitution_excluded.pot').is_file()
pot = (app.outdir / 'prolog_epilog_substitution_excluded.pot').read_text(encoding='utf8')
Expand Down
Loading

0 comments on commit 462404c

Please sign in to comment.