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(); }
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)); }
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)); }
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)); }
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)); }
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(); }
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(); }
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); }
public void ExtractOr() { Initializable <string> .Initialized("A") .ExtractOr("B") .Should() .Be("A"); Initializable <string> .UnInitialized() .ExtractOr("B") .Should() .Be("B"); }
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()); }
public void IsInitialized() { Initializable <SomeClass> .Initialized(new SomeClass()) .IsInitialized .Should() .BeTrue(); Initializable <SomeClass> .UnInitialized() .IsInitialized .Should() .BeFalse(); }
public void ExtractOrThrow() { Initializable <string> .Initialized("A") .ExtractOrThrow() .Should() .Be("A"); Initializable <string> .UnInitialized() .Invoking(x => x.ExtractOrThrow()) .Should() .Throw <InvalidOperationException>() .WithMessage(ExceptionMessages.ValueNotInitialized); }
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)); }
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(); }
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); }
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."); }
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)); }
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(); }
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))); }