LLM-Automated-Test-Generation-Agent / scripts /mutation_sensitivity.py
SevdanurGenc's picture
Upload 300 files
9438a57 verified
Raw History Blame Contribute Delete
4.27 kB
#!/usr/bin/env python3
"""Mutasyon skorunun örneklem büyüklüğüne duyarlılığı (referans paketlerle, LLM gerekmez).
Her öğede üretilebilen TÜM mutantlar bir kez değerlendirilir; örnekleme deterministik ve iç içe olduğundan
(N mutantlık örneklem, M > N örnekleminin ön ekidir) N = 10, 20, 30, 50 için skorlar aynı sonuçlardan
hesaplanır. Çıktı: reports/MUTASYON_DUYARLILIK.md (+ .json)
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "backend"))
from app.eval.benchmark import load_manifest # noqa: E402
from app.mutation.mutator import MutationContext, generate_mutants, run_mutation # noqa: E402
from app.sandbox.registry import get_runner # noqa: E402
SIZES = [10, 20, 30, 50]
def score(statuses: list[str]):
valid = [s for s in statuses if s != "invalid"]
return round(100.0 * sum(s in ("killed", "timeout") for s in valid) / len(valid), 1) if valid else None
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--out", default=str(ROOT / "reports" / "MUTASYON_DUYARLILIK.md"))
args = ap.parse_args()
junit4 = get_runner("java").framework() == "junit4"
rows = []
for it in load_manifest():
if not it.get("reference_tests") or it.get("kind") == "ui":
continue
key = "reference_tests_junit4" if junit4 and it.get("reference_tests_junit4") else "reference_tests"
tests = (ROOT / "benchmarks" / it[key]).read_text(encoding="utf-8")
runner = get_runner(it["language"], it.get("kind", "unit"))
base = runner.run(it["code"], tests, it.get("kind", "unit"), None, with_coverage=True)
lang = it["language"]
rep = run_mutation(MutationContext(
it["code"], lang, base,
lambda c: runner.run(c, tests, it.get("kind", "unit"), None, with_coverage=False,
timeout=max(20, int(base.duration_s * 3) + 10))), 100_000,
workers=2 if lang == "java" else 3)
status = {(m.line, m.operator, m.mutated): m.status for m in rep.mutants}
row = {"id": it["id"], "split": it.get("split", "dev"), "language": lang, "all_n": len(rep.mutants),
"all_valid": sum(m.status != "invalid" for m in rep.mutants),
"all": score([m.status for m in rep.mutants])}
for n in SIZES:
sample = generate_mutants(it["code"], lang, n)
row[str(n)] = score([status[(m.line, m.operator, m.mutated)] for m in sample])
rows.append(row)
print(f" {it['id']:<16} tümü={row['all']} ({row['all_n']} mutant) "
+ " ".join(f"N{n}={row[str(n)]}" for n in SIZES), flush=True)
def mean(k):
v = [r[k] for r in rows if r[k] is not None]
return round(sum(v) / len(v), 1) if v else None
def mad(k): # tüm mutantlara göre ortalama mutlak sapma
v = [abs(r[k] - r["all"]) for r in rows if r[k] is not None and r["all"] is not None]
return round(sum(v) / len(v), 1) if v else None
lines = ["# Mutasyon skorunun örneklem büyüklüğüne duyarlılığı", "",
"İnsan yazımı referans paketler; örnekleme operatör aileleri arasında dengeli, sabit tohumlu ve iç içe.", "",
"| Öğe | Bölüm | Dil | Mutant (tümü / geçerli) | " + " | ".join(f"N={n}" for n in SIZES) + " | Tümü |",
"|---|---|---|---|" + "---|" * len(SIZES) + "---|"]
for r in rows:
lines.append(f"| {r['id']} | {r['split']} | {r['language']} | {r['all_n']} / {r['all_valid']} | "
+ " | ".join(f"{r[str(n)]}" for n in SIZES) + f" | {r['all']} |")
lines.append("| **Ortalama** | | | | " + " | ".join(f"**{mean(str(n))}**" for n in SIZES) + f" | **{mean('all')}** |")
lines.append("| Tümüne göre ort. mutlak sapma | | | | " + " | ".join(f"{mad(str(n))}" for n in SIZES) + " | 0 |")
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text("\n".join(lines) + "\n", encoding="utf-8")
out.with_suffix(".json").write_text(json.dumps(rows, indent=1))
print("\n".join(lines[-2:]), f"\n→ {out}")
if __name__ == "__main__":
main()