···11+v0.2.1 (04 March 2022)
22+---------------------
33+44+Build and compatibility fixes.
55+66+v0.2 (04 May 2020)
77+---------------------
88+99+New generators, printers and port to dune.
1010+1111+v0.1 (01 February 2018)
1212+---------------------
1313+1414+Initial release
+8
LICENSE.md
···11+Copyright (c) 2017 Stephen Dolan
22+33+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:
44+55+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
66+77+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.
88+
+82
README.md
···11+# Crowbar
22+33+**Crowbar** is a library for testing code, combining QuickCheck-style
44+ property-based testing and the magical bug-finding powers of
55+ [afl-fuzz](http://lcamtuf.coredump.cx/afl/).
66+77+## TL;DR
88+99+There are [some examples](./examples).
1010+1111+Some brief hints:
1212+1313+1. Use an opam switch with AFL instrumentation enabled (e.g. `opam sw 4.04.0+afl`).
1414+2. Run in AFL mode with `afl-fuzz -i in -o out -- ./_build/myprog.exe @@`.
1515+3. If you run your executable without arguments, crowbar will perform some simple (non-AFL) testing instead.
1616+4. Test binaries have a small amount of documentation, available with `--help`.
1717+1818+## writing tests
1919+2020+To test your software, come up with a property you'd like to test, then decide on the input you'd like for Crowbar to vary. A Crowbar test is some invocation of `Crowbar.check_eq` or `Crowbar.check`:
2121+2222+```ocaml
2323+let identity x =
2424+ Crowbar.check_eq x x
2525+```
2626+2727+and instructions for running the test with generated items with `Crowbar.add_test`:
2828+2929+```ocaml
3030+let () =
3131+ Crowbar.(add_test ~name:"identity function" [int] (fun i -> identity i))
3232+```
3333+3434+There are [more examples available](./examples), with varying levels complexity.
3535+3636+## building tests
3737+3838+Include `crowbar` in your list of dependencies via your favorite build system. The resulting executable is a Crowbar test. (Be sure to build a native-code executable, not bytecode.)
3939+4040+To build tests that run under AFL, you'll need to build your tests with a compiler that has AFL instrumentation enabled. (You can also enable it specifically for your build, although this is not recommended if your code has any dependencies, including the OCaml standard library). OCaml compiler variants with AFL enabled by default are available in `opam` with the `+afl` tag. All versions published starting with 4.05.0 are available, along with a backported 4.04.0.
4141+4242+```shell
4343+$ opam switch 4.06.0+afl
4444+$ eval `opam config env`
4545+$ ./build_my_rad_test.sh # or your relevant build runes
4646+```
4747+4848+## running Tests
4949+5050+Crowbar tests have two modes:
5151+5252+* a simple quickcheck-like mode for testing propositions against totally random input
5353+* a mode using [afl-persistent](https://github.com/stedolan/ocaml-afl-persistent) to get good performance from `afl-fuzz` with OCaml's instrumentation enabled
5454+5555+Crowbar tests can be directly invoked with `--help` for more documentation at runtime.
5656+5757+### fully random test mode
5858+5959+If you wish to use the quickcheck-like, fully random mode to run all tests distributed here, build the tests as above and then run the binary with no arguments.
6060+6161+```
6262+$ ./my_rad_test.exe | head -5
6363+the first test: PASS
6464+6565+the second test: PASS
6666+```
6767+6868+### AFL mode requirements
6969+7070+To run the tests in AFL mode, you'll need to install American Fuzzy Lop ([latest source tarball](http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz), although your distribution may also have a package available).
7171+7272+Once `afl-fuzz` is available on your system, create an `input` directory with a non-empty file in it (or use `test/input`, conveniently provided in this repository), and an `output` directory for `afl-fuzz` to store its findings. Then, invoke your test binary:
7373+7474+```
7575+afl-fuzz -i test/input -o output ./my_rad_test.exe @@
7676+```
7777+7878+This will launch AFL, which will generate new test cases and track the exploration of the state space. When inputs are discovered which cause a property not to hold, they will be reported as crashes (along with actual crashes, although in the OCaml standard library these are rare). See the [afl-fuzz documentation](https://lcamtuf.coredump.cx/afl/status_screen.txt) for more on AFL's excellent interface.
7979+8080+# What bugs have you found?
8181+8282+[An open issue](https://github.com/stedolan/crowbar/issues/2) has a list of issues discovered by testing with Crowbar. If you use Crowbar to improve your software, please let us know!
+43
crowbar.opam
···11+# This file is generated by dune, edit dune-project instead
22+opam-version: "2.0"
33+synopsis: "Write tests, let a fuzzer find failing cases"
44+description: """
55+Crowbar is a library for testing code, combining QuickCheck-style
66+property-based testing and the magical bug-finding powers of
77+[afl-fuzz](http://lcamtuf.coredump.cx/afl/).
88+"""
99+maintainer: ["Stephen Dolan <stephen.dolan@cl.cam.ac.uk>"]
1010+authors: ["Stephen Dolan <stephen.dolan@cl.cam.ac.uk>"]
1111+license: "MIT"
1212+homepage: "https://github.com/stedolan/crowbar"
1313+bug-reports: "https://github.com/stedolan/crowbar/issues"
1414+depends: [
1515+ "dune" {>= "2.9"}
1616+ "ocaml" {>= "4.08"}
1717+ "cmdliner" {>= "1.1.0"}
1818+ "afl-persistent" {>= "1.1"}
1919+ "calendar" {>= "2.00" & with-test}
2020+ "fpath" {with-test}
2121+ "pprint" {with-test}
2222+ "uucp" {with-test}
2323+ "uunf" {with-test}
2424+ "uutf" {with-test}
2525+ "odoc" {with-doc}
2626+]
2727+build: [
2828+ ["dune" "subst"] {dev}
2929+ [
3030+ "dune"
3131+ "build"
3232+ "-p"
3333+ name
3434+ "-j"
3535+ jobs
3636+ "--promote-install-files=false"
3737+ "@install"
3838+ "@runtest" {with-test}
3939+ "@doc" {with-doc}
4040+ ]
4141+ ["dune" "install" "-p" name "--create-install-files" name]
4242+]
4343+dev-repo: "git+https://github.com/stedolan/crowbar.git"
···11+open Crowbar
22+33+module Map = Map.Make (struct
44+ type t = int
55+ let compare (i : int) (j : int) = compare i j
66+end)
77+88+type t = ((int * int) list * int Map.t)
99+1010+let check_map ((list, map) : t) =
1111+ let rec dedup k = function
1212+ | [] -> []
1313+ | (k', v') :: rest when k = k' -> dedup k rest
1414+ | (k', v') :: rest ->
1515+ (k', v') :: dedup k' rest in
1616+ let list = match List.stable_sort (fun a b -> compare (fst a) (fst b)) list with
1717+ | [] -> []
1818+ | (k, v) :: rest -> (k, v) :: dedup k rest in
1919+ List.for_all (fun (k, v) -> Map.find k map = v) list &&
2020+ list = Map.bindings map
2121+2222+let map_gen : t gen = fix (fun map_gen -> choose [
2323+ const ([], Map.empty);
2424+ map [uint8; uint8; map_gen] (fun k v (l, m) ->
2525+ (k, v) :: l, Map.add k v m);
2626+ map [uint8; uint8] (fun k v ->
2727+ [k, v], Map.singleton k v);
2828+ map [uint8; map_gen] (fun k (l, m) ->
2929+ let rec rem_all k l =
3030+ let l' = List.remove_assoc k l in
3131+ if l = l' then l else rem_all k l' in
3232+ rem_all k l, Map.remove k m);
3333+ (* merge? *)
3434+ map [map_gen; map_gen] (fun (l, m) (l', m') ->
3535+ l @ l', Map.union (fun k a b -> Some a) m m');
3636+ map [uint8; map_gen] (fun k (list, map) ->
3737+ let (l, v, r) = Map.split k map in
3838+ let (l', vr') = List.partition (fun (kx,vx) -> kx < k) list in
3939+ let r' = List.filter (fun (kx, vx) -> kx <> k) vr' in
4040+ let v' = match List.assoc k vr' with n -> Some n | exception Not_found -> None in
4141+ assert (v = v');
4242+ (l' @ List.map (fun (k,v) -> k,v+42) r',
4343+ Map.union (fun k a b -> assert false) l (Map.map (fun v -> v + 42) r)))])
4444+4545+let () =
4646+ add_test ~name:"map" [map_gen] @@ fun m ->
4747+ check (check_map m)
···11+type data =
22+ | Datum of string
33+ | Block of header * data list
44+and header = string
55+66+type _ ty =
77+ | Int : int ty
88+ | Bool : bool ty
99+ | Prod : 'a ty * 'b ty -> ('a * 'b) ty
1010+ | List : 'a ty -> 'a list ty
1111+1212+let rec pp_ty : type a . _ -> a ty -> unit = fun ppf ->
1313+ let printf fmt = Format.fprintf ppf fmt in
1414+ function
1515+ | Int -> printf "Int"
1616+ | Bool -> printf "Bool"
1717+ | Prod(ta, tb) -> printf "Prod(%a,%a)" pp_ty ta pp_ty tb
1818+ | List t -> printf "List(%a)" pp_ty t
1919+2020+let rec serialize : type a . a ty -> a -> data = function
2121+ | Int -> fun n -> Datum (string_of_int n)
2222+ | Bool -> fun b -> Datum (string_of_bool b)
2323+ | Prod (ta, tb) -> fun (va, vb) ->
2424+ Block("pair", [serialize ta va; serialize tb vb])
2525+ | List t -> fun vs ->
2626+ Block("list", List.map (serialize t) vs)
2727+2828+let rec deserialize : type a . a ty -> data -> a = function[@warning "-8"]
2929+ | Int -> fun (Datum s) -> int_of_string s
3030+ | Bool -> fun (Datum s) -> bool_of_string s
3131+ | Prod (ta, tb) -> fun (Block("pair", [sa; sb])) ->
3232+ (deserialize ta sa, deserialize tb sb)
3333+ | List t -> fun (Block("list", ss)) ->
3434+ List.map (deserialize t) ss
+47
examples/serializer/test_serializer.ml
···11+open Crowbar
22+33+module S = Serializer
44+55+type any_ty = Any : 'a S.ty -> any_ty
66+77+let ty_gen =
88+ with_printer (fun ppf (Any t)-> S.pp_ty ppf t) @@
99+ fix (fun ty_gen -> choose [
1010+ const (Any S.Int);
1111+ const (Any S.Bool);
1212+ map [ty_gen; ty_gen] (fun (Any ta) (Any tb) ->
1313+ Any (S.Prod (ta, tb)));
1414+ map [ty_gen] (fun (Any t) -> Any (List t));
1515+ ])
1616+1717+let prod_gen ga gb = map [ga; gb] (fun va vb -> (va, vb))
1818+1919+let rec gen_of_ty : type a . a S.ty -> a gen = function
2020+ | S.Int -> int
2121+ | S.Bool -> bool
2222+ | S.Prod (ta, tb) -> prod_gen (gen_of_ty ta) (gen_of_ty tb)
2323+ | S.List t -> list (gen_of_ty t)
2424+2525+type pair = Pair : 'a S.ty * 'a -> pair
2626+2727+(* The generator for the final value, [gen_of_ty t], depends on the
2828+ generated type representation, [t]. This dynamic dependency cannot
2929+ be expressed with [map], it requires [dynamic_bind]. *)
3030+let pair_gen : pair gen =
3131+ dynamic_bind ty_gen @@ fun (Any t) ->
3232+ map [gen_of_ty t] (fun v -> Pair (t, v))
3333+3434+let rec printer_of_ty : type a . a S.ty -> a printer = function
3535+ | S.Int -> pp_int
3636+ | S.Bool -> pp_bool
3737+ | S.Prod (ta, tb) -> (fun ppf (a, b) ->
3838+ pp ppf "(%a, %a)" (printer_of_ty ta) a (printer_of_ty tb) b)
3939+ | S.List t -> pp_list (printer_of_ty t)
4040+4141+let check_pair (Pair (t, v)) =
4242+ let data = S.serialize t v in
4343+ match S.deserialize t data with
4444+ | exception _ -> fail "incorrect deserialization"
4545+ | v' -> check_eq ~pp:(printer_of_ty t) v v'
4646+4747+let () = add_test ~name:"pairs" [pair_gen] check_pair
···11+open Crowbar
22+33+let uchar =
44+ map [int32] (fun n ->
55+ let n = (Int32.to_int n land 0xFFFFFFF) mod 0x10FFFF in
66+ try Uchar.of_int n
77+ with Invalid_argument _ -> bad_test ())
88+99+let unicode = list1 uchar
1010+1111+let norm form str =
1212+ let n = Uunf.create form in
1313+ let rec add acc v = match Uunf.add n v with
1414+ | `Uchar u -> add (u :: acc) `Await
1515+ | `Await | `End -> acc in
1616+ let rec go acc = function
1717+ | [] -> List.rev (add acc `End)
1818+ | (v :: vs) -> go (add acc (`Uchar v)) vs in
1919+ go [] str
2020+2121+let unicode_to_string s =
2222+ let b = Buffer.create 10 in
2323+ List.iter (Uutf.Buffer.add_utf_8 b) s;
2424+ Buffer.contents b
2525+2626+2727+let pp_unicode ppf s =
2828+ Format.fprintf ppf "@[<v 2>";
2929+ Format.fprintf ppf "@[\"%s\"@]@ " (unicode_to_string s);
3030+ s |> List.iter (fun u ->
3131+ Format.fprintf ppf "@[U+%04x %s (%a)@]@ " (Uchar.to_int u) (Uucp.Name.name u) Uucp.Block.pp (Uucp.Block.block u));
3232+ Format.fprintf ppf "@]\n"
3333+3434+3535+let unicode = with_printer pp_unicode unicode
3636+3737+let () =
3838+ add_test ~name:"uunf" [unicode] @@ fun s ->
3939+ let nfc = norm `NFC s in
4040+ let nfd = norm `NFD s in
4141+ let nfkc = norm `NFKC s in
4242+ let nfkd = norm `NFKD s in
4343+(* [s; nfc; nfd; nfkc; nfkd] |> List.iter (fun s ->
4444+ Printf.printf "[%s]\n" (unicode_to_string s));
4545+ Printf.printf "\n%!";*)
4646+4747+ let tests =
4848+ [
4949+ nfc, [
5050+ norm `NFC nfc;
5151+ norm `NFC nfd];
5252+5353+ nfd, [
5454+ norm `NFD nfc;
5555+ norm `NFD nfd];
5656+5757+ nfkc, [
5858+ norm `NFC nfkc;
5959+ norm `NFC nfkd;
6060+ norm `NFKC nfc;
6161+ norm `NFKC nfd;
6262+ norm `NFKC nfkc;
6363+ norm `NFKC nfkd];
6464+6565+ nfkd, [
6666+ norm `NFD nfkc;
6767+ norm `NFD nfkd;
6868+ norm `NFKD nfc;
6969+ norm `NFKD nfd;
7070+ norm `NFKD nfkc;
7171+ norm `NFKD nfkd]
7272+ ] in
7373+ tests |> List.iter (fun (s, eqs) ->
7474+ List.iter (fun s' -> check_eq ~pp:pp_unicode s s') eqs)
7575+
+4
examples/xmldiff/dune
···11+; disabled because of xmldiff compat issues
22+;(test
33+; (name test_xmldiff)
44+; (libraries xmldiff crowbar))
+42
examples/xmldiff/test_xmldiff.ml
···11+open Crowbar
22+33+let ident = choose [const "a"; const "b"; const "c"]
44+let elem_name = map [ident] (fun s -> ("", s))
55+66+77+let attrs =
88+ choose [
99+ const Xmldiff.Nmap.empty;
1010+ map [elem_name; ident] Xmldiff.Nmap.singleton
1111+ ]
1212+1313+let rec xml = lazy (
1414+ choose [
1515+ const (`D "a");
1616+ map [ident] (fun s -> `D s);
1717+ map [elem_name; attrs; list (unlazy xml)] (fun s attrs elems ->
1818+ let rec normalise = function
1919+ | ([] | [_]) as x -> x
2020+ | `E _ as el :: xs ->
2121+ el :: normalise xs
2222+ | `D s :: xs ->
2323+ match normalise xs with
2424+ | `D s' :: xs' ->
2525+ `D (s ^ s') :: xs'
2626+ | xs' -> `D s :: xs' in
2727+ `E (s, attrs, normalise elems))
2828+ ])
2929+3030+let lazy xml = xml
3131+3232+let xml = map [xml] (fun d -> `E (("", "a"), Xmldiff.Nmap.empty, [d]))
3333+3434+let pp_xml ppf xml =
3535+ pp ppf "%s" (Xmldiff.string_of_xml xml)
3636+let xml = with_printer pp_xml xml
3737+3838+3939+let () =
4040+ add_test ~name:"xmldiff" [xml; xml] @@ fun xml1 xml2 ->
4141+ let (patch, xml3) = Xmldiff.diff_with_final_tree xml1 xml2 in
4242+ check_eq ~pp:pp_xml xml2 xml3
+624
src/crowbar.ml
···11+(* Fix for OCaml 5.0 *)
22+let () = Random.init 42
33+44+type src = Random of Random.State.t | Fd of Unix.file_descr
55+type state =
66+ {
77+ chan : src;
88+ buf : Bytes.t;
99+ mutable offset : int;
1010+ mutable len : int
1111+ }
1212+1313+type 'a printer = Format.formatter -> 'a -> unit
1414+1515+type 'a strat =
1616+ | Choose of 'a gen list
1717+ | Map : ('f, 'a) gens * 'f -> 'a strat
1818+ | Bind : 'a gen * ('a -> 'b gen) -> 'b strat
1919+ | Option : 'a gen -> 'a option strat
2020+ | List : 'a gen -> 'a list strat
2121+ | List1 : 'a gen -> 'a list strat
2222+ | Array : 'a gen -> 'a array strat
2323+ | Array1 : 'a gen -> 'a array strat
2424+ | Unlazy of 'a gen Lazy.t
2525+ | Primitive of (state -> 'a)
2626+ | Print of 'a printer * 'a gen
2727+2828+and 'a gen =
2929+ { strategy: 'a strat;
3030+ small_examples: 'a list; }
3131+3232+and ('k, 'res) gens =
3333+ | [] : ('res, 'res) gens
3434+ | (::) : 'a gen * ('k, 'res) gens -> ('a -> 'k, 'res) gens
3535+3636+type nonrec +'a list = 'a list = [] | (::) of 'a * 'a list
3737+3838+let unlazy f = { strategy = Unlazy f; small_examples = [] }
3939+4040+let fix f =
4141+ let rec lazygen = lazy (f (unlazy lazygen)) in
4242+ Lazy.force lazygen
4343+4444+let map (type f) (type a) (gens : (f, a) gens) (f : f) =
4545+ { strategy = Map (gens, f); small_examples = match gens with [] -> [f] | _ -> [] }
4646+4747+let dynamic_bind m f = {strategy = Bind(m, f); small_examples = [] }
4848+4949+let const x = map [] x
5050+let choose gens = { strategy = Choose gens; small_examples = List.map (fun x -> x.small_examples) gens |> List.concat }
5151+let option gen = { strategy = Option gen; small_examples = [None] }
5252+let list gen = { strategy = List gen; small_examples = [[]] }
5353+let list1 gen = { strategy = List1 gen; small_examples = List.map (fun x -> [x]) gen.small_examples }
5454+let array gen = { strategy = Array gen; small_examples = [[||]] }
5555+let array1 gen = { strategy = Array1 gen; small_examples = List.map (fun x -> [|x|]) gen.small_examples }
5656+let primitive f ex = { strategy = Primitive f; small_examples = [ex] }
5757+5858+let pair gena genb =
5959+ map (gena :: genb :: []) (fun a b -> (a, b))
6060+6161+let concat_gen_list sep l =
6262+ match l with
6363+ | h::t -> List.fold_left (fun acc e ->
6464+ map [acc; sep; e] (fun acc sep e -> acc ^ sep ^ e)
6565+ ) h t
6666+ | [] -> const ""
6767+6868+let with_printer pp gen = {strategy = Print (pp, gen); small_examples = gen.small_examples }
6969+7070+let result gena genb =
7171+ choose [
7272+ map [gena] (fun va -> Ok va);
7373+ map [genb] (fun vb -> Error vb);
7474+ ]
7575+7676+7777+let pp = Format.fprintf
7878+let pp_int ppf n = pp ppf "%d" n
7979+let pp_int32 ppf n = pp ppf "%s" (Int32.to_string n)
8080+let pp_int64 ppf n = pp ppf "%s" (Int64.to_string n)
8181+let pp_float ppf f = pp ppf "%f" f
8282+let pp_bool ppf b = pp ppf "%b" b
8383+let pp_char ppf c = pp ppf "%c" c
8484+let pp_uchar ppf c = pp ppf "U+%04x" (Uchar.to_int c)
8585+let pp_string ppf s = pp ppf "%S" s
8686+(* taken from OCaml stdlib *)
8787+let pp_print_iter ~pp_sep iter pp_v ppf v =
8888+ let is_first = ref true in
8989+ let pp_v v =
9090+ if !is_first then is_first := false else pp_sep ppf ();
9191+ pp_v ppf v
9292+ in
9393+ iter pp_v v
9494+let pp_list pv ppf l =
9595+ pp ppf "@[<hv 1>[%a]@]"
9696+ (pp_print_iter ~pp_sep:(fun ppf () -> pp ppf ";@ ") List.iter pv) l
9797+let pp_array pv ppf a =
9898+ pp ppf "@[<hv 1>[|%a|]@]"
9999+ (pp_print_iter ~pp_sep:(fun ppf () -> pp ppf ";@ ") Array.iter pv) a
100100+let pp_option pv ppf = function
101101+ | None ->
102102+ Format.fprintf ppf "None"
103103+ | Some x ->
104104+ Format.fprintf ppf "(Some %a)" pv x
105105+106106+exception BadTest of string
107107+exception FailedTest of unit printer
108108+let guard = function
109109+ | true -> ()
110110+ | false -> raise (BadTest "guard failed")
111111+let bad_test () = raise (BadTest "bad test")
112112+let nonetheless = function
113113+ | None -> bad_test ()
114114+ | Some a -> a
115115+116116+let get_data chan buf off len =
117117+ match chan with
118118+ | Random rand ->
119119+ for i = off to off + len - 1 do
120120+ Bytes.set buf i (Char.chr (Random.State.bits rand land 0xff))
121121+ done;
122122+ len - off
123123+ | Fd ch ->
124124+ Unix.read ch buf off len
125125+126126+let refill src =
127127+ assert (src.offset <= src.len);
128128+ let remaining = src.len - src.offset in
129129+ (* move remaining data to start of buffer *)
130130+ Bytes.blit src.buf src.offset src.buf 0 remaining;
131131+ src.len <- remaining;
132132+ src.offset <- 0;
133133+ let read = get_data src.chan src.buf remaining (Bytes.length src.buf - remaining) in
134134+ if read = 0 then
135135+ raise (BadTest "premature end of file")
136136+ else
137137+ src.len <- remaining + read
138138+139139+let rec getbytes src n =
140140+ assert (src.offset <= src.len);
141141+ if n > Bytes.length src.buf then failwith "request too big";
142142+ if src.len - src.offset >= n then
143143+ let off = src.offset in
144144+ (src.offset <- src.offset + n; off)
145145+ else
146146+ (refill src; getbytes src n)
147147+148148+let read_char src =
149149+ let off = getbytes src 1 in
150150+ Bytes.get src.buf off
151151+152152+let read_byte src =
153153+ Char.code (read_char src)
154154+155155+let read_bool src =
156156+ let n = read_byte src in
157157+ n land 1 = 1
158158+159159+let bool = with_printer pp_bool (primitive read_bool false)
160160+161161+let uint8 = with_printer pp_int (primitive read_byte 0)
162162+let int8 = with_printer pp_int (map [uint8] (fun n -> n - 128))
163163+164164+let read_uint16 src =
165165+ let off = getbytes src 2 in
166166+ Bytes.get_uint16_le src.buf off
167167+168168+let read_int16 src =
169169+ let off = getbytes src 2 in
170170+ Bytes.get_int16_le src.buf off
171171+172172+let uint16 = with_printer pp_int (primitive read_uint16 0)
173173+let int16 = with_printer pp_int (primitive read_int16 0)
174174+175175+let read_int32 src =
176176+ let off = getbytes src 4 in
177177+ Bytes.get_int32_le src.buf off
178178+179179+let read_int64 src =
180180+ let off = getbytes src 8 in
181181+ Bytes.get_int64_le src.buf off
182182+183183+let int32 = with_printer pp_int32 (primitive read_int32 0l)
184184+let int64 = with_printer pp_int64 (primitive read_int64 0L)
185185+186186+let int =
187187+ with_printer pp_int
188188+ (if Sys.word_size <= 32 then
189189+ map [int32] Int32.to_int
190190+ else
191191+ map [int64] Int64.to_int)
192192+193193+let float = with_printer pp_float (primitive (fun src ->
194194+ let off = getbytes src 8 in
195195+ let i64 = Bytes.get_int64_le src.buf off in
196196+ Int64.float_of_bits i64) 0.)
197197+198198+let char = with_printer pp_char (primitive read_char 'a')
199199+200200+(* maybe print as a hexdump? *)
201201+let bytes = with_printer pp_string (primitive (fun src ->
202202+ (* null-terminated, with '\001' as an escape code *)
203203+ let buf = Bytes.make 64 '\255' in
204204+ let rec read_bytes p =
205205+ if p >= Bytes.length buf then p else
206206+ match read_char src with
207207+ | '\000' -> p
208208+ | '\001' ->
209209+ Bytes.set buf p (read_char src);
210210+ read_bytes (p + 1)
211211+ | c ->
212212+ Bytes.set buf p c;
213213+ read_bytes (p + 1) in
214214+ let count = read_bytes 0 in
215215+ Bytes.sub_string buf 0 count) "")
216216+217217+let bytes_fixed n = with_printer pp_string (primitive (fun src ->
218218+ let off = getbytes src n in
219219+ Bytes.sub_string src.buf off n) (String.make n 'a'))
220220+221221+let choose_int n state =
222222+ assert (n > 0);
223223+ if n = 1 then
224224+ 0
225225+ else if (n <= 0x100) then
226226+ read_byte state mod n
227227+ else if (n < 0x1000000) then
228228+ Int32.(to_int (abs (rem (read_int32 state) (of_int n))))
229229+ else
230230+ Int64.(to_int (abs (rem (read_int64 state) (of_int n))))
231231+232232+let range ?(min=0) n =
233233+ if n <= 0 then
234234+ raise (Invalid_argument "Crowbar.range: argument n must be positive");
235235+ if min < 0 then
236236+ raise (Invalid_argument "Crowbar.range: argument min must be positive or null");
237237+ with_printer pp_int (primitive (fun s -> min + choose_int n s) min)
238238+239239+let uchar : Uchar.t gen =
240240+ map [range 0x110000] (fun x ->
241241+ guard (Uchar.is_valid x); Uchar.of_int x)
242242+let uchar = with_printer pp_uchar uchar
243243+244244+let rec sequence = function
245245+ g::gs -> map [g; sequence gs] (fun x xs -> x::xs)
246246+| [] -> const []
247247+248248+let shuffle_arr arr =
249249+ let n = Array.length arr in
250250+ let gs = List.init n (fun i -> range ~min:i (n - i)) in
251251+ map [sequence gs] @@ fun js ->
252252+ js |> List.iteri (fun i j ->
253253+ let t = arr.(i) in arr.(i) <- arr.(j); arr.(j) <- t);
254254+ arr
255255+256256+let shuffle l = map [shuffle_arr (Array.of_list l)] Array.to_list
257257+258258+exception GenFailed of exn * Printexc.raw_backtrace * unit printer
259259+260260+let rec generate : type a . int -> state -> a gen -> a * unit printer =
261261+ fun size input gen ->
262262+ if size <= 1 && gen.small_examples <> [] then List.hd gen.small_examples, fun ppf () -> pp ppf "?" else
263263+ match gen.strategy with
264264+ | Choose gens ->
265265+ (* FIXME: better distribution? *)
266266+ (* FIXME: choices of size > 255? *)
267267+ let n = choose_int (List.length gens) input in
268268+ let v, pv = generate size input (List.nth gens n) in
269269+ v, fun ppf () -> pp ppf "#%d %a" n pv ()
270270+ | Map ([], k) ->
271271+ k, fun ppf () -> pp ppf "?"
272272+ | Map (gens, f) ->
273273+ let rec len : type k res . int -> (k, res) gens -> int =
274274+ fun acc xs -> match xs with
275275+ | [] -> acc
276276+ | _ :: xs -> len (1 + acc) xs in
277277+ let n = len 0 gens in
278278+ (* the size parameter is (apparently?) meant to ensure that generation
279279+ eventually terminates, by limiting the set of options from which the
280280+ generator might choose once we've gotten deep into a tree. make sure we
281281+ always mark our passing, even when we've mapped one value into another,
282282+ so we don't blow the stack. *)
283283+ let size = (size - 1) / n in
284284+ let v, pvs = gen_apply size input gens f in
285285+ begin match v with
286286+ | Ok v -> v, pvs
287287+ | Error (e, bt) -> raise (GenFailed (e, bt, pvs))
288288+ end
289289+ | Bind (m, f) ->
290290+ let index, pv_index = generate (size - 1) input m in
291291+ let a, pv = generate (size - 1) input (f index) in
292292+ a, (fun ppf () -> pp ppf "(%a) => %a" pv_index () pv ())
293293+ | Option gen ->
294294+ if size < 1 then
295295+ None, fun ppf () -> pp ppf "None"
296296+ else if read_bool input then
297297+ let v, pv = generate size input gen in
298298+ Some v, fun ppf () -> pp ppf "Some (%a)" pv ()
299299+ else
300300+ None, fun ppf () -> pp ppf "None"
301301+ | List gen ->
302302+ let elems = generate_list size input gen in
303303+ List.map fst elems,
304304+ fun ppf () -> pp_list (fun ppf (_, pv) -> pv ppf ()) ppf elems
305305+ | List1 gen ->
306306+ let elems = generate_list1 size input gen in
307307+ List.map fst elems,
308308+ fun ppf () -> pp_list (fun ppf (_, pv) -> pv ppf ()) ppf elems
309309+ | Array gen ->
310310+ let elems = generate_list size input gen in
311311+ let elems = Array.of_list elems in
312312+ Array.map fst elems, fun ppf () -> pp_array (fun ppf (_, pv) -> pv ppf ()) ppf elems
313313+ | Array1 gen ->
314314+ let elems = generate_list1 size input gen in
315315+ let elems = Array.of_list elems in
316316+ Array.map fst elems, fun ppf () -> pp_array (fun ppf (_, pv) -> pv ppf ()) ppf elems
317317+ | Primitive gen ->
318318+ gen input, fun ppf () -> pp ppf "?"
319319+ | Unlazy gen ->
320320+ generate size input (Lazy.force gen)
321321+ | Print (ppv, gen) ->
322322+ let v, _ = generate size input gen in
323323+ v, fun ppf () -> ppv ppf v
324324+325325+and generate_list : type a . int -> state -> a gen -> (a * unit printer) list =
326326+ fun size input gen ->
327327+ if size <= 1 then []
328328+ else if read_bool input then
329329+ generate_list1 size input gen
330330+ else
331331+ []
332332+333333+and generate_list1 : type a . int -> state -> a gen -> (a * unit printer) list =
334334+ fun size input gen ->
335335+ let ans = generate (size/2) input gen in
336336+ ans :: generate_list (size/2) input gen
337337+338338+and gen_apply :
339339+ type k res . int -> state ->
340340+ (k, res) gens -> k ->
341341+ (res, exn * Printexc.raw_backtrace) result * unit printer =
342342+ fun size state gens f ->
343343+ let rec go :
344344+ type k res . int -> state ->
345345+ (k, res) gens -> k ->
346346+ (res, exn * Printexc.raw_backtrace) result * unit printer list =
347347+ fun size input gens -> match gens with
348348+ | [] -> fun x -> Ok x, []
349349+ | g :: gs -> fun f ->
350350+ let v, pv = generate size input g in
351351+ let res, pvs =
352352+ match f v with
353353+ | exception (BadTest _ as e) -> raise e
354354+ | exception e ->
355355+ Error (e, Printexc.get_raw_backtrace ()) , []
356356+ | fv -> go size input gs fv in
357357+ res, pv :: pvs in
358358+ let v, pvs = go size state gens f in
359359+ let pvs = fun ppf () ->
360360+ match pvs with
361361+ | [pv] ->
362362+ pv ppf ()
363363+ | pvs ->
364364+ pp_list (fun ppf pv -> pv ppf ()) ppf pvs in
365365+ v, pvs
366366+367367+368368+let fail s = raise (FailedTest (fun ppf () -> pp ppf "%s" s))
369369+370370+let failf format =
371371+ Format.kasprintf fail format
372372+373373+let check = function
374374+ | true -> ()
375375+ | false -> raise (FailedTest (fun ppf () -> pp ppf "check false"))
376376+377377+let check_eq ?pp:pv ?cmp ?eq a b =
378378+ let pass = match eq, cmp with
379379+ | Some eq, _ -> eq a b
380380+ | None, Some cmp -> cmp a b = 0
381381+ | None, None ->
382382+ Stdlib.compare a b = 0 in
383383+ if pass then
384384+ ()
385385+ else
386386+ raise (FailedTest (fun ppf () ->
387387+ match pv with
388388+ | None -> pp ppf "different"
389389+ | Some pv -> pp ppf "@[<hv>%a@ !=@ %a@]" pv a pv b))
390390+391391+let () = Printexc.record_backtrace true
392392+393393+type test = Test : string * ('f, unit) gens * 'f -> test
394394+395395+type test_status =
396396+ | TestPass of unit printer
397397+ | BadInput of string
398398+ | GenFail of exn * Printexc.raw_backtrace * unit printer
399399+ | TestExn of exn * Printexc.raw_backtrace * unit printer
400400+ | TestFail of unit printer * unit printer
401401+402402+let run_once (gens : (_, unit) gens) f state =
403403+ match gen_apply 100 state gens f with
404404+ | Ok (), pvs -> TestPass pvs
405405+ | Error (FailedTest p, _), pvs -> TestFail (p, pvs)
406406+ | Error (e, bt), pvs -> TestExn (e, bt, pvs)
407407+ | exception (BadTest s) -> BadInput s
408408+ | exception (GenFailed (e, bt, pvs)) -> GenFail (e, bt, pvs)
409409+410410+let classify_status = function
411411+ | TestPass _ -> `Pass
412412+ | BadInput _ -> `Bad
413413+ | GenFail _ -> `Fail (* slightly dubious... *)
414414+ | TestExn _ | TestFail _ -> `Fail
415415+416416+let print_status ppf status =
417417+ let print_ex ppf (e, bt) =
418418+ pp ppf "%s" (Printexc.to_string e);
419419+ bt
420420+ |> Printexc.raw_backtrace_to_string
421421+ |> Str.split (Str.regexp "\n")
422422+ |> List.iter (pp ppf "@,%s") in
423423+ match status with
424424+ | TestPass pvs ->
425425+ pp ppf "When given the input:@.@[<v 4>@,%a@,@]@.the test passed."
426426+ pvs ()
427427+ | BadInput s ->
428428+ pp ppf "The testcase was invalid:@.%s" s
429429+ | GenFail (e, bt, pvs) ->
430430+ pp ppf "When given the input:@.@[<4>%a@]@.the testcase generator threw an exception:@.@[<v 4>@,%a@,@]"
431431+ pvs ()
432432+ print_ex (e, bt)
433433+ | TestExn (e, bt, pvs) ->
434434+ pp ppf "When given the input:@.@[<v 4>@,%a@,@]@.the test threw an exception:@.@[<v 4>@,%a@,@]"
435435+ pvs ()
436436+ print_ex (e, bt)
437437+ | TestFail (err, pvs) ->
438438+ pp ppf "When given the input:@.@[<v 4>@,%a@,@]@.the test failed:@.@[<v 4>@,%a@,@]"
439439+ pvs ()
440440+ err ()
441441+442442+let prng_state_of_seed seed =
443443+ (* try to make this independent of word size *)
444444+ let seed = Int64.( [|
445445+ to_int (logand (of_int 0xffff) seed);
446446+ to_int (logand (of_int 0xffff) (shift_right seed 16));
447447+ to_int (logand (of_int 0xffff) (shift_right seed 32));
448448+ to_int (logand (of_int 0xffff) (shift_right seed 48)) |]) in
449449+ Random.State.make seed
450450+let src_of_seed seed =
451451+ Random (prng_state_of_seed seed)
452452+453453+let run_test ~mode ~silent ?(verbose=false) (Test (name, gens, f)) =
454454+ let show_status_line ?(clear=false) stat =
455455+ Printf.printf "%s: %s\n" name stat;
456456+ if clear then print_newline ();
457457+ flush stdout in
458458+ let ppf = Format.std_formatter in
459459+ if not silent && Unix.isatty Unix.stdout then
460460+ show_status_line ~clear:false "....";
461461+ let status = match mode with
462462+ | `Once state ->
463463+ run_once gens f state
464464+ | `Repeat (iters, seedseed) ->
465465+ let worst_status = ref (TestPass (fun _ () -> ())) in
466466+ let npass = ref 0 in
467467+ let nbad = ref 0 in
468468+ let seedsrc = prng_state_of_seed seedseed in
469469+ while !npass < iters && classify_status !worst_status = `Pass do
470470+ let seed = Random.State.int64 seedsrc Int64.max_int in
471471+ let state = { chan = src_of_seed seed;
472472+ buf = Bytes.make 256 '0';
473473+ offset = 0; len = 0 } in
474474+ let status = run_once gens f state in
475475+ begin match classify_status status with
476476+ | `Pass -> incr npass
477477+ | `Bad -> incr nbad
478478+ | `Fail ->
479479+ worst_status := status
480480+ end;
481481+ done;
482482+ let status = !worst_status in
483483+ status in
484484+ if silent && verbose && classify_status status = `Fail then begin
485485+ show_status_line
486486+ ~clear:true "FAIL";
487487+ pp ppf "%a@." print_status status;
488488+ end;
489489+ if not silent then begin
490490+ match classify_status status with
491491+ | `Pass ->
492492+ show_status_line
493493+ ~clear:true "PASS";
494494+ if verbose then pp ppf "%a@." print_status status
495495+ | `Fail ->
496496+ show_status_line
497497+ ~clear:true "FAIL";
498498+ pp ppf "%a@." print_status status;
499499+ | `Bad ->
500500+ show_status_line
501501+ ~clear:true "BAD";
502502+ pp ppf "%a@." print_status status;
503503+ end;
504504+ status
505505+506506+exception TestFailure
507507+let run_all_tests seed repeat file verbosity infinity tests =
508508+ match file with
509509+ | None ->
510510+ let seed = match seed with
511511+ | Some seed -> seed
512512+ | None -> Random.int64 (Int64.max_int)
513513+ in
514514+ if infinity then
515515+ (* infinite QuickCheck mode *)
516516+ let rec go ntests alltests tests = match tests with
517517+ | [] ->
518518+ go ntests alltests alltests
519519+ | t :: rest ->
520520+ if ntests mod 10000 = 0 then Printf.eprintf "\r%d%!" ntests;
521521+ let chan = src_of_seed seed in
522522+ let state = { chan ; buf = Bytes.make 256 '0'; offset = 0; len = 0 } in
523523+ match classify_status (run_test ~mode:(`Once state) ~silent:true ~verbose:true t) with
524524+ | `Fail -> Printf.printf "%d tests passed before first failure\n%!" ntests
525525+ | _ -> go (ntests + 1) alltests rest in
526526+ let () = go 0 tests tests in
527527+ 1
528528+ else
529529+ (* limited-run QuickCheck mode *)
530530+ let failures = ref 0 in
531531+ let () = tests |> List.iter (fun t ->
532532+ match (run_test ~mode:(`Repeat (repeat, seed)) ~silent:false t |> classify_status) with
533533+ | `Fail -> failures := !failures + 1
534534+ | _ -> ()
535535+ )
536536+ in
537537+ !failures
538538+ | Some file ->
539539+ (* AFL mode *)
540540+ let verbose = List.length verbosity > 0 in
541541+ let () = AflPersistent.run (fun () ->
542542+ let fd = Unix.openfile file [Unix.O_RDONLY] 0o000 in
543543+ let state = { chan = Fd fd; buf = Bytes.make 256 '0';
544544+ offset = 0; len = 0 } in
545545+ let status =
546546+ try run_test ~mode:(`Once state) ~silent:false ~verbose @@
547547+ List.nth tests (choose_int (List.length tests) state)
548548+ with
549549+ BadTest s -> BadInput s
550550+ in
551551+ Unix.close fd;
552552+ match classify_status status with
553553+ | `Pass | `Bad -> ()
554554+ | `Fail ->
555555+ Printexc.record_backtrace false;
556556+ raise TestFailure)
557557+ in
558558+ 0 (* failures come via the exception mechanism above *)
559559+560560+let last_generated_name = ref 0
561561+let generate_name () =
562562+ incr last_generated_name;
563563+ "test" ^ string_of_int !last_generated_name
564564+565565+let registered_tests = ref []
566566+567567+let add_test ?name gens f =
568568+ let name = match name with
569569+ | None -> generate_name ()
570570+ | Some name -> name in
571571+ registered_tests := Test (name, gens, f) :: !registered_tests
572572+573573+(* cmdliner stuff *)
574574+575575+let randomness_file =
576576+ let doc = "A file containing some bytes, consulted in constructing test cases. \
577577+ When `afl-fuzz` is calling the test binary, use `@@` to indicate that \
578578+ `afl-fuzz` should put its test case here \
579579+ (e.g. `afl-fuzz -i input -o output ./my_crowbar_test @@`). Re-run a test by \
580580+ supplying the test file here \
581581+ (e.g. `./my_crowbar_test output/crashes/id:000000`). If no file is \
582582+ specified, the test will use OCaml's Random module as a source of \
583583+ randomness for a predefined number of rounds." in
584584+ Cmdliner.Arg.(value & pos 0 (some file) None & info [] ~doc ~docv:"FILE")
585585+586586+let seed =
587587+ let doc = "The seed (an int64) for the PRNG. Use as an alternative to FILE
588588+ when running in non-AFL (quickcheck) mode." in
589589+ Cmdliner.Arg.(value & opt (some int64) None & info ["s"; "seed"] ~doc ~docv:"SEED")
590590+591591+let repeat =
592592+ let doc = "The number of times to repeat the test in quick-check." in
593593+ Cmdliner.Arg.(value & opt int 5000 & info ["r"; "repeat"] ~doc ~docv:"REPEAT")
594594+595595+let verbosity =
596596+ let doc = "Print information on each test as it's conducted." in
597597+ Cmdliner.Arg.(value & flag_all & info ["v"; "verbose"] ~doc ~docv:"VERBOSE")
598598+599599+let infinity =
600600+ let doc = "In non-AFL (quickcheck) mode, continue running until a test failure is \
601601+ discovered. No attempt is made to track which tests have already been run, \
602602+ so some tests may be repeated, and if there are no failures reachable, the \
603603+ test will never terminate without outside intervention." in
604604+ Cmdliner.Arg.(value & flag & info ["i"] ~doc ~docv:"INFINITE")
605605+606606+let crowbar_info = Cmdliner.Cmd.info @@ Filename.basename Sys.argv.(0)
607607+608608+let () =
609609+ at_exit (fun () ->
610610+ let t = !registered_tests in
611611+ registered_tests := [];
612612+ match t with
613613+ | [] -> ()
614614+ | t ->
615615+ let cmd = Cmdliner.Term.(const run_all_tests $ seed $ repeat $ randomness_file $ verbosity $
616616+ infinity $ const (List.rev t)) in
617617+ exit @@ Cmdliner.Cmd.eval' ~catch:false (Cmdliner.Cmd.v crowbar_info cmd)
618618+ )
619619+620620+module Syntax = struct
621621+ let ( let* ) = dynamic_bind
622622+ let ( let+ ) gen map_fn = map [ gen ] map_fn
623623+ let ( and+ ) = pair
624624+end
+276
src/crowbar.mli
···11+(** {1:top Types } *)
22+33+type 'a gen
44+(** ['a gen] knows how to generate ['a] for use in Crowbar tests. *)
55+66+type ('k, 'res) gens =
77+ | [] : ('res, 'res) gens
88+ | (::) : 'a gen * ('k, 'res) gens -> ('a -> 'k, 'res) gens
99+(** multiple generators are passed to functions using a listlike syntax.
1010+ for example, [map [int; int] (fun a b -> a + b)] *)
1111+1212+type 'a printer = Format.formatter -> 'a -> unit
1313+(** pretty-printers for items generated by Crowbar; useful for the user in
1414+ translating test failures into bugfixes. *)
1515+1616+(**/**)
1717+(* re-export stdlib's list
1818+ We only want to override [] syntax in the argument to Map *)
1919+type nonrec +'a list = 'a list = [] | (::) of 'a * 'a list
2020+(**/**)
2121+2222+(** {1:generators Generators } *)
2323+2424+(** {2:simple_generators Simple Generators } *)
2525+2626+val int : int gen
2727+(** [int] generates an integer ranging from min_int to max_int, inclusive.
2828+ If you need integers from a smaller domain, consider using {!range}. *)
2929+3030+val uint8 : int gen
3131+(** [uint8] generates an unsigned byte, ranging from 0 to 255 inclusive. *)
3232+3333+val int8 : int gen
3434+(** [int8] generates a signed byte, ranging from -128 to 127 inclusive. *)
3535+3636+val uint16 : int gen
3737+(** [uint16] generates an unsigned 16-bit integer,
3838+ ranging from 0 to 65535 inclusive. *)
3939+4040+val int16 : int gen
4141+(** [int16] generates a signed 16-bit integer,
4242+ ranging from -32768 to 32767 inclusive. *)
4343+4444+val int32 : Int32.t gen
4545+(** [int32] generates a 32-bit signed integer. *)
4646+4747+val int64 : Int64.t gen
4848+(** [int64] generates a 64-bit signed integer. *)
4949+5050+val float : float gen
5151+(** [float] generates a double-precision floating-point number. *)
5252+5353+val char : char gen
5454+(** [char] generates a char. *)
5555+5656+val uchar : Uchar.t gen
5757+(** [uchar] generates a Unicode scalar value *)
5858+5959+val bytes : string gen
6060+(** [bytes] generates a string of arbitrary length (including zero-length strings). *)
6161+6262+val bytes_fixed : int -> string gen
6363+(** [bytes_fixed length] generates a string of the specified length. *)
6464+6565+val bool : bool gen
6666+(** [bool] generates a yes or no answer. *)
6767+6868+val range : ?min:int -> int -> int gen
6969+(** [range ?min n] is a generator for integers between [min] (inclusive)
7070+ and [min + n] (exclusive). Default [min] value is 0.
7171+ [range ?min n] will raise [Invalid_argument] for [n <= 0].
7272+*)
7373+7474+(** {2:generator_functions Functions on Generators } *)
7575+7676+val map : ('f, 'a) gens -> 'f -> 'a gen
7777+(** [map gens map_fn] provides a means for creating generators using other
7878+ generators' output. For example, one might generate a Char.t from a
7979+ {!uint8}:
8080+ {[
8181+ open Crowbar
8282+ let char_gen : Char.t gen = map [uint8] Char.chr
8383+ ]}
8484+*)
8585+8686+val unlazy : 'a gen Lazy.t -> 'a gen
8787+(** [unlazy gen] forces the generator [gen]. It is useful when defining
8888+ generators for recursive data types:
8989+9090+ {[
9191+ open Crowbar
9292+ type a = A of int | Self of a
9393+ let rec a_gen = lazy (
9494+ choose [
9595+ map [int] (fun i -> A i);
9696+ map [(unlazy a_gen)] (fun s -> Self s);
9797+ ])
9898+ let lazy a_gen = a_gen
9999+ ]}
100100+*)
101101+102102+val fix : ('a gen -> 'a gen) -> 'a gen
103103+(** [fix fn] applies the function [fn]. It is useful when defining generators
104104+ for recursive data types:
105105+106106+ {[
107107+ open Crowbar
108108+ type a = A of int | Self of a
109109+ let rec a_gen = fix (fun a_gen ->
110110+ choose [
111111+ map [int] (fun i -> A i);
112112+ map [a_gen] (fun s -> Self s);
113113+ ])
114114+ ]}
115115+ *)
116116+117117+val const : 'a -> 'a gen
118118+(** [const a] always generates [a]. *)
119119+120120+val choose : 'a gen list -> 'a gen
121121+(** [choose gens] chooses a generator arbitrarily from [gens]. *)
122122+123123+val option : 'a gen -> 'a option gen
124124+(** [option gen] generates either [None] or [Some x], where [x] is the item
125125+ generated by [gen]. *)
126126+127127+val pair : 'a gen -> 'b gen -> ('a * 'b) gen
128128+(** [pair gena gen] generates (a, b)
129129+ where [a] is generated by [gena] and [b] by [genb]. *)
130130+131131+val result : 'a gen -> 'b gen -> ('a, 'b) result gen
132132+(** [result gena genb] generates either [Ok va] or [Error vb],
133133+ where [va], [vb] are generated by [gena], [genb] respectively. *)
134134+135135+val list : 'a gen -> 'a list gen
136136+(** [list gen] makes a generator for lists using [gen]. Lists may be empty; for
137137+ non-empty lists, use {!list1}. *)
138138+139139+val list1 : 'a gen -> 'a list gen
140140+(** [list1 gen] makes non-empty list generators. For potentially empty lists,
141141+ use {!list}.*)
142142+143143+val array : 'a gen -> 'a array gen
144144+(** [array gen] makes a generator for arrays using [gen]. Arrays may be empty; for
145145+ non-empty arrays, use {!array1}. *)
146146+147147+val array1 : 'a gen -> 'a array gen
148148+(** [array1 gen] makes non-empty array generators. For potentially empty arrays,
149149+ use {!array}.*)
150150+151151+val shuffle : 'a list -> 'a list gen
152152+(** [shuffle l] generates random permutations of [l]. *)
153153+154154+val concat_gen_list : string gen -> string gen list -> string gen
155155+(** [concat_gen_list sep l] concatenates a list of string gen [l] inserting the
156156+ separator [sep] between each *)
157157+158158+val with_printer : 'a printer -> 'a gen -> 'a gen
159159+(** [with_printer printer gen] generates the same values as [gen]. If [gen]
160160+ is used to create a failing test case and the test was reached by
161161+ calling [check_eq] without [pp] set, [printer] will be used to print the
162162+ failing test case. *)
163163+164164+val dynamic_bind : 'a gen -> ('a -> 'b gen) -> 'b gen
165165+(** [dynamic_bind gen f] is a monadic bind, it allows to express the
166166+ generation of a value whose generator itself depends on
167167+ a previously generated value. This is in contrast with [map gen f],
168168+ where no further generation happens in [f] after [gen] has
169169+ generated an element.
170170+171171+ An typical example where this sort of dependencies is required is
172172+ a serialization library exporting combinators letting you build
173173+ values of the form ['a serializer]. You may want to test this
174174+ library by first generating a pair of a serializer and generator
175175+ ['a serializer * 'a gen] for arbitrary ['a], and then generating
176176+ values of type ['a] depending on the (generated) generator to test
177177+ the serializer. There is such an example in the
178178+ [examples/serializer/] directory of the Crowbar implementation.
179179+180180+ Because the structure of a generator built with [dynamic_bind] is
181181+ opaque/dynamic (it depends on generated values), the Crowbar
182182+ library cannot analyze its statically
183183+ (without generating anything) -- the generator is opaque to the
184184+ library, hidden in a function. In particular, many optimizations or
185185+ or fuzzing techniques based on generator analysis are
186186+ impossible. As a client of the library, you should avoid
187187+ [dynamic_bind] whenever it is not strictly required to express
188188+ a given generator, so that you can take advantage of these features
189189+ (present or future ones). Use the least powerful/complex
190190+ combinators that suffice for your needs.
191191+*)
192192+193193+(** {1:printing Printing } *)
194194+195195+(* Format.fprintf, renamed *)
196196+val pp : Format.formatter -> ('a, Format.formatter, unit) format -> 'a
197197+val pp_int : int printer
198198+val pp_int32 : Int32.t printer
199199+val pp_int64 : Int64.t printer
200200+val pp_float : float printer
201201+val pp_bool : bool printer
202202+val pp_string : string printer
203203+val pp_list : 'a printer -> 'a list printer
204204+val pp_option : 'a printer -> 'a option printer
205205+206206+(** {1:testing Testing} *)
207207+208208+val add_test :
209209+ ?name:string -> ('f, unit) gens -> 'f -> unit
210210+(** [add_test name generators test_fn] adds [test_fn] to the list of eligible
211211+ tests to be run when the program is invoked. At runtime, random data will
212212+ be sent to [generators] to create the input necessary to run [test_fn]. Any
213213+ failures will be printed annotated with [name]. *)
214214+215215+(** {2:aborting Aborting Tests} *)
216216+217217+val guard : bool -> unit
218218+(** [guard b] aborts a test if [b] is false. The test will not be recorded
219219+ or reported as a failure. *)
220220+221221+val bad_test : unit -> 'a
222222+(** [bad_test ()] aborts a test. The test will not be recorded or reported
223223+ as a failure. *)
224224+225225+val nonetheless : 'a option -> 'a
226226+(** [nonetheless o] aborts a test if [o] is None. The test will not be recorded
227227+ or reported as a failure. *)
228228+229229+(** {2:failing Failing} *)
230230+231231+val fail : string -> 'a
232232+(** [fail message] generates a test failure and prints [message]. *)
233233+234234+val failf : ('a, Format.formatter, unit, _) format4 -> 'a
235235+(** [failf format ...] generates a test failure and prints the message
236236+ specified by the format string [format] and the following arguments.
237237+ It is set up so that [%a] calls for an ['a printer] and an ['a] value. *)
238238+239239+(** {2:asserting Asserting Properties} *)
240240+241241+val check : bool -> unit
242242+(** [check b] generates a test failure if [b] is false. No useful information
243243+ will be printed in this case. *)
244244+245245+val check_eq : ?pp:('a printer) -> ?cmp:('a -> 'a -> int) -> ?eq:('a -> 'a -> bool) ->
246246+ 'a -> 'a -> unit
247247+(** [check_eq pp cmp eq x y] evaluates whether x and y are equal, and if they
248248+ are not, raises a failure and prints an error message.
249249+ Equality is evaluated as follows:
250250+251251+ {ol
252252+ {- use a provided [eq]}
253253+ {- if no [eq] is provided, use a provided [cmp]}
254254+ {- if neither [eq] nor [cmp] is provided, use Stdlib.compare}}
255255+256256+ If [pp] is provided, use this to print [x] and [y] if they are not equal.
257257+ If [pp] is not provided, a best-effort printer will be generated from the
258258+ printers for primitive generators and any printers registered with
259259+ [with_printer] and used. *)
260260+261261+262262+(** {1:syntax Syntax module } *)
263263+module Syntax : sig
264264+ val ( let+ ) : 'a gen -> ('a -> 'b) -> 'b gen
265265+ (** [let+ x = gen in e] is equivalent to [map [ gen ] (fun x -> e)]. *)
266266+267267+ val ( let* ) : 'a gen -> ('a -> 'b gen) -> 'b gen
268268+ (** Equivalent to {!dynamic_bind}.
269269+ [let* x = gen in e] is equivalent to [dynamic_bind gen (fun x -> e)]. *)
270270+271271+ val ( and+ ) : 'a gen -> 'b gen -> ('a * 'b) gen
272272+ (** Equivalent to {!pair}.
273273+ [let+ x = gen_x and+ y = gen_y and+ z = gen_z in e]
274274+ is equivalent to
275275+ [ map [pair (pair gen_x gen_y) gen_z)] (fun ((x, y), z) -> e) ]. *)
276276+end