diff options
author | Stuart Halloway <stu@thinkrelevance.com> | 2009-08-25 21:52:48 -0400 |
---|---|---|
committer | Stuart Halloway <stu@thinkrelevance.com> | 2009-08-25 21:52:48 -0400 |
commit | c3cc2ea4356abc6c9e3faf43c862e3d6df3f28de (patch) | |
tree | 4517577a06fefe89b2a343198b8e93cde3be48d3 | |
parent | addc9c1460d56074962d85a99685573319a3c496 (diff) | |
parent | f191c6be636ad682343279280fbe02fb2a956a03 (diff) |
Merge branch 'expect'
-rw-r--r-- | build.xml | 2 | ||||
-rw-r--r-- | src/clojure/contrib/expect.clj | 276 | ||||
-rw-r--r-- | src/clojure/contrib/expect/test_adapter.clj | 37 | ||||
-rw-r--r-- | src/clojure/contrib/java_utils.clj | 1 | ||||
-rw-r--r-- | src/clojure/contrib/ns_utils.clj | 17 | ||||
-rw-r--r-- | src/clojure/contrib/seq_utils.clj | 10 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib.clj | 3 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib/expect_test.clj | 131 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib/expect_test/test_adapter_test.clj | 18 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib/seq_utils_test.clj | 11 | ||||
-rw-r--r-- | src/clojure/contrib/test_contrib/test_java_utils.clj | 8 |
11 files changed, 513 insertions, 1 deletions
@@ -122,6 +122,8 @@ <arg value="clojure.contrib.duck-streams"/> <arg value="clojure.contrib.error-kit"/> <arg value="clojure.contrib.except"/> + <arg value="clojure.contrib.expect"/> + <arg value="clojure.contrib.expect.test-is-adapter"/> <arg value="clojure.contrib.fcase"/> <arg value="clojure.contrib.find-namespaces"/> <arg value="clojure.contrib.fnmap"/> diff --git a/src/clojure/contrib/expect.clj b/src/clojure/contrib/expect.clj new file mode 100644 index 00000000..1d78e939 --- /dev/null +++ b/src/clojure/contrib/expect.clj @@ -0,0 +1,276 @@ +;;; clojure.contrib.expect.clj: mocking/expectation framework for Clojure
+
+;; by Matt Clark
+
+;; Copyright (c) Matt Clark, 2009. 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).
+;; 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.
+;;------------------------------------------------------------------------------
+
+(comment
+ ;; This is a simple function mocking library I accidentally wrote as a side
+ ;; effect of trying to write an opengl library in clojure. This is loosely
+ ;; based on various ruby and java mocking frameworks I have used in the past
+ ;; such as mockito, easymock, and whatever rspec uses.
+ ;;
+ ;; expect uses bindings to wrap the functions that are being tested and
+ ;; then validates the invocation count at the end. The expect macro is the
+ ;; main entry point and it is given a vector of binding pairs.
+ ;; The first of each pair names the dependent function you want to override,
+ ;; while the second is a hashmap containing the mock description, usually
+ ;; created via the simple helper methods described below.
+ ;;
+ ;; Usage:
+ ;;
+ ;; there are one or more dependent functions:
+
+ (defn dep-fn1 [] "time consuming calculation in 3rd party library")
+ (defn dep-fn2 [x] "function with undesirable side effects while testing")
+
+ ;; then we have the code under test that calls these other functions:
+
+ (defn my-code-under-test [] (dep-fn1) (dep-fn2 "a") (+ 2 2))
+
+ ;; to test this code, we simply surround it with an expect macro within
+ ;; the test:
+
+ (expect [dep-fn1 (times 1)
+ dep-fn2 (times 1 (has-args [#(= "a" %)]))]
+ (my-code-under-test))
+
+ ;; When an expectation fails during execution of the function under test,
+ ;; an error condition function is called with the name of the function
+ ;; being mocked, the expected form and the actual value. These
+ ;; error functions can be overridden to allow easy integration into
+ ;; test frameworks such as test-is by reporting errors in the function
+ ;; overrides.
+
+ ) ;; end comment
+
+(ns clojure.contrib.expect
+ (:use [clojure.contrib.seq-utils :only (positions)]
+ [clojure.contrib.def :only (defmacro-)]))
+
+
+;;------------------------------------------------------------------------------
+;; These are the error condition functions. Override them to integrate into
+;; the test framework of your choice, or to simply customize error handling.
+
+(defn report-problem
+ ([function expected actual]
+ (report-problem function expected actual "Expectation not met."))
+ ([function expected actual message]
+ (prn (str message " Function name: " function
+ " expected: " expected " actual: " actual))))
+
+(defn no-matching-function-signature [function expected actual]
+ (report-problem function expected actual
+ "No matching real function signature for given argument count."))
+
+(defn unexpected-args [function expected actual i]
+ (report-problem function expected actual
+ (str "Argument " i " has an unexpected value for function.")))
+
+(defn incorrect-invocation-count [function expected actual]
+ (report-problem function expected actual "Unexpected invocation count."))
+
+
+;;------------------------------------------------------------------------------
+;; Internal Functions - ignore these
+
+
+(defn- has-arg-count-match?
+ "Given the sequence of accepted argument vectors for a function,
+returns true if at least one matches the given-count value."
+ [arg-lists given-count]
+ (some #(let [[ind] (positions #{'&} %)]
+ (if ind
+ (>= given-count ind)
+ (= (count %) given-count)))
+ arg-lists))
+
+
+(defn has-matching-signature?
+ "Calls no-matching-function-signature if no match is found for the given
+function. If no argslist meta data is available for the function, it is
+not called."
+ [fn-name args]
+ (let [arg-count (count args)
+ arg-lists (:arglists (meta (resolve fn-name)))]
+ (if (and arg-lists (not (has-arg-count-match? arg-lists arg-count)))
+ (no-matching-function-signature fn-name arg-lists args))))
+
+
+(defn make-arg-checker
+ "Creates the argument verifying function for a replaced dependency within
+the expectation bound scope. These functions take the additional argument
+of the name of the replaced function, then the rest of their args. It is
+designed to be called from the mock function generated in the first argument
+of the mock info object created by make-mock."
+ [arg-preds arg-pred-forms]
+ (let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)]
+ (fn [fn-name & args]
+ (every? true?
+ (map (fn [pred arg pred-form i] (if (pred arg) true
+ (unexpected-args fn-name pred-form arg i)))
+ sanitized-preds args arg-pred-forms (iterate inc 0))))))
+
+
+(defn make-count-checker
+ "creates the count checker that is invoked at the end of an expectation, after
+the code under test has all been executed. The function returned takes the
+name of the associated dependency and the invocation count as arguments."
+ [pred pred-form]
+ (let [pred-fn (if (integer? pred) #(= pred %) pred)]
+ (fn [fn-name v] (if (pred-fn v) true
+ (incorrect-invocation-count fn-name pred-form v)))))
+
+; Borrowed from clojure core. Remove if this ever becomes public there.
+(defmacro- assert-args
+ [fnname & pairs]
+ `(do (when-not ~(first pairs)
+ (throw (IllegalArgumentException.
+ ~(str fnname " requires " (second pairs)))))
+ ~(let [more (nnext pairs)]
+ (when more
+ (list* `assert-args fnname more)))))
+
+(defn make-mock
+ "creates a vector containing the following information for the named function:
+1. dependent function replacement - verifies signature, calls arg checker,
+increases count, returns return value.
+2. an atom containing the invocation count
+3. the invocation count checker function
+4. a symbol of the name of the function being replaced."
+ [fn-name expectation-hash]
+ (assert-args make-mock
+ (map? expectation-hash) "a map of expectations")
+ (let [arg-checker (or (expectation-hash :has-args) (fn [& args] true))
+ count-atom (atom 0)
+ ret-fn (or
+ (expectation-hash :calls)
+ (fn [& args] (expectation-hash :returns)))]
+ [(fn [& args]
+ (has-matching-signature? fn-name args)
+ (apply arg-checker fn-name args)
+ (swap! count-atom inc)
+ (apply ret-fn args))
+ count-atom
+ (or (expectation-hash :times) (fn [fn-name v] true))
+ fn-name]))
+
+
+(defn validate-counts
+ "given the sequence of all mock data for the expectation, simply calls the
+count checker for each dependency."
+ [mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i)))
+
+(defn #^{:private true} make-bindings [expect-bindings mock-data-sym]
+ `[~@(interleave (map #(first %) (partition 2 expect-bindings))
+ (map (fn [i] `(nth (nth ~mock-data-sym ~i) 0))
+ (range (quot (count expect-bindings) 2))))])
+
+
+;;------------------------------------------------------------------------------
+;; These are convenience functions to improve the readability and use of this
+;; library. Useful in expressions such as:
+;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc)
+
+(defn once [x] (= 1 x))
+
+(defn never [x] (zero? x))
+
+(defn more-than [x] #(< x %))
+
+(defn less-than [x] #(> x %))
+
+(defn between [x y] #(and (< x %) (> y %)))
+
+
+;;------------------------------------------------------------------------------
+;; The following functions can be used to build up the expectation hash.
+
+(defn returns
+ "Creates or associates to an existing expectation hash the :returns key with
+a value to be returned by the expectation after a successful invocation
+matching its expected arguments (if applicable).
+Usage:
+(returns ret-value expectation-hash?)"
+
+ ([val] (returns val {}))
+ ([val expectation-hash] (assoc expectation-hash :returns val)))
+
+
+(defn calls
+ "Creates or associates to an existing expectation hash the :calls key with a
+function that will be called with the given arguments. The return value from
+this function will be returned returned by the expected function. If both this
+and returns are specified, the return value of \"calls\" will have precedence.
+Usage:
+(calls some-fn expectation-hash?)"
+
+ ([val] (calls val {}))
+ ([val expectation-hash] (assoc expectation-hash :calls val)))
+
+
+(defmacro has-args
+ "Creates or associates to an existing expectation hash the :has-args key with
+a value corresponding to a function that will either return true if its
+argument expectations are met or throw an exception with the details of the
+first failed argument it encounters.
+Only specify as many predicates as you are interested in verifying. The rest
+of the values are safely ignored.
+Usage:
+(has-args [arg-pred-1 arg-pred-2 ... arg-pred-n] expectation-hash?)"
+
+ ([arg-pred-forms] `(has-args ~arg-pred-forms {}))
+ ([arg-pred-forms expect-hash-form]
+ (assert-args has-args
+ (vector? arg-pred-forms) "a vector of argument predicates")
+ `(assoc ~expect-hash-form :has-args
+ (make-arg-checker ~arg-pred-forms '~arg-pred-forms))))
+
+
+(defmacro times
+ "Creates or associates to an existing expectation hash the :times key with a
+value corresponding to a predicate function which expects an integer value.
+This function can either be specified as the first argument to times or can be
+the result of calling times with an integer argument, in which case the
+predicate will default to being an exact match. This predicate is called at
+the end of an expect expression to validate that an expected dependency
+function was called the expected number of times.
+Usage:
+(times n)
+(times #(> n %))
+(times n expectation-hash)"
+ ([times-fn] `(times ~times-fn {}))
+ ([times-fn expectation-hash]
+ `(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn))))
+
+
+;-------------------------------------------------------------------------------
+; The main expect macro.
+(defmacro expect
+ "Use expect to redirect calls to dependent functions that are made within the
+code under test. Instead of calling the functions that would normally be used,
+temporary stubs are used, which can verify function parameters and call counts.
+Return values can also be specified as needed.
+Usage:
+(expect [dep-fn (has-args [arg-pred1] (times n (returns x)))]
+ (function-under-test a b c))"
+
+ [expect-bindings & body]
+ (assert-args expect
+ (vector? expect-bindings) "a vector of expectation bindings"
+ (even? (count expect-bindings))
+ "an even number of forms in expectation bindings")
+ (let [mock-data (gensym "mock-data_")]
+ `(let [~mock-data (map (fn [args#]
+ (apply clojure.contrib.expect/make-mock args#))
+ ~(cons 'list (map (fn [[n m]] (vector (list 'quote n) m))
+ (partition 2 expect-bindings))))]
+ (binding ~(make-bindings expect-bindings mock-data) ~@body)
+ (clojure.contrib.expect/validate-counts ~mock-data) true)))
diff --git a/src/clojure/contrib/expect/test_adapter.clj b/src/clojure/contrib/expect/test_adapter.clj new file mode 100644 index 00000000..50e50125 --- /dev/null +++ b/src/clojure/contrib/expect/test_adapter.clj @@ -0,0 +1,37 @@ +;;; test_adapter.clj: clojure.test adapter for mocking/expectation framework for Clojure + +;; by Matt Clark + +;; Copyright (c) Matt Clark, 2009. 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). +;; 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. + +(ns clojure.contrib.expect.test-adapter + (:require [clojure.contrib.expect :as expect]) + (:use clojure.test + clojure.contrib.ns-utils)) + +(immigrate 'clojure.contrib.expect) + +(defn report-problem + "This function is designed to be used in a binding macro to override +the report-problem function in the test-expect namespace. Instead of printing +the error to the console, the error is logged via test-is." + [fn-name expected actual msg] + (report {:type :fail, + :message (str msg " Function name: " fn-name), + :expected expected, + :actual actual})) + + +(defmacro expect [& body] + "Use this macro instead of the standard test-expect expect macro to have +failures reported through test-is." + `(binding [expect/report-problem report-problem] + (expect/expect ~@body))) + + + diff --git a/src/clojure/contrib/java_utils.clj b/src/clojure/contrib/java_utils.clj index ce2b7777..579d1128 100644 --- a/src/clojure/contrib/java_utils.clj +++ b/src/clojure/contrib/java_utils.clj @@ -186,3 +186,4 @@ (defmethod as-url String [#^String x] (URL. x)) +(defmethod as-url File [#^File x] (.toURL x)) diff --git a/src/clojure/contrib/ns_utils.clj b/src/clojure/contrib/ns_utils.clj index 7a866d5a..d77b3217 100644 --- a/src/clojure/contrib/ns_utils.clj +++ b/src/clojure/contrib/ns_utils.clj @@ -22,6 +22,9 @@ ;; 'print-docs' prints documentation for the public vars in a ;; namespace ;; +;; 'immigrate' Create a public var in this namespace for each +;; public var in the namespaces named by ns-names. +;; From James Reeves ;; Convenience ;; ;; 'vars' returns a sorted seq of symbols naming public vars @@ -87,3 +90,17 @@ "Prints documentation for the public vars in a namespace" [nsname] `(print-docs (get-ns '~nsname))) + +(defn immigrate + "Create a public var in this namespace for each public var in the + namespaces named by ns-names. The created vars have the same name, root + binding, and metadata as the original except that their :ns metadata + value is this namespace." + [& ns-names] + (doseq [ns ns-names] + (require ns) + (doseq [[sym var] (ns-publics ns)] + (let [sym (with-meta sym (assoc (meta var) :ns *ns*))] + (if (.hasRoot var) + (intern *ns* sym (.getRoot var)) + (intern *ns* sym)))))) diff --git a/src/clojure/contrib/seq_utils.clj b/src/clojure/contrib/seq_utils.clj index d4c29c46..ad913f70 100644 --- a/src/clojure/contrib/seq_utils.clj +++ b/src/clojure/contrib/seq_utils.clj @@ -211,3 +211,13 @@ (cons (if (identical? x NIL) nil x) (drain)))))))))) +(defn positions + "Returns a lazy sequence containing the positions at which pred + is true for items in coll." + [pred coll] + (for [[idx elt] (indexed coll) :when (pred elt)] idx)) + + + + + diff --git a/src/clojure/contrib/test_contrib.clj b/src/clojure/contrib/test_contrib.clj index d7b2597b..8402b7ba 100644 --- a/src/clojure/contrib/test_contrib.clj +++ b/src/clojure/contrib/test_contrib.clj @@ -20,7 +20,8 @@ [:complex-numbers :fnmap :macro-utils :monads :pprint.pretty :pprint.cl-format :str-utils :shell-out :test-graph :test-dataflow :test-java-utils :test-lazy-seqs - :test-trace :test-jmx :java-utils]) + :test-trace :test-jmx :java-utils :expect-test :expect-test.test-adapter-test + :seq-utils-test]) (def test-namespaces (map #(symbol (str "clojure.contrib.test-contrib." (name %))) diff --git a/src/clojure/contrib/test_contrib/expect_test.clj b/src/clojure/contrib/test_contrib/expect_test.clj new file mode 100644 index 00000000..52f37741 --- /dev/null +++ b/src/clojure/contrib/test_contrib/expect_test.clj @@ -0,0 +1,131 @@ +(ns clojure.contrib.test-contrib.expect-test + (:use clojure.test) + (:require [clojure.contrib.expect :as expect])) + +; Used as dummy dependency functions +(defn fn1 [x] :ignore) +(defn fn2 [x y] :ignore) +(defn fn3 ([x] :ignore) + ([x y z] :ignore)) +(defn fn4 [x y & r] :ignore) + +;functions created using fn directly lack the argslist meta data +(def deffed-differently (fn [x] :ignore)) + +(defmacro assert-called [fn-name called? & body] + `(let [called-status?# (atom false)] + (binding [~fn-name (fn [& args#] (reset! called-status?# true))] ~@body) + (is (= ~called? @called-status?#)))) + +(deftest test-convenience + (testing "once" + (is (false? (expect/once 0))) + (is (false? (expect/once 123))) + (is (true? (expect/once 1)))) + + (testing "never" + (is (false? (expect/never 4))) + (is (true? (expect/never 0)))) + + (testing "more-than" + (is (false? ((expect/more-than 5) 3))) + (is (true? ((expect/more-than 5) 9)))) + + (testing "less-than" + (is (true? ((expect/less-than 5) 3))) + (is (false? ((expect/less-than 5) 9)))) + + (testing "between" + (is (true? ((expect/between 5 8) 6))) + (is (false? ((expect/between 5 8) 5))))) + + +(deftest test-returns + (is (= {:returns 5} (expect/returns 5))) + (is (= {:other-key "test" :returns nil} (expect/returns nil {:other-key "test"})))) + + +(deftest test-has-args + (let [ex (:has-args (expect/has-args [1]))] + (is (fn? ex)) + (is (ex 'fn1 1)) + (is (ex 'fn1 1 5 6)) + (assert-called expect/unexpected-args true (ex 'fn1 5))) + (is (contains? (expect/has-args [] {:pre-existing-key "test"}) :pre-existing-key)) + (is (true? (((expect/has-args [5]) :has-args)'fn1 5)))) + + +(deftest test-has-matching-signature + (assert-called expect/no-matching-function-signature true + (expect/has-matching-signature? 'clojure.contrib.test-contrib.expect-test/fn2 [1])) + (assert-called expect/no-matching-function-signature true + (expect/has-matching-signature? 'clojure.contrib.test-contrib.expect-test/fn3 [1 3])) + (assert-called expect/no-matching-function-signature false + (expect/has-matching-signature? 'clojure.contrib.test-contrib.expect-test/fn3 [1 3 5])) + (assert-called expect/no-matching-function-signature false + (expect/has-matching-signature? 'clojure.contrib.test-contrib.expect-test/fn4 [1 3 5 7 9])) + (assert-called expect/no-matching-function-signature false + (expect/has-matching-signature? 'clojure.contrib.test-contrib.expect-test/fn4 [1 3])) + (assert-called expect/no-matching-function-signature true + (expect/has-matching-signature? 'clojure.contrib.test-contrib.expect-test/fn4 [1])) + (assert-called expect/no-matching-function-signature false + (expect/has-matching-signature? 'clojure.contrib.test-contrib.expect-test/deffed-differently [1]))) + + +(deftest test-times + (is (fn? ((expect/times #(= 1 %)) :times))) + (is (contains? (expect/times #(= 1 %) {:existing-key "test"}) :existing-key))) + +(deftest test-make-mock + (testing "invalid arguments" + (is (thrown? IllegalArgumentException (expect/make-mock [5])))) + + (testing "valid counter and unevaluated returns" + (let [[mock counter count-checker] (expect/make-mock 'fn1 (expect/returns 5 (expect/times 1)))] + (is (fn? mock)) + (is (= 0 @counter)) + (is (= 5 (mock :ignore-me))) + (is (= 1 @counter)))) + + (testing "returns as expected" + (let [[mock] (expect/make-mock 'fn1 (expect/returns 5))] + (is (= 5 (mock :ignore)))) + (let [[mock] (expect/make-mock 'fn1 (expect/returns #(* 2 %)))] + (is (= 10 ((mock :ignore) 5)) ":returns a function should not automatically + evaluate it."))) + + (testing "calls replacement-fn and returns the result" + (let [[mock] (expect/make-mock 'fn1 (expect/calls #(* 3 %)))] + (is (= 15 (mock 5)))) + (let [[mock] (expect/make-mock 'fn1 (expect/calls #(* 2 %) (expect/returns 3)))] + (is (= 10 (mock 5))))) + + (testing "argument validation" + (let [[mock] (expect/make-mock 'fn1 (expect/has-args [#(= 5 %)]))] + (assert-called expect/unexpected-args true (mock "test")) + (is (nil? (mock 5)))))) + + +(deftest test-make-count-checker + (let [checker (expect/make-count-checker 5 5)] + (assert-called expect/incorrect-invocation-count false (checker 'fn1 5)) + (assert-called expect/incorrect-invocation-count true (checker 'fn1 3)))) + + +(deftest test-validate-counts + (assert-called expect/incorrect-invocation-count false + (expect/validate-counts (list [(fn []) (atom 0) (expect/make-count-checker #(< % 6) '#(< % 6)) 'fn1]))) + (assert-called expect/incorrect-invocation-count true + (expect/validate-counts (list [(fn []) (atom 0) (expect/make-count-checker 4 4) 'fn1])))) + + +(deftest test-expect-macro + (let [under-test (fn [x] (fn1 x))] + (is (true? (expect/expect [fn1 (expect/times 1 (expect/has-args [#(= 3 %)]))] + (under-test 3)))) + (assert-called expect/unexpected-args true (expect/expect [fn1 (expect/times 1 (expect/has-args [#(= 4 %)]))] + (under-test 3)))) + (let [under-test (fn [] (fn2 (fn1 1) 3))] + (is (true? (expect/expect [fn1 (expect/times 1 (expect/has-args [#(= 1 %)] (expect/returns 2))) + fn2 (expect/times 1 (expect/has-args [#(= 2 %) #(= 3 %)] (expect/returns 5)))] + (under-test))))))
\ No newline at end of file diff --git a/src/clojure/contrib/test_contrib/expect_test/test_adapter_test.clj b/src/clojure/contrib/test_contrib/expect_test/test_adapter_test.clj new file mode 100644 index 00000000..df74404e --- /dev/null +++ b/src/clojure/contrib/test_contrib/expect_test/test_adapter_test.clj @@ -0,0 +1,18 @@ +(ns clojure.contrib.test-contrib.expect-test.test-adapter-test + (:use clojure.contrib.expect.test-adapter + [clojure.contrib.test-contrib.expect-test :only (assert-called)] + clojure.test)) + +(deftest test-report-problem-called + (def #^{:private true} fn1 (fn [x] "dummy code")) + (def #^{:private true} fn2 (fn [x y] "dummy code2")) + (let [under-test (fn [x] (fn1 x))] + (assert-called clojure.contrib.expect.test-adapter/report-problem + true (expect [fn1 (times 5)] (under-test "hi"))))) + +(deftest test-is-report-called + (assert-called clojure.test/report true + (clojure.contrib.expect.test-adapter/report-problem + 'fn-name 5 6 "fake problem"))) + + diff --git a/src/clojure/contrib/test_contrib/seq_utils_test.clj b/src/clojure/contrib/test_contrib/seq_utils_test.clj new file mode 100644 index 00000000..f8815041 --- /dev/null +++ b/src/clojure/contrib/test_contrib/seq_utils_test.clj @@ -0,0 +1,11 @@ +(ns clojure.contrib.test-contrib.seq-utils-test + (:use clojure.test + clojure.contrib.seq-utils)) + + +(deftest test-positions + (are [expected pred coll] (= expected (positions pred coll)) + [2] string? [:a :b "c"] + () :d [:a :b :c] + [0 2] #{:d} [:d :a :d :a])) +
\ No newline at end of file diff --git a/src/clojure/contrib/test_contrib/test_java_utils.clj b/src/clojure/contrib/test_contrib/test_java_utils.clj index 8a56b197..abf1b9a6 100644 --- a/src/clojure/contrib/test_contrib/test_java_utils.clj +++ b/src/clojure/contrib/test_contrib/test_java_utils.clj @@ -3,6 +3,7 @@ [clojure.contrib.duck-streams :only (spit)] clojure.contrib.java-utils) (:import [java.io File] + [java.net URL URI] [java.util Properties])) (deftest test-relative-path-string @@ -23,6 +24,13 @@ (is (= (File. "bar") (as-file (File. "bar"))))) ) +(deftest test-as-url + (are [result expr] (= result expr) + (URL. "http://foo") (as-url (URL. "http://foo")) + (URL. "http://foo") (as-url "http://foo") + (URL. "http://foo") (as-url (URI. "http://foo")) + (URL. "file:/foo") (as-url (File. "/foo")))) + (deftest test-file (testing "single argument" (is (= (File. "foo") (file "foo")))) |