Skip to content

Commit

Permalink
Introducing Routine is test-assertion trait (#3991)
Browse files Browse the repository at this point in the history
Introducing Routine is test-assertion trait

Marking a subroutine with the "is test-assertion" trait, indicates that
the subroutine produces Test (aka TAP) output.  All of the exported
subroutines in Test implicitely have this trait.

When a test fails and a failure needs to be reported, the file and
line at which a test assertion is *called* is shown.  However, if you build
your own subroutines for doing several similar tests depending
on arguments given, any failure will be reported *inside* that subroutine
rather than at the location where your own subroutine was called.  Which
is extremely annoying when writing extensive, parameter driven tests.

With this trait, you can mark your own testing subroutines to get failing
tests to report at the place your own testing subroutine is called!

    use Test;
    sub foo-test($value) is test-assertion {
        is $value, 42, "is the value 42?";  # <-- do *not* report this line
    }
    foo-test(666);    # <-- report *this* line

You can even nest such test assertion subroutines: any failures will
always point to the call to the outer subroutine;

    sub bar-test($value) is test-assertion {
        foo-test($value);  # <-- do *not* report this line
    }
    bar-test(666);    # <-- report this line
  • Loading branch information
lizmat committed Oct 30, 2020
1 parent 084b92a commit 585db59
Showing 1 changed file with 21 additions and 6 deletions.
27 changes: 21 additions & 6 deletions lib/Test.rakumod
Expand Up @@ -64,6 +64,12 @@ our sub todo_output is rw {
$todo_output
}

multi sub trait_mod:<is>(Routine:D $r, :$test-assertion!) is export {
$r.^mixin( role is-test-assertion {
method is-test-assertion(--> True) { }
}) if $test-assertion;
}

proto sub plan ($?, Cool :$skip-all) {*}

my class X::SubtestsSkipped is Exception {}
Expand Down Expand Up @@ -755,15 +761,24 @@ sub proclaim(Bool(Mu) $cond, $desc is copy, $unescaped-prefix = '') {
unless $cond {
my $caller;
# sub proclaim is not called directly, so 2 is minimum level
my int $level = 2;
my int $level = 1;

repeat while $?FILE.ends-with($caller.file) {
$caller = callframe($level++);
}
repeat {
$caller = callframe(++$level);
} while $?FILE.ends-with($caller.file);

# initial level for reporting
my $tester = $caller;

repeat {
$tester = callframe($level) # the next one should be reported
if ($caller = callframe($level++)).code.?is-test-assertion;
} until $caller.file.ends-with('.nqp');

# the final place we want to report from
_diag $desc
?? "Failed test '$desc'\nat $caller.file() line $caller.line()"
!! "Failed test at $caller.file() line $caller.line()";
?? "Failed test '$desc'\nat $tester.file() line $tester.line()"
!! "Failed test at $tester.file() line $tester.line()";
}

# must clear this between tests
Expand Down

0 comments on commit 585db59

Please sign in to comment.