1 ;;;-*- Mode: Lisp; Package: lift -*-
5 (defmethod accumulate-problem ((problem test-failure-mixin
) result
)
6 (setf (failures result
) (append (failures result
) (list problem
))))
8 (defmethod accumulate-problem ((problem testsuite-failure
) result
)
9 (setf (failures result
) (append (failures result
) (list problem
))))
11 (defmethod accumulate-problem ((problem test-expected-failure
) result
)
12 (setf (expected-failures result
)
13 (append (expected-failures result
) (list problem
))))
15 (defmethod accumulate-problem ((problem test-error-mixin
) result
)
16 (setf (errors result
) (append (errors result
) (list problem
))))
18 (defmethod accumulate-problem ((problem testsuite-error
) result
)
19 (setf (errors result
) (append (errors result
) (list problem
))))
21 (defmethod accumulate-problem ((problem test-serious-condition
) result
)
22 (setf (errors result
) (append (errors result
) (list problem
))))
24 (defmethod accumulate-problem ((problem testsuite-serious-condition
) result
)
25 (setf (errors result
) (append (errors result
) (list problem
))))
27 (defmethod accumulate-problem ((problem test-expected-error
) result
)
28 (setf (expected-errors result
)
29 (append (expected-errors result
) (list problem
))))
31 (defmethod accumulate-problem ((problem testcase-skipped
) result
)
32 (setf (skipped-test-cases result
)
33 (append (skipped-test-cases result
) (list problem
))))
35 (defmethod accumulate-problem ((problem testsuite-skipped
) result
)
36 (setf (skipped-testsuites result
)
37 (append (skipped-testsuites result
) (list problem
))))
40 ;;; ---------------------------------------------------------------------------
42 ;;; ---------------------------------------------------------------------------
44 (defcondition (lift-compile-error :exportp nil
) (error)
46 "Compile error: '~S'" msg
)
48 (defcondition testsuite-not-defined
(lift-compile-error)
50 "Test class ~A not defined before it was used."
53 (defcondition testsuite-ambiguous
(lift-compile-error)
54 (testsuite-name possible-matches
)
55 "There are several test suites named ~s: they are ~{~s~^, ~}"
56 testsuite-name possible-matches
)
58 (defcondition test-case-not-defined
(lift-compile-error)
59 (testsuite-name test-case-name
)
60 "Testsuite ~s has no test-case named ~s."
61 testsuite-name test-case-name
)
63 (defcondition test-case-ambiguous
(lift-compile-error)
64 (testsuite-name test-case-name possible-matches
)
65 "There are several test cases named ~s.~s: they are ~{~s~^, ~}"
66 testsuite-name test-case-name possible-matches
)
68 (defcondition test-condition
(warning)
69 ((message :initform
""))
72 (defcondition (test-timeout-condition :slot-names
(message)) (test-condition)
73 ((maximum-time :initform
*test-maximum-time
*))
74 "Test ran out of time (longer than ~S-second~:P)"
77 (defcondition (ensure-failed-error :slot-names
(message)) (test-condition)
78 ((assertion :initform
""))
79 "Ensure failed: ~S ~@[(~a)~]" assertion message
)
81 (defcondition (ensure-null-failed-error :slot-names
(message)) (ensure-failed-error)
83 (assertion :initform
""))
84 "Ensure null failed: ~s evaluates to ~s ~@[(~a)~]"
85 assertion value message
)
87 (defcondition ensure-expected-condition
(test-condition)
88 ((expected-condition-type
92 "Expected ~S but got ~S~@[:~_ ~A~]"
93 expected-condition-type
94 (type-of the-condition
)
95 (and (typep the-condition
'condition
)
98 (defcondition ensure-expected-no-warning-condition
(test-condition)
101 "Expected no warnings but got ~S"
104 (defcondition failed-comparison-condition
(test-condition)
105 (first-value second-value test
))
107 (defcondition (ensure-not-same :slot-names
(first-value second-value test message
))
108 (failed-comparison-condition)
110 "Ensure-same: ~S is not ~a to ~S~@[ (~a)~]"
111 first-value
(test-function-name test
)
112 second-value message
)
114 (defcondition (ensure-same :slot-names
(first-value second-value test message
))
115 (failed-comparison-condition)
117 "Ensure-different: ~S is ~a to ~S~@[ (~a)~]"
118 first-value
(test-function-name test
) second-value message
)
120 (defcondition ensure-cases-failure
(test-condition)
122 (problems :initform nil
)
123 (errors :initform nil
))
126 "Ensure-cases: ~d case~:p with ~[~:;~:*~d error~:p; ~]~[~:;~:*~d failure~:p; ~]"
127 total
(length errors
) (length problems
))
129 (format stream
"~&Errors: ~@< ~@;~{~% ~{~20s ~3,8@t~a~}~^, ~}~:>"
132 (format stream
"~&Failures: ~@< ~@;~{~% ~{~20s ~3,8@t~a~}~^, ~}~:>"
135 (defcondition unexpected-success-failure
(test-condition)
136 (expected expected-more
)
137 "Test succeeded but we expected ~s (~s)"
138 expected expected-more
)
140 (defun build-lift-error-message (context message
&rest arguments
)
143 (apply #'format nil message arguments
)))
145 (defun signal-lift-error (context message
&rest arguments
)
146 (let ((c (make-condition
148 :msg
(apply #'build-lift-error-message
149 context message arguments
))))
153 (defun report-lift-error (context message
&rest arguments
)
154 (format *debug-io
* "~&~A."
155 (apply #'build-lift-error-message context message arguments
))
158 (defun lift-report-condition (c)
159 (format *debug-io
* "~&~A." c
))
161 (defun maybe-raise-not-same-condition (value-1 value-2 test
162 report
&rest arguments
)
163 (let ((condition (make-condition 'ensure-not-same
165 :second-value value-2
167 :message
(when report
169 report arguments
)))))
170 (if (find-restart 'ensure-failed
)
171 (invoke-restart 'ensure-failed condition
)
174 (defun maybe-raise-ensure-same-condition (value-1 value-2 test
175 report
&rest arguments
)
176 (let ((condition (make-condition 'ensure-same
178 :second-value value-2
180 :message
(when report
182 report arguments
)))))
183 (if (find-restart 'ensure-failed
)
184 (invoke-restart 'ensure-failed condition
)
188 ;;; ---------------------------------------------------------------------------
190 ;;; ---------------------------------------------------------------------------
192 (defmethod testsuite-setup ((testsuite test-mixin
) (result test-result
))
195 (defmethod testsuite-setup :before
((testsuite test-mixin
) (result test-result
))
196 (push (type-of testsuite
) (suites-run result
))
197 (setf (current-step result
) :testsuite-setup
))
199 (defmethod testsuite-expects-error ((testsuite test-mixin
))
202 (defmethod testsuite-expects-failure ((testsuite test-mixin
))
205 (defmethod testsuite-teardown ((testsuite test-mixin
) (result test-result
))
209 (defmethod testsuite-teardown :after
210 ((testsuite test-mixin
) (result test-result
))
211 (setf (current-step result
) :testsuite-teardown
212 (real-end-time result
) (get-test-real-time)
213 (real-end-time-universal result
) (get-universal-time)))
217 (defun canonize-skip-tests (skip-tests)
220 (cond ((or (atom datum
)
221 (and (= (length datum
) 1)
222 (setf datum
(first datum
)))
223 (and (= (length datum
) 2) (null (second datum
))
224 (setf datum
(first datum
))))
225 (cons (find-testsuite datum
:errorp t
) nil
))
226 ((= (length datum
) 2)
227 (cons (find-testsuite (first datum
) :errorp t
)
228 (or (and (keywordp (second datum
)) (second datum
))
229 (find-test-case (find-testsuite (first datum
))
230 (second datum
) :errorp t
))))
232 (warn "Unable to interpret skip datum ~a. Ignoring."
236 (defun test-result-property (result property
&optional default
)
237 (getf (test-result-properties result
) property default
))
239 (defun (setf test-result-property
) (value result property
)
240 (setf (getf (test-result-properties result
) property
) value
))
242 (defmethod write-profile-information ((suite t
))
245 (defmethod equality-test ((suite test-mixin
))
248 (defmethod setup-test :before
((test test-mixin
))
249 (setf *test-scratchpad
* nil
))
251 (defmethod setup-test ((test test-mixin
))
254 (defmethod setup-test ((test symbol
))
255 (let ((*current-test
* (make-testsuite test nil
)))
256 (setup-test *current-test
*)
259 (defmethod test-case-teardown progn
((test test-mixin
) (result test-result
))
262 (defmethod test-case-teardown :around
((test test-mixin
) (result test-result
))
263 (setf (current-step result
) :test-teardown
)
266 (defmethod initialize-instance :after
((testsuite test-mixin
) &rest initargs
267 &key
&allow-other-keys
)
268 (declare (ignorable initargs
))
269 (when (null (testsuite-name testsuite
))
270 (setf (slot-value testsuite
'name
)
271 (symbol-name (type-of testsuite
)))))
273 (defmethod print-object ((tc test-mixin
) stream
)
274 (print-unreadable-object (tc stream
:identity t
:type t
)
275 (format stream
"~a" (testsuite-name tc
))))
277 ;;; ---------------------------------------------------------------------------
279 ;;; ---------------------------------------------------------------------------
281 (defun initialize-current-definition ()
282 (setf *current-definition
* nil
))
284 (defun set-definition (name value
)
285 (let ((current (assoc name
*current-definition
*)))
287 (setf (cdr current
) value
)
288 (push (cons name value
) *current-definition
*)))
292 (defstruct (code-block (:type list
) (:conc-name nil
))
293 block-name
(priority 0) filter code operate-when
)
295 (defun add-code-block (name priority operate-when filter handler code
)
296 (let ((current (assoc name
*code-blocks
*))
297 (value (make-code-block
298 :operate-when operate-when
304 (setf (cdr current
) value
)
305 (push (cons name value
) *code-blocks
*))
307 `(defmethod block-handler ((name (eql ',name
)) value
)
308 (declare (ignorable value
))
310 (setf *code-blocks
* (sort *code-blocks
* #'<
311 :key
(lambda (name.cb
)
312 (priority (cdr name.cb
))))))
314 (defmacro deftest
(testsuite-name superclasses slots
&rest
316 "The `deftest` form is obsolete, see [deftestsuite][]."
318 (warn "Deftest is obsolete, use deftestsuite instead.")
319 `(deftestsuite ,testsuite-name
,superclasses
,slots
,@clauses-and-options
))
321 (setf *code-blocks
* nil
)
326 '((setf (def :setup
) (cleanup-parsed-parameter value
)))
327 'build-setup-test-method
)
330 :teardown
100 :methods
331 (lambda () (or (def :teardown
) (def :direct-slot-names
)))
332 '((setf (def :teardown
) (cleanup-parsed-parameter value
)))
333 'build-test-teardown-method
)
337 (lambda () (def :functions
))
338 '((push value
(def :functions
)))
339 'build-test-local-functions
)
342 :documentation
0 :class-def
344 '((setf (def :documentation
) (first value
)))
348 :export-p
0 :class-def
350 '((setf (def :export-p
) (first value
)))
354 :export-slots
0 :class-def
356 '((setf (def :export-slots
) (first value
)))
360 :run-setup
0 :class-def
362 '((push (first value
) (def :default-initargs
))
363 (push :run-setup
(def :default-initargs
))
364 (setf (def :run-setup
) (first value
)))
365 'check-run-setup-value
)
367 (defun %valid-run-setup-values
()
368 '(:once-per-session
:once-per-suite
369 :once-per-test-case
:never
))
371 (defun check-run-setup-value ()
372 (when (def :run-setup
)
373 (unless (member (def :run-setup
) (%valid-run-setup-values
))
374 (error "The :run-setup option must be one of ~{~a~^, ~}."
375 (%valid-run-setup-values
)))))
378 :equality-test
0 :methods
379 (lambda () (def :equality-test
))
380 '((setf (def :equality-test
) (cleanup-parsed-parameter value
)))
381 'build-test-equality-test
)
384 :expected-error
0 :methods
385 (lambda () (def :expected-error
))
386 '((setf (def :expected-error
) (cleanup-parsed-parameter value
)))
387 'build-testsuite-expected-error
)
390 :expected-failure
0 :methods
391 (lambda () (def :expected-failure
))
392 '((setf (def :expected-failure
) (cleanup-parsed-parameter value
)))
393 'build-testsuite-expected-failure
)
396 :log-file
0 :class-def
398 '((push (first value
) (def :default-initargs
))
399 (push :log-file
(def :default-initargs
)))
403 :dynamic-variables
0 :class-def
405 '((setf (def :direct-dynamic-variables
) value
))
409 :categories
0 :class-def
411 '((push value
(def :categories
)))
415 :default-initargs
1 :class-def
416 (lambda () (def :default-initargs
))
417 '((dolist (x (reverse (cleanup-parsed-parameter value
)))
418 (push x
(def :default-initargs
))))
421 (defmacro deftestsuite
(testsuite-name superclasses slots
&rest
424 Creates a testsuite named `testsuite-name` and, optionally, the code required for test setup, test tear-down and the actual test-cases. A testsuite is a collection of test-cases and other testsuites.
426 Test suites can have multiple superclasses (just like the classes that they are). Usually, these will be other test classes and the class hierarchy becomes the test case hierarchy. If necessary, however, non-testsuite classes can also be used as superclasses.
428 Slots are specified as in defclass with the following additions:
430 * Initargs and accessors are automatically defined. If a slot is named`my-slot`, then the initarg will be `:my-slot` and the accessors will be `my-slot` and `(setf my-slot)`.
431 * If the second argument is not a CLOS slot option keyword, then it will be used as the `:initform` for the slot. I.e., if you have
433 (deftestsuite my-test ()
436 then `my-slot` will be initialized to 23 during test setup.
438 Test options are one of :setup, :teardown, :test, :tests, :documentation, :export-p, :dynamic-variables, :export-slots, :function, :categories, :run-setup, or :equality-test.
440 * :categories - a list of symbols. Categories allow you to groups tests into clusters outside of the basic hierarchy. This provides finer grained control on selecting which tests to run. May be specified multiple times.
442 * :documentation - a string specifying any documentation for the test. Should only be specified once.
444 * :dynamic-variables - a list of atoms or pairs of the form (name value). These specify any special variables that should be bound in a let around the body of the test. The name should be symbol designating a special variable. The value (if supplied) will be bound to the variable. If the value is not supplied, the variable will be bound to nil. Should only be specified once.
446 * :equality-test - the name of the function to be used by default in calls to ensure-same and ensure-different. Should only be supplied once.
448 * :export-p - If true, the testsuite name will be exported from the current package. Should only be specified once.
450 * :export-slots - if true, any slots specified in the test suite will be exported from the current package. Should only be specified once.
452 * :function - creates a locally accessible function for this test suite. May be specified multiple times.
454 * :run-setup - specify when to run the setup code for this test suite. Allowed values are
456 * :once-per-test-case or t (the default)
461 :run-setup is handy when a testsuite has a time consuming setup phase that you do not want to repeat for every test.
463 * :setup - a list of forms to be evaluated before each test case is run. Should only be specified once.
465 * :teardown - a list of forms to be evaluated after each test case is run. Should only be specified once.
467 * :test - Define a single test case. Can be specified multiple times.
469 * :tests - Define multiple test cases for this test suite. Can be specified multiple times.
474 (let ((test-list nil
)
477 ;; convert any clause like :setup foo into (:setup foo)
478 (setf clauses-and-options
479 (convert-clauses-into-lists clauses-and-options
*deftest-clauses
*))
480 (initialize-current-definition)
481 (setf (def :testsuite-name
) testsuite-name
)
482 (setf (def :superclasses
) (mapcar (lambda (class) (find-testsuite class
:errorp t
))
484 (setf (def :deftestsuite
) t
)
485 ;; parse clauses into defs
486 (loop for clause in clauses-and-options do
488 (symbol (pushnew clause options
))
489 (cons (destructuring-bind (kind &rest spec
) clause
491 (:test
(push (first spec
) test-list
))
493 (loop for test in spec do
494 (push test test-list
)))
495 (t (block-handler kind spec
)))))
496 (t (error "When parsing ~S" clause
))))
497 (let ((slot-names nil
) (slot-specs nil
))
498 (loop for slot in
(if (listp slots
) slots
(list slots
)) do
499 (push (if (consp slot
) (first slot
) slot
) slot-names
)
500 (push (parse-brief-slot slot
) slot-specs
))
501 (setf (def :slot-specs
) (nreverse slot-specs
)
502 (def :direct-slot-names
) (nreverse slot-names
)
503 (def :slots-parsed
) t
))
504 ;;?? issue 27: breaks 'encapsulation' of code-block mechanism
505 (setf (def :function-specs
)
506 (loop for spec in
(def :functions
) collect
507 (destructuring-bind (name arglist
&body body
) (first spec
)
508 (declare (ignore body
))
511 (empty-test-tables testsuite-name
)
512 (compute-superclass-inheritence)
514 (setf *testsuite-test-count
* 0)
515 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
516 (eval-when (:compile-toplevel
)
517 (push ',return
*test-is-being-compiled?
*))
518 (eval-when (:load-toplevel
)
519 (push ',return
*test-is-being-loaded?
*))
520 (eval-when (:execute
)
521 (push ',return
*test-is-being-executed?
*))
522 ;; remove previous methods (do this _before_ we define the class)
523 (unless (or *test-is-being-compiled?
*
524 *test-is-being-loaded?
*)
525 (remove-previous-definitions ',(def :testsuite-name
)))
528 (let ((*test-is-being-defined?
* t
))
529 (setf *last-test-case-name
* nil
)
530 (setf *last-testsuite-name
* ',(def :testsuite-name
)
531 (test-slots ',(def :testsuite-name
))
533 (testsuite-dynamic-variables ',(def :testsuite-name
))
534 ',(def :dynamic-variables
)
535 ;;?? issue 27: breaks 'encapsulation' of code-block
537 (testsuite-function-specs ',(def :testsuite-name
))
538 ',(def :function-specs
))
539 ,@(when (def :export-p
)
540 `((export '(,(def :testsuite-name
)))))
541 ,@(when (def :export-slots?
)
542 `((export ',(def :direct-slot-names
))))
543 ;; make a place to save test-case information
544 (empty-test-tables ',(def :testsuite-name
))
547 ,@(loop for
(nil . block
) in
*code-blocks
*
550 (eq (operate-when block
) :methods
)
551 (or (not (filter block
))
552 (funcall (filter block
)))) collect
553 (funcall (code block
)))
554 ,@(when (def :dynamic-variables
)
555 `((defmethod do-testing :around
556 ((suite ,(def :testsuite-name
)) result fn
)
557 (declare (ignore result fn
))
559 (cond ((done-dynamics? suite
)
562 (setf (slot-value suite
'done-dynamics?
) t
)
563 (let* (,@(def :dynamic-variables
))
566 #'car
(def :dynamic-variables
))))
567 (call-next-method))))))))
570 `((let ((*test-evaluate-when-defined?
* nil
))
571 ,@(loop for test in
(nreverse test-list
) collect
572 `(addtest (,(def :testsuite-name
))
574 (setf *testsuite-test-count
* nil
))))
575 ,(if (and test-list
*test-evaluate-when-defined?
*)
576 `(unless (or *test-is-being-compiled?
*
577 *test-is-being-loaded?
*)
578 (let ((*test-break-on-errors?
* *test-break-on-errors?
*))
579 (run-tests :suite
',testsuite-name
)))
580 `(find-class ',testsuite-name
)))
582 (setf *test-is-being-compiled?
*
583 (remove ',return
*test-is-being-compiled?
*))
584 (setf *test-is-being-loaded?
*
585 (remove ',return
*test-is-being-loaded?
*))
586 (setf *test-is-being-executed?
*
587 (remove ',return
*test-is-being-executed?
*)))))))
589 (defun compute-superclass-inheritence ()
590 ;;?? issue 27: break encapsulation of code blocks
591 ;;?? we assume that we won't have too deep a hierarchy or too many
592 ;; dv's or functions so that having lots of duplicate names is OK
594 (inherited-dynamic-variables nil
)
595 (dynamic-variables (%build-pairs
(def :direct-dynamic-variables
)))
596 (function-specs nil
))
597 (dolist (super (def :superclasses
))
598 (cond ((find-testsuite super
)
599 (setf slots
(append slots
(test-slots super
))
600 inherited-dynamic-variables
601 (append inherited-dynamic-variables
602 (testsuite-dynamic-variables super
))
604 (append function-specs
605 (testsuite-function-specs super
))))
607 (error 'testsuite-not-defined
:testsuite-name super
))))
608 (loop for pair in inherited-dynamic-variables
609 unless
(find (first pair
) dynamic-variables
:key
#'first
) collect
610 (progn (push pair dynamic-variables
) pair
))
611 (setf (def :slot-names
)
612 (remove-duplicates (append (def :direct-slot-names
) slots
))
613 (def :dynamic-variables
) (nreverse dynamic-variables
)
614 (def :function-specs
)
616 (append (def :function-specs
) function-specs
)))
617 (setf (def :superclasses
)
618 (loop for class in
(def :superclasses
)
619 unless
(some (lambda (oter)
620 (and (not (eq class oter
))
621 (member class
(superclasses oter
))))
622 (def :superclasses
)) collect
625 (defun %build-pairs
(putative-pairs)
627 (dolist (putative-pair putative-pairs
)
628 (if (atom putative-pair
)
629 (push (list putative-pair nil
) result
)
630 (push putative-pair result
)))
633 (defmacro addtest
(name &body test
)
634 "Adds a single new test-case to the most recently defined testsuite."
640 (options nil
) (documentation nil
)
641 (looks-like-suite-name (looks-like-suite-name-p name
)))
642 (cond (looks-like-suite-name
644 (setf (def :testsuite-name
) (first name
)
648 ;; the 'name' is really part of the test...
649 (setf body
(cons name test
))))
650 (unless (property-list-p options
)
651 (signal-lift-error 'add-test
"test-case options must be a property list and \"~s`\" is not" options
))
652 (when (getf options
:documentation
)
653 (setf documentation
(getf options
:documentation
))
654 (remf options
:documentation
))
655 (unless (def :testsuite-name
)
656 (when *last-testsuite-name
*
657 (setf (def :testsuite-name
) *last-testsuite-name
*)))
658 (unless (def :testsuite-name
)
659 (signal-lift-error 'add-test
+lift-no-current-test-class
+))
660 (unless (or (def :deftestsuite
)
661 (find-testsuite (def :testsuite-name
)))
662 (signal-lift-error 'add-test
+lift-test-class-not-found
+
663 (def :testsuite-name
)))
664 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
665 (eval-when (:compile-toplevel
)
666 (push ',return
*test-is-being-compiled?
*))
667 (eval-when (:load-toplevel
)
668 (push ',return
*test-is-being-loaded?
*))
669 (eval-when (:execute
)
670 (push ',return
*test-is-being-executed?
*))
672 (let ((*test-is-being-defined?
* t
))
673 (muffle-redefinition-warnings
674 ,(build-test-test-method (def :testsuite-name
) body options
))
675 ,@(when documentation
677 ',(def :test-case-name
)
678 (test-case-documentation ',(def :testsuite-name
)))
680 ,@(when *compile-file-pathname
*
682 ',(def :test-case-name
)
683 (test-case-source-file ',(def :testsuite-name
)))
684 ,(namestring *compile-file-pathname
*))
687 ',(def :test-case-name
)
688 (test-case-source-position ',(def :testsuite-name
)))
689 ,(current-source-position))))
690 (setf *last-testsuite-name
* ',(def :testsuite-name
))
691 (if *test-evaluate-when-defined?
*
692 (unless (or *test-is-being-compiled?
*
693 *test-is-being-loaded?
*)
694 (let ((*test-break-on-errors?
* (testing-interactively-p)))
698 (setf *test-is-being-compiled?
*
699 (remove ',return
*test-is-being-compiled?
*)
700 *test-is-being-loaded?
*
701 (remove ',return
*test-is-being-loaded?
*)
702 *test-is-being-executed?
*
703 (remove ',return
*test-is-being-executed?
*))))))
705 (defmacro addbenchmark
((suite-name &rest options
) test-case-name
&body body
)
706 "Adds a single new test-benchmark to testsuite suite-name."
710 (let ((documentation nil
))
711 (unless (property-list-p options
)
714 "benchmark options must be a property list and \"~s`\" is not" options
))
715 (when (getf options
:documentation
)
716 (setf documentation
(getf options
:documentation
))
717 (remf options
:documentation
))
719 (signal-lift-error 'addbenchmark
+lift-no-current-test-class
+))
720 (unless (find-testsuite suite-name
)
722 'addbenchmark
+lift-test-class-not-found
+ suite-name
))
723 (setf (def :testsuite-name
) suite-name
724 (def :test-case-name
) test-case-name
)
725 `(eval-when (:compile-toplevel
:load-toplevel
:execute
)
726 (let ((*test-is-being-defined?
* t
))
727 (muffle-redefinition-warnings
728 ,(build-benchmark-function
729 suite-name test-case-name body options
))
730 ,@(when documentation
732 ',(def :test-case-name
)
733 (test-case-documentation ',(def :testsuite-name
)))
735 (setf *last-testsuite-name
* ',(def :testsuite-name
))
736 ',(def :test-case-name
)))))
738 (defun looks-like-suite-name-p (form)
741 (find-testsuite (first form
))))
743 (defun property-list-p (form)
748 for want-keyword?
= t then
(not want-keyword?
) do
749 (when (and want-keyword?
(not (keywordp x
)))
750 (return-from check-it nil
))
751 (setf even?
(not even?
)))
752 (return-from check-it even?
)))))
755 (property-list-p '(:a
:b
))
756 (property-list-p '(:a
2 :b
3 :c
5 :d
8))
757 (property-list-p nil
)
760 (property-list-p '(3))
761 (property-list-p '(3 :a
))
762 (property-list-p '(:a
3 :b
))
765 (defun remove-test (&key
(test-case *last-test-case-name
*)
766 (suite *last-testsuite-name
*))
767 (assert suite nil
"Test suite could not be determined.")
768 (assert test-case nil
"Test-case could not be determined.")
769 (setf (testsuite-tests suite
)
770 (remove test-case
(testsuite-tests suite
))))
772 (defun make-testsuite (suite-name args
)
773 (let ((testsuite (find-testsuite suite-name
:errorp t
))
776 (setf result
(apply #'make-instance testsuite args
))
777 (error "Testsuite ~a not found." suite-name
))
780 (defun skip-test-case-p (result suite-name test-case-name
)
781 (declare (ignore result
))
782 (find-if (lambda (skip-datum)
784 (and (eq suite-name
(car skip-datum
))
785 (eq test-case-name
(cdr skip-datum
)))
786 (subtypep suite-name
(car skip-datum
))))
789 (defun skip-test-suite-children-p (result suite-name
)
790 (declare (ignore result
))
791 (find-if (lambda (skip-datum)
792 (and (subtypep suite-name
(car skip-datum
))
793 (null (cdr skip-datum
))))
796 (defmethod skip-test-case (result suite-name test-case-name
)
797 (report-test-problem 'testcase-skipped result suite-name test-case-name nil
))
799 (defmethod skip-testsuite (result suite-name
)
800 (report-test-problem 'testsuite-skipped result suite-name nil nil
))
802 (defun test-case-expects-error-p (suite-name test-case-name
)
803 (or (testsuite-expects-error *current-test
*)
804 (test-case-option suite-name test-case-name
:expected-error
)))
806 (defun test-case-expects-failure-p (suite-name test-case-name
)
807 (or (testsuite-expects-failure *current-test
*)
808 (test-case-option suite-name test-case-name
:expected-failure
)))
810 (defun test-case-expects-problem-p (suite-name test-case-name
)
811 (test-case-option suite-name test-case-name
:expected-problem
))
813 (defun check-for-surprises (suite-name test-case-name
)
814 (let* ((expected-failure-p (test-case-expects-failure-p
815 suite-name test-case-name
))
816 (expected-error-p (test-case-expects-error-p
817 suite-name test-case-name
))
818 (expected-problem-p (test-case-expects-problem-p
819 suite-name test-case-name
))
824 (make-condition 'unexpected-success-failure
826 :expected-more expected-failure-p
)))
829 (make-condition 'unexpected-success-failure
831 :expected-more expected-error-p
)))
834 (make-condition 'unexpected-success-failure
836 :expected-more expected-problem-p
))))
838 (if (find-restart 'ensure-failed
)
839 (invoke-restart 'ensure-failed condition
)
842 (defun error-okay-p (suite-name test-case-name
)
843 (or (test-case-expects-error-p suite-name test-case-name
)
844 (test-case-expects-problem-p suite-name test-case-name
)))
846 (defun failure-okay-p (suite-name test-case-name
)
847 (or (test-case-expects-failure-p suite-name test-case-name
)
848 (test-case-expects-problem-p suite-name test-case-name
)))
850 (defun report-test-problem (problem-type result suite-name method condition
855 (declare (ignorable docs option
))
856 (cond ((and (eq problem-type
'test-failure
)
857 (not (typep condition
'unexpected-success-failure
))
858 (test-case-expects-failure-p suite-name method
))
859 (setf problem-type
'test-expected-failure
860 option
:expected-failure
))
861 ((and (eq problem-type
'test-error
)
862 (test-case-expects-error-p suite-name method
))
863 (setf problem-type
'test-expected-error
864 option
:expected-error
))
865 ((and (or (eq problem-type
'test-failure
)
866 (eq problem-type
'test-error
))
867 (test-case-expects-problem-p suite-name method
))
868 (setf problem-type
(or (and (eq problem-type
'test-failure
)
869 'test-expected-failure
)
870 (and (eq problem-type
'test-error
)
871 'test-expected-error
))
872 option
:expected-problem
)))
873 (let ((problem (apply #'make-instance problem-type
874 :testsuite suite-name
876 :test-condition condition
877 :test-step
(current-step result
)
878 :testsuite-initargs
(testsuite-initargs result
)
881 (setf (getf (test-data *current-test
*) :problem
) problem
))
882 (accumulate-problem problem result
)
883 (when (and *test-maximum-failure-count
*
884 (numberp *test-maximum-failure-count
*)
885 (>= (length (failures result
)) *test-maximum-failure-count
*))
886 (cancel-testing :failures
))
887 (when (and *test-maximum-error-count
*
888 (numberp *test-maximum-error-count
*)
889 (>= (length (errors result
)) *test-maximum-error-count
*))
890 (cancel-testing :errors
))
893 (defun cancel-testing (why)
894 (declare (ignore why
))
896 (let ((restart (find-restart name
)))
897 (when restart
(invoke-restart restart
*test-result
*)))))
898 (do-it 'cancel-testing-from-configuration
)
899 (do-it 'cancel-testing
)))
901 ;;; ---------------------------------------------------------------------------
902 ;;; test-result and printing
903 ;;; ---------------------------------------------------------------------------
905 (defun get-test-print-length ()
906 (let ((foo *test-print-length
*))
907 (if (eq foo
:follow-print
) *print-length
* foo
)))
909 (defun get-test-print-level ()
910 (let ((foo *test-print-level
*))
911 (if (eq foo
:follow-print
) *print-level
* foo
)))
913 (defun record-start-times (result suite
)
914 (setf (current-step result
) :start-test
916 `(:start-time
,(get-test-real-time)
917 :start-time-universal
,(get-universal-time))))
919 (defun record-end-times (result suite
)
920 (setf (current-step result
) :end-test
921 (getf (test-data suite
) :end-time
) (get-test-real-time)
922 (end-time result
) (get-test-real-time)
923 (getf (test-data suite
) :end-time-universal
) (get-universal-time)
924 (end-time-universal result
) (get-universal-time)))
926 (defmethod make-test-result (for test-mode
&rest args
)
927 (apply #'make-instance
'test-result
932 (defun testing-interactively-p ()
935 (defmethod print-object ((tr test-result
) stream
)
936 (let ((complete-success?
(and (null (errors tr
))
938 (null (expected-failures tr
))
939 (null (expected-errors tr
)))))
940 (let* ((*print-level
* (get-test-print-level))
941 (*print-length
* (get-test-print-length))
942 (non-failure-failures
945 (member (class-of (test-condition failure
))
946 (subclasses 'unexpected-success-failure
:proper? nil
)))
947 (expected-failures tr
)))
948 (expected-failures (- (length (expected-failures tr
))
949 non-failure-failures
)))
950 (print-unreadable-object (tr stream
)
951 (cond ((and (null (tests-run tr
)) complete-success?
)
952 (format stream
"~A: no tests run" (results-for tr
)))
953 ((eq (test-mode tr
) :single
)
954 (cond ((test-interactive? tr
)
956 (cond (complete-success?
957 (format stream
"Test passed"))
959 (format stream
"Error during testing"))
960 ((expected-errors tr
)
961 (format stream
"Expected error during testing"))
963 (format stream
"Test failed"))
964 ((plusp non-failure-failures
)
965 (format stream
"Test succeeded unexpectedly"))
967 (format stream
"Test failed expectedly"))))
970 (format stream
"~A.~A ~A"
972 (second (first (tests-run tr
)))
973 (cond (complete-success?
979 (when (or (expected-errors tr
) (expected-failures tr
))
980 (format stream
"(~[~:;, ~:*~A expected failure~:P~]~[~:;, ~:*~A succeeded unexpectedly~]~[~:;, ~:*~A expected error~:P~])"
981 expected-failures non-failure-failures
982 (expected-errors tr
))))))
984 ;; multiple tests run
985 (format stream
"Results for ~A " (results-for tr
))
986 (if complete-success?
987 (format stream
"[~A Successful test~:P]"
988 (length (tests-run tr
)))
989 (format stream
"~A Test~:P~[~:;, ~:*~A Failure~:P~]~[~:;, ~:*~A Error~:P~]~[~:;, ~:*~A Expected failure~:P~]~[~:;, ~:*~A Expected error~:P~]"
990 (length (tests-run tr
))
991 (length (failures tr
))
993 (length (expected-failures tr
))
994 (length (expected-errors tr
))))))
995 ;; note that suites with no tests think that they are completely
996 ;; successful. Optimistic little buggers, huh?
997 (when (and (not complete-success?
) *test-describe-if-not-successful?
*)
999 (print-test-result-details stream tr t t
))))))
1001 (defmethod describe-object ((result test-result
) stream
)
1002 (describe-test-result result stream
))
1004 (defmethod describe-test-result (result stream
1006 (show-details-p *test-show-details-p
*)
1007 (show-expected-p *test-show-expected-p
*)
1008 (show-code-p *test-show-code-p
*))
1009 (let* ((number-of-failures (length (failures result
)))
1010 (number-of-errors (length (errors result
)))
1011 (number-of-expected-errors (length (expected-errors result
)))
1012 (non-failure-failures
1015 (member (class-of (test-condition failure
))
1016 (subclasses 'unexpected-success-failure
:proper? nil
)))
1017 (expected-failures result
)))
1018 (number-of-expected-failures (- (length (expected-failures result
))
1019 non-failure-failures
))
1020 (*print-level
* (get-test-print-level))
1021 (*print-length
* (get-test-print-length)))
1022 (unless *test-is-being-defined?
*
1023 (print-test-summary result stream
)
1024 (when (and show-details-p
1025 (or (plusp number-of-failures
)
1026 (plusp number-of-expected-failures
)
1027 (plusp number-of-errors
)
1028 (plusp number-of-expected-errors
)))
1029 (format stream
"~%~%")
1030 (print-test-result-details
1031 stream result show-expected-p show-code-p
)
1032 (print-test-summary result stream
)))))
1034 (defun print-test-summary (result stream
)
1035 (let* ((number-of-failures (length (failures result
)))
1036 (number-of-errors (length (errors result
)))
1037 (number-of-expected-errors (length (expected-errors result
)))
1038 (non-failure-failures
1041 (member (class-of (test-condition failure
))
1042 (subclasses 'unexpected-success-failure
:proper? nil
)))
1043 (expected-failures result
)))
1044 (number-of-expected-failures (- (length (expected-failures result
))
1045 non-failure-failures
)))
1046 (format stream
"~&Test Report for ~A: ~D test~:P run"
1047 (results-for result
) (length (tests-run result
)))
1048 (cond ((or (failures result
) (errors result
)
1049 (expected-failures result
) (expected-errors result
))
1050 (format stream
"~[~:;, ~:*~A Error~:P~]~[~:;, ~:*~A Failure~:P~]~[~:;, ~:*~A Expected error~:P~]~[~:;, ~:*~A Expected failure~:P~]~[~:;, ~:*~A Successful Surprise~:P~]."
1053 number-of-expected-errors
1054 number-of-expected-failures
1055 non-failure-failures
))
1056 ((or (expected-failures result
) (expected-errors result
))
1057 (format stream
", all passed *~[~:;, ~:*~A Expected error~:P~]~[~:;, ~:*~A Expected failure~:P~])."
1058 number-of-expected-errors
1059 number-of-expected-failures
))
1061 (format stream
", all passed!")))))
1063 (defun print-test-result-details (stream result show-expected-p show-code-p
)
1064 (loop for report in
(errors result
) do
1065 (print-test-problem "ERROR :" report stream
1067 (loop for report in
(failures result
) do
1068 (print-test-problem "Failure:" report stream
1070 (when show-expected-p
1071 (loop for report in
(expected-failures result
) do
1072 (print-test-problem "Expected failure:" report stream
1074 (loop for report in
(expected-errors result
) do
1075 (print-test-problem "Expected Error :" report stream
1078 (defmethod print-test-problem (prefix (report testsuite-problem-mixin
) stream show-code-p
)
1080 (let* ((suite-name (testsuite report
))
1081 (method (test-method report
))
1082 (condition (test-condition report
))
1083 (code (test-report-code suite-name method
))
1084 (step (test-step report
))
1085 (testsuite-name method
)
1086 (*print-level
* (get-test-print-level))
1087 (*print-length
* (get-test-print-length)))
1088 (let ((*package
* (symbol-package method
))
1089 (doc-string (gethash testsuite-name
1090 (test-case-documentation suite-name
)))
1091 (source-file (gethash testsuite-name
1092 (test-case-source-file suite-name
))))
1093 (format stream
"~&~A ~(~A : ~A~)" prefix suite-name testsuite-name
)
1095 (setf code
(with-output-to-string (out)
1098 (format stream
"~&~< ~@;~
1099 ~@[Documentation: ~<~@;~a~:>~]~
1100 ~@[~&Source : ~<~@;~a~:>~]~
1101 ~@[~&Condition : ~<~@;~a~:>~]~
1104 ~&~:>" `((,doc-string
) (,source-file
) (,condition
) (,step
) (,code
)))))
1106 (format stream
"~&Error printing problem report: ~a" c
))))
1108 (defmethod print-test-problem (prefix (report test-configuration-problem-mixin
) stream show-code-p
)
1109 (declare (ignore show-code-p
))
1110 (format stream
"~&~A ~a~%~%" prefix
(test-problem-message report
)))
1113 ;;; ---------------------------------------------------------------------------
1115 ;;; ---------------------------------------------------------------------------
1117 (defun test-report-code (suite-name test-case-name
)
1118 (gethash test-case-name
(test-name->code-table suite-name
)))
1120 ;;; ---------------------------------------------------------------------------
1122 ;;; ---------------------------------------------------------------------------
1124 (defun remove-test-methods (test-name)
1126 (length (testsuite-tests test-name
))
1127 (setf (testsuite-tests test-name
) nil
)))
1129 (defun remove-previous-definitions (classname)
1130 "Remove the methods of this class and all its subclasses."
1131 (let ((classes-removed nil
)
1132 (class (find-class classname nil
))
1135 (loop for subclass in
(subclasses class
:proper? nil
) do
1136 (push subclass classes-removed
)
1138 (remove-test-methods (class-name subclass
)))
1140 ;;?? causing more trouble than it solves...??
1141 (setf (find-class (class-name subclass
)) nil
))
1143 (unless (length-1-list-p classes-removed
)
1145 "~&;;; Removed Test suite ~(~A~) and its subclasses (~{~<~s~>~^, ~})."
1148 (mapcar #'class-name classes-removed
))
1150 (unless (zerop removed-count
)
1152 "~&;;; Removed ~D methods from test suite ~(~A~)~@[ and its subclasses~]."
1153 removed-count classname
1154 (not (length-1-list-p classes-removed
)))))))
1156 (defun (setf test-environment-value
) (value name
)
1157 (setf (slot-value *current-test
* name
) value
))
1159 (defun test-environment-value (name)
1160 (slot-value *current-test
* name
))
1162 (defun build-test-local-functions ()
1165 (lambda (function-spec)
1166 (destructuring-bind (name arglist
&body body
) (first function-spec
)
1167 `(defmethod flet-test-function ((testsuite ,(def :testsuite-name
))
1168 (function-name (eql ',name
))
1172 `(destructuring-bind ,arglist args
1174 `(progn ,@body
))))))
1177 (defun build-test-equality-test ()
1178 (let ((test-name (def :testsuite-name
))
1179 (equality-test (def :equality-test
)))
1181 (defmethod equality-test ((testsuite ,test-name
))
1184 (defun build-testsuite-expected-error ()
1185 (let ((test-name (def :testsuite-name
))
1186 (expected-error (def :expected-error
)))
1188 (defmethod testsuite-expects-error ((testsuite ,test-name
))
1190 ,expected-error
)))))
1192 (defun build-testsuite-expected-failure ()
1193 (let ((test-name (def :testsuite-name
))
1194 (expected-failure (def :expected-failure
)))
1196 (defmethod testsuite-expects-failure ((testsuite ,test-name
))
1198 ,expected-failure
)))))
1200 (defun build-test-teardown-method ()
1201 (let ((test-name (def :testsuite-name
))
1202 (teardown (def :teardown
)))
1204 (unless (consp teardown
)
1205 (setf teardown
(list teardown
)))
1206 (when (length-1-list-p teardown
)
1207 (setf teardown
(list teardown
)))
1208 (when (symbolp (first teardown
))
1209 (setf teardown
(list teardown
))))
1210 (let* ((teardown-code `(,@(when teardown
1211 `((with-test-slots ,@teardown
)))))
1212 (test-code `(,@teardown-code
)))
1214 ,@(when teardown-code
1215 `((defmethod test-case-teardown progn
((testsuite ,test-name
)
1216 (result test-result
))
1217 (when (run-teardown-p testsuite
:test-case
)
1219 ,@(when teardown-code
1220 `((defmethod testsuite-teardown ((testsuite ,test-name
)
1221 (result test-result
))
1222 (when (run-teardown-p testsuite
:testsuite
)
1225 (defun build-setup-test-method ()
1226 (let ((test-name (def :testsuite-name
))
1227 (setup (def :setup
)))
1228 ;;?? ewww, this smells bad
1230 (unless (consp setup
)
1231 (setf setup
(list setup
)))
1232 (when (length-1-list-p setup
)
1233 (setf setup
(list setup
)))
1234 (when (symbolp (first setup
))
1235 (setf setup
(list setup
))))
1237 `(defmethod setup-test :after
((testsuite ,test-name
))
1240 ;; rather use remove-method
1241 `(defmethod setup-test :after
((testsuite ,test-name
))
1244 (defmethod setup-test :around
((test test-mixin
))
1245 (when (run-setup-p test
)
1247 (setf (slot-value test
'done-setup?
) t
)))
1249 (defun run-setup-p (testsuite)
1250 (case (run-setup testsuite
)
1251 (:once-per-session
(error "not implemented"))
1252 (:once-per-suite
(not (done-setup? testsuite
)))
1253 ((:once-per-test-case t
) t
)
1255 (t (error "Don't know about ~s for run-setup" (run-setup testsuite
)))))
1257 (defun run-teardown-p (testsuite when
)
1260 (ecase (run-setup testsuite
)
1261 (:once-per-session nil
)
1262 (:once-per-suite nil
)
1263 ((:once-per-test-case t
) t
)
1264 ((:never nil
) nil
)))
1266 (ecase (run-setup testsuite
)
1267 (:once-per-session nil
)
1269 ((:once-per-test-case t
) nil
)
1270 ((:never nil
) nil
)))))
1272 (defun current-source-position ()
1274 (or (second comp
::*compile-file-last-form-location
*)
1275 (file-position comp
::*compile-file-stream
*))
1280 (defun test-is-being-redefined-p (test-suite-name test-case-name
)
1281 (let* ((old-source (gethash test-case-name
(test-case-source-file test-suite-name
)))
1282 (old-position (gethash test-case-name
(test-case-source-position test-suite-name
)))
1283 (new-source (namestring *compile-file-pathname
*))
1284 (new-position (current-source-position)))
1287 (or (not (string= old-source new-source
))
1288 (not (= old-position new-position
))))))
1290 (defun build-test-test-method (suite-name test-body options
)
1291 (multiple-value-bind (test-case-name body name-supplied?
)
1292 (parse-test-body test-body
)
1293 (declare (ignorable name-supplied?
))
1294 (unless (consp (first body
))
1295 (setf body
(list body
)))
1296 (setf (def :test-case-name
) test-case-name
)
1299 (when *test-is-being-compiled?
*
1300 (when (test-is-being-redefined-p ',suite-name
',test-case-name
)
1301 (let ((original-source
1302 (gethash ',test-case-name
(test-case-source-file ',suite-name
)))
1303 (new-source (namestring *compile-file-pathname
*))
1305 (gethash ',test-case-name
(test-case-source-position ',suite-name
)))
1306 (new-pos (current-source-position)))
1307 (if (string= original-source new-source
)
1308 ;; we assume that the environment has already printed the file name
1309 (warn "Test ~a/~a is being redefined from position ~d to ~d"
1310 ',suite-name
',test-case-name original-pos new-pos
)
1311 (warn "Test ~a/~a is being redefined from file ~a at position ~
1312 to file ~a position ~d"
1313 ',suite-name
',test-case-name original-source original-pos
1314 new-source new-pos
))
1315 (when *break-on-redefinition
*
1317 (setf (gethash ',test-case-name
(test-name->code-table
',suite-name
)) ',body
1318 (gethash ',body
(test-code->name-table
',suite-name
)) ',test-case-name
)
1320 ,@(when name-supplied?
1321 `((ccl:record-source-file
',test-case-name
'test-case
)))
1322 (unless (find ',test-case-name
(testsuite-tests ',suite-name
))
1323 (setf (testsuite-tests ',suite-name
)
1324 (append (testsuite-tests ',suite-name
) (list ',test-case-name
))))
1325 (setf (gethash ',suite-name
*test-case-options
*) nil
)
1326 (defmethod set-test-case-options
1327 ((suite-name (eql ',suite-name
)) (test-case-name (eql ',test-case-name
)))
1329 (build-test-case-options
1330 suite-name test-case-name options
)))
1331 (setf (gethash ',test-case-name
(test-name->methods
',suite-name
))
1333 (declare (ignorable testsuite
))
1335 `((set-test-case-options ',suite-name
',test-case-name
)))
1336 (with-test-slots ,@body
)))
1337 (setf *last-test-case-name
* ',test-case-name
)
1338 (when (and *test-print-when-defined?
*
1339 (not (or *test-is-being-compiled?
*
1341 (format *debug-io
* "~&;Test Created: ~(~S.~S~)."
1342 ',suite-name
',test-case-name
))
1343 *last-test-case-name
*)))
1345 (defun parse-test-body (test-body)
1346 (let ((test-name nil
)
1348 (test-number (1+ (testsuite-test-count *last-testsuite-name
*)))
1349 (name-supplied? nil
))
1350 (setf test-name
(first test-body
))
1351 (cond ((symbolp test-name
)
1353 (intern (format nil
"~A" test-name
))
1354 body
(rest test-body
)
1356 ((and (test-code->name-table
*last-testsuite-name
*)
1359 (test-code->name-table
*last-testsuite-name
*))))
1360 (setf body test-body
))
1363 (intern (format nil
"TEST-~A"
1366 (values test-name body name-supplied?
)))
1368 (defun build-benchmark-function (suite-name test-case-name body options
)
1369 (let ((duration 2) style
)
1370 (when (getf options
:style
)
1371 (setf style
(getf options
:style
))
1372 (remf options
:style
))
1373 (when (getf options
:duration
2)
1374 (setf duration
(getf options
:duration
2))
1375 (remf options
:duration
))
1378 ,@(when name-supplied?
1379 `((ccl:record-source-file
',test-case-name
'test-case
)))
1380 (unless (find ',test-case-name
(testsuite-tests ',suite-name
))
1381 (setf (testsuite-tests ',suite-name
)
1382 (append (testsuite-tests ',suite-name
) (list ',test-case-name
))))
1383 ;;?? to defer until after compile...?
1385 `((defmethod set-test-case-options
1386 ((suite-name (eql ',suite-name
))
1387 (test-case-name (eql ',test-case-name
)))
1388 ,@(build-test-case-options
1389 suite-name test-case-name options
))))
1390 (setf (gethash ',test-case-name
(test-name->methods
',suite-name
))
1392 (declare (ignorable testsuite
))
1396 (getf (test-data *current-test
*) :benchmark-count
)))
1397 (declare (ignorable benchmark-count
))
1399 `((set-test-case-options ',suite-name
',test-case-name
)))
1402 `((setf benchmark-count
1403 (while-counting-repetitions (,duration
)
1406 `((setf benchmark-count
1407 (while-counting-events (,duration
)
1411 (setf *last-test-case-name
* ',test-case-name
))))
1413 (defun build-test-class ()
1414 ;; for now, we don't generate code from :class-def code-blocks
1415 ;; they are executed only for effect.
1416 (loop for
(nil . block
) in
*code-blocks
*
1419 (eq (operate-when block
) :class-def
)
1420 (or (not (filter block
))
1421 (funcall (filter block
)))) collect
1422 (funcall (code block
)))
1423 (unless (some (lambda (superclass)
1424 (testsuite-p superclass
))
1425 (def :superclasses
))
1426 (pushnew 'test-mixin
(def :superclasses
)))
1427 ;; build basic class and standard class
1428 `(defclass ,(def :testsuite-name
) (,@(def :superclasses
))
1429 ,(loop for name in
(def :direct-slot-names
) collect
1430 (let ((it (find name
(def :slot-specs
) :key
#'car
)))
1433 ,@(when (def :documentation
)
1434 `((:documentation
,(def :documentation
))))
1436 ,@(def :default-initargs
)
1437 ,@(when *load-pathname
*
1438 `(:test-source-file
,(namestring *compile-file-pathname
*))))))
1440 (defun parse-test-slots (slot-specs)
1441 (loop for spec in slot-specs collect
1442 (let ((parsed-spec spec
))
1443 (if (member :initform parsed-spec
)
1444 (let ((pos (position :initform parsed-spec
)))
1445 (append (subseq parsed-spec
0 pos
)
1446 (subseq parsed-spec
(+ pos
2))))
1449 ;; some handy properties
1450 (defclass-property test-slots
)
1451 (defclass-property test-code-
>name-table
)
1452 (defclass-property test-name-
>code-table
)
1453 (defclass-property test-case-documentation
)
1454 (defclass-property testsuite-tests
)
1455 (defclass-property testsuite-dynamic-variables
)
1456 (defclass-property test-name-
>methods
)
1457 (defclass-property test-case-source-file
)
1458 (defclass-property test-case-source-position
)
1460 ;;?? issue 27: break encapsulation of code blocks
1461 (defclass-property testsuite-function-specs
)
1463 (defun empty-test-tables (test-name)
1464 (when (find-class test-name nil
)
1465 (setf (test-code->name-table test-name
)
1466 (make-hash-table :test
#'equal
)
1467 (test-name->code-table test-name
)
1468 (make-hash-table :test
#'equal
)
1469 (test-name->methods test-name
)
1470 (make-hash-table :test
#'eq
)
1471 (test-case-documentation test-name
)
1472 (make-hash-table :test
#'equal
)
1473 (test-case-source-file test-name
)
1474 (make-hash-table :test
#'equal
)
1475 (test-case-source-position test-name
)
1476 (make-hash-table :test
#'equal
))))
1478 (pushnew :timeout
*deftest-clauses
*)
1481 :timeout
1 :class-def
1482 (lambda () (def :timeout
))
1483 '((setf (def :timeout
) (cleanup-parsed-parameter value
)))
1485 (unless (some (lambda (super)
1486 (member (find-class 'process-test-mixin
)
1487 (superclasses super
)))
1488 (def :superclasses
))
1489 (pushnew 'process-test-mixin
(def :superclasses
)))
1490 (push (def :timeout
) (def :default-initargs
))
1491 (push :maximum-time
(def :default-initargs
))
1494 (defmethod do-test :around
((suite test-mixin
) name result
)
1495 (declare (ignore result
))
1497 (with-profile-report ((format nil
"~a-~a"
1498 (testsuite-name suite
) name
)
1501 (call-next-method)))
1503 (defmethod do-test :around
((suite process-test-mixin
) name result
)
1504 (declare (ignore name
))
1505 (handler-bind ((timeout-error
1507 (let ((suite-name (class-name (class-of suite
))))
1508 (report-test-problem
1509 'test-timeout-failure result suite-name
(current-method suite
)
1510 (make-instance 'test-timeout-condition
1511 :maximum-time
(maximum-time suite
))))
1512 (if (find-restart 'test-failed
)
1513 (invoke-restart 'test-failed c
)
1515 (with-timeout ((maximum-time suite
))
1519 (defmethod testsuite-log-data ((suite t
))
1522 (defmethod testsuite-log-data :around
((suite t
))
1523 (multiple-value-bind (additional error?
)
1524 (ignore-errors (call-next-method))
1526 `(:error
"error occured gathering additional data")
1529 (defmethod test-case-teardown :around
((suite log-results-mixin
) result
)
1530 (declare (ignore result
))
1531 (let ((problem (getf (test-data suite
) :problem
)))
1532 (unless (and problem
(typep problem
'test-error-mixin
))
1535 (getf (test-data suite
) :seconds
)
1536 (getf (test-data suite
) :conses
)
1538 `(,@(testsuite-log-data suite
))))))
1540 ;;?? might be "cleaner" with a macrolet (cf. lift-result)
1541 (defun lift-property (name)
1542 (when *current-test
*
1543 (getf (getf (test-data *current-test
*) :properties
) name
)))
1546 (setf (getf (getf (third (first (tests-run *test-result
*))) :properties
) :foo
)
1549 (defun (setf lift-property
) (value name
)
1550 (when *current-test
*
1551 (setf (getf (getf (test-data *current-test
*) :properties
) name
) value
)))
1555 (defmacro with-test
(&body forms
)
1556 "Execute forms in the context of the current test class."
1557 (let* ((testsuite-name *last-testsuite-name
*)
1558 (test-case (make-instance test-class
)))
1559 `(eval-when (:execute
)
1561 (setup-test ,test-case
)
1563 (with-test-slots ,@forms
))
1564 (test-case-teardown ,test-case result
)))))
1566 (defvar *test-case-options
* (make-hash-table))
1568 (defun remove-test-case-options (suite-name)
1569 (remhash suite-name
*test-case-options
*))
1571 (defun test-case-option (suite-name case-name option-name
)
1572 (let* ((suite-options (gethash suite-name
*test-case-options
*))
1573 (case-options (and suite-options
1574 (gethash case-name suite-options
))))
1575 (getf (car case-options
) option-name
)))
1577 (defun (setf test-case-option
) (value suite-name case-name option-name
)
1578 (let ((suite-options (gethash suite-name
*test-case-options
*)))
1579 (unless suite-options
1580 (setf suite-options
(setf (gethash suite-name
*test-case-options
*)
1581 (make-hash-table))))
1582 (multiple-value-bind (case-options found?
)
1583 (gethash case-name suite-options
)
1586 (setf (gethash case-name suite-options
) (cons nil nil
))))
1587 (setf (getf (car case-options
) option-name
) value
))))
1589 (defun build-test-case-options (suite-name case-name options
)
1590 (loop for
(k v
) on options by
#'cddr collect
1599 "Unknown option-name ~s when trying to set a test-case-option for ~a/~a"
1600 k suite-name case-name
)
1601 `(setf (test-case-option ',suite-name
',case-name
,k
) ,v
))))
1604 (test-case-option 'test-dependencies-helper
'test-c
:depends-on
)
1605 (setf (test-case-option 'test-dependencies-helper
'test-c
:depends-on
) :test-c
)
1606 (remove-test-case-options 'test-dependencies-helper
)