Test case template for the SCION and W3C conformance corpora.
Test-side surface for chart authors, versioned with the engine: no module in
lib/ outside Statifier.Testing.* may reference anything inside it, so the
engine never consults the test harness to decide behavior (ADR-0053,
amending ADR-0006). This module is also the supported entry point for a
downstream application testing its own charts, not only the corpus's driver
use Statifier.Testing.Caseworks the same way outside this repository as it does inside it.
Every generated corpus test goes through test_scxml/4 and nothing else, so
this module is the entire coupling surface between the corpus and the library
(ADR-0006). It needs exactly four things from Statifier to drive a chart:
- parse an SCXML document,
- build and initialize a state chart from it,
- send an event synchronously and get the next state chart back,
- read the active leaf-state set.
Each of those lives in its own private helper below - parse_document/1,
initialize/1, send_event/2, active_leaf_states/1 - and each is a thin
adapter over Statifier's four-function API (Statifier.compile/1,
Statifier.initialize/2, Statifier.send_event/2,
Statifier.active_leaf_states/1): unwrapping the {:ok, _} / {:error, _}
tuples and translating the corpus's event map into the shape Statifier
expects. The four-function contract stays a hard constraint on the library
surface rather than something the corpus can widen.
The assertion path reads two more things, both because a terminated chart's
configuration is empty by construction (exit_interpreter/1, Appendix D)
and the harness still has to say what the chart's leaves were at exit:
initialize/1andsend_event/2now also return the call's effect list.assert_configuration/3looks for a{:done, _}effect in it and, when found, restores that effect'sconfigurationfield -Effect.Donecarries the terminal configuration - onto the%MachineState{}before reading leaves off it - so the terminal position is observed instead of the post-exit emptiness. This is still driving-side plumbing routed to the assertion, not a fifth driving function.Statifier.MachineState.active_leaf_states/1(the untranslated, index-keyed version behindStatifier.active_leaf_states/1) is read directly byassert_every_leaf_named/2to compare its size against the id-translated set, catching a nameless active leaf that the id-based comparison would silently drop. This is an inspection of a value the harness already holds, not a new way to drive the chart.
Documents using features v2 does not support flunk with the feature named
(Statifier.Testing.FeatureDetector) - they never skip, so an unimplemented
feature can never masquerade as a passing test. Both suites are excluded by
default (:scion, :scxml_w3), so mix test stays green while
test.regression's ratchet is the thing that grows.
Two driving paths, one closed contract
test_scxml/4 routes each document, on the same detected feature set, to
one of two private paths. Documents that need real delivery, wall-clock
timers, or child sessions (@session_features below) drive through a
Statifier.Session; every other document drives synchronously through the
four functions this moduledoc opens with. The session path keeps
Statifier.compile/1 and Statifier.active_leaf_states/1 exactly as they
were and replaces the other two with five: initialize becomes
Statifier.start_session/2, send_event becomes
Statifier.Session.send_event/2, and driving a live session additionally
needs Statifier.Session.snapshot/1 to read a configuration,
Statifier.Session.status/1 to know when it has settled, and
Statifier.Session.stop/2 to tear it down. Nine functions across the two
paths, enumerated in ADR-0006 and closed: adding a tenth, or a third
driving path, reopens that record rather than being a harness change.
Either way the corpus still cannot widen the library surface, because every
one of the nine is public API carried by its own record.
v1's StateMachine and logging helpers are deliberately not ported: the
subsystems they drove do not exist in v2, and reintroducing them here would
grow the coupling surface this module exists to hold flat.
Summary
Functions
Whether a document needs the session layer to reach its expected
configurations. Public so the routing decision can be asserted directly;
test_scxml/4 is the only caller in the corpus.
Tests SCXML state chart behavior.
Functions
Whether a document needs the session layer to reach its expected
configurations. Public so the routing decision can be asserted directly;
test_scxml/4 is the only caller in the corpus.
@spec test_scxml( xml :: String.t(), description :: String.t(), expected_initial_config :: [String.t()], events :: [{map(), [String.t()]}], opts :: keyword() ) :: :ok
Tests SCXML state chart behavior.
xml- SCXML document stringdescription- test description, for debuggingexpected_initial_config- active leaf state IDs expected after initializeevents- list of{event_map, expected_states}tuples, whereevent_mapcarries the event name under"name"opts- seeOptionsbelow
Flunks with the feature named if the document depends on an SCXML feature v2 does not support yet, rather than reporting a false pass.
Options
Both default to values tuned for this repo's conformance corpus. A downstream chart whose load-bearing delay is longer than the corpus's needs the knob. Both are ignored on the synchronous path, which has no timing to tune.
:settle_window_ms(default100) - how long to wait for pending timers to drain before sending the next event. Long enough to drain load-bearing intermediate delays, short enough never to let a guard send fire.:configuration_deadline_ms(default4000) - upper bound on waiting for a session to reach an expected configuration. Bounds only the wrong answer: a chart that cannot change again exits the poll immediately.