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