]> git.mxchange.org Git - simgear.git/blob - simgear/structure/state_machine_test.cxx
StateMachine tweaks.
[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 static int dummy_cmd_state = 0;
47
48 bool dummyCommand(const SGPropertyNode* arg)
49 {
50     ++dummy_cmd_state;
51     return true;
52 }
53
54 #define COMPARE(a, b) \
55     if ((a) != (b))  { \
56         cerr << "failed:" << #a << " != " << #b << endl; \
57         cerr << "\tgot:'" << a << "'" << endl; \
58         exit(1); \
59     }
60
61 #define VERIFY(a) \
62     if (!(a))  { \
63         cerr << "failed:" << #a << endl; \
64         exit(1); \
65     }
66     
67 using namespace simgear;
68
69 #define BUILD_MACHINE_1() \
70     StateMachine_ptr sm(new StateMachine); \
71     StateMachine::State_ptr stateA = sm->createState("a"); \
72     StateMachine::State_ptr stateB = sm->createState("b"); \
73     StateMachine::State_ptr stateC = sm->createState("c"); \
74     \
75     DummyCondition* trigger1 = new DummyCondition; \
76     StateMachine::Transition_ptr t1 = sm->createTransition(">b", stateB); \
77     t1->addSourceState(stateA); \
78     t1->setTriggerCondition(trigger1); \
79     \
80     DummyCondition* trigger2 = new DummyCondition; \
81     StateMachine::Transition_ptr t2 = sm->createTransition(">c", stateC); \
82     t2->addSourceState(stateB); \
83     t2->setTriggerCondition(trigger2); \
84     \
85     DummyCondition* trigger3 = new DummyCondition; \
86     StateMachine::Transition_ptr t3 = sm->createTransition(">a", stateA); \
87     t3->addSourceState(stateC); \
88     t3->addSourceState(stateB); \
89     t3->setTriggerCondition(trigger3); \
90     sm->init();
91
92 void testBasic()
93 {
94     BUILD_MACHINE_1();
95 ////////////////////////////////////////////
96     COMPARE(sm->state()->name(), "a");
97     
98     COMPARE(sm->indexOfState(stateA), 0);
99     COMPARE(sm->findStateByName("c"), stateC);
100     
101     sm->changeToState(stateC);
102     COMPARE(sm->state(), stateC);
103     
104     trigger3->_state = true;
105     sm->update(1.0);
106     COMPARE(sm->state()->name(), "a");
107     trigger3->_state = false;
108     
109     trigger1->_state = true;
110     sm->update(1.0);
111     trigger1->_state = false;
112     COMPARE(sm->state()->name(), "b");
113     
114     trigger3->_state = true;
115     sm->update(1.0);
116     COMPARE(sm->state()->name(), "a");
117     trigger3->_state = false;
118     
119     trigger1->_state = true;
120     sm->update(1.0);
121     trigger1->_state = false;
122     COMPARE(sm->state()->name(), "b");
123     
124     trigger2->_state = true;
125     sm->update(1.0);
126     trigger2->_state = false;
127     COMPARE(sm->state()->name(), "c");
128     
129 //////////////////////////////////////////
130     COMPARE(sm->root()->getIntValue("current-index"), 2);
131     COMPARE(sm->root()->getStringValue("current-name"), string("c"));
132     
133     sm->root()->setStringValue("current-name", "b");
134     COMPARE(sm->state()->name(), "b");
135
136 ////////////////////////////////////////
137     COMPARE(sm->findStateByName("foo"), NULL);
138     COMPARE(sm->indexOfState(StateMachine::State_ptr()), -1);
139     
140     COMPARE(sm->stateByIndex(1), stateB);
141     
142     try {
143         sm->stateByIndex(44);
144         VERIFY(false && "should have raised an exception");
145     } catch (sg_exception& e){
146         // expected!
147     }
148 }
149
150 void testNoSourcesTransition()
151 {
152     BUILD_MACHINE_1();
153
154     DummyCondition* trigger4 = new DummyCondition;
155     StateMachine::Transition_ptr t4 = sm->createTransition("alwaysToA", stateA); \
156     t4->setTriggerCondition(trigger4); \
157     sm->init();
158     
159     COMPARE(sm->state()->name(), "a");
160     trigger1->_state = true;
161     sm->update(1.0);
162     trigger1->_state = false;
163     COMPARE(sm->state()->name(), "b");
164     
165     trigger4->_state = true;
166     sm->update(1.0);
167     trigger4->_state = false;
168     COMPARE(sm->state()->name(), "a");
169 }
170
171 void testBindings()
172 {    
173     SGCommandMgr* cmdMgr = SGCommandMgr::instance();
174     cmdMgr->addCommand("dummy", dummyCommand);
175     BUILD_MACHINE_1();
176     
177     t2->addBinding(new SGBinding("dummy"));
178     
179     stateA->addEntryBinding(new SGBinding("dummy"));
180     stateA->addExitBinding(new SGBinding("dummy"));
181     stateC->addEntryBinding(new SGBinding("dummy"));
182     
183 ////////////////////////
184     COMPARE(sm->state()->name(), "a");
185     trigger1->_state = true;
186     sm->update(1.0);
187     trigger1->_state = false;
188     COMPARE(sm->state()->name(), "b");
189     COMPARE(dummy_cmd_state, 1); // exit state A
190     
191     trigger2->_state = true;
192     sm->update(1.0);
193     trigger2->_state = false;
194     COMPARE(dummy_cmd_state, 3); // fire transition 2, enter state C
195     
196     dummy_cmd_state = 0;
197     sm->changeToState(stateA);
198     COMPARE(dummy_cmd_state, 1); // enter state A
199     trigger1->_state = true;
200     sm->update(1.0);
201     trigger1->_state = false;
202     COMPARE(dummy_cmd_state, 2); // exit state A
203     
204 ////////////////////////
205     t3->addBinding(new SGBinding("dummy"));
206     t3->addBinding(new SGBinding("dummy"));
207     t3->addBinding(new SGBinding("dummy"));
208     
209     sm->changeToStateName("b");
210     dummy_cmd_state = 0;
211     trigger3->_state = true;
212     sm->update(1.0);
213     trigger3->_state = false;
214     COMPARE(dummy_cmd_state, 4); // three transition bindings, enter A
215 }
216
217 void testParse()
218 {
219     const char* xml = "<?xml version=\"1.0\"?>"
220         "<PropertyList>"
221             "<state>"
222                 "<name>one</name>"
223             "</state>"
224             "<state>"
225                 "<name>two</name>"
226             "</state>"
227         "</PropertyList>";
228     
229     SGPropertyNode* desc = new SGPropertyNode;
230     readProperties(xml, strlen(xml), desc);
231     
232     SGPropertyNode_ptr root(new SGPropertyNode);
233     StateMachine_ptr sm = StateMachine::createFromPlist(desc, root);
234     
235     VERIFY(sm->findStateByName("one") != NULL);
236     VERIFY(sm->findStateByName("two") != NULL);
237 }
238
239 int main(int argc, char* argv[])
240 {
241     testBasic();
242     testBindings();
243     testParse();
244     testNoSourcesTransition();
245     
246     cout << __FILE__ << ": All tests passed" << endl;
247     return EXIT_SUCCESS;
248 }
249