Anlass: Plankritik 2 von C1 (After Install), fremde pyproject.toml im Windows-Temp. Gegenprobe an sechs mutanten.toml im Vault: gleiche Ergebnisse, C1 unter Windows-Temp jetzt 69 OK statt 101 FEHLT. Co-Authored-By: Claude Opus 5.5 <[email protected]>
1350 lines
48 KiB
Python
1350 lines
48 KiB
Python
#!/usr/bin/env python3
|
|
"""Treiber fuer mutanten_lauf.py: 46 Angriffsfaelle auf einer Wegwerfkopie.
|
|
|
|
Zweck: Baut in einem Temp-Ordner unter /tmp ein kleines Testprojekt, fuehrt
|
|
mutanten_lauf.py je Fall mit einer eigenen mutanten.toml aus und vergleicht
|
|
Exit-Code und ein Meldungsstueck gegen die Erwartung aus dem Entwurf,
|
|
Abschnitt 6. Nach jedem Fall wird geprueft, dass jede Datei des kleinen
|
|
Projekts wieder byte-gleich mit dem Ausgangsstand ist.
|
|
|
|
Aufruf: python mutanten_lauf_treiber.py
|
|
Baut nichts ausserhalb von /tmp, geht nicht ins Netz, raeumt am Ende auf.
|
|
|
|
Spezifikation: .vault-tools/plan-kopie/mutanten_lauf_ENTWURF.md, Abschnitt 6.
|
|
|
|
Fall 35 (Nachbesserung 2026-09-28, N11) prueft das Aufraeumen bei SIGTERM: Ein
|
|
Mutant haengt, der Laeufer bekommt nach wenigen Sekunden selbst ein SIGTERM.
|
|
Damit die Pruefung nur die Reste dieses einen Falls sieht, bekommt der Laeufer
|
|
dafuer ein eigenes TMPDIR (tempfile respektiert die Umgebungsvariable).
|
|
|
|
Faelle 36 bis 40 (2026-10-06) pruefen die pytest-Konfiguration: Die Wurzel
|
|
liegt dafuer als k/ in einem eigenen Rahmen-Ordner, darueber wahlweise eine
|
|
fremde pyproject.toml mit addopts, die zu -p no:cacheprovider nicht passen
|
|
(wie im Windows-Temp bei der Plankritik 2 von C1), oder ein fremdes conftest.py.
|
|
Dazu ein Bedienfehler von pytest in der Grundlinie und unter einem Mutanten.
|
|
|
|
Faelle 41 bis 45 (Nachbesserung nach der Pruefung vom 2026-10-06): Wurzel ueber
|
|
einen Symlink, Rueckgabewert 3, die Gleichwertigkeitsprobe gegen pytest selbst
|
|
(mutanten_lauf_probe_konfig.py), ein Abbruch in nur einer von zwei
|
|
Interpretergruppen an erster und an letzter Stelle, ein ueberlanger Knoten.
|
|
Fall 46 (Nachbesserung 2): Rueckgabewert 3 unter einem Mutanten, Kurzfassung in
|
|
der Mutantenzeile und lange Fassung im Bericht.
|
|
"""
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import shutil
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
from pathlib import Path
|
|
|
|
HIER = Path(__file__).resolve().parent
|
|
LAEUFER = HIER / "mutanten_lauf.py"
|
|
PROBE = HIER / "mutanten_lauf_probe_konfig.py"
|
|
INTERPRETER = "/home/claude/venvs/trading/bin/python"
|
|
|
|
FEHLERLISTE = []
|
|
|
|
|
|
def melde(fallname, ok, zusatz=""):
|
|
if ok:
|
|
print("{0} ok".format(fallname))
|
|
else:
|
|
print("{0} FEHLER: {1}".format(fallname, zusatz))
|
|
FEHLERLISTE.append(fallname)
|
|
|
|
|
|
def sha_von(pfad):
|
|
return hashlib.sha256(pfad.read_bytes()).hexdigest()
|
|
|
|
|
|
def snapshot(dateien):
|
|
return {p: sha_von(p) for p in dateien}
|
|
|
|
|
|
def pruefe_unveraendert(dateien, snap, fallname):
|
|
for p in dateien:
|
|
if not p.exists():
|
|
melde(fallname + " (rueckstellung)", False, "Datei fehlt nach dem Lauf: {0}".format(p))
|
|
return
|
|
if sha_von(p) != snap[p]:
|
|
melde(fallname + " (rueckstellung)", False, "Datei nicht byte-gleich zurueckgesetzt: {0}".format(p))
|
|
return
|
|
|
|
|
|
def starte_laeufer(basis, toml_pfad, zusatzargumente=None, bericht_pfad=None, zeitlimit=None):
|
|
cmd = [INTERPRETER, str(LAEUFER), "--wurzel", str(basis), "--mutanten", str(toml_pfad)]
|
|
if zeitlimit is not None:
|
|
cmd += ["--zeitlimit", str(zeitlimit)]
|
|
if bericht_pfad is not None:
|
|
cmd += ["--bericht", str(bericht_pfad)]
|
|
if zusatzargumente:
|
|
cmd += zusatzargumente
|
|
return subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
|
|
|
|
|
def schreibe_toml(pfad, text):
|
|
pfad.write_text(text, encoding="utf-8")
|
|
|
|
|
|
def enthaelt(text, teilstueck):
|
|
return teilstueck in (text or "")
|
|
|
|
|
|
# --- Projektaufbau ----------------------------------------------------
|
|
|
|
def baue_projekt(basis):
|
|
(basis / "paket").mkdir(parents=True)
|
|
(basis / "tests").mkdir(parents=True)
|
|
|
|
(basis / "paket" / "__init__.py").write_text("", encoding="utf-8")
|
|
|
|
(basis / "paket" / "modul.py").write_text(
|
|
"def f1(x):\n"
|
|
" if x > 0:\n"
|
|
" return True\n"
|
|
" return False\n"
|
|
"\n"
|
|
"\n"
|
|
"def f2(y):\n"
|
|
" if y >= 10:\n"
|
|
" return \"gross\"\n"
|
|
" return \"klein\"\n"
|
|
"\n"
|
|
"\n"
|
|
"def f3(z):\n"
|
|
" if z == 0:\n"
|
|
" pass # platzhalter\n"
|
|
" return z\n"
|
|
"\n"
|
|
"\n"
|
|
"def f4(w):\n"
|
|
" if w == 1:\n"
|
|
" pass # platzhalter\n"
|
|
" return w\n"
|
|
"\n"
|
|
"\n"
|
|
"def f5(a, b):\n"
|
|
" resultat = a + b\n"
|
|
" return resultat\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "paket" / "modul_syntax.py").write_text(
|
|
"def f6(n):\n"
|
|
" return n * 2\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "paket" / "modul_zeit.py").write_text(
|
|
"def f7(n):\n"
|
|
" return n + 1\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "paket" / "modul_crlf.py").write_bytes(
|
|
b"def f8(n):\r\n return n\r\n",
|
|
)
|
|
|
|
(basis / "paket" / "modul_binaer.py").write_bytes(
|
|
b"\xff\xfe nicht utf-8 \x80\x81\n",
|
|
)
|
|
|
|
(basis / "tests" / "__init__.py").write_text("", encoding="utf-8")
|
|
|
|
(basis / "tests" / "test_modul.py").write_text(
|
|
"from paket.modul import f1, f2, f5\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_f1_positiv():\n"
|
|
" assert f1(5) is True\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_f1_negativ():\n"
|
|
" assert f1(-3) is False\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_f2_gross():\n"
|
|
" assert f2(15) == \"gross\"\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_f2_klein():\n"
|
|
" assert f2(1) == \"klein\"\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_f5_summe():\n"
|
|
" assert f5(2, 3) == 5\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "tests" / "test_modul_syntax.py").write_text(
|
|
"from paket.modul_syntax import f6\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_f6():\n"
|
|
" assert f6(3) == 6\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "tests" / "test_zeit.py").write_text(
|
|
"from paket.modul_zeit import f7\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_f7():\n"
|
|
" assert f7(1) == 2\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "tests" / "test_faelle.py").write_text(
|
|
"import pytest\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_immer_rot():\n"
|
|
" assert False\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_immer_uebersprungen():\n"
|
|
" pytest.skip(\"fuer den Treiber-Fall 14\")\n"
|
|
"\n"
|
|
"\n"
|
|
"@pytest.mark.integration\n"
|
|
"def test_wird_abgewaehlt():\n"
|
|
" assert True\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "tests" / "test_abdichtung.py").write_text(
|
|
"import os\n"
|
|
"\n"
|
|
"\n"
|
|
"def test_abdichtung_home_und_proxy():\n"
|
|
" home = os.environ.get(\"HOME\", \"\")\n"
|
|
" proxy = os.environ.get(\"https_proxy\", \"\")\n"
|
|
" assert \"mutanten_lauf_home_\" in home and proxy == \"http://127.0.0.1:9\"\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
(basis / "pyproject.toml").write_text(
|
|
"[tool.pytest.ini_options]\n"
|
|
"addopts = '-m \"not integration\"'\n"
|
|
"markers = [\n"
|
|
" \"integration: wird vom Treiber absichtlich abgewaehlt\",\n"
|
|
"]\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
getrackte_dateien = [
|
|
basis / "pyproject.toml",
|
|
basis / "paket" / "__init__.py",
|
|
basis / "paket" / "modul.py",
|
|
basis / "paket" / "modul_syntax.py",
|
|
basis / "paket" / "modul_zeit.py",
|
|
basis / "paket" / "modul_crlf.py",
|
|
basis / "paket" / "modul_binaer.py",
|
|
basis / "tests" / "__init__.py",
|
|
basis / "tests" / "test_modul.py",
|
|
basis / "tests" / "test_modul_syntax.py",
|
|
basis / "tests" / "test_zeit.py",
|
|
basis / "tests" / "test_faelle.py",
|
|
basis / "tests" / "test_abdichtung.py",
|
|
]
|
|
return getrackte_dateien
|
|
|
|
|
|
# --- TOML-Bausteine -----------------------------------------------------
|
|
|
|
def kopf(python=INTERPRETER, zusatz=""):
|
|
return "format = 1\npython = \"{0}\"\n{1}\n".format(python, zusatz)
|
|
|
|
|
|
def mutant_d1_d2():
|
|
return (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"D1\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
"\n"
|
|
"[[mutant]]\n"
|
|
"id = \"D2\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return \"gross\"\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return \"klein\"\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f2_gross\"]\n"
|
|
)
|
|
|
|
|
|
def mutant_luecke(id_, rot_bei_liste):
|
|
rot_bei_text = ", ".join('"{0}"'.format(x) for x in rot_bei_liste)
|
|
return (
|
|
"[[mutant]]\n"
|
|
"id = \"{0}\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = [{1}]\n"
|
|
).format(id_, rot_bei_text)
|
|
|
|
|
|
def mutant_korrekt(id_):
|
|
return (
|
|
"[[mutant]]\n"
|
|
"id = \"{0}\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return \"gross\"\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return \"klein\"\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f2_gross\"]\n"
|
|
).format(id_)
|
|
|
|
|
|
# --- Faelle ---------------------------------------------------------------
|
|
|
|
def fall_01(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = (
|
|
ergebnis.returncode == 0
|
|
and ergebnis.stdout.count(" OK ") >= 2
|
|
and "D1 OK" in ergebnis.stdout
|
|
and "D2 OK" in ergebnis.stdout
|
|
)
|
|
melde("Fall 1", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_02(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + mutant_luecke("L1", ["tests/test_modul.py::test_f2_gross", "tests/test_modul.py::test_f1_positiv"]))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 1 and "L1 LUECKE" in ergebnis.stdout
|
|
melde("Fall 2", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_03(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + mutant_luecke("L2", ["tests/test_modul.py::test_f1_positiv", "tests/test_modul.py::test_f2_gross"]))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 1 and "L2 LUECKE" in ergebnis.stdout
|
|
melde("Fall 3", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_04(basis, projektpfad):
|
|
toml_a = projektpfad / "mutanten_4a.toml"
|
|
schreibe_toml(toml_a, kopf() + mutant_luecke("L3", ["tests/test_modul.py::test_f2_gross", "tests/test_modul.py::test_f1_positiv"]) + "\n" + mutant_korrekt("K3"))
|
|
ergebnis_a = starte_laeufer(basis, toml_a)
|
|
ok_a = (
|
|
ergebnis_a.returncode == 1
|
|
and "L3 LUECKE" in ergebnis_a.stdout
|
|
and "K3 OK" in ergebnis_a.stdout
|
|
)
|
|
|
|
toml_b = projektpfad / "mutanten_4b.toml"
|
|
schreibe_toml(toml_b, kopf() + mutant_korrekt("K4") + "\n" + mutant_luecke("L4", ["tests/test_modul.py::test_f2_gross", "tests/test_modul.py::test_f1_positiv"]))
|
|
ergebnis_b = starte_laeufer(basis, toml_b)
|
|
ok_b = (
|
|
ergebnis_b.returncode == 1
|
|
and "L4 LUECKE" in ergebnis_b.stdout
|
|
and "K4 OK" in ergebnis_b.stdout
|
|
)
|
|
|
|
melde("Fall 4", ok_a and ok_b, "a: exit={0}\n{1}\nb: exit={2}\n{3}".format(
|
|
ergebnis_a.returncode, ergebnis_a.stdout, ergebnis_b.returncode, ergebnis_b.stdout))
|
|
|
|
|
|
def fall_05(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M5\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return None\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 1 and "M5 MUSTER" in ergebnis.stdout and "Trefferzahl=0" in ergebnis.stdout
|
|
melde("Fall 5", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_06(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M6\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" pass # platzhalter\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" pass\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 1 and "M6 MUSTER" in ergebnis.stdout and "Trefferzahl=2" in ergebnis.stdout
|
|
melde("Fall 6", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_07(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M7\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 1 and "M7 MUSTER" in ergebnis.stdout and "Trefferzahl=0" in ergebnis.stdout
|
|
melde("Fall 7", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_08(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M8\"\n"
|
|
"datei = \"paket/modul_crlf.py\"\n"
|
|
"aus = '''\n"
|
|
" return n\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return 0\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 1 and "M8 MUSTER" in ergebnis.stdout and "Datei hat CR" in ergebnis.stdout
|
|
melde("Fall 8", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_09(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M9\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" if y >= 10:\n"
|
|
" return \"gross\"\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" if y >= 10:\n"
|
|
" return \"mittel\"\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f2_gross\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 0 and "M9 OK" in ergebnis.stdout
|
|
melde("Fall 9", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_10(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M10\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" resultat = a + b\n"
|
|
"'''\n"
|
|
"wird = \"\"\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f5_summe\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 0 and "M10 OK" in ergebnis.stdout
|
|
melde("Fall 10", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_11(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M11\"\n"
|
|
"datei = \"paket/modul_syntax.py\"\n"
|
|
"aus = '''\n"
|
|
" return n * 2\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return n *\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul_syntax.py::test_f6\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = (
|
|
ergebnis.returncode == 0
|
|
and "M11 OK" in ergebnis.stdout
|
|
and "Sammelfehler" in ergebnis.stdout
|
|
)
|
|
melde("Fall 11", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_12(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M12\"\n"
|
|
"datei = \"paket/modul_zeit.py\"\n"
|
|
"aus = '''\n"
|
|
" return n + 1\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" while True:\n"
|
|
" pass\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_zeit.py::test_f7\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml, zeitlimit=5)
|
|
ok = ergebnis.returncode == 1 and "M12 ZEIT" in ergebnis.stdout
|
|
melde("Fall 12", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_13(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_luecke("M13", ["tests/test_faelle.py::test_immer_rot"]))
|
|
schreibe_toml(toml, kopf() + mutant_luecke("M13", ["tests/test_faelle.py::test_immer_rot"]))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 13", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_14(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + mutant_luecke("M14", ["tests/test_faelle.py::test_immer_uebersprungen"]))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 14", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_15(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + mutant_luecke("M15", ["tests/test_faelle.py::test_wird_abgewaehlt"]))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ausgabe = ergebnis.stdout + ergebnis.stderr
|
|
ok = ergebnis.returncode == 2 and ("abgew" in ausgabe.lower() or "ABGEWAEHLT" in ausgabe)
|
|
melde("Fall 15", ok, "exit={0}\n{1}".format(ergebnis.returncode, ausgabe))
|
|
|
|
|
|
def fall_16(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + mutant_luecke("M16", ["tests/test_modul.py::test_gibt_es_nicht"]))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 16", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_17(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
|
|
git_ordner = basis / ".git"
|
|
git_ordner.mkdir()
|
|
ergebnis_ordner = starte_laeufer(basis, toml)
|
|
git_ordner.rmdir()
|
|
ok_ordner = ergebnis_ordner.returncode == 2
|
|
|
|
git_datei = basis / ".git"
|
|
git_datei.write_text("kein echtes repo", encoding="utf-8")
|
|
ergebnis_datei = starte_laeufer(basis, toml)
|
|
git_datei.unlink()
|
|
ok_datei = ergebnis_datei.returncode == 2
|
|
|
|
melde("Fall 17", ok_ordner and ok_datei, "ordner exit={0}, datei exit={1}".format(
|
|
ergebnis_ordner.returncode, ergebnis_datei.returncode))
|
|
|
|
|
|
def fall_18(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf(zusatz="ohne = [\"geheim/geheimnis.toml\"]\n") + mutant_d1_d2().split("\n", 2)[-1])
|
|
|
|
geheim_ordner = basis / "geheim"
|
|
geheim_ordner.mkdir()
|
|
(geheim_ordner / "geheimnis.toml").write_text("x = 1\n", encoding="utf-8")
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
shutil.rmtree(geheim_ordner)
|
|
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 18", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_19(basis, projektpfad):
|
|
toml_a = projektpfad / "mutanten_19a.toml"
|
|
schreibe_toml(toml_a, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M19a\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rotbei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis_a = starte_laeufer(basis, toml_a)
|
|
ok_a = ergebnis_a.returncode == 2
|
|
|
|
toml_b = projektpfad / "mutanten_19b.toml"
|
|
schreibe_toml(toml_b, "unbekannt = 1\n" + mutant_d1_d2())
|
|
ergebnis_b = starte_laeufer(basis, toml_b)
|
|
ok_b = ergebnis_b.returncode == 2
|
|
|
|
melde("Fall 19", ok_a and ok_b, "a exit={0}, b exit={1}".format(ergebnis_a.returncode, ergebnis_b.returncode))
|
|
|
|
|
|
def fall_20(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + mutant_korrekt("GLEICH") + "\n" + mutant_korrekt("GLEICH"))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 20", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_21(basis, projektpfad):
|
|
toml_a = projektpfad / "mutanten_21a.toml"
|
|
schreibe_toml(toml_a, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M21a\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = []\n"
|
|
))
|
|
ergebnis_a = starte_laeufer(basis, toml_a)
|
|
ok_a = ergebnis_a.returncode == 2
|
|
|
|
toml_b = projektpfad / "mutanten_21b.toml"
|
|
schreibe_toml(toml_b, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M21b\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = \"tests/test_modul.py::test_f1_positiv\"\n"
|
|
))
|
|
ergebnis_b = starte_laeufer(basis, toml_b)
|
|
ok_b = ergebnis_b.returncode == 2
|
|
|
|
melde("Fall 21", ok_a and ok_b, "a exit={0}, b exit={1}".format(ergebnis_a.returncode, ergebnis_b.returncode))
|
|
|
|
|
|
def fall_22(basis, projektpfad):
|
|
varianten = {
|
|
"22a (..)": "paket/../paket/modul.py",
|
|
"22b (absolut)": "/etc/passwd",
|
|
"22c (fehlt)": "paket/existiert_nicht.py",
|
|
}
|
|
alle_ok = True
|
|
zusatz = []
|
|
for name, datei in varianten.items():
|
|
toml = projektpfad / "mutanten_{0}.toml".format(name.split()[0])
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M22\"\n"
|
|
"datei = \"{0}\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
).format(datei))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
alle_ok = alle_ok and ok
|
|
zusatz.append("{0}: exit={1}".format(name, ergebnis.returncode))
|
|
melde("Fall 22", alle_ok, "; ".join(zusatz))
|
|
|
|
|
|
def fall_23(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M23\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = [\"UNBEKANNT::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 23", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_24(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M24\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" return True\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 24", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_25(basis, projektpfad):
|
|
zusatz = []
|
|
alle_ok = True
|
|
|
|
toml = projektpfad / "mutanten_25a.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M25a\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = \"\"\n"
|
|
"wird = '''\n"
|
|
" return False\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
e = starte_laeufer(basis, toml)
|
|
ok = e.returncode == 2
|
|
alle_ok = alle_ok and ok
|
|
zusatz.append("aus leer: exit={0}".format(e.returncode))
|
|
|
|
toml = projektpfad / "mutanten_25b.toml"
|
|
schreibe_toml(toml, "format = 2\npython = \"{0}\"\n\n".format(INTERPRETER) + mutant_korrekt("M25b"))
|
|
e = starte_laeufer(basis, toml)
|
|
ok = e.returncode == 2
|
|
alle_ok = alle_ok and ok
|
|
zusatz.append("format=2: exit={0}".format(e.returncode))
|
|
|
|
toml = projektpfad / "mutanten_25c.toml"
|
|
schreibe_toml(toml, "python = \"{0}\"\n\n".format(INTERPRETER) + mutant_korrekt("M25c"))
|
|
e = starte_laeufer(basis, toml)
|
|
ok = e.returncode == 2
|
|
alle_ok = alle_ok and ok
|
|
zusatz.append("format fehlt: exit={0}".format(e.returncode))
|
|
|
|
toml = projektpfad / "mutanten_25d.toml"
|
|
schreibe_toml(toml, "format = 1\npython = \"bin/python\"\n\n" + mutant_korrekt("M25d"))
|
|
e = starte_laeufer(basis, toml)
|
|
ok = e.returncode == 2
|
|
alle_ok = alle_ok and ok
|
|
zusatz.append("python relativ: exit={0}".format(e.returncode))
|
|
|
|
toml = projektpfad / "mutanten_25e.toml"
|
|
schreibe_toml(toml, "format = 1\npython = \"/nicht/vorhanden/python\"\n\n" + mutant_korrekt("M25e"))
|
|
e = starte_laeufer(basis, toml)
|
|
ok = e.returncode == 2
|
|
alle_ok = alle_ok and ok
|
|
zusatz.append("python existiert nicht: exit={0}".format(e.returncode))
|
|
|
|
melde("Fall 25", alle_ok, "; ".join(zusatz))
|
|
|
|
|
|
def fall_26(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, "format = 1\npython = \"{0}\"\n[[mutant\n".format(INTERPRETER))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 26", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_27(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + "mutant = []\n")
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 27", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_28(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(basis, toml, zusatzargumente=["--nur", "UNBEKANNTEID"])
|
|
ok = ergebnis.returncode == 2
|
|
melde("Fall 28", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_29(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(basis, toml, zusatzargumente=["--nur", "D1"])
|
|
ok = (
|
|
ergebnis.returncode == 0
|
|
and "D1 OK" in ergebnis.stdout
|
|
and "D2" not in ergebnis.stdout.replace("mutanten gesamt: 2, ausgewaehlt: 1", "")
|
|
)
|
|
melde("Fall 29", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_30(basis, projektpfad):
|
|
toml_sauber = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml_sauber, mutant_d1_d2())
|
|
e1 = starte_laeufer(basis, toml_sauber, zusatzargumente=["--nur-muster"])
|
|
ok1 = e1.returncode == 0
|
|
|
|
toml_luecke = projektpfad / "mutanten_6.toml"
|
|
schreibe_toml(toml_luecke, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M6\"\n"
|
|
"datei = \"paket/modul.py\"\n"
|
|
"aus = '''\n"
|
|
" pass # platzhalter\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" pass\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
e2 = starte_laeufer(basis, toml_luecke, zusatzargumente=["--nur-muster"])
|
|
ok2 = e2.returncode == 1
|
|
|
|
melde("Fall 30", ok1 and ok2, "sauber exit={0}, fall6 exit={1}".format(e1.returncode, e2.returncode))
|
|
|
|
|
|
def fall_31(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M31\"\n"
|
|
"datei = \"paket/modul_binaer.py\"\n"
|
|
"aus = '''\n"
|
|
" irrelevant\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" irrelevant2\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ausgabe = ergebnis.stdout + ergebnis.stderr
|
|
ok = ergebnis.returncode in (1, 2) and ("UTF-8" in ausgabe or "utf-8" in ausgabe)
|
|
melde("Fall 31", ok, "exit={0}\n{1}".format(ergebnis.returncode, ausgabe))
|
|
|
|
|
|
def fall_32(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M32\"\n"
|
|
"datei = \"tests/test_abdichtung.py\"\n"
|
|
"aus = '''\n"
|
|
" assert \"mutanten_lauf_home_\" in home and proxy == \"http://127.0.0.1:9\"\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" assert False\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_abdichtung.py::test_abdichtung_home_und_proxy\"]\n"
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 0 and "M32 OK" in ergebnis.stdout
|
|
melde("Fall 32", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
|
|
|
|
def fall_33(basis, projektpfad):
|
|
import json as jsonmod
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_luecke("L1", ["tests/test_modul.py::test_f2_gross", "tests/test_modul.py::test_f1_positiv"]))
|
|
schreibe_toml(toml, kopf() + mutant_luecke("L1", ["tests/test_modul.py::test_f2_gross", "tests/test_modul.py::test_f1_positiv"]))
|
|
bericht_pfad = projektpfad / "bericht_33.json"
|
|
ergebnis = starte_laeufer(basis, toml, bericht_pfad=bericht_pfad)
|
|
ok = ergebnis.returncode == 1
|
|
if ok:
|
|
try:
|
|
daten = jsonmod.loads(bericht_pfad.read_text(encoding="utf-8"))
|
|
ok = (
|
|
isinstance(daten, dict)
|
|
and any(m.get("id") == "L1" and m.get("status") == "LUECKE" for m in daten.get("mutanten", []))
|
|
)
|
|
except (ValueError, OSError) as exc:
|
|
ok = False
|
|
ergebnis = subprocess.CompletedProcess(ergebnis.args, ergebnis.returncode, ergebnis.stdout + "\nJSON-Fehler: " + str(exc), ergebnis.stderr)
|
|
melde("Fall 33", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
try:
|
|
bericht_pfad.unlink()
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
def fall_34(basis, projektpfad):
|
|
# Ein blosser Symlink auf die Python-Binaerdatei bricht die
|
|
# Venv-Erkennung (site.py sucht pyvenv.cfg im Verzeichnis des
|
|
# Symlinks, nicht im Zielverzeichnis) - pytest waere dann nicht
|
|
# importierbar. Deshalb wird das ganze Venv-Verzeichnis verlinkt.
|
|
alt_python_dir = Path(tempfile.mkdtemp(prefix="mutanten_lauf_fall34_"))
|
|
venv_verzeichnis = Path(INTERPRETER).parent.parent
|
|
venv_link = alt_python_dir / "venv_alt"
|
|
alt_python = venv_link / "bin" / "python"
|
|
try:
|
|
os.symlink(venv_verzeichnis, venv_link)
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, (
|
|
kopf(zusatz="\n[python_je_datei]\n\"tests/test_modul.py\" = \"{0}\"\n".format(alt_python)) +
|
|
mutant_d1_d2().split("\n", 2)[-1]
|
|
))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ok = ergebnis.returncode == 0 and "D1 OK" in ergebnis.stdout and "D2 OK" in ergebnis.stdout
|
|
melde("Fall 34", ok, "exit={0}\n{1}".format(ergebnis.returncode, ergebnis.stdout))
|
|
finally:
|
|
shutil.rmtree(alt_python_dir, ignore_errors=True)
|
|
|
|
|
|
def fall_35(basis, projektpfad):
|
|
# N11: Ein Mutant haengt (Endlosschleife), der Laeufer selbst bekommt nach wenigen
|
|
# Sekunden SIGTERM. Erwartet: der Laeufer raeumt auf und meldet ABBRUCH: SIGTERM,
|
|
# kein pytest-Prozess dieses Falls bleibt am Leben, keine Temp-Reste bleiben liegen,
|
|
# und die Zieldatei ist danach byte-gleich mit dem Ausgangsstand (generisch geprueft
|
|
# durch pruefe_unveraendert im main()).
|
|
toml = projektpfad / "mutanten_35.toml"
|
|
schreibe_toml(toml, (
|
|
kopf() +
|
|
"[[mutant]]\n"
|
|
"id = \"M35\"\n"
|
|
"datei = \"paket/modul_zeit.py\"\n"
|
|
"aus = '''\n"
|
|
" return n + 1\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
" while True:\n"
|
|
" pass\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_zeit.py::test_f7\"]\n"
|
|
))
|
|
|
|
eigenes_tmpdir = Path(tempfile.mkdtemp(prefix="mutanten_lauf_fall35_tmpdir_"))
|
|
env = os.environ.copy()
|
|
env["TMPDIR"] = str(eigenes_tmpdir)
|
|
cmd = [INTERPRETER, str(LAEUFER), "--wurzel", str(basis), "--mutanten", str(toml), "--zeitlimit", "60"]
|
|
ok = True
|
|
zusatz = ""
|
|
try:
|
|
proc = subprocess.Popen(cmd, cwd=str(basis), env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
|
time.sleep(3)
|
|
proc.send_signal(signal.SIGTERM)
|
|
try:
|
|
stdout, _ = proc.communicate(timeout=30)
|
|
except subprocess.TimeoutExpired:
|
|
proc.kill()
|
|
stdout, _ = proc.communicate()
|
|
ok = False
|
|
zusatz += "Laeufer endete nicht nach SIGTERM; "
|
|
|
|
if "ABBRUCH: SIGTERM" not in stdout:
|
|
ok = False
|
|
zusatz += "keine ABBRUCH-Zeile; "
|
|
|
|
# kein Prozess dieses Falls lebt danach: Sein HOME liegt unter eigenes_tmpdir,
|
|
# und diese Umgebungsvariable geht an den pytest-Unterprozess mit.
|
|
uebrige_pids = []
|
|
for envdatei in Path("/proc").glob("[0-9]*/environ"):
|
|
try:
|
|
inhalt = envdatei.read_bytes()
|
|
except OSError:
|
|
continue
|
|
if str(eigenes_tmpdir).encode() in inhalt:
|
|
uebrige_pids.append(envdatei.parent.name)
|
|
if uebrige_pids:
|
|
ok = False
|
|
zusatz += "Prozesse mit HOME im Fall-Tmpdir noch da: {0}; ".format(uebrige_pids)
|
|
|
|
# keine Temp-Ordner und keine Ergebnisdatei aus diesem Lauf
|
|
uebrige_ordner = (
|
|
list(eigenes_tmpdir.glob("mutanten_lauf_home_*"))
|
|
+ list(eigenes_tmpdir.glob("mutanten_lauf_plugin_*"))
|
|
+ list(eigenes_tmpdir.glob("mutanten_lauf_ergebnis_*"))
|
|
)
|
|
if uebrige_ordner:
|
|
ok = False
|
|
zusatz += "Temp-Reste: {0}; ".format(uebrige_ordner)
|
|
|
|
melde("Fall 35", ok, zusatz + "stdout(Ende)={0}".format(stdout[-500:]))
|
|
finally:
|
|
shutil.rmtree(eigenes_tmpdir, ignore_errors=True)
|
|
|
|
|
|
# --- Faelle 36 bis 40: pytest-Konfiguration ---------------------------------
|
|
|
|
FREMDE_KONFIG = (
|
|
"[tool.pytest.ini_options]\n"
|
|
"addopts = \"--new-first --failed-first\"\n"
|
|
)
|
|
|
|
FREMDES_CONFTEST = "raise RuntimeError(\"fremdes conftest oberhalb der Wurzel geladen\")\n"
|
|
|
|
|
|
def baue_rahmen(eigene_konfig, fremde_konfig, fremdes_conftest):
|
|
rahmen = Path(tempfile.mkdtemp(prefix="mutanten_lauf_treiber_rahmen_"))
|
|
if fremde_konfig:
|
|
(rahmen / "pyproject.toml").write_text(FREMDE_KONFIG, encoding="utf-8")
|
|
if fremdes_conftest:
|
|
(rahmen / "conftest.py").write_text(FREMDES_CONFTEST, encoding="utf-8")
|
|
wurzel = rahmen / "k"
|
|
dateien = baue_projekt(wurzel)
|
|
if not eigene_konfig:
|
|
(wurzel / "pyproject.toml").unlink()
|
|
dateien = [p for p in dateien if p != wurzel / "pyproject.toml"]
|
|
return rahmen, wurzel, dateien
|
|
|
|
|
|
def fall_36(basis, projektpfad):
|
|
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=False, fremde_konfig=True, fremdes_conftest=False)
|
|
try:
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(wurzel, toml)
|
|
ok = (
|
|
ergebnis.returncode == 0
|
|
and "D1 OK" in ergebnis.stdout
|
|
and "D2 OK" in ergebnis.stdout
|
|
and "keine in der Wurzel, leere Datei" in ergebnis.stdout
|
|
)
|
|
melde("Fall 36", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
|
|
pruefe_unveraendert(dateien, snap, "Fall 36")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
def fall_37(basis, projektpfad):
|
|
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=True, fremdes_conftest=False)
|
|
try:
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
sauber = starte_laeufer(wurzel, toml)
|
|
schreibe_toml(toml, kopf() + mutant_luecke("M37", ["tests/test_faelle.py::test_wird_abgewaehlt"]))
|
|
abgewaehlt = starte_laeufer(wurzel, toml)
|
|
ausgabe = abgewaehlt.stdout + abgewaehlt.stderr
|
|
ok = (
|
|
sauber.returncode == 0
|
|
and "D1 OK" in sauber.stdout
|
|
and "D2 OK" in sauber.stdout
|
|
and "k/pyproject.toml (aus der Wurzel)" in sauber.stdout
|
|
and abgewaehlt.returncode == 2
|
|
and "ABGEWAEHLT" in ausgabe
|
|
)
|
|
melde("Fall 37", ok, "exit={0}/{1}\n{2}{3}\n{4}".format(
|
|
sauber.returncode, abgewaehlt.returncode, sauber.stdout, sauber.stderr, ausgabe))
|
|
pruefe_unveraendert(dateien, snap, "Fall 37")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
def fall_38(basis, projektpfad):
|
|
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=False, fremdes_conftest=False)
|
|
try:
|
|
(wurzel / "pyproject.toml").write_text(
|
|
"[tool.pytest.ini_options]\n"
|
|
"addopts = \"--gibt-es-nicht\"\n",
|
|
encoding="utf-8",
|
|
)
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(wurzel, toml)
|
|
ausgabe = ergebnis.stdout + ergebnis.stderr
|
|
ok = (
|
|
ergebnis.returncode == 2
|
|
and "pytest-Bedienfehler (Rueckgabewert 4)" in ausgabe
|
|
and "--gibt-es-nicht" in ausgabe
|
|
and "ist FEHLT" not in ausgabe
|
|
)
|
|
melde("Fall 38", ok, "exit={0}\n{1}".format(ergebnis.returncode, ausgabe))
|
|
pruefe_unveraendert(dateien, snap, "Fall 38")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
def fall_39(basis, projektpfad):
|
|
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=False, fremde_konfig=False, fremdes_conftest=True)
|
|
try:
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(wurzel, toml)
|
|
ok = ergebnis.returncode == 0 and "D1 OK" in ergebnis.stdout and "D2 OK" in ergebnis.stdout
|
|
melde("Fall 39", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
|
|
pruefe_unveraendert(dateien, snap, "Fall 39")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
def fall_40(basis, projektpfad):
|
|
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=False, fremdes_conftest=False)
|
|
try:
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + (
|
|
"[[mutant]]\n"
|
|
"id = \"M40\"\n"
|
|
"datei = \"pyproject.toml\"\n"
|
|
"aus = '''\n"
|
|
"addopts = '-m \"not integration\"'\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
"addopts = \"--gibt-es-nicht\"\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
bericht = rahmen / "bericht40.json"
|
|
ergebnis = starte_laeufer(wurzel, toml, bericht_pfad=bericht)
|
|
mutantenzeile = [z for z in ergebnis.stdout.split("\n") if z.startswith("M40 ")]
|
|
try:
|
|
ausgabe_bericht = json.loads(bericht.read_text(encoding="utf-8"))["mutanten"][0]["tests"][0].get("ausgabe", "")
|
|
except (OSError, ValueError, KeyError, IndexError):
|
|
ausgabe_bericht = ""
|
|
ok = (
|
|
ergebnis.returncode == 1
|
|
and len(mutantenzeile) == 1
|
|
and "M40 UNKLAR" in mutantenzeile[0]
|
|
and "pytest-Bedienfehler (Rueckgabewert 4)" in mutantenzeile[0]
|
|
and "--gibt-es-nicht" in mutantenzeile[0]
|
|
and "Ende der Ausgabe:" in ausgabe_bericht
|
|
)
|
|
melde("Fall 40", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
|
|
pruefe_unveraendert(dateien, snap, "Fall 40")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
def fall_41(basis, projektpfad):
|
|
rahmen = Path(tempfile.mkdtemp(prefix="mutanten_lauf_treiber_symlink_"))
|
|
try:
|
|
dateien = baue_projekt(rahmen / "echt" / "k")
|
|
os.symlink(rahmen / "echt", rahmen / "link")
|
|
wurzel = rahmen / "link" / "k"
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(wurzel, toml)
|
|
ok = ergebnis.returncode == 0 and "D1 OK" in ergebnis.stdout and "D2 OK" in ergebnis.stdout
|
|
melde("Fall 41", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
|
|
pruefe_unveraendert(dateien, snap, "Fall 41")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
def fall_42(basis, projektpfad):
|
|
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=False, fremdes_conftest=False)
|
|
try:
|
|
(wurzel / "conftest.py").write_text(
|
|
"def pytest_collection_modifyitems(items):\n"
|
|
" raise RuntimeError(\"interner Fehler fuer Fall 42\")\n",
|
|
encoding="utf-8",
|
|
)
|
|
dateien = dateien + [wurzel / "conftest.py"]
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, mutant_d1_d2())
|
|
ergebnis = starte_laeufer(wurzel, toml)
|
|
ausgabe = ergebnis.stdout + ergebnis.stderr
|
|
ok = (
|
|
ergebnis.returncode == 2
|
|
and "pytest-Abbruch mit internem Fehler (Rueckgabewert 3)" in ausgabe
|
|
and "interner Fehler fuer Fall 42" in ausgabe
|
|
and "ist FEHLT" not in ausgabe
|
|
)
|
|
melde("Fall 42", ok, "exit={0}\n{1}".format(ergebnis.returncode, ausgabe))
|
|
pruefe_unveraendert(dateien, snap, "Fall 42")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
def fall_43(basis, projektpfad):
|
|
arbeit = Path(tempfile.mkdtemp(prefix="mutanten_lauf_treiber_probe_"))
|
|
try:
|
|
ergebnis = subprocess.run(
|
|
[INTERPRETER, str(PROBE), str(LAEUFER), str(arbeit)],
|
|
capture_output=True, text=True, timeout=900,
|
|
)
|
|
ok = ergebnis.returncode == 0 and ", abweichungen: 0" in ergebnis.stdout
|
|
melde("Fall 43", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
|
|
finally:
|
|
shutil.rmtree(arbeit, ignore_errors=True)
|
|
|
|
|
|
def fall_44(basis, projektpfad):
|
|
zweit_dir = Path(tempfile.mkdtemp(prefix="mutanten_lauf_fall44_"))
|
|
venv_link = zweit_dir / "venv_zwei"
|
|
zweit_python = venv_link / "bin" / "python"
|
|
try:
|
|
os.symlink(Path(INTERPRETER).parent.parent, venv_link)
|
|
toml = projektpfad / "mutanten.toml"
|
|
zusatz = "\n[python_je_datei]\n\"tests/test_zeit.py\" = \"{0}\"\n".format(zweit_python)
|
|
befund_zwei = "Grundlinie: pytest-Bedienfehler (Rueckgabewert 4) mit {0};".format(zweit_python)
|
|
befund_eins = "Grundlinie: pytest-Bedienfehler (Rueckgabewert 4) mit {0};".format(INTERPRETER)
|
|
reihenfolgen = [
|
|
["tests/test_zeit.py::test_gibt_es_nicht", "tests/test_modul.py::test_f1_positiv"],
|
|
["tests/test_modul.py::test_f1_positiv", "tests/test_zeit.py::test_gibt_es_nicht"],
|
|
]
|
|
for stelle, rot_bei in zip(("erste", "letzte"), reihenfolgen):
|
|
schreibe_toml(toml, kopf(zusatz=zusatz) + mutant_luecke("M44", rot_bei))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ausgabe = ergebnis.stdout + ergebnis.stderr
|
|
kopfzeilen = [z for z in ergebnis.stdout.split("\n") if z.startswith("pytest-konfiguration (Grundlinie, ")]
|
|
ok = (
|
|
ergebnis.returncode == 2
|
|
and ausgabe.count(befund_zwei) == 1
|
|
and befund_eins not in ausgabe
|
|
and "ist FEHLT" not in ausgabe
|
|
and "test_f1_positiv ist" not in ausgabe
|
|
and len(kopfzeilen) == 2
|
|
and sum(1 for z in kopfzeilen if z.startswith("pytest-konfiguration (Grundlinie, {0}): ".format(INTERPRETER))) == 1
|
|
and sum(1 for z in kopfzeilen if z.startswith("pytest-konfiguration (Grundlinie, {0}): ".format(zweit_python))) == 1
|
|
)
|
|
melde("Fall 44 ({0} Stelle)".format(stelle), ok, "exit={0}\n{1}".format(ergebnis.returncode, ausgabe))
|
|
finally:
|
|
shutil.rmtree(zweit_dir, ignore_errors=True)
|
|
|
|
|
|
def fall_45(basis, projektpfad):
|
|
toml = projektpfad / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + mutant_luecke("M45", ["tests/" + "a" * 300 + ".py::test_x"]))
|
|
ergebnis = starte_laeufer(basis, toml)
|
|
ausgabe = ergebnis.stdout + ergebnis.stderr
|
|
ok = (
|
|
ergebnis.returncode == 2
|
|
and "Grundlinie: pytest-" in ausgabe
|
|
and "unerwarteter Abbruch" not in ausgabe
|
|
)
|
|
melde("Fall 45", ok, "exit={0}\n{1}".format(ergebnis.returncode, ausgabe[-2000:]))
|
|
|
|
|
|
def fall_46(basis, projektpfad):
|
|
rahmen, wurzel, dateien = baue_rahmen(eigene_konfig=True, fremde_konfig=False, fremdes_conftest=False)
|
|
try:
|
|
(wurzel / "conftest.py").write_text("X = 1\n", encoding="utf-8")
|
|
dateien = dateien + [wurzel / "conftest.py"]
|
|
snap = snapshot(dateien)
|
|
toml = rahmen / "mutanten.toml"
|
|
schreibe_toml(toml, kopf() + (
|
|
"[[mutant]]\n"
|
|
"id = \"M46\"\n"
|
|
"datei = \"conftest.py\"\n"
|
|
"aus = '''\n"
|
|
"X = 1\n"
|
|
"'''\n"
|
|
"wird = '''\n"
|
|
"def pytest_collection_modifyitems(items):\n"
|
|
" raise RuntimeError(\"intern Fall 46\")\n"
|
|
"'''\n"
|
|
"rot_bei = [\"tests/test_modul.py::test_f1_positiv\"]\n"
|
|
))
|
|
bericht = rahmen / "bericht46.json"
|
|
ergebnis = starte_laeufer(wurzel, toml, bericht_pfad=bericht)
|
|
mutantenzeile = [z for z in ergebnis.stdout.split("\n") if z.startswith("M46 ")]
|
|
try:
|
|
ausgabe_bericht = json.loads(bericht.read_text(encoding="utf-8"))["mutanten"][0]["tests"][0].get("ausgabe", "")
|
|
except (OSError, ValueError, KeyError, IndexError):
|
|
ausgabe_bericht = ""
|
|
ok = (
|
|
ergebnis.returncode == 1
|
|
and len(mutantenzeile) == 1
|
|
and "M46 UNKLAR" in mutantenzeile[0]
|
|
and "pytest-Abbruch mit internem Fehler (Rueckgabewert 3): " in mutantenzeile[0]
|
|
and "intern Fall 46" in mutantenzeile[0]
|
|
and "Ende der Ausgabe:" in ausgabe_bericht
|
|
)
|
|
melde("Fall 46", ok, "exit={0}\n{1}{2}".format(ergebnis.returncode, ergebnis.stdout, ergebnis.stderr))
|
|
pruefe_unveraendert(dateien, snap, "Fall 46")
|
|
finally:
|
|
shutil.rmtree(rahmen, ignore_errors=True)
|
|
|
|
|
|
FAELLE = [
|
|
fall_01, fall_02, fall_03, fall_04, fall_05, fall_06, fall_07, fall_08, fall_09, fall_10,
|
|
fall_11, fall_12, fall_13, fall_14, fall_15, fall_16, fall_17, fall_18, fall_19, fall_20,
|
|
fall_21, fall_22, fall_23, fall_24, fall_25, fall_26, fall_27, fall_28, fall_29, fall_30,
|
|
fall_31, fall_32, fall_33, fall_34, fall_35, fall_36, fall_37, fall_38, fall_39, fall_40,
|
|
fall_41, fall_42, fall_43, fall_44, fall_45, fall_46,
|
|
]
|
|
|
|
|
|
def main():
|
|
basis = Path(tempfile.mkdtemp(prefix="mutanten_lauf_treiber_"))
|
|
projektpfad = basis
|
|
try:
|
|
getrackte_dateien = baue_projekt(basis)
|
|
snap = snapshot(getrackte_dateien)
|
|
|
|
for funktion in FAELLE:
|
|
vor_lauf = len(FEHLERLISTE)
|
|
try:
|
|
funktion(basis, projektpfad)
|
|
except Exception as exc:
|
|
melde(funktion.__name__, False, "Ausnahme: {0}".format(exc))
|
|
pruefe_unveraendert(getrackte_dateien, snap, funktion.__name__)
|
|
|
|
anzahl_fehler = len(FEHLERLISTE)
|
|
print("gerechnete Fehler: {0}".format(anzahl_fehler))
|
|
return 0 if anzahl_fehler == 0 else 1
|
|
finally:
|
|
shutil.rmtree(basis, ignore_errors=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|