Skip to content

[qcheck-STM] Emit a function to shrink cmd arguments #269

Description

@jmid

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions