Fixrunde 1 zu Task 6b. - setup_pi.sh raeumt die Ablage vor dem scp ab und bricht ab, wenn display.py dort fehlt. Ein Reststand aus einem abgebrochenen Lauf liess "scp -r" ins vorhandene Ziel hineinkopieren; die Installationsschleife ueberspringt Verzeichnisse und fand nichts - der Lauf meldete Erfolg, aktivierte den Anzeigedienst und hinterliess ein leeres display/: 203/EXEC im 5-Sekunden-Takt bei schwarzem Panel. - Der Paketschritt steht jetzt hinter der Installation und warnt nur. Eine Migrationsstation steht typischerweise nicht am Netz; unter set -e davor hiess ein gescheitertes apt: gar keine Station statt einer Station ohne Panel. "apt-get update" kommt vor dem install, der Anzeigedienst startet nach dem Paketschritt. - setup_pi.sh raeumt display/ auf dem Pi wie deploy.sh auf (im Repo geloeschte Dateien verschwinden), der Trap haengt auch an INT/TERM/HUP, und mktemp bekommt eine Vorlage: ohne sie beachtet BSD-mktemp TMPDIR nicht, und die Temp-Datei landete je nach Plattform woanders. - test_host.sh ruft test_deploy.sh auf - 95 Zusagen liefen nur bei Handaufruf, darunter die Deploy-Haelfte der Anzeige-Auslieferung. Der Coverage-Test deckt jetzt auch die Shell-Suiten ab und zaehlt dabei nur nicht auskommentierte Zeilen. - Neue Szenarien fuer alle vier Punkte, darunter die Zusage, dass beim gescheiterten Schreiben eines Profils keine Datei mit dem WLAN-Passwort liegenbleibt.
110 lines
4.9 KiB
Python
110 lines
4.9 KiB
Python
"""Wache dagegen, dass eine Testdatei aus allen Suiten herausfaellt.
|
|
|
|
Genau das war schon einmal passiert: tools/test_host.sh rief pytest auf
|
|
tools/mkimage/tests und tools/station/tests auf - tools/mkimage/test_nl_mkimage.py
|
|
liegt aber DIREKT in tools/mkimage/ und lief damit in keiner Suite mehr mit. 19 Tests
|
|
waren still verschwunden, darunter test_stays_python38_compatible, die Wache fuer die
|
|
bindende 3.8-Randbedingung des Docker-Build-Containers (`str | None` in nl_mkimage.py
|
|
hatte den Build schon einmal gebrochen).
|
|
|
|
Eine stumm ausgefallene Testdatei ist schlimmer als eine fehlende: die Suite bleibt
|
|
gruen und behauptet damit eine Deckung, die es nicht gibt. Deshalb wird hier nicht ein
|
|
einzelner Pfad festgenagelt, sondern die Regel selbst - JEDE test_*.py unter tools/
|
|
muss von einem der Pfade erfasst sein, die tools/test_host.sh an pytest uebergibt.
|
|
Eine neue Testdatei an einer neuen Stelle faellt damit sofort auf, statt erst dann,
|
|
wenn jemand sie vermisst.
|
|
"""
|
|
import re
|
|
from pathlib import Path
|
|
|
|
TOOLS_DIR = Path(__file__).resolve().parents[2]
|
|
REPO_ROOT = TOOLS_DIR.parent
|
|
TEST_HOST = TOOLS_DIR / "test_host.sh"
|
|
|
|
# Verzeichnisse, die keine Tests DIESES Projekts enthalten: virtuelle Umgebungen und
|
|
# installierte Fremdpakete bringen eigene test_*.py mit, die hier nichts zu suchen haben.
|
|
IGNORED_PARTS = (".venv", "__pycache__", "site-packages", "node_modules", ".git")
|
|
|
|
|
|
def pytest_paths():
|
|
"""Die Pfade, die tools/test_host.sh an pytest uebergibt, als aufgeloeste Pfade."""
|
|
text = TEST_HOST.read_text()
|
|
match = re.search(r"^\s*python3 -m pytest (.+?)(?:\s+-\S+)*\s*$", text, re.M)
|
|
assert match, "in tools/test_host.sh ist kein 'python3 -m pytest'-Aufruf zu finden"
|
|
paths = []
|
|
for raw in match.group(1).split():
|
|
arg = raw.strip('"')
|
|
if arg.startswith("-"):
|
|
continue
|
|
paths.append(Path(arg.replace("$ROOT", str(REPO_ROOT))).resolve())
|
|
assert paths, "der pytest-Aufruf in tools/test_host.sh uebergibt keine Pfade"
|
|
return paths
|
|
|
|
|
|
def own_test_files():
|
|
return sorted(
|
|
p.resolve()
|
|
for p in TOOLS_DIR.rglob("test_*.py")
|
|
if not any(part in IGNORED_PARTS for part in p.parts)
|
|
)
|
|
|
|
|
|
def test_every_test_file_is_reached_by_test_host():
|
|
roots = pytest_paths()
|
|
orphans = []
|
|
for test_file in own_test_files():
|
|
if not any(test_file == r or r in test_file.parents for r in roots):
|
|
orphans.append(test_file.relative_to(REPO_ROOT))
|
|
assert not orphans, (
|
|
"diese Testdateien laufen in keiner Suite mit - tools/test_host.sh sammelt sie "
|
|
"nicht ein: {}. Entweder den Pfad in test_host.sh erweitern oder die Datei "
|
|
"dorthin verschieben, wo sie eingesammelt wird.".format(
|
|
", ".join(str(o) for o in orphans)
|
|
)
|
|
)
|
|
|
|
|
|
def test_every_shell_suite_is_called_by_test_host():
|
|
"""Dieselbe Regel fuer die Shell-Suiten (Task 6b F1, I3).
|
|
|
|
test_deploy.sh war in tools/test_host.sh nicht aufgefuehrt und lief damit nur bei
|
|
Handaufruf - 95 Zusagen, die niemand mehr sah. Die .py-Regel oben deckt genau diese
|
|
Haelfte nicht ab, weil die Shell-Suiten nicht ueber pytest laufen, sondern einzeln
|
|
aufgerufen werden.
|
|
"""
|
|
# Nur AUSFUEHRBARE Zeilen zaehlen, keine Kommentare: der Kommentarblock ueber den
|
|
# Aufrufen nennt test_deploy.sh beim Namen, und eine blosse Textsuche waere damit
|
|
# auch dann erfuellt, wenn die Aufrufzeile fehlt - der Test waere gruen, ohne etwas
|
|
# zu pruefen (genau die Falle, die er verhindern soll).
|
|
lines = [
|
|
line.strip()
|
|
for line in TEST_HOST.read_text().splitlines()
|
|
if line.strip() and not line.strip().startswith("#")
|
|
]
|
|
missing = []
|
|
for suite in sorted((TOOLS_DIR / "station" / "tests").glob("test_*.sh")):
|
|
# So, wie test_host.sh sie aufruft: "$ROOT/tools/station/tests/<name>"
|
|
if not any(suite.name in line for line in lines):
|
|
missing.append(suite.relative_to(REPO_ROOT))
|
|
assert not missing, (
|
|
"diese Shell-Suiten ruft tools/test_host.sh nicht auf, sie laufen also nur bei "
|
|
"Handaufruf: {}".format(", ".join(str(m) for m in missing))
|
|
)
|
|
|
|
|
|
def test_python38_guard_is_among_them():
|
|
"""Die konkrete Datei, deren Ausfall den Build-Bruch damals durchgelassen haette."""
|
|
guard = (TOOLS_DIR / "mkimage" / "test_nl_mkimage.py").resolve()
|
|
assert guard.exists(), "tools/mkimage/test_nl_mkimage.py fehlt"
|
|
roots = pytest_paths()
|
|
assert any(guard == r or r in guard.parents for r in roots), (
|
|
"tools/mkimage/test_nl_mkimage.py wird von tools/test_host.sh nicht eingesammelt "
|
|
"- darin steht test_stays_python38_compatible, die einzige Wache gegen erneute "
|
|
"3.10-Syntax in nl_mkimage.py (der Docker-Build-Container hat Python 3.8.10)"
|
|
)
|
|
|
|
|
|
# Kein eigener "ist vermin installiert"-Test hier: test_nl_mkimage.py importiert vermin
|
|
# ohnehin hart (kein Skip), und ein fehlendes Paket faellt dort mit derselben Deutlichkeit
|
|
# auf. Zwei Tests fuer dieselbe Zusage sind nur zwei Stellen zum Pflegen.
|