|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r62473 - trunk/libs/msm/test
From: christophe.j.henry_at_[hidden]
Date: 2010-06-06 04:45:19
Author: chenry
Date: 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
New Revision: 62473
URL: http://svn.boost.org/trac/boost/changeset/62473
Log:
added unit tests
Added:
trunk/libs/msm/test/
trunk/libs/msm/test/Anonymous.cpp (contents, props changed)
trunk/libs/msm/test/AnonymousEuml.cpp (contents, props changed)
trunk/libs/msm/test/CompositeEuml.cpp (contents, props changed)
trunk/libs/msm/test/CompositeMachine.cpp (contents, props changed)
trunk/libs/msm/test/Constructor.cpp (contents, props changed)
trunk/libs/msm/test/History.cpp (contents, props changed)
trunk/libs/msm/test/OrthogonalDeferred.cpp (contents, props changed)
trunk/libs/msm/test/OrthogonalDeferred2.cpp (contents, props changed)
trunk/libs/msm/test/OrthogonalDeferredEuml.cpp (contents, props changed)
trunk/libs/msm/test/SimpleEuml.cpp (contents, props changed)
trunk/libs/msm/test/SimpleEuml2.cpp (contents, props changed)
trunk/libs/msm/test/SimpleInternal.cpp (contents, props changed)
trunk/libs/msm/test/SimpleInternalEuml.cpp (contents, props changed)
trunk/libs/msm/test/SimpleInternalFunctors.cpp (contents, props changed)
trunk/libs/msm/test/SimpleMachine.cpp (contents, props changed)
trunk/libs/msm/test/SimpleWithFunctors.cpp (contents, props changed)
Added: trunk/libs/msm/test/Anonymous.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/Anonymous.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,172 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+//front-end
+#include <boost/msm/front/state_machine_def.hpp>
+
+#define BOOST_TEST_MODULE MyTest
+#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& evt)
+ {
+ ++always_true_counter;
+ return true;
+ }
+ bool always_false(none const& evt)
+ {
+ ++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.get_state<my_machine_::State1&>().entry_counter=0;
+ fsm.get_state<my_machine_::State1&>().exit_counter=0;
+ fsm.get_state<my_machine_::State2&>().entry_counter=0;
+ fsm.get_state<my_machine_::State2&>().exit_counter=0;
+ fsm.get_state<my_machine_::State3&>().entry_counter=0;
+ fsm.get_state<my_machine_::State3&>().exit_counter=0;
+ fsm.get_state<my_machine_::State4&>().entry_counter=0;
+ fsm.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");
+
+ }
+}
+
Added: trunk/libs/msm/test/AnonymousEuml.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/AnonymousEuml.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,155 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+#include <boost/msm/front/euml/euml.hpp>
+
+#define BOOST_TEST_MODULE MyTest
+#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& evt,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& evt,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");
+
+ }
+}
+
Added: trunk/libs/msm/test/CompositeEuml.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/CompositeEuml.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,268 @@
+#include <iostream>
+#include <boost/msm/back/state_machine.hpp>
+#include <boost/msm/front/euml/euml.hpp>
+
+#define BOOST_TEST_MODULE MyTest
+#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");
+ }
+}
+
Added: trunk/libs/msm/test/CompositeMachine.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/CompositeMachine.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,333 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+//front-end
+#include <boost/msm/front/state_machine_def.hpp>
+#define BOOST_TEST_MODULE MyTest
+#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& e, FSM&,int state)
+ {
+ BOOST_FAIL("no_transition called!");
+ }
+ // init counters
+ template <class Event,class FSM>
+ void on_entry(Event const&,FSM& fsm)
+ {
+ fsm.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.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");
+ }
+}
+
Added: trunk/libs/msm/test/Constructor.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/Constructor.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,198 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+//front-end
+#include <boost/msm/front/state_machine_def.hpp>
+#define BOOST_TEST_MODULE MyTest
+#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.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.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");
+ }
+}
+
Added: trunk/libs/msm/test/History.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/History.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,344 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+//front-end
+#include <boost/msm/front/state_machine_def.hpp>
+#define BOOST_TEST_MODULE MyTest
+#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& e, FSM&,int state)
+ {
+ BOOST_FAIL("no_transition called!");
+ }
+ // init counters
+ template <class Event,class FSM>
+ void on_entry(Event const&,FSM& fsm)
+ {
+ fsm.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.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");
+
+ }
+}
+
Added: trunk/libs/msm/test/OrthogonalDeferred.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/OrthogonalDeferred.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,476 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+//front-end
+#include <boost/msm/front/state_machine_def.hpp>
+#define BOOST_TEST_MODULE MyTest
+#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& e, FSM&,int state)
+ {
+ BOOST_FAIL("no_transition called!");
+ }
+ // init counters
+ template <class Event,class FSM>
+ void on_entry(Event const&,FSM& fsm)
+ {
+ fsm.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.get_state<player_::Paused&>().exit_counter=0;
+ fsm.get_state<player_::AllOk&>().entry_counter=0;
+ fsm.get_state<player_::AllOk&>().exit_counter=0;
+ fsm.get_state<player_::ErrorMode&>().entry_counter=0;
+ fsm.get_state<player_::ErrorMode&>().exit_counter=0;
+ fsm.get_state<player_::ErrorTerminate&>().entry_counter=0;
+ fsm.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");
+
+ }
+}
+
Added: trunk/libs/msm/test/OrthogonalDeferred2.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/OrthogonalDeferred2.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,483 @@
+#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>
+
+#define BOOST_TEST_MODULE MyTest
+#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& e, FSM&,int state)
+ {
+ BOOST_FAIL("no_transition called!");
+ }
+ // init counters
+ template <class Event,class FSM>
+ void on_entry(Event const&,FSM& fsm)
+ {
+ fsm.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song1&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song2&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().get_state<player_::Playing::Song3&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.get_state<player_::Paused&>().exit_counter=0;
+ fsm.get_state<player_::AllOk&>().entry_counter=0;
+ fsm.get_state<player_::AllOk&>().exit_counter=0;
+ fsm.get_state<player_::ErrorMode&>().entry_counter=0;
+ fsm.get_state<player_::ErrorMode&>().exit_counter=0;
+ fsm.get_state<player_::ErrorTerminate&>().entry_counter=0;
+ fsm.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");
+
+ }
+}
+
Added: trunk/libs/msm/test/OrthogonalDeferredEuml.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/OrthogonalDeferredEuml.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,348 @@
+#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>
+
+#define BOOST_TEST_MODULE MyTest
+#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");
+
+ }
+}
+
Added: trunk/libs/msm/test/SimpleEuml.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/SimpleEuml.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,185 @@
+#include <iostream>
+#include <boost/msm/back/state_machine.hpp>
+#include <boost/msm/front/euml/euml.hpp>
+
+#define BOOST_TEST_MODULE MyTest
+#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");
+ }
+}
+
Added: trunk/libs/msm/test/SimpleEuml2.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/SimpleEuml2.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,185 @@
+#include <iostream>
+#include <boost/msm/back/state_machine.hpp>
+#include <boost/msm/front/euml/euml.hpp>
+
+#define BOOST_TEST_MODULE MyTest
+#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");
+ }
+}
+
Added: trunk/libs/msm/test/SimpleInternal.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/SimpleInternal.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,264 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+//front-end
+#include <boost/msm/front/state_machine_def.hpp>
+#define BOOST_TEST_MODULE MyTest
+#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.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.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");
+ }
+}
+
Added: trunk/libs/msm/test/SimpleInternalEuml.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/SimpleInternalEuml.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,241 @@
+#include <iostream>
+#include <boost/msm/back/state_machine.hpp>
+#include <boost/msm/front/euml/euml.hpp>
+
+#define BOOST_TEST_MODULE MyTest
+#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_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_ATTRIBUTE(unsigned int,empty_internal_guard_counter)
+ BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_action_counter)
+ 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
+ {
+ 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& evt,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& evt,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");
+ }
+}
+
Added: trunk/libs/msm/test/SimpleInternalFunctors.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/SimpleInternalFunctors.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,321 @@
+#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>
+
+#define BOOST_TEST_MODULE MyTest
+#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& evt ,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& evt ,FSM& fsm,SourceState& ,TargetState& )
+ {
+ ++fsm.internal_action_counter;
+ }
+ };
+ struct internal_guard
+ {
+ template <class EVT,class FSM,class SourceState,class TargetState>
+ bool operator()(EVT const& evt ,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& evt ,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.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().empty_internal_guard_counter=0;
+ fsm.get_state<player_::Empty&>().empty_internal_action_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.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");
+ }
+}
+
Added: trunk/libs/msm/test/SimpleMachine.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/SimpleMachine.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,245 @@
+#include <iostream>
+// back-end
+#include <boost/msm/back/state_machine.hpp>
+//front-end
+#include <boost/msm/front/state_machine_def.hpp>
+#define BOOST_TEST_MODULE MyTest
+#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.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.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");
+ }
+}
+
Added: trunk/libs/msm/test/SimpleWithFunctors.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/msm/test/SimpleWithFunctors.cpp 2010-06-06 04:45:17 EDT (Sun, 06 Jun 2010)
@@ -0,0 +1,349 @@
+#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>
+
+#define BOOST_TEST_MODULE MyTest
+#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& evt,FSM& fsm,SourceState& src,TargetState& tgt)
+ {
+ 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& evt ,FSM&,SourceState& ,TargetState& )
+ {
+ return true;
+ }
+ };
+ struct can_close_drawer
+ {
+ template <class EVT,class FSM,class SourceState,class TargetState>
+ bool operator()(EVT const& evt ,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.get_state<player_::Stopped&>().entry_counter=0;
+ fsm.get_state<player_::Stopped&>().exit_counter=0;
+ fsm.get_state<player_::Open&>().entry_counter=0;
+ fsm.get_state<player_::Open&>().exit_counter=0;
+ fsm.get_state<player_::Empty&>().entry_counter=0;
+ fsm.get_state<player_::Empty&>().exit_counter=0;
+ fsm.get_state<player_::Playing&>().entry_counter=0;
+ fsm.get_state<player_::Playing&>().exit_counter=0;
+ fsm.get_state<player_::Paused&>().entry_counter=0;
+ fsm.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");
+ }
+}
+
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk