2
0
mirror of https://github.com/boostorg/msm.git synced 2026-01-19 16:32:11 +00:00
Files
msm/test/Backmp11Deferred.cpp
2025-11-22 12:41:58 +01:00

242 lines
8.2 KiB
C++

// Copyright 2025 Christian Granzin
// Copyright 2010 Christophe Henry
// henry UNDERSCORE christophe AT hotmail DOT com
// This is an extended version of the state machine available in the boost::mpl library
// Distributed under the same license as the original.
// Copyright for the original version:
// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
// under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// back-end
#include "BackCommon.hpp"
// front-end
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#ifndef BOOST_MSM_NONSTANDALONE_TEST
#define BOOST_TEST_MODULE backmp11_deferred
#endif
#include <boost/test/unit_test.hpp>
using namespace boost::msm::front;
using namespace boost::msm::backmp11;
namespace mp11 = boost::mp11;
// Events
struct Event1 {};
struct Event2 {};
struct ManualDeferEvent {};
struct FromHandleAllToHandleNone {};
struct FromHandleNoneToHandleAll {};
struct FromDeferEvent1And2ToDeferEvent1 {};
struct FromDeferEvent1ToHandleNone {};
// Actions
struct Action
{
template <typename Fsm, typename SourceState, typename TargetState>
void operator()(const Event1&, Fsm& fsm, SourceState&, TargetState&)
{
fsm.event1_action_calls++;
}
template <typename Fsm, typename SourceState, typename TargetState>
void operator()(const Event2&, Fsm& fsm, SourceState&, TargetState&)
{
fsm.event2_action_calls++;
}
};
// Common states
struct StateHandleNone : state<> {};
struct StateHandleAll : state<> {};
template<typename T>
struct StateMachineBase_ : state_machine_def<T>
{
bool check_and_reset_event1_action_calls(size_t expected)
{
const bool result = (event1_action_calls == expected);
event1_action_calls = 0;
return result;
}
bool check_and_reset_event2_action_calls(size_t expected)
{
const bool result = (event2_action_calls == expected);
event2_action_calls = 0;
return result;
}
size_t event1_action_calls{};
size_t event2_action_calls{};
};
namespace uml_deferred
{
struct StateDeferEvent1 : state<>
{
using deferred_events = mp11::mp_list<Event1>;
};
struct StateDeferEvent1And2 : state<>
{
using deferred_events = mp11::mp_list<Event1, Event2>;
};
struct StateMachine_ : StateMachineBase_<StateMachine_>
{
using initial_state =
mp11::mp_list<StateHandleAll, StateDeferEvent1, StateDeferEvent1And2>;
using transition_table = mp11::mp_list<
Row<StateHandleNone , FromHandleNoneToHandleAll , StateHandleAll , none >,
Row<StateHandleAll , FromHandleAllToHandleNone , StateHandleNone , none >,
Row<StateDeferEvent1And2 , FromDeferEvent1And2ToDeferEvent1 , StateDeferEvent1 , none >,
Row<StateDeferEvent1 , FromDeferEvent1ToHandleNone , StateHandleNone , none >,
Row<StateHandleAll , Event1 , none , Action>,
Row<StateHandleAll , Event2 , none , Action>,
Row<StateHandleAll , ManualDeferEvent , none , Defer >
>;
};
// Pick a back-end
using Fsms = mp11::mp_list<
#ifndef BOOST_MSM_TEST_SKIP_BACKMP11
state_machine<StateMachine_>,
state_machine<StateMachine_, favor_compile_time_config>
#endif // BOOST_MSM_TEST_SKIP_BACKMP11
>;
BOOST_AUTO_TEST_CASE_TEMPLATE(uml_deferred, Fsm, Fsms)
{
Fsm fsm;
fsm.start();
fsm.process_event(Event1{});
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(0));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(0));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 1);
fsm.process_event(Event2{});
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(0));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(0));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 2);
fsm.process_event(FromDeferEvent1And2ToDeferEvent1{});
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(0));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(1));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 1);
fsm.process_event(FromDeferEvent1ToHandleNone{});
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(1));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(0));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 0);
fsm.process_event(ManualDeferEvent{});
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(0));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(0));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 1);
// The new event gets deferred: +1
// The deferred event gets consumed and deferred again: -1, +1
fsm.process_event(ManualDeferEvent{});
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(0));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(0));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 2);
fsm.stop();
}
} // namespace uml_deferred
// Test case for manual deferral by using transitions with Defer actions.
// Not specified in UML and thus no clear semantics how it should behave.
// Currently a Defer action consumes the event (it gets removed from the queue)
// and then defers it (it gets pushed back to the queue), the Defer action
// returns HANDLED_DEFERRED as processing result).
namespace action_deferred
{
struct StateDeferEvent1 : state<>
{
};
struct StateDeferEvent1And2 : state<>
{
};
struct StateMachine_ : StateMachineBase_<StateMachine_>
{
using activate_deferred_events = int;
using initial_state =
mp11::mp_list<StateHandleAll, StateDeferEvent1And2>;
using transition_table = mp11::mp_list<
Row<StateHandleNone , FromHandleNoneToHandleAll , StateHandleAll , none >,
Row<StateHandleAll , FromHandleAllToHandleNone , StateHandleNone , none >,
Row<StateDeferEvent1And2 , FromDeferEvent1And2ToDeferEvent1 , StateDeferEvent1 , none >,
Row<StateDeferEvent1 , FromDeferEvent1ToHandleNone , StateHandleNone , none >,
Row<StateDeferEvent1 , Event1 , none , Defer >,
Row<StateDeferEvent1And2 , Event1 , none , Defer >,
Row<StateDeferEvent1And2 , Event2 , none , Defer >,
Row<StateHandleAll , Event1 , none , Action>,
Row<StateHandleAll , Event2 , none , Action>
>;
};
// Pick a back-end
using Fsms = mp11::mp_list<
#ifndef BOOST_MSM_TEST_SKIP_BACKMP11
state_machine<StateMachine_>,
state_machine<StateMachine_, favor_compile_time_config>
#endif // BOOST_MSM_TEST_SKIP_BACKMP11
>;
BOOST_AUTO_TEST_CASE_TEMPLATE(action_deferred, Fsm, Fsms)
{
Fsm fsm;
fsm.start();
fsm.process_event(Event1{});
// Processed by StateHandleAll, deferred by StateDeferEvent1And2.
// Queue: Event1
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(1));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 1);
fsm.process_event(Event2{});
// Processed by StateHandleAll, deferred by StateDeferEvent1And2.
// StateHandleAll also processes Event1 again.
// Queue: Event2, Event1
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(1));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(1));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 2);
fsm.process_event(FromDeferEvent1And2ToDeferEvent1{});
// Event2 is no more deferred.
// StateHandleAll processes Event2 & Event1,
// StateDeferEvent1 defers Event1.
// Queue: Event1
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(1));
BOOST_REQUIRE(fsm.check_and_reset_event2_action_calls(1));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 1);
fsm.process_event(FromDeferEvent1ToHandleNone{});
// Event1 is no more deferred.
// StateHandleAll processes Event1.
BOOST_REQUIRE(fsm.check_and_reset_event1_action_calls(1));
BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 0);
fsm.stop();
}
} // namespace action_deferred