Packages

Property-based testing and metamorphic testing combinator library for Gleam

Current section

Files

Jump to
metamon src metamon@command.erl
Raw

src/metamon@command.erl

-module(metamon@command).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/metamon/command.gleam").
-export([new/4, no_precondition/3, always/3, name_of/1]).
-export_type([command/2]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Stateful / model-based testing primitives.\n"
"\n"
" A `Command(model, real)` represents one transition that can be\n"
" applied to two parallel worlds:\n"
"\n"
" * **model**: a pure description of the expected state (typically\n"
" a record or a `Dict`). The `next_model` step says how the\n"
" command updates this expected state.\n"
" * **real**: the system under test (a database connection, a\n"
" stateful module, an in-memory cache). The `run` step performs\n"
" the command on the real world and returns `Ok(Nil)` on\n"
" success or `Error(reason)` when an invariant is violated.\n"
"\n"
" The `precondition` decides whether the command can fire in the\n"
" current model state; the runner skips commands whose preconditions\n"
" are false.\n"
"\n"
" See `metamon/stateful` for the runner that drives a list of\n"
" commands against an initial `(model, real)` pair.\n"
).
-type command(HWL, HWM) :: {command,
binary(),
fun((HWL) -> boolean()),
fun((HWL) -> HWL),
fun((HWM) -> {ok, nil} | {error, binary()})}.
-file("src/metamon/command.gleam", 32).
?DOC(" Construct a command. `name` is shown in failure reports.\n").
-spec new(
binary(),
fun((HWN) -> boolean()),
fun((HWN) -> HWN),
fun((HWO) -> {ok, nil} | {error, binary()})
) -> command(HWN, HWO).
new(Name, Pre, Next, Run) ->
{command, Name, Pre, Next, Run}.
-file("src/metamon/command.gleam", 54).
?DOC(
" Construct a command with no precondition — the precondition arm is\n"
" fixed to `fn(_m) { True }`, so the command's gating step is a\n"
" no-op.\n"
"\n"
" \"No precondition\" is **not** the same as \"always runs\": if the\n"
" command's `run` returns `Error(reason)`, the runner halts the\n"
" sequence with `Failed`, and any later commands in the list are\n"
" skipped. Use `command.new` when the command should be gated on the\n"
" current model state.\n"
"\n"
" `command.always` is a synonym for `no_precondition` kept for\n"
" backward compatibility; new code should prefer this name because\n"
" it reads correctly at the call site.\n"
).
-spec no_precondition(
binary(),
fun((HWT) -> HWT),
fun((HWU) -> {ok, nil} | {error, binary()})
) -> command(HWT, HWU).
no_precondition(Name, Next, Run) ->
{command, Name, fun(_) -> true end, Next, Run}.
-file("src/metamon/command.gleam", 67).
?DOC(
" Synonym for `no_precondition`. The name reads as \"always runs\",\n"
" which overstates the contract — the command's `run` can still\n"
" return `Error`, halting the sequence. Prefer `no_precondition` in\n"
" new code; this constructor is retained as an alias so existing\n"
" call sites continue to compile.\n"
).
-spec always(
binary(),
fun((HWZ) -> HWZ),
fun((HXA) -> {ok, nil} | {error, binary()})
) -> command(HWZ, HXA).
always(Name, Next, Run) ->
no_precondition(Name, Next, Run).
-file("src/metamon/command.gleam", 76).
?DOC(" Get the command's user-facing name.\n").
-spec name_of(command(any(), any())) -> binary().
name_of(Cmd) ->
erlang:element(2, Cmd).