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);
 }