Library
Module
Module type
Parameter
Class
Class type
The library takes inspiration from Haskell's QuickCheck library. The rough idea is that the programer describes invariants that values of a certain type need to satisfy ("properties"), as functions from this type to bool. She also needs to desribe how to generate random values of the type, so that the property is tried and checked on a number of random instances.
This explains the organization of this module:
'aarbitrary
is used to describe how to generate random values, shrink them (make counter-examples as small as possible), print them, etc. Auxiliary modules such as Gen
, Print
, and Shrink
can be used along with make
to build one's own arbitrary instances.Test
is used to describe a single test, that is, a property of type 'a -> bool
combined with an 'a arbitrary
that is used to generate the test cases for this property. Optional parameters allow to specify the random generator state, number of instances to generate and test, etc.Examples:
let test =
QCheck.(Test.make ~count:1000
(list int) (fun l -> List.rev (List.rev l) = l));;
QCheck.Test.check_exn test;;
let test = QCheck.(
Test.make
~count:10_000 ~max_fail:3
(list small_nat)
(fun l -> l = List.sort compare l));;
QCheck.Test.check_exn test;;
Gen.fix
:type tree = Leaf of int | Node of tree * tree
let leaf x = Leaf x
let node x y = Node (x,y)
let g = QCheck.Gen.(sized @@ fix
(fun self n -> match n with
| 0 -> map leaf nat
| n ->
frequency
[1, map leaf nat;
2, map2 node (self (n/2)) (self (n/2))]
))
Gen.generate ~n:20 g;;
More complex and powerful combinators can be found in Gabriel Scherer's Generator
module. Its documentation can be found here.
b1 ==> b2
is the logical implication b1 => b2
ie not b1 || b2
(except that it is strict and will interact better with Test.check_exn
and the likes, because they will know the precondition was not satisfied.).
WARNING: this function should only be used in a property (see Test.make
), because it raises a special exception in case of failure of the first argument, to distinguish between failed test and failed precondition. Because of OCaml's evaluation order, both b1
and b2
are always evaluated; if b2
should only be evaluated when b1
holds, see assume
.
assume cond
checks the precondition cond
, and does nothing if cond=true
. If cond=false
, it interrupts the current test.
WARNING This function, like (==>)
, should only be used in a test. not outside. Example:
Test.make (list int) (fun l ->
assume (l <> []);
List.hd l :: List.tl l = l)
assume_fail ()
is like assume false
, but can take any type since we know it always fails (like assert false
). This is useful to ignore some branches in if
or match
.
Example:
Test.make (list int) (function
| [] -> assume_fail ()
| _::_ as l -> List.hd l :: List.tl l = l)
module Gen : sig ... end
module Print : sig ... end
module Iter : sig ... end
module Shrink : sig ... end
A value of type 'a arbitrary
glues together a random generator, and optional functions for shrinking, printing, computing the size, etc. It is the "normal" way of describing how to generate values of a given type, to be then used in tests (see Test
)
type 'a arbitrary = {
gen : 'a Gen.t;
print : ('a -> string) option;
print values
*)small : ('a -> int) option;
size of example
*)shrink : 'a Shrink.t option;
shrink to smaller examples
*)collect : ('a -> string) option;
map value to tag, and group by tag
*)}
a value of type 'a arbitrary
is an object with a method for generating random values of type 'a
, and additional methods to compute the size of values, print them, and possibly shrink them into smaller counterexamples
NOTE the collect field is unstable and might be removed, or moved into Test
.
val make :
?print:'a Print.t ->
?small:('a -> int) ->
?shrink:'a Shrink.t ->
?collect:('a -> string) ->
'a Gen.t ->
'a arbitrary
Builder for arbitrary. Default is to only have a generator, but other arguments can be added
A test is a universal property of type foo -> bool
for some type foo
, with an object of type foo arbitrary
used to generate, print, etc. values of type foo
.
See Test.make
to build a test, and Test.check_exn
to run one test simply. For more serious testing, it is better to create a testsuite and use QCheck_runner
.
module TestResult : sig ... end
Result of running a test
module Test : sig ... end
arbitrary
Choose among the given list of generators. The list must not be empty; if it is Invalid_argument is raised.
val unit : unit arbitrary
always generates ()
, obviously.
val bool : bool arbitrary
uniform boolean generator
val float : float arbitrary
generates regular floats (no nan and no infinities)
val pos_float : float arbitrary
positive float generator (no nan and no infinities)
val neg_float : float arbitrary
negative float generator (no nan and no infinities)
val int : int arbitrary
int generator. Uniformly distributed
val int_bound : int -> int arbitrary
int_bound n
is uniform between 0
and n
included
val int_range : int -> int -> int arbitrary
int_range a b
is uniform between a
and b
included. b
must be larger than a
.
val small_nat : int arbitrary
Small unsigned integers
val small_int : int arbitrary
Small unsigned integers. See Gen.small_int
.
val small_signed_int : int arbitrary
Small signed integers
val int32 : int32 arbitrary
int32 generator. Uniformly distributed
val int64 : int64 arbitrary
int generator. Uniformly distributed
val pos_int : int arbitrary
positive int generator. Uniformly distributed
val small_int_corners : unit -> int arbitrary
As small_int
, but each newly created generator starts with a list of corner cases before falling back on random generation.
val neg_int : int arbitrary
negative int generator. The distribution is similar to that of small_int
, not of pos_int
.
val char : char arbitrary
Uniformly distributed on all the chars (not just ascii or valid latin-1)
val printable_char : char arbitrary
uniformly distributed over a subset of chars
val numeral_char : char arbitrary
uniformy distributed over '0'..'9'
generates strings with a distribution of length of small_nat
val string : string arbitrary
generates strings with a distribution of length of small_nat
and distribution of characters of char
generates strings with distribution of characters if char
val printable_string : string arbitrary
generates strings with a distribution of length of small_nat
and distribution of characters of printable_char
generates strings with distribution of characters of printable_char
val small_printable_string : string arbitrary
val numeral_string : string arbitrary
generates strings with a distribution of length of small_nat
and distribution of characters of numeral_char
generates strings with a distribution of characters of numeral_char
generates lists with length from the given distribution
generates arrays with length from the given distribution
combines two generators into a generator of pairs
combines three generators into a generator of 3-tuples
val quad :
'a arbitrary ->
'b arbitrary ->
'c arbitrary ->
'd arbitrary ->
('a * 'b * 'c * 'd) arbitrary
combines four generators into a generator of 4-tuples
generator of functions of arity 1. The functions are always pure and total functions:
generator of functions of arity 2. The remark about fun1
also apply here.
Pick an element randomly in the list
Pick an element randomly in the array
val frequency :
?print:'a Print.t ->
?small:('a -> int) ->
?shrink:'a Shrink.t ->
?collect:('a -> string) ->
(int * 'a arbitrary) list ->
'a arbitrary
Similar to oneof
but with frequencies
Same as oneofl
, but each element is paired with its frequency in the probability distribution (the higher, the more likely)
Same as frequencyl
, but with an array
map f a
returns a new arbitrary instance that generates values using a#gen
and then transforms them through f
.
Specialization of map
when the transformation preserves the type, which makes shrinker, printer, etc. still relevant
val map_keep_input :
?print:'b Print.t ->
?small:('b -> int) ->
('a -> 'b) ->
'a arbitrary ->
('a * 'b) arbitrary
map_keep_input f a
generates random values from a
, and maps them into values of type 'b
using the function f
, but it also keeps the original value. For shrinking, it is assumed that f
is monotonic and that smaller input values will map into smaller values