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