==== Automatisches Testen zur Ladezeit ====
Die korrekte Funktionsweise von Wort-Definitionen sollte sichergestellt werden, um robuste und zuverlässige Programme zu bekommen.
Traditionell testet der Forth-Programmierer seine Definitionen "Bottom-Up" unmittelbar nach dem er sie vorgenommen hat.
Um dieses Tests etwa bei Änderungen nicht ständig wiederholen zu müssen, macht es Sinn, sie zu automatisieren und vom Forth-System selbst vornehmen zu lassen. Das ist insbesondere sinnvoll, um die korrekte Funktionsweise von Bibliotheks-Funktionen sicherzustellen, deren Definitionen auf verschiedene - sich ja in Details unterscheidende - Forth-Systeme geladen werden sollen.
In Forth kann man die Ladezeit, die Übersetzungszeit und die Laufzeit unterscheiden. Der hier
vorgestellte kleine Test-Rahmen erlaubt das Testen zur Ladezeit.
Nachdem Definitionen vorgenommen werden, etwa das Durchlaufen einer gelinkten Liste mit Aufsummieren der Elemente:
: sum-list ( ’list -- n )
\ calculate the sum N of all elements in list ’LIST
0 SWAP
BEGIN ( n l )
?DUP
WHILE ( n l )
DUP CELL+ @ ROT +
SWAP @
REPEAT ; ( n )
ist sicherzustellen, dass diese Definition wie erwartet arbeitet. Dazu legt man im Dictonary geeignete Datenstrukturen an, ruft ''sum-list'' auf und überprüft das Resultat. Der Forth-Programmierer macht das meist interaktiv, aber wir wollen das hier zur Ladezeit erledigen und schreiben im Anschluss an die ''sum-list''-Definition:
Test,_that 0 sum-list has 0 as_result.
Test,_that here 0 , 30 ,
here swap , 20 ,
here swap , 10 ,
sum-list
has 60 as_result.
Jeder Testfall wird mit ''Test,_that'' eingeleitet (friert den Stack und Dictionary-Zustand ein).
Dann werden Hilfsdefinitionen vorgenommen (die ggf, das Dictionary ändern).
Es folgen Aufrufe der ursprünglichen Definitionen, ''sum-list'' in unserem Beispiel.
Mit ''has'' ... ''as_result.'' wird überprüft, dass der Stack die gewünschten Werte enthält
und bei Nichtübereinstimmung ein Fehler ausgegeben. Stimmen berechnete und Sollwerte
auf dem Stack überein, wird der Stack und das Dictionary von allen Testdefinition bereinigt,
so dass nur die ursprünglichen Definitionen übrig bleiben.
Dann kann mit dem Laden des weiteren Programms fortgefahren werden.
Hier also die Implementierung des Test-Rahmens:
\ Test frame
\ Test,_that has as_result.
VARIABLE d0
VARIABLE d1
: #items ( -- n ) d1 @ d0 @ - ;
: Test,_that ( i*x -- i*x )
DEPTH d0 !
S" MARKER *TeSt*" EVALUATE ;
: has ( i*x -- i*x ) DEPTH d1 ! ;
: ?wrong ( f -- ) Abort" Test failed!" ;
: as_result. ( d0*x [d1-d0]*x i*x -- d0*x )
DEPTH d1 @ - #items - ?wrong
#items 0 ?DO I PICK I #items + 1+ PICK - ?wrong LOOP
#items 2* 0 ?DO DROP LOOP
S" *TeSt*" EVALUATE ;