Subject: Re: bug#71300: [PATCH v3] doc: Document SRFI 64.

From: Maxim Cournoyer <[email-masked]>
Date: Mon, 23 Dec 2024 13:48:52 +0900

Hi,

Finally acting on this, now that it's already been merged by Ludovic
^^'.

"Dr. Arne Babenhauserheide" <[email-masked]> writes:

> Maxim Cournoyer <[email-masked]> writes:
>
>> diff --git a/doc/ref/srfi-modules.texi b/doc/ref/srfi-modules.texi
>> index 02da3e2f2..4d408d6cb 100644
>> --- a/doc/ref/srfi-modules.texi
>> +++ b/doc/ref/srfi-modules.texi
>> @@ -55,7 +56,7 @@ get the relevant SRFI documents from the SRFI home page
>>  * SRFI-60::                     Integers as bits.
>>  * SRFI-61::                     A more general `cond' clause
>>  * SRFI-62::                     S-expression comments.
>> -* SRFI-64::                     A Scheme API for test suites.
>> +* SRFI 64::                     A Scheme API for test suites.
>
> If you change this for one, please harmonize the others (with or without
> -). Consistency wins here.

Ludovic kept the current convention, so that's tackled.

>> @@ -5290,12 +5291,827 @@ needed to get SRFI-61 itself.  Extended @code{cond} is documented in
> …
>> +There are other testing frameworks written in Scheme, including
>> +@url{https://docs.racket-lang.org/rackunit/, RackUnit}.  However
>> +RackUnit is not portable.  It is also a bit on the verbose side.  It
>> +would be useful to have a bridge between this framework and RackUnit so
>> +RackUnit tests could run under this framework and vice versa.  There
>> +exists also at least one Scheme wrapper providing a Scheme interface to
>> +the ``standard'' @url{https://www.junit.org/, JUnit} API for Java.  It
>> +would be useful to have a bridge so that tests written using this
>> +framework can run under a JUnit runner.  Neither of these features are
>> +part of this specification.
>
> Is this relevant for Guile?
> If not, I’d take the racket specific part out.

It's relevant to the context in which this SRFI was created.  Not
critical for Guile, but maybe nice for the reader wanting to know the
full story.  I don't feel strongly about removing it though.

>> +This API makes use of implicit dynamic state, including an implicit
>> +``test runner''.  This makes the API convenient and terse to use, but it
>> +may be a little less elegant and ``compositional'' than using explicit
>> +test objects, such as JUnit-style frameworks.  It is not claimed to
>> +follow either object-oriented or functional design principles, but I
>> +hope it is useful and convenient to use and extend.
>
> This sub-sentence ("but I hope...") isn’t needed here, I think.

I turned it into 'but it is hoped', i.e. neutral tone at least.

>> +Test cases are executed in the context of a @dfn{test runner}, which is
>> +a object that accumulates and reports test results.  This
>> specification
>
> Typo: a object -> an object
>
> (this might also be a good change/PR for srfi 64 itself ⇒ upstream)

Thanks.  Fixed, and an upstream PR already submitted for this one and
others you found:
<https://github.com/scheme-requests-for-implementation/srfi-64/pull/9>.

>> +defines how to create and use custom test runners, but implementations
>> +should also provide a default test runner.  It is suggested (but not
>
> Does Guile provide a default test runner?
> ⇒ that may be good to note instead of "should also".

I've added to this paragraph documenting what the implementation in
Guile does:

--8<---------------cut here---------------start------------->8---
modified   doc/ref/srfi-modules.texi
@@ -5393,7 +5393,25 @@ should also provide a default test runner.  It is suggested (but not
 required) that loading the above file in a top-level environment will
 cause the tests to be executed using an implementation-specified default
 test runner, and @code{test-end} will cause a summary to be displayed in
-an implementation-specified manner.
+an implementation-specified manner.  The SRFI 64 implementation used in
+Guile provides such a default test runner; running the above snippet at
+the REPL prints:
+
+@example
+*** Entering test group: vec-test ***
+$1 = #t
+* PASS:
+$2 = ((pass . 1))
+* PASS:
+$3 = ((pass . 2))
+* PASS:
+$4 = ((pass . 3))
+*** Leaving test group: vec-test ***
+*** Test suite finished. ***
+*** # of expected passes    : 3
+@end example
+
+It also returns the @code{<test-runner>} object.

 @subsubheading Simple test-cases
--8<---------------cut here---------------end--------------->8---

>> +required) that loading the above file in a top-level environment will
>> +cause the tests to be executed using an implementation-specified default
>> +test runner, and @code{test-end} will cause a summary to be displayed in
>> +an implementation-specified manner.
> …
>> +For testing approximate equality of inexact reals
>> +we can use @code{test-approximate}:
>> +
>> +@deffn {Scheme Syntax} test-approximate [test-name] expected test-expr error
>> +
>> +This is equivalent to (except that each argument is only evaluated
>> +once):
>> +
>> +@lisp
>> +(test-assert [test-name]
>> +  (and (>= test-expr (- expected error))
>> +       (<= test-expr (+ expected error))))
>> +@end lisp
>> +@end deffn
>
> It would be nice to have an explicit example here.
>

I've added an example:

--8<---------------cut here---------------start------------->8---
@@ -5455,6 +5473,14 @@ once):
   (and (>= test-expr (- expected error))
        (<= test-expr (+ expected error))))
 @end lisp
+
+Here's an example:
+@lisp
+(test-approximate "is 22/7 within 1% of π?"
+ 3.1415926535
+ 22/7
+ 1/100)
+@end lisp
 @end deffn

 @subsubheading Tests for catching errors
--8<---------------cut here---------------end--------------->8---
>> +@lisp
>> +(test-error #t (vector-ref '#(1 2) 9))
>> +@end lisp
>> +
>> +This specification leaves it implementation-defined (or for a future
>> +specification) what form @var{test-error} may take, though all
>> +implementations must allow @code{#t}.
>
> It would be good to show what Guile accepts instead.

I've kept the original text, but appended this paragraph to it:

--8<---------------cut here---------------start------------->8---
@@ -5492,6 +5518,23 @@ classes:

 An implementation that cannot catch exceptions should skip
 @code{test-error} forms.
+
+The SRFI 64 implementation in Guile supports specifying @var{error-type}
+as either:
+@iterate
+@item
+@code{#f}, meaning the test is @emph{not} expected to produce an error
+@item
+@code{#t}, meaning the test is expected to produce an error, of any type
+@item
+A native exception type, as created via @code{make-exception-type} or
+@code{make-condition-type} from SRFI-35
+@item
+A predicate, which will be applied to the exception caught
+to determine whether is it of the right type
+@item
+A symbol, for the exception kind of legacy
+@code{make-exception-from-throw} style exceptions.
 @end deffn

 @subsubheading Testing syntax
--8<---------------cut here---------------end--------------->8---

> …
>> +@lisp
>> +;; Kawa-specific example
>> +(test-error <java.lang.IndexOutOfBoundsException> (vector-ref '#(1 2) 9))
>> +@end lisp
>
> An example with Guile would be good.

I've added this:

--8<---------------cut here---------------start------------->8---
 @end deffn

+Below are some examples valid in Guile:
+
+@lisp
+(test-error "expect old-style exception kind"
+ 'numerical-overflow
+ (/ 1 0))
+@end lisp
+
+@lisp
+(use-modules (ice-9 exceptions)) ;for standard exception types
+
+(test-error "expect a native exception type"
+ &warning
+ (raise-exception (make-warning)))
+
+(test-error "expect a native exception, using predicate"
+ warning?
+ (raise-exception (make-warning)))
+@end lisp
+
+@lisp
+(use-modules (srfi srfi-35))
+
+(test-error "expect a serious SRFI 35 condition type"
+ &serious
+ (raise-exception (condition (&serious))))
+
+(test-error "expect a serious SRFI 35 condition type, using predicate"
+ serious-condition?
+ (raise-exception (condition (&serious))))
+@end lisp
+
 @subsubheading Testing syntax
--8<---------------cut here---------------end--------------->8---

>> +@subsubheading Test specifiers
>> +
>> +Sometimes we want to only run certain tests, or we know that certain
>> +tests are expected to fail.  A @dfn{test specifier} is one-argument
>> +function that takes a test-runner and returns a boolean.  The
>> specifier
>
> is *a* one-argument function
> (also for upstream)

Fixed.

> Aside from these, this patch looks good to me.
> Thank you!

Thank you.  As I said, it's been applied already, thanks to Ludovic.
I'll try ensure this follow-up also does!

-- 
Thanks,
Maxim