Raku Books / Creating a Compiler in Raku / Test suite
Test runner
Let us start with a simple program that creates a variable and assigns a numeric value to it. We have at least two ways to do that:
my a;
a = 10;
my b = 20;This program does not print anything, so let us add a couple of
say calls.
my a;
a = 10;
say a;
my b = 20;
say b;Save the test in a file, for example,
variable-declaration.t, in a dedicated directory
t and run it. It will generate the result mixed with some
test output (e.g., a full AST and a dump of the %!var hash)
that we added to the compiler earlier. As we are still going to work on
the compiler, let us keep the helper output but filter it out to
/dev/null. The good thing is that Rakudo’s dd
routine prints its output to STDERR, not to
STDOUT.
$ ./lingua t/variable-declaration.t 2>/dev/null
OK
10
20This looks clean, and we even can omit the OK line by using the
note function instead of a conventional
say:
if $ast {
note 'OK';
$evaluator.eval($ast.made);
}Now, all the helper output goes to STDERR, and the
program payload is printed via STDOUT.
$ ./lingua t/variable-declaration.t 2>/dev/null
10
20This output is what we have to compare with the correct result. The simplest way is to save it in another file and just compare the files. The other option is to type the answers in the test program itself in the comments. To allow other comments, which are real comments explaining the program, let us start our special type of comments with a double hash sign:
my a;
a = 10;
say a; ## 10
my b = 20;
say b; ## 20The task is now trivial. We need to create a script that run all the
files in the t directory, extracts the comments from them,
and compares the expected data with the real output. To simplify the
task even more, let us assume that the test files are small programs, so
if something goes wrong, you can easily spot the line where the error
happened.
Let us create another executable program, run-tests. It
scans the t directory and runs the tests one by one.
#!/usr/bin/env raku
my @errors;
test-file($_) for dir('t').sort;
say '-' x 9, ' Summary ', '-' x 9;
if @errors {
my $n = @errors.elems;
my $s = $n > 1 ?? 's' !! '';
say "$n test file$s failed:";
say "\t$_" for @errors;
}
else {
say "All tests successfully ran.";
}If there were tests with errors, their list will be printed at the end of the output.
Running a single test is quite straightforward. The following
function does the job using the built-in run function, that places the
output from STDOUT and STDERR to the
out and err attributes.
sub test-file($file) {
my $path = $file.path;
print "$path... ";
my $proc = run('./lingua', $path, :out, :err);
my $out = $proc.out.slurp;
my $err = $proc.err.slurp;
my $ok = 1;
if $err ~~ /^Error/ {
$ok = 0;
}
else {
my $expected = expected($path);
if $expected ne $out {
say "\nGOT: $out";
say "EXPECTED: $expected";
$ok = 0;
}
}
if $ok {
say "\e[32mOK\e[39m";
}
else {
say "\e[31mFAIL\e[39m";
$err ~~ s:g/^^/# /;
say $err;
push @errors, $path;
}
}Escape sequences in the output add colours so that you can
immediately see red FAILs or green OKs.
There are two possibilities to fail a test: either the file was not
parsed, or it generated the output, which differs from expectations. The
expected output string is computed from the ##
comments:
sub expected($path) {
my $expected;
for $path.IO.lines -> $line {
$line ~~ /'##'\s*(\N*)/;
next unless $0;
$expected ~= "$0\n";
}
return $expected;
}🖥 This program reads the local environment (files, directories, or the command line), so it runs on your own computer but not in the sandboxed in-browser engine.
Run the program and you should see the following output:
$ ./run-tests
t/variable-declaration.t... OK
--------- Summary ---------
All tests successfully ran.Now we can add more tests to cover more features of Lingua.
Course navigation
← Test suite | Tests →