1 // Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8
9 #include <util/state_model.h>
10
11 #include <gtest/gtest.h>
12
13 using namespace std;
14 using namespace isc;
15 using namespace isc::util;
16
17 namespace {
18
19 /// @brief Test derivation of StateModel for exercising state model mechanics.
20 ///
21 /// This class facilitates testing by making non-public methods accessible so
22 /// they can be invoked directly in test routines. It implements a very
23 /// rudimentary state model, sufficient to test the state model mechanics
24 /// supplied by the base class.
25 class StateModelTest : public StateModel, public testing::Test {
26 public:
27
28 ///@brief StateModelTest states
29 ///@brief Fake state used for handler mapping tests.
30 static const int DUMMY_ST = SM_DERIVED_STATE_MIN + 1;
31
32 ///@brief Starting state for the test state model.
33 static const int READY_ST = SM_DERIVED_STATE_MIN + 2;
34
35 ///@brief State which simulates doing asynchronous work.
36 static const int DO_WORK_ST = SM_DERIVED_STATE_MIN + 3;
37
38 ///@brief State which finishes off processing.
39 static const int DONE_ST = SM_DERIVED_STATE_MIN + 4;
40
41 ///@brief State in which model is always paused.
42 static const int PAUSE_ALWAYS_ST = SM_DERIVED_STATE_MIN + 5;
43
44 ///@brief State in which model is paused at most once.
45 static const int PAUSE_ONCE_ST = SM_DERIVED_STATE_MIN + 6;
46
47 // StateModelTest events
48 ///@brief Event used to trigger initiation of asynchronous work.
49 static const int WORK_START_EVT = SM_DERIVED_EVENT_MIN + 1;
50
51 ///@brief Event issued when the asynchronous work "completes".
52 static const int WORK_DONE_EVT = SM_DERIVED_EVENT_MIN + 2;
53
54 ///@brief Event issued when all the work is done.
55 static const int ALL_DONE_EVT = SM_DERIVED_EVENT_MIN + 3;
56
57 ///@brief Event used to trigger an attempt to transition to bad state
58 static const int FORCE_UNDEFINED_ST_EVT = SM_DERIVED_EVENT_MIN + 4;
59
60 ///@brief Event used to trigger an attempt to transition to bad state
61 static const int SIMULATE_ERROR_EVT = SM_DERIVED_EVENT_MIN + 5;
62
63 ///@brief Event used to indicate that state machine is unpaused.
64 static const int UNPAUSED_EVT = SM_DERIVED_EVENT_MIN + 6;
65
66 /// @brief Constructor
67 ///
68 /// Parameters match those needed by StateModel.
StateModelTest()69 StateModelTest() : dummy_called_(false), work_completed_(false),
70 failure_explanation_("") {
71 }
72 /// @brief Destructor
~StateModelTest()73 virtual ~StateModelTest() {
74 }
75
76 /// @brief Fetches the value of the dummy called flag.
getDummyCalled()77 bool getDummyCalled() {
78 return (dummy_called_);
79 }
80
81 /// @brief StateHandler for fake state, DummyState.
82 ///
83 /// It simply sets the dummy called flag to indicate that this method
84 /// was invoked.
dummyHandler()85 void dummyHandler() {
86 dummy_called_ = true;
87 }
88
89 /// @brief Returns the failure explanation string.
90 ///
91 /// This value is set only via onModelFailure and it stores whatever
92 /// explanation that method was passed.
getFailureExplanation()93 const std::string& getFailureExplanation() {
94 return (failure_explanation_);
95 }
96
97 /// @brief Returns indication of whether or not the model succeeded.
98 ///
99 /// If true, this indicates that the test model executed correctly through
100 /// to completion. The flag is only by the DONE_ST handler.
getWorkCompleted()101 bool getWorkCompleted() {
102 return (work_completed_);
103 }
104
105 /// @brief State handler for the READY_ST.
106 ///
107 /// Serves as the starting state handler, it consumes the
108 /// START_EVT "transitioning" to the state, DO_WORK_ST and
109 /// sets the next event to WORK_START_EVT.
readyHandler()110 void readyHandler() {
111 switch(getNextEvent()) {
112 case START_EVT:
113 transition(DO_WORK_ST, WORK_START_EVT);
114 break;
115 default:
116 // its bogus
117 isc_throw(StateModelError, "readyHandler:invalid event: "
118 << getContextStr());
119 }
120 }
121
122 /// @brief State handler for the DO_WORK_ST.
123 ///
124 /// Simulates a state that starts some form of asynchronous work.
125 /// When next event is WORK_START_EVT it sets the status to pending
126 /// and signals the state model must "wait" for an event by setting
127 /// next event to NOP_EVT.
128 ///
129 /// When next event is IO_COMPLETED_EVT, it transitions to the state,
130 /// DONE_ST, and sets the next event to WORK_DONE_EVT.
doWorkHandler()131 void doWorkHandler() {
132 switch(getNextEvent()) {
133 case WORK_START_EVT:
134 postNextEvent(NOP_EVT);
135 break;
136 case WORK_DONE_EVT:
137 work_completed_ = true;
138 transition(DONE_ST, ALL_DONE_EVT);
139 break;
140 case FORCE_UNDEFINED_ST_EVT:
141 transition(9999, ALL_DONE_EVT);
142 break;
143 case SIMULATE_ERROR_EVT:
144 throw std::logic_error("Simulated Unexpected Error");
145 break;
146 default:
147 // its bogus
148 isc_throw(StateModelError, "doWorkHandler:invalid event: "
149 << getContextStr());
150 }
151 }
152
153 /// @brief State handler for the DONE_ST.
154 ///
155 /// This is the last state in the model. Note that it sets the
156 /// status to completed and next event to NOP_EVT.
doneWorkHandler()157 void doneWorkHandler() {
158 switch(getNextEvent()) {
159 case ALL_DONE_EVT:
160 endModel();
161 break;
162 default:
163 // its bogus
164 isc_throw(StateModelError, "doneWorkHandler:invalid event: "
165 << getContextStr());
166 }
167 }
168
169 /// @brief State handler for PAUSE_ALWAYS_ST and PAUSE_ONCE_ST.
pauseHandler()170 void pauseHandler() {
171 postNextEvent(NOP_EVT);
172 }
173
174 /// @brief Construct the event dictionary.
defineEvents()175 virtual void defineEvents() {
176 // Invoke the base call implementation first.
177 StateModel::defineEvents();
178
179 // Define our events.
180 defineEvent(WORK_START_EVT, "WORK_START_EVT");
181 defineEvent(WORK_DONE_EVT , "WORK_DONE_EVT");
182 defineEvent(ALL_DONE_EVT, "ALL_DONE_EVT");
183 defineEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT");
184 defineEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT");
185 defineEvent(UNPAUSED_EVT, "UNPAUSED_EVT");
186 }
187
188 /// @brief Verify the event dictionary.
verifyEvents()189 virtual void verifyEvents() {
190 // Invoke the base call implementation first.
191 StateModel::verifyEvents();
192
193 // Verify our events.
194 getEvent(WORK_START_EVT);
195 getEvent(WORK_DONE_EVT);
196 getEvent(ALL_DONE_EVT);
197 getEvent(FORCE_UNDEFINED_ST_EVT);
198 getEvent(SIMULATE_ERROR_EVT);
199 getEvent(UNPAUSED_EVT);
200 }
201
202 /// @brief Construct the state dictionary.
defineStates()203 virtual void defineStates() {
204 // Invoke the base call implementation first.
205 StateModel::defineStates();
206
207 // Define our states.
208 defineState(DUMMY_ST, "DUMMY_ST",
209 std::bind(&StateModelTest::dummyHandler, this));
210
211 defineState(READY_ST, "READY_ST",
212 std::bind(&StateModelTest::readyHandler, this));
213
214 defineState(DO_WORK_ST, "DO_WORK_ST",
215 std::bind(&StateModelTest::doWorkHandler, this));
216
217 defineState(DONE_ST, "DONE_ST",
218 std::bind(&StateModelTest::doneWorkHandler, this));
219
220 defineState(PAUSE_ALWAYS_ST, "PAUSE_ALWAYS_ST",
221 std::bind(&StateModelTest::pauseHandler, this),
222 STATE_PAUSE_ALWAYS);
223
224 defineState(PAUSE_ONCE_ST, "PAUSE_ONCE_ST",
225 std::bind(&StateModelTest::pauseHandler, this),
226 STATE_PAUSE_ONCE);
227 }
228
229 /// @brief Verify the state dictionary.
verifyStates()230 virtual void verifyStates() {
231 // Invoke the base call implementation first.
232 StateModel::verifyStates();
233
234 // Verify our states.
235 getStateInternal(DUMMY_ST);
236 getStateInternal(READY_ST);
237 getStateInternal(DO_WORK_ST);
238 getStateInternal(DONE_ST);
239 getStateInternal(PAUSE_ALWAYS_ST);
240 getStateInternal(PAUSE_ONCE_ST);
241 }
242
243 /// @brief Manually construct the event and state dictionaries.
244 /// This allows testing without running startModel.
initDictionaries()245 void initDictionaries() {
246 ASSERT_NO_THROW(defineEvents());
247 ASSERT_NO_THROW(verifyEvents());
248 ASSERT_NO_THROW(defineStates());
249 ASSERT_NO_THROW(verifyStates());
250 }
251
252 /// @brief Tests the event dictionary entry for the given event value.
checkEvent(const int value,const std::string & label)253 bool checkEvent(const int value, const std::string& label) {
254 EventPtr event;
255 try {
256 event = getEvent(value);
257 EXPECT_TRUE(event);
258 EXPECT_EQ(value, event->getValue());
259 EXPECT_EQ(label, event->getLabel());
260 } catch (const std::exception& ex) {
261 return false;
262 }
263
264 return true;
265 }
266
267 /// @brief Tests the state dictionary entry for the given state value.
checkState(const int value,const std::string & label)268 bool checkState(const int value, const std::string& label) {
269 EventPtr state;
270 try {
271 state = getState(value);
272 EXPECT_TRUE(state);
273 EXPECT_EQ(value, state->getValue());
274 EXPECT_EQ(label, state->getLabel());
275 } catch (const std::exception& ex) {
276 return false;
277 }
278
279 return true;
280 }
281
282
283 /// @brief Handler called when the model suffers an execution error.
onModelFailure(const std::string & explanation)284 virtual void onModelFailure(const std::string& explanation) {
285 failure_explanation_ = explanation;
286 }
287
288 /// @brief Indicator of whether or not the DUMMY_ST handler has been called.
289 bool dummy_called_;
290
291 /// @brief Indicator of whether or not DONE_ST handler has been called.
292 bool work_completed_;
293
294 /// @brief Stores the failure explanation
295 std::string failure_explanation_;
296 };
297
298 // Declare them so gtest can see them.
299 const int StateModelTest::DUMMY_ST;
300 const int StateModelTest::READY_ST;
301 const int StateModelTest::DO_WORK_ST;
302 const int StateModelTest::DONE_ST;
303 const int StateModelTest::WORK_START_EVT;
304 const int StateModelTest::WORK_DONE_EVT;
305 const int StateModelTest::ALL_DONE_EVT;
306 const int StateModelTest::PAUSE_ALWAYS_ST;
307 const int StateModelTest::PAUSE_ONCE_ST;
308
309 /// @brief Checks the fundamentals of defining and retrieving events.
TEST_F(StateModelTest,eventDefinition)310 TEST_F(StateModelTest, eventDefinition) {
311 // After construction, the event dictionary should be empty. Verify that
312 // getEvent will throw when event is not defined.
313 EXPECT_THROW(getEvent(NOP_EVT), StateModelError);
314
315 // Verify that we can add a handler to the map.
316 ASSERT_NO_THROW(defineEvent(NOP_EVT, "NOP_EVT"));
317
318 // Verify that we can find the event by value and its content is correct.
319 EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
320
321 // Verify that we cannot add a duplicate.
322 ASSERT_THROW(defineEvent(NOP_EVT, "NOP_EVT"), StateModelError);
323
324 // Verify that we can still find the event.
325 EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
326 }
327
328 /// @brief Tests event dictionary construction and verification.
TEST_F(StateModelTest,eventDictionary)329 TEST_F(StateModelTest, eventDictionary) {
330 // After construction, the event dictionary should be empty.
331 // Make sure that verifyEvents() throws.
332 EXPECT_THROW(verifyEvents(), StateModelError);
333
334 // Construct the dictionary and verify it.
335 EXPECT_NO_THROW(defineEvents());
336 EXPECT_NO_THROW(verifyEvents());
337
338 // Verify base class events are defined.
339 EXPECT_TRUE(checkEvent(NOP_EVT, "NOP_EVT"));
340 EXPECT_TRUE(checkEvent(START_EVT, "START_EVT"));
341 EXPECT_TRUE(checkEvent(END_EVT, "END_EVT"));
342 EXPECT_TRUE(checkEvent(FAIL_EVT, "FAIL_EVT"));
343
344 // Verify stub class events are defined.
345 EXPECT_TRUE(checkEvent(WORK_START_EVT, "WORK_START_EVT"));
346 EXPECT_TRUE(checkEvent(WORK_DONE_EVT, "WORK_DONE_EVT"));
347 EXPECT_TRUE(checkEvent(ALL_DONE_EVT, "ALL_DONE_EVT"));
348 EXPECT_TRUE(checkEvent(FORCE_UNDEFINED_ST_EVT, "FORCE_UNDEFINED_ST_EVT"));
349 EXPECT_TRUE(checkEvent(SIMULATE_ERROR_EVT, "SIMULATE_ERROR_EVT"));
350
351 // Verify that undefined events are handled correctly.
352 EXPECT_THROW(getEvent(9999), StateModelError);
353 EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getEventLabel(9999));
354 }
355
356 /// @brief General testing of event context accessors.
357 /// Most if not all of these are also tested as a byproduct off larger tests.
TEST_F(StateModelTest,eventContextAccessors)358 TEST_F(StateModelTest, eventContextAccessors) {
359 // Construct the event definitions, normally done by startModel.
360 ASSERT_NO_THROW(defineEvents());
361 ASSERT_NO_THROW(verifyEvents());
362
363 // Verify the post-construction values.
364 EXPECT_EQ(NOP_EVT, getNextEvent());
365 EXPECT_EQ(NOP_EVT, getLastEvent());
366
367 // Call setEvent which will update both next event and last event.
368 EXPECT_NO_THROW(postNextEvent(START_EVT));
369
370 // Verify the values are what we expect.
371 EXPECT_EQ(START_EVT, getNextEvent());
372 EXPECT_EQ(NOP_EVT, getLastEvent());
373
374 // Call setEvent again.
375 EXPECT_NO_THROW(postNextEvent(WORK_START_EVT));
376
377 // Verify the values are what we expect.
378 EXPECT_EQ(WORK_START_EVT, getNextEvent());
379 EXPECT_EQ(START_EVT, getLastEvent());
380
381 // Verify that posting an undefined event throws.
382 EXPECT_THROW(postNextEvent(9999), StateModelError);
383 }
384
385 /// @brief Tests the fundamental methods used for state handler mapping.
386 /// Verifies the ability to search for and add entries in the state handler map.
TEST_F(StateModelTest,stateDefinition)387 TEST_F(StateModelTest, stateDefinition) {
388 // After construction, the state dictionary should be empty. Verify that
389 // getState will throw when, state is not defined.
390 EXPECT_THROW(getState(READY_ST), StateModelError);
391
392 // Verify that we can add a state to the dictionary.
393 ASSERT_NO_THROW(defineState(READY_ST, "READY_ST",
394 std::bind(&StateModelTest::dummyHandler,
395 this)));
396
397 // Verify that we can find the state by its value.
398 StatePtr state;
399 EXPECT_NO_THROW(state = getState(READY_ST));
400 EXPECT_TRUE(state);
401
402 // Verify the state's value and label.
403 EXPECT_EQ(READY_ST, state->getValue());
404 EXPECT_EQ("READY_ST", state->getLabel());
405
406 // Now verify that retrieved state's handler executes the correct method.
407 // Make sure the dummy called flag is false prior to invocation.
408 EXPECT_FALSE(getDummyCalled());
409
410 // Invoke the state's handler.
411 EXPECT_NO_THROW(state->run());
412
413 // Verify the dummy called flag is now true.
414 EXPECT_TRUE(getDummyCalled());
415
416 // Verify that we cannot add a duplicate.
417 EXPECT_THROW(defineState(READY_ST, "READY_ST",
418 std::bind(&StateModelTest::readyHandler, this)),
419 StateModelError);
420
421 // Verify that we can still find the state.
422 EXPECT_NO_THROW(getState(READY_ST));
423 }
424
425 /// @brief Tests state dictionary initialization and validation.
426 /// This tests the basic concept of state dictionary initialization and
427 /// verification by manually invoking the methods normally called by startModel.
TEST_F(StateModelTest,stateDictionary)428 TEST_F(StateModelTest, stateDictionary) {
429 // Verify that the map validation throws prior to the dictionary being
430 // initialized.
431 EXPECT_THROW(verifyStates(), StateModelError);
432
433 // Construct the dictionary and verify it.
434 ASSERT_NO_THROW(defineStates());
435 EXPECT_NO_THROW(verifyStates());
436
437 // Verify the base class states.
438 EXPECT_TRUE(checkState(NEW_ST, "NEW_ST"));
439 EXPECT_TRUE(checkState(END_ST, "END_ST"));
440
441 // Verify stub class states.
442 EXPECT_TRUE(checkState(DUMMY_ST, "DUMMY_ST"));
443 EXPECT_TRUE(checkState(READY_ST, "READY_ST"));
444 EXPECT_TRUE(checkState(DO_WORK_ST, "DO_WORK_ST"));
445 EXPECT_TRUE(checkState(DONE_ST, "DONE_ST"));
446
447 // Verify that undefined states are handled correctly.
448 EXPECT_THROW(getState(9999), StateModelError);
449 EXPECT_EQ(LabeledValueSet::UNDEFINED_LABEL, getStateLabel(9999));
450 }
451
452 /// @brief General testing of state context accessors.
453 /// Most if not all of these are also tested as a byproduct off larger tests.
TEST_F(StateModelTest,stateContextAccessors)454 TEST_F(StateModelTest, stateContextAccessors) {
455 // setState will throw unless we initialize the handler map.
456 ASSERT_NO_THROW(defineStates());
457 ASSERT_NO_THROW(verifyStates());
458
459 // Verify post-construction state values.
460 EXPECT_EQ(NEW_ST, getCurrState());
461 EXPECT_EQ(NEW_ST, getPrevState());
462
463 // Call setState which will update both state and previous state.
464 EXPECT_NO_THROW(setState(READY_ST));
465
466 // Verify the values are what we expect.
467 EXPECT_EQ(READY_ST, getCurrState());
468 EXPECT_EQ(NEW_ST, getPrevState());
469
470 // Call setState again.
471 EXPECT_NO_THROW(setState(DO_WORK_ST));
472
473 // Verify the values are what we expect.
474 EXPECT_EQ(DO_WORK_ST, getCurrState());
475 EXPECT_EQ(READY_ST, getPrevState());
476
477 // Verify that calling setState with an state that has no handler
478 // will throw.
479 EXPECT_THROW(setState(-1), StateModelError);
480
481 // Verify that calling setState with NEW_ST is ok.
482 EXPECT_NO_THROW(setState(NEW_ST));
483
484 // Verify that calling setState with END_ST is ok.
485 EXPECT_NO_THROW(setState(END_ST));
486
487 // Verify that calling setState with an undefined state throws.
488 EXPECT_THROW(setState(9999), StateModelError);
489 }
490
491 /// @brief Checks that invoking runModel prior to startModel is not allowed.
TEST_F(StateModelTest,runBeforeStart)492 TEST_F(StateModelTest, runBeforeStart) {
493 // Verify that the failure explanation is empty and work is not done.
494 EXPECT_TRUE(getFailureExplanation().empty());
495
496 // Attempt to call runModel before startModel. This should result in an
497 // orderly model failure.
498 ASSERT_NO_THROW(runModel(START_EVT));
499
500 // Check that state and event are correct.
501 EXPECT_EQ(END_ST, getCurrState());
502 EXPECT_EQ(FAIL_EVT, getNextEvent());
503
504 // Verify that failure explanation is not empty.
505 EXPECT_FALSE(getFailureExplanation().empty());
506 }
507
508 /// @brief Tests that the endModel may be used to transition the model to
509 /// a normal conclusion.
TEST_F(StateModelTest,transitionWithEnd)510 TEST_F(StateModelTest, transitionWithEnd) {
511 // Init dictionaries manually, normally done by startModel.
512 initDictionaries();
513
514 // call transition to move from NEW_ST to DUMMY_ST with START_EVT
515 EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
516
517 // Verify that state and event members are as expected.
518 EXPECT_EQ(DUMMY_ST, getCurrState());
519 EXPECT_EQ(NEW_ST, getPrevState());
520 EXPECT_EQ(START_EVT, getNextEvent());
521 EXPECT_EQ(NOP_EVT, getLastEvent());
522
523 // Call endModel to transition us to the end of the model.
524 EXPECT_NO_THROW(endModel());
525
526 // Verify state and event members are correctly set.
527 EXPECT_EQ(END_ST, getCurrState());
528 EXPECT_EQ(DUMMY_ST, getPrevState());
529 EXPECT_EQ(END_EVT, getNextEvent());
530 EXPECT_EQ(START_EVT, getLastEvent());
531 }
532
533 /// @brief Tests that the abortModel may be used to transition the model to
534 /// failed conclusion.
TEST_F(StateModelTest,transitionWithAbort)535 TEST_F(StateModelTest, transitionWithAbort) {
536 // Init dictionaries manually, normally done by startModel.
537 initDictionaries();
538
539 // call transition to move from NEW_ST to DUMMY_ST with START_EVT
540 EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
541
542 // Verify that state and event members are as expected.
543 EXPECT_EQ(DUMMY_ST, getCurrState());
544 EXPECT_EQ(NEW_ST, getPrevState());
545 EXPECT_EQ(START_EVT, getNextEvent());
546 EXPECT_EQ(NOP_EVT, getLastEvent());
547
548 // Call endModel to transition us to the end of the model.
549 EXPECT_NO_THROW(abortModel("test invocation"));
550
551 // Verify state and event members are correctly set.
552 EXPECT_EQ(END_ST, getCurrState());
553 EXPECT_EQ(DUMMY_ST, getPrevState());
554 EXPECT_EQ(FAIL_EVT, getNextEvent());
555 EXPECT_EQ(START_EVT, getLastEvent());
556 }
557
558 /// @brief Tests that the boolean indicators for on state entry and exit
559 /// work properly.
TEST_F(StateModelTest,doFlags)560 TEST_F(StateModelTest, doFlags) {
561 // Init dictionaries manually, normally done by startModel.
562 initDictionaries();
563
564 // Verify that "do" flags are false.
565 EXPECT_FALSE(doOnEntry());
566 EXPECT_FALSE(doOnExit());
567
568 // call transition to move from NEW_ST to DUMMY_ST with START_EVT
569 EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
570
571 // We are transitioning states, so "do" flags should be true.
572 EXPECT_TRUE(doOnEntry());
573 EXPECT_TRUE(doOnExit());
574
575 // "do" flags are one-shots, so they should now both be false.
576 EXPECT_FALSE(doOnEntry());
577 EXPECT_FALSE(doOnExit());
578
579 // call transition to re-enter same state, "do" flags should be false.
580 EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
581
582 // "do" flags should be false.
583 EXPECT_FALSE(doOnEntry());
584 EXPECT_FALSE(doOnExit());
585
586 }
587
588 /// @brief Verifies that the model status methods accurately reflect the model
589 /// status. It also verifies that the dictionaries can be modified before
590 /// the model is running but not after.
TEST_F(StateModelTest,statusMethods)591 TEST_F(StateModelTest, statusMethods) {
592 // Init dictionaries manually, normally done by startModel.
593 initDictionaries();
594
595 // After construction, state model is "new", all others should be false.
596 EXPECT_TRUE(isModelNew());
597 EXPECT_FALSE(isModelRunning());
598 EXPECT_FALSE(isModelWaiting());
599 EXPECT_FALSE(isModelDone());
600 EXPECT_FALSE(didModelFail());
601
602 // Verify that events and states can be added before the model is started.
603 EXPECT_NO_THROW(defineEvent(9998, "9998"));
604 EXPECT_NO_THROW(defineState(9998, "9998",
605 std::bind(&StateModelTest::readyHandler,
606 this)));
607
608 // "START" the model.
609 // Fake out starting the model by calling transition to move from NEW_ST
610 // to DUMMY_ST with START_EVT. If we used startModel this would blow by
611 // the status of "running" but not "waiting".
612 EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
613
614
615 // Verify that events and states cannot be added after the model is started.
616 EXPECT_THROW(defineEvent(9999, "9999"), StateModelError);
617 EXPECT_THROW(defineState(9999, "9999",
618 std::bind(&StateModelTest::readyHandler, this)),
619 StateModelError);
620
621 // The state and event combos set above, should show the model as
622 // "running", all others should be false.
623 EXPECT_FALSE(isModelNew());
624 EXPECT_TRUE(isModelRunning());
625 EXPECT_FALSE(isModelWaiting());
626 EXPECT_FALSE(isModelDone());
627 EXPECT_FALSE(didModelFail());
628
629 // call transition to submit NOP_EVT to current state, DUMMY_ST.
630 EXPECT_NO_THROW(transition(DUMMY_ST, NOP_EVT));
631
632 // Verify the status methods are correct: with next event set to NOP_EVT,
633 // model should be "running" and "waiting".
634 EXPECT_FALSE(isModelNew());
635 EXPECT_TRUE(isModelRunning());
636 EXPECT_TRUE(isModelWaiting());
637 EXPECT_FALSE(isModelDone());
638 EXPECT_FALSE(didModelFail());
639
640 // Call endModel to transition us to the end of the model.
641 EXPECT_NO_THROW(endModel());
642
643 // With state set to END_ST, model should be done.
644 EXPECT_FALSE(isModelNew());
645 EXPECT_FALSE(isModelRunning());
646 EXPECT_FALSE(isModelWaiting());
647 EXPECT_TRUE(isModelDone());
648 EXPECT_FALSE(didModelFail());
649 }
650
651 /// @brief Tests that the model status methods are correct after a model
652 /// failure.
TEST_F(StateModelTest,statusMethodsOnFailure)653 TEST_F(StateModelTest, statusMethodsOnFailure) {
654 // Construct the event and state definitions, normally done by startModel.
655 ASSERT_NO_THROW(defineEvents());
656 ASSERT_NO_THROW(verifyEvents());
657 ASSERT_NO_THROW(defineStates());
658 ASSERT_NO_THROW(verifyStates());
659
660 // call transition to move from NEW_ST to DUMMY_ST with START_EVT
661 EXPECT_NO_THROW(transition(DUMMY_ST, START_EVT));
662
663 // Call endModel to transition us to the end of the model.
664 EXPECT_NO_THROW(abortModel("test invocation"));
665
666 // With state set to END_ST, model should be done.
667 EXPECT_FALSE(isModelNew());
668 EXPECT_FALSE(isModelRunning());
669 EXPECT_FALSE(isModelWaiting());
670 EXPECT_TRUE(isModelDone());
671 EXPECT_TRUE(didModelFail());
672 }
673
674 /// @brief Checks that the context strings accurately reflect context and
675 /// are safe to invoke.
TEST_F(StateModelTest,contextStrs)676 TEST_F(StateModelTest, contextStrs) {
677 // Verify context methods do not throw prior to dictionary init.
678 ASSERT_NO_THROW(getContextStr());
679 ASSERT_NO_THROW(getPrevContextStr());
680
681 // Construct the event and state definitions, normally done by startModel.
682 ASSERT_NO_THROW(defineEvents());
683 ASSERT_NO_THROW(verifyEvents());
684 ASSERT_NO_THROW(defineStates());
685 ASSERT_NO_THROW(verifyStates());
686
687 // transition uses setState and setEvent, testing it tests all three.
688 EXPECT_NO_THROW(transition(READY_ST, START_EVT));
689
690 // Verify the current context string depicts correct state and event.
691 std::string ctx_str;
692 ASSERT_NO_THROW(ctx_str = getContextStr());
693 EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(READY_ST)));
694 EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(START_EVT)));
695
696 // Verify the previous context string depicts correct state and event.
697 ASSERT_NO_THROW(ctx_str = getPrevContextStr());
698 EXPECT_NE(std::string::npos, ctx_str.find(getStateLabel(NEW_ST)));
699 EXPECT_NE(std::string::npos, ctx_str.find(getEventLabel(NOP_EVT)));
700 }
701
702 /// @brief Tests that undefined states are handled gracefully.
703 /// This test verifies that attempting to transition to an undefined state,
704 /// which constitutes a model violation, results in an orderly model failure.
TEST_F(StateModelTest,undefinedState)705 TEST_F(StateModelTest, undefinedState) {
706 // Verify that the failure explanation is empty and work is not done.
707 EXPECT_TRUE(getFailureExplanation().empty());
708 EXPECT_FALSE(getWorkCompleted());
709
710 // First, lets execute the state model to a known valid point, by
711 // calling startModel. This should run the model through to DO_WORK_ST.
712 ASSERT_NO_THROW(startModel(READY_ST));
713
714 // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
715 EXPECT_EQ(DO_WORK_ST, getCurrState());
716 EXPECT_EQ(NOP_EVT, getNextEvent());
717
718 // Resume the model with next event set to cause the DO_WORK_ST handler
719 // to transition to an undefined state. This should cause it to return
720 // without throwing and yield a failed model.
721 EXPECT_NO_THROW(runModel(FORCE_UNDEFINED_ST_EVT));
722
723 // Verify that status methods are correct: model is done but failed.
724 EXPECT_FALSE(isModelNew());
725 EXPECT_FALSE(isModelRunning());
726 EXPECT_FALSE(isModelWaiting());
727 EXPECT_TRUE(isModelDone());
728 EXPECT_TRUE(didModelFail());
729
730 // Verify that failure explanation is not empty.
731 EXPECT_FALSE(getFailureExplanation().empty());
732
733 // Verify that work completed flag is still false.
734 EXPECT_FALSE(getWorkCompleted());
735 }
736
737 /// @brief Tests that an unexpected exception thrown by a state handler is
738 /// handled gracefully. State models are supposed to account for and handle
739 /// all errors that they actions (i.e. handlers) may cause. In the event they
740 /// do not, this constitutes a model violation. This test verifies such
741 /// violations are handled correctly and result in an orderly model failure.
TEST_F(StateModelTest,unexpectedError)742 TEST_F(StateModelTest, unexpectedError) {
743 // Verify that the failure explanation is empty and work is not done.
744 EXPECT_TRUE(getFailureExplanation().empty());
745 EXPECT_FALSE(getWorkCompleted());
746
747 // First, lets execute the state model to a known valid point, by
748 // calling startModel with a start state of READY_ST.
749 // This should run the model through to DO_WORK_ST.
750 ASSERT_NO_THROW(startModel(READY_ST));
751
752 // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
753 EXPECT_EQ(DO_WORK_ST, getCurrState());
754 EXPECT_EQ(NOP_EVT, getNextEvent());
755
756 // Resume the model with next event set to cause the DO_WORK_ST handler
757 // to transition to an undefined state. This should cause it to return
758 // without throwing and yield a failed model.
759 EXPECT_NO_THROW(runModel(SIMULATE_ERROR_EVT));
760
761 // Verify that status methods are correct: model is done but failed.
762 EXPECT_FALSE(isModelNew());
763 EXPECT_FALSE(isModelRunning());
764 EXPECT_FALSE(isModelWaiting());
765 EXPECT_TRUE(isModelDone());
766 EXPECT_TRUE(didModelFail());
767
768 // Verify that failure explanation is not empty.
769 EXPECT_FALSE(getFailureExplanation().empty());
770
771 // Verify that work completed flag is still false.
772 EXPECT_FALSE(getWorkCompleted());
773 }
774
775 /// @brief Tests that undefined events are handled gracefully.
776 /// This test verifies that submitting an undefined event to the state machine
777 /// results, which constitutes a model violation, results in an orderly model
778 /// failure.
TEST_F(StateModelTest,undefinedEvent)779 TEST_F(StateModelTest, undefinedEvent) {
780 // Verify that the failure explanation is empty and work is not done.
781 EXPECT_TRUE(getFailureExplanation().empty());
782 EXPECT_FALSE(getWorkCompleted());
783
784 // First, lets execute the state model to a known valid point, by
785 // calling startModel with a start state of READY_ST.
786 // This should run the model through to DO_WORK_ST.
787 ASSERT_NO_THROW(startModel(READY_ST));
788
789 // Verify we are in the state of DO_WORK_ST with event of NOP_EVT.
790 EXPECT_EQ(DO_WORK_ST, getCurrState());
791 EXPECT_EQ(NOP_EVT, getNextEvent());
792
793 // Attempting to post an undefined event within runModel should cause it
794 // to return without throwing and yield a failed model.
795 EXPECT_NO_THROW(runModel(9999));
796
797 // Verify that status methods are correct: model is done but failed.
798 EXPECT_FALSE(isModelNew());
799 EXPECT_FALSE(isModelRunning());
800 EXPECT_FALSE(isModelWaiting());
801 EXPECT_TRUE(isModelDone());
802 EXPECT_TRUE(didModelFail());
803
804 // Verify that failure explanation is not empty.
805 EXPECT_FALSE(getFailureExplanation().empty());
806
807 // Verify that work completed flag is still false.
808 EXPECT_FALSE(getWorkCompleted());
809 }
810
811 /// @brief Test the basic mechanics of state model execution.
812 /// This test exercises the ability to execute state model from start to
813 /// finish, including the handling of a asynchronous IO operation.
TEST_F(StateModelTest,stateModelTest)814 TEST_F(StateModelTest, stateModelTest) {
815 // Verify that status methods are correct: model is new.
816 EXPECT_TRUE(isModelNew());
817 EXPECT_FALSE(isModelRunning());
818 EXPECT_FALSE(isModelWaiting());
819 EXPECT_FALSE(isModelDone());
820
821 // Verify that the failure explanation is empty and work is not done.
822 EXPECT_TRUE(getFailureExplanation().empty());
823 EXPECT_FALSE(getWorkCompleted());
824
825 // Launch the transaction by calling startModel. The state model
826 // should run up until the simulated async work operation is initiated
827 // in DO_WORK_ST.
828 ASSERT_NO_THROW(startModel(READY_ST));
829
830 // Verify that we are now in state of DO_WORK_ST, the last event was
831 // WORK_START_EVT, the next event is NOP_EVT.
832 EXPECT_EQ(DO_WORK_ST, getCurrState());
833 EXPECT_EQ(WORK_START_EVT, getLastEvent());
834 EXPECT_EQ(NOP_EVT, getNextEvent());
835
836 // Simulate completion of async work completion by resuming runModel with
837 // an event of WORK_DONE_EVT.
838 ASSERT_NO_THROW(runModel(WORK_DONE_EVT));
839
840 // Verify that the state model has progressed through to completion:
841 // it is in the DONE_ST, the status is ST_COMPLETED, and the next event
842 // is NOP_EVT.
843 EXPECT_EQ(END_ST, getCurrState());
844 EXPECT_EQ(END_EVT, getNextEvent());
845
846 // Verify that status methods are correct: model done.
847 EXPECT_FALSE(isModelNew());
848 EXPECT_FALSE(isModelRunning());
849 EXPECT_FALSE(isModelWaiting());
850 EXPECT_TRUE(isModelDone());
851
852 // Verify that failure explanation is empty.
853 EXPECT_TRUE(getFailureExplanation().empty());
854
855 // Verify that work completed flag is true.
856 EXPECT_TRUE(getWorkCompleted());
857 }
858
859 // This test verifies the pausing and un-pausing capabilities of the state
860 // model.
TEST_F(StateModelTest,stateModelPause)861 TEST_F(StateModelTest, stateModelPause) {
862 // Verify that status methods are correct: model is new.
863 EXPECT_TRUE(isModelNew());
864 EXPECT_FALSE(isModelRunning());
865 EXPECT_FALSE(isModelWaiting());
866 EXPECT_FALSE(isModelDone());
867 EXPECT_FALSE(isModelPaused());
868
869 // Verify that the failure explanation is empty and work is not done.
870 EXPECT_TRUE(getFailureExplanation().empty());
871 EXPECT_FALSE(getWorkCompleted());
872
873 // Transition straight to the state in which the model should always
874 // pause.
875 ASSERT_NO_THROW(startModel(PAUSE_ALWAYS_ST));
876
877 // Verify it was successful and that the model is paused.
878 EXPECT_EQ(PAUSE_ALWAYS_ST, getCurrState());
879 EXPECT_TRUE(isModelPaused());
880
881 // Run the model again. It should still be paused.
882 ASSERT_NO_THROW(runModel(NOP_EVT));
883 EXPECT_TRUE(isModelPaused());
884
885 // Unpause the model and transition to the state in which the model
886 // should be paused at most once.
887 unpauseModel();
888 transition(PAUSE_ONCE_ST, NOP_EVT);
889 EXPECT_EQ(PAUSE_ONCE_ST, getCurrState());
890 EXPECT_TRUE(isModelPaused());
891
892 // The model should still be paused until explicitly unpaused.
893 ASSERT_NO_THROW(runModel(NOP_EVT));
894 EXPECT_EQ(PAUSE_ONCE_ST, getCurrState());
895 EXPECT_TRUE(isModelPaused());
896
897 unpauseModel();
898
899 // Transition back to the first state. The model should pause again.
900 transition(PAUSE_ALWAYS_ST, NOP_EVT);
901 EXPECT_EQ(PAUSE_ALWAYS_ST, getCurrState());
902 EXPECT_TRUE(isModelPaused());
903
904 ASSERT_NO_THROW(runModel(NOP_EVT));
905 EXPECT_EQ(PAUSE_ALWAYS_ST, getCurrState());
906 EXPECT_TRUE(isModelPaused());
907
908 // Unpause the model and transition to the state in which the model
909 // should pause only once. This time it should not pause.
910 unpauseModel();
911 transition(PAUSE_ONCE_ST, NOP_EVT);
912 EXPECT_EQ(PAUSE_ONCE_ST, getCurrState());
913 EXPECT_FALSE(isModelPaused());
914 }
915
916 }
917