diff options
author | Rich Hickey <richhickey@gmail.com> | 2009-07-14 20:11:22 -0400 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2009-07-14 20:11:22 -0400 |
commit | 1ac552bb74ce96df21fa870e57b0fbf5e336c9f0 (patch) | |
tree | 2a81916605700d8fe06eec47e054f38b2d44c3a7 | |
parent | 9b6b549df3d2d835f6c2305e6962e31b21b374af (diff) | |
parent | 12888faab8f5c7bc13da5a8f506c2a594c48500c (diff) |
Merge branch 'master' into chunks
43 files changed, 6046 insertions, 55 deletions
@@ -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, |