X-Git-Url: https://git.teslayout.com/public/public/public/?a=blobdiff_plain;f=example%2Fdiameter%2Flauncher%2Ftesting%2FTestStep.cpp;h=fdbd7989fc8a3374e52dc728919f3028913cb7b8;hb=61f1340da3cae5159d2e3bc14fc47c6d4bf9453e;hp=c8aef4e765aafc2dd528abcc46d10211f5b8457b;hpb=888c763c650fbe314ba47bb0840c47fc508cc837;p=anna.git diff --git a/example/diameter/launcher/testing/TestStep.cpp b/example/diameter/launcher/testing/TestStep.cpp index c8aef4e..fdbd798 100644 --- a/example/diameter/launcher/testing/TestStep.cpp +++ b/example/diameter/launcher/testing/TestStep.cpp @@ -16,6 +16,11 @@ #include // exit #include // waitpid, pid_t, WNOHANG +// cmd with fork: +#include +#include + + // Project #include #include @@ -24,19 +29,18 @@ #include #include #include -#include #include #include // Process -#include +#include #include #include #include -#include #include #include #include +#include namespace { @@ -58,19 +62,19 @@ namespace { sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, 0) != -1) { status = system(cmd.c_str()); - /* POPEN version: - char readbuf[256]; - FILE *fp = popen(cmd.c_str(), "r"); - if (fp) { - while(fgets(readbuf, sizeof(readbuf), fp)) - step->appendOutput("\n"); - step->appendOutput(readbuf); - status = pclose(fp); - } - else { - status = -1; - } - */ + /* POPEN version: + char readbuf[256]; + FILE *fp = popen(cmd.c_str(), "r"); + if (fp) { + while(fgets(readbuf, sizeof(readbuf), fp)) + step->appendOutput("\n"); + step->appendOutput(readbuf); + status = pclose(fp); + } + else { + status = -1; + } + */ } else { perror(0); @@ -93,19 +97,63 @@ namespace { // TODO: mutex the step while setting data here !! } - bool decodeMessage(const anna::DataBlock &message, anna::diameter::codec::Message *messageCodec) throw() { + void cmdRunOnThreadWithFork (TestStepCmd *step, const std::string &cmd) { + + // Thread running: + step->setThreadRunning(true); + + pid_t cpid, w; + int status = -2; + + if ((cpid = fork()) < 0) { + step->setErrorMsg("Error in fork()"); + } + else if (cpid == 0) { + // child + status = system(cmd.c_str()); + _exit(WEXITSTATUS(status)); + } + else { + // parent + step->setChildPid(cpid); + do { + w = waitpid(cpid, &status, WUNTRACED | WCONTINUED); + if (w != -1) { + + if (WIFEXITED(status)) { + step->setResultCode(WEXITSTATUS(status)); // rc = status >>= 8; // divide by 256 + break; + } + else if (WIFSIGNALED(status)) { + step->setErrorMsg(anna::functions::asString("killed by signal %d", WTERMSIG(status))); + step->setResultCode(128 + WTERMSIG(status)); + break; + } else if (WIFSTOPPED(status)) { + step->setErrorMsg(anna::functions::asString("stopped by signal %d", WSTOPSIG(status))); + } else if (WIFCONTINUED(status)) { + step->setErrorMsg("continued"); + } + } + else { + step->setErrorMsg("waitpid error"); + step->setResultCode(-1); + break; + } + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + + step->complete(); + } + } + + bool decodeMessage(const anna::DataBlock &message, anna::diameter::codec::Message &messageCodec) throw() { if (message.isEmpty()) return false; bool result = true; try { - if (!messageCodec) { - Launcher& my_app = static_cast (anna::app::functions::getApp()); - messageCodec = new anna::diameter::codec::Message(my_app.getCodecEngine()); - } - - messageCodec->decode(message); + messageCodec.clear(); + messageCodec.decode(message); } catch (anna::RuntimeException &ex) { ex.trace(); @@ -114,7 +162,6 @@ namespace { return result; } - } @@ -130,13 +177,27 @@ void TestStep::initialize(TestCase *testCase) { a_number = testCase->steps() + 1; // testCase is not NULL } +bool TestStep::decodeMessage(bool trust) throw() { + if (a_messageCodec) return true; + a_messageCodec = new anna::diameter::codec::Message; + if (::decodeMessage(a_message, *a_messageCodec)) return true; + if (trust) { + LOGDEBUG(anna::Logger::debug("Error DECODING, but trusting it ...", ANNA_FILE_LOCATION)); + return true; + } + + delete a_messageCodec; + a_messageCodec = NULL; + return false; +} + const char* TestStep::asText(const Type::_v type) throw() { - static const char* text [] = { "Unconfigured", "Timeout", "Sendxml2e", "Sendxml2c", "Delay", "Wait", "Cmd" }; + static const char* text [] = { "Unconfigured", "Timeout", "Sendxml2e", "Sendxml2c", "Delay", "Wait", "Command" }; return text [type]; } -anna::xml::Node* TestStep::asXML(anna::xml::Node* parent) const +anna::xml::Node* TestStep::asXML(anna::xml::Node* parent) throw() { anna::xml::Node* result = parent->createChild("TestStep"); @@ -146,41 +207,52 @@ throw() { // Begin std::string s_aux = a_beginTimestamp.asString(); -// int deltaMs = (int)(a_beginTimestamp - a_testCase->getStartTimestamp()); -// if (a_beginTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0); + // int deltaMs = (int)(a_beginTimestamp - a_testCase->getStartTimestamp()); + // if (a_beginTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0); result->createAttribute("BeginTimestamp", s_aux); // End s_aux = a_endTimestamp.asString(); -// deltaMs = (int)(a_endTimestamp - a_testCase->getStartTimestamp()); -// if (a_endTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0); + // deltaMs = (int)(a_endTimestamp - a_testCase->getStartTimestamp()); + // if (a_endTimestamp != 0 && deltaMs > 0) s_aux += anna::functions::asString(" [%.3f]", deltaMs/1000.0); result->createAttribute("EndTimestamp", s_aux); return result; } -std::string TestStep::asXMLString() const throw() { +std::string TestStep::asXMLString() throw() { anna::xml::Node root("root"); return anna::xml::Compiler().apply(asXML(&root)); } bool TestStep::execute() throw() { - LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION)); + + int ia = a_testCase->interactiveAmount(); + if (ia > -1) { + if (ia == 0) return false; + a_testCase->interactiveExecution(); + LOGDEBUG(anna::Logger::debug("Interactive execution ...", ANNA_FILE_LOCATION)); + if (a_executed) return false; // avoid repeating (this implies amount consumption) + } + + LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s (step number %d) for Test Case %llu (%p)", asText(a_type), a_number, a_testCase->getId(), this), ANNA_FILE_LOCATION)); setBeginTimestamp(anna::functions::millisecond()); + a_executed = true; return do_execute(); } void TestStep::complete() throw() { - LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION)); + LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s (step number %d) for Test Case %llu (%p)", asText(a_type), a_number, a_testCase->getId(), this), ANNA_FILE_LOCATION)); a_completed = true; setEndTimestamp(anna::functions::millisecond()); do_complete(); } void TestStep::reset() throw() { - LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION)); + LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s (step number %d) for Test Case %llu (%p)", asText(a_type), a_number, a_testCase->getId(), this), ANNA_FILE_LOCATION)); // type and testCase kept a_completed = false; + a_executed = false; a_beginTimestamp = 0; a_endTimestamp = 0; do_reset(); @@ -195,7 +267,7 @@ void TestStep::next() throw() { //////////////////////////////////////////////////////////////////////////////////////////////////////// // TestStepTimeout //////////////////////////////////////////////////////////////////////////////////////////////////////// -anna::xml::Node* TestStepTimeout::asXML(anna::xml::Node* parent) const +anna::xml::Node* TestStepTimeout::asXML(anna::xml::Node* parent) throw() { anna::xml::Node* result = TestStep::asXML(parent); // end timestamp will be 0 if test finished OK //parent->createChild("TestStepTimeout"); @@ -232,8 +304,6 @@ void TestStepTimeout::do_complete() throw() { } void TestStepTimeout::do_reset() throw() { - LOGDEBUG(anna::Logger::debug(anna::functions::asString("REXXXXXET %s for Test Case %llu (%p) (%p) (a_timer %p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this, a_timer), ANNA_FILE_LOCATION)); - try { TestManager::instantiate().cancelTimer(a_timer); } @@ -247,12 +317,7 @@ void TestStepTimeout::do_reset() throw() { //////////////////////////////////////////////////////////////////////////////////////////////////////// // TestStepSendxml //////////////////////////////////////////////////////////////////////////////////////////////////////// -TestStepSendxml::~TestStepSendxml() { - delete a_messageCodec; - a_messageCodec = NULL; -} - -anna::xml::Node* TestStepSendxml::asXML(anna::xml::Node* parent) const +anna::xml::Node* TestStepSendxml::asXML(anna::xml::Node* parent) throw() { anna::xml::Node* result = TestStep::asXML(parent); //parent->createChild("TestStepSendxml"); @@ -268,11 +333,14 @@ throw() { } } - if (decodeMessage(a_message, a_messageCodec)) { + if (decodeMessage()) { xmlmsg = "\n"; xmlmsg += a_messageCodec->asXMLString(); xmlmsg += "\n"; } + else { + xmlmsg = ""; + } if (msg != "") result->createAttribute("Message", msg); if (xmlmsg != "") result->createAttribute("XMLMessage", xmlmsg); @@ -285,38 +353,49 @@ throw() { bool TestStepSendxml::do_execute() throw() { bool success = false; - std::string failReason, s_warn; - MyDiameterEntity *entity = a_realmNode->getEntity(); // by default - MyLocalServer *localServer = a_realmNode->getDiameterServer(); // by default + std::string failReason; + MyDiameterEntity *entity = a_originHost->getEntity(); // by default + MyLocalServer *localServer = a_originHost->getDiameterServer(); // by default const TestStepWait *tsw = NULL; - - // Create comm message: - anna::diameter::comm::Message *msg = a_realmNode->createCommMessage(); - //msg->clearBody(); - msg->setBody(a_message); + anna::diameter::comm::Message *msg = a_originHost->createCommMessage(); try { - // Update sequence for answers: - if (a_waitForRequestStepNumber != -1) { // is an answer: try to copy sequence information; alert about Session-Id discrepance - // Request which was received: - tsw = (const TestStepWait*)(a_testCase->getStep(a_waitForRequestStepNumber)); - const anna::DataBlock &request = tsw->getMsgDataBlock(); - anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(request); - anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(request); - // Update sequence: - anna::diameter::codec::functions::setHopByHop(a_message, hbh); - anna::diameter::codec::functions::setEndToEnd(a_message, ete); - - // Check Session-Id for warning ... - std::string sessionIdAnswer = anna::diameter::helpers::base::functions::getSessionId(a_message); - std::string sessionIdRequest = anna::diameter::helpers::base::functions::getSessionId(request); - if (sessionIdRequest != sessionIdAnswer) { - s_warn = anna::functions::asString("Sending an answer which Session-Id (%s) is different than supposed corresponding request (%s)", sessionIdAnswer.c_str(), sessionIdRequest.c_str()); - LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION)); - a_testCase->addDebugSummaryHint(s_warn); + + if (a_waitForRequestStepNumber != -1) { + + // Referenced request in the 'wait for request step': + tsw = static_cast(a_testCase->getStep(a_waitForRequestStepNumber)); + const anna::DataBlock &referenceRequest = tsw->getMsgDataBlock(); + std::string sessionIdReferenceRequest = anna::diameter::helpers::base::functions::getSessionId(referenceRequest); + bool thisIsAnswer = anna::diameter::codec::functions::isRequest(getMsgDataBlock()); + + if (thisIsAnswer) { // is an answer: try to copy sequence information; alert about Session-Id discrepance + anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(referenceRequest); + anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(referenceRequest); + + // Update sequence: + anna::diameter::codec::functions::setHopByHop(a_message, hbh); + anna::diameter::codec::functions::setEndToEnd(a_message, ete); + } + + // Session-Id substitution: + std::string thisSessionId = anna::diameter::helpers::base::functions::getSessionId(getMsgDataBlock()); + if (thisSessionId != sessionIdReferenceRequest) { + static anna::diameter::codec::Message codecMsg; + codecMsg.decode(getMsgDataBlock()); + codecMsg.getAvp("Session-Id")->getUTF8String()->setValue(sessionIdReferenceRequest); + a_message = codecMsg.code(); + 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()); + LOGDEBUG(anna::Logger::debug(trace, ANNA_FILE_LOCATION)); + a_testCase->addDebugSummaryHint(trace); } } + // Create comm message: + //msg->clearBody(); + msg->setBody(a_message); + + if (getType() == Type::Sendxml2e) { anna::diameter::comm::ClientSession *usedClientSession = NULL; @@ -345,13 +424,10 @@ bool TestStepSendxml::do_execute() throw() { } // else (normal sending) // Detailed log: - if(a_realmNode->logEnabled()) { - if (!a_messageCodec) - decodeMessage(a_message, a_messageCodec); - - if (a_messageCodec) { + if(a_originHost->logEnabled()) { + if (decodeMessage(true /* trust */)) { std::string detail = usedClientSession ? usedClientSession->asString() : ""; // shouldn't happen - a_realmNode->writeLogFile(*a_messageCodec, (success ? "sent2e" : "send2eError"), detail); + a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2e" : "send2eError"), detail); } } } @@ -382,13 +458,10 @@ bool TestStepSendxml::do_execute() throw() { } // else (normal sending) // Detailed log: - if(a_realmNode->logEnabled()) { - if (!a_messageCodec) - decodeMessage(a_message, a_messageCodec); - - if (a_messageCodec) { + if(a_originHost->logEnabled()) { + if (decodeMessage(true /* trust */)) { std::string detail = usedServerSession ? usedServerSession->asString() : ""; // shouldn't happen - a_realmNode->writeLogFile(*a_messageCodec, (success ? "sent2c" : "send2cError"), detail); + a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2c" : "send2cError"), detail); } } } @@ -398,7 +471,7 @@ bool TestStepSendxml::do_execute() throw() { } // release msg - a_realmNode->releaseCommMessage(msg); + a_originHost->releaseCommMessage(msg); if (!success) { a_testCase->addDebugSummaryHint(failReason); // before report (when set Failed state); @@ -414,22 +487,24 @@ bool TestStepSendxml::do_execute() throw() { void TestStepSendxml::do_reset() throw() { a_expired = false; //a_message.clear(); + //a_messageAlreadyDecoded = false; } //////////////////////////////////////////////////////////////////////////////////////////////////////// // TestStepDelay //////////////////////////////////////////////////////////////////////////////////////////////////////// -anna::xml::Node* TestStepDelay::asXML(anna::xml::Node* parent) const +anna::xml::Node* TestStepDelay::asXML(anna::xml::Node* parent) throw() { anna::xml::Node* result = TestStep::asXML(parent); //parent->createChild("TestStepDelay"); - result->createAttribute("Delay", a_delay.asString()); + result->createAttribute("Delay", ((a_delay == 0) ? "dummy step, no delay" : a_delay.asString())); return result; } bool TestStepDelay::do_execute() throw() { + if (a_delay == 0) { complete(); return true; } // special case try { a_timer = TestManager::instantiate().createTimer((TestCaseStep*)this, a_delay, TestTimer::Type::Delay); } @@ -443,11 +518,14 @@ bool TestStepDelay::do_execute() throw() { } void TestStepDelay::do_complete() throw() { + if (a_delay == 0) return; // special case a_timer = NULL; next(); // next() invoked here because execute() is always false for delay and never advance the iterator + // TODO, avoid this recursion } void TestStepDelay::do_reset() throw() { + if (a_delay == 0) return; // special case try { TestManager::instantiate().cancelTimer(a_timer); } @@ -461,14 +539,10 @@ void TestStepDelay::do_reset() throw() { //////////////////////////////////////////////////////////////////////////////////////////////////////// // TestStepWait //////////////////////////////////////////////////////////////////////////////////////////////////////// -TestStepWait::~TestStepWait() { - delete a_messageCodec; - a_messageCodec = NULL; -} - void TestStepWait::setCondition(bool fromEntity, - const std::string &code, const std::string &bitR, const std::string &resultCode, const std::string &sessionId, - const std::string &hopByHop, const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw() { + const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId, + const std::string &sessionId, const std::string &resultCode, + const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw() { a_condition.setReceivedFromEntity(fromEntity); a_condition.setCode(code); @@ -487,7 +561,7 @@ void TestStepWait::setCondition(bool fromEntity, const std::string ®exp) thro a_condition.setRegexp(regexp); } -anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent) const +anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent) throw() { anna::xml::Node* result = TestStep::asXML(parent); //parent->createChild("TestStepWait"); @@ -506,14 +580,19 @@ throw() { } } - if (decodeMessage(a_message, a_messageCodec)) { + if (decodeMessage()) { xmlmsg = "\n"; xmlmsg += a_messageCodec->asXMLString(); xmlmsg += "\n"; } + else { + xmlmsg = ""; + } if (msg != "") result->createAttribute("MatchedMessage", msg); if (xmlmsg != "") result->createAttribute("MatchedXMLMessage", xmlmsg); + if (a_clientSession) result->createAttribute("ClientSessionOrigin", anna::functions::asString("%p", a_clientSession)); + if (a_serverSession) result->createAttribute("ServerSessionOrigin", anna::functions::asString("%p", a_serverSession)); return result; } @@ -523,13 +602,13 @@ bool TestStepWait::do_execute() throw() { } void TestStepWait::do_complete() throw() { - a_testCase->process(); // next() not invoked; we only want to reactivate the test case + //a_testCase->process(); // next() not invoked; we only want to reactivate the test case + // avoid stack overflow: we will process the test case externally when incoming message is fulfilled (TestCase.cpp), and TestManager is noticed } bool TestStepWait::fulfilled(const anna::DataBlock &db/*, bool matchSessionId*/) throw() { if (a_condition.comply(db/*, matchSessionId*/)) { - //a_message = db; // store matched - a_message.assign(db); + a_message = db; // store matched complete(); return true; } @@ -546,17 +625,22 @@ void TestStepWait::do_reset() throw() { //////////////////////////////////////////////////////////////////////////////////////////////////////// // TestStepCmd //////////////////////////////////////////////////////////////////////////////////////////////////////// -anna::xml::Node* TestStepCmd::asXML(anna::xml::Node* parent) const +anna::xml::Node* TestStepCmd::asXML(anna::xml::Node* parent) throw() { anna::xml::Node* result = TestStep::asXML(parent); //parent->createChild("TestStepCmd"); result->createAttribute("Script", (a_script != "") ? a_script:""); - result->createAttribute("Parameters", (a_parameters != "") ? a_parameters:""); if (a_errorMsg != "") result->createAttribute("ErrorMessage", a_errorMsg); - if (!a_threadRunning && a_resultCode != -2) { - result->createAttribute("ResultCode", a_resultCode); - //if (a_output != "") result->createAttribute("Output", a_output); + if (a_threadRunning) { + if (a_childPid != -1) + result->createAttribute("ChildPid", a_childPid); + } + else { + if (a_resultCode != -2) { + result->createAttribute("ResultCode", a_resultCode); + //if (a_output != "") result->createAttribute("Output", a_output); + } } return result; @@ -566,8 +650,6 @@ bool TestStepCmd::do_execute() throw() { if (!a_threadRunning /* || a_threadDeprecated DO NOT WANT TO OVERLAP ... */) { // Special tags to replace: std::string cmd = getScript(); - cmd += " "; - cmd += getParameters(); size_t index; while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID)) != std::string::npos) cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID), anna::functions::asString(TestManager::instantiate().getPoolCycle())); @@ -577,11 +659,13 @@ bool TestStepCmd::do_execute() throw() { cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID), anna::functions::asString(getNumber())); a_thread = std::thread(cmdRunOnThread, this, cmd); + //a_thread = std::thread(cmdRunOnThreadWithFork, this, cmd); + a_thread.detach(); } return false; // don't go next (wait complete): If system function on thread stucks, then the reset test case will stuck here forever. - // We must implement a interrupt procedure for the thread on reset call... TODO ! + // We must implement a interrupt procedure for the thread on reset call... TODO ! } void TestStepCmd::do_complete() throw() { @@ -599,17 +683,25 @@ void TestStepCmd::do_complete() throw() { a_testCase->setState(TestCase::State::Failed); else next(); // next() invoked here because execute() is always false for delay and never advance the iterator + // TODO, avoid this recursion } void TestStepCmd::do_reset() throw() { - if (a_threadRunning) { - std::string s_warn = anna::functions::asString("Thread still in progress: deprecating step %d for Test Case %llu", getNumber(), a_testCase->getId()); - LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION)); - a_threadDeprecated = true; - } + if (a_threadRunning) { + std::string s_warn = anna::functions::asString("Thread still in progress: deprecating step %d for Test Case %llu", getNumber(), a_testCase->getId()); + LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION)); + a_threadDeprecated = true; + } +// if (a_threadRunning) { +// 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()); +// LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION)); +// kill (a_childPid, SIGKILL); +// } a_resultCode = -2; + a_errorMsg = ""; //a_output = ""; + a_childPid = -1; }