1// Copyright 2010 Christophe Henry
2// henry UNDERSCORE christophe AT hotmail DOT com
3// This is an extended version of the state machine available in the boost::mpl library
4// Distributed under the same license as the original.
5// Copyright for the original version:
6// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
7// under the Boost Software License, Version 1.0. (See accompanying
8// file LICENSE_1_0.txt or copy at
9// http://www.boost.org/LICENSE_1_0.txt)
10
11#include <boost/msm/back/state_machine.hpp>
12#include <boost/msm/front/euml/euml.hpp>
13
14#ifndef BOOST_MSM_NONSTANDALONE_TEST
15#define BOOST_TEST_MODULE MyTest
16#endif
17#include <boost/test/unit_test.hpp>
18// include headers that implement a archive in simple text format
19#include <boost/archive/text_oarchive.hpp>
20#include <boost/archive/text_iarchive.hpp>
21#include <boost/serialization/tracking.hpp>
22
23#include <sstream>
24
25using namespace std;
26using namespace boost::msm::front::euml;
27namespace msm = boost::msm;
28
29namespace
30{
31 // A "complicated" event type that carries some data.
32 enum DiskTypeEnum
33 {
34 DISK_CD=0,
35 DISK_DVD=1
36 };
37 // events
38 BOOST_MSM_EUML_EVENT(play)
39 BOOST_MSM_EUML_EVENT(end_pause)
40 BOOST_MSM_EUML_EVENT(stop)
41 BOOST_MSM_EUML_EVENT(pause)
42 BOOST_MSM_EUML_EVENT(open_close)
43 // A "complicated" event type that carries some data.
44 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name)
45 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type)
46 BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes)
47 BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes)
48
49 //states
50 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter)
51 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter)
52
53 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty)
54 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open)
55 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped)
56 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing)
57 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused)
58
59 //fsm
60 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter)
61 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter)
62 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter)
63 BOOST_MSM_EUML_ACTION(No_Transition)
64 {
65 template <class FSM,class Event>
66 void operator()(Event const&,FSM&,int)
67 {
68 BOOST_FAIL("no_transition called!");
69 }
70 };
71 BOOST_MSM_EUML_ACTION(good_disk_format)
72 {
73 template <class FSM,class EVT,class SourceState,class TargetState>
74 bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
75 {
76 if (evt.get_attribute(cd_type)!=DISK_CD)
77 {
78 return false;
79 }
80 return true;
81 }
82 };
83 BOOST_MSM_EUML_TRANSITION_TABLE((
84 Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ),
85 Playing == Paused + end_pause ,
86 // +------------------------------------------------------------------------------+
87 Empty == Open + open_close / ++fsm_(can_close_drawer_counter),
88 // +------------------------------------------------------------------------------+
89 Open == Empty + open_close ,
90 Open == Paused + open_close ,
91 Open == Stopped + open_close ,
92 Open == Playing + open_close ,
93 // +------------------------------------------------------------------------------+
94 Paused == Playing + pause ,
95 // +------------------------------------------------------------------------------+
96 Stopped == Playing + stop ,
97 Stopped == Paused + stop ,
98 Stopped == Empty + cd_detected [good_disk_format ||
99 (event_(cd_type)==Int_<DISK_CD>())] / process_(play) ,
100 Stopped == Stopped + stop
101 // +------------------------------------------------------------------------------+
102 ),transition_table)
103
104 BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
105 init_ << Empty, // Init State
106 no_action, // Entry
107 no_action, // Exit
108 attributes_ << start_playback_counter
109 << can_close_drawer_counter << test_fct_counter, // Attributes
110 configure_ << no_configure_, // configuration
111 No_Transition // no_transition handler
112 ),
113 player_) //fsm name
114
115 typedef msm::back::state_machine<player_> player;
116
117// static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" };
118
119
120 BOOST_AUTO_TEST_CASE( my_test )
121 {
122 player p2;
123
124 p2.start();
125 BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1,
126 "Empty entry not called correctly");
127
128 p2.process_event(evt: open_close());
129 BOOST_CHECK_MESSAGE(p2.current_state()[0] == 2,"Open should be active"); //Open
130 BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1,
131 "Empty exit not called correctly");
132 BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1,
133 "Open entry not called correctly");
134
135 // test the serialization
136 std::ostringstream ofs;
137 // save fsm to archive (current state is Open)
138 {
139 boost::archive::text_oarchive oa(ofs);
140 // write class instance to archive
141 oa << p2;
142 }
143 // reload fsm in state Open
144 player p;
145 {
146 // create and open an archive for input
147 std::istringstream ifs(ofs.str());
148 boost::archive::text_iarchive ia(ifs);
149 // read class state from archive
150 ia >> p;
151 }
152
153 p.process_event(evt: open_close());
154 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty
155
156 p.process_event(
157 evt: cd_detected("louie, louie",DISK_DVD));
158 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty
159 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1,
160 "Open exit not called correctly");
161
162 p.process_event(
163 evt: cd_detected("louie, louie",DISK_CD));
164 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
165 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1,
166 "Stopped entry not called correctly");
167 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1,
168 "Stopped exit not called correctly");
169 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1,
170 "Playing entry not called correctly");
171 BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly");
172 BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly");
173
174 p.process_event(evt: pause());
175 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused
176 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1,
177 "Playing exit not called correctly");
178 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1,
179 "Paused entry not called correctly");
180
181 // go back to Playing
182 p.process_event(evt: end_pause());
183 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
184 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1,
185 "Paused exit not called correctly");
186 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2,
187 "Playing entry not called correctly");
188
189 p.process_event(evt: pause());
190 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused
191 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2,
192 "Playing exit not called correctly");
193 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2,
194 "Paused entry not called correctly");
195
196 p.process_event(evt: stop());
197 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
198 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2,
199 "Paused exit not called correctly");
200 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2,
201 "Stopped entry not called correctly");
202
203 p.process_event(evt: stop());
204 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
205 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2,
206 "Stopped exit not called correctly");
207 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3,
208 "Stopped entry not called correctly");
209 }
210}
211
212// eliminate object tracking (even if serialized through a pointer)
213// at the risk of a programming error creating duplicate objects.
214// this is to get rid of warning because p is not const
215BOOST_CLASS_TRACKING(player, boost::serialization::track_never)
216

source code of boost/libs/msm/test/SerializeSimpleEuml.cpp