예제 #1
0
        public void ExecuteTransitionWithHistoryTypeDeep()
        {
            var stateContainer = new StateContainer <States, Events>();
            var testee         = new StateMachineBuilder <States, Events>()
                                 .WithStateContainer(stateContainer)
                                 .Build();

            testee.EnterInitialState(stateContainer, stateContainer, this.stateDefinitions, States.D1B);
            testee.Fire(Events.A, stateContainer, stateContainer, this.stateDefinitions);

            this.ClearRecords();

            testee.Fire(Events.D, stateContainer, stateContainer, this.stateDefinitions);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.D1B));

            this.CheckRecord <ExitRecord>(States.A);
            this.CheckRecord <EntryRecord>(States.D);
            this.CheckRecord <EntryRecord>(States.D1);
            this.CheckRecord <EntryRecord>(States.D1B);
            this.CheckNoRemainingRecords();
        }
예제 #2
0
        public void MissingTransition()
        {
            var stateDefinitionBuilder = new StateDefinitionsBuilder <States, Events>();

            stateDefinitionBuilder
            .In(States.A)
            .On(Events.B)
            .Goto(States.B);
            var stateDefinitions = stateDefinitionBuilder.Build();
            var stateContainer   = new StateContainer <States, Events>();

            var testee = new StateMachineBuilder <States, Events>()
                         .WithStateContainer(stateContainer)
                         .Build();

            var declined = false;

            testee.TransitionDeclined += (sender, e) => { declined = true; };

            testee.EnterInitialState(stateContainer, stateDefinitions, States.A);

            testee.Fire(Events.C, stateContainer, stateContainer, stateDefinitions);

            declined.Should().BeTrue("Declined event was not fired");
            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.A));
        }
예제 #3
0
        public async Task GuardWithoutArguments()
        {
            var stateDefinitionsBuilder = new StateDefinitionsBuilder <States, Events>();

            stateDefinitionsBuilder
            .In(States.A)
            .On(Events.B)
            .If(() => false).Goto(States.C)
            .If(() => true).Goto(States.B);
            var stateDefinitions = stateDefinitionsBuilder.Build();

            var stateContainer = new StateContainer <States, Events>();
            var testee         = new StateMachineBuilder <States, Events>()
                                 .WithStateContainer(stateContainer)
                                 .Build();

            await testee.EnterInitialState(stateContainer, stateContainer, stateDefinitions, States.A)
            .ConfigureAwait(false);

            await testee.Fire(Events.B, Missing.Value, stateContainer, stateContainer, stateDefinitions)
            .ConfigureAwait(false);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.B));
        }
        public async Task StaysInCurrentState_WhenAGuardThrowsAnException()
        {
            var eventArguments = new object[] { 1, 2, "test" };
            var exception      = new Exception();

            bool ThrowingGuard() => throw exception;

            var stateDefinitionsBuilder = new StateDefinitionsBuilder <States, Events>();

            stateDefinitionsBuilder
            .In(States.A)
            .On(Events.B)
            .If(ThrowingGuard).Goto(States.B);
            var stateDefinitions = stateDefinitionsBuilder.Build();

            var stateContainer = new StateContainer <States, Events>();
            var testee         = new StateMachineBuilder <States, Events>()
                                 .WithStateContainer(stateContainer)
                                 .Build();

            testee.TransitionExceptionThrown += (sender, eventArgs) => { };

            await testee.EnterInitialState(stateContainer, stateDefinitions, States.A)
            .ConfigureAwait(false);

            await testee.Fire(Events.B, eventArguments, stateContainer, stateDefinitions)
            .ConfigureAwait(false);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.A));
        }
예제 #5
0
        public void GuardWithASingleArgument()
        {
            var stateDefinitionBuilder = new StateDefinitionsBuilder <States, Events>();

            stateDefinitionBuilder
            .In(States.A)
            .On(Events.B)
            .If <int>(SingleIntArgumentGuardReturningFalse).Goto(States.C)
            .If(() => false).Goto(States.D)
            .If(() => false).Goto(States.E)
            .If <int>(SingleIntArgumentGuardReturningTrue).Goto(States.B);
            var stateDefinitions = stateDefinitionBuilder
                                   .Build();
            var stateContainer = new StateContainer <States, Events>();

            var testee = new StateMachineBuilder <States, Events>()
                         .WithStateContainer(stateContainer)
                         .Build();

            testee.EnterInitialState(stateContainer, stateDefinitions, States.A);

            testee.Fire(Events.B, 3, stateContainer, stateDefinitions);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.B));
        }
