2008-08-23 10:04:45 -05:00
|
|
|
"""Test the Sphinx class."""
|
2024-03-16 12:29:44 -05:00
|
|
|
from __future__ import annotations
|
2019-02-19 21:32:17 -06:00
|
|
|
|
2022-06-16 13:50:01 -05:00
|
|
|
import shutil
|
|
|
|
import sys
|
|
|
|
from io import StringIO
|
|
|
|
from pathlib import Path
|
2024-03-16 12:29:44 -05:00
|
|
|
from typing import TYPE_CHECKING
|
2019-02-19 21:32:17 -06:00
|
|
|
from unittest.mock import Mock
|
|
|
|
|
2018-02-19 07:39:14 -06:00
|
|
|
import pytest
|
2013-01-20 17:09:43 -06:00
|
|
|
from docutils import nodes
|
2014-04-28 21:46:47 -05:00
|
|
|
|
2022-06-16 13:50:01 -05:00
|
|
|
import sphinx.application
|
2018-03-23 00:26:07 -05:00
|
|
|
from sphinx.errors import ExtensionError
|
2024-03-23 18:43:54 -05:00
|
|
|
from sphinx.testing.util import SphinxTestApp
|
2018-02-19 07:39:14 -06:00
|
|
|
from sphinx.util import logging
|
2024-03-23 18:43:54 -05:00
|
|
|
from sphinx.util.console import strip_colors
|
2008-08-23 10:04:45 -05:00
|
|
|
|
2024-03-16 12:29:44 -05:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
import os
|
2008-08-23 10:04:45 -05:00
|
|
|
|
2024-03-16 12:29:44 -05:00
|
|
|
|
|
|
|
def test_instantiation(
|
|
|
|
tmp_path_factory: pytest.TempPathFactory,
|
|
|
|
rootdir: str | os.PathLike[str] | None,
|
|
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
|
|
) -> None:
|
2022-06-16 13:50:01 -05:00
|
|
|
# Given
|
|
|
|
src_dir = tmp_path_factory.getbasetemp() / 'root'
|
|
|
|
|
|
|
|
# special support for sphinx/tests
|
|
|
|
if rootdir and not src_dir.exists():
|
|
|
|
shutil.copytree(Path(str(rootdir)) / 'test-root', src_dir)
|
|
|
|
|
2024-07-23 10:55:21 -05:00
|
|
|
saved_path = sys.path.copy()
|
2022-06-16 13:50:01 -05:00
|
|
|
|
|
|
|
# When
|
|
|
|
app_ = SphinxTestApp(
|
2023-07-27 18:39:12 -05:00
|
|
|
srcdir=src_dir,
|
2022-06-16 13:50:01 -05:00
|
|
|
status=StringIO(),
|
2023-02-17 16:11:14 -06:00
|
|
|
warning=StringIO(),
|
2022-06-16 13:50:01 -05:00
|
|
|
)
|
2024-07-23 10:55:21 -05:00
|
|
|
sys.path[:] = saved_path
|
2022-06-16 13:50:01 -05:00
|
|
|
app_.cleanup()
|
|
|
|
|
|
|
|
# Then
|
|
|
|
assert isinstance(app_, sphinx.application.Sphinx)
|
|
|
|
|
|
|
|
|
2024-07-23 09:35:55 -05:00
|
|
|
def test_events(app):
|
2014-09-21 10:17:02 -05:00
|
|
|
def empty():
|
|
|
|
pass
|
2017-01-05 09:46:42 -06:00
|
|
|
with pytest.raises(ExtensionError) as excinfo:
|
|
|
|
app.connect("invalid", empty)
|
|
|
|
assert "Unknown event name: invalid" in str(excinfo.value)
|
2008-08-23 10:04:45 -05:00
|
|
|
|
|
|
|
app.add_event("my_event")
|
2017-01-05 09:46:42 -06:00
|
|
|
with pytest.raises(ExtensionError) as excinfo:
|
|
|
|
app.add_event("my_event")
|
|
|
|
assert "Event 'my_event' already present" in str(excinfo.value)
|
2008-08-23 10:04:45 -05:00
|
|
|
|
|
|
|
def mock_callback(a_app, *args):
|
|
|
|
assert a_app is app
|
|
|
|
assert emit_args == args
|
|
|
|
return "ret"
|
|
|
|
emit_args = (1, 3, "string")
|
|
|
|
listener_id = app.connect("my_event", mock_callback)
|
|
|
|
assert app.emit("my_event", *emit_args) == ["ret"], "Callback not called"
|
|
|
|
|
|
|
|
app.disconnect(listener_id)
|
|
|
|
assert app.emit("my_event", *emit_args) == [], \
|
|
|
|
"Callback called when disconnected"
|
|
|
|
|
|
|
|
|
2024-07-23 09:35:55 -05:00
|
|
|
def test_emit_with_nonascii_name_node(app):
|
2018-12-15 08:02:28 -06:00
|
|
|
node = nodes.section(names=['\u65e5\u672c\u8a9e'])
|
2013-01-20 17:09:43 -06:00
|
|
|
app.emit('my_event', node)
|
|
|
|
|
|
|
|
|
2024-07-23 09:35:55 -05:00
|
|
|
def test_extensions(app):
|
2014-09-21 10:17:02 -05:00
|
|
|
app.setup_extension('shutil')
|
2024-07-23 09:35:55 -05:00
|
|
|
warning = strip_colors(app.warning.getvalue())
|
2018-09-12 06:09:23 -05:00
|
|
|
assert "extension 'shutil' has no setup() function" in warning
|
2014-09-21 10:17:02 -05:00
|
|
|
|
|
|
|
|
2024-07-23 09:35:55 -05:00
|
|
|
def test_extension_in_blacklist(app):
|
2016-06-01 01:32:24 -05:00
|
|
|
app.setup_extension('sphinxjp.themecore')
|
2024-07-23 09:35:55 -05:00
|
|
|
msg = strip_colors(app.warning.getvalue())
|
2016-05-30 05:53:59 -05:00
|
|
|
assert msg.startswith("WARNING: the extension 'sphinxjp.themecore' was")
|
2016-06-01 01:32:24 -05:00
|
|
|
|
|
|
|
|
2017-01-05 10:14:47 -06:00
|
|
|
@pytest.mark.sphinx(testroot='add_source_parser')
|
2024-07-23 09:35:55 -05:00
|
|
|
def test_add_source_parser(app):
|
2019-03-29 09:52:32 -05:00
|
|
|
assert set(app.config.source_suffix) == {'.rst', '.test'}
|
2018-02-11 06:13:32 -06:00
|
|
|
|
|
|
|
# .rst; only in :confval:`source_suffix`
|
|
|
|
assert '.rst' not in app.registry.get_source_parsers()
|
2024-07-10 15:43:14 -05:00
|
|
|
assert app.registry.source_suffix['.rst'] == 'restructuredtext'
|
2018-02-11 06:13:32 -06:00
|
|
|
|
|
|
|
# .test; configured by API
|
|
|
|
assert app.registry.source_suffix['.test'] == 'test'
|
|
|
|
assert 'test' in app.registry.get_source_parsers()
|
|
|
|
assert app.registry.get_source_parsers()['test'].__name__ == 'TestSourceParser'
|
2017-12-29 09:54:55 -06:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.sphinx(testroot='extensions')
|
2024-07-23 09:35:55 -05:00
|
|
|
def test_add_is_parallel_allowed(app):
|
|
|
|
logging.setup(app, app.status, app.warning)
|
2017-12-29 09:54:55 -06:00
|
|
|
|
|
|
|
assert app.is_parallel_allowed('read') is True
|
|
|
|
assert app.is_parallel_allowed('write') is True
|
2024-07-23 09:35:55 -05:00
|
|
|
assert app.warning.getvalue() == ''
|
2017-12-29 09:54:55 -06:00
|
|
|
|
|
|
|
app.setup_extension('read_parallel')
|
|
|
|
assert app.is_parallel_allowed('read') is True
|
|
|
|
assert app.is_parallel_allowed('write') is True
|
2024-07-23 09:35:55 -05:00
|
|
|
assert app.warning.getvalue() == ''
|
2017-12-29 09:54:55 -06:00
|
|
|
app.extensions.pop('read_parallel')
|
|
|
|
|
|
|
|
app.setup_extension('write_parallel')
|
|
|
|
assert app.is_parallel_allowed('read') is False
|
|
|
|
assert app.is_parallel_allowed('write') is True
|
2018-02-06 10:06:31 -06:00
|
|
|
assert ("the write_parallel extension does not declare if it is safe "
|
2024-07-23 09:35:55 -05:00
|
|
|
"for parallel reading, assuming it isn't - please ") in app.warning.getvalue()
|
2017-12-29 09:54:55 -06:00
|
|
|
app.extensions.pop('write_parallel')
|
2024-07-23 09:35:55 -05:00
|
|
|
app.warning.truncate(0) # reset warnings
|
2017-12-29 09:54:55 -06:00
|
|
|
|
|
|
|
app.setup_extension('read_serial')
|
|
|
|
assert app.is_parallel_allowed('read') is False
|
2024-07-23 09:35:55 -05:00
|
|
|
assert "the read_serial extension is not safe for parallel reading" in app.warning.getvalue()
|
|
|
|
app.warning.truncate(0) # reset warnings
|
2017-12-29 09:54:55 -06:00
|
|
|
assert app.is_parallel_allowed('write') is True
|
2024-07-23 09:35:55 -05:00
|
|
|
assert app.warning.getvalue() == ''
|
2017-12-29 09:54:55 -06:00
|
|
|
app.extensions.pop('read_serial')
|
|
|
|
|
|
|
|
app.setup_extension('write_serial')
|
|
|
|
assert app.is_parallel_allowed('read') is False
|
|
|
|
assert app.is_parallel_allowed('write') is False
|
2018-02-06 10:06:31 -06:00
|
|
|
assert ("the write_serial extension does not declare if it is safe "
|
2024-07-23 09:35:55 -05:00
|
|
|
"for parallel reading, assuming it isn't - please ") in app.warning.getvalue()
|
2017-12-29 09:54:55 -06:00
|
|
|
app.extensions.pop('write_serial')
|
2024-07-23 09:35:55 -05:00
|
|
|
app.warning.truncate(0) # reset warnings
|
2019-02-19 21:32:17 -06:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.sphinx('dummy', testroot='root')
|
|
|
|
def test_build_specific(app):
|
|
|
|
app.builder.build = Mock()
|
|
|
|
filenames = [app.srcdir / 'index.txt', # normal
|
|
|
|
app.srcdir / 'images', # without suffix
|
|
|
|
app.srcdir / 'notfound.txt', # not found
|
|
|
|
app.srcdir / 'img.png', # unknown suffix
|
|
|
|
'/index.txt', # external file
|
|
|
|
app.srcdir / 'subdir', # directory
|
|
|
|
app.srcdir / 'subdir/includes.txt', # file on subdir
|
|
|
|
app.srcdir / 'subdir/../subdir/excluded.txt'] # not normalized
|
|
|
|
app.build(False, filenames)
|
|
|
|
|
2019-12-22 11:09:45 -06:00
|
|
|
expected = ['index', 'subdir/includes', 'subdir/excluded']
|
2019-02-19 21:32:17 -06:00
|
|
|
app.builder.build.assert_called_with(expected,
|
|
|
|
method='specific',
|
2019-12-22 11:09:45 -06:00
|
|
|
summary='3 source files given on command line')
|