Property-based testing for Odin.
Write a property, draw generated inputs, and run it many times with deterministic
replay and shrinking. pbt works with Odin libraries, CLI programs, HTTP
services, stateful models, and Gransk-style external runners.
package cart_test
import "core:testing"
import pbt "pbt"
cart_total :: proc(qty, price_cents: int) -> int {
return qty * price_cents
}
cart_total_is_stable :: proc(t: ^pbt.T) -> pbt.Result {
qty := pbt.draw(t, pbt.int_range(0, 100))
price := pbt.draw(t, pbt.int_range(0, 10_000))
return pbt.equal(cart_total(qty, price), qty * price)
}
@(test)
test_cart_total :: proc(t: ^testing.T) {
result := pbt.check("cart total is stable", cart_total_is_stable, {
num_tests = 1_000,
seed = 123,
})
defer pbt.destroy_check_result(&result)
pbt.require_pass(t, result)
}draw records the choices needed for replay and shrinking. check runs the
property many times. A failing result keeps the seed, choices, shrunk case,
notes, labels, and event trace needed to reproduce the failure.
- Deterministic seeds and strict replay
- Default-on shrinking from recorded choice streams
- Integer, float, bool, string, byte, UUID, email, date, identifier, CLI, URL, HTTP, JSON, array, dictionary, tuple, enum, optional, and unique generators
- Generator combinators such as
map_gen,bind,one_of,frequency,sized,resize,scale, andsuch_that pass,fail,discard,error,assert,equal, andcounterexample- Notes, labels, classification, collection, and coverage requirements
- Stateful model testing
- Process, stdin, persistent line-protocol, HTTP, and statechart adapters
- JSON/text result output and Gransk runner support
For command-line or Gransk-compatible runners, collect properties and use the
CLI helpers for listing, tags, replay, JSON output, exit codes, and
--fail-fast:
properties := [?]pbt.Property_Case{
{name = "cart total", property = cart_total_is_stable, tags = []string{"cart"}},
}
pbt.run_cli(properties[:], os.args[1:], {num_tests = 1_000})Start with docs/runner-starter.md for a runner template.
pbt/- core packagepbt_statechart/- adapter helpers for statechart model testsexamples/- runnable runner and adapter examplesdocs/public-api.md- full API notesdocs/gransk-runner-contract.md- machine-readable runner contractBENCHMARKS.md- benchmark commands and current measurements
- docs/public-api.md - API surface and design notes
- docs/gransk-runner-example.md - minimal Gransk-facing executable
- docs/external-target-examples.md - CLI argv and stdin target wrappers
- docs/http-target-runner.md - generated HTTP requests against a service
- docs/stateful-http-runner.md - CRUD-style stateful HTTP model
- docs/line-protocol-example.md - persistent subprocess target
- docs/statechart-model-example.md - statechart-backed stateful model
- docs/api-comparison.md - comparison with QuickCheck and test.check
odin test ./pbt
odin test ./pbt_statechart
odin run examples/gransk_runner -collection:pbt=. -- --list-properties
odin run examples/runner_starter -collection:pbt=.
odin run benchmarks/check_bench.odin -file -collection:pbt=. -o:speed