Mutantenstufe der Plankritik nach der Pruefung vom 2026-09-28: Netzsperre per unshare, ungepufferte Ausgabe, Zeitlimit je Mutant 120 s, Aufraeumen bei SIGTERM, Modus-Zeile, mutanten_status, Faelle und Treiberfall 35

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-09-28 08:19:23 +02:00
1 parent bc9b4f4497
commit b1ad461acf
8 files changed
+336 -49

No files matched your search

@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Treiber fuer mutanten_lauf.py: 34 Angriffsfaelle auf einer Wegwerfkopie.
"""Treiber fuer mutanten_lauf.py: 35 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
@@ -11,14 +11,21 @@ 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).
"""
import hashlib
import os
import shutil
import signal
import subprocess
import sys
import tempfile
import time
from pathlib import Path
HIER = Path(__file__).resolve().parent
@@ -930,11 +937,84 @@ def fall_34(basis, projektpfad):
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 = [
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_31, fall_32, fall_33, fall_34, fall_35,
]