$search
00001 /*************************************************************************** 00002 tag: Peter Soetens Mon Jan 10 15:59:51 CET 2005 state_test.cpp 00003 00004 state_test.cpp - description 00005 ------------------- 00006 begin : Mon January 10 2005 00007 copyright : (C) 2005 Peter Soetens 00008 email : peter.soetens@mech.kuleuven.ac.be 00009 00010 *************************************************************************** 00011 * * 00012 * This program is free software; you can redistribute it and/or modify * 00013 * it under the terms of the GNU General Public License as published by * 00014 * the Free Software Foundation; either version 2 of the License, or * 00015 * (at your option) any later version. * 00016 * * 00017 ***************************************************************************/ 00018 00019 #include "unit.hpp" 00020 00021 #include <rtt-config.h> 00022 #include <extras/SimulationThread.hpp> 00023 #include <extras/SimulationActivity.hpp> 00024 #include <scripting/StateMachine.hpp> 00025 #include <scripting/ParsedStateMachine.hpp> 00026 #include <scripting/DumpObject.hpp> 00027 #include <scripting/Parser.hpp> 00028 00029 #include <Service.hpp> 00030 #include <TaskContext.hpp> 00031 #include <OperationCaller.hpp> 00032 #include <Port.hpp> 00033 #include <scripting/ScriptingService.hpp> 00034 #include "operations_fixture.hpp" 00035 00036 #include <string> 00037 #include <iostream> 00038 #include <sstream> 00039 00040 using namespace RTT; 00041 using namespace RTT::detail; 00042 using namespace std; 00043 00044 class StateTest 00045 : public OperationsFixture 00046 { 00047 public: 00048 Parser parser; 00049 InputPort<double> d_event; 00050 InputPort<bool> b_event; 00051 InputPort<int> t_event; 00052 Operation<void(double)> o_event; 00053 OutputPort<double> d_event_source; 00054 OutputPort<bool> b_event_source; 00055 OutputPort<int> t_event_source; 00056 ScriptingService::shared_ptr sa; 00057 00058 void log(const std::string& msg) { 00059 Logger::log(Logger::Info) << msg << endlog(); 00060 } 00061 void doState(const std::string& name, const std::string& prog, TaskContext*, bool test=true ); 00062 void parseState( const std::string& prog, TaskContext*, bool test=true ); 00063 void runState(const std::string& name, TaskContext*, bool test=true ); 00064 void checkState( const std::string& name, TaskContext*, bool test=true ); 00065 void finishState( std::string const& name, TaskContext*, bool test=true ); 00066 00067 std::string sline; 00068 public: 00069 StateTest() 00070 : 00071 d_event("d_event"), b_event("b_event"), t_event("t_event"), o_event("o_event"), 00072 d_event_source("d_event_source"), b_event_source("b_event_source"), t_event_source("t_event_source") 00073 ,sa( ScriptingService::Create(tc) ) 00074 { 00075 tc->stop(); 00076 tc->setActivity( new SimulationActivity(0.001) ); 00077 00078 tc->ports()->addPort( d_event ); 00079 tc->ports()->addPort( b_event ); 00080 tc->ports()->addPort( t_event ); 00081 tc->provides()->addOperation( o_event ); 00082 tc->ports()->addPort( d_event_source ); 00083 tc->ports()->addPort( b_event_source ); 00084 tc->ports()->addPort( t_event_source ); 00085 00086 d_event_source.connectTo( &d_event ); 00087 b_event_source.connectTo( &b_event ); 00088 t_event_source.connectTo( &t_event ); 00089 tc->start(); 00090 i = 0; 00091 SimulationThread::Instance()->stop(); 00092 00093 tc->addOperation("log", &StateTest::log, this); 00094 } 00095 ~StateTest(){ 00096 } 00097 }; 00098 00099 BOOST_FIXTURE_TEST_SUITE( StateTestSuite, StateTest ) 00100 00101 BOOST_AUTO_TEST_CASE( testParseState) 00102 { 00103 // a state which should never fail 00104 string prog = string("StateMachine X {\n") 00105 + " param int isten\n" 00106 + " param bool isflse\n" 00107 + " param bool isok\n" 00108 + " param double isnegative\n" 00109 + " var bool istrrue = true\n" 00110 + " var double d_dummy = -1.0\n" 00111 + " var int i_dummy = -1\n" 00112 + " var bool varinit = false\n" 00113 + " var bool b_dummy = true\n" // 10 00114 + " initial state INIT {\n" 00115 // XXX bug : preconditions are not checked in the initial state. 00116 // + " preconditions {\n" 00117 // + " if (istrrue == false) || (isflse == true) || (isten != 10) ||( isnegative >= 0. ) then select PRE_PARAMFAIL\n" 00118 // + " if false then select PRE_ERROR\n" 00119 // + " if (isnegative != -1.) then select PRE_PARAMFAIL\n" 00120 // + " if (istrrue != true) then select PRE_PARAMFAIL\n" 00121 // + " if (isflse != false) then select PRE_PARAMFAIL\n" 00122 // + " if (isten != 10 ) then select PRE_PARAMFAIL\n" 00123 // + " if (d_dummy != -1.) || (i_dummy != -1) then select PRE_VARFAIL\n" 00124 // + " }\n" 00125 + " entry {\n" 00126 + " set varinit = (d_dummy != -1.) || (i_dummy != -1) \n" 00127 + " do test.good()\n" 00128 + " set d_dummy = 1.234\n" 00129 + " set i_dummy = -2\n" 00130 + " do test.good()\n" 00131 + " }\n" 00132 + " handle {\n" 00133 + " do test.good()\n" // 20 00134 + " }\n" 00135 + " exit {\n" 00136 + " do test.good()\n" 00137 + " set d_dummy = 0.0\n" 00138 + " set i_dummy = 0\n" 00139 + " }\n" 00140 + " transitions {\n" 00141 + " if false then select ERROR\n" 00142 + " if varinit == true then select PRE_VARFAIL\n" 00143 + " if (d_dummy != 1.234) || (i_dummy != -2) then select ENTRYFAIL\n" // 30 00144 + " }\n" 00145 + " transition if (istrrue == false) || (isflse == true) || (isten != 10) ||( isnegative >= 0. ) then select PARAMFAIL\n" 00146 + " transition if isok == false then select PARAMFAIL\n" 00147 + " transition select FINI\n" 00148 + " transition select ERROR\n" // do not reach 00149 + " }\n" 00150 + " state PRE_ERROR { entry { do test.assert(false) }\n" 00151 + " }\n" 00152 + " state PRE_PARAMFAIL { entry { do test.assert(false) }\n" 00153 + " }\n" 00154 + " state PRE_VARFAIL { entry { do test.assert(false) }\n" 00155 + " }\n" 00156 + " state ERROR { entry { do test.assert(false) }\n" 00157 + " }\n" 00158 + " state PARAMFAIL {\n" 00159 + " entry { \n" 00160 + " do test.assertMsg( isten == 10, \"isten parameter not correctly initialised\")\n" 00161 + " do test.assertMsg( istrrue == true, \"istrrue parameter not correctly initialised\")\n" 00162 + " do test.assertMsg( isok == true, \"isok parameter not correctly initialised\")\n" 00163 + " do test.assertMsg( isflse == false, \"isflse parameter not correctly initialised\")\n" 00164 + " do test.assertMsg( true == true, \"true ident not correctly done\")\n" 00165 + " do test.assertMsg( false == false, \"false ident not correctly done\")\n" 00166 + " do test.assertMsg( isnegative == -1.0, \"isnegative parameter not correctly initialised\")\n" 00167 + " }\n" 00168 + " }\n" 00169 + " state VARFAIL { entry { do test.assert(false) }\n" 00170 + " }\n" 00171 + " state EXITFAIL { entry { do test.assert(false) }\n" 00172 + " }\n" 00173 + " state ENTRYFAIL { entry { do test.assert(false) }\n" 00174 + " }\n" 00175 + " final state FINI {\n" 00176 + " preconditions {\n" 00177 + " if (d_dummy != 0.) || (i_dummy != 0) then select EXITFAIL\n" 00178 + " if false then select ERROR\n" 00179 + " }\n" 00180 + " entry {\n" 00181 + " do test.good()\n" 00182 + " set d_dummy = -1.\n" 00183 + " set i_dummy = -1\n" 00184 + " }\n" 00185 + " handle {\n" 00186 + " do test.good()\n" 00187 + " }\n" 00188 + " exit {\n" 00189 + " do test.good()\n" 00190 + " }\n" 00191 + " transitions {\n" 00192 + " if false then select ERROR\n" 00193 + " select INIT\n" 00194 + " select ERROR\n" // do not reach 00195 + " }\n" 00196 + " }\n" 00197 + " }\n" 00198 // + " RootMachine X x( isten = 10, isflse = false, isnegative = -1.0) \n" // instantiate a non hierarchical SC 00199 + " RootMachine X x( isten = 10, isok = true, isflse=false, isnegative = -1.0) \n" // instantiate a non hierarchical SC 00200 ; 00201 00202 this->doState("x", prog, tc ); 00203 this->finishState( "x", tc ); 00204 } 00205 00206 BOOST_AUTO_TEST_CASE( testStateFailure) 00207 { 00208 // test _command_ (through methods though) failure detection on several places. 00209 // it is an incomplete test, even more that parsing should fail on the second 00210 // run since the type 'X' is defined twice. 00211 string prog = string("StateMachine X {\n") 00212 + " initial state INIT {\n" 00213 + " entry {\n" 00214 + " do test.increase()\n" // set i to i+1 00215 + " do test.assert( test.i != 1)\n" // fail if i == 1 00216 + " }\n" 00217 + " run {\n" 00218 + " do test.assert( test.i != 2)\n" 00219 + " }\n" 00220 + " exit {\n" 00221 + " do test.assert( test.i != 3)\n" 00222 + " }\n" 00223 + " transitions {\n" 00224 + " if (true) then { do test.assert( test.i != 7); } select FINI\n" 00225 + " }\n" 00226 + " }\n" 00227 + " state ERROR {\n" 00228 + " }\n" 00229 + " final state FINI {\n" 00230 + " entry {\n" 00231 + " do test.assert( test.i != 4)\n" 00232 + " }\n" 00233 + " run {\n" 00234 + " do test.assert( test.i != 5)\n" 00235 + " }\n" 00236 + " exit {\n" 00237 + " do test.assert( test.i != 6)\n" 00238 + " }\n" 00239 + " transitions {\n" 00240 + " select ERROR\n" 00241 + " }\n" 00242 + " }\n" 00243 + " }\n" 00244 + " RootMachine X x\n" // instantiate a non hierarchical SC 00245 ; 00246 00247 // should fail each time 00248 const int max = 7; 00249 int x = 0; 00250 while ( i < max && x < max) { 00251 this->doState("x", prog, tc, false ); 00252 //cout << "i is: "<< i <<endl; 00253 // assert that an error happened : 00254 BOOST_CHECK_MESSAGE( sa->getStateMachineStatus("x") == StateMachine::Status::error, "Status is: " + sa->getStateMachineStatusStr("x") ); 00255 00256 this->finishState( "x", tc, false); 00257 ++x; 00258 } 00259 } 00260 BOOST_AUTO_TEST_CASE( testStateChildren) 00261 { 00262 // instantiate two children and check init of vars and params 00263 string prog = string("StateMachine Y {\n") 00264 + " param double isnegative\n" 00265 + " var double t = 1.0\n" 00266 + " initial state INIT {\n" 00267 + " entry {\n" 00268 + " do test.good()\n" 00269 + " }\n" 00270 + " transitions {\n" 00271 + " if isnegative >= 0. then select PARAMFAIL\n" 00272 + " select FINI\n" 00273 + " }\n" 00274 + " }\n" 00275 + " state ERROR { entry { do test.assert(false) }\n" 00276 + " }\n" 00277 + " state PARAMFAIL { entry { do test.assert(false) }\n" 00278 + " }\n" 00279 + " state VARFAIL { entry { do test.assert(false) }\n" 00280 + " }\n" 00281 + " state EXITFAIL { entry { do test.assert(false) }\n" 00282 + " }\n" 00283 + " state ENTRYFAIL { entry { do test.assert(false) }\n" 00284 + " }\n" 00285 + " final state FINI {\n" 00286 + " entry {\n" 00287 + " do test.good()\n" 00288 + " }\n" 00289 + " transitions {\n" 00290 + " select INIT\n" 00291 + " }\n" 00292 + " }\n" 00293 + " }\n" 00294 + string("StateMachine Z {\n") 00295 + " param double neg\n" 00296 + " initial state INIT {\n" 00297 + " transitions {\n" 00298 + " if neg >= 0. then select PARAMFAIL\n" 00299 + " select FINI\n" 00300 + " }\n" 00301 + " }\n" 00302 + " state PARAMFAIL { entry { do test.assert(false) }\n" 00303 + " }\n" 00304 + " final state FINI {\n" 00305 + " transitions {\n" 00306 + " select INIT\n" 00307 + " }\n" 00308 + " }\n" 00309 + " }\n" 00310 + string("StateMachine X {\n") 00311 + " param double isnegative\n" 00312 + " var double d_dummy = -2.0\n" 00313 + " var int i_dummy = -1\n" 00314 + " SubMachine Y y1(isnegative = d_dummy)\n" 00315 + " SubMachine Y y2(isnegative = -3.0)\n" 00316 + " SubMachine Y y3(isnegative = isnegative)\n" 00317 + " SubMachine Z z1( neg = d_dummy)\n" 00318 + " initial state INIT {\n" 00319 + " entry {\n" 00320 + " do test.good()\n" 00321 + " do y1.activate()\n" 00322 + " do y2.activate()\n" 00323 + " do y3.activate()\n" 00324 + " do z1.activate()\n" 00325 + " }\n" 00326 + " exit {\n" 00327 + " do y1.start()\n" 00328 + " do y2.start()\n" 00329 + " do y3.start()\n" 00330 + " do z1.start()\n" 00331 + " }\n" 00332 + " transitions {\n" 00333 + " select FINI\n" 00334 + " }\n" 00335 + " }\n" 00336 + " state ERROR { entry { do test.assert(false) }\n" 00337 + " }\n" 00338 + " state PARAMFAIL {\n" 00339 + " entry { \n" 00340 + " do test.assertMsg( y3.isnegative == isnegative, \"y3 parameter not correctly initialised\")\n" 00341 + " do test.assertMsg( y2.isnegative == -3.0, \"y2 parameter not correctly initialised\")\n" 00342 + " do test.assertMsg( y1.isnegative == d_dummy, \"y1 parameter not correctly initialised\")\n" 00343 + " do test.assertMsg( z1.neg == d_dummy, \"z1 parameter not correctly initialised\")\n" 00344 + " }\n" 00345 + " }\n" 00346 + " state VARFAIL { entry { do test.assert(false) }\n" 00347 + " }\n" 00348 + " state EXITFAIL { entry { do test.assert(false) }\n" 00349 + " }\n" 00350 + " state ENTRYFAIL { entry { do test.assert(false) }\n" 00351 + " }\n" 00352 + " final state FINI {\n" 00353 + " entry {\n" 00354 + " do test.good()\n" 00355 + " do y1.stop()\n" 00356 + " do y2.stop()\n" 00357 + " do y3.stop()\n" 00358 + " do z1.stop()\n" 00359 + " }\n" 00360 + " exit {\n" 00361 + " do test.good()\n" 00362 + " do y1.deactivate()\n" 00363 + " do y2.deactivate()\n" 00364 + " do y3.deactivate()\n" 00365 + " do z1.deactivate()\n" 00366 + " }\n" 00367 + " transitions {\n" 00368 + " if z1.neg != d_dummy then select PARAMFAIL\n" 00369 + " if y1.isnegative != d_dummy then select PARAMFAIL\n" 00370 + " if y2.isnegative != -3.0 then select PARAMFAIL\n" 00371 + " if y3.isnegative != isnegative then select PARAMFAIL\n" 00372 + " select INIT\n" 00373 + " }\n" 00374 + " }\n" 00375 + " }\n" 00376 + " RootMachine X x( isnegative = -1.0) \n" // instantiate a hierarchical SC 00377 ; 00378 00379 this->doState("x", prog, tc ); 00380 this->finishState( "x", tc); 00381 } 00382 00383 BOOST_AUTO_TEST_CASE( testStateEmpty) 00384 { 00385 // test processing of completely empty states 00386 string prog = string("StateMachine X {\n") 00387 + " initial state INIT {\n" 00388 + " transitions {\n" 00389 + " select TEST;\n" // only a transition 00390 + " }\n" 00391 + " }\n" 00392 + " state TEST {\n" // not even used 00393 + " }\n" 00394 + " final state FINI {\n" // completely empty 00395 + " }\n" 00396 + " }\n" 00397 + " RootMachine X x\n" // instantiate a non hierarchical SC 00398 ; 00399 this->doState("x", prog, tc ); 00400 this->finishState( "x", tc); 00401 } 00402 00403 BOOST_AUTO_TEST_CASE( testStateEmptyChild) 00404 { 00405 // test processing of completely empty states 00406 string prog = string("StateMachine Y {\n") 00407 + " initial state INIT {\n" 00408 + " }\n" 00409 + " final state FINI {\n" 00410 + " }\n" 00411 + "}\n" 00412 + "StateMachine X {\n" 00413 + " SubMachine Y y;\n" 00414 + " initial state INIT {\n" 00415 + " }\n" 00416 + " final state FINI {\n" 00417 + " }\n" 00418 + "}\n" 00419 + "RootMachine X x\n" 00420 ; 00421 00422 this->doState("x", prog, tc ); 00423 this->finishState( "x", tc); 00424 } 00425 00426 BOOST_AUTO_TEST_CASE( testStateTransitions) 00427 { 00428 // test processing of transition statements. 00429 string prog = string("StateMachine X {\n") 00430 + " initial state INIT {\n" 00431 + " var int i = 0;\n" // transition counter 00432 + " var int j = 0;\n" // entry counter 00433 + " var int k = 0;\n" // run counter 00434 + " entry {\n" 00435 + " set j = j + 1\n" 00436 + " }\n" 00437 + " run {\n" 00438 + " set k = k + 1\n" 00439 + " }\n" 00440 + " transitions {\n" 00441 + " if i < 5 then {\n" 00442 + " set i = i + 1;\n" // execute this program 00443 + " } select INIT\n" // test EXPLICIT transition to self: no entry/exit. 00444 + " if i < 10 then {\n" 00445 + " set i = i + 1;\n" // execute this program 00446 + " }\n" // test IMPLICIT transition to self: no entry/exit. 00447 + " if i < 10 then {\n" 00448 + " } select TRANS_SHOULD_NOT_CHECK\n" // should never be reached. 00449 + " if i == 10 then {\n" 00450 + " set i = i + 1;\n" // execute this program 00451 + " } select TEST_ENTRY\n" 00452 + " }\n" 00453 + " }\n" 00454 + " state TEST_ENTRY {\n" // test if entry was executed one time 00455 + " transitions {\n" 00456 + " if k != i then {\n" 00457 + " } select RUN_FAILED\n" 00458 + " if j != 1 then {\n" 00459 + " } select ENTRY_FAILED\n" 00460 + " else select FINI\n" 00461 + " }\n" 00462 + " }\n" 00463 + " state TRANS_SHOULD_NOT_CHECK {\n" // failure state 00464 + " entry { do test.assert(false); }\n" 00465 + " }\n" 00466 + " state ENTRY_FAILED {\n" // failure state 00467 + " entry { do test.assert(false); }\n" 00468 + " }\n" 00469 + " state RUN_FAILED {\n" // failure state 00470 + " entry { do test.assert(false); }\n" 00471 + " }\n" 00472 + " final state FINI {\n" // Success state. 00473 + " }\n" 00474 + " }\n" 00475 + " RootMachine X x\n" // instantiate a non hierarchical SC 00476 ; 00477 this->doState("x", prog, tc ); 00478 BOOST_CHECK( sa->getStateMachine( "x" )->inState("FINI") ); 00479 this->finishState( "x", tc); 00480 } 00481 00482 BOOST_AUTO_TEST_CASE( testStateTransitionStop ) 00483 { 00484 // test processing of transition statements. 00485 string prog = string("StateMachine X {\n") 00486 + " initial state INIT {\n" 00487 + " transitions {\n" 00488 + " if stop() == true then select NEXT\n" // calls stop on the component ! 00489 + " }\n" 00490 + " }\n" 00491 + " state NEXT {\n" // Success state. 00492 + " entry { do test.assert(true); }\n" 00493 + " }\n" 00494 + " final state FINI {\n" // Failure state. 00495 + " entry { do test.assert(true); }\n" 00496 + " }\n" 00497 + " }\n" 00498 + " RootMachine X x\n" // instantiate a non hierarchical SC 00499 ; 00500 this->doState("x", prog, tc ); 00501 BOOST_CHECK( sa->getStateMachine( "x" )->inState("NEXT") ); 00502 this->finishState( "x", tc); 00503 } 00504 00505 BOOST_AUTO_TEST_CASE( testStateGlobalTransitions) 00506 { 00507 // test processing of transition statements. 00508 string prog = string("StateMachine X {\n") 00509 + " var int gi = 0;\n" // transition counter 00510 + " transitions {\n" 00511 00512 + " if gi < 5 then {\n" 00513 + " set gi = gi + 1;\n" // execute this program 00514 + " } select INIT\n" // test EXPLICIT transition to self: no entry/exit. 00515 00516 + " if gi < 10 then {\n" 00517 + " if gi < 5 then do test.assert(false);\n" 00518 + " set gi = gi + 1;\n" // execute this program 00519 + " }\n" // test IMPLICIT transition to self: no entry/exit. 00520 + " if gi < 10 then {\n" 00521 + " do test.assert(false);\n" 00522 + " } select TRANS_SHOULD_NOT_CHECK\n" // should never be reached. 00523 + " if gi >= 10 then {\n" 00524 + " } select FINI\n" // we must be checked before 00525 + " }\n" 00526 + " initial state INIT {\n" 00527 + " var int i = 0;\n" // transition counter 00528 + " var int j = 0;\n" // entry counter 00529 + " var int k = 0;\n" // run counter 00530 + " entry {\n" 00531 + " set j = j + 1\n" 00532 + " }\n" 00533 + " run {\n" 00534 + " set k = k + 1\n" 00535 + " }\n" 00536 + " transitions {\n" 00537 + " if i < 5 then {\n" 00538 + " set i = i + 1;\n" // execute this program 00539 + " } select INIT\n" // test EXPLICIT transition to self: no entry/exit. 00540 + " if i < 10 then {\n" 00541 + " set i = i + 1;\n" // execute this program 00542 + " }\n" // test IMPLICIT transition to self: no entry/exit. 00543 + " if i < 10 then {\n" 00544 + " } select TRANS_SHOULD_NOT_CHECK\n" // should never be reached. 00545 + " }\n" 00546 + " }\n" 00547 + " state TRANS_SHOULD_NOT_CHECK {\n" // failure state 00548 + " entry { do test.assert(false); }\n" 00549 + " }\n" 00550 + " state ENTRY_FAILED {\n" // failure state 00551 + " entry { do test.assert(false); }\n" 00552 + " }\n" 00553 + " state RUN_FAILED {\n" // failure state 00554 + " entry { do test.assert(false); }\n" 00555 + " }\n" 00556 + " final state FINI {\n" // Success state. 00557 + " }\n" 00558 + " }\n" 00559 + " RootMachine X x\n" // instantiate a non hierarchical SC 00560 ; 00561 this->doState("x", prog, tc ); 00562 BOOST_CHECK( sa->getStateMachine( "x" )->inState("FINI") ); 00563 this->finishState( "x", tc); 00564 } 00565 00566 00567 BOOST_AUTO_TEST_CASE( testStateSubStateVars) 00568 { 00569 // test get/set access of substate variables and parameters 00570 string prog = string("StateMachine Y {\n") 00571 + " param double isnegative\n" 00572 + " var double t = 1.0\n" 00573 + " initial state INIT {\n" 00574 + " transitions {\n" 00575 + " if isnegative >= 0. then select PARAMFAIL\n" 00576 + " if t >= 0. then select VARFAIL\n" 00577 + " select FINI\n" 00578 + " }\n" 00579 + " exit { set isnegative = +1.0 }\n" 00580 + " }\n" // 11 00581 + " state ERROR { entry { do test.assert(false) }\n" 00582 + " }\n" 00583 + " state PARAMFAIL { entry { do test.assert(false) }\n" 00584 + " }\n" 00585 + " state VARFAIL { entry { do test.assert(false) }\n" 00586 + " }\n" 00587 + " state EXITFAIL { entry { do test.assert(false) }\n" 00588 + " }\n" 00589 + " state ENTRYFAIL { entry { do test.assert(false) }\n" 00590 + " }\n" // 21 00591 + " final state FINI {\n" 00592 + " transitions {\n" 00593 + " if isnegative <= 0. then select PARAMFAIL\n" 00594 + " }\n" 00595 + " }\n" 00596 + " }\n" 00597 + string("StateMachine X {\n") 00598 + " param double isnegative\n" 00599 + " var double d_dummy = -2.0\n" 00600 + " var int i_dummy = -1\n" //31 00601 + " SubMachine Y y1(isnegative = d_dummy)\n" 00602 + " initial state INIT {\n" 00603 + " entry {\n" 00604 + " do y1.activate()\n" 00605 + " set y1.t = -1.0 \n" 00606 + " }\n" 00607 + " exit {\n" 00608 + " do y1.start()\n" 00609 + " }\n" 00610 + " transitions {\n" 00611 + " select TEST\n" 00612 + " }\n" 00613 + " }\n" 00614 + " state TEST {\n" 00615 + " entry {\n" 00616 + " do yield\n" 00617 + " do test.assert( y1.inState(\"FINI\") )\n" // if y1 not in FINI, stop here. 00618 + " }\n" 00619 + " transitions {\n" 00620 + " select FINI\n" 00621 + " }\n" 00622 + " }\n" 00623 + " final state FINI {\n" 00624 + " entry {\n" 00625 + " do y1.stop()\n" // prepare y1 to start-over 00626 + " }\n" 00627 + " exit {\n" 00628 + " set y1.isnegative = -1.0 \n" 00629 + " do y1.deactivate()\n" 00630 + " }\n" 00631 + " transitions {\n" 00632 + " select INIT\n" // start-over y1. 00633 + " }\n" 00634 + " }\n" 00635 + " }\n" 00636 + " RootMachine X x( isnegative = -1.0) \n" // instantiate a hierarchical SC 00637 ; 00638 00639 this->doState("x", prog, tc ); 00640 this->finishState( "x", tc); 00641 } 00642 00643 BOOST_AUTO_TEST_CASE( testStateSubStateCommands) 00644 { 00645 // test get/set access of substate variables and parameters 00646 string prog = string("StateMachine Y {\n") 00647 + " param double isnegative\n" 00648 + " var double t = 1.0\n" 00649 + " initial state INIT {\n" 00650 + " transitions {\n" 00651 + " if isnegative < 0. then select ISNEGATIVE\n" 00652 + " if t >= 0. then select ISPOSITIVE\n" 00653 + " select DEFAULT\n" 00654 + " }\n" 00655 + " }\n" 00656 + " state ISNEGATIVE {\n" 00657 + " transitions {\n" 00658 + " select INIT\n" 00659 + " }\n" 00660 + " }\n" 00661 + " state ISPOSITIVE {\n" 00662 + " transitions {\n" 00663 + " select INIT\n" 00664 + " }\n" 00665 // 20 : 00666 + " }\n" 00667 + " state DEFAULT {\n" 00668 + " transitions {\n" 00669 + " select FINI\n" 00670 + " }\n" 00671 + " }\n" 00672 + " final state FINI {\n" 00673 + " }\n" 00674 + " }\n" 00675 + string("StateMachine X {\n") // 1 00676 + " SubMachine Y y1(isnegative = -1.0)\n" 00677 + " initial state INIT {\n" 00678 + " entry {\n" 00679 + " set y1.t = -1.0 \n" 00680 + " do y1.activate()\n" 00681 + " do y1.requestState(\"ISNEGATIVE\")\n" 00682 + " do test.assert( y1.inState(\"ISNEGATIVE\") )\n" 00683 + " do y1.requestState(\"INIT\")\n" 00684 + " do test.assert( y1.inState(\"INIT\") )\n" 00685 + " set y1.isnegative = +1.0 \n" 00686 + " try y1.requestState(\"ISNEGATIVE\") \n " 00687 + " catch \n{\n" 00688 + " do test.assert( y1.inState(\"INIT\") )\n" // do not leave INIT 00689 + " }\n" 00690 + " do test.assert( y1.inState(\"INIT\") )\n" // do not leave INIT 00691 + " do y1.requestState(\"FINI\")\n" // request final state 00692 + " do test.assert( y1.inState(\"FINI\") )\n" 00693 + " }\n" 00694 + " exit {\n" 00695 + " do y1.requestState(\"INIT\")\n" // request initial state 00696 + " do test.assert( y1.inState(\"INIT\") )\n" 00697 + " set y1.isnegative = +1.0 \n" 00698 + " set y1.t = -1.0 \n" 00699 + " do y1.start()\n" // must reach FINI after a while. 00700 + " while ! y1.inState(\"FINI\") \n" 00701 + " do nothing\n" 00702 + " }\n" 00703 + " transitions {\n" 00704 + " select FINI\n" 00705 + " }\n" 00706 + " }\n" 00707 + " final state FINI {\n" 00708 + " entry {\n" 00709 + " do y1.stop()\n" 00710 + " }\n" 00711 + " exit {\n" 00712 + " do y1.deactivate()\n" 00713 + " }\n" 00714 + " transitions {\n" 00715 + " select INIT\n" 00716 + " }\n" 00717 + " }\n" 00718 + " }\n" 00719 + " RootMachine X x() \n" // instantiate a hierarchical SC 00720 ; 00721 00722 this->doState("x", prog, tc ); 00723 this->finishState( "x", tc); 00724 } 00725 00726 #ifdef ORO_SIGNALLING_OPERATIONS 00727 BOOST_AUTO_TEST_CASE( testStateOperationSignalTransition ) 00728 { 00729 // test event reception in sub states. 00730 string prog = string("StateMachine X {\n") 00731 + " var double et = 0.0\n" 00732 + " initial state INIT {\n" 00733 + " transition o_event(et) select FINI\n" // test signal transition 00734 + " }\n" 00735 + " final state FINI {} \n" 00736 + "}\n" 00737 + "RootMachine X x()\n"; 00738 this->parseState( prog, tc ); 00739 StateMachinePtr sm = sa->getStateMachine("x"); 00740 BOOST_REQUIRE( sm ); 00741 this->runState("x", tc); 00742 checkState( "x", tc); 00743 OperationCaller<void(double)> mo( tc->provides()->getOperation("o_event"), tc->engine()); 00744 mo(3.33); 00745 checkState( "x", tc); 00746 BOOST_CHECK( SimulationThread::Instance()->run(100) ); 00747 checkState( "x", tc); 00748 BOOST_CHECK( sm->inState("FINI") ); 00749 this->checkState("x",tc); 00750 this->finishState("x", tc); 00751 } 00752 00753 BOOST_AUTO_TEST_CASE( testStateOperationSignalTransitionProgram ) 00754 { 00755 string prog = string("StateMachine X {\n") 00756 + " var double et = 0.0\n" 00757 + " initial state INIT {\n" 00758 + " transition o_event(et) { test.i = 5; } select FINI\n" // test program 00759 + " }\n" 00760 + " final state FINI {} \n" 00761 + "}\n" 00762 + "RootMachine X x()\n"; 00763 this->parseState( prog, tc ); 00764 StateMachinePtr sm = sa->getStateMachine("x"); 00765 BOOST_REQUIRE( sm ); 00766 //checkState( prog, tc); 00767 this->runState("x", tc); 00768 checkState( "x", tc); 00769 // causes error state when received in INIT: 00770 OperationCaller<void(double)> mo( tc->provides()->getOperation("o_event"), tc->engine()); 00771 mo(3.33); 00772 checkState( "x", tc); 00773 BOOST_CHECK( SimulationThread::Instance()->run(1000) ); 00774 BOOST_CHECK_EQUAL( i, 5 ); 00775 checkState( "x", tc); 00776 BOOST_CHECK( sm->inState("FINI") ); 00777 this->checkState("x",tc); 00778 this->finishState("x", tc); 00779 } 00780 00781 BOOST_AUTO_TEST_CASE( testStateOperationSignalGuard ) 00782 { 00783 string prog = string("StateMachine X {\n") 00784 + " var double et = 0.0\n" 00785 + " initial state INIT {\n" 00786 + " transition o_event(et) if (et == 3.33) then select FINI\n" // test guard 00787 + " }\n" 00788 + " final state FINI {} \n" 00789 + "}\n" 00790 + "RootMachine X x()\n"; 00791 this->parseState( prog, tc ); 00792 StateMachinePtr sm = sa->getStateMachine("x"); 00793 BOOST_REQUIRE( sm ); 00794 //checkState( prog, tc); 00795 this->runState("x", tc); 00796 checkState( "x", tc); 00797 // causes error state when received in INIT: 00798 OperationCaller<void(double)> mo( tc->provides()->getOperation("o_event"), tc->engine()); 00799 mo(3.33); 00800 checkState( "x", tc); 00801 BOOST_CHECK( SimulationThread::Instance()->run(1000) ); 00802 checkState( "x", tc); 00803 BOOST_CHECK( sm->inState("FINI") ); 00804 this->checkState("x",tc); 00805 this->finishState("x", tc); 00806 } 00807 #endif 00808 00809 BOOST_AUTO_TEST_CASE( testStateEvents) 00810 { 00811 // test event reception in sub states. 00812 string prog = string("StateMachine Y {\n") 00813 + " var int t = 0\n" 00814 + " var double et = 0.0\n" 00815 + " var bool eb = false\n" 00816 + " var bool eflag = false\n" 00817 + " transition t_event(t) { do log(\"Global Transition to TESTSELF\");} select TESTSELF\n" // test self transition 00818 + " transition d_event(et)\n" 00819 + " if et < 0. then { do log(\"Global ISNEGATIVE Transition\");} select ISNEGATIVE\n" 00820 + " else { do log(\"Global ISPOSITIVE Transition\");} select ISPOSITIVE\n" // NewData == false !!! 00821 + " initial state INIT {\n" 00822 + " entry { do log(\"INIT\"); set eb = false; }\n" 00823 + " }\n" 00824 + " state ISNEGATIVE {\n" 00825 + " entry { do log(\"ISNEGATIVE\");}\n" 00826 + " transition b_event(eb)\n" 00827 + " if (eb) then { do log(\"Local ISNEGATIVE->INIT Transition\");} select INIT\n" 00828 + " }\n" 00829 + " state ISPOSITIVE {\n" 00830 + " entry { do log(\"ISPOSITIVE\");}\n" 00831 + " transition b_event(eb)\n" 00832 + " if (eb == true) then { do log(\"Local ISPOSITIVE->INIT Transition for b_event\");} select INIT\n" // 20 00833 #ifdef ORO_SIGNALLING_OPERATIONS 00834 + " transition o_event(et)\n" 00835 + " if ( et == 3.0 ) then { do log(\"Local ISPOSITIVE->INIT Transition for o_event\");} select INIT\n" 00836 #endif 00837 + " }\n" 00838 + " state TESTSELF {\n" 00839 + " entry {\n" 00840 + " do log(\"TESTSELF\");\n" 00841 + " set eflag = !eflag\n" 00842 + " }\n" 00843 + " transition t_event(t) { do log(\"Self Transition in TESTSELF\");} select TESTSELF\n" // does not execute entry {}, overrides global t_event() 00844 + " transition b_event(eb)\n" 00845 + " if (eb == true) then { do log(\"Local TESTSELF->INIT Transition\");} select INIT\n" 00846 + " else { log(\"Failed to select INIT upon event.\");}\n" 00847 + " }\n" 00848 + " final state FINI {\n" 00849 + " entry { do log(\"FINI\");}\n" 00850 + " }\n" 00851 + " }\n" // 40 00852 + string("StateMachine X {\n") // 1 00853 + " SubMachine Y y1()\n" 00854 + " initial state INIT {\n" 00855 + " entry {\n" 00856 + " do y1.trace(true)\n" 00857 + " do y1.activate()\n" 00858 + " do y1.start()\n" 00859 + " do yield\n" 00860 + " }" 00861 + " run {\n" 00862 00863 + " do d_event_source.write(-1.0)\n" 00864 + " do nothing\n" 00865 + " do test.assert( !y1.inState(\"INIT\") )\n" 00866 + " do test.assert( !y1.inState(\"ISPOSITIVE\") )\n" 00867 + " do test.assert( y1.inState(\"ISNEGATIVE\") )\n" 00868 + " do b_event_source.write( true )\n" // go to INIT. 00869 + " do yield\n" 00870 + " do test.assert( y1.inState(\"INIT\") )\n" 00871 00872 + " do d_event_source.write(+1.0)\n" 00873 + " do nothing\n" 00874 + " do test.assert( !y1.inState(\"INIT\") )\n" 00875 + " do test.assert( y1.inState(\"ISPOSITIVE\") )\n" 00876 + " do test.assert( !y1.inState(\"ISNEGATIVE\") )\n" 00877 + " if ( !y1.inState(\"ISPOSITIVE\") ) then\n" 00878 + " do test.assertMsg( false, \"Not ISNEGATIVE but \" + y1.getState() )\n" 00879 + " do test.assert( y1.inState(\"ISPOSITIVE\") )\n" 00880 + " do b_event_source.write( true )\n" // go to INIT. 00881 + " do yield\n" 00882 + " do test.assert( y1.inState(\"INIT\") )\n" 00883 #ifdef ORO_SIGNALLING_OPERATIONS 00884 // test operation 00885 + " do d_event_source.write(+1.0)\n" 00886 + " do nothing\n" 00887 + " do test.assert( !y1.inState(\"INIT\") )\n" 00888 + " do test.assert( y1.inState(\"ISPOSITIVE\") )\n" 00889 + " do test.assert( !y1.inState(\"ISNEGATIVE\") )\n" 00890 + " if ( !y1.inState(\"ISPOSITIVE\") ) then\n" 00891 + " do test.assertMsg( false, \"Not ISNEGATIVE but \" + y1.getState() )\n" 00892 + " do test.assert( y1.inState(\"ISPOSITIVE\") )\n" 00893 + " do o_event( 3.0 )\n" // go to INIT. 00894 + " do yield\n" 00895 + " do test.assert( y1.inState(\"INIT\") )\n" 00896 #endif 00897 // test self transitions 00898 + " set y1.eflag = true;\n" 00899 + " do t_event_source.write(1)\n" 00900 + " do nothing\n" 00901 + " do test.assert( !y1.inState(\"INIT\") )\n" 00902 + " do test.assert( !y1.inState(\"ISPOSITIVE\") )\n" 00903 + " do test.assert( !y1.inState(\"ISNEGATIVE\") )\n" 00904 + " do test.assert( y1.inState(\"TESTSELF\") )\n" 00905 + " do test.assert( y1.eflag == false ) /* first */\n" 00906 + " do t_event_source.write(1)\n" 00907 + " do nothing\n" 00908 + " do test.assert( y1.inState(\"TESTSELF\") )\n" 00909 + " do test.assert( y1.eflag == false ) /* second */\n" // no entry 00910 + " do log(\"Trigger b_event.\");\n" 00911 + " do b_event_source.write(true);\n" 00912 + " yield;\n" 00913 + " do test.assert( y1.inState(\"INIT\") ) /* last */\n" 00914 + " }\n" 00915 + " transitions {\n" 00916 + " select FINI\n" 00917 + " }\n" 00918 + " }\n" 00919 + " final state FINI {\n" 00920 + " entry {\n" 00921 + " do y1.deactivate()\n" 00922 //+ " do test.assert(false)\n" 00923 + " }\n" 00924 + " transitions {\n" 00925 + " select INIT\n" 00926 + " }\n" 00927 + " }\n" 00928 + " }\n" 00929 + " RootMachine X x() \n" // instantiate a hierarchical SC 00930 ; 00931 00932 this->doState("x", prog, tc ); 00933 //BOOST_CHECK( tc->engine()->states()->getStateMachine( "x" )->inState("FINI") ); 00934 this->finishState( "x", tc); 00935 } 00936 00937 BOOST_AUTO_TEST_SUITE_END() 00938 00939 void StateTest::doState( const std::string& name, const std::string& prog, TaskContext* tc, bool test ) 00940 { 00941 BOOST_CHECK( tc->engine() ); 00942 00943 parseState( prog, tc, test); 00944 runState(name, tc, test); 00945 checkState(name, tc, test); 00946 } 00947 00948 void StateTest::parseState(const std::string& prog, TaskContext* tc, bool test ) 00949 { 00950 // Alternative way: test ScriptingService as well. 00951 try { 00952 sa->loadStateMachines( prog, std::string("state_test.cpp"), true ); 00953 } 00954 catch( const file_parse_exception& exc ) 00955 { 00956 BOOST_REQUIRE_MESSAGE( !test, exc.what() ); 00957 } 00958 catch( const parse_exception& exc ) 00959 { 00960 BOOST_REQUIRE_MESSAGE( !test, exc.what() ); 00961 } 00962 catch( const program_load_exception& e) 00963 { 00964 BOOST_REQUIRE_MESSAGE( !test, e.what() ); 00965 } 00966 catch( const std::exception& e ) { 00967 BOOST_CHECK_MESSAGE( !test , e.what()); 00968 BOOST_REQUIRE_MESSAGE( !test, "Uncaught Processor load exception" ); 00969 } 00970 } 00971 00972 void StateTest::runState(const std::string& name, TaskContext* tc, bool test ) 00973 { 00974 StateMachinePtr sm = sa->getStateMachine(name); 00975 BOOST_REQUIRE( sm ); 00976 sm->trace(true); 00977 OperationCaller<bool(StateMachine*)> act = tc->provides(name)->getOperation("activate"); 00978 OperationCaller<bool(StateMachine*)> autom = tc->provides(name)->getOperation("automatic"); 00979 BOOST_CHECK( act(sm.get()) ); 00980 BOOST_CHECK( SimulationThread::Instance()->run(1) ); 00981 BOOST_CHECK_MESSAGE( sm->isActive(), "Error : Activate Command for '"+sm->getName()+"' did not have effect." ); 00982 BOOST_CHECK( autom(sm.get()) || !test ); 00983 00984 BOOST_CHECK( SimulationThread::Instance()->run(1000) ); 00985 } 00986 00987 void StateTest::checkState(const std::string& name, TaskContext* tc, bool test ) 00988 { 00989 StateMachinePtr sm = sa->getStateMachine(name); 00990 BOOST_REQUIRE( sm ); 00991 if ( test ) { 00992 // check error status of parent : 00993 BOOST_CHECK_MESSAGE( sm->isActive(), "Error : State Context '"+sm->getName()+"' did not get activated." ); 00994 stringstream errormsg; 00995 int line = sm->getLineNumber(); 00996 errormsg <<" in StateMachine "+sm->getName() 00997 <<" in state "<< (sm->currentState() ? sm->currentState()->getName() : "(null)") 00998 <<" on line " << line <<" of that StateMachine:"<<endl; 00999 { 01000 stringstream sctext( sm->getText() ); 01001 int cnt = 1; 01002 while ( cnt++ <line && sctext ) { 01003 string garbage; 01004 getline( sctext, garbage, '\n' ); 01005 } 01006 getline( sctext, sline, '\n' ); 01007 } 01008 errormsg <<"here > " << sline << endl; 01009 if ( sm->inError() ) { 01010 RTT::scripting::DumpObject( tc->provides() ); 01011 RTT::scripting::DumpObject( tc->provides(name) ); 01012 } 01013 BOOST_CHECK_MESSAGE( sm->inError() == false, "Runtime error (inError() == true) encountered" + errormsg.str() ); 01014 // check error status of all children: 01015 StateMachine::ChildList cl = sm->getChildren(); 01016 StateMachine::ChildList::iterator clit = cl.begin(); 01017 while( clit != cl.end() ) { 01018 line = (*clit)->getLineNumber(); 01019 { 01020 stringstream sctext( (*clit)->getText() ); 01021 int cnt = 1; 01022 while ( cnt++ <line && sctext ) { 01023 string garbage; 01024 getline( sctext, garbage, '\n' ); 01025 } 01026 getline( sctext, sline, '\n' ); 01027 } 01028 stringstream cerrormsg; 01029 if ( (*clit)->currentState() ) 01030 cerrormsg <<" in child "<< (*clit)->getName() <<" in state "<<(*clit)->currentState()->getName()<< " on line " << (*clit)->getLineNumber() <<" of that StateMachine."<<endl <<"here > " << sline << endl; 01031 else 01032 cerrormsg <<" child "<< (*clit)->getName() << " (deactivated) on line " << (*clit)->getLineNumber() <<" of that StateMachine."<<endl<<"here > " << sline << endl; 01033 01034 BOOST_CHECK_MESSAGE( (*clit)->inError() == false, "Runtime error (inError() == true) encountered" + cerrormsg.str() ); 01035 if ( (*clit)->inError() == false && sm->inError() == true) { 01036 cout << "Child Status:" << cerrormsg.str() << endl; 01037 } 01038 ++clit; 01039 } 01040 } 01041 } 01042 01043 void StateTest::finishState(std::string const& name, TaskContext* tc, bool test) 01044 { 01045 StateMachinePtr sm = sa->getStateMachine(name); 01046 BOOST_REQUIRE( sm ); 01047 BOOST_CHECK( sa->getStateMachine( name )->stop() ); 01048 BOOST_CHECK( SimulationThread::Instance()->run(500) ); 01049 if (test) { 01050 stringstream errormsg; 01051 errormsg << " on line " << sm->getLineNumber() <<", status is "<< sa->getStateMachineStatusStr(name) <<endl <<"here > " << sline << endl;; 01052 BOOST_CHECK_MESSAGE( sm->isStopped(), "StateMachine stalled " + errormsg.str() ); 01053 } 01054 // you can call deactivate even when the proc is not running. 01055 // but deactivation may be 'in progress if exit state has commands in it. 01056 BOOST_CHECK( sa->getStateMachine( name )->deactivate() ); 01057 BOOST_CHECK( SimulationThread::Instance()->run(200) ); 01058 if ( sm->isActive() ) 01059 BOOST_CHECK( sa->getStateMachine( name )->deactivate() ); 01060 BOOST_CHECK( SimulationThread::Instance()->run(200) ); 01061 BOOST_CHECK( sa->getStateMachine( name )->isActive() == false ); 01062 01063 // only stop now, since deactivate won't work if simtask not running. 01064 tc->stop(); 01065 01066 try { 01067 BOOST_CHECK( sa->unloadStateMachine( name ) ); 01068 } 01069 catch( const program_unload_exception& e) 01070 { 01071 BOOST_REQUIRE_MESSAGE( false, e.what() ); 01072 } 01073 catch( ... ) { 01074 BOOST_REQUIRE_MESSAGE( false, "Uncaught Processor unload exception" ); 01075 } 01076 01077 } 01078