예제 #6
0
        public void InternalTransition()
        {
            var executed = false;

            var stateDefinitionBuilder = new StateDefinitionsBuilder <States, Events>();

            stateDefinitionBuilder
            .In(States.A)
            .On(Events.A)
            .Execute(() => executed = true);
            var stateDefinitions = stateDefinitionBuilder.Build();
            var stateContainer   = new StateContainer <States, Events>();

            var testee = new StateMachineBuilder <States, Events>()
                         .WithStateContainer(stateContainer)
                         .Build();

            testee.EnterInitialState(stateContainer, stateDefinitions, States.A);

            testee.Fire(Events.A, stateContainer, stateContainer, stateDefinitions);

            executed.Should().BeTrue("internal transition was not executed.");
            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.A));
        }
예제 #7
0
        public void ExecuteTransitionBetweenStatesOnDifferentLevelsDownwards()
        {
            var stateContainer = new StateContainer <States, Events>();
            var testee         = new StateMachineBuilder <States, Events>()
                                 .WithStateContainer(stateContainer)
                                 .Build();

            testee.EnterInitialState(stateContainer, stateContainer, this.stateDefinitions, States.B2);

            this.ClearRecords();

            testee.Fire(Events.C1B, stateContainer, stateContainer, this.stateDefinitions);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.C1B));

            this.CheckRecord <ExitRecord>(States.B2);
            this.CheckRecord <ExitRecord>(States.B);
            this.CheckRecord <EntryRecord>(States.C);
            this.CheckRecord <EntryRecord>(States.C1);
            this.CheckRecord <EntryRecord>(States.C1B);
            this.CheckNoRemainingRecords();
        }
예제 #8
0
        public void SetsCurrentStateOnLoadingFromPersistedState(string dummyName, Func <StateMachineDefinition <States, Events>, IStateMachine <States, Events> > createStateMachine)
        {
            var loader    = A.Fake <IStateMachineLoader <States> >();
            var extension = A.Fake <IExtension <States, Events> >();

            A.CallTo(() => loader.LoadCurrentState())
            .Returns(Initializable <States> .Initialized(States.C));

            var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <States, Events>();

            stateMachineDefinitionBuilder
            .In(States.A);
            stateMachineDefinitionBuilder
            .In(States.C);
            var stateMachineDefinition = stateMachineDefinitionBuilder
                                         .WithInitialState(States.A)
                                         .Build();

            var testee = createStateMachine(stateMachineDefinition);

            testee.AddExtension(extension);

            testee.Load(loader);

            A.CallTo(() =>
                     extension.Loaded(
                         A <IStateMachineInformation <States, Events> > .Ignored,
                         A <Initializable <States> >
                         .That
                         .Matches(currentState =>
                                  currentState.IsInitialized &&
                                  currentState.ExtractOrThrow() == States.C),
                         A <IReadOnlyDictionary <States, States> > .Ignored))
            .MustHaveHappenedOnceExactly();
        }
예제 #9
0
        private static async Task SwitchStateTo(
            IStateDefinition <TState, TEvent> newState,
            StateContainer <TState, TEvent> stateContainer,
            IStateMachineInformation <TState, TEvent> stateMachineInformation)
        {
            var oldState = stateContainer.CurrentState.ExtractOr(null);

            stateContainer.CurrentState = Initializable <IStateDefinition <TState, TEvent> > .Initialized(newState);

            await stateContainer
            .ForEach(extension =>
                     extension.SwitchedState(stateMachineInformation, oldState, newState))
            .ConfigureAwait(false);
        }
예제 #10
0
        public void ExtractOr()
        {
            Initializable <string>
            .Initialized("A")
            .ExtractOr("B")
            .Should()
            .Be("A");

            Initializable <string>
            .UnInitialized()
            .ExtractOr("B")
            .Should()
            .Be("B");
        }
예제 #11
0
        public void Map()
        {
            Initializable <SomeClass>
            .Initialized(new SomeClass { SomeValue = "A" })
            .Map(x => x.SomeValue)
            .Should()
            .BeEquivalentTo(Initializable <string> .Initialized("A"));

            Initializable <SomeClass>
            .UnInitialized()
            .Map(x => x.SomeValue)
            .Should()
            .BeEquivalentTo(Initializable <string> .UnInitialized());
        }
