Add missed files from prior commit
[sbcl.git] / tests / assertoid.lisp
blob4c3b43f87ce89a75f6f24422c22d77ce5f3448e0
1 ;;;; the ASSERTOID macro, asserting something with added generality
2 ;;;; to help in regression tests
4 ;;;; This software is part of the SBCL system. See the README file for
5 ;;;; more information.
6 ;;;;
7 ;;;; While most of SBCL is derived from the CMU CL system, the test
8 ;;;; files (like this one) were written from scratch after the fork
9 ;;;; from CMU CL.
10 ;;;;
11 ;;;; This software is in the public domain and is provided with
12 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
13 ;;;; more information.
15 (cl:defpackage "ASSERTOID"
16 (:use "CL")
17 (:export "GRAB-CONDITION" "ASSERT-ERROR"
18 "HAS-ERROR?" "IS" "ASSERTOID"
19 "ASSERT-SIGNAL" "ASSERT-NO-SIGNAL"
20 "LEGACY-EVAL-P"
21 "EQUAL-MOD-GENSYMS" "CHECK-FUNCTION-EVALUATION-ORDER"))
23 (cl:in-package "ASSERTOID")
25 (defmacro grab-condition (&body body)
26 `(nth-value 1
27 (ignore-errors ,@body)))
29 (defmacro has-error? (form &optional (error-subtype-spec 'error))
30 `(typep (nth-value 1 (ignore-errors ,form)) ',error-subtype-spec))
32 (defmacro assert-error (form &optional (error-subtype-spec 'error))
33 `(assert (typep (nth-value 1 (ignore-errors ,form)) ',error-subtype-spec)))
35 (defun %assert-signal (thunk condition-type
36 expected-min-count expected-max-count)
37 (declare (ignore condition-type))
38 (let ((count 0))
39 (prog1
40 (funcall thunk (lambda (condition)
41 (incf count)
42 (when (typep condition 'warning)
43 (muffle-warning condition))))
44 (assert (<= expected-min-count count expected-max-count)))))
46 (defmacro assert-signal (form &optional
47 (condition-type 'condition)
48 (expected-min-count 1)
49 (expected-max-count expected-min-count))
50 (let ((handle (gensym)))
51 `(%assert-signal
52 (lambda (,handle)
53 (handler-bind ((,condition-type ,handle)) ,form))
54 ',condition-type ,expected-min-count ,expected-max-count)))
56 (defun %assert-no-signal (thunk condition-type)
57 (declare (ignore condition-type))
58 (let ((signaled-condition))
59 (prog1
60 (funcall thunk (lambda (condition)
61 (setf signaled-condition condition)
62 (when (typep condition 'warning)
63 (muffle-warning condition))))
64 (assert (not signaled-condition)))))
66 (defmacro assert-no-signal (form &optional (condition-type 'condition))
67 (let ((handle (gensym)))
68 `(%assert-no-signal
69 (lambda (,handle)
70 (handler-bind ((,condition-type ,handle)) ,form))
71 ',condition-type)))
73 ;;; EXPR is an expression to evaluate (both with EVAL and with
74 ;;; COMPILE/FUNCALL). EXTRA-OPTIMIZATIONS is a list of lists of
75 ;;; optimizations to pass to (DECLARE (OPTIMIZE ..)), to cause the
76 ;;; expression to be tested in other than the default optimization
77 ;;; level(s).
78 ;;;
79 ;;; The messiness with the various flavors of EXPECTED stuff is
80 ;;; to handle various issues:
81 ;;; * Some things are expected to signal errors instead of returning
82 ;;; ordinary values.
83 ;;; * Some things are expected to return multiple values.
84 ;;; * Some things can return any of several values (e.g. generalized
85 ;;; booleans).
86 ;;; The default is to expect a generalized boolean true.
87 ;;;
88 ;;; Use EXPECTED-LAMBDA to require an answer which satisfies the given
89 ;;; LAMBDA. EXPECTED-EQL, EXPECTED-EQUAL, and EXPECTED-EQUALP are
90 ;;; shorthand for special cases of EXPECTED-LAMBDA.
91 ;;;
92 ;;; Use EXPECTED-ERROR to require an error to be signalled. Use
93 ;;; EXPECTED-ERROR-LAMBDA to require that an error be signalled and
94 ;;; that further it satisfies the given lambda.
95 (defmacro assertoid (expr
96 &key
97 extra-optimizations
98 (expected-eql nil expected-eql-p)
99 (expected-equal nil expected-equal-p)
100 (expected-equalp nil expected-equalp-p)
101 (expected-lambda (cond
102 (expected-eql-p
103 (lambda (x)
104 (eql x (eval expected-eql))))
105 (expected-equal-p
106 (lambda (x)
107 (equal x (eval expected-equal))))
108 (expected-equalp-p
109 (lambda (x)
110 (equalp x (eval expected-equalp))))
112 (lambda (x)
113 x)))
114 expected-lambda-p)
115 (expected-error-type nil expected-error-type-p)
116 (expected-error-lambda (if expected-error-type
117 (lambda (condition)
118 (typep condition
119 expected-error-type))
120 nil)
121 expected-error-lambda-p))
122 (when (> (count-if #'identity
123 (vector expected-eql-p
124 expected-equal-p
125 expected-equalp-p
126 expected-lambda-p
127 expected-error-type-p
128 expected-error-lambda-p))
130 (error "multiple EXPECTED-FOO arguments"))
131 (when expected-error-lambda
132 (error "stub: expected-error functionality not supported yet"))
133 (let ((eval-expected-lambda (eval expected-lambda)))
134 (flet ((frob (evaloid)
135 (let ((result (funcall evaloid expr)))
136 (unless (funcall eval-expected-lambda result)
137 (error "failed assertoid ~S" expr))))
138 (compile-as-evaloid (optimizations)
139 (lambda (expr)
140 (funcall (compile nil
141 `(lambda ()
142 (declare (optimize ,@optimizations))
143 ,expr))))))
144 (frob #'eval)
145 (frob (compile-as-evaloid ()))
146 (dolist (i extra-optimizations)
147 (frob (compile-as-evaloid i))))))
149 ;;; examples
150 (assertoid (= 2 (length (list 1 2))))
151 (assertoid (= 2 (length (list 1 2)))
152 :extra-optimizations (((speed 2) (space 3))
153 ((speed 1) (space 3))))
154 (assertoid (cons 1 2)
155 :expected-lambda (lambda (x) (equal x '(1 . 2))))
156 (assertoid (cons (list 1 2) (list 1 2))
157 :expected-equal '((1 2) 1 2))
158 ;;; not implemented yet:
159 #+nil (assertoid (length (eval (find-package :cl)))
160 :expected-error-type 'type-error)
162 (defmacro is (form)
163 (if (consp form)
164 (destructuring-bind (op expected real) form
165 `(let ((expected-value ,expected)
166 (real-value ,real))
167 (unless (,op expected-value real-value)
168 (error "Wanted ~S, got ~S:~% ~S"
169 expected-value real-value ',form))))
170 `(unless ,form
171 (error "~S evaluated to NIL" ',form))))
173 ;; Return T if two sexprs are EQUAL, considering uninterned symbols
174 ;; in expression A as EQ to one in B provided that there exists a
175 ;; mapping that makes the forms EQUAL.
176 ;; This is helpful when testing complicated macroexpanders.
177 ;; Note that this is much simpler than unification,
178 ;; because symbols can only be replaced by other symbols.
179 (defun equal-mod-gensyms (a b &optional (pred #'equal))
180 (let ((subst-table (make-hash-table :test 'eq)))
181 (labels ((recurse (a b)
182 (cond ((and (consp a) (consp b))
183 (and (recurse (car a) (car b))
184 (recurse (cdr a) (cdr b))))
185 ((and (symbolp a) (symbolp b))
186 (multiple-value-bind (replacement found)
187 (gethash a subst-table a)
188 (or (eq replacement b)
189 (and (not found)
190 (not (symbol-package a))
191 (setf (gethash a subst-table) b)))))
192 (t ; strings, numbers
193 (funcall pred a b)))))
194 (recurse a b))))
196 (defun legacy-eval-p ()
197 (and (eq sb-ext:*evaluator-mode* :interpret)
198 (find-package "SB-EVAL")))
200 (defmacro check-function-evaluation-order (form)
201 (let ((evals (gensym "EVALS"))
202 expected)
203 `(let ((,evals))
204 (multiple-value-prog1
205 (,(car form)
206 ,@(loop for i from 0
207 for arg in (cdr form)
208 collect `(progn
209 (push ,i ,evals)
210 ,arg)
212 (push i expected)))
213 (assert (equal ,evals ',expected)
214 () 'simple-error
215 :format-control "Bad evaluation order of ~s:~% ~s"
216 :format-arguments (list ',form
217 (reverse ,evals)))))))