metamon/command

Stateful / model-based testing primitives.

A Command(model, real) represents one transition that can be applied to two parallel worlds:

The precondition decides whether the command can fire in the current model state; the runner skips commands whose preconditions are false.

See metamon/stateful for the runner that drives a list of commands against an initial (model, real) pair.

Types

A single transition in a model-based test.

pub type Command(model, real) {
  Command(
    name: String,
    precondition: fn(model) -> Bool,
    next_model: fn(model) -> model,
    run: fn(real) -> Result(Nil, String),
  )
}

Constructors

  • Command(
      name: String,
      precondition: fn(model) -> Bool,
      next_model: fn(model) -> model,
      run: fn(real) -> Result(Nil, String),
    )

Values

pub fn always(
  name name: String,
  next_model next: fn(model) -> model,
  run run: fn(real) -> Result(Nil, String),
) -> Command(model, real)

Synonym for no_precondition. The name reads as “always runs”, which overstates the contract — the command’s run can still return Error, halting the sequence. Prefer no_precondition in new code; this constructor is retained as an alias so existing call sites continue to compile.

pub fn name_of(cmd: Command(model, real)) -> String

Get the command’s user-facing name.

pub fn new(
  name name: String,
  precondition pre: fn(model) -> Bool,
  next_model next: fn(model) -> model,
  run run: fn(real) -> Result(Nil, String),
) -> Command(model, real)

Construct a command. name is shown in failure reports.

pub fn no_precondition(
  name name: String,
  next_model next: fn(model) -> model,
  run run: fn(real) -> Result(Nil, String),
) -> Command(model, real)

Construct a command with no precondition — the precondition arm is fixed to fn(_m) { True }, so the command’s gating step is a no-op.

“No precondition” is not the same as “always runs”: if the command’s run returns Error(reason), the runner halts the sequence with Failed, and any later commands in the list are skipped. Use command.new when the command should be gated on the current model state.

command.always is a synonym for no_precondition kept for backward compatibility; new code should prefer this name because it reads correctly at the call site.

Search Document