]> git.mxchange.org Git - simgear.git/blob - simgear/structure/state_machine_test.cxx
slider - fix shifted check while dragging
[simgear.git] / simgear / structure / state_machine_test.cxx
1 #ifdef HAVE_CONFIG_H
2 #  include <simgear_config.h>
3 #endif
4
5 #ifdef NDEBUG
6 // Always enable DEBUG mode in test application, otherwise "assert" test
7 // statements have no effect and don't actually test anything (catch 17 ;-) ).
8 #undef NDEBUG
9 #endif
10
11 #include <simgear/compiler.h>
12
13 #include <iostream>
14 #include <cassert>
15 #include <cstdlib>
16 #include <cstring>
17
18 #include "StateMachine.hxx"
19
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
27 using std::string;
28 using std::cout;
29 using std::cerr;
30 using std::endl;
31
32 // SGCondition subclass we can trivially manipulate from test code.
33 class DummyCondition : public SGCondition
34 {
35 public:
36     DummyCondition(): _state(false) { }
37     
38     virtual bool test() const
39     {
40         return _state;
41     }
42     
43     bool _state;
44 };
45
46 class DummyThing
47 {
48 public:
49     DummyThing() : dummy_cmd_state(0) { }
50     
51     bool someCommand(const SGPropertyNode* arg)
52     {
53         dummy_cmd_state++;
54         return true;
55     }
56     
57     int dummy_cmd_state;
58 };
59
60 #define COMPARE(a, b) \
61     if ((a) != (b))  { \
62         cerr << "failed:" << #a << " != " << #b << endl; \
63         cerr << "\tgot:'" << a << "'" << endl; \
64         exit(1); \
65     }
66
67 #define VERIFY(a) \
68     if (!(a))  { \
69         cerr << "failed:" << #a << endl; \
70         exit(1); \
71     }
72     
73 using namespace simgear;
74
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"); \
80     \
81     DummyCondition* trigger1 = new DummyCondition; \
82     StateMachine::Transition_ptr t1 = sm->createTransition(">b", stateB); \
83     t1->addSourceState(stateA); \
84     t1->setTriggerCondition(trigger1); \
85     \
86     DummyCondition* trigger2 = new DummyCondition; \
87     StateMachine::Transition_ptr t2 = sm->createTransition(">c", stateC); \
88     t2->addSourceState(stateB); \
89     t2->setTriggerCondition(trigger2); \
90     \
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); \
96     sm->init();
97
98 void testBasic()
99 {
100     BUILD_MACHINE_1();
101 ////////////////////////////////////////////
102     COMPARE(sm->state()->name(), "a");
103     
104     COMPARE(sm->indexOfState(stateA), 0);
105     COMPARE(sm->findStateByName("c"), stateC);
106     
107     sm->changeToState(stateC);
108     COMPARE(sm->state(), stateC);
109     
110     trigger3->_state = true;
111     sm->update(1.0);
112     COMPARE(sm->state()->name(), "a");
113     trigger3->_state = false;
114     
115     trigger1->_state = true;
116     sm->update(1.0);
117     trigger1->_state = false;
118     COMPARE(sm->state()->name(), "b");
119     
120     trigger3->_state = true;
121     sm->update(1.0);
122     COMPARE(sm->state()->name(), "a");
123     trigger3->_state = false;
124     
125     trigger1->_state = true;
126     sm->update(1.0);
127     trigger1->_state = false;
128     COMPARE(sm->state()->name(), "b");
129     
130     trigger2->_state = true;
131     sm->update(1.0);
132     trigger2->_state = false;
133     COMPARE(sm->state()->name(), "c");
134     
135 //////////////////////////////////////////
136     COMPARE(sm->root()->getIntValue("current-index"), 2);
137     COMPARE(sm->root()->getStringValue("current-name"), string("c"));
138     
139     sm->root()->setStringValue("current-name", "b");
140     COMPARE(sm->state()->name(), "b");
141
142 ////////////////////////////////////////
143     COMPARE(sm->findStateByName("foo"), NULL);
144     COMPARE(sm->indexOfState(StateMachine::State_ptr()), -1);
145     
146     COMPARE(sm->stateByIndex(1), stateB);
147     
148     try {
149         sm->stateByIndex(44);
150         VERIFY(false && "should have raised an exception");
151     } catch (sg_exception& e){
152         // expected!
153     }
154 }
155
156 void testNoSourcesTransition()
157 {
158     BUILD_MACHINE_1();
159
160     DummyCondition* trigger4 = new DummyCondition;
161     StateMachine::Transition_ptr t4 = sm->createTransition("alwaysToA", stateA); \
162     t4->setTriggerCondition(trigger4); \
163     sm->init();
164     
165     COMPARE(sm->state()->name(), "a");
166     trigger1->_state = true;
167     sm->update(1.0);
168     trigger1->_state = false;
169     COMPARE(sm->state()->name(), "b");
170     
171     trigger4->_state = true;
172     sm->update(1.0);
173     trigger4->_state = false;
174     COMPARE(sm->state()->name(), "a");
175 }
176
177 void testBindings()
178 {    
179     SGCommandMgr* cmdMgr = SGCommandMgr::instance();
180     DummyThing thing;
181     cmdMgr->addCommand("dummy", &thing, &DummyThing::someCommand);
182     BUILD_MACHINE_1();
183     
184     t2->addBinding(new SGBinding("dummy"));
185     
186     stateA->addEntryBinding(new SGBinding("dummy"));
187     stateA->addExitBinding(new SGBinding("dummy"));
188     stateC->addEntryBinding(new SGBinding("dummy"));
189     
190 ////////////////////////
191     COMPARE(sm->state()->name(), "a");
192     trigger1->_state = true;
193     sm->update(1.0);
194     trigger1->_state = false;
195     COMPARE(sm->state()->name(), "b");
196     COMPARE(thing.dummy_cmd_state, 1); // exit state A
197     
198     trigger2->_state = true;
199     sm->update(1.0);
200     trigger2->_state = false;
201     COMPARE(thing.dummy_cmd_state, 3); // fire transition 2, enter state C
202     
203     thing.dummy_cmd_state = 0;
204     sm->changeToState(stateA);
205     COMPARE(thing.dummy_cmd_state, 1); // enter state A
206     trigger1->_state = true;
207     sm->update(1.0);
208     trigger1->_state = false;
209     COMPARE(thing.dummy_cmd_state, 2); // exit state A
210     
211 ////////////////////////
212     t3->addBinding(new SGBinding("dummy"));
213     t3->addBinding(new SGBinding("dummy"));
214     t3->addBinding(new SGBinding("dummy"));
215     
216     sm->changeToStateName("b");
217     thing.dummy_cmd_state = 0;
218     trigger3->_state = true;
219     sm->update(1.0);
220     trigger3->_state = false;
221     COMPARE(thing.dummy_cmd_state, 4); // three transition bindings, enter A
222 }
223
224 void testParse()
225 {
226     const char* xml = "<?xml version=\"1.0\"?>"
227         "<PropertyList>"
228             "<state>"
229                 "<name>one</name>"
230             "</state>"
231             "<state>"
232                 "<name>two</name>"
233             "</state>"
234         "</PropertyList>";
235     
236     SGPropertyNode* desc = new SGPropertyNode;
237     readProperties(xml, strlen(xml), desc);
238     
239     SGPropertyNode_ptr root(new SGPropertyNode);
240     StateMachine_ptr sm = StateMachine::createFromPlist(desc, root);
241     
242     VERIFY(sm->findStateByName("one") != NULL);
243     VERIFY(sm->findStateByName("two") != NULL);
244 }
245
246 int main(int argc, char* argv[])
247 {
248     testBasic();
249     testBindings();
250     testParse();
251     testNoSourcesTransition();
252     
253     cout << __FILE__ << ": All tests passed" << endl;
254     return EXIT_SUCCESS;
255 }
256