Lyx bringt kein Test-Framework mit — und braucht keins. Ein Test ist ein Programm, das etwas prüft und PASS/FAIL druckt. Das klingt karg, hat aber einen Vorteil: Ein Test ist genau so schwer zu verstehen wie ein gewöhnliches Programm, und er läuft ohne Zusatzwerkzeug überall, wo der Compiler läuft.
Der Compiler selbst wird so getestet — über 600 Testskripte im aurum-Repository, dazu eine Liste von 505 lauffähigen .lyx-Tests.
→ Fehlersuche · Compiler-Parameter · Standard Library
Die zu prüfende Unit:
unit tst.rechner;
pub fn Addiere(a: int64, b: int64): int64 { return a + b; }
pub fn Teile(a: int64, b: int64, out_rest: int64): int64 {
if (b == 0) { return 0 - 1; }
poke64(out_rest, a - (a / b) * b);
return a / b;
}
Der Test dazu — hier eigenständig, damit er ohne die Nachbardatei übersetzt:
import std.io;
import std.alloc;
// Im Guide steht die gepruefte Unit daneben; hier sind ihre beiden
// Funktionen mit eingerueckt, damit das Beispiel fuer sich uebersetzt.
fn Addiere(a: int64, b: int64): int64 { return a + b; }
fn Teile(a: int64, b: int64, out_rest: int64): int64 {
if (b == 0) { return 0 - 1; }
poke64(out_rest, a - (a / b) * b);
return a / b;
}
var g_pass: int64;
var g_fail: int64;
fn Pruefe(name: pchar, ist: int64, soll: int64): void {
if (ist == soll) {
g_pass := g_pass + 1;
PrintLn(StrConcat("PASS: ", name));
} else {
g_fail := g_fail + 1;
PrintLn(StrConcat(StrConcat("FAIL: ", name), StrConcat(" — erwartet ",
StrConcat(IntToStr(soll), StrConcat(", erhalten ", IntToStr(ist))))));
}
}
fn main(): int64 {
g_pass := 0;
g_fail := 0;
Pruefe("Addiere: gewoehnlich", Addiere(2, 3), 5);
Pruefe("Addiere: Null", Addiere(0, 0), 0);
Pruefe("Addiere: negativ", Addiere(-2, 5), 3);
var rest: int64 := alloc(8);
Pruefe("Teile: Ergebnis", Teile(7, 2, rest), 3);
Pruefe("Teile: Rest", peek64(rest), 1);
Pruefe("Teile: durch Null meldet -1", Teile(7, 0, rest), -1);
free(rest, 8);
PrintLn(StrConcat(StrConcat(IntToStr(g_pass), " PASS, "),
StrConcat(IntToStr(g_fail), " FAIL")));
if (g_fail > 0) { return 1; }
return 0;
}
PASS: Addiere: gewoehnlich
PASS: Addiere: Null
PASS: Addiere: negativ
PASS: Teile: Ergebnis
PASS: Teile: Rest
PASS: Teile: durch Null meldet -1
6 PASS, 0 FAIL
Übersetzt und ausgeführt mit lyxc 1.1.3I; Exit-Code 0.
Drei Dinge daran sind Absicht:
FAIL ohne Zahlen zwingt zum Nachbauen des Falls.1 bei Fehlern) — damit taugt der Test für ein Skript.Wo die Prüfung nicht im Programm stehen kann — Compilerfehler, Ausgabe eines Werkzeugs, Dateigrößen — nimmt man ein Shell-Skript. Das Muster aus dem Compiler-Repository:
#!/bin/bash
# #1670 — ClassName(): Klassenname zur Laufzeit.
set -u
cd "$(dirname "$0")/.."
LYXC=${LYXC:-./lyxc}
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
ok() { echo "PASS: $1"; PASS=$((PASS+1)); }
bad() { echo "FAIL: $1"; FAIL=$((FAIL+1)); }
pruefe() { if [ "$2" = "$3" ]; then ok "$1"; else bad "$1 (erwartet '$3', erhalten '$2')"; fi; }
cat > "$TMP/t.lyx" <<'EOF'
… Testprogramm …
EOF
if ! "$LYXC" --std-path=. "$TMP/t.lyx" -o "$TMP/t" > "$TMP/t.log" 2>&1; then
bad "uebersetzt"; grep -E "error" "$TMP/t.log" | head -3
else
ok "uebersetzt"
timeout 60 "$TMP/t" > "$TMP/t.out" 2>&1
pruefe "dynamischer Name" "$(grep '^dyn=' "$TMP/t.out" | cut -d= -f2)" "TAbl"
fi
Bewährte Punkte daraus:
mktemp -d plus trap — der Test hinterlässt nichts.timeout um jeden Lauf; ein hängender Test blockiert sonst die ganze Reihe.lyxc nie in eine Pipe schreiben lassen, die früh schließt (head, grep -m): SIGPIPE bricht ihn still ab, es entsteht kein Binary, und der Exit-Code bleibt 0.dyn=TAbl) und im Skript danach greifen — robuster als Zeilennummern.
Ein Test, der nur den einfachen Fall abdeckt, ist auch grün, wenn die Umsetzung falsch ist. Beispiel aus dem Bestand: ClassName() auf einer direkt deklarierten Variablen wäre auch mit einer rein statischen Antwort grün — der eigentliche Punkt ist die Variable vom Basistyp, die die tatsächliche Klasse melden muss.
Daraus die Regel: Prüfe das, was kaputtgehen kann, nicht das, was offensichtlich geht.
–compile-unit übersetzt eine Unit für sich allein — bis in den Codegen (#1587). Das ist die billigste Prüfung überhaupt und findet alles, was die Semantikprüfung sieht:
lyxc meineunit.lyx --compile-unit -o meineunit.lyu
Seit lyxc 1.1.x meldet die Prüfung deutlich mehr als früher: unbekannte Felder über Unit-Grenzen, falsche Stelligkeiten, doppelte Mitglieder, Vorwärtsverweise. Eine grüne Übersetzung sagt heute mehr aus als vor einem Jahr.
Im Compiler-Repository hängen die Tests an make-Zielen:
| Ziel | Inhalt |
|---|---|
make test | Die Skripte aus tests/*.sh |
make test-lyx | Die Liste tests/suite-full.txt (505 Einträge) |
make test-known-red | Die bekannt roten aus tests/known-red.txt — wird rot, wenn einer davon wieder grün ist |
Der letzte Punkt ist der interessante: Ein bekannt roter Test, der repariert wurde, meldet sich selbst — sonst veraltet die Liste still.
Ebenfalls aus dem Bestand gelernt: Die Erfolgskonvention war uneinheitlich (manche Tests liefern 0, manche 42, eine Familie druckt „ALL PASS„ und endet mit 1). Der Runner urteilt deshalb nach der Ausgabe: eine FAIL-Zeile ist rot, PASS/OK: ohne FAIL ist grün, ein Absturz (rc ≥ 128) ist immer rot. Wer eine eigene Reihe aufbaut, legt die Konvention besser von Anfang an fest.
Für dieses Wiki gibt es doku-pruefer.py: Es zieht jeden <code lyx>-Block mit fn main( heraus, übersetzt ihn und hält zusätzlich jede Aufruf-Stelligkeit gegen die echte pub fn-Zeile der Standardbibliothek. Dasselbe Verfahren taugt für jede Dokumentation, die Codebeispiele enthält — ein Beispiel, das nie übersetzt wurde, ist eine Behauptung.
timeout. Ein Fehler, der zur Endlosschleife führt, hält die Reihe an, statt aufzufallen.0.LYXC=${LYXC:-./lyxc} macht die Wahl sichtbar.
Letzte Aktualisierung: 2026-08-19 — Beispiel mit lyxc 1.1.3I übersetzt und ausgeführt; Skriptmuster und Suite-Mechanik aus dem aurum-Repository erhoben.