Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
FiniteStateMachineTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file FiniteStateMachineTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #include <boost/test/unit_test.hpp>
10 
12 
13 #include <iostream>
14 #include <optional>
15 #include <stdexcept>
16 
17 namespace tt = boost::test_tools;
18 
19 namespace Acts {
20 
21 namespace Test {
22 
23 namespace states {
24 struct Disconnected {};
25 
26 struct Connecting {};
27 struct Pinging {};
28 struct Connected {};
29 } // namespace states
30 
31 namespace events {
32 struct Connect {};
33 struct Established {};
34 struct Timeout {};
35 struct Ping {};
36 struct Pong {};
37 struct Disconnect {};
38 } // namespace events
39 
40 struct fsm : FiniteStateMachine<fsm, states::Disconnected, states::Connecting,
41  states::Pinging, states::Connected> {
42  fsm() : fsm_base(states::Disconnected{}) {}
43 
44  event_return on_event(const states::Disconnected& /*unused*/,
45  const events::Connect& /*unused*/) {
46  return states::Connecting{};
47  }
48 
49  event_return on_event(const states::Connecting& /*unused*/,
50  const events::Established& /*unused*/) {
51  return states::Connected{};
52  }
53 
54  event_return on_event(const states::Connected& /*unused*/,
55  const events::Ping& /*unused*/) {
56  std::cout << "ping!" << std::endl;
57  setState(states::Pinging{});
58  return process_event(events::Pong{});
59  }
60 
61  event_return on_event(const states::Pinging& /*unused*/,
62  const events::Pong& /*unused*/) {
63  std::cout << "pong!" << std::endl;
64  return states::Connected{};
65  }
66 
67  event_return on_event(const states::Connected& /*unused*/,
68  const events::Timeout& /*unused*/) {
69  return states::Connecting{};
70  }
71 
72  event_return on_event(const states::Connected& /*unused*/,
73  const events::Disconnect& /*unused*/) {
74  return states::Disconnected{};
75  }
76 
77  template <typename State, typename Event>
78  event_return on_event(const State& /*unused*/,
79  const Event& /*unused*/) const {
80  return Terminated{};
81  }
82 
83  template <typename State, typename... Args>
84  void on_enter(const State& /*unused*/, Args&&... /*unused*/) {}
85 
86  template <typename State, typename... Args>
87  void on_exit(const State& /*unused*/, Args&&... /*unused*/) {}
88 
89  template <typename... Args>
90  void on_process(Args&&... /*unused*/) {}
91 };
92 
93 BOOST_AUTO_TEST_SUITE(Utilities)
94 
95 BOOST_AUTO_TEST_CASE(Transitions) {
96  fsm sm{};
97  BOOST_CHECK(sm.is(states::Disconnected{}));
98  sm.dispatch(events::Connect{});
99  BOOST_CHECK(sm.is(states::Connecting{}));
100  sm.dispatch(events::Established{});
101  BOOST_CHECK(sm.is(states::Connected{}));
102  sm.dispatch(events::Ping{});
103  sm.dispatch(events::Ping{});
104  sm.dispatch(events::Ping{});
105  sm.dispatch(events::Ping{});
106  BOOST_CHECK(sm.is(states::Connected{}));
107  sm.dispatch(events::Timeout{});
108  BOOST_CHECK(sm.is(states::Connecting{}));
109  sm.dispatch(events::Established{});
110  BOOST_CHECK(sm.is(states::Connected{}));
111  sm.dispatch(events::Disconnect{});
112  BOOST_CHECK(sm.is(states::Disconnected{}));
113 }
114 
116  fsm sm{};
117  BOOST_CHECK(sm.is(states::Disconnected{}));
118 
119  sm.dispatch(events::Disconnect{});
120  BOOST_CHECK(sm.terminated());
121 }
122 
123 struct fsm2
124  : FiniteStateMachine<fsm2, states::Disconnected, states::Connected> {
125  fsm2() : fsm_base(states::Disconnected{}) {}
126 
127  event_return on_event(const states::Disconnected& /*unused*/,
128  const events::Connect& /*unused*/, double f) {
129  std::cout << "f: " << f << std::endl;
130  return states::Connected{};
131  }
132 
133  event_return on_event(const states::Connected& /*unused*/,
134  const events::Disconnect& /*unused*/) {
135  std::cout << "disconnect!" << std::endl;
136  return states::Disconnected{};
137  }
138 
139  template <typename State, typename Event, typename... Args>
140  event_return on_event(const State& /*unused*/, const Event& /*unused*/,
141  Args&&... /*unused*/) const {
142  return Terminated{};
143  }
144 
145  template <typename... Args>
146  void on_enter(const Terminated& /*unused*/, Args&&... /*unused*/) {
147  throw std::runtime_error("FSM terminated!");
148  }
149 
150  template <typename State, typename... Args>
151  void on_enter(const State& /*unused*/, Args&&... /*unused*/) {}
152 
153  template <typename State, typename... Args>
154  void on_exit(const State& /*unused*/, Args&&... /*unused*/) {}
155  template <typename... Args>
156  void on_process(Args&&... /*unused*/) {}
157 };
158 
160  fsm2 sm{};
161  BOOST_CHECK(sm.is(states::Disconnected{}));
162 
163  sm.dispatch(events::Connect{}, 42.);
164  BOOST_CHECK(sm.is(states::Connected{}));
165  sm.dispatch(events::Disconnect{});
166  BOOST_CHECK(sm.is(states::Disconnected{}));
167  sm.dispatch(events::Connect{}, -1.);
168 
169  // call disconnect, but disconnect does not accept this call signature
170  BOOST_REQUIRE_THROW(sm.dispatch(events::Disconnect{}, 9), std::runtime_error);
171  BOOST_CHECK(sm.terminated());
172 
173  // cannot dispatch on terminated (in this specific configuration, in
174  // general terminated is just another state).
175  BOOST_REQUIRE_THROW(sm.dispatch(events::Connect{}), std::runtime_error);
176  // still in terminated
177  BOOST_CHECK(sm.terminated());
178 
179  // we can reset the state though!
180  sm.setState(states::Disconnected{});
181  BOOST_CHECK(sm.is(states::Disconnected{}));
182  sm.dispatch(events::Connect{}, -1.);
183  BOOST_CHECK(sm.is(states::Connected{}));
184 }
185 
186 struct S1 {};
187 struct S2 {};
188 struct S3 {};
189 
190 struct E1 {};
191 struct E2 {};
192 struct E3 {};
193 
194 struct fsm3 : FiniteStateMachine<fsm3, S1, S2, S3> {
195  bool on_exit_called = false;
196  bool on_enter_called = false;
197  bool on_process_called = false;
198  void reset() {
199  on_exit_called = false;
200  on_enter_called = false;
201  on_process_called = false;
202  }
203 
204  // S1 + E1 = S2
205  event_return on_event(const S1& /*unused*/, const E1& /*unused*/) {
206  return S2{};
207  }
208 
209  // S2 + E1 = S2
210  // external transition to self
211  event_return on_event(const S2& /*unused*/, const E1& /*unused*/) {
212  return S2{};
213  }
214 
215  // S2 + E2
216  // internal transition
217  event_return on_event(const S2& /*unused*/, const E2& /*unused*/) {
218  return std::nullopt;
219  // return S2{};
220  }
221 
222  // S2 + E3 = S3
223  // external transition
224  event_return on_event(const S2& /*unused*/, const E3& /*unused*/) {
225  return S3{};
226  }
227 
228  // catchers
229 
230  template <typename State, typename Event, typename... Args>
231  event_return on_event(const State& /*unused*/, const Event& /*unused*/,
232  Args&&... /*unused*/) const {
233  return Terminated{};
234  }
235 
236  template <typename State, typename... Args>
237  void on_enter(const State& /*unused*/, Args&&... /*unused*/) {
238  on_enter_called = true;
239  }
240 
241  template <typename State, typename... Args>
242  void on_exit(const State& /*unused*/, Args&&... /*unused*/) {
243  on_exit_called = true;
244  }
245 
246  template <typename... Args>
247  void on_process(Args&&... /*unused*/) {
248  on_process_called = true;
249  }
250 };
251 
252 BOOST_AUTO_TEST_CASE(InternalTransitions) {
253  fsm3 sm;
254  BOOST_CHECK(sm.is(S1{}));
255 
256  sm.dispatch(E1{});
257  BOOST_CHECK(sm.is(S2{}));
258  BOOST_CHECK(sm.on_exit_called);
259  BOOST_CHECK(sm.on_enter_called);
260  BOOST_CHECK(sm.on_process_called);
261 
262  sm.reset();
263 
264  sm.dispatch(E1{});
265  // still in S2
266  BOOST_CHECK(sm.is(S2{}));
267  // on_enter / exit should have been called
268  BOOST_CHECK(sm.on_exit_called);
269  BOOST_CHECK(sm.on_enter_called);
270  BOOST_CHECK(sm.on_process_called);
271  sm.reset();
272 
273  sm.dispatch(E2{});
274  // still in S2
275  BOOST_CHECK(sm.is(S2{}));
276  // on_enter / exit should NOT have been called
277  BOOST_CHECK(!sm.on_exit_called);
278  BOOST_CHECK(!sm.on_enter_called);
279  BOOST_CHECK(sm.on_process_called);
280  sm.reset();
281 
282  sm.dispatch(E3{});
283  BOOST_CHECK(sm.is(S3{}));
284  // on_enter / exit should have been called
285  BOOST_CHECK(sm.on_exit_called);
286  BOOST_CHECK(sm.on_enter_called);
287  BOOST_CHECK(sm.on_process_called);
288 
289  sm.setState(S1{});
290  sm.reset();
291  BOOST_CHECK(sm.is(S1{}));
292  // dispatch invalid event
293  sm.dispatch(E3{});
294  // should be terminated now
295  BOOST_CHECK(sm.terminated());
296  // hooks should have fired
297  BOOST_CHECK(sm.on_exit_called);
298  BOOST_CHECK(sm.on_enter_called);
299  BOOST_CHECK(sm.on_process_called);
300 }
301 
302 BOOST_AUTO_TEST_SUITE_END()
303 
304 } // namespace Test
305 } // namespace Acts