module QCheck:sig
..end
This explains the organization of this module:
'a arbitrary
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 QCheck.Gen
, QCheck.Print
, and QCheck.Shrink
can be used along with QCheck.make
to build one's own arbitrary instances.QCheck.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.
let test =
QCheck.(Test.make ~count:1000
(list int) (fun l -> List.rev (List.rev l) = l));;
QCheck.Test.run_exn test;;
let test = QCheck.(
Test.make
~count:10_000 ~max_fail:3
(list small_int)
(fun l -> l = List.sort compare l));;
QCheck.Test.check_exn test;;
QCheck.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.
val (==>) : bool -> bool -> bool
b1 ==> b2
is the logical implication b1 => b2
ie not b1 || b2
(except that it is strict and will interact
better with QCheck.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 QCheck.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 QCheck.assume
.
val assume : bool -> unit
assume cond
checks the precondition cond
, and does nothing
if cond=true
. If cond=false
, it interrupts the current test.
WARNING This function, like QCheck.(==>)
, 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)
val assume_fail : unit -> 'a
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 QCheck.Test
)
type 'a
arbitrary = {
|
gen : |
|||
|
print : |
(* |
print values
| *) |
|
small : |
(* |
size of example
| *) |
|
shrink : |
(* |
shrink to smaller examples
| *) |
|
collect : |
(* |
map value to tag, and group by tag
| *) |
'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 QCheck.Test
.
val make : ?print:'a Print.t ->
?small:('a -> int) ->
?shrink:'a Shrink.t ->
?collect:('a -> string) -> 'a Gen.t -> 'a arbitrary
val set_print : 'a Print.t -> 'a arbitrary -> 'a arbitrary
val set_small : ('a -> int) -> 'a arbitrary -> 'a arbitrary
val set_shrink : 'a Shrink.t -> 'a arbitrary -> 'a arbitrary
val set_collect : ('a -> string) -> 'a arbitrary -> 'a arbitrary
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 QCheck.Test.make
to build a test, and QCheck.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
module Test:sig
..end
QCheck.arbitrary
val choose : 'a arbitrary list -> 'a arbitrary
val unit : unit arbitrary
()
, obviously.val bool : bool arbitrary
val float : float arbitrary
val pos_float : float arbitrary
val neg_float : float arbitrary
val int : int arbitrary
val int_bound : int -> int arbitrary
int_bound n
is uniform between 0
and n
includedval 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
val small_int : int arbitrary
QCheck.Gen.small_int
.val (--) : int -> int -> int arbitrary
QCheck.int_range
val int32 : int32 arbitrary
val int64 : int64 arbitrary
val pos_int : int arbitrary
val small_int_corners : unit -> int arbitrary
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
small_int
, not of pos_int
.val char : char arbitrary
val printable_char : char arbitrary
val numeral_char : char arbitrary
'0'..'9'
val string_gen_of_size : int Gen.t -> char Gen.t -> string arbitrary
val string_gen : char Gen.t -> string arbitrary
small_int
val string : string arbitrary
small_int
and distribution of characters of char
val small_string : string arbitrary
val string_of_size : int Gen.t -> string arbitrary
char
val printable_string : string arbitrary
small_int
and distribution of characters of printable_char
val printable_string_of_size : int Gen.t -> string arbitrary
printable_char
val small_printable_string : string arbitrary
val numeral_string : string arbitrary
small_int
and distribution of characters of numeral_char
val numeral_string_of_size : int Gen.t -> string arbitrary
numeral_char
val list : 'a arbitrary -> 'a list arbitrary
small_int
val list_of_size : int Gen.t -> 'a arbitrary -> 'a list arbitrary
val array : 'a arbitrary -> 'a array arbitrary
small_int
val array_of_size : int Gen.t -> 'a arbitrary -> 'a array arbitrary
val pair : 'a arbitrary -> 'b arbitrary -> ('a * 'b) arbitrary
val triple : 'a arbitrary ->
'b arbitrary -> 'c arbitrary -> ('a * 'b * 'c) arbitrary
val quad : 'a arbitrary ->
'b arbitrary ->
'c arbitrary ->
'd arbitrary -> ('a * 'b * 'c * 'd) arbitrary
val option : 'a arbitrary -> 'a option arbitrary
val fun1 : 'a arbitrary -> 'b arbitrary -> ('a -> 'b) arbitrary
val fun2 : 'a arbitrary ->
'b arbitrary ->
'c arbitrary -> ('a -> 'b -> 'c) arbitrary
fun1
also apply
here.val oneofl : ?print:'a Print.t ->
?collect:('a -> string) -> 'a list -> 'a arbitrary
val oneofa : ?print:'a Print.t ->
?collect:('a -> string) -> 'a array -> 'a arbitrary
val oneof : 'a arbitrary list -> 'a arbitrary
val always : ?print:'a Print.t -> 'a -> 'a arbitrary
val frequency : ?print:'a Print.t ->
?small:('a -> int) ->
?shrink:'a Shrink.t ->
?collect:('a -> string) ->
(int * 'a arbitrary) list -> 'a arbitrary
QCheck.oneof
but with frequenciesval frequencyl : ?print:'a Print.t ->
?small:('a -> int) -> (int * 'a) list -> 'a arbitrary
QCheck.oneofl
, but each element is paired with its frequency in
the probability distribution (the higher, the more likely)val frequencya : ?print:'a Print.t ->
?small:('a -> int) -> (int * 'a) array -> 'a arbitrary
QCheck.frequencyl
, but with an arrayval map : ?rev:('b -> 'a) -> ('a -> 'b) -> 'a arbitrary -> 'b arbitrary
map f a
returns a new arbitrary instance that generates values using
a#gen
and then transforms them through f
.rev
: if provided, maps values back to type 'a
so that the printer,
shrinker, etc. of a
can be used. We assume f
is monotonic in
this case (that is, smaller inputs are transformed into smaller outputs).val map_same_type : ('a -> 'a) -> 'a arbitrary -> 'a arbitrary
map
when the transformation preserves the type, which
makes shrinker, printer, etc. still relevantval 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 valuesprint
: optional printer for the f
's output