diff --git a/src/Stateless/StateMachine.cs b/src/Stateless/StateMachine.cs index 270cc620..5d5a0e5f 100644 --- a/src/Stateless/StateMachine.cs +++ b/src/Stateless/StateMachine.cs @@ -423,6 +423,10 @@ void InternalFireOne(TTrigger trigger, params object[] args) case DynamicTriggerBehaviour _ when result.Handler.ResultsInTransitionFrom(source, args, out var destination): case TransitioningTriggerBehaviour _ when result.Handler.ResultsInTransitionFrom(source, args, out destination): { + //If a trigger was found on a superstate that would cause unintended reentry, don't trigger. + if (source.Equals(destination)) + break; + // Handle transition, and set new state var transition = new Transition(source, destination, trigger, args); HandleTransitioningTrigger(args, representativeState, transition); diff --git a/test/Stateless.Tests/StateMachineFixture.cs b/test/Stateless.Tests/StateMachineFixture.cs index 5f2803b9..f89b8560 100644 --- a/test/Stateless.Tests/StateMachineFixture.cs +++ b/test/Stateless.Tests/StateMachineFixture.cs @@ -108,6 +108,29 @@ public void WhenInSubstate_TriggerIgnoredInSuperstate_RemainsInSubstate() Assert.Equal(State.B, sm.State); } + [Fact] + public void WhenInSubstate_TriggerSuperStateTwiceToSameSubstate_DoesNotReenterSubstate() + { + var sm = new StateMachine(State.A); + var eCount = 0; + + sm.Configure(State.B) + .OnEntry(() => { eCount++;}) + .SubstateOf(State.C); + + sm.Configure(State.A) + .SubstateOf(State.C); + + + sm.Configure(State.C) + .Permit(Trigger.X, State.B); + + sm.Fire(Trigger.X); + sm.Fire(Trigger.X); + + Assert.Equal(1, eCount); + } + [Fact] public void PermittedTriggersIncludeSuperstatePermittedTriggers() {