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 (cl:in-package
:cl-user
)
16 (load "test-util.lisp")
18 ;;; Until 0.7.7.21, (MAKE-CONDITION 'FILE-ERROR :PATHNAME "FOO")
19 ;;; wasn't printable, because the REPORT function for FILE-ERROR
20 ;;; referred to unbound slots. This was reported and fixed by Antonio
21 ;;; Martinez (sbcl-devel 2002-09-10).
23 "~&printable now: ~A~%"
24 (make-condition 'file-error
:pathname
"foo"))
28 (macrolet ((opaque-error (arg) `(error ,arg
)))
31 (let ((restarts (remove 'res
(compute-restarts c
)
34 (assert (= (length restarts
) 2))
35 (invoke-restart (second restarts
))))))
36 (let ((foo1 (make-condition 'error
))
37 (foo2 (make-condition 'error
)))
39 (with-condition-restarts foo1
(list (find-restart 'res
))
49 (macrolet ((opaque-error (arg) `(error ,arg
)))
50 (let ((foo1 (make-condition 'error
))
51 (foo2 (make-condition 'error
)))
55 (let ((restarts (remove 'res
(compute-restarts foo1
)
58 (assert (= (length restarts
) 1))
59 (invoke-restart (first restarts
))))))
61 (with-condition-restarts foo1
(list (find-restart 'res
))
73 (c1 (make-condition 'error
))
74 (c2 (make-condition 'error
)))
79 (flet ((check-restarts (length)
81 (length (remove 'foo
(compute-restarts c1
)
87 (invoke-restart (find-restart 'foo c1
))))))
92 (foo () :test
(lambda (c) (declare (ignore c
)) visible
)
96 ;;; First argument of CERROR is a format control
100 ((type-error (lambda (c)
103 (simple-error (lambda (c)
105 (return (if (find-restart 'continue
)
108 (cerror (formatter "Continue from ~A") "bug ~A" :bug
)))
111 (with-test (:name
(handler-bind :smoke
))
113 (flet ((handler (condition)
114 (declare (ignore condition
))
116 (macrolet ((test (handler)
119 (handler-bind ((condition ,handler
))
122 ;; Test optimized special cases.
123 (test (lambda (condition) (handler condition
)))
124 (test #'(lambda (condition) (handler condition
)))
125 ;; Test default behavior.
126 ;; (test 'handler) would require function definition => not pure
129 (with-test (:name
(handler-bind :malformed-bindings
))
130 (flet ((test (binding)
133 (macroexpand `(handler-bind (,binding
)))
135 (assert (equal (list binding
)
136 (simple-condition-format-arguments e
)))
139 (test 1) ; not even a list
140 (test '()) ; missing condition type and handler
141 (test '(error)) ; missing handler
142 (test '(error #'print
:foo
)) ; too many elements
145 ;;; clauses in HANDLER-CASE are allowed to have declarations (and
146 ;;; indeed, only declarations)
147 (with-test (:name
(handler-case declare
))
148 (assert (null (handler-case (error "foo")
150 (declare (optimize speed
)))))))
152 (with-test (:name
(signal warning muffle-warning control-error
))
154 (handler-bind ((warning #'muffle-warning
))
156 ;; if it's a control error, it had better be printable
157 (control-error (c) (format nil
"~A" c
))
158 ;; there had better be an error
159 (:no-error
(&rest args
) (error "No error: ~S" args
))))
161 (with-test (:name
(check-type type-error
))
163 (funcall (lambda (x) (check-type x fixnum
) x
) t
)
165 (assert (and (subtypep (type-error-expected-type c
) 'fixnum
)
166 (subtypep 'fixnum
(type-error-expected-type c
))))
167 (assert (eq (type-error-datum c
) t
)))
168 (:no-error
(&rest rest
) (error "no error: ~S" rest
))))
170 ;;; ANSI specifies TYPE-ERROR if datum and arguments of ERROR are not
171 ;;; designators for a condition. Reported by Bruno Haible on cmucl-imp
173 (with-test (:name
(error :invalid-arguments type-error
))
174 (flet ((test (&rest args
)
175 (multiple-value-bind (res err
)
176 (ignore-errors (apply #'error args
))
178 (assert (typep err
'type-error
))
179 (assert-no-signal (type-error-datum err
))
180 (assert-no-signal (type-error-expected-type err
)))))
181 (test '#:no-such-condition
)
185 (test (make-instance 'standard-object
))))
187 ;;; If CERROR is given a condition, any remaining arguments are only
188 ;;; used for the continue format control.
189 (with-test (:name
(cerror :condition-object-and-format-arguments
))
192 ((simple-error (lambda (c) (incf x
) (continue c
))))
193 (cerror "Continue from ~A at ~A"
194 (make-condition 'simple-error
:format-control
"foo"
195 :format-arguments nil
)
196 'cerror
(get-universal-time))
199 ;; Test some of the variations permitted by the RESTART-CASE syntax.
200 (with-test (:name
(restart-case :smoke
))
202 ((test (clause &optional
(expected ''(:ok
)) (args '(:ok
)))
203 `(assert (equal ,expected
209 (invoke-restart ',(first clause
) ,@args
))))
213 (test (foo (quux) quux
))
214 (test (foo (&optional quux
) quux
))
215 ;; Multiple values should work.
216 (test (foo (a b
) (values a b
)) '(1 2) (1 2))
217 ;; Although somewhat unlikely, these should be legal and return
218 ;; the respective keyword when the restart is invoked.
219 (test (foo () :report
) '(:report
) ())
220 (test (foo () :interactive
) '(:interactive
) ())
221 (test (foo () :test
) '(:test
) ())
222 ;; Declarations should work normally as part of the restart body.
223 (test (foo (quux) :declare
()) '(nil))
224 (test (foo () :declare
() :report
"quux") '("quux") ())))
226 (with-test (:name
(restart-case :malformed-clauses
))
228 ((test (clause &optional
(expected clause
))
232 `(restart-case (error "foo") ,',clause
))
234 (assert (equal '(restart-case ,expected
)
235 (simple-condition-format-arguments e
)))
238 (test :report
) ; not even a list
240 (test (foo)) ; no lambda-list
241 (test (foo :report
)) ; no lambda-list
242 (test (foo :report
"quux")) ; no lambda-list
243 (test (foo :report
"quux" (quux))) ; confused report and lambda list
246 (with-test (:name
:simple-condition-without-args
)
247 (let ((sc (make-condition 'simple-condition
)))
248 (assert (not (simple-condition-format-control sc
)))
249 (assert (not (simple-condition-format-arguments sc
)))
250 (assert (stringp (prin1-to-string sc
)))
256 (when (and (equal "No format-control for ~S"
257 (simple-condition-format-control c
))
259 (simple-condition-format-arguments c
))))
262 (with-test (:name
:malformed-simple-condition-printing-type-error
)
263 (assert (eq :type-error
266 (make-condition 'simple-error
:format-control
"" :format-arguments
8))
268 (when (and (eq 'list
(type-error-expected-type e
))
269 (eql 8 (type-error-datum e
)))
272 (with-test (:name
(:printing-unintitialized-condition
:bug-1184586
))
273 (prin1-to-string (make-condition 'simple-type-error
)))
275 (with-test (:name
(:print-undefined-function-condition
))
276 (handler-case (funcall '#:foo
)
277 (undefined-function (c) (princ-to-string c
))))
279 ;; Printing a READER-ERROR while the underlying stream is still open
280 ;; should print the stream position information.
281 (with-test (:name
(reader-error :stream-error-position-info
:open-stream
285 "Line: 1, Column: 22, File-Position: 22"
286 (with-input-from-string (stream "no-such-package::symbol")
289 (reader-error (condition) (princ-to-string condition
)))))))
291 ;; Printing a READER-ERROR when the underlying stream has been closed
292 ;; should still work, but the stream information will not be printed.
293 (with-test (:name
(reader-error :stream-error-position-info
:closed-stream
297 "Package NO-SUCH-PACKAGE does not exist"
299 (with-input-from-string (stream "no-such-package::symbol")
301 (reader-error (condition) (princ-to-string condition
))))))
303 (with-test (:name
(make-condition :non-condition-class
))
304 (assert (search "does not designate a condition class"
306 (make-condition 'standard-class
)
307 (type-error (condition)
308 (princ-to-string condition
))))))
310 ;; When called with a symbol not designating a condition class,
311 ;; MAKE-CONDITION used to signal an error which printed as "NIL does
312 ;; not designate a condition class.".
313 (with-test (:name
(make-condition :correct-error-for-undefined-condition
315 (assert (search (string 'no-such-condition
)
317 (make-condition 'no-such-condition
)
318 (type-error (condition)
319 (princ-to-string condition
))))))
321 ;; Using an undefined condition type in a HANDLER-BIND clause should
322 ;; signal an ERROR at runtime. Bug 1378939 was about landing in LDB
323 ;; because of infinite recursion in SIGNAL instead.
324 (with-test (:name
(handler-bind :undefined-condition-type
326 (multiple-value-bind (fun failure-p warnings style-warnings
)
327 (checked-compile '(lambda ()
328 (handler-bind ((no-such-condition-class #'print
))
329 (error "does not matter")))
330 :allow-style-warnings t
)
331 (declare (ignore failure-p warnings
))
332 (assert (= (length style-warnings
) 1))
333 (assert-error (funcall fun
) simple-error
)))
335 ;; Using an undefined condition type in a HANDLER-BIND clause should
336 ;; signal a [STYLE-]WARNING at compile time.
337 (with-test (:name
(handler-bind :undefined-condition-type
338 :compile-time-warning
))
339 (multiple-value-bind (fun failure-p warnings style-warnings
)
340 (checked-compile '(lambda ()
341 (handler-bind ((no-such-condition-class #'print
))))
342 :allow-style-warnings t
)
343 (declare (ignore fun failure-p warnings
))
344 (assert (= (length style-warnings
) 1))))
346 ;; Empty bindings in HANDLER-BIND pushed an empty cluster onto
347 ;; *HANDLER-CLUSTERS* which was not expected by SIGNAL (and wasteful).
348 (with-test (:name
(handler-bind :empty-bindings
:bug-1388707
))
349 (assert-error (handler-bind () (error "Foo")) simple-error
))
351 ;; Parsing of #'FUNCTION in %HANDLER-BIND was too liberal.
352 ;; This code should not compile.
353 (with-test (:name
(handler-bind :no-sloppy-semantics
))
354 (multiple-value-bind (fun failure-p
)
355 (checked-compile '(lambda (x)
356 (sb-impl::%handler-bind
357 ((condition (function (lambda (c) (print c
)) garb
)))
360 (declare (ignore fun
))
363 (multiple-value-bind (fun failure-p
)
364 (checked-compile '(lambda (x)
365 (handler-bind ((warning "woot")) (print x
)))
366 :allow-failure t
:allow-warnings t
)
367 (declare (ignore fun
))
370 (with-test (:name
(handler-bind satisfies
:predicate style-warning
))
371 (multiple-value-bind (fun failure-p warnings style-warnings
)
374 ;; Just in case we ever change the meaning of #'F in high
375 ;; safety so that it evals #'F, this test will break,
376 ;; indicating that HANDLER-BIND will have to be changed.
377 (declare (optimize (safety 3)))
378 (declare (notinline +))
379 (handler-bind (((satisfies snorky
) #'abort
)) (+ 2 2)))
380 :allow-style-warnings t
)
381 (declare (ignore failure-p warnings
))
382 (assert (= (length style-warnings
) 1))
383 (assert (= (funcall fun
) 4)))) ; there is no runtime error either
385 (with-test (:name
:with-condition-restarts-evaluation-order
)
387 (with-condition-restarts (progn
389 (make-condition 'error
))
390 (progn (push 2 result
) nil
)
392 (assert (equal result
'(3 2 1)))))
394 (with-test (:name
(type-error print
*print-pretty
*))
395 (let ((error (make-condition 'type-error
:datum
1 :expected-type
'string
)))
396 (assert (string= (let ((*print-pretty
* nil
))
397 (princ-to-string error
))
398 "The value 1 is not of type STRING"))
399 (assert (string= (let ((*print-pretty
* t
))
400 (princ-to-string error
))
406 ;;; Instances of LAYOUT for condition classoids created by genesis
407 ;;; should resemble ones created normally. Due to a bug, they did not.
408 (with-test (:name
:condition-layout-lengths
)
409 (loop for layout being each hash-value of
(sb-kernel:classoid-subclasses
410 (sb-kernel:find-classoid
'condition
))
411 for len
= (sb-kernel:layout-length layout
)
412 minimize len into min
413 maximize len into max
414 finally
(assert (= min max
))))