1 //////////////////////////////////////////////////////////////////////////////
2 // Copyright 2004-2007 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 //////////////////////////////////////////////////////////////////////////////
9 #include "OuterOrthogonal.hpp"
10 #include "InnermostDefault.hpp"
12 #include <boost/statechart/state_machine.hpp>
13 #include <boost/statechart/null_exception_translator.hpp>
14 #include <boost/statechart/exception_translator.hpp>
15 #include <boost/statechart/event.hpp>
16 #include <boost/statechart/transition.hpp>
17 #include <boost/statechart/custom_reaction.hpp>
19 #include <boost/mpl/list.hpp>
21 #include <boost/test/test_tools.hpp>
32 namespace sc
= boost::statechart
;
33 namespace mpl
= boost::mpl
;
37 typedef std::string
ActionDescription();
38 typedef ActionDescription
* ActionDescriptionPtr
;
39 typedef std::vector
< ActionDescriptionPtr
> ActionDescriptionSequence
;
40 typedef ActionDescriptionSequence::const_iterator SequenceIterator
;
41 typedef void Action( ActionDescriptionSequence
& );
42 typedef Action
* ActionPtr
;
44 template< class State
>
45 std::string
EntryDescription()
47 static const std::string entry
= "Entry: ";
48 return entry
+ typeid( State
).name() + "\n";
51 template< class State
>
52 std::string
ExitFnDescription()
54 static const std::string exitFunction
= "exit(): ";
55 return exitFunction
+ typeid( State
).name() + "\n";
58 template< class State
>
59 std::string
DtorDescription()
61 static const std::string destructor
= "Destructor: ";
62 return destructor
+ typeid( State
).name() + "\n";
65 template< class Context
, class Event
>
66 std::string
TransDescription()
68 static const std::string transition
= "Transition: ";
69 static const std::string event
= " with Event: ";
70 return transition
+ typeid( Context
).name() +
71 event
+ typeid( Event
).name() + "\n";
74 template< ActionPtr pAction
>
75 std::string
ThrowDescription()
77 static const std::string throwing
= "Throwing exception in ";
78 ActionDescriptionSequence sequence
;
80 return throwing
+ sequence
.front()();
84 template< class State
>
85 void Entry( ActionDescriptionSequence
& sequence
)
87 sequence
.push_back( &EntryDescription
< State
> );
90 template< class State
>
91 void ExitFn( ActionDescriptionSequence
& sequence
)
93 sequence
.push_back( &ExitFnDescription
< State
> );
96 template< class State
>
97 void Dtor( ActionDescriptionSequence
& sequence
)
99 sequence
.push_back( &DtorDescription
< State
> );
102 template< class State
>
103 void Exit( ActionDescriptionSequence
& sequence
)
105 ExitFn
< State
>( sequence
);
106 Dtor
< State
>( sequence
);
109 template< class Context
, class Event
>
110 void Trans( ActionDescriptionSequence
& sequence
)
112 sequence
.push_back( &TransDescription
< Context
, Event
> );
115 template< ActionPtr pAction
>
116 void Throw( ActionDescriptionSequence
& sequence
)
118 sequence
.push_back( &ThrowDescription
< pAction
> );
121 const int arrayLength
= 30;
122 typedef ActionPtr ActionArray
[ arrayLength
];
125 class TransitionTestException
: public std::runtime_error
128 TransitionTestException() : std::runtime_error( "Oh la la!" ) {}
132 // This test state machine is a beefed-up version of the one presented in
133 // "Practical Statecharts in C/C++" by Miro Samek, CMP Books 2002
134 struct A
: sc::event
< A
> {};
135 struct B
: sc::event
< B
> {};
136 struct C
: sc::event
< C
> {};
137 struct D
: sc::event
< D
> {};
138 struct E
: sc::event
< E
> {};
139 struct F
: sc::event
< F
> {};
140 struct G
: sc::event
< G
> {};
141 struct H
: sc::event
< H
> {};
144 template< class M
> struct S0
;
145 template< class Translator
>
146 struct TransitionTest
: sc::state_machine
<
147 TransitionTest
< Translator
>, S0
< TransitionTest
< Translator
> >,
148 std::allocator
< void >, Translator
>
151 //////////////////////////////////////////////////////////////////////////
152 TransitionTest() : pThrowAction_( 0 ), unconsumedEventCount_( 0 ) {}
156 // Since state destructors access the state machine object, we need to
157 // make sure that all states are destructed before this subtype
158 // portion is destructed.
162 void CompareToExpectedActionSequence( ActionArray
& actions
)
164 expectedSequence_
.clear();
166 // Copy all non-null pointers in actions into expectedSequence_
167 for ( ActionPtr
* pCurrent
= &actions
[ 0 ];
168 ( pCurrent
!= &actions
[ arrayLength
] ) && ( *pCurrent
!= 0 );
171 ( *pCurrent
)( expectedSequence_
);
174 if ( ( expectedSequence_
.size() != actualSequence_
.size() ) ||
175 !std::equal( expectedSequence_
.begin(),
176 expectedSequence_
.end(), actualSequence_
.begin() ) )
178 std::string message
= "\nExpected action sequence:\n";
180 for ( SequenceIterator pExpected
= expectedSequence_
.begin();
181 pExpected
!= expectedSequence_
.end(); ++pExpected
)
183 message
+= ( *pExpected
)();
186 message
+= "\nActual action sequence:\n";
188 for ( SequenceIterator pActual
= actualSequence_
.begin();
189 pActual
!= actualSequence_
.end(); ++pActual
)
191 message
+= ( *pActual
)();
194 BOOST_FAIL( message
.c_str() );
197 actualSequence_
.clear();
200 void ClearActualSequence()
202 actualSequence_
.clear();
205 void ThrowAction( ActionPtr pThrowAction
)
207 pThrowAction_
= pThrowAction
;
210 template< class State
>
213 StoreActualAction
< &Entry
< State
> >();
216 template< class State
>
217 void ActualExitFunction()
219 StoreActualAction
< &ExitFn
< State
> >();
222 template< class State
>
223 void ActualDestructor()
225 StoreActualAction
< &Dtor
< State
> >();
228 template< class Context
, class Event
>
229 void ActualTransition()
231 StoreActualAction
< &Trans
< Context
, Event
> >();
234 void unconsumed_event( const sc::event_base
& )
236 ++unconsumedEventCount_
;
239 unsigned int GetUnconsumedEventCount() const
241 return unconsumedEventCount_
;
245 //////////////////////////////////////////////////////////////////////////
246 template< ActionPtr pAction
>
247 void StoreActualAction()
249 if ( pAction
== pThrowAction_
)
251 Throw
< pAction
>( actualSequence_
);
252 throw TransitionTestException();
256 pAction( actualSequence_
);
260 ActionPtr pThrowAction_
;
261 ActionDescriptionSequence actualSequence_
;
262 ActionDescriptionSequence expectedSequence_
;
263 unsigned int unconsumedEventCount_
;
266 template< class M
> struct S1
;
267 template< class M
> struct S211
;
269 struct S0
: Orthogonal0
< S0
< M
>, M
, S1
< M
> >
271 typedef Orthogonal0
< S0
< M
>, M
, S1
< M
> > my_base
;
273 typedef sc::transition
< E
, S211
< M
> > reactions
;
275 S0( typename
my_base::my_context ctx
) : my_base( ctx
) {}
277 void Transit( const A
& evt
) { TransitImpl( evt
); }
278 void Transit( const B
& evt
) { TransitImpl( evt
); }
279 void Transit( const C
& evt
) { TransitImpl( evt
); }
280 void Transit( const D
& evt
) { TransitImpl( evt
); }
281 void Transit( const F
& evt
) { TransitImpl( evt
); }
282 void Transit( const G
& evt
) { TransitImpl( evt
); }
283 void Transit( const H
& evt
) { TransitImpl( evt
); }
286 template< class Event
>
287 void TransitImpl( const Event
& )
289 this->outermost_context().template ActualTransition
< S0
< M
>, Event
>();
293 template< class M
> struct S11
;
294 template< class M
> struct S21
;
296 struct S2
: Orthogonal2
< S2
< M
>, S0
< M
>, S21
< M
> >
298 typedef Orthogonal2
< S2
< M
>, S0
< M
>, S21
< M
> > my_base
;
300 sc::transition
< C
, S1
< M
>, S0
< M
>, &S0
< M
>::Transit
>,
301 sc::transition
< F
, S11
< M
>, S0
< M
>, &S0
< M
>::Transit
>
304 S2( typename
my_base::my_context ctx
) : my_base( ctx
) {}
308 struct S21
: Orthogonal1
<
309 S21
< M
>, typename S2
< M
>::template orthogonal
< 2 >, S211
< M
> >
312 S21
< M
>, typename S2
< M
>::template orthogonal
< 2 >, S211
< M
>
315 sc::transition
< H
, S21
< M
>, S0
< M
>, &S0
< M
>::Transit
>,
316 sc::transition
< B
, S211
< M
>, S0
< M
>, &S0
< M
>::Transit
>
319 S21( typename
my_base::my_context ctx
) : my_base( ctx
) {}
323 struct S211
: InnermostDefault
<
324 S211
< M
>, typename S21
< M
>::template orthogonal
< 1 > >
326 typedef InnermostDefault
<
327 S211
< M
>, typename S21
< M
>::template orthogonal
< 1 > > my_base
;
329 sc::transition
< D
, S21
< M
>, S0
< M
>, &S0
< M
>::Transit
>,
330 sc::transition
< G
, S0
< M
> >
333 S211( typename
my_base::my_context ctx
) : my_base( ctx
) {}
337 struct S1
: Orthogonal1
< S1
< M
>, S0
< M
>, S11
< M
> >
339 typedef Orthogonal1
< S1
< M
>, S0
< M
>, S11
< M
> > my_base
;
341 sc::transition
< A
, S1
< M
>, S0
< M
>, &S0
< M
>::Transit
>,
342 sc::transition
< B
, S11
< M
>, S0
< M
>, &S0
< M
>::Transit
>,
343 sc::transition
< C
, S2
< M
>, S0
< M
>, &S0
< M
>::Transit
>,
344 sc::transition
< D
, S0
< M
> >,
345 sc::transition
< F
, S211
< M
>, S0
< M
>, &S0
< M
>::Transit
>
348 S1( typename
my_base::my_context ctx
) : my_base( ctx
) {}
352 struct S11
: InnermostDefault
<
353 S11
< M
>, typename S1
< M
>::template orthogonal
< 1 > >
355 typedef InnermostDefault
<
356 S11
< M
>, typename S1
< M
>::template orthogonal
< 1 > > my_base
;
358 sc::transition
< G
, S211
< M
>, S0
< M
>, &S0
< M
>::Transit
>,
359 sc::custom_reaction
< H
>
362 S11( typename
my_base::my_context ctx
) : my_base( ctx
) {}
364 sc::result
react( const H
& )
366 this->outermost_context().template ActualTransition
< S11
< M
>, H
>();
367 return this->discard_event();
373 struct TransitionEventBaseTest
:
374 sc::state_machine
< TransitionEventBaseTest
, X1
>
377 TransitionEventBaseTest() : actionCallCounter_( 0 ) {}
379 void Transit( const sc::event_base
& eventBase
)
382 ( dynamic_cast< const B
* >( &eventBase
) != 0 ) ||
383 ( dynamic_cast< const D
* >( &eventBase
) != 0 ) );
384 ++actionCallCounter_
;
387 unsigned int GetActionCallCounter() const
389 return actionCallCounter_
;
393 unsigned int actionCallCounter_
;
396 struct X2
: sc::simple_state
< X2
, TransitionEventBaseTest
>
398 typedef sc::transition
< sc::event_base
, X1
,
399 TransitionEventBaseTest
, &TransitionEventBaseTest::Transit
> reactions
;
402 struct X1
: sc::simple_state
< X1
, TransitionEventBaseTest
>
404 typedef sc::transition
< sc::event_base
, X2
> reactions
;
408 void TestTransitions( M
& machine
)
415 Entry
< Default0
< S1
< M
> > >,
417 Entry
< Default2
< S1
< M
> > >,
418 Entry
< Default1
< S0
< M
> > >,
419 Entry
< Default2
< S0
< M
> > >
421 machine
.CompareToExpectedActionSequence( init
);
423 machine
.process_event( A() );
426 Exit
< Default2
< S1
< M
> > >,
428 Exit
< Default0
< S1
< M
> > >,
432 Entry
< Default0
< S1
< M
> > >,
434 Entry
< Default2
< S1
< M
> > >
436 machine
.CompareToExpectedActionSequence( a1
);
438 machine
.process_event( B() );
441 Exit
< Default2
< S1
< M
> > >,
443 Exit
< Default0
< S1
< M
> > >,
447 Entry
< Default0
< S1
< M
> > >,
449 Entry
< Default2
< S1
< M
> > >
451 machine
.CompareToExpectedActionSequence( b1
);
453 machine
.process_event( C() );
456 Exit
< Default2
< S1
< M
> > >,
458 Exit
< Default0
< S1
< M
> > >,
462 Entry
< Default0
< S2
< M
> > >,
463 Entry
< Default1
< S2
< M
> > >,
465 Entry
< Default0
< S21
< M
> > >,
467 Entry
< Default2
< S21
< M
> > >
469 machine
.CompareToExpectedActionSequence( c1
);
471 machine
.process_event( D() );
474 Exit
< Default2
< S21
< M
> > >,
476 Exit
< Default0
< S21
< M
> > >,
480 Entry
< Default0
< S21
< M
> > >,
482 Entry
< Default2
< S21
< M
> > >
484 machine
.CompareToExpectedActionSequence( d2
);
486 machine
.process_event( E() );
489 Exit
< Default2
< S0
< M
> > >,
490 Exit
< Default1
< S0
< M
> > >,
491 Exit
< Default2
< S21
< M
> > >,
493 Exit
< Default0
< S21
< M
> > >,
495 Exit
< Default1
< S2
< M
> > >,
496 Exit
< Default0
< S2
< M
> > >,
501 Entry
< Default0
< S2
< M
> > >,
502 Entry
< Default1
< S2
< M
> > >,
504 Entry
< Default0
< S21
< M
> > >,
506 Entry
< Default2
< S21
< M
> > >,
507 Entry
< Default1
< S0
< M
> > >,
508 Entry
< Default2
< S0
< M
> > >
510 machine
.CompareToExpectedActionSequence( e2
);
512 machine
.process_event( F() );
515 Exit
< Default2
< S21
< M
> > >,
517 Exit
< Default0
< S21
< M
> > >,
519 Exit
< Default1
< S2
< M
> > >,
520 Exit
< Default0
< S2
< M
> > >,
524 Entry
< Default0
< S1
< M
> > >,
526 Entry
< Default2
< S1
< M
> > >
528 machine
.CompareToExpectedActionSequence( f2
);
530 machine
.process_event( G() );
533 Exit
< Default2
< S1
< M
> > >,
535 Exit
< Default0
< S1
< M
> > >,
539 Entry
< Default0
< S2
< M
> > >,
540 Entry
< Default1
< S2
< M
> > >,
542 Entry
< Default0
< S21
< M
> > >,
544 Entry
< Default2
< S21
< M
> > >
546 machine
.CompareToExpectedActionSequence( g1
);
548 machine
.process_event( H() );
551 Exit
< Default2
< S21
< M
> > >,
553 Exit
< Default0
< S21
< M
> > >,
557 Entry
< Default0
< S21
< M
> > >,
559 Entry
< Default2
< S21
< M
> > >
561 machine
.CompareToExpectedActionSequence( h2
);
563 BOOST_REQUIRE( machine
.GetUnconsumedEventCount() == 0 );
564 machine
.process_event( A() );
565 BOOST_REQUIRE( machine
.GetUnconsumedEventCount() == 1 );
569 machine
.CompareToExpectedActionSequence( a2
);
571 machine
.process_event( B() );
574 Exit
< Default2
< S21
< M
> > >,
576 Exit
< Default0
< S21
< M
> > >,
580 Entry
< Default0
< S21
< M
> > >,
582 Entry
< Default2
< S21
< M
> > >
584 machine
.CompareToExpectedActionSequence( b2
);
586 machine
.process_event( C() );
589 Exit
< Default2
< S21
< M
> > >,
591 Exit
< Default0
< S21
< M
> > >,
593 Exit
< Default1
< S2
< M
> > >,
594 Exit
< Default0
< S2
< M
> > >,
598 Entry
< Default0
< S1
< M
> > >,
600 Entry
< Default2
< S1
< M
> > >
602 machine
.CompareToExpectedActionSequence( c2
);
604 machine
.process_event( D() );
607 Exit
< Default2
< S0
< M
> > >,
608 Exit
< Default1
< S0
< M
> > >,
609 Exit
< Default2
< S1
< M
> > >,
611 Exit
< Default0
< S1
< M
> > >,
616 Entry
< Default0
< S1
< M
> > >,
618 Entry
< Default2
< S1
< M
> > >,
619 Entry
< Default1
< S0
< M
> > >,
620 Entry
< Default2
< S0
< M
> > >
622 machine
.CompareToExpectedActionSequence( d1
);
624 machine
.process_event( F() );
627 Exit
< Default2
< S1
< M
> > >,
629 Exit
< Default0
< S1
< M
> > >,
633 Entry
< Default0
< S2
< M
> > >,
634 Entry
< Default1
< S2
< M
> > >,
636 Entry
< Default0
< S21
< M
> > >,
638 Entry
< Default2
< S21
< M
> > >
640 machine
.CompareToExpectedActionSequence( f1
);
642 machine
.process_event( G() );
645 Exit
< Default2
< S0
< M
> > >,
646 Exit
< Default1
< S0
< M
> > >,
647 Exit
< Default2
< S21
< M
> > >,
649 Exit
< Default0
< S21
< M
> > >,
651 Exit
< Default1
< S2
< M
> > >,
652 Exit
< Default0
< S2
< M
> > >,
657 Entry
< Default0
< S1
< M
> > >,
659 Entry
< Default2
< S1
< M
> > >,
660 Entry
< Default1
< S0
< M
> > >,
661 Entry
< Default2
< S0
< M
> > >
663 machine
.CompareToExpectedActionSequence( g2
);
665 machine
.process_event( H() );
670 machine
.CompareToExpectedActionSequence( h1
);
672 machine
.process_event( E() );
675 Exit
< Default2
< S0
< M
> > >,
676 Exit
< Default1
< S0
< M
> > >,
677 Exit
< Default2
< S1
< M
> > >,
679 Exit
< Default0
< S1
< M
> > >,
684 Entry
< Default0
< S2
< M
> > >,
685 Entry
< Default1
< S2
< M
> > >,
687 Entry
< Default0
< S21
< M
> > >,
689 Entry
< Default2
< S21
< M
> > >,
690 Entry
< Default1
< S0
< M
> > >,
691 Entry
< Default2
< S0
< M
> > >
693 machine
.CompareToExpectedActionSequence( e1
);
698 Exit
< Default2
< S0
< M
> > >,
699 Exit
< Default1
< S0
< M
> > >,
700 Exit
< Default2
< S21
< M
> > >,
702 Exit
< Default0
< S21
< M
> > >,
704 Exit
< Default1
< S2
< M
> > >,
705 Exit
< Default0
< S2
< M
> > >,
709 machine
.CompareToExpectedActionSequence( term
);
711 machine
.ThrowAction( &Entry
< Default0
< S1
< M
> > > );
712 BOOST_REQUIRE_THROW( machine
.initiate(), TransitionTestException
);
713 ActionArray initThrow1
=
717 &::Throw
< &::Entry
< Default0
< S1
< M
> > > >,
721 machine
.CompareToExpectedActionSequence( initThrow1
);
722 BOOST_REQUIRE( machine
.terminated() );
724 machine
.ThrowAction( &Entry
< S11
< M
> > );
725 BOOST_REQUIRE_THROW( machine
.initiate(), TransitionTestException
);
726 ActionArray initThrow2
=
730 Entry
< Default0
< S1
< M
> > >,
731 &::Throw
< &::Entry
< S11
< M
> > >,
732 Dtor
< Default0
< S1
< M
> > >,
736 machine
.CompareToExpectedActionSequence( initThrow2
);
737 BOOST_REQUIRE( machine
.terminated() );
739 machine
.ThrowAction( &Trans
< S0
< M
>, A
> );
741 BOOST_REQUIRE_THROW( machine
.process_event( A() ), TransitionTestException
);
742 ActionArray a1Throw1
=
746 Entry
< Default0
< S1
< M
> > >,
748 Entry
< Default2
< S1
< M
> > >,
749 Entry
< Default1
< S0
< M
> > >,
750 Entry
< Default2
< S0
< M
> > >,
751 Exit
< Default2
< S1
< M
> > >,
753 Exit
< Default0
< S1
< M
> > >,
755 &::Throw
< &::Trans
< S0
< M
>, A
> >,
756 Dtor
< Default2
< S0
< M
> > >,
757 Dtor
< Default1
< S0
< M
> > >,
760 machine
.CompareToExpectedActionSequence( a1Throw1
);
761 BOOST_REQUIRE( machine
.terminated() );
763 machine
.ThrowAction( &Entry
< S211
< M
> > );
765 BOOST_REQUIRE_THROW( machine
.process_event( C() ), TransitionTestException
);
766 ActionArray c1Throw1
=
770 Entry
< Default0
< S1
< M
> > >,
772 Entry
< Default2
< S1
< M
> > >,
773 Entry
< Default1
< S0
< M
> > >,
774 Entry
< Default2
< S0
< M
> > >,
775 Exit
< Default2
< S1
< M
> > >,
777 Exit
< Default0
< S1
< M
> > >,
781 Entry
< Default0
< S2
< M
> > >,
782 Entry
< Default1
< S2
< M
> > >,
784 Entry
< Default0
< S21
< M
> > >,
785 &::Throw
< &::Entry
< S211
< M
> > >,
786 Dtor
< Default2
< S0
< M
> > >,
787 Dtor
< Default1
< S0
< M
> > >,
788 Dtor
< Default0
< S21
< M
> > >,
790 Dtor
< Default1
< S2
< M
> > >,
791 Dtor
< Default0
< S2
< M
> > >,
795 machine
.CompareToExpectedActionSequence( c1Throw1
);
796 BOOST_REQUIRE( machine
.terminated() );
798 machine
.ThrowAction( &ExitFn
< S11
< M
> > );
800 BOOST_REQUIRE_THROW( machine
.process_event( C() ), TransitionTestException
);
801 ActionArray c1Throw2
=
805 Entry
< Default0
< S1
< M
> > >,
807 Entry
< Default2
< S1
< M
> > >,
808 Entry
< Default1
< S0
< M
> > >,
809 Entry
< Default2
< S0
< M
> > >,
810 Exit
< Default2
< S1
< M
> > >,
811 &::Throw
< &::ExitFn
< S11
< M
> > >,
813 Dtor
< Default2
< S0
< M
> > >,
814 Dtor
< Default1
< S0
< M
> > >,
815 Dtor
< Default0
< S1
< M
> > >,
819 machine
.CompareToExpectedActionSequence( c1Throw2
);
820 BOOST_REQUIRE( machine
.terminated() );
821 BOOST_REQUIRE( machine
.GetUnconsumedEventCount() == 1 );
825 int test_main( int, char* [] )
827 TransitionTest
< sc::null_exception_translator
> null_machine
;
828 TestTransitions( null_machine
);
829 TransitionTest
< sc::exception_translator
<> > machine
;
830 TestTransitions( machine
);
832 TransitionEventBaseTest eventBaseMachine
;
833 eventBaseMachine
.initiate();
834 BOOST_REQUIRE_NO_THROW( eventBaseMachine
.state_cast
< const X1
& >() );
835 eventBaseMachine
.process_event( A() );
836 BOOST_REQUIRE_NO_THROW( eventBaseMachine
.state_cast
< const X2
& >() );
837 BOOST_REQUIRE( eventBaseMachine
.GetActionCallCounter() == 0 );
838 eventBaseMachine
.process_event( B() );
839 BOOST_REQUIRE_NO_THROW( eventBaseMachine
.state_cast
< const X1
& >() );
840 BOOST_REQUIRE( eventBaseMachine
.GetActionCallCounter() == 1 );
841 eventBaseMachine
.process_event( C() );
842 BOOST_REQUIRE_NO_THROW( eventBaseMachine
.state_cast
< const X2
& >() );
843 BOOST_REQUIRE( eventBaseMachine
.GetActionCallCounter() == 1 );
844 eventBaseMachine
.process_event( D() );
845 BOOST_REQUIRE_NO_THROW( eventBaseMachine
.state_cast
< const X1
& >() );
846 BOOST_REQUIRE( eventBaseMachine
.GetActionCallCounter() == 2 );