2 # include <simgear_config.h>
6 // Always enable DEBUG mode in test application, otherwise "assert" test
7 // statements have no effect and don't actually test anything (catch 17 ;-) ).
11 #include <simgear/compiler.h>
18 #include "StateMachine.hxx"
20 #include <simgear/structure/SGBinding.hxx>
21 #include <simgear/structure/exception.hxx>
22 #include <simgear/props/condition.hxx>
23 #include <simgear/props/props.hxx>
24 #include <simgear/props/props_io.hxx>
25 #include <simgear/structure/commands.hxx>
26 #include <simgear/misc/test_macros.hxx>
33 // SGCondition subclass we can trivially manipulate from test code.
34 class DummyCondition : public SGCondition
37 DummyCondition(): _state(false) { }
39 virtual bool test() const
50 DummyThing() : dummy_cmd_state(0) { }
52 bool someCommand(const SGPropertyNode* arg)
61 using namespace simgear;
63 #define BUILD_MACHINE_1() \
64 StateMachine_ptr sm(new StateMachine); \
65 StateMachine::State_ptr stateA = sm->createState("a"); \
66 StateMachine::State_ptr stateB = sm->createState("b"); \
67 StateMachine::State_ptr stateC = sm->createState("c"); \
69 DummyCondition* trigger1 = new DummyCondition; \
70 StateMachine::Transition_ptr t1 = sm->createTransition(">b", stateB); \
71 t1->addSourceState(stateA); \
72 t1->setTriggerCondition(trigger1); \
74 DummyCondition* trigger2 = new DummyCondition; \
75 StateMachine::Transition_ptr t2 = sm->createTransition(">c", stateC); \
76 t2->addSourceState(stateB); \
77 t2->setTriggerCondition(trigger2); \
79 DummyCondition* trigger3 = new DummyCondition; \
80 StateMachine::Transition_ptr t3 = sm->createTransition(">a", stateA); \
81 t3->addSourceState(stateC); \
82 t3->addSourceState(stateB); \
83 t3->setTriggerCondition(trigger3); \
89 ////////////////////////////////////////////
90 COMPARE(sm->state()->name(), "a");
92 COMPARE(sm->indexOfState(stateA), 0);
93 COMPARE(sm->findStateByName("c"), stateC);
95 sm->changeToState(stateC);
96 COMPARE(sm->state(), stateC);
98 trigger3->_state = true;
100 COMPARE(sm->state()->name(), "a");
101 trigger3->_state = false;
103 trigger1->_state = true;
105 trigger1->_state = false;
106 COMPARE(sm->state()->name(), "b");
108 trigger3->_state = true;
110 COMPARE(sm->state()->name(), "a");
111 trigger3->_state = false;
113 trigger1->_state = true;
115 trigger1->_state = false;
116 COMPARE(sm->state()->name(), "b");
118 trigger2->_state = true;
120 trigger2->_state = false;
121 COMPARE(sm->state()->name(), "c");
123 //////////////////////////////////////////
124 COMPARE(sm->root()->getIntValue("current-index"), 2);
125 COMPARE(sm->root()->getStringValue("current-name"), string("c"));
127 sm->root()->setStringValue("current-name", "b");
128 COMPARE(sm->state()->name(), "b");
130 ////////////////////////////////////////
131 COMPARE(sm->findStateByName("foo"), NULL);
132 COMPARE(sm->indexOfState(StateMachine::State_ptr()), -1);
134 COMPARE(sm->stateByIndex(1), stateB);
137 sm->stateByIndex(44);
138 VERIFY(false && "should have raised an exception");
139 } catch (sg_exception& e){
144 void testNoSourcesTransition()
148 DummyCondition* trigger4 = new DummyCondition;
149 StateMachine::Transition_ptr t4 = sm->createTransition("alwaysToA", stateA); \
150 t4->setTriggerCondition(trigger4); \
153 COMPARE(sm->state()->name(), "a");
154 trigger1->_state = true;
156 trigger1->_state = false;
157 COMPARE(sm->state()->name(), "b");
159 trigger4->_state = true;
161 trigger4->_state = false;
162 COMPARE(sm->state()->name(), "a");
167 SGCommandMgr* cmdMgr = SGCommandMgr::instance();
169 cmdMgr->addCommand("dummy", &thing, &DummyThing::someCommand);
172 t2->addBinding(new SGBinding("dummy"));
174 stateA->addEntryBinding(new SGBinding("dummy"));
175 stateA->addExitBinding(new SGBinding("dummy"));
176 stateC->addEntryBinding(new SGBinding("dummy"));
178 ////////////////////////
179 COMPARE(sm->state()->name(), "a");
180 trigger1->_state = true;
182 trigger1->_state = false;
183 COMPARE(sm->state()->name(), "b");
184 COMPARE(thing.dummy_cmd_state, 1); // exit state A
186 trigger2->_state = true;
188 trigger2->_state = false;
189 COMPARE(thing.dummy_cmd_state, 3); // fire transition 2, enter state C
191 thing.dummy_cmd_state = 0;
192 sm->changeToState(stateA);
193 COMPARE(thing.dummy_cmd_state, 1); // enter state A
194 trigger1->_state = true;
196 trigger1->_state = false;
197 COMPARE(thing.dummy_cmd_state, 2); // exit state A
199 ////////////////////////
200 t3->addBinding(new SGBinding("dummy"));
201 t3->addBinding(new SGBinding("dummy"));
202 t3->addBinding(new SGBinding("dummy"));
204 sm->changeToStateName("b");
205 thing.dummy_cmd_state = 0;
206 trigger3->_state = true;
208 trigger3->_state = false;
209 COMPARE(thing.dummy_cmd_state, 4); // three transition bindings, enter A
214 const char* xml = "<?xml version=\"1.0\"?>"
224 SGPropertyNode* desc = new SGPropertyNode;
225 readProperties(xml, strlen(xml), desc);
227 SGPropertyNode_ptr root(new SGPropertyNode);
228 StateMachine_ptr sm = StateMachine::createFromPlist(desc, root);
230 VERIFY(sm->findStateByName("one") != NULL);
231 VERIFY(sm->findStateByName("two") != NULL);
234 int main(int argc, char* argv[])
239 testNoSourcesTransition();
241 cout << __FILE__ << ": All tests passed" << endl;