4 (:report-property
:if-exists
:supersede
)
5 (:report-property
:unique-name nil
)
6 (:report-property
:format
:html
)
7 (:report-property
:name
"index")
8 (:report-property
:relative-to db.agraph.tests
)
10 For text based reports like
:describe
, the report name is the filename
11 where the report is placed or a stream
(e.g.
, *standard-output
*).
13 The
:name property specifies the name and type.
15 There are three ways to specify the directory
:
19 3. the current directory
(via *default-pathname-defaults
*)
21 If
:full-name is a pathname with a name and type
, then these will be
22 used rather than
:name. If
:unique-name is true
(and the destination
23 is not a stream
), then the date and an integer tag will be added to the
24 name. E.g.
, the path
`/tmp
/lift-tests
/report.txt
` will become
25 `/tmp
/lift-tests
/report-2009-02-01-1.txt
`.
28 For HTML
, The report name specifies a _directory_. The
:name property
31 There are three ways to specify the directory location.
35 3. the current directory
(via *default-pathname-defaults
*)
37 In all cases
, the report will go into
41 (defun show-test-warning (message &rest args
)
42 (apply #'format
*error-output
* message args
))
44 (defgeneric generate-report-summary-pathname
()
47 (defgeneric handle-config-preference
(name args
)
50 (defvar *current-configuration-stream
* nil
)
52 (defvar *current-asdf-system-name
* nil
53 "Holds the name of the system being tested when using the `:generic`
56 LIFT needs this to run the `:generic` configuration because this is
57 how it determines which configuration file to load. If you use
58 `asdf:test-op` then this value will be set automatically.
59 Otherwise, you will need to set it yourself.")
61 (eval-when (:load-toplevel
:execute
)
62 (when (find-package :asdf
)
63 (defmethod asdf:perform
:around
((operation asdf
:test-op
) (c asdf
:system
))
64 (let ((*current-asdf-system-name
* (asdf:component-name c
)))
65 (call-next-method)))))
67 (defun lift-relative-pathname (pathname &optional
(errorp nil
))
68 "Merges pathname with either the path to the currently loading system
69 \(if there is one\) or the *default-pathname-defaults*."
70 (let* ((asdf-package (find-package :asdf
))
71 (srp-symbol (and asdf-package
72 (find-symbol (symbol-name 'system-relative-pathname
)
74 (srp (and *current-asdf-system-name
* srp-symbol
)))
75 (labels ((try-it (path)
76 (let ((pathname (merge-pathnames pathname path
)))
77 (if errorp
(and pathname
(probe-file pathname
)) pathname
))))
78 (or (and srp
(try-it (funcall srp
*current-asdf-system-name
* "")))
79 (try-it *default-pathname-defaults
*)
81 (and (not asdf-package
)
82 (error "Unable to use :generic configuration option because ASDF is not loaded."))
84 (error "Unable to use :generic configuration option because asdf:system-relative-pathname is not function bound (maybe try updating ASDF?)"))
85 (and (not *current-asdf-system-name
*)
86 (error "Unable to use :generic configuration option
87 because the current system cannot be determined. You can either
88 use asdf:test-op or bind *current-asdf-system-name* yourself."))))))
90 (defun find-generic-test-configuration (&optional
(errorp nil
))
92 (and path
(probe-file path
))))
93 (or (try-it (lift-relative-pathname "lift-local.config" errorp
))
94 (try-it (lift-relative-pathname "lift-standard.config" errorp
))
96 (error "Unable to use :generic configuration file neither lift-local.config nor lift-standard.config can be found.")))))
98 (defun report-summary-pathname ()
99 (unique-filename (generate-report-summary-pathname)))
101 (defmethod generate-report-summary-pathname ()
102 (lift-relative-pathname "test-results/summary.sav"))
105 (generate-report-summary-pathname)
107 (defun run-tests-from-file (path)
108 (let ((real-path (cond ((eq path
:generic
)
110 (find-generic-test-configuration t
)))
112 (probe-file path
)))))
114 (error "Unable to find configuration file ~s" path
))
116 (let* ((*package
* *package
*)
118 (result (make-test-result path
:multiple
))
119 (*lift-dribble-pathname
* nil
)
120 (*lift-debug-output
* *debug-io
*)
121 (*lift-standard-output
* *standard-output
*)
122 (*test-break-on-errors?
* nil
)
123 (*test-run-subsuites?
* t
)
124 (*lift-equality-test
* 'equal
)
125 (*test-print-length
* :follow-print
)
126 (*test-print-level
* :follow-print
)
127 (*lift-if-dribble-exists
* :append
)
128 (*test-result
* result
))
129 (%run-tests-from-file path
)))))
131 (defun %run-tests-from-file
(path)
132 (with-open-file (*current-configuration-stream
* path
134 :if-does-not-exist
:error
)
137 (loop while
(not (eq (setf form
(read *current-configuration-stream
*
138 nil
:eof nil
)) :eof
))
141 ((error (lambda (c) (format
143 "Error while running ~a from ~a: ~a"
145 (pprint (get-backtrace c
))
146 (invoke-debugger c
))))
147 (format t
"~&handle config: ~s" form
)
151 (assert (typep name
'symbol
) nil
152 "Each command must be a symbol and ~s is not." name
)
153 (setf args
(massage-arguments args
))
155 ;; check for preferences first (i.e., keywords)
156 ((eq (symbol-package name
)
157 (symbol-package :keyword
))
158 ;; must be a preference
159 (handle-config-preference name args
))
160 ((and run-tests-p
(find-testsuite name
:errorp nil
))
161 (multiple-value-bind (_ restartedp
)
163 (if (find-testsuite name
:errorp nil
)
164 (run-tests :suite name
165 :result
*test-result
*
166 :testsuite-initargs args
)
168 "~&Warning: testsuite ~s not found, skipping" name
))
169 (cancel-testing-from-configuration (result)
170 :report
(lambda (stream)
171 (format stream
"Cancel testing from file ~a"
173 (declare (ignore result
))
176 (with-simple-restart (cancel-testing-from-configuration
177 "Cancel testing from file ~a" path
)
178 (if (find-testsuite name
:errorp nil
)
179 (run-tests :suite name
180 :result
*test-result
*
181 :testsuite-initargs args
)
183 "~&Warning: testsuite ~s not found, skipping" name
)))
185 ;; no more testing; continue to process commands
187 (setf run-tests-p nil
))))
190 "Don't understand '~s' while reading from ~s"
192 (values *test-result
*))
194 (defun massage-arguments (args)
195 (loop for arg in args collect
196 (cond ((and (symbolp arg
)
197 (string= (symbol-name arg
) (symbol-name '*standard-output
*)))
201 (defmethod handle-config-preference ((name t
) args
)
202 (show-test-warning "Unknown preference ~s (with arguments ~s)"
205 (defmethod handle-config-preference ((name (eql :include
)) args
)
206 (%run-tests-from-file
(merge-pathnames (ensure-string (first args
))
207 *current-configuration-stream
*)))
209 (defconfig-variable :dribble
*lift-dribble-pathname
*)
211 (defconfig-variable :debug-output
*lift-debug-output
*)
213 (defconfig-variable :standard-output
*lift-standard-output
*)
215 (defconfig-variable :break-on-errors?
*test-break-on-errors?
*)
217 (defconfig-variable :do-children?
*test-run-subsuites?
*)
219 (defconfig-variable :equality-test
*lift-equality-test
*)
221 (defconfig-variable :print-length
*test-print-length
*)
223 (defconfig-variable :print-level
*test-print-level
*)
225 (defconfig-variable :print-suite-names
*test-print-testsuite-names
*)
227 (defconfig-variable :print-test-case-names
*test-print-test-case-names
*)
229 (defconfig-variable :if-dribble-exists
*lift-if-dribble-exists
*)
231 (defmethod handle-config-preference ((name (eql :report-property
))
233 (setf (test-result-property *test-result
* (first args
)) (second args
)))
235 (defconfig-variable :profiling-threshold
*profiling-threshold
*)
237 (defconfig-variable :count-calls-p
*count-calls-p
*)
239 (defconfig-variable :log-pathname
*lift-report-pathname
*)
241 (defconfig-variable :maximum-failures
*test-maximum-failure-count
*)
242 (defconfig-variable :maximum-failure-count
*test-maximum-failure-count
*)
244 (defconfig-variable :maximum-errors
*test-maximum-error-count
*)
245 (defconfig-variable :maximum-error-count
*test-maximum-error-count
*)
247 (defgeneric report-pathname
(method &optional result
))
249 (defmethod report-pathname :around
((method (eql :html
))
250 &optional
(result *test-result
*))
251 (cond ((and (test-result-property result
:full-pathname
)
252 (streamp (test-result-property result
:full-pathname
)))
255 (let ((old-name (test-result-property result
:name
))
256 (old-full-pathname (test-result-property result
:full-pathname
))
257 (old-unique-name (test-result-property result
:unique-name
)))
260 (setf (test-result-property result
:name
) t
261 (test-result-property result
:unique-name
) nil
)
262 (let ((destination (pathname-sans-name+type
(call-next-method))))
266 (make-pathname :directory
`(:relative
,old-name
))
270 (make-pathname :name
"index" :type
"html")
271 (pathname-sans-name+type
273 (unique-directory destination
)
275 (setf (test-result-property result
:name
) old-name
276 (test-result-property result
:full-pathname
)
278 (test-result-property result
:unique-name
)
279 old-unique-name
))))))
282 (defmethod report-pathname :around
((method t
) &optional
(result *test-result
*))
283 "Make sure that directories exist"
284 (let ((output (call-next-method)))
285 (cond ((streamp output
)
288 (ensure-directories-exist output
)
291 (defmethod report-pathname ((method t
) &optional
(result *test-result
*))
292 (let* ((given-report-name (test-result-property result
:name
))
293 (report-type (string-downcase
295 (test-result-property result
:format
))))
296 (report-name (or (and given-report-name
297 (not (eq given-report-name t
))
300 (make-pathname :type report-type
)))
301 (format nil
"report.~a" report-type
)))
303 (dest (or (and (setf via
:full-pathname
)
304 (test-result-property result
:full-pathname
)
306 (test-result-property result
:full-pathname
))
307 (test-result-property result
:full-pathname
))
308 (and (setf via
:full-pathname
)
309 (test-result-property result
:full-pathname
)
311 (test-result-property result
:full-pathname
)))
312 (cond ((eq given-report-name t
)
313 (test-result-property result
:full-pathname
))
314 ((null given-report-name
)
316 (test-result-property result
:full-pathname
)
320 (test-result-property result
:full-pathname
)
321 given-report-name
))))
322 (and (setf via
:relative-to
)
324 (test-result-property result
:relative-to
)))
326 (asdf:find-system relative-to nil
)
327 (asdf:system-relative-pathname
328 relative-to report-name
))))
329 (and (setf via
:current-directory
)
331 (make-pathname :defaults report-name
)))))
332 (unique-name?
(test-result-property result
:unique-name
)))
334 (if (and unique-name?
(not (streamp dest
)))
335 (unique-filename dest
)
339 (defmethod handle-config-preference ((name (eql :build-report
))
341 (declare (ignore args
))
342 (let* ((format (or (test-result-property *test-result
* :format
)
344 (dest (report-pathname format
*test-result
*)))
345 (with-standard-io-syntax
346 (let ((*print-readably
* nil
))
351 "Error ~a while generating report (format ~s) to ~a"
354 "~%~%Backtrace~%~%~s"
355 (get-backtrace c
)))))
358 (ensure-directories-exist dest
)
359 (writable-directory-p dest
))
360 (format *debug-io
* "~&Sending report (format ~s) to ~a"
363 *test-result
* dest format
))
365 (format *debug-io
* "~&Unable to write report (format ~s) to ~a"
370 "Start tracing each of the arguments to :trace."
371 (eval `(trace ,@args
)))
374 (eval `(untrace ,@args
)))
376 (defconfig :skip-tests-reset
377 (setf *skip-tests
* nil
))
379 (defconfig :skip-testsuites
380 (loop for arg in args do
381 (if (find-testsuite arg
)
382 (push arg
*skip-tests
*)
383 (show-test-warning "Unable to find testsuite ~a to skip" arg
))))
385 (defconfig :skip-tests
386 (loop for arg in args do
387 (let ((suite (if (consp arg
) (first arg
) arg
))
388 (test-case (if (consp arg
) (second arg
) nil
)))
389 (cond ((not (or (atom arg
)
390 (= (length arg
) 1) (= (length arg
) 2)))
392 ":skip-tests takes atoms or two element lists as arguments. Ignoring ~a in ~a"
394 ((and (null test-case
) (null (find-testsuite suite
)))
396 "Unable to find testsuite ~a to skip" suite
))
397 ((and test-case
(null (find-test-case suite test-case
)))
399 "Unable to find test-case ~a in testsuite ~a to skip"
402 (push (list suite test-case
) *skip-tests
*))))))