int main() { StateMachine stateMachine; stateMachine.setOnStartCallback([] () { std::cout << "state machine started" << std::endl; }); stateMachine.setOnStopCallback([] () { std::cout << "state machine stopped" << std::endl; }); State* state1 = new State(); state1->setOnEnterCallback([] (const Event*) { std::cout << "entering state1" << std::endl; }); state1->setOnExitCallback([] (const Event*) { std::cout << "exiting state1" << std::endl; }); State* state2 = new State(); state2->setOnEnterCallback([] (const Event*) { std::cout << "entering state2" << std::endl; }); state2->setOnExitCallback([] (const Event*) { std::cout << "exiting state2" << std::endl; }); State* state3 = new State(); state3->setOnEnterCallback([] (const Event*) { std::cout << "entering state3" << std::endl; }); state3->setOnExitCallback([] (const Event*) { std::cout << "exiting state3" << std::endl; }); stateMachine.addTransition(12, state1, state2); stateMachine.addTransition(13, state1, state3); stateMachine.addTransition(23, state2, state3); stateMachine.addTransition(31, state3, state1); stateMachine.setInitialState(state1); stateMachine.start(); stateMachine.postEvent(new Event(12)); stateMachine.postEvent(new Event(23)); stateMachine.postEvent(new Event(31)); stateMachine.postEvent(new Event(13)); return 0; }
GEP_UNITTEST_TEST(StateMachine, UpdateStepBehavior) { GEP_UNITTEST_SETUP_EVENT_GLOBALS; auto& logging = TestLogging::instance(); //DummyLogging::instance(); // set up update framework _updateFramework.setElapsedTime(10.0f); // Add at least one callback so we know that we are still updating size_t frameCount = 0; _updateFramework.registerUpdateCallback([&](float){ logging.logWarning("frame %u:", frameCount); }); StateMachineFactory factory(&g_stdAllocator); StateMachine* pMainFsm = nullptr; // Test data. size_t conditionCheckCount = 0; size_t updateCount = 0; // Actual tests. Don't put test data in here that has to be referenced // by the state machines, as they will get out of scope when pMainFsm // is actually run. { pMainFsm = factory.create("fsm"); pMainFsm->setLogging(&logging); auto pState_A = pMainFsm->create<State>("A"); auto pState_B = pMainFsm->create<State>("B"); auto pState_C = pMainFsm->create<State>("C"); pState_C->setLeaveCondition([](){ return true; }); // Adding state transitions pMainFsm->addTransition("__enter", "A"); pMainFsm->addTransition("A", "B", [&](){ // This should be hit conditionCheckCount++; return true; }); pMainFsm->addTransition("B", "C", [&](){ // This should not be hit conditionCheckCount++; return true; }); pMainFsm->addTransition("C", "__leave"); pState_B->getLeaveEvent()->registerListener([&](State::LeaveEventData* pData){ GEP_ASSERT(conditionCheckCount == 1, "Condition check 'A->B' not performed!"); conditionCheckCount = 0; pData->setNextState(pState_C); return gep::EventResult::Handled; }); pState_C->getLeaveEvent()->registerListener([&](State::LeaveEventData*){ GEP_ASSERT(conditionCheckCount == 0, "Condition check 'B->C' should not have been performed!"); conditionCheckCount = 0; return gep::EventResult::Handled; }); // Set update event listeners. They should use the UpdateStepBehavior options. ////////////////////////////////////////////////////////////////////////// pState_A->getUpdateEvent()->registerListener([&](State::UpdateEventData* pData){ pData->setUpdateStepBehavior(State::UpdateStepBehavior::Leave); updateCount++; return gep::EventResult::Handled; }); pState_B->getUpdateEvent()->registerListener([&](State::UpdateEventData* pData){ pData->setUpdateStepBehavior(State::UpdateStepBehavior::LeaveWithNoConditionChecks); return gep::EventResult::Handled; }); } pMainFsm->setLogging(&logging); // Run the machine pMainFsm->run(_updateFramework); for(frameCount = 0; frameCount < 5; ++frameCount) { _updateFramework.run(); } GEP_ASSERT(updateCount > 0, "State A should have at least be updated once!"); }