Windtrap runs unit, property, stateful and expect tests from one flat interface, with coverage and mutation testing in the companion ppx. A suite is an ordinary executable that dune runtest builds and runs.
open Windtrap
let basics =
group "basics"
[
test "evicts the oldest entry when full" (fun () ->
let c = Lru.create 2 in
List.iter (fun k -> Lru.add c k k) [ 1; 2; 3 ];
equal (option int) None (Lru.find c 1));
test "lists its keys, most recent first" (fun () ->
let c = Lru.create 3 in
List.iter (fun k -> Lru.add c k k) [ 1; 2; 3 ];
ignore (Lru.find c 1);
expect (Lru.to_string c) @@ __POS_OF__ {|1 3 2|});
]
let bounded =
prop "never holds more than its capacity" Gen.(list int) (fun keys ->
let c = Lru.create 3 in
List.iter (fun k -> Lru.add c k k) keys;
at_most int ~than:3 (Lru.size c))
let () = exit (run "lru" [ basics; bounded ])A run with nothing to report prints one line:
$ dune runtest
mylib: 3 passed in 1.0ms (seed s1:b02192cebcec40d2).test and group declare a suite, and run runs it. Assertions such as equal, less, contains, raises and require_some take a witness, for example int or list string, and a failure prints the values it compared.
test "splits on commas" (fun () ->
equal (list string) [ "a"; "b" ] (String.split_on_char ',' "a,b"))prop checks a law over values generated with Gen. A failing input is reduced to a smaller one that still fails. Law states textbook laws, such as an involution or a round trip, and a failure names each term.
prop "rev is an involution" Gen.(list int) (Law.involutive (list int) List.rev)stateful runs generated programs of calls on a system and on a reference, such as a model of its state or another implementation, and compares what each call returns or raises. A failing program is reduced to a shorter one and printed as the calls that ran. With ~domains:2, the middle of each program runs on two domains at once, and the test fails when no order of the calls explains the results.
command "pop" (queue ^-> returns int) Model.pop Bounded_queue.popexpect compares a string with a literal in the test's source, and expect_file with a file. ppx_windtrap provides let%expect_test and [%expect]. dune promote accepts a change.
expect (Printf.sprintf "%d items" (List.length cart)) @@ __POS_OF__ {|3 items|}ppx_windtrap.coverage instruments a library, and windtrap coverage reports the expressions that the tests did not run.
dune runtest --instrument-with ppx_windtrap.coverage
dune exec windtrap -- coverageppx_windtrap.mutate compiles mutants of a library, small changes such as >= into >, into its test executables. --mutate runs the tests on each mutant and reports the mutants that no test fails on.
dune exec --instrument-with ppx_windtrap.mutate test/test_calc.exe -- --mutate opam install windtrap
opam install ppx_windtrap # expect tests, coverage, mutation testingThe manual, doc/manual/, has one page per need:
How-to:
expect, expect_file, let%expect_test.dune runtest and CI.lib/windtrap.mli, and ppx/ppx_windtrap.mli for the inline test forms.Questions are welcome on the OCaml forum.
examples/ holds the project of each manual page, run by dune runtest; its README lists them.
Windtrap builds on ideas and code from several OCaml projects:
THIRD_PARTY_LICENSES.md holds the notices of the code derived from them.