QCheck-STM will by default only shrink cmd lists.
In practice, this means that, e.g., the random argument -5 passed to Hashtbl.create (-5) will not be shrunk to the minimal Hashtbl.create (-1) before being printed.
To enable argument shrinking one has to write/emit a shrink_cmd function and pass it to arb_cmd.
Here's an example from multicoretests:
https://github.com/ocaml-multicore/multicoretests/blob/160394955526794ab5349ce683d9b7664d43bcb9/src/weak/stm_tests.ml#L33-L47
let shrink_cmd c = match c with
| Length -> Iter.empty
| Set (i, d_opt) -> Iter.map (fun i -> Set (i,d_opt)) (Shrink.int i)
| Get i -> Iter.map (fun i -> Get i) (Shrink.int i)
| Get_copy i -> Iter.map (fun i -> Get_copy i) (Shrink.int i)
| Check i -> Iter.map (fun i -> Check i) (Shrink.int i)
| Fill (i,j,d_opt) ->
Iter.(map (fun i -> Fill (i,j,d_opt)) (Shrink.int i)
<+>
map (fun j -> Fill (i,j,d_opt)) (Shrink.int j))
let arb_cmd s =
[...]
QCheck.make ~print:show_cmd ~shrink:shrink_cmd [...]
- When no shrinking is needed (e.g., if there are no arguments or only a unit arg) one can just return the empty iterator
Iter.empty
- When there's only one argument, one can use
Iter.map over a built-in shrinker combinator from QCheck, e.g., of int, char, string, ...
- When there's more than one argument, we should shrink each argument separately. The
Fill above case does this by composing two iterators with <+> (a shorthand for Iter.append). Why? Because a shrinker of i may return the empty iterator, if i is already minimal, e.g., 0 and we don't want the shrinker to only reduce when both i and j can be reduced. Their shrinking should be independent.
QCheck-STM will by default only shrink
cmd lists.In practice, this means that, e.g., the random argument
-5passed toHashtbl.create (-5)will not be shrunk to the minimalHashtbl.create (-1)before being printed.To enable argument shrinking one has to write/emit a
shrink_cmdfunction and pass it toarb_cmd.Here's an example from multicoretests:
https://github.com/ocaml-multicore/multicoretests/blob/160394955526794ab5349ce683d9b7664d43bcb9/src/weak/stm_tests.ml#L33-L47
Iter.emptyIter.mapover a built-in shrinker combinator from QCheck, e.g., ofint,char,string, ...Fillabove case does this by composing two iterators with<+>(a shorthand forIter.append). Why? Because a shrinker ofimay return the empty iterator, ifiis already minimal, e.g.,0and we don't want the shrinker to only reduce when bothiandjcan be reduced. Their shrinking should be independent.