clan-core/pkgs/clan-cli/tests/command.py

65 lines
1.7 KiB
Python
Raw Normal View History

2023-08-09 14:38:08 +00:00
import os
import signal
import subprocess
2023-11-29 11:40:48 +00:00
from collections.abc import Iterator
2023-10-24 14:44:54 +00:00
from pathlib import Path
2023-11-29 11:40:48 +00:00
from typing import IO, Any
2023-08-09 14:38:08 +00:00
import pytest
_FILE = None | int | IO[Any]
2023-08-09 14:38:08 +00:00
class Command:
def __init__(self) -> None:
2023-11-29 11:40:48 +00:00
self.processes: list[subprocess.Popen[str]] = []
2023-08-09 14:38:08 +00:00
def run(
self,
2023-11-29 11:40:48 +00:00
command: list[str],
extra_env: dict[str, str] = {},
2023-08-09 14:38:08 +00:00
stdin: _FILE = None,
stdout: _FILE = None,
stderr: _FILE = None,
2023-11-29 11:40:48 +00:00
workdir: Path | None = None,
2023-08-09 14:38:08 +00:00
) -> subprocess.Popen[str]:
env = os.environ.copy()
env.update(extra_env)
# We start a new session here so that we can than more reliably kill all childs as well
p = subprocess.Popen(
command,
env=env,
start_new_session=True,
stdout=stdout,
stderr=stderr,
stdin=stdin,
text=True,
2023-10-24 14:44:54 +00:00
cwd=workdir,
2023-08-09 14:38:08 +00:00
)
self.processes.append(p)
return p
def terminate(self) -> None:
# Stop in reverse order in case there are dependencies.
# We just kill all processes as quickly as possible because we don't
# care about corrupted state and want to make tests fasts.
for p in reversed(self.processes):
try:
os.killpg(os.getpgid(p.pid), signal.SIGKILL)
except OSError:
pass
@pytest.fixture
def command() -> Iterator[Command]:
"""
Starts a background command. The process is automatically terminated in the end.
>>> p = command.run(["some", "daemon"])
>>> print(p.pid)
"""
c = Command()
try:
yield c
finally:
c.terminate()