public void Guard_is_called_with_the_expected_parameters() { var machine = TinyStateMachine <DoorState, DoorEvents> .Create(DoorState.Closed) .Tr(DoorState.Closed, DoorEvents.Open, DoorState.Open) .Guard((from, trigger, to) => { Assert.That(from, Is.EqualTo(DoorState.Closed)); Assert.That(trigger, Is.EqualTo(DoorEvents.Open)); Assert.That(to, Is.EqualTo(DoorState.Open)); return(true); }) .Tr(DoorState.Open, DoorEvents.Close, DoorState.Closed) .Guard((from, trigger, to) => { Assert.That(from, Is.EqualTo(DoorState.Open)); Assert.That(trigger, Is.EqualTo(DoorEvents.Close)); Assert.That(to, Is.EqualTo(DoorState.Closed)); return(true); }) .Compile(); Parallel.ForEach(GetTokens(machine), storage => { Assert.That(storage.State, Is.EqualTo(DoorState.Closed)); machine.Fire(DoorEvents.Open, storage); Assert.That(storage.State, Is.EqualTo(DoorState.Open)); machine.Fire(DoorEvents.Close, storage); Assert.That(storage.State, Is.EqualTo(DoorState.Closed)); }); }
public void Guard_can_stop_transition() { var machine = TinyStateMachine <DoorState, DoorEvents> .Create(DoorState.Closed) .Tr(DoorState.Closed, DoorEvents.Open, DoorState.Open) .Guard(() => false) .Tr(DoorState.Open, DoorEvents.Close, DoorState.Closed) .Compile(); Parallel.ForEach(GetTokens(machine), storage => { Assert.That(storage.State, Is.EqualTo(DoorState.Closed)); machine.Fire(DoorEvents.Open, storage); Assert.That(storage.State, Is.EqualTo(DoorState.Closed)); }); }
public void Calling_Reset_method_does_not_call_guard_or_trigger_transitions() { var machine = TinyStateMachine <DoorState, DoorEvents> .Create(DoorState.Closed) .Tr(DoorState.Closed, DoorEvents.Open, DoorState.Open) .On((t) => Assert.Fail()) .Guard((from, trigger, to) => false) .Tr(DoorState.Open, DoorEvents.Close, DoorState.Closed) .On((t) => Assert.Fail()) .Guard((from, trigger, to) => false) .Compile(); Parallel.ForEach(GetTokens(machine), storage => { Assert.That(storage.State, Is.EqualTo(DoorState.Closed)); machine.Reset(DoorState.Open, storage); Assert.That(storage.State, Is.EqualTo(DoorState.Open)); machine.Reset(DoorState.Closed, storage); Assert.That(storage.State, Is.EqualTo(DoorState.Closed)); }); }
public void Appropriate_action_is_called_on_transition() { var machine = TinyStateMachine <DoorState, DoorEvents> .Create <DoorMemory>(DoorState.Closed) .Tr <bool>(DoorState.Closed, DoorEvents.Open, DoorState.Open) .On((t, value) => t.wasDoorOpened = value) .Tr(DoorState.Open, DoorEvents.Close, DoorState.Closed) .On((t) => t.wasDoorClosed = true) .Compile(); var doorMemory = new DoorMemory[ParallelTestCount]; for (int i = 0; i < ParallelTestCount; i++) { doorMemory[i] = new DoorMemory(machine); } Parallel.ForEach(doorMemory, storage => { Assert.That(storage.wasDoorOpened, Is.False); Assert.That(storage.wasDoorClosed, Is.False); Assert.Throws <InvalidOperationException>(() => machine.Fire(DoorEvents.Open, storage) ); Assert.Throws <InvalidOperationException>(() => machine.Fire(DoorEvents.Open, storage, 14) ); machine.Fire(DoorEvents.Open, storage, true); Assert.That(storage.wasDoorOpened, Is.True); Assert.That(storage.wasDoorClosed, Is.False); machine.Fire(DoorEvents.Close, storage); Assert.That(storage.wasDoorOpened, Is.True); Assert.That(storage.wasDoorClosed, Is.True); }); }
public void WorkTheDoor() { // Declare the FSM and specify the starting state. var doorFsmCompiler = TinyStateMachine <DoorState, DoorEvents> .Create <Door>(DoorState.Closed); // Now configure the state transition table. doorFsmCompiler.Tr(DoorState.Closed, DoorEvents.Open, DoorState.Open) .Tr <bool>(DoorState.Open, DoorEvents.Close, DoorState.Closed) .On((d, slammed) => d.WasSlammed = slammed); var doorFsm = doorFsmCompiler.Compile(); var door = new Door() { Memory = doorFsm.CreateMemory() }; // As specified in the constructor, the door starts closed. Debug.Assert(door.Memory.IsInState(DoorState.Closed)); // Let's trigger a transition doorFsm.Fire(DoorEvents.Open, door); // Door is now open. Debug.Assert(door.Memory.IsInState(DoorState.Open)); // create as many doors as needed var otherDoor = new Door() { Memory = doorFsm.CreateMemory() }; // The state machine is shared, but the state is not Debug.Assert(otherDoor.Memory.IsInState(DoorState.Closed)); // According to the transition table, closing a door requires // a bool argument. The following will throw an exception bool exceptionWasThrown = false; try { // Let's trigger the other transition doorFsm.Fire(DoorEvents.Close, door); } catch { exceptionWasThrown = true; } Debug.Assert(exceptionWasThrown == true); // Door is still open. Debug.Assert(door.Memory.IsInState(DoorState.Open)); // Slam it this time doorFsm.Fire(DoorEvents.Close, door, true); // Door is now closed. Debug.Assert(door.Memory.IsInState(DoorState.Closed)); // Door is was slammed. Debug.Assert(door.WasSlammed); // still closed Debug.Assert(otherDoor.Memory.IsInState(DoorState.Closed)); // According to the transition table, a closed door // cannot be closed. The following will throw an exception exceptionWasThrown = false; try { doorFsm.Fire(DoorEvents.Close, door); } catch { exceptionWasThrown = true; } Debug.Assert(exceptionWasThrown == true); }
private static TinyStateMachine <DoorState, DoorEvents> .Machine <TMemory> .Compiler GetFixtureCompiler <TMemory>() where TMemory : TinyStateMachine <DoorState, DoorEvents> .IStorage { return(TinyStateMachine <DoorState, DoorEvents> .Create <TMemory>(DoorState.Closed) .Tr(DoorState.Closed, DoorEvents.Open, DoorState.Open) .Tr(DoorState.Open, DoorEvents.Close, DoorState.Closed).compiler); }