Mutantenlaeufer: pytest-Konfiguration wie pytest, nie oberhalb der Wurzel; Abbruch von pytest mit Ursache statt FEHLT; Gleichwertigkeitsprobe, Treiber 46 Faelle; zwei Pruefrunden
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]>
This commit is contained in:
1 parent
ed55848b00
commit
30ed04a643
8 files changed
+3457
-19
No files matched your search
@@ -24,6 +24,37 @@ absolutem Pfad ausserhalb von HOME sind nicht abgedichtet. Die eigentliche
|
||||
Sperre bleibt die Netzsperre der Testdateien selbst und eine Kopie ohne
|
||||
Geheimnisdateien (der Schluessel "ohne" in mutanten.toml).
|
||||
|
||||
pytest-Konfiguration (seit 2026-10-06): Fuer jeden pytest-Aufruf bestimmt das
|
||||
Skript die Konfigurationsdatei so, wie pytest 9.1.1 sie selbst suchen wuerde
|
||||
(_pytest/config/findpaths.py: determine_setup, locate_config,
|
||||
get_common_ancestor), sucht aber nie oberhalb der Wurzel, und gibt sie mit -c
|
||||
vor. Ohne Konfiguration in der Wurzel bekommt pytest eine leere Datei aus dem
|
||||
Plugin-Ordner, dazu --rootdir und --confcutdir. Grund: Unter dem Windows-Temp
|
||||
fand pytest eine fremde pyproject.toml, deren addopts nicht zu
|
||||
-p no:cacheprovider passten, und jeder Test der Grundlinie stand als FEHLT da
|
||||
(Plankritik 2 von C1 im Projekt After Install, 2026-10-05). Die Wurzel wird
|
||||
vorher mit realpath aufgeloest: pytest rechnet vom Arbeitsordner aus, den der
|
||||
Kernel ohne Verknuepfungen meldet; ueber einen Symlink angegeben, passten die
|
||||
Knotennamen sonst nicht (Pruefung vom 2026-10-06). Benannte Luecken: tox.ini
|
||||
und setup.cfg werden zeilenweise nach ihrem Abschnitt durchsucht wie in
|
||||
iniconfig 2.3.0 (_parse.py, _parseline), eine Datei, die iniconfig nicht lesen
|
||||
kann, uebergeht der Laeufer, pytest bricht dort mit 4 ab; ein setup.cfg mit
|
||||
[pytest] (bei pytest selbst ein Fehler) gilt hier als keine Konfiguration; ohne
|
||||
Konfiguration in der Wurzel ist config.inipath die leere Datei, ini-Werte vom
|
||||
Typ paths aus -o oder PYTEST_ADDOPTS gelten dann relativ zum Plugin-Ordner;
|
||||
iniconfig trennt Zeilen mit splitlines, der Laeufer auch. Liegt rootdir unter
|
||||
der Wurzel (Konfiguration oder setup.py in einem Unterordner der Tests),
|
||||
bildet pytest die Knotennamen relativ dazu, und rot_bei passt nicht mehr: Die
|
||||
Grundlinie meldet FEHLT, die Kopfzeile nennt die Datei.
|
||||
|
||||
Endet pytest mit Rueckgabewert 3 (interner Fehler) oder 4 (Bedienfehler,
|
||||
ExitCode in _pytest/config/__init__.py), steht das Ende seiner Ausgabe in der
|
||||
Grundlinie als eigener Befund je Interpreter und unter einem Mutanten, als
|
||||
eine Zeile mit der Ursache vorn, in der Meldung der Tests ohne Bericht und
|
||||
ohne Sammelfehler. Ein Sammelfehler bleibt ROT, auch wenn pytest dabei mit 4
|
||||
endet: Ein Knoten in einer Datei, die sich nicht sammeln laesst, ergibt
|
||||
zusaetzlich "found no collectors for" (Treiber-Fall 11).
|
||||
|
||||
Spezifikation: .vault-tools/plan-kopie/mutanten_lauf_ENTWURF.md
|
||||
|
||||
Entscheidungen, die der Entwurf offen liess (siehe Bericht des Executers):
|
||||
@@ -119,6 +150,20 @@ def pytest_deselected(items):
|
||||
TOP_LEVEL_SCHLUESSEL = {"format", "python", "ohne", "kuerzel", "python_je_datei", "mutant"}
|
||||
MUTANT_SCHLUESSEL = {"id", "datei", "aus", "wird", "rot_bei", "hinweis"}
|
||||
|
||||
# Reihenfolge wie config_names in locate_config, pytest 9.1.1.
|
||||
PYTEST_KONFIG_NAMEN = (
|
||||
"pytest.toml",
|
||||
".pytest.toml",
|
||||
"pytest.ini",
|
||||
".pytest.ini",
|
||||
"pyproject.toml",
|
||||
"tox.ini",
|
||||
"setup.cfg",
|
||||
)
|
||||
LEERE_KONFIG_NAME = "pytest.ini"
|
||||
PYTEST_ABBRUCH = {3: "pytest-Abbruch mit internem Fehler", 4: "pytest-Bedienfehler"}
|
||||
AUSGABE_ENDE_ZEILEN = 20
|
||||
|
||||
|
||||
class RuecksetzenGescheitert(Exception):
|
||||
def __init__(self, mutant_id, datei):
|
||||
@@ -427,6 +472,170 @@ def gruppiere_nach_interpreter(knoten_liste, standard_python, python_je_datei):
|
||||
return gruppen
|
||||
|
||||
|
||||
def _ini_hat_abschnitt(pfad, abschnitt):
|
||||
"""Suche nach der Abschnittszeile wie _parseline in iniconfig 2.3.0: "[" in
|
||||
Spalte 0, Kommentare nach # und ; entfernt, dann "]" am Ende. Eine nicht
|
||||
lesbare Datei zaehlt als Treffer, damit pytest den Fehler selbst meldet."""
|
||||
try:
|
||||
text = pfad.read_text(encoding="utf-8", errors="replace")
|
||||
except OSError:
|
||||
return True
|
||||
for zeile in text.splitlines():
|
||||
zeile = zeile.rstrip()
|
||||
if not zeile.startswith("["):
|
||||
continue
|
||||
for zeichen in "#;":
|
||||
zeile = zeile.split(zeichen)[0].rstrip()
|
||||
if zeile.endswith("]") and zeile[1:-1] == abschnitt:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _ist_pytest_konfig(pfad):
|
||||
"""Nachbau von load_config_dict_from_file(pfad) is not None, pytest 9.1.1."""
|
||||
name = pfad.name
|
||||
if name in ("pytest.toml", ".pytest.toml", "pytest.ini", ".pytest.ini"):
|
||||
return True
|
||||
if name == "tox.ini":
|
||||
return _ini_hat_abschnitt(pfad, "pytest")
|
||||
if name == "setup.cfg":
|
||||
return _ini_hat_abschnitt(pfad, "tool:pytest")
|
||||
if name == "pyproject.toml":
|
||||
try:
|
||||
daten = tomllib.loads(pfad.read_text(encoding="utf-8"))
|
||||
except (OSError, UnicodeDecodeError, tomllib.TOMLDecodeError):
|
||||
return True
|
||||
werkzeug = daten.get("tool", {})
|
||||
tool_pytest = werkzeug.get("pytest", {}) if isinstance(werkzeug, dict) else {}
|
||||
return isinstance(tool_pytest, dict) and bool(tool_pytest)
|
||||
return False
|
||||
|
||||
|
||||
def _innerhalb(pfad, wurzel):
|
||||
return pfad == wurzel or wurzel in pfad.parents
|
||||
|
||||
|
||||
def _existiert(pfad):
|
||||
"""Wie safe_exists in pytest 9.1.1: ein zu langer Name ist kein Absturz."""
|
||||
try:
|
||||
return pfad.exists()
|
||||
except (OSError, ValueError):
|
||||
return False
|
||||
|
||||
|
||||
def _konfig_suchen(startordner, wurzel):
|
||||
"""Nachbau von locate_config, pytest 9.1.1; die Suche endet an der Wurzel."""
|
||||
erstes_pyproject = None
|
||||
for start in startordner:
|
||||
for basis in (start, *start.parents):
|
||||
if not _innerhalb(basis, wurzel):
|
||||
break
|
||||
for name in PYTEST_KONFIG_NAMEN:
|
||||
pfad = basis / name
|
||||
if pfad.is_file():
|
||||
if name == "pyproject.toml" and erstes_pyproject is None:
|
||||
erstes_pyproject = pfad
|
||||
if _ist_pytest_konfig(pfad):
|
||||
return pfad
|
||||
return erstes_pyproject
|
||||
|
||||
|
||||
def _gemeinsamer_vorfahr(pfade, wurzel):
|
||||
"""Nachbau von get_common_ancestor, pytest 9.1.1, mit der Wurzel als Aufrufordner."""
|
||||
vorfahr = None
|
||||
for pfad in pfade:
|
||||
if not _existiert(pfad):
|
||||
continue
|
||||
if vorfahr is None:
|
||||
vorfahr = pfad
|
||||
elif vorfahr in pfad.parents or pfad == vorfahr:
|
||||
continue
|
||||
elif pfad in vorfahr.parents:
|
||||
vorfahr = pfad
|
||||
else:
|
||||
try:
|
||||
vorfahr = Path(os.path.commonpath([str(pfad), str(vorfahr)]))
|
||||
except ValueError:
|
||||
pass
|
||||
if vorfahr is None:
|
||||
return wurzel
|
||||
if vorfahr.is_file():
|
||||
return vorfahr.parent
|
||||
return vorfahr
|
||||
|
||||
|
||||
def pytest_konfig_bestimmen(wurzel, knoten_liste):
|
||||
"""Konfiguration und rootdir eines Aufrufs, wie determine_setup in pytest
|
||||
9.1.1 sie ohne -c bestimmt, aber nie oberhalb der Wurzel gesucht.
|
||||
Rueckgabe (konfig, rootdir); konfig ist None, wenn die Wurzel keine hat."""
|
||||
wurzel = Path(os.path.realpath(str(wurzel)))
|
||||
ordner = []
|
||||
for knoten in knoten_liste:
|
||||
pfad = wurzel / knoten.split("::", 1)[0]
|
||||
if _existiert(pfad):
|
||||
ordner.append(pfad if pfad.is_dir() else pfad.parent)
|
||||
vorfahr = _gemeinsamer_vorfahr(ordner, wurzel)
|
||||
if not _innerhalb(vorfahr, wurzel):
|
||||
vorfahr = wurzel
|
||||
konfig = _konfig_suchen([vorfahr], wurzel)
|
||||
if konfig is not None:
|
||||
return konfig, konfig.parent
|
||||
for basis in (vorfahr, *vorfahr.parents):
|
||||
if not _innerhalb(basis, wurzel):
|
||||
break
|
||||
if (basis / "setup.py").is_file():
|
||||
return None, basis
|
||||
if ordner != [vorfahr]:
|
||||
konfig = _konfig_suchen(ordner, wurzel)
|
||||
if konfig is not None:
|
||||
return konfig, konfig.parent
|
||||
return None, wurzel
|
||||
|
||||
|
||||
def pytest_konfig_argumente(wurzel, knoten_liste, leere_konfig):
|
||||
"""Die Konfiguration der Wurzel mit -c; sonst die leere Datei mit --rootdir
|
||||
und --confcutdir. Ohne --confcutdir laege confcutdir im Ordner der leeren
|
||||
Datei, und pytest laede conftest.py-Dateien oberhalb der Wurzel
|
||||
(_pytest/config/__init__.py, pytest 9.1.1)."""
|
||||
konfig, rootdir = pytest_konfig_bestimmen(wurzel, knoten_liste)
|
||||
if konfig is not None:
|
||||
return ["-c", str(konfig)]
|
||||
return ["-c", str(leere_konfig), "--rootdir", str(rootdir), "--confcutdir", str(rootdir)]
|
||||
|
||||
|
||||
def pytest_konfig_beschreibung(wurzel, knoten_liste):
|
||||
konfig, rootdir = pytest_konfig_bestimmen(wurzel, knoten_liste)
|
||||
if konfig is not None:
|
||||
return "{0} (aus der Wurzel)".format(konfig)
|
||||
return "keine in der Wurzel, leere Datei; rootdir {0}".format(rootdir)
|
||||
|
||||
|
||||
def ausgabe_ende(stdout_bytes, stderr_bytes):
|
||||
"""Die letzten nicht leeren Zeilen von stdout und stderr, je hoechstens 300 Zeichen."""
|
||||
text = (
|
||||
(stdout_bytes or b"").decode("utf-8", errors="replace")
|
||||
+ "\n"
|
||||
+ (stderr_bytes or b"").decode("utf-8", errors="replace")
|
||||
)
|
||||
zeilen = [zeile.rstrip()[:300] for zeile in text.split("\n") if zeile.strip()]
|
||||
return zeilen[-AUSGABE_ENDE_ZEILEN:]
|
||||
|
||||
|
||||
def kernzeile(ende, rueckgabe):
|
||||
"""Die Zeile, die die Ursache nennt: bei 4 die erste mit "error" ausser der
|
||||
Gebrauchszeile von argparse, bei 3 die letzte mit INTERNALERROR> (danach
|
||||
folgt noch die Summenzeile, Treiber-Fall 46), ohne Treffer die letzte."""
|
||||
if rueckgabe == 4:
|
||||
for zeile in ende:
|
||||
if "error" in zeile.lower() and not zeile.startswith("ERROR: usage:"):
|
||||
return zeile.strip()
|
||||
if rueckgabe == 3:
|
||||
interne = [zeile for zeile in ende if zeile.startswith("INTERNALERROR>")]
|
||||
if interne:
|
||||
return interne[-1].strip()
|
||||
return ende[-1].strip() if ende else "(keine Ausgabe)"
|
||||
|
||||
|
||||
def umgebung_abdichten(home_dir, plugin_dir, ergebnis_pfad):
|
||||
"""Abdichtung nach Entwurf, Abschnitt 5. Siehe Kopfkommentar fuer die Luecken."""
|
||||
env = os.environ.copy()
|
||||
@@ -448,10 +657,17 @@ def pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimi
|
||||
ergebnis_datei.close()
|
||||
ergebnis_pfad = ergebnis_datei.name
|
||||
env = umgebung_abdichten(home_dir, plugin_dir, ergebnis_pfad)
|
||||
cmd = [python_exe, "-m", "pytest", "-q", "-p", "no:cacheprovider", "-p", PLUGIN_NAME] + list(knoten_liste)
|
||||
konfig_argumente = pytest_konfig_argumente(wurzel, knoten_liste, Path(plugin_dir) / LEERE_KONFIG_NAME)
|
||||
cmd = (
|
||||
[python_exe, "-m", "pytest", "-q", "-p", "no:cacheprovider", "-p", PLUGIN_NAME]
|
||||
+ konfig_argumente
|
||||
+ list(knoten_liste)
|
||||
)
|
||||
|
||||
zeit_ueberschritten = False
|
||||
absturz = None
|
||||
abbruch = None
|
||||
ausgabe = (b"", b"")
|
||||
proc = None
|
||||
try:
|
||||
try:
|
||||
@@ -465,14 +681,14 @@ def pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimi
|
||||
|
||||
if proc is not None:
|
||||
try:
|
||||
proc.communicate(timeout=zeitlimit)
|
||||
ausgabe = proc.communicate(timeout=zeitlimit)
|
||||
except subprocess.TimeoutExpired:
|
||||
zeit_ueberschritten = True
|
||||
try:
|
||||
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
|
||||
except (ProcessLookupError, PermissionError):
|
||||
pass
|
||||
proc.communicate()
|
||||
ausgabe = proc.communicate()
|
||||
except BaseException:
|
||||
# Auch ein SIGTERM (SigtermUnterbrechung) darf keinen pytest-Prozess
|
||||
# zurueck lassen: Prozessgruppe beenden, dann weiterwerfen, damit die
|
||||
@@ -487,6 +703,17 @@ def pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimi
|
||||
except Exception:
|
||||
pass
|
||||
raise
|
||||
if not zeit_ueberschritten and proc.returncode in PYTEST_ABBRUCH:
|
||||
ende = ausgabe_ende(*ausgabe)
|
||||
abbruch = {
|
||||
"lang": "{0} (Rueckgabewert {1}) mit {2}; Ende der Ausgabe:\n {3}".format(
|
||||
PYTEST_ABBRUCH[proc.returncode], proc.returncode, python_exe,
|
||||
"\n ".join(ende) or "(keine Ausgabe)",
|
||||
),
|
||||
"kurz": "{0} (Rueckgabewert {1}): {2}".format(
|
||||
PYTEST_ABBRUCH[proc.returncode], proc.returncode, kernzeile(ende, proc.returncode),
|
||||
),
|
||||
}
|
||||
|
||||
meldungen = []
|
||||
try:
|
||||
@@ -502,7 +729,7 @@ def pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimi
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
return zeit_ueberschritten, meldungen, absturz
|
||||
return zeit_ueberschritten, meldungen, absturz, abbruch
|
||||
finally:
|
||||
# Muss auch bei einem SIGTERM mitten im Lauf greifen (N4): sonst bleibt die
|
||||
# Ergebnisdatei liegen, obwohl der pytest-Prozess schon beendet ist.
|
||||
@@ -518,12 +745,15 @@ def gruppen_ausfuehren(gruppen, wurzel, home_dir, plugin_dir, zeitlimit):
|
||||
sammelfehler = {}
|
||||
zeit_ueberschritten = False
|
||||
absturzmeldungen = []
|
||||
abbrueche = {}
|
||||
for python_exe, knoten_liste in gruppen.items():
|
||||
zeit, meldungen, absturz = pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimit)
|
||||
zeit, meldungen, absturz, abbruch = pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimit)
|
||||
if zeit:
|
||||
zeit_ueberschritten = True
|
||||
if absturz:
|
||||
absturzmeldungen.append(absturz)
|
||||
if abbruch:
|
||||
abbrueche[python_exe] = abbruch
|
||||
for m in meldungen:
|
||||
art = m.get("art")
|
||||
if art == "test":
|
||||
@@ -537,7 +767,7 @@ def gruppen_ausfuehren(gruppen, wurzel, home_dir, plugin_dir, zeitlimit):
|
||||
sammelfehler[m.get("knoten")] = m.get("meldung", "")
|
||||
elif art == "abgewaehlt":
|
||||
deselected.add(m.get("knoten"))
|
||||
return reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen
|
||||
return reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen, abbrueche
|
||||
|
||||
|
||||
def ergebnis_bestimmen(knoten, reports, deselected):
|
||||
@@ -578,17 +808,25 @@ def baseline_pruefen(kontext, ausgewaehlte_mutanten, home_dir, plugin_dir, zeitl
|
||||
alle_tests.append(knoten)
|
||||
|
||||
gruppen = gruppiere_nach_interpreter(alle_tests, kontext["python"], kontext["python_je_datei"])
|
||||
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen = gruppen_ausfuehren(
|
||||
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen, abbrueche = gruppen_ausfuehren(
|
||||
gruppen, kontext["wurzel"], home_dir, plugin_dir, zeitlimit,
|
||||
)
|
||||
|
||||
befunde = list(absturzmeldungen)
|
||||
abgebrochen = set()
|
||||
for python_exe, knoten_liste in gruppen.items():
|
||||
if python_exe in abbrueche:
|
||||
befunde.append("Grundlinie: " + abbrueche[python_exe]["lang"])
|
||||
abgebrochen.update(knoten_liste)
|
||||
ergebnis_je_knoten = {}
|
||||
for knoten in alle_tests:
|
||||
if zeit_ueberschritten:
|
||||
status, meldung = "ZEIT", "Zeitlimit in der Grundlinie ueberschritten"
|
||||
else:
|
||||
status, meldung = ergebnis_bestimmen(knoten, reports, deselected)
|
||||
if status == "FEHLT" and knoten in abgebrochen:
|
||||
ergebnis_je_knoten[knoten] = {"ergebnis": status, "meldung": "pytest abgebrochen, siehe Befunde"}
|
||||
continue
|
||||
ergebnis_je_knoten[knoten] = {"ergebnis": status, "meldung": meldung}
|
||||
if status != "GRUEN":
|
||||
zusatz = " ({0})".format(meldung) if meldung else ""
|
||||
@@ -615,7 +853,7 @@ def mutant_anwenden_und_pruefen(mutant, treffer_start, aus_zeilen, wird_zeilen,
|
||||
|
||||
knoten_liste = [knoten for _, knoten in mutant["rot_bei"]]
|
||||
gruppen = gruppiere_nach_interpreter(knoten_liste, standard_python, python_je_datei)
|
||||
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen = gruppen_ausfuehren(
|
||||
reports, deselected, sammelfehler, zeit_ueberschritten, absturzmeldungen, abbrueche = gruppen_ausfuehren(
|
||||
gruppen, wurzel, home_dir, plugin_dir, zeitlimit,
|
||||
)
|
||||
|
||||
@@ -630,6 +868,7 @@ def mutant_anwenden_und_pruefen(mutant, treffer_start, aus_zeilen, wird_zeilen,
|
||||
unklar = True
|
||||
for eintrag, knoten in mutant["rot_bei"]:
|
||||
ergebnis, meldung = ergebnis_bestimmen(knoten, reports, deselected)
|
||||
abbruch = None
|
||||
if ergebnis == "FEHLT":
|
||||
sammel_meldung = sammelfehler_fuer(knoten, sammelfehler)
|
||||
if sammel_meldung is not None:
|
||||
@@ -637,9 +876,15 @@ def mutant_anwenden_und_pruefen(mutant, treffer_start, aus_zeilen, wird_zeilen,
|
||||
meldung = "Sammelfehler: " + sammel_meldung
|
||||
else:
|
||||
unklar = True
|
||||
abbruch = abbrueche.get(python_je_datei.get(knoten.split("::", 1)[0], standard_python))
|
||||
if abbruch:
|
||||
meldung = abbruch["kurz"]
|
||||
if ergebnis in ("GRUEN", "UEBERSPRUNGEN", "ABGEWAEHLT"):
|
||||
luecke = True
|
||||
test_ergebnisse.append({"eintrag": eintrag, "knoten": knoten, "ergebnis": ergebnis, "meldung": meldung})
|
||||
eintrag_bericht = {"eintrag": eintrag, "knoten": knoten, "ergebnis": ergebnis, "meldung": meldung}
|
||||
if ergebnis == "FEHLT" and abbruch:
|
||||
eintrag_bericht["ausgabe"] = abbruch["lang"]
|
||||
test_ergebnisse.append(eintrag_bericht)
|
||||
|
||||
if unklar:
|
||||
status = "UNKLAR"
|
||||
@@ -692,6 +937,15 @@ def kopfzeilen_ausgeben(kontext, ausgewaehlte_mutanten):
|
||||
interpreter_menge.update(kontext["python_je_datei"].values())
|
||||
for interpreter in sorted(interpreter_menge):
|
||||
print("python {0}: {1}".format(interpreter, python_version_ermitteln(interpreter)))
|
||||
alle_knoten = []
|
||||
for mutant in ausgewaehlte_mutanten:
|
||||
for _, knoten in mutant["rot_bei"]:
|
||||
if knoten not in alle_knoten:
|
||||
alle_knoten.append(knoten)
|
||||
gruppen = gruppiere_nach_interpreter(alle_knoten, kontext["python"], kontext["python_je_datei"])
|
||||
for interpreter, knoten_liste in gruppen.items():
|
||||
print("pytest-konfiguration (Grundlinie, {0}): {1}".format(
|
||||
interpreter, pytest_konfig_beschreibung(kontext["wurzel"], knoten_liste)))
|
||||
|
||||
|
||||
def zusammenfassung_rechnen(mutant_liste):
|
||||
@@ -788,6 +1042,7 @@ def ablauf(args, bericht):
|
||||
aktueller_mutant_id = None
|
||||
try:
|
||||
(plugin_dir / (PLUGIN_NAME + ".py")).write_text(PLUGIN_QUELLTEXT, encoding="utf-8")
|
||||
(plugin_dir / LEERE_KONFIG_NAME).write_text("", encoding="utf-8")
|
||||
|
||||
baseline_befunde, baseline_ergebnisse = baseline_pruefen(
|
||||
kontext, ausgewaehlte_mutanten, home_dir, plugin_dir, args.zeitlimit,
|
||||
|
||||
Reference in new issue
Block a user