Skip to content

Commit 98d0cc8

Browse files
authored
[tests] skip tests that need remote content if host is offline (#12091)
1 parent 23dd754 commit 98d0cc8

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

sphinx/testing/fixtures.py

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,16 @@
77
import sys
88
from collections import namedtuple
99
from io import StringIO
10-
from typing import TYPE_CHECKING, Any, Callable
10+
from typing import TYPE_CHECKING, Optional
1111

1212
import pytest
1313

1414
from sphinx.testing.util import SphinxTestApp, SphinxTestAppWrapperForSkipBuilding
1515

1616
if TYPE_CHECKING:
17-
from collections.abc import Generator
17+
from collections.abc import Callable, Generator
1818
from pathlib import Path
19+
from typing import Any
1920

2021
DEFAULT_ENABLED_MARKERS = [
2122
(
@@ -60,8 +61,13 @@ def restore(self, key: str) -> dict[str, StringIO]:
6061

6162

6263
@pytest.fixture()
63-
def app_params(request: Any, test_params: dict, shared_result: SharedResult,
64-
sphinx_test_tempdir: str, rootdir: str) -> _app_params:
64+
def app_params(
65+
request: Any,
66+
test_params: dict,
67+
shared_result: SharedResult,
68+
sphinx_test_tempdir: str,
69+
rootdir: str,
70+
) -> _app_params:
6571
"""
6672
Parameters that are specified by 'pytest.mark.sphinx' for
6773
sphinx.application.Sphinx initialization
@@ -128,8 +134,12 @@ def test_params(request: Any) -> dict:
128134

129135

130136
@pytest.fixture()
131-
def app(test_params: dict, app_params: tuple[dict, dict], make_app: Callable,
132-
shared_result: SharedResult) -> Generator[SphinxTestApp, None, None]:
137+
def app(
138+
test_params: dict,
139+
app_params: tuple[dict, dict],
140+
make_app: Callable,
141+
shared_result: SharedResult,
142+
) -> Generator[SphinxTestApp, None, None]:
133143
"""
134144
Provides the 'sphinx.application.Sphinx' object
135145
"""
@@ -218,6 +228,52 @@ def if_graphviz_found(app: SphinxTestApp) -> None: # NoQA: PT004
218228
pytest.skip('graphviz "dot" is not available')
219229

220230

231+
_HOST_ONLINE_ERROR = pytest.StashKey[Optional[str]]()
232+
233+
234+
def _query(address: tuple[str, int]) -> str | None:
235+
import socket
236+
237+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
238+
try:
239+
sock.settimeout(5)
240+
sock.connect(address)
241+
except OSError as exc:
242+
# other type of errors are propagated
243+
return str(exc)
244+
return None
245+
246+
247+
@pytest.fixture(scope='session')
248+
def sphinx_remote_query_address() -> tuple[str, int]:
249+
"""Address to which a query is made to check that the host is online.
250+
251+
By default, onlineness is tested by querying the DNS server ``1.1.1.1``
252+
but users concerned about privacy might change it in ``conftest.py``.
253+
"""
254+
return ('1.1.1.1', 80)
255+
256+
257+
@pytest.fixture(scope='session')
258+
def if_online( # NoQA: PT004
259+
request: pytest.FixtureRequest,
260+
sphinx_remote_query_address: tuple[str, int],
261+
) -> None:
262+
"""Skip the test if the host has no connection.
263+
264+
Usage::
265+
266+
@pytest.mark.usefixtures('if_online')
267+
def test_if_host_is_online(): ...
268+
"""
269+
if _HOST_ONLINE_ERROR not in request.session.stash:
270+
# do not use setdefault() to avoid creating a socket connection
271+
lookup_error = _query(sphinx_remote_query_address)
272+
request.session.stash[_HOST_ONLINE_ERROR] = lookup_error
273+
if (error := request.session.stash[_HOST_ONLINE_ERROR]) is not None:
274+
pytest.skip('host appears to be offline (%s)' % error)
275+
276+
221277
@pytest.fixture(scope='session')
222278
def sphinx_test_tempdir(tmp_path_factory: Any) -> Path:
223279
"""Temporary directory."""
@@ -233,8 +289,8 @@ def rollback_sysmodules() -> Generator[None, None, None]: # NoQA: PT004
233289
For example, used in test_ext_autosummary.py to permit unloading the
234290
target module to clear its cache.
235291
"""
292+
sysmodules = list(sys.modules)
236293
try:
237-
sysmodules = list(sys.modules)
238294
yield
239295
finally:
240296
for modname in list(sys.modules):

tests/test_builders/test_build_html_image.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pytest
66

77

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

2627

28+
@pytest.mark.usefixtures('if_online')
2729
@pytest.mark.sphinx('html', testroot='remote-logo')
2830
def test_html_remote_logo(app, status, warning):
2931
app.build(force_all=True)

tests/test_builders/test_build_latex.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,7 @@ def test_latex_raw_directive(app, status, warning):
13981398
assert 'LaTeX: abc def ghi' in result
13991399

14001400

1401+
@pytest.mark.usefixtures('if_online')
14011402
@pytest.mark.sphinx('latex', testroot='images')
14021403
def test_latex_images(app, status, warning):
14031404
app.build(force_all=True)

0 commit comments

Comments
 (0)