Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 2,563 Bytes
24e70ef | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #!/usr/bin/env python3
"""Run mutmut and enforce a minimum mutation kill rate."""
from __future__ import annotations
import argparse
import re
import shutil
import subprocess
import sys
from pathlib import Path
STATS = re.compile(
r"(?P<done>\d+)/(?P<total>\d+)\s+.*? (?P<killed>\d+)\s+.*? "
r"(?P<uncovered>\d+)\s+.*? (?P<timeout>\d+)\s+.*? "
r"(?P<suspicious>\d+)\s+.*? (?P<survived>\d+)\s+.*? "
r"(?P<skipped>\d+)"
)
def last_stats(output: str) -> dict[str, int] | None:
matches = list(STATS.finditer(output.replace("\r", "\n")))
if not matches:
return None
return {key: int(value) for key, value in matches[-1].groupdict().items()}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--min-kill-rate", type=float, required=True)
parser.add_argument(
"--repository",
type=Path,
help="Project root to mutate; defaults to the repository root.",
)
arguments = parser.parse_args()
repository = (
arguments.repository.resolve()
if arguments.repository is not None
else Path(__file__).resolve().parents[1]
)
if not (repository / "pyproject.toml").is_file():
parser.error(f"mutation project has no pyproject.toml: {repository}")
mutant_workspace = repository / "mutants"
if mutant_workspace.exists():
shutil.rmtree(mutant_workspace)
completed = subprocess.run(
["mutmut", "run"],
capture_output=True,
text=True,
check=False,
cwd=repository,
)
output = completed.stdout + completed.stderr
if completed.returncode != 0:
sys.stderr.write(output)
return completed.returncode
stats = last_stats(output)
if stats is None or stats["done"] != stats["total"]:
sys.stderr.write(output)
sys.stderr.write("mutation gate failed: incomplete or unreadable statistics\n")
return 2
caught = stats["killed"] + stats["timeout"]
catchable = caught + stats["survived"] + stats["uncovered"]
if catchable == 0:
sys.stderr.write("mutation gate failed: no mutants were generated\n")
return 2
rate = 100 * caught / catchable
print(f"mutation kill rate: {rate:.1f}% ({caught}/{catchable})")
if rate < arguments.min_kill_rate:
sys.stderr.write(
f"mutation gate failed: {rate:.1f}% is below "
f"{arguments.min_kill_rate:.1f}%\n"
)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
|