예제 #12
0
        public void IsInitialized()
        {
            Initializable <SomeClass>
            .Initialized(new SomeClass())
            .IsInitialized
            .Should()
            .BeTrue();

            Initializable <SomeClass>
            .UnInitialized()
            .IsInitialized
            .Should()
            .BeFalse();
        }
예제 #13
0
        public void ExtractOrThrow()
        {
            Initializable <string>
            .Initialized("A")
            .ExtractOrThrow()
            .Should()
            .Be("A");

            Initializable <string>
            .UnInitialized()
            .Invoking(x => x.ExtractOrThrow())
            .Should()
            .Throw <InvalidOperationException>()
            .WithMessage(ExceptionMessages.ValueNotInitialized);
        }
예제 #14
0
        public void InternalTransition()
        {
            var stateContainer = new StateContainer <States, Events>();
            var testee         = new StateMachineBuilder <States, Events>()
                                 .WithStateContainer(stateContainer)
                                 .Build();

            testee.EnterInitialState(stateContainer, stateContainer, this.stateDefinitions, States.A);
            this.ClearRecords();

            testee.Fire(Events.A, stateContainer, stateContainer, this.stateDefinitions);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.A));
        }
예제 #15
0
        public void InitializeToTopLevelState()
        {
            var stateContainer = new StateContainer <States, Events>();
            var testee         = new StateMachineBuilder <States, Events>()
                                 .WithStateContainer(stateContainer)
                                 .Build();

            testee.EnterInitialState(stateContainer, stateContainer, this.stateDefinitions, States.A);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.A));

            this.CheckRecord <EntryRecord>(States.A);
            this.CheckNoRemainingRecords();
        }
예제 #16
0
        private static async Task SwitchStateTo(
            IStateDefinition <TState, TEvent> newState,
            StateContainer <TState, TEvent> stateContainer,
            IStateDefinitionDictionary <TState, TEvent> stateDefinitions)
        {
            var oldState = stateContainer
                           .CurrentStateId
                           .Map(x => stateDefinitions[x])
                           .ExtractOr(null);

            stateContainer.CurrentStateId = Initializable <TState> .Initialized(newState.Id);

            await stateContainer
            .ForEach(extension =>
                     extension.SwitchedState(oldState, newState))
            .ConfigureAwait(false);
        }
예제 #17
0
        public void ExceptionThrowingGuard()
        {
            var       eventArguments        = new object[] { 1, 2, "test" };
            var       exception             = new Exception();
            States?   recordedStateId       = null;
            Events?   recordedEventId       = null;
            object    recordedEventArgument = null;
            Exception recordedException     = null;

            var stateDefinitionsBuilder = new StateDefinitionsBuilder <States, Events>();

            stateDefinitionsBuilder.In(States.A)
            .On(Events.B)
            .If(() => throw exception)
            .Goto(States.B);
            var stateDefinitions = stateDefinitionsBuilder.Build();

            var stateContainer = new StateContainer <States, Events>();

            var testee = new StateMachineBuilder <States, Events>()
                         .WithStateContainer(stateContainer)
                         .Build();

            var transitionDeclined = false;

            testee.TransitionDeclined        += (sender, e) => transitionDeclined = true;
            testee.TransitionExceptionThrown += (sender, eventArgs) =>
            {
                recordedStateId       = eventArgs.StateId;
                recordedEventId       = eventArgs.EventId;
                recordedEventArgument = eventArgs.EventArgument;
                recordedException     = eventArgs.Exception;
            };

            testee.EnterInitialState(stateContainer, stateContainer, stateDefinitions, States.A);

            testee.Fire(Events.B, eventArguments, stateContainer, stateContainer, stateDefinitions);

            recordedStateId.Should().Be(States.A);
            recordedEventId.Should().Be(Events.B);
            recordedEventArgument.Should().Be(eventArguments);
            recordedException.Should().Be(exception);
            stateContainer.CurrentStateId.Should().BeEquivalentTo(Initializable <States> .Initialized(States.A));
            transitionDeclined.Should().BeTrue("transition was not declined.");
        }
