Codebase list test-check-clojure / b7e7e91
generators.{[clj cljs] -> cljc} Nicolas Berger authored 8 years ago Gary Fredericks committed 8 years ago
3 changed file(s) with 735 addition(s) and 1400 deletion(s). Raw diff Collapse all Expand all
+0
-711
src/main/clojure/clojure/test/check/generators.clj less more
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.generators
10 (:refer-clojure :exclude [int vector list hash-map map keyword
11 char boolean byte bytes sequence
12 shuffle not-empty symbol namespace])
13 (:require [clojure.core :as core]
14 [clojure.test.check.random :as random]
15 [clojure.test.check.rose-tree :as rose]))
16
17
18 ;; Gen
19 ;; (internal functions)
20 ;; ---------------------------------------------------------------------------
21
22 (defrecord Generator [gen])
23
24 (defn generator?
25 "Test if `x` is a generator. Generators should be treated as opaque values."
26 [x]
27 (instance? Generator x))
28
29 (defn- make-gen
30 [generator-fn]
31 (Generator. generator-fn))
32
33 (defn call-gen
34 {:no-doc true}
35 [{generator-fn :gen} rnd size]
36 (generator-fn rnd size))
37
38 (defn gen-pure
39 {:no-doc true}
40 [value]
41 (make-gen
42 (fn [rnd size]
43 value)))
44
45 (defn gen-fmap
46 {:no-doc true}
47 [k {h :gen}]
48 (make-gen
49 (fn [rnd size]
50 (k (h rnd size)))))
51
52 (defn gen-bind
53 {:no-doc true}
54 [{h :gen} k]
55 (make-gen
56 (fn [rnd size]
57 (let [[r1 r2] (random/split rnd)
58 inner (h r1 size)
59 {result :gen} (k inner)]
60 (result r2 size)))))
61
62 (defn lazy-random-states
63 "Given a random number generator, returns an infinite lazy sequence
64 of random number generators."
65 [rr]
66 (lazy-seq
67 (let [[r1 r2] (random/split rr)]
68 (cons r1
69 (lazy-random-states r2)))))
70
71 (defn- gen-seq->seq-gen
72 "Takes a sequence of generators and returns a generator of sequences (er, vectors)."
73 [gens]
74 (make-gen
75 (fn [rnd size]
76 (mapv #(call-gen % %2 size) gens (random/split-n rnd (count gens))))))
77
78 ;; Exported generator functions
79 ;; ---------------------------------------------------------------------------
80
81 (defn fmap
82 [f gen]
83 (assert (generator? gen) "Second arg to fmap must be a generator")
84 (gen-fmap (partial rose/fmap f) gen))
85
86
87 (defn return
88 "Create a generator that always returns `value`,
89 and never shrinks. You can think of this as
90 the `constantly` of generators."
91 [value]
92 (gen-pure (rose/pure value)))
93
94 (defn- bind-helper
95 [k]
96 (fn [rose]
97 (gen-fmap rose/join
98 (make-gen
99 (fn [rnd size]
100 (rose/fmap #(call-gen % rnd size)
101 (rose/fmap k rose)))))))
102
103 (defn bind
104 "Create a new generator that passes the result of `gen` into function
105 `k`. `k` should return a new generator. This allows you to create new
106 generators that depend on the value of other generators. For example,
107 to create a generator which first generates a vector of integers, and
108 then chooses a random element from that vector:
109
110 (gen/bind (gen/such-that not-empty (gen/vector gen/int))
111 ;; this function takes a realized vector,
112 ;; and then returns a new generator which
113 ;; chooses a random element from it
114 gen/elements)
115
116 "
117 [generator k]
118 (assert (generator? generator) "First arg to bind must be a generator")
119 (gen-bind generator (bind-helper k)))
120
121 ;; Helpers
122 ;; ---------------------------------------------------------------------------
123
124 (defn make-size-range-seq
125 {:no-doc true}
126 [max-size]
127 (cycle (range 0 max-size)))
128
129 (defn sample-seq
130 "Return a sequence of realized values from `generator`."
131 ([generator] (sample-seq generator 100))
132 ([generator max-size]
133 (let [r (random/make-random)
134 size-seq (make-size-range-seq max-size)]
135 (core/map (comp rose/root #(call-gen generator %1 %2))
136 (lazy-random-states r)
137 size-seq))))
138
139 (defn sample
140 "Return a sequence of `num-samples` (default 10)
141 realized values from `generator`."
142 ([generator]
143 (sample generator 10))
144 ([generator num-samples]
145 (assert (generator? generator) "First arg to sample must be a generator")
146 (take num-samples (sample-seq generator))))
147
148
149 (defn generate
150 "Returns a single sample value from the generator, using a default
151 size of 30."
152 ([generator]
153 (generate generator 30))
154 ([generator size]
155 (let [rng (random/make-random)]
156 (rose/root (call-gen generator rng size)))))
157
158
159 ;; Internal Helpers
160 ;; ---------------------------------------------------------------------------
161
162 (defn- halfs
163 [n]
164 (take-while (partial not= 0) (iterate #(quot % 2) n)))
165
166 (defn- shrink-int
167 [integer]
168 (core/map (partial - integer) (halfs integer)))
169
170 (defn- int-rose-tree
171 [value]
172 (rose/make-rose value (core/map int-rose-tree (shrink-int value))))
173
174 ;; calc-long is factored out to support testing the surprisingly tricky double math. Note:
175 ;; An extreme long value does not have a precision-preserving representation as a double.
176 ;; Be careful about changing this code unless you understand what's happening in these
177 ;; examples:
178 ;;
179 ;; (= (long (- Integer/MAX_VALUE (double (- Integer/MAX_VALUE 10)))) 10)
180 ;; (= (long (- Long/MAX_VALUE (double (- Long/MAX_VALUE 10)))) 0)
181
182 (defn- calc-long
183 [factor lower upper]
184 ;; these pre- and post-conditions are disabled for deployment
185 #_ {:pre [(float? factor) (>= factor 0.0) (< factor 1.0)
186 (integer? lower) (integer? upper) (<= lower upper)]
187 :post [(integer? %)]}
188 ;; Use -' on width to maintain accuracy with overflow protection.
189 (let [width (-' upper lower -1)]
190 ;; Preserve long precision if the width is in the long range. Otherwise, we must accept
191 ;; less precision because doubles don't have enough bits to preserve long equivalence at
192 ;; extreme values.
193 (if (< width Long/MAX_VALUE)
194 (+ lower (long (Math/floor (* factor width))))
195 ;; Clamp down to upper because double math.
196 (min upper (long (Math/floor (+ lower (* factor width))))))))
197
198 (defn- rand-range
199 [rnd lower upper]
200 {:pre [(<= lower upper)]}
201 (calc-long (random/rand-double rnd) lower upper))
202
203 (defn sized
204 "Create a generator that depends on the size parameter.
205 `sized-gen` is a function that takes an integer and returns
206 a generator."
207 [sized-gen]
208 (make-gen
209 (fn [rnd size]
210 (let [sized-gen (sized-gen size)]
211 (call-gen sized-gen rnd size)))))
212
213 ;; Combinators and helpers
214 ;; ---------------------------------------------------------------------------
215
216 (defn resize
217 "Create a new generator with `size` always bound to `n`."
218 [n generator]
219 (assert (generator? generator) "Second arg to resize must be a generator")
220 (let [{:keys [gen]} generator]
221 (make-gen
222 (fn [rnd _size]
223 (gen rnd n)))))
224
225 (defn scale
226 "Create a new generator that modifies the size parameter by the given function. Intended to
227 support generators with sizes that need to grow at different rates compared to the normal
228 linear scaling."
229 ([f generator]
230 (sized (fn [n] (resize (f n) generator)))))
231
232 (defn choose
233 "Create a generator that returns long integers in the range `lower` to `upper`, inclusive."
234 [lower upper]
235 ;; cast to long to support doubles as arguments per TCHECK-73
236 (let [lower (long lower)
237 upper (long upper)]
238 (make-gen
239 (fn [rnd _size]
240 (let [value (rand-range rnd lower upper)]
241 (rose/filter
242 #(and (>= % lower) (<= % upper))
243 (int-rose-tree value)))))))
244
245 (defn one-of
246 "Create a generator that randomly chooses a value from the list of
247 provided generators. Shrinks toward choosing an earlier generator,
248 as well as shrinking the value generated by the chosen generator.
249
250 Examples:
251
252 (one-of [gen/int gen/boolean (gen/vector gen/int)])
253
254 "
255 [generators]
256 (assert (every? generator? generators)
257 "Arg to one-of must be a collection of generators")
258 (bind (choose 0 (dec (count generators)))
259 (partial nth generators)))
260
261 (defn- pick
262 [[h & tail] n]
263 (let [[chance gen] h]
264 (if (<= n chance)
265 gen
266 (recur tail (- n chance)))))
267
268 (defn frequency
269 "Create a generator that chooses a generator from `pairs` based on the
270 provided likelihoods. The likelihood of a given generator being chosen is
271 its likelihood divided by the sum of all likelihoods
272
273 Examples:
274
275 (gen/frequency [[5 gen/int] [3 (gen/vector gen/int)] [2 gen/boolean]])
276 "
277 [pairs]
278 (assert (every? (fn [[x g]] (and (number? x) (generator? g)))
279 pairs)
280 "Arg to frequency must be a list of [num generator] pairs")
281 (let [total (apply + (core/map first pairs))]
282 (gen-bind (choose 1 total)
283 #(pick pairs (rose/root %)))))
284
285 (defn elements
286 "Create a generator that randomly chooses an element from `coll`.
287
288 Examples:
289
290 (gen/elements [:foo :bar :baz])
291 "
292 [coll]
293 (assert (seq coll) "elements cannot be called with an empty collection")
294 (let [v (vec coll)]
295 (gen-bind (choose 0 (dec (count v)))
296 #(gen-pure (rose/fmap v %)))))
297
298 (defn- such-that-helper
299 [max-tries pred gen tries-left rand-seed size]
300 (if (zero? tries-left)
301 (throw (ex-info (str "Couldn't satisfy such-that predicate after "
302 max-tries " tries.") {}))
303 (let [[r1 r2] (random/split rand-seed)
304 value (call-gen gen r1 size)]
305 (if (pred (rose/root value))
306 (rose/filter pred value)
307 (recur max-tries pred gen (dec tries-left) r2 (inc size))))))
308
309 (defn such-that
310 "Create a generator that generates values from `gen` that satisfy predicate
311 `pred`. Care is needed to ensure there is a high chance `gen` will satisfy
312 `pred`. By default, `such-that` will try 10 times to generate a value that
313 satisfies the predicate. If no value passes this predicate after this number
314 of iterations, a runtime exception will be throw. You can pass an optional
315 third argument to change the number of times tried. Note also that each
316 time such-that retries, it will increase the size parameter.
317
318 Examples:
319
320 ;; generate non-empty vectors of integers
321 ;; (note, gen/not-empty does exactly this)
322 (gen/such-that not-empty (gen/vector gen/int))
323 "
324 ([pred gen]
325 (such-that pred gen 10))
326 ([pred gen max-tries]
327 (assert (generator? gen) "Second arg to such-that must be a generator")
328 (make-gen
329 (fn [rand-seed size]
330 (such-that-helper max-tries pred gen max-tries rand-seed size)))))
331
332 (defn not-empty
333 "Modifies a generator so that it doesn't generate empty collections.
334
335 Examples:
336
337 ;; generate a vector of booleans, but never the empty vector
338 (gen/not-empty (gen/vector gen/boolean))
339 "
340 [gen]
341 (assert (generator? gen) "Arg to not-empty must be a generator")
342 (such-that core/not-empty gen))
343
344 (defn no-shrink
345 "Create a new generator that is just like `gen`, except does not shrink
346 at all. This can be useful when shrinking is taking a long time or is not
347 applicable to the domain."
348 [gen]
349 (assert (generator? gen) "Arg to no-shrink must be a generator")
350 (gen-bind gen
351 (fn [[root _children]]
352 (gen-pure
353 [root []]))))
354
355 (defn shrink-2
356 "Create a new generator like `gen`, but will consider nodes for shrinking
357 even if their parent passes the test (up to one additional level)."
358 [gen]
359 (assert (generator? gen) "Arg to shrink-2 must be a generator")
360 (gen-bind gen (comp gen-pure rose/collapse)))
361
362 (def boolean
363 "Generates one of `true` or `false`. Shrinks to `false`."
364 (elements [false true]))
365
366 (defn tuple
367 "Create a generator that returns a vector, whose elements are chosen
368 from the generators in the same position. The individual elements shrink
369 according to their generator, but the value will never shrink in count.
370
371 Examples:
372
373 (def t (tuple gen/int gen/boolean))
374 (sample t)
375 ;; => ([1 true] [2 true] [2 false] [1 false] [0 true] [-2 false] [-6 false]
376 ;; => [3 true] [-4 false] [9 true]))
377 "
378 [& generators]
379 (assert (every? generator? generators)
380 "Args to tuple must be generators")
381 (gen-bind (gen-seq->seq-gen generators)
382 (fn [roses]
383 (gen-pure (rose/zip core/vector roses)))))
384
385 (def int
386 "Generates a positive or negative integer bounded by the generator's
387 `size` parameter.
388 (Really returns a long)"
389 (sized (fn [size] (choose (- size) size))))
390
391 (def nat
392 "Generates natural numbers, starting at zero. Shrinks to zero."
393 (fmap #(Math/abs (long %)) int))
394
395 (def pos-int
396 "Generate positive integers bounded by the generator's `size` parameter."
397 nat)
398
399 (def neg-int
400 "Generate negative integers bounded by the generator's `size` parameter."
401 (fmap (partial * -1) nat))
402
403 (def s-pos-int
404 "Generate strictly positive integers bounded by the generator's `size`
405 parameter."
406 (fmap inc nat))
407
408 (def s-neg-int
409 "Generate strictly negative integers bounded by the generator's `size`
410 parameter."
411 (fmap dec neg-int))
412
413 (defn vector
414 "Create a generator whose elements are chosen from `gen`. The count of the
415 vector will be bounded by the `size` generator parameter."
416 ([generator]
417 (assert (generator? generator) "Arg to vector must be a generator")
418 (gen-bind
419 (sized #(choose 0 %))
420 (fn [num-elements-rose]
421 (gen-bind (gen-seq->seq-gen
422 (repeat (rose/root num-elements-rose)
423 generator))
424 (fn [roses]
425 (gen-pure (rose/shrink core/vector
426 roses)))))))
427 ([generator num-elements]
428 (assert (generator? generator) "First arg to vector must be a generator")
429 (apply tuple (repeat num-elements generator)))
430 ([generator min-elements max-elements]
431 (assert (generator? generator) "First arg to vector must be a generator")
432 (gen-bind
433 (choose min-elements max-elements)
434 (fn [num-elements-rose]
435 (gen-bind (gen-seq->seq-gen
436 (repeat (rose/root num-elements-rose)
437 generator))
438 (fn [roses]
439 (gen-bind
440 (gen-pure (rose/shrink core/vector
441 roses))
442 (fn [rose]
443 (gen-pure (rose/filter
444 (fn [v] (and (>= (count v) min-elements)
445 (<= (count v) max-elements))) rose))))))))))
446
447 (defn list
448 "Like `vector`, but generates lists."
449 [generator]
450 (assert (generator? generator) "First arg to list must be a generator")
451 (gen-bind (sized #(choose 0 %))
452 (fn [num-elements-rose]
453 (gen-bind (gen-seq->seq-gen
454 (repeat (rose/root num-elements-rose)
455 generator))
456 (fn [roses]
457 (gen-pure (rose/shrink core/list
458 roses)))))))
459
460 (defn- swap
461 [coll [i1 i2]]
462 (assoc coll i2 (coll i1) i1 (coll i2)))
463
464 (defn
465 ^{:added "0.6.0"}
466 shuffle
467 "Create a generator that generates random permutations of `coll`. Shrinks
468 toward the original collection: `coll`. `coll` will be turned into a vector,
469 if it's not already."
470 [coll]
471 (let [index-gen (choose 0 (dec (count coll)))]
472 (fmap (partial reduce swap (vec coll))
473 ;; a vector of swap instructions, with count between
474 ;; zero and 2 * count. This means that the average number
475 ;; of instructions is count, which should provide sufficient
476 ;; (though perhaps not 'perfect') shuffling. This still gives us
477 ;; nice, relatively quick shrinks.
478 (vector (tuple index-gen index-gen) 0 (* 2 (count coll))))))
479
480 (def byte
481 "Generates `java.lang.Byte`s, using the full byte-range."
482 (fmap core/byte (choose Byte/MIN_VALUE Byte/MAX_VALUE)))
483
484 (def bytes
485 "Generates byte-arrays."
486 (fmap core/byte-array (vector byte)))
487
488 (defn map
489 "Create a generator that generates maps, with keys chosen from
490 `key-gen` and values chosen from `val-gen`."
491 [key-gen val-gen]
492 (let [input (vector (tuple key-gen val-gen))]
493 (fmap (partial into {}) input)))
494
495 (defn hash-map
496 "Like clojure.core/hash-map, except the values are generators.
497 Returns a generator that makes maps with the supplied keys and
498 values generated using the supplied generators.
499
500 Examples:
501
502 (gen/hash-map :a gen/boolean :b gen/nat)
503 "
504 [& kvs]
505 (assert (even? (count kvs)))
506 (let [ks (take-nth 2 kvs)
507 vs (take-nth 2 (rest kvs))]
508 (assert (every? generator? vs)
509 "Value args to hash-map must be generators")
510 (fmap (partial zipmap ks)
511 (apply tuple vs))))
512
513 (def char
514 "Generates character from 0-255."
515 (fmap core/char (choose 0 255)))
516
517 (def char-ascii
518 "Generate only ascii character."
519 (fmap core/char (choose 32 126)))
520
521 (def char-alphanumeric
522 "Generate alphanumeric characters."
523 (fmap core/char
524 (one-of [(choose 48 57)
525 (choose 65 90)
526 (choose 97 122)])))
527
528 (def ^{:deprecated "0.6.0"}
529 char-alpha-numeric
530 "Deprecated - use char-alphanumeric instead.
531
532 Generate alphanumeric characters."
533 char-alphanumeric)
534
535 (def char-alpha
536 "Generate alpha characters."
537 (fmap core/char
538 (one-of [(choose 65 90)
539 (choose 97 122)])))
540
541 (def ^{:private true} char-symbol-special
542 "Generate non-alphanumeric characters that can be in a symbol."
543 (elements [\* \+ \! \- \_ \?]))
544
545 (def ^{:private true} char-keyword-rest
546 "Generate characters that can be the char following first of a keyword."
547 (frequency [[2 char-alphanumeric]
548 [1 char-symbol-special]]))
549
550 (def ^{:private true} char-keyword-first
551 "Generate characters that can be the first char of a keyword."
552 (frequency [[2 char-alpha]
553 [1 char-symbol-special]]))
554
555 (def string
556 "Generate strings. May generate unprintable characters."
557 (fmap clojure.string/join (vector char)))
558
559 (def string-ascii
560 "Generate ascii strings."
561 (fmap clojure.string/join (vector char-ascii)))
562
563 (def string-alphanumeric
564 "Generate alphanumeric strings."
565 (fmap clojure.string/join (vector char-alphanumeric)))
566
567 (def ^{:deprecated "0.6.0"}
568 string-alpha-numeric
569 "Deprecated - use string-alphanumeric instead.
570
571 Generate alphanumeric strings."
572 string-alphanumeric)
573
574 (defn- +-or---digit?
575 "Returns true if c is \\+ or \\- and d is non-nil and a digit.
576
577 Symbols that start with +3 or -2 are not readable because they look
578 like numbers."
579 [c ^Character d]
580 (core/boolean (and d
581 (or (= \+ c)
582 (= \- c))
583 (Character/isDigit d))))
584
585 (def ^{:private true} namespace-segment
586 "Generate the segment of a namespace."
587 (->> (tuple char-keyword-first (vector char-keyword-rest))
588 (such-that (fn [[c [d]]] (not (+-or---digit? c d))))
589 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
590
591 (def ^{:private true} namespace
592 "Generate a namespace (or nil for no namespace)."
593 (->> (vector namespace-segment)
594 (fmap (fn [v] (when (seq v)
595 (clojure.string/join "." v))))))
596
597 (def ^{:private true} keyword-segment-rest
598 "Generate segments of a keyword (between \\:)"
599 (->> (tuple char-keyword-rest (vector char-keyword-rest))
600 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
601
602 (def ^{:private true} keyword-segment-first
603 "Generate segments of a keyword that can be first (between \\:)"
604 (->> (tuple char-keyword-first (vector char-keyword-rest))
605 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
606
607 (def keyword
608 "Generate keywords without namespaces."
609 (->> (tuple keyword-segment-first (vector keyword-segment-rest))
610 (fmap (fn [[c cs]]
611 (core/keyword (clojure.string/join ":" (cons c cs)))))))
612
613 (def
614 ^{:added "0.5.9"}
615 keyword-ns
616 "Generate keywords with optional namespaces."
617 (->> (tuple namespace char-keyword-first (vector char-keyword-rest))
618 (fmap (fn [[ns c cs]]
619 (core/keyword ns (clojure.string/join (cons c cs)))))))
620
621 (def ^{:private true} char-symbol-first
622 (frequency [[10 char-alpha]
623 [5 char-symbol-special]
624 [1 (return \.)]]))
625
626 (def ^{:private true} char-symbol-rest
627 (frequency [[10 char-alphanumeric]
628 [5 char-symbol-special]
629 [1 (return \.)]]))
630
631 (def symbol
632 "Generate symbols without namespaces."
633 (frequency [[100 (->> (tuple char-symbol-first (vector char-symbol-rest))
634 (such-that (fn [[c [d]]] (not (+-or---digit? c d))))
635 (fmap (fn [[c cs]] (core/symbol (clojure.string/join (cons c cs))))))]
636 [1 (return '/)]]))
637
638 (def
639 ^{:added "0.5.9"}
640 symbol-ns
641 "Generate symbols with optional namespaces."
642 (frequency [[100 (->> (tuple namespace char-symbol-first (vector char-symbol-rest))
643 (such-that (fn [[_ c [d]]] (not (+-or---digit? c d))))
644 (fmap (fn [[ns c cs]] (core/symbol ns (clojure.string/join (cons c cs))))))]
645 [1 (return '/)]]))
646
647 (def ratio
648 "Generates a `clojure.lang.Ratio`. Shrinks toward 0. Not all values generated
649 will be ratios, as many values returned by `/` are not ratios."
650 (fmap
651 (fn [[a b]] (/ a b))
652 (tuple int
653 (such-that (complement zero?) int))))
654
655 (def simple-type
656 (one-of [int char string ratio boolean keyword keyword-ns symbol symbol-ns]))
657
658 (def simple-type-printable
659 (one-of [int char-ascii string-ascii ratio boolean keyword keyword-ns symbol symbol-ns]))
660
661 (defn container-type
662 [inner-type]
663 (one-of [(vector inner-type)
664 (list inner-type)
665 (map inner-type inner-type)]))
666
667 (defn- recursive-helper
668 [container-gen-fn scalar-gen scalar-size children-size height]
669 (if (zero? height)
670 (resize scalar-size scalar-gen)
671 (resize children-size
672 (container-gen-fn
673 (recursive-helper
674 container-gen-fn scalar-gen
675 scalar-size children-size (dec height))))))
676
677 (defn
678 ^{:added "0.5.9"}
679 recursive-gen
680 "This is a helper for writing recursive (tree-shaped) generators. The first
681 argument should be a function that takes a generator as an argument, and
682 produces another generator that 'contains' that generator. The vector function
683 in this namespace is a simple example. The second argument is a scalar
684 generator, like boolean. For example, to produce a tree of booleans:
685
686 (gen/recursive-gen gen/vector gen/boolean)
687
688 Vectors or maps either recurring or containing booleans or integers:
689
690 (gen/recursive-gen (fn [inner] (gen/one-of [(gen/vector inner)
691 (gen/map inner inner)]))
692 (gen/one-of [gen/boolean gen/int]))
693 "
694 [container-gen-fn scalar-gen]
695 (assert (generator? scalar-gen)
696 "Second arg to recursive-gen must be a generator")
697 (sized (fn [size]
698 (bind (choose 1 5)
699 (fn [height] (let [children-size (Math/pow size (/ 1 height))]
700 (recursive-helper container-gen-fn scalar-gen size
701 children-size height)))))))
702
703 (def any
704 "A recursive generator that will generate many different, often nested, values"
705 (recursive-gen container-type simple-type))
706
707 (def any-printable
708 "Like any, but avoids characters that the shell will interpret as actions,
709 like 7 and 14 (bell and alternate character set command)"
710 (recursive-gen container-type simple-type-printable))
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.generators
10 (:refer-clojure :exclude [int vector list hash-map map keyword
11 char boolean byte bytes sequence
12 shuffle not-empty symbol namespace])
13 (:require [#?(:clj clojure.core :cljs cljs.core) :as core]
14 [clojure.test.check.random :as random]
15 [clojure.test.check.rose-tree :as rose]
16 #?@(:cljs [[goog.string :as gstring]
17 [clojure.string]])))
18
19
20 ;; Gen
21 ;; (internal functions)
22 ;; ---------------------------------------------------------------------------
23
24 (defrecord Generator [gen])
25
26 (defn generator?
27 "Test if `x` is a generator. Generators should be treated as opaque values."
28 [x]
29 (instance? Generator x))
30
31 (defn- make-gen
32 [generator-fn]
33 (Generator. generator-fn))
34
35 (defn call-gen
36 {:no-doc true}
37 [{generator-fn :gen} rnd size]
38 (generator-fn rnd size))
39
40 (defn gen-pure
41 {:no-doc true}
42 [value]
43 (make-gen
44 (fn [rnd size]
45 value)))
46
47 (defn gen-fmap
48 {:no-doc true}
49 [k {h :gen}]
50 (make-gen
51 (fn [rnd size]
52 (k (h rnd size)))))
53
54 (defn gen-bind
55 {:no-doc true}
56 [{h :gen} k]
57 (make-gen
58 (fn [rnd size]
59 (let [[r1 r2] (random/split rnd)
60 inner (h r1 size)
61 {result :gen} (k inner)]
62 (result r2 size)))))
63
64 (defn lazy-random-states
65 "Given a random number generator, returns an infinite lazy sequence
66 of random number generators."
67 [rr]
68 (lazy-seq
69 (let [[r1 r2] (random/split rr)]
70 (cons r1
71 (lazy-random-states r2)))))
72
73 (defn- gen-seq->seq-gen
74 "Takes a sequence of generators and returns a generator of sequences (er, vectors)."
75 [gens]
76 (make-gen
77 (fn [rnd size]
78 (mapv #(call-gen % %2 size) gens (random/split-n rnd (count gens))))))
79
80 ;; Exported generator functions
81 ;; ---------------------------------------------------------------------------
82
83 (defn fmap
84 [f gen]
85 (assert (generator? gen) "Second arg to fmap must be a generator")
86 (gen-fmap (partial rose/fmap f) gen))
87
88
89 (defn return
90 "Create a generator that always returns `value`,
91 and never shrinks. You can think of this as
92 the `constantly` of generators."
93 [value]
94 (gen-pure (rose/pure value)))
95
96 (defn- bind-helper
97 [k]
98 (fn [rose]
99 (gen-fmap rose/join
100 (make-gen
101 (fn [rnd size]
102 (rose/fmap #(call-gen % rnd size)
103 (rose/fmap k rose)))))))
104
105 (defn bind
106 "Create a new generator that passes the result of `gen` into function
107 `k`. `k` should return a new generator. This allows you to create new
108 generators that depend on the value of other generators. For example,
109 to create a generator which first generates a vector of integers, and
110 then chooses a random element from that vector:
111
112 (gen/bind (gen/such-that not-empty (gen/vector gen/int))
113 ;; this function takes a realized vector,
114 ;; and then returns a new generator which
115 ;; chooses a random element from it
116 gen/elements)
117
118 "
119 [generator k]
120 (assert (generator? generator) "First arg to bind must be a generator")
121 (gen-bind generator (bind-helper k)))
122
123 ;; Helpers
124 ;; ---------------------------------------------------------------------------
125
126 (defn make-size-range-seq
127 {:no-doc true}
128 [max-size]
129 (cycle (range 0 max-size)))
130
131 (defn sample-seq
132 "Return a sequence of realized values from `generator`."
133 ([generator] (sample-seq generator 100))
134 ([generator max-size]
135 (let [r (random/make-random)
136 size-seq (make-size-range-seq max-size)]
137 (core/map (comp rose/root #(call-gen generator %1 %2))
138 (lazy-random-states r)
139 size-seq))))
140
141 (defn sample
142 "Return a sequence of `num-samples` (default 10)
143 realized values from `generator`."
144 ([generator]
145 (sample generator 10))
146 ([generator num-samples]
147 (assert (generator? generator) "First arg to sample must be a generator")
148 (take num-samples (sample-seq generator))))
149
150
151 (defn generate
152 "Returns a single sample value from the generator, using a default
153 size of 30."
154 ([generator]
155 (generate generator 30))
156 ([generator size]
157 (let [rng (random/make-random)]
158 (rose/root (call-gen generator rng size)))))
159
160
161 ;; Internal Helpers
162 ;; ---------------------------------------------------------------------------
163
164 (defn- halfs
165 [n]
166 (take-while (partial not= 0) (iterate #(quot % 2) n)))
167
168 (defn- shrink-int
169 [integer]
170 (core/map (partial - integer) (halfs integer)))
171
172 (defn- int-rose-tree
173 [value]
174 (rose/make-rose value (core/map int-rose-tree (shrink-int value))))
175
176 ;; calc-long is factored out to support testing the surprisingly tricky double math. Note:
177 ;; An extreme long value does not have a precision-preserving representation as a double.
178 ;; Be careful about changing this code unless you understand what's happening in these
179 ;; examples:
180 ;;
181 ;; (= (long (- Integer/MAX_VALUE (double (- Integer/MAX_VALUE 10)))) 10)
182 ;; (= (long (- Long/MAX_VALUE (double (- Long/MAX_VALUE 10)))) 0)
183
184 (defn- calc-long
185 [factor lower upper]
186 ;; these pre- and post-conditions are disabled for deployment
187 #_ {:pre [(float? factor) (>= factor 0.0) (< factor 1.0)
188 (integer? lower) (integer? upper) (<= lower upper)]
189 :post [(integer? %)]}
190 ;; Use -' on width to maintain accuracy with overflow protection.
191 #?(:clj
192 (let [width (-' upper lower -1)]
193 ;; Preserve long precision if the width is in the long range. Otherwise, we must accept
194 ;; less precision because doubles don't have enough bits to preserve long equivalence at
195 ;; extreme values.
196 (if (< width Long/MAX_VALUE)
197 (+ lower (long (Math/floor (* factor width))))
198 ;; Clamp down to upper because double math.
199 (min upper (long (Math/floor (+ lower (* factor width)))))))
200
201 :cljs
202 (long (Math/floor (+ lower (- (* factor (+ 1.0 upper))
203 (* factor lower)))))))
204
205 (defn- rand-range
206 [rnd lower upper]
207 {:pre [(<= lower upper)]}
208 (calc-long (random/rand-double rnd) lower upper))
209
210 (defn sized
211 "Create a generator that depends on the size parameter.
212 `sized-gen` is a function that takes an integer and returns
213 a generator."
214 [sized-gen]
215 (make-gen
216 (fn [rnd size]
217 (let [sized-gen (sized-gen size)]
218 (call-gen sized-gen rnd size)))))
219
220 ;; Combinators and helpers
221 ;; ---------------------------------------------------------------------------
222
223 (defn resize
224 "Create a new generator with `size` always bound to `n`."
225 [n generator]
226 (assert (generator? generator) "Second arg to resize must be a generator")
227 (let [{:keys [gen]} generator]
228 (make-gen
229 (fn [rnd _size]
230 (gen rnd n)))))
231
232 (defn scale
233 "Create a new generator that modifies the size parameter by the given function. Intended to
234 support generators with sizes that need to grow at different rates compared to the normal
235 linear scaling."
236 ([f generator]
237 (sized (fn [n] (resize (f n) generator)))))
238
239 (defn choose
240 #?(:clj
241 "Create a generator that returns long integers in the range `lower` to `upper`, inclusive."
242
243 :cljs
244 "Create a generator that returns numbers in the range
245 `lower` to `upper`, inclusive.")
246 [lower upper]
247 ;; cast to long to support doubles as arguments per TCHECK-73
248 (let #?(:clj
249 [lower (long lower)
250 upper (long upper)]
251
252 :cljs ;; does nothing, no long in cljs
253 [])
254 (make-gen
255 (fn [rnd _size]
256 (let [value (rand-range rnd lower upper)]
257 (rose/filter
258 #(and (>= % lower) (<= % upper))
259 (int-rose-tree value)))))))
260
261 (defn one-of
262 "Create a generator that randomly chooses a value from the list of
263 provided generators. Shrinks toward choosing an earlier generator,
264 as well as shrinking the value generated by the chosen generator.
265
266 Examples:
267
268 (one-of [gen/int gen/boolean (gen/vector gen/int)])
269
270 "
271 [generators]
272 (assert (every? generator? generators)
273 "Arg to one-of must be a collection of generators")
274 (bind (choose 0 (dec (count generators)))
275 (partial nth generators)))
276
277 (defn- pick
278 [[h & tail] n]
279 (let [[chance gen] h]
280 (if (<= n chance)
281 gen
282 (recur tail (- n chance)))))
283
284 (defn frequency
285 "Create a generator that chooses a generator from `pairs` based on the
286 provided likelihoods. The likelihood of a given generator being chosen is
287 its likelihood divided by the sum of all likelihoods
288
289 Examples:
290
291 (gen/frequency [[5 gen/int] [3 (gen/vector gen/int)] [2 gen/boolean]])
292 "
293 [pairs]
294 (assert (every? (fn [[x g]] (and (number? x) (generator? g)))
295 pairs)
296 "Arg to frequency must be a list of [num generator] pairs")
297 (let [total (apply + (core/map first pairs))]
298 (gen-bind (choose 1 total)
299 #(pick pairs (rose/root %)))))
300
301 (defn elements
302 "Create a generator that randomly chooses an element from `coll`.
303
304 Examples:
305
306 (gen/elements [:foo :bar :baz])
307 "
308 [coll]
309 (assert (seq coll) "elements cannot be called with an empty collection")
310 (let [v (vec coll)]
311 (gen-bind (choose 0 (dec (count v)))
312 #(gen-pure (rose/fmap v %)))))
313
314 (defn- such-that-helper
315 [max-tries pred gen tries-left rand-seed size]
316 (if (zero? tries-left)
317 (throw (ex-info (str "Couldn't satisfy such-that predicate after "
318 max-tries " tries.") {}))
319 (let [[r1 r2] (random/split rand-seed)
320 value (call-gen gen r1 size)]
321 (if (pred (rose/root value))
322 (rose/filter pred value)
323 (recur max-tries pred gen (dec tries-left) r2 (inc size))))))
324
325 (defn such-that
326 "Create a generator that generates values from `gen` that satisfy predicate
327 `pred`. Care is needed to ensure there is a high chance `gen` will satisfy
328 `pred`. By default, `such-that` will try 10 times to generate a value that
329 satisfies the predicate. If no value passes this predicate after this number
330 of iterations, a runtime exception will be throw. You can pass an optional
331 third argument to change the number of times tried. Note also that each
332 time such-that retries, it will increase the size parameter.
333
334 Examples:
335
336 ;; generate non-empty vectors of integers
337 ;; (note, gen/not-empty does exactly this)
338 (gen/such-that not-empty (gen/vector gen/int))
339 "
340 ([pred gen]
341 (such-that pred gen 10))
342 ([pred gen max-tries]
343 (assert (generator? gen) "Second arg to such-that must be a generator")
344 (make-gen
345 (fn [rand-seed size]
346 (such-that-helper max-tries pred gen max-tries rand-seed size)))))
347
348 (defn not-empty
349 "Modifies a generator so that it doesn't generate empty collections.
350
351 Examples:
352
353 ;; generate a vector of booleans, but never the empty vector
354 (gen/not-empty (gen/vector gen/boolean))
355 "
356 [gen]
357 (assert (generator? gen) "Arg to not-empty must be a generator")
358 (such-that core/not-empty gen))
359
360 (defn no-shrink
361 "Create a new generator that is just like `gen`, except does not shrink
362 at all. This can be useful when shrinking is taking a long time or is not
363 applicable to the domain."
364 [gen]
365 (assert (generator? gen) "Arg to no-shrink must be a generator")
366 (gen-bind gen
367 (fn [rose]
368 (gen-pure (rose/make-rose (rose/root rose) [])))))
369
370 (defn shrink-2
371 "Create a new generator like `gen`, but will consider nodes for shrinking
372 even if their parent passes the test (up to one additional level)."
373 [gen]
374 (assert (generator? gen) "Arg to shrink-2 must be a generator")
375 (gen-bind gen (comp gen-pure rose/collapse)))
376
377 (def boolean
378 "Generates one of `true` or `false`. Shrinks to `false`."
379 (elements [false true]))
380
381 (defn tuple
382 "Create a generator that returns a vector, whose elements are chosen
383 from the generators in the same position. The individual elements shrink
384 according to their generator, but the value will never shrink in count.
385
386 Examples:
387
388 (def t (tuple gen/int gen/boolean))
389 (sample t)
390 ;; => ([1 true] [2 true] [2 false] [1 false] [0 true] [-2 false] [-6 false]
391 ;; => [3 true] [-4 false] [9 true]))
392 "
393 [& generators]
394 (assert (every? generator? generators)
395 "Args to tuple must be generators")
396 (gen-bind (gen-seq->seq-gen generators)
397 (fn [roses]
398 (gen-pure (rose/zip core/vector roses)))))
399
400 (def int
401 "Generates a positive or negative integer bounded by the generator's
402 `size` parameter.
403 (Really returns a long)"
404 (sized (fn [size] (choose (- size) size))))
405
406 (def nat
407 "Generates natural numbers, starting at zero. Shrinks to zero."
408 (fmap #(Math/abs (long %)) int))
409
410 (def pos-int
411 "Generate positive integers bounded by the generator's `size` parameter."
412 nat)
413
414 (def neg-int
415 "Generate negative integers bounded by the generator's `size` parameter."
416 (fmap (partial * -1) nat))
417
418 (def s-pos-int
419 "Generate strictly positive integers bounded by the generator's `size`
420 parameter."
421 (fmap inc nat))
422
423 (def s-neg-int
424 "Generate strictly negative integers bounded by the generator's `size`
425 parameter."
426 (fmap dec neg-int))
427
428 (defn vector
429 "Create a generator whose elements are chosen from `gen`. The count of the
430 vector will be bounded by the `size` generator parameter."
431 ([generator]
432 (assert (generator? generator) "Arg to vector must be a generator")
433 (gen-bind
434 (sized #(choose 0 %))
435 (fn [num-elements-rose]
436 (gen-bind (gen-seq->seq-gen
437 (repeat (rose/root num-elements-rose)
438 generator))
439 (fn [roses]
440 (gen-pure (rose/shrink core/vector
441 roses)))))))
442 ([generator num-elements]
443 (assert (generator? generator) "First arg to vector must be a generator")
444 (apply tuple (repeat num-elements generator)))
445 ([generator min-elements max-elements]
446 (assert (generator? generator) "First arg to vector must be a generator")
447 (gen-bind
448 (choose min-elements max-elements)
449 (fn [num-elements-rose]
450 (gen-bind (gen-seq->seq-gen
451 (repeat (rose/root num-elements-rose)
452 generator))
453 (fn [roses]
454 (gen-bind
455 (gen-pure (rose/shrink core/vector
456 roses))
457 (fn [rose]
458 (gen-pure (rose/filter
459 (fn [v] (and (>= (count v) min-elements)
460 (<= (count v) max-elements))) rose))))))))))
461
462 (defn list
463 "Like `vector`, but generates lists."
464 [generator]
465 (assert (generator? generator) "First arg to list must be a generator")
466 (gen-bind (sized #(choose 0 %))
467 (fn [num-elements-rose]
468 (gen-bind (gen-seq->seq-gen
469 (repeat (rose/root num-elements-rose)
470 generator))
471 (fn [roses]
472 (gen-pure (rose/shrink core/list
473 roses)))))))
474
475 (defn- swap
476 [coll [i1 i2]]
477 (assoc coll i2 (coll i1) i1 (coll i2)))
478
479 (defn
480 ^{:added "0.6.0"}
481 shuffle
482 "Create a generator that generates random permutations of `coll`. Shrinks
483 toward the original collection: `coll`. `coll` will be turned into a vector,
484 if it's not already."
485 [coll]
486 (let [index-gen (choose 0 (dec (count coll)))]
487 (fmap (partial reduce swap (vec coll))
488 ;; a vector of swap instructions, with count between
489 ;; zero and 2 * count. This means that the average number
490 ;; of instructions is count, which should provide sufficient
491 ;; (though perhaps not 'perfect') shuffling. This still gives us
492 ;; nice, relatively quick shrinks.
493 (vector (tuple index-gen index-gen) 0 (* 2 (count coll))))))
494
495 ;; NOTE cljs: Comment out for now - David
496
497 #?(:clj
498 (def byte
499 "Generates `java.lang.Byte`s, using the full byte-range."
500 (fmap core/byte (choose Byte/MIN_VALUE Byte/MAX_VALUE))))
501
502 #?(:clj
503 (def bytes
504 "Generates byte-arrays."
505 (fmap core/byte-array (vector byte))))
506
507 (defn map
508 "Create a generator that generates maps, with keys chosen from
509 `key-gen` and values chosen from `val-gen`."
510 [key-gen val-gen]
511 (let [input (vector (tuple key-gen val-gen))]
512 (fmap (partial into {}) input)))
513
514 (defn hash-map
515 "Like clojure.core/hash-map, except the values are generators.
516 Returns a generator that makes maps with the supplied keys and
517 values generated using the supplied generators.
518
519 Examples:
520
521 (gen/hash-map :a gen/boolean :b gen/nat)
522 "
523 [& kvs]
524 (assert (even? (count kvs)))
525 (let [ks (take-nth 2 kvs)
526 vs (take-nth 2 (rest kvs))]
527 (assert (every? generator? vs)
528 "Value args to hash-map must be generators")
529 (fmap (partial zipmap ks)
530 (apply tuple vs))))
531
532 (def char
533 "Generates character from 0-255."
534 (fmap core/char (choose 0 255)))
535
536 (def char-ascii
537 "Generate only ascii character."
538 (fmap core/char (choose 32 126)))
539
540 (def char-alphanumeric
541 "Generate alphanumeric characters."
542 (fmap core/char
543 (one-of [(choose 48 57)
544 (choose 65 90)
545 (choose 97 122)])))
546
547 (def ^{:deprecated "0.6.0"}
548 char-alpha-numeric
549 "Deprecated - use char-alphanumeric instead.
550
551 Generate alphanumeric characters."
552 char-alphanumeric)
553
554 (def char-alpha
555 "Generate alpha characters."
556 (fmap core/char
557 (one-of [(choose 65 90)
558 (choose 97 122)])))
559
560 (def ^{:private true} char-symbol-special
561 "Generate non-alphanumeric characters that can be in a symbol."
562 (elements [\* \+ \! \- \_ \?]))
563
564 (def ^{:private true} char-keyword-rest
565 "Generate characters that can be the char following first of a keyword."
566 (frequency [[2 char-alphanumeric]
567 [1 char-symbol-special]]))
568
569 (def ^{:private true} char-keyword-first
570 "Generate characters that can be the first char of a keyword."
571 (frequency [[2 char-alpha]
572 [1 char-symbol-special]]))
573
574 (def string
575 "Generate strings. May generate unprintable characters."
576 (fmap clojure.string/join (vector char)))
577
578 (def string-ascii
579 "Generate ascii strings."
580 (fmap clojure.string/join (vector char-ascii)))
581
582 (def string-alphanumeric
583 "Generate alphanumeric strings."
584 (fmap clojure.string/join (vector char-alphanumeric)))
585
586 (def ^{:deprecated "0.6.0"}
587 string-alpha-numeric
588 "Deprecated - use string-alphanumeric instead.
589
590 Generate alphanumeric strings."
591 string-alphanumeric)
592
593 (defn- digit?
594 [d]
595 #?(:clj (Character/isDigit ^Character d)
596 :cljs (gstring/isNumeric d)))
597
598 (defn- +-or---digit?
599 "Returns true if c is \\+ or \\- and d is non-nil and a digit.
600
601 Symbols that start with +3 or -2 are not readable because they look
602 like numbers."
603 [c d]
604 (core/boolean (and d
605 (or (#?(:clj = :cljs identical?) \+ c)
606 (#?(:clj = :cljs identical?) \- c))
607 (digit? d))))
608
609 (def ^{:private true} namespace-segment
610 "Generate the segment of a namespace."
611 (->> (tuple char-keyword-first (vector char-keyword-rest))
612 (such-that (fn [[c [d]]] (not (+-or---digit? c d))))
613 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
614
615 (def ^{:private true} namespace
616 "Generate a namespace (or nil for no namespace)."
617 (->> (vector namespace-segment)
618 (fmap (fn [v] (when (seq v)
619 (clojure.string/join "." v))))))
620
621 (def ^{:private true} keyword-segment-rest
622 "Generate segments of a keyword (between \\:)"
623 (->> (tuple char-keyword-rest (vector char-keyword-rest))
624 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
625
626 (def ^{:private true} keyword-segment-first
627 "Generate segments of a keyword that can be first (between \\:)"
628 (->> (tuple char-keyword-first (vector char-keyword-rest))
629 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
630
631 (def keyword
632 "Generate keywords without namespaces."
633 (->> (tuple keyword-segment-first (vector keyword-segment-rest))
634 (fmap (fn [[c cs]]
635 (core/keyword (clojure.string/join ":" (cons c cs)))))))
636
637 (def
638 ^{:added "0.5.9"}
639 keyword-ns
640 "Generate keywords with optional namespaces."
641 (->> (tuple namespace char-keyword-first (vector char-keyword-rest))
642 (fmap (fn [[ns c cs]]
643 (core/keyword ns (clojure.string/join (cons c cs)))))))
644
645 (def ^{:private true} char-symbol-first
646 (frequency [[10 char-alpha]
647 [5 char-symbol-special]
648 [1 (return \.)]]))
649
650 (def ^{:private true} char-symbol-rest
651 (frequency [[10 char-alphanumeric]
652 [5 char-symbol-special]
653 [1 (return \.)]]))
654
655 (def symbol
656 "Generate symbols without namespaces."
657 (frequency [[100 (->> (tuple char-symbol-first (vector char-symbol-rest))
658 (such-that (fn [[c [d]]] (not (+-or---digit? c d))))
659 (fmap (fn [[c cs]] (core/symbol (clojure.string/join (cons c cs))))))]
660 [1 (return '/)]]))
661
662 (def
663 ^{:added "0.5.9"}
664 symbol-ns
665 "Generate symbols with optional namespaces."
666 (frequency [[100 (->> (tuple namespace char-symbol-first (vector char-symbol-rest))
667 (such-that (fn [[_ c [d]]] (not (+-or---digit? c d))))
668 (fmap (fn [[ns c cs]] (core/symbol ns (clojure.string/join (cons c cs))))))]
669 [1 (return '/)]]))
670
671 (def ratio
672 "Generates a `clojure.lang.Ratio`. Shrinks toward 0. Not all values generated
673 will be ratios, as many values returned by `/` are not ratios."
674 (fmap
675 (fn [[a b]] (/ a b))
676 (tuple int
677 (such-that (complement zero?) int))))
678
679 (def simple-type
680 (one-of [int char string ratio boolean keyword keyword-ns symbol symbol-ns]))
681
682 (def simple-type-printable
683 (one-of [int char-ascii string-ascii ratio boolean keyword keyword-ns symbol symbol-ns]))
684
685 (defn container-type
686 [inner-type]
687 (one-of [(vector inner-type)
688 (list inner-type)
689 (map inner-type inner-type)]))
690
691 (defn- recursive-helper
692 [container-gen-fn scalar-gen scalar-size children-size height]
693 (if (zero? height)
694 (resize scalar-size scalar-gen)
695 (resize children-size
696 (container-gen-fn
697 (recursive-helper
698 container-gen-fn scalar-gen
699 scalar-size children-size (dec height))))))
700
701 (defn
702 ^{:added "0.5.9"}
703 recursive-gen
704 "This is a helper for writing recursive (tree-shaped) generators. The first
705 argument should be a function that takes a generator as an argument, and
706 produces another generator that 'contains' that generator. The vector function
707 in this namespace is a simple example. The second argument is a scalar
708 generator, like boolean. For example, to produce a tree of booleans:
709
710 (gen/recursive-gen gen/vector gen/boolean)
711
712 Vectors or maps either recurring or containing booleans or integers:
713
714 (gen/recursive-gen (fn [inner] (gen/one-of [(gen/vector inner)
715 (gen/map inner inner)]))
716 (gen/one-of [gen/boolean gen/int]))
717 "
718 [container-gen-fn scalar-gen]
719 (assert (generator? scalar-gen)
720 "Second arg to recursive-gen must be a generator")
721 (sized (fn [size]
722 (bind (choose 1 5)
723 (fn [height] (let [children-size (Math/pow size (/ 1 height))]
724 (recursive-helper container-gen-fn scalar-gen size
725 children-size height)))))))
726
727 (def any
728 "A recursive generator that will generate many different, often nested, values"
729 (recursive-gen container-type simple-type))
730
731 (def any-printable
732 "Like any, but avoids characters that the shell will interpret as actions,
733 like 7 and 14 (bell and alternate character set command)"
734 (recursive-gen container-type simple-type-printable))
+0
-689
src/main/clojure/clojure/test/check/generators.cljs less more
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.generators
10 (:refer-clojure :exclude [int vector list hash-map map keyword
11 char boolean byte bytes sequence
12 shuffle not-empty symbol namespace])
13 (:require [cljs.core :as core]
14 [clojure.test.check.random :as random]
15 [clojure.test.check.rose-tree :as rose]
16 [goog.string :as gstring]
17 [clojure.string]))
18
19
20 ;; Gen
21 ;; (internal functions)
22 ;; ---------------------------------------------------------------------------
23
24 (defrecord Generator [gen])
25
26 (defn generator?
27 "Test if `x` is a generator. Generators should be treated as opaque values."
28 [x]
29 (instance? Generator x))
30
31 (defn- make-gen
32 [generator-fn]
33 (Generator. generator-fn))
34
35 (defn call-gen
36 {:no-doc true}
37 [{generator-fn :gen} rnd size]
38 (generator-fn rnd size))
39
40 (defn gen-pure
41 {:no-doc true}
42 [value]
43 (make-gen
44 (fn [rnd size]
45 value)))
46
47 (defn gen-fmap
48 {:no-doc true}
49 [k {h :gen}]
50 (make-gen
51 (fn [rnd size]
52 (k (h rnd size)))))
53
54 (defn gen-bind
55 {:no-doc true}
56 [{h :gen} k]
57 (make-gen
58 (fn [rnd size]
59 (let [[r1 r2] (random/split rnd)
60 inner (h r1 size)
61 {result :gen} (k inner)]
62 (result r2 size)))))
63
64 (defn lazy-random-states
65 "Given a random number generator, returns an infinite lazy sequence
66 of random number generators."
67 [rr]
68 (lazy-seq
69 (let [[r1 r2] (random/split rr)]
70 (cons r1
71 (lazy-random-states r2)))))
72
73 (defn- gen-seq->seq-gen
74 "Takes a sequence of generators and returns a generator of sequences (er, vectors)."
75 [gens]
76 (make-gen
77 (fn [rnd size]
78 (mapv #(call-gen % %2 size) gens (random/split-n rnd (count gens))))))
79
80 ;; Exported generator functions
81 ;; ---------------------------------------------------------------------------
82
83 (defn fmap
84 [f gen]
85 (assert (generator? gen) "Second arg to fmap must be a generator")
86 (gen-fmap #(rose/fmap f %) gen))
87
88
89 (defn return
90 "Create a generator that always returns `value`,
91 and never shrinks. You can think of this as
92 the `constantly` of generators."
93 [value]
94 (gen-pure (rose/pure value)))
95
96 (defn- bind-helper
97 [k]
98 (fn [rose]
99 (gen-fmap rose/join
100 (make-gen
101 (fn [rnd size]
102 (rose/fmap #(call-gen % rnd size)
103 (rose/fmap k rose)))))))
104
105 (defn bind
106 "Create a new generator that passes the result of `gen` into function
107 `k`. `k` should return a new generator. This allows you to create new
108 generators that depend on the value of other generators. For example,
109 to create a generator which first generates a vector of integers, and
110 then chooses a random element from that vector:
111
112 (gen/bind (gen/such-that not-empty (gen/vector gen/int))
113 ;; this function takes a realized vector,
114 ;; and then returns a new generator which
115 ;; chooses a random element from it
116 gen/elements)
117
118 "
119 [generator k]
120 (assert (generator? generator) "First arg to bind must be a generator")
121 (gen-bind generator (bind-helper k)))
122
123 ;; Helpers
124 ;; ---------------------------------------------------------------------------
125
126 (defn make-size-range-seq
127 {:no-doc true}
128 [max-size]
129 (cycle (range 0 max-size)))
130
131 (defn sample-seq
132 "Return a sequence of realized values from `generator`."
133 ([generator] (sample-seq generator 100))
134 ([generator max-size]
135 (let [r (random/make-random)
136 size-seq (make-size-range-seq max-size)]
137 (core/map #(rose/root (call-gen generator %1 %2))
138 (lazy-random-states r)
139 size-seq))))
140
141 (defn sample
142 "Return a sequence of `num-samples` (default 10)
143 realized values from `generator`."
144 ([generator]
145 (sample generator 10))
146 ([generator num-samples]
147 (assert (generator? generator) "First arg to sample must be a generator")
148 (take num-samples (sample-seq generator))))
149
150
151 (defn generate
152 "Returns a single sample value from the generator, using a default
153 size of 30."
154 ([generator]
155 (generate generator 30))
156 ([generator size]
157 (let [rng (random/make-random)]
158 (rose/root (call-gen generator rng size)))))
159
160 ;; Internal Helpers
161 ;; ---------------------------------------------------------------------------
162
163 (defn- halfs
164 [n]
165 (take-while #(not= 0 %) (iterate #(quot % 2) n)))
166
167 (defn- shrink-int
168 [integer]
169 (core/map #(- integer %) (halfs integer)))
170
171 (defn- int-rose-tree
172 [value]
173 (rose/make-rose value (core/map int-rose-tree (shrink-int value))))
174
175 (defn- rand-range
176 [rnd lower upper]
177 {:pre [(<= lower upper)]}
178 (let [factor (random/rand-double rnd)]
179 (long (Math/floor (+ lower (- (* factor (+ 1.0 upper))
180 (* factor lower)))))))
181
182 (defn sized
183 "Create a generator that depends on the size parameter.
184 `sized-gen` is a function that takes an integer and returns
185 a generator."
186 [sized-gen]
187 (make-gen
188 (fn [rnd size]
189 (let [sized-gen (sized-gen size)]
190 (call-gen sized-gen rnd size)))))
191
192 ;; Combinators and helpers
193 ;; ---------------------------------------------------------------------------
194
195 (defn resize
196 "Create a new generator with `size` always bound to `n`."
197 [n generator]
198 (assert (generator? generator) "Second arg to resize must be a generator")
199 (let [{:keys [gen]} generator]
200 (make-gen
201 (fn [rnd _size]
202 (gen rnd n)))))
203
204 (defn scale
205 "Create a new generator that modifies the size parameter by the given function. Intended to
206 support generators with sizes that need to grow at different rates compared to the normal
207 linear scaling."
208 ([f generator]
209 (sized (fn [n] (resize (f n) generator)))))
210
211 (defn choose
212 "Create a generator that returns numbers in the range
213 `min-range` to `max-range`, inclusive."
214 [lower upper]
215 (make-gen
216 (fn [rnd _size]
217 (let [value (rand-range rnd lower upper)]
218 (rose/filter
219 #(and (>= % lower) (<= % upper))
220 (int-rose-tree value))))))
221
222 (defn one-of
223 "Create a generator that randomly chooses a value from the list of
224 provided generators. Shrinks toward choosing an earlier generator,
225 as well as shrinking the value generated by the chosen generator.
226
227 Examples:
228
229 (one-of [gen/int gen/boolean (gen/vector gen/int)])
230
231 "
232 [generators]
233 (assert (every? generator? generators)
234 "Arg to one-of must be a collection of generators")
235 (bind (choose 0 (dec (count generators)))
236 #(nth generators %)))
237
238 (defn- pick
239 [[h & tail] n]
240 (let [[chance gen] h]
241 (if (<= n chance)
242 gen
243 (recur tail (- n chance)))))
244
245 (defn frequency
246 "Create a generator that chooses a generator from `pairs` based on the
247 provided likelihoods. The likelihood of a given generator being chosen is
248 its likelihood divided by the sum of all likelihoods
249
250 Examples:
251
252 (gen/frequency [[5 gen/int] [3 (gen/vector gen/int)] [2 gen/boolean]])
253 "
254 [pairs]
255 (assert (every? (fn [[x g]] (and (number? x) (generator? g)))
256 pairs)
257 "Arg to frequency must be a list of [num generator] pairs")
258 (let [total (apply + (core/map first pairs))]
259 (gen-bind (choose 1 total)
260 #(pick pairs (rose/root %)))))
261
262 (defn elements
263 "Create a generator that randomly chooses an element from `coll`.
264
265 Examples:
266
267 (gen/elements [:foo :bar :baz])
268 "
269 [coll]
270 (assert (seq coll) "elements cannot be called with an empty collection")
271 (let [v (vec coll)]
272 (gen-bind (choose 0 (dec (count v)))
273 #(gen-pure (rose/fmap v %)))))
274
275 (defn- such-that-helper
276 [max-tries pred gen tries-left rng size]
277 (if (zero? tries-left)
278 (throw (ex-info (str "Couldn't satisfy such-that predicate after "
279 max-tries " tries.") {}))
280 (let [[r1 r2] (random/split rng)
281 value (call-gen gen r1 size)]
282 (if (pred (rose/root value))
283 (rose/filter pred value)
284 (recur max-tries pred gen (dec tries-left) r2 (inc size))))))
285
286 (defn such-that
287 "Create a generator that generates values from `gen` that satisfy predicate
288 `pred`. Care is needed to ensure there is a high chance `gen` will satisfy
289 `pred`. By default, `such-that` will try 10 times to generate a value that
290 satisfies the predicate. If no value passes this predicate after this number
291 of iterations, a runtime exception will be throw. You can pass an optional
292 third argument to change the number of times tried. Note also that each
293 time such-that retries, it will increase the size parameter.
294
295 Examples:
296
297 ;; generate non-empty vectors of integers
298 ;; (note, gen/not-empty does exactly this)
299 (gen/such-that not-empty (gen/vector gen/int))
300 "
301 ([pred gen]
302 (such-that pred gen 10))
303 ([pred gen max-tries]
304 (assert (generator? gen) "Second arg to such-that must be a generator")
305 (make-gen
306 (fn [rand-seed size]
307 (such-that-helper max-tries pred gen max-tries rand-seed size)))))
308
309 (defn not-empty
310 "Modifies a generator so that it doesn't generate empty collections.
311
312 Examples:
313
314 ;; generate a vector of booleans, but never the empty vector
315 (gen/not-empty (gen/vector gen/boolean))
316 "
317 [gen]
318 (assert (generator? gen) "Arg to not-empty must be a generator")
319 (such-that core/not-empty gen))
320
321 (defn no-shrink
322 "Create a new generator that is just like `gen`, except does not shrink
323 at all. This can be useful when shrinking is taking a long time or is not
324 applicable to the domain."
325 [gen]
326 (assert (generator? gen) "Arg to no-shrink must be a generator")
327 (gen-bind gen
328 (fn [rose]
329 (gen-pure (rose/make-rose (rose/root rose) [])))))
330
331 (defn shrink-2
332 "Create a new generator like `gen`, but will consider nodes for shrinking
333 even if their parent passes the test (up to one additional level)."
334 [gen]
335 (assert (generator? gen) "Arg to shrink-2 must be a generator")
336 (gen-bind gen (comp gen-pure rose/collapse)))
337
338 (def boolean
339 "Generates one of `true` or `false`. Shrinks to `false`."
340 (elements [false true]))
341
342 (defn tuple
343 "Create a generator that returns a vector, whose elements are chosen
344 from the generators in the same position. The individual elements shrink
345 according to their generator, but the value will never shrink in count.
346
347 Examples:
348
349 (def t (tuple gen/int gen/boolean))
350 (sample t)
351 ;; => ([1 true] [2 true] [2 false] [1 false] [0 true] [-2 false] [-6 false]
352 ;; => [3 true] [-4 false] [9 true]))
353 "
354 [& generators]
355 (assert (every? generator? generators)
356 "Args to tuple must be generators")
357 (gen-bind (gen-seq->seq-gen generators)
358 (fn [roses]
359 (gen-pure (rose/zip core/vector roses)))))
360
361 (def int
362 "Generates a positive or negative integer bounded by the generator's
363 `size` parameter.
364 (Really returns a long)"
365 (sized (fn [size] (choose (- size) size))))
366
367 (def nat
368 "Generates natural numbers, starting at zero. Shrinks to zero."
369 (fmap #(Math/abs (long %)) int))
370
371 (def pos-int
372 "Generate positive integers bounded by the generator's `size` parameter."
373 nat)
374
375 (def neg-int
376 "Generate negative integers bounded by the generator's `size` parameter."
377 (fmap #(* -1 %) nat))
378
379 (def s-pos-int
380 "Generate strictly positive integers bounded by the generator's `size`
381 parameter."
382 (fmap inc nat))
383
384 (def s-neg-int
385 "Generate strictly negative integers bounded by the generator's `size`
386 parameter."
387 (fmap dec neg-int))
388
389 (defn vector
390 "Create a generator whose elements are chosen from `gen`. The count of the
391 vector will be bounded by the `size` generator parameter."
392 ([generator]
393 (assert (generator? generator) "Arg to vector must be a generator")
394 (gen-bind
395 (sized #(choose 0 %))
396 (fn [num-elements-rose]
397 (gen-bind (gen-seq->seq-gen
398 (repeat (rose/root num-elements-rose)
399 generator))
400 (fn [roses]
401 (gen-pure (rose/shrink core/vector
402 roses)))))))
403 ([generator num-elements]
404 (assert (generator? generator) "First arg to vector must be a generator")
405 (apply tuple (repeat num-elements generator)))
406 ([generator min-elements max-elements]
407 (assert (generator? generator) "First arg to vector must be a generator")
408 (gen-bind
409 (choose min-elements max-elements)
410 (fn [num-elements-rose]
411 (gen-bind (gen-seq->seq-gen
412 (repeat (rose/root num-elements-rose)
413 generator))
414 (fn [roses]
415 (gen-bind
416 (gen-pure (rose/shrink core/vector
417 roses))
418 (fn [rose]
419 (gen-pure (rose/filter
420 (fn [v] (and (>= (count v) min-elements)
421 (<= (count v) max-elements))) rose))))))))))
422
423 (defn list
424 "Like `vector`, but generates lists."
425 [generator]
426 (assert (generator? generator) "First arg to list must be a generator")
427 (gen-bind (sized #(choose 0 %))
428 (fn [num-elements-rose]
429 (gen-bind (gen-seq->seq-gen
430 (repeat (rose/root num-elements-rose)
431 generator))
432 (fn [roses]
433 (gen-pure (rose/shrink core/list
434 roses)))))))
435
436 (defn- swap
437 [coll [i1 i2]]
438 (assoc coll i2 (coll i1) i1 (coll i2)))
439
440 (defn
441 ^{:added "0.6.0"}
442 shuffle
443 "Create a generator that generates random permutations of `coll`. Shrinks
444 toward the original collection: `coll`. `coll` will be turned into a vector,
445 if it's not already."
446 [coll]
447 (let [index-gen (choose 0 (dec (count coll)))]
448 (fmap #(reduce swap (vec coll) %)
449 ;; a vector of swap instructions, with count between
450 ;; zero and 2 * count. This means that the average number
451 ;; of instructions is count, which should provide sufficient
452 ;; (though perhaps not 'perfect') shuffling. This still gives us
453 ;; nice, relatively quick shrinks.
454 (vector (tuple index-gen index-gen) 0 (* 2 (count coll))))))
455
456 ;; NOTE: Comment out for now - David
457 ;;
458 ;; (def byte
459 ;; "Generates `java.lang.Byte`s, using the full byte-range."
460 ;; (fmap core/byte (choose Byte/MIN_VALUE Byte/MAX_VALUE)))
461
462 ;; (def bytes
463 ;; "Generates byte-arrays."
464 ;; (fmap core/byte-array (vector byte)))
465
466 (defn map
467 "Create a generator that generates maps, with keys chosen from
468 `key-gen` and values chosen from `val-gen`."
469 [key-gen val-gen]
470 (let [input (vector (tuple key-gen val-gen))]
471 (fmap #(into {} %) input)))
472
473 (defn hash-map
474 "Like clojure.core/hash-map, except the values are generators.
475 Returns a generator that makes maps with the supplied keys and
476 values generated using the supplied generators.
477
478 Examples:
479
480 (gen/hash-map :a gen/boolean :b gen/nat)
481 "
482 [& kvs]
483 (assert (even? (count kvs)))
484 (let [ks (take-nth 2 kvs)
485 vs (take-nth 2 (rest kvs))]
486 (assert (every? generator? vs)
487 "Value args to hash-map must be generators")
488 (fmap #(zipmap ks %)
489 (apply tuple vs))))
490
491 (def char
492 "Generates character from 0-255."
493 (fmap core/char (choose 0 255)))
494
495 (def char-ascii
496 "Generate only ascii character."
497 (fmap core/char (choose 32 126)))
498
499 (def char-alphanumeric
500 "Generate alphanumeric characters."
501 (fmap core/char
502 (one-of [(choose 48 57)
503 (choose 65 90)
504 (choose 97 122)])))
505
506 (def ^{:deprecated "0.6.0"}
507 char-alpha-numeric
508 "Deprecated - use char-alphanumeric instead.
509
510 Generate alphanumeric characters."
511 char-alphanumeric)
512
513 (def char-alpha
514 "Generate alpha characters."
515 (fmap core/char
516 (one-of [(choose 65 90)
517 (choose 97 122)])))
518
519 (def ^{:private true} char-symbol-special
520 "Generate non-alphanumeric characters that can be in a symbol."
521 (elements [\* \+ \! \- \_ \?]))
522
523 (def ^{:private true} char-keyword-rest
524 "Generate characters that can be the char following first of a keyword."
525 (frequency [[2 char-alphanumeric]
526 [1 char-symbol-special]]))
527
528 (def ^{:private true} char-keyword-first
529 "Generate characters that can be the first char of a keyword."
530 (frequency [[2 char-alpha]
531 [1 char-symbol-special]]))
532
533 (def string
534 "Generate strings. May generate unprintable characters."
535 (fmap clojure.string/join (vector char)))
536
537 (def string-ascii
538 "Generate ascii strings."
539 (fmap clojure.string/join (vector char-ascii)))
540
541 (def string-alphanumeric
542 "Generate alphanumeric strings."
543 (fmap clojure.string/join (vector char-alphanumeric)))
544
545 (def ^{:deprecated "0.6.0"}
546 string-alpha-numeric
547 "Deprecated - use string-alphanumeric instead.
548
549 Generate alphanumeric strings."
550 string-alphanumeric)
551
552 (defn- +-or---digit?
553 "Returns true if c is \\+ or \\- and d is non-nil and a digit.
554
555 Symbols that start with +3 or -2 are not readable because they look
556 like numbers."
557 [c d]
558 (core/boolean (and d
559 (or (identical? \+ c)
560 (identical? \- c))
561 (gstring/isNumeric d))))
562
563 (def ^{:private true} namespace-segment
564 "Generate the segment of a namespace."
565 (->> (tuple char-keyword-first (vector char-keyword-rest))
566 (such-that (fn [[c [d]]] (not (+-or---digit? c d))))
567 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
568
569 (def ^{:private true} namespace
570 "Generate a namespace (or nil for no namespace)."
571 (->> (vector namespace-segment)
572 (fmap (fn [v] (when (seq v)
573 (clojure.string/join "." v))))))
574
575 (def ^{:private true} keyword-segment-rest
576 "Generate segments of a keyword (between \\:)"
577 (->> (tuple char-keyword-rest (vector char-keyword-rest))
578 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
579
580 (def ^{:private true} keyword-segment-first
581 "Generate segments of a keyword that can be first (between \\:)"
582 (->> (tuple char-keyword-first (vector char-keyword-rest))
583 (fmap (fn [[c cs]] (clojure.string/join (cons c cs))))))
584
585 (def keyword
586 "Generate keywords without namespaces."
587 (->> (tuple keyword-segment-first (vector keyword-segment-rest))
588 (fmap (fn [[c cs]]
589 (core/keyword (clojure.string/join ":" (cons c cs)))))))
590
591 (def
592 ^{:added "0.5.9"}
593 keyword-ns
594 "Generate keywords with optional namespaces."
595 (->> (tuple namespace char-keyword-first (vector char-keyword-rest))
596 (fmap (fn [[ns c cs]]
597 (core/keyword ns (clojure.string/join (cons c cs)))))))
598
599 (def ^{:private true} char-symbol-first
600 (frequency [[10 char-alpha]
601 [5 char-symbol-special]
602 [1 (return \.)]]))
603
604 (def ^{:private true} char-symbol-rest
605 (frequency [[10 char-alphanumeric]
606 [5 char-symbol-special]
607 [1 (return \.)]]))
608
609 (def symbol
610 "Generate symbols without namespaces."
611 (frequency [[100 (->> (tuple char-symbol-first (vector char-symbol-rest))
612 (such-that (fn [[c [d]]] (not (+-or---digit? c d))))
613 (fmap (fn [[c cs]] (core/symbol (clojure.string/join (cons c cs))))))]
614 [1 (return '/)]]))
615
616 (def
617 ^{:added "0.5.9"}
618 symbol-ns
619 "Generate symbols with optional namespaces."
620 (frequency [[100 (->> (tuple namespace char-symbol-first (vector char-symbol-rest))
621 (such-that (fn [[_ c [d]]] (not (+-or---digit? c d))))
622 (fmap (fn [[ns c cs]] (core/symbol ns (clojure.string/join (cons c cs))))))]
623 [1 (return '/)]]))
624
625 (def ratio
626 "Generates a `clojure.lang.Ratio`. Shrinks toward 0. Not all values generated
627 will be ratios, as many values returned by `/` are not ratios."
628 (fmap
629 (fn [[a b]] (/ a b))
630 (tuple int
631 (such-that (complement zero?) int))))
632
633 (def simple-type
634 (one-of [int char string ratio boolean keyword keyword-ns symbol symbol-ns]))
635
636 (def simple-type-printable
637 (one-of [int char-ascii string-ascii ratio boolean keyword keyword-ns symbol symbol-ns]))
638
639 (defn container-type
640 [inner-type]
641 (one-of [(vector inner-type)
642 (list inner-type)
643 (map inner-type inner-type)]))
644
645 (defn- recursive-helper
646 [container-gen-fn scalar-gen scalar-size children-size height]
647 (if (zero? height)
648 (resize scalar-size scalar-gen)
649 (resize children-size
650 (container-gen-fn
651 (recursive-helper
652 container-gen-fn scalar-gen
653 scalar-size children-size (dec height))))))
654
655 (defn
656 ^{:added "0.5.9"}
657 recursive-gen
658 "This is a helper for writing recursive (tree-shaped) generators. The first
659 argument should be a function that takes a generator as an argument, and
660 produces another generator that 'contains' that generator. The vector function
661 in this namespace is a simple example. The second argument is a scalar
662 generator, like boolean. For example, to produce a tree of booleans:
663
664 (gen/recursive-gen gen/vector gen/boolean)
665
666 Vectors or maps either recurring or containing booleans or integers:
667
668 (gen/recursive-gen (fn [inner] (gen/one-of [(gen/vector inner)
669 (gen/map inner inner)]))
670 (gen/one-of [gen/boolean gen/int]))
671 "
672 [container-gen-fn scalar-gen]
673 (assert (generator? scalar-gen)
674 "Second arg to recursive-gen must be a generator")
675 (sized (fn [size]
676 (bind (choose 1 5)
677 (fn [height] (let [children-size (Math/pow size (/ 1 height))]
678 (recursive-helper container-gen-fn scalar-gen size
679 children-size height)))))))
680
681 (def any
682 "A recursive generator that will generate many different, often nested, values"
683 (recursive-gen container-type simple-type))
684
685 (def any-printable
686 "Like any, but avoids characters that the shell will interpret as actions,
687 like 7 and 14 (bell and alternate character set command)"
688 (recursive-gen container-type simple-type-printable))