Hegel.StateMachine (hegel_elixir v0.1.0)

View Source

Model-based property testing driven by libhegel's state-machine engine.

Rules transform immutable Elixir state and invariants inspect it. The engine chooses rules, enables random rule subsets for swarm testing, and shrinks a failing history by deleting or simplifying whole rule spans.

machine =
  Hegel.StateMachine.new([])
  |> Hegel.StateMachine.rule(:push, fn stack, tc ->
    [Hegel.draw(tc, Hegel.Generators.integer()) | stack]
  end)
  |> Hegel.StateMachine.rule(:pop, fn
    [], tc -> Hegel.assume(tc, false, "stack is empty")
    [_ | rest], _tc -> rest
  end)
  |> Hegel.StateMachine.invariant(:length, fn stack, _tc ->
    length(stack) >= 0
  end)

Hegel.StateMachine.run(tc, machine)

A rule can reject its precondition with Hegel.assume/3. Hegel discards that rule attempt while keeping the example. An invariant that returns false or nil fails the property.

Summary

Functions

Adds an invariant checked before the first rule and after each accepted rule.

Creates a state-machine description from an initial state or zero-arity factory.

Adds a named state transition.

Runs a state-machine description inside the current Hegel example.

Types

invariant()

@type invariant() :: {String.t(), (state(), Hegel.TestCase.t() -> term())}

rule()

@type rule() :: {String.t(), (state(), Hegel.TestCase.t() -> state())}

state()

@type state() :: term()

t()

@type t() :: %Hegel.StateMachine{
  initial: state() | (-> state()),
  invariants: [invariant()],
  rules: [rule()]
}

Functions

invariant(machine, name, function)

Adds an invariant checked before the first rule and after each accepted rule.

new(initial)

Creates a state-machine description from an initial state or zero-arity factory.

rule(machine, name, function)

Adds a named state transition.

run(test_case, description)

Runs a state-machine description inside the current Hegel example.