mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
doc: Document SRFI-64.
This is an import of the 'Abstract', 'Rationale', and 'Specification' sections from the upstream specification text, with some manual adjustment. * doc/ref/srfi-modules.texi (SRFI 64): New subsection. Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
parent
c9d0a0c48c
commit
98ffeacf2c
2 changed files with 847 additions and 7 deletions
|
@ -24,8 +24,31 @@ any later version published by the Free Software Foundation; with no
|
|||
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
|
||||
copy of the license is included in the section entitled ``GNU Free
|
||||
Documentation License.''
|
||||
@end copying
|
||||
|
||||
Additionally, the documentation of the SRFI 64 module is adapted from
|
||||
its specification text, which is made available under the following
|
||||
Expat license:
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@end copying
|
||||
|
||||
@c Notes
|
||||
@c
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
@c -*-texinfo-*-
|
||||
@c This is part of the GNU Guile Reference Manual.
|
||||
@c Copyright (C) 1996, 1997, 2000-2004, 2006, 2007-2014, 2017, 2018, 2019, 2020
|
||||
@c Copyright (C) 1996-1997, 2000-2004, 2006-2014, 2017-2020, 2023
|
||||
@c Free Software Foundation, Inc.
|
||||
@c Copyright (C) 2005-2006 Per Bothner
|
||||
@c See the file guile.texi for copying conditions.
|
||||
|
||||
@node SRFI Support
|
||||
|
@ -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:: Writing test suites.
|
||||
* SRFI-67:: Compare procedures
|
||||
* SRFI-69:: Basic hash tables.
|
||||
* SRFI-71:: Extended let-syntax for multiple values.
|
||||
|
@ -5292,12 +5293,828 @@ needed to get SRFI-61 itself. Extended @code{cond} is documented in
|
|||
Starting from version 2.0, Guile's @code{read} supports SRFI-62/R7RS
|
||||
S-expression comments by default.
|
||||
|
||||
@c This SRFI 64 documentation was "snarfed" from upstream specification
|
||||
@c HTML document using the 'snarfi' script.
|
||||
@node SRFI-64
|
||||
@subsection SRFI-64 - A Scheme API for test suites.
|
||||
@cindex SRFI-64
|
||||
@subsection SRFI-64: A Scheme API for Test Suites
|
||||
@cindex SRFI-64, test suites
|
||||
@cindex test suites, SRFI-64
|
||||
|
||||
See @uref{http://srfi.schemers.org/srfi-64/srfi-64.html, the
|
||||
specification of SRFI-64}.
|
||||
@menu
|
||||
* SRFI-64 Abstract::
|
||||
* SRFI-64 Rationale::
|
||||
* SRFI-64 Writing Basic Test Suites::
|
||||
* SRFI-64 Conditonal Test Suites and Other Advanced Features::
|
||||
* SRFI-64 Test Runner::
|
||||
* SRFI-64 Test Results::
|
||||
* SRFI-64 Writing a New Test Runner::
|
||||
@end menu
|
||||
|
||||
@node SRFI-64 Abstract
|
||||
@subsubsection SRFI-64 Abstract
|
||||
|
||||
This defines an API for writing @dfn{test suites}, to make it easy to
|
||||
portably test Scheme APIs, libraries, applications, and implementations.
|
||||
A test suite is a collection of @dfn{test cases} that execute in the
|
||||
context of a @dfn{test-runner}. This specification also supports
|
||||
writing new test-runners, to allow customization of reporting and
|
||||
processing the result of running test suites.
|
||||
|
||||
@node SRFI-64 Rationale
|
||||
@subsubsection SRFI-64 Rationale
|
||||
|
||||
The Scheme community needs a standard for writing test suites. Every
|
||||
SRFI or other library should come with a test suite. Such a test suite
|
||||
must be portable, without requiring any non-standard features, such as
|
||||
modules. The test suite implementation or "runner" need not be
|
||||
portable, but it is desirable that it be possible to write a portable
|
||||
basic implementation.
|
||||
|
||||
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.
|
||||
|
||||
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 proposal allows converting a Scheme source file to a
|
||||
test suite by just adding a few macros. You don't have to
|
||||
write the entire file in a new form, thus you don't have to
|
||||
re-indent it.
|
||||
|
||||
All names defined by the API start with the prefix @samp{test-}. All
|
||||
function-like forms are defined as syntax. They may be implemented as
|
||||
functions or macros or built-ins. The reason for specifying them as
|
||||
syntax is to allow specific tests to be skipped without evaluating
|
||||
sub-expressions, or for implementations to add features such as printing
|
||||
line numbers or catching exceptions.
|
||||
|
||||
@node SRFI-64 Writing Basic Test Suites
|
||||
@subsubsection SRFI-64 Writing Basic Test Suites
|
||||
|
||||
Let's start with a simple example. This is a complete self-contained
|
||||
test-suite.
|
||||
|
||||
@lisp
|
||||
;; Initialize and give a name to a simple testsuite.
|
||||
(test-begin "vec-test")
|
||||
(define v (make-vector 5 99))
|
||||
;; Require that an expression evaluate to true.
|
||||
(test-assert (vector? v))
|
||||
;; Test that an expression is eqv? to some other expression.
|
||||
(test-eqv 99 (vector-ref v 2))
|
||||
(vector-set! v 2 7)
|
||||
(test-eqv 7 (vector-ref v 2))
|
||||
;; Finish the testsuite, and report results.
|
||||
(test-end "vec-test")
|
||||
@end lisp
|
||||
|
||||
This testsuite could be saved in its own source file. Nothing else is
|
||||
needed: We do not require any top-level forms, so it is easy to wrap an
|
||||
existing program or test to this form, without adding indentation. It
|
||||
is also easy to add new tests, without having to name individual tests
|
||||
(though that is optional).
|
||||
|
||||
Test cases are executed in the context of a @dfn{test runner}, which is
|
||||
a object that accumulates and reports test results. This specification
|
||||
defines how to create and use custom test runners, but implementations
|
||||
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.
|
||||
|
||||
@subsubheading Simple test-cases
|
||||
|
||||
Primitive test cases test that a given condition is true. They may have
|
||||
a name. The core test case form is @code{test-assert}:
|
||||
|
||||
@deffn {Scheme Syntax} test-assert [test-name] expression
|
||||
|
||||
This evaluates the @var{expression}. The test passes if the result is
|
||||
true; if the result is false, a test failure is reported. The test also
|
||||
fails if an exception is raised, assuming the implementation has a way
|
||||
to catch exceptions. How the failure is reported depends on the test
|
||||
runner environment. The @var{test-name} is a string that names the test
|
||||
case. (Though the @var{test-name} is a string literal in the examples,
|
||||
it is an expression. It is evaluated only once.) It is used when
|
||||
reporting errors, and also when skipping tests, as described below. It
|
||||
is an error to invoke @code{test-assert}if there is no current test
|
||||
runner.
|
||||
@end deffn
|
||||
|
||||
The following forms may be more convenient than using @code{test-assert}
|
||||
directly:
|
||||
|
||||
@deffn {Scheme Syntax} test-eqv [test-name] expected test-expr
|
||||
|
||||
This is equivalent to:
|
||||
|
||||
@lisp
|
||||
(test-assert [@var{test-name}] (eqv? @var{expected} @var{test-expr}))
|
||||
@end lisp
|
||||
|
||||
@end deffn
|
||||
|
||||
Similarly @code{test-equal} and @code{test-eq} are shorthand for
|
||||
@code{test-assert} combined with @code{equal?} or @code{eq?},
|
||||
respectively:
|
||||
|
||||
@deffn {Scheme Syntax} test-equal [test-name] expected test-expr
|
||||
@deffnx {Scheme Syntax} test-eq [test-name] expected test-expr
|
||||
|
||||
Here is a simple example:
|
||||
|
||||
@lisp
|
||||
(define (mean x y) (/ (+ x y) 2.0))
|
||||
(test-eqv 4 (mean 3 5))
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
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
|
||||
|
||||
@subsubheading Tests for catching errors
|
||||
|
||||
We need a way to specify that evaluation @emph{should} fail. This
|
||||
verifies that errors are detected when required.
|
||||
|
||||
@deffn {Scheme Syntax} test-error [[test-name] error-type] test-expr
|
||||
|
||||
Evaluating @var{test-expr} is expected to signal an error. The kind of
|
||||
error is indicated by @var{error-type}.
|
||||
|
||||
If the @var{error-type} is left out, or it is @code{#t}, it means "some
|
||||
kind of unspecified error should be signaled". For example:
|
||||
|
||||
@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}. Some implementations may support
|
||||
@url{https://srfi.schemers.org/srfi-35/srfi-35.html, SRFI-35's
|
||||
conditions}, but these are only standardized for
|
||||
@url{https://srfi.schemers.org/srfi-36/srfi-36.html, SRFI-36's I/O
|
||||
conditions}, which are seldom useful in test suites. An implementation
|
||||
may also allow implementation-specific ``exception types''. For example
|
||||
Java-based implementations may allow the names of Java exception
|
||||
classes:
|
||||
|
||||
@lisp
|
||||
;; Kawa-specific example
|
||||
(test-error <java.lang.IndexOutOfBoundsException> (vector-ref '#(1 2) 9))
|
||||
@end lisp
|
||||
|
||||
An implementation that cannot catch exceptions should skip
|
||||
@code{test-error} forms.
|
||||
@end deffn
|
||||
|
||||
@subsubheading Testing syntax
|
||||
|
||||
Testing syntax is tricky, especially if we want to check that invalid
|
||||
syntax is causing an error. The following utility function can help:
|
||||
|
||||
@deffn {Scheme Procedure} test-read-eval-string string
|
||||
|
||||
This function parses @var{string} (using @code{read}) and evaluates the
|
||||
result. The result of evaluation is returned from
|
||||
@code{test-read-eval-string}. An error is signalled if there are unread
|
||||
characters after the @code{read} is done. For example:
|
||||
@code{(test-read-eval-string "(+ 3 4)")} @i{evaluates to} @code{7}.
|
||||
@code{(test-read-eval-string "(+ 3 4")} @i{signals an error}.
|
||||
@code{(test-read-eval-string "(+ 3 4) ")} @i{signals an error}, because
|
||||
there is extra ``junk'' (@i{i.e.} a space) after the list is read.
|
||||
|
||||
The @code{test-read-eval-string} used in tests:
|
||||
|
||||
@lisp
|
||||
(test-equal 7 (test-read-eval-string "(+ 3 4)"))
|
||||
(test-error (test-read-eval-string "(+ 3"))
|
||||
(test-equal #\newline (test-read-eval-string "#\\newline"))
|
||||
(test-error (test-read-eval-string "#\\newlin"))
|
||||
;; Skip the next 2 tests unless srfi-62 is available.
|
||||
(test-skip (cond-expand (srfi-62 0) (else 2)))
|
||||
(test-equal 5 (test-read-eval-string "(+ 1 #;(* 2 3) 4)"))
|
||||
(test-equal '(x z) (test-read-string "(list 'x #;'y 'z)"))
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
@subsubheading Test groups and paths
|
||||
|
||||
A @dfn{test group} is a named sequence of forms containing testcases,
|
||||
expressions, and definitions. Entering a group sets the @dfn{test group
|
||||
name}; leaving a group restores the previous group name. These are
|
||||
dynamic (run-time) operations, and a group has no other effect or
|
||||
identity. Test groups are informal groupings: they are neither Scheme
|
||||
values, nor are they syntactic forms.
|
||||
@c (More formal <q>test suite</q> values are introduced below.)
|
||||
A test group may contain nested inner test groups.
|
||||
The @dfn{test group path} is a list of the currently-active
|
||||
(entered) test group names, oldest (outermost) first.
|
||||
|
||||
@deffn {Scheme Syntax} test-begin suite-name [count]
|
||||
|
||||
A @code{test-begin} enters a new test group. The @var{suite-name}
|
||||
becomes the current test group name, and is added to the end of the test
|
||||
group path. Portable test suites should use a string literal for
|
||||
@var{suite-name}; the effect of expressions or other kinds of literals
|
||||
is unspecified.
|
||||
|
||||
@emph{Rationale:} In some ways using symbols would be preferable.
|
||||
However, we want human-readable names, and standard Scheme does not
|
||||
provide a way to include spaces or mixed-case text in literal symbols.
|
||||
|
||||
The optional @var{count} must match the number of test-cases executed by
|
||||
this group. (Nested test groups count as a single test case for this
|
||||
count.) This extra test may be useful to catch cases where a test
|
||||
doesn't get executed because of some unexpected error.
|
||||
|
||||
Additionally, if there is no currently executing test runner,
|
||||
one is installed in an implementation-defined manner.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} test-end [suite-name]
|
||||
|
||||
A @code{test-end} leaves the current test group.
|
||||
An error is reported if the @var{suite-name} does not
|
||||
match the current test group name.
|
||||
@c If it does match an earlier name in the test group path, intervening
|
||||
@c groups are left.
|
||||
|
||||
Additionally, if the matching @code{test-begin}installed a new
|
||||
test-runner, then the @code{test-end} will uninstall it, after reporting
|
||||
the accumulated test results in an implementation-defined manner.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} test-group suite-name decl-or-expr @dots{}
|
||||
|
||||
Equivalent to:
|
||||
|
||||
@lisp
|
||||
(if (not (test-to-skip% (var suite-name)))
|
||||
(dynamic-wind
|
||||
(lambda () (test-begin (var suite-name)))
|
||||
(lambda () (var decl-or-expr) ...)
|
||||
(lambda () (test-end (var suite-name)))))
|
||||
@end lisp
|
||||
|
||||
This is usually equivalent to executing the @var{decl-or-expr}s
|
||||
within the named test group. However, the entire group is skipped
|
||||
if it matched an active @code{test-skip} (see later).
|
||||
Also, the @code{test-end} is executed in case of an exception.
|
||||
@end deffn
|
||||
|
||||
@subsubheading Handling set-up and cleanup
|
||||
|
||||
@deffn {Scheme Syntax} test-group-with-cleanup suite-name decl-or-expr @dots{} cleanup-form
|
||||
|
||||
Execute each of the @var{decl-or-expr} forms in order (as in a
|
||||
@var{<body>}), and then execute the @var{cleanup-form}. The latter
|
||||
should be executed even if one of a @var{decl-or-expr} forms raises an
|
||||
exception (assuming the implementation has a way to catch exceptions).
|
||||
|
||||
For example:
|
||||
|
||||
@lisp
|
||||
(let ((f (open-output-file "log")))
|
||||
(test-group-with-cleanup "test-file"
|
||||
(do-a-bunch-of-tests f)
|
||||
(close-output-port f)))
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
@node SRFI-64 Conditonal Test Suites and Other Advanced Features
|
||||
@subsubsection SRFI-64 Conditonal Test Suites and Other Advanced Features
|
||||
|
||||
The following describes features for controlling which tests to execute,
|
||||
or specifying that some tests are @emph{expected} to fail.
|
||||
|
||||
@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
|
||||
may be run before a test is performed, and the result may control
|
||||
whether the test is executed. For convenience, a specifier may also be
|
||||
a non-procedure value, which is coerced to a specifier procedure, as
|
||||
described below for @var{count} and @var{name}.
|
||||
|
||||
A simple example is:
|
||||
|
||||
@lisp
|
||||
(if (var some-condition) (test-skip 2)) ;; skip next 2 tests
|
||||
@end lisp
|
||||
|
||||
@deffn {Scheme Procedure} test-match-name name
|
||||
|
||||
The resulting specifier matches if the current test name (as returned by
|
||||
@code{test-runner-test-name}) is @code{equal?} to @var{name}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} test-match-nth n [count]
|
||||
|
||||
This evaluates to a @emph{stateful} predicate: A counter keeps track of
|
||||
how many times it has been called. The predicate matches the @var{n}'th
|
||||
time it is called (where @code{1} is the first time), and the next
|
||||
@samp{(- @var{count} 1)} times, where @var{count} defaults to @code{1}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} test-match-any specifier @dots{}
|
||||
|
||||
The resulting specifier matches if any @var{specifier} matches. Each
|
||||
@var{specifier} is applied, in order, so side-effects from a later
|
||||
@var{specifier} happen even if an earlier @var{specifier} is true.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} test-match-all specifier @dots{}
|
||||
|
||||
The resulting specifier matches if each @var{specifier} matches. Each
|
||||
@var{specifier} is applied, in order, so side-effects from a later
|
||||
@var{specifier} happen even if an earlier @var{specifier} is false.
|
||||
@end deffn
|
||||
|
||||
@var{count} @i{(i.e. an integer)}
|
||||
Convenience short-hand for: @samp{(test-match-nth 1 @var{count})}.
|
||||
|
||||
@var{name} @i{(i.e. a string)}
|
||||
Convenience short-hand for @samp{(test-match-name @var{name})}.
|
||||
|
||||
@subsubheading Skipping selected tests
|
||||
|
||||
In some cases you may want to skip a test.
|
||||
|
||||
@deffn {Scheme Syntax} test-skip specifier
|
||||
|
||||
Evaluating @code{test-skip} adds the resulting @var{specifier} to the
|
||||
set of currently active skip-specifiers. Before each test (or
|
||||
@code{test-group}) the set of active skip-specifiers are applied to the
|
||||
active test-runner. If any specifier matches, then the test is skipped.
|
||||
|
||||
For convenience, if the @var{specifier} is a string that is syntactic
|
||||
sugar for @code{(test-match-name @var{specifier})}. For example:
|
||||
|
||||
@lisp
|
||||
(test-skip "test-b")
|
||||
(test-assert "test-a") ;; executed
|
||||
(test-assert "test-b") ;; skipped
|
||||
@end lisp
|
||||
|
||||
Any skip specifiers introduced by a @code{test-skip} are removed by a
|
||||
following non-nested @code{test-end}.
|
||||
|
||||
@lisp
|
||||
(test-begin "group1")
|
||||
(test-skip "test-a")
|
||||
(test-assert "test-a") ;; skipped
|
||||
(test-end "group1") ;; Undoes the prior test-skip
|
||||
(test-assert "test-a") ;; executed
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
@subsubheading Expected failures
|
||||
|
||||
Sometimes you know a test case will fail, but you don't have time to or
|
||||
can't fix it. Maybe a certain feature only works on certain platforms.
|
||||
However, you want the test-case to be there to remind you to fix it.
|
||||
You want to note that such tests are expected to fail.
|
||||
|
||||
@deffn {Scheme Syntax} test-expect-fail specifier
|
||||
|
||||
Matching tests (where matching is defined as in @code{test-skip})
|
||||
are expected to fail. This only affects test reporting,
|
||||
not test execution. For example:
|
||||
|
||||
@lisp
|
||||
(test-expect-fail 2)
|
||||
(test-eqv ...) ;; expected to fail
|
||||
(test-eqv ...) ;; expected to fail
|
||||
(test-eqv ...) ;; expected to pass
|
||||
@end lisp
|
||||
@end deffn
|
||||
|
||||
@node SRFI-64 Test Runner
|
||||
@subsubsection SRFI-64 Test Runner
|
||||
|
||||
A @dfn{test-runner} is an object that runs a test-suite, and manages the
|
||||
state. The test group path, and the sets skip and expected-fail
|
||||
specifiers are part of the test-runner. A test-runner will also
|
||||
typically accumulate statistics about executed tests,
|
||||
|
||||
@deffn {Scheme Procedure} test-runner? value
|
||||
|
||||
True if and only if @var{value} is a test-runner object.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Parameter} test-runner-current
|
||||
@deffnx {Scheme Parameter} test-runner-current runner
|
||||
|
||||
Get or set the current test-runner.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-get
|
||||
|
||||
Same as @code{(test-runner-current)}, but throws an exception if there
|
||||
is no current test-runner.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-simple
|
||||
|
||||
Creates a new simple test-runner, that prints errors and a summary on
|
||||
the standard output port.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-null
|
||||
|
||||
Creates a new test-runner, that does nothing with the test results.
|
||||
This is mainly meant for extending when writing a custom runner.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-create
|
||||
|
||||
Create a new test-runner. Equivalent to @samp{((test-runner-factory))}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Parameter} test-runner-factory
|
||||
@deffnx {Scheme Parameter} test-runner-factory factory
|
||||
|
||||
Get or set the current test-runner factory. A factory is a
|
||||
zero-argument function that creates a new test-runner. The default
|
||||
value is @code{test-runner-simple}.
|
||||
@end deffn
|
||||
|
||||
@subsubheading Running specific tests with a specified runner
|
||||
|
||||
@deffn {Scheme Procedure} test-apply [runner] specifier @dots{} procedure
|
||||
|
||||
Calls @var{procedure} with no arguments using the specified @var{runner}
|
||||
as the current test-runner. If @var{runner} is omitted, then
|
||||
@code{(test-runner-current)} is used. (If there is no current runner,
|
||||
one is created as in @code{test-begin}.) If one or more
|
||||
@var{specifier}s are listed then only tests matching the
|
||||
@var{specifier}s are executed. A @var{specifier} has the same form as
|
||||
one used for @code{test-skip}. A test is executed if it matches any of
|
||||
the @var{specifier}s in the @code{test-apply} @emph{and} does not match
|
||||
any active @code{test-skip} specifiers.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} test-with-runner runner decl-or-expr @dots{}
|
||||
|
||||
Executes each @var{decl-or-expr} in order in a context where the current
|
||||
test-runner is @var{runner}.
|
||||
@end deffn
|
||||
|
||||
@node SRFI-64 Test Results
|
||||
@subsubsection SRFI-64 Test Results
|
||||
|
||||
Running a test sets various status properties in the current test-runner.
|
||||
This can be examined by a custom test-runner,
|
||||
or (more rarely) in a test-suite.
|
||||
|
||||
@subsubheading Result Kind
|
||||
|
||||
Running a test may yield one of the following
|
||||
status symbols:
|
||||
|
||||
@table @asis
|
||||
@item @code{'pass}
|
||||
The test passed, as expected.
|
||||
|
||||
@item @code{'fail}
|
||||
The test failed (and was not expected to).
|
||||
|
||||
@item @code{'xfail}
|
||||
The test failed and was expected to.
|
||||
|
||||
@item @code{'xpass}
|
||||
The test passed, but was expected to fail.
|
||||
|
||||
@item @code{'skip}
|
||||
The test was skipped.
|
||||
@end table
|
||||
|
||||
@deffn {Scheme Procedure} test-result-kind [runner]
|
||||
|
||||
Returns one of the above result codes from the most recent tests.
|
||||
Returns @code{#f} if no tests have been run yet. If we've started on a
|
||||
new test, but don't have a result yet, then the result kind is
|
||||
@code{'xfail} if the test is expected to fail, @code{'skip} if the test
|
||||
is supposed to be skipped, or @code{#f} otherwise.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-passed? [runner]
|
||||
|
||||
True if the value of @samp{(test-result-kind [@var{runner}])} is one of
|
||||
@code{'pass} or @code{'xpass}. This is a convenient shorthand that
|
||||
might be useful in a test suite to only run certain tests if the
|
||||
previous test passed.
|
||||
@end deffn
|
||||
|
||||
@subsubheading Test result properties
|
||||
|
||||
A test runner also maintains a set of more detailed
|
||||
``result@tie{}properties'' associated with the current or most recent
|
||||
test. (I.e. the properties of the most recent test are available as
|
||||
long as a new test hasn't started.) Each property has a name (a symbol)
|
||||
and a value (any value). Some properties are standard or set by the
|
||||
implementation; implementations can add more.
|
||||
|
||||
@deffn {Scheme Procedure} test-result-ref runner pname [default]
|
||||
|
||||
Returns the property value associated with the @var{pname} property name
|
||||
(a symbol). If there is no value associated with @var{pname} return
|
||||
@var{default}, or @code{#f} if @var{default} isn't specified.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Syntax} test-result-set! runner pname value
|
||||
|
||||
Sets the property value associated with the @var{pname} property name to
|
||||
@var{value}. Usually implementation code should call this function, but
|
||||
it may be useful for a custom test-runner to add extra properties.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-result-remove runner pname
|
||||
|
||||
Remove the property with the name @var{pname}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-result-clear runner
|
||||
|
||||
Remove all result properties. The implementation automatically calls
|
||||
@code{test-result-clear} at the start of a @code{test-assert} and
|
||||
similar procedures.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-result-alist runner
|
||||
|
||||
Returns an association list of the current result properties. It is
|
||||
unspecified if the result shares state with the test-runner. The result
|
||||
should not be modified; on the other hand, the result may be implicitly
|
||||
modified by future @code{test-result-set!} or @code{test-result-remove}
|
||||
calls. However, a @code{test-result-clear} does not modify the returned
|
||||
alist. Thus you can ``archive'' result objects from previous runs.
|
||||
@end deffn
|
||||
|
||||
@subsubheading Standard result properties
|
||||
|
||||
The set of available result properties is implementation-specific.
|
||||
However, it is suggested that the following might be provided:
|
||||
@table @asis
|
||||
|
||||
@item @code{'result-kind}
|
||||
The result kind, as defined previously.
|
||||
This is the only mandatory result property.
|
||||
@code{(test-result-kind @var{runner})} is equivalent to:
|
||||
@code{(test-result-ref @var{runner} 'result-kind)}
|
||||
|
||||
@item @code{'source-file}
|
||||
@itemx @code{'source-line}
|
||||
If known, the location of test statements (such as @code{test-assert})
|
||||
in test suite source code.
|
||||
|
||||
@item @code{'source-form}
|
||||
The source form, if meaningful and known.
|
||||
|
||||
@item @code{'expected-value}
|
||||
The expected non-error result, if meaningful and known.
|
||||
|
||||
@item @code{'expected-error}
|
||||
The @var{error-type}specified in a @code{test-error}, if it meaningful and known.
|
||||
|
||||
@item @code{'actual-value}
|
||||
The actual non-error result value, if meaningful and known.
|
||||
|
||||
@item @code{'actual-error}
|
||||
The error value, if an error was signalled and it is known.
|
||||
The actual error value is implementation-defined.
|
||||
@end table
|
||||
|
||||
@node SRFI-64 Writing a New Test Runner
|
||||
@subsubsection SRFI-64 Writing a New Test Runner
|
||||
|
||||
This section specifies how to write a test-runner. It can be ignored if
|
||||
you just want to write test-cases.
|
||||
|
||||
@subsubheading Call-back Functions
|
||||
|
||||
These call-back functions are ``methods'' (in the object-oriented sense)
|
||||
of a test-runner. A method @code{test-runner-on-@var{event}} is called
|
||||
by the implementation when @var{event} happens.
|
||||
|
||||
To define (set) the callback function for @var{event} use the following
|
||||
expression. (This is normally done when initializing a test-runner.)
|
||||
|
||||
@code{(test-runner-on-@var{event}! @var{runner} @var{event-function})}
|
||||
|
||||
An @var{event-function} takes a test-runner argument, and possibly other
|
||||
arguments, depending on the @var{event}.
|
||||
|
||||
To extract (get) the callback function for @var{event} do this:
|
||||
@code{(test-runner-on-@var{event} @var{runner})}
|
||||
|
||||
To extract call the callback function for @var{event} use the following
|
||||
expression. (This is normally done by the implementation core.)
|
||||
@samp{((test-runner-on-@var{event} @var{runner}) @var{runner}
|
||||
@var{other-args} @dots{})}.
|
||||
|
||||
The following call-back hooks are available.
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-on-test-begin runner
|
||||
@deffnx {Scheme Procedure} test-runner-on-test-begin! runner on-test-begin-function
|
||||
@deffnx {Scheme Procedure} on-test-begin-function runner
|
||||
|
||||
The @var{on-test-begin-function} is called at the start of an
|
||||
individual testcase, before the test expression (and expected value) are
|
||||
evaluated.
|
||||
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-on-test-end runner
|
||||
@deffnx {Scheme Procedure} test-runner-on-test-end! runner on-test-end-function
|
||||
@deffnx {Scheme Procedure} on-test-end-function runner
|
||||
|
||||
The @var{on-test-end-function} is called at the end of an
|
||||
individual testcase, when the result of the test is available.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-on-group-begin runner
|
||||
@deffnx {Scheme Procedure} test-runner-on-group-begin! runner on-group-begin-function
|
||||
@deffnx {Scheme Procedure} on-group-begin-function runner suite-name count
|
||||
|
||||
The @var{on-group-begin-function} is called by a @code{test-begin},
|
||||
including at the start of a @code{test-group}. The @var{suite-name} is
|
||||
a Scheme string, and @var{count} is an integer or @code{#f}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-on-group-end runner
|
||||
@deffnx {Scheme Procedure} test-runner-on-group-end! runner on-group-end-function
|
||||
@deffnx {Scheme Procedure} on-group-end-function runner
|
||||
|
||||
The @var{on-group-end-function} is called by a @code{test-end},
|
||||
including at the end of a @code{test-group}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-on-bad-count runner
|
||||
@deffnx {Scheme Procedure} test-runner-on-bad-count! runner on-bad-count-function
|
||||
@deffnx {Scheme Procedure} on-bad-count-function runner actual-count expected-count
|
||||
|
||||
Called from @code{test-end} (before the @var{on-group-end-function} is
|
||||
called) if an @var{expected-count} was specified by the matching
|
||||
@code{test-begin} and the @var{expected-count} does not match the
|
||||
@var{actual-count} of tests actually executed or skipped.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-on-bad-end-name runner
|
||||
@deffnx {Scheme Procedure} test-runner-on-bad-end-name! runner on-bad-end-name-function
|
||||
@deffnx {Scheme Procedure} on-bad-end-name-function runner begin-name end-name
|
||||
|
||||
Called from @code{test-end} (before the @var{on-group-end-function} is
|
||||
called) if a @var{suite-name} was specified, and it did not that the
|
||||
name in the matching @code{test-begin}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-on-final runner
|
||||
@deffnx {Scheme Procedure} test-runner-on-final! runner on-final-function
|
||||
@deffnx {Scheme Procedure} on-final-function runner
|
||||
|
||||
The @var{on-final-function} takes one parameter (a test-runner) and
|
||||
typically displays a summary (count) of the tests. The
|
||||
@var{on-final-function} is called after called the
|
||||
@var{on-group-end-function} correspondiong to the outermost
|
||||
@code{test-end}. The default value is @code{test-on-final-simple} which
|
||||
writes to the standard output port the number of tests of the various
|
||||
kinds.
|
||||
@end deffn
|
||||
|
||||
The default test-runner returned by @code{test-runner-simple} uses the
|
||||
following call-back functions:
|
||||
|
||||
@deffn {Scheme Procedure} test-on-test-begin-simple runner
|
||||
@deffnx {Scheme Procedure} test-on-test-end-simple runner
|
||||
@deffnx {Scheme Procedure} test-on-group-begin-simple runner suite-name count
|
||||
@deffnx {Scheme Procedure} test-on-group-end-simple runner
|
||||
@deffnx {Scheme Procedure} test-on-bad-count-simple runner actual-count expected-count
|
||||
@deffnx {Scheme Procedure} test-on-bad-end-name-simple runner begin-name end-name
|
||||
|
||||
You can call those if you want to write your own test-runner.
|
||||
@end deffn
|
||||
|
||||
@subsubheading Test-runner components
|
||||
|
||||
The following functions are for accessing the other components of a
|
||||
test-runner. They would normally only be used to write a new
|
||||
test-runner or a match-predicate.
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-pass-count runner
|
||||
|
||||
Returns the number of tests that passed, and were expected to pass.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-fail-count runner
|
||||
|
||||
Returns the number of tests that failed, but were expected to pass.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-xpass-count runner
|
||||
|
||||
Returns the number of tests that passed, but were expected to fail.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-xfail-count runner
|
||||
|
||||
Returns the number of tests that failed, and were expected to pass.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-skip-count runner
|
||||
|
||||
Returns the number of tests or test groups that were skipped.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-test-name runner
|
||||
|
||||
Returns the name of the current test or test group, as a string. During
|
||||
execution of @code{test-begin} this is the name of the test group;
|
||||
during the execution of an actual test, this is the name of the
|
||||
test-case. If no name was specified, the name is the empty string.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-group-path runner
|
||||
|
||||
A list of names of groups we're nested in, with the outermost group
|
||||
first.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-group-stack runner
|
||||
|
||||
A list of names of groups we're nested in, with the outermost group
|
||||
last. (This is more efficient than @code{test-runner-group-path}, since
|
||||
it doesn't require any copying.)
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-aux-value runner
|
||||
@deffnx {Scheme Procedure} test-runner-aux-value! runner on-test
|
||||
|
||||
Get or set the @code{aux-value} field of a test-runner. This field is
|
||||
not used by this API or the @code{test-runner-simple} test-runner, but
|
||||
may be used by custom test-runners to store extra state.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} test-runner-reset runner
|
||||
|
||||
Resets the state of the @var{runner} to its initial state.
|
||||
@end deffn
|
||||
|
||||
@subsubheading Example
|
||||
|
||||
This is an example of a simple custom test-runner.
|
||||
Loading this program before running a test-suite will install
|
||||
it as the default test runner.
|
||||
|
||||
@lisp
|
||||
(define (my-simple-runner filename)
|
||||
(let ((runner (test-runner-null))
|
||||
(port (open-output-file filename))
|
||||
(num-passed 0)
|
||||
(num-failed 0))
|
||||
(test-runner-on-test-end! runner
|
||||
(lambda (runner)
|
||||
(case (test-result-kind runner)
|
||||
((pass xpass) (set! num-passed (+ num-passed 1)))
|
||||
((fail xfail) (set! num-failed (+ num-failed 1)))
|
||||
(else #t))))
|
||||
(test-runner-on-final! runner
|
||||
(lambda (runner)
|
||||
(format port "Passing tests: ~d.~%Failing tests: ~d.~%"
|
||||
num-passed num-failed)
|
||||
(close-output-port port)))
|
||||
runner))
|
||||
(test-runner-factory
|
||||
(lambda () (my-simple-runner "/tmp/my-test.log")))
|
||||
@end lisp
|
||||
|
||||
@node SRFI-67
|
||||
@subsection SRFI-67 - Compare procedures
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue