Revert "Update README.md"
[lift.git] / dev / lift.lisp
blobd6f7eaa5f131e3d2d8f5b0c964f49fbe546dbf6d
1 ;;;-*- Mode: Lisp; Package: lift -*-
3 (in-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 ;;; ---------------------------------------------------------------------------
41 ;;; test conditions
42 ;;; ---------------------------------------------------------------------------
44 (defcondition (lift-compile-error :exportp nil) (error)
45 (msg)
46 "Compile error: '~S'" msg)
48 (defcondition testsuite-not-defined (lift-compile-error)
49 (testsuite-name)
50 "Test class ~A not defined before it was used."
51 testsuite-name)
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 ""))
70 "~%~A" message)
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)"
75 maximum-time)
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)
82 ((value :initform "")
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
89 :initform nil)
90 (the-condition
91 :initform nil))
92 "Expected ~S but got ~S~@[:~_ ~A~]"
93 expected-condition-type
94 (type-of the-condition)
95 (and (typep the-condition 'condition)
96 the-condition))
98 (defcondition ensure-expected-no-warning-condition (test-condition)
99 ((the-condition
100 :initform nil))
101 "Expected no warnings but got ~S"
102 the-condition)
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)
121 ((total :initform 0)
122 (problems :initform nil)
123 (errors :initform nil))
124 (format
125 stream
126 "Ensure-cases: ~d case~:p with ~[~:;~:*~d error~:p; ~]~[~:;~:*~d failure~:p; ~]"
127 total (length errors) (length problems))
128 (when errors
129 (format stream "~&Errors: ~@< ~@;~{~% ~{~20s ~3,8@t~a~}~^, ~}~:>"
130 errors))
131 (when problems
132 (format stream "~&Failures: ~@< ~@;~{~% ~{~20s ~3,8@t~a~}~^, ~}~:>"
133 problems)))
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)
141 (format nil "~A: ~A"
142 context
143 (apply #'format nil message arguments)))
145 (defun signal-lift-error (context message &rest arguments)
146 (let ((c (make-condition
147 'lift-compile-error
148 :msg (apply #'build-lift-error-message
149 context message arguments))))
150 (unless (signal c)
151 (error c))))
153 (defun report-lift-error (context message &rest arguments)
154 (format *debug-io* "~&~A."
155 (apply #'build-lift-error-message context message arguments))
156 (values))
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
164 :first-value value-1
165 :second-value value-2
166 :test test
167 :message (when report
168 (apply #'format nil
169 report arguments)))))
170 (if (find-restart 'ensure-failed)
171 (invoke-restart 'ensure-failed condition)
172 (warn condition))))
174 (defun maybe-raise-ensure-same-condition (value-1 value-2 test
175 report &rest arguments)
176 (let ((condition (make-condition 'ensure-same
177 :first-value value-1
178 :second-value value-2
179 :test test
180 :message (when report
181 (apply #'format nil
182 report arguments)))))
183 (if (find-restart 'ensure-failed)
184 (invoke-restart 'ensure-failed condition)
185 (warn condition))))
188 ;;; ---------------------------------------------------------------------------
189 ;;; test-mixin
190 ;;; ---------------------------------------------------------------------------
192 (defmethod testsuite-setup ((testsuite test-mixin) (result test-result))
193 (values))
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))
200 nil)
202 (defmethod testsuite-expects-failure ((testsuite test-mixin))
203 nil)
205 (defmethod testsuite-teardown ((testsuite test-mixin) (result test-result))
206 ;; no-op
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)))
215 ;;;;
217 (defun canonize-skip-tests (skip-tests)
218 (mapcar
219 (lambda (datum)
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."
233 datum))))
234 skip-tests))
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))
246 #'equal)
248 (defmethod setup-test :before ((test test-mixin))
249 (setf *test-scratchpad* nil))
251 (defmethod setup-test ((test test-mixin))
252 (values))
254 (defmethod setup-test ((test symbol))
255 (let ((*current-test* (make-testsuite test nil)))
256 (setup-test *current-test*)
257 *current-test*))
259 (defmethod test-case-teardown progn ((test test-mixin) (result test-result))
260 (values))
262 (defmethod test-case-teardown :around ((test test-mixin) (result test-result))
263 (setf (current-step result) :test-teardown)
264 (call-next-method))
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 ;;; ---------------------------------------------------------------------------
278 ;;; macros
279 ;;; ---------------------------------------------------------------------------
281 (defun initialize-current-definition ()
282 (setf *current-definition* nil))
284 (defun set-definition (name value)
285 (let ((current (assoc name *current-definition*)))
286 (if current
287 (setf (cdr current) value)
288 (push (cons name value) *current-definition*)))
290 (values value))
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
299 :block-name name
300 :priority priority
301 :filter filter
302 :code code)))
303 (if current
304 (setf (cdr current) value)
305 (push (cons name value) *code-blocks*))
306 (eval
307 `(defmethod block-handler ((name (eql ',name)) value)
308 (declare (ignorable value))
309 ,@handler)))
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
315 clauses-and-options)
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)
323 (add-code-block
324 :setup 1 :methods
325 (lambda () t)
326 '((setf (def :setup) (cleanup-parsed-parameter value)))
327 'build-setup-test-method)
329 (add-code-block
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)
335 (add-code-block
336 :function 0 :methods
337 (lambda () (def :functions))
338 '((push value (def :functions)))
339 'build-test-local-functions)
341 (add-code-block
342 :documentation 0 :class-def
344 '((setf (def :documentation) (first value)))
345 nil)
347 (add-code-block
348 :export-p 0 :class-def
350 '((setf (def :export-p) (first value)))
351 nil)
353 (add-code-block
354 :export-slots 0 :class-def
356 '((setf (def :export-slots) (first value)))
357 nil)
359 (add-code-block
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)))))
377 (add-code-block
378 :equality-test 0 :methods
379 (lambda () (def :equality-test))
380 '((setf (def :equality-test) (cleanup-parsed-parameter value)))
381 'build-test-equality-test)
383 (add-code-block
384 :expected-error 0 :methods
385 (lambda () (def :expected-error))
386 '((setf (def :expected-error) (cleanup-parsed-parameter value)))
387 'build-testsuite-expected-error)
389 (add-code-block
390 :expected-failure 0 :methods
391 (lambda () (def :expected-failure))
392 '((setf (def :expected-failure) (cleanup-parsed-parameter value)))
393 'build-testsuite-expected-failure)
395 (add-code-block
396 :log-file 0 :class-def
398 '((push (first value) (def :default-initargs))
399 (push :log-file (def :default-initargs)))
400 nil)
402 (add-code-block
403 :dynamic-variables 0 :class-def
405 '((setf (def :direct-dynamic-variables) value))
406 nil)
408 (add-code-block
409 :categories 0 :class-def
411 '((push value (def :categories)))
412 nil)
414 (add-code-block
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))))
419 nil)
421 (defmacro deftestsuite (testsuite-name superclasses slots &rest
422 clauses-and-options)
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 ()
434 ((my-slot 23)))
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)
457 * :once-per-session
458 * :once-per-suite
459 * :never or nil
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.
471 #+no-lift-tests
472 `(values)
473 #-no-lift-tests
474 (let ((test-list nil)
475 (options nil)
476 (return (gensym)))
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))
483 superclasses))
484 (setf (def :deftestsuite) t)
485 ;; parse clauses into defs
486 (loop for clause in clauses-and-options do
487 (typecase clause
488 (symbol (pushnew clause options))
489 (cons (destructuring-bind (kind &rest spec) clause
490 (case kind
491 (:test (push (first spec) test-list))
492 (:tests
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))
509 `(,name ,arglist))))
510 ;;?? needed
511 (empty-test-tables testsuite-name)
512 (compute-superclass-inheritence)
513 (prog2
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)))
526 ,(build-test-class)
527 (unwind-protect
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))
532 ',(def :slot-names)
533 (testsuite-dynamic-variables ',(def :testsuite-name))
534 ',(def :dynamic-variables)
535 ;;?? issue 27: breaks 'encapsulation' of code-block
536 ;; mechanism
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))
545 ;; create methods
546 ;; setup :before
547 ,@(loop for (nil . block) in *code-blocks*
548 when (and block
549 (code block)
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))
558 (with-test-slots
559 (cond ((done-dynamics? suite)
560 (call-next-method))
562 (setf (slot-value suite 'done-dynamics?) t)
563 (let* (,@(def :dynamic-variables))
564 (declare (special
565 ,@(mapcar
566 #'car (def :dynamic-variables))))
567 (call-next-method))))))))
568 ;; tests
569 ,@(when test-list
570 `((let ((*test-evaluate-when-defined?* nil))
571 ,@(loop for test in (nreverse test-list) collect
572 `(addtest (,(def :testsuite-name))
573 ,@test))
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)))
581 ;; cleanup
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
593 (let ((slots nil)
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))
603 function-specs
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)
615 (remove-duplicates
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
623 class))))
625 (defun %build-pairs (putative-pairs)
626 (let ((result nil))
627 (dolist (putative-pair putative-pairs)
628 (if (atom putative-pair)
629 (push (list putative-pair nil) result)
630 (push putative-pair result)))
631 (nreverse result)))
633 (defmacro addtest (name &body test)
634 "Adds a single new test-case to the most recently defined testsuite."
635 #+no-lift-tests
636 `nil
637 #-no-lift-tests
638 (let ((body nil)
639 (return (gensym))
640 (options nil) (documentation nil)
641 (looks-like-suite-name (looks-like-suite-name-p name)))
642 (cond (looks-like-suite-name
643 ;; testsuite given
644 (setf (def :testsuite-name) (first name)
645 options (rest name)
646 name nil body test))
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?*))
671 (unwind-protect
672 (let ((*test-is-being-defined?* t))
673 (muffle-redefinition-warnings
674 ,(build-test-test-method (def :testsuite-name) body options))
675 ,@(when documentation
676 `((setf (gethash
677 ',(def :test-case-name)
678 (test-case-documentation ',(def :testsuite-name)))
679 ,documentation)))
680 ,@(when *compile-file-pathname*
681 `((setf (gethash
682 ',(def :test-case-name)
683 (test-case-source-file ',(def :testsuite-name)))
684 ,(namestring *compile-file-pathname*))
685 #+allegro
686 (setf (gethash
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)))
695 (run-test)))
696 (values)))
697 ;; cleanup
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."
707 #+no-lift-tests
708 `nil
709 #-no-lift-tests
710 (let ((documentation nil))
711 (unless (property-list-p options)
712 (signal-lift-error
713 'addbenchmark
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))
718 (unless suite-name
719 (signal-lift-error 'addbenchmark +lift-no-current-test-class+))
720 (unless (find-testsuite suite-name)
721 (signal-lift-error
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
731 `((setf (gethash
732 ',(def :test-case-name)
733 (test-case-documentation ',(def :testsuite-name)))
734 ,documentation)))
735 (setf *last-testsuite-name* ',(def :testsuite-name))
736 ',(def :test-case-name)))))
738 (defun looks-like-suite-name-p (form)
739 (and (consp form)
740 (atom (first form))
741 (find-testsuite (first form))))
743 (defun property-list-p (form)
744 (and (listp form)
745 (block check-it
746 (let ((even? t))
747 (loop for x in 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)
759 (property-list-p 3)
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))
774 result)
775 (if testsuite
776 (setf result (apply #'make-instance testsuite args))
777 (error "Testsuite ~a not found." suite-name))
778 result))
780 (defun skip-test-case-p (result suite-name test-case-name)
781 (declare (ignore result))
782 (find-if (lambda (skip-datum)
783 (if (cdr 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))))
787 *skip-tests*))
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))))
794 *skip-tests*))
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))
820 (condition nil))
821 (cond
822 (expected-failure-p
823 (setf condition
824 (make-condition 'unexpected-success-failure
825 :expected :failure
826 :expected-more expected-failure-p)))
827 (expected-error-p
828 (setf condition
829 (make-condition 'unexpected-success-failure
830 :expected :error
831 :expected-more expected-error-p)))
832 (expected-problem-p
833 (setf condition
834 (make-condition 'unexpected-success-failure
835 :expected :problem
836 :expected-more expected-problem-p))))
837 (when condition
838 (if (find-restart 'ensure-failed)
839 (invoke-restart 'ensure-failed condition)
840 (warn 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
851 &rest args)
852 ;; ick
853 (let ((docs nil)
854 (option nil))
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
875 :test-method method
876 :test-condition condition
877 :test-step (current-step result)
878 :testsuite-initargs (testsuite-initargs result)
879 args)))
880 (when *current-test*
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))
891 problem)))
893 (defun cancel-testing (why)
894 (declare (ignore why))
895 (flet ((do-it (name)
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
915 (test-data suite)
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
928 :results-for for
929 :test-mode test-mode
930 args))
932 (defun testing-interactively-p ()
933 (values nil))
935 (defmethod print-object ((tr test-result) stream)
936 (let ((complete-success? (and (null (errors tr))
937 (null (failures 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
943 (count-if
944 (lambda (failure)
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)
955 ;; interactive
956 (cond (complete-success?
957 (format stream "Test passed"))
958 ((errors tr)
959 (format stream "Error during testing"))
960 ((expected-errors tr)
961 (format stream "Expected error during testing"))
962 ((failures tr)
963 (format stream "Test failed"))
964 ((plusp non-failure-failures)
965 (format stream "Test succeeded unexpectedly"))
967 (format stream "Test failed expectedly"))))
969 ;; from run-test
970 (format stream "~A.~A ~A"
971 (results-for tr)
972 (second (first (tests-run tr)))
973 (cond (complete-success?
974 "passed")
975 ((errors tr)
976 "Error")
978 "failed")))
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))
992 (length (errors 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?*)
998 (format stream "~%")
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
1005 &key
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
1013 (count-if
1014 (lambda (failure)
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
1039 (count-if
1040 (lambda (failure)
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~]."
1051 number-of-errors
1052 number-of-failures
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
1066 show-code-p))
1067 (loop for report in (failures result) do
1068 (print-test-problem "Failure:" report stream
1069 show-code-p))
1070 (when show-expected-p
1071 (loop for report in (expected-failures result) do
1072 (print-test-problem "Expected failure:" report stream
1073 show-code-p))
1074 (loop for report in (expected-errors result) do
1075 (print-test-problem "Expected Error :" report stream
1076 show-code-p))))
1078 (defmethod print-test-problem (prefix (report testsuite-problem-mixin) stream show-code-p)
1079 (handler-case
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)
1094 (if show-code-p
1095 (setf code (with-output-to-string (out)
1096 (pprint code out)))
1097 (setf code nil))
1098 (format stream "~&~< ~@;~
1099 ~@[Documentation: ~<~@;~a~:>~]~
1100 ~@[~&Source : ~<~@;~a~:>~]~
1101 ~@[~&Condition : ~<~@;~a~:>~]~
1102 ~@[~&During : ~a~]~
1103 ~@[~&Code : ~a~]~
1104 ~&~:>" `((,doc-string) (,source-file) (,condition) (,step) (,code)))))
1105 (error (c)
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 ;;; ---------------------------------------------------------------------------
1114 ;;; test-reports
1115 ;;; ---------------------------------------------------------------------------
1117 (defun test-report-code (suite-name test-case-name)
1118 (gethash test-case-name (test-name->code-table suite-name)))
1120 ;;; ---------------------------------------------------------------------------
1121 ;;; utilities
1122 ;;; ---------------------------------------------------------------------------
1124 (defun remove-test-methods (test-name)
1125 (prog1
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))
1133 (removed-count 0))
1134 (when class
1135 (loop for subclass in (subclasses class :proper? nil) do
1136 (push subclass classes-removed)
1137 (incf removed-count
1138 (remove-test-methods (class-name subclass)))
1139 #+Ignore
1140 ;;?? causing more trouble than it solves...??
1141 (setf (find-class (class-name subclass)) nil))
1143 (unless (length-1-list-p classes-removed)
1144 (format *debug-io*
1145 "~&;;; Removed Test suite ~(~A~) and its subclasses (~{~<~s~>~^, ~})."
1146 classname (sort
1147 (delete classname
1148 (mapcar #'class-name classes-removed))
1149 #'string-lessp)))
1150 (unless (zerop removed-count)
1151 (format *debug-io*
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 ()
1163 `(progn
1164 ,@(mapcar
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))
1169 &rest args)
1170 (with-test-slots
1171 ,(if arglist
1172 `(destructuring-bind ,arglist args
1173 ,@body)
1174 `(progn ,@body))))))
1175 (def :functions))))
1177 (defun build-test-equality-test ()
1178 (let ((test-name (def :testsuite-name))
1179 (equality-test (def :equality-test)))
1180 `(progn
1181 (defmethod equality-test ((testsuite ,test-name))
1182 ,equality-test))))
1184 (defun build-testsuite-expected-error ()
1185 (let ((test-name (def :testsuite-name))
1186 (expected-error (def :expected-error)))
1187 `(progn
1188 (defmethod testsuite-expects-error ((testsuite ,test-name))
1189 (with-test-slots
1190 ,expected-error)))))
1192 (defun build-testsuite-expected-failure ()
1193 (let ((test-name (def :testsuite-name))
1194 (expected-failure (def :expected-failure)))
1195 `(progn
1196 (defmethod testsuite-expects-failure ((testsuite ,test-name))
1197 (with-test-slots
1198 ,expected-failure)))))
1200 (defun build-test-teardown-method ()
1201 (let ((test-name (def :testsuite-name))
1202 (teardown (def :teardown)))
1203 (when 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)))
1213 `(progn
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)
1218 ,@test-code))))
1219 ,@(when teardown-code
1220 `((defmethod testsuite-teardown ((testsuite ,test-name)
1221 (result test-result))
1222 (when (run-teardown-p testsuite :testsuite)
1223 ,@test-code))))))))
1225 (defun build-setup-test-method ()
1226 (let ((test-name (def :testsuite-name))
1227 (setup (def :setup)))
1228 ;;?? ewww, this smells bad
1229 (when setup
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))))
1236 (if setup
1237 `(defmethod setup-test :after ((testsuite ,test-name))
1238 (with-test-slots
1239 ,@setup))
1240 ;; rather use remove-method
1241 `(defmethod setup-test :after ((testsuite ,test-name))
1242 ))))
1244 (defmethod setup-test :around ((test test-mixin))
1245 (when (run-setup-p test)
1246 (call-next-method)
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)
1254 ((:never nil) nil)
1255 (t (error "Don't know about ~s for run-setup" (run-setup testsuite)))))
1257 (defun run-teardown-p (testsuite when)
1258 (ecase when
1259 (:test-case
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)))
1265 (:testsuite
1266 (ecase (run-setup testsuite)
1267 (:once-per-session nil)
1268 (:once-per-suite t)
1269 ((:once-per-test-case t) nil)
1270 ((:never nil) nil)))))
1272 (defun current-source-position ()
1273 #+allegro
1274 (or (second comp::*compile-file-last-form-location*)
1275 (file-position comp::*compile-file-stream*))
1276 #-allegro
1279 #+allegro
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)))
1285 (and old-source
1286 old-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)
1297 `(progn
1298 #+allegro
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*))
1304 (original-pos
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*
1316 (break)))))
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)
1319 #+(or mcl ccl)
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)))
1328 ,@(when options
1329 (build-test-case-options
1330 suite-name test-case-name options)))
1331 (setf (gethash ',test-case-name (test-name->methods ',suite-name))
1332 (lambda (testsuite)
1333 (declare (ignorable testsuite))
1334 ,@(when options
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)
1347 (body 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)
1352 (setf test-name
1353 (intern (format nil "~A" test-name))
1354 body (rest test-body)
1355 name-supplied? t))
1356 ((and (test-code->name-table *last-testsuite-name*)
1357 (setf test-name
1358 (gethash test-body
1359 (test-code->name-table *last-testsuite-name*))))
1360 (setf body test-body))
1362 (setf test-name
1363 (intern (format nil "TEST-~A"
1364 test-number))
1365 body test-body)))
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))
1376 `(progn
1377 #+(or mcl ccl)
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...?
1384 ,@(when options
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))
1391 (lambda (testsuite)
1392 (declare (ignorable testsuite))
1393 (with-test-slots
1394 (symbol-macrolet
1395 ((benchmark-count
1396 (getf (test-data *current-test*) :benchmark-count)))
1397 (declare (ignorable benchmark-count))
1398 ,@(when options
1399 `((set-test-case-options ',suite-name ',test-case-name)))
1400 ,@(ecase style
1401 (:repetition
1402 `((setf benchmark-count
1403 (while-counting-repetitions (,duration)
1404 ,@body))))
1405 (:events
1406 `((setf benchmark-count
1407 (while-counting-events (,duration)
1408 ,@body))))
1409 ((nil)
1410 `,body))))))
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*
1417 when (and block
1418 (code block)
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)))
1431 (assert it)
1432 it))
1433 ,@(when (def :documentation)
1434 `((:documentation ,(def :documentation))))
1435 (:default-initargs
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))))
1447 parsed-spec))))
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*)
1480 (add-code-block
1481 :timeout 1 :class-def
1482 (lambda () (def :timeout))
1483 '((setf (def :timeout) (cleanup-parsed-parameter value)))
1484 (lambda ()
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))
1492 nil))
1494 (defmethod do-test :around ((suite test-mixin) name result)
1495 (declare (ignore result))
1496 (if (profile suite)
1497 (with-profile-report ((format nil "~a-~a"
1498 (testsuite-name suite) name)
1499 (profile suite))
1500 (call-next-method))
1501 (call-next-method)))
1503 (defmethod do-test :around ((suite process-test-mixin) name result)
1504 (declare (ignore name))
1505 (handler-bind ((timeout-error
1506 (lambda (c)
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)
1514 (error c)))))
1515 (with-timeout ((maximum-time suite))
1516 (call-next-method))
1519 (defmethod testsuite-log-data ((suite t))
1520 nil)
1522 (defmethod testsuite-log-data :around ((suite t))
1523 (multiple-value-bind (additional error?)
1524 (ignore-errors (call-next-method))
1525 (if error?
1526 `(:error "error occured gathering additional data")
1527 additional)))
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))
1533 (generate-log-entry
1535 (getf (test-data suite) :seconds)
1536 (getf (test-data suite) :conses)
1537 :additional-data
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)))
1545 #+(or)
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)))
1554 #+Later
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)
1560 (prog2
1561 (setup-test ,test-case)
1562 (progn
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)
1584 (unless found?
1585 (setf case-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
1591 (progn
1592 (assert (member k
1593 '(:expected-error
1594 :expected-failure
1595 :expected-problem
1596 :depends-on
1597 :documentation))
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)