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>
32 // SGCondition subclass we can trivially manipulate from test code.
33 class DummyCondition : public SGCondition
36 DummyCondition(): _state(false) { }
38 virtual bool test() const
49 DummyThing() : dummy_cmd_state(0) { }
51 bool someCommand(const SGPropertyNode* arg)
60 #define COMPARE(a, b) \
62 cerr << "failed:" << #a << " != " << #b << endl; \
63 cerr << "\tgot:'" << a << "'" << endl; \
69 cerr << "failed:" << #a << endl; \
73 using namespace simgear;
75 #define BUILD_MACHINE_1() \
76 StateMachine_ptr sm(new StateMachine); \
77 StateMachine::State_ptr stateA = sm->createState("a"); \
78 StateMachine::State_ptr stateB = sm->createState("b"); \
79 StateMachine::State_ptr stateC = sm->createState("c"); \
81 DummyCondition* trigger1 = new DummyCondition; \
82 StateMachine::Transition_ptr t1 = sm->createTransition(">b", stateB); \
83 t1->addSourceState(stateA); \
84 t1->setTriggerCondition(trigger1); \
86 DummyCondition* trigger2 = new DummyCondition; \
87 StateMachine::Transition_ptr t2 = sm->createTransition(">c", stateC); \
88 t2->addSourceState(stateB); \
89 t2->setTriggerCondition(trigger2); \
91 DummyCondition* trigger3 = new DummyCondition; \
92 StateMachine::Transition_ptr t3 = sm->createTransition(">a", stateA); \
93 t3->addSourceState(stateC); \
94 t3->addSourceState(stateB); \
95 t3->setTriggerCondition(trigger3); \
101 ////////////////////////////////////////////
102 COMPARE(sm->state()->name(), "a");
104 COMPARE(sm->indexOfState(stateA), 0);
105 COMPARE(sm->findStateByName("c"), stateC);
107 sm->changeToState(stateC);
108 COMPARE(sm->state(), stateC);
110 trigger3->_state = true;
112 COMPARE(sm->state()->name(), "a");
113 trigger3->_state = false;
115 trigger1->_state = true;
117 trigger1->_state = false;
118 COMPARE(sm->state()->name(), "b");
120 trigger3->_state = true;
122 COMPARE(sm->state()->name(), "a");
123 trigger3->_state = false;
125 trigger1->_state = true;
127 trigger1->_state = false;
128 COMPARE(sm->state()->name(), "b");
130 trigger2->_state = true;
132 trigger2->_state = false;
133 COMPARE(sm->state()->name(), "c");
135 //////////////////////////////////////////
136 COMPARE(sm->root()->getIntValue("current-index"), 2);
137 COMPARE(sm->root()->getStringValue("current-name"), string("c"));
139 sm->root()->setStringValue("current-name", "b");
140 COMPARE(sm->state()->name(), "b");
142 ////////////////////////////////////////
143 COMPARE(sm->findStateByName("foo"), NULL);
144 COMPARE(sm->indexOfState(StateMachine::State_ptr()), -1);
146 COMPARE(sm->stateByIndex(1), stateB);
149 sm->stateByIndex(44);
150 VERIFY(false && "should have raised an exception");
151 } catch (sg_exception& e){
156 void testNoSourcesTransition()
160 DummyCondition* trigger4 = new DummyCondition;
161 StateMachine::Transition_ptr t4 = sm->createTransition("alwaysToA", stateA); \
162 t4->setTriggerCondition(trigger4); \
165 COMPARE(sm->state()->name(), "a");
166 trigger1->_state = true;
168 trigger1->_state = false;
169 COMPARE(sm->state()->name(), "b");
171 trigger4->_state = true;
173 trigger4->_state = false;
174 COMPARE(sm->state()->name(), "a");
179 SGCommandMgr* cmdMgr = SGCommandMgr::instance();
181 cmdMgr->addCommand("dummy", &thing, &DummyThing::someCommand);
184 t2->addBinding(new SGBinding("dummy"));
186 stateA->addEntryBinding(new SGBinding("dummy"));
187 stateA->addExitBinding(new SGBinding("dummy"));
188 stateC->addEntryBinding(new SGBinding("dummy"));
190 ////////////////////////
191 COMPARE(sm->state()->name(), "a");
192 trigger1->_state = true;
194 trigger1->_state = false;
195 COMPARE(sm->state()->name(), "b");
196 COMPARE(thing.dummy_cmd_state, 1); // exit state A
198 trigger2->_state = true;
200 trigger2->_state = false;
201 COMPARE(thing.dummy_cmd_state, 3); // fire transition 2, enter state C
203 thing.dummy_cmd_state = 0;
204 sm->changeToState(stateA);
205 COMPARE(thing.dummy_cmd_state, 1); // enter state A
206 trigger1->_state = true;
208 trigger1->_state = false;
209 COMPARE(thing.dummy_cmd_state, 2); // exit state A
211 ////////////////////////
212 t3->addBinding(new SGBinding("dummy"));
213 t3->addBinding(new SGBinding("dummy"));
214 t3->addBinding(new SGBinding("dummy"));
216 sm->changeToStateName("b");
217 thing.dummy_cmd_state = 0;
218 trigger3->_state = true;
220 trigger3->_state = false;
221 COMPARE(thing.dummy_cmd_state, 4); // three transition bindings, enter A
226 const char* xml = "<?xml version=\"1.0\"?>"
236 SGPropertyNode* desc = new SGPropertyNode;
237 readProperties(xml, strlen(xml), desc);
239 SGPropertyNode_ptr root(new SGPropertyNode);
240 StateMachine_ptr sm = StateMachine::createFromPlist(desc, root);
242 VERIFY(sm->findStateByName("one") != NULL);
243 VERIFY(sm->findStateByName("two") != NULL);
246 int main(int argc, char* argv[])
251 testNoSourcesTransition();
253 cout << __FILE__ << ": All tests passed" << endl;