rlm@10
|
1 ; Copyright (c) Rich Hickey. All rights reserved.
|
rlm@10
|
2 ; The use and distribution terms for this software are covered by the
|
rlm@10
|
3 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
|
rlm@10
|
4 ; which can be found in the file epl-v10.html at the root of this distribution.
|
rlm@10
|
5 ; By using this software in any fashion, you are agreeing to be bound by
|
rlm@10
|
6 ; the terms of this license.
|
rlm@10
|
7 ; You must not remove this notice, or any other, from this software.
|
rlm@10
|
8
|
rlm@10
|
9
|
rlm@10
|
10 ;; Tests for the Clojure functions documented at the URL:
|
rlm@10
|
11 ;;
|
rlm@10
|
12 ;; http://clojure.org/Evaluation
|
rlm@10
|
13 ;;
|
rlm@10
|
14 ;; by J. McConnell
|
rlm@10
|
15 ;; Created 22 October 2008
|
rlm@10
|
16
|
rlm@10
|
17 (ns clojure.test-clojure.evaluation
|
rlm@10
|
18 (:use clojure.test))
|
rlm@10
|
19
|
rlm@10
|
20 (import '(java.lang Boolean)
|
rlm@10
|
21 '(clojure.lang Compiler Compiler$CompilerException))
|
rlm@10
|
22
|
rlm@10
|
23 (defmacro test-that
|
rlm@10
|
24 "Provides a useful way for specifying the purpose of tests. If the first-level
|
rlm@10
|
25 forms are lists that make a call to a clojure.test function, it supplies the
|
rlm@10
|
26 purpose as the msg argument to those functions. Otherwise, the purpose just
|
rlm@10
|
27 acts like a comment and the forms are run unchanged."
|
rlm@10
|
28 [purpose & test-forms]
|
rlm@10
|
29 (let [tests (map
|
rlm@10
|
30 #(if (= (:ns (meta (resolve (first %))))
|
rlm@10
|
31 (the-ns 'clojure.test))
|
rlm@10
|
32 (concat % (list purpose))
|
rlm@10
|
33 %)
|
rlm@10
|
34 test-forms)]
|
rlm@10
|
35 `(do ~@tests)))
|
rlm@10
|
36
|
rlm@10
|
37 (deftest Eval
|
rlm@10
|
38 (is (= (eval '(+ 1 2 3)) (Compiler/eval '(+ 1 2 3))))
|
rlm@10
|
39 (is (= (eval '(list 1 2 3)) '(1 2 3)))
|
rlm@10
|
40 (is (= (eval '(list + 1 2 3)) (list clojure.core/+ 1 2 3)))
|
rlm@10
|
41 (test-that "Non-closure fns are supported as code"
|
rlm@10
|
42 (is (= (eval (eval '(list + 1 2 3))) 6)))
|
rlm@10
|
43 (is (= (eval (list '+ 1 2 3)) 6)))
|
rlm@10
|
44
|
rlm@10
|
45 ; not using Clojure's RT/classForName since a bug in it could hide a bug in
|
rlm@10
|
46 ; eval's resolution
|
rlm@10
|
47 (defn class-for-name [name]
|
rlm@10
|
48 (java.lang.Class/forName name))
|
rlm@10
|
49
|
rlm@10
|
50 (defmacro in-test-ns [& body]
|
rlm@10
|
51 `(binding [*ns* *ns*]
|
rlm@10
|
52 (in-ns 'clojure.test-clojure.evaluation)
|
rlm@10
|
53 ~@body))
|
rlm@10
|
54
|
rlm@10
|
55 ;;; Literals tests ;;;
|
rlm@10
|
56
|
rlm@10
|
57 (defmacro #^{:private true} evaluates-to-itself? [expr]
|
rlm@10
|
58 `(let [v# ~expr
|
rlm@10
|
59 q# (quote ~expr)]
|
rlm@10
|
60 (is (= (eval q#) q#) (str q# " does not evaluate to itself"))))
|
rlm@10
|
61
|
rlm@10
|
62 (deftest Literals
|
rlm@10
|
63 ; Strings, numbers, characters, nil and keywords should evaluate to themselves
|
rlm@10
|
64 (evaluates-to-itself? "test")
|
rlm@10
|
65 (evaluates-to-itself? "test
|
rlm@10
|
66 multi-line
|
rlm@10
|
67 string")
|
rlm@10
|
68 (evaluates-to-itself? 1)
|
rlm@10
|
69 (evaluates-to-itself? 1.0)
|
rlm@10
|
70 (evaluates-to-itself? 1.123456789)
|
rlm@10
|
71 (evaluates-to-itself? 1/2)
|
rlm@10
|
72 (evaluates-to-itself? 1M)
|
rlm@10
|
73 (evaluates-to-itself? 999999999999999999)
|
rlm@10
|
74 (evaluates-to-itself? \a)
|
rlm@10
|
75 (evaluates-to-itself? \newline)
|
rlm@10
|
76 (evaluates-to-itself? nil)
|
rlm@10
|
77 (evaluates-to-itself? :test)
|
rlm@10
|
78 ; Boolean literals should evaluate to Boolean.{TRUE|FALSE}
|
rlm@10
|
79 (is (identical? (eval true) Boolean/TRUE))
|
rlm@10
|
80 (is (identical? (eval false) Boolean/FALSE)))
|
rlm@10
|
81
|
rlm@10
|
82 ;;; Symbol resolution tests ;;;
|
rlm@10
|
83
|
rlm@10
|
84 (def foo "abc")
|
rlm@10
|
85 (in-ns 'resolution-test)
|
rlm@10
|
86 (def bar 123)
|
rlm@10
|
87 (def #^{:private true} baz 456)
|
rlm@10
|
88 (in-ns 'clojure.test-clojure.evaluation)
|
rlm@10
|
89
|
rlm@10
|
90 (defn a-match? [re s] (not (nil? (re-matches re s))))
|
rlm@10
|
91
|
rlm@10
|
92 (defmacro throws-with-msg
|
rlm@10
|
93 ([re form] `(throws-with-msg ~re ~form Exception))
|
rlm@10
|
94 ([re form x] `(throws-with-msg
|
rlm@10
|
95 ~re
|
rlm@10
|
96 ~form
|
rlm@10
|
97 ~(if (instance? Exception x) x Exception)
|
rlm@10
|
98 ~(if (instance? String x) x nil)))
|
rlm@10
|
99 ([re form class msg]
|
rlm@10
|
100 `(let [ex# (try
|
rlm@10
|
101 ~form
|
rlm@10
|
102 (catch ~class e# e#)
|
rlm@10
|
103 (catch Exception e#
|
rlm@10
|
104 (let [cause# (.getCause e#)]
|
rlm@10
|
105 (if (= ~class (class cause#)) cause# (throw e#)))))]
|
rlm@10
|
106 (is (a-match? ~re (.toString ex#))
|
rlm@10
|
107 (or ~msg
|
rlm@10
|
108 (str "Expected exception that matched " (pr-str ~re)
|
rlm@10
|
109 ", but got exception with message: \"" ex#))))))
|
rlm@10
|
110
|
rlm@10
|
111 (deftest SymbolResolution
|
rlm@10
|
112 (test-that
|
rlm@10
|
113 "If a symbol is namespace-qualified, the evaluated value is the value
|
rlm@10
|
114 of the binding of the global var named by the symbol"
|
rlm@10
|
115 (is (= (eval 'resolution-test/bar) 123)))
|
rlm@10
|
116
|
rlm@10
|
117 (test-that
|
rlm@10
|
118 "It is an error if there is no global var named by the symbol"
|
rlm@10
|
119 (throws-with-msg
|
rlm@10
|
120 #".*Unable to resolve symbol: bar.*" (eval 'bar)))
|
rlm@10
|
121
|
rlm@10
|
122 (test-that
|
rlm@10
|
123 "It is an error if the symbol reference is to a non-public var in a
|
rlm@10
|
124 different namespace"
|
rlm@10
|
125 (throws-with-msg
|
rlm@10
|
126 #".*resolution-test/baz is not public.*"
|
rlm@10
|
127 (eval 'resolution-test/baz)
|
rlm@10
|
128 Compiler$CompilerException))
|
rlm@10
|
129
|
rlm@10
|
130 (test-that
|
rlm@10
|
131 "If a symbol is package-qualified, its value is the Java class named by the
|
rlm@10
|
132 symbol"
|
rlm@10
|
133 (is (= (eval 'java.lang.Math) (class-for-name "java.lang.Math"))))
|
rlm@10
|
134
|
rlm@10
|
135 (test-that
|
rlm@10
|
136 "If a symbol is package-qualified, it is an error if there is no Class named
|
rlm@10
|
137 by the symbol"
|
rlm@10
|
138 (is (thrown? Compiler$CompilerException (eval 'java.lang.FooBar))))
|
rlm@10
|
139
|
rlm@10
|
140 (test-that
|
rlm@10
|
141 "If a symbol is not qualified, the following applies, in this order:
|
rlm@10
|
142
|
rlm@10
|
143 1. If it names a special form it is considered a special form, and must
|
rlm@10
|
144 be utilized accordingly.
|
rlm@10
|
145
|
rlm@10
|
146 2. A lookup is done in the current namespace to see if there is a mapping
|
rlm@10
|
147 from the symbol to a class. If so, the symbol is considered to name a
|
rlm@10
|
148 Java class object.
|
rlm@10
|
149
|
rlm@10
|
150 3. If in a local scope (i.e. in a function definition), a lookup is done
|
rlm@10
|
151 to see if it names a local binding (e.g. a function argument or
|
rlm@10
|
152 let-bound name). If so, the value is the value of the local binding.
|
rlm@10
|
153
|
rlm@10
|
154 4. A lookup is done in the current namespace to see if there is a mapping
|
rlm@10
|
155 from the symbol to a var. If so, the value is the value of the binding
|
rlm@10
|
156 of the var referred-to by the symbol.
|
rlm@10
|
157
|
rlm@10
|
158 5. It is an error."
|
rlm@10
|
159
|
rlm@10
|
160 ; First
|
rlm@10
|
161 (doall (for [form '(def if do let quote var fn loop recur throw try
|
rlm@10
|
162 monitor-enter monitor-exit)]
|
rlm@10
|
163 (is (thrown? Compiler$CompilerException (eval form)))))
|
rlm@10
|
164 (let [if "foo"]
|
rlm@10
|
165 (is (thrown? Compiler$CompilerException (eval 'if)))
|
rlm@10
|
166
|
rlm@10
|
167 ; Second
|
rlm@10
|
168 (is (= (eval 'Boolean) (class-for-name "java.lang.Boolean"))))
|
rlm@10
|
169 (let [Boolean "foo"]
|
rlm@10
|
170 (is (= (eval 'Boolean) (class-for-name "java.lang.Boolean"))))
|
rlm@10
|
171
|
rlm@10
|
172 ; Third
|
rlm@10
|
173 (is (= (eval '(let [foo "bar"] foo)) "bar"))
|
rlm@10
|
174
|
rlm@10
|
175 ; Fourth
|
rlm@10
|
176 (in-test-ns (is (= (eval 'foo) "abc")))
|
rlm@10
|
177 (is (thrown? Compiler$CompilerException (eval 'bar))) ; not in this namespace
|
rlm@10
|
178
|
rlm@10
|
179 ; Fifth
|
rlm@10
|
180 (is (thrown? Compiler$CompilerException (eval 'foobar)))))
|
rlm@10
|
181
|
rlm@10
|
182 ;;; Metadata tests ;;;
|
rlm@10
|
183
|
rlm@10
|
184 (defstruct struct-with-symbols (with-meta 'k {:a "A"}))
|
rlm@10
|
185
|
rlm@10
|
186 (deftest Metadata
|
rlm@10
|
187
|
rlm@10
|
188 (test-that
|
rlm@10
|
189 "find returns key symbols and their metadata"
|
rlm@10
|
190 (let [s (struct struct-with-symbols 1)]
|
rlm@10
|
191 (is (= {:a "A"} (meta (first (find s 'k))))))))
|
rlm@10
|
192
|
rlm@10
|
193 ;;; Collections tests ;;;
|
rlm@10
|
194 (def x 1)
|
rlm@10
|
195 (def y 2)
|
rlm@10
|
196
|
rlm@10
|
197 (deftest Collections
|
rlm@10
|
198 (in-test-ns
|
rlm@10
|
199 (test-that
|
rlm@10
|
200 "Vectors and Maps yield vectors and (hash) maps whose contents are the
|
rlm@10
|
201 evaluated values of the objects they contain."
|
rlm@10
|
202 (is (= (eval '[x y 3]) [1 2 3]))
|
rlm@10
|
203 (is (= (eval '{:x x :y y :z 3}) {:x 1 :y 2 :z 3}))
|
rlm@10
|
204 (is (instance? clojure.lang.IPersistentMap (eval '{:x x :y y})))))
|
rlm@10
|
205
|
rlm@10
|
206 (in-test-ns
|
rlm@10
|
207 (test-that
|
rlm@10
|
208 "Metadata maps yield maps whose contents are the evaluated values of
|
rlm@10
|
209 the objects they contain. If a vector or map has metadata, the evaluated
|
rlm@10
|
210 metadata map will become the metadata of the resulting value."
|
rlm@10
|
211 (is (= (eval #^{:x x} '[x y]) #^{:x 1} [1 2]))))
|
rlm@10
|
212
|
rlm@10
|
213 (test-that
|
rlm@10
|
214 "An empty list () evaluates to an empty list."
|
rlm@10
|
215 (is (= (eval '()) ()))
|
rlm@10
|
216 (is (empty? (eval ())))
|
rlm@10
|
217 (is (= (eval (list)) ())))
|
rlm@10
|
218
|
rlm@10
|
219 (test-that
|
rlm@10
|
220 "Non-empty lists are considered calls"
|
rlm@10
|
221 (is (thrown? Compiler$CompilerException (eval '(1 2 3))))))
|
rlm@10
|
222
|
rlm@10
|
223 (deftest Macros)
|
rlm@10
|
224
|
rlm@10
|
225 (deftest Loading)
|