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
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
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"
17 (:export
"GRAB-CONDITION" "ASSERT-ERROR"
18 "HAS-ERROR?" "IS" "ASSERTOID"
19 "ASSERT-SIGNAL" "ASSERT-NO-SIGNAL"
21 "EQUAL-MOD-GENSYMS" "CHECK-FUNCTION-EVALUATION-ORDER"))
23 (cl:in-package
"ASSERTOID")
25 (defmacro grab-condition
(&body body
)
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
))
40 (funcall thunk
(lambda (condition)
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)))
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))
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)))
70 (handler-bind ((,condition-type
,handle
)) ,form
))
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
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
83 ;;; * Some things are expected to return multiple values.
84 ;;; * Some things can return any of several values (e.g. generalized
86 ;;; The default is to expect a generalized boolean true.
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.
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
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
104 (eql x
(eval expected-eql
))))
107 (equal x
(eval expected-equal
))))
110 (equalp x
(eval expected-equalp
))))
115 (expected-error-type nil expected-error-type-p
)
116 (expected-error-lambda (if expected-error-type
119 expected-error-type
))
121 expected-error-lambda-p
))
122 (when (> (count-if #'identity
123 (vector expected-eql-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)
140 (funcall (compile nil
142 (declare (optimize ,@optimizations
))
145 (frob (compile-as-evaloid ()))
146 (dolist (i extra-optimizations
)
147 (frob (compile-as-evaloid i
))))))
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
)
164 (destructuring-bind (op expected real
) form
165 `(let ((expected-value ,expected
)
167 (unless (,op expected-value real-value
)
168 (error "Wanted ~S, got ~S:~% ~S"
169 expected-value real-value
',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
)
190 (not (symbol-package a
))
191 (setf (gethash a subst-table
) b
)))))
192 (t ; strings, numbers
193 (funcall pred 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"))
204 (multiple-value-prog1
207 for arg in
(cdr form
)
213 (assert (equal ,evals
',expected
)
215 :format-control
"Bad evaluation order of ~s:~% ~s"
216 :format-arguments
(list ',form
217 (reverse ,evals
)))))))