ClojureCheck / src / main / clojure / clojurecheck / core.clj

Meikel  Brandmey… 62b279d 
Meikel  Brandmey… acb0d44 



















Meikel  Brandmey… 469a528 
Meikel  Brandmey… acb0d44 














Meikel  Brandmey… bdc9632 
Meikel  Brandmey… acb0d44 











Meikel  Brandmey… bdc9632 


Meikel  Brandmey… acb0d44 



Meikel  Brandmey… bdc9632 
Meikel  Brandmey… acb0d44 

Meikel  Brandmey… bdc9632 

Meikel  Brandmey… acb0d44 
Meikel  Brandmey… bdc9632 


Meikel  Brandmey… acb0d44 

Meikel  Brandmey… bdc9632 
Meikel  Brandmey… acb0d44 




Meikel  Brandmey… 19eec4f 
Meikel  Brandmey… aad6847 
Meikel  Brandmey… 34c73fe 
Meikel  Brandmey… aad6847 


Meikel  Brandmey… acb0d44 
Meikel  Brandmey… 806d5e7 
Meikel  Brandmey… e6520ef 






Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… 469a528 
Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… e6520ef 

























Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… 349b2cd 

























Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… 01e3820 




















Meikel  Brandmey… 2d436c3 

















Meikel  Brandmey… 17df1c4 



Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… 17df1c4 


Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… 17df1c4 
Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… 17df1c4 
Meikel  Brandmey… 2158cdd 



Meikel  Brandmey… 17df1c4 
Meikel  Brandmey… 71e2634 
Meikel  Brandmey… 17df1c4 

Meikel  Brandmey… 85c314b 
Meikel  Brandmey… 17df1c4 

Meikel  Brandmey… 85c314b 
Meikel  Brandmey… 17df1c4 


Meikel  Brandmey… 71e2634 

Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… aad6847 





Meikel  Brandmey… 2158cdd 

Meikel  Brandmey… aad6847 
Meikel  Brandmey… 2158cdd 


Meikel  Brandmey… aad6847 
Meikel  Brandmey… 806d5e7 
Meikel  Brandmey… aad6847 
Meikel  Brandmey… 806d5e7 

Meikel  Brandmey… aad6847 
Meikel  Brandmey… 806d5e7 
Meikel  Brandmey… aad6847 
Meikel  Brandmey… 806d5e7 
Meikel  Brandmey… aad6847 






Meikel  Brandmey… 806d5e7 

Meikel  Brandmey… aad6847 
Meikel  Brandmey… 806d5e7 
Meikel  Brandmey… aad6847 






Meikel  Brandmey… 2eba281 
Meikel  Brandmey… 34c73fe 












Meikel  Brandmey… 469a528 
Meikel  Brandmey… 2eba281 
Meikel  Brandmey… 34c73fe 
Meikel  Brandmey… 359f247 
Meikel  Brandmey… 49c4938 
Meikel  Brandmey… 8ca977e 





















































Meikel  Brandmey… 49c4938 














Meikel  Brandmey… 1f8a4d2 











































































Meikel  Brandmey… 71d6b27 
Meikel  Brandmey… 49c4938 
















Meikel  Brandmey… 359f247 



Meikel  Brandmey… 9601dc2 
Meikel  Brandmey… 359f247 






Meikel  Brandmey… 160cf6f 

Meikel  Brandmey… 2158cdd 

Meikel  Brandmey… 71d6b27 
Meikel  Brandmey… f1cc5cf 



Meikel  Brandmey… 9601dc2 
Meikel  Brandmey… f1cc5cf 

Meikel  Brandmey… 82d7b8f 




Meikel  Brandmey… 9601dc2 
Meikel  Brandmey… 82d7b8f 

Meikel  Brandmey… 9d4983d 
Meikel  Brandmey… 2158cdd 









Meikel  Brandmey… 71d6b27 


Meikel  Brandmey… 9d4983d 
Meikel  Brandmey… bf92d52 

Meikel  Brandmey… 19fd4ce 

Meikel  Brandmey… 9601dc2 
Meikel  Brandmey… bf92d52 

Meikel  Brandmey… 2158cdd 
Meikel  Brandmey… c068303 



