Codebase list ocaml-qcheck / 2574c45
New upstream version 0.13 Stephane Glondu authored 3 years ago Stéphane Glondu committed 3 years ago
10 changed file(s) with 172 addition(s) and 30 deletion(s). Raw diff Collapse all Expand all
00 # Changes
1
2 ## 0.13
3
4 - make counter private
5 - Add debug shrinking log
6 - fix: small fix related to stdlib/pervasives
7 - feat: add flatten combinators in `gen`
18
29 ## 0.12
310
1919 @dune exec example/ounit/QCheck_ounit_test.exe
2020
2121 example-runner:
22 @dune exec example/QCheck_runner_test.exe
22 @dune exec example/QCheck_runner_test.exe -- --debug-shrink=log.tmp
2323
2424 example-alcotest:
2525 @dune exec example/alcotest/QCheck_alcotest_test.exe
33 homepage: "https://github.com/c-cube/qcheck/"
44 synopsis: "Alcotest backend for qcheck"
55 doc: ["http://c-cube.github.io/qcheck/"]
6 version: "0.12"
6 version: "0.13"
77 tags: [
88 "test"
99 "quickcheck"
33 homepage: "https://github.com/c-cube/qcheck/"
44 synopsis: "Core qcheck library"
55 doc: ["http://c-cube.github.io/qcheck/"]
6 version: "0.12"
6 version: "0.13"
77 tags: [
88 "test"
99 "property"
33 homepage: "https://github.com/c-cube/qcheck/"
44 doc: ["http://c-cube.github.io/qcheck/"]
55 synopsis: "OUnit backend for qcheck"
6 version: "0.12"
6 version: "0.13"
77 tags: [
88 "qcheck"
99 "quickcheck"
33 synopsis: "Compatibility package for qcheck"
44 homepage: "https://github.com/c-cube/qcheck/"
55 doc: ["http://c-cube.github.io/qcheck/"]
6 version: "0.12"
6 version: "0.13"
77 tags: [
88 "test"
99 "property"
55
66 (** {1 Quickcheck inspired property-based testing} *)
77
8 let poly_compare=compare
89 open Printf
910
1011 module RS = Random.State
197198 let array gen st = array_size nat gen st
198199 let array_repeat n g = array_size (return n) g
199200
201 let flatten_l l st = List.map (fun f->f st) l
202 let flatten_a a st = Array.map (fun f->f st) a
203 let flatten_opt o st =
204 match o with
205 | None -> None
206 | Some f -> Some (f st)
207 let flatten_res r st =
208 match r with
209 | Ok f -> Ok (f st)
210 | Error e -> Error e
211
200212 let shuffle_a a st =
201213 for i = Array.length a-1 downto 1 do
202214 let j = Random.State.int st (i+1) in
216228 (float_bound_inclusive 1. st ** (1. /. fl_w), v)
217229 in
218230 let samples = List.rev_map sample l in
219 List.sort (fun (w1, _) (w2, _) -> compare w1 w2) samples |> List.rev_map snd
231 List.sort (fun (w1, _) (w2, _) -> poly_compare w1 w2) samples |> List.rev_map snd
220232
221233 let pair g1 g2 st = (g1 st, g2 st)
222234
11681180 (* all counter-examples in [l] have same size according to [small],
11691181 so we just compare to the first one, and we enforce
11701182 the invariant *)
1171 begin match Pervasives.compare (small instance) (small c_ex'.instance) with
1183 begin match poly_compare (small instance) (small c_ex'.instance) with
11721184 | 0 -> res.state <- Failed {instances=c_ex :: l} (* same size: add [c_ex] to [l] *)
11731185 | n when n<0 -> res.state <- Failed {instances=[c_ex]} (* drop [l] *)
11741186 | _ -> () (* drop [c_ex], not small enough *)
15711583 let median = ref 0 in
15721584 let median_num = ref 0 in (* how many values have we seen yet? once >= !n/2 we set median *)
15731585 (Hashtbl.fold (fun i cnt acc -> (i,cnt)::acc) tbl [])
1574 |> List.sort (fun (i,_) (j,_) -> compare i j)
1586 |> List.sort (fun (i,_) (j,_) -> poly_compare i j)
15751587 |> List.iter
15761588 (fun (i,cnt) ->
15771589 if !median_num < !num/2 then (
164164
165165 val (<$>) : ('a -> 'b) -> 'a t -> 'b t
166166 (** An infix synonym for {!map}
167 @since NEXT_RELEASE *)
167 @since 0.13 *)
168168
169169 val oneof : 'a t list -> 'a t
170170 (** Constructs a generator that selects among a given list of generators. *)
321321 val char_range : char -> char -> char t
322322 (** Generates chars between the two bounds, inclusive.
323323 Example: [char_range 'a' 'z'] for all lower case ascii letters.
324 @since NEXT_RELEASE *)
324 @since 0.13 *)
325325
326326 val string_size : ?gen:char t -> int t -> string t
327327 (** Builds a string generator from a (non-negative) size generator.
348348 val small_list : 'a t -> 'a list t
349349 (** Generates lists of small size (see {!small_nat}).
350350 @since 0.5.3 *)
351
352 val flatten_l : 'a t list -> 'a list t
353 (** Generate a list of elements from individual generators
354 @since 0.13 *)
355
356 val flatten_a : 'a t array -> 'a array t
357 (** Generate an array of elements from individual generators
358 @since 0.13 *)
359
360 val flatten_opt : 'a t option -> 'a option t
361 (** Generate an option from an optional generator
362 @since 0.13 *)
363
364 val flatten_res : ('a t, 'e) result -> ('a,'e) result t
365 (** Generate a result from [Ok g], an error from [Error e]
366 @since 0.13 *)
351367
352368 val small_array : 'a t -> 'a array t
353369 (** Generates arrays of small size (see {!small_nat}).
9393 let r = ref false in
9494 (fun () -> !r), (fun b -> r := b)
9595
96 let debug_shrink, set_debug_shrink =
97 let r = ref None in
98 (fun () -> !r), (fun s -> r := Some (open_out s))
99
100 let debug_shrink_list, set_debug_shrink_list =
101 let r = ref [] in
102 (fun () -> !r), (fun b -> r := b :: !r)
103
96104 module Raw = struct
97105 type ('b,'c) printer = {
98106 info: 'a. ('a,'b,'c,unit) format4 -> 'a;
107115 cli_rand : Random.State.t;
108116 cli_slow_test : int; (* how many slow tests to display? *)
109117 cli_colors: bool;
118 cli_debug_shrink : out_channel option;
119 cli_debug_shrink_list : string list;
110120 }
111121
112122 (* main callback for individual tests
164174 ; "--seed", Arg.Set_int seed, " set random seed (to repeat tests)"
165175 ; "--long", Arg.Unit set_long_tests, " run long tests"
166176 ; "-bt", Arg.Unit set_backtraces, " enable backtraces"
177 ; "--debug-shrink", Arg.String set_debug_shrink, " enable shrinking debug to <file>"
178 ; "--debug-shrink-list", Arg.String set_debug_shrink_list, " filter test to debug shrinking on"
167179 ]
168180 ) in
169181 Arg.parse_argv argv options (fun _ ->()) "run qtest suite";
170182 let cli_rand = setup_random_state_ () in
171183 { cli_verbose=verbose(); cli_long_tests=long_tests(); cli_rand;
172184 cli_print_list= !print_list; cli_slow_test= !slow;
173 cli_colors= !colors; }
185 cli_colors= !colors; cli_debug_shrink = debug_shrink();
186 cli_debug_shrink_list = debug_shrink_list(); }
174187 end
175188
176189 open Raw
188201 type res =
189202 | Res : 'a QCheck.Test.cell * 'a QCheck.TestResult.t -> res
190203
204 type handler = {
205 handler : 'a. 'a QCheck.Test.handler;
206 }
207
208 type handler_gen =
209 colors:bool ->
210 debug_shrink:(out_channel option) ->
211 debug_shrink_list:(string list) ->
212 size:int -> out:out_channel -> verbose:bool -> counter -> handler
213
191214 let pp_counter ~size out c =
192215 let t = Unix.gettimeofday () -. c.start in
193216 Printf.fprintf out "%*d %*d %*d %*d / %*d %7.1fs"
194217 size c.gen size c.errored size c.failed
195218 size c.passed size c.expected t
196219
197 let handler ~size ~out ~verbose c name _ r =
198 let st = function
199 | QCheck.Test.Generating -> "generating"
200 | QCheck.Test.Collecting _ -> "collecting"
201 | QCheck.Test.Testing _ -> " testing"
202 | QCheck.Test.Shrunk (i, _) ->
203 Printf.sprintf "shrinking: %4d" i
204 | QCheck.Test.Shrinking (i, j, _) ->
205 Printf.sprintf "shrinking: %4d.%04d" i j
220 let debug_shrinking_counter_example cell out x =
221 match (QCheck.Test.get_arbitrary cell).QCheck.print with
222 | None -> Printf.fprintf out "<no printer provided>"
223 | Some print -> Printf.fprintf out "%s" (print x)
224
225 let debug_shrinking_size cell out x =
226 match (QCheck.Test.get_arbitrary cell).QCheck.small with
227 | None -> ()
228 | Some f -> Printf.fprintf out ", size %d" (f x)
229
230 let debug_shrinking_choices_aux ~colors out name i cell x =
231 Printf.fprintf out "\n~~~ %a %s\n\n"
232 (Color.pp_str_c ~colors `Cyan) "Shrink" (String.make 69 '~');
233 Printf.fprintf out
234 "Test %s sucessfully shrunk counter example (step %d%a) to:\n\n%a\n%!"
235 name i
236 (debug_shrinking_size cell) x
237 (debug_shrinking_counter_example cell) x
238
239 let debug_shrinking_choices
240 ~colors ~debug_shrink ~debug_shrink_list name cell i x =
241 match debug_shrink with
242 | None -> ()
243 | Some out ->
244 begin match debug_shrink_list with
245 | [] ->
246 debug_shrinking_choices_aux ~colors out name i cell x
247 | l when List.mem name l ->
248 debug_shrinking_choices_aux ~colors out name i cell x
249 | _ -> ()
250 end
251
252
253 let default_handler
254 ~colors ~debug_shrink ~debug_shrink_list
255 ~size ~out ~verbose c =
256 let handler name cell r =
257 let st = function
258 | QCheck.Test.Generating -> "generating"
259 | QCheck.Test.Collecting _ -> "collecting"
260 | QCheck.Test.Testing _ -> " testing"
261 | QCheck.Test.Shrunk (i, _) ->
262 Printf.sprintf "shrinking: %4d" i
263 | QCheck.Test.Shrinking (i, j, _) ->
264 Printf.sprintf "shrinking: %4d.%04d" i j
265 in
266 (* debug shrinking choices *)
267 begin match r with
268 | QCheck.Test.Shrunk (i, x) ->
269 debug_shrinking_choices
270 ~colors ~debug_shrink ~debug_shrink_list name cell i x
271 | _ ->
272 ()
273 end;
274 (* use timestamps for rate-limiting *)
275 let now=Unix.gettimeofday() in
276 if verbose && now -. !last_msg > get_time_between_msg () then (
277 last_msg := now;
278 Printf.fprintf out "%s[ ] %a %s (%s)%!"
279 Color.reset_line (pp_counter ~size) c name (st r)
280 )
206281 in
207 (* use timestamps for rate-limiting *)
208 let now=Unix.gettimeofday() in
209 if verbose && now -. !last_msg > get_time_between_msg () then (
210 last_msg := now;
211 Printf.fprintf out "%s[ ] %a %s (%s)%!"
212 Color.reset_line (pp_counter ~size) c name (st r)
213 )
214
282 { handler; }
215283
216284 let step ~size ~out ~verbose c name _ _ r =
217285 let aux = function
308376 print_messages ~colors out cell c_ex.QCheck.TestResult.msg_l
309377
310378 let run_tests
311 ?(colors=true) ?(verbose=verbose()) ?(long=long_tests()) ?(out=stdout) ?(rand=random_state()) l =
379 ?(handler=default_handler)
380 ?(colors=true) ?(verbose=verbose()) ?(long=long_tests())
381 ?(debug_shrink=debug_shrink()) ?(debug_shrink_list=debug_shrink_list())
382 ?(out=stdout) ?(rand=random_state()) l =
312383 let module T = QCheck.Test in
313384 let module R = QCheck.TestResult in
314385 let pp_color = Color.pp_str_c ~bold:true ~colors in
330401 Printf.fprintf out "%s[ ] %a %s%!"
331402 Color.reset_line (pp_counter ~size) c (T.get_name cell);
332403 let r = QCheck.Test.check_cell ~long ~rand
333 ~handler:(handler ~size ~out ~verbose c)
404 ~handler:(handler ~colors ~debug_shrink ~debug_shrink_list
405 ~size ~out ~verbose c).handler
334406 ~step:(step ~size ~out ~verbose c)
335407 ~call:(callback ~size ~out ~verbose ~colors c)
336408 cell
5757 (** Set the minimum tiem between messages.
5858 @since 0.9 *)
5959
60
61 (** {2 Event handlers} *)
62
63 type counter = private {
64 start : float;
65 expected : int;
66 mutable gen : int;
67 mutable passed : int;
68 mutable failed : int;
69 mutable errored : int;
70 }
71 (** The type of counter used to keep tracks of the events received for a given
72 test cell. *)
73
74 type handler = {
75 handler : 'a. 'a QCheck.Test.handler;
76 }
77 (** A type to represent polymorphic-enough handlers for test cells. *)
78
79 type handler_gen =
80 colors:bool ->
81 debug_shrink:(out_channel option) ->
82 debug_shrink_list:(string list) ->
83 size:int -> out:out_channel -> verbose:bool -> counter -> handler
84 (** An alias type to a generator of handlers for test cells. *)
85
86 val default_handler : handler_gen
87 (** The default handler used. *)
88
89
6090 (** {2 Run a Suite of Tests and Get Results} *)
6191
6292 val run_tests :
93 ?handler:handler_gen ->
6394 ?colors:bool -> ?verbose:bool -> ?long:bool ->
95 ?debug_shrink:(out_channel option) ->
96 ?debug_shrink_list:(string list) ->
6497 ?out:out_channel -> ?rand:Random.State.t ->
6598 QCheck.Test.t list -> int
6699 (** Run a suite of tests, and print its results. This is an heritage from
163196 cli_rand : Random.State.t;
164197 cli_slow_test : int; (* how many slow tests to display? *)
165198 cli_colors: bool;
199 cli_debug_shrink : out_channel option;
200 cli_debug_shrink_list : string list;
166201 }
167202
168203 val parse_cli : full_options:bool -> string array -> cli_args