// 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) #ifndef BOOST_MSM_NONSTANDALONE_TEST #define BOOST_TEST_MODULE backmp11_copy_move_test #endif #include // back-end #include "Backmp11.hpp" #include //front-end #include "FrontCommon.hpp" #include "Utils.hpp" namespace msm = boost::msm; namespace mp11 = boost::mp11; using namespace msm::front; using namespace msm::backmp11; namespace { // Events. struct EnterSubmachine{}; // States. struct MyState : public test::StateBase { MyState() = default; MyState(const MyState&) { copied_to_counter += 1; } MyState& operator=(const MyState&) { copied_to_counter += 1; return *this; } MyState(MyState&&) { moved_to_counter += 1; } MyState& operator=(MyState&&) { moved_to_counter += 1; return *this; } size_t copied_to_counter{}; size_t moved_to_counter{}; }; struct Context { int foo{42}; }; using Observer = msm::backmp11::default_observer; template struct hierarchical_state_machine { struct StateMachine_; struct ConfigWithRootSm; using StateMachine = state_machine; struct ConfigWithRootSm : Config { using root_sm = StateMachine; using context = Context; using observer = msm::backmp11::observer_ref; }; struct Submachine_ : public test::StateMachineBase_ { using initial_state = MyState; }; using Submachine = state_machine; struct StateMachine_ : public test::StateMachineBase_ { using initial_state = MyState; using transition_table = mp11::mp_list< Row >; }; }; using TestMachines = mp11::mp_list< hierarchical_state_machine<>, hierarchical_state_machine >; BOOST_AUTO_TEST_CASE_TEMPLATE(copy_operators, StateMachine, TestMachines) { using TestMachine = typename StateMachine::StateMachine; Context context; Observer observer; TestMachine test_machine{context, observer}; test_machine.start(); test_machine.process_event(EnterSubmachine{}); BOOST_REQUIRE(test_machine.template is_state_active()); { TestMachine other_test_machine{test_machine}; BOOST_REQUIRE(other_test_machine.get_context().foo == 42); auto& other_submachine = other_test_machine.template get_state(); BOOST_REQUIRE(other_test_machine.template is_state_active()); BOOST_REQUIRE(&test_machine.get_root_sm() != &other_test_machine.get_root_sm()); auto& other_submachine_state = other_submachine.template get_state(); ASSERT_ONE_AND_RESET(other_submachine_state.copied_to_counter); } { TestMachine other_test_machine{context, observer}; other_test_machine = test_machine; BOOST_REQUIRE(other_test_machine.get_context().foo == 42); auto& other_submachine = other_test_machine.template get_state(); BOOST_REQUIRE(other_test_machine.template is_state_active()); BOOST_REQUIRE(&test_machine.get_root_sm() != &other_test_machine.get_root_sm()); auto& other_submachine_state = other_submachine.template get_state(); ASSERT_ONE_AND_RESET(other_submachine_state.copied_to_counter); } test_machine.stop(); } BOOST_AUTO_TEST_CASE_TEMPLATE(move_operators, StateMachine, TestMachines) { using TestMachine = typename StateMachine::StateMachine; Context context; Observer observer; TestMachine test_machine{context, observer}; test_machine.start(); test_machine.process_event(EnterSubmachine{}); BOOST_REQUIRE(test_machine.template is_state_active()); { TestMachine other_test_machine{std::move(test_machine)}; BOOST_REQUIRE(other_test_machine.get_context().foo == 42); auto& other_submachine = other_test_machine.template get_state(); BOOST_REQUIRE(other_test_machine.template is_state_active()); BOOST_REQUIRE(&test_machine.get_root_sm() != &other_test_machine.get_root_sm()); auto& other_submachine_state = other_submachine.template get_state(); ASSERT_ONE_AND_RESET(other_submachine_state.moved_to_counter); } { TestMachine other_test_machine{context, observer}; other_test_machine = std::move(test_machine); BOOST_REQUIRE(other_test_machine.get_context().foo == 42); auto& other_submachine = other_test_machine.template get_state(); BOOST_REQUIRE(other_test_machine.template is_state_active()); BOOST_REQUIRE(&test_machine.get_root_sm() != &other_test_machine.get_root_sm()); auto& other_submachine_state = other_submachine.template get_state(); ASSERT_ONE_AND_RESET(other_submachine_state.moved_to_counter); } test_machine.stop(); } BOOST_AUTO_TEST_CASE_TEMPLATE(copy_event_pool, StateMachine, TestMachines) { using TestMachine = typename StateMachine::StateMachine; Context context; Observer observer; TestMachine test_machine{context, observer}; test_machine.start(); test_machine.enqueue_event(EnterSubmachine{}); { TestMachine other_test_machine{context, observer}; other_test_machine = test_machine; BOOST_REQUIRE(other_test_machine.process_event_pool() == 1); BOOST_REQUIRE(other_test_machine.template is_state_active()); } BOOST_REQUIRE(test_machine.process_event_pool() == 1); BOOST_REQUIRE(test_machine.template is_state_active()); test_machine.stop(); } BOOST_AUTO_TEST_CASE_TEMPLATE(move_event_pool, StateMachine, TestMachines) { using TestMachine = typename StateMachine::StateMachine; Context context; Observer observer; TestMachine test_machine{context, observer}; test_machine.start(); test_machine.enqueue_event(EnterSubmachine{}); { TestMachine other_test_machine{std::move(test_machine)}; BOOST_REQUIRE(other_test_machine.process_event_pool() == 1); BOOST_REQUIRE(other_test_machine.template is_state_active()); } BOOST_REQUIRE(test_machine.process_event_pool() == 0); BOOST_REQUIRE(test_machine.template is_state_active()); test_machine.stop(); } } // namespace