tests.fixtures

Define pytest fixtures for use throughout the tests package and doctest tests.

 1"""Define `pytest` fixtures for use throughout the `tests` package and `doctest` tests."""
 2
 3from __future__ import annotations
 4
 5import socket
 6import subprocess
 7from collections.abc import Iterator
 8
 9import pytest
10
11# ruff: noqa: PLC0415
12
13
14@pytest.fixture(scope='session')
15def dicom_storescp() -> str:
16    """Return the path to the `dicom-storescp` executable."""
17    import shutil
18
19    if (dicom_storescp := shutil.which('dicom-storescp')) is None:
20        pytest.skip('`dicom-storescp` not found. To install it, run `cargo install dicom-storescp`')
21
22    return dicom_storescp
23
24
25@pytest.fixture(scope='session')
26def localhost() -> Iterator[tuple[str, int]]:
27    """Query the OS for the first available port; return the hostname of the socket as well."""
28    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
29        sock.bind(('', 0))
30        host, port = sock.getsockname()
31        sock.close()
32        yield (host, port)
33
34
35@pytest.fixture(scope='session')
36def scpserver(dicom_storescp: str, localhost: tuple[str, int]) -> Iterator[str]:
37    """Start a DICOM SCP server for use with tests."""
38    host, port = localhost
39    with subprocess.Popen([dicom_storescp, '-p', str(port)], text=True) as sub_proc:
40        with pytest.raises(subprocess.TimeoutExpired):
41            sub_proc.wait(timeout=0.01)
42
43        assert None is sub_proc.returncode
44
45        try:
46            yield f'{host}:{port}'
47        finally:
48            sub_proc.terminate()
@pytest.fixture(scope='session')
def dicom_storescp() -> str:
15@pytest.fixture(scope='session')
16def dicom_storescp() -> str:
17    """Return the path to the `dicom-storescp` executable."""
18    import shutil
19
20    if (dicom_storescp := shutil.which('dicom-storescp')) is None:
21        pytest.skip('`dicom-storescp` not found. To install it, run `cargo install dicom-storescp`')
22
23    return dicom_storescp

Return the path to the dicom-storescp executable.

@pytest.fixture(scope='session')
def localhost() -> Iterator[tuple[str, int]]:
26@pytest.fixture(scope='session')
27def localhost() -> Iterator[tuple[str, int]]:
28    """Query the OS for the first available port; return the hostname of the socket as well."""
29    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
30        sock.bind(('', 0))
31        host, port = sock.getsockname()
32        sock.close()
33        yield (host, port)

Query the OS for the first available port; return the hostname of the socket as well.

@pytest.fixture(scope='session')
def scpserver(dicom_storescp: str, localhost: tuple[str, int]) -> Iterator[str]:
36@pytest.fixture(scope='session')
37def scpserver(dicom_storescp: str, localhost: tuple[str, int]) -> Iterator[str]:
38    """Start a DICOM SCP server for use with tests."""
39    host, port = localhost
40    with subprocess.Popen([dicom_storescp, '-p', str(port)], text=True) as sub_proc:
41        with pytest.raises(subprocess.TimeoutExpired):
42            sub_proc.wait(timeout=0.01)
43
44        assert None is sub_proc.returncode
45
46        try:
47            yield f'{host}:{port}'
48        finally:
49            sub_proc.terminate()

Start a DICOM SCP server for use with tests.