EIC 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 
15 namespace tt = boost::test_tools;
16 
17 namespace Acts {
18 
19 namespace Test {
20 
21 namespace states {
22 struct Disconnected {};
23 
24 struct Connecting {};
25 struct Pinging {};
26 struct Connected {};
27 } // namespace states
28 
29 namespace events {
30 struct Connect {};
31 struct Established {};
32 struct Timeout {};
33 struct Ping {};
34 struct Pong {};
35 struct Disconnect {};
36 } // namespace events
37 
38 struct fsm : FiniteStateMachine<fsm, states::Disconnected, states::Connecting,
39  states::Pinging, states::Connected> {
40  fsm() : fsm_base(states::Disconnected{}){};
41 
43  return states::Connecting{};
44  }
45 
47  return states::Connected{};
48  }
49 
51  std::cout << "ping!" << std::endl;
53  return process_event(events::Pong{});
54  }
55 
57  std::cout << "pong!" << std::endl;
58  return states::Connected{};
59  }
60 
62  return states::Connecting{};
63  }
64 
66  return states::Disconnected{};
67  }
68 
69  template <typename State, typename Event>
70  event_return on_event(const State&, const Event&) const {
71  return Terminated{};
72  }
73 
74  template <typename State, typename... Args>
75  void on_enter(const State&, Args&&...) {}
76 
77  template <typename State, typename... Args>
78  void on_exit(const State&, Args&&...) {}
79 
80  template <typename... Args>
81  void on_process(Args&&...) {}
82 };
83 
84 BOOST_AUTO_TEST_SUITE(Utilities)
85 
86 BOOST_AUTO_TEST_CASE(Transitions) {
87  fsm sm{};
88  BOOST_CHECK(sm.is(states::Disconnected{}));
89  sm.dispatch(events::Connect{});
90  BOOST_CHECK(sm.is(states::Connecting{}));
91  sm.dispatch(events::Established{});
92  BOOST_CHECK(sm.is(states::Connected{}));
93  sm.dispatch(events::Ping{});
94  sm.dispatch(events::Ping{});
95  sm.dispatch(events::Ping{});
96  sm.dispatch(events::Ping{});
97  BOOST_CHECK(sm.is(states::Connected{}));
98  sm.dispatch(events::Timeout{});
99  BOOST_CHECK(sm.is(states::Connecting{}));
100  sm.dispatch(events::Established{});
101  BOOST_CHECK(sm.is(states::Connected{}));
102  sm.dispatch(events::Disconnect{});
103  BOOST_CHECK(sm.is(states::Disconnected{}));
104 }
105 
107  fsm sm{};
108  BOOST_CHECK(sm.is(states::Disconnected{}));
109 
110  sm.dispatch(events::Disconnect{});
111  BOOST_CHECK(sm.terminated());
112 }
113 
114 struct fsm2
115  : FiniteStateMachine<fsm2, states::Disconnected, states::Connected> {
116  fsm2() : fsm_base(states::Disconnected{}){};
117 
119  double f) {
120  std::cout << "f: " << f << std::endl;
121  return states::Connected{};
122  }
123 
125  std::cout << "disconnect!" << std::endl;
126  return states::Disconnected{};
127  }
128 
129  template <typename State, typename Event, typename... Args>
130  event_return on_event(const State&, const Event&, Args&&...) const {
131  return Terminated{};
132  }
133 
134  template <typename... Args>
135  void on_enter(const Terminated&, Args&&...) {
136  throw std::runtime_error("FSM terminated!");
137  }
138 
139  template <typename State, typename... Args>
140  void on_enter(const State&, Args&&...) {}
141 
142  template <typename State, typename... Args>
143  void on_exit(const State&, Args&&...) {}
144  template <typename... Args>
145  void on_process(Args&&...) {}
146 };
147 
149  fsm2 sm{};
150  BOOST_CHECK(sm.is(states::Disconnected{}));
151 
152  sm.dispatch(events::Connect{}, 42.);
153  BOOST_CHECK(sm.is(states::Connected{}));
154  sm.dispatch(events::Disconnect{});
155  BOOST_CHECK(sm.is(states::Disconnected{}));
156  sm.dispatch(events::Connect{}, -1.);
157 
158  // call disconnect, but disconnect does not accept this call signature
159  BOOST_REQUIRE_THROW(sm.dispatch(events::Disconnect{}, 9), std::runtime_error);
160  BOOST_CHECK(sm.terminated());
161 
162  // cannot dispatch on terminated (in this specific configuration, in
163  // general terminated is just another state).
164  BOOST_REQUIRE_THROW(sm.dispatch(events::Connect{}), std::runtime_error);
165  // still in terminated
166  BOOST_CHECK(sm.terminated());
167 
168  // we can reset the state though!
169  sm.setState(states::Disconnected{});
170  BOOST_CHECK(sm.is(states::Disconnected{}));
171  sm.dispatch(events::Connect{}, -1.);
172  BOOST_CHECK(sm.is(states::Connected{}));
173 }
174 
175 struct S1 {};
176 struct S2 {};
177 struct S3 {};
178 
179 struct E1 {};
180 struct E2 {};
181 struct E3 {};
182 
183 struct fsm3 : FiniteStateMachine<fsm3, S1, S2, S3> {
184  bool on_exit_called = false;
185  bool on_enter_called = false;
186  bool on_process_called = false;
187  void reset() {
188  on_exit_called = false;
189  on_enter_called = false;
190  on_process_called = false;
191  }
192 
193  // S1 + E1 = S2
194  event_return on_event(const S1&, const E1&) { return S2{}; }
195 
196  // S2 + E1 = S2
197  // external transition to self
198  event_return on_event(const S2&, const E1&) { return S2{}; }
199 
200  // S2 + E2
201  // internal transition
202  event_return on_event(const S2&, const E2&) {
203  return std::nullopt;
204  // return S2{};
205  }
206 
207  // S2 + E3 = S3
208  // external transition
209  event_return on_event(const S2&, const E3&) { return S3{}; }
210 
211  // catchers
212 
213  template <typename State, typename Event, typename... Args>
214  event_return on_event(const State&, const Event&, Args&&...) const {
215  return Terminated{};
216  }
217 
218  template <typename State, typename... Args>
219  void on_enter(const State&, Args&&...) {
220  on_enter_called = true;
221  }
222 
223  template <typename State, typename... Args>
224  void on_exit(const State&, Args&&...) {
225  on_exit_called = true;
226  }
227 
228  template <typename... Args>
229  void on_process(Args&&...) {
230  on_process_called = true;
231  }
232 };
233 
234 BOOST_AUTO_TEST_CASE(InternalTransitions) {
235  fsm3 sm;
236  BOOST_CHECK(sm.is(S1{}));
237 
238  sm.dispatch(E1{});
239  BOOST_CHECK(sm.is(S2{}));
240  BOOST_CHECK(sm.on_exit_called);
241  BOOST_CHECK(sm.on_enter_called);
242  BOOST_CHECK(sm.on_process_called);
243 
244  sm.reset();
245 
246  sm.dispatch(E1{});
247  // still in S2
248  BOOST_CHECK(sm.is(S2{}));
249  // on_enter / exit should have been called
250  BOOST_CHECK(sm.on_exit_called);
251  BOOST_CHECK(sm.on_enter_called);
252  BOOST_CHECK(sm.on_process_called);
253  sm.reset();
254 
255  sm.dispatch(E2{});
256  // still in S2
257  BOOST_CHECK(sm.is(S2{}));
258  // on_enter / exit should NOT have been called
259  BOOST_CHECK(!sm.on_exit_called);
260  BOOST_CHECK(!sm.on_enter_called);
261  BOOST_CHECK(sm.on_process_called);
262  sm.reset();
263 
264  sm.dispatch(E3{});
265  BOOST_CHECK(sm.is(S3{}));
266  // on_enter / exit should have been called
267  BOOST_CHECK(sm.on_exit_called);
268  BOOST_CHECK(sm.on_enter_called);
269  BOOST_CHECK(sm.on_process_called);
270 
271  sm.setState(S1{});
272  sm.reset();
273  BOOST_CHECK(sm.is(S1{}));
274  // dispatch invalid event
275  sm.dispatch(E3{});
276  // should be terminated now
277  BOOST_CHECK(sm.terminated());
278  // hooks should have fired
279  BOOST_CHECK(sm.on_exit_called);
280  BOOST_CHECK(sm.on_enter_called);
281  BOOST_CHECK(sm.on_process_called);
282 }
283 
284 BOOST_AUTO_TEST_SUITE_END()
285 
286 } // namespace Test
287 } // namespace Acts