Here’s a very small unit testing framework that I whopped up in a few minutes, in case it’s useful to anyone. Test cases are declared with (test $) and by inclusion in (list of tests []), and pass if they succeed and fail if they fail.
%% testrunner.dg
(extension version) Unit test runner v0.1 by Susan Davis (line)
%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])
(interface (test $<Testname))
(all tests pass)
(program entry point)
(list of tests $Tests)
(length of $Tests into $Number)
(bold) Attempting $Number unit tests (roman) (line)
(stoppable) (exhaust) {
*($Test is one of $Tests)
Testing $Test:
(run-test $Test)
}
(if) (all tests pass) (then)
(bold) $Number tests passed successfully. (roman) (par)
(else)
(bold) FAILED TEST. (roman) (par)
(endif)
(error $Code entry point)
(now) ~(all tests pass)
(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
(stop)
(run-test $Test)
(if)
(test $Test)
(then)
Passed! (line)
(else)
Failed. (space) :-\( (line)
(now) ~(all tests pass)
(stop)
(endif)
Exit with an error status code if applicable, if @lft adds support for exiting the debugger with a specified status someday.
%% testrunner.dg
(extension version) Unit test runner v0.4 by Susan Davis (line)
%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])
(interface (test $<Testname))
(interface (set up $<Testname)) %% special setup for the test
(set up $) %% don't fail if no setup provided
(interface (clean up $<Testname)) %% restore to original state afterward
(clean up $) %% don't fail if no cleanup provided
(global variable (list of tests $))
(all tests pass)
(program entry point)
(list of tests $Tests)
(length of $Tests into $Number)
(bold) Attempting $Number unit tests (roman) (line)
(stoppable) (exhaust) {
*($Test is one of $Tests)
Testing $Test:
(run-test $Test)
}
(if) (all tests pass) (then)
(bold) $Number tests passed successfully. (roman) (par)
(quit 0)
(else)
(bold) FAILED TEST. (roman) (par)
(quit 1)
(endif)
(error $Code entry point)
(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
(now) ~(all tests pass)
(quit 1) %% or -1 if we ever get signed integers
(run-test $Test)
(if)
(set up $Test)
(test $Test)
(clean up $Test)
(then)
Passed! (line)
(else)
Failed. (space) :-\( (line)
(now) ~(all tests pass)
(stop)
(endif)
%% Delete this interface and predicate if lft adds a builtin for it in
%% dgdebug, to specify the exit status from the debugger for CI/CD pipelines
%% and/or shell scripts.
(interface (quit $<ExitStatus))
(quit $) (quit)
Added a patch from @ifste to fix some behaviour in ($ contains sublist $)
%% testrunner.dg
(extension version) Unit test runner v0.5 by Susan Davis (line)
%% (test #foo) expr will pass if expr is true and fail if expr is false.
%% Include #foo in the global variable (list of tests [test1 test2 ...])
(interface (test $<Testname))
(interface (set up $<Testname)) %% special setup for the test
(interface (clean up $<Testname)) %% restore to original state afterward
(interface (assert $<Var = $Val))
(assert $Thing1 = $Thing2) %% true if both are bound and ($Thing1 = $Thing2)
(fully bound $Thing1)
(bound $Thing2)
($Thing1 = $Thing2)
(global variable (list of tests $))
(all tests pass)
(program entry point)
(list of tests $Tests)
(length of $Tests into $Number)
(bold) Attempting $Number unit tests (roman) (line)
(stoppable) (exhaust) {
*($Test is one of $Tests)
Testing $Test:
(run-test $Test)
}
(if) (all tests pass) (then)
(bold) $Number tests passed successfully. (roman) (par)
(quit 0)
(else)
(bold) FAILED TEST. (roman) (par)
(quit 1)
(endif)
(error $Code entry point)
(bold) FAILED TEST -- FATAL ERROR $Code. (roman) (par)
(now) ~(all tests pass)
(quit 1) %% or -1 if we ever get signed integers
(run-test $Test)
(if)
(stoppable) (set up $Test) %% don't fail if absent
(test $Test)
(stoppable) (clean up $Test) %% don't fail if absent
(then)
Passed! (line)
(else)
Failed. (space) :-\( (line)
(now) ~(all tests pass)
(stop)
(endif)
%% patch for ($ contains sublist $) issues; delete when fixed
($List contains sublist [$Head | $Tail])
*(split $List by [$Head] into $ and $Rest)
(append $Tail $ $Rest)
%% Delete this interface and predicate if lft adds a builtin for it in
%% dgdebug, to specify the exit status from the debugger for CI/CD pipelines
%% and/or shell scripts.
(interface (quit $<ExitStatus))
(quit $) (quit)