Andrew,
Thanks for your reply.
On 2026-06-19 18:29 +0700, Andrew Tropin wrote:
> > 1. Why is the parameter called 'test-runner*' and not 'test-runner'?
>
> No particular reason, it's just a naming convention in guile-ares-rs
> project (where reference implementation of the suitbl testing library is
> developed) to make dynamically scoped variables stand out. I derived it
> from clojure's style of `*dynamic-var*`, but without prefix asterisk. I
> know it conflicts with let* and alike. Also, somebody already mentioned
> and suggest current-test-runner.
Yes, I heartily endorse 'current-test-runner'. The "current-" prefix
is standard for several parameters. SRFI 64 also uses
'current-test-runner' for the equivalent feature.
> > 2. There is currently no assertion support for multiple values.
>
> [snip]
>
> I gave it a brief thought and it still seems general enough (I will
> address issue with predicate form replying to the #3). The design of
> SRFI-269 is heavily relies on shifting as much as possible to test
> runner and focusing only on definition semantics, not execusion. So,
> it's the responsibility of test runner to decide what asserting means.
>
> As for test runner in suitbl, we just inherit semantics of `if`. For
> Guile it's following (I just checked it a few minutes ago): If
> there are 0 values, it's exception, if more than zero the first value is
> checked for truethness.
(This is intertwined with the predicate-argument form issue.)
I think this is a bit too subtle, especially when the default test
runner may silently discard extra values! There should be a way to
test multiple values explicitly, rather than leaving it to the runner
to decide what a "pass" is in the multiple-values case (all values
truthy?).
Here's a rough sketch of a reworked assertion form which supports
multi-valued test expressions *and* some degree of rich-mode error
reporting. It is based vaguely on SRFI 61's generalized 'cond'
clause.
== Begin spec ==
(is <generator> <consumer>)
<generator> and <consumer> are expressions, and <consumer> must
evaluate to a procedure. Asserts that
(call-with-values (lambda () <generator>)
<consumer>)
evaluates to a single true value. The entity constructed from this
form has (at least) the following keys:
assertion/generator-thunk
Equivalent to (lambda () <generator>).
assertion/generator-source
A datum representation of <generator>.
assertion/consumer-proc
The value of <consumer>.
assertion/consumer-source
A datum representation of <consumer>.
assertion/location
An assertion source code location
(is <generator> <consumer> <description>)
As above, with a human-readable description. (Keys as above, plus
'assertion/description'.)
(is <expression>)
Special case. Equivalent to
(is <expression> values).
== End spec ==
This subsumes the current assertion model. The special-case
'assertion/args-thunk' can be eliminated, since this model decouples
test-expression evaluation and consumer application. <generator>, its
values, and <consumer> can all be reported separately, without the need
for subterm analysis. (Instead of writing (is (integer? (+ 2 2))) to
get rich-mode reporting, you would write (is (+ 2 2) integer?).)
A minor drawback is losing (is <expression> <description>) due to
syntactic ambiguity. That needs some work. One solution is to "tag"
the <description> argument, e.g.
(is (integer? 4) (desc "4 is an integer")).
While not exactly beautiful, this trick has the advantage of allowing
descriptions to appear anywhere, since they can be unambiguously
identified:
(is (desc "4 is an integer") 4 integer?)
I'm not sure the additional syntax is worth it, but it's an idea. SRFI
64, chibi-test, and CHICKEN test users are probably used to writing
their descriptions first, which becomes possible with a tagged
<description> argument.
Let me know what you think.
--
Wolfgang Corcoran-Mathe <xxxxxx@sigwinch.xyz>