Fix make-array transforms.
[sbcl.git] / tests / condition.impure.lisp
index e21854c..10825ed 100644 (file)
@@ -11,6 +11,8 @@
 
 (cl:in-package :cl-user)
 
+(use-package :test-util)
+
 ;;; Bug from CLOCC.
 (defpackage :p1
   (:use :cl)
@@ -37,6 +39,8 @@
   (assert (eql (code-msg code) 2))
   (assert (eql (%code-msg code) 1)))
 
+(in-package :cl-user)
+
 ;;; Check that initializing the condition class metaobject doesn't create
 ;;; any instances. Reported by Marco Baringer on sbcl-devel Mon, 05 Jul 2004.
 (defvar *condition-count* 0)
                '(and condition counted-condition)))
 
 (define-condition picky-condition () ())
-(restart-case
-    (handler-case
-        (error 'picky-condition)
-      (picky-condition (c)
-        (assert (eq (car (compute-restarts)) (car (compute-restarts c))))))
-  (picky-restart ()
-    :report "Do nothing."
-    :test (lambda (c)
-            (typep c '(or null picky-condition)))
-    'ok))
+
+(with-test (:name (:picky-condition compute-restarts))
+  (restart-case
+      (handler-case
+          (error 'picky-condition)
+        (picky-condition (c)
+          ;; The PICKY-RESTART should be applicable for the
+          ;; PICKY-CONDITION and all other cases.
+          (assert (eq (restart-name (first (compute-restarts))) 'picky-restart))
+          (assert (eq (restart-name (first (compute-restarts c))) 'picky-restart))
+          (assert (eq (car (compute-restarts)) (car (compute-restarts c))))
+          ;; ANOTHER-PICKY-RESTART should not be applicable for the
+          ;; PICKY-CONDITION, but all other cases.
+          (assert (not (find 'another-picky-restart (compute-restarts c)
+                             :key #'restart-name)))
+          (assert (find 'another-picky-restart (compute-restarts)
+                        :key #'restart-name))
+          :ok))
+    (picky-restart ()
+      :report "Do nothing."
+      :test (lambda (c) (typep c '(or null picky-condition))))
+    (another-picky-restart ()
+      :report "Do nothing as well"
+      :test (lambda (c) (typep c '(not picky-condition))))))
 
 ;;; adapted from Helmut Eller on cmucl-imp
-(assert (eq 'it
-            (restart-case
-                (handler-case
-                    (error 'picky-condition)
-                  (picky-condition (c)
-                    (invoke-restart (find-restart 'give-it c))))
-              (give-it ()
-                :test (lambda (c) (typep c 'picky-condition))
-                'it))))
+(with-test (:name (:picky-condition invoke-restart))
+  (assert (eq 'it
+              (restart-case
+                  (handler-case
+                      (error 'picky-condition)
+                    (picky-condition (c)
+                      (invoke-restart (find-restart 'give-it c))))
+                (give-it ()
+                  :test (lambda (c) (typep c 'picky-condition))
+                  'it)))))
 
 ;;; In sbcl-1.0.9, a condition derived from CL:STREAM-ERROR (or
 ;;; CL:READER-ERROR or or CL:PARSE-ERROR) didn't inherit a usable
 (define-condition my-stream-error-1-0-9 (stream-error) ())
 (define-condition parse-foo-error-1-0-9 (parse-error) ())
 (define-condition read-bar-error-1-0-9 (reader-error) ())
-(let (;; instances created initializing all the slots specified in
-      ;; ANSI CL
-      (parse-foo-error-1-0-9 (make-condition 'parse-foo-error-1-0-9
-                                             :stream *standard-input*))
-      (read-foo-error-1-0-9 (make-condition 'read-bar-error-1-0-9
-                                            :stream *standard-input*))
-      (my-stream-error-1-0-9 (make-condition 'my-stream-error-1-0-9
-                                             :stream *standard-input*)))
-  ;; should be printable
-  (dolist (c (list
-              ;; but not yet, o lord (should be fixed soon by WHN, in
-              ;; one or more commits ca. 1.0.9.55+, #+NILed out 'til
-              ;; then)
-              #+nil my-stream-error-1-0-9
-              #+nil parse-foo-error-1-0-9
-              ;; fixed, hallelujah
-              read-foo-error-1-0-9))
-    ;; escaped or not
-    (dolist (*print-escape* '(nil t))
-      (write c :stream (make-string-output-stream)))))
+(with-test (:name :printable-conditions)
+  (let (;; instances created initializing all the slots specified in
+        ;; ANSI CL
+        (parse-foo-error-1-0-9 (make-condition 'parse-foo-error-1-0-9
+                                               :stream *standard-input*))
+        (read-foo-error-1-0-9 (make-condition 'read-bar-error-1-0-9
+                                              :stream *standard-input*))
+        (my-stream-error-1-0-9 (make-condition 'my-stream-error-1-0-9
+                                               :stream *standard-input*)))
+    ;; should be printable
+    (dolist (c (list
+                my-stream-error-1-0-9
+                parse-foo-error-1-0-9
+                read-foo-error-1-0-9))
+      ;; whether escaped or not
+      (dolist (*print-escape* '(nil t))
+        (write c :stream (make-string-output-stream))))))
+
+;;; Reported by Michael Weber: restart computation in :TEST-FUNCTION used to
+;;; cause infinite recursion.
+(defun restart-test-finds-restarts ()
+  (restart-bind
+      ((bar (lambda ()
+              (return-from restart-test-finds-restarts 42))
+         :test-function
+         (lambda (condition)
+           (declare (ignore condition))
+           (find-restart 'qux))))
+    (when (find-restart 'bar)
+      (invoke-restart 'bar))))
+(assert (not (restart-test-finds-restarts)))
+
+(with-test (:name :bug-896379)
+  (let ((*evaluator-mode* :compile))
+    (handler-bind ((style-warning #'error))
+      (let ((reader (gensym "READER"))
+            (name (gensym "FOO-ERROR")))
+        (eval `(define-condition ,name (error)
+                 ((slot :initarg :slot :reader ,reader))
+                 (:report (lambda (c stream)
+                            (format stream "Oops: ~S" (,reader c))))))))))
+
+(with-test (:name :define-condition-result)
+  (let ((name (gensym "CONDITION")))
+    (assert
+     (eq (eval `(define-condition ,name () ()))
+         name))))
+
+;;; bug-1164970
+
+(define-condition condition-with-default-initargs (condition)
+  ()
+  (:default-initargs :foo 1))
+
+(with-test (:name (sb-mop:class-direct-default-initargs :for-condition-class
+                   :bug-1164970))
+  ;; CLASS-DIRECT-DEFAULT-INITARGS used to return nil for all
+  ;; condition classes.
+  (let ((initargs (sb-mop:class-direct-default-initargs
+                   (find-class 'condition-with-default-initargs))))
+    (assert (equal (subseq (first initargs) 0 2) '(:foo 1)))))
+
+;;; bug-539517
+
+(defconstant +error-when-called+ (lambda () (error "oops")))
+
+(define-condition condition-with-constant-function-initarg ()
+  ((foo :initarg :foo
+        :reader condition-with-constant-function-initarg-foo))
+  (:default-initargs :foo +error-when-called+))
+
+(with-test (:name (:condition-with-constant-function-initarg :bug-539517))
+  ;; The default initarg handling for condition classes used to
+  ;; confuse constant functions (thus +ERROR-WHEN-CALLED+) and
+  ;; initfunctions. This lead to +ERROR-WHEN-CALLED+ being called as
+  ;; if it was an initfunction.
+  (assert (functionp
+           (condition-with-constant-function-initarg-foo
+            (make-condition 'condition-with-constant-function-initarg))))
+  (assert (functionp
+           (condition-with-constant-function-initarg-foo
+            (make-instance 'condition-with-constant-function-initarg)))))
+
+;; Same problem
+
+(define-condition condition-with-constant-function-initform ()
+  ((foo :initarg :foo
+        :reader condition-with-constant-function-initform-foo
+        :initform +error-when-called+)))
+
+(with-test (:name (:condition-with-constant-function-slot-initform))
+  (assert (functionp
+           (condition-with-constant-function-initform-foo
+            (make-condition 'condition-with-constant-function-initform))))
+  (assert (functionp
+           (condition-with-constant-function-initform-foo
+            (make-instance 'condition-with-constant-function-initform)))))
+
+;;; bug-1164969
+
+(defvar bar-counter 0)
+
+(defvar baz-counter 0)
+
+(define-condition condition-with-non-constant-default-initarg ()
+  ((bar :initarg :bar
+        :reader condition-with-non-constant-default-initarg-bar)
+   (baz :initarg :baz
+        :reader condition-with-non-constant-default-initarg-baz
+        :initform (incf baz-counter)))
+  (:default-initargs :bar (incf bar-counter)))
+(define-condition condition-with-non-constant-default-initarg ()
+  ((bar :initarg :bar
+        :reader condition-with-non-constant-default-initarg-bar)
+   (baz :initarg :baz
+        :reader condition-with-non-constant-default-initarg-baz
+        :initform (incf baz-counter)))
+  (:default-initargs :bar (incf bar-counter)))
+
+(with-test (:name (:redefining-condition-with-non-constant-default-initarg
+                   :bug-1164969))
+  ;; Redefining conditions could lead to multiple evaluations of
+  ;; initfunctions for slots and default initargs. We need all the
+  ;; combinations of make-condition/instance and eval/compile because
+  ;; they failed differently.
+  (macrolet ((test (case &body body)
+               `(progn
+                  (setf bar-counter 0
+                        baz-counter 0)
+                  (let ((instance (progn ,@body)))
+                    (assert
+                     (= 1 (condition-with-non-constant-default-initarg-bar
+                           instance))
+                     nil
+                     ,(format nil "Assertion failed for default initarg initfunction for ~A"
+                              case))
+                    (assert
+                     (= 1 (condition-with-non-constant-default-initarg-baz
+                           instance))
+                     nil
+                     ,(format nil "Assertion failed for slot initfunction for ~A"
+                              case)))
+                  (assert (= 1 bar-counter))
+                  (assert (= 1 baz-counter)))))
+
+    ;; Go through EVAL to avoid optimizations.
+    (test :eval+make-condition
+      (eval '(make-condition
+              'condition-with-non-constant-default-initarg)))
+    (test :eval+make-instance
+      (eval '(make-instance
+              'condition-with-non-constant-default-initarg)))
+
+    ;; Allow optimizations.
+    (test :compile+make-condition
+      (make-condition
+       'condition-with-non-constant-default-initarg))
+    (test :compile+make-instance
+      (make-instance
+       'condition-with-non-constant-default-initarg))))
+
+;;; bug-1049404
+
+(define-condition condition-with-class-allocation ()
+  ((count :accessor condition-with-class-allocation-count
+          :initform 0
+          :allocation :class)))
+
+(with-test (:name (:condition-with-class-allocation :bug-1049404))
+  (loop repeat 5 do
+           (incf (condition-with-class-allocation-count
+                  (make-condition 'condition-with-class-allocation))))
+  (assert (= 5 (condition-with-class-allocation-count
+                (make-condition 'condition-with-class-allocation)))))
+
+;;; bug-789497
+
+(with-test (:name (assert :print-intermediate-results :bug-789497))
+  (macrolet ((test (bindings expression expected-message)
+               `(let ,bindings
+                  (handler-case (assert ,expression)
+                    (simple-error (condition)
+                      (assert (string= (princ-to-string condition)
+                                       ,expected-message)))))))
+    ;; Constant and variables => no special report.
+    (test () nil "The assertion NIL failed.")
+    (test ((a nil)) a "The assertion A failed.")
+    ;; Special operators => no special report.
+    (test ((a nil) (b nil)) (or a b) "The assertion (OR A B) failed.")
+    (test ((a nil) (b t)) (and a b) "The assertion (AND A B) failed.")
+    ;; Functions with constant and non-constant arguments => include
+    ;; non-constant arguments in report.
+    (test ((a t)) (not a) "The assertion (NOT A) failed with A = T.")
+    (test () (not t) "The assertion (NOT T) failed.")
+    (test ((a -1)) (plusp (signum a))
+          "The assertion (PLUSP (SIGNUM A)) failed with (SIGNUM A) = -1.")))
+
+(with-test (:name (find-restart :recheck-conditions-and-tests :bug-774410))
+  (let ((activep t))
+    (restart-bind ((switchable-restart
+                     (constantly 'irrelevant)
+                     :test-function (lambda (condition)
+                                      (declare (ignore condition))
+                                      activep)))
+      (let ((actual-restart (find-restart 'switchable-restart)))
+        ;; Inactive because of condition-restarts associations.
+        (let ((required-condition (make-condition 'condition))
+              (wrong-condition (make-condition 'condition)))
+          (with-condition-restarts required-condition (list actual-restart)
+            (assert (null (find-restart actual-restart wrong-condition)))))
+
+        ;; Inactive because of test-function.
+        (setf activep nil)
+        (assert (null (find-restart actual-restart)))))))