summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2009-07-14 20:11:22 -0400
committerRich Hickey <richhickey@gmail.com>2009-07-14 20:11:22 -0400
commit1ac552bb74ce96df21fa870e57b0fbf5e336c9f0 (patch)
tree2a81916605700d8fe06eec47e054f38b2d44c3a7
parent9b6b549df3d2d835f6c2305e6962e31b21b374af (diff)
parent12888faab8f5c7bc13da5a8f506c2a594c48500c (diff)
Merge branch 'master' into chunks
-rw-r--r--build.xml19
-rw-r--r--src/clj/clojure/core.clj163
-rw-r--r--src/clj/clojure/main.clj8
-rw-r--r--src/clj/clojure/stacktrace.clj74
-rw-r--r--src/clj/clojure/template.clj55
-rw-r--r--src/clj/clojure/test.clj923
-rw-r--r--src/clj/clojure/test/tap.clj111
-rw-r--r--src/clj/clojure/walk.clj133
-rw-r--r--src/jvm/clojure/lang/Compiler.java2
-rw-r--r--src/jvm/clojure/lang/LispReader.java12
-rw-r--r--src/jvm/clojure/lang/LockingTransaction.java5
-rw-r--r--src/jvm/clojure/lang/RT.java7
-rw-r--r--src/jvm/clojure/lang/Ref.java44
-rw-r--r--test/clojure/test_clojure.clj68
-rw-r--r--test/clojure/test_clojure/agents.clj43
-rw-r--r--test/clojure/test_clojure/atoms.clj20
-rw-r--r--test/clojure/test_clojure/clojure_set.clj120
-rw-r--r--test/clojure/test_clojure/clojure_xml.clj21
-rw-r--r--test/clojure/test_clojure/clojure_zip.clj48
-rw-r--r--test/clojure/test_clojure/compilation.clj39
-rw-r--r--test/clojure/test_clojure/control.clj117
-rw-r--r--test/clojure/test_clojure/data_structures.clj736
-rw-r--r--test/clojure/test_clojure/evaluation.clj229
-rw-r--r--test/clojure/test_clojure/for.clj123
-rw-r--r--test/clojure/test_clojure/java_interop.clj407
-rw-r--r--test/clojure/test_clojure/logic.clj205
-rw-r--r--test/clojure/test_clojure/macros.clj18
-rw-r--r--test/clojure/test_clojure/metadata.clj19
-rw-r--r--test/clojure/test_clojure/multimethods.clj27
-rw-r--r--test/clojure/test_clojure/ns_libs.clj28
-rw-r--r--test/clojure/test_clojure/numbers.clj391
-rw-r--r--test/clojure/test_clojure/other_functions.clj60
-rw-r--r--test/clojure/test_clojure/parallel.clj29
-rw-r--r--test/clojure/test_clojure/predicates.clj142
-rw-r--r--test/clojure/test_clojure/printer.clj83
-rw-r--r--test/clojure/test_clojure/reader.clj299
-rw-r--r--test/clojure/test_clojure/refs.clj22
-rw-r--r--test/clojure/test_clojure/sequences.clj984
-rw-r--r--test/clojure/test_clojure/special.clj24
-rw-r--r--test/clojure/test_clojure/test.clj113
-rw-r--r--test/clojure/test_clojure/test_fixtures.clj41
-rw-r--r--test/clojure/test_clojure/test_utils.clj33
-rw-r--r--test/clojure/test_clojure/vars.clj56
43 files changed, 6046 insertions, 55 deletions
diff --git a/build.xml b/build.xml
index 2fa8d8bd..b37adefc 100644
--- a/build.xml
+++ b/build.xml
@@ -9,6 +9,7 @@
</description>
<property name="src" location="src"/>
+ <property name="test" location="test"/>
<property name="jsrc" location="${src}/jvm"/>
<property name="cljsrc" location="${src}/clj"/>
<property name="build" location="classes"/>
@@ -92,6 +93,11 @@
<arg value="clojure.xml"/>
<arg value="clojure.zip"/>
<arg value="clojure.inspector"/>
+ <arg value="clojure.walk"/>
+ <arg value="clojure.stacktrace"/>
+ <arg value="clojure.template"/>
+ <arg value="clojure.test"/>
+ <arg value="clojure.test.tap"/>
</java>
</target>
@@ -110,6 +116,19 @@
<copy file="${clojure_jar}" tofile="${clojure_noversion_jar}" />
</target>
+ <target name="test"
+ description="Run clojure tests">
+<!-- depends="clojure"> -->
+ <java classname="clojure.main">
+ <classpath>
+ <path location="${test}"/>
+ <path location="${clojure_jar}"/>
+ </classpath>
+ <arg value="-e"/>
+ <arg value="(require '(clojure [test-clojure :as main])) (main/run)"/>
+ </java>
+ </target>
+
<target name="clojure-slim" depends="compile-java"
description="Create clojure-slim jar file (omits compiled Clojure code)">
<jar jarfile="${slim_jar}">
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 6b7661fb..8ed4aaf1 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -129,17 +129,6 @@
vector? (fn vector? [x] (instance? clojure.lang.IPersistentVector x)))
(def
- #^{:private true}
- sigs
- (fn [fdecl]
- (if (seq? (first fdecl))
- (loop [ret [] fdecl fdecl]
- (if fdecl
- (recur (conj ret (first (first fdecl))) (next fdecl))
- (seq ret)))
- (list (first fdecl)))))
-
-(def
#^{:arglists '([map key val] [map key val & kvs])
:doc "assoc[iate]. When applied to a map, returns a new map of the
same (hashed/sorted) type, that contains the mapping of key(s) to
@@ -169,6 +158,27 @@
with-meta (fn with-meta [#^clojure.lang.IObj x m]
(. x (withMeta m))))
+(def
+ #^{:private true}
+ sigs
+ (fn [fdecl]
+ (let [asig
+ (fn [fdecl]
+ (let [arglist (first fdecl)
+ body (next fdecl)]
+ (if (map? (first body))
+ (if (next body)
+ (with-meta arglist (conj (if (meta arglist) (meta arglist) {}) (first body)))
+ arglist)
+ arglist)))]
+ (if (seq? (first fdecl))
+ (loop [ret [] fdecls fdecl]
+ (if fdecls
+ (recur (conj ret (asig (first fdecls))) (next fdecls))
+ (seq ret)))
+ (list (asig fdecl))))))
+
+
(def
#^{:arglists '([coll])
:doc "Return the last item in coll, in linear time"}
@@ -1300,14 +1310,30 @@
:validator validate-fn
+ :min-history (default 0)
+ :max-history (default 10)
+
If metadata-map is supplied, it will be come the metadata on the
ref. validate-fn must be nil or a side-effect-free fn of one
argument, which will be passed the intended new state on any state
change. If the new state is unacceptable, the validate-fn should
return false or throw an exception. validate-fn will be called on
- transaction commit, when all refs have their final values."
+ transaction commit, when all refs have their final values.
+
+ Normally refs accumulate history dynamically as needed to deal with
+ read demands. If you know in advance you will need history you can
+ set :min-history to ensure it will be available when first needed (instead
+ of after a read fault). History is limited, and the limit can be set
+ with :max-history."
([x] (new clojure.lang.Ref x))
- ([x & options] (setup-reference (ref x) options)))
+ ([x & options]
+ (let [r #^clojure.lang.Ref (setup-reference (ref x) options)
+ opts (apply hash-map options)]
+ (when (:max-history opts)
+ (.setMaxHistory r (:max-history opts)))
+ (when (:min-history opts)
+ (.setMinHistory r (:min-history opts)))
+ r)))
(defn deref
"Also reader macro: @ref/@agent/@var/@atom/@delay/@future. Within a transaction,
@@ -1415,6 +1441,25 @@
[#^clojure.lang.Ref ref val]
(. ref (set val)))
+(defn ref-history-count
+ "Returns the history count of a ref"
+ [#^clojure.lang.Ref ref]
+ (.getHistoryCount ref))
+
+(defn ref-min-history
+ "Gets the min-history of a ref, or sets it and returns the ref"
+ ([#^clojure.lang.Ref ref]
+ (.getMinHistory ref))
+ ([#^clojure.lang.Ref ref n]
+ (.setMinHistory ref n)))
+
+(defn ref-max-history
+ "Gets the max-history of a ref, or sets it and returns the ref"
+ ([#^clojure.lang.Ref ref]
+ (.getMaxHistory ref))
+ ([#^clojure.lang.Ref ref n]
+ (.setMaxHistory ref n)))
+
(defn ensure
"Must be called in a transaction. Protects the ref from modification
by other transactions. Returns the in-transaction-value of
@@ -1497,10 +1542,10 @@
false."
{:tag Boolean}
[pred coll]
- (if (seq coll)
- (and (pred (first coll))
- (recur pred (next coll)))
- true))
+ (cond
+ (nil? (seq coll)) true
+ (pred (first coll)) (recur pred (next coll))
+ :else false))
(def
#^{:tag Boolean
@@ -1778,15 +1823,24 @@
(defn partition
"Returns a lazy sequence of lists of n items each, at offsets step
apart. If step is not supplied, defaults to n, i.e. the partitions
- do not overlap."
+ do not overlap. If a pad collection is supplied, use its elements as
+ necessary to complete last partition upto n items. In case there are
+ not enough padding elements, return a partition with less than n items."
([n coll]
(partition n n coll))
([n step coll]
- (lazy-seq
- (when-let [s (seq coll)]
- (let [p (take n s)]
- (when (= n (count p))
- (cons p (partition n step (drop step s)))))))))
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (let [p (take n s)]
+ (when (= n (count p))
+ (cons p (partition n step (drop step s))))))))
+ ([n step pad coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (let [p (take n s)]
+ (if (= n (count p))
+ (cons p (partition n step pad (drop step s)))
+ (list (take n (concat p pad)))))))))
;; evaluation
@@ -2657,7 +2711,7 @@
(even? (count bindings)) "an even number of forms in binding vector")
`(let* ~(destructure bindings) ~@body))
-;redefine fn with destructuring
+;redefine fn with destructuring and pre/post conditions
(defmacro fn
"(fn name? [params* ] exprs*)
(fn name? ([params* ] exprs*)+)
@@ -2673,9 +2727,26 @@
sigs (if name (next sigs) sigs)
sigs (if (vector? (first sigs)) (list sigs) sigs)
psig (fn [sig]
- (let [[params & body] sig]
+ (let [[params & body] sig
+ conds (when (and (next body) (map? (first body)))
+ (first body))
+ body (if conds (next body) body)
+ conds (or conds ^params)
+ pre (:pre conds)
+ post (:post conds)
+ body (if post
+ `((let [~'% ~(if (< 1 (count body))
+ `(do ~@body)
+ (first body))]
+ ~@(map (fn [c] `(assert ~c)) post)
+ ~'%))
+ body)
+ body (if pre
+ (concat (map (fn [c] `(assert ~c)) pre)
+ body)
+ body)]
(if (every? symbol? params)
- sig
+ (cons params body)
(loop [params params
new-params []
lets []]
@@ -2844,8 +2915,9 @@
"Evaluates expr and throws an exception if it does not evaluate to
logical true."
[x]
- `(when-not ~x
- (throw (new Exception (str "Assert failed: " (pr-str '~x))))))
+ (when *assert*
+ `(when-not ~x
+ (throw (new Exception (str "Assert failed: " (pr-str '~x)))))))
(defn test
"test [v] finds fn at key :test in var metadata and calls it,
@@ -3034,16 +3106,20 @@
[v] (instance? clojure.lang.Var v))
(defn slurp
- "Reads the file named by f into a string and returns it."
- [#^String f]
- (with-open [r (new java.io.BufferedReader (new java.io.FileReader f))]
+ "Reads the file named by f using the encoding enc into a string
+ and returns it."
+ ([f] (slurp f (.name (java.nio.charset.Charset/defaultCharset))))
+ ([#^String f #^String enc]
+ (with-open [r (new java.io.BufferedReader
+ (new java.io.InputStreamReader
+ (new java.io.FileInputStream f) enc))]
(let [sb (new StringBuilder)]
- (loop [c (. r (read))]
+ (loop [c (.read r)]
(if (neg? c)
(str sb)
(do
- (. sb (append (char c)))
- (recur (. r (read)))))))))
+ (.append sb (char c))
+ (recur (.read r)))))))))
(defn subs
"Returns the substring of s beginning at start inclusive, and ending
@@ -3735,11 +3811,11 @@
All definitions a lib makes should be in its associated namespace.
'require loads a lib by loading its root resource. The root resource path
- is derived from the root directory path by repeating its last component
- and appending '.clj'. For example, the lib 'x.y.z has root directory
- <classpath>/x/y/z; root resource <classpath>/x/y/z/z.clj. The root
- resource should contain code to create the lib's namespace and load any
- additional lib resources.
+ is derived from the lib name in the following manner:
+ Consider a lib named by the symbol 'x.y.z; it has the root directory
+ <classpath>/x/y/, and its root resource is <classpath>/x/y/z.clj. The root
+ resource should contain code to create the lib's namespace (usually by using
+ the ns macro) and load any additional lib resources.
Libspecs
@@ -3766,7 +3842,14 @@
already loaded
:reload-all implies :reload and also forces loading of all libs that the
identified libs directly or indirectly load via require or use
- :verbose triggers printing information about each load, alias, and refer"
+ :verbose triggers printing information about each load, alias, and refer
+
+ Example:
+
+ The following would load the libraries clojure.zip and clojure.set
+ abbreviated as 's'.
+
+ (require '(clojure zip [set :as s]))"
[& args]
(apply load-libs :require args))
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj
index ee185afe..58fa0a3f 100644
--- a/src/clj/clojure/main.clj
+++ b/src/clj/clojure/main.clj
@@ -16,17 +16,19 @@
(defmacro with-bindings
"Executes body in the context of thread-local bindings for several vars
- that often need to be set!: *ns* *warn-on-reflection* *print-meta*
- *print-length* *print-level* *compile-path* *command-line-args* *1
- *2 *3 *e"
+ that often need to be set!: *ns* *warn-on-reflection* *math-context*
+ *print-meta* *print-length* *print-level* *compile-path*
+ *command-line-args* *1 *2 *3 *e"
[& body]
`(binding [*ns* *ns*
*warn-on-reflection* *warn-on-reflection*
+ *math-context* *math-context*
*print-meta* *print-meta*
*print-length* *print-length*
*print-level* *print-level*
*compile-path* (System/getProperty "clojure.compile.path" "classes")
*command-line-args* *command-line-args*
+ *assert* *assert*
*1 nil
*2 nil
*3 nil
diff --git a/src/clj/clojure/stacktrace.clj b/src/clj/clojure/stacktrace.clj
new file mode 100644
index 00000000..52d03b9b
--- /dev/null
+++ b/src/clj/clojure/stacktrace.clj
@@ -0,0 +1,74 @@
+; 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.
+
+;;; stacktrace.clj: print Clojure-centric stack traces
+
+;; by Stuart Sierra
+;; January 6, 2009
+
+(ns
+ #^{:author "Stuart Sierra",
+ :doc "Print Clojure-centric stack traces"}
+ clojure.stacktrace)
+
+(defn root-cause
+ "Returns the last 'cause' Throwable in a chain of Throwables."
+ [tr]
+ (if-let [cause (.getCause tr)]
+ (recur cause)
+ tr))
+
+(defn print-trace-element
+ "Prints a Clojure-oriented view of one element in a stack trace."
+ [e]
+ (let [class (.getClassName e)
+ method (.getMethodName e)]
+ (let [match (re-matches #"^([A-Za-z0-9_.-]+)\$(\w+)__\d+$" class)]
+ (if (and match (= "invoke" method))
+ (apply printf "%s/%s" (rest match))
+ (printf "%s.%s" class method))))
+ (printf " (%s:%d)" (or (.getFileName e) "") (.getLineNumber e)))
+
+(defn print-throwable
+ "Prints the class and message of a Throwable."
+ [tr]
+ (printf "%s: %s" (.getName (class tr)) (.getMessage tr)))
+
+(defn print-stack-trace
+ "Prints a Clojure-oriented stack trace of tr, a Throwable.
+ Prints a maximum of n stack frames (default: unlimited).
+ Does not print chained exceptions (causes)."
+ ([tr] (print-stack-trace tr nil))
+ ([tr n]
+ (let [st (.getStackTrace tr)]
+ (print-throwable tr)
+ (newline)
+ (print " at ")
+ (print-trace-element (first st))
+ (newline)
+ (doseq [e (if (nil? n)
+ (rest st)
+ (take (dec n) (rest st)))]
+ (print " ")
+ (print-trace-element e)
+ (newline)))))
+
+(defn print-cause-trace
+ "Like print-stack-trace but prints chained exceptions (causes)."
+ ([tr] (print-cause-trace tr nil))
+ ([tr n]
+ (print-stack-trace tr n)
+ (when-let [cause (.getCause tr)]
+ (print "Caused by: " )
+ (recur cause n))))
+
+(defn e
+ "REPL utility. Prints a brief stack trace for the root cause of the
+ most recent exception."
+ []
+ (print-stack-trace (root-cause *e) 8))
diff --git a/src/clj/clojure/template.clj b/src/clj/clojure/template.clj
new file mode 100644
index 00000000..d62390a6
--- /dev/null
+++ b/src/clj/clojure/template.clj
@@ -0,0 +1,55 @@
+; 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.
+
+;;; template.clj - anonymous functions that pre-evaluate sub-expressions
+
+;; By Stuart Sierra
+;; June 23, 2009
+
+;; CHANGE LOG
+;;
+;; June 23, 2009: complete rewrite, eliminated _1,_2,... argument
+;; syntax
+;;
+;; January 20, 2009: added "template?" and checks for valid template
+;; expressions.
+;;
+;; December 15, 2008: first version
+
+
+(ns #^{:author "Stuart Sierra"
+ :doc "Macros that expand to repeated copies of a template expression."}
+ clojure.template
+ (:require [clojure.walk :as walk]))
+
+(defn apply-template
+ "For use in macros. argv is an argument list, as in defn. expr is
+ a quoted expression using the symbols in argv. values is a sequence
+ of values to be used for the arguments.
+
+ apply-template will recursively replace argument symbols in expr
+ with their corresponding values, returning a modified expr.
+
+ Example: (apply-template '[x] '(+ x x) '[2])
+ ;=> (+ 2 2)"
+ [argv expr values]
+ (assert (vector? argv))
+ (assert (every? symbol? argv))
+ (walk/prewalk-replace (zipmap argv values) expr))
+
+(defmacro do-template
+ "Repeatedly copies expr (in a do block) for each group of arguments
+ in values. values are automatically partitioned by the number of
+ arguments in argv, an argument vector as in defn.
+
+ Example: (macroexpand '(do-template [x y] (+ y x) 2 4 3 5))
+ ;=> (do (+ 4 2) (+ 5 3))"
+ [argv expr & values]
+ (let [c (count argv)]
+ `(do ~@(map (fn [a] (apply-template argv expr a))
+ (partition c values)))))
diff --git a/src/clj/clojure/test.clj b/src/clj/clojure/test.clj
new file mode 100644
index 00000000..7195034b
--- /dev/null
+++ b/src/clj/clojure/test.clj
@@ -0,0 +1,923 @@
+; 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.
+
+;;; test.clj: test framework for Clojure
+
+;; by Stuart Sierra
+;; March 28, 2009
+
+;; Thanks to Chas Emerick, Allen Rohner, and Stuart Halloway for
+;; contributions and suggestions.
+
+
+
+(comment
+ ;; Inspired by many Common Lisp test frameworks and clojure/test,
+ ;; this file is a Clojure test framework.
+ ;;
+ ;;
+ ;;
+ ;; ASSERTIONS
+ ;;
+ ;; The core of the library is the "is" macro, which lets you make
+ ;; assertions of any arbitrary expression:
+
+ (is (= 4 (+ 2 2)))
+ (is (instance? Integer 256))
+ (is (.startsWith "abcde" "ab"))
+
+ ;; You can type an "is" expression directly at the REPL, which will
+ ;; print a message if it fails.
+ ;;
+ ;; user> (is (= 5 (+ 2 2)))
+ ;;
+ ;; FAIL in (:1)
+ ;; expected: (= 5 (+ 2 2))
+ ;; actual: (not (= 5 4))
+ ;; false
+ ;;
+ ;; The "expected:" line shows you the original expression, and the
+ ;; "actual:" shows you what actually happened. In this case, it
+ ;; shows that (+ 2 2) returned 4, which is not = to 5. Finally, the
+ ;; "false" on the last line is the value returned from the
+ ;; expression. The "is" macro always returns the result of the
+ ;; inner expression.
+ ;;
+ ;; There are two special assertions for testing exceptions. The
+ ;; "(is (thrown? c ...))" form tests if an exception of class c is
+ ;; thrown:
+
+ (is (thrown? ArithmeticException (/ 1 0)))
+
+ ;; "(is (thrown-with-msg? c re ...))" does the same thing and also
+ ;; tests that the message on the exception matches the regular
+ ;; expression re:
+
+ (is (thrown-with-msg? ArithmeticException #"Divide by zero"
+ (/ 1 0)))
+
+ ;;
+ ;;
+ ;;
+ ;; DOCUMENTING TESTS
+ ;;
+ ;; "is" takes an optional second argument, a string describing the
+ ;; assertion. This message will be included in the error report.
+
+ (is (= 5 (+ 2 2)) "Crazy arithmetic")
+
+ ;; In addition, you can document groups of assertions with the
+ ;; "testing" macro, which takes a string followed by any number of
+ ;; assertions. The string will be included in failure reports.
+ ;; Calls to "testing" may be nested, and all of the strings will be
+ ;; joined together with spaces in the final report, in a style
+ ;; similar to RSpec <http://rspec.info/>
+
+ (testing "Arithmetic"
+ (testing "with positive integers"
+ (is (= 4 (+ 2 2)))
+ (is (= 7 (+ 3 4))))
+ (testing "with negative integers"
+ (is (= -4 (+ -2 -2)))
+ (is (= -1 (+ 3 -4)))))
+
+ ;; Note that, unlike RSpec, the "testing" macro may only be used
+ ;; INSIDE a "deftest" or "with-test" form (see below).
+ ;;
+ ;;
+ ;;
+ ;; DEFINING TESTS
+ ;;
+ ;; There are two ways to define tests. The "with-test" macro takes
+ ;; a defn or def form as its first argument, followed by any number
+ ;; of assertions. The tests will be stored as metadata on the
+ ;; definition.
+
+ (with-test
+ (defn my-function [x y]
+ (+ x y))
+ (is (= 4 (my-function 2 2)))
+ (is (= 7 (my-function 3 4))))
+
+ ;; As of Clojure SVN rev. 1221, this does not work with defmacro.
+ ;; See http://code.google.com/p/clojure/issues/detail?id=51
+ ;;
+ ;; The other way lets you define tests separately from the rest of
+ ;; your code, even in a different namespace:
+
+ (deftest addition
+ (is (= 4 (+ 2 2)))
+ (is (= 7 (+ 3 4))))
+
+ (deftest subtraction
+ (is (= 1 (- 4 3)))
+ (is (= 3 (- 7 4))))
+
+ ;; This creates functions named "addition" and "subtraction", which
+ ;; can be called like any other function. Therefore, tests can be
+ ;; grouped and composed, in a style similar to the test framework in
+ ;; Peter Seibel's "Practical Common Lisp"
+ ;; <http://www.gigamonkeys.com/book/practical-building-a-unit-test-framework.html>
+
+ (deftest arithmetic
+ (addition)
+ (subtraction))
+
+ ;; The names of the nested tests will be joined in a list, like
+ ;; "(arithmetic addition)", in failure reports. You can use nested
+ ;; tests to set up a context shared by several tests.
+ ;;
+ ;;
+ ;;
+ ;; RUNNING TESTS
+ ;;
+ ;; Run tests with the function "(run-tests namespaces...)":
+
+ (run-tests 'your.namespace 'some.other.namespace)
+
+ ;; If you don't specify any namespaces, the current namespace is
+ ;; used. To run all tests in all namespaces, use "(run-all-tests)".
+ ;;
+ ;; By default, these functions will search for all tests defined in
+ ;; a namespace and run them in an undefined order. However, if you
+ ;; are composing tests, as in the "arithmetic" example above, you
+ ;; probably do not want the "addition" and "subtraction" tests run
+ ;; separately. In that case, you must define a special function
+ ;; named "test-ns-hook" that runs your tests in the correct order:
+
+ (defn test-ns-hook []
+ (arithmetic))
+
+ ;;
+ ;;
+ ;;
+ ;; OMITTING TESTS FROM PRODUCTION CODE
+ ;;
+ ;; You can bind the variable "*load-tests*" to false when loading or
+ ;; compiling code in production. This will prevent any tests from
+ ;; being created by "with-test" or "deftest".
+ ;;
+ ;;
+ ;;
+ ;; FIXTURES (new)
+ ;;
+ ;; Fixtures allow you to run code before and after tests, to set up
+ ;; the context in which tests should be run.
+ ;;
+ ;; A fixture is just a function that calls another function passed as
+ ;; an argument. It looks like this:
+ (defn my-fixture [f]
+ ;; Perform setup, establish bindings, whatever.
+ (f) ;; Then call the function we were passed.
+ ;; Tear-down / clean-up code here.
+ )
+
+ ;; Fixtures are attached to namespaces in one of two ways. "each"
+ ;; fixtures are run repeatedly, once for each test function created
+ ;; with "deftest" or "with-test". "each" fixtures are useful for
+ ;; establishing a consistent before/after state for each test, like
+ ;; clearing out database tables.
+ ;;
+ ;; "each" fixtures can be attached to the current namespace like this:
+ (use-fixtures :each fixture1 fixture2 ...)
+ ;; The fixture1, fixture2 are just functions like the example above.
+ ;; They can also be anonymous functions, like this:
+ (use-fixtures :each (fn [f] setup... (f) cleanup...))
+ ;;
+ ;; The other kind of fixture, a "once" fixture, is only run once,
+ ;; around ALL the tests in the namespace. "once" fixtures are useful
+ ;; for tasks that only need to be performed once, like establishing
+ ;; database connections, or for time-consuming tasks.
+ ;;
+ ;; Attach "once" fixtures to the current namespace like this:
+ (use-fixtures :once fixture1 fixture2 ...)
+ ;;
+ ;;
+ ;;
+ ;; SAVING TEST OUTPUT TO A FILE
+ ;;
+ ;; All the test reporting functions write to the var *test-out*. By
+ ;; default, this is the same as *out*, but you can rebind it to any
+ ;; PrintWriter. For example, it could be a file opened with
+ ;; clojure.contrib.duck-streams/writer.
+ ;;
+ ;;
+ ;;
+ ;; EXTENDING TEST-IS (ADVANCED)
+ ;;
+ ;; You can extend the behavior of the "is" macro by defining new
+ ;; methods for the "assert-expr" multimethod. These methods are
+ ;; called during expansion of the "is" macro, so they should return
+ ;; quoted forms to be evaluated.
+ ;;
+ ;; You can plug in your own test-reporting framework by rebinding
+ ;; the "report" function: (report event)
+ ;;
+ ;; The 'event' argument is a map. It will always have a :type key,