blob: 85abd195e8315303d86079135c9067d419c09712 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
; Copyright (c) Rich Hickey. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
; which can be found in the file epl-v10.html at the root of this distribution.
; By using this software in any fashion, you are agreeing to be bound by
; the terms of this license.
; You must not remove this notice, or any other, from this software.
; Author: Stuart Halloway
(ns clojure.test-clojure.protocols
(:use clojure.test clojure.test-clojure.protocols.examples)
(:require [clojure.test-clojure.protocols.more-examples :as other]))
;; temporary hack until I decide how to cleanly reload protocol
(defn reload-example-protocols
[]
(alter-var-root #'clojure.test-clojure.protocols.examples/ExampleProtocol
assoc :impls {})
(alter-var-root #'clojure.test-clojure.protocols.more-examples/SimpleProtocol
assoc :impls {})
(require :reload
'clojure.test-clojure.protocols.examples
'clojure.test-clojure.protocols.more-examples))
(defn method-names
"return sorted list of method names on a class"
[c]
(->> (.getMethods c)
(map #(.getName %))
(sort)))
(deftest protocols-test
(testing "protocol fns throw IllegalArgumentException if no impl matches"
(is (thrown-with-msg?
IllegalArgumentException
#"No implementation of method: :foo of protocol: #'clojure.test-clojure.protocols.examples/ExampleProtocol found for class: java.lang.Integer"
(foo 10))))
(testing "protocols generate a corresponding interface using _ instead of - for method names"
(is (= ["bar" "baz" "baz" "foo" "with_quux"] (method-names clojure.test_clojure.protocols.examples.ExampleProtocol))))
(testing "protocol will work with instances of its interface (use for interop, not in Clojure!)"
(let [obj (proxy [clojure.test_clojure.protocols.examples.ExampleProtocol] []
(foo [] "foo!"))]
(is (= "foo!" (.foo obj)) "call through interface")
(is (= "foo!" (foo obj)) "call through protocol")))
(testing "you can implement just part of a protocol if you want"
(let [obj (reify ExampleProtocol
(baz [a b] "two-arg baz!"))]
(is (= "two-arg baz!" (baz obj nil)))
(is (thrown? AbstractMethodError (baz obj))))))
(deftest extend-test
(testing "you can extend a protocol to a class"
(extend String ExampleProtocol
{:foo identity})
(is (= "pow" (foo "pow"))))
(testing "you can have two methods with the same name. Just use namespaces!"
(extend String other/SimpleProtocol
{:foo (fn [s] (.toUpperCase s))})
(is (= "POW" (other/foo "pow"))))
(testing "you can extend deftype types"
(deftype Widget [name])
(extend
::Widget
ExampleProtocol
{:foo (fn [this] (str "widget " (:name this)))})
(is (= "widget z" (foo (Widget "z"))))))
(deftest extends?-test
(reload-example-protocols)
(deftype Whatzit []
ExampleProtocol)
(testing "returns nil if a type does not implement the protocol at all"
(is (nil? (extends? other/SimpleProtocol ::Whatzit))))
(testing "returns nil if a type implements the protocol directly" ;; TBD false?
(is (nil? (extends? ExampleProtocol ::Whatzit))))
(testing "returns true if a type explicitly extends protocol"
(extend
::Whatzit
other/SimpleProtocol
{:foo identity})
(is (true? (extends? other/SimpleProtocol ::Whatzit)))))
;; what happens if you extend after implementing directly?
;; todo: investigate how nil-handling changes error handling
|