Make INFO's compiler-macro more forgiving.
[sbcl.git] / tests / condition.impure.lisp
blob66ebf656d71b1bb904f4bd3bf85dfefe24663f10
1 ;;;; This software is part of the SBCL system. See the README file for
2 ;;;; more information.
3 ;;;;
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
6 ;;;; from CMU CL.
7 ;;;;
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)
16 ;;; Bug from CLOCC.
17 (defpackage :p1
18 (:use :cl)
19 (:export #:code #:code-msg #:%code-msg))
20 (in-package :p1)
21 (define-condition code ()
22 ((msg :reader code-msg :reader %code-msg :initarg :msg)))
24 (defpackage :p2
25 (:use :cl :p1))
26 (in-package :p2)
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)))
42 (in-package :cl-user)
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))
56 (restart-case
57 (handler-case
58 (error 'picky-condition)
59 (picky-condition (c)
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)
70 :key #'restart-name))
71 :ok))
72 (picky-restart ()
73 :report "Do nothing."
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))
81 (assert (eq 'it
82 (restart-case
83 (handler-case
84 (error 'picky-condition)
85 (picky-condition (c)
86 (invoke-restart (find-restart 'give-it c))))
87 (give-it ()
88 :test (lambda (c) (typep c 'picky-condition))
89 'it)))))
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.
96 ;;;
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
116 ;; ANSI CL
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
124 (dolist (c (list
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 ()
135 (restart-bind
136 ((bar (lambda ()
137 (return-from restart-test-finds-restarts 42))
138 :test-function
139 (lambda (condition)
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")))
158 (assert
159 (eq (eval `(define-condition ,name () ()))
160 name))))
162 ;;; bug-1164970
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
169 :bug-1164970))
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)))))
176 ;;; bug-539517
178 (defconstant +error-when-called+ (lambda () (error "oops")))
180 (define-condition condition-with-constant-function-initarg ()
181 ((foo :initarg :foo
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.
190 (assert (functionp
191 (condition-with-constant-function-initarg-foo
192 (make-condition 'condition-with-constant-function-initarg))))
193 (assert (functionp
194 (condition-with-constant-function-initarg-foo
195 (make-instance 'condition-with-constant-function-initarg)))))
197 ;; Same problem
199 (define-condition condition-with-constant-function-initform ()
200 ((foo :initarg :foo
201 :reader condition-with-constant-function-initform-foo
202 :initform +error-when-called+)))
204 (with-test (:name (:condition-with-constant-function-slot-initform))
205 (assert (functionp
206 (condition-with-constant-function-initform-foo
207 (make-condition 'condition-with-constant-function-initform))))
208 (assert (functionp
209 (condition-with-constant-function-initform-foo
210 (make-instance 'condition-with-constant-function-initform)))))
212 ;;; bug-1164969
214 (defvar bar-counter 0)
216 (defvar baz-counter 0)
218 (define-condition condition-with-non-constant-default-initarg ()
219 ((bar :initarg :bar
220 :reader condition-with-non-constant-default-initarg-bar)
221 (baz :initarg :baz
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 ()
226 ((bar :initarg :bar
227 :reader condition-with-non-constant-default-initarg-bar)
228 (baz :initarg :baz
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
234 :bug-1164969))
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)
240 `(progn
241 (setf bar-counter 0
242 baz-counter 0)
243 (let ((instance (progn ,@body)))
244 (assert
245 (= 1 (condition-with-non-constant-default-initarg-bar
246 instance))
248 ,(format nil "Assertion failed for default initarg initfunction for ~A"
249 case))
250 (assert
251 (= 1 (condition-with-non-constant-default-initarg-baz
252 instance))
254 ,(format nil "Assertion failed for slot initfunction for ~A"
255 case)))
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
269 (make-condition
270 'condition-with-non-constant-default-initarg))
271 (test :compile+make-instance
272 (make-instance
273 'condition-with-non-constant-default-initarg))))
275 ;;; bug-1049404
277 (define-condition condition-with-class-allocation ()
278 ((count :accessor condition-with-class-allocation-count
279 :initform 0
280 :allocation :class)))
282 (with-test (:name (:condition-with-class-allocation :bug-1049404))
283 (loop repeat 5 do
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)))))
289 ;;; bug-789497
291 (with-test (:name (assert :print-intermediate-results :bug-789497))
292 (macrolet ((test (bindings expression expected-message)
293 `(let ,bindings
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 (with-test (:name (find-restart :recheck-conditions-and-tests :bug-774410))
319 (let ((activep t))
320 (restart-bind ((switchable-restart
321 (constantly 'irrelevant)
322 :test-function (lambda (condition)
323 (declare (ignore condition))
324 activep)))
325 (let ((actual-restart (find-restart 'switchable-restart)))
326 ;; Active/inactive because of condition-restarts associations.
327 (let ((required-condition (make-condition 'condition))
328 (wrong-condition (make-condition 'condition)))
329 (with-condition-restarts required-condition (list actual-restart)
330 (assert (find-restart actual-restart required-condition))
331 (assert (not (find-restart actual-restart wrong-condition)))))
333 ;; Inactive because of test-function.
334 (setf activep nil)
335 (assert (not (find-restart actual-restart)))
336 (assert (not (find-restart actual-restart (make-condition 'condition))))))))
338 (macrolet
339 ((with-testing-restart ((&key
340 (condition-var (gensym))
341 (condition-restart-p t))
342 &body body)
343 `(block block
344 (handler-bind ((condition (lambda (,condition-var)
345 (declare (ignorable ,condition-var))
346 ,@body)))
347 (restart-bind ((testing-restart
348 (lambda () (return-from block :transfer))
349 :test-function (lambda (condition)
350 (typep condition 'condition))))
351 ,(if condition-restart-p
352 `(let ((condition (make-condition 'condition)))
353 (with-condition-restarts condition (car sb-kernel:*restart-clusters*)
354 (signal condition)))
355 `(signal (make-condition 'condition)))
356 :no-transfer)))))
358 (with-test (:name (invoke-restart :test-function))
360 ;; When given a restart name, there is no condition so
361 ;; INVOKE-RESTART cannot call the :test-function. SBCL considers
362 ;; the restart unsuitable for the requested invocation. See
363 ;; comment in INVOKE-RESTART.
364 (assert-error (with-testing-restart ()
365 (invoke-restart 'testing-restart))
366 control-error)
368 ;; When given a RESTART instance (which could only have been found
369 ;; by passing an appropriate condition to FIND-RESTART),
370 ;; INVOKE-RESTART does not call the :test-function. Again, see
371 ;; comment in INVOKE-RESTART.
372 (assert (eq :transfer
373 (with-testing-restart (:condition-var condition)
374 (invoke-restart
375 (find-restart 'testing-restart condition)))))
377 ;; Some other condition, even if it passes the :test-function,
378 ;; only works if the restart has not been associated to the
379 ;; original condition.
380 (assert (eq :transfer
381 (with-testing-restart (:condition-restart-p nil)
382 (invoke-restart
383 (find-restart 'testing-restart (make-condition 'condition))))))
384 (with-testing-restart ()
385 (assert (not (find-restart 'testing-restart (make-condition 'condition))))))
387 (with-test (:name (invoke-restart-interactively :test-function))
388 ;; Comments in (INVOKE-RESTART :TEST-FUNCTION) apply here as well.
389 (assert-error
390 (with-testing-restart ()
391 (invoke-restart-interactively 'testing-restart))
392 control-error)
394 (assert (eq :transfer
395 (with-testing-restart (:condition-var condition)
396 (invoke-restart-interactively
397 (find-restart 'testing-restart condition)))))
399 (assert (eq :transfer
400 (with-testing-restart (:condition-restart-p nil)
401 (invoke-restart-interactively
402 (find-restart 'testing-restart (make-condition 'condition))))))))
404 (defun case-failure-example (x) (etypecase x (function 1) (symbol 2)))
405 ;; The :report method should not print "wanted one of #'SYMBOL"
406 (with-test (:name :case-failure-report-pprint-silliness)
407 (handler-case (case-failure-example 3)
408 (condition (c)
409 (let ((str (write-to-string c :escape nil :pretty t)))
410 (assert (not (search "#'SYMBOL" str)))))))
412 (define-condition a-condition-with-a-classy-slot ()
413 ((a :allocation :class :initform 'foo)))
415 (defvar *a-classy-classoid*
416 (sb-kernel:find-classoid 'a-condition-with-a-classy-slot))
417 ;; precondition to the test
418 (assert (= (length (sb-kernel::condition-classoid-class-slots
419 *a-classy-classoid*))
422 (define-condition a-condition-with-a-classy-slot ()
423 ((b :allocation :class :initform 'baz)
424 (a :allocation :class :initform 'foo)))
426 (with-test (:name :condition-classoid-redef-with-class-slot)
427 (assert (= (length (sb-kernel::condition-classoid-class-slots
428 *a-classy-classoid*))
429 2)))