annotate src/clojure/test_clojure/protocols.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 ; Author: Stuart Halloway
rlm@10 10
rlm@10 11 (ns clojure.test-clojure.protocols
rlm@10 12 (:use clojure.test clojure.test-clojure.protocols.examples)
rlm@10 13 (:require [clojure.test-clojure.protocols.more-examples :as other]
rlm@10 14 [clojure.set :as set]
rlm@10 15 clojure.test-clojure.helpers)
rlm@10 16 (:import [clojure.test_clojure.protocols.examples ExampleInterface]))
rlm@10 17
rlm@10 18 ;; temporary hack until I decide how to cleanly reload protocol
rlm@10 19 (defn reload-example-protocols
rlm@10 20 []
rlm@10 21 (alter-var-root #'clojure.test-clojure.protocols.examples/ExampleProtocol
rlm@10 22 assoc :impls {})
rlm@10 23 (alter-var-root #'clojure.test-clojure.protocols.more-examples/SimpleProtocol
rlm@10 24 assoc :impls {})
rlm@10 25 (require :reload
rlm@10 26 'clojure.test-clojure.protocols.examples
rlm@10 27 'clojure.test-clojure.protocols.more-examples))
rlm@10 28
rlm@10 29 (defn method-names
rlm@10 30 "return sorted list of method names on a class"
rlm@10 31 [c]
rlm@10 32 (->> (.getMethods c)
rlm@10 33 (map #(.getName %))
rlm@10 34 (sort)))
rlm@10 35
rlm@10 36 (defrecord EmptyRecord [])
rlm@10 37 (defrecord TestRecord [a b])
rlm@10 38 (defn r
rlm@10 39 ([a b] (TestRecord. a b))
rlm@10 40 ([a b meta ext] (TestRecord. a b meta ext)))
rlm@10 41 (defrecord MapEntry [k v]
rlm@10 42 java.util.Map$Entry
rlm@10 43 (getKey [_] k)
rlm@10 44 (getValue [_] v))
rlm@10 45
rlm@10 46 (deftest protocols-test
rlm@10 47 (testing "protocol fns have useful metadata"
rlm@10 48 (let [common-meta {:ns (find-ns 'clojure.test-clojure.protocols.examples)
rlm@10 49 :protocol #'ExampleProtocol}]
rlm@10 50 (are [m f] (= (merge (quote m) common-meta)
rlm@10 51 (meta (var f)))
rlm@10 52 {:name foo :arglists ([a]) :doc "method with one arg"} foo
rlm@10 53 {:name bar :arglists ([a b]) :doc "method with two args"} bar
rlm@10 54 {:name baz :arglists ([a] [a b]) :doc "method with multiple arities" :tag String} baz
rlm@10 55 {:name with-quux :arglists ([a]) :doc "method name with a hyphen"} with-quux)))
rlm@10 56 (testing "protocol fns throw IllegalArgumentException if no impl matches"
rlm@10 57 (is (thrown-with-msg?
rlm@10 58 IllegalArgumentException
rlm@10 59 #"No implementation of method: :foo of protocol: #'clojure.test-clojure.protocols.examples/ExampleProtocol found for class: java.lang.Integer"
rlm@10 60 (foo 10))))
rlm@10 61 (testing "protocols generate a corresponding interface using _ instead of - for method names"
rlm@10 62 (is (= ["bar" "baz" "baz" "foo" "with_quux"] (method-names clojure.test_clojure.protocols.examples.ExampleProtocol))))
rlm@10 63 (testing "protocol will work with instances of its interface (use for interop, not in Clojure!)"
rlm@10 64 (let [obj (proxy [clojure.test_clojure.protocols.examples.ExampleProtocol] []
rlm@10 65 (foo [] "foo!"))]
rlm@10 66 (is (= "foo!" (.foo obj)) "call through interface")
rlm@10 67 (is (= "foo!" (foo obj)) "call through protocol")))
rlm@10 68 (testing "you can implement just part of a protocol if you want"
rlm@10 69 (let [obj (reify ExampleProtocol
rlm@10 70 (baz [a b] "two-arg baz!"))]
rlm@10 71 (is (= "two-arg baz!" (baz obj nil)))
rlm@10 72 (is (thrown? AbstractMethodError (baz obj)))))
rlm@10 73 (testing "you can redefine a protocol with different methods"
rlm@10 74 (eval '(defprotocol Elusive (old-method [x])))
rlm@10 75 (eval '(defprotocol Elusive (new-method [x])))
rlm@10 76 (is (= :new-method (eval '(new-method (reify Elusive (new-method [x] :new-method))))))
rlm@10 77 (is (fails-with-cause? IllegalArgumentException #"No method of interface: user\.Elusive found for function: old-method of protocol: Elusive \(The protocol method may have been defined before and removed\.\)"
rlm@10 78 (eval '(old-method (reify Elusive (new-method [x] :new-method))))))))
rlm@10 79
rlm@10 80 (deftype ExtendTestWidget [name])
rlm@10 81 (deftype HasProtocolInline []
rlm@10 82 ExampleProtocol
rlm@10 83 (foo [this] :inline))
rlm@10 84 (deftest extend-test
rlm@10 85 (testing "you can extend a protocol to a class"
rlm@10 86 (extend String ExampleProtocol
rlm@10 87 {:foo identity})
rlm@10 88 (is (= "pow" (foo "pow"))))
rlm@10 89 (testing "you can have two methods with the same name. Just use namespaces!"
rlm@10 90 (extend String other/SimpleProtocol
rlm@10 91 {:foo (fn [s] (.toUpperCase s))})
rlm@10 92 (is (= "POW" (other/foo "pow"))))
rlm@10 93 (testing "you can extend deftype types"
rlm@10 94 (extend
rlm@10 95 ExtendTestWidget
rlm@10 96 ExampleProtocol
rlm@10 97 {:foo (fn [this] (str "widget " (.name this)))})
rlm@10 98 (is (= "widget z" (foo (ExtendTestWidget. "z"))))))
rlm@10 99
rlm@10 100 (deftest illegal-extending
rlm@10 101 (testing "you cannot extend a protocol to a type that implements the protocol inline"
rlm@10 102 (is (fails-with-cause? IllegalArgumentException #".*HasProtocolInline already directly implements interface"
rlm@10 103 (eval '(extend clojure.test-clojure.protocols.HasProtocolInline
rlm@10 104 clojure.test-clojure.protocols.examples/ExampleProtocol
rlm@10 105 {:foo (fn [_] :extended)})))))
rlm@10 106 (testing "you cannot extend to an interface"
rlm@10 107 (is (fails-with-cause? IllegalArgumentException #"interface clojure.test_clojure.protocols.examples.ExampleProtocol is not a protocol"
rlm@10 108 (eval '(extend clojure.test-clojure.protocols.HasProtocolInline
rlm@10 109 clojure.test_clojure.protocols.examples.ExampleProtocol
rlm@10 110 {:foo (fn [_] :extended)}))))))
rlm@10 111
rlm@10 112 (deftype ExtendsTestWidget []
rlm@10 113 ExampleProtocol)
rlm@10 114 (deftest extends?-test
rlm@10 115 (reload-example-protocols)
rlm@10 116 (testing "returns false if a type does not implement the protocol at all"
rlm@10 117 (is (false? (extends? other/SimpleProtocol ExtendsTestWidget))))
rlm@10 118 (testing "returns true if a type implements the protocol directly" ;; semantics changed 4/15/2010
rlm@10 119 (is (true? (extends? ExampleProtocol ExtendsTestWidget))))
rlm@10 120 (testing "returns true if a type explicitly extends protocol"
rlm@10 121 (extend
rlm@10 122 ExtendsTestWidget
rlm@10 123 other/SimpleProtocol
rlm@10 124 {:foo identity})
rlm@10 125 (is (true? (extends? other/SimpleProtocol ExtendsTestWidget)))))
rlm@10 126
rlm@10 127 (deftype ExtendersTestWidget [])
rlm@10 128 (deftest extenders-test
rlm@10 129 (reload-example-protocols)
rlm@10 130 (testing "a fresh protocol has no extenders"
rlm@10 131 (is (nil? (extenders ExampleProtocol))))
rlm@10 132 (testing "extending with no methods doesn't count!"
rlm@10 133 (deftype Something [])
rlm@10 134 (extend ::Something ExampleProtocol)
rlm@10 135 (is (nil? (extenders ExampleProtocol))))
rlm@10 136 (testing "extending a protocol (and including an impl) adds an entry to extenders"
rlm@10 137 (extend ExtendersTestWidget ExampleProtocol {:foo identity})
rlm@10 138 (is (= [ExtendersTestWidget] (extenders ExampleProtocol)))))
rlm@10 139
rlm@10 140 (deftype SatisfiesTestWidget []
rlm@10 141 ExampleProtocol)
rlm@10 142 (deftest satisifies?-test
rlm@10 143 (reload-example-protocols)
rlm@10 144 (let [whatzit (SatisfiesTestWidget.)]
rlm@10 145 (testing "returns false if a type does not implement the protocol at all"
rlm@10 146 (is (false? (satisfies? other/SimpleProtocol whatzit))))
rlm@10 147 (testing "returns true if a type implements the protocol directly"
rlm@10 148 (is (true? (satisfies? ExampleProtocol whatzit))))
rlm@10 149 (testing "returns true if a type explicitly extends protocol"
rlm@10 150 (extend
rlm@10 151 SatisfiesTestWidget
rlm@10 152 other/SimpleProtocol
rlm@10 153 {:foo identity})
rlm@10 154 (is (true? (satisfies? other/SimpleProtocol whatzit))))) )
rlm@10 155
rlm@10 156 (deftype ReExtendingTestWidget [])
rlm@10 157 (deftest re-extending-test
rlm@10 158 (reload-example-protocols)
rlm@10 159 (extend
rlm@10 160 ReExtendingTestWidget
rlm@10 161 ExampleProtocol
rlm@10 162 {:foo (fn [_] "first foo")
rlm@10 163 :baz (fn [_] "first baz")})
rlm@10 164 (testing "if you re-extend, the old implementation is replaced (not merged!)"
rlm@10 165 (extend
rlm@10 166 ReExtendingTestWidget
rlm@10 167 ExampleProtocol
rlm@10 168 {:baz (fn [_] "second baz")
rlm@10 169 :bar (fn [_ _] "second bar")})
rlm@10 170 (let [whatzit (ReExtendingTestWidget.)]
rlm@10 171 (is (thrown? IllegalArgumentException (foo whatzit)))
rlm@10 172 (is (= "second bar" (bar whatzit nil)))
rlm@10 173 (is (= "second baz" (baz whatzit))))))
rlm@10 174
rlm@10 175 (defrecord DefrecordObjectMethodsWidgetA [a])
rlm@10 176 (defrecord DefrecordObjectMethodsWidgetB [a])
rlm@10 177 (deftest defrecord-object-methods-test
rlm@10 178 (testing "= depends on fields and type"
rlm@10 179 (is (true? (= (DefrecordObjectMethodsWidgetA. 1) (DefrecordObjectMethodsWidgetA. 1))))
rlm@10 180 (is (false? (= (DefrecordObjectMethodsWidgetA. 1) (DefrecordObjectMethodsWidgetA. 2))))
rlm@10 181 (is (false? (= (DefrecordObjectMethodsWidgetA. 1) (DefrecordObjectMethodsWidgetB. 1))))))
rlm@10 182
rlm@10 183 (deftest defrecord-acts-like-a-map
rlm@10 184 (let [rec (r 1 2)]
rlm@10 185 (is (.equals (r 1 3 {} {:c 4}) (merge rec {:b 3 :c 4})))
rlm@10 186 (is (.equals {:foo 1 :b 2} (set/rename-keys rec {:a :foo})))
rlm@10 187 (is (.equals {:a 11 :b 2 :c 10} (merge-with + rec {:a 10 :c 10})))))
rlm@10 188
rlm@10 189 (deftest degenerate-defrecord-test
rlm@10 190 (let [empty (EmptyRecord.)]
rlm@10 191 (is (nil? (seq empty)))
rlm@10 192 (is (not (.containsValue empty :a)))))
rlm@10 193
rlm@10 194 (deftest defrecord-interfaces-test
rlm@10 195 (testing "java.util.Map"
rlm@10 196 (let [rec (r 1 2)]
rlm@10 197 (is (= 2 (.size rec)))
rlm@10 198 (is (= 3 (.size (assoc rec :c 3))))
rlm@10 199 (is (not (.isEmpty rec)))
rlm@10 200 (is (.isEmpty (EmptyRecord.)))
rlm@10 201 (is (.containsKey rec :a))
rlm@10 202 (is (not (.containsKey rec :c)))
rlm@10 203 (is (.containsValue rec 1))
rlm@10 204 (is (not (.containsValue rec 3)))
rlm@10 205 (is (= 1 (.get rec :a)))
rlm@10 206 (is (thrown? UnsupportedOperationException (.put rec :a 1)))
rlm@10 207 (is (thrown? UnsupportedOperationException (.remove rec :a)))
rlm@10 208 (is (thrown? UnsupportedOperationException (.putAll rec {})))
rlm@10 209 (is (thrown? UnsupportedOperationException (.clear rec)))
rlm@10 210 (is (= #{:a :b} (.keySet rec)))
rlm@10 211 (is (= #{1 2} (set (.values rec))))
rlm@10 212 (is (= #{[:a 1] [:b 2]} (.entrySet rec)))
rlm@10 213
rlm@10 214 ))
rlm@10 215 (testing "IPersistentCollection"
rlm@10 216 (testing ".cons"
rlm@10 217 (let [rec (r 1 2)]
rlm@10 218 (are [x] (= rec (.cons rec x))
rlm@10 219 nil {})
rlm@10 220 (is (= (r 1 3) (.cons rec {:b 3})))
rlm@10 221 (is (= (r 1 4) (.cons rec [:b 4])))
rlm@10 222 (is (= (r 1 5) (.cons rec (MapEntry. :b 5))))))))
rlm@10 223
rlm@10 224 (defrecord RecordWithSpecificFieldNames [this that k m o])
rlm@10 225 (deftest defrecord-with-specific-field-names
rlm@10 226 (let [rec (new RecordWithSpecificFieldNames 1 2 3 4 5)]
rlm@10 227 (is (= rec rec))
rlm@10 228 (is (= 1 (:this (with-meta rec {:foo :bar}))))
rlm@10 229 (is (= 3 (get rec :k)))
rlm@10 230 (is (= (seq rec) '([:this 1] [:that 2] [:k 3] [:m 4] [:o 5])))
rlm@10 231 (is (= (dissoc rec :k) {:this 1, :that 2, :m 4, :o 5}))))
rlm@10 232
rlm@10 233 (deftest reify-test
rlm@10 234 (testing "of an interface"
rlm@10 235 (let [s :foo
rlm@10 236 r (reify
rlm@10 237 java.util.List
rlm@10 238 (contains [_ o] (= s o)))]
rlm@10 239 (testing "implemented methods"
rlm@10 240 (is (true? (.contains r :foo)))
rlm@10 241 (is (false? (.contains r :bar))))
rlm@10 242 (testing "unimplemented methods"
rlm@10 243 (is (thrown? AbstractMethodError (.add r :baz))))))
rlm@10 244 (testing "of two interfaces"
rlm@10 245 (let [r (reify
rlm@10 246 java.util.List
rlm@10 247 (contains [_ o] (= :foo o))
rlm@10 248 java.util.Collection
rlm@10 249 (isEmpty [_] false))]
rlm@10 250 (is (true? (.contains r :foo)))
rlm@10 251 (is (false? (.contains r :bar)))
rlm@10 252 (is (false? (.isEmpty r)))))
rlm@10 253 (testing "you can't define a method twice"
rlm@10 254 (is (fails-with-cause?
rlm@10 255 java.lang.ClassFormatError #"^(Repetitive|Duplicate) method name"
rlm@10 256 (eval '(reify
rlm@10 257 java.util.List
rlm@10 258 (size [_] 10)
rlm@10 259 java.util.Collection
rlm@10 260 (size [_] 20))))))
rlm@10 261 (testing "you can't define a method not on an interface/protocol/j.l.Object"
rlm@10 262 (is (fails-with-cause?
rlm@10 263 IllegalArgumentException #"^Can't define method not in interfaces: foo"
rlm@10 264 (eval '(reify java.util.List (foo [_]))))))
rlm@10 265 (testing "of a protocol"
rlm@10 266 (let [r (reify
rlm@10 267 ExampleProtocol
rlm@10 268 (bar [this o] o)
rlm@10 269 (baz [this] 1)
rlm@10 270 (baz [this o] 2))]
rlm@10 271 (= :foo (.bar r :foo))
rlm@10 272 (= 1 (.baz r))
rlm@10 273 (= 2 (.baz r nil))))
rlm@10 274 (testing "destructuring in method def"
rlm@10 275 (let [r (reify
rlm@10 276 ExampleProtocol
rlm@10 277 (bar [this [_ _ item]] item))]
rlm@10 278 (= :c (.bar r [:a :b :c]))))
rlm@10 279 (testing "methods can recur"
rlm@10 280 (let [r (reify
rlm@10 281 java.util.List
rlm@10 282 (get [_ index]
rlm@10 283 (if (zero? index)
rlm@10 284 :done
rlm@10 285 (recur (dec index)))))]
rlm@10 286 (is (= :done (.get r 0)))
rlm@10 287 (is (= :done (.get r 1)))))
rlm@10 288 (testing "disambiguating with type hints"
rlm@10 289 (testing "you must hint an overloaded method"
rlm@10 290 (is (fails-with-cause?
rlm@10 291 IllegalArgumentException #"Must hint overloaded method: hinted"
rlm@10 292 (eval '(reify clojure.test_clojure.protocols.examples.ExampleInterface (hinted [_ o]))))))
rlm@10 293 (testing "hinting"
rlm@10 294 (let [r (reify
rlm@10 295 ExampleInterface
rlm@10 296 (hinted [_ ^int i] (inc i))
rlm@10 297 (hinted [_ ^String s] (str s s)))]
rlm@10 298 (is (= 2 (.hinted r 1)))
rlm@10 299 (is (= "xoxo" (.hinted r "xo")))))))
rlm@10 300