Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tests] skip tests that need remote content if host is offline #12091

Merged
merged 2 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 63 additions & 7 deletions sphinx/testing/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@
import sys
from collections import namedtuple
from io import StringIO
from typing import TYPE_CHECKING, Any, Callable
from typing import TYPE_CHECKING, Optional

import pytest

from sphinx.testing.util import SphinxTestApp, SphinxTestAppWrapperForSkipBuilding

if TYPE_CHECKING:
from collections.abc import Generator
from collections.abc import Callable, Generator
from pathlib import Path
from typing import Any

DEFAULT_ENABLED_MARKERS = [
(
Expand Down Expand Up @@ -60,8 +61,13 @@ def restore(self, key: str) -> dict[str, StringIO]:


@pytest.fixture()
def app_params(request: Any, test_params: dict, shared_result: SharedResult,
sphinx_test_tempdir: str, rootdir: str) -> _app_params:
def app_params(
request: Any,
test_params: dict,
shared_result: SharedResult,
sphinx_test_tempdir: str,
rootdir: str,
) -> _app_params:
"""
Parameters that are specified by 'pytest.mark.sphinx' for
sphinx.application.Sphinx initialization
Expand Down Expand Up @@ -128,8 +134,12 @@ def test_params(request: Any) -> dict:


@pytest.fixture()
def app(test_params: dict, app_params: tuple[dict, dict], make_app: Callable,
shared_result: SharedResult) -> Generator[SphinxTestApp, None, None]:
def app(
test_params: dict,
app_params: tuple[dict, dict],
make_app: Callable,
shared_result: SharedResult,
) -> Generator[SphinxTestApp, None, None]:
"""
Provides the 'sphinx.application.Sphinx' object
"""
Expand Down Expand Up @@ -218,6 +228,52 @@ def if_graphviz_found(app: SphinxTestApp) -> None: # NoQA: PT004
pytest.skip('graphviz "dot" is not available')


_HOST_ONLINE_ERROR = pytest.StashKey[Optional[str]]()


def _query(address: tuple[str, int]) -> str | None:
import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
try:
sock.settimeout(5)
sock.connect(address)
except OSError as exc:
# other type of errors are propagated
return str(exc)
return None


@pytest.fixture(scope='session')
def sphinx_remote_query_address() -> tuple[str, int]:
"""Address to which a query is made to check that the host is online.

By default, onlineness is tested by querying the DNS server ``1.1.1.1``
but users concerned about privacy might change it in ``conftest.py``.
"""
return ('1.1.1.1', 80)


@pytest.fixture(scope='session')
def if_online( # NoQA: PT004
request: pytest.FixtureRequest,
sphinx_remote_query_address: tuple[str, int],
) -> None:
"""Skip the test if the host has no connection.

Usage::

@pytest.mark.usefixtures('if_online')
def test_if_host_is_online(): ...
"""
if _HOST_ONLINE_ERROR not in request.session.stash:
# do not use setdefault() to avoid creating a socket connection
lookup_error = _query(sphinx_remote_query_address)
request.session.stash[_HOST_ONLINE_ERROR] = lookup_error
if (error := request.session.stash[_HOST_ONLINE_ERROR]) is not None:
pytest.skip('host appears to be offline (%s)' % error)


@pytest.fixture(scope='session')
def sphinx_test_tempdir(tmp_path_factory: Any) -> Path:
"""Temporary directory."""
Expand All @@ -233,8 +289,8 @@ def rollback_sysmodules() -> Generator[None, None, None]: # NoQA: PT004
For example, used in test_ext_autosummary.py to permit unloading the
target module to clear its cache.
"""
sysmodules = list(sys.modules)
try:
sysmodules = list(sys.modules)
yield
finally:
for modname in list(sys.modules):
Expand Down
2 changes: 2 additions & 0 deletions tests/test_builders/test_build_html_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest


@pytest.mark.usefixtures('if_online')
@pytest.mark.sphinx('html', testroot='images')
def test_html_remote_images(app, status, warning):
app.build(force_all=True)
Expand All @@ -24,6 +25,7 @@ def test_html_encoded_image(app, status, warning):
assert (app.outdir / '_images/img_#1.png').exists()


@pytest.mark.usefixtures('if_online')
@pytest.mark.sphinx('html', testroot='remote-logo')
def test_html_remote_logo(app, status, warning):
app.build(force_all=True)
Expand Down
1 change: 1 addition & 0 deletions tests/test_builders/test_build_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -1398,6 +1398,7 @@ def test_latex_raw_directive(app, status, warning):
assert 'LaTeX: abc def ghi' in result


@pytest.mark.usefixtures('if_online')
@pytest.mark.sphinx('latex', testroot='images')
def test_latex_images(app, status, warning):
app.build(force_all=True)
Expand Down