Merge pull request 'tests: rewrite port allocation function' (#194) from Mic92-main into main
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
, pytest
|
, pytest
|
||||||
, pytest-cov
|
, pytest-cov
|
||||||
, pytest-subprocess
|
, pytest-subprocess
|
||||||
|
, pytest-parallel
|
||||||
, python3
|
, python3
|
||||||
, runCommand
|
, runCommand
|
||||||
, self
|
, self
|
||||||
@@ -36,6 +37,7 @@ let
|
|||||||
pytest
|
pytest
|
||||||
pytest-cov
|
pytest-cov
|
||||||
pytest-subprocess
|
pytest-subprocess
|
||||||
|
pytest-parallel
|
||||||
openssh
|
openssh
|
||||||
stdenv.cc
|
stdenv.cc
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ exclude = ["clan_cli.nixpkgs*"]
|
|||||||
clan_cli = [ "config/jsonschema/*", "webui/assets/**/*"]
|
clan_cli = [ "config/jsonschema/*", "webui/assets/**/*"]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail"
|
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail --workers auto"
|
||||||
norecursedirs = "tests/helpers"
|
norecursedirs = "tests/helpers"
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
|
|||||||
@@ -1,46 +1,55 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import socket
|
import socket
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
NEXT_PORT = 10000
|
|
||||||
|
def _unused_port(socket_type: int) -> int:
|
||||||
|
"""Find an unused localhost port from 1024-65535 and return it."""
|
||||||
|
with contextlib.closing(socket.socket(type=socket_type)) as sock:
|
||||||
|
sock.bind(("127.0.0.1", 0))
|
||||||
|
return sock.getsockname()[1]
|
||||||
|
|
||||||
|
|
||||||
def check_port(port: int) -> bool:
|
PortFunction = Callable[[], int]
|
||||||
tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
with tcp, udp:
|
|
||||||
try:
|
|
||||||
tcp.bind(("127.0.0.1", port))
|
|
||||||
udp.bind(("127.0.0.1", port))
|
|
||||||
return True
|
|
||||||
except socket.error:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def check_port_range(port_range: range) -> bool:
|
@pytest.fixture(scope="session")
|
||||||
for port in port_range:
|
def unused_tcp_port() -> PortFunction:
|
||||||
if not check_port(port):
|
"""A function, producing different unused TCP ports."""
|
||||||
return False
|
produced = set()
|
||||||
return True
|
|
||||||
|
def factory() -> int:
|
||||||
|
"""Return an unused port."""
|
||||||
|
port = _unused_port(socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
while port in produced:
|
||||||
|
port = _unused_port(socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
produced.add(port)
|
||||||
|
|
||||||
|
return port
|
||||||
|
|
||||||
|
return factory
|
||||||
|
|
||||||
|
|
||||||
class Ports:
|
@pytest.fixture(scope="session")
|
||||||
def allocate(self, num: int) -> int:
|
def unused_udp_port() -> PortFunction:
|
||||||
"""
|
"""A function, producing different unused UDP ports."""
|
||||||
Allocates
|
produced = set()
|
||||||
"""
|
|
||||||
global NEXT_PORT
|
|
||||||
while NEXT_PORT + num <= 65535:
|
|
||||||
start = NEXT_PORT
|
|
||||||
NEXT_PORT += num
|
|
||||||
if not check_port_range(range(start, NEXT_PORT)):
|
|
||||||
continue
|
|
||||||
return start
|
|
||||||
raise Exception("cannot find enough free port")
|
|
||||||
|
|
||||||
|
def factory() -> int:
|
||||||
|
"""Return an unused port."""
|
||||||
|
port = _unused_port(socket.SOCK_DGRAM)
|
||||||
|
|
||||||
@pytest.fixture
|
while port in produced:
|
||||||
def ports() -> Ports:
|
port = _unused_port(socket.SOCK_DGRAM)
|
||||||
return Ports()
|
|
||||||
|
produced.add(port)
|
||||||
|
|
||||||
|
return port
|
||||||
|
|
||||||
|
return factory
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import pytest
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from command import Command
|
from command import Command
|
||||||
from ports import Ports
|
from ports import PortFunction
|
||||||
|
|
||||||
|
|
||||||
class Sshd:
|
class Sshd:
|
||||||
@@ -104,8 +104,13 @@ exec {bash} -l "${{@}}"
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def sshd(sshd_config: SshdConfig, command: "Command", ports: "Ports") -> Iterator[Sshd]:
|
def sshd(
|
||||||
port = ports.allocate(1)
|
sshd_config: SshdConfig, command: "Command", unused_tcp_port: "PortFunction"
|
||||||
|
) -> Iterator[Sshd]:
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
subprocess.run(["echo", "hello"], check=True)
|
||||||
|
port = unused_tcp_port()
|
||||||
sshd = shutil.which("sshd")
|
sshd = shutil.which("sshd")
|
||||||
assert sshd is not None, "no sshd binary found"
|
assert sshd is not None, "no sshd binary found"
|
||||||
env = {}
|
env = {}
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from ports import Ports
|
from ports import PortFunction
|
||||||
|
|
||||||
|
|
||||||
def test_start_server(ports: Ports, temporary_dir: Path) -> None:
|
def test_start_server(unused_tcp_port: PortFunction, temporary_dir: Path) -> None:
|
||||||
port = ports.allocate(1)
|
port = unused_tcp_port()
|
||||||
|
|
||||||
fifo = temporary_dir / "fifo"
|
fifo = temporary_dir / "fifo"
|
||||||
os.mkfifo(fifo)
|
os.mkfifo(fifo)
|
||||||
|
|||||||
Reference in New Issue
Block a user