Use short form of DEFSETF for GET and GETHASH
[sbcl.git] / tests / assertoid.lisp
blob8cdfae4df852750bbf1a792b33b0461b4194530e
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 "EQUAL-MOD-GENSYMS"))
22 (cl:in-package "ASSERTOID")
24 (defmacro grab-condition (&body body)
25 `(nth-value 1
26 (ignore-errors ,@body)))
28 (defmacro has-error? (form &optional (error-subtype-spec 'error))
29 `(typep (nth-value 1 (ignore-errors ,form)) ',error-subtype-spec))
31 (defmacro assert-error (form &optional (error-subtype-spec 'error))
32 `(assert (typep (nth-value 1 (ignore-errors ,form)) ',error-subtype-spec)))
34 (defmacro assert-signal (form &optional (signal-type 'condition))
35 (let ((signal (gensym)))
36 `(let (,signal)
37 (handler-bind ((,signal-type (lambda (c)
38 (setf ,signal c))))
39 ,form)
40 (assert ,signal))))
42 (defmacro assert-no-signal (form &optional (signal-type 'condition))
43 (let ((signal (gensym)))
44 `(let (,signal)
45 (handler-bind ((,signal-type (lambda (c)
46 (setf ,signal c))))
47 ,form)
48 (assert (not ,signal)))))
50 ;;; EXPR is an expression to evaluate (both with EVAL and with
51 ;;; COMPILE/FUNCALL). EXTRA-OPTIMIZATIONS is a list of lists of
52 ;;; optimizations to pass to (DECLARE (OPTIMIZE ..)), to cause the
53 ;;; expression to be tested in other than the default optimization
54 ;;; level(s).
55 ;;;
56 ;;; The messiness with the various flavors of EXPECTED stuff is
57 ;;; to handle various issues:
58 ;;; * Some things are expected to signal errors instead of returning
59 ;;; ordinary values.
60 ;;; * Some things are expected to return multiple values.
61 ;;; * Some things can return any of several values (e.g. generalized
62 ;;; booleans).
63 ;;; The default is to expect a generalized boolean true.
64 ;;;
65 ;;; Use EXPECTED-LAMBDA to require an answer which satisfies the given
66 ;;; LAMBDA. EXPECTED-EQL, EXPECTED-EQUAL, and EXPECTED-EQUALP are
67 ;;; shorthand for special cases of EXPECTED-LAMBDA.
68 ;;;
69 ;;; Use EXPECTED-ERROR to require an error to be signalled. Use
70 ;;; EXPECTED-ERROR-LAMBDA to require that an error be signalled and
71 ;;; that further it satisfies the given lambda.
72 (defmacro assertoid (expr
73 &key
74 extra-optimizations
75 (expected-eql nil expected-eql-p)
76 (expected-equal nil expected-equal-p)
77 (expected-equalp nil expected-equalp-p)
78 (expected-lambda (cond
79 (expected-eql-p
80 (lambda (x)
81 (eql x (eval expected-eql))))
82 (expected-equal-p
83 (lambda (x)
84 (equal x (eval expected-equal))))
85 (expected-equalp-p
86 (lambda (x)
87 (equalp x (eval expected-equalp))))
89 (lambda (x)
90 x)))
91 expected-lambda-p)
92 (expected-error-type nil expected-error-type-p)
93 (expected-error-lambda (if expected-error-type
94 (lambda (condition)
95 (typep condition
96 expected-error-type))
97 nil)
98 expected-error-lambda-p))
99 (when (> (count-if #'identity
100 (vector expected-eql-p
101 expected-equal-p
102 expected-equalp-p
103 expected-lambda-p
104 expected-error-type-p
105 expected-error-lambda-p))
107 (error "multiple EXPECTED-FOO arguments"))
108 (when expected-error-lambda
109 (error "stub: expected-error functionality not supported yet"))
110 (let ((eval-expected-lambda (eval expected-lambda)))
111 (flet ((frob (evaloid)
112 (let ((result (funcall evaloid expr)))
113 (unless (funcall eval-expected-lambda result)
114 (error "failed assertoid ~S" expr))))
115 (compile-as-evaloid (optimizations)
116 (lambda (expr)
117 (funcall (compile nil
118 `(lambda ()
119 (declare (optimize ,@optimizations))
120 ,expr))))))
121 (frob #'eval)
122 (frob (compile-as-evaloid ()))
123 (dolist (i extra-optimizations)
124 (frob (compile-as-evaloid i))))))
126 ;;; examples
127 (assertoid (= 2 (length (list 1 2))))
128 (assertoid (= 2 (length (list 1 2)))
129 :extra-optimizations (((speed 2) (space 3))
130 ((speed 1) (space 3))))
131 (assertoid (cons 1 2)
132 :expected-lambda (lambda (x) (equal x '(1 . 2))))
133 (assertoid (cons (list 1 2) (list 1 2))
134 :expected-equal '((1 2) 1 2))
135 ;;; not implemented yet:
136 #+nil (assertoid (length (eval (find-package :cl)))
137 :expected-error-type 'type-error)
139 (defmacro is (form)
140 (if (consp form)
141 (destructuring-bind (op expected real) form
142 `(let ((expected-value ,expected)
143 (real-value ,real))
144 (unless (,op expected-value real-value)
145 (error "Wanted ~S, got ~S:~% ~S"
146 expected-value real-value ',form))))
147 `(unless ,form
148 (error "~S evaluated to NIL" ',form))))
150 ;; Return T if two sexprs are EQUAL, considering uninterned symbols
151 ;; in expression A as EQ to one in B provided that there exists a
152 ;; mapping that makes the forms EQUAL.
153 ;; This is helpful when testing complicated macroexpanders.
154 ;; Note that this is much simpler than unification,
155 ;; because symbols can only be replaced by other symbols.
156 (defun equal-mod-gensyms (a b &optional (pred #'equal))
157 (let ((subst-table (make-hash-table :test 'eq)))
158 (labels ((recurse (a b)
159 (cond ((and (consp a) (consp b))
160 (and (recurse (car a) (car b))
161 (recurse (cdr a) (cdr b))))
162 ((and (symbolp a) (symbolp b))
163 (multiple-value-bind (replacement found)
164 (gethash a subst-table a)
165 (or (eq replacement b)
166 (and (not found)
167 (not (symbol-package a))
168 (setf (gethash a subst-table) b)))))
169 (t ; strings, numbers
170 (funcall pred a b)))))
171 (recurse a b))))