generators.{[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.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 | ; 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)) |