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 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 override void Loaded( IStateMachineInformation <State, Event> stateMachineInformation, Initializable <State> loadedCurrentState, IDictionary <State, State> loadedHistoryStates) { this.LoadedCurrentState.Add(loadedCurrentState.Value); }
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 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 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)); }
private void InsertEntry(Initializable entry) { if ((entry != null) && !toInitialize.ContainsKey(entry)) { toInitialize.Add(entry, null); } }
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(); }
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 LoadingNonInitializedStateMachine( PassiveStateMachine <State, Event> loadedMachine) { "when a not started state machine is loaded".x(() => { var loader = new StateMachineLoader <State>(); loader.SetCurrentState(Initializable <State> .UnInitialized()); loader.SetHistoryStates(new Dictionary <State, State>()); var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <State, Event>(); SetupStates(stateMachineDefinitionBuilder); loadedMachine = stateMachineDefinitionBuilder .WithInitialState(State.A) .Build() .CreatePassiveStateMachine(); loadedMachine.Load(loader); }); "it should not be initialized already".x(() => { var stateMachineSaver = new StateMachineSaver <State>(); loadedMachine.Save(stateMachineSaver); stateMachineSaver .CurrentStateId .IsInitialized .Should() .BeFalse(); }); }
/// <summary> /// Initializes a new instance of the <see cref="StateMachine{TState,TEvent}"/> class. /// </summary> /// <param name="name">The name of this state machine used in log messages.</param> /// <param name="factory">The factory used to create internal instances.</param> public StateMachine(string name, IFactory <TState, TEvent> factory) { this.name = name; this.factory = factory ?? new StandardFactory <TState, TEvent>(this, this); this.states = new StateDictionary <TState, TEvent>(this.factory); this.extensions = new List <IExtension <TState, TEvent> >(); this.initialStateId = new Initializable <TState>(); }
public async Task <bool> Load(IAsyncStateMachineLoader <TState> stateMachineLoader) { Guard.AgainstNullArgument(nameof(stateMachineLoader), stateMachineLoader); this.CheckThatStateMachineIsNotAlreadyInitialized(); Initializable <TState> loadedCurrentState = await stateMachineLoader.LoadCurrentState().ConfigureAwait(false); IDictionary <TState, TState> historyStates = await stateMachineLoader.LoadHistoryStates().ConfigureAwait(false); var initialized = SetCurrentState(); LoadHistoryStates(); NotifyExtensions(); return(initialized); bool SetCurrentState() { if (loadedCurrentState.IsInitialized) { this.currentState = this.states[loadedCurrentState.Value]; return(true); } this.currentState = null; return(false); } void LoadHistoryStates() { foreach (KeyValuePair <TState, TState> historyState in historyStates) { IState <TState, TEvent> superState = this.states[historyState.Key]; IState <TState, TEvent> lastActiveState = this.states[historyState.Value]; if (!superState.SubStates.Contains(lastActiveState)) { throw new InvalidOperationException(ExceptionMessages.CannotSetALastActiveStateThatIsNotASubState); } superState.LastActiveState = lastActiveState; } } void NotifyExtensions() { this.extensions.ForEach( extension => extension.Loaded( this, loadedCurrentState, historyStates)); } }
public void IsInitialized() { Initializable <SomeClass> .Initialized(new SomeClass()) .IsInitialized .Should() .BeTrue(); Initializable <SomeClass> .UnInitialized() .IsInitialized .Should() .BeFalse(); }
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 ExtractOr() { Initializable <string> .Initialized("A") .ExtractOr("B") .Should() .Be("A"); Initializable <string> .UnInitialized() .ExtractOr("B") .Should() .Be("B"); }
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 ExtractOrThrow() { Initializable <string> .Initialized("A") .ExtractOrThrow() .Should() .Be("A"); Initializable <string> .UnInitialized() .Invoking(x => x.ExtractOrThrow()) .Should() .Throw <InvalidOperationException>() .WithMessage(ExceptionMessages.ValueNotInitialized); }
/// <summary> /// Generates a report of the state machine. /// </summary> /// <param name="name">The name of the state machine.</param> /// <param name="states">The states.</param> /// <param name="initialStateId">The initial state id.</param> public void Report(string name, IEnumerable <IState <TState, TEvent> > states, Initializable <TState> initialStateId) { this.edgeId = 0; this.initialStateId = initialStateId; Ensure.ArgumentNotNull(states, "states"); XElement graph = CreateGraph(); this.AddNodes(graph, states); this.AddEdges(graph, states); XDocument doc = CreateXmlDocument(graph); doc.Save(this.outputStream); }
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)); }
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 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 void FlattenNode(List <Initializable> initOrder, Initializable node) { if (!initOrder.Contains(node)) { var dependencies = toInitialize[node]; var index = initOrder.Count; initOrder.Add(node); foreach (var dependency in dependencies) { FlattenNode(initOrder, dependency); } initOrder.Add(node); initOrder.RemoveAt(index); } }
public void Register(Initializable target, params Initializable[] dependencies) { var first = toInitialize.Count == 0; InsertEntry(target); toInitialize[target] = dependencies; foreach (var dependency in dependencies) { InsertEntry(dependency); } if (first) { state.RunCoroutine(InitializeAll()); } }
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."); }
// Start is called before the first frame update public void Init() { for (int i = 0; i < initializable.Length; i++) { if (!initializable[i]) { Debug.LogError("initializable[" + i + "]Not found"); continue; } Initializable init = initializable[i].GetComponent <Initializable>(); if (init == null) { Debug.LogError("initializable[" + i + "] Init Script Not found"); continue; } init.Init(); } }
/// <summary> /// Generates a report of the state machine. /// </summary> /// <param name="name">The name of the state machine.</param> /// <param name="states">The states.</param> /// <param name="initialState">The initial state id.</param> public void Report(string name, IEnumerable<IState<TState, TEvent>> states, Initializable<TState> initialState) { var statesList = states.ToList(); this.edgeId = 0; this.initialStateId = initialState; Ensure.ArgumentNotNull(statesList, "states"); XElement graph = CreateGraph(); this.AddNodes(graph, statesList); this.AddEdges(graph, statesList); XDocument doc = CreateXmlDocument(graph); doc.Save(this.textWriter); }
public void SetsHistoryStatesOnLoadingFromPersistedState() { var exitedD2 = false; var stateMachineDefinitionBuilder = new StateMachineDefinitionBuilder <States, Events>(); stateMachineDefinitionBuilder .DefineHierarchyOn(States.D) .WithHistoryType(HistoryType.Deep) .WithInitialSubState(States.D1) .WithSubState(States.D2); stateMachineDefinitionBuilder .In(States.A) .On(Events.D).Goto(States.D) .On(Events.A); stateMachineDefinitionBuilder .In(States.D2) .ExecuteOnExit(() => exitedD2 = true) .On(Events.A).Goto(States.A); var testee = stateMachineDefinitionBuilder .WithInitialState(States.A) .Build() .CreatePassiveStateMachine(); var loader = A.Fake <IStateMachineLoader <States, Events> >(); A.CallTo(() => loader.LoadHistoryStates()) .Returns(new Dictionary <States, States> { { States.D, States.D2 } }); A.CallTo(() => loader.LoadCurrentState()) .Returns(Initializable <States> .UnInitialized()); testee.Load(loader); testee.Start(); testee.Fire(Events.D); // should go to loaded last active state D2, not initial state D1 exitedD2 = false; testee.Fire(Events.A); testee.Stop(); exitedD2.Should().BeTrue(); }
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 X1(Initializable initializable) { this.initializable = initializable; }