1 ;;;; side-effect-free tests of the condition system
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
14 ;;; Until 0.7.7.21, (MAKE-CONDITION 'FILE-ERROR :PATHNAME "FOO")
15 ;;; wasn't printable, because the REPORT function for FILE-ERROR
16 ;;; referred to unbound slots. This was reported and fixed by Antonio
17 ;;; Martinez (sbcl-devel 2002-09-10).
19 "~&printable now: ~A~%"
20 (make-condition 'file-error
:pathname
"foo"))
24 (macrolet ((opaque-error (arg) `(error ,arg
)))
27 (let ((restarts (remove 'res
(compute-restarts c
)
30 (assert (= (length restarts
) 2))
31 (invoke-restart (second restarts
))))))
32 (let ((foo1 (make-condition 'error
))
33 (foo2 (make-condition 'error
)))
35 (with-condition-restarts foo1
(list (find-restart 'res
))
45 (macrolet ((opaque-error (arg) `(error ,arg
)))
46 (let ((foo1 (make-condition 'error
))
47 (foo2 (make-condition 'error
)))
51 (let ((restarts (remove 'res
(compute-restarts foo1
)
54 (assert (= (length restarts
) 1))
55 (invoke-restart (first restarts
))))))
57 (with-condition-restarts foo1
(list (find-restart 'res
))
69 (c1 (make-condition 'error
))
70 (c2 (make-condition 'error
)))
75 (flet ((check-restarts (length)
77 (length (remove 'foo
(compute-restarts c1
)
83 (invoke-restart (find-restart 'foo c1
))))))
88 (foo () :test
(lambda (c) (declare (ignore c
)) visible
)
92 ;;; First argument of CERROR is a format control
96 ((type-error (lambda (c)
99 (simple-error (lambda (c)
101 (return (if (find-restart 'continue
)
104 (cerror (formatter "Continue from ~A") "bug ~A" :bug
)))
107 (with-test (:name
:disallow-bogus-coerce-to-condition
)
108 ;; COERCE-TO-CONDITION has an ftype which precludes passing junk
109 ;; if caught at compile-time.
110 ;; A non-constant non-condition-designator was able to sneak through.
111 (multiple-value-bind (c err
)
112 (ignore-errors (sb-kernel::coerce-to-condition
113 (opaque-identity #p
"foo")
116 (assert (search "does not designate a condition"
117 (write-to-string err
:escape nil
)))))
119 (with-test (:name
(handler-bind :smoke
))
121 (flet ((handler (condition)
122 (declare (ignore condition
))
124 (macrolet ((test (handler)
127 (handler-bind ((condition ,handler
))
130 ;; Test optimized special cases.
131 (test (lambda (condition) (handler condition
)))
132 (test #'(lambda (condition) (handler condition
)))
133 ;; Test default behavior.
134 ;; (test 'handler) would require function definition => not pure
137 (with-test (:name
(handler-bind :malformed-bindings
))
138 (flet ((test (binding)
141 (macroexpand `(handler-bind (,binding
)))
143 (assert (equal (list binding
)
144 (simple-condition-format-arguments e
)))
147 (test 1) ; not even a list
148 (test '()) ; missing condition type and handler
149 (test '(error)) ; missing handler
150 (test '(error #'print
:foo
)) ; too many elements
153 ;;; clauses in HANDLER-CASE are allowed to have declarations (and
154 ;;; indeed, only declarations)
155 (with-test (:name
(handler-case declare
))
156 (assert (null (handler-case (error "foo")
158 (declare (optimize speed
)))))))
160 (with-test (:name
(signal warning muffle-warning control-error
))
162 (handler-bind ((warning #'muffle-warning
))
164 ;; if it's a control error, it had better be printable
165 (control-error (c) (format nil
"~A" c
))
166 ;; there had better be an error
167 (:no-error
(&rest args
) (error "No error: ~S" args
))))
169 (with-test (:name
(check-type type-error
))
171 (funcall (lambda (x) (check-type x fixnum
) x
) t
)
173 (assert (and (subtypep (type-error-expected-type c
) 'fixnum
)
174 (subtypep 'fixnum
(type-error-expected-type c
))))
175 (assert (eq (type-error-datum c
) t
)))
176 (:no-error
(&rest rest
) (error "no error: ~S" rest
))))
178 ;;; ANSI specifies TYPE-ERROR if datum and arguments of ERROR are not
179 ;;; designators for a condition. Reported by Bruno Haible on cmucl-imp
181 (with-test (:name
(error :invalid-arguments type-error
))
182 (flet ((test (&rest args
)
183 (multiple-value-bind (res err
)
184 (ignore-errors (apply #'error args
))
186 (assert (typep err
'type-error
))
187 (assert-no-signal (type-error-datum err
))
188 (assert-no-signal (type-error-expected-type err
)))))
189 (test '#:no-such-condition
)
193 (test (make-instance 'standard-object
))))
195 ;;; If CERROR is given a condition, any remaining arguments are only
196 ;;; used for the continue format control.
197 (with-test (:name
(cerror :condition-object-and-format-arguments
))
200 ((simple-error (lambda (c) (incf x
) (continue c
))))
201 (cerror "Continue from ~A at ~A"
202 (make-condition 'simple-error
:format-control
"foo"
203 :format-arguments nil
)
204 'cerror
(get-universal-time))
207 ;; Test some of the variations permitted by the RESTART-CASE syntax.
208 (with-test (:name
(restart-case :smoke
))
210 ((test (clause &optional
(expected ''(:ok
)) (args '(:ok
)))
211 `(assert (equal ,expected
217 (invoke-restart ',(first clause
) ,@args
))))
221 (test (foo (quux) quux
))
222 (test (foo (&optional quux
) quux
))
223 ;; Multiple values should work.
224 (test (foo (a b
) (values a b
)) '(1 2) (1 2))
225 ;; Although somewhat unlikely, these should be legal and return
226 ;; the respective keyword when the restart is invoked.
227 (test (foo () :report
) '(:report
) ())
228 (test (foo () :interactive
) '(:interactive
) ())
229 (test (foo () :test
) '(:test
) ())
230 ;; Declarations should work normally as part of the restart body.
231 (test (foo () :declare
()) '(nil) ())
232 (test (foo () :declare
() :report
"quux") '("quux") ())))
234 (with-test (:name
(restart-case :malformed-clauses
))
236 ((test (clause &optional
(expected clause
))
240 `(restart-case (error "foo") ,',clause
))
242 (assert (equal '(restart-case ,expected
)
243 (simple-condition-format-arguments e
)))
246 (test :report
) ; not even a list
248 (test (foo)) ; no lambda-list
249 (test (foo :report
)) ; no lambda-list
250 (test (foo :report
"quux")) ; no lambda-list
251 (test (foo :report
"quux" (quux))) ; confused report and lambda list
254 (with-test (:name
:simple-condition-without-args
)
255 (let ((sc (make-condition 'simple-condition
)))
256 (assert (not (simple-condition-format-control sc
)))
257 (assert (not (simple-condition-format-arguments sc
)))
258 (assert (stringp (prin1-to-string sc
)))
264 (when (and (equal "No format-control for ~S"
265 (simple-condition-format-control c
))
267 (simple-condition-format-arguments c
))))
270 (with-test (:name
:malformed-simple-condition-printing-type-error
)
271 (assert (eq :type-error
274 (make-condition 'simple-error
:format-control
"" :format-arguments
8))
276 (when (and (eq 'list
(type-error-expected-type e
))
277 (eql 8 (type-error-datum e
)))
280 (with-test (:name
(:printing-unintitialized-condition
:bug-1184586
))
281 (prin1-to-string (make-condition 'simple-type-error
)))
283 (with-test (:name
(:print-undefined-function-condition
)
285 (handler-case (funcall '#:foo
)
286 (undefined-function (c) (princ-to-string c
))))
288 ;; Printing a READER-ERROR while the underlying stream is still open
289 ;; should print the stream position information.
290 (with-test (:name
(reader-error :stream-error-position-info
:open-stream
293 ;; High debug avoids stack-allocating the stream.
294 ;; It would be fine to stack-allocate it, because the handler-case does not
295 ;; use the stream outside of its extent, however, because ALLOCATE-CONDITION
296 ;; doesn't know when you will use the stream, it always replaces a DX stream
297 ;; with a dummy. The dummy stream would not have position information.
298 (declare (optimize debug
))
301 "Line: 1, Column: 22, File-Position: 22"
302 (with-input-from-string (stream "no-such-package::symbol")
305 (reader-error (condition) (princ-to-string condition
))))))))
307 ;; Printing a READER-ERROR when the underlying stream has been closed
308 ;; should still work, but the stream information will not be printed.
309 (with-test (:name
(reader-error :stream-error-position-info
:closed-stream
311 ;; This test operates on a closed stream that has dynamic extent (theoretically).
312 ;; SAFETY 3 prevents a memory fault by not actually stack-allocating it.
313 (declare (optimize (safety 3)))
316 "Package NO-SUCH-PACKAGE does not exist"
318 (with-input-from-string (stream "no-such-package::symbol")
320 (reader-error (condition) (princ-to-string condition
))))))
322 (with-test (:name
(make-condition :non-condition-class
))
323 (assert (search "does not designate a condition class"
325 (make-condition 'standard-class
)
326 (type-error (condition)
327 (princ-to-string condition
))))))
329 ;; When called with a symbol not designating a condition class,
330 ;; MAKE-CONDITION used to signal an error which printed as "NIL does
331 ;; not designate a condition class.".
332 (with-test (:name
(make-condition :correct-error-for-undefined-condition
334 (assert (search (string 'no-such-condition
)
336 (make-condition 'no-such-condition
)
337 (type-error (condition)
338 (princ-to-string condition
))))))
340 ;; Using an undefined condition type in a HANDLER-BIND clause should
341 ;; signal an ERROR at runtime. Bug 1378939 was about landing in LDB
342 ;; because of infinite recursion in SIGNAL instead.
343 (with-test (:name
(handler-bind :undefined-condition-type
345 (multiple-value-bind (fun failure-p warnings style-warnings
)
346 (checked-compile '(lambda ()
347 (handler-bind ((no-such-condition-class #'print
))
348 (error "does not matter")))
349 :allow-style-warnings t
)
350 (declare (ignore failure-p warnings
))
351 (assert (= (length style-warnings
) 1))
352 (assert-error (funcall fun
) simple-error
)))
354 ;; Using an undefined condition type in a HANDLER-BIND clause should
355 ;; signal a [STYLE-]WARNING at compile time.
356 (with-test (:name
(handler-bind :undefined-condition-type
357 :compile-time-warning
))
358 (multiple-value-bind (fun failure-p warnings style-warnings
)
359 (checked-compile '(lambda ()
360 (handler-bind ((no-such-condition-class #'print
))))
361 :allow-style-warnings t
)
362 (declare (ignore fun failure-p warnings
))
363 (assert (= (length style-warnings
) 1))))
365 ;; Empty bindings in HANDLER-BIND pushed an empty cluster onto
366 ;; *HANDLER-CLUSTERS* which was not expected by SIGNAL (and wasteful).
367 (with-test (:name
(handler-bind :empty-bindings
:bug-1388707
))
368 (assert-error (handler-bind () (error "Foo")) simple-error
))
370 ;; Parsing of #'FUNCTION in %HANDLER-BIND was too liberal.
371 ;; This code should not compile.
372 (with-test (:name
(handler-bind :no-sloppy-semantics
))
373 (multiple-value-bind (fun failure-p
)
374 (checked-compile '(lambda (x)
375 (sb-kernel::%handler-bind
376 ((condition (function (lambda (c) (print c
)) garb
)))
379 (declare (ignore fun
))
382 (multiple-value-bind (fun failure-p
)
383 (checked-compile '(lambda (x)
384 (handler-bind ((warning "woot")) (print x
)))
385 :allow-failure t
:allow-warnings t
)
386 (declare (ignore fun
))
389 (with-test (:name
(handler-bind satisfies
:predicate style-warning
))
390 (multiple-value-bind (fun failure-p warnings style-warnings
)
393 ;; Just in case we ever change the meaning of #'F in high
394 ;; safety so that it evals #'F, this test will break,
395 ;; indicating that HANDLER-BIND will have to be changed.
396 (declare (optimize (safety 3)))
397 (declare (notinline +))
398 (handler-bind (((satisfies snorky
) #'abort
)) (+ 2 2)))
399 :allow-style-warnings t
)
400 (declare (ignore failure-p warnings
))
401 (assert (= (length style-warnings
) 1))
402 (assert (= (funcall fun
) 4)))) ; there is no runtime error either
404 (with-test (:name
:with-condition-restarts-evaluation-order
)
406 (with-condition-restarts (progn
408 (make-condition 'error
))
409 (progn (push 2 result
) nil
)
411 (assert (equal result
'(3 2 1)))))
413 (with-test (:name
(type-error print
*print-pretty
*))
414 (let ((error (make-condition 'type-error
:datum
1 :expected-type
'string
)))
415 (assert (string= (let ((*print-pretty
* nil
))
416 (princ-to-string error
))
417 "The value 1 is not of type STRING"))
418 (assert (string= (let ((*print-pretty
* t
))
419 (princ-to-string error
))
425 ;;; Instances of LAYOUT for condition classoids created by genesis
426 ;;; should resemble ones created normally. Due to a bug, they did not.
427 ;;; (The LENGTH slot had the wrong value)
428 (with-test (:name
:condition-layout-lengths
)
429 (loop for layout being each hash-value of
(sb-kernel:classoid-subclasses
430 (sb-kernel:find-classoid
'condition
))
431 for len
= (sb-kernel:layout-length layout
)
432 minimize len into min
433 maximize len into max
434 finally
(assert (= min max
))))
436 (with-test (:name
:allocate-condition-odd-length-keys
)
437 (multiple-value-bind (newcond error
)
438 (ignore-errors (make-condition 'warning
:a
1 :b
))
439 (declare (ignore newcond
))
440 (assert (string= (write-to-string error
:escape nil
)
441 "odd-length initializer list: (:A 1 :B)."))))
443 (with-test (:name
:type-error-on-dx-object
444 :skipped-on
:interpreter
)
446 (sb-int:dx-let
((a (make-array 3)))
448 (print (1+ (aref a
0))))
450 (assert (equal (sb-kernel:type-error-datum-stored-type e
)
451 '(simple-vector 3))))))
453 (with-test (:name
(:handler-bind-evaluation-count
:lp1916302
))
455 (handler-bind ((condition (let ((x 0))
458 (push (incf x
) list
)))))
461 (assert (equalp '(2 1) list
))))
463 (with-test (:name
(:handler-bind-evaluation-count
:separate-establishment
))
466 (handler-bind ((condition (let ((x 0))
469 (push (incf x
) list
)))))
470 (signal 'condition
)))
471 (assert (equalp '(1 1) list
))))