Gaps identified by comparing against Boost.SML and HFSM2 test suites.
-
source() + history target — If a source()-routed transition targets
shallow_history("/P"), does the history restore work correctly? The LCA calculation changes when source differs from the structurally-containing state. -
source() + choice pseudostate —
source("/P/A")routing into achoice()that fans out via guards. Does source resolution compose with dynamic routing? -
source() + completion chaining — Transition via source() lands in a state that has a completion transition. Does the completion fire? (pipe_model tests this without source(), but never with it.)
-
source() + defer — Event deferred in state A, then state changes to B where it's not deferred. When the deferred event replays, does source() correctly match the new active state (B), not the original state (A)?
-
source() with deep nesting — source() specifying a grandchild (
source("/R/P/C/GC")) where the transition is defined on a great-grandparent. Tests that ancestor-matching logic works beyond parent→child. -
Multiple source() transitions, same event, overlapping ancestor — Two transitions on the same event:
source("/P/A")andsource("/P/A/X"). When in state/P/A/X, which wins? Tests priority/specificity resolution.
-
process() + history — No process() test drives shallow/deep history. The dispatch tests cover it, but process() has a different code path.
-
process() + choice — Choice pseudostate never exercised through process().
-
process() + defer — Deferred events are never tested through process(). Does process() correctly replay deferred events after a state change?
-
process() + final state — Does process() handle entering a final state and triggering parent completion?
-
Guard side-effect on source miss — Verify the guard lambda is never invoked when source doesn't match. Also verify effects are skipped on source miss.
-
source() with wildcard event (AnyEvent) — Does
transition(source("/P/A"), target("/P/B"))(noon<>) work as a completion from a specific source? What abouton<AnyEvent>()+source()? -
Multiple process() calls triggering chained completions — State A →(Go via source)→ B →(completion)→ C →(completion)→ D. Does the full chain execute in a single
process<Go>()call? -
source() on the root state —
source("/R")where R is the top-level machine state. Edge case for ancestor matching. -
Concurrent source() transitions with conflicting guards — Two transitions: same event, same source, different guards. Only one guard passes. Tests that the framework tries both and picks the one that passes.
-
Orthogonal/parallel regions — SML tests these heavily (6 dedicated test cases). If we plan to support them, source() + orthogonal regions is critical.
-
Thread safety — SML has
policies_thread_safe.cppwith 2 threads × 1000 iterations. If process() is meant to be used concurrently, we need this. -
Serialization round-trip — HFSM2 tests serializing active state configuration and restoring it. Useful for save/load in game-dev.
-
constexpr execution — SML tests that simple state machines can be fully evaluated at compile time via
static_assert. -
Scale stress — SML tests 257 states/events, HFSM2 tests 394 states/150 regions. Our largest model is the traffic light (8 states). A model with 50+ states would stress the template machinery.
-
Regression test directory — Both SML and HFSM2 maintain
test/reported/directories for bugs found by users. We should start one now, even if empty. -
Compile-time error tests — SML verifies that invalid DSL usage (non-callable guard, invalid transition table) produces clear errors, not template noise.
-
Property-based fuzzing — Generate random event sequences, assert invariants: active state is always valid, entry/exit are always paired, source-miss never fires effects.
-
PSSM conformance — The OMG publishes 103 test cases for UML state machine semantics. No library passes all of them, but tracking which we comply with would be differentiating.
-
Dispatch return value with source() —
dispatch_return_test.cpptests return values for guard rejection and deferral, but never with source()-routed transitions. Does a source-miss return "unhandled" or "no transition"?