fix(windows): leere Symbole und der verschwundene Eintrag in "Installierte Apps"
Ampel / ampel (push) Successful in 54s

Beides vom Commander gemeldet, beides gemessen statt vermutet.

## 1. Weisses leeres Blatt statt Symbol

In deploy/worker-windows/rippy.ico steckte GENAU EIN Bild:

    Bilder = 1
      256x256   32 bit   5657 Bytes   PNG

Windows holt sich fuer jede Stelle die passende Groesse -- 16x16 fuer die
Taskleiste, 32/48 fuer Desktop und Startmenue. Fehlt sie, skaliert die Shell
nicht zuverlaessig selbst, sondern zeigt das leere Blatt. Bei einer Datei mit
nur einem PNG-komprimierten 256er passiert das regelmaessig.

packaging/windows/icon.py baut die Datei jetzt mit neun Groessen
(16/20/24/32/40/48/64/128/256, LANCZOS beim Verkleinern). Nachgemessen an
der neuen EXE: 16x16 mit 138 Farben, 32x32 mit 264 -- kein leeres Blatt mehr.
build.py laesst ein unvollstaendiges Symbol gar nicht mehr durch, und
test_icon.py haelt die Pflichtgroessen fest (mit dem alten Stand rot gesehen).

## 2. Rippy stand nicht in "Installierte Apps"

Die Ursache war MEINE Testsuite. Gemessen:

    Rippy.exe --nicht-starten   ->  Eintrag: DA  (Rippy 2.0.0)
    pytest -q                   ->  Eintrag: WEG

test_installation_legt_die_dateien_an_... schrieb in den ECHTEN
Uninstall-Schluessel und loeschte ihn im finally wieder -- also auch den des
Commanders. Der Autostart-Eintrag ging denselben Weg.

Der Schluesselname stand als VORGABEWERT in jeder Signatur, und Vorgabewerte
wertet Python zur Definitionszeit aus: Ein Test konnte ihn gar nicht umbiegen.
Jetzt steht dort None, aufgeloest zur Laufzeit -- damit wirkt ein
monkeypatch.setattr(reg, "SCHLUESSEL", ...) ueberall.

Dazu ein Waechter (test_die_testsuite_ruehrt_den_ECHTEN_eintrag_nicht_an):
Er merkt sich den echten Zustand vorher, laesst eine vollstaendige
Test-Installation samt Autostart laufen und prueft danach, dass sich am
echten Eintrag nichts geaendert hat.

Das ist derselbe Fehler wie vorhin bei den Verknuepfungen, nur an anderer
Stelle. Die Regel gehoert in den Code, nicht in meinen Kopf: **Ein Test
raeumt nur weg, was er selbst angelegt hat.**

Ampel lokal: 590 gruen, ruff sauber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-28 12:57:50 +02:00
co-authored by Claude Opus 5
parent e1984dc543
commit 60c0f93237
6 changed files with 330 additions and 15 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 31 KiB

