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