-module(scamper@testing). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/scamper/testing.gleam"). -export([assert_transition/3, assert_final/1, reachable_states/2, run_events/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( " Test helpers for scamper FSMs.\n" "\n" " Provides convenience functions for testing state machine behavior.\n" " These functions use `let assert` which panics on failure — they are\n" " designed for test code only, not for library or application code.\n" ). -file("src/scamper/testing.gleam", 16). ?DOC( " Attempt a transition and assert the machine reaches the expected state.\n" " Panics if the transition fails or the machine is not in the expected state.\n" " Returns the new machine for chaining.\n" ). -spec assert_transition(scamper:machine(IYE, IYF, IYG), IYG, IYE) -> scamper:machine(IYE, IYF, IYG). assert_transition(Machine, Event, Expected_state) -> New_machine@1 = case scamper:transition(Machine, Event) of {ok, New_machine} -> New_machine; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"scamper/testing"/utf8>>, function => <<"assert_transition"/utf8>>, line => 21, value => _assert_fail, start => 763, 'end' => 826, pattern_start => 774, pattern_end => 789}) end, case scamper:current_state(New_machine@1) =:= Expected_state of true -> nil; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"scamper/testing"/utf8>>, function => <<"assert_transition"/utf8>>, line => 22, value => _assert_fail@1, start => 829, 'end' => 899, pattern_start => 840, pattern_end => 844}) end, New_machine@1. -file("src/scamper/testing.gleam", 29). ?DOC( " Assert that the machine is in a final state.\n" " Panics if the machine is not in a final state.\n" " Returns the machine for chaining.\n" ). -spec assert_final(scamper:machine(IYN, IYO, IYP)) -> scamper:machine(IYN, IYO, IYP). assert_final(Machine) -> case scamper:is_final(Machine) of true -> nil; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"scamper/testing"/utf8>>, function => <<"assert_final"/utf8>>, line => 32, value => _assert_fail, start => 1159, 'end' => 1202, pattern_start => 1170, pattern_end => 1174}) end, Machine. -file("src/scamper/testing.gleam", 38). ?DOC( " Find all states reachable from the initial state via transition rules.\n" " Delegates to validation.reachable_states.\n" ). -spec reachable_states(scamper@config:config(IYW, any(), any()), IYW) -> list(IYW). reachable_states(Config, Initial_state) -> scamper@validation:reachable_states(Config, Initial_state). -file("src/scamper/testing.gleam", 48). ?DOC( " Run a sequence of events against the machine.\n" " Returns the final machine if all transitions succeed,\n" " or the first TransitionError encountered.\n" ). -spec run_events(scamper:machine(IZD, IZE, IZF), list(IZF)) -> {ok, scamper:machine(IZD, IZE, IZF)} | {error, scamper@error:transition_error(IZD, IZF)}. run_events(Machine, Events) -> gleam@list:try_fold( Events, Machine, fun(M, Evt) -> scamper:transition(M, Evt) end ).