Improvements from anna fork
[anna.git] / source / testing / TestStep.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 // Standard
10 #include <string>
11 #include <iostream>
12 #include <errno.h>
13
14 #include <signal.h>     // sigaction, sigemptyset, struct sigaction, SIGCHLD, SA_RESTART, SA_NOCLDSTOP
15 #include <stdio.h>      // perror
16 #include <stdlib.h>     // exit
17 #include <sys/wait.h>   // waitpid, pid_t, WNOHANG
18 #include <climits>
19
20 // cmd with fork:
21 #include <sys/types.h>
22 #include <unistd.h>
23
24
25 // Project
26 #include <anna/testing/TestStep.hpp>
27
28 #include <anna/testing/defines.hpp>
29 #include <anna/diameter/codec/Message.hpp>
30 #include <anna/diameter.comm/OriginHost.hpp>
31 #include <anna/testing/TestCase.hpp>
32 #include <anna/testing/TestManager.hpp>
33 #include <anna/testing/TestTimer.hpp>
34 #include <anna/xml/Compiler.hpp>
35 #include <anna/core/util/Millisecond.hpp>
36 #include <anna/diameter.comm/Message.hpp>
37 #include <anna/diameter.comm/Entity.hpp>
38 #include <anna/diameter.comm/LocalServer.hpp>
39 #include <anna/diameter.comm/ClientSession.hpp>
40 #include <anna/diameter.comm/ServerSession.hpp>
41 #include <anna/diameter.comm/Server.hpp>
42 #include <anna/core/tracing/Logger.hpp>
43 #include <anna/diameter/codec/functions.hpp>
44 #include <anna/diameter/helpers/base/functions.hpp>
45
46
47
48 using namespace anna::testing;
49
50
51 namespace {
52
53   void handle_sigchld(int sig) {
54     while (waitpid((pid_t)(-1 /* any child (the only) */), 0, WNOHANG|WNOWAIT) > 0) {}
55   }
56
57   void cmdRunOnThread (TestStepCmd *step, const std::string &cmd) {
58
59     // Thread running:
60     step->setThreadRunning(true);
61
62     int status = -2;
63
64     struct sigaction sa;
65     sa.sa_handler = &handle_sigchld;
66     sigemptyset(&sa.sa_mask);
67     sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
68     if (sigaction(SIGCHLD, &sa, 0) != -1) {
69
70       status = system(cmd.c_str());
71       /* POPEN version:
72           char readbuf[256];
73           FILE *fp = popen(cmd.c_str(), "r");
74           if (fp) {
75             while(fgets(readbuf, sizeof(readbuf), fp))
76               step->appendOutput("\n");
77               step->appendOutput(readbuf);
78               status = pclose(fp);
79           }
80           else {
81             status = -1;
82           }
83        */
84     }
85     else {
86       perror(0);
87     }
88     // This can be implemented portably and somewhat more concisely with the signal function if you prefer:
89     // if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
90     //   perror(0);
91     //   exit(1);
92     // }
93
94     if (status < 0) {
95       char buf[256];
96       char const * str = strerror_r(errno, buf, 256);
97       step->setErrorMsg(anna::functions::asString("errno = %d (%s)", errno, str));
98     }
99
100     step->setResultCode(WEXITSTATUS(status)); // rc = status >>= 8; // divide by 256
101     step->complete();
102     // TODO: terminate thread when deprecated (RT signal ?)
103     // TODO: mutex the step while setting data here !!
104   }
105
106 /*
107   void cmdRunOnThreadWithFork (TestStepCmd *step, const std::string &cmd) {
108
109     // Thread running:
110     step->setThreadRunning(true);
111
112     pid_t cpid, w;
113     int status = -2;
114
115     if ((cpid = fork()) < 0) {
116       step->setErrorMsg("Error in fork()");
117     }
118     else if (cpid == 0) {
119       // child
120       status = system(cmd.c_str());
121       _exit(WEXITSTATUS(status));
122     }
123     else {
124       // parent
125       step->setChildPid(cpid);
126       do {
127         w = waitpid(cpid, &status, WUNTRACED | WCONTINUED);
128         if (w != -1) {
129
130           if (WIFEXITED(status)) {
131             step->setResultCode(WEXITSTATUS(status)); // rc = status >>= 8; // divide by 256
132             break;
133           }
134           else if (WIFSIGNALED(status)) {
135             step->setErrorMsg(anna::functions::asString("killed by signal %d", WTERMSIG(status)));
136             step->setResultCode(128 + WTERMSIG(status));
137             break;
138           } else if (WIFSTOPPED(status)) {
139             step->setErrorMsg(anna::functions::asString("stopped by signal %d", WSTOPSIG(status)));
140           } else if (WIFCONTINUED(status)) {
141             step->setErrorMsg("continued");
142           }
143         }
144         else {
145           step->setErrorMsg("waitpid error");
146           step->setResultCode(-1);
147           break;
148         }
149       } while (!WIFEXITED(status) && !WIFSIGNALED(status));
150
151       step->complete();
152     }
153   }
154 */
155
156   bool decodeMessage(const anna::DataBlock &message, anna::diameter::codec::Message &messageCodec) throw() {
157
158     if (message.isEmpty())
159       return false;
160
161     bool result = true;
162     try {
163       messageCodec.clear();
164       messageCodec.decode(message);
165     }
166     catch (anna::RuntimeException &ex) {
167       ex.trace();
168       result = false;
169     }
170
171     return result;
172   }
173 }
174
175
176 ////////////////////////////////////////////////////////////////////////////////////////////////////////
177 // TestStep
178 ////////////////////////////////////////////////////////////////////////////////////////////////////////
179 void TestStep::initialize(TestCase *testCase) {
180   a_testCase = testCase;
181   a_completed = false;
182   a_type = Type::Unconfigured;
183   a_beginTimestamp = 0;
184   a_endTimestamp = 0;
185   a_number = testCase->steps() + 1; // testCase is not NULL
186 }
187
188 bool TestStep::decodeMessage(bool trust) throw() {
189   if (a_messageCodec) return true;
190   a_messageCodec = new anna::diameter::codec::Message;
191   if (::decodeMessage(a_message, *a_messageCodec)) return true;
192   if (trust) {
193     LOGDEBUG(anna::Logger::debug("Error DECODING, but trusting it ...", ANNA_FILE_LOCATION));
194     return true;
195   }
196
197   delete a_messageCodec;
198   a_messageCodec = NULL;
199   return false;
200 }
201
202 const char* TestStep::asText(const Type::_v type)
203 throw() {
204   static const char* text [] = { "Unconfigured", "Timeout", "SendDiameterXmlToEntity", "SendDiameterXmlToClient", "Delay", "Wait", "Command", "IpLimit" };
205   return text [type];
206 }
207
208 anna::xml::Node* TestStep::asXML(anna::xml::Node* parent)
209 throw() {
210   anna::xml::Node* result = parent->createChild("TestStep");
211
212   result->createAttribute("Number", a_number);
213   result->createAttribute("Type", asText(a_type));
214   result->createAttribute("Completed", (a_completed ? "yes":"no"));
215
216   // Begin
217   std::string s_aux = a_beginTimestamp.asString();
218   //  int deltaMs = (int)(a_beginTimestamp - a_testCase->getStartTimestamp());
219   //  if (a_beginTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0);
220   result->createAttribute("BeginTimestamp", s_aux);
221
222   // End
223   s_aux = a_endTimestamp.asString();
224   //  deltaMs = (int)(a_endTimestamp - a_testCase->getStartTimestamp());
225   //  if (a_endTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0);
226   result->createAttribute("EndTimestamp", s_aux);
227
228   if (a_beginTimestamp != 0 && a_endTimestamp != 0)
229     result->createAttribute("LapseMilliseconds", getLapseMs());
230
231   return result;
232 }
233
234 std::string TestStep::asXMLString() throw() {
235   anna::xml::Node root("root");
236   return anna::xml::Compiler().apply(asXML(&root));
237 }
238
239 bool TestStep::execute() throw() {
240
241   int ia = a_testCase->interactiveAmount();
242   if (ia > -1) {
243     if (ia == 0) return false;
244     a_testCase->interactiveExecution();
245     LOGDEBUG(anna::Logger::debug("Interactive execution ...", ANNA_FILE_LOCATION));
246     if (a_executed) return false; // avoid repeating (this implies amount consumption)
247   }
248
249   const char *literal = "Execute %s Test Step %d/%d (test case %llu)";
250
251   TestManager& testManager (TestManager::instantiate ());
252   if (testManager.getDumpStdout()) {
253     bool dump = (a_type == Type::Wait) ? (!a_completed) : true;
254     if (dump) {
255     std::cout << std::endl << "=> " << anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()) << std::endl;
256     }
257   }
258
259   LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()), ANNA_FILE_LOCATION));
260   setBeginTimestamp(anna::functions::millisecond());
261   a_executed = true;
262   return do_execute();
263 }
264
265 void TestStep::complete() throw() {
266
267   const char *literal = "Complete %s Test Step %d/%d (test case %llu)";
268
269   TestManager& testManager (TestManager::instantiate ());
270   if (testManager.getDumpStdout()) {
271     std::cout << "=> " << anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()) << std::endl;
272   }
273
274   LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()), ANNA_FILE_LOCATION));
275   a_completed = true;
276   setEndTimestamp(anna::functions::millisecond());
277   do_complete();
278 }
279
280 void TestStep::reset() throw() {
281
282   const char *literal = "Reset %s Test Step %d/%d (test case %llu)";
283
284   TestManager& testManager (TestManager::instantiate ());
285   if (testManager.getDumpStdout()) {
286     std::cout << "=> " << anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()) << std::endl;
287   }
288
289   LOGDEBUG(anna::Logger::debug(anna::functions::asString(literal, asText(a_type), a_number, a_testCase->steps(), a_testCase->getId()), ANNA_FILE_LOCATION));
290   // type and testCase kept
291   a_completed = false;
292   a_executed = false;
293   a_beginTimestamp = 0;
294   a_endTimestamp = 0;
295   do_reset();
296 }
297
298 void TestStep::next() throw() {
299   a_testCase->nextStep();
300   a_testCase->process();
301 }
302
303
304 ////////////////////////////////////////////////////////////////////////////////////////////////////////
305 // TestStepTimeout
306 ////////////////////////////////////////////////////////////////////////////////////////////////////////
307 anna::xml::Node* TestStepTimeout::asXML(anna::xml::Node* parent)
308 throw() {
309   anna::xml::Node* result = TestStep::asXML(parent); // end timestamp will be 0 if test finished OK
310   //parent->createChild("TestStepTimeout");
311   result->createAttribute("Timeout", a_timeout.asString());
312
313   return result;
314 }
315
316 bool TestStepTimeout::do_execute() throw() {
317   try {
318     a_timer = TestManager::instantiate().createTimer((TestCaseStep*)this, a_timeout, TestTimer::Type::TimeLeft);
319   }
320   catch (anna::RuntimeException &ex) {
321     ex.trace();
322     a_testCase->addDebugSummaryHint(ex.asString()); // before report (when set Failed state)
323     a_testCase->setState(TestCase::State::Failed);
324   }
325
326   return true; // go next
327 }
328
329 void TestStepTimeout::do_complete() throw() {
330   int stepNumber = getNumber();
331   if (stepNumber == a_testCase->steps()) {
332     a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) but it was the last test case step", stepNumber)); // before report (when set Failed state)
333     a_testCase->setState(TestCase::State::Success);
334     if (TestManager::instantiate().getDumpStdout()) {
335       std::cout << "Expired Timeout Test LAST Step: considered SUCCESSFUL TEST CASE" << std::endl;
336     }
337   }
338   else if (a_testCase->getState() == TestCase::State::InProgress) { // sure
339     a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) before test case finished", stepNumber)); // before report (when set Failed state)
340     a_testCase->setState(TestCase::State::Failed);
341     if (TestManager::instantiate().getDumpStdout()) {
342       std::cout << "Expired Timeout Test Step: FAILED TEST CASE" << std::endl;
343     }
344   }
345
346   a_timer = NULL;
347 }
348
349 void TestStepTimeout::cancelTimer() throw() {
350   if (!a_timer) return;
351   try {
352     TestManager::instantiate().cancelTimer(a_timer);
353   }
354   catch (anna::RuntimeException &ex) {
355     ex.trace();
356   }
357   a_timer = NULL;
358   //a_timeout = 0; THIS IS CONFIGURATION INFO
359 }
360
361 void TestStepTimeout::do_reset() throw() {
362   cancelTimer();
363 }
364
365 ////////////////////////////////////////////////////////////////////////////////////////////////////////
366 // TestStepSendDiameterXml
367 ////////////////////////////////////////////////////////////////////////////////////////////////////////
368 anna::xml::Node* TestStepSendDiameterXml::asXML(anna::xml::Node* parent)
369 throw() {
370   anna::xml::Node* result = TestStep::asXML(parent);
371   //parent->createChild("TestStepSendDiameterXml");
372   std::string msg = "", xmlmsg = "";
373
374   // Message
375   if (TestManager::instantiate().getDumpHex()) {
376     if (a_message.isEmpty()) {
377       msg = "<empty>";
378     }
379     else {
380       msg = "\n"; msg += a_message.asString(); msg += "\n";
381     }
382   }
383
384   if (decodeMessage()) {
385     xmlmsg = "\n";
386     xmlmsg += a_messageCodec->asXMLString();
387     xmlmsg += "\n";
388   }
389   else {
390     xmlmsg = "<unable to decode, check traces>";
391   }
392
393   if (msg != "") result->createAttribute("Message", msg);
394   if (xmlmsg != "") result->createAttribute("XMLMessage", xmlmsg);
395   result->createAttribute("Expired", (a_expired ? "yes":"no"));
396   if (a_waitForRequestStepNumber != -1)
397     result->createAttribute("WaitForRequestStepNumber", a_waitForRequestStepNumber);
398
399   return result;
400 }
401
402 bool TestStepSendDiameterXml::do_execute() throw() {
403   bool success = false;
404   std::string failReason = "Error sending diameter message";
405   anna::diameter::comm::Entity *entity = a_originHost->getEntity(); // by default
406   anna::diameter::comm::LocalServer *localServer = a_originHost->getDiameterServer(); // by default
407   const TestStepWaitDiameter *tsw = NULL;
408   anna::diameter::comm::Message *msg = a_originHost->createCommMessage();
409
410   try {
411
412     if (a_waitForRequestStepNumber != -1) {
413       bool thisIsAnswer = anna::diameter::codec::functions::isAnswer(getMsgDataBlock());
414       LOGDEBUG(
415          std::string trace = anna::functions::asString("'Wait For Request' step number for this %s: %d", (thisIsAnswer ? "answer":"request"), a_waitForRequestStepNumber);
416          anna::Logger::debug(trace, ANNA_FILE_LOCATION);
417       );
418
419       // Referenced request in the 'wait for request step':
420       tsw = static_cast<const TestStepWaitDiameter*>(a_testCase->getStep(a_waitForRequestStepNumber));
421       const anna::DataBlock &referenceRequest = tsw->getMsgDataBlock();
422       std::string sessionIdReferenceRequest = anna::diameter::helpers::base::functions::getSessionId(referenceRequest);
423
424       if (thisIsAnswer) { // is an answer: try to copy sequence information; alert about Session-Id discrepance
425         anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(referenceRequest);
426         anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(referenceRequest);
427
428         // Update sequence:
429         anna::diameter::codec::functions::setHopByHop(a_message, hbh);
430         anna::diameter::codec::functions::setEndToEnd(a_message, ete);
431       }
432
433       // Session-Id substitution:
434       std::string thisSessionId = anna::diameter::helpers::base::functions::getSessionId(getMsgDataBlock());
435       if (thisSessionId != sessionIdReferenceRequest) {
436         static anna::diameter::codec::Message codecMsg;
437         codecMsg.decode(getMsgDataBlock());
438         codecMsg.getAvp("Session-Id")->getUTF8String()->setValue(sessionIdReferenceRequest);
439         a_message = codecMsg.code();
440         std::string trace = anna::functions::asString("Replacing %s Session-Id (%s) to set the corresponding request one (%s)", (thisIsAnswer ? "answer":"request"), thisSessionId.c_str(), sessionIdReferenceRequest.c_str());
441         LOGDEBUG(anna::Logger::debug(trace, ANNA_FILE_LOCATION));
442         a_testCase->addDebugSummaryHint(trace);
443       }
444     }
445
446     // Create comm message:
447     //msg->clearBody();
448     msg->setBody(a_message);
449
450
451     if (getType() == Type::Sendxml2e) {
452       anna::diameter::comm::ClientSession *usedClientSession = NULL;
453
454       if (tsw) { // is an answer for a received request on wait condition
455         anna::diameter::comm::ClientSession *clientSession = tsw->getClientSession();
456         if (clientSession) {
457           /* response NULL (is an answer) */clientSession->send(msg);
458           success = true;
459           usedClientSession = clientSession;
460         }
461         else {
462           failReason = "Reference wait step didn't store a valid client session. Unable to send the message";
463           LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
464         }
465       }
466       else {
467         if (entity) {
468           success = entity->send(msg);
469           anna::diameter::comm::Server *usedServer = entity->getLastUsedResource();
470           usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
471         }
472         else {
473           failReason = "There is no diameter entity currently configured. Unable to send the message";
474           LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
475         }
476       } // else (normal sending)
477
478       // Detailed log:
479       if(a_originHost->logEnabled()) {
480         if (decodeMessage(true /* trust */)) {
481           std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // shouldn't happen
482           a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2e" : "send2eError"), detail);
483         }
484       }
485     }
486     else if (getType() == Type::Sendxml2c) {
487       anna::diameter::comm::ServerSession *usedServerSession = NULL;
488
489       if (tsw) { // is an answer for a received request on wait condition
490         anna::diameter::comm::ServerSession *serverSession = tsw->getServerSession();
491         if (serverSession) {
492           /* response NULL (is an answer) */serverSession->send(msg);
493           success = true;
494           usedServerSession = serverSession;
495         }
496         else {
497           failReason = "Reference wait step didn't store a valid server session. Unable to send the message";
498           LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
499         }
500       }
501       else {
502         if (localServer) {
503           success = localServer->send(msg);
504           usedServerSession = localServer->getLastUsedResource();
505         }
506         else {
507           failReason = "There is no diameter local server currently configured. Unable to send the message";
508           LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION));
509         }
510       } // else (normal sending)
511
512       // Detailed log:
513       if(a_originHost->logEnabled()) {
514         if (decodeMessage(true /* trust */)) {
515           std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // shouldn't happen
516           a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2c" : "send2cError"), detail);
517         }
518       }
519     }
520
521   } catch(anna::RuntimeException &ex) {
522     failReason = ex.asString();
523   }
524
525   // release msg
526   a_originHost->releaseCommMessage(msg);
527
528   if (!success) {
529     a_testCase->addDebugSummaryHint(failReason); // before report (when set Failed state);
530     a_testCase->setState(TestCase::State::Failed);
531   }
532   else {
533     complete();
534   }
535
536   if (TestManager::instantiate().getDumpStdout()) {
537     std::cout << "Executed SendDiameterXml Test Step: " << (success ? "OK":"ERROR") << std::endl;
538   }
539
540   return success; // go next if sent was OK
541 }
542
543 void TestStepSendDiameterXml::do_reset() throw() {
544   a_expired = false;
545   //a_message.clear();
546   //a_messageAlreadyDecoded = false;
547 }
548
549 ////////////////////////////////////////////////////////////////////////////////////////////////////////
550 // TestStepDelay
551 ////////////////////////////////////////////////////////////////////////////////////////////////////////
552 anna::xml::Node* TestStepDelay::asXML(anna::xml::Node* parent)
553 throw() {
554   anna::xml::Node* result = TestStep::asXML(parent);
555   //parent->createChild("TestStepDelay");
556
557   result->createAttribute("Delay", ((a_delay == 0) ? "dummy step, no delay" : a_delay.asString()));
558
559   return result;
560 }
561
562 bool TestStepDelay::do_execute() throw() {
563   if (a_delay == 0) { complete(); return true; } // special case
564   try {
565     a_timer = TestManager::instantiate().createTimer((TestCaseStep*)this, a_delay, TestTimer::Type::Delay);
566   }
567   catch (anna::RuntimeException &ex) {
568     ex.trace();
569     a_testCase->addDebugSummaryHint(ex.asString()); // before report (when set Failed state)
570     a_testCase->setState(TestCase::State::Failed);
571   }
572
573   return false; // don't go next (wait complete)
574 }
575
576 void TestStepDelay::do_complete() throw() {
577   if (a_delay == 0) return; // special case
578   a_timer = NULL;
579   if (TestManager::instantiate().getDumpStdout()) {
580     std::cout << "Consumed Delay Test Step (" << a_delay << " milliseconds)" << std::endl;
581   }
582   next(); // next() invoked here because execute() is always false for delay and never advance the iterator
583   // TODO, avoid this recursion
584 }
585
586 void TestStepDelay::cancelTimer() throw() {
587   if (!a_timer) return;
588   if (a_delay == 0) return; // special case
589   try {
590     TestManager::instantiate().cancelTimer(a_timer);
591   }
592   catch (anna::RuntimeException &ex) {
593     ex.trace();
594   }
595   a_timer = NULL;
596   //a_delay = 0; THIS IS CONFIGURATION INFO
597 }
598
599 void TestStepDelay::do_reset() throw() {
600   cancelTimer();
601 }
602
603
604 ////////////////////////////////////////////////////////////////////////////////////////////////////////
605 // TestStepWaitDiameter
606 ////////////////////////////////////////////////////////////////////////////////////////////////////////
607 void TestStepWaitDiameter::setCondition(bool fromEntity,
608     const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
609     const std::string &sessionId, const std::string &resultCode,
610     const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw() {
611
612   a_condition.setReceivedFromEntity(fromEntity);
613   a_condition.setCode(code);
614   a_condition.setBitR(bitR);
615   a_condition.setResultCode(resultCode);
616   a_condition.setSessionId(sessionId);
617   a_condition.setHopByHop(hopByHop);
618   a_condition.setMsisdn(msisdn);
619   a_condition.setImsi(imsi);
620   a_condition.setServiceContextId(serviceContextId);
621 }
622
623 void TestStepWaitDiameter::setConditionRegexpHex(bool fromEntity, const std::string &regexp) throw() {
624   a_condition.setReceivedFromEntity(fromEntity);
625   a_condition.setRegexpHex(regexp);
626 }
627
628 void TestStepWaitDiameter::setConditionRegexpXml(bool fromEntity, const std::string &regexp) throw() {
629   a_condition.setReceivedFromEntity(fromEntity);
630   a_condition.setRegexpXml(regexp);
631 }
632
633 anna::xml::Node* TestStepWaitDiameter::asXML(anna::xml::Node* parent)
634 throw() {
635   anna::xml::Node* result = TestStep::asXML(parent);
636   //parent->createChild("TestStepWaitDiameter");
637   std::string msg = "", xmlmsg = "";
638
639   // Condition
640   a_condition.asXML(result);
641
642   // Message
643   if (TestManager::instantiate().getDumpHex()) {
644     if (a_message.isEmpty()) {
645       msg = "<empty>";
646     }
647     else {
648       msg = "\n"; msg += a_message.asString(); msg += "\n";
649     }
650   }
651
652   if (a_message.isEmpty()) {
653       xmlmsg = "<empty>";
654   }
655   else {
656     if (decodeMessage()) {
657       xmlmsg = "\n";
658       xmlmsg += a_messageCodec->asXMLString();
659       xmlmsg += "\n";
660     }
661     else {
662       xmlmsg = "<unable to decode, check traces>";
663     }
664   }
665
666   if (msg != "") result->createAttribute("MatchedMessage", msg);
667   if (xmlmsg != "") result->createAttribute("MatchedXMLMessage", xmlmsg);
668   if (a_clientSession) result->createAttribute("ClientSessionOrigin", anna::functions::asString("%p", a_clientSession));
669   if (a_serverSession) result->createAttribute("ServerSessionOrigin", anna::functions::asString("%p", a_serverSession));
670
671   return result;
672 }
673
674 bool TestStepWaitDiameter::do_execute() throw() {
675   return a_completed;
676 }
677
678 void TestStepWaitDiameter::do_complete() throw() {
679   //a_testCase->process(); // next() not invoked; we only want to reactivate the test case
680   // avoid stack overflow: we will process the test case externally when incoming message is fulfilled (TestCase.cpp), and TestManager is noticed
681 }
682
683 bool TestStepWaitDiameter::fulfilled(const anna::DataBlock &db/*, bool matchSessionId*/) throw() {
684   if (a_condition.comply(db/*, matchSessionId*/)) {
685     a_message = db; // store matched
686     complete();
687
688     if (TestManager::instantiate().getDumpStdout()) {
689       std::cout << "Fulfilled Wait diameter Test Step" << std::endl;
690     }
691
692     return true;
693   }
694
695   if (TestManager::instantiate().getDumpStdout()) {
696     std::cout << "NOT Fulfilled Wait diameter Test Step" << std::endl;
697   }
698
699   return false;
700 }
701
702 void TestStepWaitDiameter::do_reset() throw() {
703   a_message.clear();
704   a_clientSession = NULL;
705   a_serverSession = NULL;
706 }
707
708 ////////////////////////////////////////////////////////////////////////////////////////////////////////
709 // TestStepCmd
710 ////////////////////////////////////////////////////////////////////////////////////////////////////////
711 anna::xml::Node* TestStepCmd::asXML(anna::xml::Node* parent)
712 throw() {
713   anna::xml::Node* result = TestStep::asXML(parent);
714   //parent->createChild("TestStepCmd");
715
716   result->createAttribute("Script", (a_script != "") ? a_script:"<no script>");
717   if (a_errorMsg != "") result->createAttribute("ErrorMessage", a_errorMsg);
718   if (a_threadRunning) {
719     if (a_childPid != -1)
720       result->createAttribute("ChildPid", a_childPid);
721   }
722   else {
723     if (a_resultCode != -2) {
724       result->createAttribute("ResultCode", a_resultCode);
725       //if (a_output != "") result->createAttribute("Output", a_output);
726     }
727   }
728
729   return result;
730 }
731
732 bool TestStepCmd::do_execute() throw() {
733   if (!a_threadRunning /* || a_threadDeprecated DO NOT WANT TO OVERLAP ... */) {
734     // Special tags to replace:
735     std::string cmd = getScript();
736     size_t index;
737     while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID)) != std::string::npos)
738       cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID), anna::functions::asString(TestManager::instantiate().getPoolCycle()));
739     while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID)) != std::string::npos)
740       cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID), anna::functions::asString(a_testCase->getId()));
741     while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID)) != std::string::npos)
742       cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID), anna::functions::asString(getNumber()));
743
744     a_thread = std::thread(cmdRunOnThread, this, cmd);
745     //a_thread = std::thread(cmdRunOnThreadWithFork, this, cmd);
746
747     a_thread.detach();
748   }
749
750   return false; // don't go next (wait complete): If system function on thread stucks, then the reset test case will stuck here forever.
751   // We must implement a interrupt procedure for the thread on reset call... TODO !
752 }
753
754 void TestStepCmd::do_complete() throw() {
755
756   if (TestManager::instantiate().getDumpStdout()) {
757     std::cout << "Executed Command Test Step (" << a_script << ") [rc=" << a_resultCode << "]" << std::endl;
758   }
759
760   a_threadRunning = false;
761   if (a_threadDeprecated) {
762     a_threadDeprecated = false;
763     do_reset();
764     setErrorMsg(anna::functions::asString("Step %d deprecated due to previous reset for Test Case %llu", getNumber(), a_testCase->getId()));
765     a_testCase->setState(TestCase::State::Failed);
766     return; // ignore TODO: interrupt the thread to avoid execution of the script
767   }
768
769   if (getResultCode() != 0)
770     a_testCase->setState(TestCase::State::Failed);
771   else
772     next(); // next() invoked here because execute() is always false for delay and never advance the iterator
773   // TODO, avoid this recursion
774 }
775
776 void TestStepCmd::do_reset() throw() {
777
778     if (a_threadRunning) {
779       std::string s_warn = anna::functions::asString("Thread still in progress: deprecating step %d for Test Case %llu", getNumber(), a_testCase->getId());
780       LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION));
781       a_threadDeprecated = true;
782     }
783 //  if (a_threadRunning) {
784 //    std::string s_warn = anna::functions::asString("Thread still in progress: killing child pid %d within step %d for Test Case %llu", a_childPid, getNumber(), a_testCase->getId());
785 //    LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION));
786 //    kill (a_childPid, SIGKILL);
787 //  }
788
789   a_resultCode = -2;
790   a_errorMsg = "";
791   //a_output = "";
792   a_childPid = -1;
793 }
794
795 anna::xml::Node* TestStepIpLimit::asXML(anna::xml::Node* parent)
796 throw() {
797   anna::xml::Node* result = TestStep::asXML(parent);
798   std::string limit = (a_ipLimit != UINT_MAX) ? anna::functions::asString(a_ipLimit) : "<no limit>";
799   result->createAttribute("IpLimit", limit);
800
801   return result;
802 }
803
804 bool TestStepIpLimit::do_execute() throw() {
805   TestManager::instantiate().setInProgressLimit(a_ipLimit);
806   complete();
807   return true; // go next
808 }
809
810 void TestStepIpLimit::do_complete() throw() {
811   if (TestManager::instantiate().getDumpStdout()) {
812     std::string limit = (a_ipLimit != UINT_MAX) ? anna::functions::asString(a_ipLimit) : "<no limit>";
813     std::cout << "Executed IpLimit Test Step (value = " << limit << ")" << std::endl;
814   }
815 }
816