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