1 //////////////////////////////////////////////////////////////////////////////
2 // Copyright 2005-2006 Andreas Huber Doenni
3 // Distributed under the Boost Software License, Version 1.0. (See accompany-
4 // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //////////////////////////////////////////////////////////////////////////////
6 
7 
8 
9 #include <boost/statechart/state_machine.hpp>
10 #include <boost/statechart/event.hpp>
11 #include <boost/statechart/simple_state.hpp>
12 #include <boost/statechart/transition.hpp>
13 #include <boost/statechart/custom_reaction.hpp>
14 
15 #include <boost/mpl/list.hpp>
16 
17 #include <boost/test/test_tools.hpp>
18 
19 #include <set>
20 #include <map>
21 #include <string>
22 
23 #include <cstddef> // size_t
24 
25 
26 
27 namespace sc = boost::statechart;
28 namespace mpl = boost::mpl;
29 
30 
31 
32 struct EvToC : sc::event< EvToC > {};
33 struct EvToD : sc::event< EvToD > {};
34 
35 struct EvDiscardNever : sc::event< EvDiscardNever > {};
36 struct EvDiscardInB : sc::event< EvDiscardInB > {};
37 struct EvDiscardInD : sc::event< EvDiscardInD > {};
38 
39 struct EvTransit : sc::event< EvTransit > {};
40 struct EvTransitWithAction : sc::event< EvTransitWithAction > {};
41 struct EvDefer : sc::event< EvDefer > {};
42 struct EvTerminate : sc::event< EvTerminate > {};
43 
44 
45 struct A;
46 struct CustomReactionTest : sc::state_machine< CustomReactionTest, A >
47 {
48   public:
49     //////////////////////////////////////////////////////////////////////////
50     CustomReactionTest();
51 
VisitedCustomReactionTest52     void Visited( const state_base_type & stt )
53     {
54       const StateNamesMap::const_iterator found =
55         stateNamesMap_.find( stt.dynamic_type() );
56       BOOST_REQUIRE( found != stateNamesMap_.end() );
57       visitedStates_.insert( found->second );
58     }
59 
ClearVisitedCustomReactionTest60     void ClearVisited()
61     {
62       visitedStates_.clear();
63     }
64 
AssertVisitedAllCustomReactionTest65     void AssertVisitedAll( const std::string & stateNames ) const
66     {
67       for ( std::string::const_iterator expectedName = stateNames.begin();
68         expectedName != stateNames.end(); ++expectedName )
69       {
70         BOOST_REQUIRE( visitedStates_.find(
71           std::string( 1, *expectedName ) ) != visitedStates_.end() );
72       }
73     }
74 
AssertVisitedOneCustomReactionTest75     void AssertVisitedOne( const std::string & stateNames ) const
76     {
77       std::size_t found = 0;
78 
79       for ( std::string::const_iterator actualName = stateNames.begin();
80         actualName != stateNames.end(); ++actualName )
81       {
82         found = found + ( visitedStates_.find(
83           std::string( 1, *actualName ) ) != visitedStates_.end() ) ? 1 : 0;
84       }
85 
86       BOOST_REQUIRE( found == 1 );
87     }
88 
TransitionActionCustomReactionTest89     void TransitionAction( const EvTransitWithAction & ) {}
90 
91   private:
92     //////////////////////////////////////////////////////////////////////////
93     typedef std::map< state_base_type::id_type, std::string > StateNamesMap;
94     typedef std::set< std::string > VisitedStates;
95 
96     StateNamesMap stateNamesMap_;
97     VisitedStates visitedStates_;
98 };
99 
100 struct B;
101 struct A : sc::simple_state< A, CustomReactionTest, B >
102 {
103   typedef mpl::list<
104     sc::custom_reaction< EvDiscardNever >,
105     sc::custom_reaction< EvDiscardInB >,
106     sc::custom_reaction< EvDiscardInD >,
107     sc::custom_reaction< EvDefer >,
108     sc::custom_reaction< EvTerminate >,
109     sc::custom_reaction< EvTransitWithAction >,
110     sc::custom_reaction< EvTransit >
111   > reactions;
112 
reactA113   sc::result react( const EvDiscardNever & )
114   {
115     outermost_context().Visited( *this );
116     return forward_event();
117   }
118 
reactA119   sc::result react( const EvDiscardInB & )
120   {
121     BOOST_FAIL( "An event discarded in B must never reach A" );
122     return discard_event();
123   }
124 
reactA125   sc::result react( const EvDiscardInD & )
126   {
127     BOOST_FAIL( "An event discarded in D must never reach B" );
128     return discard_event();
129   }
130 
131   // The following code is here just to make sure that calls to the transit<>,
132   // defer_event and terminate functions actually compile.
133   // Their functionality is tested extensively in TransitionTest,
134   // DeferralTest and TerminationTest with appropriate reactions. Internally,
135   // these reactions call exactly the same functions as the following custom
136   // reactions call.
reactA137   sc::result react( const EvDefer & )
138   {
139     return defer_event();
140   }
141 
reactA142   sc::result react( const EvTerminate & )
143   {
144     return terminate();
145   }
146 
reactA147   sc::result react( const EvTransit & )
148   {
149     return transit< A >();
150   }
151 
reactA152   sc::result react( const EvTransitWithAction & evt )
153   {
154     return transit< A >( &CustomReactionTest::TransitionAction, evt );
155   }
156 };
157 
158   struct C;
159   struct B : sc::simple_state< B, A, C >
160   {
161     typedef mpl::list<
162       sc::custom_reaction< EvDiscardNever >,
163       sc::custom_reaction< EvDiscardInB >,
164       sc::custom_reaction< EvDiscardInD >
165     > reactions;
166 
reactB167     sc::result react( const EvDiscardNever & )
168     {
169       outermost_context().Visited( *this );
170       return forward_event();
171     }
172 
reactB173     sc::result react( const EvDiscardInB & )
174     {
175       outermost_context().Visited( *this );
176       return discard_event();
177     }
178 
reactB179     sc::result react( const EvDiscardInD & )
180     {
181       BOOST_FAIL( "An event discarded in D must never reach B" );
182       return discard_event();
183     }
184   };
185 
186     struct E;
187     struct F;
188     struct D : sc::simple_state< D, B, mpl::list< E, F > >
189     {
190       typedef mpl::list<
191         sc::transition< EvToC, C >,
192         sc::custom_reaction< EvDiscardNever >,
193         sc::custom_reaction< EvDiscardInB >,
194         sc::custom_reaction< EvDiscardInD >
195       > reactions;
196 
reactD197       sc::result react( const EvDiscardNever & )
198       {
199         outermost_context().Visited( *this );
200         return forward_event();
201       }
202 
reactD203       sc::result react( const EvDiscardInB & )
204       {
205         outermost_context().Visited( *this );
206         return forward_event();
207       }
208 
reactD209       sc::result react( const EvDiscardInD & )
210       {
211         outermost_context().Visited( *this );
212         return discard_event();
213       }
214     };
215 
216       struct E : sc::simple_state< E, D::orthogonal< 0 >  >
217       {
218         typedef mpl::list<
219           sc::custom_reaction< EvDiscardNever >,
220           sc::custom_reaction< EvDiscardInB >,
221           sc::custom_reaction< EvDiscardInD >
222         > reactions;
223 
reactE224         sc::result react( const EvDiscardNever & )
225         {
226           outermost_context().Visited( *this );
227           return forward_event();
228         }
229 
reactE230         sc::result react( const EvDiscardInB & )
231         {
232           outermost_context().Visited( *this );
233           return forward_event();
234         }
235 
reactE236         sc::result react( const EvDiscardInD & )
237         {
238           outermost_context().Visited( *this );
239           return forward_event();
240         }
241       };
242 
243       struct F : sc::simple_state< F, D::orthogonal< 1 > >
244       {
245         typedef mpl::list<
246           sc::custom_reaction< EvDiscardNever >,
247           sc::custom_reaction< EvDiscardInB >,
248           sc::custom_reaction< EvDiscardInD >
249         > reactions;
250 
reactF251         sc::result react( const EvDiscardNever & )
252         {
253           outermost_context().Visited( *this );
254           return forward_event();
255         }
256 
reactF257         sc::result react( const EvDiscardInB & )
258         {
259           outermost_context().Visited( *this );
260           return forward_event();
261         }
262 
reactF263         sc::result react( const EvDiscardInD & )
264         {
265           outermost_context().Visited( *this );
266           return forward_event();
267         }
268       };
269 
270     struct C : sc::simple_state< C, B >
271     {
272       typedef mpl::list<
273         sc::transition< EvToD, D >,
274         sc::custom_reaction< EvDiscardNever >,
275         sc::custom_reaction< EvDiscardInB >,
276         sc::custom_reaction< EvDiscardInD >
277       > reactions;
278 
reactC279       sc::result react( const EvDiscardNever & )
280       {
281         outermost_context().Visited( *this );
282         return forward_event();
283       }
284 
reactC285       sc::result react( const EvDiscardInB & )
286       {
287         outermost_context().Visited( *this );
288         return forward_event();
289       }
290 
reactC291       sc::result react( const EvDiscardInD & )
292       {
293         outermost_context().Visited( *this );
294         return forward_event();
295       }
296     };
297 
CustomReactionTest()298 CustomReactionTest::CustomReactionTest()
299 {
300   // We're not using custom type information to make this test work even when
301   // BOOST_STATECHART_USE_NATIVE_RTTI is defined
302   stateNamesMap_[ A::static_type() ] = "A";
303   stateNamesMap_[ B::static_type() ] = "B";
304   stateNamesMap_[ C::static_type() ] = "C";
305   stateNamesMap_[ D::static_type() ] = "D";
306   stateNamesMap_[ E::static_type() ] = "E";
307   stateNamesMap_[ F::static_type() ] = "F";
308 }
309 
310 
311 struct X1;
312 struct CustomReactionEventBaseTest : sc::state_machine< CustomReactionEventBaseTest, X1 >
313 {
314   public:
CustomReactionEventBaseTestCustomReactionEventBaseTest315     CustomReactionEventBaseTest() : reactionCount_( 0 ) {}
316 
IncrementReactionCountCustomReactionEventBaseTest317     void IncrementReactionCount()
318     {
319       ++reactionCount_;
320     }
321 
GetReactionCountCustomReactionEventBaseTest322     unsigned int GetReactionCount() const
323     {
324       return reactionCount_;
325     }
326 
327   private:
328     unsigned int reactionCount_;
329 };
330 
331 struct X1 : sc::simple_state< X1, CustomReactionEventBaseTest >
332 {
333   typedef sc::custom_reaction< sc::event_base > reactions;
334 
reactX1335   sc::result react( const sc::event_base & )
336   {
337     outermost_context().IncrementReactionCount();
338     return discard_event();
339   }
340 };
341 
342 
test_main(int,char * [])343 int test_main( int, char* [] )
344 {
345   CustomReactionTest machine;
346   machine.initiate();
347 
348   machine.process_event( EvDiscardNever() );
349   machine.AssertVisitedAll( "ABC" );
350   machine.ClearVisited();
351 
352   machine.process_event( EvDiscardInB() );
353   machine.AssertVisitedAll( "BC" );
354   machine.process_event( EvToD() );
355   machine.ClearVisited();
356 
357   machine.process_event( EvDiscardNever() );
358   machine.AssertVisitedAll( "ABDEF" );
359   machine.ClearVisited();
360 
361   machine.process_event( EvDiscardInD() );
362   machine.AssertVisitedAll( "D" );
363   machine.AssertVisitedOne( "EF" );
364   machine.ClearVisited();
365 
366   machine.process_event( EvDiscardInB() );
367   machine.AssertVisitedAll( "BD" );
368   machine.AssertVisitedOne( "EF" );
369   machine.ClearVisited();
370 
371 
372   CustomReactionEventBaseTest eventBaseMachine;
373   eventBaseMachine.initiate();
374   BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 0 );
375   eventBaseMachine.process_event( EvToC() );
376   BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 1 );
377   eventBaseMachine.process_event( EvToD() );
378   BOOST_REQUIRE( eventBaseMachine.GetReactionCount() == 2 );
379 
380   return 0;
381 }
382