annotate src/clojure/test_clojure/evaluation.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
rev   line source
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)