2 ;; A test harness for Allegro CL.
4 ;; copyright (c) 1985-1986 Franz Inc, Alameda, CA
5 ;; copyright (c) 1986-2001 Franz Inc, Berkeley, CA - All rights reserved.
7 ;; This code is free software; you can redistribute it and/or
8 ;; modify it under the terms of the version 2.1 of
9 ;; the GNU Lesser General Public License as published by
10 ;; the Free Software Foundation, as clarified by the Franz
11 ;; preamble to the LGPL found in
12 ;; http://opensource.franz.com/preamble.html.
14 ;; This code is distributed in the hope that it will be useful,
15 ;; but without any warranty; without even the implied warranty of
16 ;; merchantability or fitness for a particular purpose. See the GNU
17 ;; Lesser General Public License for more details.
19 ;; Version 2.1 of the GNU Lesser General Public License can be
20 ;; found at http://opensource.franz.com/license.html.
21 ;; If it is not present, you can access it from
22 ;; http://www.gnu.org/copyleft/lesser.txt (until superseded by a newer
23 ;; version) or write to the Free Software Foundation, Inc., 59 Temple
24 ;; Place, Suite 330, Boston, MA 02111-1307 USA
26 ;;;; from the original ACL 6.1 sources:
27 ;; $Id: acl-compat-tester.cl,v 1.1 2002/04/02 21:29:45 kevin Exp $
30 (defpackage :util.test
34 ;;;; Control variables:
35 #:*break-on-test-failures*
36 #:*error-protect-tests*
39 #:*test-unexpected-failures*
51 (in-package :util.test)
53 (define-condition simple-break (break simple-condition) ())
55 ;; the if* macro used in Allegro:
57 ;; This is in the public domain... please feel free to put this definition
58 ;; in your code or distribute it with your version of lisp.
60 (eval-when (:compile-toplevel :load-toplevel :execute)
61 (defvar if*-keyword-list '("then" "thenret" "else" "elseif")))
63 (defmacro if* (&rest args)
64 (do ((xx (reverse args) (cdr xx))
71 (cond ((eq state :compl)
73 (t (error "if*: illegal form ~s" args))))
74 (cond ((and (symbolp (car xx))
75 (member (symbol-name (car xx))
77 :test #'string-equal))
78 (setq lookat (symbol-name (car xx)))))
80 (cond ((eq state :init)
81 (cond (lookat (cond ((string-equal lookat "thenret")
85 "if*: bad keyword ~a" lookat))))
88 (push (car xx) col))))
91 (cond ((string-equal lookat "else")
94 "if*: multiples elses")))
97 (push `(t ,@col) totalcol))
98 ((string-equal lookat "then")
100 (t (error "if*: bad keyword ~s"
102 (t (push (car xx) col))))
106 "if*: keyword ~s at the wrong place " (car xx)))
107 (t (setq state :compl)
108 (push `(,(car xx) ,@col) totalcol))))
110 (cond ((not (string-equal lookat "elseif"))
111 (error "if*: missing elseif clause ")))
112 (setq state :init)))))
117 (defvar *break-on-test-failures* nil
118 "When a test failure occurs, common-lisp:break is called, allowing
119 interactive debugging of the failure.")
121 (defvar *test-errors* 0
122 "The value is the number of test errors which have occurred.")
123 (defvar *test-successes* 0
124 "The value is the number of test successes which have occurred.")
125 (defvar *test-unexpected-failures* 0
126 "The value is the number of unexpected test failures which have occurred.")
128 (defvar *error-protect-tests* nil
129 "Protect each test from errors. If an error occurs, then that will be
130 taken as a test failure unless test-error is being used.")
132 (defmacro test-values-errorset (form &optional announce catch-breaks)
134 (let ((g-announce (gensym))
135 (g-catch-breaks (gensym)))
136 `(let* ((,g-announce ,announce)
137 (,g-catch-breaks ,catch-breaks))
138 (handler-case (cons t (multiple-value-list ,form))
139 (condition (condition)
140 (if* (and (null ,g-catch-breaks)
141 (typep condition 'simple-break))
142 then (break condition)
144 then (format *error-output* "~&Condition type: ~a~%"
145 (class-of condition))
146 (format *error-output* "~&Message: ~a~%" condition))
149 (defmacro test-values (form &optional announce catch-breaks)
151 (if* *error-protect-tests*
152 then `(test-values-errorset ,form ,announce ,catch-breaks)
153 else `(cons t (multiple-value-list ,form))))
155 (defmacro test (expected-value test-form
156 &key (test #'eql test-given)
157 (multiple-values nil multiple-values-given)
158 (fail-info nil fail-info-given)
159 (known-failure nil known-failure-given)
161 ;;;;;;;;;; internal, undocumented keywords:
162 ;;;; Note about these keywords: if they were documented, we'd have a
163 ;;;; problem, since they break the left-to-right order of evaluation.
164 ;;;; Specifically, errorset breaks it, and I don't see any way around
165 ;;;; that. `errorset' is used by the old test.cl module (eg,
166 ;;;; test-equal-errorset).
169 (wanted-message nil wanted-message-given)
170 (got-message nil got-message-given))
171 "Perform a single test. `expected-value' is the reference value for the
172 test. `test-form' is a form that will produce the value to be compared to
173 the expected-value. If the values are not the same, then an error is
174 logged, otherwise a success is logged.
176 Normally the comparison of values is done with `eql'. The `test' keyword
177 argument can be used to specify other comparison functions, such as eq,
178 equal,equalp, string=, string-equal, etc.
180 Normally, only the first return value from the test-form is considered,
181 however if `multiple-values' is t, then all values returned from test-form
184 `fail-info' allows more information to be printed with a test failure.
186 `known-failure' marks the test as a known failure. This allows for
187 programs that do regression analysis on the output from a test run to
188 discriminate on new versus known failures."
190 :expected-result ,expected-value
192 (,(if errorset 'test-values-errorset 'test-values) ,test-form t)
193 ,@(when test-given `(:predicate ,test))
194 ,@(when multiple-values-given `(:multiple-values ,multiple-values))
195 ,@(when fail-info-given `(:fail-info ,fail-info))
196 ,@(when known-failure-given `(:known-failure ,known-failure))
197 :test-form ',(if reported-form reported-form test-form)
198 ,@(when wanted-message-given `(:wanted-message ,wanted-message))
199 ,@(when got-message-given `(:got-message ,got-message))))
201 (defmethod conditionp ((thing condition)) t)
202 (defmethod conditionp ((thing t)) nil)
204 (defmacro test-error (form &key announce
206 (fail-info nil fail-info-given)
207 (known-failure nil known-failure-given)
208 (condition-type ''simple-error)
209 (include-subtypes nil include-subtypes-given)
210 (format-control nil format-control-given)
211 (format-arguments nil format-arguments-given))
212 "Test that `form' signals an error. The order of evaluation of the
213 arguments is keywords first, then test form.
215 If `announce' is non-nil, then cause the error message to be printed.
217 The `catch-breaks' is non-nil then consider a call to common-lisp:break an
220 `fail-info' allows more information to be printed with a test failure.
222 `known-failure' marks the test as a known failure. This allows for
223 programs that do regression analysis on the output from a test run to
224 discriminate on new versus known failures.
226 If `condition-type' is non-nil, it should be a symbol naming a condition
227 type, which is used to check against the signalled condition type. The
228 test will fail if they do not match.
230 `include-subtypes', used with `condition-type', can be used to match a
231 condition to an entire subclass of the condition type hierarchy.
233 `format-control' and `format-arguments' can be used to check the error
235 (let ((g-announce (gensym))
236 (g-catch-breaks (gensym))
237 (g-fail-info (gensym))
238 (g-known-failure (gensym))
239 (g-condition-type (gensym))
240 (g-include-subtypes (gensym))
241 (g-format-control (gensym))
242 (g-format-arguments (gensym))
244 `(let* ((,g-announce ,announce)
245 (,g-catch-breaks ,catch-breaks)
246 ,@(when fail-info-given `((,g-fail-info ,fail-info)))
247 ,@(when known-failure-given `((,g-known-failure ,known-failure)))
248 (,g-condition-type ,condition-type)
249 ,@(when include-subtypes-given
250 `((,g-include-subtypes ,include-subtypes)))
251 ,@(when format-control-given
252 `((,g-format-control ,format-control)))
253 ,@(when format-arguments-given
254 `((,g-format-arguments ,format-arguments)))
255 (,g-c (test-values-errorset ,form ,g-announce ,g-catch-breaks)))
260 (test-values (and (conditionp ,g-c)
261 ,@(if* include-subtypes-given
262 then `((if* ,g-include-subtypes
263 then (typep ,g-c ,g-condition-type)
264 else (eq (class-of ,g-c)
266 ,g-condition-type))))
267 else `((eq (class-of ,g-c)
268 (find-class ,g-condition-type))))
269 ,@(when format-control-given
271 (null ,g-format-control)
273 (concatenate 'simple-string
274 "~1@<" ,g-format-control "~:@>")
275 (simple-condition-format-control ,g-c)))))
276 ,@(when format-arguments-given
278 (null ,g-format-arguments)
281 (simple-condition-format-arguments ,g-c))))))
284 ,@(when fail-info-given `(:fail-info ,g-fail-info))
285 ,@(when known-failure-given `(:known-failure ,g-known-failure))
286 :condition-type ,g-condition-type
288 ,@(when include-subtypes-given
289 `(:include-subtypes ,g-include-subtypes))
290 ,@(when format-control-given
291 `(:format-control ,g-format-control))
292 ,@(when format-arguments-given
293 `(:format-arguments ,g-format-arguments))))))
295 (defmacro test-no-error (form &key announce
297 (fail-info nil fail-info-given)
298 (known-failure nil known-failure-given))
299 "Test that `form' does not signal an error. The order of evaluation of
300 the arguments is keywords first, then test form.
302 If `announce' is non-nil, then cause the error message to be printed.
304 The `catch-breaks' is non-nil then consider a call to common-lisp:break an
307 `fail-info' allows more information to be printed with a test failure.
309 `known-failure' marks the test as a known failure. This allows for
310 programs that do regression analysis on the output from a test run to
311 discriminate on new versus known failures."
312 (let ((g-announce (gensym))
313 (g-catch-breaks (gensym))
314 (g-fail-info (gensym))
315 (g-known-failure (gensym))
317 `(let* ((,g-announce ,announce)
318 (,g-catch-breaks ,catch-breaks)
319 ,@(when fail-info-given `((,g-fail-info ,fail-info)))
320 ,@(when known-failure-given `((,g-known-failure ,known-failure)))
321 (,g-c (test-values-errorset ,form ,g-announce ,g-catch-breaks)))
325 :test-results (test-values (not (conditionp ,g-c)))
328 ,@(when fail-info-given `(:fail-info ,g-fail-info))
329 ,@(when known-failure-given `(:known-failure ,g-known-failure))))))
331 (defvar *warn-cookie* (cons nil nil))
333 (defmacro test-warning (form &key fail-info known-failure)
334 "Test that `form' signals a warning. The order of evaluation of
335 the arguments is keywords first, then test form.
337 `fail-info' allows more information to be printed with a test failure.
339 `known-failure' marks the test as a known failure. This allows for
340 programs that do regression analysis on the output from a test run to
341 discriminate on new versus known failures."
342 (let ((g-fail-info (gensym))
343 (g-known-failure (gensym))
345 `(let* ((,g-fail-info ,fail-info)
346 (,g-known-failure ,known-failure)
347 (,g-value (test-values-errorset ,form nil t)))
350 (if* (or (typep ,g-value 'simple-warning) (typep ,g-value 'warning))
352 else ;; test produced no warning
355 :reported-form ,form ;; quoted by test macro
356 :wanted-message "a warning"
357 :got-message "no warning"
358 :fail-info ,g-fail-info
359 :known-failure ,g-known-failure))))
361 (defmacro test-no-warning (form &key fail-info known-failure)
362 "Test that `form' does not signal a warning. The order of evaluation of
363 the arguments is keywords first, then test form.
365 `fail-info' allows more information to be printed with a test failure.
367 `known-failure' marks the test as a known failure. This allows for
368 programs that do regression analysis on the output from a test run to
369 discriminate on new versus known failures."
370 (let ((g-fail-info (gensym))
371 (g-known-failure (gensym))
373 `(let* ((,g-fail-info ,fail-info)
374 (,g-known-failure ,known-failure)
375 (,g-value (test-values-errorset ,form nil t)))
378 (if* (or (typep ,g-value 'simple-warning) (typep ,g-value 'warning))
379 then nil ;; test produced warning
382 :reported-form ',form
383 :wanted-message "no warning"
384 :got-message "a warning"
385 :fail-info ,g-fail-info
386 :known-failure ,g-known-failure))))
388 (defvar *announce-test* nil) ;; if true announce each test that was done
390 (defmacro errorset (form &optional announce catch-breaks)
391 ;; Evaluate FORM, and if there are no errors and FORM returns
392 ;; values v1,v2,...,vn, then return values t,v1,v2,...,vn. If an
393 ;; error occurs while evaluating FORM, then return nil immediately.
394 ;; If ANNOUNCE is t, then the error message will be printed out.
396 `(handler-case (values-list (cons t (multiple-value-list ,form)))
398 (declare (ignore-if-unused condition))
399 ,@(if announce `((format *error-output* "~&Error: ~a~%" condition)))
401 (simple-break (condition)
402 (declare (ignore-if-unused condition))
403 ,@(if announce `((format *error-output* "~&Warning: ~a~%" condition))
406 `(handler-case (values-list (cons t (multiple-value-list ,form)))
408 (declare (ignore-if-unused condition))
409 ,@(if announce `((format *error-output* "~&Error: ~a~%" condition)))
412 (defun test-check (&key (predicate #'eql)
413 expected-result test-results test-form
414 multiple-values fail-info known-failure
415 wanted-message got-message condition-type condition
416 include-subtypes format-control format-arguments
417 &aux fail predicate-failed got wanted)
418 ;; for debugging large/complex test sets:
419 (when *announce-test*
420 (format t "Just did test ~s~%" test-form)
423 ;; this is an internal function
424 (flet ((check (expected-result result)
427 (errorset (funcall predicate expected-result result) t)))
428 (failed (null (car results))))
430 then (setq predicate-failed t)
432 else (cadr results)))))
433 (when (conditionp test-results)
434 (setq condition test-results)
435 (setq test-results nil))
436 (when (null (car test-results))
438 (if* (and (not fail) (not multiple-values))
439 then ;; should be a single result
440 ;; expected-result is the single result wanted
441 (when (not (and (cdr test-results)
442 (check expected-result (cadr test-results))))
444 (when (and (not fail) (cddr test-results))
445 (setq fail 'single-got-multiple))
446 else ;; multiple results wanted
447 ;; expected-result is a list of results, each of which
448 ;; should be checked against the corresponding test-results
449 ;; using the predicate
450 (do ((got (cdr test-results) (cdr got))
451 (want expected-result (cdr want)))
452 ((or (null got) (null want))
453 (when (not (and (null want) (null got)))
455 (when (not (check (car got) (car want)))
456 (return (setq fail t)))))
458 then (when (not known-failure)
459 (format *error-output*
460 "~& * * * UNEXPECTED TEST FAILURE * * *~%")
461 (incf *test-unexpected-failures*))
462 (format *error-output* "~&Test failed: ~@[known failure: ~*~]~s~%"
463 known-failure test-form)
464 (if* (eq 'single-got-multiple fail)
468 Reason: additional value were returned from test form.~%")
469 elseif predicate-failed
470 then (format *error-output* "Reason: predicate error.~%")
471 elseif (null (car test-results))
472 then (format *error-output* "~
473 Reason: an error~@[ (of type `~s')~] was detected.~%"
474 (when condition (class-of condition)))
476 then (if* (not (conditionp condition))
477 then (format *error-output* "~
478 Reason: expected but did not detect an error of type `~s'.~%"
480 elseif (null condition-type)
481 then (format *error-output* "~
482 Reason: detected an unexpected error of type `~s':
486 elseif (not (if* include-subtypes
487 then (typep condition condition-type)
488 else (eq (class-of condition)
489 (find-class condition-type))))
490 then (format *error-output* "~
491 Reason: detected an incorrect condition type.~%")
492 (format *error-output*
493 " wanted: ~s~%" condition-type)
494 (format *error-output*
495 " got: ~s~%" (class-of condition))
496 elseif (and format-control
499 (concatenate 'simple-string
500 "~1@<" format-control "~:@>"))
502 (simple-condition-format-control
504 then ;; format control doesn't match
505 (format *error-output* "~
506 Reason: the format-control was incorrect.~%")
507 (format *error-output* " wanted: ~s~%" wanted)
508 (format *error-output* " got: ~s~%" got)
509 elseif (and format-arguments
511 (setq got format-arguments)
513 (simple-condition-format-arguments
515 then (format *error-output* "~
516 Reason: the format-arguments were incorrect.~%")
517 (format *error-output* " wanted: ~s~%" wanted)
518 (format *error-output* " got: ~s~%" got)
519 else ;; what else????
520 (error "internal-error"))
521 else (let ((*print-length* 50)
524 then (format *error-output*
525 " wanted: ~a~%" wanted-message)
526 else (if* (not multiple-values)
527 then (format *error-output*
532 " wanted values: ~{~s~^, ~}~%"
535 then (format *error-output*
536 " got: ~a~%" got-message)
537 else (if* (not multiple-values)
538 then (format *error-output* " got: ~s~%"
539 (second test-results))
542 " got values: ~{~s~^, ~}~%"
543 (cdr test-results))))))
545 (format *error-output* "Additional info: ~a~%" fail-info))
547 (when *break-on-test-failures*
548 (break "~a is non-nil." '*break-on-test-failures*))
549 else (when known-failure
550 (format *error-output*
551 "~&Expected test failure for ~s did not occur.~%"
554 (format *error-output* "Additional info: ~a~%" fail-info))
556 (incf *test-successes*))
559 (defmacro with-tests ((&key (name "unnamed")) &body body)
560 (let ((g-name (gensym)))
561 `(flet ((doit () ,@body))
562 (let ((,g-name ,name)
565 (*test-unexpected-failures* 0))
566 (format *error-output* "Begin ~a test~%" ,g-name)
567 (if* *break-on-test-failures*
569 else (handler-case (doit)
574 ~&Test ~a aborted by signalling an uncaught error:~%~a~%"
577 (let ((state (sys:gsgc-switch :print)))
578 (setf (sys:gsgc-switch :print) nil)
579 (format t "~&**********************************~%" ,g-name)
580 (format t "End ~a test~%" ,g-name)
581 (format t "Errors detected in this test: ~s " *test-errors*)
582 (unless (zerop *test-unexpected-failures*)
583 (format t "UNEXPECTED: ~s" *test-unexpected-failures*))
584 (format t "~%Successes this test:~s~%" *test-successes*)
585 (setf (sys:gsgc-switch :print) state))
588 (format t "~&**********************************~%" ,g-name)
589 (format t "End ~a test~%" ,g-name)
590 (format t "Errors detected in this test: ~s " *test-errors*)
591 (unless (zerop *test-unexpected-failures*)
592 (format t "UNEXPECTED: ~s" *test-unexpected-failures*))
593 (format t "~%Successes this test:~s~%" *test-successes*))
596 (provide :tester #+module-versions 1.1)