|
100 | 100 | (when-let [e (ctevent->event m)] |
101 | 101 | (event/report-fn e))) |
102 | 102 |
|
103 | | -(defprotocol Test |
104 | | - (test-name [_]) |
105 | | - (test-fn [_]) |
106 | | - (test-input [_])) |
107 | | - |
108 | | -(extend-protocol Test |
109 | | - clojure.lang.Var |
110 | | - (test-name |
111 | | - [v] |
112 | | - (-> (when-let [ns (.ns v)] |
113 | | - (str ns "/" (.sym v)) |
114 | | - (.sym v)) |
115 | | - symbol)) |
116 | | - (test-fn |
117 | | - [this] |
118 | | - @this) |
119 | | - (test-input |
120 | | - [v] |
121 | | - (map #(%) (:clojure.test.generative/inputs (meta v))))) |
122 | | - |
123 | 103 | (defn run-iter |
124 | 104 | "Run a single test iteration" |
125 | | - [test] |
126 | | - (let [name (test-name test) |
127 | | - f (test-fn test) |
128 | | - input (test-input test)] |
129 | | - (event/report :test/iter :level :debug :name name :args input :tags #{:begin}) |
130 | | - (try |
131 | | - (let [result (apply f input)] |
132 | | - (when-not (realized? *failed*) |
133 | | - (event/report :test/iter :level :debug :name name :return result :tags #{:end}))) |
134 | | - (catch Throwable t |
135 | | - (deliver *failed* :error) |
136 | | - (event/report :error :name name :exception t))))) |
| 105 | + [name f input] |
| 106 | + (event/report :test/iter :level :debug :name name :args input :tags #{:begin}) |
| 107 | + (try |
| 108 | + (let [result (apply f input)] |
| 109 | + (when-not (realized? *failed*) |
| 110 | + (event/report :test/iter :level :debug :name name :return result :tags #{:end}))) |
| 111 | + (catch Throwable t |
| 112 | + (deliver *failed* :error) |
| 113 | + (event/report :error :name name :exception t)))) |
137 | 114 |
|
138 | 115 | (defn run-for |
139 | 116 | "Run f (presumably for side effects) repeatedly on n threads, |
|
144 | 121 | (map |
145 | 122 | #(future |
146 | 123 | (try |
147 | | - (let [seed (+ % 42)] |
| 124 | + (let [seed (+ % 42) |
| 125 | + name (:name test) |
| 126 | + f (:fn test)] |
148 | 127 | (binding [gen/*seed* seed |
149 | 128 | gen/*rnd* (java.util.Random. seed) |
150 | 129 | *failed* (promise)] |
151 | | - (event/report :test/test :tags #{:begin} :test/seed gen/*seed* :name (test-name test)) |
152 | | - (loop [iter 0] |
153 | | - (let [result (run-iter test) |
154 | | - now (System/currentTimeMillis) |
| 130 | + (event/report :test/test :tags #{:begin} :test/seed gen/*seed* :name name) |
| 131 | + (loop [iter 0 |
| 132 | + [input & more] ((:inputs test))] |
| 133 | + (let [now (System/currentTimeMillis) |
155 | 134 | failed? (realized? *failed*)] |
156 | | - (if (and (< now (+ start msec)) |
157 | | - (not failed?)) |
158 | | - (recur (inc iter)) |
| 135 | + (if input |
| 136 | + (let [result (run-iter name f input)] |
| 137 | + (if (and (< now (+ start msec)) |
| 138 | + (not failed?)) |
| 139 | + (recur (inc iter) more) |
| 140 | + (event/report :test/test |
| 141 | + :msec (- now start) |
| 142 | + :count (inc iter) |
| 143 | + :tags #{:end} |
| 144 | + :test/result (if failed? :test/fail :test/pass) |
| 145 | + :level (if failed? :warn :info) |
| 146 | + :name name))) |
159 | 147 | (event/report :test/test |
160 | 148 | :msec (- now start) |
161 | 149 | :count (inc iter) |
162 | | - :tags #{:end} |
| 150 | + :tags #{:end :test/inputs-exhausted} |
163 | 151 | :test/result (if failed? :test/fail :test/pass) |
164 | 152 | :level (if failed? :warn :info) |
165 | | - :name (test-name test))))))) |
| 153 | + :name name)))))) |
166 | 154 | (catch Throwable t |
167 | | - (event/report :error :level :error :exception t :name (test-name test))))) |
| 155 | + (event/report :error :level :error :exception t :name name)))) |
168 | 156 | (range nthreads)))] |
169 | 157 | (doseq [f futs] @f))) |
170 | 158 |
|
|
176 | 164 | (doseq [test tests] |
177 | 165 | (run-for test nthreads test-msec)))) |
178 | 166 |
|
179 | | -#_(defn set-seed |
180 | | - [n] |
181 | | - (set! gen/*rnd* (java.util.Random. n))) |
182 | | - |
183 | | -(defn gentest? |
184 | | - [v] |
185 | | - (boolean (:clojure.test.generative/inputs (meta v)))) |
| 167 | +(defmulti var-tests |
| 168 | + "TestContainer.tests support for vars. To create custom test |
| 169 | + types, define vars that have :c.t.g/type metadata, and then add |
| 170 | + a matching var-tests method that returns a collection of tests." |
| 171 | + (fn [v] (:clojure.test.generative/type (meta v)))) |
| 172 | + |
| 173 | +(defmethod var-tests :defspec [^clojure.lang.Var v] |
| 174 | + [{:name (-> (when-let [ns (.ns v)] |
| 175 | + (str ns "/" (.sym v)) |
| 176 | + (.sym v)) |
| 177 | + symbol) |
| 178 | + :fn @v |
| 179 | + :inputs (fn [] |
| 180 | + (repeatedly |
| 181 | + (fn [] |
| 182 | + (mapv #(%) (:clojure.test.generative/arg-fns (meta v))))))}]) |
| 183 | + |
| 184 | +(defmethod var-tests nil [v] nil) |
| 185 | + |
| 186 | +(defprotocol TestContainer |
| 187 | + (tests |
| 188 | + [_] |
| 189 | + "Returns a collection of generative tests, where a test is a map with |
| 190 | + :name ns-qualified symbol |
| 191 | + :fn fn to test |
| 192 | + :inputs fn returning a (possibly infinite!) sequence of inputs |
| 193 | +
|
| 194 | + All input generation should use gen/*seed* and gen/*rnd* |
| 195 | + if a source of pseudo-randomness is needed.")) |
| 196 | + |
| 197 | +(extend-protocol TestContainer |
| 198 | + clojure.lang.Var |
| 199 | + (tests [v] (var-tests v))) |
186 | 200 |
|
187 | 201 | (defn find-vars-in-namespaces |
188 | 202 | [& nses] |
|
195 | 209 | (doseq [ns nses] (require ns)) |
196 | 210 | (apply find-vars-in-namespaces nses))) |
197 | 211 |
|
198 | | -(defn find-gentests-in-vars |
199 | | - [& vars] |
200 | | - (filter gentest? vars)) |
201 | | - |
202 | 212 | (defn run-generative-tests |
203 | 213 | "Run generative tests." |
204 | 214 | [nses nthreads msec] |
205 | 215 | (let [c (count (->> (apply find-vars-in-namespaces nses) |
206 | | - (filter gentest?)))] |
| 216 | + (mapcat tests)))] |
207 | 217 | (when-not (zero? c) |
208 | 218 | (let [test-msec (quot msec c)] |
209 | 219 | (doseq [ns nses] |
210 | 220 | (when-let [fs (->> (find-vars-in-namespaces ns) |
211 | | - (filter gentest?) |
| 221 | + (mapcat tests) |
212 | 222 | seq)] |
213 | 223 | (event/report :test/group |
214 | 224 | :name ns |
|
300 | 310 | [& dirs] |
301 | 311 | (if (seq dirs) |
302 | 312 | (try |
303 | | - (let [results (apply test-dirs dirs)] |
| 313 | + (let [results (apply test-dirs dirs) |
| 314 | + failed? (boolean (some failed? (vals results)))] |
304 | 315 | (doseq [[k v] results] |
305 | 316 | (println (str "\nFramework " k)) |
306 | 317 | (println v)) |
307 | | - (System/exit (if (some failed? (vals results)) 1 0))) |
| 318 | + (when failed? |
| 319 | + (binding [*out* *err*] |
| 320 | + (println "\n*** Some tests failed ***\n"))) |
| 321 | + (System/exit (if failed? 1 0))) |
308 | 322 | (catch Throwable t |
309 | 323 | (.printStackTrace t) |
310 | 324 | (System/exit -1)) |
|
0 commit comments