Skip to content

Commit fe4922b

Browse files
interactive dev wip
1 parent fd2c3da commit fe4922b

4 files changed

Lines changed: 81 additions & 79 deletions

File tree

examples/hello_world.clj

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
(require
22
'[clojure.data.generators :as gen]
33
'[clojure.test.generative :as test :refer (defspec)]
4-
'[clojure.test.generative.event :as event]
54
'[clojure.test.generative.runner :as runner])
65

76
;; generators have names that shadow core names of things generated
@@ -34,24 +33,42 @@
3433
;; the next two steps are executed for you by the standard runner...
3534

3635
;; tests are extracted from vars
37-
(def tests (runner/var-tests #'longs-are-closed-under-increment))
36+
(def tests (runner/get-tests #'longs-are-closed-under-increment))
3837

39-
;; install awesome console UI for tests
40-
(event/install-default-handlers)
38+
(first tests)
4139

4240
;; run test with some generated inputs
43-
(runner/run-for
41+
(runner/run-one
4442
(first tests)
45-
1
46-
1000)
43+
1000
44+
[42])
45+
46+
(runner/run-n
47+
2
48+
1000
49+
tests)
50+
51+
;; repl-friendly use
52+
(runner/run-vars
53+
2 1000 #'longs-are-closed-under-increment)
54+
55+
;; peek at what defspec tells us
56+
(meta #'longs-are-closed-under-increment)
57+
58+
;; test that will fail
59+
(defspec collections-are-small
60+
count
61+
[^{:tag (gen/vec gen/short (gen/uniform 0 25))} l]
62+
(assert (< % 20)))
63+
64+
;; run as from REPL
65+
(runner/run-vars
66+
2 1000 #'collections-are-small)
67+
(ex-data *e)
68+
69+
;; run as from suite
70+
(runner/run-suite {:threads 1 :msec 500} (runner/get-tests #'collections-are-small))
71+
4772

48-
;; actual test data structure exposes a lazy infinite seq of inputs
49-
(take 2 ((:inputs (first tests))))
5073

51-
;; the real story behind that console ui
52-
(reset! @#'event/handlers [])
53-
(runner/run-for
54-
(first tests)
55-
1
56-
10)
5774

src/main/clojure/clojure/test/generative.clj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
and runs validator-body forms (if any), which have access to both
5050
args and %. The defined function.
5151
52-
Args must have type hints (i.e. :tag metdata), which are
52+
Args must have type hints (i.e. :tag metadata), which are
5353
interpreted as instructions for generating test input
5454
data. Unquoted names in type hints are resolved in the
5555
c.t.g.generators namespace, which has generator functions for
@@ -72,7 +72,6 @@
7272
seq)]
7373
(throw (IllegalArgumentException. (str "Missing tags for " (seq (map first missing-tags)) " in " name))))
7474
`(defn ~(with-meta name (assoc (meta name)
75-
::type :defspec
7675
::arg-fns (into [] (map #(-> % meta :tag tag->gen eval) args))))
7776
~(into [] (map (fn [a#] (with-meta a# (dissoc (meta a#) :tag))) args))
7877
(let [~'% (apply ~fn-to-test ~args)]

src/main/clojure/clojure/test/generative/runner.clj

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
read-string
2626
10000]])
2727

28-
(defn config
28+
(defn- config
2929
[]
3030
(reduce
3131
(fn [m [prop path coerce default]]
@@ -43,39 +43,27 @@
4343
(locking rnd
4444
(.nextInt rnd)))
4545

46-
(defmulti var-tests
47-
"TestContainer.tests support for vars. To create custom test
48-
types, define vars that have :c.t.g/type metadata, and then add
49-
a matching var-tests method that returns a collection of tests."
50-
(fn [v] (:clojure.test.generative/type (meta v))))
51-
52-
(defmethod var-tests :defspec [^clojure.lang.Var v]
53-
[{:name (-> (when-let [ns (.ns v)]
54-
(str ns "/" (.sym v))
55-
(.sym v))
56-
symbol)
57-
:f @v
58-
:inputs (fn []
59-
(repeatedly
60-
(fn []
61-
(into [] (map #(%) (:clojure.test.generative/arg-fns (meta v)))))))}])
62-
63-
(defmethod var-tests nil [v] nil)
64-
6546
(defprotocol TestContainer
66-
(tests
67-
[_]
68-
"Returns a collection of generative tests, where a test is a map with
69-
:name ns-qualified symbol
70-
:f fn to test
71-
:inputs fn returning a (possibly infinite!) sequence of inputs
72-
73-
All input generation should use and gen/*rnd*
74-
if a source of pseudo-randomness is needed."))
47+
(get-tests [_]))
7548

7649
(extend-protocol TestContainer
77-
clojure.lang.Var
78-
(tests [v] (var-tests v)))
50+
clojure.lang.Var
51+
(get-tests
52+
[v]
53+
(when-let [arg-fns (:clojure.test.generative/arg-fns (meta v))]
54+
[{:test (-> (if-let [ns (.ns v)]
55+
(str ns "/" (.sym v))
56+
(.sym v))
57+
symbol)
58+
:input-gen (fn []
59+
(repeatedly
60+
(fn []
61+
(into [] (map #(%) arg-fns)))))}]))
62+
63+
clojure.lang.MapEquivalence
64+
(get-tests
65+
[m] m))
66+
7967

8068
(defn find-vars-in-namespaces
8169
[& nses]
@@ -92,16 +80,17 @@
9280
"Run f (presumably for side effects) repeatedly on n threads,
9381
until msec has passed or somebody throws an exception.
9482
Returns as many status maps as seeds passed in."
95-
[{:keys [name f inputs]} msec seeds]
96-
(print (str "\n" name)) (flush)
97-
(let [start (System/currentTimeMillis)
83+
[{:keys [test input-gen]} msec seeds]
84+
(prn) (prn test)
85+
(let [f (eval test)
86+
start (System/currentTimeMillis)
9887
futs (mapv
9988
#(future
10089
(try
10190
(binding [gen/*rnd* (java.util.Random. %)]
10291
(loop [iter 0
103-
[input & more] (inputs)]
104-
(let [status {:iter iter :seed % :name name :input input}]
92+
[input & more] (input-gen)]
93+
(let [status {:iter iter :seed % :test test :input input}]
10594
(if input
10695
(let [failure (try
10796
(apply f input)
@@ -112,35 +101,46 @@
112101
(cond
113102
failure failure
114103
(< now (+ start msec)) (recur (inc iter) more)
115-
:else status))
104+
:else (select-keys status [:test :seed :iter])))
116105
(assoc status :exhausted true)))))))
117106
seeds)]
118107
(map deref futs)))
119108

120109
(defn run-n
121110
"Run tests in parallel on nthreads, dividing msec equally between the tests."
122-
[tests msec nthreads]
111+
[nthreads msec tests]
123112
(mapcat #(run-one % (/ msec (count tests)) (repeatedly nthreads next-seed)) tests))
124113

125-
(defn run-var
126-
[var msec nthreads]
127-
(run-n (var-tests var) msec nthreads))
114+
(defn failed?
115+
"Does test result indicate a failure?"
116+
[result]
117+
(contains? result :exception))
118+
119+
(defn run-vars
120+
"Designed for interactive use. Prints results to *out* and throws
121+
on first failure encountered."
122+
[nthreads msec & test-containers]
123+
(doseq [result (run-n nthreads msec (mapcat get-tests test-containers))]
124+
(if (failed? result)
125+
(throw (ex-info "Generative test failed" result))
126+
(prn result))))
128127

129128
(defn dir-tests
130129
"Returns all tests in dirs"
131-
[& dirs]
130+
[dirs]
132131
(let [load (fn [s] (require s) s)]
133132
(->> (mapcat #(ns/find-namespaces-in-dir (java.io.File. ^String %)) dirs)
134133
(map load)
135134
(apply find-vars-in-namespaces)
136-
(mapcat tests))))
135+
(mapcat get-tests))))
137136

138-
(defn run-tests-in-dirs
139-
[{:keys [threads msec verbose]} & dirs]
137+
(defn run-suite
138+
"Designed for test suite use."
139+
[{:keys [threads msec verbose]} tests]
140140
(reduce
141141
(fn [{:keys [failures iters tests]} result]
142142
(if (or verbose (:exception result))
143-
(pprint/pprint result)
143+
(do (prn) (prn result))
144144
(print "."))
145145
(when (:exception result)
146146
(.printStackTrace ^Throwable (:exception result)))
@@ -149,14 +149,14 @@
149149
:iters (+ iters (:iter result))
150150
:tests (inc tests)})
151151
{:failures 0 :iters 0 :tests 0}
152-
(run-n (apply dir-tests dirs) msec threads)))
152+
(run-n threads msec tests)))
153153

154154
(defn -main
155155
"Command line entry point. Calls System.exit!"
156156
[& dirs]
157157
(if (seq dirs)
158158
(try
159-
(let [result (apply run-tests-in-dirs (config) dirs)]
159+
(let [result (run-suite (config) (dir-tests dirs))]
160160
(println "\n" result)
161161
(System/exit (:failures result)))
162162
(catch Throwable t

src/test/clojure/clojure/test/generative/runner_test.clj

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)