Meikel  Brandmey… 469a528 
Meikel  Brandmey… c068303 













Meikel  Brandmey… 9601dc2 
Meikel  Brandmey… c068303 






Meikel  Brandmey… 71d6b27 
Meikel  Brandmey… c068303 

Meikel  Brandmey… 71d6b27 
Meikel  Brandmey… c068303 
Meikel  Brandmey… 469a528 
Meikel  Brandmey… fd3a6f3 


Meikel  Brandmey… 71d6b27 
Meikel  Brandmey… fd3a6f3 
Meikel  Brandmey… 469a528 
Meikel  Brandmey… c068303 
Meikel  Brandmey… fd3a6f3 


Meikel  Brandmey… 71d6b27 
Meikel  Brandmey… 469a528 

Meikel  Brandmey… c068303 






Meikel  Brandmey… 9601dc2 
Meikel  Brandmey… c068303 







Meikel  Brandmey… fd3a6f3 
Meikel  Brandmey… c068303 

Meikel  Brandmey… fd3a6f3 
Meikel  Brandmey… c068303 















Meikel  Brandmey… fd3a6f3 
Meikel  Brandmey… c068303 

Meikel  Brandmey… fd3a6f3 
Meikel  Brandmey… c068303 







  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
; Copyright 2010,2012 © Meikel Brandmeyer.
; All rights reserved.
; 
; Permission is hereby granted, free of charge, to any person obtaining a
; copy of this software and associated documentation files (the "Software"),
; to deal in the Software without restriction, including without limitation
; the rights to use, copy, modify, merge, publish, distribute, sublicense,
; and/or sell copies of the Software, and to permit persons to whom the
; Software is furnished to do so, subject to the following conditions:
; 
; The above copyright notice and this permission notice shall be included
; in all copies or substantial portions of the Software.
; 
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
; OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
; DEALINGS IN THE SOFTWARE.

(ns #^{:author "Meikel Brandmeyer"
      :doc
  "clojurecheck - property based testing

  clojurecheck is an extensions to clojure.test. It provides generators
  for different values and datastructures. With their help random input
  for test cases are generated to test the behaviour of the code under
  test with more and more complex input.

  Example:
    (ns my.package
      (:use clojure.test)
      (:require [clojurecheck.core :as cc]))

    (defn angular-diff
      [a b]
      (-> (- a b) Math/abs (mod 180)))

    (deftest angular-diff-standard-test
      (are [x y] (= x y)
        (angular-diff   0   0) 0
        (angular-diff  90  90) 0
        (angular-diff   0  45) 45
        (angular-diff  45   0) 45
        (angular-diff   0 270) 90
        (angular-diff (* 360 2) (+ (* 360 4) 23)) 23))

    (deftest angular-diff-property
      (cc/property „angular-diff is smallest angel between a and b“
        [a    (cc/int)
         n    (cc/int)
         diff (cc/int :lower -180 :upper 180)]
        (let [b (+ a (* 360 n) diff)]
          (is (= (angular-diff a b) (Math/abs diff))))))

  And a result:
    my.package=> (run-tests)
    Testing my.package

    FAIL in (angular-diff-property) (core.clj:305)
    falsified 'angular-diff is smallest angel between a and b' in 5 attempts
    inputs where:
      a = -2
      n = 1
      diff = -3
    failed assertions where:
      expected: (= (angular-diff a b) (Math/abs diff))
        actual: (not (= 177 3))

    Ran 2 tests containing 7 assertions.
    1 failures, 0 errors.
    {:type :summary, :test 2, :pass 6, :fail 1, :error 0}"}
  clojurecheck.core
  (:refer-clojure
    :exclude (long int double float
              boolean
              list vec
              set sorted-set
              hash-map sorted-map))
  (:use clojure.test))

