QCheck easier to use, with printing combinators module

and simpler ('a -> bool) properties
This commit is contained in:
Simon Cruanes 2013-09-28 12:43:38 +02:00
parent ead09ae349
commit b4f3925a45
2 changed files with 93 additions and 59 deletions

101
qCheck.ml
View file

@ -29,14 +29,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Arbitrary = struct module Arbitrary = struct
type 'a t = Random.State.t -> 'a type 'a t = Random.State.t -> 'a
let int st = Random.State.int st max_int let int n st = Random.State.int st n
let int_range ?(start=0) ~stop st = let int_range ~start ~stop st =
let n = stop - start in let n = stop - start in
if n <= 0 if n <= 0
then 0 then 0
else start + Random.State.int st n else start + Random.State.int st n
let small_int = int 100
let bool = Random.State.bool let bool = Random.State.bool
let float f st = Random.State.float st f let float f st = Random.State.float st f
@ -46,7 +48,7 @@ module Arbitrary = struct
let alpha st = let alpha st =
Char.chr (Char.code 'a' + Random.State.int st (Char.code 'z' - Char.code 'a')) Char.chr (Char.code 'a' + Random.State.int st (Char.code 'z' - Char.code 'a'))
let string ~len st = let string_len len st =
let n = len st in let n = len st in
assert (n>=0); assert (n>=0);
let s = String.create n in let s = String.create n in
@ -55,6 +57,8 @@ module Arbitrary = struct
done; done;
s s
let string st = string_len (int 10) st
let map ar f st = f (ar st) let map ar f st = f (ar st)
let rec _make_list ar st acc n = let rec _make_list ar st acc n =
@ -62,7 +66,7 @@ module Arbitrary = struct
let x = ar st in let x = ar st in
_make_list ar st (x::acc) (n-1) _make_list ar st (x::acc) (n-1)
let list ~len ar st = let list ?(len=int 10) ar st =
let n = len st in let n = len st in
_make_list ar st [] n _make_list ar st [] n
@ -74,7 +78,7 @@ module Arbitrary = struct
let list_repeat len ar st = let list_repeat len ar st =
_make_list ar st [] len _make_list ar st [] len
let array ~len ar st = let array ?(len=int 10) ar st =
let n = len st in let n = len st in
Array.init n (fun _ -> ar st) Array.init n (fun _ -> ar st)
@ -118,41 +122,57 @@ module Arbitrary = struct
fix ~max ~base f st fix ~max ~base f st
end end
(** {2 Pretty printing} *)
module PP = struct
type 'a t = 'a -> string
let int = string_of_int
let bool = string_of_bool
let float = string_of_float
let string s = s
let char c =
let s = "_" in
s.[0] <- c;
s
let list pp l =
let b = Buffer.create 25 in
Buffer.add_char b '(';
List.iteri (fun i x ->
if i > 0 then Buffer.add_string b ", ";
Buffer.add_string b (pp x))
l;
Buffer.add_char b ')';
Buffer.contents b
let array pp a =
let b = Buffer.create 25 in
Buffer.add_char b '[';
Array.iteri (fun i x ->
if i > 0 then Buffer.add_string b ", ";
Buffer.add_string b (pp x))
a;
Buffer.add_char b ']';
Buffer.contents b
end
(** {2 Testing} *) (** {2 Testing} *)
module Prop = struct module Prop = struct
type 'a t = { type 'a t = 'a -> bool
name : string;
precond : 'a -> bool; exception PrecondFail
test: 'a -> bool;
} (** A simple property on elements of type 'a *)
let __true _ = true
let make_prop ?(precond=__true) name test = {
precond;
name;
test;
}
let (>::) name test = make_prop name test
let (==>) a b = let (==>) a b =
{ b with precond = (fun x -> a x && b.precond x); } fun x ->
if not (a x) then raise PrecondFail else b x
let (&&&) a b = let (&&&) a b x = a x && b x
{ precond = (fun x -> a.precond x && b.precond x);
name = Printf.sprintf "%s and %s" a.name b.name;
test = (fun x -> a.test x && b.test x);
}
let (|||) a b = let (|||) a b x = a x || b x
{ precond = (fun x -> a.precond x && b.precond x);
name = Printf.sprintf "%s or %s" a.name b.name;
test = (fun x -> a.test x || b.test x);
}
let (!!!) a = { a with name = "not " ^ a.name; test = (fun x -> not (a.test x)); } let (!!!) a x = not (a x)
end end
type 'a result = type 'a result =
@ -160,16 +180,17 @@ type 'a result =
| Failed of 'a list | Failed of 'a list
| Error of exn | Error of exn
let check ?(rand=Random.State.make_self_init ()) ?(n=100) ~gen ~prop = let check ?(rand=Random.State.make_self_init ()) ?(n=100) gen prop =
let precond_failed = ref 0 in let precond_failed = ref 0 in
let failures = ref [] in let failures = ref [] in
try try
for i = 0 to n - 1 do for i = 0 to n - 1 do
let x = gen rand in let x = gen rand in
if not (prop.Prop.precond x) try
then incr precond_failed if not (prop x)
else if not (prop.Prop.test x) then failures := x :: !failures
then failures := x :: !failures with Prop.PrecondFail ->
incr precond_failed
done; done;
match !failures with match !failures with
| [] -> Ok (n, !precond_failed) | [] -> Ok (n, !precond_failed)
@ -179,9 +200,9 @@ let check ?(rand=Random.State.make_self_init ()) ?(n=100) ~gen ~prop =
(** {2 Main} *) (** {2 Main} *)
let run ?pp ?n ~rand ~gen ~prop = let run ?pp ?n ?(rand=Random.State.make_self_init()) ?(name="<no name>") gen prop =
Printf.printf "testing property %s...\n" prop.Prop.name; Printf.printf "testing property %s...\n" name;
match check ~rand ?n ~gen ~prop with match check ~rand ?n gen prop with
| Ok (n, prefail) -> | Ok (n, prefail) ->
Printf.printf "passed %d tests (%d preconditions failed)\n" n prefail; Printf.printf "passed %d tests (%d preconditions failed)\n" n prefail;
true true

View file

@ -32,12 +32,15 @@ module Arbitrary : sig
type 'a t = Random.State.t -> 'a type 'a t = Random.State.t -> 'a
(** A generator of arbitrary values of type 'a *) (** A generator of arbitrary values of type 'a *)
val int : int t val int : int -> int t
(** Any integer *) (** Any integer *)
val int_range : ?start:int -> stop:int -> int t val int_range : start:int -> stop:int -> int t
(* Integer range *) (* Integer range *)
val small_int : int t
(** Ints lower than 100 *)
val bool : bool t val bool : bool t
(** Arbitrary boolean *) (** Arbitrary boolean *)
@ -50,13 +53,16 @@ module Arbitrary : sig
val float : float -> float t val float : float -> float t
(** Random float *) (** Random float *)
val string : len:int t -> string t val string : string t
(** Random strings of small length *)
val string_len : int t -> string t
(** String of random length *) (** String of random length *)
val map : 'a t -> ('a -> 'b) -> 'b t val map : 'a t -> ('a -> 'b) -> 'b t
(** Transform an arbitrary into another *) (** Transform an arbitrary into another *)
val list : len:int t -> 'a t -> 'a list t val list : ?len:int t -> 'a t -> 'a list t
(** List of arbitrary length *) (** List of arbitrary length *)
val opt : 'a t -> 'a option t val opt : 'a t -> 'a option t
@ -65,7 +71,7 @@ module Arbitrary : sig
val list_repeat : int -> 'a t -> 'a list t val list_repeat : int -> 'a t -> 'a list t
(** Lists of given length exactly *) (** Lists of given length exactly *)
val array : len:int t -> 'a t -> 'a array t val array : ?len:int t -> 'a t -> 'a array t
(** Random array of random length *) (** Random array of random length *)
val array_repeat : int -> 'a t -> 'a array t val array_repeat : int -> 'a t -> 'a array t
@ -91,19 +97,25 @@ module Arbitrary : sig
(** Recursive values of at most given random depth *) (** Recursive values of at most given random depth *)
end end
(** {2 Pretty printing} *)
module PP : sig
type 'a t = 'a -> string
val int : int t
val bool : bool t
val float : float t
val char : char t
val string : string t
val list : 'a t -> 'a list t
val array : 'a t -> 'a array t
end
(** {2 Testing} *) (** {2 Testing} *)
module Prop : sig module Prop : sig
type 'a t = { type 'a t = 'a -> bool
name : string;
precond : 'a -> bool;
test: 'a -> bool;
} (** A simple property on elements of type 'a *)
val (>::) : string -> ('a -> bool) -> 'a t
(** Build a test with the given name *)
val make_prop : ?precond:('a -> bool) -> string -> ('a -> bool) -> 'a t
val (==>) : ('a -> bool) -> 'a t -> 'a t val (==>) : ('a -> bool) -> 'a t -> 'a t
(** Precondition for a test *) (** Precondition for a test *)
@ -123,15 +135,16 @@ type 'a result =
| Failed of 'a list | Failed of 'a list
| Error of exn | Error of exn
val check : ?rand:Random.State.t -> ?n:int -> val check : ?rand:Random.State.t -> ?n:int ->
gen:'a Arbitrary.t -> prop:'a Prop.t -> 'a result 'a Arbitrary.t -> 'a Prop.t -> 'a result
(** Check that the property [prop] holds on [n] random instances of the type (** Check that the property [prop] holds on [n] random instances of the type
'a, as generated by the arbitrary instance [gen] *) 'a, as generated by the arbitrary instance [gen] *)
(** {2 Main} *) (** {2 Main} *)
val run : ?pp:('a -> string) -> ?n:int -> rand:Random.State.t -> val run : ?pp:('a -> string) -> ?n:int ->
gen:'a Arbitrary.t -> prop:'a Prop.t -> bool ?rand:Random.State.t -> ?name:string ->
'a Arbitrary.t -> 'a Prop.t -> bool
(** Run and print result *) (** Run and print result *)
val run_tests : (rand:Random.State.t -> bool) list -> bool val run_tests : (rand:Random.State.t -> bool) list -> bool