"""Pytest invocation inside the sandbox + JSON-report parsing. Kept separate from `runner.py` so the Docker orchestration and the pytest-harness concerns don't tangle. """ from __future__ import annotations import json from pathlib import Path from typing import Any from .resource_limits import REPORT_FILENAME, REPORT_PATH_IN_CONTAINER def pytest_command() -> list[str]: """Pytest argv used inside the sandbox container. `-p no:cacheprovider` stops pytest from writing `.pytest_cache/` into the bind-mounted /work as the sandbox uid, which would be un-deletable by the host on Linux (CI, CHTC). """ return [ "pytest", "-q", "-p", "no:cacheprovider", "--json-report", f"--json-report-file={REPORT_PATH_IN_CONTAINER}", "test_solution.py", ] def parse_report(workdir: Path) -> tuple[int, int]: """Read the JSON report from the host side of the bind mount. Returns (0, 0) if the report is missing or malformed — typical when the container was killed before pytest could flush. """ report = workdir / REPORT_FILENAME if not report.exists(): return 0, 0 try: summary = json.loads(report.read_text()).get("summary", {}) return int(summary.get("passed", 0)), int(summary.get("total", 0)) except Exception: return 0, 0 def was_oom_killed(container: Any) -> bool: """True if the kernel OOM-killed the container due to mem_limit.""" try: container.reload() return bool(container.attrs.get("State", {}).get("OOMKilled", False)) except Exception: return False