#!/usr/bin/env python3 """Treiber fuer mutanten_lauf.py: 34 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. """ import hashlib import os import shutil import subprocess import sys import tempfile from pathlib import Path HIER = Path(__file__).resolve().parent LAEUFER = HIER / "mutanten_lauf.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) 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, ] 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())