diff --git a/src/Stateless/Graph/GraphStyleBase.cs b/src/Stateless/Graph/GraphStyleBase.cs index f7139c6a..0b79c5df 100644 --- a/src/Stateless/Graph/GraphStyleBase.cs +++ b/src/Stateless/Graph/GraphStyleBase.cs @@ -77,17 +77,11 @@ public virtual List FormatAllTransitions(List transitions) line = FormatOneTransition(stay.SourceState.NodeName, stay.Trigger.UnderlyingTrigger.ToString(), null, stay.SourceState.NodeName, stay.Guards.Select(x => x.Description)); } - else if (stay.SourceState.EntryActions.Count == 0) - { - line = FormatOneTransition(stay.SourceState.NodeName, stay.Trigger.UnderlyingTrigger.ToString(), - null, stay.SourceState.NodeName, stay.Guards.Select(x => x.Description)); - } else { - // There are entry functions into the state, so call out that this transition - // does invoke them (since normally a transition back into the same state doesn't) line = FormatOneTransition(stay.SourceState.NodeName, stay.Trigger.UnderlyingTrigger.ToString(), - stay.SourceState.EntryActions, stay.SourceState.NodeName, stay.Guards.Select(x => x.Description)); + stay.DestinationEntryActions.Select(x => x.Method.Description), + stay.SourceState.NodeName, stay.Guards.Select(x => x.Description)); } } else diff --git a/src/Stateless/Graph/StateGraph.cs b/src/Stateless/Graph/StateGraph.cs index ec05045a..0e460201 100644 --- a/src/Stateless/Graph/StateGraph.cs +++ b/src/Stateless/Graph/StateGraph.cs @@ -141,6 +141,16 @@ void AddTransitions(StateMachineInfo machineInfo) Transitions.Add(stay); fromState.Leaving.Add(stay); fromState.Arriving.Add(stay); + + // If the reentrant transition causes the state's entry action to be executed, this is shown + // explicity in the state graph by adding it to the DestinationEntryActions list. + if (stay.ExecuteEntryExitActions) + { + foreach (var action in stateInfo.EntryActions.Where(a => a.FromTrigger is null)) + { + stay.DestinationEntryActions.Add(action); + } + } } else { diff --git a/test/Stateless.Tests/DotGraphFixture.cs b/test/Stateless.Tests/DotGraphFixture.cs index 784c262c..70b65aa3 100644 --- a/test/Stateless.Tests/DotGraphFixture.cs +++ b/test/Stateless.Tests/DotGraphFixture.cs @@ -582,6 +582,65 @@ public void Initial_State_Not_Changed_After_Trigger_Fired() Assert.Equal(expected, dotGraph); } + [Fact] + public void Reentrant_Transition_Shows_Entry_Action_When_Action_Is_Configured_With_OnEntryFrom() + { + var expected = Prefix(Style.UML) + + Box(Style.UML, "A") + + Box(Style.UML, "B") + + Line("A", "B", "X / OnEntry") + + Line("B", "B", "X / OnEntry") + + suffix; + + var sm = new StateMachine(State.A); + + sm.Configure(State.A) + .Permit(Trigger.X, State.B); + + var list = new List(); + sm.Configure(State.B) + .OnEntryFrom(Trigger.X, OnEntry, entryActionDescription: "OnEntry") + .PermitReentry(Trigger.X); + + string dotGraph = UmlDotGraph.Format(sm.GetInfo()); + +#if WRITE_DOTS_TO_FOLDER + System.IO.File.WriteAllText(DestinationFolder + "Reentrant_Transition_Shows_Entry_Action_When_Action_Is_Configured_With_OnEntryFrom.dot", dotGraph); +#endif + + Assert.Equal(expected, dotGraph); + } + + [Fact] + public void Reentrant_Transition_Shows_Entry_Action_When_Action_Is_Configured_With_OnEntryFrom_And_Trigger_Has_Parameter() + { + var expected = Prefix(Style.UML) + + Box(Style.UML, "A") + + Box(Style.UML, "B") + + Line("A", "B", "X / LogTrigger") + + Line("B", "B", "X / LogTrigger") + + suffix; + + var sm = new StateMachine(State.A); + var triggerX = sm.SetTriggerParameters(Trigger.X); + + sm.Configure(State.A) + .Permit(Trigger.X, State.B); + + var list = new List(); + sm.Configure(State.B) + .OnEntryFrom(triggerX, list.Add, entryActionDescription: "LogTrigger") + .PermitReentry(Trigger.X); + + string dotGraph = UmlDotGraph.Format(sm.GetInfo()); + +#if WRITE_DOTS_TO_FOLDER + System.IO.File.WriteAllText(DestinationFolder + "Reentrant_Transition_Shows_Entry_Action_When_Action_Is_Configured_With_OnEntryFrom_And_Trigger_Has_Parameter.dot", dotGraph); +#endif + + Assert.Equal(expected, dotGraph); + } + private void TestEntryAction() { } private void TestEntryActionString(string val) { } private State DestinationSelector() { return State.A; }