diff src/clojure/contrib/mock.clj.rej @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/clojure/contrib/mock.clj.rej	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,569 @@
     1.4 +diff a/src/main/clojure/clojure/contrib/mock.clj b/src/main/clojure/clojure/contrib/mock.clj	(rejected hunks)
     1.5 +@@ -1,285 +1,282 @@
     1.6 +-;;; clojure.contrib.mock.clj: mocking/expectation framework for Clojure
     1.7 +-
     1.8 +-;; by Matt Clark
     1.9 +-
    1.10 +-;; Copyright (c) Matt Clark, 2009. All rights reserved.  The use
    1.11 +-;; and distribution terms for this software are covered by the Eclipse
    1.12 +-;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php).
    1.13 +-;; By using this software in any fashion, you are
    1.14 +-;; agreeing to be bound by the terms of this license.  You must not
    1.15 +-;; remove this notice, or any other, from this software.
    1.16 +-;;------------------------------------------------------------------------------
    1.17 +-
    1.18 +-(comment
    1.19 +-  ;; This is a simple function mocking library I accidentally wrote as a side
    1.20 +-  ;; effect of trying to write an opengl library in clojure. This is loosely
    1.21 +-  ;; based on various ruby and java mocking frameworks I have used in the past
    1.22 +-  ;; such as mockito, easymock, and whatever rspec uses.
    1.23 +-  ;;
    1.24 +-  ;; expect uses bindings to wrap the functions that are being tested and
    1.25 +-  ;; then validates the invocation count at the end. The expect macro is the
    1.26 +-  ;; main entry point and it is given a vector of binding pairs.
    1.27 +-  ;; The first of each pair names the dependent function you want to override,
    1.28 +-  ;; while the second is a hashmap containing the mock description, usually
    1.29 +-  ;; created via the simple helper methods described below.
    1.30 +-  ;;
    1.31 +-  ;; Usage:
    1.32 +-  ;;
    1.33 +-  ;; there are one or more dependent functions:
    1.34 +-  
    1.35 +-  (defn dep-fn1 [] "time consuming calculation in 3rd party library")
    1.36 +-  (defn dep-fn2 [x] "function with undesirable side effects while testing")
    1.37 +-  
    1.38 +-  ;; then we have the code under test that calls these other functions:
    1.39 +-  
    1.40 +-  (defn my-code-under-test [] (dep-fn1) (dep-fn2 "a") (+ 2 2))
    1.41 +-
    1.42 +-  ;; to test this code, we simply surround it with an expect macro within
    1.43 +-  ;; the test:
    1.44 +-  
    1.45 +-  (expect [dep-fn1 (times 1)
    1.46 +-           dep-fn2 (times 1 (has-args [#(= "a" %)]))]
    1.47 +-    (my-code-under-test))
    1.48 +-
    1.49 +-  ;; When an expectation fails during execution of the function under test,
    1.50 +-  ;; an error condition function is called with the name of the function
    1.51 +-  ;; being mocked, the expected form and the actual value. These
    1.52 +-  ;; error functions can be overridden to allow easy integration into
    1.53 +-  ;; test frameworks such as test-is by reporting errors in the function
    1.54 +-  ;; overrides.
    1.55 +-  
    1.56 +-  ) ;; end comment
    1.57 +-
    1.58 +-(ns clojure.contrib.mock
    1.59 +-  ^{:author "Matt Clark",
    1.60 +-     :doc "function mocking/expectations for Clojure" }
    1.61 +-  (:use [clojure.contrib.seq :only (positions)]
    1.62 +-        [clojure.contrib.def :only (defmacro-)]))
    1.63 +-
    1.64 +-
    1.65 +-;;------------------------------------------------------------------------------
    1.66 +-;; These are the error condition functions. Override them to integrate into
    1.67 +-;; the test framework of your choice, or to simply customize error handling.
    1.68 +-
    1.69 +-(defn report-problem
    1.70 +-  {:dynamic true}
    1.71 +-  ([function expected actual]
    1.72 +-    (report-problem function expected actual "Expectation not met."))
    1.73 +-  ([function expected actual message]
    1.74 +-    (prn (str message " Function name: " function
    1.75 +-           " expected: " expected " actual: " actual))))
    1.76 +-
    1.77 +-(defn no-matching-function-signature
    1.78 +-  {:dynamic true} 
    1.79 +-  [function expected actual]
    1.80 +-  (report-problem function expected actual
    1.81 +-    "No matching real function signature for given argument count."))
    1.82 +-
    1.83 +-(defn unexpected-args 
    1.84 +-  {:dynamic true}
    1.85 +-  [function expected actual i]
    1.86 +-  (report-problem function expected actual
    1.87 +-    (str "Argument " i " has an unexpected value for function.")))
    1.88 +-
    1.89 +-(defn incorrect-invocation-count 
    1.90 +-  {:dynamic true}
    1.91 +-  [function expected actual]
    1.92 +-  (report-problem function expected actual "Unexpected invocation count."))
    1.93 +-
    1.94 +-
    1.95 +-;;------------------------------------------------------------------------------
    1.96 +-;;  Internal Functions - ignore these
    1.97 +-
    1.98 +-
    1.99 +-(defn- has-arg-count-match?
   1.100 +-  "Given the sequence of accepted argument vectors for a function,
   1.101 +-returns true if at least one matches the given-count value."
   1.102 +-  [arg-lists given-count]
   1.103 +-  (some #(let [[ind] (positions #{'&} %)]
   1.104 +-           (if ind
   1.105 +-             (>= given-count ind)
   1.106 +-             (= (count %) given-count)))
   1.107 +-        arg-lists))
   1.108 +-
   1.109 +-
   1.110 +-(defn has-matching-signature?
   1.111 +-  "Calls no-matching-function-signature if no match is found for the given
   1.112 +-function. If no argslist meta data is available for the function, it is
   1.113 +-not called."
   1.114 +-  [fn-name args]
   1.115 +-  (let [arg-count (count args)
   1.116 +-            arg-lists (:arglists (meta (resolve fn-name)))]
   1.117 +-        (if (and arg-lists (not (has-arg-count-match? arg-lists arg-count)))
   1.118 +-          (no-matching-function-signature fn-name arg-lists args))))
   1.119 +-
   1.120 +-
   1.121 +-(defn make-arg-checker
   1.122 +-  "Creates the argument verifying function for a replaced dependency within
   1.123 +-the expectation bound scope. These functions take the additional argument
   1.124 +-of the name of the replaced function, then the rest of their args. It is
   1.125 +-designed to be called from the mock function generated in the first argument
   1.126 +-of the mock info object created by make-mock."
   1.127 +-  [arg-preds arg-pred-forms]
   1.128 +-  (let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)]
   1.129 +-    (fn [fn-name & args]
   1.130 +-      (every? true?
   1.131 +-        (map (fn [pred arg pred-form i] (if (pred arg) true
   1.132 +-                                          (unexpected-args fn-name pred-form arg i)))
   1.133 +-          sanitized-preds args arg-pred-forms (iterate inc 0))))))
   1.134 +-
   1.135 +-
   1.136 +-(defn make-count-checker
   1.137 +-  "creates the count checker that is invoked at the end of an expectation, after
   1.138 +-the code under test has all been executed. The function returned takes the
   1.139 +-name of the associated dependency and the invocation count as arguments."
   1.140 +-  [pred pred-form]
   1.141 +-  (let [pred-fn (if (integer? pred) #(= pred %) pred)]
   1.142 +-    (fn [fn-name v] (if (pred-fn v) true
   1.143 +-                      (incorrect-invocation-count fn-name pred-form v)))))
   1.144 +-
   1.145 +-; Borrowed from clojure core. Remove if this ever becomes public there.
   1.146 +-(defmacro- assert-args
   1.147 +-  [fnname & pairs]
   1.148 +-  `(do (when-not ~(first pairs)
   1.149 +-         (throw (IllegalArgumentException.
   1.150 +-                  ~(str fnname " requires " (second pairs)))))
   1.151 +-     ~(let [more (nnext pairs)]
   1.152 +-        (when more
   1.153 +-          (list* `assert-args fnname more)))))
   1.154 +-
   1.155 +-(defn make-mock
   1.156 +-  "creates a vector containing the following information for the named function:
   1.157 +-1. dependent function replacement - verifies signature, calls arg checker,
   1.158 +-increases count, returns return value.
   1.159 +-2. an atom containing the invocation count
   1.160 +-3. the invocation count checker function
   1.161 +-4. a symbol of the name of the function being replaced."
   1.162 +-  [fn-name expectation-hash]
   1.163 +-  (assert-args make-mock
   1.164 +-    (map? expectation-hash) "a map of expectations")
   1.165 +-  (let [arg-checker (or (expectation-hash :has-args) (fn [& args] true))
   1.166 +-        count-atom (atom 0)
   1.167 +-        ret-fn (or
   1.168 +-                 (expectation-hash :calls)
   1.169 +-                 (fn [& args] (expectation-hash :returns)))]
   1.170 +-    [(fn [& args]
   1.171 +-       (has-matching-signature? fn-name args)
   1.172 +-       (apply arg-checker fn-name args)
   1.173 +-       (swap! count-atom inc)
   1.174 +-       (apply ret-fn args))
   1.175 +-     count-atom
   1.176 +-     (or (expectation-hash :times) (fn [fn-name v] true))
   1.177 +-     fn-name]))
   1.178 +-
   1.179 +-
   1.180 +-(defn validate-counts
   1.181 +-  "given the sequence of all mock data for the expectation, simply calls the
   1.182 +-count checker for each dependency."
   1.183 +-  [mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i)))
   1.184 +-
   1.185 +-(defn ^{:private true} make-bindings [expect-bindings mock-data-sym]
   1.186 +-  `[~@(interleave (map #(first %) (partition 2 expect-bindings))
   1.187 +-        (map (fn [i] `(nth (nth ~mock-data-sym ~i) 0))
   1.188 +-          (range (quot (count expect-bindings) 2))))])
   1.189 +-
   1.190 +-
   1.191 +-;;------------------------------------------------------------------------------
   1.192 +-;; These are convenience functions to improve the readability and use of this
   1.193 +-;; library. Useful in expressions such as:
   1.194 +-;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc)
   1.195 +-
   1.196 +-(defn once [x] (= 1 x))
   1.197 +-
   1.198 +-(defn never [x] (zero? x))
   1.199 +-
   1.200 +-(defn more-than [x] #(< x %))
   1.201 +-
   1.202 +-(defn less-than [x] #(> x %))
   1.203 +-
   1.204 +-(defn between [x y] #(and (< x %) (> y %)))
   1.205 +-
   1.206 +-
   1.207 +-;;------------------------------------------------------------------------------
   1.208 +-;; The following functions can be used to build up the expectation hash.
   1.209 +-
   1.210 +-(defn returns
   1.211 +-  "Creates or associates to an existing expectation hash the :returns key with
   1.212 +-a value to be returned by the expectation after a successful invocation
   1.213 +-matching its expected arguments (if applicable).
   1.214 +-Usage:
   1.215 +-(returns ret-value expectation-hash?)"
   1.216 +-
   1.217 +-  ([val] (returns val {}))
   1.218 +-  ([val expectation-hash] (assoc expectation-hash :returns val)))
   1.219 +-
   1.220 +-
   1.221 +-(defn calls
   1.222 +-  "Creates or associates to an existing expectation hash the :calls key with a
   1.223 +-function that will be called with the given arguments. The return value from
   1.224 +-this function will be returned returned by the expected function. If both this
   1.225 +-and returns are specified, the return value of \"calls\" will have precedence.
   1.226 +-Usage:
   1.227 +-(calls some-fn expectation-hash?)"
   1.228 +-
   1.229 +-  ([val] (calls val {}))
   1.230 +-  ([val expectation-hash] (assoc expectation-hash :calls val)))
   1.231 +-
   1.232 +-
   1.233 +-(defmacro has-args
   1.234 +-  "Creates or associates to an existing expectation hash the :has-args key with
   1.235 +-a value corresponding to a function that will either return true if its
   1.236 +-argument expectations are met or throw an exception with the details of the
   1.237 +-first failed argument it encounters.
   1.238 +-Only specify as many predicates as you are interested in verifying. The rest
   1.239 +-of the values are safely ignored.
   1.240 +-Usage:
   1.241 +-(has-args [arg-pred-1 arg-pred-2 ... arg-pred-n] expectation-hash?)"
   1.242 +-
   1.243 +-  ([arg-pred-forms] `(has-args ~arg-pred-forms {}))
   1.244 +-  ([arg-pred-forms expect-hash-form]
   1.245 +-    (assert-args has-args
   1.246 +-      (vector? arg-pred-forms) "a vector of argument predicates")
   1.247 +-    `(assoc ~expect-hash-form :has-args
   1.248 +-       (make-arg-checker ~arg-pred-forms '~arg-pred-forms))))
   1.249 +-
   1.250 +-
   1.251 +-(defmacro times
   1.252 +-  "Creates or associates to an existing expectation hash the :times key with a
   1.253 +-value corresponding to a predicate function which expects an integer value.
   1.254 +-This function can either be specified as the first argument to times or can be
   1.255 +-the result of calling times with an integer argument, in which case the
   1.256 +-predicate will default to being an exact match.  This predicate is called at
   1.257 +-the end of an expect expression to validate that an expected dependency
   1.258 +-function was called the expected number of times.
   1.259 +-Usage:
   1.260 +-(times n)
   1.261 +-(times #(> n %))
   1.262 +-(times n expectation-hash)"
   1.263 +-  ([times-fn] `(times ~times-fn {}))
   1.264 +-  ([times-fn expectation-hash]
   1.265 +-    `(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn))))
   1.266 +-
   1.267 +-
   1.268 +-;-------------------------------------------------------------------------------
   1.269 +-; The main expect macro.
   1.270 +-(defmacro expect
   1.271 +- "Use expect to redirect calls to dependent functions that are made within the
   1.272 +-code under test. Instead of calling the functions that would normally be used,
   1.273 +-temporary stubs are used, which can verify function parameters and call counts.
   1.274 +-Return values can also be specified as needed.
   1.275 +-Usage:
   1.276 +-(expect [dep-fn (has-args [arg-pred1] (times n (returns x)))]
   1.277 +-  (function-under-test a b c))"
   1.278 +-
   1.279 +-  [expect-bindings & body]
   1.280 +-  (assert-args expect
   1.281 +-    (vector? expect-bindings) "a vector of expectation bindings"
   1.282 +-    (even? (count expect-bindings))
   1.283 +-    "an even number of forms in expectation bindings")
   1.284 +-  (let [mock-data (gensym "mock-data_")]
   1.285 +-    `(let [~mock-data (map (fn [args#]
   1.286 +-                             (apply clojure.contrib.mock/make-mock args#))
   1.287 +-                        ~(cons 'list (map (fn [[n m]] (vector (list 'quote n) m))
   1.288 +-                                       (partition 2 expect-bindings))))]
   1.289 +-       (binding ~(make-bindings expect-bindings mock-data) ~@body)
   1.290 +-       (clojure.contrib.mock/validate-counts ~mock-data) true)))
   1.291 ++;;; clojure.contrib.mock.clj: mocking/expectation framework for Clojure
   1.292 ++
   1.293 ++;;  by Matt Clark
   1.294 ++
   1.295 ++;;  Copyright (c) Matt Clark, 2009. All rights reserved.  The use and
   1.296 ++;;  distribution terms for this software are covered by the Eclipse Public
   1.297 ++;;  License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can
   1.298 ++;;  be found in the file epl-v10.html at the root of this distribution.  By
   1.299 ++;;  using this software in any fashion, you are agreeing to be bound by the
   1.300 ++;;  terms of this license.  You must not remove this notice, or any other
   1.301 ++;;  from this software.
   1.302 ++;;------------------------------------------------------------------------------
   1.303 ++
   1.304 ++(comment
   1.305 ++  ;; Mock is a function mocking utility loosely based on various ruby and java
   1.306 ++  ;; mocking frameworks such as mockito, easymock, and rspec yet adapted to
   1.307 ++  ;; fit the functional style of clojure.
   1.308 ++  ;;
   1.309 ++  ;; Mock uses bindings to wrap the functions that are being tested and
   1.310 ++  ;; then validates the invocation count at the end. The expect macro is the
   1.311 ++  ;; main entry point and it is given a vector of binding pairs.
   1.312 ++  ;; The first of each pair names the dependent function you want to override
   1.313 ++  ;; while the second is a hashmap containing the mock description, usually
   1.314 ++  ;; created via the simple helper methods described below.
   1.315 ++  ;;
   1.316 ++  ;; Usage:
   1.317 ++  ;;
   1.318 ++  ;; there are one or more dependent functions:
   1.319 ++
   1.320 ++  (defn dep-fn1 [] "time consuming calculation in 3rd party library")
   1.321 ++  (defn dep-fn2 [x] "function with undesirable side effects while testing")
   1.322 ++
   1.323 ++  ;; then we have the code under test that calls these other functions:
   1.324 ++
   1.325 ++  (defn my-code-under-test [] (dep-fn1) (dep-fn2 "a") (+ 2 2))
   1.326 ++
   1.327 ++  ;; to test this code, we simply surround it with an expect macro within
   1.328 ++  ;; the test:
   1.329 ++
   1.330 ++  (expect [dep-fn1 (times 1)
   1.331 ++           dep-fn2 (times 1 (has-args [#(= "a" %)]))]
   1.332 ++          (my-code-under-test))
   1.333 ++
   1.334 ++  ;; When an expectation fails during execution of the function under test
   1.335 ++  ;; an error condition function is called with the name of the function
   1.336 ++  ;; being mocked, the expected form and the actual value. These
   1.337 ++  ;; error functions can be overridden to allow easy integration into
   1.338 ++  ;; test frameworks such as test-is by reporting errors in the function
   1.339 ++  ;; overrides.
   1.340 ++
   1.341 ++  ) ;; end comment
   1.342 ++
   1.343 ++(ns clojure.contrib.mock
   1.344 ++  ^{:author "Matt Clark"
   1.345 ++     :doc "function mocking/expectations for Clojure" }
   1.346 ++  (:use [clojure.contrib.seq :only (positions)]
   1.347 ++        [clojure.contrib.def :only (defmacro-)]))
   1.348 ++
   1.349 ++
   1.350 ++;;------------------------------------------------------------------------------
   1.351 ++;; These are the error condition functions. Override them to integrate into
   1.352 ++;; the test framework of your choice, or to simply customize error handling.
   1.353 ++
   1.354 ++(defn report-problem
   1.355 ++  {:dynamic true}
   1.356 ++  ([function expected actual]
   1.357 ++     (report-problem function expected actual "Expectation not met."))
   1.358 ++  ([function expected actual message]
   1.359 ++     (prn (str message " Function name: " function
   1.360 ++               " expected: " expected " actual: " actual))))
   1.361 ++
   1.362 ++(defn no-matching-function-signature
   1.363 ++  {:dynamic true}
   1.364 ++  [function expected actual]
   1.365 ++  (report-problem function expected actual
   1.366 ++                  "No matching real function signature for given argument count."))
   1.367 ++
   1.368 ++(defn unexpected-args
   1.369 ++  {:dynamic true}
   1.370 ++  [function expected actual i]
   1.371 ++  (report-problem function expected actual
   1.372 ++                  (str "Argument " i " has an unexpected value for function.")))
   1.373 ++
   1.374 ++(defn incorrect-invocation-count
   1.375 ++  {:dynamic true}
   1.376 ++  [function expected actual]
   1.377 ++  (report-problem function expected actual "Unexpected invocation count."))
   1.378 ++
   1.379 ++
   1.380 ++;;------------------------------------------------------------------------------
   1.381 ++;;  Internal Functions - ignore these
   1.382 ++
   1.383 ++
   1.384 ++(defn- has-arg-count-match?
   1.385 ++  "Given the sequence of accepted argument vectors for a function
   1.386 ++returns true if at least one matches the given-count value."
   1.387 ++  [arg-lists given-count]
   1.388 ++  (some #(let [[ind] (positions #{'&} %)]
   1.389 ++           (if ind
   1.390 ++             (>= given-count ind)
   1.391 ++             (= (count %) given-count)))
   1.392 ++        arg-lists))
   1.393 ++
   1.394 ++
   1.395 ++(defn has-matching-signature?
   1.396 ++  "Calls no-matching-function-signature if no match is found for the given
   1.397 ++function. If no argslist meta data is available for the function, it is
   1.398 ++not called."
   1.399 ++  [fn-name args]
   1.400 ++  (let [arg-count (count args)
   1.401 ++        arg-lists (:arglists (meta (resolve fn-name)))]
   1.402 ++    (if (and arg-lists (not (has-arg-count-match? arg-lists arg-count)))
   1.403 ++      (no-matching-function-signature fn-name arg-lists args))))
   1.404 ++
   1.405 ++
   1.406 ++(defn make-arg-checker
   1.407 ++  "Creates the argument verifying function for a replaced dependency within
   1.408 ++the expectation bound scope. These functions take the additional argument
   1.409 ++of the name of the replaced function, then the rest of their args. It is
   1.410 ++designed to be called from the mock function generated in the first argument
   1.411 ++of the mock info object created by make-mock."
   1.412 ++  [arg-preds arg-pred-forms]
   1.413 ++  (let [sanitized-preds (map (fn [v] (if (fn? v) v #(= v %))) arg-preds)]
   1.414 ++    (fn [fn-name & args]
   1.415 ++      (every? true?
   1.416 ++              (map (fn [pred arg pred-form i] (if (pred arg) true
   1.417 ++                                                  (unexpected-args fn-name
   1.418 ++                                                                   pred-form arg i)))
   1.419 ++                   sanitized-preds args arg-pred-forms (iterate inc 0))))))
   1.420 ++
   1.421 ++
   1.422 ++(defn make-count-checker
   1.423 ++  "creates the count checker that is invoked at the end of an expectation, after
   1.424 ++the code under test has all been executed. The function returned takes the
   1.425 ++name of the associated dependency and the invocation count as arguments."
   1.426 ++  [pred pred-form]
   1.427 ++  (let [pred-fn (if (integer? pred) #(= pred %) pred)]
   1.428 ++    (fn [fn-name v] (if (pred-fn v) true
   1.429 ++                        (incorrect-invocation-count fn-name pred-form v)))))
   1.430 ++
   1.431 ++(defn make-mock
   1.432 ++  "creates a vector containing the following information for the named function:
   1.433 ++1. dependent function replacement - verifies signature, calls arg checker
   1.434 ++increases count, returns return value.
   1.435 ++2. an atom containing the invocation count
   1.436 ++3. the invocation count checker function
   1.437 ++4. a symbol of the name of the function being replaced."
   1.438 ++  [fn-name expectation-hash]
   1.439 ++  {:pre [(map? expectation-hash)
   1.440 ++         (symbol? fn-name)]}
   1.441 ++  (let [arg-checker (or (expectation-hash :has-args) (fn [& args] true))
   1.442 ++        count-atom (atom 0)
   1.443 ++        ret-fn (or
   1.444 ++                (expectation-hash :calls)
   1.445 ++                (fn [& args] (expectation-hash :returns)))]
   1.446 ++    [(fn [& args]
   1.447 ++       (has-matching-signature? fn-name args)
   1.448 ++       (apply arg-checker fn-name args)
   1.449 ++       (swap! count-atom inc)
   1.450 ++       (apply ret-fn args))
   1.451 ++     count-atom
   1.452 ++     (or (expectation-hash :times) (fn [fn-name v] true))
   1.453 ++     fn-name]))
   1.454 ++
   1.455 ++
   1.456 ++(defn validate-counts
   1.457 ++  "given the sequence of all mock data for the expectation, simply calls the
   1.458 ++count checker for each dependency."
   1.459 ++  [mock-data] (doseq [[mfn i checker fn-name] mock-data] (checker fn-name @i)))
   1.460 ++
   1.461 ++(defn- make-bindings [expect-bindings mock-data-sym]
   1.462 ++  `[~@(interleave (map #(first %) (partition 2 expect-bindings))
   1.463 ++                  (map (fn [i] `(nth (nth ~mock-data-sym ~i) 0))
   1.464 ++                       (range (quot (count expect-bindings) 2))))])
   1.465 ++
   1.466 ++
   1.467 ++;;------------------------------------------------------------------------------
   1.468 ++;; These are convenience functions to improve the readability and use of this
   1.469 ++;; library. Useful in expressions such as:
   1.470 ++;; (expect [dep-fn1 (times (more-than 1) (returns 15)) etc)
   1.471 ++
   1.472 ++;; best used in the times function
   1.473 ++(defn once [x] (= 1 x))
   1.474 ++
   1.475 ++(defn never [x] (zero? x))
   1.476 ++
   1.477 ++(defn more-than [x] #(< x %))
   1.478 ++
   1.479 ++(defn less-than [x] #(> x %))
   1.480 ++
   1.481 ++(defn between [x y] #(and (< x %) (> y %)))
   1.482 ++
   1.483 ++;;best used in the has-args function
   1.484 ++(defn anything [x] true)
   1.485 ++
   1.486 ++
   1.487 ++;;------------------------------------------------------------------------------
   1.488 ++;; The following functions can be used to build up the expectation hash.
   1.489 ++
   1.490 ++(defn returns
   1.491 ++  "Creates or associates to an existing expectation hash the :returns key with
   1.492 ++a value to be returned by the expectation after a successful invocation
   1.493 ++matching its expected arguments (if applicable).
   1.494 ++Usage:
   1.495 ++(returns ret-value expectation-hash?)"
   1.496 ++
   1.497 ++  ([val] (returns val {}))
   1.498 ++  ([val expectation-hash]
   1.499 ++   {:pre [(map? expectation-hash)]}
   1.500 ++   (assoc expectation-hash :returns val)))
   1.501 ++
   1.502 ++
   1.503 ++(defn calls
   1.504 ++  "Creates or associates to an existing expectation hash the :calls key with a
   1.505 ++function that will be called with the given arguments. The return value from
   1.506 ++this function will be returned by the expected function. If both this
   1.507 ++and returns are specified, the return value of \"calls\" will have precedence.
   1.508 ++Usage:
   1.509 ++(calls some-fn expectation-hash?)"
   1.510 ++
   1.511 ++  ([val] (calls val {}))
   1.512 ++  ([val expectation-hash]
   1.513 ++   {:pre [(map? expectation-hash)]}
   1.514 ++   (assoc expectation-hash :calls val)))
   1.515 ++
   1.516 ++
   1.517 ++(defmacro has-args
   1.518 ++  "Creates or associates to an existing expectation hash the :has-args key with
   1.519 ++a value corresponding to a function that will either return true if its
   1.520 ++argument expectations are met or throw an exception with the details of the
   1.521 ++first failed argument it encounters.
   1.522 ++Only specify as many predicates as you are interested in verifying. The rest
   1.523 ++of the values are safely ignored.
   1.524 ++Usage:
   1.525 ++(has-args [arg-pred-1 arg-pred-2 ... arg-pred-n] expectation-hash?)"
   1.526 ++
   1.527 ++  ([arg-pred-forms] `(has-args ~arg-pred-forms {}))
   1.528 ++  ([arg-pred-forms expectation-hash]
   1.529 ++   {:pre [(vector? arg-pred-forms)
   1.530 ++          (map? expectation-hash)]}
   1.531 ++    `(assoc ~expectation-hash :has-args
   1.532 ++       (make-arg-checker ~arg-pred-forms '~arg-pred-forms))))
   1.533 ++
   1.534 ++
   1.535 ++(defmacro times
   1.536 ++  "Creates or associates to an existing expectation hash the :times key with a
   1.537 ++value corresponding to a predicate function which expects an integer value.
   1.538 ++Also, an integer can be specified, in which case the times will only be an
   1.539 ++exact match. The times check is called at the end of an expect expression to
   1.540 ++validate that an expected dependency function was called the expected
   1.541 ++number of times.
   1.542 ++Usage:
   1.543 ++(times n)
   1.544 ++(times #(> n %))
   1.545 ++(times n expectation-hash)"
   1.546 ++  ([times-fn] `(times ~times-fn {}))
   1.547 ++  ([times-fn expectation-hash]
   1.548 ++   {:pre [(map? expectation-hash)]}
   1.549 ++   `(assoc ~expectation-hash :times (make-count-checker ~times-fn '~times-fn))))
   1.550 ++
   1.551 ++
   1.552 ++;-------------------------------------------------------------------------------
   1.553 ++; The main expect macro.
   1.554 ++(defmacro expect
   1.555 ++  "Use expect to redirect calls to dependent functions that are made within the
   1.556 ++code under test. Instead of calling the functions that would normally be used
   1.557 ++temporary stubs are used, which can verify function parameters and call counts.
   1.558 ++Return values of overridden functions can also be specified as needed.
   1.559 ++Usage:
   1.560 ++(expect [dep-fn (has-args [arg-pred1] (times n (returns x)))]
   1.561 ++        (function-under-test a b c))"
   1.562 ++
   1.563 ++  [expect-bindings & body]
   1.564 ++   {:pre [(vector? expect-bindings)
   1.565 ++          (even? (count expect-bindings))]}
   1.566 ++  (let [mock-data (gensym "mock-data_")]
   1.567 ++    `(let [~mock-data (map (fn [args#]
   1.568 ++                             (apply clojure.contrib.mock/make-mock args#))
   1.569 ++                        ~(cons 'list (map (fn [[n m]] (vector (list 'quote n) m))
   1.570 ++                                       (partition 2 expect-bindings))))]
   1.571 ++       (binding ~(make-bindings expect-bindings mock-data) ~@body)
   1.572 ++       (clojure.contrib.mock/validate-counts ~mock-data) true)))