clojure_test_check_test.{[clj cljs] -> cljc}
Nicolas Berger authored 8 years ago
Gary Fredericks committed 8 years ago
0 | ; Copyright (c) Rich Hickey, Reid Draper, and contributors. | |
1 | ; All rights reserved. | |
2 | ; The use and distribution terms for this software are covered by the | |
3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
4 | ; which can be found in the file epl-v10.html at the root of this distribution. | |
5 | ; By using this software in any fashion, you are agreeing to be bound by | |
6 | ; the terms of this license. | |
7 | ; You must not remove this notice, or any other, from this software. | |
8 | ||
9 | (ns clojure.test.check.test | |
10 | (:use clojure.test) | |
11 | (:require [clojure.test.check :as tc] | |
12 | [clojure.test.check.generators :as gen] | |
13 | [clojure.test.check.properties :as prop] | |
14 | [clojure.test.check.rose-tree :as rose] | |
15 | [clojure.test.check.random :as random] | |
16 | [clojure.test.check.clojure-test :as ct :refer (defspec)] | |
17 | [clojure.edn :as edn])) | |
18 | ||
19 | (deftest generators-are-generators | |
20 | (testing "generator? returns true when called with a generator" | |
21 | (is (gen/generator? gen/int)) | |
22 | (is (gen/generator? (gen/vector gen/int))) | |
23 | (is (gen/generator? (gen/return 5))))) | |
24 | ||
25 | (deftest values-are-not-generators | |
26 | (testing "generator? returns false when called with a value" | |
27 | (is (not (gen/generator? 5))) | |
28 | (is (not (gen/generator? int))) | |
29 | (is (not (gen/generator? [1 2 3]))))) | |
30 | ||
31 | ;; plus and 0 form a monoid | |
32 | ;; --------------------------------------------------------------------------- | |
33 | ||
34 | (defn passes-monoid-properties | |
35 | [a b c] | |
36 | (and (= (+ 0 a) a) | |
37 | (= (+ a 0) a) | |
38 | (= (+ a (+ b c)) (+ (+ a b) c)))) | |
39 | ||
40 | (deftest plus-and-0-are-a-monoid | |
41 | (testing "+ and 0 form a monoid" | |
42 | (is (let [p (prop/for-all* [gen/int gen/int gen/int] passes-monoid-properties)] | |
43 | (:result | |
44 | (tc/quick-check 1000 p))))) | |
45 | (testing "with ratios as well" | |
46 | (is (let [p (prop/for-all* [gen/ratio gen/ratio gen/ratio] passes-monoid-properties)] | |
47 | (:result | |
48 | (tc/quick-check 1000 p)))))) | |
49 | ||
50 | ;; reverse | |
51 | ;; --------------------------------------------------------------------------- | |
52 | ||
53 | (defn reverse-equal?-helper | |
54 | [l] | |
55 | (let [r (vec (reverse l))] | |
56 | (and (= (count l) (count r)) | |
57 | (= (seq l) (rseq r))))) | |
58 | ||
59 | (deftest reverse-equal? | |
60 | (testing "For all vectors L, reverse(reverse(L)) == L" | |
61 | (is (let [p (prop/for-all* [(gen/vector gen/int)] reverse-equal?-helper)] | |
62 | (:result (tc/quick-check 1000 p)))))) | |
63 | ||
64 | ;; failing reverse | |
65 | ;; --------------------------------------------------------------------------- | |
66 | ||
67 | (deftest bad-reverse-test | |
68 | (testing "For all vectors L, L == reverse(L). Not true" | |
69 | (is (false? | |
70 | (let [p (prop/for-all* [(gen/vector gen/int)] #(= (reverse %) %))] | |
71 | (:result (tc/quick-check 1000 p))))))) | |
72 | ||
73 | ;; failing element remove | |
74 | ;; --------------------------------------------------------------------------- | |
75 | ||
76 | (defn first-is-gone | |
77 | [l] | |
78 | (not (some #{(first l)} (vec (rest l))))) | |
79 | ||
80 | (deftest bad-remove | |
81 | (testing "For all vectors L, if we remove the first element E, E should not | |
82 | longer be in the list. (This is a false assumption)" | |
83 | (is (false? | |
84 | (let [p (prop/for-all* [(gen/vector gen/int)] first-is-gone)] | |
85 | (:result (tc/quick-check 1000 p))))))) | |
86 | ||
87 | ;; exceptions shrink and return as result | |
88 | ;; --------------------------------------------------------------------------- | |
89 | ||
90 | (def exception (Exception. "I get caught")) | |
91 | ||
92 | (defn exception-thrower | |
93 | [& args] | |
94 | (throw exception)) | |
95 | ||
96 | (deftest exceptions-are-caught | |
97 | (testing "Exceptions during testing are caught. They're also shrunk as long | |
98 | as they continue to throw." | |
99 | (is (= [exception [0]] | |
100 | (let [result | |
101 | (tc/quick-check | |
102 | 1000 (prop/for-all* [gen/int] exception-thrower))] | |
103 | [(:result result) (get-in result [:shrunk :smallest])]))))) | |
104 | ||
105 | ;; Count and concat work as expected | |
106 | ;; --------------------------------------------------------------------------- | |
107 | ||
108 | (defn concat-counts-correct | |
109 | [a b] | |
110 | (= (count (concat a b)) | |
111 | (+ (count a) (count b)))) | |
112 | ||
113 | (deftest count-and-concat | |
114 | (testing "For all vectors A and B: | |
115 | length(A + B) == length(A) + length(B)" | |
116 | (is (:result | |
117 | (let [p (prop/for-all* [(gen/vector gen/int) | |
118 | (gen/vector gen/int)] concat-counts-correct)] | |
119 | (tc/quick-check 1000 p)))))) | |
120 | ||
121 | ;; Interpose (Count) | |
122 | ;; --------------------------------------------------------------------------- | |
123 | ||
124 | (defn interpose-twice-the-length ;; (or one less) | |
125 | [v] | |
126 | (let [interpose-count (count (interpose :i v)) | |
127 | original-count (count v)] | |
128 | (or | |
129 | (= (* 2 original-count) interpose-count) | |
130 | (= (dec (* 2 original-count)) interpose-count)))) | |
131 | ||
132 | ||
133 | (deftest interpose-creates-sequence-twice-the-length | |
134 | (testing | |
135 | "Interposing a collection with a value makes its count | |
136 | twice the original collection, or ones less." | |
137 | (is (:result | |
138 | (tc/quick-check 1000 (prop/for-all [v (gen/vector gen/int)] (interpose-twice-the-length v))))))) | |
139 | ||
140 | ;; Lists and vectors are equivalent with seq abstraction | |
141 | ;; --------------------------------------------------------------------------- | |
142 | ||
143 | (defn list-vector-round-trip-equiv | |
144 | [a] | |
145 | ;; NOTE: can't use `(into '() ...)` here because that | |
146 | ;; puts the list in reverse order. clojure.test.check found that bug | |
147 | ;; pretty quickly... | |
148 | (= a (apply list (vec a)))) | |
149 | ||
150 | (deftest list-and-vector-round-trip | |
151 | (testing | |
152 | "" | |
153 | (is (:result | |
154 | (tc/quick-check | |
155 | 1000 (prop/for-all* | |
156 | [(gen/list gen/int)] list-vector-round-trip-equiv)))))) | |
157 | ||
158 | ;; keyword->string->keyword roundtrip | |
159 | ;; --------------------------------------------------------------------------- | |
160 | ||
161 | (def keyword->string->keyword (comp keyword clojure.string/join rest str)) | |
162 | ||
163 | (defn keyword-string-roundtrip-equiv | |
164 | [k] | |
165 | (= k (keyword->string->keyword k))) | |
166 | ||
167 | (deftest keyword-string-roundtrip | |
168 | (testing | |
169 | "For all keywords, turning them into a string and back is equivalent | |
170 | to the original string (save for the `:` bit)" | |
171 | (is (:result | |
172 | (tc/quick-check 1000 (prop/for-all* | |
173 | [gen/keyword] keyword-string-roundtrip-equiv) | |
174 | :max-size 25))))) | |
175 | ||
176 | ;; Boolean and/or | |
177 | ;; --------------------------------------------------------------------------- | |
178 | ||
179 | (deftest boolean-or | |
180 | (testing | |
181 | "`or` with true and anything else should be true" | |
182 | (is (:result (tc/quick-check | |
183 | 1000 (prop/for-all* | |
184 | [gen/boolean] #(or % true))))))) | |
185 | ||
186 | (deftest boolean-and | |
187 | (testing | |
188 | "`and` with false and anything else should be false" | |
189 | (is (:result (tc/quick-check | |
190 | 1000 (prop/for-all* | |
191 | [gen/boolean] #(not (and % false)))))))) | |
192 | ||
193 | ;; Sorting | |
194 | ;; --------------------------------------------------------------------------- | |
195 | ||
196 | (defn elements-are-in-order-after-sorting | |
197 | [v] | |
198 | (every? identity (map <= (partition 2 1 (sort v))))) | |
199 | ||
200 | (deftest sorting | |
201 | (testing | |
202 | "For all vectors V, sorted(V) should have the elements in order" | |
203 | (is (:result | |
204 | (tc/quick-check | |
205 | 1000 | |
206 | (prop/for-all* | |
207 | [(gen/vector gen/int)] elements-are-in-order-after-sorting)))))) | |
208 | ||
209 | ;; Constant generators | |
210 | ;; --------------------------------------------------------------------------- | |
211 | ||
212 | ;; A constant generator always returns its created value | |
213 | (defspec constant-generators 100 | |
214 | (prop/for-all [a (gen/return 42)] | |
215 | (print "") | |
216 | (= a 42))) | |
217 | ||
218 | (deftest constant-generators-dont-shrink | |
219 | (testing | |
220 | "Generators created with `gen/return` should not shrink" | |
221 | (is (= [42] | |
222 | (let [result (tc/quick-check 100 | |
223 | (prop/for-all | |
224 | [a (gen/return 42)] | |
225 | false))] | |
226 | (-> result :shrunk :smallest)))))) | |
227 | ||
228 | ;; Tests are deterministic | |
229 | ;; --------------------------------------------------------------------------- | |
230 | ||
231 | (defn vector-elements-are-unique | |
232 | [v] | |
233 | (== (count v) (count (distinct v)))) | |
234 | ||
235 | (defn unique-test | |
236 | [seed] | |
237 | (tc/quick-check 1000 | |
238 | (prop/for-all* | |
239 | [(gen/vector gen/int)] vector-elements-are-unique) | |
240 | :seed seed)) | |
241 | ||
242 | (defn equiv-runs | |
243 | [seed] | |
244 | (= (unique-test seed) (unique-test seed))) | |
245 | ||
246 | (deftest tests-are-deterministic | |
247 | (testing "If two runs are started with the same seed, they should | |
248 | return the same results." | |
249 | (is (:result | |
250 | (tc/quick-check 1000 (prop/for-all* [gen/int] equiv-runs)))))) | |
251 | ||
252 | ;; Generating basic generators | |
253 | ;; -------------------------------------------------------------------------- | |
254 | (deftest generators-test | |
255 | (let [t (fn [generator pred] | |
256 | (is (:result (tc/quick-check 100 | |
257 | (prop/for-all [x generator] | |
258 | (pred x))))))] | |
259 | ||
260 | (testing "keyword" (t gen/keyword keyword?)) | |
261 | (testing "ratio" (t gen/ratio (some-fn ratio? integer?))) | |
262 | (testing "byte" (t gen/byte #(instance? Byte %))) | |
263 | (testing "bytes" (t gen/bytes #(instance? (Class/forName "[B") %))) | |
264 | ||
265 | (testing "char" (t gen/char char?)) | |
266 | (testing "char-ascii" (t gen/char-ascii char?)) | |
267 | (testing "char-alphanumeric" (t gen/char-alphanumeric char?)) | |
268 | (testing "string" (t gen/string string?)) | |
269 | (testing "string-ascii" (t gen/string-ascii string?)) | |
270 | (testing "string-alphanumeric" (t gen/string-alphanumeric string?)) | |
271 | ||
272 | (testing "vector" (t (gen/vector gen/int) vector?)) | |
273 | (testing "list" (t (gen/list gen/int) list?)) | |
274 | (testing "map" (t (gen/map gen/int gen/int) map?)) | |
275 | )) | |
276 | ||
277 | ;; Generating proper matrices | |
278 | ;; --------------------------------------------------------------------------- | |
279 | ||
280 | (defn proper-matrix? | |
281 | "Check if provided nested vectors form a proper matrix — that is, all nested | |
282 | vectors have the same length" | |
283 | [mtx] | |
284 | (let [first-size (count (first mtx))] | |
285 | (every? (partial = first-size) (map count (rest mtx))))) | |
286 | ||
287 | (deftest proper-matrix-test | |
288 | (testing | |
289 | "can generate proper matrices" | |
290 | (is (:result (tc/quick-check | |
291 | 100 (prop/for-all | |
292 | [mtx (gen/vector (gen/vector gen/int 3) 3)] | |
293 | (proper-matrix? mtx))))))) | |
294 | ||
295 | (def bounds-and-vector | |
296 | (gen/bind (gen/tuple gen/s-pos-int gen/s-pos-int) | |
297 | (fn [[a b]] | |
298 | (let [minimum (min a b) | |
299 | maximum (max a b)] | |
300 | (gen/tuple (gen/return [minimum maximum]) | |
301 | (gen/vector gen/int minimum maximum)))))) | |
302 | ||
303 | (deftest proper-vector-test | |
304 | (testing | |
305 | "can generate vectors with sizes in a provided range" | |
306 | (is (:result (tc/quick-check | |
307 | 100 (prop/for-all | |
308 | [b-and-v bounds-and-vector] | |
309 | (let [[[minimum maximum] v] b-and-v | |
310 | c (count v)] | |
311 | (and (<= c maximum) | |
312 | (>= c minimum))))))))) | |
313 | ||
314 | ;; Tuples and Pairs retain their count during shrinking | |
315 | ;; --------------------------------------------------------------------------- | |
316 | ||
317 | (defn n-int-generators | |
318 | [n] | |
319 | (vec (repeat n gen/int))) | |
320 | ||
321 | (def tuples | |
322 | [(apply gen/tuple (n-int-generators 1)) | |
323 | (apply gen/tuple (n-int-generators 2)) | |
324 | (apply gen/tuple (n-int-generators 3)) | |
325 | (apply gen/tuple (n-int-generators 4)) | |
326 | (apply gen/tuple (n-int-generators 5)) | |
327 | (apply gen/tuple (n-int-generators 6))]) | |
328 | ||
329 | (defn get-tuple-gen | |
330 | [index] | |
331 | (nth tuples (dec index))) | |
332 | ||
333 | (defn inner-tuple-property | |
334 | [size] | |
335 | (prop/for-all [t (get-tuple-gen size)] | |
336 | false)) | |
337 | ||
338 | (defspec tuples-retain-size-during-shrinking 1000 | |
339 | (prop/for-all [index (gen/choose 1 6)] | |
340 | (let [result (tc/quick-check | |
341 | 100 (inner-tuple-property index))] | |
342 | (= index (count (-> result | |
343 | :shrunk :smallest first)))))) | |
344 | ||
345 | ;; Bind works | |
346 | ;; --------------------------------------------------------------------------- | |
347 | ||
348 | (def nat-vec | |
349 | (gen/such-that not-empty | |
350 | (gen/vector gen/nat))) | |
351 | ||
352 | (def vec-and-elem | |
353 | (gen/bind nat-vec | |
354 | (fn [v] | |
355 | (gen/tuple (gen/elements v) (gen/return v))))) | |
356 | ||
357 | (defspec element-is-in-vec 100 | |
358 | (prop/for-all [[element coll] vec-and-elem] | |
359 | (some #{element} coll))) | |
360 | ||
361 | ;; fmap is respected during shrinking | |
362 | ;; --------------------------------------------------------------------------- | |
363 | ||
364 | (def plus-fifty | |
365 | (gen/fmap (partial + 50) gen/nat)) | |
366 | ||
367 | (deftest f-map-respected-during-shrinking | |
368 | (testing | |
369 | "Generators created fmap should have that function applied | |
370 | during shrinking" | |
371 | (is (= [50] | |
372 | (let [result (tc/quick-check 100 | |
373 | (prop/for-all | |
374 | [a plus-fifty] | |
375 | false))] | |
376 | (-> result :shrunk :smallest)))))) | |
377 | ||
378 | ;; gen/int returns an integer when size is a double; regression for TCHECK-73 | |
379 | ;; --------------------------------------------------------------------------- | |
380 | ||
381 | (def gen-double | |
382 | (gen/fmap (fn [[x y]] (double (+ x (/ y 10)))) | |
383 | (gen/tuple gen/pos-int (gen/choose 0 9)))) | |
384 | ||
385 | (defspec gen-int-with-double-size 1000 | |
386 | (prop/for-all [size gen-double] | |
387 | (integer? (gen/generate gen/int size)))) | |
388 | ||
389 | ;; recursive-gen doesn't change ints to doubles; regression for TCHECK-73 | |
390 | ;; --------------------------------------------------------------------------- | |
391 | ||
392 | (defspec recursive-generator-test 100 | |
393 | (let [btree* (fn [g] (gen/hash-map | |
394 | :value gen/int | |
395 | :left g | |
396 | :right g)) | |
397 | btree (gen/recursive-gen btree* (gen/return nil)) | |
398 | valid? (fn valid? [tree] | |
399 | (and (integer? (:value tree)) | |
400 | (or (nil? (:left tree)) | |
401 | (valid? (:left tree))) | |
402 | (or (nil? (:right tree)) | |
403 | (valid? (:right tree)))))] | |
404 | (prop/for-all [t btree] (valid? t)))) | |
405 | ||
406 | (deftest calc-long-increasing | |
407 | ;; access internal gen/calc-long function for testing | |
408 | (are [low high] (apply < (map #(@#'gen/calc-long % low high) (range 0.0 0.9999 0.111))) | |
409 | ;; low and high should not be too close, 100 is a reasonable spread | |
410 | (- Long/MAX_VALUE 100) Long/MAX_VALUE | |
411 | Long/MIN_VALUE (+ Long/MIN_VALUE 100) | |
412 | Long/MIN_VALUE 0 | |
413 | 0 100 | |
414 | -100 0 | |
415 | 0 Long/MAX_VALUE | |
416 | Long/MIN_VALUE Long/MAX_VALUE)) | |
417 | ||
418 | ;; edn rountrips | |
419 | ;; --------------------------------------------------------------------------- | |
420 | ||
421 | (defn edn-roundtrip? | |
422 | [value] | |
423 | (= value (-> value prn-str edn/read-string))) | |
424 | ||
425 | (defspec edn-roundtrips 50 | |
426 | (prop/for-all [a gen/any] | |
427 | (edn-roundtrip? a))) | |
428 | ||
429 | ;; not-empty works | |
430 | ;; --------------------------------------------------------------------------- | |
431 | ||
432 | (defspec not-empty-works 100 | |
433 | (prop/for-all [v (gen/not-empty (gen/vector gen/boolean))] | |
434 | (not-empty v))) | |
435 | ||
436 | ;; no-shrink works | |
437 | ;; --------------------------------------------------------------------------- | |
438 | ||
439 | (defn run-no-shrink | |
440 | [i] | |
441 | (tc/quick-check 100 | |
442 | (prop/for-all [coll (gen/vector gen/nat)] | |
443 | (some #{i} coll)))) | |
444 | ||
445 | (defspec no-shrink-works 100 | |
446 | (prop/for-all [i gen/nat] | |
447 | (let [result (run-no-shrink i)] | |
448 | (if (:result result) | |
449 | true | |
450 | (= (:fail result) | |
451 | (-> result :shrunk :smallest)))))) | |
452 | ||
453 | ;; elements works with a variety of input | |
454 | ;; --------------------------------------------------------------------------- | |
455 | ||
456 | (deftest elements-with-empty | |
457 | (is (thrown? AssertionError (gen/elements ())))) | |
458 | ||
459 | (defspec elements-with-a-set 100 | |
460 | (prop/for-all [num (gen/elements #{9 10 11 12})] | |
461 | (<= 9 num 12))) | |
462 | ||
463 | ||
464 | ;; choose respects bounds during shrinking | |
465 | ;; --------------------------------------------------------------------------- | |
466 | ||
467 | (def range-gen | |
468 | (gen/fmap (fn [[a b]] | |
469 | [(min a b) (max a b)]) | |
470 | (gen/tuple gen/int gen/int))) | |
471 | ||
472 | (defspec choose-respects-bounds-during-shrinking 100 | |
473 | (prop/for-all [[mini maxi] range-gen | |
474 | random-seed gen/nat | |
475 | size gen/nat] | |
476 | (let [tree (gen/call-gen | |
477 | (gen/choose mini maxi) | |
478 | (random/make-random random-seed) | |
479 | size)] | |
480 | (every? | |
481 | #(and (<= mini %) (>= maxi %)) | |
482 | (rose/seq tree))))) | |
483 | ||
484 | ||
485 | ;; rand-range copes with full range of longs as bounds | |
486 | ;; --------------------------------------------------------------------------- | |
487 | ||
488 | (deftest rand-range-copes-with-full-range-of-longs | |
489 | (let [[low high] (reduce | |
490 | (fn [[low high :as margins] x] | |
491 | (cond | |
492 | (< x low) [x high] | |
493 | (> x high) [low x] | |
494 | :else margins)) | |
495 | [Long/MAX_VALUE Long/MIN_VALUE] | |
496 | ; choose uses rand-range directly, reasonable proxy for its | |
497 | ; guarantees | |
498 | (take 1e6 (gen/sample-seq (gen/choose Long/MIN_VALUE Long/MAX_VALUE))))] | |
499 | (is (< low high)) | |
500 | (is (< low Integer/MIN_VALUE)) | |
501 | (is (> high Integer/MAX_VALUE)))) | |
502 | ||
503 | ;; rand-range yields values inclusive of both lower & upper bounds provided to it | |
504 | ;; further, that generators that use rand-range use its full range of values | |
505 | ;; --------------------------------------------------------------------------- | |
506 | ||
507 | (deftest rand-range-uses-inclusive-bounds | |
508 | (let [bounds [5 7] | |
509 | rand-range (fn [r] (apply #'gen/rand-range r bounds))] | |
510 | (loop [trials 0 | |
511 | bounds (set bounds) | |
512 | r (random/make-random)] | |
513 | (cond | |
514 | (== trials 10000) | |
515 | (is nil (str "rand-range didn't return both of its bounds after 10000 trials; " | |
516 | "it is possible for this to fail without there being a problem, " | |
517 | "but we should be able to rely upon probability to not bother us " | |
518 | "too frequently.")) | |
519 | (empty? bounds) (is true) | |
520 | :else (let [[r1 r2] (random/split r)] | |
521 | (recur (inc trials) (disj bounds (rand-range r1)) r2)))))) | |
522 | ||
523 | (deftest elements-generates-all-provided-values | |
524 | (let [options [:a 42 'c/d "foo"]] | |
525 | (is (->> (reductions | |
526 | disj | |
527 | (set options) | |
528 | (gen/sample-seq (gen/elements options))) | |
529 | (take 10000) | |
530 | (some empty?)) | |
531 | (str "elements didn't return all of its candidate values after 10000 trials; " | |
532 | "it is possible for this to fail without there being a problem, " | |
533 | "but we should be able to rely upon probability to not bother us " | |
534 | "too frequently.")))) | |
535 | ||
536 | ;; shuffling a vector generates a permutation of that vector | |
537 | ;; --------------------------------------------------------------------------- | |
538 | ||
539 | (def original-vector-and-permutation | |
540 | (gen/bind (gen/vector gen/int) | |
541 | #(gen/tuple (gen/return %) (gen/shuffle %)))) | |
542 | ||
543 | (defspec shuffled-vector-is-a-permutation-of-original 100 | |
544 | (prop/for-all [[coll permutation] original-vector-and-permutation] | |
545 | (= (sort coll) (sort permutation)))) | |
546 | ||
547 | ;; vector can generate large vectors; regression for TCHECK-49 | |
548 | ;; --------------------------------------------------------------------------- | |
549 | ||
550 | (deftest large-vector-test | |
551 | (is (= 100000 | |
552 | (count (first (gen/sample | |
553 | (gen/vector gen/nat 100000) | |
554 | 1)))))) | |
555 | ||
556 | ;; scale controls growth rate of generators | |
557 | ;; --------------------------------------------------------------------------- | |
558 | ||
559 | (deftest scale-test | |
560 | (let [g (gen/scale (partial min 10) gen/pos-int) ;; should limit size to 10 | |
561 | samples (gen/sample g 1000)] | |
562 | (is (every? (partial >= 11) samples)) | |
563 | (is (some (partial = 10) samples)))) | |
564 | ||
565 | ;; generator dev helpers | |
566 | ;; --------------------------------------------------------------------------- | |
567 | ||
568 | (deftest generate-test | |
569 | (is (string? (gen/generate gen/string))) | |
570 | (is (string? (gen/generate gen/string 42)))) | |
571 | ||
572 | ;; defspec macro | |
573 | ;; --------------------------------------------------------------------------- | |
574 | ||
575 | (defspec run-only-once 1 (prop/for-all* [gen/int] (constantly true))) | |
576 | ||
577 | (defspec run-default-times (prop/for-all* [gen/int] (constantly true))) | |
578 | ||
579 | (defspec run-with-map1 {:num-tests 1} (prop/for-all* [gen/int] (constantly true))) | |
580 | ||
581 | (defspec run-with-map {:num-tests 1 | |
582 | :seed 1} | |
583 | (prop/for-all [a gen/int] | |
584 | (= a 0))) | |
585 | ||
586 | (def my-defspec-options {:num-tests 1 :seed 1}) | |
587 | ||
588 | (defspec run-with-symbolic-options my-defspec-options | |
589 | (prop/for-all [a gen/int] | |
590 | (= a 0))) | |
591 | ||
592 | (defspec run-with-no-options | |
593 | (prop/for-all [a gen/int] | |
594 | (integer? a))) | |
595 | ||
596 | (defspec run-float-time 1e3 | |
597 | (prop/for-all [a gen/int] | |
598 | (integer? a))) | |
599 | ||
600 | ;; TCHECK-77 Regression | |
601 | ;; --------------------------------------------------------------------------- | |
602 | ||
603 | (deftest choose-distribution-sanity-check | |
604 | (testing | |
605 | "Should not get the same random value more than 90% of the time" | |
606 | ;; This is a probabilistic test; the odds of a false-positive | |
607 | ;; failure for the ranges with two elements should be roughly 1 in | |
608 | ;; 10^162 (and even rarer for larger ranges), so it will never | |
609 | ;; ever happen. | |
610 | (are [low high] (let [xs (gen/sample (gen/choose low high) 1000) | |
611 | count-of-most-frequent (apply max (vals (frequencies xs)))] | |
612 | (< count-of-most-frequent 900)) | |
613 | (dec Long/MAX_VALUE) Long/MAX_VALUE | |
614 | Long/MIN_VALUE (inc Long/MIN_VALUE) | |
615 | Long/MIN_VALUE 0 | |
616 | 0 1 | |
617 | -1 0 | |
618 | 0 Long/MAX_VALUE | |
619 | Long/MIN_VALUE Long/MAX_VALUE))) |
0 | ; Copyright (c) Rich Hickey, Reid Draper, and contributors. | |
1 | ; All rights reserved. | |
2 | ; The use and distribution terms for this software are covered by the | |
3 | ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
4 | ; which can be found in the file epl-v10.html at the root of this distribution. | |
5 | ; By using this software in any fashion, you are agreeing to be bound by | |
6 | ; the terms of this license. | |
7 | ; You must not remove this notice, or any other, from this software. | |
8 | ||
9 | (ns clojure.test.check.test | |
10 | #?(:clj (:use clojure.test)) | |
11 | (:require #?(:cljs | |
12 | [cljs.test :as test :refer-macros [deftest testing is]]) | |
13 | [clojure.test.check :as tc] | |
14 | [clojure.test.check.generators :as gen] | |
15 | [clojure.test.check.properties :as prop #?@(:cljs [:include-macros true])] | |
16 | [clojure.test.check.rose-tree :as rose] | |
17 | [clojure.test.check.random :as random] | |
18 | [clojure.test.check.clojure-test :as ct #?(:clj :refer :cljs :refer-macros) (defspec)] | |
19 | #?(:clj [clojure.edn :as edn] | |
20 | :cljs [cljs.reader :as edn]))) | |
21 | ||
22 | (deftest generators-are-generators | |
23 | (testing "generator? returns true when called with a generator" | |
24 | (is (gen/generator? gen/int)) | |
25 | (is (gen/generator? (gen/vector gen/int))) | |
26 | (is (gen/generator? (gen/return 5))))) | |
27 | ||
28 | (deftest values-are-not-generators | |
29 | (testing "generator? returns false when called with a value" | |
30 | (is (not (gen/generator? 5))) | |
31 | (is (not (gen/generator? int))) | |
32 | (is (not (gen/generator? [1 2 3]))))) | |
33 | ||
34 | ;; plus and 0 form a monoid | |
35 | ;; --------------------------------------------------------------------------- | |
36 | ||
37 | (defn passes-monoid-properties | |
38 | [a b c] | |
39 | (and (= (+ 0 a) a) | |
40 | (= (+ a 0) a) | |
41 | (= (+ a (+ b c)) (+ (+ a b) c)))) | |
42 | ||
43 | (deftest plus-and-0-are-a-monoid | |
44 | (testing "+ and 0 form a monoid" | |
45 | (is (let [p (prop/for-all* [gen/int gen/int gen/int] passes-monoid-properties)] | |
46 | (:result | |
47 | (tc/quick-check 1000 p))))) | |
48 | #?(:clj | |
49 | (testing "with ratios as well" | |
50 | (is (let [p (prop/for-all* [gen/ratio gen/ratio gen/ratio] passes-monoid-properties)] | |
51 | (:result | |
52 | (tc/quick-check 1000 p))))) | |
53 | ||
54 | ;; NOTE: no ratios in ClojureScript - David | |
55 | )) | |
56 | ||
57 | ;; reverse | |
58 | ;; --------------------------------------------------------------------------- | |
59 | ||
60 | (defn reverse-equal?-helper | |
61 | [l] | |
62 | (let [r (vec (reverse l))] | |
63 | (and (= (count l) (count r)) | |
64 | (= (seq l) (rseq r))))) | |
65 | ||
66 | (deftest reverse-equal? | |
67 | (testing "For all vectors L, reverse(reverse(L)) == L" | |
68 | (is (let [p (prop/for-all* [(gen/vector gen/int)] reverse-equal?-helper)] | |
69 | (:result (tc/quick-check 1000 p)))))) | |
70 | ||
71 | ;; failing reverse | |
72 | ;; --------------------------------------------------------------------------- | |
73 | ||
74 | (deftest bad-reverse-test | |
75 | (testing "For all vectors L, L == reverse(L). Not true" | |
76 | (is (false? | |
77 | (let [p (prop/for-all* [(gen/vector gen/int)] #(= (reverse %) %))] | |
78 | (:result (tc/quick-check 1000 p))))))) | |
79 | ||
80 | ;; failing element remove | |
81 | ;; --------------------------------------------------------------------------- | |
82 | ||
83 | (defn first-is-gone | |
84 | [l] | |
85 | (not (some #{(first l)} (vec (rest l))))) | |
86 | ||
87 | (deftest bad-remove | |
88 | (testing "For all vectors L, if we remove the first element E, E should not | |
89 | longer be in the list. (This is a false assumption)" | |
90 | (is (false? | |
91 | (let [p (prop/for-all* [(gen/vector gen/int)] first-is-gone)] | |
92 | (:result (tc/quick-check 1000 p))))))) | |
93 | ||
94 | ;; exceptions shrink and return as result | |
95 | ;; --------------------------------------------------------------------------- | |
96 | ||
97 | (def exception (#?(:clj Exception. :cljs js/Error.) "I get caught")) | |
98 | ||
99 | (defn exception-thrower | |
100 | [& args] | |
101 | (throw exception)) | |
102 | ||
103 | (deftest exceptions-are-caught | |
104 | (testing "Exceptions during testing are caught. They're also shrunk as long | |
105 | as they continue to throw." | |
106 | (is (= [exception [0]] | |
107 | (let [result | |
108 | (tc/quick-check | |
109 | 1000 (prop/for-all* [gen/int] exception-thrower))] | |
110 | [(:result result) (get-in result [:shrunk :smallest])]))))) | |
111 | ||
112 | ;; Count and concat work as expected | |
113 | ;; --------------------------------------------------------------------------- | |
114 | ||
115 | (defn concat-counts-correct | |
116 | [a b] | |
117 | (= (count (concat a b)) | |
118 | (+ (count a) (count b)))) | |
119 | ||
120 | (deftest count-and-concat | |
121 | (testing "For all vectors A and B: | |
122 | length(A + B) == length(A) + length(B)" | |
123 | (is (:result | |
124 | (let [p (prop/for-all* [(gen/vector gen/int) | |
125 | (gen/vector gen/int)] concat-counts-correct)] | |
126 | (tc/quick-check 1000 p)))))) | |
127 | ||
128 | ;; Interpose (Count) | |
129 | ;; --------------------------------------------------------------------------- | |
130 | ||
131 | (defn interpose-twice-the-length ;; (or one less) | |
132 | [v] | |
133 | (let [interpose-count (count (interpose :i v)) | |
134 | original-count (count v)] | |
135 | (or | |
136 | (= (* 2 original-count) interpose-count) | |
137 | (= (dec (* 2 original-count)) interpose-count)))) | |
138 | ||
139 | ||
140 | (deftest interpose-creates-sequence-twice-the-length | |
141 | (testing | |
142 | "Interposing a collection with a value makes its count | |
143 | twice the original collection, or ones less." | |
144 | (is (:result | |
145 | (tc/quick-check 1000 (prop/for-all [v (gen/vector gen/int)] (interpose-twice-the-length v))))))) | |
146 | ||
147 | ;; Lists and vectors are equivalent with seq abstraction | |
148 | ;; --------------------------------------------------------------------------- | |
149 | ||
150 | (defn list-vector-round-trip-equiv | |
151 | [a] | |
152 | ;; NOTE: can't use `(into '() ...)` here because that | |
153 | ;; puts the list in reverse order. clojure.test.check found that bug | |
154 | ;; pretty quickly... | |
155 | (= a (apply list (vec a)))) | |
156 | ||
157 | (deftest list-and-vector-round-trip | |
158 | (testing | |
159 | "" | |
160 | (is (:result | |
161 | (tc/quick-check | |
162 | 1000 (prop/for-all* | |
163 | [(gen/list gen/int)] list-vector-round-trip-equiv)))))) | |
164 | ||
165 | ;; keyword->string->keyword roundtrip | |
166 | ;; --------------------------------------------------------------------------- | |
167 | ||
168 | (def keyword->string->keyword | |
169 | (comp keyword name)) | |
170 | ||
171 | (defn keyword-string-roundtrip-equiv | |
172 | [k] | |
173 | (= k (keyword->string->keyword k))) | |
174 | ||
175 | ;; NOTE cljs: this is one of the slowest due to how keywords are constructed | |
176 | ;; drop N to 100 - David | |
177 | (deftest keyword-string-roundtrip | |
178 | (testing | |
179 | "For all keywords, turning them into a string and back is equivalent | |
180 | to the original string (save for the `:` bit)" | |
181 | (is (:result | |
182 | (let [n #?(:clj 1000 :cljs 100)] | |
183 | (tc/quick-check n (prop/for-all* | |
184 | [gen/keyword] keyword-string-roundtrip-equiv) | |
185 | :max-size 25)))))) | |
186 | ||
187 | ;; Boolean and/or | |
188 | ;; --------------------------------------------------------------------------- | |
189 | ||
190 | (deftest boolean-or | |
191 | (testing | |
192 | "`or` with true and anything else should be true" | |
193 | (is (:result (tc/quick-check | |
194 | 1000 (prop/for-all* | |
195 | [gen/boolean] #(or % true))))))) | |
196 | ||
197 | (deftest boolean-and | |
198 | (testing | |
199 | "`and` with false and anything else should be false" | |
200 | (is (:result (tc/quick-check | |
201 | 1000 (prop/for-all* | |
202 | [gen/boolean] #(not (and % false)))))))) | |
203 | ||
204 | ;; Sorting | |
205 | ;; --------------------------------------------------------------------------- | |
206 | ||
207 | (defn elements-are-in-order-after-sorting | |
208 | [v] | |
209 | (every? identity (map <= (partition 2 1 (sort v))))) | |
210 | ||
211 | (deftest sorting | |
212 | (testing | |
213 | "For all vectors V, sorted(V) should have the elements in order" | |
214 | (is (:result | |
215 | (tc/quick-check | |
216 | 1000 | |
217 | (prop/for-all* | |
218 | [(gen/vector gen/int)] elements-are-in-order-after-sorting)))))) | |
219 | ||
220 | ;; Constant generators | |
221 | ;; --------------------------------------------------------------------------- | |
222 | ||
223 | ;; A constant generator always returns its created value | |
224 | (defspec constant-generators 100 | |
225 | (prop/for-all [a (gen/return 42)] | |
226 | (print "") | |
227 | (= a 42))) | |
228 | ||
229 | (deftest constant-generators-dont-shrink | |
230 | (testing | |
231 | "Generators created with `gen/return` should not shrink" | |
232 | (is (= [42] | |
233 | (let [result (tc/quick-check 100 | |
234 | (prop/for-all | |
235 | [a (gen/return 42)] | |
236 | false))] | |
237 | (-> result :shrunk :smallest)))))) | |
238 | ||
239 | ;; Tests are deterministic | |
240 | ;; --------------------------------------------------------------------------- | |
241 | ||
242 | (defn vector-elements-are-unique | |
243 | [v] | |
244 | (== (count v) (count (distinct v)))) | |
245 | ||
246 | (defn unique-test | |
247 | [seed] | |
248 | (tc/quick-check 1000 | |
249 | (prop/for-all* | |
250 | [(gen/vector gen/int)] vector-elements-are-unique) | |
251 | :seed seed)) | |
252 | ||
253 | (defn equiv-runs | |
254 | [seed] | |
255 | (= (unique-test seed) (unique-test seed))) | |
256 | ||
257 | (deftest tests-are-deterministic | |
258 | (testing "If two runs are started with the same seed, they should | |
259 | return the same results." | |
260 | (is (:result | |
261 | (tc/quick-check 1000 (prop/for-all* [gen/int] equiv-runs)))))) | |
262 | ||
263 | ;; Generating basic generators | |
264 | ;; -------------------------------------------------------------------------- | |
265 | (deftest generators-test | |
266 | (let [t (fn [generator pred] | |
267 | (is (:result (tc/quick-check 100 | |
268 | (prop/for-all [x generator] | |
269 | (pred x)))))) | |
270 | is-char-fn #?(:clj char? :cljs string?)] | |
271 | ||
272 | (testing "keyword" (t gen/keyword keyword?)) | |
273 | ||
274 | ;; No ratio in cljs | |
275 | #?@(:clj [ | |
276 | (testing "ratio" (t gen/ratio (some-fn ratio? integer?))) | |
277 | (testing "byte" (t gen/byte #(instance? Byte %))) | |
278 | (testing "bytes" (t gen/bytes #(instance? (Class/forName "[B") %)))]) | |
279 | ||
280 | ||
281 | (testing "char" (t gen/char is-char-fn)) | |
282 | (testing "char-ascii" (t gen/char-ascii is-char-fn)) | |
283 | (testing "char-alphanumeric" (t gen/char-alphanumeric is-char-fn)) | |
284 | (testing "string" (t gen/string string?)) | |
285 | (testing "string-ascii" (t gen/string-ascii string?)) | |
286 | (testing "string-alphanumeric" (t gen/string-alphanumeric string?)) | |
287 | ||
288 | (testing "vector" (t (gen/vector gen/int) vector?)) | |
289 | (testing "list" (t (gen/list gen/int) list?)) | |
290 | (testing "map" (t (gen/map gen/int gen/int) map?)) | |
291 | )) | |
292 | ||
293 | ;; Generating proper matrices | |
294 | ;; --------------------------------------------------------------------------- | |
295 | ||
296 | (defn proper-matrix? | |
297 | "Check if provided nested vectors form a proper matrix — that is, all nested | |
298 | vectors have the same length" | |
299 | [mtx] | |
300 | (let [first-size (count (first mtx))] | |
301 | (every? (partial = first-size) (map count (rest mtx))))) | |
302 | ||
303 | (deftest proper-matrix-test | |
304 | (testing | |
305 | "can generate proper matrices" | |
306 | (is (:result (tc/quick-check | |
307 | 100 (prop/for-all | |
308 | [mtx (gen/vector (gen/vector gen/int 3) 3)] | |
309 | (proper-matrix? mtx))))))) | |
310 | ||
311 | (def bounds-and-vector | |
312 | (gen/bind (gen/tuple gen/s-pos-int gen/s-pos-int) | |
313 | (fn [[a b]] | |
314 | (let [minimum (min a b) | |
315 | maximum (max a b)] | |
316 | (gen/tuple (gen/return [minimum maximum]) | |
317 | (gen/vector gen/int minimum maximum)))))) | |
318 | ||
319 | (deftest proper-vector-test | |
320 | (testing | |
321 | "can generate vectors with sizes in a provided range" | |
322 | (is (:result (tc/quick-check | |
323 | 100 (prop/for-all | |
324 | [b-and-v bounds-and-vector] | |
325 | (let [[[minimum maximum] v] b-and-v | |
326 | c (count v)] | |
327 | (and (<= c maximum) | |
328 | (>= c minimum))))))))) | |
329 | ||
330 | ;; Tuples and Pairs retain their count during shrinking | |
331 | ;; --------------------------------------------------------------------------- | |
332 | ||
333 | (defn n-int-generators | |
334 | [n] | |
335 | (vec (repeat n gen/int))) | |
336 | ||
337 | (def tuples | |
338 | [(apply gen/tuple (n-int-generators 1)) | |
339 | (apply gen/tuple (n-int-generators 2)) | |
340 | (apply gen/tuple (n-int-generators 3)) | |
341 | (apply gen/tuple (n-int-generators 4)) | |
342 | (apply gen/tuple (n-int-generators 5)) | |
343 | (apply gen/tuple (n-int-generators 6))]) | |
344 | ||
345 | (defn get-tuple-gen | |
346 | [index] | |
347 | (nth tuples (dec index))) | |
348 | ||
349 | (defn inner-tuple-property | |
350 | [size] | |
351 | (prop/for-all [t (get-tuple-gen size)] | |
352 | false)) | |
353 | ||
354 | (defspec tuples-retain-size-during-shrinking 1000 | |
355 | (prop/for-all [index (gen/choose 1 6)] | |
356 | (let [result (tc/quick-check | |
357 | 100 (inner-tuple-property index))] | |
358 | (= index (count (-> result | |
359 | :shrunk :smallest first)))))) | |
360 | ||
361 | ;; Bind works | |
362 | ;; --------------------------------------------------------------------------- | |
363 | ||
364 | (def nat-vec | |
365 | (gen/such-that not-empty | |
366 | (gen/vector gen/nat))) | |
367 | ||
368 | (def vec-and-elem | |
369 | (gen/bind nat-vec | |
370 | (fn [v] | |
371 | (gen/tuple (gen/elements v) (gen/return v))))) | |
372 | ||
373 | (defspec element-is-in-vec 100 | |
374 | (prop/for-all [[element coll] vec-and-elem] | |
375 | (some #{element} coll))) | |
376 | ||
377 | ;; fmap is respected during shrinking | |
378 | ;; --------------------------------------------------------------------------- | |
379 | ||
380 | (def plus-fifty | |
381 | (gen/fmap (partial + 50) gen/nat)) | |
382 | ||
383 | (deftest f-map-respected-during-shrinking | |
384 | (testing | |
385 | "Generators created fmap should have that function applied | |
386 | during shrinking" | |
387 | (is (= [50] | |
388 | (let [result (tc/quick-check 100 | |
389 | (prop/for-all | |
390 | [a plus-fifty] | |
391 | false))] | |
392 | (-> result :shrunk :smallest)))))) | |
393 | ||
394 | ;; gen/int returns an integer when size is a double; regression for TCHECK-73 | |
395 | ;; --------------------------------------------------------------------------- | |
396 | ||
397 | (def gen-double | |
398 | (gen/fmap (fn [[x y]] (double (+ x (/ y 10)))) | |
399 | (gen/tuple gen/pos-int (gen/choose 0 9)))) | |
400 | ||
401 | (defspec gen-int-with-double-size 1000 | |
402 | (prop/for-all [size gen-double] | |
403 | (integer? (gen/generate gen/int size)))) | |
404 | ||
405 | ;; recursive-gen doesn't change ints to doubles; regression for TCHECK-73 | |
406 | ;; --------------------------------------------------------------------------- | |
407 | ||
408 | (defspec recursive-generator-test 100 | |
409 | (let [btree* (fn [g] (gen/hash-map | |
410 | :value gen/int | |
411 | :left g | |
412 | :right g)) | |
413 | btree (gen/recursive-gen btree* (gen/return nil)) | |
414 | valid? (fn valid? [tree] | |
415 | (and (integer? (:value tree)) | |
416 | (or (nil? (:left tree)) | |
417 | (valid? (:left tree))) | |
418 | (or (nil? (:right tree)) | |
419 | (valid? (:right tree)))))] | |
420 | (prop/for-all [t btree] (valid? t)))) | |
421 | ||
422 | ;; NOTE cljs: adjust for JS numerics - NB | |
423 | ||
424 | #?(:clj | |
425 | (deftest calc-long-increasing | |
426 | ;; access internal gen/calc-long function for testing | |
427 | (are [low high] (apply < (map #(@#'gen/calc-long % low high) (range 0.0 0.9999 0.111))) | |
428 | ;; low and high should not be too close, 100 is a reasonable spread | |
429 | (- Long/MAX_VALUE 100) Long/MAX_VALUE | |
430 | Long/MIN_VALUE (+ Long/MIN_VALUE 100) | |
431 | Long/MIN_VALUE 0 | |
432 | 0 100 | |
433 | -100 0 | |
434 | 0 Long/MAX_VALUE | |
435 | Long/MIN_VALUE Long/MAX_VALUE))) | |
436 | ||
437 | ;; edn rountrips | |
438 | ;; --------------------------------------------------------------------------- | |
439 | ||
440 | (defn edn-roundtrip? | |
441 | [value] | |
442 | (= value (-> value prn-str edn/read-string))) | |
443 | ||
444 | (defspec edn-roundtrips 50 | |
445 | (prop/for-all [a gen/any] | |
446 | (edn-roundtrip? a))) | |
447 | ||
448 | ;; not-empty works | |
449 | ;; --------------------------------------------------------------------------- | |
450 | ||
451 | (defspec not-empty-works 100 | |
452 | (prop/for-all [v (gen/not-empty (gen/vector gen/boolean))] | |
453 | (not-empty v))) | |
454 | ||
455 | ;; no-shrink works | |
456 | ;; --------------------------------------------------------------------------- | |
457 | ||
458 | (defn run-no-shrink | |
459 | [i] | |
460 | (tc/quick-check 100 | |
461 | (prop/for-all [coll (gen/vector gen/nat)] | |
462 | (some #{i} coll)))) | |
463 | ||
464 | (defspec no-shrink-works 100 | |
465 | (prop/for-all [i gen/nat] | |
466 | (let [result (run-no-shrink i)] | |
467 | (if (:result result) | |
468 | true | |
469 | (= (:fail result) | |
470 | (-> result :shrunk :smallest)))))) | |
471 | ||
472 | ;; elements works with a variety of input | |
473 | ;; --------------------------------------------------------------------------- | |
474 | ||
475 | (deftest elements-with-empty | |
476 | (is (thrown? #?(:clj AssertionError :cljs js/Error) | |
477 | (gen/elements ())))) | |
478 | ||
479 | (defspec elements-with-a-set 100 | |
480 | (prop/for-all [num (gen/elements #{9 10 11 12})] | |
481 | (<= 9 num 12))) | |
482 | ||
483 | ||
484 | ;; choose respects bounds during shrinking | |
485 | ;; --------------------------------------------------------------------------- | |
486 | ||
487 | (def range-gen | |
488 | (gen/fmap (fn [[a b]] | |
489 | [(min a b) (max a b)]) | |
490 | (gen/tuple gen/int gen/int))) | |
491 | ||
492 | (defspec choose-respects-bounds-during-shrinking 100 | |
493 | (prop/for-all [[mini maxi] range-gen | |
494 | random-seed gen/nat | |
495 | size gen/nat] | |
496 | (let [tree (gen/call-gen | |
497 | (gen/choose mini maxi) | |
498 | (random/make-random random-seed) | |
499 | size)] | |
500 | (every? | |
501 | #(and (<= mini %) (>= maxi %)) | |
502 | (rose/seq tree))))) | |
503 | ||
504 | ||
505 | ;; rand-range copes with full range of longs as bounds | |
506 | ;; --------------------------------------------------------------------------- | |
507 | ||
508 | ;; NOTE cljs: need to adjust for JS numerics - David | |
509 | ||
510 | #?(:clj | |
511 | (deftest rand-range-copes-with-full-range-of-longs | |
512 | (let [[low high] (reduce | |
513 | (fn [[low high :as margins] x] | |
514 | (cond | |
515 | (< x low) [x high] | |
516 | (> x high) [low x] | |
517 | :else margins)) | |
518 | [Long/MAX_VALUE Long/MIN_VALUE] | |
519 | ; choose uses rand-range directly, reasonable proxy for its | |
520 | ; guarantees | |
521 | (take 1e6 (gen/sample-seq (gen/choose Long/MIN_VALUE Long/MAX_VALUE))))] | |
522 | (is (< low high)) | |
523 | (is (< low Integer/MIN_VALUE)) | |
524 | (is (> high Integer/MAX_VALUE))))) | |
525 | ||
526 | ;; rand-range yields values inclusive of both lower & upper bounds provided to it | |
527 | ;; further, that generators that use rand-range use its full range of values | |
528 | ;; --------------------------------------------------------------------------- | |
529 | ||
530 | (deftest rand-range-uses-inclusive-bounds | |
531 | (let [bounds [5 7] | |
532 | rand-range (fn [r] (apply #'gen/rand-range r bounds))] | |
533 | (loop [trials 0 | |
534 | bounds (set bounds) | |
535 | r (random/make-random)] | |
536 | (cond | |
537 | (== trials 10000) | |
538 | (is nil (str "rand-range didn't return both of its bounds after 10000 trials; " | |
539 | "it is possible for this to fail without there being a problem, " | |
540 | "but we should be able to rely upon probability to not bother us " | |
541 | "too frequently.")) | |
542 | (empty? bounds) (is true) | |
543 | :else (let [[r1 r2] (random/split r)] | |
544 | (recur (inc trials) (disj bounds (rand-range r1)) r2)))))) | |
545 | ||
546 | (deftest elements-generates-all-provided-values | |
547 | (let [options [:a 42 'c/d "foo"]] | |
548 | (is (->> (reductions | |
549 | disj | |
550 | (set options) | |
551 | (gen/sample-seq (gen/elements options))) | |
552 | (take 10000) | |
553 | (some empty?)) | |
554 | (str "elements didn't return all of its candidate values after 10000 trials; " | |
555 | "it is possible for this to fail without there being a problem, " | |
556 | "but we should be able to rely upon probability to not bother us " | |
557 | "too frequently.")))) | |
558 | ||
559 | ;; shuffling a vector generates a permutation of that vector | |
560 | ;; --------------------------------------------------------------------------- | |
561 | ||
562 | (def original-vector-and-permutation | |
563 | (gen/bind (gen/vector gen/int) | |
564 | #(gen/tuple (gen/return %) (gen/shuffle %)))) | |
565 | ||
566 | (defspec shuffled-vector-is-a-permutation-of-original 100 | |
567 | (prop/for-all [[coll permutation] original-vector-and-permutation] | |
568 | (= (sort coll) (sort permutation)))) | |
569 | ||
570 | ;; vector can generate large vectors; regression for TCHECK-49 | |
571 | ;; --------------------------------------------------------------------------- | |
572 | ||
573 | (deftest large-vector-test | |
574 | (is (= 100000 | |
575 | (count (first (gen/sample | |
576 | (gen/vector gen/nat 100000) | |
577 | 1)))))) | |
578 | ||
579 | ;; scale controls growth rate of generators | |
580 | ;; --------------------------------------------------------------------------- | |
581 | ||
582 | (deftest scale-test | |
583 | (let [g (gen/scale (partial min 10) gen/pos-int) ;; should limit size to 10 | |
584 | samples (gen/sample g 1000)] | |
585 | (is (every? (partial >= 11) samples)) | |
586 | (is (some (partial = 10) samples)))) | |
587 | ||
588 | ;; generator dev helpers | |
589 | ;; --------------------------------------------------------------------------- | |
590 | ||
591 | (deftest generate-test | |
592 | (is (string? (gen/generate gen/string))) | |
593 | (is (string? (gen/generate gen/string 42)))) | |
594 | ||
595 | ;; defspec macro | |
596 | ;; --------------------------------------------------------------------------- | |
597 | ||
598 | (defspec run-only-once 1 (prop/for-all* [gen/int] (constantly true))) | |
599 | ||
600 | (defspec run-default-times (prop/for-all* [gen/int] (constantly true))) | |
601 | ||
602 | (defspec run-with-map1 {:num-tests 1} (prop/for-all* [gen/int] (constantly true))) | |
603 | ||
604 | (defspec run-with-map {:num-tests 1 | |
605 | :seed 1} | |
606 | (prop/for-all [a gen/int] | |
607 | (= a 0))) | |
608 | ||
609 | (def my-defspec-options {:num-tests 1 :seed 1}) | |
610 | ||
611 | (defspec run-with-symbolic-options my-defspec-options | |
612 | (prop/for-all [a gen/int] | |
613 | (= a 0))) | |
614 | ||
615 | (defspec run-with-no-options | |
616 | (prop/for-all [a gen/int] | |
617 | (integer? a))) | |
618 | ||
619 | (defspec run-float-time 1e3 | |
620 | (prop/for-all [a gen/int] | |
621 | (integer? a))) | |
622 | ||
623 | ;; TCHECK-77 Regression | |
624 | ;; --------------------------------------------------------------------------- | |
625 | ||
626 | ;; Note cljs: need to adjust for JS numerics - NB | |
627 | #?(:clj | |
628 | (deftest choose-distribution-sanity-check | |
629 | (testing | |
630 | "Should not get the same random value more than 90% of the time" | |
631 | ;; This is a probabilistic test; the odds of a false-positive | |
632 | ;; failure for the ranges with two elements should be roughly 1 in | |
633 | ;; 10^162 (and even rarer for larger ranges), so it will never | |
634 | ;; ever happen. | |
635 | (are [low high] (let [xs (gen/sample (gen/choose low high) 1000) | |
636 | count-of-most-frequent (apply max (vals (frequencies xs)))] | |
637 | (< count-of-most-frequent 900)) | |
638 | (dec Long/MAX_VALUE) Long/MAX_VALUE | |
639 | Long/MIN_VALUE (inc Long/MIN_VALUE) | |
640 | Long/MIN_VALUE 0 | |
641 | 0 1 | |
642 | -1 0 | |
643 | 0 Long/MAX_VALUE | |
644 | Long/MIN_VALUE Long/MAX_VALUE)))) |
0 | (ns clojure.test.check.test | |
1 | (:require [cljs.test :as test :refer-macros [deftest testing is]] | |
2 | [clojure.test.check :as tc] | |
3 | [clojure.test.check.generators :as gen] | |
4 | [clojure.test.check.properties :as prop :include-macros true] | |
5 | [clojure.test.check.random :as random] | |
6 | [clojure.test.check.rose-tree :as rose] | |
7 | [clojure.test.check.clojure-test :as ct :refer-macros [defspec]] | |
8 | [cljs.reader :as edn])) | |
9 | ||
10 | (deftest generators-are-generators | |
11 | (testing "generator? returns true when called with a generator" | |
12 | (is (gen/generator? gen/int)) | |
13 | (is (gen/generator? (gen/vector gen/int))) | |
14 | (is (gen/generator? (gen/return 5))))) | |
15 | ||
16 | ||
17 | (deftest values-are-not-generators | |
18 | (testing "generator? returns false when called with a value" | |
19 | (is (not (gen/generator? 5))) | |
20 | (is (not (gen/generator? int))) | |
21 | (is (not (gen/generator? [1 2 3]))))) | |
22 | ||
23 | ;; plus and 0 form a monoid | |
24 | ;; --------------------------------------------------------------------------- | |
25 | ||
26 | (defn passes-monoid-properties | |
27 | [a b c] | |
28 | (and | |
29 | (= (+ 0 a) a) | |
30 | (= (+ a 0) a) | |
31 | (= (+ a (+ b c)) (+ (+ a b) c)))) | |
32 | ||
33 | (deftest plus-and-0-are-a-monoid | |
34 | (testing "+ and 0 form a monoid" | |
35 | (is | |
36 | (let [p (prop/for-all* [gen/int gen/int gen/int] | |
37 | passes-monoid-properties)] | |
38 | (:result (tc/quick-check 1000 p))))) | |
39 | ;; NOTE: no ratios in ClojureScript - David | |
40 | ;; (testing "with ratios as well" | |
41 | ;; (is | |
42 | ;; (let [p (prop/for-all* [gen/ratio gen/ratio gen/ratio] | |
43 | ;; passes-monoid-properties)] | |
44 | ;; (:result (tc/quick-check 1000 p))))) | |
45 | ) | |
46 | ||
47 | ;; reverse | |
48 | ;; --------------------------------------------------------------------------- | |
49 | ||
50 | (defn reverse-equal?-helper | |
51 | [l] | |
52 | (let [r (vec (reverse l))] | |
53 | (and (= (count l) (count r)) | |
54 | (= (seq l) (rseq r))))) | |
55 | ||
56 | (deftest reverse-equal? | |
57 | (testing "For all vectors L, reverse(reverse(L)) == L" | |
58 | (is (let [p (prop/for-all* [(gen/vector gen/int)] reverse-equal?-helper)] | |
59 | (:result (tc/quick-check 1000 p)))))) | |
60 | ||
61 | ;; failing reverse | |
62 | ;; --------------------------------------------------------------------------- | |
63 | ||
64 | (deftest bad-reverse-test | |
65 | (testing "For all vectors L, L == reverse(L). Not true" | |
66 | (is (false? | |
67 | (let [p (prop/for-all* [(gen/vector gen/int)] #(= (reverse %) %))] | |
68 | (:result (tc/quick-check 1000 p))))))) | |
69 | ||
70 | ;; failing element remove | |
71 | ;; --------------------------------------------------------------------------- | |
72 | ||
73 | (defn first-is-gone | |
74 | [l] | |
75 | (not (some #{(first l)} (vec (rest l))))) | |
76 | ||
77 | (deftest bad-remove | |
78 | (testing "For all vectors L, if we remove the first element E, E should not | |
79 | longer be in the list. (This is a false assumption)" | |
80 | (is (false? | |
81 | (let [p (prop/for-all* [(gen/vector gen/int)] first-is-gone)] | |
82 | (:result (tc/quick-check 1000 p))))))) | |
83 | ||
84 | ;; exceptions shrink and return as result | |
85 | ;; --------------------------------------------------------------------------- | |
86 | ||
87 | (def exception (js/Error. "I get caught")) | |
88 | ||
89 | (defn exception-thrower | |
90 | [& args] | |
91 | (throw exception)) | |
92 | ||
93 | (deftest exceptions-are-caught | |
94 | (testing "Exceptions during testing are caught. They're also shrunk as long | |
95 | as they continue to throw." | |
96 | (is (= [exception [0]] | |
97 | (let [result | |
98 | (tc/quick-check | |
99 | 1000 (prop/for-all* [gen/int] exception-thrower))] | |
100 | [(:result result) (get-in result [:shrunk :smallest])]))))) | |
101 | ||
102 | ;; Count and concat work as expected | |
103 | ;; --------------------------------------------------------------------------- | |
104 | ||
105 | (defn concat-counts-correct | |
106 | [a b] | |
107 | (= (count (concat a b)) | |
108 | (+ (count a) (count b)))) | |
109 | ||
110 | (deftest count-and-concat | |
111 | (testing "For all vectors A and B: | |
112 | length(A + B) == length(A) + length(B)" | |
113 | (is (:result | |
114 | (let [p (prop/for-all* [(gen/vector gen/int) | |
115 | (gen/vector gen/int)] concat-counts-correct)] | |
116 | (tc/quick-check 1000 p)))))) | |
117 | ||
118 | ;; Interpose (Count) | |
119 | ;; --------------------------------------------------------------------------- | |
120 | ||
121 | (defn interpose-twice-the-length ;; (or one less) | |
122 | [v] | |
123 | (let [interpose-count (count (interpose :i v)) | |
124 | original-count (count v)] | |
125 | (or | |
126 | (= (* 2 original-count) interpose-count) | |
127 | (= (dec (* 2 original-count)) interpose-count)))) | |
128 | ||
129 | ||
130 | (deftest interpose-creates-sequence-twice-the-length | |
131 | (testing | |
132 | "Interposing a collection with a value makes its count | |
133 | twice the original collection, or ones less." | |
134 | (is (:result | |
135 | (tc/quick-check 1000 (prop/for-all [v (gen/vector gen/int)] (interpose-twice-the-length v))))))) | |
136 | ||
137 | ;; Lists and vectors are equivalent with seq abstraction | |
138 | ;; --------------------------------------------------------------------------- | |
139 | ||
140 | (defn list-vector-round-trip-equiv | |
141 | [a] | |
142 | ;; NOTE: can't use `(into '() ...)` here because that | |
143 | ;; puts the list in reverse order. clojure.test.check found that bug | |
144 | ;; pretty quickly... | |
145 | (= a (apply list (vec a)))) | |
146 | ||
147 | (deftest list-and-vector-round-trip | |
148 | (testing | |
149 | "" | |
150 | (is (:result | |
151 | (tc/quick-check | |
152 | 1000 (prop/for-all* | |
153 | [(gen/list gen/int)] list-vector-round-trip-equiv)))))) | |
154 | ||
155 | ;; keyword->string->keyword roundtrip | |
156 | ;; --------------------------------------------------------------------------- | |
157 | ||
158 | (defn keyword-string-roundtrip-equiv | |
159 | [k] | |
160 | (= k (keyword (name k)))) | |
161 | ||
162 | ;; NOTE: this is one of the slowest due to how keywords are constructed | |
163 | ;; drop N to 200 - David | |
164 | (deftest keyword-string-roundtrip | |
165 | (testing | |
166 | "For all keywords, turning them into a string and back is equivalent | |
167 | to the original string (save for the `:` bit)" | |
168 | (is (:result | |
169 | (tc/quick-check 100 (prop/for-all* | |
170 | [gen/keyword] keyword-string-roundtrip-equiv) | |
171 | :max-size 25))))) | |
172 | ||
173 | ;; Boolean and/or | |
174 | ;; --------------------------------------------------------------------------- | |
175 | ||
176 | (deftest boolean-or | |
177 | (testing | |
178 | "`or` with true and anything else should be true" | |
179 | (is (:result (tc/quick-check | |
180 | 1000 (prop/for-all* | |
181 | [gen/boolean] #(or % true))))))) | |
182 | ||
183 | (deftest boolean-and | |
184 | (testing | |
185 | "`and` with false and anything else should be false" | |
186 | (is (:result (tc/quick-check | |
187 | 1000 (prop/for-all* | |
188 | [gen/boolean] #(not (and % false)))))))) | |
189 | ||
190 | ;; Sorting | |
191 | ;; --------------------------------------------------------------------------- | |
192 | ||
193 | (defn elements-are-in-order-after-sorting | |
194 | [v] | |
195 | (every? identity (map <= (partition 2 1 (sort v))))) | |
196 | ||
197 | (deftest sorting | |
198 | (testing | |
199 | "For all vectors V, sorted(V) should have the elements in order" | |
200 | (is (:result | |
201 | (tc/quick-check | |
202 | 1000 | |
203 | (prop/for-all* | |
204 | [(gen/vector gen/int)] elements-are-in-order-after-sorting)))))) | |
205 | ||
206 | ;; Constant generators | |
207 | ;; --------------------------------------------------------------------------- | |
208 | ||
209 | ;; A constant generator always returns its created value | |
210 | (defspec constant-generators 100 | |
211 | (prop/for-all [a (gen/return 42)] | |
212 | (print "") | |
213 | (= a 42))) | |
214 | ||
215 | (deftest constant-generators-dont-shrink | |
216 | (testing "Generators created with `gen/return` should not shrink" | |
217 | (is (= [42] | |
218 | (let [result (tc/quick-check 100 | |
219 | (prop/for-all | |
220 | [a (gen/return 42)] | |
221 | false))] | |
222 | (-> result :shrunk :smallest)))))) | |
223 | ||
224 | ;; Tests are deterministic | |
225 | ;; --------------------------------------------------------------------------- | |
226 | ||
227 | (defn vector-elements-are-unique | |
228 | [v] | |
229 | (== (count v) (count (distinct v)))) | |
230 | ||
231 | (defn unique-test | |
232 | [seed] | |
233 | (tc/quick-check 1000 | |
234 | (prop/for-all* | |
235 | [(gen/vector gen/int)] vector-elements-are-unique) | |
236 | :seed seed)) | |
237 | ||
238 | (defn equiv-runs | |
239 | [seed] | |
240 | (= (unique-test seed) (unique-test seed))) | |
241 | ||
242 | (deftest tests-are-deterministic | |
243 | (testing "If two runs are started with the same seed, they should | |
244 | return the same results." | |
245 | (is (:result | |
246 | (tc/quick-check 1000 (prop/for-all* [gen/int] equiv-runs)))))) | |
247 | ||
248 | ;; Generating basic generators | |
249 | ;; -------------------------------------------------------------------------- | |
250 | ||
251 | (deftest generators-test | |
252 | (let [t (fn [generator pred] | |
253 | (is (:result (tc/quick-check 100 | |
254 | (prop/for-all [x generator] | |
255 | (pred x))))))] | |
256 | ||
257 | (testing "keyword" (t gen/keyword keyword?)) | |
258 | ;; (testing "ratio" (t gen/ratio clojure.lang.Ratio)) | |
259 | ;; (testing "byte" (t gen/byte Byte)) | |
260 | ;; (testing "bytes" (t gen/bytes (Class/forName "[B"))) | |
261 | ||
262 | (testing "char" (t gen/char string?)) | |
263 | (testing "char-ascii" (t gen/char-ascii string?)) | |
264 | (testing "char-alphanumeric" (t gen/char-alphanumeric string?)) | |
265 | (testing "string" (t gen/string string?)) | |
266 | (testing "string-ascii" (t gen/string-ascii string?)) | |
267 | (testing "string-alphanumeric" (t gen/string-alphanumeric string?)) | |
268 | ||
269 | (testing "vector" (t (gen/vector gen/int) vector?)) | |
270 | (testing "list" (t (gen/list gen/int) list?)) | |
271 | (testing "map" (t (gen/map gen/int gen/int) map?)) | |
272 | )) | |
273 | ||
274 | ;; Generating proper matrices | |
275 | ;; --------------------------------------------------------------------------- | |
276 | ||
277 | (defn proper-matrix? | |
278 | "Check if provided nested vectors form a proper matrix — that is, all nested | |
279 | vectors have the same length" | |
280 | [mtx] | |
281 | (let [first-size (count (first mtx))] | |
282 | (every? (partial = first-size) (map count (rest mtx))))) | |
283 | ||
284 | (deftest proper-matrix-test | |
285 | (testing | |
286 | "can generate proper matrices" | |
287 | (is (:result (tc/quick-check | |
288 | 100 (prop/for-all | |
289 | [mtx (gen/vector (gen/vector gen/int 3) 3)] | |
290 | (proper-matrix? mtx))))))) | |
291 | ||
292 | (def bounds-and-vector | |
293 | (gen/bind (gen/tuple gen/s-pos-int gen/s-pos-int) | |
294 | (fn [[a b]] | |
295 | (let [minimum (min a b) | |
296 | maximum (max a b)] | |
297 | (gen/tuple (gen/return [minimum maximum]) | |
298 | (gen/vector gen/int minimum maximum)))))) | |
299 | ||
300 | (deftest proper-vector-test | |
301 | (testing | |
302 | "can generate vectors with sizes in a provided range" | |
303 | (is (:result (tc/quick-check | |
304 | 100 (prop/for-all | |
305 | [b-and-v bounds-and-vector] | |
306 | (let [[[minimum maximum] v] b-and-v | |
307 | c (count v)] | |
308 | (and (<= c maximum) | |
309 | (>= c minimum))))))))) | |
310 | ||
311 | ;; Tuples and Pairs retain their count during shrinking | |
312 | ;; --------------------------------------------------------------------------- | |
313 | ||
314 | (defn n-int-generators | |
315 | [n] | |
316 | (vec (repeat n gen/int))) | |
317 | ||
318 | (def tuples | |
319 | [(apply gen/tuple (n-int-generators 1)) | |
320 | (apply gen/tuple (n-int-generators 2)) | |
321 | (apply gen/tuple (n-int-generators 3)) | |
322 | (apply gen/tuple (n-int-generators 4)) | |
323 | (apply gen/tuple (n-int-generators 5)) | |
324 | (apply gen/tuple (n-int-generators 6))]) | |
325 | ||
326 | (defn get-tuple-gen | |
327 | [index] | |
328 | (nth tuples (dec index))) | |
329 | ||
330 | (defn inner-tuple-property | |
331 | [size] | |
332 | (prop/for-all [t (get-tuple-gen size)] | |
333 | false)) | |
334 | ||
335 | (defspec tuples-retain-size-during-shrinking 1000 | |
336 | (prop/for-all [index (gen/choose 1 6)] | |
337 | (let [result (tc/quick-check | |
338 | 100 (inner-tuple-property index))] | |
339 | (= index (count (-> result | |
340 | :shrunk :smallest first)))))) | |
341 | ||
342 | ;; Bind works | |
343 | ;; --------------------------------------------------------------------------- | |
344 | ||
345 | (def nat-vec | |
346 | (gen/such-that not-empty | |
347 | (gen/vector gen/nat))) | |
348 | ||
349 | (def vec-and-elem | |
350 | (gen/bind nat-vec | |
351 | (fn [v] | |
352 | (gen/tuple (gen/elements v) (gen/return v))))) | |
353 | ||
354 | (defspec element-is-in-vec 100 | |
355 | (prop/for-all [[element coll] vec-and-elem] | |
356 | (some #{element} coll))) | |
357 | ||
358 | ;; fmap is respected during shrinking | |
359 | ;; --------------------------------------------------------------------------- | |
360 | ||
361 | (def plus-fifty | |
362 | (gen/fmap (partial + 50) gen/nat)) | |
363 | ||
364 | (deftest f-map-respected-during-shrinking | |
365 | (testing | |
366 | "Generators created fmap should have that function applied | |
367 | during shrinking" | |
368 | (is (= [50] | |
369 | (let [result (tc/quick-check 100 | |
370 | (prop/for-all | |
371 | [a plus-fifty] | |
372 | false))] | |
373 | (-> result :shrunk :smallest)))))) | |
374 | ||
375 | ;; edn rountrips | |
376 | ;; --------------------------------------------------------------------------- | |
377 | ||
378 | ;; TODO: EDN round trips sometimes fail - David | |
379 | ||
380 | (defn edn-roundtrip? | |
381 | [value] | |
382 | (= value (-> value prn-str edn/read-string))) | |
383 | ||
384 | (defspec edn-roundtrips 50 | |
385 | (prop/for-all [a gen/any] | |
386 | (edn-roundtrip? a))) | |
387 | ||
388 | ;; not-empty works | |
389 | ;; --------------------------------------------------------------------------- | |
390 | ||
391 | (defspec not-empty-works 100 | |
392 | (prop/for-all [v (gen/not-empty (gen/vector gen/boolean))] | |
393 | (not-empty v))) | |
394 | ||
395 | ;; no-shrink works | |
396 | ;; --------------------------------------------------------------------------- | |
397 | ||
398 | (defn run-no-shrink | |
399 | [i] | |
400 | (tc/quick-check 100 | |
401 | (prop/for-all [coll (gen/vector gen/nat)] | |
402 | (some #{i} coll)))) | |
403 | ||
404 | (defspec no-shrink-works 100 | |
405 | (prop/for-all [i gen/nat] | |
406 | (let [result (run-no-shrink i)] | |
407 | (if (:result result) | |
408 | true | |
409 | (= (:fail result) | |
410 | (-> result :shrunk :smallest)))))) | |
411 | ||
412 | ;; elements works with a variety of input | |
413 | ;; --------------------------------------------------------------------------- | |
414 | ||
415 | (deftest elements-with-empty | |
416 | (is (thrown? js/Error (gen/elements ())))) | |
417 | ||
418 | (defspec elements-with-a-set 100 | |
419 | (prop/for-all [num (gen/elements #{9 10 11 12})] | |
420 | (<= 9 num 12))) | |
421 | ||
422 | ||
423 | ;; choose respects bounds during shrinking | |
424 | ;; --------------------------------------------------------------------------- | |
425 | ||
426 | (def range-gen | |
427 | (gen/fmap (fn [[a b]] | |
428 | [(min a b) (max a b)]) | |
429 | (gen/tuple gen/int gen/int))) | |
430 | ||
431 | (defspec choose-respects-bounds-during-shrinking 100 | |
432 | (prop/for-all [[mini maxi] range-gen | |
433 | random-seed gen/nat | |
434 | size gen/nat] | |
435 | (let [tree (gen/call-gen | |
436 | (gen/choose mini maxi) | |
437 | (random/make-random random-seed) | |
438 | size)] | |
439 | (every? | |
440 | #(and (<= mini %) (>= maxi %)) | |
441 | (rose/seq tree))))) | |
442 | ||
443 | ;; rand-range copes with full range of longs as bounds | |
444 | ;; --------------------------------------------------------------------------- | |
445 | ||
446 | ;; NOTE: need to adjust for JS numerics - David | |
447 | ||
448 | ;; (deftest rand-range-copes-with-full-range-of-longs | |
449 | ;; (let [[low high] (reduce | |
450 | ;; (fn [[low high :as margins] x] | |
451 | ;; (cond | |
452 | ;; (< x low) [x high] | |
453 | ;; (> x high) [low x] | |
454 | ;; :else margins)) | |
455 | ;; [Long/MAX_VALUE Long/MIN_VALUE] | |
456 | ;; ; choose uses rand-range directly, reasonable proxy for its | |
457 | ;; ; guarantees | |
458 | ;; (take 1e6 (gen/sample-seq (gen/choose Long/MIN_VALUE Long/MAX_VALUE))))] | |
459 | ;; (is (< low high)) | |
460 | ;; (is (< low Integer/MIN_VALUE)) | |
461 | ;; (is (> high Integer/MAX_VALUE)))) | |
462 | ||
463 | ;; rand-range yields values inclusive of both lower & upper bounds provided to it | |
464 | ;; further, that generators that use rand-range use its full range of values | |
465 | ;; --------------------------------------------------------------------------- | |
466 | ||
467 | (deftest rand-range-uses-inclusive-bounds | |
468 | (let [bounds [5 7] | |
469 | rand-range (fn [r] (apply gen/rand-range r bounds))] | |
470 | (loop [trials 0 | |
471 | bounds (set bounds) | |
472 | r (random/make-random)] | |
473 | (cond | |
474 | (== trials 10000) | |
475 | (is nil (str "rand-range didn't return both of its bounds after 10000 trials; " | |
476 | "it is possible for this to fail without there being a problem, " | |
477 | "but we should be able to rely upon probability to not bother us " | |
478 | "too frequently.")) | |
479 | (empty? bounds) (is true) | |
480 | :else (let [[r1 r2] (random/split r)] | |
481 | (recur (inc trials) (disj bounds (rand-range r1)) r2)))))) | |
482 | ||
483 | (deftest elements-generates-all-provided-values | |
484 | (let [options [:a 42 'c/d "foo"]] | |
485 | (is (->> (reductions | |
486 | disj | |
487 | (set options) | |
488 | (gen/sample-seq (gen/elements options))) | |
489 | (take 10000) | |
490 | (some empty?)) | |
491 | (str "elements didn't return all of its candidate values after 10000 trials; " | |
492 | "it is possible for this to fail without there being a problem, " | |
493 | "but we should be able to rely upon probability to not bother us " | |
494 | "too frequently.")))) | |
495 | ||
496 | ;; shuffling a vector generates a permutation of that vector | |
497 | ;; --------------------------------------------------------------------------- | |
498 | ||
499 | (def original-vector-and-permutation | |
500 | (gen/bind (gen/vector gen/int) | |
501 | #(gen/tuple (gen/return %) (gen/shuffle %)))) | |
502 | ||
503 | (defspec shuffled-vector-is-a-permutation-of-original 100 | |
504 | (prop/for-all [[coll permutation] original-vector-and-permutation] | |
505 | (= (sort coll) (sort permutation)))) | |
506 | ||
507 | ;; vector can generate large vectors; regression for TCHECK-49 | |
508 | ;; --------------------------------------------------------------------------- | |
509 | ||
510 | (deftest large-vector-test | |
511 | (is (= 100000 | |
512 | (count (first (gen/sample | |
513 | (gen/vector gen/nat 100000) | |
514 | 1)))))) | |
515 | ||
516 | ;; scale controls growth rate of generators | |
517 | ;; --------------------------------------------------------------------------- | |
518 | ||
519 | (deftest scale-test | |
520 | (let [g (gen/scale (partial min 10) gen/pos-int) ;; should limit size to 10 | |
521 | samples (gen/sample g 1000)] | |
522 | (is (every? (partial >= 11) samples)) | |
523 | (is (some (partial = 10) samples)))) | |
524 | ||
525 | ;; generator dev helpers | |
526 | ;; --------------------------------------------------------------------------- | |
527 | ||
528 | (deftest generate-test | |
529 | (is (string? (gen/generate gen/string))) | |
530 | (is (string? (gen/generate gen/string 42)))) | |
531 | ||
532 | ;; defspec macro | |
533 | ;; --------------------------------------------------------------------------- | |
534 | ||
535 | (defspec run-only-once 1 (prop/for-all* [gen/int] (constantly true))) | |
536 | ||
537 | (defspec run-default-times (prop/for-all* [gen/int] (constantly true))) | |
538 | ||
539 | (defspec run-with-map1 {:num-tests 1} (prop/for-all* [gen/int] (constantly true))) | |
540 | ||
541 | (defspec run-with-map {:num-tests 1 | |
542 | :seed 1} | |
543 | (prop/for-all [a gen/int] | |
544 | (= a 0))) | |
545 | ||
546 | (def my-defspec-options {:num-tests 1 :seed 1}) | |
547 | ||
548 | (defspec run-with-symbolic-options my-defspec-options | |
549 | (prop/for-all [a gen/int] | |
550 | (= a 0))) | |
551 | ||
552 | (defspec run-with-no-options | |
553 | (prop/for-all [a gen/int] | |
554 | (integer? a))) |