Ejemplo n.º 1
0
        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));
        }
Ejemplo n.º 2
0
        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());
        }
Ejemplo n.º 3
0
        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());
        }
Ejemplo n.º 4
0
        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());
        }
Ejemplo n.º 5
0
        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));
        }
Ejemplo n.º 6
0
        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"));
        }
Ejemplo n.º 7
0
        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));
        }
Ejemplo n.º 8
0
        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());
        }
Ejemplo n.º 11
0
        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());
        }
Ejemplo n.º 12
0
        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());
        }
Ejemplo n.º 13
0
        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")
                });
            });
        }
Ejemplo n.º 14
0
        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();
        }