diff options
Diffstat (limited to 'src/boost/libs/msm/test')
30 files changed, 8107 insertions, 0 deletions
diff --git a/src/boost/libs/msm/test/Anonymous.cpp b/src/boost/libs/msm/test/Anonymous.cpp new file mode 100644 index 000000000..0055bb5db --- /dev/null +++ b/src/boost/libs/msm/test/Anonymous.cpp @@ -0,0 +1,184 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + + + // front-end: define the FSM structure + struct my_machine_ : public msm::front::state_machine_def<my_machine_> + { + unsigned int state2_to_state3_counter; + unsigned int state3_to_state4_counter; + unsigned int always_true_counter; + unsigned int always_false_counter; + + my_machine_(): + state2_to_state3_counter(0), + state3_to_state4_counter(0), + always_true_counter(0), + always_false_counter(0) + {} + + // The list of FSM states + struct State1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct State3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct State4 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + void State2ToState3(none const&) { ++state2_to_state3_counter; } + void State3ToState4(none const&) { ++state3_to_state4_counter; } + // guard conditions + bool always_true(none const& ) + { + ++always_true_counter; + return true; + } + bool always_false(none const& ) + { + ++always_false_counter; + return false; + } + + typedef my_machine_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < State1 , none , State2 >, + a_row < State2 , none , State3 , &p::State2ToState3 >, + // +---------+-------------+---------+---------------------+----------------------+ + row < State3 , none , State4 , &p::State3ToState4 , &p::always_true >, + g_row < State3 , none , State4 , &p::always_false >, + _row < State4 , event1 , State1 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<my_machine_::State1&>().entry_counter=0; + fsm.template get_state<my_machine_::State1&>().exit_counter=0; + fsm.template get_state<my_machine_::State2&>().entry_counter=0; + fsm.template get_state<my_machine_::State2&>().exit_counter=0; + fsm.template get_state<my_machine_::State3&>().entry_counter=0; + fsm.template get_state<my_machine_::State3&>().exit_counter=0; + fsm.template get_state<my_machine_::State4&>().entry_counter=0; + fsm.template get_state<my_machine_::State4&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + //static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 1,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 1,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 1,"State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 1,"State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 1,"State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 1,"State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 1,"State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.always_true_counter == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.always_false_counter == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 1,"action not called correctly"); + + + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 2,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 2,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 2,"State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 2,"State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 2,"State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 2,"State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 2,"State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().exit_counter == 1,"State4 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.always_true_counter == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.always_false_counter == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 2,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 2,"action not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/AnonymousEuml.cpp b/src/boost/libs/msm/test/AnonymousEuml.cpp new file mode 100644 index 000000000..f4643dd35 --- /dev/null +++ b/src/boost/libs/msm/test/AnonymousEuml.cpp @@ -0,0 +1,167 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +using namespace boost::msm::front::euml; + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(event1) + + // The list of FSM states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State2) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State3) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),State4) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,state2_to_state3_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,state3_to_state4_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,always_true_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,always_false_counter) + + // transition actions + BOOST_MSM_EUML_ACTION(State2ToState3) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(state2_to_state3_counter); + } + }; + BOOST_MSM_EUML_ACTION(State3ToState4) + { + template <class FSM,class EVT,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(state3_to_state4_counter); + } + }; + // guard conditions + BOOST_MSM_EUML_ACTION(always_true) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(always_true_counter); + return true; + } + }; + BOOST_MSM_EUML_ACTION(always_false) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(always_false_counter); + return false; + } + }; + + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + + BOOST_MSM_EUML_TRANSITION_TABLE(( + State2 == State1 , + State3 == State2 / State2ToState3, + State4 == State3 [always_true] / State3ToState4, + State4 == State3 [always_false], + State1 == State4 + event1 + // +------------------------------------------------------------------------------+ + ),transition_table) + + // create a state machine "on the fly" + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << State1, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << state2_to_state3_counter << state3_to_state4_counter + << always_true_counter << always_false_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + my_machine_) //fsm name + + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + //static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + my_machine p; + + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + // in this case it will also immediately trigger all anonymous transitions + p.start(); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(exit_counter) == 1, + "State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(entry_counter) == 1, + "State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(exit_counter) == 1, + "State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(entry_counter) == 1, + "State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(exit_counter) == 1, + "State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(entry_counter) == 1, + "State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State4)&>().get_attribute(entry_counter)== 1, + "State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_true_counter) == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_false_counter) == 1,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state2_to_state3_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state3_to_state4_counter) == 1,"action not called correctly"); + + + // this event will bring us back to the initial state and thus, a new "loop" will be started + p.process_event(event1); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active"); //State4 + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(exit_counter) == 2, + "State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State1)&>().get_attribute(entry_counter) == 2, + "State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(exit_counter) == 2, + "State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State2)&>().get_attribute(entry_counter) == 2, + "State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(exit_counter) == 2, + "State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State3)&>().get_attribute(entry_counter) == 2, + "State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(State4)&>().get_attribute(entry_counter)== 2, + "State4 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_true_counter) == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(always_false_counter) == 2,"guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state2_to_state3_counter) == 2,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(state3_to_state4_counter) == 2,"action not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/CompositeEuml.cpp b/src/boost/libs/msm/test/CompositeEuml.cpp new file mode 100644 index 000000000..34ada4e95 --- /dev/null +++ b/src/boost/libs/msm/test/CompositeEuml.cpp @@ -0,0 +1,280 @@ +// 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) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(region2_evt) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + // Playing is now a state machine itself. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_next_song_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_prev_song_guard_counter) + // It has 5 substates + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song2) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song3) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State2) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song , + Song1 == Song2 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , + Song3 == Song2 + next_song / ++fsm_(start_next_song_counter) , + Song2 == Song3 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , + Region2State2 == Region2State1 + region2_evt + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1 << Region2State1, // Init State + ++state_(entry_counter), // Entry + ++state_(exit_counter), // Exit + attributes_ << entry_counter << exit_counter + << start_next_song_counter + << start_prev_song_guard_counter // Attributes + ),Playing_) + // choice of back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[1] == 3,"Region2State1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State1)&>().get_attribute(entry_counter) == 1, + "Region2State1 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(exit_counter) == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 0, + "submachine action not called correctly"); + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(entry_counter) == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(exit_counter) == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 1, + "submachine action not called correctly"); + + p.process_event(previous_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(exit_counter) == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_prev_song_guard_counter) == 1, + "submachine guard not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State2)&>().get_attribute(entry_counter) == 0, + "Region2State2 entry not called correctly"); + + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/CompositeMachine.cpp b/src/boost/libs/msm/test/CompositeMachine.cpp new file mode 100644 index 000000000..674f316b4 --- /dev/null +++ b/src/boost/libs/msm/test/CompositeMachine.cpp @@ -0,0 +1,345 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/Constructor.cpp b/src/boost/libs/msm/test/Constructor.cpp new file mode 100644 index 000000000..86ae978be --- /dev/null +++ b/src/boost/libs/msm/test/Constructor.cpp @@ -0,0 +1,210 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + struct SomeExternalContext + { + SomeExternalContext(int b):bla(b){} + int bla; + }; + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_(SomeExternalContext& context,int someint): + context_(context), + someint_(someint) + {} + + SomeExternalContext& context_; + int someint_; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {} + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + fsm.context_ = 20; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + SomeExternalContext ctx(3); + player p(boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p.context_.bla == 3,"wrong value passed"); + BOOST_CHECK_MESSAGE(p.someint_ == 5,"wrong value passed"); + ctx.bla = 10; + BOOST_CHECK_MESSAGE(p.context_.bla == 10,"error with reference"); + p.start(); + BOOST_CHECK_MESSAGE(p.context_.bla == 20,"error with fsm entry behavior"); + } +} + diff --git a/src/boost/libs/msm/test/Entries.cpp b/src/boost/libs/msm/test/Entries.cpp new file mode 100644 index 000000000..7f746d0f8 --- /dev/null +++ b/src/boost/libs/msm/test/Entries.cpp @@ -0,0 +1,297 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + struct event3 {}; + struct event4 {}; + struct event5 {}; + struct event6 + { + event6(){} + template <class Event> + event6(Event const&){} + }; + // front-end: define the FSM structure + struct Fsm_ : public msm::front::state_machine_def<Fsm_> + { + // The list of FSM states + struct State1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubFsm2_ : public msm::front::state_machine_def<SubFsm2_> + { + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + unsigned int entry_action_counter; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + + struct SubState1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState1b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState2 : public msm::front::state<> , public msm::front::explicit_entry<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState2b : public msm::front::state<> , public msm::front::explicit_entry<1> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // test with a pseudo entry + struct PseudoEntry1 : public msm::front::entry_pseudo_state<0> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct SubState3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct PseudoExit1 : public msm::front::exit_pseudo_state<event6> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // action methods + void entry_action(event4 const&) + { + ++entry_action_counter; + } + // the initial state. Must be defined + typedef mpl::vector<SubState1,SubState1b> initial_state; + + typedef mpl::vector<SubState2b> explicit_creation; + + // Transition table for SubFsm2 + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +--------------+-------------+------------+------------------------+----------------------+ + a_row < PseudoEntry1 , event4 , SubState3 ,&SubFsm2_::entry_action >, + _row < SubState2 , event6 , SubState1 >, + _row < SubState3 , event5 , PseudoExit1 > + // +--------------+-------------+------------+------------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + typedef msm::back::state_machine<SubFsm2_> SubFsm2; + + // the initial state of the player SM. Must be defined + typedef State1 initial_state; + + // transition actions + // guard conditions + + // Transition table for Fsm + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------------------+--------+------------------------------------+-------+--------+ + _row < State1 , event1 , SubFsm2 >, + _row < State1 , event2 , SubFsm2::direct<SubFsm2_::SubState2> >, + _row < State1 , event3 , mpl::vector<SubFsm2::direct<SubFsm2_::SubState2>, + SubFsm2::direct<SubFsm2_::SubState2b> > >, + _row < State1 , event4 , SubFsm2::entry_pt + <SubFsm2_::PseudoEntry1> >, + // +---------------------+--------+------------------------------------+-------+--------+ + _row < SubFsm2 , event1 , State1 >, + _row < SubFsm2::exit_pt + <SubFsm2_::PseudoExit1>, event6 , State2 > + // +---------------------+--------+------------------------------------+-------+--------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int ) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<Fsm_::State1&>().entry_counter=0; + fsm.template get_state<Fsm_::State1&>().exit_counter=0; + fsm.template get_state<Fsm_::State2&>().entry_counter=0; + fsm.template get_state<Fsm_::State2&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().entry_action_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1b&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState1b&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2b&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState2b&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState3&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::SubState3&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::PseudoEntry1&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::PseudoEntry1&>().exit_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::exit_pt<SubFsm2_::PseudoExit1>&>().entry_counter=0; + fsm.template get_state<Fsm_::SubFsm2&>().template get_state<Fsm_::SubFsm2::exit_pt<SubFsm2_::PseudoExit1>&>().exit_counter=0; + + } + }; + typedef msm::back::state_machine<Fsm_> Fsm; +// static char const* const state_names[] = { "State1", "SubFsm2","State2" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + Fsm p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 1,"State1 entry not called correctly"); + + p.process_event(event1()); + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 1,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 2,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 1,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 1,"SubFsm2 entry not called correctly"); + + p.process_event(event2()); + p.process_event(event6()); + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 2,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 3,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 2,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 2,"SubFsm2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().entry_counter == 1, + "SubFsm2::SubState2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().exit_counter == 1, + "SubFsm2::SubState2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState1&>().entry_counter == 2, + "SubFsm2::SubState1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState1&>().exit_counter == 2, + "SubFsm2::SubState1 exit not called correctly"); + + p.process_event(event3()); + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 3,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().entry_counter == 4,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 3,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 3,"SubFsm2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().entry_counter == 2, + "SubFsm2::SubState2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2&>().exit_counter == 2, + "SubFsm2::SubState2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2b&>().entry_counter == 1, + "SubFsm2::SubState2b entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState2b&>().exit_counter == 1, + "SubFsm2::SubState2b exit not called correctly"); + + p.process_event(event4()); + p.process_event(event5()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"State2 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State1&>().exit_counter == 4,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::State2&>().entry_counter == 1,"State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().exit_counter == 4,"SubFsm2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_counter == 4,"SubFsm2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::PseudoEntry1&>().entry_counter == 1, + "SubFsm2::PseudoEntry1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::PseudoEntry1&>().exit_counter == 1, + "SubFsm2::PseudoEntry1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState3&>().entry_counter == 1, + "SubFsm2::SubState3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2_::SubState3&>().exit_counter == 1, + "SubFsm2::SubState3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2::exit_pt<Fsm_::SubFsm2_::PseudoExit1>&>().entry_counter == 1, + "SubFsm2::PseudoExit1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().get_state<Fsm_::SubFsm2::exit_pt<Fsm_::SubFsm2_::PseudoExit1>&>().exit_counter == 1, + "SubFsm2::PseudoExit1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Fsm_::SubFsm2&>().entry_action_counter == 1,"Action not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/EventQueue.cpp b/src/boost/libs/msm/test/EventQueue.cpp new file mode 100644 index 000000000..a9e451b7e --- /dev/null +++ b/src/boost/libs/msm/test/EventQueue.cpp @@ -0,0 +1,360 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int test_fct_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + test_fct_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.test_fct_counter; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.start_playback_counter; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + struct can_close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.can_close_drawer_counter; + return true; + } + }; + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , can_close_drawer >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( test_event_queue ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.enqueue_event(open_close()); + p.execute_single_queued_event(); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.enqueue_event(open_close()); + p.enqueue_event( + cd_detected("louie, louie",DISK_DVD)); + p.enqueue_event( + cd_detected("louie, louie",DISK_CD)); + p.execute_queued_events(); + + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.test_fct_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.enqueue_event(end_pause()); + p.execute_queued_events(); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.enqueue_event(pause()); + p.enqueue_event(stop()); + p.enqueue_event(stop()); + p.execute_queued_events(); + + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/History.cpp b/src/boost/libs/msm/test/History.cpp new file mode 100644 index 000000000..b12890706 --- /dev/null +++ b/src/boost/libs/msm/test/History.cpp @@ -0,0 +1,356 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int ) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 3, + "Song2 entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 2, + "Song1 entry not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/Jamfile.v2 b/src/boost/libs/msm/test/Jamfile.v2 new file mode 100644 index 000000000..a00c2cec8 --- /dev/null +++ b/src/boost/libs/msm/test/Jamfile.v2 @@ -0,0 +1,50 @@ +# test/Jamfile.v2 controls building of MSM Library unit tests +# +# Copyright (c) 2010 Christophe Henry +# +# Use, modification and distribution is subject to 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) + +import testing ; + +project msm + : + requirements + <include>. + <toolset>gcc:<cxxflags>"-ftemplate-depth-300 -g0" + <toolset>darwin:<cxxflags>"-ftemplate-depth-300 -g0" + <toolset>intel:<cxxflags>"-g0" + <toolset>gcc:<optimization>off + <toolset>darwin:<optimization>off + <toolset>intel:<optimization>off + <library>/boost/test//boost_unit_test_framework/<link>static + <library>/boost/serialization//boost_serialization/<link>static + ; + +test-suite msm-unit-tests + : + [ run Anonymous.cpp ] + [ run AnonymousEuml.cpp ] + [ run CompositeEuml.cpp ] + [ run CompositeMachine.cpp ] + [ run Constructor.cpp ] + [ run Entries.cpp ] + [ run History.cpp ] + [ run OrthogonalDeferred.cpp ] + [ run OrthogonalDeferred2.cpp ] + [ run OrthogonalDeferred3.cpp ] + [ run OrthogonalDeferredEuml.cpp ] + [ run SimpleEuml.cpp ] + [ run SimpleEuml2.cpp ] + [ run SimpleInternal.cpp ] + [ run SimpleInternalEuml.cpp ] + [ run SimpleInternalFunctors.cpp ] + [ run SimpleMachine.cpp ] + [ run SimpleWithFunctors.cpp ] + [ run Serialize.cpp ] + [ run SerializeWithHistory.cpp ] + [ run SerializeSimpleEuml.cpp ] + [ run TestConstructor.cpp ] + ; + diff --git a/src/boost/libs/msm/test/OrthogonalDeferred.cpp b/src/boost/libs/msm/test/OrthogonalDeferred.cpp new file mode 100644 index 000000000..a5da22368 --- /dev/null +++ b/src/boost/libs/msm/test/OrthogonalDeferred.cpp @@ -0,0 +1,488 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + struct do_terminate {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int report_error_counter; + unsigned int report_end_error_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + report_error_counter(0), + report_end_error_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + typedef mpl::vector<play> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector<play> deferred_events; + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Stopped : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct ErrorTerminate : public msm::front::terminate_state<> // terminates the state machine + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + void report_error(error_found const&) {++report_error_counter;} + void report_end_error(end_error const&) {++report_end_error_counter;} + + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode, end_error ,AllOk , &p::report_end_error >, + _row < AllOk , do_terminate,ErrorTerminate > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int ) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + fsm.template get_state<player_::AllOk&>().entry_counter=0; + fsm.template get_state<player_::AllOk&>().exit_counter=0; + fsm.template get_state<player_::ErrorMode&>().entry_counter=0; + fsm.template get_state<player_::ErrorMode&>().exit_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().entry_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + //static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 0,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 0,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == false,"CDLoaded should not be active"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + //deferred event should have been processed + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == true,"FirstSongPlaying should be active"); + + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == false,"FirstSongPlaying should not be active"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == false,"PlayingPaused should not be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == true,"CDLoaded should be active"); + //BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded,player::Flag_AND>() == false,"CDLoaded with AND should not be active"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + //test interrupt + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(error_found()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + + // try generating more events + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().exit_counter == 1,"ErrorMode exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().entry_counter == 2,"AllOk entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 3,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 3,"Playing entry not called correctly"); + + //test terminate + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(do_terminate()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 2,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().entry_counter == 1,"ErrorTerminate entry not called correctly"); + + // try generating more events + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().exit_counter == 0,"ErrorTerminate exit not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/OrthogonalDeferred2.cpp b/src/boost/libs/msm/test/OrthogonalDeferred2.cpp new file mode 100644 index 000000000..9e630cca3 --- /dev/null +++ b/src/boost/libs/msm/test/OrthogonalDeferred2.cpp @@ -0,0 +1,495 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.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 MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + struct do_terminate {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // we want deferred events and no state requires deferred events (only the fsm in the + // transition table), so the fsm does. + typedef int activate_deferred_events; + + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int report_error_counter; + unsigned int report_end_error_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + report_error_counter(0), + report_end_error_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Stopped : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct ErrorTerminate : public msm::front::terminate_state<> // terminates the state machine + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + void report_error(error_found const&) {++report_error_counter;} + void report_end_error(end_error const&) {++report_end_error_counter;} + + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + Row < Open , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + Row < Empty , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode, end_error ,AllOk , &p::report_end_error >, + _row < AllOk , do_terminate,ErrorTerminate > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + fsm.template get_state<player_::AllOk&>().entry_counter=0; + fsm.template get_state<player_::AllOk&>().exit_counter=0; + fsm.template get_state<player_::ErrorMode&>().entry_counter=0; + fsm.template get_state<player_::ErrorMode&>().exit_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().entry_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + //static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 0,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 0,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == false,"CDLoaded should not be active"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + //deferred event should have been processed + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == true,"FirstSongPlaying should be active"); + + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == false,"FirstSongPlaying should not be active"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == false,"PlayingPaused should not be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == true,"CDLoaded should be active"); + //BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded,player::Flag_AND>() == false,"CDLoaded with AND should not be active"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + //test interrupt + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(error_found()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + + // try generating more events + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().exit_counter == 1,"ErrorMode exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().entry_counter == 2,"AllOk entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 3,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 3,"Playing entry not called correctly"); + + //test terminate + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(do_terminate()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 2,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().entry_counter == 1,"ErrorTerminate entry not called correctly"); + + // try generating more events + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().exit_counter == 0,"ErrorTerminate exit not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/OrthogonalDeferred3.cpp b/src/boost/libs/msm/test/OrthogonalDeferred3.cpp new file mode 100644 index 000000000..3ba684487 --- /dev/null +++ b/src/boost/libs/msm/test/OrthogonalDeferred3.cpp @@ -0,0 +1,511 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.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 MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct error_found {}; + struct end_error {}; + struct do_terminate {}; + + // Flags. Allow information about a property of the current state + struct PlayingPaused{}; + struct CDLoaded {}; + struct FirstSongPlaying {}; + + // A "complicated" event type that carries some data. + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // we want deferred events and no state requires deferred events (only the fsm in the + // transition table), so the fsm does. + typedef int activate_deferred_events; + + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int report_error_counter; + unsigned int report_end_error_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + report_error_counter(0), + report_end_error_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Stopped : public msm::front::state<> + { + typedef mpl::vector1<CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the player state machine contains a state which is himself a state machine + // as you see, no need to declare it anywhere so Playing can be developed separately + // by another team in another module. For simplicity I just declare it inside player + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + typedef mpl::vector1<FirstSongPlaying> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + typedef mpl::vector2<PlayingPaused,CDLoaded> flag_list; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct AllOk : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // this state is also made terminal so that all the events are blocked + struct ErrorMode : //public msm::front::terminate_state<> // ErrorMode terminates the state machine + public msm::front::interrupt_state<end_error> // ErroMode just interrupts. Will resume if + // the event end_error is generated + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct ErrorTerminate : public msm::front::terminate_state<> // terminates the state machine + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Empty,AllOk> initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + void report_error(error_found const&) {++report_error_counter;} + void report_end_error(end_error const&) {++report_end_error_counter;} + + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + struct is_play_event + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + bool is_play = boost::any_cast<play>(&evt) != 0; + return is_play; + } + }; + struct MyDefer + { + // mark as deferring to avoid stack overflows in certain conditions + typedef int deferring_action; + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) const + { + fsm.defer_event(play()); + } + }; + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + Row < Open , play , none , Defer , none >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + Row < Empty , boost::any , none , MyDefer , is_play_event >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < AllOk , error_found ,ErrorMode, &p::report_error >, + a_row <ErrorMode, end_error ,AllOk , &p::report_end_error >, + _row < AllOk , do_terminate,ErrorTerminate > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_ERROR("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + fsm.template get_state<player_::AllOk&>().entry_counter=0; + fsm.template get_state<player_::AllOk&>().exit_counter=0; + fsm.template get_state<player_::ErrorMode&>().entry_counter=0; + fsm.template get_state<player_::ErrorMode&>().exit_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().entry_counter=0; + fsm.template get_state<player_::ErrorTerminate&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + //static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 0,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 0,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == false,"CDLoaded should not be active"); + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + //deferred event should have been processed + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == true,"FirstSongPlaying should be active"); + + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<FirstSongPlaying>() == false,"FirstSongPlaying should not be active"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == true,"PlayingPaused should be active"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<PlayingPaused>() == false,"PlayingPaused should not be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded>() == true,"CDLoaded should be active"); + //BOOST_CHECK_MESSAGE(p.is_flag_active<CDLoaded,player::Flag_AND>() == false,"CDLoaded with AND should not be active"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + //test interrupt + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(error_found()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + + // try generating more events + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 1,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().entry_counter == 1,"ErrorMode entry not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorMode&>().exit_counter == 1,"ErrorMode exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().entry_counter == 2,"AllOk entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 3,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 3,"Playing entry not called correctly"); + + //test terminate + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(do_terminate()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::AllOk&>().exit_counter == 2,"AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().entry_counter == 1,"ErrorTerminate entry not called correctly"); + + // try generating more events + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<player_::ErrorTerminate&>().exit_counter == 0,"ErrorTerminate exit not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/OrthogonalDeferredEuml.cpp b/src/boost/libs/msm/test/OrthogonalDeferredEuml.cpp new file mode 100644 index 000000000..272d954fd --- /dev/null +++ b/src/boost/libs/msm/test/OrthogonalDeferredEuml.cpp @@ -0,0 +1,359 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/euml/euml.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front::euml; + +namespace +{ + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(next_song) + BOOST_MSM_EUML_EVENT(previous_song) + BOOST_MSM_EUML_EVENT(error_found) + BOOST_MSM_EUML_EVENT(end_error) + BOOST_MSM_EUML_EVENT(do_terminate) + + // Flags. Allow information about a property of the current state + BOOST_MSM_EUML_FLAG(PlayingPaused) + BOOST_MSM_EUML_FLAG(CDLoaded) + BOOST_MSM_EUML_FLAG(FirstSongPlaying) + + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_ << play),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< CDLoaded << play),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< CDLoaded),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< CDLoaded << PlayingPaused),Paused) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),AllOk) + BOOST_MSM_EUML_TERMINATE_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter), + ErrorTerminate) + BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter), + ErrorMode) + + // Playing is now a state machine itself. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_next_song_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_prev_song_guard_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter, configure_<< FirstSongPlaying ),Song1) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song2) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song3) + + // Playing has a transition table + BOOST_MSM_EUML_TRANSITION_TABLE(( + // +------------------------------------------------------------------------------+ + Song2 == Song1 + next_song , + Song1 == Song2 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , + Song3 == Song2 + next_song / ++fsm_(start_next_song_counter) , + Song2 == Song3 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) + // +------------------------------------------------------------------------------+ + ),playing_transition_table ) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT + init_ << Song1, // Init State + ++state_(entry_counter), // Entry + ++state_(exit_counter), // Exit + attributes_ << entry_counter << exit_counter + << start_next_song_counter + << start_prev_song_guard_counter, // Attributes + configure_<< PlayingPaused << CDLoaded //flags + ),Playing_) + + // back-end + typedef msm::back::state_machine<Playing_> Playing_type; + Playing_type const Playing; + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,report_error_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,report_end_error_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / ++fsm_(start_playback_counter), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected , + Stopped == Stopped + stop , + ErrorMode == AllOk + error_found / ++fsm_(report_error_counter), + AllOk == ErrorMode+ end_error / ++fsm_(report_end_error_counter), + ErrorTerminate== AllOk +do_terminate + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty << AllOk, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter << can_close_drawer_counter + << report_error_counter << report_end_error_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + //static char const* const state_names[] = { "Stopped", "Paused","Open", "Empty", "Playing" ,"AllOk","ErrorMode","ErrorTerminate" }; + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + // test deferred event + // deferred in Empty and Open, will be handled only after event cd_detected + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 0, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 0,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() == false,"CDLoaded should not be active"); + + p.process_event(open_close); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + //deferred event should have been processed + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(entry_counter) == 1, + "Song1 entry not called correctly"); + + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() == true,"FirstSongPlaying should be active"); + + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(exit_counter) == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 0, + "submachine action not called correctly"); + + p.process_event(next_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(entry_counter) == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(exit_counter) == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 1, + "submachine action not called correctly"); + + p.process_event(previous_song); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(exit_counter) == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<Playing_type&>().get_attribute(start_prev_song_guard_counter) == 1, + "submachine guard not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == true,"PlayingPaused should be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() == false,"FirstSongPlaying should not be active"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == true,"PlayingPaused should be active"); + + // go back to Playing + p.process_event(end_pause); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 2,"Playing entry not called correctly"); + + p.process_event(pause); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + //flags + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() == false,"PlayingPaused should not be active"); + BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() == true,"CDLoaded should be active"); + //BOOST_CHECK_MESSAGE(p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() == false,"CDLoaded with AND should not be active"); + + p.process_event(stop); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + //test interrupt + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(error_found); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(exit_counter) == 1, + "AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorMode)&>().get_attribute(entry_counter) == 1, + "ErrorMode entry not called correctly"); + + // try generating more events + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 6,"ErrorMode should be active"); //ErrorMode + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(exit_counter) == 1, + "AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorMode)&>().get_attribute(entry_counter) == 1, + "ErrorMode entry not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + p.process_event(end_error); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorMode)&>().get_attribute(exit_counter) == 1, + "ErrorMode exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(entry_counter) == 2, + "AllOk entry not called correctly"); + + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 3, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 3,"Playing entry not called correctly"); + + //test terminate + BOOST_CHECK_MESSAGE(p.current_state()[1] == 5,"AllOk should be active"); //AllOk + p.process_event(do_terminate); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(AllOk)&>().get_attribute(exit_counter) == 2, + "AllOk exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorTerminate)&>().get_attribute(entry_counter) == 1, + "ErrorTerminate entry not called correctly"); + + // try generating more events + p.process_event(stop); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(ErrorTerminate)&>().get_attribute(exit_counter) == 0, + "ErrorTerminate exit not called correctly"); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + p.process_event(end_error()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 7,"ErrorTerminate should be active"); //ErrorTerminate + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + + } +} + diff --git a/src/boost/libs/msm/test/Serialize.cpp b/src/boost/libs/msm/test/Serialize.cpp new file mode 100644 index 000000000..ed3035099 --- /dev/null +++ b/src/boost/libs/msm/test/Serialize.cpp @@ -0,0 +1,311 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + int front_end_data; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + front_end_data(4) + {} + + //we want to serialize some data contained by the front-end + // to achieve this, ask for it + typedef int do_serialize; + // and provide a serialize + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & front_end_data; + } + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + int some_dummy_data; + // we want Empty to be serialized + typedef int do_serialize; + template<class Archive> + void serialize(Archive & ar, const unsigned int ) + { + ar & some_dummy_data; + } + + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().some_dummy_data=3; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + // test the serialization + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Open) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // we now use p2 as it was loaded + // check that we kept Empty's data value + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().some_dummy_data == 3,"Empty not deserialized correctly"); + BOOST_CHECK_MESSAGE(p2.front_end_data == 4,"Front-end not deserialized correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + diff --git a/src/boost/libs/msm/test/SerializeSimpleEuml.cpp b/src/boost/libs/msm/test/SerializeSimpleEuml.cpp new file mode 100644 index 000000000..2a5769b9c --- /dev/null +++ b/src/boost/libs/msm/test/SerializeSimpleEuml.cpp @@ -0,0 +1,216 @@ +// 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) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p2; + + p2.start(); + BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p2.process_event(open_close()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + // test the serialization + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Open) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p2; + } + // reload fsm in state Open + player p; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p; + } + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) diff --git a/src/boost/libs/msm/test/SerializeWithHistory.cpp b/src/boost/libs/msm/test/SerializeWithHistory.cpp new file mode 100644 index 000000000..cf1e11d6c --- /dev/null +++ b/src/boost/libs/msm/test/SerializeWithHistory.cpp @@ -0,0 +1,367 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> +// include headers that implement a archive in simple text format +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/tracking.hpp> + +#include <fstream> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + struct cd_detected + { + cd_detected(std::string name) + : name(name) + {} + std::string name; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int start_next_song_counter; + unsigned int start_prev_song_guard_counter; + + Playing_(): + start_next_song_counter(0), + start_prev_song_guard_counter(0) + {} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) {++start_next_song_counter; } + void start_prev_song(PreviousSong const&) { } + // guard conditions + bool start_prev_song_guard(PreviousSong const&) {++start_prev_song_guard_counter;return true; } + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + _row < Song1 , NextSong , Song2 >, + row < Song2 , PreviousSong, Song1 , &pl::start_prev_song,&pl::start_prev_song_guard>, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard> + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // back-end + typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&) {} + //guards + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song1&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song2&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().template get_state<player_::Playing::Song3&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event(cd_detected("louie, louie")); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter == 1, + "Song1 entry not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 1, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter == 1, + "Song1 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 0, + "submachine action not called correctly"); + + p.process_event(NextSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 2,"Song3 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter == 1, + "Song3 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter == 1, + "Song2 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_next_song_counter == 1, + "submachine action not called correctly"); + + p.process_event(PreviousSong()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter == 2, + "Song2 entry not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter == 1, + "Song3 exit not called correctly"); + BOOST_CHECK_MESSAGE( + p.get_state<player_::Playing&>().start_prev_song_guard_counter == 1, + "submachine guard not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + std::ofstream ofs("fsm.txt"); + // save fsm to archive (current state is Pause, Playing is in Song2) + { + boost::archive::text_oarchive oa(ofs); + // write class instance to archive + oa << p; + } + // reload fsm in state Open + player p2; + { + // create and open an archive for input + std::ifstream ifs("fsm.txt"); + boost::archive::text_iarchive ia(ifs); + // read class state from archive + ia >> p2; + } + // go back to Playing + p2.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p2.get_state<player_::Playing&>().current_state()[0] == 1,"Song2 should be active"); + + p2.process_event(pause()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 4,"Paused should be active"); //Paused + + p2.process_event(stop()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 0,"Stopped should be active"); //Stopped + + p2.process_event(stop()); + BOOST_CHECK_MESSAGE(p2.current_state()[0] == 0,"Stopped should be active"); //Stopped + + p2.process_event(play()); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Playing&>().current_state()[0] == 0,"Song1 should be active"); + } +} +// eliminate object tracking (even if serialized through a pointer) +// at the risk of a programming error creating duplicate objects. +// this is to get rid of warning because p is not const +BOOST_CLASS_TRACKING(player, boost::serialization::track_never) + diff --git a/src/boost/libs/msm/test/SimpleEuml.cpp b/src/boost/libs/msm/test/SimpleEuml.cpp new file mode 100644 index 000000000..8d66e9eac --- /dev/null +++ b/src/boost/libs/msm/test/SimpleEuml.cpp @@ -0,0 +1,197 @@ +// 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) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/SimpleEuml2.cpp b/src/boost/libs/msm/test/SimpleEuml2.cpp new file mode 100644 index 000000000..e30b84aac --- /dev/null +++ b/src/boost/libs/msm/test/SimpleEuml2.cpp @@ -0,0 +1,197 @@ +// 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) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter)) == Playing, + Paused + end_pause == Playing, + // +------------------------------------------------------------------------------+ + Open + open_close / ++fsm_(can_close_drawer_counter) == Empty, + // +------------------------------------------------------------------------------+ + Empty + open_close == Open, + Paused + open_close == Open, + Stopped + open_close == Open, + Playing + open_close == Open, + // +------------------------------------------------------------------------------+ + Playing + pause == Paused, + // +------------------------------------------------------------------------------+ + Playing + stop == Stopped, + Paused + stop == Stopped, + Empty + cd_detected [good_disk_format || + (event_(cd_type)==Int_<DISK_CD>())] / process_(play) == Stopped, + Stopped + stop == Stopped + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter + << can_close_drawer_counter << test_fct_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/SimpleInternal.cpp b/src/boost/libs/msm/test/SimpleInternal.cpp new file mode 100644 index 000000000..544e02132 --- /dev/null +++ b/src/boost/libs/msm/test/SimpleInternal.cpp @@ -0,0 +1,276 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct internal_evt {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int internal_action_counter; + unsigned int internal_guard_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + internal_action_counter(0), + internal_guard_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + void internal_action(internal_evt const&){++internal_action_counter; } + bool internal_guard(cd_detected const&){++internal_guard_counter;return false;} + bool internal_guard2(internal_evt const&){++internal_guard_counter;return true;} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + irow < Empty , internal_evt, &p::internal_action ,&p::internal_guard2 >, + _irow < Empty , to_ignore >, + g_irow < Empty , cd_detected ,&p::internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + // internal events + p.process_event(to_ignore()); + p.process_event(internal_evt()); + BOOST_CHECK_MESSAGE(p.internal_action_counter == 1,"Internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1,"Internal guard not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 2,"Internal guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3,"Internal guard not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/SimpleInternalEuml.cpp b/src/boost/libs/msm/test/SimpleInternalEuml.cpp new file mode 100644 index 000000000..f02ad501b --- /dev/null +++ b/src/boost/libs/msm/test/SimpleInternalEuml.cpp @@ -0,0 +1,255 @@ +// 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) + +#include <iostream> +#include <boost/msm/back/state_machine.hpp> +#include <boost/msm/front/euml/euml.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace boost::msm::front::euml; +namespace msm = boost::msm; + +namespace +{ + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + // events + BOOST_MSM_EUML_EVENT(play) + BOOST_MSM_EUML_EVENT(end_pause) + BOOST_MSM_EUML_EVENT(stop) + BOOST_MSM_EUML_EVENT(pause) + BOOST_MSM_EUML_EVENT(open_close) + BOOST_MSM_EUML_EVENT(internal_evt) + BOOST_MSM_EUML_EVENT(to_ignore) + // A "complicated" event type that carries some data. + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) + BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) + BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) + + //states + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) + + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) + BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) + + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_guard_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_action_counter) + + BOOST_MSM_EUML_ACTION(internal_guard_fct) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&, FSM& ,SourceState& src,TargetState& ) + { + ++src.get_attribute(empty_internal_guard_counter); + return false; + } + }; + BOOST_MSM_EUML_DECLARE_STATE((++state_(entry_counter),++state_(exit_counter), + attributes_ << entry_counter << exit_counter + << empty_internal_guard_counter << empty_internal_action_counter),Empty_def) + // derive to be able to add an internal transition table + struct Empty_impl : public Empty_def + { + Empty_impl(){} + BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( + internal_evt [internal_guard_fct] / ++source_(empty_internal_action_counter) + )) + }; + // declare an instance for the stt as we are manually declaring a state + Empty_impl const Empty; + + //fsm + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_action_counter) + BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_guard_counter) + BOOST_MSM_EUML_ACTION(No_Transition) + { + template <class FSM,class Event> + void operator()(Event const&,FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + BOOST_MSM_EUML_ACTION(good_disk_format) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) + { + if (evt.get_attribute(cd_type)!=DISK_CD) + { + return false; + } + return true; + } + }; + BOOST_MSM_EUML_ACTION(internal_guard) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(internal_guard_counter); + return false; + } + }; + BOOST_MSM_EUML_ACTION(internal_guard2) + { + template <class FSM,class EVT,class SourceState,class TargetState> + bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.get_attribute(internal_guard_counter); + return true; + } + }; + BOOST_MSM_EUML_TRANSITION_TABLE(( + Playing == Stopped + play / ++fsm_(start_playback_counter) , + Playing == Paused + end_pause , + // +------------------------------------------------------------------------------+ + Empty == Open + open_close / ++fsm_(can_close_drawer_counter), + Empty + to_ignore , + Empty + internal_evt [internal_guard2] / ++fsm_(internal_action_counter) , + Empty + cd_detected [internal_guard] , + // +------------------------------------------------------------------------------+ + Open == Empty + open_close , + Open == Paused + open_close , + Open == Stopped + open_close , + Open == Playing + open_close , + // +------------------------------------------------------------------------------+ + Paused == Playing + pause , + // +------------------------------------------------------------------------------+ + Stopped == Playing + stop , + Stopped == Paused + stop , + Stopped == Empty + cd_detected [good_disk_format] , + Stopped == Stopped + stop + // +------------------------------------------------------------------------------+ + ),transition_table) + + BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT + init_ << Empty, // Init State + no_action, // Entry + no_action, // Exit + attributes_ << start_playback_counter << can_close_drawer_counter + << internal_action_counter << internal_guard_counter, // Attributes + configure_ << no_configure_, // configuration + No_Transition // no_transition handler + ), + player_) //fsm name + + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, + "Empty entry not called correctly"); + + // internal events + p.process_event(to_ignore); + p.process_event(internal_evt); + BOOST_CHECK_MESSAGE(p.get_attribute(internal_action_counter) == 1,"Internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(internal_guard_counter) == 1,"Internal guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_action_counter) == 0, + "Empty internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_guard_counter) == 1, + "Empty internal guard not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 1, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, + "Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, + "Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, + "Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + p.process_event(play); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 2, + "Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, + "Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, + "Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, + "Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, + "Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, + "Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, + "Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, + "Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, + "Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, + "Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, + "Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/SimpleInternalFunctors.cpp b/src/boost/libs/msm/test/SimpleInternalFunctors.cpp new file mode 100644 index 000000000..ebc659f2c --- /dev/null +++ b/src/boost/libs/msm/test/SimpleInternalFunctors.cpp @@ -0,0 +1,333 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +using namespace std; +namespace msm = boost::msm; +using namespace msm::front; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct internal_evt {}; + struct to_ignore {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int internal_action_counter; + unsigned int internal_guard_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + internal_action_counter(0), + internal_guard_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + unsigned int empty_internal_guard_counter; + unsigned int empty_internal_action_counter; + struct internal_guard_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM&,SourceState& src,TargetState& ) + { + ++src.empty_internal_guard_counter; + return false; + } + }; + struct internal_action_fct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& src,TargetState& ) + { + ++src.empty_internal_action_counter; + } + }; + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Internal < internal_evt , internal_action_fct ,internal_guard_fct > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + struct internal_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.internal_action_counter; + } + }; + struct internal_guard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.internal_guard_counter; + return false; + } + }; + struct internal_guard2 + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.internal_guard_counter; + return true; + } + }; + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + Row < Empty , internal_evt, none , internal_action ,internal_guard2 >, + Row < Empty , to_ignore , none , none , none >, + Row < Empty , cd_detected , none , none ,internal_guard >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().empty_internal_guard_counter=0; + fsm.template get_state<player_::Empty&>().empty_internal_action_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + // internal events + p.process_event(to_ignore()); + p.process_event(internal_evt()); + BOOST_CHECK_MESSAGE(p.internal_action_counter == 1,"Internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1,"Internal guard not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_action_counter == 0,"Empty internal action not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_guard_counter == 1,"Empty internal guard not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 2,"Internal guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3,"Internal guard not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/SimpleMachine.cpp b/src/boost/libs/msm/test/SimpleMachine.cpp new file mode 100644 index 000000000..5f61ac7b8 --- /dev/null +++ b/src/boost/libs/msm/test/SimpleMachine.cpp @@ -0,0 +1,257 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) {++start_playback_counter; } + void open_drawer(open_close const&) { } + void store_cd_info(cd_detected const&) { } + void stop_playback(stop const&) { } + void pause_playback(pause const&) { } + void resume_playback(end_pause const&) { } + void stop_and_open(open_close const&) { } + void stopped_again(stop const&){} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + bool can_close_drawer(open_close const&) + { + ++can_close_drawer_counter; + return true; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + g_row < Open , open_close , Empty , &p::can_close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + + p.process_event(play()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/SimpleWithFunctors.cpp b/src/boost/libs/msm/test/SimpleWithFunctors.cpp new file mode 100644 index 000000000..8f12bfba7 --- /dev/null +++ b/src/boost/libs/msm/test/SimpleWithFunctors.cpp @@ -0,0 +1,361 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace +{ + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + unsigned int start_playback_counter; + unsigned int can_close_drawer_counter; + unsigned int test_fct_counter; + + player_(): + start_playback_counter(0), + can_close_drawer_counter(0), + test_fct_counter(0) + {} + + // The list of FSM states + struct Empty : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Open : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct Playing : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + struct TestFct + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.test_fct_counter; + } + }; + struct start_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.start_playback_counter; + } + }; + struct open_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct store_cd_info + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& ) + { + fsm.process_event(play()); + } + }; + struct stop_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct pause_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct resume_playback + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct stop_and_open + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + struct stopped_again + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& ) + { + } + }; + // guard conditions + struct DummyGuard + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const&,FSM&,SourceState&,TargetState&) + { + return true; + } + }; + struct good_disk_format + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& ) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + return false; + } + return true; + } + }; + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + struct can_close_drawer + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.can_close_drawer_counter; + return true; + } + }; + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < Stopped , play , Playing , ActionSequence_ + <mpl::vector< + TestFct,start_playback> > + , DummyGuard >, + Row < Stopped , open_close , Open , open_drawer , none >, + Row < Stopped , stop , Stopped , none , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Open , open_close , Empty , close_drawer , can_close_drawer >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Empty , open_close , Open , open_drawer , none >, + Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format, + always_true> >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Playing , stop , Stopped , stop_playback , none >, + Row < Playing , pause , Paused , pause_playback , none >, + Row < Playing , open_close , Open , stop_and_open , none >, + // +---------+-------------+---------+---------------------------+----------------------+ + Row < Paused , end_pause , Playing , resume_playback , none >, + Row < Paused , stop , Stopped , stop_playback , none >, + Row < Paused , open_close , Open , stop_and_open , none > + + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Stopped&>().entry_counter=0; + fsm.template get_state<player_::Stopped&>().exit_counter=0; + fsm.template get_state<player_::Open&>().entry_counter=0; + fsm.template get_state<player_::Open&>().exit_counter=0; + fsm.template get_state<player_::Empty&>().entry_counter=0; + fsm.template get_state<player_::Empty&>().exit_counter=0; + fsm.template get_state<player_::Playing&>().entry_counter=0; + fsm.template get_state<player_::Playing&>().exit_counter=0; + fsm.template get_state<player_::Paused&>().entry_counter=0; + fsm.template get_state<player_::Paused&>().exit_counter=0; + } + + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + +// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + player p; + + p.start(); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); + + p.process_event(open_close()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_DVD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty + BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); + + p.process_event( + cd_detected("louie, louie",DISK_CD)); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); + BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); + BOOST_CHECK_MESSAGE(p.test_fct_counter == 1,"action not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); + + // go back to Playing + p.process_event(end_pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); + + p.process_event(pause()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); + + p.process_event(stop()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); + } +} + diff --git a/src/boost/libs/msm/test/Test2RegionsAnonymous.cpp b/src/boost/libs/msm/test/Test2RegionsAnonymous.cpp new file mode 100644 index 000000000..69d6137b0 --- /dev/null +++ b/src/boost/libs/msm/test/Test2RegionsAnonymous.cpp @@ -0,0 +1,172 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +// functors +#include <boost/msm/front/functor_row.hpp> +#include <boost/msm/front/euml/common.hpp> +// for And_ operator +#include <boost/msm/front/euml/operator.hpp> + +#include <boost/test/unit_test.hpp> + +using namespace std; +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace msm::front; +// for And_ operator +using namespace msm::front::euml; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + + // front-end: define the FSM structure + struct my_machine_ : public msm::front::state_machine_def<my_machine_> + { + + // The list of FSM states + struct State1 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct State3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct State1b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State2b : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + + struct always_true + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + return true; + } + }; + struct always_false + { + template <class EVT,class FSM,class SourceState,class TargetState> + bool operator()(EVT const& ,FSM&,SourceState& ,TargetState& ) + { + return false; + } + }; + + // the initial state of the player SM. Must be defined + typedef boost::mpl::vector2<State1,State1b> initial_state; + + // Transition table for player + struct transition_table : boost::mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < State1 , event1 , State2 , none , always_true >, + Row < State2 , none , State3 >, + // +---------+-------------+---------+---------------------+----------------------+ + Row < State1b , event1 , State2b , none , always_false > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const&, FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<my_machine_::State1&>().entry_counter=0; + fsm.template get_state<my_machine_::State1&>().exit_counter=0; + fsm.template get_state<my_machine_::State2&>().entry_counter=0; + fsm.template get_state<my_machine_::State2&>().exit_counter=0; + fsm.template get_state<my_machine_::State3&>().entry_counter=0; + fsm.template get_state<my_machine_::State3&>().exit_counter=0; + fsm.template get_state<my_machine_::State1b&>().entry_counter=0; + fsm.template get_state<my_machine_::State1b&>().exit_counter=0; + fsm.template get_state<my_machine_::State2b&>().entry_counter=0; + fsm.template get_state<my_machine_::State2b&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<my_machine_> my_machine; + + BOOST_AUTO_TEST_CASE( my_test ) + { + my_machine p; + + p.start(); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State1 should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State1b should be active"); + + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State3 should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State1b should be active"); + + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 1,"State1 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 1,"State1 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 1,"State2 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 1,"State2 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 0,"State3 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 1,"State3 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1b&>().entry_counter == 1,"State1b entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1b&>().exit_counter == 0,"State1b exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2b&>().entry_counter == 0,"State2b entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2b&>().exit_counter == 0,"State2b exit not called correctly"); + + + } +} + + diff --git a/src/boost/libs/msm/test/TestConstructor.cpp b/src/boost/libs/msm/test/TestConstructor.cpp new file mode 100644 index 000000000..060d11b6c --- /dev/null +++ b/src/boost/libs/msm/test/TestConstructor.cpp @@ -0,0 +1,269 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + + +namespace +{ + struct SomeExternalContext + { + SomeExternalContext(int b):bla(b){} + int bla; + }; + // events + struct play {}; + struct end_pause {}; + struct stop {}; + struct pause {}; + struct open_close {}; + struct NextSong {}; + struct PreviousSong {}; + + // A "complicated" event type that carries some data. + enum DiskTypeEnum + { + DISK_CD=0, + DISK_DVD=1 + }; + struct cd_detected + { + cd_detected(std::string name, DiskTypeEnum diskType) + : name(name), + disc_type(diskType) + {} + + std::string name; + DiskTypeEnum disc_type; + }; + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_(SomeExternalContext& context,int someint) + :context_(context) + { + BOOST_CHECK_MESSAGE(context_.bla == 3,"Wrong context value"); + BOOST_CHECK_MESSAGE(someint == 5,"Wrong int value"); + context.bla = 10; + } + + SomeExternalContext& context_; + + // The list of FSM states + struct Empty : public msm::front::state<> + { + int data_; + Empty():data_(0){} + Empty(int i):data_(i){} + // every (optional) entry/exit methods get the event passed. + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} + }; + struct Open : public msm::front::state<> + { + int data_; + Open():data_(0){} + Open(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} + }; + + // sm_ptr still supported but deprecated as functors are a much better way to do the same thing + struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> + { + template <class Event,class FSM> + void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} + void set_sm_ptr(player_* pl) + { + m_player=pl; + } + player_* m_player; + }; + + struct Playing_ : public msm::front::state_machine_def<Playing_> + { + // when playing, the CD is loaded and we are in either pause or playing (duh) + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} + + // The list of FSM states + struct Song1 : public msm::front::state<> + { + int data_; + Song1():data_(0){} + Song1(int i):data_(i){} + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} + + }; + struct Song2 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} + }; + struct Song3 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} + }; + // the initial state. Must be defined + typedef Song1 initial_state; + // transition actions + void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } + void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } + // guard conditions + + typedef Playing_ pl; // makes transition table cleaner + // Transition table for Playing + struct transition_table : mpl::vector4< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, + a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, + a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, + a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& e, FSM&,int state) + { + std::cout << "no transition from state " << state + << " on event " << typeid(e).name() << std::endl; + } + }; + // back-end + typedef msm::back::state_machine<Playing_> Playing; + + // state not defining any entry or exit + struct Paused : public msm::front::state<> + { + }; + + // the initial state of the player SM. Must be defined + typedef Empty initial_state; + + // transition actions + void start_playback(play const&) { std::cout << "player::start_playback\n"; } + void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } + void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } + void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } + void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } + void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } + void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } + void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } + void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} + // guard conditions + bool good_disk_format(cd_detected const& evt) + { + // to test a guard condition, let's say we understand only CDs, not DVD + if (evt.disc_type != DISK_CD) + { + std::cout << "wrong disk, sorry" << std::endl; + return false; + } + return true; + } + // used to show a transition conflict. This guard will simply deactivate one transition and thus + // solve the conflict + bool auto_start(cd_detected const&) + { + return false; + } + + typedef player_ p; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Stopped , play , Playing , &p::start_playback >, + a_row < Stopped , open_close , Open , &p::open_drawer >, + _row < Stopped , stop , Stopped >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Open , open_close , Empty , &p::close_drawer >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Empty , open_close , Open , &p::open_drawer >, + row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, + row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Playing , stop , Stopped , &p::stop_playback >, + a_row < Playing , pause , Paused , &p::pause_playback >, + a_row < Playing , open_close , Open , &p::stop_and_open >, + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Paused , end_pause , Playing , &p::resume_playback >, + a_row < Paused , stop , Stopped , &p::stop_playback >, + a_row < Paused , open_close , Open , &p::stop_and_open > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int) + { + BOOST_FAIL("no_transition called!"); + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + + BOOST_AUTO_TEST_CASE( my_test ) + { + SomeExternalContext ctx(3); + player p1(boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p1.context_.bla == 10,"Wrong returned context value"); + + ctx.bla = 3; + player p2(msm::back::states_ << player_::Empty(1),boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 1,"Wrong Empty value"); + + p2.set_states(msm::back::states_ << player_::Empty(5)); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 5,"Wrong Empty value"); + + p2.set_states(msm::back::states_ << player_::Empty(7) << player_::Open(2)); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 7,"Wrong Empty value"); + BOOST_CHECK_MESSAGE(p2.get_state<player_::Open&>().data_ == 2,"Wrong Open value"); + + ctx.bla = 3; + player p(msm::back::states_ << player_::Empty(1) + << player_::Playing(msm::back::states_ << player_::Playing_::Song1(8)), + boost::ref(ctx),5); + BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().get_state<player_::Playing_::Song1&>().data_ == 8,"Wrong Open value"); + } +} + diff --git a/src/boost/libs/msm/test/TestConstructorMovableOnlyTypes.cpp b/src/boost/libs/msm/test/TestConstructorMovableOnlyTypes.cpp new file mode 100644 index 000000000..5f8e1d5a6 --- /dev/null +++ b/src/boost/libs/msm/test/TestConstructorMovableOnlyTypes.cpp @@ -0,0 +1,118 @@ +// Copyright 2016 BogumiĆ Chojnowski +// bogumil DOT chojnowski AT gmail DOT com +// This is 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 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) + +#include <iostream> +#include <memory> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE MyTest +#endif +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; + + +namespace +{ + struct Lightbulp + { + Lightbulp(int c) : current(c) {} + int current; + }; + + // events + struct ev_toggle {}; + + // front-end: define the FSM structure + struct bistable_switch_ : public msm::front::state_machine_def<bistable_switch_> + { + bistable_switch_(std::unique_ptr<Lightbulp> bulp, int load) + : bulp_(std::move(bulp)) + { + BOOST_CHECK_MESSAGE(bulp_->current == 3, "Wrong current value"); + BOOST_CHECK_MESSAGE(load == 5, "Wrong load value"); + bulp_->current = 10; + } + + std::unique_ptr<Lightbulp> bulp_; + + // The list of FSM states + struct Off : public msm::front::state<> + { + template <typename Event, typename FSM> + void on_entry(Event const&, FSM& ) { } + template <typename Event, typename FSM> + void on_exit(Event const&, FSM&) { } + }; + + struct On : public msm::front::state<> + { + template <typename Event, typename FSM> + void on_entry(Event const&, FSM& ) { } + template <typename Event, typename FSM> + void on_exit(Event const&, FSM&) { } + }; + + // the initial state of the player SM. Must be defined + typedef Off initial_state; + + void turn_on(ev_toggle const&) { bulp_->current = 11; } + void turn_off(ev_toggle const&) { bulp_->current = 9; } + + typedef bistable_switch_ bs_; // makes transition table cleaner + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + a_row < Off , ev_toggle , On , &bs_::turn_on >, + a_row < On , ev_toggle , Off , &bs_::turn_off > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + // Replaces the default no-transition response. + + template <typename Event, typename FSM> + void no_transition(Event const&, FSM&, int) + { + BOOST_FAIL("no_transition called!"); + } + }; + + // Pick a back-end + typedef msm::back::state_machine<bistable_switch_> bistable_switch; + + BOOST_AUTO_TEST_CASE(my_test) + { + auto bulp = std::make_unique<Lightbulp>(3); + + bistable_switch bs(std::move(bulp), 5); + BOOST_CHECK_MESSAGE(bs.bulp_->current == 10, "Wrong returned current value"); + + bs.start(); + + bs.process_event(ev_toggle()); + BOOST_CHECK_MESSAGE(bs.bulp_->current == 11, "Wrong returned current value"); + + bs.process_event(ev_toggle()); + BOOST_CHECK_MESSAGE(bs.bulp_->current == 9, "Wrong returned current value"); + + bs.stop(); + } +} + diff --git a/src/boost/libs/msm/test/TestDeferAndMessageQueue.cpp b/src/boost/libs/msm/test/TestDeferAndMessageQueue.cpp new file mode 100644 index 000000000..767f5883a --- /dev/null +++ b/src/boost/libs/msm/test/TestDeferAndMessageQueue.cpp @@ -0,0 +1,194 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct eventResolve {}; + struct eventConnect {}; + struct eventResolved {}; + struct eventRead {}; + struct eventd {}; + + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_() + :expected_action_counter(0) + {} + + struct enqueue_action1 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + fsm.template process_event(eventResolve()); + } + }; + struct enqueue_action2 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + fsm.template process_event(eventConnect()); + } + }; + struct expected_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.expected_action_counter; + //std::cout << "expected action called" << std::endl; + } + }; + struct unexpected_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + std::cout << "unexpected action called" << std::endl; + } + }; + + // The list of FSM states + struct Unresolved : public msm::front::state<> + { + typedef mpl::vector<eventRead > deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + // Transition table for Empty + struct internal_transition_table : mpl::vector< + // Start Event Next Action Guard + Internal < eventConnect , msm::front::ActionSequence_<mpl::vector<enqueue_action1,enqueue_action2>> > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + }; + struct Resolving : public msm::front::state<> + { + typedef mpl::vector<eventConnect > deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Resolved : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct Connecting : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State22 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<Unresolved,State22> initial_state; + + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < Unresolved , eventResolve , Resolving >, + Row < Resolving , eventResolved , Resolved >, + Row < Resolved , eventConnect , Connecting , expected_action >, + Row < State22 , eventd , State22 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int ) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::Unresolved&>().entry_counter=0; + fsm.template get_state<player_::Unresolved&>().exit_counter=0; + fsm.template get_state<player_::Resolving&>().entry_counter=0; + fsm.template get_state<player_::Resolving&>().exit_counter=0; + fsm.template get_state<player_::Resolved&>().entry_counter=0; + fsm.template get_state<player_::Resolved&>().exit_counter=0; + fsm.template get_state<player_::Connecting&>().entry_counter=0; + fsm.template get_state<player_::Connecting&>().exit_counter=0; + } + int expected_action_counter; + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + BOOST_AUTO_TEST_CASE( TestDeferAndMessageQueue ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + p.process_event(eventConnect()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Resolving should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 3,"State22 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Unresolved&>().exit_counter == 1,"Unresolved exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Unresolved&>().entry_counter == 1,"Unresolved entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Resolving&>().entry_counter == 1,"Resolving entry not called correctly"); + + p.process_event(eventResolved()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Connecting should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 3,"State22 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Resolved&>().exit_counter == 1,"Resolved exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Resolved&>().entry_counter == 1,"Resolved entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Resolving&>().exit_counter == 1,"Resolving exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::Connecting&>().entry_counter == 1,"Connecting entry not called correctly"); + + BOOST_CHECK_MESSAGE(p.expected_action_counter == 1,"expected_action should have been called"); + + } +} + + diff --git a/src/boost/libs/msm/test/TestDeferAndMessageQueue2.cpp b/src/boost/libs/msm/test/TestDeferAndMessageQueue2.cpp new file mode 100644 index 000000000..f4c209912 --- /dev/null +++ b/src/boost/libs/msm/test/TestDeferAndMessageQueue2.cpp @@ -0,0 +1,155 @@ +// Copyright 2017 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + struct event3 {}; + struct eventd {}; + + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + player_() + :expected_action_counter(0),expected_action2_counter(0) + {} + // The list of FSM states + struct State11 : public msm::front::state<> + { + typedef mpl::vector<eventd> deferred_events; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State12 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State13 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct enqueue_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + fsm.template process_event(event2()); + } + }; + struct expected_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.expected_action_counter; + } + }; + struct expected_action2 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.expected_action2_counter; + } + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<State11> initial_state; + + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < State11 , event1 , State12 , enqueue_action >, + Row < State12 , event2 , State13 , expected_action2 >, + Row < State12 , eventd , State13 , expected_action >, + Row < State13 , event2 , State11 >, + Row < State13 , eventd , State11 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int ) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::State11&>().entry_counter=0; + fsm.template get_state<player_::State11&>().exit_counter=0; + fsm.template get_state<player_::State12&>().entry_counter=0; + fsm.template get_state<player_::State12&>().exit_counter=0; + fsm.template get_state<player_::State13&>().entry_counter=0; + fsm.template get_state<player_::State13&>().exit_counter=0; + } + int expected_action_counter; + int expected_action2_counter; + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + BOOST_AUTO_TEST_CASE( TestDeferAndMessageQueue2 ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + p.process_event(eventd()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State11 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 1,"State11 entry not called correctly"); + + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State11 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().exit_counter == 1,"State11 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().entry_counter == 1,"State12 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().exit_counter == 1,"State12 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 2,"State11 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State13&>().exit_counter == 1,"State13 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State13&>().entry_counter == 1,"State13 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.expected_action_counter == 1,"expected_action should have been called"); + BOOST_CHECK_MESSAGE(p.expected_action2_counter == 0,"expected_action2 should not have been called"); + } +} + + diff --git a/src/boost/libs/msm/test/TestDeferAndMessageQueue3.cpp b/src/boost/libs/msm/test/TestDeferAndMessageQueue3.cpp new file mode 100644 index 000000000..ed4e63427 --- /dev/null +++ b/src/boost/libs/msm/test/TestDeferAndMessageQueue3.cpp @@ -0,0 +1,158 @@ +// Copyright 2017 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + struct event3 {}; + struct eventd {}; + + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // in this test, we inverse the order deferred queue / event queue + typedef int event_queue_before_deferred_queue; + + player_() + :expected_action_counter(0),expected_action2_counter(0) + {} + // The list of FSM states + struct State11 : public msm::front::state<> + { + typedef mpl::vector<eventd> deferred_events; + + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State12 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State13 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct enqueue_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + fsm.template process_event(event2()); + } + }; + struct expected_action + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.expected_action_counter; + } + }; + struct expected_action2 + { + template <class EVT,class FSM,class SourceState,class TargetState> + void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) + { + ++fsm.expected_action2_counter; + } + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<State11> initial_state; + + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < State11 , event1 , State12 , enqueue_action >, + Row < State12 , event2 , State13 , expected_action2 >, + Row < State12 , eventd , State13 , expected_action >, + Row < State13 , event2 , State11 >, + Row < State13 , eventd , State11 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int ) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::State11&>().entry_counter=0; + fsm.template get_state<player_::State11&>().exit_counter=0; + fsm.template get_state<player_::State12&>().entry_counter=0; + fsm.template get_state<player_::State12&>().exit_counter=0; + fsm.template get_state<player_::State13&>().entry_counter=0; + fsm.template get_state<player_::State13&>().exit_counter=0; + } + int expected_action_counter; + int expected_action2_counter; + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + BOOST_AUTO_TEST_CASE( TestDeferAndMessageQueue2 ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + p.process_event(eventd()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State11 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 1,"State11 entry not called correctly"); + + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"State11 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().exit_counter == 1,"State11 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().entry_counter == 1,"State12 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().exit_counter == 1,"State12 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 2,"State11 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State13&>().exit_counter == 1,"State13 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State13&>().entry_counter == 1,"State13 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.expected_action_counter == 0,"expected_action should have been called"); + BOOST_CHECK_MESSAGE(p.expected_action2_counter == 1,"expected_action2 should not have been called"); + } +} + + diff --git a/src/boost/libs/msm/test/TestDeferIn2Regions.cpp b/src/boost/libs/msm/test/TestDeferIn2Regions.cpp new file mode 100644 index 000000000..ff822f290 --- /dev/null +++ b/src/boost/libs/msm/test/TestDeferIn2Regions.cpp @@ -0,0 +1,169 @@ +// 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) + +#include <iostream> +// back-end +#include <boost/msm/back/state_machine.hpp> +//front-end +#include <boost/msm/front/state_machine_def.hpp> +#include <boost/msm/front/functor_row.hpp> + +#include <boost/test/unit_test.hpp> + +namespace msm = boost::msm; +namespace mpl = boost::mpl; +using namespace boost::msm::front; + +namespace +{ + // events + struct event1 {}; + struct event2 {}; + struct event3 {}; + struct eventd {}; + + + // front-end: define the FSM structure + struct player_ : public msm::front::state_machine_def<player_> + { + // The list of FSM states + struct State11 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State12 : public msm::front::state<> + { + typedef mpl::vector<eventd> deferred_events; + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State13 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State21 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + struct State22 : public msm::front::state<> + { + template <class Event,class FSM> + void on_entry(Event const&,FSM& ) {++entry_counter;} + template <class Event,class FSM> + void on_exit(Event const&,FSM& ) {++exit_counter;} + int entry_counter; + int exit_counter; + }; + // the initial state of the player SM. Must be defined + typedef mpl::vector<State11,State21> initial_state; + + + // Transition table for player + struct transition_table : mpl::vector< + // Start Event Next Action Guard + // +---------+-------------+---------+---------------------+----------------------+ + Row < State11 , event1 , State12 >, + Row < State12 , event2 , State13 >, + + Row < State21 , event3 , State22 >, + Row < State22 , eventd , State21 > + // +---------+-------------+---------+---------------------+----------------------+ + > {}; + + // Replaces the default no-transition response. + template <class FSM,class Event> + void no_transition(Event const& , FSM&,int ) + { + BOOST_FAIL("no_transition called!"); + } + // init counters + template <class Event,class FSM> + void on_entry(Event const&,FSM& fsm) + { + fsm.template get_state<player_::State11&>().entry_counter=0; + fsm.template get_state<player_::State11&>().exit_counter=0; + fsm.template get_state<player_::State12&>().entry_counter=0; + fsm.template get_state<player_::State12&>().exit_counter=0; + fsm.template get_state<player_::State13&>().entry_counter=0; + fsm.template get_state<player_::State13&>().exit_counter=0; + fsm.template get_state<player_::State21&>().entry_counter=0; + fsm.template get_state<player_::State22&>().exit_counter=0; + } + }; + // Pick a back-end + typedef msm::back::state_machine<player_> player; + + BOOST_AUTO_TEST_CASE( TestDeferIn2Regions ) + { + player p; + // needed to start the highest-level SM. This will call on_entry and mark the start of the SM + p.start(); + + p.process_event(event1()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"State12 should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().exit_counter == 1,"State11 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 1,"State11 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().entry_counter == 1,"State12 entry not called correctly"); + + // deferred + p.process_event(eventd()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"State12 should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().exit_counter == 1,"State11 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State11&>().entry_counter == 1,"State11 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().entry_counter == 1,"State12 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State12&>().exit_counter == 0,"State12 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().exit_counter == 0,"State21 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().entry_counter == 1,"State21 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().exit_counter == 0,"State22 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().entry_counter == 0,"State22 entry not called correctly"); + + p.process_event(event3()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"State12 should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().exit_counter == 1,"State21 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().entry_counter == 2,"State21 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().exit_counter == 1,"State22 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().entry_counter == 1,"State22 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_deferred_queue().size() == 1,"Deferred queue should have one element"); + p.clear_deferred_queue(); + + p.process_event(event2()); + BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"State13 should be active"); + BOOST_CHECK_MESSAGE(p.current_state()[1] == 2,"State21 should be active"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().exit_counter == 1,"State21 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State21&>().entry_counter == 2,"State21 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().exit_counter == 1,"State22 exit not called correctly"); + BOOST_CHECK_MESSAGE(p.get_state<player_::State22&>().entry_counter == 1,"State22 entry not called correctly"); + BOOST_CHECK_MESSAGE(p.get_deferred_queue().size() == 0,"Deferred queue should have no element"); + } +} + + |