Implementation for the R6RS (rnrs sorting) library.
[bpt/guile.git] / test-suite / lib.scm
CommitLineData
000ee07f 1;;;; test-suite/lib.scm --- generic support for testing
7b041912 2;;;; Copyright (C) 1999, 2000, 2001, 2004, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
bba2d190 3;;;;
53befeb7
NJ
4;;;; This program is free software; you can redistribute it and/or
5;;;; modify it under the terms of the GNU Lesser General Public
6;;;; License as published by the Free Software Foundation; either
7;;;; version 3, or (at your option) any later version.
bba2d190 8;;;;
000ee07f
JB
9;;;; This program is distributed in the hope that it will be useful,
10;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
53befeb7 12;;;; GNU Lesser General Public License for more details.
bba2d190 13;;;;
53befeb7
NJ
14;;;; You should have received a copy of the GNU Lesser General Public
15;;;; License along with this software; see the file COPYING.LESSER.
16;;;; If not, write to the Free Software Foundation, Inc., 51 Franklin
17;;;; Street, Fifth Floor, Boston, MA 02110-1301 USA
000ee07f 18
6b4113af 19(define-module (test-suite lib)
8bc4547c 20 :use-module (ice-9 stack-catch)
1a179b03 21 :use-module (ice-9 regex)
c45de346 22 :autoload (srfi srfi-1) (append-map)
1a179b03 23 :export (
000ee07f 24
6b4113af 25 ;; Exceptions which are commonly being tested for.
30a700c8 26 exception:syntax-pattern-unmatched
d6754c23 27 exception:bad-variable
d6e04e7c 28 exception:missing-expression
08c608e1 29 exception:out-of-range exception:unbound-var
0ac46745 30 exception:used-before-defined
08c608e1 31 exception:wrong-num-args exception:wrong-type-arg
0825ae0b 32 exception:numerical-overflow
d15ad007 33 exception:struct-set!-denied
a1ef7406 34 exception:system-error
7b041912 35 exception:encoding-error
d15ad007 36 exception:miscellaneous-error
970aac16 37 exception:string-contains-nul
0ba0b384 38 exception:read-error
01ad5a7b 39 exception:null-pointer-error
6b4113af 40
000ee07f 41 ;; Reporting passes and failures.
6b4113af
DH
42 run-test
43 pass-if expect-fail
44 pass-if-exception expect-fail-exception
000ee07f
JB
45
46 ;; Naming groups of tests in a regular fashion.
47 with-test-prefix with-test-prefix* current-test-prefix
d6e04e7c 48 format-test-name
000ee07f 49
2d04022c
NJ
50 ;; Using the debugging evaluator.
51 with-debugging-evaluator with-debugging-evaluator*
52
c45de346
LC
53 ;; Using a given locale
54 with-locale with-locale* with-latin1-locale with-latin1-locale*
ce3ed012 55
000ee07f
JB
56 ;; Reporting results in various ways.
57 register-reporter unregister-reporter reporter-registered?
58 make-count-reporter print-counts
bba2d190 59 make-log-reporter
087dab1c 60 full-reporter
d6e04e7c 61 user-reporter))
000ee07f
JB
62
63
64;;;; If you're using Emacs's Scheme mode:
000ee07f
JB
65;;;; (put 'with-test-prefix 'scheme-indent-function 1)
66
67\f
57e7f270
DH
68;;;; CORE FUNCTIONS
69;;;;
70;;;; The function (run-test name expected-result thunk) is the heart of the
71;;;; testing environment. The first parameter NAME is a unique name for the
72;;;; test to be executed (for an explanation of this parameter see below under
73;;;; TEST NAMES). The second parameter EXPECTED-RESULT is a boolean value
74;;;; that indicates whether the corresponding test is expected to pass. If
75;;;; EXPECTED-RESULT is #t the test is expected to pass, if EXPECTED-RESULT is
76;;;; #f the test is expected to fail. Finally, THUNK is the function that
77;;;; actually performs the test. For example:
78;;;;
79;;;; (run-test "integer addition" #t (lambda () (= 2 (+ 1 1))))
80;;;;
81;;;; To report success, THUNK should either return #t or throw 'pass. To
82;;;; report failure, THUNK should either return #f or throw 'fail. If THUNK
83;;;; returns a non boolean value or throws 'unresolved, this indicates that
84;;;; the test did not perform as expected. For example the property that was
85;;;; to be tested could not be tested because something else went wrong.
86;;;; THUNK may also throw 'untested to indicate that the test was deliberately
87;;;; not performed, for example because the test case is not complete yet.
88;;;; Finally, if THUNK throws 'unsupported, this indicates that this test
89;;;; requires some feature that is not available in the configured testing
90;;;; environment. All other exceptions thrown by THUNK are considered as
91;;;; errors.
92;;;;
6b4113af
DH
93;;;;
94;;;; Convenience macros for tests expected to pass or fail
95;;;;
bba2d190 96;;;; * (pass-if name body) is a short form for
57e7f270 97;;;; (run-test name #t (lambda () body))
bba2d190 98;;;; * (expect-fail name body) is a short form for
57e7f270
DH
99;;;; (run-test name #f (lambda () body))
100;;;;
bba2d190 101;;;; For example:
57e7f270
DH
102;;;;
103;;;; (pass-if "integer addition" (= 2 (+ 1 1)))
6b4113af
DH
104;;;;
105;;;;
106;;;; Convenience macros to test for exceptions
107;;;;
108;;;; The following macros take exception parameters which are pairs
109;;;; (type . message), where type is a symbol that denotes an exception type
110;;;; like 'wrong-type-arg or 'out-of-range, and message is a string holding a
111;;;; regular expression that describes the error message for the exception
112;;;; like "Argument .* out of range".
113;;;;
114;;;; * (pass-if-exception name exception body) will pass if the execution of
115;;;; body causes the given exception to be thrown. If no exception is
116;;;; thrown, the test fails. If some other exception is thrown, is is an
117;;;; error.
118;;;; * (expect-fail-exception name exception body) will pass unexpectedly if
119;;;; the execution of body causes the given exception to be thrown. If no
120;;;; exception is thrown, the test fails expectedly. If some other
121;;;; exception is thrown, it is an error.
57e7f270
DH
122
123\f
000ee07f
JB
124;;;; TEST NAMES
125;;;;
126;;;; Every test in the test suite has a unique name, to help
127;;;; developers find tests that are failing (or unexpectedly passing),
128;;;; and to help gather statistics.
129;;;;
130;;;; A test name is a list of printable objects. For example:
131;;;; ("ports.scm" "file" "read and write back list of strings")
132;;;; ("ports.scm" "pipe" "read")
133;;;;
134;;;; Test names may contain arbitrary objects, but they always have
135;;;; the following properties:
136;;;; - Test names can be compared with EQUAL?.
137;;;; - Test names can be reliably stored and retrieved with the standard WRITE
138;;;; and READ procedures; doing so preserves their identity.
bba2d190 139;;;;
000ee07f 140;;;; For example:
bba2d190 141;;;;
57e7f270 142;;;; (pass-if "simple addition" (= 4 (+ 2 2)))
bba2d190 143;;;;
000ee07f
JB
144;;;; In that case, the test name is the list ("simple addition").
145;;;;
e46083d5
DH
146;;;; In the case of simple tests the expression that is tested would often
147;;;; suffice as a test name by itself. Therefore, the convenience macros
148;;;; pass-if and expect-fail provide a shorthand notation that allows to omit
149;;;; a test name in such cases.
150;;;;
151;;;; * (pass-if expression) is a short form for
152;;;; (run-test 'expression #t (lambda () expression))
153;;;; * (expect-fail expression) is a short form for
154;;;; (run-test 'expression #f (lambda () expression))
155;;;;
156;;;; For example:
157;;;;
158;;;; (pass-if (= 2 (+ 1 1)))
159;;;;
000ee07f
JB
160;;;; The WITH-TEST-PREFIX syntax and WITH-TEST-PREFIX* procedure establish
161;;;; a prefix for the names of all tests whose results are reported
162;;;; within their dynamic scope. For example:
bba2d190 163;;;;
000ee07f
JB
164;;;; (begin
165;;;; (with-test-prefix "basic arithmetic"
166;;;; (pass-if "addition" (= (+ 2 2) 4))
05c4ba00 167;;;; (pass-if "subtraction" (= (- 4 2) 2)))
000ee07f 168;;;; (pass-if "multiplication" (= (* 2 2) 4)))
bba2d190 169;;;;
000ee07f
JB
170;;;; In that example, the three test names are:
171;;;; ("basic arithmetic" "addition"),
05c4ba00 172;;;; ("basic arithmetic" "subtraction"), and
000ee07f
JB
173;;;; ("multiplication").
174;;;;
175;;;; WITH-TEST-PREFIX can be nested. Each WITH-TEST-PREFIX postpends
176;;;; a new element to the current prefix:
bba2d190 177;;;;
000ee07f
JB
178;;;; (with-test-prefix "arithmetic"
179;;;; (with-test-prefix "addition"
180;;;; (pass-if "integer" (= (+ 2 2) 4))
181;;;; (pass-if "complex" (= (+ 2+3i 4+5i) 6+8i)))
182;;;; (with-test-prefix "subtraction"
183;;;; (pass-if "integer" (= (- 2 2) 0))
184;;;; (pass-if "complex" (= (- 2+3i 1+2i) 1+1i))))
bba2d190 185;;;;
000ee07f
JB
186;;;; The four test names here are:
187;;;; ("arithmetic" "addition" "integer")
188;;;; ("arithmetic" "addition" "complex")
189;;;; ("arithmetic" "subtraction" "integer")
190;;;; ("arithmetic" "subtraction" "complex")
191;;;;
192;;;; To print a name for a human reader, we DISPLAY its elements,
193;;;; separated by ": ". So, the last set of test names would be
194;;;; reported as:
bba2d190 195;;;;
000ee07f
JB
196;;;; arithmetic: addition: integer
197;;;; arithmetic: addition: complex
198;;;; arithmetic: subtraction: integer
199;;;; arithmetic: subtraction: complex
200;;;;
201;;;; The Guile benchmarks use with-test-prefix to include the name of
202;;;; the source file containing the test in the test name, to help
203;;;; developers to find failing tests, and to provide each file with its
204;;;; own namespace.
205
206\f
207;;;; REPORTERS
bba2d190 208;;;;
000ee07f
JB
209;;;; A reporter is a function which we apply to each test outcome.
210;;;; Reporters can log results, print interesting results to the
211;;;; standard output, collect statistics, etc.
bba2d190 212;;;;
57e7f270
DH
213;;;; A reporter function takes two mandatory arguments, RESULT and TEST, and
214;;;; possibly additional arguments depending on RESULT; its return value
000ee07f
JB
215;;;; is ignored. RESULT has one of the following forms:
216;;;;
bba2d190 217;;;; pass - The test named TEST passed.
57e7f270
DH
218;;;; Additional arguments are ignored.
219;;;; upass - The test named TEST passed unexpectedly.
220;;;; Additional arguments are ignored.
221;;;; fail - The test named TEST failed.
222;;;; Additional arguments are ignored.
223;;;; xfail - The test named TEST failed, as expected.
224;;;; Additional arguments are ignored.
225;;;; unresolved - The test named TEST did not perform as expected, for
226;;;; example the property that was to be tested could not be
227;;;; tested because something else went wrong.
228;;;; Additional arguments are ignored.
229;;;; untested - The test named TEST was not actually performed, for
bba2d190 230;;;; example because the test case is not complete yet.
57e7f270
DH
231;;;; Additional arguments are ignored.
232;;;; unsupported - The test named TEST requires some feature that is not
233;;;; available in the configured testing environment.
234;;;; Additional arguments are ignored.
235;;;; error - An error occurred while the test named TEST was
236;;;; performed. Since this result means that the system caught
237;;;; an exception it could not handle, the exception arguments
238;;;; are passed as additional arguments.
000ee07f
JB
239;;;;
240;;;; This library provides some standard reporters for logging results
241;;;; to a file, reporting interesting results to the user, and
242;;;; collecting totals.
087dab1c
JB
243;;;;
244;;;; You can use the REGISTER-REPORTER function and friends to add
245;;;; whatever reporting functions you like. If you don't register any
246;;;; reporters, the library uses FULL-REPORTER, which simply writes
247;;;; all results to the standard output.
000ee07f
JB
248
249\f
57e7f270
DH
250;;;; MISCELLANEOUS
251;;;;
252
6b4113af 253;;; Define some exceptions which are commonly being tested for.
30a700c8
LC
254(define exception:syntax-pattern-unmatched
255 (cons 'syntax-error "source expression failed to match any pattern"))
d6754c23
DH
256(define exception:bad-variable
257 (cons 'syntax-error "Bad variable"))
d6e04e7c
DH
258(define exception:missing-expression
259 (cons 'misc-error "^missing or extra expression"))
6b4113af 260(define exception:out-of-range
a47d845f 261 (cons 'out-of-range "^.*out of range"))
08c608e1
DH
262(define exception:unbound-var
263 (cons 'unbound-variable "^Unbound variable"))
0ac46745
MV
264(define exception:used-before-defined
265 (cons 'unbound-variable "^Variable used before given a value"))
08c608e1
DH
266(define exception:wrong-num-args
267 (cons 'wrong-number-of-args "^Wrong number of arguments"))
6b4113af 268(define exception:wrong-type-arg
a47d845f 269 (cons 'wrong-type-arg "^Wrong type"))
0825ae0b
KR
270(define exception:numerical-overflow
271 (cons 'numerical-overflow "^Numerical overflow"))
d15ad007
LC
272(define exception:struct-set!-denied
273 (cons 'misc-error "^set! denied for field"))
a1ef7406
LC
274(define exception:system-error
275 (cons 'system-error ".*"))
7b041912 276(define exception:encoding-error
a3d7d5d5 277 (cons 'encoding-error "(cannot convert to output locale|input locale conversion error)"))
d15ad007
LC
278(define exception:miscellaneous-error
279 (cons 'misc-error "^.*"))
0ba0b384
LC
280(define exception:read-error
281 (cons 'read-error "^.*$"))
01ad5a7b
LC
282(define exception:null-pointer-error
283 (cons 'null-pointer-error "^.*$"))
6b4113af 284
970aac16
KR
285;; as per throw in scm_to_locale_stringn()
286(define exception:string-contains-nul
287 (cons 'misc-error "^string contains #\\\\nul character"))
288
289
57e7f270
DH
290;;; Display all parameters to the default output port, followed by a newline.
291(define (display-line . objs)
292 (for-each display objs)
293 (newline))
294
295;;; Display all parameters to the given output port, followed by a newline.
296(define (display-line-port port . objs)
297 (for-each (lambda (obj) (display obj port)) objs)
298 (newline port))
299
300\f
301;;;; CORE FUNCTIONS
302;;;;
303
304;;; The central testing routine.
305;;; The idea is taken from Greg, the GNUstep regression test environment.
306(define run-test #f)
307(let ((test-running #f))
308 (define (local-run-test name expect-pass thunk)
309 (if test-running
310 (error "Nested calls to run-test are not permitted.")
311 (let ((test-name (full-name name)))
312 (set! test-running #t)
313 (catch #t
314 (lambda ()
315 (let ((result (thunk)))
316 (if (eq? result #t) (throw 'pass))
317 (if (eq? result #f) (throw 'fail))
318 (throw 'unresolved)))
319 (lambda (key . args)
320 (case key
bba2d190 321 ((pass)
57e7f270 322 (report (if expect-pass 'pass 'upass) test-name))
bba2d190 323 ((fail)
57e7f270 324 (report (if expect-pass 'fail 'xfail) test-name))
bba2d190 325 ((unresolved untested unsupported)
57e7f270 326 (report key test-name))
bba2d190 327 ((quit)
57e7f270
DH
328 (report 'unresolved test-name)
329 (quit))
bba2d190 330 (else
57e7f270
DH
331 (report 'error test-name (cons key args))))))
332 (set! test-running #f))))
333 (set! run-test local-run-test))
334
335;;; A short form for tests that are expected to pass, taken from Greg.
ce09ee19
AW
336(define-syntax pass-if
337 (syntax-rules ()
338 ((_ name)
339 ;; presume this is a simple test, i.e. (pass-if (even? 2))
340 ;; where the body should also be the name.
341 (run-test 'name #t (lambda () name)))
342 ((_ name rest ...)
343 (run-test name #t (lambda () rest ...)))))
57e7f270
DH
344
345;;; A short form for tests that are expected to fail, taken from Greg.
ce09ee19
AW
346(define-syntax expect-fail
347 (syntax-rules ()
348 ((_ name)
349 ;; presume this is a simple test, i.e. (expect-fail (even? 2))
350 ;; where the body should also be the name.
351 (run-test 'name #f (lambda () name)))
352 ((_ name rest ...)
353 (run-test name #f (lambda () rest ...)))))
57e7f270 354
6b4113af
DH
355;;; A helper function to implement the macros that test for exceptions.
356(define (run-test-exception name exception expect-pass thunk)
357 (run-test name expect-pass
358 (lambda ()
8bc4547c 359 (stack-catch (car exception)
6b4113af 360 (lambda () (thunk) #f)
bba2d190
TTN
361 (lambda (key proc message . rest)
362 (cond
363 ;; handle explicit key
364 ((string-match (cdr exception) message)
365 #t)
366 ;; handle `(error ...)' which uses `misc-error' for key and doesn't
367 ;; yet format the message and args (we have to do it here).
368 ((and (eq? 'misc-error (car exception))
369 (list? rest)
370 (string-match (cdr exception)
371 (apply simple-format #f message (car rest))))
372 #t)
e6729603
DH
373 ;; handle syntax errors which use `syntax-error' for key and don't
374 ;; yet format the message and args (we have to do it here).
375 ((and (eq? 'syntax-error (car exception))
376 (list? rest)
377 (string-match (cdr exception)
378 (apply simple-format #f message (car rest))))
379 #t)
bba2d190
TTN
380 ;; unhandled; throw again
381 (else
382 (apply throw key proc message rest))))))))
6b4113af
DH
383
384;;; A short form for tests that expect a certain exception to be thrown.
ce09ee19
AW
385(define-syntax pass-if-exception
386 (syntax-rules ()
387 ((_ name exception body rest ...)
388 (run-test-exception name exception #t (lambda () body rest ...)))))
6b4113af
DH
389
390;;; A short form for tests expected to fail to throw a certain exception.
ce09ee19
AW
391(define-syntax expect-fail-exception
392 (syntax-rules ()
393 ((_ name exception body rest ...)
394 (run-test-exception name exception #f (lambda () body rest ...)))))
6b4113af 395
57e7f270
DH
396\f
397;;;; TEST NAMES
398;;;;
399
400;;;; Turn a test name into a nice human-readable string.
401(define (format-test-name name)
7b041912
LC
402 ;; Choose a Unicode-capable encoding so that the string port can contain any
403 ;; valid Unicode character.
404 (with-fluids ((%default-port-encoding "UTF-8"))
405 (call-with-output-string
406 (lambda (port)
407 (let loop ((name name)
408 (separator ""))
409 (if (pair? name)
410 (begin
411 (display separator port)
412 (display (car name) port)
413 (loop (cdr name) ": "))))))))
57e7f270
DH
414
415;;;; For a given test-name, deliver the full name including all prefixes.
416(define (full-name name)
417 (append (current-test-prefix) (list name)))
000ee07f
JB
418
419;;; A fluid containing the current test prefix, as a list.
420(define prefix-fluid (make-fluid))
421(fluid-set! prefix-fluid '())
57e7f270
DH
422(define (current-test-prefix)
423 (fluid-ref prefix-fluid))
000ee07f
JB
424
425;;; Postpend PREFIX to the current name prefix while evaluting THUNK.
426;;; The name prefix is only changed within the dynamic scope of the
427;;; call to with-test-prefix*. Return the value returned by THUNK.
428(define (with-test-prefix* prefix thunk)
429 (with-fluids ((prefix-fluid
430 (append (fluid-ref prefix-fluid) (list prefix))))
431 (thunk)))
432
433;;; (with-test-prefix PREFIX BODY ...)
434;;; Postpend PREFIX to the current name prefix while evaluating BODY ...
435;;; The name prefix is only changed within the dynamic scope of the
436;;; with-test-prefix expression. Return the value returned by the last
437;;; BODY expression.
438(defmacro with-test-prefix (prefix . body)
439 `(with-test-prefix* ,prefix (lambda () ,@body)))
440
2d04022c
NJ
441;;; Call THUNK using the debugging evaluator.
442(define (with-debugging-evaluator* thunk)
443 (let ((dopts #f))
444 (dynamic-wind
445 (lambda ()
446 (set! dopts (debug-options))
447 (debug-enable 'debug))
448 thunk
449 (lambda ()
450 (debug-options dopts)))))
451
452;;; Evaluate BODY... using the debugging evaluator.
453(define-macro (with-debugging-evaluator . body)
454 `(with-debugging-evaluator* (lambda () ,@body)))
455
ce3ed012
MG
456;;; Call THUNK with a given locale
457(define (with-locale* nloc thunk)
458 (let ((loc #f))
459 (dynamic-wind
460 (lambda ()
461 (if (defined? 'setlocale)
462 (begin
f5147c84
LC
463 (set! loc (false-if-exception (setlocale LC_ALL)))
464 (if (or (not loc)
465 (not (false-if-exception (setlocale LC_ALL nloc))))
ce3ed012
MG
466 (throw 'unresolved)))
467 (throw 'unresolved)))
468 thunk
469 (lambda ()
f5147c84 470 (if (and (defined? 'setlocale) loc)
ce3ed012
MG
471 (setlocale LC_ALL loc))))))
472
473;;; Evaluate BODY... using the given locale.
f5147c84
LC
474(define-syntax with-locale
475 (syntax-rules ()
476 ((_ loc body ...)
477 (with-locale* loc (lambda () body ...)))))
2d04022c 478
c45de346
LC
479;;; Try out several ISO-8859-1 locales and run THUNK under the one that works
480;;; (if any).
481(define (with-latin1-locale* thunk)
482 (define %locales
483 (append-map (lambda (name)
484 (list (string-append name ".ISO-8859-1")
485 (string-append name ".iso88591")
486 (string-append name ".ISO8859-1")))
487 '("ca_ES" "da_DK" "de_DE" "es_ES" "es_MX" "en_GB" "en_US"
488 "fr_FR" "pt_PT" "nl_NL" "sv_SE")))
489
490 (let loop ((locales %locales))
491 (if (null? locales)
492 (throw 'unresolved)
493 (catch 'unresolved
494 (lambda ()
495 (with-locale* (car locales) thunk))
496 (lambda (key . args)
497 (loop (cdr locales)))))))
498
499;;; Evaluate BODY... using an ISO-8859-1 locale or throw `unresolved' if none
500;;; was found.
501(define-syntax with-latin1-locale
502 (syntax-rules ()
503 ((_ body ...)
504 (with-latin1-locale* (lambda () body ...)))))
505
000ee07f 506\f
57e7f270 507;;;; REPORTERS
bba2d190 508;;;;
000ee07f
JB
509
510;;; The global list of reporters.
511(define reporters '())
512
087dab1c
JB
513;;; The default reporter, to be used only if no others exist.
514(define default-reporter #f)
515
000ee07f
JB
516;;; Add the procedure REPORTER to the current set of reporter functions.
517;;; Signal an error if that reporter procedure object is already registered.
518(define (register-reporter reporter)
519 (if (memq reporter reporters)
520 (error "register-reporter: reporter already registered: " reporter))
521 (set! reporters (cons reporter reporters)))
522
523;;; Remove the procedure REPORTER from the current set of reporter
524;;; functions. Signal an error if REPORTER is not currently registered.
525(define (unregister-reporter reporter)
526 (if (memq reporter reporters)
527 (set! reporters (delq! reporter reporters))
528 (error "unregister-reporter: reporter not registered: " reporter)))
529
530;;; Return true iff REPORTER is in the current set of reporter functions.
531(define (reporter-registered? reporter)
532 (if (memq reporter reporters) #t #f))
533
000ee07f 534;;; Send RESULT to all currently registered reporter functions.
57e7f270 535(define (report . args)
087dab1c 536 (if (pair? reporters)
57e7f270 537 (for-each (lambda (reporter) (apply reporter args))
087dab1c 538 reporters)
57e7f270 539 (apply default-reporter args)))
000ee07f
JB
540
541\f
57e7f270
DH
542;;;; Some useful standard reporters:
543;;;; Count reporters count the occurrence of each test result type.
544;;;; Log reporters write all test results to a given log file.
545;;;; Full reporters write all test results to the standard output.
546;;;; User reporters write interesting test results to the standard output.
547
548;;; The complete list of possible test results.
bba2d190 549(define result-tags
57e7f270
DH
550 '((pass "PASS" "passes: ")
551 (fail "FAIL" "failures: ")
552 (upass "UPASS" "unexpected passes: ")
553 (xfail "XFAIL" "expected failures: ")
554 (unresolved "UNRESOLVED" "unresolved test cases: ")
555 (untested "UNTESTED" "untested test cases: ")
556 (unsupported "UNSUPPORTED" "unsupported test cases: ")
557 (error "ERROR" "errors: ")))
558
559;;; The list of important test results.
bba2d190 560(define important-result-tags
57e7f270
DH
561 '(fail upass unresolved error))
562
563;;; Display a single test result in formatted form to the given port
564(define (print-result port result name . args)
565 (let* ((tag (assq result result-tags))
566 (label (if tag (cadr tag) #f)))
567 (if label
568 (begin
569 (display label port)
570 (display ": " port)
571 (display (format-test-name name) port)
572 (if (pair? args)
573 (begin
574 (display " - arguments: " port)
575 (write args port)))
576 (newline port))
577 (error "(test-suite lib) FULL-REPORTER: unrecognized result: "
578 result))))
000ee07f
JB
579
580;;; Return a list of the form (COUNTER RESULTS), where:
581;;; - COUNTER is a reporter procedure, and
582;;; - RESULTS is a procedure taking no arguments which returns the
583;;; results seen so far by COUNTER. The return value is an alist
584;;; mapping outcome symbols (`pass', `fail', etc.) onto counts.
585(define (make-count-reporter)
57e7f270 586 (let ((counts (map (lambda (tag) (cons (car tag) 0)) result-tags)))
000ee07f 587 (list
57e7f270
DH
588 (lambda (result name . args)
589 (let ((pair (assq result counts)))
bba2d190 590 (if pair
57e7f270 591 (set-cdr! pair (+ 1 (cdr pair)))
bba2d190 592 (error "count-reporter: unexpected test result: "
57e7f270 593 (cons result (cons name args))))))
000ee07f
JB
594 (lambda ()
595 (append counts '())))))
596
597;;; Print a count reporter's results nicely. Pass this function the value
598;;; returned by a count reporter's RESULTS procedure.
57e7f270 599(define (print-counts results . port?)
bba2d190 600 (let ((port (if (pair? port?)
57e7f270
DH
601 (car port?)
602 (current-output-port))))
603 (newline port)
604 (display-line-port port "Totals for this test run:")
605 (for-each
606 (lambda (tag)
607 (let ((result (assq (car tag) results)))
608 (if result
609 (display-line-port port (caddr tag) (cdr result))
610 (display-line-port port
611 "Test suite bug: "
612 "no total available for `" (car tag) "'"))))
613 result-tags)
614 (newline port)))
000ee07f
JB
615
616;;; Return a reporter procedure which prints all results to the file
617;;; FILE, in human-readable form. FILE may be a filename, or a port.
618(define (make-log-reporter file)
619 (let ((port (if (output-port? file) file
620 (open-output-file file))))
57e7f270
DH
621 (lambda args
622 (apply print-result port args)
000ee07f
JB
623 (force-output port))))
624
087dab1c 625;;; A reporter that reports all results to the user.
57e7f270
DH
626(define (full-reporter . args)
627 (apply print-result (current-output-port) args))
087dab1c
JB
628
629;;; A reporter procedure which shows interesting results (failures,
57e7f270
DH
630;;; unexpected passes etc.) to the user.
631(define (user-reporter result name . args)
632 (if (memq result important-result-tags)
633 (apply full-reporter result name args)))
087dab1c
JB
634
635(set! default-reporter full-reporter)