Fix make-array transforms.
[sbcl.git] / tests / condition.impure.lisp
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.
11
12 (cl:in-package :cl-user)
13
14 (use-package :test-util)
15
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)))
23
24 (defpackage :p2
25   (:use :cl :p1))
26 (in-package :p2)
27 (define-condition code1 (code)
28   ((msg :accessor code-msg :initarg :msg)))
29
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)))
41
42 (in-package :cl-user)
43
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)))
52
53 (define-condition picky-condition () ())
54
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))))))
78
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)))))
90
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))))))
131
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)))
145
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))))))))))
155
156 (with-test (:name :define-condition-result)
157   (let ((name (gensym "CONDITION")))
158     (assert
159      (eq (eval `(define-condition ,name () ()))
160          name))))
161
162 ;;; bug-1164970
163
164 (define-condition condition-with-default-initargs (condition)
165   ()
166   (:default-initargs :foo 1))
167
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)))))
175
176 ;;; bug-539517
177
178 (defconstant +error-when-called+ (lambda () (error "oops")))
179
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+))
184
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)))))
196
197 ;; Same problem
198
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+)))
203
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)))))
211
212 ;;; bug-1164969
213
214 (defvar bar-counter 0)
215
216 (defvar baz-counter 0)
217
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)))
232
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))
247                      nil
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))
253                      nil
254                      ,(format nil "Assertion failed for slot initfunction for ~A"
255                               case)))
256                   (assert (= 1 bar-counter))
257                   (assert (= 1 baz-counter)))))
258
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)))
266
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))))
274
275 ;;; bug-1049404
276
277 (define-condition condition-with-class-allocation ()
278   ((count :accessor condition-with-class-allocation-count
279           :initform 0
280           :allocation :class)))
281
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)))))
288
289 ;;; bug-789497
290
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
311 (with-test (:name (find-restart :recheck-conditions-and-tests :bug-774410))
312   (let ((activep t))
313     (restart-bind ((switchable-restart
314                      (constantly 'irrelevant)
315                      :test-function (lambda (condition)
316                                       (declare (ignore condition))
317                                       activep)))
318       (let ((actual-restart (find-restart 'switchable-restart)))
319         ;; Inactive because of condition-restarts associations.
320         (let ((required-condition (make-condition 'condition))
321               (wrong-condition (make-condition 'condition)))
322           (with-condition-restarts required-condition (list actual-restart)
323             (assert (null (find-restart actual-restart wrong-condition)))))
324
325         ;; Inactive because of test-function.
326         (setf activep nil)
327         (assert (null (find-restart actual-restart)))))))