PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/test/clojure/test_clojure/protocols.clj

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