1 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; While most of SBCL is derived from the CMU CL system, the test
5 ;;;; files (like this one) were written from scratch after the fork
8 ;;;; This software is in the public domain and is provided with
9 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
10 ;;;; more information.
12 (cl:in-package
:cl-user
)
14 (use-package :test-util
)
19 (:export
#:code
#:code-msg
#:%code-msg
))
21 (define-condition code
()
22 ((msg :reader code-msg
:reader %code-msg
:initarg
:msg
)))
27 (define-condition code1
(code)
28 ((msg :accessor code-msg
:initarg
:msg
)))
30 (let ((code (make-condition 'code
:msg
1)))
31 (assert (typep code
'code
))
32 (assert (eql (code-msg code
) 1))
33 (assert (eql (%code-msg code
) 1)))
34 (let ((code (make-condition 'code1
:msg
1)))
35 (assert (typep code
'code
))
36 (assert (eql (code-msg code
) 1))
37 (assert (eql (%code-msg code
) 1))
38 (setf (code-msg code
) 2)
39 (assert (eql (code-msg code
) 2))
40 (assert (eql (%code-msg code
) 1)))
44 ;;; Check that initializing the condition class metaobject doesn't create
45 ;;; any instances. Reported by Marco Baringer on sbcl-devel Mon, 05 Jul 2004.
46 (defvar *condition-count
* 0)
47 (define-condition counted-condition
() ((slot :initform
(incf *condition-count
*))))
48 (defmethod frob-counted-condition ((x counted-condition
)) x
)
49 (assert (= 0 *condition-count
*))
50 (assert (typep (sb-mop:class-prototype
(find-class 'counted-condition
))
51 '(and condition counted-condition
)))
53 (define-condition picky-condition
() ())
55 (with-test (:name
(:picky-condition compute-restarts
))
58 (error 'picky-condition
)
60 ;; The PICKY-RESTART should be applicable for the
61 ;; PICKY-CONDITION and all other cases.
62 (assert (eq (restart-name (first (compute-restarts))) 'picky-restart
))
63 (assert (eq (restart-name (first (compute-restarts c
))) 'picky-restart
))
64 (assert (eq (car (compute-restarts)) (car (compute-restarts c
))))
65 ;; ANOTHER-PICKY-RESTART should not be applicable for the
66 ;; PICKY-CONDITION, but all other cases.
67 (assert (not (find 'another-picky-restart
(compute-restarts c
)
68 :key
#'restart-name
)))
69 (assert (find 'another-picky-restart
(compute-restarts)
74 :test
(lambda (c) (typep c
'(or null picky-condition
))))
75 (another-picky-restart ()
76 :report
"Do nothing as well"
77 :test
(lambda (c) (typep c
'(not picky-condition
))))))
79 ;;; adapted from Helmut Eller on cmucl-imp
80 (with-test (:name
(:picky-condition invoke-restart
))
84 (error 'picky-condition
)
86 (invoke-restart (find-restart 'give-it c
))))
88 :test
(lambda (c) (typep c
'picky-condition
))
91 ;;; In sbcl-1.0.9, a condition derived from CL:STREAM-ERROR (or
92 ;;; CL:READER-ERROR or or CL:PARSE-ERROR) didn't inherit a usable
93 ;;; PRINT-OBJECT method --- the PRINT-OBJECT code implicitly assumed
94 ;;; that CL:STREAM-ERROR was like a SIMPLE-CONDITION, with args and
95 ;;; format control, which seems to be a preANSIism.
97 ;;; (The spec for DEFINE-CONDITION says that if :REPORT is not
98 ;;; supplied, "information about how to report this type of condition
99 ;;; is inherited from the PARENT-TYPE." The spec doesn't explicitly
100 ;;; forbid the inherited printer from trying to read slots which
101 ;;; aren't portably specified for the condition, but it doesn't seem
102 ;;; reasonable for the inherited printer to do so. It does seem
103 ;;; reasonable for app code to derive a new condition from
104 ;;; CL:READER-ERROR (perhaps for an error in a readmacro) or
105 ;;; CL:PARSE-ERROR (perhaps for an error in an operator
106 ;;; READ-MY-FAVORITE-DATA-STRUCTURE) or CL:STREAM-ERROR (dunno why
107 ;;; offhand, but perhaps for some Gray-stream-ish reason), not define
108 ;;; a :REPORT method for its new condition, and expect to inherit from
109 ;;; the application's printer all the cruft required for describing
110 ;;; the location of the error in the input.)
111 (define-condition my-stream-error-1-0-9
(stream-error) ())
112 (define-condition parse-foo-error-1-0-9
(parse-error) ())
113 (define-condition read-bar-error-1-0-9
(reader-error) ())
114 (with-test (:name
:printable-conditions
)
115 (let (;; instances created initializing all the slots specified in
117 (parse-foo-error-1-0-9 (make-condition 'parse-foo-error-1-0-9
118 :stream
*standard-input
*))
119 (read-foo-error-1-0-9 (make-condition 'read-bar-error-1-0-9
120 :stream
*standard-input
*))
121 (my-stream-error-1-0-9 (make-condition 'my-stream-error-1-0-9
122 :stream
*standard-input
*)))
123 ;; should be printable
125 my-stream-error-1-0-9
126 parse-foo-error-1-0-9
127 read-foo-error-1-0-9
))
128 ;; whether escaped or not
129 (dolist (*print-escape
* '(nil t
))
130 (write c
:stream
(make-string-output-stream))))))
132 ;;; Reported by Michael Weber: restart computation in :TEST-FUNCTION used to
133 ;;; cause infinite recursion.
134 (defun restart-test-finds-restarts ()
137 (return-from restart-test-finds-restarts
42))
140 (declare (ignore condition
))
141 (find-restart 'qux
))))
142 (when (find-restart 'bar
)
143 (invoke-restart 'bar
))))
144 (assert (not (restart-test-finds-restarts)))
146 (with-test (:name
:bug-896379
)
147 (let ((*evaluator-mode
* :compile
))
148 (handler-bind ((style-warning #'error
))
149 (let ((reader (gensym "READER"))
150 (name (gensym "FOO-ERROR")))
151 (eval `(define-condition ,name
(error)
152 ((slot :initarg
:slot
:reader
,reader
))
153 (:report
(lambda (c stream
)
154 (format stream
"Oops: ~S" (,reader c
))))))))))
156 (with-test (:name
:define-condition-result
)
157 (let ((name (gensym "CONDITION")))
159 (eq (eval `(define-condition ,name
() ()))
164 (define-condition condition-with-default-initargs
(condition)
166 (:default-initargs
:foo
1))
168 (with-test (:name
(sb-mop:class-direct-default-initargs
:for-condition-class
170 ;; CLASS-DIRECT-DEFAULT-INITARGS used to return nil for all
171 ;; condition classes.
172 (let ((initargs (sb-mop:class-direct-default-initargs
173 (find-class 'condition-with-default-initargs
))))
174 (assert (equal (subseq (first initargs
) 0 2) '(:foo
1)))))
178 (defconstant +error-when-called
+ (lambda () (error "oops")))
180 (define-condition condition-with-constant-function-initarg
()
182 :reader condition-with-constant-function-initarg-foo
))
183 (:default-initargs
:foo
+error-when-called
+))
185 (with-test (:name
(:condition-with-constant-function-initarg
:bug-539517
))
186 ;; The default initarg handling for condition classes used to
187 ;; confuse constant functions (thus +ERROR-WHEN-CALLED+) and
188 ;; initfunctions. This lead to +ERROR-WHEN-CALLED+ being called as
189 ;; if it was an initfunction.
191 (condition-with-constant-function-initarg-foo
192 (make-condition 'condition-with-constant-function-initarg
))))
194 (condition-with-constant-function-initarg-foo
195 (make-instance 'condition-with-constant-function-initarg
)))))
199 (define-condition condition-with-constant-function-initform
()
201 :reader condition-with-constant-function-initform-foo
202 :initform
+error-when-called
+)))
204 (with-test (:name
(:condition-with-constant-function-slot-initform
))
206 (condition-with-constant-function-initform-foo
207 (make-condition 'condition-with-constant-function-initform
))))
209 (condition-with-constant-function-initform-foo
210 (make-instance 'condition-with-constant-function-initform
)))))
214 (defvar bar-counter
0)
216 (defvar baz-counter
0)
218 (define-condition condition-with-non-constant-default-initarg
()
220 :reader condition-with-non-constant-default-initarg-bar
)
222 :reader condition-with-non-constant-default-initarg-baz
223 :initform
(incf baz-counter
)))
224 (:default-initargs
:bar
(incf bar-counter
)))
225 (define-condition condition-with-non-constant-default-initarg
()
227 :reader condition-with-non-constant-default-initarg-bar
)
229 :reader condition-with-non-constant-default-initarg-baz
230 :initform
(incf baz-counter
)))
231 (:default-initargs
:bar
(incf bar-counter
)))
233 (with-test (:name
(:redefining-condition-with-non-constant-default-initarg
235 ;; Redefining conditions could lead to multiple evaluations of
236 ;; initfunctions for slots and default initargs. We need all the
237 ;; combinations of make-condition/instance and eval/compile because
238 ;; they failed differently.
239 (macrolet ((test (case &body body
)
243 (let ((instance (progn ,@body
)))
245 (= 1 (condition-with-non-constant-default-initarg-bar
248 ,(format nil
"Assertion failed for default initarg initfunction for ~A"
251 (= 1 (condition-with-non-constant-default-initarg-baz
254 ,(format nil
"Assertion failed for slot initfunction for ~A"
256 (assert (= 1 bar-counter
))
257 (assert (= 1 baz-counter
)))))
259 ;; Go through EVAL to avoid optimizations.
260 (test :eval
+make-condition
261 (eval '(make-condition
262 'condition-with-non-constant-default-initarg
)))
263 (test :eval
+make-instance
264 (eval '(make-instance
265 'condition-with-non-constant-default-initarg
)))
267 ;; Allow optimizations.
268 (test :compile
+make-condition
270 'condition-with-non-constant-default-initarg
))
271 (test :compile
+make-instance
273 'condition-with-non-constant-default-initarg
))))
277 (define-condition condition-with-class-allocation
()
278 ((count :accessor condition-with-class-allocation-count
280 :allocation
:class
)))
282 (with-test (:name
(:condition-with-class-allocation
:bug-1049404
))
284 (incf (condition-with-class-allocation-count
285 (make-condition 'condition-with-class-allocation
))))
286 (assert (= 5 (condition-with-class-allocation-count
287 (make-condition 'condition-with-class-allocation
)))))
291 (with-test (:name
(assert :print-intermediate-results
:bug-789497
))
292 (macrolet ((test (bindings expression expected-message
)
294 (handler-case (assert ,expression
)
295 (simple-error (condition)
296 (assert (string= (princ-to-string condition
)
297 ,expected-message
)))))))
298 ;; Constant and variables => no special report.
299 (test () nil
"The assertion NIL failed.")
300 (test ((a nil
)) a
"The assertion A failed.")
301 ;; Special operators => no special report.
302 (test ((a nil
) (b nil
)) (or a b
) "The assertion (OR A B) failed.")
303 (test ((a nil
) (b t
)) (and a b
) "The assertion (AND A B) failed.")
304 ;; Functions with constant and non-constant arguments => include
305 ;; non-constant arguments in report.
306 (test ((a t
)) (not a
) "The assertion (NOT A) failed with A = T.")
307 (test () (not t
) "The assertion (NOT T) failed.")
308 (test ((a -
1)) (plusp (signum a
))
309 "The assertion (PLUSP (SIGNUM A)) failed with (SIGNUM A) = -1.")
310 ;; Same for local functions.
311 (flet ((my-not (x) (not x
))
312 (my-plusp (x) (plusp x
)))
313 (test ((a t
)) (my-not a
) "The assertion (MY-NOT A) failed with A = T.")
314 (test () (my-not t
) "The assertion (MY-NOT T) failed.")
315 (test ((a -
1)) (my-plusp (signum a
))
316 "The assertion (MY-PLUSP (SIGNUM A)) failed with (SIGNUM A) = -1."))))
318 ;; If ASSERT thinks it's checking a function call form, it binds the arguments
319 ;; to local vars so that it can show them on failure. But it would accidentally
320 ;; treat a local macro as a function if its name coincided with a global.
321 (defun compute (a b
) (declare (ignore a b
)))
322 (defun compute-something-else (a b
) (list a b
))
323 (defun assert-arg-eval-order (a b
)
325 (flet ((eval-arg (arg) (setq evals
(nconc evals
(list arg
))) arg
))
326 (macrolet ((compute (x y z
)
327 (declare (ignore y
)) ; does not appear in expansion
328 `(compute-something-else ,z
,x
)))
329 (assert (compute (eval-arg a
)
330 (eval-arg (error "bork"))
333 (with-test (:name
:assert-arg-eval-order
)
334 (assert (equal (assert-arg-eval-order 1 2) '(2 1))))
336 (with-test (:name
(find-restart :recheck-conditions-and-tests
:bug-774410
))
338 (restart-bind ((switchable-restart
339 (constantly 'irrelevant
)
340 :test-function
(lambda (condition)
341 (declare (ignore condition
))
343 (let ((actual-restart (find-restart 'switchable-restart
)))
344 ;; Active/inactive because of condition-restarts associations.
345 (let ((required-condition (make-condition 'condition
))
346 (wrong-condition (make-condition 'condition
)))
347 (with-condition-restarts required-condition
(list actual-restart
)
348 (assert (find-restart actual-restart required-condition
))
349 (assert (not (find-restart actual-restart wrong-condition
)))))
351 ;; Inactive because of test-function.
353 (assert (not (find-restart actual-restart
)))
354 (assert (not (find-restart actual-restart
(make-condition 'condition
))))))))
357 ((with-testing-restart ((&key
358 (condition-var (gensym))
359 (condition-restart-p t
))
362 (handler-bind ((condition (lambda (,condition-var
)
363 (declare (ignorable ,condition-var
))
365 (restart-bind ((testing-restart
366 (lambda () (return-from block
:transfer
))
367 :test-function
(lambda (condition)
368 (typep condition
'condition
))))
369 ,(if condition-restart-p
370 `(let ((condition (make-condition 'condition
)))
371 (with-condition-restarts condition
(car sb-kernel
:*restart-clusters
*)
373 `(signal (make-condition 'condition
)))
376 (with-test (:name
(invoke-restart :test-function
))
378 ;; When given a restart name, there is no condition so
379 ;; INVOKE-RESTART cannot call the :test-function. SBCL considers
380 ;; the restart unsuitable for the requested invocation. See
381 ;; comment in INVOKE-RESTART.
382 (assert-error (with-testing-restart ()
383 (invoke-restart 'testing-restart
))
386 ;; When given a RESTART instance (which could only have been found
387 ;; by passing an appropriate condition to FIND-RESTART),
388 ;; INVOKE-RESTART does not call the :test-function. Again, see
389 ;; comment in INVOKE-RESTART.
390 (assert (eq :transfer
391 (with-testing-restart (:condition-var condition
)
393 (find-restart 'testing-restart condition
)))))
395 ;; Some other condition, even if it passes the :test-function,
396 ;; only works if the restart has not been associated to the
397 ;; original condition.
398 (assert (eq :transfer
399 (with-testing-restart (:condition-restart-p nil
)
401 (find-restart 'testing-restart
(make-condition 'condition
))))))
402 (with-testing-restart ()
403 (assert (not (find-restart 'testing-restart
(make-condition 'condition
))))))
405 (with-test (:name
(invoke-restart-interactively :test-function
))
406 ;; Comments in (INVOKE-RESTART :TEST-FUNCTION) apply here as well.
408 (with-testing-restart ()
409 (invoke-restart-interactively 'testing-restart
))
412 (assert (eq :transfer
413 (with-testing-restart (:condition-var condition
)
414 (invoke-restart-interactively
415 (find-restart 'testing-restart condition
)))))
417 (assert (eq :transfer
418 (with-testing-restart (:condition-restart-p nil
)
419 (invoke-restart-interactively
420 (find-restart 'testing-restart
(make-condition 'condition
))))))))
422 (defun case-failure-example (x) (etypecase x
(function 1) (symbol 2)))
423 ;; The :report method should not print "wanted one of #'SYMBOL"
424 (with-test (:name
:case-failure-report-pprint-silliness
)
425 (handler-case (case-failure-example 3)
427 (let ((str (write-to-string c
:escape nil
:pretty t
)))
428 (assert (not (search "#'SYMBOL" str
)))))))
430 (define-condition a-condition-with-a-classy-slot
()
431 ((a :allocation
:class
:initform
'foo
)))
433 (defvar *a-classy-classoid
*
434 (sb-kernel:find-classoid
'a-condition-with-a-classy-slot
))
435 ;; precondition to the test
436 (assert (= (length (sb-kernel::condition-classoid-class-slots
437 *a-classy-classoid
*))
440 (define-condition a-condition-with-a-classy-slot
()
441 ((b :allocation
:class
:initform
'baz
)
442 (a :allocation
:class
:initform
'foo
)))
444 (with-test (:name
:condition-classoid-redef-with-class-slot
)
445 (assert (= (length (sb-kernel::condition-classoid-class-slots
446 *a-classy-classoid
*))
449 (defun some-silly-handler (x) (declare (ignore x
)))
451 ;; Note, you should exercise extreme caution when printing *HANDLER-CLUSTERS*
452 ;; from the REPL or you will likely confuse yourself.
453 ;; At any point, the clusters are composed of a chain of dx conses.
454 ;; One way to print them is like this:
455 ;; (funcall (lambda () (print sb-kernel::*handler-clusters*) nil))
457 (with-test (:name
:handler-bind-expansion-variations
)
459 ;; exercise all 3 possibilities for the TEST of a clause
460 (handler-bind ((error #'print
))
461 ;; first cluster, first binding, first part
462 (let ((test (caaar sb-kernel
:*handler-clusters
*)))
463 (assert (eq test
(sb-kernel:find-classoid-cell
'error
)))))
464 (locally (declare (muffle-conditions style-warning
))
465 (handler-bind (((satisfies some-silly-fun
) #'print
))
466 (let ((test (caaar sb-kernel
:*handler-clusters
*)))
467 (assert (eq test
(sb-int:find-fdefn
'some-silly-fun
))))))
468 (handler-bind (((or warning error
) #'print
))
469 (let ((test (caaar sb-kernel
:*handler-clusters
*)))
470 (assert (functionp test
))))
472 ;; exercise all 3 possibilities for the HANDLER of a clause
474 (flet ((frob (x) (print x
)))
475 (handler-bind ((warning
476 (lambda (c) (frob (frob c
)))))
477 ;; first cluster, first binding, second part
478 (let ((fn (cdaar sb-kernel
:*handler-clusters
*)))
479 (assert (functionp fn
)))))
481 (handler-bind ((warning (eval ''muffle-warning
)))
482 (let ((fn (cdaar sb-kernel
:*handler-clusters
*)))
483 ;; the function is stored as a symbol
484 (assert (eq fn
'muffle-warning
))))
486 (handler-bind ((warning 'some-silly-handler
))
487 (let ((fn (cdaar sb-kernel
:*handler-clusters
*)))
488 ;; the function is stored as an fdefn
489 (assert (typep fn
'sb-kernel
::fdefn
))))
491 (handler-bind ((warning 'muffle-warning
))
492 (let ((fn (cdaar sb-kernel
:*handler-clusters
*)))
493 ;; the function is stored directly because it's a builtin.
494 (assert (eq fn
(symbol-function 'muffle-warning
))))))
496 ;; Oddly enough, at least one other Lisp considers this example
497 ;; *not* to fail, quite explicitly - the handler function in the
498 ;; handler-cluster is a lambda that returns a function that is
499 ;; the real handler function.
500 (defun this-should-fail ()
501 (declare (muffle-conditions style-warning
))
502 (handler-bind ((condition #'some-nonexistent-handler
))
505 (with-test (:name
:handler-bind-evals-handlers-immediately
)
506 (assert-error (this-should-fail))
507 (defun some-nonexistent-handler (x) x
)
508 (assert (integerp (this-should-fail)))) ; but not now it shouldn't