예제 #18
0
        public void EntryActionWhenThrowingExceptionThenNotificationAndStateIsEntered()
        {
            var       eventArguments        = new object[] { 1, 2, "test" };
            var       exception             = new Exception();
            States?   recordedStateId       = null;
            Events?   recordedEventId       = null;
            object    recordedEventArgument = null;
            Exception recordedException     = null;

            var stateDefinitionsBuilder = new StateDefinitionsBuilder <States, Events>();

            stateDefinitionsBuilder
            .In(States.A)
            .On(Events.B)
            .Goto(States.B);
            stateDefinitionsBuilder
            .In(States.B)
            .ExecuteOnEntry(() => throw exception);
            var stateDefinitions = stateDefinitionsBuilder.Build();

            var stateContainer = new StateContainer <States, Events>();

            var testee = new StateMachineBuilder <States, Events>()
                         .WithStateContainer(stateContainer)
                         .Build();

            testee.TransitionExceptionThrown += (sender, eventArgs) =>
            {
                recordedStateId       = eventArgs.StateId;
                recordedEventId       = eventArgs.EventId;
                recordedEventArgument = eventArgs.EventArgument;
                recordedException     = eventArgs.Exception;
            };

            testee.EnterInitialState(stateContainer, stateContainer, stateDefinitions, States.A);

            testee.Fire(Events.B, eventArguments, stateContainer, stateContainer, stateDefinitions);

            recordedStateId.Should().Be(States.A);
            recordedEventId.Should().Be(Events.B);
            recordedEventArgument.Should().Be(eventArguments);
            recordedException.Should().Be(exception);
            stateContainer.CurrentStateId.Should().BeEquivalentTo(Initializable <States> .Initialized(States.B));
        }
예제 #19
0
        public void InitializeStateWithSubStates()
        {
            var stateContainer = new StateContainer <States, Events>();
            var testee         = new StateMachineBuilder <States, Events>()
                                 .WithStateContainer(stateContainer)
                                 .Build();

            stateContainer.SetLastActiveStateFor(States.D, this.stateDefinitions[States.D1]);
            stateContainer.SetLastActiveStateFor(States.D1, this.stateDefinitions[States.D1A]);

            testee.EnterInitialState(stateContainer, stateContainer, this.stateDefinitions, States.D);

            stateContainer
            .CurrentStateId
            .Should()
            .BeEquivalentTo(Initializable <States> .Initialized(States.D1A));

            this.CheckRecord <EntryRecord>(States.D);
            this.CheckRecord <EntryRecord>(States.D1);
            this.CheckRecord <EntryRecord>(States.D1A);
            this.CheckNoRemainingRecords();
        }
예제 #20
0
        public void Loading(
            StateMachineSaver <State> saver,
            StateMachineLoader <State> loader,
            FakeExtension extension,
            State sourceState,
            State targetState)
        {
            "establish a saved state machine with history".x(() =>
            {
                var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <State, Event>();
                SetupStates(stateMachineDefinitionBuilder);
                var machine = stateMachineDefinitionBuilder
                              .WithInitialState(State.A)
                              .Build()
                              .CreatePassiveStateMachine();

                machine.Start();
                machine.Fire(Event.S2); // set history of super state S
                machine.Fire(Event.B);  // set current state to B

                saver  = new StateMachineSaver <State>();
                loader = new StateMachineLoader <State>();

                machine.Save(saver);
            });

            "when state machine is loaded".x(() =>
            {
                loader.SetCurrentState(saver.CurrentStateId);
                loader.SetHistoryStates(saver.HistoryStates);

                var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <State, Event>();
                SetupStates(stateMachineDefinitionBuilder);
                var loadedMachine = stateMachineDefinitionBuilder
                                    .WithInitialState(State.A)
                                    .Build()
                                    .CreatePassiveStateMachine();

                extension = new FakeExtension();
                loadedMachine.AddExtension(extension);

                loadedMachine.Load(loader);

                loadedMachine.TransitionCompleted += (sender, args) =>
                {
                    sourceState = args.StateId;
                    targetState = args.NewStateId;
                };

                loadedMachine.Start();
                loadedMachine.Fire(Event.S);
            });

            "it should reset current state".x(() =>
                                              sourceState
                                              .Should()
                                              .Be(State.B));

            "it should reset all history states of super states".x(() =>
                                                                   targetState
                                                                   .Should()
                                                                   .Be(State.S2));

            "it should notify extensions".x(()
                                            => extension
                                            .LoadedCurrentState
                                            .Should()
                                            .BeEquivalentTo(Initializable <State> .Initialized(State.B)));
        }