;; # Interfaces
;;
;; ## Generator
;;
;; A generator is used to generate random input values. A generator
;; supports a single method – `arbitrary` – which is called by the
;; `generate` function.
(defprotocol Generator
  (#^{:added "2.1"} arbitrary
    [generator size]
    "Choose an arbitrary value by virtue of the given generator.
  The `size` parameter may be used to generate increasingly
  complex values. May return `nil` when it was not possible to
  generate a value."))

;; Since a generator might return `nil` in case it was not able to
;; generate a value (cf. `guard`) `generate` will try to `*trials*`
;; times to generate a value.
(def #^{:doc "Number of trials a property is tested with generated input.
  Default is 1000."
        :added   "2.0"
        :dynamic true}
  *trials*
  1000)

(defn generate
  "Try *trials* times to generate a valid random input. This is the
  public entry point to generate a value via a generator."
  {:added "2.0"}
  [gen size]
  (loop [n *trials*]
    (if (pos? n)
      (if-let [dval (arbitrary gen size)]
        dval
        (recur (dec n)))
      (throw (Exception. "trials exhausted while generating input")))))

;; ## Domain Values
;;
;; So far we didn't consider the actual type of value a generator
;; returns. So naive implementation just returns the generated value.
;; However this is not enough for shrinking. The value itself is not
;; enough to do meaningful shrinking. You have to know how you arrived
;; at the value at hand. So the value itself needs to carry such
;; knowledge.
;;
;; So generators return so called “domain values”. They carry the
;; generated value itself as well as the instructions on how to shrink
;; the given value.
;;
;; Shrinking itself is implemented as a sequence of shrunken values.
;; They are tried in turn with the initially failing property. In case
;; the property still fails the failing value is further shrunken. In
;; case the property suddenly succeeds the next value from the sequence
;; is tried.
(defprotocol DomainValue
  (#^{:added "2.1"} value
    [this]
    "Return the generated value associated with this domain value.")
  (#^{:added "2.1"} shrink
    [this]
    "Return a sequence of less complex value than the one given.
  In case the value is not further shrinkable, return `nil`."))

;; # Implementation
;;
;; ## Basic values
;;
;; There are two very basic types of values.
;;
;; * `SimpleValue` carries a value which is not shrinkable.
;; * `ShrinkableValue` carries a `DomainValue` and function which
;;   specifies how the value should be shrunken.
(deftype SimpleValue [v]
  DomainValue
  (value  [this] v)
  (shrink [this] nil))

;; These two types are used for the very basic generators. However
;; it is perfectly possibly to define custom domain value types.
(deftype ShrinkableValue [v f]
  DomainValue
  (value  [this] (value v))
  (shrink [this] (f v)))

;; Some of Clojure's internal types are also acting as domain values.
;; The map to the corresponding behaviour of the given type in the
;; role of an generator.
(extend-protocol DomainValue

  ;; Sequences are considered as tuples of generated values.
  ;; Shrink one after the other.
  clojure.lang.ISeq
  (value  [this] (map value this))
  (shrink [this] (mapcat shrink this))

  ;; Maps are also acting as domain values. The keys are fixed
  ;; but the values are domain values. Again we try to shrink
  ;; here one after the other.
  clojure.lang.IPersistentMap
  (value  [this] (into (empty this) (for [[k v] this] [k (value v)])))
  (shrink [this] (for [[k v] this sv (shrink v)] (assoc this k sv))))

;; ## Basic generators
;;
;; Here we define some basic generators which are basically the
;; lowest level building blocks of our generator DSL.
(extend-protocol Generator

  ;; Functions may be used directly as generators. They generate
  ;; a `SimpleValue` which is not shrinable.
  clojure.lang.Fn
  (arbitrary [this size] (SimpleValue. (this size)))

  ;; Vectors and sequences are treated as tupels of generators.
  clojure.lang.PersistentVector
  (arbitrary [this size] (arbitrary (seq this) size))

  clojure.lang.ISeq
  (arbitrary [this size] (map #(generate % size) this))

  ;; Maps generate static maps with fixed keys mapped to the
  ;; values generated by the named generators.
  clojure.lang.IPersistentMap
  (arbitrary [this size]
    (into (empty this) (for [[k gen] this] [k (generate gen size)])))

  ;; Any other arbitrary object, which does not take part in the
  ;; `Generator` protocol gives a generator which always generates
  ;; the given value.
  Object
  (arbitrary [this size] (arbitrary (constantly this) size)))

;; ## Numeric generators
;;
;; The first a little more complex generators are the numeric ones.
;; We define a common generator type, which handles generating
;; numeric values based on a custom provided `random` function.
(deftype NumericGenerator
  [random lower upper]
  Generator
  (arbitrary [this size]
    (let [[low high] (if size
                       [(max (- size) lower) (min size upper)]
                       [lower upper])]
      (SimpleValue. (+ low (random (- high low)))))))

(defn long
  "Generates a random integral number between lower and upper.
  The interval is limited by the size guidance."
  {:added "2.1"}
  [& {:keys [lower upper] :or {lower -32768 upper 32767}}]
  (NumericGenerator. rand-int lower upper))

(def #^{:doc   "Alternative name for `long`."
        :added "2.0"}
  int
  long)
(alter-meta! #'int merge (meta #'long) (meta #'int))

(defn double
  "Generates a random floating point number between lower and upper.
  The interval is limited by the size guidance."
  {:added "2.1"}
  [& {:keys [lower upper] :or {lower -32768.0 upper 32767.0}}]
  (NumericGenerator. rand lower upper))

(def #^{:doc   "Alternative name for `double`."
        :added "2.0"}
  float
  double)
(alter-meta! #'float merge (meta #'double) (meta #'float))

;; ## Boolean generators
(defn boolean
  "Generates a boolean value which generates the value `true`
  with probability `p`."
  {:added "2.1"}
  [p]
  {:pre [(<= 0.0 p) (<= p 1.0)]}
  (reify
    Generator
    (arbitrary [this _size] (SimpleValue. (< (rand) p)))))

(def #^{:doc "Generates a random boolean value with 50% chance of
  a `true` value."
        :added "2.0"}
  bool
  (boolean 0.5))

;; ## `let-gen` and `generators`
;;
;; `let-gen` was supposed to provide some domonad style interface
;; for easy building of custom generators. However this seriously
;; interferes with shrinking, since a generator might depend on its
;; parameters on the random value of a previous generator. It is not
;; clear how shrinking should work in this case.
;;
;; Hence, `let-gen` is deprecated in favor of `generators` which
;; acts more like `binding` in that it binds things in parallel.
(defmacro let-gen
  "Takes a vector of let-like bindings. let-gen returns itself
  a generator. When called it evaluates the generators on the
  right hand side and assigns the result to the corresponding
  local. Later generator definitions may refer to previous locals
  as in a usual let.

  Similar to for and doseq you can intersperse the bindings with
  directives, which modify the behaviour.

    * :when (pred? ...):
      In case the predicate evaluates to false the generation
      process is cancelled and retried.
    * :let [...]:
      Takes a normal let-style binding and makes the bindings
      available to the following generator definitions."
  {:added      "2.0"
   :deprecated true}
  [bindings expr]
  (@#'clojure.core/assert-args
       (vector? bindings)       "a vector for its bindings"
       (even? (count bindings)) "an even number of forms in the bindings vector")
  (let [size       (gensym "size__")
        emit-g     (fn [[local gen] body]
                     `(let [dval#  (generate ~gen ~size)
                            ~local (value dval#)]
                        ~body))
        emit-p     (fn [pred body]
                     `(when ~pred
                        ~body))
        emit-l     (fn [bindings body]
                     `(let ~bindings
                        ~body))]
    `(reify
       Generator
       (arbitrary [this# ~size]
         ~(reduce
            (fn [body [v t :as bs]]
              (case t
                :when (emit-p v body)
                :let  (emit-l v body)
                (emit-g [t v] body)))
            `(SimpleValue. ~expr)
            (partition 2 (rseq bindings)))))))

(declare transform)

(defmacro generators
  "Takes a map of locals to generators. Upon generation of a value, binds
  the generated values to the locals and executes body to obtain the final
  value. Binding happens in parallel—similar to `binding`.

  Shrinking is handled by shrinking each generator one after the other
  and executing the body with the shrunken values in place."
  {:added "2.1"}
  [bindings & body]
  (let [ks  (keys bindings)
        gen (zipmap (map (fn [k] `(quote ~k)) ks) (vals bindings))]
    `(transform ~gen (fn [{:syms [~@ks]}] ~@body))))

;; ## Data structure generators
(defn list
  "Generates a list based on the given generator. The length of
  the list is an integer generator. The default grows with the
  size guidance. The size guidance is passed verbatim to the
  item generator."
  {:added "2.0"}
  [item & {:keys [length] :or {length (int)}}]
  (generators {len      length
               elements (repeat item)}
    (take len elements)))

(defn vec
  "Generates a vector based on the given generator. The length of
  the vector is an integer generator. The default grows with the
  size guidance. The size guidance is passed verbatim to the item
  generator."
  {:added "2.0"}
  [item & {:keys [length] :or {length (int)}}]
  (generators {elems (list item :length length)}
    (clojure.core/vec elems)))

(defn set
  "Generates a set based on the given generator. The size of
  the set is an integer generator. The default grows with the
  size guidance. The size guidance is passed verbatim to the
  item generator."
  {:added "2.0"}
  [item & {:keys [length] :or {length (int)}}]
  (generators {elems (list item :length length)}
    (clojure.core/set elems)))

(defn sorted-set
  "Generates a sorted-set based on the given generator. The size of
  the sorted-set is an integer generator. The default grows with the
  size guidance. The size guidance is passed verbatim to the item
  generator."
  {:added "2.0"}
  [item & {:keys [length] :or {length (int)}}]
  (generators {elems (list item :length length)}
    (apply clojure.core/sorted-set elems)))

(defn hash-map
  "Generates a hash-map based on the given generators. The size of
  the hash-map is an integer generator. The default grows with the
  size guidance. The size guidance is passed verbatim to the key
  and value generators."
  {:added "2.0"}
  [keys vals & {:keys [length] :or {length (int)}}]
  (generators {len length
               ks  (repeat keys)
               vs  (repeat vals)}
    (zipmap (take len ks) (take len vs))))

(defn sorted-map
  "Generates a sorted-map based on the given generators. The size of
  the sorted-map is an integer generator. The default grows with the
  size guidance. The size guidance is passed verbatim to the key and
  value generators."
  {:added "2.0"}
  [keys vals & {:keys [length] :or {length (int)}}]
  (generators {len length
               ks  (repeat keys)
               vs  (repeat vals)}
    (apply clojure.core/sorted-map (interleave (take len ks) (take len vs)))))

(defn string
  "Generates a string taking characters from the given generator. The
  length of the string is an integer generator. The default grows with
  the size guidance. The size guidance is passed verbatim to the
  character generator."
  {:added "2.1"}
  [characters & {:keys [length] :or {length (int)}}]
  (generators {chs (list characters :length length)}
    (apply str chs)))

;; ## Generator transformers
(defn transform
  "Generates a value according to `gen` and calls `f` with the generated
  value—not the domain value. The return value of `f` becomes the new
  value.

  Shrinking is handled by shrinking the original domain value and
  mapping `f` over the resulting sequence of shrunken values."
  {:added "2.1"}
  [gen f]
  (let [transformer (fn transformer [dval]
                      (ShrinkableValue. (SimpleValue. (f (value dval)))
                                        (fn [_]
                                          (map transformer (shrink dval)))))]
    (reify
      Generator
      (arbitrary [this size] (transformer (generate gen size))))))

(defn frequency
  "Chooses one of the given generators based on the associated
  weights. The size guidance is passed verbatim to the chosen
  generator."
  {:added "2.0"}
  [choices]
  (let [freqs   (reductions + (vals choices))
        total   (last freqs)
        freqs   (map #(-> % (/ total) clojure.core/float) freqs)
        choices (map vector (keys choices) freqs)
        choose  (fn []
                  (let [dice (rand)]
                    ; XXX: c cannot be nil, because it is a generator.
                    (some (fn [[c f]] (when (< dice f) c)) choices)))]
    (reify
      Generator
      (arbitrary [this size] (generate (choose) size)))))

(defn one-of
  "Chooses one of the given generators with equal probability.
  The size guidance is passed verbatim to the chosen generator."
  {:added "2.0"}
  [choices]
  (frequency (zipmap choices (repeat 1))))

(defn element
  "Choose one of the given elements with equal probability.
  Since the elements are \"constant\" generators the size
  guidance is ignored."
  {:added "2.0"}
  [choices]
  (one-of (map constantly choices)))

(defn guard
  "Guard the given generator with the predicate. But note, that this
  can be quite inefficient. It is better to generate only interesting
  values in the first place."
  {:added "2.1"}
  [generator pred]
  (reify
    Generator
    (arbitrary
      [this size]
      (when-let [dval (arbitrary generator size)]
        (when (pred (value dval))
          dval)))))

(defn sized
  "Modify the size guidance according to f and pass it on to the
  given generator. If f is not a function it will be taken turned
  into a function returning the given value as constant."
  {:added "2.0"}
  [f gen]
  (let [f (if (fn? f) f (constantly f))]
    (reify Generator (arbitrary [this size] (arbitrary gen (f size))))))

(defn *size-scale*
  "The scale function used to scale up the size guidance with increasing
  trials while testing a property with generated input."
  {:added "2.0" :dynamic true}
  [n]
  (if (even? n)
    (/ n 2)
    (/ (inc n) 2)))

(defn property*
  "The property* driver handles the work when testing a property. It
  expects:
    * a descriptive message for failure reporting
    * a list of locals (also for reporting)
    * a generator which takes the scaled size and returns the input
      for the property
    * the property test in form of a function of the generated
      input."
  {:added "2.0"}
  [msg locals gen prop]
  (let [results   (atom [])
        report-fn #(swap! results conj %)]
    (loop [n 1]
      (reset! results [])
      (if (< *trials* n)
        (report {:type :pass})
        (let [input (->> n *size-scale* (generate gen))]
          (try
            (binding [report report-fn]
              (prop (value input)))
            (let [failures (filter #(-> % :type (not= :pass)) @results)]
              (when-let [failures (seq failures)]
                (do-report {:type     ::property-fail
                            :message  msg
                            :locals   locals
                            :input    (value input)
                            :attempts n
                            :failures failures})))
            (catch Throwable t
              (do-report {:type    ::property-error
                          :message msg
                          :locals  locals
                          :input   (value input)
                          :error   t})))
          (recur (inc n)))))))

(defmacro property
  "Defines a property consisting of a binding vector as for let-gen
  which associates locals with the given generators. When testing the
  property the locals will be assigned the values generated.

  The body is a normal deftest body."
  {:added "2.0"}
  [msg bindings & body]
  (let [locals (remove keyword? (take-nth 2 bindings))]
    `(property* ~msg
                (quote ~locals)
                (let-gen ~bindings [~@locals])
                (fn [[~@locals]] ~@body))))

(defmethod report ::property-fail
  [{:keys [message locals input attempts failures] :as this}]
  (with-test-out
    (inc-report-counter :fail)
    (println "\nFAIL in" (testing-vars-str this))
    (when (seq *testing-contexts*) (println (testing-contexts-str)))
    (println "falsified" (if message (str "'" message "'") "property")
             "in" attempts "attempts")
    (println "inputs where:")
    (doseq [[local value] (map vector locals input)]
      (println " " local "=" (pr-str value)))
    (println "failed assertions where:")
    (doseq [fail failures]
      (println "  expected:" (pr-str (:expected fail)))
      (print "    actual: ")
      (let [actual (:actual fail)]
        (if (instance? Throwable actual)
          (clojure.stacktrace/print-cause-trace actual *stack-trace-depth*)
          (prn actual))))))

(defmethod report ::property-error
  [{:keys [message locals input attempt error] :as this}]
  (with-test-out
    (inc-report-counter :error)
    (println "\nERROR in" (testing-vars-str this))
    (when (seq *testing-contexts*) (println (testing-contexts-str)))
    (println (if message message "property") (str "(in attempt " attempt))
    (println "inputs where:")
    (doseq [[local value] (map vector locals input)]
      (println " " local "=" (pr-str value)))
    (println "error was:")
    (if (instance? Throwable error)
      (clojure.stacktrace/print-cause-trace error *stack-trace-depth*)
      (prn error))))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.