Mix.Statifier.Corpus.HostCase (Statifier v2.10.0)

Copy Markdown View Source

Runs a corpus case that carries a host object (ADR-0070 decision 5): the host registers the case's send_types (ADR-0069), and the sends handed to it must be the case's expect_sends.

Statifier.Testing.Case.test_scxml/4 starts its session with no registration, so a host case runs here instead, through the same public session API: Statifier.start_session/2 with :send_types naming Mix.Statifier.Corpus.HostCase.Processor for every registered type, Statifier.Session.send_event/2 for each step, with the step's event data, when it gives one, as the injected event's payload, and Statifier.Session.status/1 and Statifier.Session.snapshot/1 to wait for the chart to settle and read its active leaf states. The waiting follows test_scxml/4's: a pending library timer is given a short window to fire before the next event, and a configuration is read once the session has had nothing queued and no timer pending on two consecutive polls, or the chart has left the running status, or a deadline passed.

A case agrees when the active leaf states after initialization and after every step are the ones it expects, and the sends handed to the processor over the whole run, in order, are exactly its expect_sends. Each handed send is written in the case's language-neutral item shape: type, target, event with its name and, when the send carries a payload, its data; delay_ms for a delayed send; and send_id only when the author named the send, which is when the delivered event carries one. A registered delayed send is the host's timer, and this host never fires one.

An expect_sends item may carry an outcome the runner performs or observes for that send (ADR-0070's 2026-09-23 Amendment). With "fail", the runner reports the send handed at that item's position through Statifier.Session.failed_send/3 as soon as it reads the processor's message, before it reads the next configuration, so the step that led to the send is the one whose configuration shows what the sender made of the error.communication it got. With "cancelled", a <cancel> naming the send must reach the processor after it was handed the send: when a cancel reaches the processor, the runner writes "outcome": "cancelled" on each delayed send handed before it under the cancel's send id, generated or not, whose item asks for it, so the comparison above refuses a marked item no cancel reached. An item with no outcome claims nothing about a cancel: a cancel naming its send is not compared.

A host object may also carry declared_events and expect_accepts, present together or not at all (ADR-0071 decision 7). Before it starts the session, the runner calls Statifier.Chart.check_accepts/2 on the compiled chart with declared_events, and the case agrees only when both lists it answers are exactly expect_accepts' unreachable and undeclared, order included. Either key without the other is a disagreement.

A host object may carry a diff pair as well - to_source and expect_diff, with an optional mapping and expect_compatible_at (ADR-0072, conformance/schema/case.json) - and this runner compares none of them: it runs such a case like any other host case. Nothing in lib/ calls the chart diff or the position predicate (ADR-0072 decision 6), so the test suite compares those four keys (test/corpus/diff_cases_test.exs), through run/2's :after_steps option for the position the steps leave.

A host object may carry event_io_processors, the one host key a w3c case may carry (ADR-0075 decision 7): the Event I/O Processor URIs the host runs with a location that reaches the running session. For each one the runner starts a loopback front (Mix.Statifier.BasicHTTPFront) and registers the processor under its URI and its short form with the front's base URL, beside the case's send_types, so every send to it is delivered through the front and none is handed to the recording processor (with_event_io_processors/2). The front is stopped when the case ends. The item set is closed; its one member is the Basic HTTP Event I/O Processor, Statifier.Send.BasicHTTP.

Summary

Functions

The Event I/O Processor URIs of host.event_io_processors' closed item set that source names as a <send>'s type, sorted: what a corpus case transformed from source declares in host.event_io_processors.

Runs one host case, returning :agree or {:disagree, message}.

Runs fun with the :send_types entries that register every processor uris names, delivering through a loopback front started for the call and stopped when fun returns or raises; answers what fun answers.

Functions

event_io_processors(source)

@spec event_io_processors(source :: String.t()) :: [String.t()]

The Event I/O Processor URIs of host.event_io_processors' closed item set that source names as a <send>'s type, sorted: what a corpus case transformed from source declares in host.event_io_processors.

Examples

iex> Mix.Statifier.Corpus.HostCase.event_io_processors(
...>   ~s|<send type="http://www.w3.org/TR/scxml/#BasicHTTPEventProcessor" event="e"/>|
...> )
["http://www.w3.org/TR/scxml/#BasicHTTPEventProcessor"]

iex> Mix.Statifier.Corpus.HostCase.event_io_processors(~s|<send event="e"/>|)
[]

run(corpus_case, opts \\ [])

@spec run(
  corpus_case :: map(),
  opts :: [
    {:after_steps,
     (Statifier.MachineState.t() -> :ok | {:disagree, String.t()})}
  ]
) :: :agree | {:disagree, String.t()}

Runs one host case, returning :agree or {:disagree, message}.

opts takes :after_steps, a function the runner calls once, after the last step's configuration agrees and before it compares the handed sends, with the session's settled Statifier.MachineState; it answers :ok or {:disagree, message}, and a disagreement is the case's. A case that disagrees before the sends are compared leaves them in the calling process's mailbox, so each run belongs in a process of its own, as Mix.Statifier.Corpus.Runner.run/1 gives it.

with_event_io_processors(uris, fun)

@spec with_event_io_processors(
  uris :: [String.t()],
  fun :: (%{required(String.t()) => {module(), keyword()}} -> result)
) :: result
when result: term()

Runs fun with the :send_types entries that register every processor uris names, delivering through a loopback front started for the call and stopped when fun returns or raises; answers what fun answers.

Each processor is registered under its URI and its short form, with the front's base URL as its :base_url (ADR-0075 decisions 2 and 3). With no URI, no front is started and fun is handed an empty map. A URI outside the closed set raises.