+17 -1
View File
@@ -94,8 +94,24 @@ def pruefen() -> list:
maengel.append( maengel.append(
"docker/ui/dist fehlt — die Oberflaeche ist nicht gebaut. " "docker/ui/dist fehlt — die Oberflaeche ist nicht gebaut. "
"Abhilfe: cd docker/ui && npm ci && npm run build") "Abhilfe: cd docker/ui && npm ci && npm run build")
if not os.path.isfile(os.path.join(REPO, "deploy", "worker-windows", "rippy.ico")): icon = os.path.join(REPO, "deploy", "worker-windows", "rippy.ico")
if not os.path.isfile(icon):
maengel.append("deploy/worker-windows/rippy.ico fehlt.") maengel.append("deploy/worker-windows/rippy.ico fehlt.")
else:
# Nicht nur DA, sondern VOLLSTAENDIG. Am 28.08.2026 steckte genau ein
# Bild darin (256x256), und Windows zeigte auf Desktop, im Startmenue
# und in der Taskleiste ein leeres Blatt. Eine Datei, die es gibt, ist
# noch keine Datei, die funktioniert.
import sys as _sys
_sys.path.insert(0, HIER)
from icon import fehlende_pflichtgroessen
fehlt = fehlende_pflichtgroessen(icon)
if fehlt:
maengel.append(
"rippy.ico fehlen die Groessen %s — Windows zeigt dann ein "
"leeres Blatt. Abhilfe: python packaging/windows/icon.py"
% (fehlt,))
try: try:
import PyInstaller # noqa: F401 import PyInstaller # noqa: F401
except ImportError: except ImportError:
+142
View File
@@ -0,0 +1,142 @@
"""Ein Windows-Symbol, das Windows auch wirklich anzeigt.
## Der Befund (28.08.2026)
Commander: *„Startmenü, Desktop und Taskleisten-Icon ohne icon file (Weißes
Leeres Blatt)."*
Nachgesehen, was in `deploy/worker-windows/rippy.ico` steckte:
Bilder = 1
256x256 32 bit 5657 Bytes PNG
**Genau ein Bild, und zwar das größte.** Windows holt sich für jede Stelle
die passende Größe aus der Datei:
Taskleiste, Verknüpfung klein 16x16
Desktop, Startmenü 32x32 / 48x48
Große Kacheln, Alt-Tab 256x256
Fehlt die passende Größe, skaliert die Shell nicht immer selbst — sie zeigt
das leere Blatt. Bei einer Datei, die NUR ein PNG-komprimiertes 256er
enthält, passiert das zuverlässig.
## Was diese Datei tut
Sie baut aus dem vorhandenen Symbol eine `.ico` mit allen gebrauchten Größen.
Die kleinen Größen werden mit `LANCZOS` verkleinert — bei 16x16 ist das der
Unterschied zwischen einem erkennbaren Zeichen und Pixelbrei.
Aufruf (macht der Bau selbst, siehe `build.py`):
python packaging/windows/icon.py [--quelle DATEI] [--ziel DATEI]
"""
import argparse
import os
# Was Windows an den einzelnen Stellen holt. 24 und 64 sind fuer hohe
# Bildschirmaufloesungen (150 %/200 % Skalierung) — ohne sie skaliert die
# Shell aus 32 bzw. 48 hoch, und das sieht ausgefranst aus.
GROESSEN = (16, 20, 24, 32, 40, 48, 64, 128, 256)
HIER = os.path.dirname(os.path.abspath(__file__))
REPO = os.path.dirname(os.path.dirname(HIER))
QUELLE = os.path.join(REPO, "deploy", "worker-windows", "rippy.ico")
def groessen_in(pfad: str) -> list:
"""Welche Bildgroessen stecken in einer .ico? (ohne Pillow, reines Lesen)
Fuer die Pruefung im Bau: Eine Datei mit nur EINER Groesse ist genau der
Fehler, den es hier zu verhindern gilt.
"""
import struct
with open(pfad, "rb") as f:
roh = f.read()
if len(roh) < 6:
return []
reserviert, typ, anzahl = struct.unpack("<HHH", roh[:6])
if reserviert != 0 or typ != 1:
return []
gefunden = []
for i in range(anzahl):
block = roh[6 + i * 16: 22 + i * 16]
if len(block) < 16:
break
breite = block[0] or 256 # 0 heisst 256 (passt nicht in ein Byte)
gefunden.append(breite)
return sorted(gefunden)
# Ohne diese Groessen sieht der Nutzer an der jeweiligen Stelle ein leeres
# Blatt. Gehoert hierher und nicht in den Test, damit der BAU sie kennt.
PFLICHTGROESSEN = (16, 32, 48, 256)
def fehlende_pflichtgroessen(pfad: str) -> tuple:
"""Welche Pflichtgroessen fehlen? Leer heisst: alles da."""
vorhanden = set(groessen_in(pfad))
return tuple(k for k in PFLICHTGROESSEN if k not in vorhanden)
def bauen(quelle: str = None, ziel: str = None) -> str:
"""Baut die vollstaendige .ico. Gibt den Zielpfad zurueck.
Wirft, wenn es nicht geht — ein Symbol, das still fehlschlaegt, fuehrt
genau zu dem leeren Blatt, das hier abgestellt werden soll.
"""
from PIL import Image
quelle = quelle or QUELLE
ziel = ziel or quelle
bild = Image.open(quelle)
bild = bild.convert("RGBA")
# Aus dem groessten vorhandenen Bild verkleinern, nicht aus einem
# zufaellig ausgewaehlten: Pillow oeffnet bei einer .ico sonst die erste
# Groesse, und aus 16x16 laesst sich kein 256er machen.
if hasattr(bild, "size") and bild.size[0] < 256:
try:
bild = Image.open(quelle)
bild.size = (256, 256) # Pillow: gewuenschte Groesse waehlen
bild = bild.convert("RGBA")
except Exception: # noqa: BLE001
pass
ebenen = []
for kante in GROESSEN:
if kante >= bild.size[0]:
ebenen.append(bild.copy().resize((kante, kante), Image.LANCZOS))
else:
# LANCZOS auch beim Verkleinern: bei 16x16 ist das der Unterschied
# zwischen einem erkennbaren Zeichen und Pixelbrei.
ebenen.append(bild.resize((kante, kante), Image.LANCZOS))
ebenen[-1].save(ziel, format="ICO",
sizes=[(k, k) for k in GROESSEN])
return ziel
def main(argv=None) -> int:
p = argparse.ArgumentParser(description="Baut eine vollstaendige rippy.ico")
p.add_argument("--quelle", default=QUELLE)
p.add_argument("--ziel", default=None)
p.add_argument("--nur-pruefen", action="store_true")
args = p.parse_args(argv)
vorher = groessen_in(args.quelle)
print("Vorher: %s" % (vorher or "keine lesbare .ico"))
if args.nur_pruefen:
return 0 if len(vorher) > 1 else 1
ziel = bauen(args.quelle, args.ziel)
print("Nachher: %s" % groessen_in(ziel))
print("Geschrieben: %s (%d Bytes)" % (ziel, os.path.getsize(ziel)))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+76
View File
@@ -0,0 +1,76 @@
"""Das mitgelieferte Symbol muss alle Groessen haben, die Windows holt.
## Der Befund (28.08.2026)
Commander: *„Startmenue, Desktop und Taskleisten-Icon ohne icon file (Weisses
Leeres Blatt)."*
In `deploy/worker-windows/rippy.ico` steckte GENAU EIN Bild: 256x256 als PNG.
Windows holt sich fuer jede Stelle die passende Groesse:
Taskleiste, kleine Verknuepfung 16x16
Desktop, Startmenue 32x32 / 48x48
Alt-Tab, grosse Kacheln 256x256
Fehlt sie, skaliert die Shell nicht zuverlaessig selbst — sie zeigt das leere
Blatt. Bei einer Datei mit nur einem PNG-komprimierten 256er passiert das
regelmaessig.
Diese Pruefung braucht kein Pillow und kein Windows: Sie liest den
ICO-Verzeichniskopf direkt. Damit laeuft sie auch auf der Ampel.
"""
import os
import pytest
HIER = os.path.dirname(os.path.abspath(__file__))
REPO = os.path.dirname(os.path.dirname(HIER))
ICON = os.path.join(REPO, "deploy", "worker-windows", "rippy.ico")
def _icon_modul():
import sys
sys.path.insert(0, HIER)
import icon
return icon
def _groessen():
return _icon_modul().groessen_in(ICON)
# Aus icon.py geholt, nicht abgeschrieben: Sonst koennte der Bau eine Groesse
# fordern, die dieser Test nicht prueft — oder umgekehrt.
PFLICHTGROESSEN = _icon_modul().PFLICHTGROESSEN
def test_icon_ist_da():
assert os.path.isfile(ICON), ICON
def test_icon_ist_eine_gueltige_ico():
assert _groessen(), "Kein lesbarer ICO-Verzeichniskopf"
@pytest.mark.parametrize("kante", PFLICHTGROESSEN)
def test_pflichtgroesse_ist_enthalten(kante):
"""DER Fehler vom 28.08.2026: nur 256x256 drin, alles andere leer."""
vorhanden = _groessen()
assert kante in vorhanden, (
"%dx%d fehlt in rippy.ico (drin: %s). Windows zeigt an dieser Stelle "
"ein leeres Blatt. Abhilfe: python packaging/windows/icon.py"
% (kante, kante, vorhanden)
)
def test_eine_einzige_groesse_reicht_nie():
"""Der Zustand, in dem die Datei war. Als eigener Test, damit die
Fehlermeldung die Ursache nennt und nicht nur eine fehlende Zahl."""
assert len(_groessen()) > 1, (
"rippy.ico enthaelt nur EINE Groesse — genau der Zustand, der zu den "
"leeren Symbolen auf Desktop, im Startmenue und in der Taskleiste "
"gefuehrt hat."
)
+25 -12
View File
@@ -47,6 +47,19 @@ import sys
# Der Schlüsselname unter Uninstall. Muss über Versionen hinweg GLEICH bleiben — # Der Schlüsselname unter Uninstall. Muss über Versionen hinweg GLEICH bleiben —
# ändert er sich, entsteht bei einem Update ein zweiter Eintrag, und der alte # ändert er sich, entsteht bei einem Update ein zweiter Eintrag, und der alte
# bleibt als Leiche stehen. # bleibt als Leiche stehen.
# ## Warum der Name ueberschreibbar sein MUSS (28.08.2026)
#
# Er stand als Vorgabewert direkt in jeder Signatur. Vorgabewerte wertet
# Python zur DEFINITIONSZEIT aus — ein Test konnte den Namen also gar nicht
# umbiegen und arbeitete zwangslaeufig am ECHTEN System.
#
# Gemessen, was das anrichtet: Nach `Rippy.exe` (installiert) stand der
# Eintrag da; nach EINEM `pytest -q` war er weg. Der Commander sah Rippy
# danach nicht mehr unter „Installierte Apps" — sein Autostart-Eintrag ging
# denselben Weg. Der Test raeumte auf, was er nie angelegt hatte.
#
# Deshalb: `None` in der Signatur, aufgeloest zur LAUFZEIT. Damit wirkt ein
# `monkeypatch.setattr(reg, "SCHLUESSEL", ...)` ueberall.
SCHLUESSEL = "Rippy" SCHLUESSEL = "Rippy"
UNINSTALL_PFAD = r"Software\Microsoft\Windows\CurrentVersion\Uninstall" UNINSTALL_PFAD = r"Software\Microsoft\Windows\CurrentVersion\Uninstall"
@@ -56,8 +69,8 @@ ANZEIGENAME = "Rippy"
HERAUSGEBER = "Rippy" HERAUSGEBER = "Rippy"
def uninstall_pfad(schluessel: str = SCHLUESSEL) -> str: def uninstall_pfad(schluessel: str = None) -> str:
return UNINSTALL_PFAD + "\\" + schluessel return UNINSTALL_PFAD + "\\" + (schluessel or SCHLUESSEL)
def _quoten(pfad: str) -> str: def _quoten(pfad: str) -> str:
@@ -118,7 +131,7 @@ def _winreg():
def eintragen(installationsordner: str, deinstaller: str, version: str, def eintragen(installationsordner: str, deinstaller: str, version: str,
icon: str = "", schluessel: str = SCHLUESSEL) -> None: icon: str = "", schluessel: str = None) -> None:
"""Schreibt den Eintrag für „Programme und Features".""" """Schreibt den Eintrag für „Programme und Features"."""
winreg = _winreg() winreg = _winreg()
werte = eintrag_werte( werte = eintrag_werte(
@@ -133,7 +146,7 @@ def eintragen(installationsordner: str, deinstaller: str, version: str,
winreg.SetValueEx(k, name, 0, winreg.REG_SZ, str(wert)) winreg.SetValueEx(k, name, 0, winreg.REG_SZ, str(wert))
def austragen(schluessel: str = SCHLUESSEL) -> bool: def austragen(schluessel: str = None) -> bool:
"""Entfernt den Eintrag. False, wenn es ihn gar nicht gab.""" """Entfernt den Eintrag. False, wenn es ihn gar nicht gab."""
winreg = _winreg() winreg = _winreg()
try: try:
@@ -143,7 +156,7 @@ def austragen(schluessel: str = SCHLUESSEL) -> bool:
return False return False
def ist_eingetragen(schluessel: str = SCHLUESSEL) -> bool: def ist_eingetragen(schluessel: str = None) -> bool:
winreg = _winreg() winreg = _winreg()
try: try:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, uninstall_pfad(schluessel)): with winreg.OpenKey(winreg.HKEY_CURRENT_USER, uninstall_pfad(schluessel)):
@@ -152,7 +165,7 @@ def ist_eingetragen(schluessel: str = SCHLUESSEL) -> bool:
return False return False
def gelesene_werte(schluessel: str = SCHLUESSEL) -> dict: def gelesene_werte(schluessel: str = None) -> dict:
"""Was WIRKLICH in der Registry steht — für `rippy doctor` und Tests. """Was WIRKLICH in der Registry steht — für `rippy doctor` und Tests.
Nachsehen statt glauben: Ein Schreibvorgang, der quittiert wurde, ist Nachsehen statt glauben: Ein Schreibvorgang, der quittiert wurde, ist
@@ -176,28 +189,28 @@ def gelesene_werte(schluessel: str = SCHLUESSEL) -> dict:
# ── Autostart ─────────────────────────────────────────────────────────── # ── Autostart ───────────────────────────────────────────────────────────
def autostart_setzen(befehl: str, name: str = SCHLUESSEL) -> None: def autostart_setzen(befehl: str, name: str = None) -> None:
winreg = _winreg() winreg = _winreg()
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, RUN_PFAD) as k: with winreg.CreateKey(winreg.HKEY_CURRENT_USER, RUN_PFAD) as k:
winreg.SetValueEx(k, name, 0, winreg.REG_SZ, befehl) winreg.SetValueEx(k, name or SCHLUESSEL, 0, winreg.REG_SZ, befehl)
def autostart_entfernen(name: str = SCHLUESSEL) -> bool: def autostart_entfernen(name: str = None) -> bool:
winreg = _winreg() winreg = _winreg()
try: try:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_PFAD, 0, with winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_PFAD, 0,
winreg.KEY_ALL_ACCESS) as k: winreg.KEY_ALL_ACCESS) as k:
winreg.DeleteValue(k, name) winreg.DeleteValue(k, name or SCHLUESSEL)
return True return True
except FileNotFoundError: except FileNotFoundError:
return False return False
def autostart_wert(name: str = SCHLUESSEL) -> str: def autostart_wert(name: str = None) -> str:
winreg = _winreg() winreg = _winreg()
try: try:
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_PFAD) as k: with winreg.OpenKey(winreg.HKEY_CURRENT_USER, RUN_PFAD) as k:
return winreg.QueryValueEx(k, name)[0] return winreg.QueryValueEx(k, name or SCHLUESSEL)[0]
except (FileNotFoundError, OSError): except (FileNotFoundError, OSError):
return "" return ""
+70 -2
View File
@@ -61,8 +61,23 @@ def test_ordner_ohne_abschliessenden_trenner():
# ── Installation (echt, in einem Testordner) ──────────────────────────── # ── Installation (echt, in einem Testordner) ────────────────────────────
@nur_windows @nur_windows
def test_installation_legt_die_dateien_an_und_meldet_sich_bei_windows(tmp_path, monkeypatch): def test_installation_legt_die_dateien_an_und_meldet_sich_bei_windows(tmp_path, monkeypatch):
"""Der ganze Kreislauf. Der Registry-Schluessel ist der ECHTE — deshalb """Der ganze Kreislauf — vollstaendig in einem eigenen Sandkasten.
wird am Ende immer aufgeraeumt, auch wenn der Test scheitert.
## ⚠️ WARUM HIER EIN EIGENER REGISTRY-SCHLUESSEL STEHT (28.08.2026)
Weil hier vorher der ECHTE stand. Der Test schrieb hinein und loeschte ihn
im `finally` wieder — auch den des Commanders. Gemessen:
Rippy.exe --nicht-starten -> Eintrag: DA (Rippy 2.0.0)
pytest -q -> Eintrag: WEG
Rippy verschwand danach aus „Installierte Apps", und der
Autostart-Eintrag ging denselben Weg. Der Commander hat es gemeldet,
bevor ich es bemerkt habe.
Das ist derselbe Fehler wie unten bei den Verknuepfungen, nur an einer
anderen Stelle: **Ein Test raeumt nur weg, was er selbst angelegt hat.**
Wenn „aufraeumen" fremden Zustand trifft, ist es kein Aufraeumen.
## ⚠️ WARUM HIER KEINE VERKNUEPFUNGEN ANGELEGT WERDEN (28.08.2026) ## ⚠️ WARUM HIER KEINE VERKNUEPFUNGEN ANGELEGT WERDEN (28.08.2026)
@@ -90,6 +105,14 @@ def test_installation_legt_die_dateien_an_und_meldet_sich_bei_windows(tmp_path,
from rippy.platform import verknuepfungen as vk from rippy.platform import verknuepfungen as vk
from rippy.platform import win_registry as reg from rippy.platform import win_registry as reg
# Sicherung 3 (28.08.2026): ein EIGENER Registry-Schluessel.
# Vorher schrieb dieser Test in den ECHTEN Eintrag und loeschte ihn im
# finally wieder -- also auch den des Commanders. Gemessen: nach
# `Rippy.exe` stand der Eintrag da, nach EINEM `pytest -q` war er weg,
# und Rippy verschwand aus "Installierte Apps". Der Autostart-Eintrag
# ging denselben Weg.
monkeypatch.setattr(reg, "SCHLUESSEL", "RippyTestInstallation")
# Sicherung 2: Selbst ein versehentliches `verknuepfen=True` bliebe folgenlos. # Sicherung 2: Selbst ein versehentliches `verknuepfen=True` bliebe folgenlos.
angelegt = [] angelegt = []
monkeypatch.setattr( monkeypatch.setattr(
@@ -137,6 +160,8 @@ def test_installation_ruehrt_die_echten_verknuepfungen_nicht_an(tmp_path, monkey
from rippy.platform import verknuepfungen as vk from rippy.platform import verknuepfungen as vk
from rippy.platform import win_registry as reg from rippy.platform import win_registry as reg
monkeypatch.setattr(reg, "SCHLUESSEL", "RippyTestWaechter")
aufrufe = [] aufrufe = []
monkeypatch.setattr(vk, "anlegen", monkeypatch.setattr(vk, "anlegen",
lambda *a, **k: aufrufe.append(a) or True) lambda *a, **k: aufrufe.append(a) or True)
@@ -156,6 +181,49 @@ def test_installation_ruehrt_die_echten_verknuepfungen_nicht_an(tmp_path, monkey
assert aufrufe == [] assert aufrufe == []
@nur_windows
def test_die_testsuite_ruehrt_den_ECHTEN_eintrag_nicht_an(tmp_path, monkeypatch):
"""Der Waechter gegen den Vorfall vom 28.08.2026.
Er sieht VORHER nach, ob Rippy auf dieser Maschine installiert ist,
laesst dann eine vollstaendige Test-Installation samt Autostart laufen —
und prueft danach, dass sich am echten Eintrag NICHTS geaendert hat.
Ohne diesen Waechter reicht ein vergessenes `monkeypatch` in einem
kuenftigen Test, und Rippy verschwindet beim naechsten `pytest -q`
wieder aus „Installierte Apps". Das ist schon einmal passiert, und der
Commander musste es melden.
"""
from rippy.platform import verknuepfungen as vk
from rippy.platform import win_registry as reg
echt_vorher = reg.ist_eingetragen("Rippy")
autostart_vorher = reg.autostart_wert("Rippy")
monkeypatch.setattr(reg, "SCHLUESSEL", "RippyTestUnberuehrt")
monkeypatch.setattr(vk, "alle_anlegen", lambda *a, **k: {})
attrappe = tmp_path / "q" / "RippySetup.exe"
attrappe.parent.mkdir()
attrappe.write_bytes(b"x" * 16)
monkeypatch.setattr(windows_app, "_eigene_datei", lambda: str(attrappe))
try:
windows_app.installieren(ziel=str(tmp_path / "i"), autostart=True,
starten_danach=False, verknuepfen=False)
finally:
reg.autostart_entfernen()
reg.austragen()
assert reg.ist_eingetragen("Rippy") == echt_vorher, (
"Die Testsuite hat den ECHTEN Uninstall-Eintrag veraendert — genau "
"dadurch verschwand Rippy aus 'Installierte Apps'."
)
assert reg.autostart_wert("Rippy") == autostart_vorher, (
"Die Testsuite hat den ECHTEN Autostart-Eintrag veraendert."
)
# ── Rueckmeldung ohne Konsole ─────────────────────────────────────────── # ── Rueckmeldung ohne Konsole ───────────────────────────────────────────
def test_ohne_konsole_kommt_ein_fenster(monkeypatch): def test_ohne_konsole_kommt_ein_fenster(monkeypatch):
"""Seit die EXE ohne Konsole gebaut wird (Wunsch des Commanders am """Seit die EXE ohne Konsole gebaut wird (Wunsch des Commanders am