March Docs

Check

Check module: property-test runner with integrated shrinking.

Runs a Gen.Generator(a)-produced value through a user-supplied property function a configurable number of times. When a property fails — whether by returning false, raising an assert failure, or crashing with any other runtime exception — the runner walks the failing input's shrink tree (integrated into the generator per Hedgehog's design) to find a minimal counterexample, then panics with a report that includes the shrunk value, the number of runs, and the seed for replay.

The existing test ... do ... end runner catches the panic and reports the property as a failing test. No compiler changes required beyond the __try_call builtin that catches runtime exceptions.

Typical use:

test "reverse is involution" do
  Check.all(Gen.list(Gen.int(-100, 100)), fn xs ->
    List.reverse(List.reverse(xs)) == xs
  )
end

Seed sources (highest priority first):

  1. CheckConfig.seed passed to Check.all_with
  2. MARCH_PROP_SEED environment variable (set by forge test --seed=N)
  3. current Unix time

Types

typeCheckConfigCheckConfig = { num_runs : Int, seed : Option(Int),#

Functions

fnallall(g, prop)#

Run a property with default_config() (100 runs). Returns () when the property holds on every generated value; otherwise shrinks the failing input and panics with a report (counterexample, reason, shrink steps, seed), which the test runner catches and reports. Does nothing when MARCH_SKIP_PROPERTIES=1. Random (seeded from MARCH_PROP_SEED or the clock), so not a REPL example:

Check.all(Gen.list(Gen.int(-100, 100)), fn xs ->
  List.reverse(List.reverse(xs)) == xs
)
fnall_withall_with(g, prop, config : CheckConfig)#

Run a property with an explicit config. Use to override the run count, seed, size cap or shrink budget. Run i (0-based) gets size hint i * max_size / (num_runs - 1) (just max_size when num_runs <= 1). On the first failing run the input's shrink tree is walked greedily (first failing child at each level, at most max_shrink_steps steps) and the call panics with the report described in all. Returns () if every run passes, or immediately if MARCH_SKIP_PROPERTIES=1. Seed: config.seed, else MARCH_PROP_SEED (falling back to the clock when unset or not an integer), else the current Unix time in seconds.

Check.all_with(gen, prop, { Check.default_config() with seed: Some(42) })
fndefault_configdefault_config() : CheckConfig#

Sensible defaults: 100 runs, a size hint growing linearly from 0 to 100 across the runs, up to 1000 shrink steps, and seed: None (so the seed comes from MARCH_PROP_SEED or the clock). Override fields with a record update.

Examples

march> let cfg = Check.default_config()
march> cfg.num_runs
100
march> let cfg = Check.default_config()
march> cfg.seed
None
march> let cfg = Check.default_config()
march> { cfg with num_runs: 10 }.num_runs
10
fntry_proptry_prop(prop : a -> Bool, value : a) : Result(Bool, String)#

Run a property on one value, catching any runtime failure. Returns: Ok(true) — property held Ok(false) — property returned false Err(msg) — property raised (assert, panic, match failure, etc.); msg is the runtime error text

Examples

march> Result.unwrap_or(Check.try_prop(fn x -> x > 0, 5), false)
true
march> Check.try_prop(fn x -> 10 / x > 0, 0)
Err("division by zero")