public void OtherwiseGuard( AsyncPassiveStateMachine <int, int> machine, CurrentStateExtension currentStateExtension) { "establish a state machine with otherwise guard and no machting other guard".x(async() => { machine = new AsyncPassiveStateMachine <int, int>(); currentStateExtension = new CurrentStateExtension(); machine.AddExtension(currentStateExtension); machine.In(SourceState) .On(Event) .If(() => Task.FromResult(false)).Goto(ErrorState) .Otherwise().Goto(DestinationState); await machine.Initialize(SourceState); await machine.Start(); }); "when an event is fired".x(() => machine.Fire(Event)); "it should_take_transition_guarded_with_otherwise".x(() => currentStateExtension.CurrentState.Should().Be(DestinationState)); }
public void Start( AsyncPassiveStateMachine <int, int> machine, bool entryActionExecuted) { "establish an initialized state machine".x(() => { machine = new AsyncPassiveStateMachine <int, int>(); machine.AddExtension(this.testExtension); machine.In(TestState) .ExecuteOnEntry(() => entryActionExecuted = true); machine.Initialize(TestState); }); "when starting the state machine".x(() => machine.Start()); "should set current state of state machine to state to which it is initialized".x(() => this.testExtension.CurrentState.Should().Be(TestState)); "should execute entry action of state to which state machine is initialized".x(() => entryActionExecuted.Should().BeTrue()); }
public void BeforeExecutingEntryActions( AsyncPassiveStateMachine<string, int> machine, IExtension<string, int> extension) { "establish an extension".x(() => extension = A.Fake<IExtension<string, int>>()); "establish a state machine using the extension".x(async () => { machine = new AsyncPassiveStateMachine<string, int>(Name); machine.AddExtension(extension); machine.In("0") .On(1).Goto("1"); await machine.Initialize("0"); await machine.Start(); }); "when firing an event onto the state machine".x(() => machine.Fire(1)); "it should call EnteringState on registered extensions for target state".x(() => A.CallTo(() => extension.EnteringState( A<IStateMachineInformation<string, int>>.That.Matches(x => x.Name == Name && x.CurrentStateId == "1"), A<IState<string, int>>.That.Matches(x => x.Id == "1"), A<ITransitionContext<string, int>>.That.Matches(x => x.EventId.Value == 1))) .MustHaveHappened()); }
public void Start( AsyncPassiveStateMachine <int, int> machine, bool entryActionExecuted, CurrentStateExtension currentStateExtension) { "establish an initialized state machine".x(() => { var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <int, int>(); stateMachineDefinitionBuilder .In(TestState) .ExecuteOnEntry(() => entryActionExecuted = true); machine = stateMachineDefinitionBuilder .WithInitialState(TestState) .Build() .CreatePassiveStateMachine(); currentStateExtension = new CurrentStateExtension(); machine.AddExtension(currentStateExtension); }); "when starting the state machine".x(() => machine.Start()); "should set current state of state machine to state to which it is initialized".x(() => currentStateExtension.CurrentState.Should().Be(TestState)); "should execute entry action of state to which state machine is initialized".x(() => entryActionExecuted.Should().BeTrue()); }
public void MatchingGuard( AsyncPassiveStateMachine <int, int> machine, CurrentStateExtension currentStateExtension) { "establish a state machine with guarded transitions".x(async() => { machine = new AsyncPassiveStateMachine <int, int>(); currentStateExtension = new CurrentStateExtension(); machine.AddExtension(currentStateExtension); machine.In(SourceState) .On(Event) .If(() => false).Goto(ErrorState) .If(async() => await Task.FromResult(false)).Goto(ErrorState) .If(async() => await Task.FromResult(true)).Goto(DestinationState) .If(() => true).Goto(ErrorState) .Otherwise().Goto(ErrorState); await machine.Initialize(SourceState); await machine.Start(); }); "when an event is fired".x(() => machine.Fire(Event)); "it should take transition guarded with first matching guard".x(() => currentStateExtension.CurrentState.Should().Be(DestinationState)); }
public void NoMatchingGuard( AsyncPassiveStateMachine <int, int> machine) { bool declined = false; "establish state machine with no matching guard".x(async() => { machine = new AsyncPassiveStateMachine <int, int>(); var currentStateExtension = new CurrentStateExtension(); machine.AddExtension(currentStateExtension); machine.In(SourceState) .On(Event) .If(() => Task.FromResult(false)).Goto(ErrorState); machine.TransitionDeclined += (sender, e) => declined = true; await machine.Initialize(SourceState); await machine.Start(); }); "when an event is fired".x(() => machine.Fire(Event)); "it should notify about declined transition".x(() => declined.Should().BeTrue("TransitionDeclined event should be fired")); }
public void Loading( StateMachineSaver <State> saver, StateMachineLoader <State> loader, FakeExtension extension, State sourceState, State targetState) { "establish a saved state machine with history"._(async() => { var machine = new AsyncPassiveStateMachine <State, Event>(); DefineMachine(machine); await machine.Initialize(State.A); await machine.Start(); await machine.Fire(Event.S2); // set history of super state S await machine.Fire(Event.B); // set current state to B saver = new StateMachineSaver <State>(); loader = new StateMachineLoader <State>(); await machine.Save(saver); }); "when state machine is loaded"._(async() => { loader.SetCurrentState(saver.CurrentStateId); loader.SetHistoryStates(saver.HistoryStates); extension = new FakeExtension(); var loadedMachine = new AsyncPassiveStateMachine <State, Event>(); loadedMachine.AddExtension(extension); DefineMachine(loadedMachine); await loadedMachine.Load(loader); loadedMachine.TransitionCompleted += (sender, args) => { sourceState = args.StateId; targetState = args.NewStateId; }; await loadedMachine.Start(); await loadedMachine.Fire(Event.S); }); "it should reset current state"._(() => sourceState.Should().Be(State.B)); "it should reset all history states of super states"._(() => targetState.Should().Be(State.S2)); "it should notify extensions"._(() => extension.LoadedCurrentState .Should().BeEquivalentTo(State.B)); }
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() => { var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <string, string>(); stateMachineDefinitionBuilder .DefineHierarchyOn("A") .WithHistoryType(HistoryType.None) .WithInitialSubState("A0"); stateMachineDefinitionBuilder .In("0") .On("A0") .Goto("A0"); machine = stateMachineDefinitionBuilder .WithInitialState("0") .Build() .CreatePassiveStateMachine(Name); machine.AddExtension(extension); 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.ExtractOrThrow() == "A0"), A <IStateDefinition <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.ExtractOrThrow() == "A0"), A <IStateDefinition <string, string> > .That.Matches(x => x.Id == "A0"), A <ITransitionContext <string, string> > .That.Matches(x => x.EventId.Value == "A0"))) .MustHaveHappened()); }
public void InitializationInSuperState( AsyncPassiveStateMachine <int, int> machine, CurrentStateExtension testExtension, bool entryActionOfLeafStateExecuted, bool entryActionOfSuperStateExecuted) { "establish a hierarchical state machine with super state as initial state".x(() => { var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <int, int>(); stateMachineDefinitionBuilder .DefineHierarchyOn(SuperState) .WithHistoryType(HistoryType.None) .WithInitialSubState(LeafState); stateMachineDefinitionBuilder .In(SuperState) .ExecuteOnEntry(() => entryActionOfSuperStateExecuted = true); stateMachineDefinitionBuilder .In(LeafState) .ExecuteOnEntry(() => entryActionOfLeafStateExecuted = true); machine = stateMachineDefinitionBuilder .WithInitialState(SuperState) .Build() .CreatePassiveStateMachine(); testExtension = new CurrentStateExtension(); machine.AddExtension(testExtension); }); "when starting the state machine".x(async() => await machine.Start()); "it should_set_current_state_of_state_machine_to_initial_leaf_state_of_the_state_to_which_it_is_initialized".x(() => testExtension.CurrentState .Should().Be(LeafState)); "it should_execute_entry_action_of_super_state_to_which_state_machine_is_initialized".x(() => entryActionOfSuperStateExecuted .Should().BeTrue()); "it should_execute_entry_actions_of_initial_sub_states_until_a_leaf_state_is_reached".x(() => entryActionOfLeafStateExecuted .Should().BeTrue()); }
public void InitializationInLeafState( AsyncPassiveStateMachine <int, int> machine, CurrentStateExtension testExtension, bool entryActionOfLeafStateExecuted, bool entryActionOfSuperStateExecuted) { "establish a hierarchical state machine with leaf state as initial state".x(() => { var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <int, int>(); stateMachineDefinitionBuilder .DefineHierarchyOn(SuperState) .WithHistoryType(HistoryType.None) .WithInitialSubState(LeafState); stateMachineDefinitionBuilder .In(SuperState) .ExecuteOnEntry(() => entryActionOfSuperStateExecuted = true); stateMachineDefinitionBuilder .In(LeafState) .ExecuteOnEntry(() => entryActionOfLeafStateExecuted = true); machine = stateMachineDefinitionBuilder .WithInitialState(LeafState) .Build() .CreatePassiveStateMachine(); testExtension = new CurrentStateExtension(); machine.AddExtension(testExtension); }); "when starting the state machine".x(async() => await machine.Start()); "it should set current state of state machine to state to which it is initialized".x(() => testExtension.CurrentState .Should().Be(LeafState)); "it should execute entry action of state to which state machine is initialized".x(() => entryActionOfLeafStateExecuted .Should().BeTrue()); "it should execute entry action of super states of the state to which state machine is initialized".x(() => entryActionOfSuperStateExecuted .Should().BeTrue()); }
public void Initialize( AsyncPassiveStateMachine <int, int> machine, bool entryActionExecuted) { "establish a state machine".x(() => { machine = new AsyncPassiveStateMachine <int, int>(); machine.AddExtension(this.testExtension); machine.In(TestState) .ExecuteOnEntry(() => entryActionExecuted = true); }); "when state machine is initialized".x(() => machine.Initialize(TestState)); "should not yet execute any entry actions".x(() => entryActionExecuted.Should().BeFalse()); }
public void ExecutingTransition( AsyncPassiveStateMachine <int, int> machine, string actualParameter, string asyncActualParameter, bool exitActionExecuted, bool entryActionExecuted, bool asyncExitActionExecuted, bool asyncEntryActionExecuted) { "establish a state machine with transitions".x(async() => { var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <int, int>(); stateMachineDefinitionBuilder .In(SourceState) .ExecuteOnExit(() => exitActionExecuted = true) .ExecuteOnExit(async() => { asyncExitActionExecuted = true; await Task.Yield(); }) .On(Event).Goto(DestinationState) .Execute((string p) => actualParameter = p) .Execute(async(string p) => { asyncActualParameter = p; await Task.Yield(); }); stateMachineDefinitionBuilder .In(DestinationState) .ExecuteOnEntry(() => entryActionExecuted = true) .ExecuteOnEntry(async() => { asyncEntryActionExecuted = true; await Task.Yield(); }); machine = stateMachineDefinitionBuilder .WithInitialState(SourceState) .Build() .CreatePassiveStateMachine(); machine.AddExtension(CurrentStateExtension); await machine.Start(); }); "when firing an event onto the state machine".x(() => machine.Fire(Event, Parameter)); "it should execute transition by switching state".x(() => CurrentStateExtension.CurrentState.Should().Be(DestinationState)); "it should execute synchronous transition actions".x(() => actualParameter.Should().NotBeNull()); "it should execute asynchronous transition actions".x(() => asyncActualParameter.Should().NotBeNull()); "it should pass parameters to transition action".x(() => actualParameter.Should().Be(Parameter)); "it should execute synchronous exit action of source state".x(() => exitActionExecuted.Should().BeTrue()); "it should execute asynchronous exit action of source state".x(() => asyncExitActionExecuted.Should().BeTrue()); "it should execute synchronous entry action of destination state".x(() => entryActionExecuted.Should().BeTrue()); "it should execute asynchronous entry action of destination state".x(() => asyncEntryActionExecuted.Should().BeTrue()); }
public void LoadingEventsForPassiveStateMachine( AsyncPassiveStateMachine <string, int> machine) { "establish a passive state machine".x(() => { var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <string, int>(); stateMachineDefinitionBuilder .In("A") .On(1) .Goto("B"); stateMachineDefinitionBuilder .In("B") .On(2) .Goto("C"); stateMachineDefinitionBuilder .In("C") .On(3) .Goto("A"); machine = stateMachineDefinitionBuilder .WithInitialState("A") .Build() .CreatePassiveStateMachine(); }); "when it is loaded with Events".x(async() => { var firstEvent = new EventInformation <int>(2, null); var secondEvent = new EventInformation <int>(3, null); var priorityEvent = new EventInformation <int>(1, null); var loader = new StateMachineLoader <string, int>(); loader.SetEvents(new List <EventInformation <int> > { firstEvent, secondEvent, }); loader.SetPriorityEvents(new List <EventInformation <int> > { priorityEvent }); var extension = A.Fake <IExtension <string, int> >(); machine.AddExtension(extension); await machine.Load(loader); A.CallTo(() => extension.Loaded( A <IStateMachineInformation <string, int> > .Ignored, A <Initializable <string> > .Ignored, A <IReadOnlyDictionary <string, string> > .Ignored, A <IReadOnlyCollection <EventInformation <int> > > .That .Matches(c => c.Count == 2 && c.Contains(firstEvent) && c.Contains(secondEvent)), A <IReadOnlyCollection <EventInformation <int> > > .That .Matches(c => c.Count == 1 && c.Contains(priorityEvent)))) .MustHaveHappenedOnceExactly(); }); "it should process those events".x(async() => { var transitionRecords = new List <TransitionRecord>(); machine.TransitionCompleted += (sender, args) => transitionRecords.Add( new TransitionRecord(args.EventId, args.StateId, args.NewStateId)); await machine.Start(); transitionRecords .Should() .HaveCount(3) .And .IsEquivalentInOrder(new List <TransitionRecord> { new TransitionRecord(1, "A", "B"), new TransitionRecord(2, "B", "C"), new TransitionRecord(3, "C", "A") }); }); }
public GossipManager(GossipNode gossipNode) { _gossipNode = gossipNode; _cts = new CancellationTokenSource(); _currentStateExtension = new CurrentStateExtension(); _requests = new ConcurrentDictionary <string, BaseMessage>(); _helloTimer = new Timer(new TimerCallback(HelloTimerHandle)); _heartbeatTimer = new Timer(new TimerCallback(HeartbeatTimerHandle)); _fsm = new AsyncPassiveStateMachine <NodeState, GossipEvent>("GossipManager"); _fsm.AddExtension(new ConsoleLogExtension()); _fsm.AddExtension(_currentStateExtension); _fsm.Initialize(NodeState.Initialized); _fsm.In(NodeState.Initialized) .On(GossipEvent.HelloSend) .Goto(NodeState.HelloSent) .Execute(async() => { // set hello timer await SendHello(); }); _fsm.In(NodeState.HelloSent) .On(GossipEvent.HelloAnswer) .Goto(NodeState.Infected) .Execute <HelloResponse>((msg) => { ReceiveHelloAnswer(msg); }) .On(GossipEvent.HelloExpired) .Goto(NodeState.Initialized) .Execute(async() => { // hello timer re-set await SendHello(); }); _fsm.In(NodeState.Infected) .On(GossipEvent.HeartbeatExpired) .Goto(NodeState.Susceptible) .Execute(async() => { await SendHeartbeat(); }) .On(GossipEvent.HeartbeatAnswer) .Goto(NodeState.Infected) .Execute <HeartbeatResponse>((msg) => { IGossipPeer senderPeer = msg.Members?.FirstOrDefault(); MergeLists(senderPeer, msg.Members); }); _fsm.In(NodeState.Infected) .On(GossipEvent.HelloReceive) .Execute <HelloRequest>(async(msg) => { await SendHelloAnswer(msg); }); _fsm.In(NodeState.Susceptible) .On(GossipEvent.HelloReceive) .Execute <HelloRequest>(async(msg) => { await SendHelloAnswer(msg); }) .On(GossipEvent.HeartbeatReceive) .If <HeartbeatRequest>((msg) => { return(_gossipNode.GossipPeer.Heartbeat < msg.Peer.Heartbeat); }) .Goto(NodeState.Infected) .Execute <HeartbeatRequest>(async(msg) => { await ReceiveHeartbeat(msg); }) .On(GossipEvent.HeartbeatAnswer) .If <HeartbeatResponse>((msg) => { return(msg.Members[0].Heartbeat > _gossipNode.GossipPeer.Heartbeat); }).Goto(NodeState.Infected); _fsm.Start(); }