public void CommonAncestor( AsyncPassiveStateMachine <int, int> machine) { const int commonAncestorState = 0; const int sourceState = 1; const int parentOfSourceState = 2; const int siblingOfSourceState = 3; const int destinationState = 4; const int parentOfDestinationState = 5; const int siblingOfDestinationState = 6; const int Event = 0; bool commonAncestorStateLeft = false; "establish a hierarchical state machine"._(async() => { machine = new AsyncPassiveStateMachine <int, int>(); machine.DefineHierarchyOn(commonAncestorState) .WithHistoryType(HistoryType.None) .WithInitialSubState(parentOfSourceState) .WithSubState(parentOfDestinationState); machine.DefineHierarchyOn(parentOfSourceState) .WithHistoryType(HistoryType.None) .WithInitialSubState(sourceState) .WithSubState(siblingOfSourceState); machine.DefineHierarchyOn(parentOfDestinationState) .WithHistoryType(HistoryType.None) .WithInitialSubState(destinationState) .WithSubState(siblingOfDestinationState); machine.In(sourceState) .On(Event).Goto(destinationState); machine.In(commonAncestorState) .ExecuteOnExit(() => commonAncestorStateLeft = true); await machine.Initialize(sourceState); await machine.Start(); }); "when firing an event resulting in a transition with a common ancestor"._(() => machine.Fire(Event)); "the state machine should remain inside common ancestor state"._(() => commonAncestorStateLeft .Should().BeFalse()); }
public void BeforeExecutingEntryActionsHierarchical( AsyncPassiveStateMachine<string, string> machine, IExtension<string, string> extension) { "establish an extension".x(() => extension = A.Fake<IExtension<string, string>>()); "establish a hierarchical state machine using the extension".x(async () => { machine = new AsyncPassiveStateMachine<string, string>(Name); machine.AddExtension(extension); machine.DefineHierarchyOn("A") .WithHistoryType(HistoryType.None) .WithInitialSubState("A0"); machine.In("0") .On("A0").Goto("A0"); await machine.Initialize("0"); await machine.Start(); }); "when firing an event onto the state machine".x(() => machine.Fire("A0")); "it should call EnteringState on registered extensions for entered super states of target state".x(() => A.CallTo(() => extension.EnteringState( A<IStateMachineInformation<string, string>>.That.Matches(x => x.Name == Name && x.CurrentStateId == "A0"), A<IState<string, string>>.That.Matches(x => x.Id == "A"), A<ITransitionContext<string, string>>.That.Matches(x => x.EventId.Value == "A0"))) .MustHaveHappened()); "it should call EnteringState on registered extensions for entered leaf target state".x(() => A.CallTo(() => extension.EnteringState( A<IStateMachineInformation<string, string>>.That.Matches(x => x.Name == Name && x.CurrentStateId == "A0"), A<IState<string, string>>.That.Matches(x => x.Id == "A0"), A<ITransitionContext<string, string>>.That.Matches(x => x.EventId.Value == "A0"))) .MustHaveHappened()); }
public void NoCommonAncestor( AsyncPassiveStateMachine <string, int> machine) { const string sourceState = "SourceState"; const string parentOfSourceState = "ParentOfSourceState"; const string siblingOfSourceState = "SiblingOfSourceState"; const string destinationState = "DestinationState"; const string parentOfDestinationState = "ParentOfDestinationState"; const string siblingOfDestinationState = "SiblingOfDestinationState"; const string grandParentOfSourceState = "GrandParentOfSourceState"; const string grandParentOfDestinationState = "GrandParentOfDestinationState"; const int Event = 0; var log = string.Empty; "establish a hierarchical state machine"._(async() => { machine = new AsyncPassiveStateMachine <string, int>(); machine.DefineHierarchyOn(parentOfSourceState) .WithHistoryType(HistoryType.None) .WithInitialSubState(sourceState) .WithSubState(siblingOfSourceState); machine.DefineHierarchyOn(parentOfDestinationState) .WithHistoryType(HistoryType.None) .WithInitialSubState(destinationState) .WithSubState(siblingOfDestinationState); machine.DefineHierarchyOn(grandParentOfSourceState) .WithHistoryType(HistoryType.None) .WithInitialSubState(parentOfSourceState); machine.DefineHierarchyOn(grandParentOfDestinationState) .WithHistoryType(HistoryType.None) .WithInitialSubState(parentOfDestinationState); machine.In(sourceState) .ExecuteOnExit(() => log += "exit" + sourceState) .On(Event).Goto(destinationState); machine.In(parentOfSourceState) .ExecuteOnExit(() => log += "exit" + parentOfSourceState); machine.In(destinationState) .ExecuteOnEntry(() => log += "enter" + destinationState); machine.In(parentOfDestinationState) .ExecuteOnEntry(() => log += "enter" + parentOfDestinationState); machine.In(grandParentOfSourceState) .ExecuteOnExit(() => log += "exit" + grandParentOfSourceState); machine.In(grandParentOfDestinationState) .ExecuteOnEntry(() => log += "enter" + grandParentOfDestinationState); await machine.Initialize(sourceState); await machine.Start(); }); "when firing an event resulting in a transition without a common ancestor"._(() => machine.Fire(Event)); "it should execute exit action of source state"._(() => log.Should().Contain("exit" + sourceState)); "it should execute exit action of parents of source state (recursively)"._(() => log .Should().Contain("exit" + parentOfSourceState) .And.Contain("exit" + grandParentOfSourceState)); "it should execute entry action of parents of destination state (recursively)"._(() => log .Should().Contain("enter" + parentOfDestinationState) .And.Contain("enter" + grandParentOfDestinationState)); "it should execute entry action of destination state"._(() => log.Should().Contain("enter" + destinationState)); "it should execute actions from source upwards and then downwards to destination state"._(() => { string[] states = { sourceState, parentOfSourceState, grandParentOfSourceState, grandParentOfDestinationState, parentOfDestinationState, destinationState }; var statesInOrderOfAppearanceInLog = states .OrderBy(s => log.IndexOf(s.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal)); statesInOrderOfAppearanceInLog .Should().Equal(states); }); }