1// Copyright 2024 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
12// back-end
13#include <boost/msm/back11/state_machine.hpp>
14//front-end
15#include <boost/msm/front/state_machine_def.hpp>
16#include <boost/msm/front/puml/puml.hpp>
17#include <PumlCommon.hpp>
18
19#ifndef BOOST_MSM_NONSTANDALONE_TEST
20#define BOOST_TEST_MODULE back11_simple_internal_with_puml
21#endif
22#include <boost/test/unit_test.hpp>
23
24using namespace std;
25namespace msm = boost::msm;
26using namespace msm::front;
27using namespace msm::front::puml;
28
29
30
31namespace
32{
33
34
35 // front-end: define the FSM structure
36 struct player_ : public msm::front::state_machine_def<player_>
37 {
38 unsigned int start_playback_counter=0;
39 unsigned int can_close_drawer_counter=0;
40 unsigned int internal_action_counter=0;
41 unsigned int internal_guard_counter=0;
42
43 BOOST_MSM_PUML_DECLARE_TABLE(
44 R"(
45 @startuml Player
46 skinparam linetype polyline
47 state Player{
48 [*]-> Empty
49 Stopped -> Playing : play / start_playback
50 Stopped -> Open : open_close / open_drawer
51 Stopped -> Stopped : stop
52
53 Open -> Empty : open_close [can_close_drawer]
54
55 Empty --> Open : open_close / open_drawer
56 Empty ---> Stopped : cd_detected / store_cd_info [good_disk_format]
57 Empty -> Empty : -internal_evt / internal_action [internal_guard2]
58 Empty -> Empty : -to_ignore
59 Empty -> Empty : -cd_detected [internal_guard]
60 Empty -> Empty : -internal_evt / internal_action_fct [internal_guard_fct]
61
62 Playing --> Stopped : stop / stop_playback
63 Playing -> Paused : pause / pause_playback
64 Playing --> Open : open_close / stop_and_open
65
66 Paused -> Playing : end_pause / resume_playback
67 Paused --> Stopped : stop / stop_playback
68 Paused --> Open : open_close / stop_and_open
69 }
70 @enduml
71 )"
72 )
73
74 // Replaces the default no-transition response.
75 template <class FSM,class Event>
76 void no_transition(Event const&, FSM&,int)
77 {
78 BOOST_FAIL("no_transition called!");
79 }
80 };
81 // Pick a back-end
82 typedef msm::back11::state_machine<player_> player;
83
84
85 BOOST_AUTO_TEST_CASE( back11_simple_internal_with_puml_test )
86 {
87 player p;
88
89 p.start();
90 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty")>&>().entry_counter == 1, "Empty entry not called correctly");
91 // internal events
92 p.process_event(Event<by_name(str: "to_ignore")>{});
93 p.process_event(Event<by_name(str: "internal_evt")>{});
94 BOOST_CHECK_MESSAGE(p.internal_action_counter == 1, "Internal action not called correctly");
95 BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1, "Internal guard not called correctly");
96 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty")>&>().empty_internal_action_counter == 0, "Empty internal action not called correctly");
97 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty")>&>().empty_internal_guard_counter == 1, "Empty internal guard not called correctly");
98
99 p.process_event(Event<by_name(str: "open_close")>{});
100 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
101 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty")>&>().exit_counter == 1,"Empty exit not called correctly");
102 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open")>&>().entry_counter == 1,"Open entry not called correctly");
103
104 p.process_event(Event<by_name(str: "open_close")>{});
105 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
106 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open")>&>().exit_counter == 1,"Open exit not called correctly");
107 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty")>&>().entry_counter == 2,"Empty entry not called correctly");
108 BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
109
110 p.process_event(Event<by_name(str: "cd_detected")>{"louie, louie", DISK_DVD});
111
112 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
113 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Open")>&>().exit_counter == 1,"Open exit not called correctly");
114 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty")>&>().entry_counter == 2,"Empty entry not called correctly");
115
116 p.process_event(Event<by_name(str: "cd_detected")>{"louie, louie", DISK_CD});
117 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0, "Stopped should be active"); //Stopped
118 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Empty")>&>().exit_counter == 2,"Empty exit not called correctly");
119 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped")>&>().entry_counter == 1,"Stopped entry not called correctly");
120 BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3, "Internal guard not called correctly");
121
122 p.process_event(Event<by_name(str: "play")>{});
123 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3, "Playing should be active"); //Playing
124 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped")>&>().exit_counter == 1, "Stopped exit not called correctly");
125 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing")>&>().entry_counter == 1, "Playing entry not called correctly");
126 BOOST_CHECK_MESSAGE(p.start_playback_counter == 1, "action not called correctly");
127
128 p.process_event(Event<by_name(str: "pause")>{});
129 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4, "Paused should be active"); //Paused
130 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped")>&>().exit_counter == 1, "Playing exit not called correctly");
131 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused")>&>().entry_counter == 1, "Paused entry not called correctly");
132
133 // go back to Playing
134 p.process_event(Event<by_name(str: "end_pause")>{});
135 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
136 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused")>&>().exit_counter == 1,"Paused exit not called correctly");
137 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing")>&>().entry_counter == 2,"Playing entry not called correctly");
138
139 p.process_event(Event<by_name(str: "pause")>{});
140 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
141 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Playing")>&>().exit_counter == 2,"Playing exit not called correctly");
142 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused")>&>().entry_counter == 2,"Paused entry not called correctly");
143
144 p.process_event(Event<by_name(str: "stop")>{});
145 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
146 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Paused")>&>().exit_counter == 2,"Paused exit not called correctly");
147 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped")>&>().entry_counter == 2,"Stopped entry not called correctly");
148
149 p.process_event(Event<by_name(str: "stop")>{});
150 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
151 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped")>&>().exit_counter == 2,"Stopped exit not called correctly");
152 BOOST_CHECK_MESSAGE(p.get_state<State<by_name("Stopped")>&>().entry_counter == 3,"Stopped entry not called correctly");
153
154 }
155}
156
157

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