Files
grm/scripts/check_test_speed.py
T
emilandEmil Simeonov d949bd3444
CI / lint (push) Has been cancelled
CI / unit-tests (push) Has been cancelled
CI / molecule-tests (push) Has been cancelled
Post-merge Vikunja update / vikunja (push) Has been cancelled
GRM-24: feat: bandit integration (#1)
Co-authored-by: Emil Simeonov <emil@theliberatededge.org>
Reviewed-on: #1
2026-06-19 21:17:39 +00:00

93 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Run unit tests and enforce a maximum execution-time budget.
Usage:
python3 scripts/check_test_speed.py [--max-seconds N]
"""
from __future__ import annotations
import argparse
import re
import subprocess # nosec B404
import sys
import click
from gitea_runner_manager.i18n import _
DEFAULT_MAX_SECONDS = 2.0
TEST_COMMAND = ["make", "test-unit"]
_TIMING_RE = re.compile(r"(\d+) passed in ([0-9.]+)s")
def run_tests() -> tuple[str, str]:
"""Execute the unit-test suite and return (stdout, stderr)."""
result = subprocess.run( # nosec B603
TEST_COMMAND,
capture_output=True,
text=True,
check=False,
)
return result.stdout, result.stderr
def parse_duration(output: str) -> float:
"""Extract elapsed seconds from pytest summary line.
Raises:
click.ClickException: when the timing line cannot be found.
"""
for line in output.splitlines():
match = _TIMING_RE.search(line)
if match:
return float(match.group(2))
raise click.ClickException(_("Could not parse test execution time from output."))
def check_speed(duration: float, max_seconds: float) -> None:
"""Validate duration is within budget; raise on violation."""
if duration > max_seconds:
raise click.ClickException(
_(
"Unit tests too slow: {duration:.2f}s (max allowed: {max}s).\n"
" Fix: run 'make pytest-cov' to profile, then optimise slow tests.\n"
" Hint: avoid unnecessary imports, use lighter mocks, or cache fixtures.",
duration=duration,
max=max_seconds,
)
)
def main(max_seconds: float) -> None:
"""Run tests, parse timing, and enforce the budget."""
stdout, stderr = run_tests()
combined = stdout + "\n" + stderr
click.echo(combined, err=False)
duration = parse_duration(combined)
check_speed(duration, max_seconds)
click.echo(
_(
"Unit tests passed in {duration:.2f}s (under {max}s limit).",
duration=duration,
max=max_seconds,
)
)
@click.command()
@click.option(
"--max-seconds",
type=float,
default=DEFAULT_MAX_SECONDS,
show_default=True,
help="Maximum allowed execution time in seconds.",
)
def cli(max_seconds: float) -> None:
main(max_seconds)
if __name__ == "__main__": # pragma: no cover
cli() # pragma: no cover