feat(station): Umschalter fuer Migrationsimages mit/ohne WLAN-Daten
Die WLAN-Zugangsdaten stecken zur Bauzeit in der Firmware, es gibt also zwei Abzuege je Ziel. Die Station haelt jetzt beide vor - images/ migrate-nocreds (aus tools/build_nocreds.sh, Lampe startet im Onboarding-AP) und images/migrate-wlan (aus build/, Lampe haengt sofort im eigenen WLAN) - und der Bediener waehlt per Umschalter, welcher ausgeliefert wird: "ninlight-station bildsatz" oder der Menueeintrag am Taster. Der Zustand liegt in /var/lib/ninlight-station/imageset, ueberlebt Neustarts und steht dauerhaft in der Fusszeile des Panels - in jedem Modus, auch bei ausgeschalteter Station: man soll vor dem Einschalten sehen, was gilt. Vorgabe und Rueckfallwert in JEDEM Zweifelsfall ist "ohne-wlan". Im Automatikmodus wird jede Klyqa-Lampe in Reichweite migriert, auch fremde; ein Abzug ohne Zugangsdaten ist dann ein harmloser Fehlgriff, einer mit Zugangsdaten traegt das eigene WLAN-Passwort in ein fremdes Geraet. Fehlt der gewaehlte Satz (Verzeichnis weg oder leer), wird NICHTS ausgeliefert - kein stilles Ausweichen auf den anderen Satz. Der Server antwortet 404 mit Status "noimageset", CLI und Panel sagen "FEHLT". server.py und die Stations-Unit muessen mit: die Auswahl der ausgelieferten Datei ist genau die Aufgabe des Servers, und ein Symlink-Tausch haette "gewaehlt, aber nicht vorhanden" von "dieses Modell fehlt" nicht mehr unterscheidbar gemacht.
This commit is contained in:
+142
-16
@@ -217,8 +217,12 @@ EOF
|
||||
setup_sandbox() {
|
||||
local sandbox=$1 unitfile=${2:-$REAL_UNIT}
|
||||
rm -rf "$sandbox"
|
||||
mkdir -p "$sandbox/run" "$sandbox/images/migrate" "$sandbox/bin" "$sandbox/nmstate" \
|
||||
"$sandbox/lib/display"
|
||||
mkdir -p "$sandbox/run" "$sandbox/images/migrate-nocreds" "$sandbox/images/migrate-wlan" \
|
||||
"$sandbox/bin" "$sandbox/nmstate" "$sandbox/lib/display" "$sandbox/var"
|
||||
# Je ein Image je Bildsatz - "vorhanden" heisst fuer CLI, Server und Anzeige
|
||||
# gleichermassen "mindestens ein Image darin".
|
||||
echo "ohne" >"$sandbox/images/migrate-nocreds/foo_dev.img"
|
||||
echo "mit" >"$sandbox/images/migrate-wlan/foo_dev.img"
|
||||
echo "dummy" >"$sandbox/images/ninlight_foo_ota.img"
|
||||
|
||||
# NINLIGHT_STATION_LIB zeigt auf diese Sandbox statt auf tools/station selbst.
|
||||
@@ -292,6 +296,7 @@ run_cli() {
|
||||
NINLIGHT_STATION_CONF="$sandbox/station.conf" \
|
||||
NINLIGHT_RUN_DIR="$sandbox/run" \
|
||||
NINLIGHT_STATION_LIB="$sandbox/lib" \
|
||||
NINLIGHT_IMAGESET_FILE="$sandbox/var/imageset" \
|
||||
"$CLI" "$@"
|
||||
}
|
||||
|
||||
@@ -334,7 +339,7 @@ echo
|
||||
echo "=== Fall 3: 'migrate on' faehrt ninlight-recovery herunter, BEVOR qconnex-firmware hochkommt; control.json phase=auto ==="
|
||||
SBX="$TMPROOT/t3"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" on >/dev/null 2>&1 # Recovery vorher an
|
||||
: >"$SBX/cmd.log"
|
||||
set +e
|
||||
@@ -356,7 +361,7 @@ echo
|
||||
echo "=== Fall 4: 'migrate on --listen' wie Fall 3, aber phase=listen ==="
|
||||
SBX="$TMPROOT/t4"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" on >/dev/null 2>&1
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" migrate on --listen 2>&1)
|
||||
@@ -369,7 +374,8 @@ echo
|
||||
echo "=== Fall 5: 'migrate on' ohne Migrationsimages -> Warnung auf stderr, Rueckgabewert 0 ==="
|
||||
SBX="$TMPROOT/t5"
|
||||
setup_sandbox "$SBX"
|
||||
# images/migrate/ bleibt leer
|
||||
# der gewaehlte Bildsatz ("ohne-wlan") wird geleert - setup_sandbox bestueckt beide
|
||||
rm -f "$SBX/images/migrate-nocreds"/*.img
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" migrate on 2>"$SBX/stderr.log")
|
||||
RC=$?
|
||||
@@ -468,7 +474,7 @@ echo
|
||||
echo "=== Fall 12: 'status' im Migrationsmodus zeigt Modus, Phase und zuletzt erkannte Lampe ==="
|
||||
SBX="$TMPROOT/t12"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" migrate on >/dev/null 2>&1 # phase=auto in control.json, wie es der echte Ablauf waere
|
||||
# status.json so, wie server.py sie beim Start geschrieben haette (mode/last_seen).
|
||||
cat >"$SBX/run/status.json" <<'JSON'
|
||||
@@ -513,7 +519,7 @@ echo
|
||||
echo "=== Fall 15 (wichtigster Test): 'migrate on' bei laufendem Recovery-Modus laesst GENAU EIN Profil aktiv ==="
|
||||
SBX="$TMPROOT/t15"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" on >/dev/null 2>&1
|
||||
ACTIVE_BEFORE=$(cat "$SBX/nmstate/active")
|
||||
echo "$ACTIVE_BEFORE" | grep -qx "ninlight-recovery" && pass "Vorbedingung: ninlight-recovery laeuft" || fail "Vorbedingung nicht erfuellt: $ACTIVE_BEFORE"
|
||||
@@ -538,7 +544,7 @@ SBX="$TMPROOT/tC1"
|
||||
BROKEN_UNIT="$TMPROOT/broken-ninlight-station.service"
|
||||
grep -v '^RuntimeDirectoryPreserve=restart' "$REAL_UNIT" >"$BROKEN_UNIT"
|
||||
setup_sandbox "$SBX" "$BROKEN_UNIT"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" migrate on --listen 2>"$SBX/stderr.log")
|
||||
RC=$?
|
||||
@@ -555,7 +561,7 @@ ACTIVE_AFTER=$(cat "$SBX/nmstate/active")
|
||||
# in tools/station/ninlight-station.service tatsaechlich steht und wirkt.
|
||||
SBX2="$TMPROOT/tC1-fixed"
|
||||
setup_sandbox "$SBX2"
|
||||
echo "dummy" >"$SBX2/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX2/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
set +e
|
||||
OUT2=$(run_cli "$SBX2" migrate on --listen 2>&1)
|
||||
RC2=$?
|
||||
@@ -567,7 +573,7 @@ echo
|
||||
echo "=== Zusatzfall I1: 'status' zeigt die Phase aus control.json, nicht die veraltete Phase aus status.json ==="
|
||||
SBX="$TMPROOT/tI1"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" migrate on --listen >/dev/null 2>&1
|
||||
# status.json wird nur beim Dienststart/bei einer Auslieferung geschrieben - hier
|
||||
# simuliert, wie es nach dem Start aussehen wuerde (Phase "listen", wie beim Start).
|
||||
@@ -604,7 +610,7 @@ echo
|
||||
echo "=== Zusatzfall I3: ap_switch bricht ab und faehrt beide Profile herunter, wenn 'device show wlan0' nach dem Umschalten nicht passt ==="
|
||||
SBX="$TMPROOT/tI3"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
set +e
|
||||
OUT=$(FAKE_NMCLI_DEVSHOW=fremdes-profil run_cli "$SBX" migrate on 2>"$SBX/stderr.log")
|
||||
RC=$?
|
||||
@@ -619,7 +625,7 @@ echo
|
||||
echo "=== Zusatzfall N2 (Fixrunde 2): 'status' zeigt keine veraltete Migrationsphase mehr, nachdem in den Rettungsmodus zurueckgeschaltet wurde ==="
|
||||
SBX="$TMPROOT/tN2"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" migrate on --listen >/dev/null 2>&1
|
||||
run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1 # control.json bleibt mit phase=armed liegen
|
||||
run_cli "$SBX" on >/dev/null 2>&1 # zurueck in den Rettungsmodus - control.json wird bewusst NICHT aufgeraeumt
|
||||
@@ -638,7 +644,7 @@ echo
|
||||
echo "=== Zusatzfall N2b (Fixrunde 2): dieselbe veraltete Freigabe, aber ohne aktualisierte status.json (Modus '-') - genau das woertliche Repro aus dem Review ==="
|
||||
SBX="$TMPROOT/tN2b"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" migrate on --listen >/dev/null 2>&1
|
||||
run_cli "$SBX" arm gu10_rgb >/dev/null 2>&1
|
||||
run_cli "$SBX" on >/dev/null 2>&1
|
||||
@@ -819,7 +825,7 @@ echo "=== Fall D4 (Befund Task 7f): 'off' fasst den Anzeigedienst nicht mehr an
|
||||
# naechsten Pi-Neustart dunkel. Jetzt laesst 'off' den Dienst laufen.
|
||||
SBX="$TMPROOT/tD4"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
run_cli "$SBX" migrate on >/dev/null 2>&1
|
||||
: >"$SBX/cmd.log"
|
||||
set +e
|
||||
@@ -997,7 +1003,7 @@ echo
|
||||
echo "=== Fall Z4: 'migrate on --listen --for 5' - beides zusammen ==="
|
||||
SBX="$TMPROOT/tZ4"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" migrate on --listen --for 5 2>&1)
|
||||
RC=$?
|
||||
@@ -1008,7 +1014,7 @@ set -e
|
||||
# Umgekehrte Reihenfolge der Schalter muss genauso gehen.
|
||||
SBX="$TMPROOT/tZ4b"
|
||||
setup_sandbox "$SBX"
|
||||
echo "dummy" >"$SBX/images/migrate/gu10_rgb_prod.img"
|
||||
echo "dummy" >"$SBX/images/migrate-nocreds/gu10_rgb_prod.img"
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" migrate on --for 5 --listen 2>&1)
|
||||
RC=$?
|
||||
@@ -1182,6 +1188,126 @@ echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung auf stderr" || fail "kein
|
||||
[ ! -e "$SBX/run/autooff.json" ] && pass "keine Zustandsdatei, die einen Zeitschalter vortaeuscht" || fail "autooff.json trotz gescheitertem Timer: $(cat "$SBX/run/autooff.json")"
|
||||
echo "$ERR" | grep -q "off" && pass "die Meldung sagt, dass die Station AN bleibt" || fail "die Meldung verschweigt den Zustand der Station: $ERR"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz A: die Vorgabe ohne Umschalterdatei ist der Satz OHNE WLAN-Zugangsdaten ==="
|
||||
# Die sichere Richtung: ein Fehlgriff kostet einen Onboarding-Durchgang, kein
|
||||
# WLAN-Passwort in einer fremden Lampe.
|
||||
SBX="$TMPROOT/bs1"
|
||||
setup_sandbox "$SBX"
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" bildsatz 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT"
|
||||
echo "$OUT" | grep -q "ohne-wlan" && pass "ohne Datei gilt 'ohne-wlan'" || fail "Vorgabe ist nicht 'ohne-wlan': $OUT"
|
||||
echo "$OUT" | grep -q "migrate-nocreds" && pass "und das zugehoerige Verzeichnis wird genannt" || fail "Verzeichnis fehlt in der Ausgabe: $OUT"
|
||||
[ ! -e "$SBX/var/imageset" ] && pass "das blosse Berichten legt keine Datei an" || fail "'bildsatz' ohne Argument hat geschrieben"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz B: umschalten schreibt die Datei atomar mit 0644 und bleibt stehen ==="
|
||||
SBX="$TMPROOT/bs2"
|
||||
setup_sandbox "$SBX"
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" bildsatz mit-wlan 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT"
|
||||
[ "$(cat "$SBX/var/imageset" 2>/dev/null | tr -d '\n')" = "mit-wlan" ] && pass "die Datei enthaelt 'mit-wlan'" || fail "Dateiinhalt: $(cat "$SBX/var/imageset" 2>&1)"
|
||||
MODE=$(file_mode "$SBX/var/imageset")
|
||||
[ "$MODE" = "644" ] && pass "Rechte 0644 (der Anzeigedienst muss lesen koennen)" || fail "Rechte $MODE statt 644"
|
||||
[ -z "$(find "$SBX/var" -name '.imageset-*' 2>/dev/null)" ] && pass "keine Temp-Datei liegengeblieben" || fail "Temp-Datei liegt noch da"
|
||||
echo "$OUT" | grep -qi "achtung" && pass "der Wechsel zu 'mit-wlan' warnt" || fail "keine Warnung beim Wechsel zu mit-wlan: $OUT"
|
||||
# ... und er BLEIBT stehen: ein zweiter, unabhaengiger Aufruf sieht ihn.
|
||||
OUT2=$(run_cli "$SBX" bildsatz 2>&1)
|
||||
echo "$OUT2" | grep -q "mit-wlan" && pass "der Zustand bleibt ueber Aufrufe hinweg" || fail "Zustand nicht gespeichert: $OUT2"
|
||||
# ... und zurueck.
|
||||
run_cli "$SBX" bildsatz ohne-wlan >/dev/null 2>&1
|
||||
[ "$(cat "$SBX/var/imageset" | tr -d '\n')" = "ohne-wlan" ] && pass "zurueckschalten geht auch" || fail "zurueckschalten hat nicht gewirkt"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz C: ein unbekannter Name schaltet nichts um ==="
|
||||
SBX="$TMPROOT/bs3"
|
||||
setup_sandbox "$SBX"
|
||||
printf 'mit-wlan\n' >"$SBX/var/imageset"
|
||||
set +e
|
||||
ERR=$(run_cli "$SBX" bildsatz irgendwas 2>&1 >/dev/null)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -ne 0 ] && pass "Exit-Code != 0" || fail "Exit-Code 0 fuer einen unbekannten Bildsatz"
|
||||
[ "$(cat "$SBX/var/imageset" | tr -d '\n')" = "mit-wlan" ] && pass "der bestehende Zustand blieb unveraendert" || fail "der Zustand wurde veraendert: $(cat "$SBX/var/imageset")"
|
||||
echo "$ERR" | grep -qi "fehler" && pass "Fehlermeldung auf stderr" || fail "keine Fehlermeldung: $ERR"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz D: ein kaputter Dateiinhalt faellt auf die SICHERE Vorgabe zurueck ==="
|
||||
# Nie auf den teureren Satz - das ist die eine Richtung, die kein Zweifelsfall nehmen darf.
|
||||
SBX="$TMPROOT/bs4"
|
||||
setup_sandbox "$SBX"
|
||||
printf 'voellig-anderes\n' >"$SBX/var/imageset"
|
||||
OUT=$(run_cli "$SBX" bildsatz 2>&1)
|
||||
echo "$OUT" | grep -q "ohne-wlan" && pass "kaputter Inhalt -> 'ohne-wlan'" || fail "kaputter Inhalt fuehrte nicht auf die Vorgabe: $OUT"
|
||||
echo "$OUT" | grep -q "migrate-nocreds" && pass "und das Verzeichnis des sicheren Satzes" || fail "falsches Verzeichnis: $OUT"
|
||||
# Der Bediener muss erfahren, dass sein Eintrag NICHT gewirkt hat - sonst haelt er die
|
||||
# Station fuer umgeschaltet.
|
||||
echo "$OUT" | grep -q "unbekannte Einstellung 'voellig-anderes'" \
|
||||
&& pass "die unbekannte Einstellung wird beim Namen genannt" \
|
||||
|| fail "die unbekannte Einstellung wird verschwiegen: $OUT"
|
||||
# Gegenprobe: bei einem GUELTIGEN Eintrag darf diese Zeile nicht erscheinen.
|
||||
run_cli "$SBX" bildsatz mit-wlan >/dev/null 2>&1
|
||||
OUT=$(run_cli "$SBX" bildsatz 2>&1)
|
||||
echo "$OUT" | grep -q "unbekannte Einstellung" \
|
||||
&& fail "meldet 'unbekannte Einstellung' fuer einen gueltigen Eintrag: $OUT" \
|
||||
|| pass "kein Fehlalarm bei gueltigem Eintrag"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz E: ein gewaehlter, aber fehlender Satz wird beim Umschalten UND im Status gemeldet ==="
|
||||
SBX="$TMPROOT/bs5"
|
||||
setup_sandbox "$SBX"
|
||||
rm -rf "$SBX/images/migrate-wlan"
|
||||
set +e
|
||||
ERR=$(run_cli "$SBX" bildsatz mit-wlan 2>&1 >/dev/null)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -eq 0 ] && pass "Umschalten gelingt trotzdem (der Zustand ist gueltig, nur die Bestueckung fehlt)" || fail "Exit-Code $RC"
|
||||
echo "$ERR" | grep -qi "warnung" && pass "Warnung beim Umschalten" || fail "keine Warnung: $ERR"
|
||||
OUT=$(run_cli "$SBX" status 2>&1)
|
||||
echo "$OUT" | grep -q "FEHLT" && pass "'status' sagt, dass nichts ausgeliefert wird" || fail "'status' verschweigt den fehlenden Satz: $OUT"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz F: ein LEERES Verzeichnis zaehlt genauso als fehlend ==="
|
||||
SBX="$TMPROOT/bs6"
|
||||
setup_sandbox "$SBX"
|
||||
rm -f "$SBX/images/migrate-nocreds"/*.img
|
||||
OUT=$(run_cli "$SBX" status 2>&1)
|
||||
echo "$OUT" | grep -q "FEHLT" && pass "ein leeres Bildsatzverzeichnis wird als fehlend gemeldet" || fail "ein leerer Satz gilt als vorhanden: $OUT"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz G: 'migrate on' nennt den geltenden Bildsatz in der Einschaltmeldung ==="
|
||||
# Er kann von vorletzter Woche stammen - wer einschaltet, muss ihn sehen.
|
||||
SBX="$TMPROOT/bs7"
|
||||
setup_sandbox "$SBX"
|
||||
run_cli "$SBX" bildsatz mit-wlan >/dev/null 2>&1
|
||||
set +e
|
||||
OUT=$(run_cli "$SBX" migrate on --listen 2>&1)
|
||||
RC=$?
|
||||
set -e
|
||||
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT"
|
||||
echo "$OUT" | grep -q "Bildsatz: mit-wlan" && pass "die Einschaltmeldung nennt den Bildsatz" || fail "Bildsatz fehlt in der Einschaltmeldung: $OUT"
|
||||
|
||||
echo
|
||||
echo "=== Bildsatz H: die Warnung 'keine Migrationsimages' meint den GEWAEHLTEN Satz ==="
|
||||
SBX="$TMPROOT/bs8"
|
||||
setup_sandbox "$SBX"
|
||||
rm -f "$SBX/images/migrate-wlan"/*.img # nur der andere Satz ist leer
|
||||
set +e
|
||||
ERR=$(run_cli "$SBX" migrate on --listen 2>&1 >/dev/null)
|
||||
set -e
|
||||
echo "$ERR" | grep -q "keine Migrationsimages" && fail "warnt, obwohl der GEWAEHLTE Satz ('ohne-wlan') bestueckt ist: $ERR" || pass "kein Fehlalarm - gezaehlt wird im gewaehlten Satz"
|
||||
run_cli "$SBX" bildsatz mit-wlan >/dev/null 2>&1
|
||||
set +e
|
||||
ERR=$(run_cli "$SBX" migrate on --listen 2>&1 >/dev/null)
|
||||
set -e
|
||||
echo "$ERR" | grep -q "keine Migrationsimages" && pass "nach dem Umschalten warnt sie" || fail "keine Warnung, obwohl der gewaehlte Satz leer ist: $ERR"
|
||||
|
||||
echo
|
||||
if [ "$FAILED" -eq 0 ]; then
|
||||
echo "Alle Tests bestanden."
|
||||
|
||||
Reference in New Issue
Block a user