From: Eduardo Ramos Testillano Date: Wed, 15 Feb 2017 16:01:40 +0000 (+0100) Subject: Testing library separation: now not in launcher but isolated X-Git-Tag: REFACTORING_TESTING_LIBRARY^0 X-Git-Url: https://git.teslayout.com/public/public/public/?a=commitdiff_plain;h=d723d5bf571eb48c641b092058eaa38bb6c4fcc8;p=anna.git Testing library separation: now not in launcher but isolated --- diff --git a/SConstruct b/SConstruct index e223dd8..a163297 100644 --- a/SConstruct +++ b/SConstruct @@ -64,6 +64,9 @@ env.Append (CPPPATH = [source_include, usr_local_include, usr_include ]) #env.Append (CCFLAGS = '-std=c++0x') # C++11 support: env.Append (CXXFLAGS = '-std=c++11') +# Shared libraries using static ones: +env.Append (CXXFLAGS = '-fPIC') +#env.Append (CXXFLAGS = '-export-dynamic') env.Append (LIBS = ['']) # scons -Q release=1 diff --git a/dynamic/launcher/default/Procedure.cpp b/dynamic/launcher/default/Procedure.cpp index 57bb7e0..62b0ce2 100644 --- a/dynamic/launcher/default/Procedure.cpp +++ b/dynamic/launcher/default/Procedure.cpp @@ -9,11 +9,14 @@ #include #include +// Project +#include + // Local #include "Procedure.hpp" -void Procedure::execute(const std::string &args, std::string &response) throw() { +void Procedure::execute(const std::string &args, std::string &response, anna::diameter::comm::OriginHost *originHost) throw(anna::RuntimeException) { response = "Empty default implementation. Make dynamic library and reimplement this method."; if (args != "" ) { response += " Provided argument(s) ignored: "; diff --git a/dynamic/launcher/default/Procedure.hpp b/dynamic/launcher/default/Procedure.hpp index 91247a3..c96ab07 100644 --- a/dynamic/launcher/default/Procedure.hpp +++ b/dynamic/launcher/default/Procedure.hpp @@ -13,6 +13,13 @@ #include +namespace anna { + namespace diameter { + namespace comm { + class OriginHost; + } + } +} class Procedure { anna::comm::Application *a_app; @@ -20,7 +27,7 @@ class Procedure { public: Procedure(anna::comm::Application *app) : a_app(app) {;} - virtual void execute(const std::string &args, std::string &response) throw(); + virtual void execute(const std::string &args, std::string &response, anna::diameter::comm::OriginHost *originHost) throw(anna::RuntimeException); }; #endif diff --git a/dynamic/launcher/gx/00001/CCR-I.xml b/dynamic/launcher/gx/00001/CCR-I.xml new file mode 100644 index 0000000..a57ef6d --- /dev/null +++ b/dynamic/launcher/gx/00001/CCR-I.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dynamic/launcher/gx/00001/CCR-T.xml b/dynamic/launcher/gx/00001/CCR-T.xml new file mode 100644 index 0000000..ac44202 --- /dev/null +++ b/dynamic/launcher/gx/00001/CCR-T.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/dynamic/launcher/gx/00001/Procedure.cpp b/dynamic/launcher/gx/00001/Procedure.cpp index 62975c1..63b4ef1 100644 --- a/dynamic/launcher/gx/00001/Procedure.cpp +++ b/dynamic/launcher/gx/00001/Procedure.cpp @@ -9,15 +9,273 @@ #include #include +// Project +#include +#include +#include +#include +#include +#include +#include +#include // Local #include "Procedure.hpp" -void Procedure::execute(const std::string &args, std::string &response) throw() { - response = "00001 Empty default implementation. Make dynamic library and reimplement this method."; - if (args != "" ) { - response += " Provided argument(s) ignored: "; - response += args; +namespace { + + void usage (std::string &response) { + response += "\n\nInvalid arguments. Provide these ones:"; + response += "\n"; + response += "\n|||[|CCR-T xml file]"; + response += "\n"; + response += "\nSequences are parsed when needed, over AVPs or internal values:"; + response += "\n"; + response += "\nSession-Id: ;;[;]"; + response += "\n \\_fixed_/\\_digits_/ \\_fixed_/\\_digits_/"; + response += "\n"; + response += "\nFor example, imagine a 13-digits diameter identity, and a 10-digits optional part."; + response += "\nThis procedure will sequence the range for 8 digits in this way:"; + response += "\n"; + response += "\n111111<7-digit-sequence>;BAT004;esmdx0900.gxrel10plusrealm.com;901<7-digit-sequence>"; + response += "\n"; + response += "\nThe same will be done in MSISDN and IMSI (Subscription-Data AVPs)."; + response += "\nFramed-IP-Address will be sequenced with a direct correspondence to hex value."; + response += "\n"; + response += "\nThen, you could provide these arguments: \"5000|2000000|9000000|7|CCR-I.xml|CCR-T.xml\""; + response += "\n"; + } +} + +void Procedure::execute(const std::string &args, std::string &response, anna::diameter::comm::OriginHost *originHost) throw(anna::RuntimeException) { + + response = "Dynamic procedure failed to process '"; response += args; response += "': "; + + anna::Tokenizer targs; + targs.apply(args, "|"); + + if (targs.size() < 5) { + usage(response); + return; + } + + anna::Tokenizer::const_iterator tok_it = targs.begin(); + + std::string seq_i = anna::Tokenizer::data(tok_it); tok_it++; + std::string seq_f = anna::Tokenizer::data(tok_it); tok_it++; + std::string timeout = anna::Tokenizer::data(tok_it); tok_it++; + std::string digits = anna::Tokenizer::data(tok_it); tok_it++; + std::string ccr_i = anna::Tokenizer::data(tok_it); tok_it++; + std::string ccr_t = ((tok_it != targs.end()) ? anna::Tokenizer::data(tok_it):""); + bool haveTermination = (ccr_t != ""); + + // Test cases cycles: + int i_timeout = std::atoi(timeout.c_str()); + unsigned int ll_seq_i = std::atol(seq_i.c_str()); + unsigned int ll_seq_f = std::atol(seq_f.c_str()); + unsigned int ll_seq_size = ll_seq_f - ll_seq_i + 1; + int i_digits = std::atoi(digits.c_str()); + + if (ll_seq_i > ll_seq_f) { + response += " must be greater or equal than "; + return; + } + + if (seq_f.size() > i_digits) { + response += " must be lesser than number of "; + return; + } + + unsigned int ll_seq, ll_index; + anna::Millisecond timeoutMS(i_timeout); + + // Load xml messages: + anna::diameter::codec::Message ccri, ccrt; + anna::diameter::codec::Avp *ccri_sessionId, *ccrt_sessionId, *ccri_framedIPAddress, *ccrt_framedIPAddress, *ccri_msisdn, *ccri_imsi; + anna::diameter::codec::Avp *si1, *si2, *sidata1, *sidata2, *sitype1, *sitype2; + + try { + + ///////// CCR-Initial: + ccri.loadXML(ccr_i); + + // Session-Id & Framed-Ip-Address AVPs + ccri_sessionId = ccri.getAvp("Session-Id"); + ccri_framedIPAddress = ccri.getAvp("Framed-IP-Address"); + + // Subscription-Id AVPs + if (ccri.countAvp("Subscription-Id") != 2) { + response += "Both Subscription-Id MSISDN & IMSI Avps must be present in the CCR-Initial provided !"; + return; + } + + si1 = ccri.getAvp("Subscription-Id", 1, anna::Exception::Mode::Ignore); + si2 = ccri.getAvp("Subscription-Id", 2, anna::Exception::Mode::Ignore); + + if (!si1 || !si2) { + response += "Cannot found Subscription-Id MSISDN & IMSI Avps !" ; + return; + } + + sidata1 = si1->getAvp("Subscription-Id-Data"); + sidata2 = si2->getAvp("Subscription-Id-Data"); + sitype1 = si1->getAvp("Subscription-Id-Type"); + //sitype2 = si2->getAvp("Subscription-Id-Type"); + + if (sitype1->getEnumerated()->getValue() == anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164) { + ccri_msisdn = sidata1; + ccri_imsi = sidata2; + } + else { + ccri_msisdn = sidata2; + ccri_imsi = sidata1; + } + + ///////// CCR-Termination: + if (haveTermination) { + ccrt.loadXML(ccr_t); + + // Session-Id & Framed-Ip-Address AVPs + ccrt_sessionId = ccrt.getAvp("Session-Id"); + ccrt_framedIPAddress = ccrt.getAvp("Framed-IP-Address"); + } + } + catch(anna::RuntimeException &ex) { + ex.trace(); + } + + // Prepare session-id string: + std::string sessionId = ccri_sessionId->getUTF8String()->getValue(); + std::size_t last_semicolon = sessionId.rfind(";"); + anna::Tokenizer tsessionid; + tsessionid.apply(sessionId, ";"); + + if (tsessionid.size() < 4) { + response += "Session-Id must be in form ';;;'.\n\n"; + usage(response); + return; + } + + tok_it = tsessionid.begin(); + std::string d_identity = anna::Tokenizer::data(tok_it); tok_it++; tok_it++; tok_it++; + std::string o_part = anna::Tokenizer::data(tok_it); + int d_identity_len = d_identity.size(); + int left_di = d_identity_len - i_digits; + if (left_di < 0) { + response += "Session-Id diameter identity length is lower than selected number of digits "; + response += anna::functions::asString("(%d < %d).\n\n", d_identity_len, i_digits); + usage(response); + return; + } + int o_part_len = o_part.size(); + int left_op = o_part_len - i_digits; + if (left_op < 0) { + response += "Session-Id optional part length is lower than selected number of digits "; + response += anna::functions::asString("(%d < %d).\n\n", o_part_len, i_digits); + usage(response); + return; + } + + // Idem for MSISDN & IMSI Subscription-Data: + std::string msisdn = ccri_msisdn->getUTF8String()->getValue(); + int msisdn_len = msisdn.size(); + int left_msisdn = msisdn_len - i_digits; + if (left_msisdn < 0) { + response += "MSISDN Subscription-Data length is lower than selected number of digits "; + response += anna::functions::asString("(%d < %d).\n\n", left_msisdn, i_digits); + usage(response); + return; } + std::string imsi = ccri_imsi->getUTF8String()->getValue(); + int imsi_len = imsi.size(); + int left_imsi = imsi_len - i_digits; + if (left_imsi < 0) { + response += "IMSI Subscription-Data length is lower than selected number of digits "; + response += anna::functions::asString("(%d < %d).\n\n", left_imsi, i_digits); + usage(response); + return; + } + + + // Format strings: + char ndigit_format[32]; + sprintf(ndigit_format, "%s%d%s", "%0", i_digits, "u"); + + //response += "\nSession-Id original: "; response += sessionId; + //response += "\nMSISDN original: "; response += msisdn; + //response += "\nIMSI original: "; response += imsi; + + // TestManager: + anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate(); + char cad_aux[16]; + char cad_framed[16]; + anna::testing::TestCase *tc; + + for (ll_index = 0; ll_index < ll_seq_size; ll_index++) { + + // Calculate next values //////////////////////////////////////////////////////////// + ll_seq = ll_seq_i + ll_index; + sprintf(cad_aux, ndigit_format, ll_seq); + sprintf(cad_framed, "%08x", ll_seq); + + // Activity indicator: + if (ll_seq % 10000 == 0) std::cout << "."; + + sessionId.replace(left_di, i_digits, cad_aux); + sessionId.replace(last_semicolon + left_op + 1, i_digits, cad_aux); + + msisdn.replace(left_msisdn, i_digits, cad_aux); + + imsi.replace(left_imsi, i_digits, cad_aux); + + //response += "\nSession-Id modified: "; response += sessionId; + //response += "\nMSISDN modified: "; response += msisdn; + //response += "\nIMSI modified: "; response += imsi; + //response += "\nFramedIP modified: "; response += cad_framed; + + // Update diameter messages ///////////////////////////////////////////////////////// + ccri_sessionId->getUTF8String()->setValue(sessionId); + ccri_framedIPAddress->getOctetString()->fromHexString(cad_framed); + ccri_msisdn->getUTF8String()->setValue(msisdn); + ccri_imsi->getUTF8String()->setValue(imsi); + + if (haveTermination) { + ccrt_sessionId->getUTF8String()->setValue(sessionId); + ccrt_framedIPAddress->getOctetString()->fromHexString(cad_framed); + } + + // Create testcase: + tc = testManager.getTestCase(ll_index+1); + // test|__TESTID__|timeout|6000 + // test|__TESTID__|sendxml2e|ccr-i.xml + // test|__TESTID__|waitfe|272|0|||11111__SEQ8__;BAT004;esmdx0900.gxrel10plusrealm.com;90__SEQ8__|2001 + // test|__TESTID__|sendxml2e|ccr-t.xml + // test|__TESTID__|waitfe|272|0|||11111__SEQ8__;BAT004;esmdx0900.gxrel10plusrealm.com;90__SEQ8__|2001 + + if (i_timeout != 0) { + // Step 1: timeout 5000 ms: + tc->addTimeout(timeoutMS); + } + + // Step 2: sendxml2e: CCR-Initial + tc->addSendxml2e(ccri.code(), originHost, -1 /* 'wait for request' step number for answers */); + + // Step 3: waitfe: CCA with same session id + // PARAM: 1 2 3 4 5 6 7 8 9 10 11 + // wait|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId] + tc->addWait(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", ""); + + if (haveTermination) { + // Step 4: sendxml2e: CCR-Termination + tc->addSendxml2e(ccrt.code(), originHost, -1 /* 'wait for request' step number for answers */); + + // Step 5: waitfe: CCA with same session id + tc->addWait(true /* from entity */, "272", "0", "", "", sessionId, "2001", "", "", ""); + } + } // loop + + response = "Completed provision for pid "; response += anna::functions::asString(a_app->getPid()); response += "; range ["; + response += seq_i; response += ", "; response += seq_f; response += "]; scenary: "; + response += "CCR-Initial"; if (haveTermination) response += " + CCR-Termination"; } diff --git a/dynamic/launcher/gx/00001/Procedure.hpp b/dynamic/launcher/gx/00001/Procedure.hpp index 884cad5..b46ef74 100644 --- a/dynamic/launcher/gx/00001/Procedure.hpp +++ b/dynamic/launcher/gx/00001/Procedure.hpp @@ -13,6 +13,13 @@ #include +namespace anna { + namespace diameter { + namespace comm { + class OriginHost; + } + } +} class Procedure { anna::comm::Application *a_app; @@ -20,7 +27,7 @@ class Procedure { public: Procedure(anna::comm::Application *app) : a_app(app) {;} - virtual void execute(const std::string &args, std::string &response) throw(); + virtual void execute(const std::string &args, std::string &response, anna::diameter::comm::OriginHost *originHost) throw(anna::RuntimeException); }; #endif diff --git a/dynamic/launcher/gx/00001/dynamic.suffix b/dynamic/launcher/gx/00001/dynamic.suffix new file mode 100644 index 0000000..97afc7f --- /dev/null +++ b/dynamic/launcher/gx/00001/dynamic.suffix @@ -0,0 +1,23 @@ +# Procedure arguments: +# +# ||[|CCR-Termination path file>] +# +# is the first step to control the test cases execution. A value of 0 omits +# this first step (good to avoid huge load of timers during high performance testing). +# +# All the tests not fulfilled (those where the corresponding answers are not received) +# will be eternally keep 'in-progress' state. You could consider them failed after +# a specific time. A non-zero timeout step will ensure that this situation is updated +# as failed on expiration. Diameter application request timeouts also expire but are +# not processed here (not neccessary because reception conditions are enough to block +# the flow when something is wrong). Anyway, you could check ADML counters executing +# the command: ./operation show-oam +# The main problem with a zeroed first timeout step, is that cycle repeats are not +# possible except if you reset the pool (hard reset) when close to finish, something +# very dificult to monitor. + +# Only CCR-Initial: +5000|7|CCR-I.xml + +# CCR-Initial + CCR-Termination: +5000|7|CCR-I.xml|CCR-T.xml diff --git a/example/diameter/launcher/DEPLOY.sh b/example/diameter/launcher/DEPLOY.sh index a314a30..72604a8 100755 --- a/example/diameter/launcher/DEPLOY.sh +++ b/example/diameter/launcher/DEPLOY.sh @@ -208,6 +208,8 @@ do dir=$(dirname $dl | xargs dirname) mkdir -p $DPATH/dynlibs/$dir cp $dir/$dynlib_variant/*.so $DPATH/dynlibs/$dir + cp $dir/*.xml $DPATH/dynlibs/$dir 2>/dev/null + cp $dir/dynamic.suffix $DPATH/dynlibs/$dir 2>/dev/null done cd - >/dev/null cp ./resources/scripts/select_dynlib.sh $DPATH/dynlibs/select.sh diff --git a/example/diameter/launcher/Launcher.cpp b/example/diameter/launcher/Launcher.cpp index aebebec..b141865 100644 --- a/example/diameter/launcher/Launcher.cpp +++ b/example/diameter/launcher/Launcher.cpp @@ -24,15 +24,16 @@ #include #include #include +#include #include #include // Process #include -#include #include -#include -#include +#include +#include +#include #define SIGUSR2_TASKS_INPUT_FILENAME "sigusr2.in" @@ -326,11 +327,24 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp // Checking command line parameters std::string sessionBasedModelsType; + anna::diameter::comm::Entity::SessionBasedModelsType::_v sessionBasedModelsTypeEnum; if(sessionBasedModelsClientSocketSelection) { sessionBasedModelsType = sessionBasedModelsClientSocketSelection->getValue(); - if((sessionBasedModelsType != "SessionIdHighPart") && (sessionBasedModelsType != "SessionIdOptionalPart") && (sessionBasedModelsType != "RoundRobin")) { - throw anna::RuntimeException("Parameter 'sessionBasedModelsClientSocketSelection' only accepts 'SessionIdHighPart'/'SessionIdOptionalPart'/'RoundRobin' as parameter values", ANNA_FILE_LOCATION); - } + if (sessionBasedModelsType == "RoundRobin") { + sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::RoundRobin; + } + else if (sessionBasedModelsType == "SessionIdOptionalPart") { + sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdOptionalPart; + } + else if (sessionBasedModelsType == "SessionIdHighPart") { + sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdHighPart; + } + else if (sessionBasedModelsType == "SessionIdLowPart") { + sessionBasedModelsTypeEnum = anna::diameter::comm::Entity::SessionBasedModelsType::SessionIdLowPart; + } + else { + throw anna::RuntimeException("Parameter 'sessionBasedModelsClientSocketSelection' only accepts 'SessionIdLowPart'/'SessionIdHighPart'/'SessionIdOptionalPart'/'RoundRobin' as parameter values", ANNA_FILE_LOCATION); + } } int retransmissions = retries ? retries->getIntegerValue() : 0; @@ -338,19 +352,21 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp throw anna::RuntimeException("Parameter 'retries' must be non-negative", ANNA_FILE_LOCATION); } - // Create new Node instance ///////////////////////////////////////////////////////////////// - a_workingNode = new OriginHost(originHost->getValue(), applicationId, bpd); - MyDiameterEngine *commEngine = a_workingNode->getMyDiameterEngine(); ///////////////////////////////////////////////////////////////////////////////////////////// - - // Assignments: + // Diameter communication engine: + std::string commEngineName = originHost->getValue() + "_DiameterCommEngine"; + MyDiameterEngine *commEngine = new MyDiameterEngine(commEngineName.c_str(), bpd); + commEngine->setAutoBind(false); // allow to create client-sessions without binding them, in order to set timeouts. commEngine->setMaxConnectionDelay(tcpConnectDelayMs); commEngine->setWatchdogPeriod(watchdogPeriodMs); + commEngine->setOriginHostName(originHost->getValue()); + if (originRealm) commEngine->setOriginRealmName(originRealm->getValue()); + + // Origin host node: + a_workingNode = new anna::diameter::comm::OriginHost((anna::diameter::comm::Engine*)commEngine, applicationId); a_workingNode->setRequestRetransmissions(retransmissions); + ///////////////////////////////////////////////////////////////////////////////////////////// - // Realm information: - commEngine->setOriginHost(originHost->getValue()); - if (originRealm) commEngine->setOriginRealm(originRealm->getValue()); // Diameter entity: if(entity) { @@ -367,7 +383,7 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp // Register one entity for this engine: a_workingNode->createEntity(entity->getValue(), ceaTimeoutMs, answersTimeoutMs); - a_workingNode->getEntity()->setSessionBasedModelsType(sessionBasedModelsType); + a_workingNode->getEntity()->setSessionBasedModelsType(sessionBasedModelsTypeEnum); a_workingNode->getEntity()->setBalance(balance ? (balance->getValue() == "yes") : false); // for sendings if (eventOperation) a_workingNode->getEntity()->bind(); } @@ -379,12 +395,12 @@ void Launcher::servicesFromXML(const anna::xml::Node* servicesNode, bool eventOp std::string ceaPathfile = cea ? cea->getValue() : ""; int sessions = diameterServerSessions ? diameterServerSessions->getIntegerValue() : 1; - a_workingNode->startDiameterServer(diameterServer->getValue(), sessions, allowedInactivityTimeMs, answersTimeoutMs, ceaPathfile); + a_workingNode->createDiameterServer(diameterServer->getValue(), sessions, allowedInactivityTimeMs, answersTimeoutMs, ceaPathfile); } // Logs: if (!allLogsDisabled) { - std::string host = commEngine->getOriginHost(); + std::string host = commEngine->getOriginHostName(); std::string s_log = host + ".launcher.log"; if (log) s_log = log->getValue(); bool b_splitLog = (splitLog ? (splitLog->getValue() == "yes") : false); bool b_detailedLog = (detailedLog ? (detailedLog->getValue() == "yes") : false); @@ -505,20 +521,20 @@ bool Launcher::setWorkingNode(const std::string &name) throw() { ); } else { - a_workingNode = const_cast(nodeIt->second); + a_workingNode = const_cast(nodeIt->second); result = true; } return result; } -OriginHost *Launcher::getOriginHost(const std::string &oh) const throw(anna::RuntimeException) { +anna::diameter::comm::OriginHost *Launcher::getOriginHost(const std::string &oh) const throw(anna::RuntimeException) { origin_hosts_it it = a_originHosts.find(oh); if (it != a_originHosts.end()) return it->second; throw anna::RuntimeException(anna::functions::asString("There is no origin host registered as '%s' (set Origin-Host avp correctly or force a specific host with 'node' operation)", oh.c_str()), ANNA_FILE_LOCATION); } -OriginHost *Launcher::getOriginHost(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException) { +anna::diameter::comm::OriginHost *Launcher::getOriginHost(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException) { std::string originHost = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue(); return (getOriginHost(originHost)); } @@ -528,7 +544,14 @@ void Launcher::updateOperatedOriginHostWithMessage(const anna::diameter::codec:: a_operatedHost = getOriginHost(message); } -OriginHost *Launcher::getOperatedHost() const throw(anna::RuntimeException) { +anna::diameter::comm::OriginHost *Launcher::getWorkingNode() const throw(anna::RuntimeException) { + if(!a_workingNode) + throw anna::RuntimeException("Working node not identified (try to load services)", ANNA_FILE_LOCATION); + + return a_workingNode; +} + +anna::diameter::comm::OriginHost *Launcher::getOperatedHost() const throw(anna::RuntimeException) { if(!a_operatedHost) throw anna::RuntimeException("Node not identified (try to force a specific Origin-Host with 'node' operation)", ANNA_FILE_LOCATION); @@ -536,21 +559,21 @@ OriginHost *Launcher::getOperatedHost() const throw(anna::RuntimeException) { } MyDiameterEntity *Launcher::getOperatedEntity() const throw(anna::RuntimeException) { - MyDiameterEntity *result = getOperatedHost()->getEntity(); + MyDiameterEntity *result = (MyDiameterEntity *)(getOperatedHost()->getEntity()); if (!result) throw anna::RuntimeException("No entity configured for the operated node", ANNA_FILE_LOCATION); return result; } MyLocalServer *Launcher::getOperatedServer() const throw(anna::RuntimeException) { - MyLocalServer *result = getOperatedHost()->getDiameterServer(); + MyLocalServer *result = (MyLocalServer *)(getOperatedHost()->getDiameterServer()); if (!result) throw anna::RuntimeException("No local server configured for the operated node", ANNA_FILE_LOCATION); return result; } MyDiameterEngine *Launcher::getOperatedEngine() const throw(anna::RuntimeException) { - return getOperatedHost()->getMyDiameterEngine(); // never will be NULL + return (MyDiameterEngine *)getOperatedHost()->getCommEngine(); // never will be NULL } void Launcher::initialize() @@ -560,7 +583,7 @@ throw(anna::RuntimeException) { anna::comm::Communicator::WorkMode::_v workMode(anna::comm::Communicator::WorkMode::Single); a_communicator = new MyCommunicator(workMode); a_timeEngine = new anna::timex::Engine((anna::Millisecond)600000, a_admlMinResolution); - TestManager::instantiate().setTimerController(a_timeEngine); + anna::testing::TestManager::instantiate().setTimerController(a_timeEngine); // Counters record procedure: const char *varname = "cntRecordPeriod"; @@ -575,7 +598,7 @@ throw(anna::RuntimeException) { // Testing framework: std::string tmDir = "."; if(cl.exists("tmDir")) tmDir = cl.getValue("tmDir"); - TestManager::instantiate().setReportsDirectory(tmDir); + anna::testing::TestManager::instantiate().setReportsDirectory(tmDir); // Tracing: if(cl.exists("trace")) @@ -755,7 +778,7 @@ throw(anna::RuntimeException) { // Start client connections ////////////////////////////////////////////////////////////////////////////////// MyDiameterEntity *entity; for (origin_hosts_it it = a_originHosts.begin(); it != a_originHosts.end(); it++) { - entity = it->second->getEntity(); + entity = (MyDiameterEntity *)(it->second->getEntity()); if (entity) entity->bind(); } @@ -796,11 +819,11 @@ bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBl void Launcher::resetStatistics() throw() { if (a_workingNode) { - a_workingNode->getMyDiameterEngine()->resetStatistics(); + a_workingNode->getCommEngine()->resetStatistics(); } else { for (origin_hosts_it it = a_originHosts.begin(); it != a_originHosts.end(); it++) { - it->second->getMyDiameterEngine()->resetStatistics(); + it->second->getCommEngine()->resetStatistics(); } } } @@ -1525,8 +1548,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons anna::diameter::codec::Message codecMsg; // auxiliary codec message // Singletons: - CommandLine& cl(anna::CommandLine::instantiate()); - TestManager &testManager = TestManager::instantiate(); + anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate(); /////////////////////////////////////////////////////////////////// @@ -1539,7 +1561,14 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons std::string args = ((operation.find("dynamic|") == 0) && (op_size > 8)) ? operation.substr(8) : ""; if (args == "" && op_size != 7) throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); - p.execute(args, response_content); + try { + p.execute(args, response_content, getWorkingNode()); + } + catch(anna::RuntimeException &ex) { + ex.trace(); + response_content = ex.asString(); + return; + } return; } @@ -1789,7 +1818,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons try { if(getOperatedHost()->logEnabled()) codecMsg.decode(db_aux); } catch(anna::RuntimeException &ex) { ex.trace(); } } - bool success = getOperatedEntity()->send(msg, cl.exists("balance")); + bool success = getOperatedEntity()->send(msg); getOperatedHost()->releaseCommMessage(msg); // Detailed log: @@ -2033,7 +2062,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); int id = ((param2 != "") ? atoi(param2.c_str()) : -1); - TestCase *testCase = testManager.findTestCase(id); + anna::testing::TestCase *testCase = testManager.findTestCase(id); if (testCase) { response_content = testCase->asXMLString(); @@ -2059,7 +2088,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION); int id = atoi(param3.c_str()); - TestCase *testCase = testManager.findTestCase(id); + anna::testing::TestCase *testCase = testManager.findTestCase(id); if (testCase) { if (amount == -1) { testCase->makeInteractive(false); @@ -2090,7 +2119,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); int id = ((param3 != "") ? atoi(param3.c_str()) : -1); - TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL); + anna::testing::TestCase *testCase = ((id != -1) ? testManager.findTestCase(id) : NULL); if (testCase) { bool done = testCase->reset((param2 == "hard") ? true:false); @@ -2259,7 +2288,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons } else if (param1 == "clear") { getOperatedServer()->getReactingAnswers()->clear(); } else if (param1 == "dump") { - getOperatedServer()->getReactingAnswers()->dump(); + getOperatedServer()->getReactingAnswers()->dump("programmed_answer"); } else { codecMsg.loadXML(param1); updateOperatedOriginHostWithMessage(codecMsg); @@ -2285,7 +2314,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons } else if (param1 == "clear") { getOperatedEntity()->getReactingAnswers()->clear(); } else if (param1 == "dump") { - getOperatedEntity()->getReactingAnswers()->dump(); + getOperatedEntity()->getReactingAnswers()->dump("programmed_answer"); } else { codecMsg.loadXML(param1); updateOperatedOriginHostWithMessage(codecMsg); @@ -2333,7 +2362,7 @@ throw() { statsAsXML(result); // Testing: could be heavy if test case reports are enabled - TestManager::instantiate().asXML(result); + anna::testing::TestManager::instantiate().asXML(result); return result; } diff --git a/example/diameter/launcher/Launcher.hpp b/example/diameter/launcher/Launcher.hpp index dcc684d..2d5b3a7 100644 --- a/example/diameter/launcher/Launcher.hpp +++ b/example/diameter/launcher/Launcher.hpp @@ -23,12 +23,7 @@ // Process #include #include -#include - - -#define SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID "##cycleid##" -#define SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID "##testcaseid##" -#define SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID "##teststepid##" +#include namespace anna { @@ -39,20 +34,21 @@ namespace anna { namespace codec { class Engine; } + namespace comm { + class OriginHost; + } } } class TestManager; -class OriginHost; class MyDiameterEntity; class MyLocalServer; class MyDiameterEngine; // OriginHost resources -class OriginHost; -typedef std::map origin_hosts_t; -typedef std::map::const_iterator origin_hosts_it; -typedef std::map::iterator origin_hosts_nc_it; +typedef std::map origin_hosts_t; +typedef std::map::const_iterator origin_hosts_it; +typedef std::map::iterator origin_hosts_nc_it; class Launcher : public anna::comm::Application { @@ -70,14 +66,12 @@ class Launcher : public anna::comm::Application { // Nodes deployment: origin_hosts_t a_originHosts; - OriginHost *a_workingNode; - OriginHost *a_operatedHost; // auxiliary for eventOperation + anna::diameter::comm::OriginHost *a_workingNode; + anna::diameter::comm::OriginHost *a_operatedHost; // auxiliary for eventOperation // comm resources: anna::comm::ServerSocket* a_httpServerSocket; // HTTP - const anna::Millisecond &getADMLMinResolution() const throw() { return a_admlMinResolution; } - std::string getSignalUSR2InputFile() const throw(); std::string getSignalUSR2OutputFile() const throw(); @@ -96,13 +90,14 @@ public: void startServices() throw(anna::RuntimeException); bool setWorkingNode(const std::string &name) throw(); - OriginHost *getOriginHost(const std::string &oh) const throw(anna::RuntimeException); - OriginHost *getOriginHost(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException); + anna::diameter::comm::OriginHost *getOriginHost(const std::string &oh) const throw(anna::RuntimeException); + anna::diameter::comm::OriginHost *getOriginHost(const anna::diameter::codec::Message &message) const throw(anna::RuntimeException); bool uniqueOriginHost() const throw() { return (a_originHosts.size() == 1); } // Operated host for communication resources smart assignment //////////////////////////////////////////////////////////// void updateOperatedOriginHostWithMessage(const anna::diameter::codec::Message &message) throw(anna::RuntimeException); - OriginHost *getOperatedHost() const throw(anna::RuntimeException); + anna::diameter::comm::OriginHost *getWorkingNode() const throw(anna::RuntimeException); + anna::diameter::comm::OriginHost *getOperatedHost() const throw(anna::RuntimeException); MyDiameterEntity *getOperatedEntity() const throw(anna::RuntimeException); MyLocalServer *getOperatedServer() const throw(anna::RuntimeException); MyDiameterEngine *getOperatedEngine() const throw(anna::RuntimeException); diff --git a/example/diameter/launcher/MyDiameterEngine.cpp b/example/diameter/launcher/MyDiameterEngine.cpp deleted file mode 100644 index e5700c0..0000000 --- a/example/diameter/launcher/MyDiameterEngine.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -// Project -#include -#include - -//#include -//#include -//#include -//#include -//#include - -// Process -#include - - -void MyDiameterEngine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() { - - LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEngine", "readCEA", ANNA_FILE_LOCATION)); - - // Default implementation: - if (a_ceaPathfile == "") - return anna::diameter::comm::Engine::readCEA(cea, cer); - -// if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Answer) { -//throw anna::RuntimeException("The message provided as 'CEA' is not a Capabilities-Exchange-Answer", -//} - - anna::diameter::codec::Message diameterCEA(getBaseProtocolCodecEngine()); - try { - diameterCEA.loadXML(a_ceaPathfile); - diameterCEA.setHopByHop(anna::diameter::codec::functions::getHopByHop(cer)); - diameterCEA.setEndToEnd(anna::diameter::codec::functions::getEndToEnd(cer)); - cea = diameterCEA.code(); - - } catch(anna::RuntimeException &ex) { - ex.trace(); - LOGWARNING(anna::Logger::warning("CEA file not found or unable to parse. Encoding harcoded default version ...", ANNA_FILE_LOCATION)); - //return anna::diameter::comm::Engine::readCEA(cea, cer); - // will fail with empty cea - } -} - diff --git a/example/diameter/launcher/MyDiameterEngine.hpp b/example/diameter/launcher/MyDiameterEngine.hpp index f304d0d..4fe049f 100644 --- a/example/diameter/launcher/MyDiameterEngine.hpp +++ b/example/diameter/launcher/MyDiameterEngine.hpp @@ -35,8 +35,6 @@ public: // Default implementation is enough // void readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw() {;} // DPA is not replied - void setCEA(const std::string &ceaPathfile) throw() { a_ceaPathfile = ceaPathfile; } - void readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw(); // void readDWA(anna::DataBlock &dwa, const anna::DataBlock & dwr) throw() {;} // DWA is not replied private: @@ -57,8 +55,6 @@ private: MyLocalServer* aux = static_cast (localServer); a_localServersRecycler.release(aux); } - - std::string a_ceaPathfile; // path file to optional CEA (diameter local server configuration) }; #endif diff --git a/example/diameter/launcher/MyDiameterEntity.cpp b/example/diameter/launcher/MyDiameterEntity.cpp index 5e78ab3..6027bf0 100644 --- a/example/diameter/launcher/MyDiameterEntity.cpp +++ b/example/diameter/launcher/MyDiameterEntity.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -20,8 +21,7 @@ #include #include #include -#include -#include +#include void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::ClientSession* clientSession, anna::diameter::comm::Message *request) throw() { @@ -33,7 +33,7 @@ void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::Cl // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(request->getBody()); LOGDEBUG @@ -52,54 +52,12 @@ void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::Cl } -int MyDiameterEntity::readSocketId(const anna::diameter::comm::Message* message, int maxClientSessions) const throw() { - - if(a_sessionBasedModelsType == "RoundRobin") return -1; // IEC also would return -1 - - try { - // Service-Context-Id: - anna::diameter::helpers::dcca::ChargingContext::_v chargingContext; - std::string scid = anna::diameter::helpers::dcca::functions::getServiceContextId(message->getBody(), chargingContext); - - switch(chargingContext) { - case anna::diameter::helpers::dcca::ChargingContext::Data: - case anna::diameter::helpers::dcca::ChargingContext::Voice: - case anna::diameter::helpers::dcca::ChargingContext::Content: { - // Session-Id: ';;[;="">]' - std::string sid = anna::diameter::helpers::base::functions::getSessionId(message->getBody()); - std::string diameterIdentity, optional; - anna::U32 high, low; - anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional); - - if(a_sessionBasedModelsType == "SessionIdLowPart") return (low % maxClientSessions); - - if(a_sessionBasedModelsType == "SessionIdHighPart") return (high % maxClientSessions); - - if(a_sessionBasedModelsType == "SessionIdOptionalPart") return (atoi(optional.c_str()) % maxClientSessions); - } - case anna::diameter::helpers::dcca::ChargingContext::SMS: - case anna::diameter::helpers::dcca::ChargingContext::MMS: - case anna::diameter::helpers::dcca::ChargingContext::Unknown: - default: - return -1; // IEC model and Unknown traffic types - } - } catch(anna::RuntimeException &ex) { - LOGDEBUG( - std::string msg = ex.getText(); - msg += " | Round-robin between sessions will be used to send"; - anna::Logger::debug(msg, ANNA_FILE_LOCATION); - ); - } - - return -1; -} - void MyDiameterEntity::eventRequest(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message) throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequest", ANNA_FILE_LOCATION)); // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message); @@ -154,7 +112,7 @@ throw(anna::RuntimeException) { // not found: forward to client (if exists) // Forward to client: - MyLocalServer *localServer = my_node->getDiameterServer(); + MyLocalServer *localServer = (MyLocalServer *)my_node->getDiameterServer(); if(localServer && (cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CER */) { try { @@ -176,14 +134,14 @@ throw(anna::RuntimeException) { } // Testing: - TestManager::instantiate().receiveMessage(message, my_node, clientSession); + anna::testing::TestManager::instantiate().receiveMessage(message, clientSession); } void MyDiameterEntity::eventResponse(const anna::diameter::comm::Response &response) throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventResponse", ANNA_FILE_LOCATION)); Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost *my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); anna::diameter::comm::ClassCode::_v code = response.getClassCode(); anna::diameter::comm::Response::ResultCode::_v result = response.getResultCode(); anna::diameter::comm::Message* request = const_cast(response.getRequest()); @@ -239,7 +197,7 @@ throw(anna::RuntimeException) { } // Forward to client: - MyLocalServer *localServer = my_node->getDiameterServer(); + MyLocalServer *localServer = (MyLocalServer *)my_node->getDiameterServer(); if(localServer && (request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CEA */) { anna::diameter::comm::Message *msg; @@ -251,7 +209,7 @@ throw(anna::RuntimeException) { bool success = localServer->send(msg, request->getRequestServerSessionKey()); // Detailed log: - anna::diameter::comm::ServerSession *usedServerSession = my_node->getMyDiameterEngine()->findServerSession(request->getRequestServerSessionKey()); + anna::diameter::comm::ServerSession *usedServerSession = my_node->getCommEngine()->findServerSession(request->getRequestServerSessionKey()); std::string detail = usedServerSession ? usedServerSession->asString() : ""; // esto no deberia ocurrir if(my_node->logEnabled()) { @@ -271,7 +229,7 @@ throw(anna::RuntimeException) { if(isOK || contextExpired) my_node->sendBurstMessage(); // Testing: - TestManager::instantiate().receiveMessage(*message, my_node, clientSession); + if(isOK) anna::testing::TestManager::instantiate().receiveMessage(*message, clientSession); } void MyDiameterEntity::eventUnknownResponse(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message) @@ -279,7 +237,7 @@ throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventUnknownResponse", ANNA_FILE_LOCATION)); // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message); LOGDEBUG @@ -302,7 +260,7 @@ throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventDPA", ANNA_FILE_LOCATION)); // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message); LOGDEBUG @@ -320,5 +278,5 @@ throw(anna::RuntimeException) { if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfe", clientSession->asString()); // Testing: - TestManager::instantiate().receiveMessage(message, my_node, clientSession); + anna::testing::TestManager::instantiate().receiveMessage(message, clientSession); } diff --git a/example/diameter/launcher/MyDiameterEntity.hpp b/example/diameter/launcher/MyDiameterEntity.hpp index 73494b5..a63bf28 100644 --- a/example/diameter/launcher/MyDiameterEntity.hpp +++ b/example/diameter/launcher/MyDiameterEntity.hpp @@ -11,9 +11,8 @@ // Project #include +#include -// Process -#include namespace anna { namespace diameter { @@ -25,33 +24,18 @@ namespace anna { class MyDiameterEntity : public anna::diameter::comm::Entity { - bool a_balance; // Balance over entity servers instead of doing standard behaviour (first primary, secondary if fails, etc.). Default: false. - std::string a_sessionBasedModelsType; - void eventRequestRetransmission(const anna::diameter::comm::ClientSession *, anna::diameter::comm::Message*) throw(); void eventResponse(const anna::diameter::comm::Response&) throw(anna::RuntimeException); void eventRequest(anna::diameter::comm::ClientSession *, const anna::DataBlock&) throw(anna::RuntimeException); void eventUnknownResponse(anna::diameter::comm::ClientSession *, const anna::DataBlock&) throw(anna::RuntimeException); void eventDPA(anna::diameter::comm::ClientSession *, const anna::DataBlock&) throw(anna::RuntimeException); - // Reimplementation - int readSocketId(const anna::diameter::comm::Message* message, int maxClientSessions) const throw(); - public: - MyDiameterEntity() { - a_balance = false; - a_sessionBasedModelsType = "SessionIdLowPart"; - } - - ProgrammedAnswers a_reactingAnswers; - ProgrammedAnswers *getReactingAnswers() throw() { return (ProgrammedAnswers *)&a_reactingAnswers; } + MyDiameterEntity() {;} - // Additional configuration parameters: - void setSessionBasedModelsType(const std::string &type) throw() { a_sessionBasedModelsType = type; } - void setBalance(bool balance) throw() { a_balance = balance; } - const std::string & getSessionBasedModelsType() const throw() { return a_sessionBasedModelsType; } - bool getBalance() const throw() { return a_balance; } + anna::diameter::codec::MessagesDeque a_reactingAnswers; + anna::diameter::codec::MessagesDeque *getReactingAnswers() throw() { return (anna::diameter::codec::MessagesDeque*)&a_reactingAnswers; } }; #endif diff --git a/example/diameter/launcher/MyLocalServer.cpp b/example/diameter/launcher/MyLocalServer.cpp index 7d34b46..c4f5db7 100644 --- a/example/diameter/launcher/MyLocalServer.cpp +++ b/example/diameter/launcher/MyLocalServer.cpp @@ -14,14 +14,14 @@ #include #include #include +#include // Process #include #include #include #include -#include -#include +#include void MyLocalServer::eventRequest(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message) @@ -29,7 +29,7 @@ throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventRequest", ANNA_FILE_LOCATION)); // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost *my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); anna::diameter::codec::Engine *codecEngine = my_node->getCodecEngine(); // CommandId: @@ -59,7 +59,7 @@ throw(anna::RuntimeException) { msg->forwardEndToEnd(); // end-to-end will be kept msg->setBody(message); msg->setRequestServerSessionKey(serverSession->getKey()); - bool success = entity->send(msg, my_node->getEntity()->getBalance()); + bool success = entity->send(msg); // Detailed log: if(my_node->logEnabled()) { @@ -71,7 +71,7 @@ throw(anna::RuntimeException) { // Testing: - TestManager::instantiate().receiveMessage(message, my_node, serverSession); + anna::testing::TestManager::instantiate().receiveMessage(message, serverSession); return; } @@ -90,7 +90,7 @@ throw(anna::RuntimeException) { // Decode try { codecMsg.decode(message, answer_message); } catch(anna::RuntimeException &ex) { ex.trace(); } - answer_message->setStandardToAnswer(codecMsg, my_node->getMyDiameterEngine()->getOriginHost(), my_node->getMyDiameterEngine()->getOriginRealm()); + answer_message->setStandardToAnswer(codecMsg, my_node->getCommEngine()->getOriginHostName(), my_node->getCommEngine()->getOriginRealmName()); analysisOK = (answer_message->getResultCode() == anna::diameter::helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS); } @@ -103,7 +103,7 @@ throw(anna::RuntimeException) { } else { // Testing: - TestManager::instantiate().receiveMessage(message, my_node, serverSession); + anna::testing::TestManager::instantiate().receiveMessage(message, serverSession); return; // nothing done } @@ -136,14 +136,14 @@ throw(anna::RuntimeException) { if(analysisOK && programmed) a_reactingAnswers.nextMessage(code); // Testing: - TestManager::instantiate().receiveMessage(message, my_node, serverSession); + anna::testing::TestManager::instantiate().receiveMessage(message, serverSession); } void MyLocalServer::eventResponse(const anna::diameter::comm::Response &response) throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventResponse", ANNA_FILE_LOCATION)); Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); anna::diameter::comm::ClassCode::_v code = response.getClassCode(); anna::diameter::comm::Response::ResultCode::_v result = response.getResultCode(); anna::diameter::comm::Message* request = const_cast(response.getRequest()); @@ -198,7 +198,7 @@ throw(anna::RuntimeException) { anna::diameter::comm::Entity *entity = my_node->getEntity(); if(entity) { - anna::diameter::comm::ClientSession *usedClientSession = my_node->getMyDiameterEngine()->findClientSession(request->getRequestClientSessionKey()); + anna::diameter::comm::ClientSession *usedClientSession = my_node->getCommEngine()->findClientSession(request->getRequestClientSessionKey()); std::string detail; if(my_node->logEnabled()) detail = usedClientSession ? usedClientSession->asString() : ""; // this should not happen @@ -229,7 +229,7 @@ throw(anna::RuntimeException) { } // Testing: - TestManager::instantiate().receiveMessage(*message, my_node, serverSession); + if(isOK) anna::testing::TestManager::instantiate().receiveMessage(*message, serverSession); } void MyLocalServer::eventUnknownResponse(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message) @@ -237,7 +237,7 @@ throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventUnknownResponse", ANNA_FILE_LOCATION)); // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost *my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message); LOGDEBUG @@ -259,7 +259,7 @@ throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventDPA", ANNA_FILE_LOCATION)); // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); - OriginHost * my_node = my_app.getOriginHost(getEngine()->getOriginHost()); + anna::diameter::comm::OriginHost *my_node = my_app.getOriginHost(getEngine()->getOriginHostName()); // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message); LOGDEBUG @@ -277,5 +277,5 @@ throw(anna::RuntimeException) { if(my_node->logEnabled()) my_node->writeLogFile(message, "recvfc", serverSession->asString()); // Testing: - TestManager::instantiate().receiveMessage(message, my_node, serverSession); + anna::testing::TestManager::instantiate().receiveMessage(message, serverSession); } diff --git a/example/diameter/launcher/MyLocalServer.hpp b/example/diameter/launcher/MyLocalServer.hpp index 831e15b..7f66703 100644 --- a/example/diameter/launcher/MyLocalServer.hpp +++ b/example/diameter/launcher/MyLocalServer.hpp @@ -11,9 +11,8 @@ // Project #include +#include -// Process -#include namespace anna { namespace diameter { @@ -32,8 +31,8 @@ class MyLocalServer : public anna::diameter::comm::LocalServer { public: - ProgrammedAnswers a_reactingAnswers; - ProgrammedAnswers *getReactingAnswers() throw() { return (ProgrammedAnswers *)&a_reactingAnswers; } + anna::diameter::codec::MessagesDeque a_reactingAnswers; + anna::diameter::codec::MessagesDeque *getReactingAnswers() throw() { return (anna::diameter::codec::MessagesDeque*)&a_reactingAnswers; } }; #endif diff --git a/example/diameter/launcher/OriginHost.cpp b/example/diameter/launcher/OriginHost.cpp deleted file mode 100644 index 195edad..0000000 --- a/example/diameter/launcher/OriginHost.cpp +++ /dev/null @@ -1,426 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -// Standard -#include - -// Project -#include -#include -#include -#include -#include -#include - -// Process -#include -#include - - -namespace anna { - namespace diameter { - namespace stack { - class Dictionary; - } - } -} - -OriginHost::OriginHost(const std::string &originHost, unsigned int applicationId, const anna::diameter::stack::Dictionary *baseProtocolDictionary) : - a_originHost(originHost), a_applicationId(applicationId) { - - std::string commEngineName = a_originHost + "_DiameterCommEngine"; - a_commEngine = new MyDiameterEngine(commEngineName.c_str(), baseProtocolDictionary); - a_commEngine->setAutoBind(false); // allow to create client-sessions without binding them, in order to set timeouts. - a_codecEngine = anna::diameter::codec::EngineManager::instantiate().getCodecEngine(applicationId); // i know, this is going to exist (getCodecEngine is not null) - - a_logFile = ""; - a_burstLogFile = ""; - a_splitLog = false; - a_detailedLog = false; - a_dumpLog = false; - a_entity = NULL; - a_diameterServer = NULL; - - // Comm resources: - a_allowedInactivityTime = (anna::Millisecond)90000; - a_tcpConnectDelay = (anna::Millisecond)200; - a_answersTimeout = (anna::Millisecond)10000; - a_ceaTimeout = (anna::Millisecond)10000; - a_watchdogPeriod = (anna::Millisecond)30000; - a_requestRetransmissions = 0; - - // Burst - a_burstCycle = 1; - a_burstRepeat = false; - a_burstActive = false; - a_burstLoadIndx = 0; - a_burstDeliveryIt = a_burstMessages.begin(); - a_otaRequest = 0; - a_burstPopCounter = 0; -} - - -void OriginHost::createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException) { - - anna::socket_v servers = anna::functions::getSocketVectorFromString(entityRepresentation); - std::string entityDescription = "Launcher diameter entity for "; entityDescription += a_originHost; - a_entity = (MyDiameterEntity*)(a_commEngine->createEntity(servers, entityDescription)); - a_entity->setClassCodeTimeout(anna::diameter::comm::ClassCode::Bind, bindTimeout); - a_entity->setClassCodeTimeout(anna::diameter::comm::ClassCode::ApplicationMessage, applicationTimeout); -} - -void OriginHost::startDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout, const anna::Millisecond &applicationTimeout, const std::string &ceaPathfile) throw(anna::RuntimeException) { - - //if(sessions <= 0) return; negative implies no limit for accepted connections - - std::string address; int port; - anna::functions::getAddressAndPortFromSocketLiteral(serverRepresentation, address, port); - std::string serverDescription = "Launcher diameter local server for "; serverDescription += a_originHost; - a_commEngine->setCEA(ceaPathfile); - a_diameterServer = (MyLocalServer*)(a_commEngine->createLocalServer(address, port, sessions)); - // we could set sessions = 0, and after application run(), use setMaxConnections(real sessions) - // over the local server in order to start it. - - a_diameterServer->setDescription(serverDescription); - a_diameterServer->setAllowedInactivityTime(inactivityTimeout); - a_diameterServer->setClassCodeTimeout(anna::diameter::comm::ClassCode::ApplicationMessage, applicationTimeout); -} - -anna::diameter::comm::Message *OriginHost::createCommMessage() throw(anna::RuntimeException) { - anna::diameter::comm::Message *result = a_commMessages.create(); - result->setRetries(a_requestRetransmissions); - if (a_requestRetransmissions > 0) result->setOnExpiry(anna::diameter::comm::Message::OnExpiry::Retransmit); - return result; -} - - -void OriginHost::releaseCommMessage(anna::diameter::comm::Message *msg) throw() { - a_commMessages.release(msg); -} - - -void OriginHost::writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail) const throw() { - anna::diameter::codec::Message codecMsg; - try { codecMsg.decode(db); } catch(anna::RuntimeException &ex) { ex.trace(); } - writeLogFile(codecMsg, logExtension, detail); -} - -// Already decoded: -void OriginHost::writeLogFile(const anna::diameter::codec::Message &decodedMessage, const std::string &logExtension, const std::string &detail) const throw() { - // Open target file: - std::string targetFile = a_logFile; - - if(a_splitLog) { - targetFile += "."; - targetFile += logExtension; - } - - std::ofstream out(targetFile.c_str(), std::ifstream::out | std::ifstream::app); - // Set text to dump: - std::string title = "["; - title += logExtension; - title += "]"; - // Build complete log: - std::string log = "\n"; - std::string xml = decodedMessage.asXMLString(); - - - if(a_detailedLog) { - anna::time::Date now; - now.setNow(); - title += " "; - title += now.asString(); - log += anna::functions::highlight(title, anna::functions::TextHighlightMode::OverAndUnderline); - log += xml; - log += "\n"; - log += anna::functions::highlight("Used resource"); - log += detail; - log += "\n"; - } else { - log += title; - log += "\n"; - log += xml; - log += "\n"; - } - - if(a_dumpLog) { - // .......xml - std::string name = anna::functions::asString((anna::Millisecond)anna::functions::millisecond()); - name += "."; - name += getMyDiameterEngine()->getOriginHost(); - name += "."; - name += anna::functions::asString(decodedMessage.getHopByHop()); - name += "."; - name += anna::functions::asString(decodedMessage.getEndToEnd()); - name += "."; - name += anna::functions::asString(decodedMessage.getId().first); - name += "."; - name += ((decodedMessage.getId().second) ? "request.":"answer."); - name += logExtension; - name += ".xml"; - std::ofstream outMsg(name.c_str(), std::ifstream::out | std::ifstream::app); - outMsg.write(xml.c_str(), xml.size()); - outMsg.close(); - } - - // Write and close - out.write(log.c_str(), log.size()); - out.close(); -} - -void OriginHost::writeBurstLogFile(const std::string &buffer) throw() { - std::ofstream out(a_burstLogFile.c_str(), std::ifstream::out | std::ifstream::app); - out.write(buffer.c_str(), buffer.size()); - out.close(); // close() will be called when the object is destructed (i.e., when it goes out of scope). - // you'd call close() only if you indeed for some reason wanted to close the filestream - // earlier than it goes out of scope. -} - -int OriginHost::clearBurst() throw() { - int size = a_burstMessages.size(); - - if(size) { - std::map::const_iterator it; - std::map::const_iterator it_min(a_burstMessages.begin()); - std::map::const_iterator it_max(a_burstMessages.end()); - - for(it = it_min; it != it_max; it++) releaseCommMessage((*it).second); - - a_burstMessages.clear(); - } else { - std::string msg = "Burst list already empty. Nothing done"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - } - - a_burstActive = false; - a_burstLoadIndx = 0; - a_burstDeliveryIt = a_burstMessages.begin(); - return size; -} - -int OriginHost::loadBurstMessage(const anna::DataBlock & db) throw(anna::RuntimeException) { - anna::diameter::comm::Message *msg = createCommMessage(); - msg->setBody(db); - a_burstMessages[a_burstLoadIndx++] = msg; - return (a_burstLoadIndx - 1); -} - -int OriginHost::stopBurst() throw() { - if(!a_burstActive) { - std::string msg = "Burst launch is already stopped. Nothing done"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -1; - } - - a_burstActive = false; - // Remaining on cycle: - return (a_burstMessages.size() - (*a_burstDeliveryIt).first); -} - -int OriginHost::popBurst(int releaseAmount) throw() { - if(!a_burstActive) { - std::string msg = "Burst launch is stopped. Nothing done"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -1; - } - - if(releaseAmount < 1) { - std::string msg = "No valid release amount is specified. Ignoring burst pop"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -2; - } - - int currentOTArequests = a_entity->getOTARequests(); - a_burstPopCounter = (releaseAmount > currentOTArequests) ? currentOTArequests : releaseAmount; - return a_burstPopCounter; -} - -int OriginHost::pushBurst(int loadAmount) throw() { - if(a_burstMessages.size() == 0) { - std::string msg = "Burst data not found (empty list). Ignoring burst launch"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -1; - } - - if(loadAmount < 1) { - std::string msg = "No valid load amount is specified. Ignoring burst push"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -2; - } - - a_burstActive = true; - int count; - - for(count = 0; count < loadAmount; count++) - if(!sendBurstMessage()) break; - - return count; -} - -int OriginHost::sendBurst(int loadAmount) throw() { - if(a_burstMessages.size() == 0) { - std::string msg = "Burst data not found (empty list). Ignoring burst launch"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -1; - } - - if(loadAmount < 1) { - std::string msg = "No valid load amount is specified. Ignoring burst send"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -2; - } - - int count; - - for(count = 0; count < loadAmount; count++) - if(!sendBurstMessage(true /* anyway */)) break; - - return count; -} - -int OriginHost::startBurst(int initialLoad) throw() { - if(initialLoad < 1) { - std::string msg = "No initial load is specified. Ignoring burst start"; - std::cout << msg << std::endl; - LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); - return -2; - } - - a_burstActive = true; - a_burstCycle = 1; - a_burstDeliveryIt = a_burstMessages.begin(); - return (pushBurst(initialLoad)); -} - -bool OriginHost::sendBurstMessage(bool anyway) throw() { - if(!anyway && !burstActive()) return false; - - if(a_burstPopCounter > 0) { - if(burstLogEnabled()) writeBurstLogFile("x"); - - a_burstPopCounter--; - return false; - } - - if(a_burstDeliveryIt == a_burstMessages.end()) { - a_burstDeliveryIt = a_burstMessages.begin(); - - if(!anyway) { - if(a_burstRepeat) { - a_burstCycle++; - - if(burstLogEnabled()) writeBurstLogFile(anna::functions::asString("\nCompleted burst cycle. Starting again (repeat mode) on cycle %d.\n", a_burstCycle)); - } else { - if(burstLogEnabled()) writeBurstLogFile("\nCompleted burst cycle. Burst finished (repeat mode disabled).\n"); - - stopBurst(); - return false; - } - } - } - - anna::diameter::comm::Message *msg = (*a_burstDeliveryIt).second; - int order = (*a_burstDeliveryIt).first + 1; - a_burstDeliveryIt++; - bool dot = true; - // sending - bool result = a_entity->send(msg, anna::CommandLine::instantiate().exists("balance")); - - if(burstLogEnabled()) { - if(a_burstMessages.size() >= 100) - dot = (order % (a_burstMessages.size() / 100)); - - if(dot) { - writeBurstLogFile("."); - } else { - writeBurstLogFile(anna::functions::asString(" %d", order)); - int otaReqs = a_entity->getOTARequests(); - - if(result && (otaReqs != a_otaRequest)) { - // false if was a sending after an answer received (no OTA change in this case) - // true after push and pop operations - a_otaRequest = otaReqs; - writeBurstLogFile(anna::functions::asString("[OTA %d]", a_otaRequest)); - } - } - } - - // Detailed log: - if(logEnabled()) { - anna::diameter::comm::Server *usedServer = a_entity->getLastUsedResource(); - anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL; - std::string detail = usedClientSession ? usedClientSession->asString() : ""; // esto no deberia ocurrir - writeLogFile(msg->getBody(), (result ? "sent2e" : "send2eError"), detail); // el del nodo de trabajo - } - - return result; -} - -std::string OriginHost::lookBurst(int order) const throw() { - - if (order == -1) order = a_burstDeliveryIt->first; - - std::string result = "No message found for order provided ("; - result += anna::functions::asString(order); - result += ")"; - std::map::const_iterator it = a_burstMessages.find(order - 1); - - if(it != a_burstMessages.end()) { - anna::diameter::codec::Message codecMsg; - try { codecMsg.decode((*it).second->getBody()); result = codecMsg.asXMLString(); } catch(anna::RuntimeException &ex) { ex.trace(); } - } - - return result; -} - -std::string OriginHost::gotoBurst(int order) throw() { - std::string result = "Position not found for order provided ("; - std::map::iterator it = a_burstMessages.find(order - 1); - - if(it != a_burstMessages.end()) { - a_burstDeliveryIt = it; - result = "Position updated for order provided ("; - } - - result += anna::functions::asString(order); - result += ")"; - return result; -} - -anna::xml::Node* OriginHost::asXML(anna::xml::Node* parent) const -throw() { - anna::xml::Node* result = parent->createChild("OriginHost"); - - result->createAttribute("originHost", a_originHost); - result->createAttribute("ApplicationId", a_applicationId); - result->createAttribute("originRealm", a_commEngine->getOriginRealm()); - result->createAttribute("LogFile", a_logFile); - result->createAttribute("SplitLog", a_splitLog ? "yes" : "no"); - result->createAttribute("DetailedLog", a_detailedLog ? "yes" : "no"); - result->createAttribute("DumpLog", a_dumpLog ? "yes" : "no"); - result->createAttribute("BurstLogFile", a_burstLogFile); - result->createAttribute("RequestRetransmissions", a_requestRetransmissions); - - anna::xml::Node* commEngine = result->createChild("CommEngine"); - a_commEngine->asXML(commEngine); - - return result; -} - -std::string OriginHost::asXMLString() const throw() { - anna::xml::Node root("root"); - return anna::xml::Compiler().apply(asXML(&root)); -} diff --git a/example/diameter/launcher/OriginHost.hpp b/example/diameter/launcher/OriginHost.hpp deleted file mode 100644 index 2ea2f7f..0000000 --- a/example/diameter/launcher/OriginHost.hpp +++ /dev/null @@ -1,131 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_OriginHost_hpp -#define example_diameter_launcher_OriginHost_hpp - -// Standard -#include -#include - -// Project -#include -#include -#include -#include - - -namespace anna { - namespace diameter { - namespace codec { - class Engine; - } - namespace stack { - class Dictionary; - } - namespace comm { - class Message; - } - } -} - - -class MyDiameterEntity; -class MyDiameterEngine; -class MyLocalServer; - - -class OriginHost { - - MyDiameterEngine *a_commEngine; - MyDiameterEntity *a_entity; // we could get it finding the unique instante within comm engine, but it is more comfortable assign here. - MyLocalServer* a_diameterServer; // idem - anna::diameter::codec::Engine *a_codecEngine; - - // resources - int a_requestRetransmissions; - anna::Recycler a_commMessages; - - // main - std::string a_originHost; - unsigned int a_applicationId; - - // Timming - anna::Millisecond a_allowedInactivityTime; - anna::Millisecond a_tcpConnectDelay; - anna::Millisecond a_answersTimeout; - anna::Millisecond a_ceaTimeout; - anna::Millisecond a_watchdogPeriod; - - // Logs & burst - std::string a_logFile; - bool a_splitLog, a_detailedLog, a_dumpLog; - std::string a_burstLogFile; - std::ofstream a_burstLogStream; - int a_burstCycle; - bool a_burstRepeat; - bool a_burstActive; - std::map < int /* dummy, p.e. used for order number */, anna::diameter::comm::Message* > a_burstMessages; - int a_burstLoadIndx; - std::map::const_iterator a_burstDeliveryIt; - int a_otaRequest; - int a_burstPopCounter; - -public: - OriginHost(const std::string &originHost, unsigned int applicationId, const anna::diameter::stack::Dictionary *baseProtocolDictionary); - ~OriginHost() {;} - - const std::string &getName() const throw() { return a_originHost; } - - // Core resources: - MyDiameterEngine* getMyDiameterEngine() const throw() { return a_commEngine; } - unsigned int getApplicationId() const throw() { return a_applicationId; } - anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; } - - void createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException); - MyDiameterEntity *getEntity() const throw() { return a_entity; } - void startDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout, const anna::Millisecond &applicationTimeout, const std::string &ceaPathfile) throw(anna::RuntimeException); - MyLocalServer* getDiameterServer() throw() { return a_diameterServer; } - void setRequestRetransmissions(int r) throw() { if (r >= 0) a_requestRetransmissions = r; } - - // Messages factory: - anna::diameter::comm::Message *createCommMessage() throw(anna::RuntimeException); - void releaseCommMessage(anna::diameter::comm::Message*) throw(); - - // Traffic logs & burst: - void setLogs(const std::string &log, bool splitLog, bool detailedLog, bool dumpLog, const std::string &burstLog) throw() { - a_logFile = log; - a_splitLog = splitLog; - a_detailedLog = detailedLog; - a_dumpLog = dumpLog; - a_burstLogFile = burstLog; - } - - bool logEnabled() const throw() { return (((a_logFile == "") || (a_logFile == "null")) ? false : true); } - void writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail) const throw(); - void writeLogFile(const anna::diameter::codec::Message & decodedMessage, const std::string &logExtension, const std::string &detail) const throw(); - void writeBurstLogFile(const std::string &buffer) throw(); - bool burstLogEnabled() const throw() { return (((a_burstLogFile == "") || (a_burstLogFile == "null")) ? false : true); } - int clearBurst() throw(); // returns removed - int loadBurstMessage(const anna::DataBlock & db) throw(anna::RuntimeException); - void repeatBurst(bool repeat) throw() { a_burstRepeat = repeat; } - int startBurst(int initialLoad) throw(); // return processed on start, or -1 if burst list is empty, -2 if invalid initial load (0 or negative) - int pushBurst(int loadAmount) throw(); // returns pushed (perhaps is less than provided because of no repeat mode and burst list exhausted), or -1 if burst list is empty, -2 if invalid load (0 or negative) - int sendBurst(int loadAmount) throw(); // returns sent (burst always cycled using send), returns -1 if burst list is empty, -2 if invalid load (0 or negative) - int popBurst(int releaseAmount) throw(); // returns popped (perhaps is less than provided because of OTA request), or -1 if burst stopped - int stopBurst() throw(); // returns remaining on cycle, or -1 if burst already stopped - bool burstActive() const throw() { return a_burstActive; } - bool sendBurstMessage(bool anyway = false) throw(); - std::string lookBurst(int order = -1) const throw(); - std::string gotoBurst(int order) throw(); - - anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); - std::string asXMLString() const throw(); -}; - -#endif diff --git a/example/diameter/launcher/ProgrammedAnswers.cpp b/example/diameter/launcher/ProgrammedAnswers.cpp deleted file mode 100644 index b2b89b2..0000000 --- a/example/diameter/launcher/ProgrammedAnswers.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - -// Standard -#include -#include - -// Project -#include -#include -#include - -// Process -#include - - -void ProgrammedAnswers::clear() throw () { - try { - anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate(); - anna::diameter::codec::Engine *engine; - - for (reacting_answers_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) { - anna::diameter::codec::Message *message = *(it->second->begin()); - engine = em.getCodecEngine(message->getApplicationId()); - if (engine) { - engine->releaseMessage(message); - delete (it->second); - } - else { - LOGWARNING(anna::Logger::warning("Cannot release a message for which i don't know the codec engine (check the registered stack id regarding the message application id) !", ANNA_FILE_LOCATION)); - } - } - a_deques.clear(); - } - catch (anna::RuntimeException &ex) { - ex.trace(); - } -} - -void ProgrammedAnswers::dump() throw () { - std::string outfilename, xmlString; - for (reacting_answers_const_iterator it = a_deques.begin(); - it != a_deques.end(); it++) { - int sequence = 1; - for (codec_messages_deque_const_iterator itm = it->second->begin(); - itm != it->second->end(); itm++) { - // programmed_answer.. - outfilename = "programmed_answer."; - outfilename += anna::functions::asString(it->first); - outfilename += "."; - outfilename += anna::functions::asString(sequence++); - outfilename += ".xml"; - std::ofstream outfile(outfilename.c_str(), std::ifstream::out); - xmlString = (*itm)->asXMLString(); - outfile.write(xmlString.c_str(), xmlString.size()); - outfile.close(); - } - } -} - -void ProgrammedAnswers::addMessage(int code, anna::diameter::codec::Message *message) throw () { - if (!message) - return; // just in case - - reacting_answers_const_iterator it = a_deques.find(code); - if (it != a_deques.end()) { - it->second->push_back(message); - } else { - codec_messages_deque *deque = new codec_messages_deque; - a_deques[code] = deque; - deque->push_back(message); - } -} - -anna::diameter::codec::Message* ProgrammedAnswers::getMessage(int code) const throw () { //get the front message (begin()), returns NULL if deque is empty - anna::diameter::codec::Message *result = NULL; - reacting_answers_const_iterator it = a_deques.find(code); - if (it != a_deques.end()) { - if (!it->second->empty()) - result = *(it->second->begin()); - } - return result; -} - -void ProgrammedAnswers::nextMessage(int code) throw () { //pops the deque and release the message (when deque is not empty: deque::empty) - anna::diameter::codec::Engine *engine; - - try { - reacting_answers_const_iterator it = a_deques.find(code); - if (it != a_deques.end()) { - if (!it->second->empty()) { - anna::diameter::codec::Message *message = *(it->second->begin()); - if (a_rotate) { - addMessage(code, message); - } else { - engine = anna::diameter::codec::EngineManager::instantiate().getCodecEngine(message->getApplicationId()); - if (engine) { - engine->releaseMessage(message); - } - else { - LOGWARNING(anna::Logger::warning("Cannot release a message for which i don't know the codec engine (check the registered stack id regarding the message application id) !", ANNA_FILE_LOCATION)); - return; - } - } - it->second->pop_front(); - } - } - } - catch (anna::RuntimeException &ex) { - ex.trace(); - } -} - -std::string ProgrammedAnswers::asString(const char *queueName) const throw () { - std::string result = ""; - std::string aux = "FIFO QUEUE '"; - aux += queueName; - aux += "', Rotation "; - aux += a_rotate ? "enabled" : "disabled"; - result += anna::functions::highlightJustify(aux); - if (a_deques.size() != 0) { - for (reacting_answers_const_iterator it = a_deques.begin(); - it != a_deques.end(); it++) { - if (it->second->size() != 0) { - aux = "Answer code "; - aux += anna::functions::asString(it->first); - result += anna::functions::highlightJustify(aux, - anna::functions::TextHighlightMode::OverAndUnderline, - anna::functions::TextJustifyMode::Left, '-'); - for (codec_messages_deque_const_iterator itm = it->second->begin(); - itm != it->second->end(); itm++) { - result += (*itm)->asXMLString(); - result += "\n"; - } - result += "\n"; - } - } - } else { - result = "No ocurrences found\n\n"; - } - return result; -} diff --git a/example/diameter/launcher/ProgrammedAnswers.hpp b/example/diameter/launcher/ProgrammedAnswers.hpp deleted file mode 100644 index 00b9d4d..0000000 --- a/example/diameter/launcher/ProgrammedAnswers.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_ProgrammedAnswers_hpp -#define example_diameter_launcher_ProgrammedAnswers_hpp - -// Standard -#include -#include -#include - -namespace anna { - namespace diameter { - namespace codec { - class Message; - } - } -} - - -class ProgrammedAnswers { - -typedef std::deque codec_messages_deque; -typedef std::deque::iterator codec_messages_deque_iterator; -typedef std::deque::const_iterator codec_messages_deque_const_iterator; -typedef std::map < int /* message code */, codec_messages_deque* > reacting_answers_container; -typedef std::map < int /* message code */, codec_messages_deque* >::iterator reacting_answers_iterator; -typedef std::map < int /* message code */, codec_messages_deque* >::const_iterator reacting_answers_const_iterator; - - reacting_answers_container a_deques; - bool a_rotate; - - public: - ProgrammedAnswers() { a_rotate = false; } - ~ProgrammedAnswers() { clear(); } - - bool rotate() const throw() { return a_rotate; } - void rotate(bool r) throw() { a_rotate = r; } - - void clear () throw(); - void dump () throw(); - void addMessage(int code, anna::diameter::codec::Message *message) throw(); - anna::diameter::codec::Message* getMessage(int code) const throw(); - void nextMessage(int code) throw(); - std::string asString(const char *queueName) const throw(); -}; - -#endif diff --git a/example/diameter/launcher/SConscript b/example/diameter/launcher/SConscript index d9d48ce..04ad2a5 100644 --- a/example/diameter/launcher/SConscript +++ b/example/diameter/launcher/SConscript @@ -12,7 +12,7 @@ pwd = str(Dir ('.').abspath); anna_libpaths = [] anna_libs = [] -modules = [ 'core', 'io', 'xml', 'app', 'http', 'comm', 'timex', 'http', 'statistics', 'time', 'diameter', 'diameter_comm' ]; +modules = [ 'core', 'io', 'xml', 'app', 'http', 'comm', 'timex', 'http', 'statistics', 'time', 'diameter', 'diameter_comm', 'testing' ]; for module in modules: anna_libs.append ("anna_" + module) module = module.replace("_", ".") @@ -43,12 +43,10 @@ localEnv.Append(LIBPATH = anna_libpaths) # Linking ################################################################# -# Process includes (avoid mandatory using of quoted includes and ../ paths from testing directory): +# Process includes: current_directory = Dir ('.').abspath -testing_include = os.path.join (current_directory, "testing") dynamic_include = os.path.join (pwd, "../../../../dynamic/launcher/default/") -localEnv.Append (CPPPATH = [current_directory, testing_include, dynamic_include]) - -result = localEnv.Program (pName, Glob ('*.cpp') + Glob ('testing/*.cpp')) +localEnv.Append (CPPPATH = [current_directory, dynamic_include]) +result = localEnv.Program (pName, Glob ('*.cpp')) Return ('result') diff --git a/example/diameter/launcher/deployments/st-client/checkStatus.sh b/example/diameter/launcher/deployments/st-client/checkStatus.sh new file mode 100755 index 0000000..7e7f861 --- /dev/null +++ b/example/diameter/launcher/deployments/st-client/checkStatus.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Result code: 1 (miss instances (and connections)), 2 (miss connections) +ADML_CONNECTIONS=`cat .st_conf_adml_connections 2>/dev/null` +ADML_INSTANCES=`cat .st_conf_adml_instances 2>/dev/null` +RESULT_CODE=0 + +ADML_INSTANCES_ALIVE=$(pgrep ADML- | wc -l) +echo -n "$ADML_INSTANCES_ALIVE instances alive" +if [ $ADML_INSTANCES_ALIVE -ne $ADML_INSTANCES ] +then + echo " ! (expected $ADML_INSTANCES configured)" + RESULT_CODE=1 +else + echo +fi + +ADML_CONNECTIONS_ALIVE=$(netstat -a | grep :diameter | grep ESTABLISHED | wc -l) +ADML_TOTAL_CONNECTIONS=$((ADML_CONNECTIONS*ADML_INSTANCES)) +echo -n "$ADML_CONNECTIONS_ALIVE connections established" +if [ $ADML_CONNECTIONS_ALIVE -ne $ADML_TOTAL_CONNECTIONS ] +then + echo " ! (expected $ADML_TOTAL_CONNECTIONS configured)" + [ $RESULT_CODE -ne 1 ] && RESULT_CODE=2 +else + echo +fi + +exit $RESULT_CODE + diff --git a/example/diameter/launcher/deployments/st-client/configure.sh b/example/diameter/launcher/deployments/st-client/configure.sh index b818a78..21e3112 100755 --- a/example/diameter/launcher/deployments/st-client/configure.sh +++ b/example/diameter/launcher/deployments/st-client/configure.sh @@ -57,17 +57,18 @@ calculate_deployment_layout() { echo "Orientative table" echo "-----------------------------------------------------------------------------------------------------------" echo -n "Number of instances: " + instances__dflt=$(ceil $max_connections $MAXIMUM_SUGGESTED_CLIENT_CONNECTION_PER_ADML_INSTANCE) for conn in `seq 1 $MAXIMUM_SUGGESTED_CLIENT_CONNECTION_PER_ADML_INSTANCE | tac` do instances=$(ceil $max_connections $conn) echo -n -e "\t$instances" + [ $conn -eq 1 ] && instances__dflt=$instances done echo echo -e "Connects per instance:\t10\t9\t8\t7\t6\t5\t4\t3\t2\t1" echo "===========================================================================================================" echo #echo "Input selection (connections per instance 1..$MAXIMUM_SUGGESTED_CLIENT_CONNECTION_PER_ADML_INSTANCE) [1]:" - instances__dflt=$(ceil $max_connections $MAXIMUM_SUGGESTED_CLIENT_CONNECTION_PER_ADML_INSTANCE) echo "Input the desired number of ADML instances [$instances__dflt]:" echo " (more than $instances instances, implies 1 single connection/instance)" read G_ADML_INSTANCES @@ -77,7 +78,7 @@ calculate_deployment_layout() { if [ $G_ADML_CONNECTIONS -gt $MAXIMUM_SUGGESTED_CLIENT_CONNECTION_PER_ADML_INSTANCE ] then echo "Warning: the number of connections per ADML instance ($G_ADML_CONNECTIONS) is greater" - echo " than the maximum suggested: $MAXIMUM_SUGGESTED_CLIENT_CONNECTION_PER_ADML_INSTANCE" + echo " than the maximum recommended: $MAXIMUM_SUGGESTED_CLIENT_CONNECTION_PER_ADML_INSTANCE" echo echo "Press ENTER to continue, CTRL-C to abort ..." read dummy @@ -135,12 +136,15 @@ fi calculate_deployment_layout +# Format for instance number (for example: 1 -> 001, .., 45 -> 045, 300 -> 300) +INSTANCE_FORMAT=$(echo $G_ADML_INSTANCES | wc -c) +INSTANCE_FORMAT=$((INSTANCE_FORMAT-1)) + # Dump persintently: echo $G_ADML_INSTANCES > $ADML_INSTANCES__ST_CONF_FILE echo $G_ADML_CONNECTIONS > $ADML_CONNECTIONS__ST_CONF_FILE # Rate per instance: -#rate_per_instance=$(ceil $desired_rate $G_ADML_INSTANCES) rate_per_instance=$((desired_rate/$G_ADML_INSTANCES)) [ $rate_per_instance -lt 1 ] && rate_per_instance=1 echo $rate_per_instance > $ADML_RATE_PER_INSTANCE__ST_CONF_FILE @@ -151,7 +155,6 @@ echo "Layout:" echo echo " - $G_ADML_INSTANCES ADML instances" echo " - $G_ADML_CONNECTIONS client connections per ADML instance" -#maximum_rate_1c=$((G_ADML_INSTANCES*MAXIMUM_ADML_ASYNC_RATE)) maximum_rate=$((G_ADML_INSTANCES*G_ADML_CONNECTIONS*MAXIMUM_ADML_ASYNC_RATE)) overcommit_rate_per_instance=$((G_ADML_CONNECTIONS*MAXIMUM_ADML_ASYNC_RATE)) echo @@ -171,35 +174,20 @@ echo "Input the number of test cases to program:" read N_TESTCASES while [ -z "$N_TESTCASES" ]; do read N_TESTCASES; done echo $N_TESTCASES > $N_TESTCASES__ST_CONF_FILE -testcase_per_adml_instance=$N_TESTCASES echo "Input the first test id to program [1]:" read first_value [ "$first_value" = "" ] && first_value=1 [ $first_value -lt 1 ] && first_value=1 echo -time_covered_1=$(ceil $N_TESTCASES $desired_rate) -time_covered=$(ceil $N_TESTCASES $((desired_rate*G_ADML_INSTANCES))) -echo "That amount covers $time_covered_1 seconds for one running ADML instance." -if [ $G_ADML_INSTANCES -gt 1 ] -then - echo "But you will have $G_ADML_INSTANCES instances running in parallel, then the total covered time is: $time_covered seconds" - testcase_per_adml_instance=$((N_TESTCASES/G_ADML_INSTANCES)) - echo "(aproximately, $testcase_per_adml_instance test cases will be programmed on each ADML instance)" -fi - -0>$N_TESTCASES_PROGRAM_LAYOUT__ST_CONF_FILE -for instance in `seq 1 $G_ADML_INSTANCES` -do - offset=$((testcase_per_adml_instance * (instance-1))) - ini=$((offset + first_value)) - fin=$((offset + first_value + testcase_per_adml_instance - 1)) - echo "$instance $ini $fin" >> $N_TESTCASES_PROGRAM_LAYOUT__ST_CONF_FILE -done +time_covered=$(echo "$N_TESTCASES/$desired_rate" | bc) +testcase_per_adml_instance=$((N_TESTCASES/G_ADML_INSTANCES)) +echo "Aproximately, $testcase_per_adml_instance test cases will be programmed on each ADML instance (we have $G_ADML_INSTANCES instances)." +echo "As we shall program $N_TESTCASES test cases, the total time covered for the desired system rate of $desired_rate cps, is $time_covered seconds." echo -if [ $time_covered -lt 300 ] +if [ $time_covered -lt 10 ] then - echo "$time_covered seconds is under 5 minutes, you should add more test cases to the pool except if you are sure" + echo "$time_covered seconds is under 10 seconds, you should add more test cases to the pool except if you are sure" echo " they will take less time that cycle completion. You could ensure that with a first timeout step." echo "Configuring such timeout slightly under $((1000*time_covered)) milliseconds, you could repeat the cycle safely to" echo " obtain a greater total time of testing." @@ -209,17 +197,22 @@ echo "How many total time you need to cover (in minutes):" read minutes while [ -z "$minutes" ]; do read minutes; done seconds=$((minutes*60)) -repeats=$(ceil $seconds $time_covered) +repeats=0 +[ $seconds -gt $time_covered ] && { repeats=$(ceil $seconds $time_covered) ; repeats=$((repeats-1)) ; } if [ $repeats -gt 0 ] then - echo "You will need $repeats cycles to cover $minutes minutes." - echo "Input the number of cyles [$repeats]: " - echo " (providing 0, you will cover $time_covered seconds)" + cycles=$((repeats+1)) + echo "You will need $cycles cycles to cover $minutes minutes." + echo "Input the desired number of cyles [$cycles]: " + echo " (providing 1, you will cover $time_covered seconds)" echo - read wanted_repeats - [ -z "$wanted_repeats" ] && wanted_repeats=$repeats - echo $wanted_repeats > $CYCLE_REPEATS__ST_CONF_FILE - [ $wanted_repeats -gt 0 ] && echo "Configured $wanted_repeats cycle repeats ($((wanted_repeats+1)) x $time_covered seconds ~ $(((wanted_repeats+1)*time_covered)) seconds of testing)." + read wanted_cycles + [ -z "$wanted_cycles" ] && wanted_cycles=$cycles + wanted_repeats=$((wanted_cycles-1)) + echo $wanted_cycles > $CYCLE_REPEATS__ST_CONF_FILE + [ $wanted_repeats -gt 0 ] && echo "Configured $wanted_repeats cycle repeats ($wanted_cycles cycles x $time_covered seconds ~ $((wanted_cycles*time_covered)) seconds of testing)." +else + echo 0 > $CYCLE_REPEATS__ST_CONF_FILE fi echo echo "System test configuration completed." @@ -233,9 +226,16 @@ cp services.msk services.xml sed -i 's/__CLIENT_CONNECTIONS__/'$G_ADML_CONNECTIONS'/g' services.xml cd - >/dev/null - -for instance in `seq 1 $G_ADML_INSTANCES` +# Create instances and layout: +0>$N_TESTCASES_PROGRAM_LAYOUT__ST_CONF_FILE +for i in `seq 1 $G_ADML_INSTANCES` do + instance=$(printf "%0${INSTANCE_FORMAT}d" $i) + offset=$((testcase_per_adml_instance * (i-1))) + ini=$((offset + first_value)) + fin=$((offset + first_value + testcase_per_adml_instance - 1)) + echo "$instance $ini $fin" >> $N_TESTCASES_PROGRAM_LAYOUT__ST_CONF_FILE + echo "Creating ADML instance $instance ..." mkdir -p ADMLS/ADML-$instance cd ADMLS/ADML-$instance @@ -254,6 +254,7 @@ done echo echo "Now you can run all the instances deployed: ./run.sh" +echo "To configure another layout you should execute this script again." echo echo "Done!" diff --git a/example/diameter/launcher/deployments/st-client/launchCPS.sh b/example/diameter/launcher/deployments/st-client/launchCPS.sh new file mode 100755 index 0000000..44d55b3 --- /dev/null +++ b/example/diameter/launcher/deployments/st-client/launchCPS.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +############# +# VARIABLES # +############# +ADML_INSTANCES=`cat .st_conf_adml_instances 2>/dev/null` + +############# +# FUNCTIONS # +############# +_exit() { + echo + echo $1 + echo + exit 1 +} + +############# +# EXECUTION # +############# +echo +echo +cd `dirname $0` +./checkStatus.sh +[ $? -ne 0 ] && _exit "Fix status to continue ..." +echo +echo "Input desired rate (test cases per second) to start testing [0: stop if active]:" +read desired_rate +if [ "$desired_rate" != "" ] +then + rate_per_instance=$((desired_rate/$ADML_INSTANCES)) +fi + +echo +echo "As we have $ADML_INSTANCES instances available, we shall launch $rate_per_instance test cases" +echo " per second and instance (./operation.sh \"test|ttps|$rate_per_instance\")." +echo +echo "Press ENTER to continue, CTRL-C to abort ..." +read dummy +./operation.sh "test|ttps|$rate_per_instance" + diff --git a/example/diameter/launcher/deployments/st-client/operation_all.sh b/example/diameter/launcher/deployments/st-client/operation_all.sh index e2efaa4..2dcb900 100755 --- a/example/diameter/launcher/deployments/st-client/operation_all.sh +++ b/example/diameter/launcher/deployments/st-client/operation_all.sh @@ -8,14 +8,21 @@ then exit 1 fi +if [ "$1" = "-h" -o "$1" = "--help" ] +then + first_adml=$(ls -d ADMLS/* | head -n +1) + $first_adml/operation.sh help + exit 0 +fi + for op in `ls ADMLS/*/operation.sh` do dn_op=`dirname $op` cd $dn_op echo -n "Instance `basename $dn_op`: " 0>launcher.trace - ./operation.sh $@ - [ $? -ne 0 ] && { echo ; exit 1 ; } + ./operation.sh $@ & + #[ $? -ne 0 ] && { echo ; exit 1 ; } cd - >/dev/null done diff --git a/example/diameter/launcher/deployments/st-client/program.sh b/example/diameter/launcher/deployments/st-client/program.sh index 07e7616..31eb97d 100755 --- a/example/diameter/launcher/deployments/st-client/program.sh +++ b/example/diameter/launcher/deployments/st-client/program.sh @@ -8,38 +8,91 @@ RATE_PER_INSTANCE=`cat .st_conf_rate_per_instance 2>/dev/null` DESIRED_RATE=`cat .st_conf_desired_rate 2>/dev/null` REPEATS=`cat .st_conf_cycle_repeats 2>/dev/null` PROGRAM_LAYOUT_FILE=.st_conf_n_testcases_program_layout +[ -z "$ADML_CONCURRENT_PROVISION_JOBS" ] && ADML_CONCURRENT_PROVISION_JOBS=20 ############# # FUNCTIONS # ############# _exit() { - echo - echo $1 - echo + echo -e "\n$1\n" exit 1 } usage() { - echo "Usage: $0 [-s]" - echo - echo " Performs test case programming from scratch (current test cases will be dropped from ADML involved instances)." - echo - echo " test stuff directory: contains msk files, specially a testcase file with xml files referenced inside. Those" - echo " xml files (without the .msk extension) shall exists in the directory. For example:" - echo - echo " $0 st_examples/DynamicQualification" - echo - echo " Optionally, a file called 'specific' could exists containing testcase-specific information," - echo " which normally will be used to specify database sentences. This file will be accumulated" - echo " as a cloning seed over the file 'specific.all' created on test stuff directory." - echo - echo - echo " -s: start testing just after programming, using desired rate: $DESIRED_RATE test cases per second." + cat << EOF +Usage: $0 [-s] + + Performs test case programming from scratch (current test cases will be dropped + from ADML involved instances). There are two types of programming depending on + the first argument provided: + + test stuff directory: + + The folder provided must contain msk files, specially a testcase file with + xml files referenced inside. Those xml files (without the .msk extension) + shall exists in the directory. For example: + + $0 st_examples/DynamicQualification + + Optionally, a file 'specific' could exists containing testcase-specific + information, which normally will be used to specify database sentences. + This file will be accumulated as a cloning seed over the file 'specific.all' + created on test stuff directory. + + Template type programming could be used for medium-duration testings, because + the scripting used for programming, have lots of groups divisions which even + background-executed are much more slower on programming than a c++ builtin + procedure: dynamic type is the one for large sets of test cases: + + + dynamic: + + The current dynamic procedure selected by mean 'dynlibs/select.sh' script, + will be programmed using its dirname resources: inside, you may found not + only the .so library but the needed xml files for the implemented scenary + and a file called 'dynamic.suffix' used to complete the dynamic operation + in this way: + + dynamic|||| + + For example, you could have this content for 'dynamic.suffix': + + 0|7|CCR-I.xml|CCR-T.xml (0 for timeout means no timeout step) + + in order to generate the operation: + + dynamic|0|||7|CCR-I.xml|CCR-T.xml + + which would be parsed for the specific ADML instance programmed: + + dynamic|0|2000001|2001000|7|CCR-I.xml|CCR-T.xml + + The file 'dynamic.suffix' could have several lines for several scenaries. + In this case, this script will prompt for the desired one. + + This script will build every operation for the configured ADML instances + to complete all the sequence ranges along the whole test system. + + + + -s: start testing just after programming, using desired rate: $DESIRED_RATE test cases per second. +EOF [ $ADML_INSTANCES -gt 1 ] && echo " In your case, with $ADML_INSTANCES, a rate of $RATE_PER_INSTANCE ttps will be send per instance." echo _exit } +advice_to_squeeze_idle_cpu () { + local idle=$(top -b -d 0.1 -n 2 | grep 'Cpu(s):'| tail -1 | awk '{print $8}' | sed 's/,/./') + sleep 10 + echo + echo "Idle cpu now: $idle. Check the system CPU with top. If is not overcommited, consider" + echo " increase the environment variable 'ADML_CONCURRENT_PROVISION_JOBS' (now $ADML_CONCURRENT_PROVISION_JOBS)." + echo + echo "Press ENTER to continue ..." + read dummy +} + ############# # EXECUTION # ############# @@ -53,73 +106,161 @@ miss_conf= echo [ -z "$1" ] && usage -echo "Starting testcases programming ..." +echo "Testcases programming ..." echo +cd `dirname $0` +./checkStatus.sh +[ $? -eq 1 ] && _exit "Fix status to continue ..." -./operation.sh --ping >/dev/null -[ $? -ne 0 ] && _exit "Programming aborted (some ADML client process is not running) !" -TESTCASE_DIR=$1 +# We launch in background ! +#./operation.sh --ping >/dev/null +#[ $? -ne 0 ] && _exit "Programming aborted (some ADML client process is not running) !" + +# Arguments: +PROG_TYPE=$1 AUTOSTART=$2 -[ ! -d $TESTCASE_DIR ] && _exit "Cannot found the test directory '$TESTCASE_DIR' !!" -TESTCASE=( `ls $TESTCASE_DIR/testcase*msk 2>/dev/null` ) -TESTCASE_FILES=${#TESTCASE[@]} -[ $TESTCASE_FILES -ne 1 ] && _exit "One and only one 'testcase*msk' file must be present !!" - -MAX_NUMBER_GROUPS=$(grep ^MAX_NUMBER_GROUPS= clone.sh | cut -d= -f2) -CLONE_GROUPS=$((MAX_NUMBER_GROUPS/ADML_INSTANCES)) -#CLONE_GROUPS=1 -specific= -[ -f $TESTCASE_DIR/specific ] && specific=specific - -[ -z "$ADML_CONCURRENT_PROVISION_JOBS" ] && ADML_CONCURRENT_PROVISION_JOBS=5 - -count=0 -while read -r line -do - instance=$(echo $line | awk '{ print $1 }') - ini_seq=$(echo $line | awk '{ print $2 }') - fin_seq=$(echo $line | awk '{ print $3 }') - ADML_DIR=`readlink -f ADMLS/ADML-$instance` - echo -e "\rCloning interval [$ini_seq,$fin_seq] for $(basename $ADML_DIR) ..." - ./clone.sh $ADML_DIR $TESTCASE $ini_seq $fin_seq $CLONE_GROUPS $specific & - sleep 0.1 - count=$((count+1)) - if [ $count -eq $ADML_CONCURRENT_PROVISION_JOBS ] +# test case stuff programming ######################################################### +if [ "$PROG_TYPE" != "dynamic" ] +then + TESTCASE_DIR=$PROG_TYPE + + [ ! -d $TESTCASE_DIR ] && _exit "Cannot found the test directory '$TESTCASE_DIR' !!" + TESTCASE=( `ls $TESTCASE_DIR/testcase*msk 2>/dev/null` ) + TESTCASE_FILES=${#TESTCASE[@]} + [ $TESTCASE_FILES -ne 1 ] && _exit "One and only one 'testcase*msk' file must be present !!" + + MAX_NUMBER_GROUPS=$(grep ^MAX_NUMBER_GROUPS= clone.sh | cut -d= -f2) + CLONE_GROUPS=$((MAX_NUMBER_GROUPS/ADML_INSTANCES)) + #CLONE_GROUPS=1 + specific= + [ -f $TESTCASE_DIR/specific ] && specific=specific + + count=0 + while read -r line + do + instance=$(echo $line | awk '{ print $1 }') + ini_seq=$(echo $line | awk '{ print $2 }') + fin_seq=$(echo $line | awk '{ print $3 }') + ADML_DIR=`readlink -f ADMLS/ADML-$instance` + echo -e "\rCloning interval [$ini_seq,$fin_seq] for $(basename $ADML_DIR) ..." + ./clone.sh $ADML_DIR $TESTCASE $ini_seq $fin_seq $CLONE_GROUPS $specific & + sleep 0.1 + count=$((count+1)) + if [ $count -eq $ADML_CONCURRENT_PROVISION_JOBS ] + then + wait $(jobs -p) + count=0 + fi + + done < $PROGRAM_LAYOUT_FILE + + # Advice for idle cpu: + advice_to_squeeze_idle_cpu + + # Wait background jobs to finish: + sleep 5 + echo "Waiting for clone completion ..." + echo "(please be patient, this may take a while)" + echo + echo "Background Jobs: $(jobs -p | wc -l)" + wait $(jobs -p) + + echo + echo "Programming has finished !" + echo + + echo "Configuring repeat cycles ..." + ./operation.sh "test|repeats|$REPEATS" + + if [ -n "$specific" ] then - idle_cpu=$(top -b -d 0.1 -n 2 | grep 'Cpu(s):'| tail -1 |awk '{print $8}') - echo - echo "Idle cpu = $idle_cpu%" - echo "if cpu is not overcommited, consider increase ADML_CONCURRENT_PROVISION_JOBS environment variable (now $ADML_CONCURRENT_PROVISION_JOBS)" + echo "A new file '$TESTCASE_DIR/specific.all' has been created." + echo "Probably you need to apply it before starting traffic." echo - wait $(jobs -p) - count=0 + echo "Press ENTER to continue, CTRL-C to abort ..." + read dummy fi -done < $PROGRAM_LAYOUT_FILE +# dynamic programming ################################################################# +else + TESTCASE_DIR=$(readlink -f dynlibs/libanna_dynamicLauncherProcedure.so | xargs dirname) + DYNAMIC_SUFFIX_FILE=$TESTCASE_DIR/dynamic.suffix -# Wait background jobs to finish: -sleep 5 -echo "Waiting for clone completion ..." -echo "(please be patient, this may take a while)" -echo -echo "Background Jobs: $(jobs -p | wc -l)" -wait $(jobs -p) + [ ! -f $DYNAMIC_SUFFIX_FILE ] && _exit "Missing '$DYNAMIC_SUFFIX_FILE' file.\nUse 'dynlibs/select.sh' to change the dynamic procedure and restart the ADML instances." + dynamic_suffix=( $(grep -v ^# $DYNAMIC_SUFFIX_FILE) ) -echo -echo "Programming has finished !" -echo + # Multiple scenarios: + suffixes=${#dynamic_suffix[@]} + if [ $suffixes -gt 1 ] + then + echo + echo "----------------------------------------------------------------" + cat $DYNAMIC_SUFFIX_FILE + echo "----------------------------------------------------------------" + echo + echo "Detected $suffixes scenarios:" + echo + tmpfile=$(mktemp) + opt=1 + for line in $(grep -v ^# $DYNAMIC_SUFFIX_FILE) + do + echo "${opt}) $line" + opt=$((opt+1)) + done > $tmpfile + cat $tmpfile + echo + echo "Select the desired option (0 to input a user defined one):" + read option + while [ -z "$option" ]; do read option ; done + if [ "$option" != "0" ] + then + dynamic_suffix=$(grep "^${option}) " $tmpfile | cut -d" " -f2-) + [ -z "$dynamic_suffix" ] && _exit "Invalid option !" + else + echo "Input specific program arguments:" + echo " (be sure that are supported by the dynamic procedure)" + echo + read dynamic_suffix + while [ -z "$dynamic_suffix" ]; do read dynamic_suffix ; done + fi + fi -echo "Configuring repeat cycles ..." -./operation.sh "test|repeats|$REPEATS" + # Modify xml files path: + xmls=( $(echo $dynamic_suffix | sed 's/'\|'/ /g') ) + dynamic_suffix=$(for xml in ${xmls[@]} + do + echo $xml | grep -q "\.xml$" + [ $? -eq 0 ] && echo -n "$TESTCASE_DIR/" + echo -n "${xml}|" + done | sed 's/'\|'$//') -if [ -n "$specific" ] -then - echo "A new file '$TESTCASE_DIR/specific.all' has been created." - echo "Probably you need to apply it before starting traffic." + # If still idle CPU, you could increase chunks number of background jobs + echo "Dynamic programming ..." echo - echo "Press ENTER to continue, CTRL-C to abort ..." - read dummy + while read -r line + do + instance=$(echo $line | awk '{ print $1 }') + ini_seq=$(echo $line | awk '{ print $2 }') + fin_seq=$(echo $line | awk '{ print $3 }') + ADML_DIR=`readlink -f ADMLS/ADML-$instance` + + cd $ADML_DIR + ./operation.sh -t 60 "dynamic|$ini_seq|$fin_seq|$dynamic_suffix" & + sleep 0.1 + count=$((count+1)) + if [ $count -eq $ADML_CONCURRENT_PROVISION_JOBS ] + then + wait $(jobs -p) + count=1 + fi + cd - >/dev/null + + done < $PROGRAM_LAYOUT_FILE + + # Advice for idle cpu: + advice_to_squeeze_idle_cpu + fi echo @@ -129,20 +270,42 @@ if [ "$AUTOSTART" = "-s" ] then start_testing=yes else - echo "Input desired rate (test cases per second) to start testing [0: nothing done]:" - read desired_rate - if [ "$desired_rate" != "" ] - then - rate_per_instance=$((desired_rate/$ADML_INSTANCES)) - [ $rate_per_instance -lt 1 ] && rate_per_instance=1 - ./operation.sh "test|ttps|$rate_per_instance" - else - echo "Remember that you could start traffic using:" - echo " ./operation.sh \"test|ttps|\"" - echo - echo "For example, to reach $DESIRED_RATE test cases per second:" - echo " ./operation.sh \"test|ttps|$RATE_PER_INSTANCE\"" - fi + cat << EOF + +To start testing, you must use the './operation.sh' script. The most + common commands used for testing are: + + - Enable reports dump for failed tests: ./operation.sh "test|report|failed" + - Launch traffic: ./operation.sh "test|ttps|" + + (*) this is related to a single instance, because operation script launched + here invokes every 'ADMLS/ADML-/operation.sh' counterpart + script. Then, you should divide the desired CPS by the number of + instances. For example, if you need $DESIRED_RATE test cases per second, + as you have $ADML_INSTANCES instances, then you should execute: + + ./operation.sh "test|ttps|$RATE_PER_INSTANCE" + + - Stop traffic: ./operation.sh "test|ttps|0" + - Reset already classified (Success/Failed) test cases: ./operation.sh "test|reset[|soft]" + - Reset also 'in-progress' state test cases: ./operation.sh "test|reset|hard" + - Clear programmed test cases: ./operation.sh "test|clear" + +Remember that './operation.sh' broadcasts the operation scripts inside + the ADML instances, then some operations should better be used within + a specific 'ADMLS/ADML-' directory to avoid console spam: + + - Check a testcase in runtime: ADMLS/ADML-001/operation.sh "test|look|" + - Execute the next programmed test case: ADMLS/ADML-001/operation.sh "test|next" + - Interactive-step execution: ADMLS/ADML-001/operation.sh "test|interact||" + - Summary: ADMLS/ADML-001/operation.sh "test|summary" + +For a complete and detailed information, execute: ./operation.sh --help | less + +You could also use './launchCPS.sh' script. + +EOF + fi if [ -n "$start_testing" ] diff --git a/example/diameter/launcher/deployments/st-client/run_all.sh b/example/diameter/launcher/deployments/st-client/run_all.sh index 3d1c33b..489855c 100755 --- a/example/diameter/launcher/deployments/st-client/run_all.sh +++ b/example/diameter/launcher/deployments/st-client/run_all.sh @@ -11,23 +11,35 @@ fi for run in `ls ADMLS/*/run.sh` do cd `dirname $run` - ./run.sh - #sleep 0.1 + instance_name=$(basename $PWD) + pkill $instance_name + [ $? -eq 0 ] && echo -n "Re-" + echo "Starting $instance_name ..." + ./run.sh > /dev/null & cd - >/dev/null done - -sleep 1 -echo -echo -echo "To enable reports dump for failed tests, execute:" -echo " ./operation.sh \"test|report|failed\"" -echo -echo "To stop the processes, you could execute:" -echo " pgrep ADML-[0-9] | xargs kill" echo -echo "When running, use script './program.sh' to configure the test cases." -echo "To configure another layout you should execute './configure.sh'." -echo -echo "Done!" +wait $(jobs -p) + +sleep 3 +./checkStatus.sh + +cat << EOF + $(pgrep ADML- | wc -l) instances alive ! + $(netstat -a | grep :diameter | grep ESTABLISHED | wc -l) connections established ! + + To stop the processes, you could execute: pgrep ADML-[0-9] | xargs kill + Anyway, executing './run.sh' again you will restart the ADML instances. + + Now it's time to './program.sh' the started instances with the desired + scenary. There are two programming variants: + + ./program.sh + ./program.sh dynamic + + + Done! +EOF + echo diff --git a/example/diameter/launcher/testing/TestCase.cpp b/example/diameter/launcher/testing/TestCase.cpp deleted file mode 100644 index 087e29b..0000000 --- a/example/diameter/launcher/testing/TestCase.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -// Standard -#include -#include -#include -#include -#include - -// Project -#include -#include -#include -#include -#include -#include -#include -#include - -// Process -#include -#include - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -void TestCase::DebugSummary::addHint(const std::string &hint) throw() { - event_t event; - event.Timestamp = anna::functions::millisecond(); - event.Hint = hint; - a_events.push_back(event); -} - -void TestCase::DebugSummary::clear() throw() { - a_events.clear(); -} - -anna::xml::Node* TestCase::DebugSummary::asXML(anna::xml::Node* parent) const throw() { - anna::xml::Node* result = parent->createChild("DebugSummary"); - - std::vector::const_iterator it; - for (it = a_events.begin(); it != a_events.end(); it++) { - anna::xml::Node* event = result->createChild("Event"); - event->createAttribute("Timestamp", (*it).Timestamp.asString()); - event->createAttribute("Hint", (*it).Hint); - } - - return result; -}; -/////////////////////////////////////////////////////////////////////////////////////////////////// - - -TestCase::TestCase(unsigned int id) : - a_id(id), - a_state(State::Initialized), - a_startTime(0), - a_interactiveAmount(-1) { - - /*a_stepsIt = a_steps.end()*/; - TestManager &testManager = TestManager::instantiate(); - testManager.tcsStateStats(State::Initialized, State::Initialized); -} - -TestCase::~TestCase() { - reset(true); // hard reset - std::vector::const_iterator it; - for (it = a_steps.begin(); it != a_steps.end(); it++) delete (*it); -} - -const char* TestCase::asText(const State::_v state) -throw() { - static const char* text [] = { "Initialized", "InProgress", "Failed", "Success" }; - return text [state]; -} - -anna::xml::Node* TestCase::asXML(anna::xml::Node* parent) const -throw() { - anna::xml::Node* result = parent->createChild("TestCase"); - - result->createAttribute("Id", a_id); - result->createAttribute("State", asText(a_state)); - result->createAttribute("StartTimestamp", a_startTime.asString()); - int steps = a_steps.size(); - if (steps != 0) { - result->createAttribute("NumberOfTestSteps", steps); - std::vector::const_iterator it; - for (it = a_steps.begin(); it != a_steps.end(); it++) { - (*it)->asXML(result); - } - } - - if (a_debugSummary.events()) { - a_debugSummary.asXML(result); - } - - result->createAttribute("Interactive", (a_interactiveAmount != -1) ? "yes":"no"); - - return result; -} - -std::string TestCase::asXMLString() const throw() { - anna::xml::Node root("root"); - return anna::xml::Compiler().apply(asXML(&root)); -} - -bool TestCase::hasSameCondition(const TestCondition &condition) const throw() { - std::vector::const_iterator it; - TestStepWait *step; - for (it = a_steps.begin(); it != a_steps.end(); it++) { - if ((*it)->getType() != TestStep::Type::Wait) continue; - step = (TestStepWait *)(*it); - if (step->getCondition() == condition) return true; - } - return false; -} - - -void TestCase::setState(const State::_v &state) throw() { - - State::_v previousState = a_state; - if (state == previousState) return; - a_state = state; - TestManager &testManager = TestManager::instantiate(); - - // stats: - testManager.tcsStateStats(previousState, state); - - - if (isFinished()) { - if ((getState() == State::Failed) && (!testManager.getDumpFailedReports())) return; - if ((getState() == State::Success) && (!testManager.getDumpSuccessReports())) return; - // report file name: cycle-.testcase-.xml - - // FORMAT: We tabulate the cycle and test case in order to ease ordering of files by mean ls: - int cycles = testManager.getPoolRepeats(); - int tests = testManager.tests(); - int cyclesWidth = (cycles<=0) ? 3 /* 1000 cycles !! */: ((int) log10 ((double) cycles) + 1); - int testsWidth = (tests<=0) ? 9 /* subscribers */: ((int) log10 ((double) tests) + 1); - std::stringstream format; - format << "/cycle-%0" << cyclesWidth << "d.testcase-%0" << testsWidth << "llu.xml"; - - // FILE NAME: - std::string file = testManager.getReportsDirectory() + anna::functions::asString(format.str().c_str(), testManager.getPoolCycle(), a_id); - std::ofstream out; - out.open(file.c_str(), std::ofstream::out | std::ofstream::app); - if(out.is_open() == false) { - std::string msg("Error opening '"); - msg += file; - msg += "' for writting"; - anna::Logger::error(msg, ANNA_FILE_LOCATION); - } - else { - out << asXMLString() << std::endl; - out.close(); - } - } -} - -bool TestCase::done() throw() { - if (a_stepsIt == a_steps.end()) { - setState(State::Success); - return true; - } - - return false; -} - -bool TestCase::process() throw() { - if (steps() == 0) { - LOGWARNING(anna::Logger::warning(anna::functions::asString("Test case %llu is empty, nothing to execute", a_id), ANNA_FILE_LOCATION)); - return false; - } - if (isFinished()) { - LOGDEBUG(anna::Logger::debug(anna::functions::asString("Test case %llu is finished, nothing done until soft-reset", a_id), ANNA_FILE_LOCATION)); - return false; - } - - if (a_state == State::Initialized) { - a_stepsIt = a_steps.begin(); - setState(State::InProgress); - - // For 'wait' steps (not really useful, but better than nothing: begin timestamp on test case start timestamp...): - a_startTime = anna::functions::millisecond(); - } - - // Check end of the test case: - if (done()) return false; - - bool somethingDone = false; - while ((*a_stepsIt)->execute()) { // executes returns 'true' if the next step must be also executed (execute until can't stand no more) - nextStep(); - // Check end of the test case: - if (done()) return false; - somethingDone = true; - } - - return somethingDone; -} - -bool TestCase::reset(bool hard) throw() { - - // Soft reset if finished: - if (!hard /* is soft reset */ && !isFinished()) return false; - - // Clean stage //////////////////////////// - // id is kept - std::vector::iterator it; - for (it = a_steps.begin(); it != a_steps.end(); it++) - (*it)->reset(); - - a_debugSummary.clear(); - a_startTime = 0; - a_interactiveAmount = -1; - - setState(State::Initialized); - - return true; -} - -void TestCase::assertInitialized() const throw(anna::RuntimeException) { - if (isFinished()) - throw anna::RuntimeException(anna::functions::asString("Cannot program anymore. The test case %llu has finished. You must reset it to append new steps (or do it during execution, which is also allowed).", a_id), ANNA_FILE_LOCATION); -} - -void TestCase::assertMessage(const anna::DataBlock &db, bool toEntity) throw(anna::RuntimeException) { - - bool isRequest = anna::diameter::codec::functions::isRequest(db); - bool registerKeys = ((isRequest && toEntity) || (!isRequest && !toEntity) /* (*) */); - // (*) we register answers Session-Id "assuming" that we will know the Session-Id values created by the client. - // This is another solution regarding diameter server testing. No sure about the final implementation. - // We will help registering also subscriber data, because certain messages (i.e. SLR) coming from clients could - // have specific Session-Id value (unknown at test programming), and normally are identified by subscriber. - - // Check hop-by-hop: - if (isRequest) { - anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db); - if (a_hopByHops.find(hbh) != a_hopByHops.end()) - throw anna::RuntimeException(anna::functions::asString("Another request has been programmed with the same hop-by-hop (%llu) in this test case (%llu)", hbh, a_id), ANNA_FILE_LOCATION); - a_hopByHops[hbh] = NULL; // may be assigned to a wait condition - } - - if (registerKeys) { - TestManager &testManager = TestManager::instantiate(); - testManager.registerSessionId(anna::diameter::helpers::base::functions::getSessionId(db), this); - - - std::string subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(db, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164); - if (subscriberId == "") // try with IMSI - subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(db, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI); - - if (subscriberId != "") - testManager.registerSubscriberId(subscriberId, this); - } -} - -void TestCase::addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException) { - assertInitialized(); - TestStepTimeout *step = new TestStepTimeout(this); - step->setTimeout(timeout); - addStep(step); -} - -void TestCase::addSendxml2e(const anna::DataBlock &db, OriginHost *host, int stepNumber) throw(anna::RuntimeException) { - assertInitialized(); - assertMessage(db, true /* to entity */); - - if (stepNumber != -1) { - const TestStep *stepReferred = getStep(stepNumber); - if (!stepReferred) - throw anna::RuntimeException(anna::functions::asString("Step number (%d) do not exists (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); - - if (stepReferred->getType() != TestStep::Type::Wait) - throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); - - const TestCondition &tc = (static_cast(stepReferred))->getCondition(); - if (tc.getCode() == "0") { // if regexp used, is not possible to detect this kind of errors - throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait for request' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); - } - } - - TestStepSendxml2e *step = new TestStepSendxml2e(this); - step->setMsgDataBlock(db); - step->setOriginHost(host); - step->setWaitForRequestStepNumber(stepNumber); // -1 means, no reference - addStep(step); -} - -void TestCase::addSendxml2c(const anna::DataBlock &db, OriginHost *host, int stepNumber) throw(anna::RuntimeException) { - assertInitialized(); - assertMessage(db, false /* to client */); - - TestStepSendxml2c *step = new TestStepSendxml2c(this); - step->setMsgDataBlock(db); - step->setOriginHost(host); - addStep(step); -} - -void TestCase::addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException) { - assertInitialized(); - TestStepDelay *step = new TestStepDelay(this); - step->setDelay(delay); - addStep(step); -} - -void TestCase::addWait(bool fromEntity, - 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(anna::RuntimeException) { - assertInitialized(); - std::string usedHopByHop = hopByHop; - TestStepWait *step = NULL; - - // Check basic conditions: - if (bitR == "1") { - if (resultCode != "") - throw anna::RuntimeException(anna::functions::asString("You cannot specify Result-Code (%s) for a wait condition of a diameter request message (test case %llu)", resultCode.c_str(), a_id), ANNA_FILE_LOCATION); - if (hopByHop != "") - throw anna::RuntimeException(anna::functions::asString("You cannot specify Hop-by-hop (%s) for a wait condition of a diameter request message (test case %llu)", hopByHop.c_str(), a_id), ANNA_FILE_LOCATION); - } - else { - if (hopByHop != "") { - if (hopByHop[0] == '#') { - if (steps() == 0) - throw anna::RuntimeException(anna::functions::asString("No steps has been programmed, step reference is nonsense (test case %llu)", a_id), ANNA_FILE_LOCATION); - - int stepNumber = atoi(hopByHop.substr(1).c_str()); - - const TestStep *stepReferred = getStep(stepNumber); - if (!stepReferred) - throw anna::RuntimeException(anna::functions::asString("Step reference number (%d) do not exists (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); - - if (stepReferred->getType() != TestStep::Type::Sendxml2e && stepReferred->getType() != TestStep::Type::Sendxml2c) - throw anna::RuntimeException(anna::functions::asString("Step number must refer to a 'sendxml2e' or 'sendxml2c' step (test case %llu)", a_id), ANNA_FILE_LOCATION); - - const anna::DataBlock &db = (static_cast(stepReferred))->getMsgDataBlock(); - bool isAnswer = anna::diameter::codec::functions::isAnswer(db); - if (isAnswer) - throw anna::RuntimeException(anna::functions::asString("Step number must refer to a request message (test case %llu)", a_id), ANNA_FILE_LOCATION); - - // Hop-by-hop: - anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db); - usedHopByHop = anna::functions::asString(hbh); - step = new TestStepWait(this); - a_hopByHops[hbh /* always exists: is the info we calculated above */] = step; - } - } - } - - if (!step) step = new TestStepWait(this); - step->setCondition(fromEntity, code, bitR, usedHopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId); - - LOGINFORMATION( - if (hasSameCondition(step->getCondition())) - anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION); - ); - - addStep(step); -} - -void TestCase::addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException) { - assertInitialized(); - - TestStepWait *step = new TestStepWait(this); - step->setCondition(fromEntity, regexp); - - LOGINFORMATION( - if (hasSameCondition(step->getCondition())) - anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION); - ); - - addStep(step); -} - -void TestCase::addCommand(const std::string &cmd) throw(anna::RuntimeException) { - assertInitialized(); - - TestStepCmd *step = new TestStepCmd(this); - step->setScript(cmd); - - addStep(step); -} - -TestStepWait *TestCase::searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw() { - - TestStepWait *result; - for (std::vector::const_iterator it = a_stepsIt /* current */; it != a_steps.end(); it++) { - if ((*it)->getType() != TestStep::Type::Wait) continue; - if ((*it)->isCompleted()) continue; - result = (TestStepWait*)(*it); - if ((result->getCondition().receivedFromEntity() == waitFromEntity) && (result->fulfilled(message))) - return result; - } - - return NULL; -} - -const TestStep *TestCase::getStep(int stepNumber) const throw() { - if (stepNumber < 1 || stepNumber > steps()) return NULL; -// return a_steps.at(stepNumber-1); // http://stackoverflow.com/questions/3269809/stdvectorat-vs-operator-surprising-results-5-to-10-times-slower-f - return a_steps[stepNumber-1]; -} diff --git a/example/diameter/launcher/testing/TestCase.hpp b/example/diameter/launcher/testing/TestCase.hpp deleted file mode 100644 index b55abd7..0000000 --- a/example/diameter/launcher/testing/TestCase.hpp +++ /dev/null @@ -1,137 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_TestCase_hpp -#define example_diameter_launcher_TestCase_hpp - -// Standard -#include -#include -#include - -// Project -#include -#include -#include - -// Process -#include - - -namespace anna { - class Millisecond; - - namespace xml { - class Node; - } -} - -class OriginHost; - - -class TestCase { - - void assertInitialized() const throw(anna::RuntimeException); - void assertMessage(const anna::DataBlock &db, bool toEntity) throw(anna::RuntimeException); - -public: - - // Debug summary: - class DebugSummary { - - typedef struct { - anna::Millisecond Timestamp; - std::string Hint; - } event_t; - - std::vector a_events; - public: - void addHint(const std::string &hint) throw(); - void clear() throw(); - int events() const throw() { return a_events.size(); } - anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); - }; - - TestCase(unsigned int id); - ~TestCase(); - - struct State { enum _v { Initialized, InProgress, Failed, Success }; }; - static const char* asText(const State::_v state) throw(); - const State::_v &getState() const throw() { return a_state; } - const anna::Millisecond &getStartTimestamp() const throw() { return a_startTime; } - void addDebugSummaryHint(const std::string &hint) throw() { a_debugSummary.addHint(hint); } - void setState(const State::_v &state) throw(); - bool isFinished() const throw() { return (getState() == State::Failed || getState() == State::Success); } - bool inProgress() const throw() { return (getState() == State::InProgress); } - bool hasSameCondition(const TestCondition &condition) const throw(); - - // Interactivity: - void makeInteractive(bool yes = true) throw() { a_interactiveAmount = (yes ? 0:-1); } - void addInteractiveAmount(unsigned int amount) throw() { - if (a_interactiveAmount == -1) makeInteractive(); - if (amount == 0) return; - a_interactiveAmount += amount; - process(); - } - int interactiveAmount() const throw() { return a_interactiveAmount; } - void interactiveExecution() throw() { a_interactiveAmount --; } - - // Step type & information - void addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException); - void addSendxml2e(const anna::DataBlock &db, OriginHost *host, int stepNumber) throw(anna::RuntimeException); - void addSendxml2c(const anna::DataBlock &db, OriginHost *host, int stepNumber) throw(anna::RuntimeException); - void addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException); - void addWait(bool fromEntity, - 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(anna::RuntimeException); - void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException); - void addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException); - void addCommand(const std::string &cmd) throw(anna::RuntimeException); - - - // Process: - void nextStep() throw() { a_stepsIt++; } - bool done() throw(); - bool process() throw(); // false to stop - - // Reset test case and underlaying information (steps context) - bool reset(bool hard /* hard reset includes in-progress test cases */) throw(); - - // getters - const anna::Millisecond &getStartTime() const throw() { return a_startTime; } - const unsigned int &getId() const throw() { return a_id; } - - //helpers - int steps() const throw() { return a_steps.size(); } - void addStep(TestStep *step) throw() { a_steps.push_back(step); } - - TestStepWait *searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw(); - // When a message arrives, we identify the test case by mean the Session-Id. Then, from the current step iterator (included), - // we search for a fulfilling condition for that message. The first found, is 'completed' and then breaks the search. - const TestStep *getStep(int stepNumber) const throw(); - - anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); - std::string asXMLString() const throw(); - - -private: - // private members: - unsigned int a_id; - std::vector a_steps; - std::vector::const_iterator a_stepsIt; - std::map a_hopByHops; // for wait-answer - State::_v a_state; - anna::Millisecond a_startTime; - DebugSummary a_debugSummary; // used when a test case has failed, uncovered message conditions, and any other hint. - int a_interactiveAmount; - - friend class TestStep; -}; - -#endif diff --git a/example/diameter/launcher/testing/TestClock.cpp b/example/diameter/launcher/testing/TestClock.cpp deleted file mode 100644 index 885e87e..0000000 --- a/example/diameter/launcher/testing/TestClock.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -// Standard -#include - -// Process -#include -#include - - -bool TestClock::tick() throw(anna::RuntimeException) { - return a_manager->tick(); -} - diff --git a/example/diameter/launcher/testing/TestClock.hpp b/example/diameter/launcher/testing/TestClock.hpp deleted file mode 100644 index e007cb6..0000000 --- a/example/diameter/launcher/testing/TestClock.hpp +++ /dev/null @@ -1,29 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_TestClock_hpp -#define example_diameter_launcher_TestClock_hpp - -// Project -#include -#include - -class TestManager; - -class TestClock : public anna::timex::Clock { - TestManager *a_manager; - -public: - TestClock(const char *clockName, const anna::Millisecond & timeout, TestManager *manager) - : a_manager(manager), anna::timex::Clock(clockName, timeout) {;} - - virtual bool tick() throw(anna::RuntimeException); -}; - -#endif - diff --git a/example/diameter/launcher/testing/TestCondition.cpp b/example/diameter/launcher/testing/TestCondition.cpp deleted file mode 100644 index ced5db6..0000000 --- a/example/diameter/launcher/testing/TestCondition.cpp +++ /dev/null @@ -1,178 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -// Standard - -// Project -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -// Process -#include - - -const char* TestCondition::asText(const Type::_v type) -throw() { - static const char* text [] = { "Generic", "Basic" }; - return text [type]; -} - -bool TestCondition::exists() const throw() { - if (a_type == Type::Generic) - return (a_regexp != ""); - else - return (a_code != "" || a_bitR != "" || a_hopByHop != "" || a_applicationId != "" || a_sessionId != "" || a_resultCode != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != ""); -} - -bool operator==(const TestCondition &c1, const TestCondition &c2) throw() { - - if (c1.a_type != c2.a_type) return false; - - if (c1.a_type == TestCondition::Type::Generic) { - if (c1.a_regexp != c2.a_regexp) return false; - } - else { - if (c1.a_code != c2.a_code) return false; - if (c1.a_bitR != c2.a_bitR) return false; - if (c1.a_hopByHop != c2.a_hopByHop) return false; - if (c1.a_applicationId != c2.a_applicationId) return false; - if (c1.a_sessionId != c2.a_sessionId) return false; - if (c1.a_resultCode != c2.a_resultCode) return false; - if (c1.a_msisdn != c2.a_msisdn) return false; - if (c1.a_imsi != c2.a_imsi) return false; - if (c1.a_serviceContextId != c2.a_serviceContextId) return false; - } - - return true; -} - -bool TestCondition::comply(const anna::DataBlock &message/*, bool matchSessionId*/) const throw() { - - if (a_type == Type::Generic) { - anna::diameter::codec::Message codecMsg; - try { codecMsg.decode(message); } catch (anna::RuntimeException &ex) { ex.trace(); } - return codecMsg.isLike(a_regexp); - } - - // Basic - std::string compare; - anna::diameter::CommandId cid; - - if (a_code != "" || a_bitR != "") { - try { - cid = anna::diameter::codec::functions::getCommandId(message); - } - catch (anna::RuntimeException &) { return false; } - } - - if (a_code != "") { - compare = anna::functions::asString(cid.first); - if (a_code != compare) return false; - } - - if (a_bitR != "") { - compare = (cid.second ? "1":"0"); - if (a_bitR != compare) return false; - } - - if (a_hopByHop != "") { - try { - anna::diameter::HopByHop h = anna::diameter::codec::functions::getHopByHop(message); - compare = anna::functions::asString(h); - } - catch (anna::RuntimeException &) { return false; } - if (a_hopByHop != compare) return false; - } - - if (a_applicationId != "") { - try { - anna::diameter::ApplicationId a = anna::diameter::codec::functions::getApplicationId(message); - compare = anna::functions::asString(a); - } - catch (anna::RuntimeException &) { return false; } - if (a_applicationId != compare) return false; - } - - //if (matchSessionId) { - if (a_sessionId != "") { - try { - compare = anna::diameter::helpers::base::functions::getSessionId(message); - } - catch (anna::RuntimeException &) { return false; } - if (a_sessionId != compare) return false; - } - //} - - if (a_resultCode != "") { - try { - anna::U32 rc = anna::diameter::helpers::base::functions::getResultCode(message); - compare = anna::functions::asString(rc); - } - catch (anna::RuntimeException &) { return false; } - if (a_resultCode != compare) return false; - } - - if (a_msisdn != "") { - try { - compare = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164); - } - catch (anna::RuntimeException &) { return false; } - if (a_msisdn != compare) return false; - } - - if (a_imsi != "") { - try { - compare = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI); - } - catch (anna::RuntimeException &) { return false; } - if (a_imsi != compare) return false; - } - - if (a_serviceContextId != "") { - try { - compare = anna::diameter::helpers::dcca::functions::getServiceContextId(message); - } - catch (anna::RuntimeException &) { return false; } - if (a_serviceContextId != compare) return false; - } - - return true; -} - -anna::xml::Node* TestCondition::asXML(anna::xml::Node* parent) const -throw() { - anna::xml::Node* result = parent->createChild("TestCondition"); - if (!exists()) return result; - - if (a_type == Type::Generic) { - if (a_regexp != "") result->createAttribute("Regexp", a_regexp); - } - else { - if (a_code != "") result->createAttribute("Code", atoi(a_code.c_str())); - if (a_bitR != "") result->createAttribute("BitR", ((a_bitR == "1") ? "yes":"no")); - if (a_hopByHop != "") result->createAttribute("HopByHop", atoll(a_hopByHop.c_str())); - if (a_applicationId != "") result->createAttribute("ApplicationId", atoll(a_applicationId.c_str())); - if (a_sessionId != "") result->createAttribute("SessionId", a_sessionId); - if (a_resultCode != "") result->createAttribute("ResultCode", atoi(a_resultCode.c_str())); - if (a_msisdn != "") result->createAttribute("Msisdn", a_msisdn); - if (a_imsi != "") result->createAttribute("Imsi", a_imsi); - if (a_serviceContextId != "") result->createAttribute("ServiceContextId", a_serviceContextId); - } - - result->createAttribute("ExpectedSource", a_rcvFromEntity ? "entity":"client"); - - return result; -} diff --git a/example/diameter/launcher/testing/TestCondition.hpp b/example/diameter/launcher/testing/TestCondition.hpp deleted file mode 100644 index 57cba63..0000000 --- a/example/diameter/launcher/testing/TestCondition.hpp +++ /dev/null @@ -1,104 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_TestCondition_hpp -#define example_diameter_launcher_TestCondition_hpp - -// Standard -#include - -// Project -#include - - -namespace anna { - namespace xml { - class Node; - } -} - - -class TestCondition { - - public: - - struct Type { enum _v { Generic, Basic }; }; - static const char* asText(const Type::_v type) throw(); - - TestCondition() : a_rcvFromEntity(true), - a_regexp(""), - a_code(""), a_bitR(""), a_hopByHop(""), a_applicationId(""), - a_sessionId(""), a_resultCode(""), - a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Basic; } - - - // Source of the received message - void setReceivedFromEntity(bool rfe) throw() { a_rcvFromEntity = rfe; } - bool receivedFromEntity() const throw() { return a_rcvFromEntity; } - - // Generic - void setRegexp(const std::string ®exp) throw() { a_regexp = regexp; } - // Basic - void setCode(const std::string &value) throw() { a_code = value; } - void setBitR(const std::string &value) throw() { a_bitR = value; } - void setHopByHop(const std::string &value) throw() { a_hopByHop = value; } - void setApplicationId(const std::string &value) throw() { a_applicationId = value; } - void setSessionId(const std::string &value) throw() { a_sessionId = value; } - void setResultCode(const std::string &value) throw() { a_resultCode = value; } - void setMsisdn(const std::string &value) throw() { a_msisdn = value; } - void setImsi(const std::string &value) throw() { a_imsi = value; } - void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; } - - bool exists() const throw(); - friend bool operator==(const TestCondition &c1, const TestCondition &c2) throw(); - - // Generic - const std::string & getRegexp() const throw() { return a_regexp; } - // Basic - const std::string & getCode() const throw() { return a_code; } - const std::string & getBitR() const throw() { return a_bitR; } - const std::string & getHopByHop() const throw() { return a_hopByHop; } - const std::string & getApplicationId() const throw() { return a_applicationId; } - const std::string & getSessionId() const throw() { return a_sessionId; } - const std::string & getResultCode() const throw() { return a_resultCode; } - const std::string & getMsisdn() const throw() { return a_msisdn; } - const std::string & getImsi() const throw() { return a_imsi; } - const std::string & getServiceContextId() const throw() { return a_serviceContextId; } - - - bool comply (const anna::DataBlock &message/*, bool matchSessionId*/) const throw(); - - - anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); - - - private: - - // Source of the received message - bool a_rcvFromEntity; - - // Type - Type::_v a_type; - - // Generic: - std::string a_regexp; - - // Basic: - std::string a_code; - std::string a_bitR; - std::string a_hopByHop; - std::string a_applicationId; - std::string a_sessionId; - std::string a_resultCode; - std::string a_msisdn; - std::string a_imsi; - std::string a_serviceContextId; -}; - - -#endif diff --git a/example/diameter/launcher/testing/TestManager.cpp b/example/diameter/launcher/testing/TestManager.cpp deleted file mode 100644 index cf768ab..0000000 --- a/example/diameter/launcher/testing/TestManager.cpp +++ /dev/null @@ -1,551 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - -// Standard -#include -#include - -// Project -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Process -#include -#include -#include -#include - - -class TestTimer; - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////// -void TestManager::StatSummary::newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw() { - - if ((beginState == TestCase::State::Initialized)&&(endState == TestCase::State::Initialized)) { // special case (new test case provisioning) - a_initializedTcs++; - return; - } - - switch (beginState) { - case TestCase::State::Initialized: a_initializedTcs--; break; - case TestCase::State::InProgress: a_inprogressTcs--; break; - case TestCase::State::Failed: a_failedTcs--; break; - case TestCase::State::Success: a_sucessTcs--; break; - default: break; - } - switch (endState) { - case TestCase::State::Initialized: a_initializedTcs++; break; - case TestCase::State::InProgress: a_inprogressTcs++; break; - case TestCase::State::Failed: a_failedTcs++; break; - case TestCase::State::Success: a_sucessTcs++; break; - default: break; - } -} - -void TestManager::StatSummary::clear() throw() { - a_initializedTcs = 0; - a_inprogressTcs = 0; - a_failedTcs = 0; - a_sucessTcs = 0; -} - -anna::xml::Node *TestManager::StatSummary::asXML(anna::xml::Node* parent) const throw() { - anna::xml::Node* result = parent->createChild("StatSummary"); - - anna::xml::Node* tcs = result->createChild("TestCasesCounts"); - tcs->createAttribute("Total", a_initializedTcs + a_inprogressTcs + a_failedTcs + a_sucessTcs); - tcs->createAttribute("Initialized", a_initializedTcs); - tcs->createAttribute("InProgress", a_inprogressTcs); - tcs->createAttribute("Failed", a_failedTcs); - tcs->createAttribute("Success", a_sucessTcs); - - return result; -} -/////////////////////////////////////////////////////////////////////////////////////////////////// - - - -TestManager::TestManager() : - anna::timex::TimeEventObserver("TestManager") { - a_timeController = NULL; - a_reportsDirectory = "./"; - - a_dumpInProgressReports = false; - a_dumpInitializedReports = false; - a_dumpFailedReports = false; - a_dumpSuccessReports = false; - - a_dumpHexMessages = false; - a_synchronousAmount = 1; - a_poolRepeats = 0; // repeat disabled by default - a_poolCycle = 1; - a_inProgressLimit = UINT_MAX; // no limit - a_clock = NULL; - //a_testPool.clear(); - //a_statSummary.clear(); - a_currentTestIt = a_testPool.end(); -} - -void TestManager::registerSessionId(const std::string &sessionId, const TestCase *testCase) throw(anna::RuntimeException) { - - std::map::const_iterator it = a_sessionIdTestCaseMap.find(sessionId); - if (it != a_sessionIdTestCaseMap.end()) { // found - unsigned int id = it->second->getId(); - if (id != testCase->getId()) { - throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such sessionId: %s", id, sessionId.c_str()), ANNA_FILE_LOCATION); - } - } - else { - a_sessionIdTestCaseMap[sessionId] = const_cast(testCase); - LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerSessionId for test case (id = %llu): %s)", testCase->getId(), sessionId.c_str()), ANNA_FILE_LOCATION)); - } -} - -void TestManager::registerSubscriberId(const std::string &subscriberId, const TestCase *testCase) throw(anna::RuntimeException) { - - std::map::const_iterator it = a_subscriberIdTestCaseMap.find(subscriberId); - if (it != a_subscriberIdTestCaseMap.end()) { // found - unsigned int id = it->second->getId(); - if (id != testCase->getId()) { - throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such subscriberId: %s", id, subscriberId.c_str()), ANNA_FILE_LOCATION); - } - } - else { - a_subscriberIdTestCaseMap[subscriberId] = const_cast(testCase); - LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerSubscriberId for test case (id = %llu): %s)", testCase->getId(), subscriberId.c_str()), ANNA_FILE_LOCATION)); - } -} - -TestTimer* TestManager::createTimer(TestCaseStep* testCaseStep, const anna::Millisecond &timeout, const TestTimer::Type::_v type) -throw(anna::RuntimeException) { - TestTimer* result(NULL); - - if(a_timeController == NULL) - throw anna::RuntimeException("You must invoke 'setTimerController' with a not NULL timex engine", ANNA_FILE_LOCATION); - - anna::Guard guard(a_timeController, "TestManager::createTimer"); // avoid interblocking - result = a_timers.create(); - result->setType(type); - result->setId((anna::timex::TimeEvent::Id) testCaseStep); - result->setObserver(this); - result->setContext(testCaseStep); - result->setTimeout(timeout); - - LOGDEBUG( - std::string msg("TestManager::createTimer | "); - msg += result->asString(); - anna::Logger::debug(msg, ANNA_FILE_LOCATION); - ); - - a_timeController->activate(result); - return result; -} - -void TestManager::cancelTimer(TestTimer* timer) -throw() { - if(timer == NULL) - return; - - LOGDEBUG( - std::string msg("TestManager::cancel | "); - msg += timer->asString(); - anna::Logger::debug(msg, ANNA_FILE_LOCATION); - ); - - try { - if(a_timeController == NULL) - a_timeController = anna::app::functions::component (ANNA_FILE_LOCATION); - - a_timeController->cancel(timer); - } catch(anna::RuntimeException& ex) { - ex.trace(); - } -} - -//------------------------------------------------------------------------------------------ -// Se invoca automaticamente desde anna::timex::Engine -//------------------------------------------------------------------------------------------ -void TestManager::release(anna::timex::TimeEvent* timeEvent) -throw() { - TestTimer* timer = static_cast (timeEvent); - timer->setContext(NULL); - a_timers.release(timer); -} - -bool TestManager::configureTTPS(int testTicksPerSecond) throw() { - - if (testTicksPerSecond == 0) { - if (a_clock) { - a_timeController->cancel(a_clock); - LOGDEBUG(anna::Logger::debug("Testing timer clock stopped !", ANNA_FILE_LOCATION)); - } - else { - LOGDEBUG(anna::Logger::debug("No testing timer started yet !", ANNA_FILE_LOCATION)); - } - return true; - } - else if (testTicksPerSecond < 0) { - LOGWARNING(anna::Logger::warning("Invalid 'ttps' provided", ANNA_FILE_LOCATION)); - return false; - } - - anna::Millisecond admlTimeInterval = anna::Millisecond(1000 / testTicksPerSecond); - a_synchronousAmount = 1; - - if (admlTimeInterval < anna::Millisecond(1)) { - LOGWARNING(anna::Logger::warning("Not allowed to configure more than 1000 events per second for for triggering testing system", ANNA_FILE_LOCATION)); - return false; - } - - Launcher& my_app = static_cast (anna::app::functions::getApp()); - const anna::Millisecond &admlMinResolution = my_app.getADMLMinResolution(); - - if (admlTimeInterval < admlMinResolution) { - int maximumObtained = 1000 / (int)admlMinResolution; - a_synchronousAmount = ceil((double)testTicksPerSecond/maximumObtained); - // calculate again: - admlTimeInterval = anna::Millisecond(a_synchronousAmount * 1000 / testTicksPerSecond); - } - - if (a_synchronousAmount > 1) { - LOGWARNING( - std::string msg = anna::functions::asString("Desired testing time trigger rate (%d events per second) requires more than one sending per event (%d every %lld milliseconds). Consider launch more instances with lower rate (for example %d ADML processes with %d ttps), or configure %d or more sockets to the remote endpoints to avoid burst sendings", - testTicksPerSecond, - a_synchronousAmount, - admlTimeInterval.getValue(), - a_synchronousAmount, - 1000/admlTimeInterval, - a_synchronousAmount); - - anna::Logger::warning(msg, ANNA_FILE_LOCATION); - ); - } - - if (a_clock) { - a_clock->setTimeout(admlTimeInterval); - } - else { - a_clock = new TestClock("Testing clock", admlTimeInterval, this); // clock - } - - if (!a_clock->isActive()) a_timeController->activate(a_clock); - - return true; -} - -bool TestManager::gotoTestCase(unsigned int id) throw() { - test_pool_it it = a_testPool.find(id); - if (it != a_testPool.end()) { - a_currentTestIt = it; - return true; - } - - return false; -} - -TestCase *TestManager::findTestCase(unsigned int id) const throw() { // id = -1 provides current test case triggered - - if (!tests()) return NULL; - test_pool_it it = ((id != -1) ? a_testPool.find(id) : a_currentTestIt); - if (it != a_testPool.end()) return const_cast(it->second); - return NULL; -} - -TestCase *TestManager::getTestCase(unsigned int id) throw() { - - test_pool_nc_it it = a_testPool.find(id); - if (it != a_testPool.end()) return it->second; - - TestCase *result = new TestCase(id); - a_testPool[id] = result; - return result; -} - -bool TestManager::clearPool() throw() { - if (!tests()) return false; - for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) delete it->second; - // TODO: stop the possible command threads or there will be a core dump - - a_testPool.clear(); - a_sessionIdTestCaseMap.clear(); - a_subscriberIdTestCaseMap.clear(); - a_currentTestIt = a_testPool.end(); - a_poolCycle = 1; - configureTTPS(0); // stop - a_statSummary.clear(); - return true; -} - -bool TestManager::resetPool(bool hard) throw() { - bool result = false; // any reset - - if (!tests()) return result; - for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) { - if (it->second->reset(hard)) - result = true; - } - //a_sessionIdTestCaseMap.clear(); - //a_subscriberIdTestCaseMap.clear(); - return result; -} - -bool TestManager::tick() throw() { - LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION)); - return execTestCases(a_synchronousAmount); -} - -bool TestManager::execTestCases(int sync_amount) throw() { - - if (!tests()) { - LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION)); - return false; - } - - // Synchronous sendings per tick: - int count = sync_amount; - while (count > 0) { - if (!nextTestCase()) return false; // stop the clock - count--; - } - - return true; -} - -bool TestManager::nextTestCase() throw() { - - while (true) { - - // Limit for in-progress test cases: - if (getInProgressCount() >= a_inProgressLimit) { - LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION)); - return true; // wait next tick to release OTA test cases - } - - // Next test case: - if (a_currentTestIt == a_testPool.end()) - a_currentTestIt = a_testPool.begin(); - else - a_currentTestIt++; - - // Completed: - if (a_currentTestIt == a_testPool.end()) { - if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) { - LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION)); - a_poolCycle = 1; - return false; - } - else { - LOGWARNING( - std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]"; - anna::Logger::warning(anna::functions::asString("Testing pool cycle %d completed (repeats configured: %d%s). Restarting for the %s cycle", a_poolCycle, a_poolRepeats, nolimit.c_str(), (a_poolRepeats == a_poolCycle) ? "last":"next"), ANNA_FILE_LOCATION); - ); - a_poolCycle++; - //a_currentTestIt = a_testPool.begin(); - return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state - // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed. - } - } - - // Soft reset to initialize already finished (in previous cycle) test cases: - a_currentTestIt->second->reset(false); - - // Process test case: - LOGDEBUG(anna::Logger::debug(anna::functions::asString("Processing test case id = %llu, currently '%s' state", a_currentTestIt->first, TestCase::asText(a_currentTestIt->second->getState())), ANNA_FILE_LOCATION)); - if (a_currentTestIt->second->getState() != TestCase::State::InProgress) { - a_currentTestIt->second->process(); - return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole - // time for complete the test cases pool regarding the single test case lifetime. You shouldn't - // forget to programm a test case timeout with a reasonable value - } - } -} - -TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() { - try { - sessionId = anna::diameter::helpers::base::functions::getSessionId(message); - } - catch (anna::RuntimeException &ex) { - //ex.trace(); - LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION)); - return NULL; - } - std::map::const_iterator sessionIdIt = a_sessionIdTestCaseMap.find(sessionId); - if (sessionIdIt != a_sessionIdTestCaseMap.end()) - return sessionIdIt->second; - - LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION)); - return NULL; -} - -TestCase *TestManager::getTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() { - try { - subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164); - if (subscriberId == "") // try with IMSI - subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI); - } - catch (anna::RuntimeException &ex) { - //ex.trace(); - LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION)); - return NULL; - } - std::map::const_iterator subscriberIdIt = a_subscriberIdTestCaseMap.find(subscriberId); - if (subscriberIdIt != a_subscriberIdTestCaseMap.end()) - return subscriberIdIt->second; - - LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION)); - return NULL; -} - -void TestManager::receiveMessage(const anna::DataBlock &message, OriginHost *host, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) { - - // Testing disabled: - if (!tests()) return; - - // Identify the test case: - std::string sessionId, subscriberId; - TestCase *tc; - tc = getTestCaseFromSessionId(message, sessionId); - if (!tc) - tc = getTestCaseFromSubscriberId(message, subscriberId); - if (!tc) { - LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from server: ", message), ANNA_FILE_LOCATION)); // this should not appear - return; - } - - // Work with Test case: - TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */); - if (!tsw) { // store as 'uncovered' - std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "':"; - - try { - static anna::diameter::codec::Message codecMsg; - codecMsg.decode(message); - hint += "\n"; hint += codecMsg.asXMLString(); - - // // Host checking: - // std::string messageOH = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue(); - // if (messageOH != host->getName()) { - // LOGWARNING(anna::Logger::warning(anna::functions::asString("Received message from host '%s', with different Origin-Host: %s", host->getName().c_str(), messageOH.c_str()), ANNA_FILE_LOCATION)); - // } - } - catch (anna::RuntimeException &ex) { - ex.trace(); - hint += "\n"; hint += ex.asString(); - } - hint += "\n"; hint += clientSession->asString(); - - tc->addDebugSummaryHint(hint); - } - else { - tsw->setClientSession(const_cast(clientSession)); - tc->process(); - } -} - -void TestManager::receiveMessage(const anna::DataBlock &message, OriginHost *host, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) { - - // Testing disabled: - if (!tests()) return; - - // Identify the test case: - std::string sessionId, subscriberId; - TestCase *tc; - tc = getTestCaseFromSessionId(message, sessionId); - if (!tc) - tc = getTestCaseFromSubscriberId(message, subscriberId); - if (!tc) { - LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from client: ", message), ANNA_FILE_LOCATION)); // this should not appear - return; - } - - // Work with Test case: - TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */); - if (!tsw) { // store as 'uncovered' - std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "':"; - - try { - static anna::diameter::codec::Message codecMsg; - codecMsg.decode(message); - hint += "\n"; hint += codecMsg.asXMLString(); - - // // Host checking: - // std::string messageOH = message.getAvp(anna::diameter::helpers::base::AVPID__Origin_Host)->getDiameterIdentity()->getValue(); - // if (messageOH != host->getName()) { - // LOGWARNING(anna::Logger::warning(anna::functions::asString("Received message from host '%s', with different Origin-Host: %s", host->getName().c_str(), messageOH.c_str()), ANNA_FILE_LOCATION)); - // } - } - catch (anna::RuntimeException &ex) { - ex.trace(); - hint += "\n"; hint += ex.asString(); - } - hint += "\n"; hint += serverSession->asString(); - - tc->addDebugSummaryHint(hint); - } - else { - tsw->setServerSession(const_cast(serverSession)); - tc->process(); - } -} - -anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const -throw() { - anna::xml::Node* result = parent->createChild("TestManager"); - - int poolSize = a_testPool.size(); - result->createAttribute("NumberOfTestCases", poolSize); - if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats); - else result->createAttribute("PoolRepeats", "disabled"); - result->createAttribute("PoolCycle", a_poolCycle); - a_statSummary.asXML(result); - if (a_inProgressLimit == UINT_MAX) - result->createAttribute("InProgressLimit", ""); - else - result->createAttribute("InProgressLimit", a_inProgressLimit); - result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no")); - result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no")); - result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no")); - result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no")); - result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no")); - result->createAttribute("ReportsDirectory", a_reportsDirectory); - if (a_clock) { - result->createAttribute("AsynchronousSendings", a_synchronousAmount); - int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout(); - result->createAttribute("TicksPerSecond", ticksPerSecond); - } - if (a_currentTestIt != a_testPool.end()) { - result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first); - } - if (poolSize != 0) { - anna::xml::Node* testCases = result->createChild("TestCases"); - for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) { - if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue; - if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue; - if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue; - if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue; - (*it).second->asXML(testCases); - } - } - - return result; -} - -std::string TestManager::asXMLString() const throw() { - anna::xml::Node root("root"); - return anna::xml::Compiler().apply(asXML(&root)); -} - diff --git a/example/diameter/launcher/testing/TestManager.hpp b/example/diameter/launcher/testing/TestManager.hpp deleted file mode 100644 index d1cc1c1..0000000 --- a/example/diameter/launcher/testing/TestManager.hpp +++ /dev/null @@ -1,192 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_TestManager_hpp -#define example_diameter_launcher_TestManager_hpp - -// Project -#include -#include -#include -#include -#include -#include - -// Process -#include -#include - - -namespace anna { - class Millisecond; - - namespace timex { - class Engine; - } - namespace diameter { - namespace comm { - class ClientSession; - class ServerSession; - } - } -} - - -class TestClock; -class TestCase; -class TestCaseStep; -class OriginHost; - - -typedef std::map test_pool_t; -typedef std::map::const_iterator test_pool_it; -typedef std::map::iterator test_pool_nc_it; - - -/** - Timer Manager for testing system -*/ -class TestManager : public anna::timex::TimeEventObserver, public anna::Singleton { - - // Statistics summary: - class StatSummary { - - unsigned int a_initializedTcs; - unsigned int a_inprogressTcs; - unsigned int a_failedTcs; - unsigned int a_sucessTcs; - - public: - StatSummary() { clear(); } - void newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw(); - void clear() throw(); - unsigned int getInProgressCount() const throw() { return a_inprogressTcs; } - anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); - }; - - - - typedef anna::Recycler timer_container; - - anna::timex::Engine* a_timeController; - - // reports - std::string a_reportsDirectory; - bool a_dumpInitializedReports, a_dumpInProgressReports, a_dumpFailedReports, a_dumpSuccessReports; - bool a_dumpHexMessages; - - // Pool of test cases - test_pool_t a_testPool; - test_pool_it a_currentTestIt; - int a_poolRepeats; // repeat pool N times - int a_poolCycle; // current cycle, from 1 to N - unsigned int a_inProgressLimit; // limit load to have this value - - // Test clock - int a_synchronousAmount; - TestClock *a_clock; - bool tick() throw(); - bool execTestCases(int sync_amount) throw(); - bool nextTestCase() throw(); - - // Test timers - timer_container a_timers; - - // Test case identifiers: - // Session-Id's - std::map a_sessionIdTestCaseMap; // stores used Session-Id values within a test case. - // No other can use them, but a test case could use more than one. - // Subscriber's - std::map a_subscriberIdTestCaseMap; // stores used Subscriber-Id values within a test case. - - - - StatSummary a_statSummary; // general statistics - - - TestManager(); - TestManager(const TestManager&); - - TestTimer* createTimer(TestCaseStep*, const anna::Millisecond &, const TestTimer::Type::_v type) throw(anna::RuntimeException); - void cancelTimer(TestTimer*) throw(); - void release(anna::timex::TimeEvent*) throw(); - - - public: - - void registerSessionId(const std::string &sessionId, const TestCase *testCase) throw(anna::RuntimeException); - void registerSubscriberId(const std::string &subscriberId, const TestCase *testCase) throw(anna::RuntimeException); - - int tests() const throw() { return a_testPool.size(); } - void setTimerController(anna::timex::Engine *engine) throw() { a_timeController = engine; } - - void setReportsDirectory(const std::string &rd) throw() { a_reportsDirectory = rd; } - const std::string &getReportsDirectory() const throw() { return a_reportsDirectory; } - - void setDumpHex(bool dh) throw() { a_dumpHexMessages = dh; } - bool getDumpHex() const throw() { return a_dumpHexMessages; } - - - void setDumpInitializedReports(bool enable) throw() { a_dumpInitializedReports = enable; } - void setDumpInProgressReports(bool enable) throw() { a_dumpInProgressReports = enable; } - void setDumpFailedReports(bool enable) throw() { a_dumpFailedReports = enable; } - void setDumpSuccessReports(bool enable) throw() { a_dumpSuccessReports = enable; } - void setDumpAllReports(bool enable) throw() { - setDumpInitializedReports(enable); - setDumpInProgressReports(enable); - setDumpFailedReports(enable); - setDumpSuccessReports(enable); - } - - bool getDumpInitializedReports() const throw() { return a_dumpInitializedReports; } - bool getDumpInProgressReports() const throw() { return a_dumpInProgressReports; } - bool getDumpFailedReports() const throw() { return a_dumpFailedReports; } - bool getDumpSuccessReports() const throw() { return a_dumpSuccessReports; } - - // Helper to calculate time interval and synchronous amount of execution tests to guarantee the input rate (tests per second) - // through the time manager which has a minimum resolution of ADML minimum resolution. The first call to this method will - // start the time trigger system and check for new test cases to be launched. - bool configureTTPS(int testTicksPerSecond) throw(); - - bool clearPool() throw(); - bool resetPool(bool hard /* hard reset includes in-progress test cases */) throw(); - void setPoolRepeats(int repeats) throw() { a_poolRepeats = repeats; } - int getPoolRepeats() const throw() { return a_poolRepeats; } - int getPoolCycle() const throw() { return a_poolCycle; } - - unsigned int getInProgressCount() const throw() { return a_statSummary.getInProgressCount(); } - unsigned int getInProgressLimit() const throw() { return a_inProgressLimit; } - void setInProgressLimit(unsigned int limit) throw() { a_inProgressLimit = limit; } // 0 = UINT_MAX (no limit) - - bool gotoTestCase(unsigned int id) throw(); - TestCase *findTestCase(unsigned int id) const throw(); // id = -1 provides current test case triggered - TestCase *getTestCase(unsigned int id) throw(); // creates/reuses a test case - - // Main logic - TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw(); - TestCase *getTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw(); - void receiveMessage(const anna::DataBlock &message, OriginHost *host, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException); - void receiveMessage(const anna::DataBlock &message, OriginHost *host, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException); - - anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); - std::string asXMLString() const throw(); - - // stats - void tcsStateStats(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw() { - a_statSummary.newTCState(beginState, endState); - } - - - friend class anna::Singleton ; - friend class TestStepTimeout; // createTimer - friend class TestStepDelay; // createTimer - friend class TestClock; // tick - friend class Launcher; // tick -}; - -#endif diff --git a/example/diameter/launcher/testing/TestStep.cpp b/example/diameter/launcher/testing/TestStep.cpp deleted file mode 100644 index fdbd798..0000000 --- a/example/diameter/launcher/testing/TestStep.cpp +++ /dev/null @@ -1,707 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -// Standard -#include -#include -#include - -#include // sigaction, sigemptyset, struct sigaction, SIGCHLD, SA_RESTART, SA_NOCLDSTOP -#include // perror -#include // exit -#include // waitpid, pid_t, WNOHANG - -// cmd with fork: -#include -#include - - -// Project -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Process -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace { - - void handle_sigchld(int sig) { - while (waitpid((pid_t)(-1 /* any child (the only) */), 0, WNOHANG|WNOWAIT) > 0) {} - } - - void cmdRunOnThread (TestStepCmd *step, const std::string &cmd) { - - // Thread running: - step->setThreadRunning(true); - - int status = -2; - - struct sigaction sa; - sa.sa_handler = &handle_sigchld; - sigemptyset(&sa.sa_mask); - 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; - } - */ - } - else { - perror(0); - } - // This can be implemented portably and somewhat more concisely with the signal function if you prefer: - // if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) { - // perror(0); - // exit(1); - // } - - if (status < 0) { - char buf[256]; - char const * str = strerror_r(errno, buf, 256); - step->setErrorMsg(anna::functions::asString("errno = %d (%s)", errno, str)); - } - - step->setResultCode(WEXITSTATUS(status)); // rc = status >>= 8; // divide by 256 - step->complete(); - // TODO: terminate thread when deprecated (RT signal ?) - // TODO: mutex the step while setting data here !! - } - - 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 { - messageCodec.clear(); - messageCodec.decode(message); - } - catch (anna::RuntimeException &ex) { - ex.trace(); - result = false; - } - - return result; - } -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// TestStep -//////////////////////////////////////////////////////////////////////////////////////////////////////// -void TestStep::initialize(TestCase *testCase) { - a_testCase = testCase; - a_completed = false; - a_type = Type::Unconfigured; - a_beginTimestamp = 0; - a_endTimestamp = 0; - 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", "Command" }; - return text [type]; -} - -anna::xml::Node* TestStep::asXML(anna::xml::Node* parent) -throw() { - anna::xml::Node* result = parent->createChild("TestStep"); - - result->createAttribute("Number", a_number); - result->createAttribute("Type", asText(a_type)); - result->createAttribute("Completed", (a_completed ? "yes":"no")); - - // 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); - 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); - result->createAttribute("EndTimestamp", s_aux); - - return result; -} - -std::string TestStep::asXMLString() throw() { - anna::xml::Node root("root"); - return anna::xml::Compiler().apply(asXML(&root)); -} - -bool TestStep::execute() throw() { - - 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)", 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)", 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(); -} - -void TestStep::next() throw() { - a_testCase->nextStep(); - a_testCase->process(); -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// TestStepTimeout -//////////////////////////////////////////////////////////////////////////////////////////////////////// -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"); - result->createAttribute("Timeout", a_timeout.asString()); - - return result; -} - -bool TestStepTimeout::do_execute() throw() { - try { - a_timer = TestManager::instantiate().createTimer((TestCaseStep*)this, a_timeout, TestTimer::Type::TimeLeft); - } - catch (anna::RuntimeException &ex) { - ex.trace(); - a_testCase->addDebugSummaryHint(ex.asString()); // before report (when set Failed state) - a_testCase->setState(TestCase::State::Failed); - } - - return true; // go next -} - -void TestStepTimeout::do_complete() throw() { - int stepNumber = getNumber(); - if (stepNumber == a_testCase->steps()) { - 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) - a_testCase->setState(TestCase::State::Success); - } - else if (a_testCase->getState() == TestCase::State::InProgress) { // sure - a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) before test case finished", stepNumber)); // before report (when set Failed state) - a_testCase->setState(TestCase::State::Failed); - } - - a_timer = NULL; -} - -void TestStepTimeout::do_reset() throw() { - try { - TestManager::instantiate().cancelTimer(a_timer); - } - catch (anna::RuntimeException &ex) { - ex.trace(); - } - a_timer = NULL; - //a_timeout = 0; THIS IS CONFIGURATION INFO -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// TestStepSendxml -//////////////////////////////////////////////////////////////////////////////////////////////////////// -anna::xml::Node* TestStepSendxml::asXML(anna::xml::Node* parent) -throw() { - anna::xml::Node* result = TestStep::asXML(parent); - //parent->createChild("TestStepSendxml"); - std::string msg = "", xmlmsg = ""; - - // Message - if (TestManager::instantiate().getDumpHex()) { - if (a_message.isEmpty()) { - msg = ""; - } - else { - msg = "\n"; msg += a_message.asString(); msg += "\n"; - } - } - - if (decodeMessage()) { - xmlmsg = "\n"; - xmlmsg += a_messageCodec->asXMLString(); - xmlmsg += "\n"; - } - else { - xmlmsg = ""; - } - - if (msg != "") result->createAttribute("Message", msg); - if (xmlmsg != "") result->createAttribute("XMLMessage", xmlmsg); - result->createAttribute("Expired", (a_expired ? "yes":"no")); - if (a_waitForRequestStepNumber != -1) - result->createAttribute("WaitForRequestStepNumber", a_waitForRequestStepNumber); - - return result; -} - -bool TestStepSendxml::do_execute() throw() { - bool success = false; - std::string failReason; - MyDiameterEntity *entity = a_originHost->getEntity(); // by default - MyLocalServer *localServer = a_originHost->getDiameterServer(); // by default - const TestStepWait *tsw = NULL; - anna::diameter::comm::Message *msg = a_originHost->createCommMessage(); - - try { - - 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; - - if (tsw) { // is an answer for a received request on wait condition - anna::diameter::comm::ClientSession *clientSession = tsw->getClientSession(); - if (clientSession) { - /* response NULL (is an answer) */clientSession->send(msg); - success = true; - usedClientSession = clientSession; - } - else { - failReason = "Reference wait step didn't store a valid client session. Unable to send the message"; - LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); - } - } - else { - if (entity) { - success = entity->send(msg); - anna::diameter::comm::Server *usedServer = entity->getLastUsedResource(); - usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL; - } - else { - failReason = "There is no diameter entity currently configured. Unable to send the message"; - LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); - } - } // else (normal sending) - - // Detailed log: - if(a_originHost->logEnabled()) { - if (decodeMessage(true /* trust */)) { - std::string detail = usedClientSession ? usedClientSession->asString() : ""; // shouldn't happen - a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2e" : "send2eError"), detail); - } - } - } - else if (getType() == Type::Sendxml2c) { - anna::diameter::comm::ServerSession *usedServerSession = NULL; - - if (tsw) { // is an answer for a received request on wait condition - anna::diameter::comm::ServerSession *serverSession = tsw->getServerSession(); - if (serverSession) { - /* response NULL (is an answer) */serverSession->send(msg); - success = true; - usedServerSession = serverSession; - } - else { - failReason = "Reference wait step didn't store a valid server session. Unable to send the message"; - LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); - } - } - else { - if (localServer) { - success = localServer->send(msg); - usedServerSession = localServer->getLastUsedResource(); - } - else { - failReason = "There is no diameter local server currently configured. Unable to send the message"; - LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); - } - } // else (normal sending) - - // Detailed log: - if(a_originHost->logEnabled()) { - if (decodeMessage(true /* trust */)) { - std::string detail = usedServerSession ? usedServerSession->asString() : ""; // shouldn't happen - a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2c" : "send2cError"), detail); - } - } - } - - } catch(anna::RuntimeException &ex) { - failReason = ex.asString(); - } - - // release msg - a_originHost->releaseCommMessage(msg); - - if (!success) { - a_testCase->addDebugSummaryHint(failReason); // before report (when set Failed state); - a_testCase->setState(TestCase::State::Failed); - } - else { - complete(); - } - - return success; // go next if sent was OK -} - -void TestStepSendxml::do_reset() throw() { - a_expired = false; - //a_message.clear(); - //a_messageAlreadyDecoded = false; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// TestStepDelay -//////////////////////////////////////////////////////////////////////////////////////////////////////// -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 == 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); - } - catch (anna::RuntimeException &ex) { - ex.trace(); - a_testCase->addDebugSummaryHint(ex.asString()); // before report (when set Failed state) - a_testCase->setState(TestCase::State::Failed); - } - - return false; // don't go next (wait complete) -} - -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); - } - catch (anna::RuntimeException &ex) { - ex.trace(); - } - a_timer = NULL; - //a_delay = 0; THIS IS CONFIGURATION INFO -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// TestStepWait -//////////////////////////////////////////////////////////////////////////////////////////////////////// -void TestStepWait::setCondition(bool fromEntity, - 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); - a_condition.setBitR(bitR); - a_condition.setResultCode(resultCode); - a_condition.setSessionId(sessionId); - a_condition.setHopByHop(hopByHop); - a_condition.setMsisdn(msisdn); - a_condition.setImsi(imsi); - a_condition.setServiceContextId(serviceContextId); -} - -void TestStepWait::setCondition(bool fromEntity, const std::string ®exp) throw() { - - a_condition.setReceivedFromEntity(fromEntity); - a_condition.setRegexp(regexp); -} - -anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent) -throw() { - anna::xml::Node* result = TestStep::asXML(parent); - //parent->createChild("TestStepWait"); - std::string msg = "", xmlmsg = ""; - - // Condition - a_condition.asXML(result); - - // Message - if (TestManager::instantiate().getDumpHex()) { - if (a_message.isEmpty()) { - msg = ""; - } - else { - msg = "\n"; msg += a_message.asString(); msg += "\n"; - } - } - - 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; -} - -bool TestStepWait::do_execute() throw() { - return a_completed; -} - -void TestStepWait::do_complete() throw() { - //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 - complete(); - return true; - } - - return false; -} - -void TestStepWait::do_reset() throw() { - a_message.clear(); - a_clientSession = NULL; - a_serverSession = NULL; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////// -// TestStepCmd -//////////////////////////////////////////////////////////////////////////////////////////////////////// -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:""); - if (a_errorMsg != "") result->createAttribute("ErrorMessage", a_errorMsg); - 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; -} - -bool TestStepCmd::do_execute() throw() { - if (!a_threadRunning /* || a_threadDeprecated DO NOT WANT TO OVERLAP ... */) { - // Special tags to replace: - std::string cmd = getScript(); - 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())); - while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID)) != std::string::npos) - cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID), anna::functions::asString(a_testCase->getId())); - while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID)) != std::string::npos) - 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 ! -} - -void TestStepCmd::do_complete() throw() { - - a_threadRunning = false; - if (a_threadDeprecated) { - a_threadDeprecated = false; - do_reset(); - setErrorMsg(anna::functions::asString("Step %d deprecated due to previous reset for Test Case %llu", getNumber(), a_testCase->getId())); - a_testCase->setState(TestCase::State::Failed); - return; // ignore TODO: interrupt the thread to avoid execution of the script - } - - if (getResultCode() != 0) - 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: 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; -} - diff --git a/example/diameter/launcher/testing/TestStep.hpp b/example/diameter/launcher/testing/TestStep.hpp deleted file mode 100644 index 75cbae3..0000000 --- a/example/diameter/launcher/testing/TestStep.hpp +++ /dev/null @@ -1,263 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_TestStep_hpp -#define example_diameter_launcher_TestStep_hpp - -// Standard -#include -#include -#include - -// Project -#include -#include -#include - -// Process -#include - - - - -namespace anna { - class Millisecond; - - namespace xml { - class Node; - } - namespace diameter { - namespace codec { - class Message; - } - namespace comm { - class ClientSession; - class ServerSession; - } - } -} - -class TestCase; -class TestTimer; -class OriginHost; - -class TestStep { - - int a_number; // step number used for xml (informational) - anna::Millisecond a_beginTimestamp; // unix time - anna::Millisecond a_endTimestamp; // unix time - bool a_executed; // used for interactive mode in order to not repeat a execution step if before completing, the user add interactive amount - - void setBeginTimestamp(const anna::Millisecond &t) throw() { a_beginTimestamp = t; } - const anna::Millisecond &getBeginTimestamp() const throw() { return a_beginTimestamp; } - void setEndTimestamp(const anna::Millisecond &t) throw() { a_endTimestamp = t; } - const anna::Millisecond &getEndTimestamp() const throw() { return a_endTimestamp; } - - void initialize(TestCase *testCase); - - public: - struct Type { enum _v { Unconfigured, Timeout, Sendxml2e, Sendxml2c, Delay, Wait, Cmd }; }; - static const char* asText(const Type::_v type) throw(); - - TestStep(TestCase *testCase) : a_message(true), a_messageCodec(NULL), a_executed(false) { initialize(testCase); } - virtual ~TestStep() {;} - - // setter & getters - const Type::_v &getType() const throw() { return a_type; } - const int &getNumber() const throw() { return a_number; } - bool isCompleted() const throw() { return a_completed; } - - bool execute() throw(); - void complete() throw(); - void reset() throw(); - void next() throw(); - virtual anna::xml::Node* asXML(anna::xml::Node* parent) throw(); - std::string asXMLString() throw(); - - protected: - TestCase *a_testCase; - bool a_completed; - Type::_v a_type; - - // Message (not for all step types) - anna::DataBlock a_message; - anna::diameter::codec::Message *a_messageCodec; // used as helper and for traffic logs - bool decodeMessage(bool trust = false) throw(); // If trust=true: decoding the previously encoded message (sendxml sentences). - // The only error would be validation ones, and we are going to ignore them here. - - virtual bool do_execute() throw() = 0; // returns true if next step must be executed - virtual void do_complete() throw() = 0; // end of transaction (delay/timeout expired, wait condition fulfilled, sending done) - // In all cases, the next step will be executed except 'timeout' which is asynchronous - // and will move to the next step just after timer creation (no complete waited) - virtual void do_reset() throw() = 0; -}; - - -class TestStepTimeout : public TestStep { - - anna::Millisecond a_timeout; - TestTimer *a_timer; // just in case i would need to cancel - - public: - TestStepTimeout(TestCase *testCase) : TestStep(testCase), a_timeout(0), a_timer(NULL) { a_type = Type::Timeout; } - - // setter & getters - void setTimeout(const anna::Millisecond &t) throw() { a_timeout = t; } - const anna::Millisecond &getTimeout() const throw() { return a_timeout; } - - // virtuals - bool do_execute() throw(); - void do_complete() throw(); // timeout reached, test case failed - void do_reset() throw(); - anna::xml::Node* asXML(anna::xml::Node* parent) throw(); -}; - - -class TestStepSendxml : public TestStep { - - protected: - // possible end points: - OriginHost *a_originHost; - - // Step number reference ('wait for request' step) - int a_waitForRequestStepNumber; - - // Expired ? - bool a_expired; // a_endTimestamp will be the expiration reception timestamp - - public: - TestStepSendxml(TestCase *testCase) : TestStep(testCase), - a_expired(false), - a_originHost(NULL), - a_waitForRequestStepNumber(-1) {;} - ~TestStepSendxml() {;} - - // setter & getters - void setOriginHost(OriginHost *host) throw() { a_originHost = host; } - OriginHost *getOriginHost() const throw() { return a_originHost; } - void setWaitForRequestStepNumber(int stepNumber) throw() { a_waitForRequestStepNumber = stepNumber; } - int getWaitForRequestStepNumber() const throw() { return a_waitForRequestStepNumber; } - void setMsgDataBlock(const anna::DataBlock &db) throw() { a_message = db; } - const anna::DataBlock &getMsgDataBlock() const throw() { return a_message; } - - // virtuals - bool do_execute() throw(); - void do_complete() throw() {;} - void do_reset() throw(); - anna::xml::Node* asXML(anna::xml::Node* parent) throw(); -}; - -class TestStepSendxml2e : public TestStepSendxml { - public: - TestStepSendxml2e(TestCase *testCase) : TestStepSendxml(testCase) { a_type = Type::Sendxml2e; } -}; - -class TestStepSendxml2c : public TestStepSendxml { - public: - TestStepSendxml2c(TestCase *testCase) : TestStepSendxml(testCase) { a_type = Type::Sendxml2c; } -}; - - -class TestStepDelay : public TestStep { - anna::Millisecond a_delay; - TestTimer *a_timer; // just in case i would need to cancel - - public: - TestStepDelay(TestCase *testCase) : TestStep(testCase), a_delay(0), a_timer(NULL) { a_type = Type::Delay; } - - // setter & getters - void setDelay(const anna::Millisecond &d) throw() { a_delay = d; } - const anna::Millisecond &getDelay() const throw() { return a_delay; } - - // virtuals - bool do_execute() throw(); - void do_complete() throw(); // delay reached - void do_reset() throw(); - anna::xml::Node* asXML(anna::xml::Node* parent) throw(); -}; - - -class TestStepWait : public TestStep { - - TestCondition a_condition; - anna::diameter::comm::ClientSession *a_clientSession; - anna::diameter::comm::ServerSession *a_serverSession; - - public: - TestStepWait(TestCase *testCase) : TestStep(testCase) { - a_type = Type::Wait; - a_clientSession = NULL; - a_serverSession = NULL; - } - ~TestStepWait() {;} - - // setter & getters - void setCondition(bool fromEntity, - 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(); - void setCondition(bool fromEntity, const std::string ®exp) throw(); - - void setClientSession(anna::diameter::comm::ClientSession *cs) throw() { a_clientSession = cs; } - void setServerSession(anna::diameter::comm::ServerSession *ss) throw() { a_serverSession = ss; } - anna::diameter::comm::ClientSession *getClientSession() const throw() { return a_clientSession; } - anna::diameter::comm::ServerSession *getServerSession() const throw() { return a_serverSession; } - - const TestCondition &getCondition() const throw() { return a_condition; } - //void setMsgDataBlock(const anna::DataBlock &db) throw() { a_message = db; } - bool fulfilled(const anna::DataBlock &db/*, bool matchSessionId = true*/) throw(); - const anna::DataBlock &getMsgDataBlock() const throw() { return a_message; } - - - // virtuals - bool do_execute() throw(); // this will be executed when test case starts (at least we could measure the time until condition is fulfilled) - void do_complete() throw(); // condition fulfilled - void do_reset() throw(); - anna::xml::Node* asXML(anna::xml::Node* parent) throw(); -}; - - -class TestStepCmd : public TestStep { - - std::string a_script; - std::thread a_thread; - bool a_threadRunning; - bool a_threadDeprecated; - int a_resultCode; - std::string a_errorMsg; - //std::string a_output; // for POPEN - - pid_t a_childPid; - - public: - TestStepCmd(TestCase *testCase) : TestStep(testCase), a_threadRunning(false), a_threadDeprecated(false), a_resultCode(-2)/*, a_output("")*/, a_errorMsg(""), a_childPid(-1) { a_type = Type::Cmd; } - - // setter & getters - void setThreadRunning(bool running) throw() { a_threadRunning = running; } - - void setResultCode(int rc) throw() { a_resultCode = rc; } - int getResultCode() const throw() { return a_resultCode; } - void setErrorMsg(const std::string &em) throw() { a_errorMsg = em; } - const std::string &getErrorMsg() const throw() { return a_errorMsg; } - //void appendOutput(const std::string &output) throw() { a_output += output; } - //const std::string &getOutput() const throw() { return a_output; } - void setChildPid(pid_t pid) throw() { a_childPid = pid; } - const pid_t &getChildPid() const throw() { return a_childPid; } - - void setScript(const std::string &script) throw() { a_script = script; } - const std::string &getScript() const throw() { return a_script; } - - // virtuals - bool do_execute() throw(); - void do_complete() throw(); - void do_reset() throw(); - anna::xml::Node* asXML(anna::xml::Node* parent) throw(); -}; - - -#endif diff --git a/example/diameter/launcher/testing/TestTimer.cpp b/example/diameter/launcher/testing/TestTimer.cpp deleted file mode 100644 index abe8b4d..0000000 --- a/example/diameter/launcher/testing/TestTimer.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -// Project -#include - -// Process -#include -#include - -// Standard -#include - - -using namespace std; -using namespace anna; - - -void TestTimer::expire(anna::timex::Engine*) -throw(anna::RuntimeException) { - - TestStep *step = getTestCaseStep(); - - // action: - step->complete(); - - LOGDEBUG( - string msg("TestTimer::expire | Completed TestStep:\n\n"); - msg += step->asXMLString(); - anna::Logger::debug(msg, ANNA_FILE_LOCATION); - ); -} - -const char* TestTimer::asText(const Type::_v type) -throw() { - static const char* text [] = { "TimeLeft", "Delay" }; - return text [type]; -} - - -string TestTimer::asString() const -throw() { - string result("TestTimer { "); - result += anna::timex::Transaction::asString(); - result += " Type: "; - result += asText(a_type); - -// const TestStep *step = getTestCaseStep(); -// -// if(step != NULL) { -// result += " | "; -// result += step->asXMLString(); -// } else -// result += " | step: "; -// } - -// switch(getType()) { -// case Type::TimeLeft: -// //result += " | xxxxx: "; -// break; -// case Type::Delay: -// //result += " | xxxxx: "; -// break; -// } - - return result += " }"; -} - diff --git a/example/diameter/launcher/testing/TestTimer.hpp b/example/diameter/launcher/testing/TestTimer.hpp deleted file mode 100644 index 3f1b5eb..0000000 --- a/example/diameter/launcher/testing/TestTimer.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore // -// // -// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // -// // -// See project site at http://redmine.teslayout.com/projects/anna-suite // -// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // - - -#ifndef example_diameter_launcher_TestTimer_hpp -#define example_diameter_launcher_TestTimer_hpp - -#include -#include -#include - - - -class TestStep; - - -class TestTimer : public anna::timex::Transaction { - -public: - - struct Type { enum _v { TimeLeft, Delay }; }; - static const char* asText(const Type::_v type) throw(); - - - TestStep* getTestCaseStep() throw() { return reinterpret_cast (getContext()); } - const TestStep* getTestCaseStep() const throw() { return reinterpret_cast (getContext()); } - - void setType(const Type::_v type) throw() { a_type = type; } - const Type::_v &getType(const Type::_v type) const throw() { return a_type; } - - std::string asString() const throw(); - -private: - Type::_v a_type; - - TestTimer() {;} - - void expire(anna::timex::Engine*) throw(anna::RuntimeException); - - friend class anna::Allocator; -}; - -#endif - diff --git a/include/anna/diameter.comm/Engine.hpp b/include/anna/diameter.comm/Engine.hpp index 4416bec..72e3ad9 100644 --- a/include/anna/diameter.comm/Engine.hpp +++ b/include/anna/diameter.comm/Engine.hpp @@ -103,32 +103,32 @@ public: /** Diameter application node origin realm - @param originRealm Used to configure the Origin-Realm for outgoing messages. + @param originRealmName Used to configure the Origin-Realm for outgoing messages. If not configured or empty string provided, host domainname will be set. */ - void setOriginRealm(const std::string & originRealm) throw(); + void setOriginRealmName(const std::string & originRealmName) throw(); /** Diameter application origin host - @param originHost Used to configure the Origin-Host for outgoing messages. + @param originHostName Used to configure the Origin-Host for outgoing messages. If not configured or empty string provided, hostname (system name) will be set. */ - void setOriginHost(const std::string & originHost) throw(); + void setOriginHostName(const std::string & originHostName) throw(); /** Gets the configured diameter application node origin realm @return Diameter application node origin realm */ - const std::string & getOriginRealm() const throw() { return a_originRealm; } + const std::string & getOriginRealmName() const throw() { return a_originRealm; } /** Gets the configured diameter application origin host @return Diameter application node origin host */ - const std::string & getOriginHost() const throw() { return a_originHost; } + const std::string & getOriginHostName() const throw() { return a_originHost; } /** @@ -573,12 +573,20 @@ public: */ virtual void readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) throw(); + /** + * Sets optional CEA from file, when default is not enough + * + * @param &ceaPathfile Path file for the CEA xml message provided + */ + void setCEA(const std::string &ceaPathfile) throw() { a_ceaPathfile = ceaPathfile; } + /** * Class user should implement this method in order to define Capabilities-Exchange-Answer for received CER over server socket. * Origin-Host and Origin-Realm are configured at comm::Engine with hostname and FQDN (Fully Qualified Domain Name). * Default implementation imply CEA with DIAMETER_SUCCESS Result-Code, and own domain node parameters, but application should * analyze the CER message in order to accept it or not (with apropiate non-success Result-Code). - * Any other implementation is responsible to build a valid CEA diameter message: + * If @setCEA was invoked, a message from file is used instead of default implementation. + * Any other implementation is responsible to build a valid CEA diameter message, even ignoring a possible cea from file when @setCEA is used: * * If one peer sends a CER message to another Peer and receiver does not have support for * @@ -729,6 +737,7 @@ private: anna::Millisecond a_watchdogPeriod; // // ServerSessions messages: + std::string a_ceaPathfile; // path file to optional CEA (diameter local server configuration) // anna::DataBlock a_cea; // anna::DataBlock a_dwa; diff --git a/include/anna/diameter.comm/Entity.hpp b/include/anna/diameter.comm/Entity.hpp index 8dec08c..b372497 100644 --- a/include/anna/diameter.comm/Entity.hpp +++ b/include/anna/diameter.comm/Entity.hpp @@ -49,6 +49,16 @@ class ClientSession; */ class Entity { +public: + + /** + * Defines behaviour on validation procedure: complete analysis or stop at first validation error over the message (by default) + */ + struct SessionBasedModelsType { enum _v { RoundRobin, SessionIdOptionalPart, SessionIdHighPart, SessionIdLowPart /* default */ }; }; + +private: + + std::vector a_servers; int a_maxServers; // -1 means "no limit to add servers" std::string a_description; @@ -59,6 +69,10 @@ class Entity { // Engine Engine *a_engine; + // Balance and socket selection: + bool a_balance; + SessionBasedModelsType::_v a_sessionBasedModelsType; + // Availability bool a_available; // any of the servers must be bound void availabilityLost() throw(); @@ -86,6 +100,8 @@ class Entity { // Private close/destroy method void close(bool destroy) throw(anna::RuntimeException); + // helpers + static const char* asText(const SessionBasedModelsType::_v) throw(); public: @@ -192,6 +208,21 @@ public: Server *getLastUsedResource() const throw() { return (a_lastUsedResource); } + /** + * Balance over entity servers or use standard behaviour (first primary, secondary if fails, etc.). + * New created entities have balance disabled. + * + * @param balance True or false to enable or disable. + */ + void setBalance (bool balance) throw() { a_balance = balance; } + + /** + * Gets the balance mode + * + * @return True if balance over entity servers is enabled, false if standard behaviour is configured (default). + */ + bool getBalance() const throw() { return a_balance; } + // SIMPLE BALANCE or STANDARD documentation version // /** @@ -223,8 +254,8 @@ public: /** Sent a message to the entity. First uses primary server, secondary if fails and so on to the last defined resource (server) within entity. Another sending algorithm (non standard) could - be enabled (balance boolean parameter): it consist in round-robin server selection to set the - first resource in a complete cycle (standard behaviour tries all servers from FIRST defined). + be enabled (@setBalance): it consist in round-robin server selection to set the first resource + in a complete cycle (standard behaviour tries all servers from FIRST defined). Anyway, last used delivery resource could be known through #getLastUsedResource(). When the message is a request, a timer will be set automatically to control the response time. @@ -232,15 +263,26 @@ public: timeout value will be configured at #setClassCodeTimeout. \param message Message sent. - \param balance False by default (standard beaviour), but useful to balance over servers within entity. @return Boolean about success in send operation. Implies true result when any of the entity servers could send the message, and false when neither of the servers was available or fail to send the message (an alarm and error counter will be generated in this case). Broadcast try to send all over the resources in spite of any fail. */ - bool send(const Message *message, bool balance = false) throw(anna::RuntimeException); - bool send(const Message &message, bool balance = false) throw(anna::RuntimeException) { return send(&message, balance); } + bool send(const Message *message) throw(anna::RuntimeException); + bool send(const Message &message) throw(anna::RuntimeException) { return send(&message); } + + /** + * Sets sessions based models type. + * \param sessionBasedModelsType Session based models type: RoundRobin, SessionIdLowPart (default), SessionIdHighPart or SessionIdOptionalPart. + */ + void setSessionBasedModelsType(const SessionBasedModelsType::_v sessionBasedModelsType) throw() { a_sessionBasedModelsType = sessionBasedModelsType; } + + /** + * Returns sessions based models type. + * \return Session based models type: RoundRobin, SessionIdLowPart, SessionIdHighPart or SessionIdOptionalPart. + */ + SessionBasedModelsType::_v getSessionBasedModelsType() const throw() { return a_sessionBasedModelsType; } /** @@ -286,7 +328,7 @@ public: subscriber and service aware contextual load balancing strategies. By default, Session-Id avp is used to select the resource. Session-Id is split into 4 sections: diameter identity, high part, low part and optional part. Default implementation analizes 'low' part, returning its - value as reference for socket selection. + value as reference for socket selection. Use @setSessionBasedModelsType to change this behaviour. When server is configured as single client session (max client sessions equal to 1), entity will ignore diff --git a/include/anna/diameter.comm/OriginHost.hpp b/include/anna/diameter.comm/OriginHost.hpp new file mode 100644 index 0000000..8338e0d --- /dev/null +++ b/include/anna/diameter.comm/OriginHost.hpp @@ -0,0 +1,123 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_diameter_comm_OriginHost_hpp +#define anna_diameter_comm_OriginHost_hpp + +// Standard +#include +#include + +// Project +#include +#include +#include +#include +#include + + +namespace anna { + namespace diameter { + namespace codec { + class Engine; + } + namespace comm { + class Engine; + class Entity; + class LocalServer; + +/** + * OriginHost with single access point for client and server + */ +class OriginHost { + + anna::diameter::comm::Engine *a_commEngine; + anna::diameter::comm::Entity *a_entity; // we could get it finding the unique instante within comm engine, but it is more comfortable assign here. + anna::diameter::comm::LocalServer* a_diameterServer; // idem + anna::diameter::codec::Engine *a_codecEngine; + + // resources + int a_requestRetransmissions; + anna::Recycler a_commMessages; + + // main + unsigned int a_applicationId; + + // Logs & burst + std::string a_logFile; + bool a_splitLog, a_detailedLog, a_dumpLog; + std::string a_burstLogFile; + std::ofstream a_burstLogStream; + int a_burstCycle; + bool a_burstRepeat; + bool a_burstActive; + std::map < int /* dummy, p.e. used for order number */, anna::diameter::comm::Message* > a_burstMessages; + int a_burstLoadIndx; + std::map::const_iterator a_burstDeliveryIt; + int a_otaRequest; + int a_burstPopCounter; + +public: + OriginHost(anna::diameter::comm::Engine* commEngine, unsigned int applicationId); + ~OriginHost() {;} + + // OriginHost name: + const std::string &getName() const throw(); + + // Core resources: + anna::diameter::comm::Engine* getCommEngine() const throw() { return a_commEngine; } + unsigned int getApplicationId() const throw() { return a_applicationId; } + anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; } + + void createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException); + anna::diameter::comm::Entity *getEntity() const throw() { return a_entity; } + + void createDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout, const anna::Millisecond &applicationTimeout, const std::string &ceaPathfile) throw(anna::RuntimeException); + anna::diameter::comm::LocalServer* getDiameterServer() throw() { return a_diameterServer; } + void setRequestRetransmissions(int r) throw() { if (r >= 0) a_requestRetransmissions = r; } + + // Messages factory: + anna::diameter::comm::Message *createCommMessage() throw(anna::RuntimeException); + void releaseCommMessage(anna::diameter::comm::Message*) throw(); + + // Traffic logs & burst: + void setLogs(const std::string &log, bool splitLog, bool detailedLog, bool dumpLog, const std::string &burstLog) throw() { + a_logFile = log; + a_splitLog = splitLog; + a_detailedLog = detailedLog; + a_dumpLog = dumpLog; + a_burstLogFile = burstLog; + } + + bool logEnabled() const throw() { return (((a_logFile == "") || (a_logFile == "null")) ? false : true); } + void writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail) const throw(); + void writeLogFile(const anna::diameter::codec::Message & decodedMessage, const std::string &logExtension, const std::string &detail) const throw(); + void writeBurstLogFile(const std::string &buffer) throw(); + bool burstLogEnabled() const throw() { return (((a_burstLogFile == "") || (a_burstLogFile == "null")) ? false : true); } + int clearBurst() throw(); // returns removed + int loadBurstMessage(const anna::DataBlock & db) throw(anna::RuntimeException); + void repeatBurst(bool repeat) throw() { a_burstRepeat = repeat; } + int startBurst(int initialLoad) throw(); // return processed on start, or -1 if burst list is empty, -2 if invalid initial load (0 or negative) + int pushBurst(int loadAmount) throw(); // returns pushed (perhaps is less than provided because of no repeat mode and burst list exhausted), or -1 if burst list is empty, -2 if invalid load (0 or negative) + int sendBurst(int loadAmount) throw(); // returns sent (burst always cycled using send), returns -1 if burst list is empty, -2 if invalid load (0 or negative) + int popBurst(int releaseAmount) throw(); // returns popped (perhaps is less than provided because of OTA request), or -1 if burst stopped + int stopBurst() throw(); // returns remaining on cycle, or -1 if burst already stopped + bool burstActive() const throw() { return a_burstActive; } + bool sendBurstMessage(bool anyway = false) throw(); + std::string lookBurst(int order = -1) const throw(); + std::string gotoBurst(int order) throw(); + + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + std::string asXMLString() const throw(); +}; + +} +} +} + +#endif diff --git a/include/anna/diameter/codec/Message.hpp b/include/anna/diameter/codec/Message.hpp index a040aa1..e7edc85 100644 --- a/include/anna/diameter/codec/Message.hpp +++ b/include/anna/diameter/codec/Message.hpp @@ -796,3 +796,4 @@ public: #endif + diff --git a/include/anna/diameter/codec/MessagesDeque.hpp b/include/anna/diameter/codec/MessagesDeque.hpp new file mode 100644 index 0000000..8a52d30 --- /dev/null +++ b/include/anna/diameter/codec/MessagesDeque.hpp @@ -0,0 +1,58 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_diameter_codec_MessagesDeque_hpp +#define anna_diameter_codec_MessagesDeque_hpp + +// Standard +#include +#include +#include + +namespace anna { +namespace diameter { +namespace codec { + + +class Message; + + +class MessagesDeque { + +typedef std::deque codec_messages_deque; +typedef std::deque::iterator codec_messages_deque_iterator; +typedef std::deque::const_iterator codec_messages_deque_const_iterator; +typedef std::map < int /* message code */, codec_messages_deque* > messages_container; +typedef std::map < int /* message code */, codec_messages_deque* >::iterator messages_iterator; +typedef std::map < int /* message code */, codec_messages_deque* >::const_iterator messages_const_iterator; + + messages_container a_deques; + bool a_rotate; + + public: + MessagesDeque() { a_rotate = false; } + ~MessagesDeque() { clear(); } + + bool rotate() const throw() { return a_rotate; } + void rotate(bool r) throw() { a_rotate = r; } + + void clear () throw(); + void dump (const char *filenamePrefix = "programmed_message") throw(); + void addMessage(int code, anna::diameter::codec::Message *message) throw(); + anna::diameter::codec::Message* getMessage(int code) const throw(); + void nextMessage(int code) throw(); + std::string asString(const char *queueName) const throw(); +}; + +} +} +} + + +#endif + diff --git a/include/anna/testing/TestCase.hpp b/include/anna/testing/TestCase.hpp new file mode 100644 index 0000000..b722cac --- /dev/null +++ b/include/anna/testing/TestCase.hpp @@ -0,0 +1,144 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_testing_TestCase_hpp +#define anna_testing_TestCase_hpp + +// Standard +#include +#include +#include + +// Project +#include +#include +#include +#include + + + +namespace anna { + class Millisecond; + + namespace xml { + class Node; + } + namespace diameter { + namespace comm { + class OriginHost; + } + } + +namespace testing { + + class TestStep; + class TestStepWait; +class TestCase { + + void assertInitialized() const throw(anna::RuntimeException); + void assertMessage(const anna::DataBlock &db, bool toEntity) throw(anna::RuntimeException); + +public: + + // Debug summary: + class DebugSummary { + + typedef struct { + anna::Millisecond Timestamp; + std::string Hint; + } event_t; + + std::vector a_events; + public: + void addHint(const std::string &hint) throw(); + void clear() throw(); + int events() const throw() { return a_events.size(); } + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + }; + + TestCase(unsigned int id); + ~TestCase(); + + struct State { enum _v { Initialized, InProgress, Failed, Success }; }; + static const char* asText(const State::_v state) throw(); + const State::_v &getState() const throw() { return a_state; } + const anna::Millisecond &getStartTimestamp() const throw() { return a_startTime; } + void addDebugSummaryHint(const std::string &hint) throw() { a_debugSummary.addHint(hint); } + void setState(const State::_v &state) throw(); + bool isFinished() const throw() { return (getState() == State::Failed || getState() == State::Success); } + bool inProgress() const throw() { return (getState() == State::InProgress); } + bool hasSameCondition(const TestCondition &condition) const throw(); + + // Interactivity: + void makeInteractive(bool yes = true) throw() { a_interactiveAmount = (yes ? 0:-1); } + void addInteractiveAmount(unsigned int amount) throw() { + if (a_interactiveAmount == -1) makeInteractive(); + if (amount == 0) return; + a_interactiveAmount += amount; + process(); + } + int interactiveAmount() const throw() { return a_interactiveAmount; } + void interactiveExecution() throw() { a_interactiveAmount --; } + + // Step type & information + void addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException); + void addSendxml2e(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException); + void addSendxml2c(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException); + void addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException); + void addWait(bool fromEntity, + 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(anna::RuntimeException); + void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException); + void addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException); + void addCommand(const std::string &cmd) throw(anna::RuntimeException); + + + // Process: + void nextStep() throw() { a_stepsIt++; } + bool done() throw(); + bool process() throw(); // false to stop + + // Reset test case and underlaying information (steps context) + bool reset(bool hard /* hard reset includes in-progress test cases */) throw(); + + // getters + const anna::Millisecond &getStartTime() const throw() { return a_startTime; } + const unsigned int &getId() const throw() { return a_id; } + + //helpers + int steps() const throw() { return a_steps.size(); } + void addStep(TestStep *step) throw() { a_steps.push_back(step); } + + TestStepWait *searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw(); + // When a message arrives, we identify the test case by mean the Session-Id. Then, from the current step iterator (included), + // we search for a fulfilling condition for that message. The first found, is 'completed' and then breaks the search. + const TestStep *getStep(int stepNumber) const throw(); + + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + std::string asXMLString() const throw(); + + +private: + // private members: + unsigned int a_id; + std::vector a_steps; + std::vector::const_iterator a_stepsIt; + std::map a_hopByHops; // for wait-answer + State::_v a_state; + anna::Millisecond a_startTime; + DebugSummary a_debugSummary; // used when a test case has failed, uncovered message conditions, and any other hint. + int a_interactiveAmount; + + friend class TestStep; +}; + +} +} + +#endif diff --git a/include/anna/testing/TestClock.hpp b/include/anna/testing/TestClock.hpp new file mode 100644 index 0000000..9ae98f4 --- /dev/null +++ b/include/anna/testing/TestClock.hpp @@ -0,0 +1,36 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_testing_TestClock_hpp +#define anna_testing_TestClock_hpp + +// Project +#include +#include + +namespace anna { + +namespace testing { + +class TestManager; + +class TestClock : public anna::timex::Clock { + TestManager *a_manager; + +public: + TestClock(const char *clockName, const anna::Millisecond & timeout, TestManager *manager) + : a_manager(manager), anna::timex::Clock(clockName, timeout) {;} + + virtual bool tick() throw(anna::RuntimeException); +}; + +} +} + +#endif + diff --git a/include/anna/testing/TestCondition.hpp b/include/anna/testing/TestCondition.hpp new file mode 100644 index 0000000..e833712 --- /dev/null +++ b/include/anna/testing/TestCondition.hpp @@ -0,0 +1,132 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_testing_TestCondition_hpp +#define anna_testing_TestCondition_hpp + +// Standard +#include + +// Project +#include + + +namespace anna { + namespace xml { + class Node; + } + +namespace testing { + + +class TestCondition { + + public: + + struct Type { enum _v { Generic, Basic }; }; + static const char* asText(const Type::_v type) throw(); + + TestCondition() : a_rcvFromEntity(true), + a_regexp(""), + a_code(""), a_bitR(""), a_hopByHop(""), a_applicationId(""), + a_sessionId(""), a_resultCode(""), + a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Basic; } + + + // Source of the received message + void setReceivedFromEntity(bool rfe) throw() { a_rcvFromEntity = rfe; } + bool receivedFromEntity() const throw() { return a_rcvFromEntity; } + + // Generic + void setRegexp(const std::string ®exp) throw() { a_regexp = regexp; } + // Basic + void setCode(const std::string &value) throw() { a_code = value; } + void setBitR(const std::string &value) throw() { a_bitR = value; } + void setHopByHop(const std::string &value) throw() { a_hopByHop = value; } + void setApplicationId(const std::string &value) throw() { a_applicationId = value; } + void setSessionId(const std::string &value) throw() { a_sessionId = value; } + void setResultCode(const std::string &value) throw() { a_resultCode = value; } + void setMsisdn(const std::string &value) throw() { a_msisdn = value; } + void setImsi(const std::string &value) throw() { a_imsi = value; } + void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; } + + bool exists() const throw(); + friend bool operator==(const TestCondition &c1, const TestCondition &c2) throw() { + + if (c1.getType() != c2.getType()) return false; + + if (c1.getType() == TestCondition::Type::Generic) { + if (c1.getRegexp() != c2.getRegexp()) return false; + } + else { + if (c1.getCode() != c2.getCode()) return false; + if (c1.getBitR() != c2.getBitR()) return false; + if (c1.getHopByHop() != c2.getHopByHop()) return false; + if (c1.getApplicationId() != c2.getApplicationId()) return false; + if (c1.getSessionId() != c2.getSessionId()) return false; + if (c1.getResultCode() != c2.getResultCode()) return false; + if (c1.getMsisdn() != c2.getMsisdn()) return false; + if (c1.getImsi() != c2.getImsi()) return false; + if (c1.getServiceContextId() != c2.getServiceContextId()) return false; + } + + return true; + } + + + + const Type::_v &getType() const throw() { return a_type; } + + // Generic + const std::string & getRegexp() const throw() { return a_regexp; } + // Basic + const std::string & getCode() const throw() { return a_code; } + const std::string & getBitR() const throw() { return a_bitR; } + const std::string & getHopByHop() const throw() { return a_hopByHop; } + const std::string & getApplicationId() const throw() { return a_applicationId; } + const std::string & getSessionId() const throw() { return a_sessionId; } + const std::string & getResultCode() const throw() { return a_resultCode; } + const std::string & getMsisdn() const throw() { return a_msisdn; } + const std::string & getImsi() const throw() { return a_imsi; } + const std::string & getServiceContextId() const throw() { return a_serviceContextId; } + + + bool comply (const anna::DataBlock &message/*, bool matchSessionId*/) const throw(); + + + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + + + private: + + // Source of the received message + bool a_rcvFromEntity; + + // Type + Type::_v a_type; + + // Generic: + std::string a_regexp; + + // Basic: + std::string a_code; + std::string a_bitR; + std::string a_hopByHop; + std::string a_applicationId; + std::string a_sessionId; + std::string a_resultCode; + std::string a_msisdn; + std::string a_imsi; + std::string a_serviceContextId; +}; + +} +} + +#endif + diff --git a/include/anna/testing/TestManager.hpp b/include/anna/testing/TestManager.hpp new file mode 100644 index 0000000..a1cf8ed --- /dev/null +++ b/include/anna/testing/TestManager.hpp @@ -0,0 +1,193 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_testing_TestManager_hpp +#define anna_testing_TestManager_hpp + +// Project +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace anna { + class Millisecond; + + namespace timex { + class Engine; + } + namespace diameter { + namespace comm { + class ClientSession; + class ServerSession; + } + } + +namespace testing { + + +class TestClock; +class TestCase; +class TestCaseStep; + + +typedef std::map test_pool_t; +typedef std::map::const_iterator test_pool_it; +typedef std::map::iterator test_pool_nc_it; + + +/** + Timer Manager for testing system +*/ +class TestManager : public anna::timex::TimeEventObserver, public anna::Singleton { + + // Statistics summary: + class StatSummary { + + unsigned int a_initializedTcs; + unsigned int a_inprogressTcs; + unsigned int a_failedTcs; + unsigned int a_sucessTcs; + + public: + StatSummary() { clear(); } + void newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw(); + void clear() throw(); + unsigned int getInProgressCount() const throw() { return a_inprogressTcs; } + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + }; + + + + typedef anna::Recycler timer_container; + + anna::timex::Engine* a_timeController; + + // reports + std::string a_reportsDirectory; + bool a_dumpInitializedReports, a_dumpInProgressReports, a_dumpFailedReports, a_dumpSuccessReports; + bool a_dumpHexMessages; + + // Pool of test cases + test_pool_t a_testPool; + test_pool_it a_currentTestIt; + int a_poolRepeats; // repeat pool N times + int a_poolCycle; // current cycle, from 1 to N + unsigned int a_inProgressLimit; // limit load to have this value + + // Test clock + int a_synchronousAmount; + TestClock *a_clock; + bool tick() throw(); + bool nextTestCase() throw(); + + // Test timers + timer_container a_timers; + + // Test case identifiers: + // Session-Id's + std::map a_sessionIdTestCaseMap; // stores used Session-Id values within a test case. + // No other can use them, but a test case could use more than one. + // Subscriber's + std::map a_subscriberIdTestCaseMap; // stores used Subscriber-Id values within a test case. + + + + StatSummary a_statSummary; // general statistics + + + TestManager(); + TestManager(const TestManager&); + + TestTimer* createTimer(TestCaseStep*, const anna::Millisecond &, const TestTimer::Type::_v type) throw(anna::RuntimeException); + void cancelTimer(TestTimer*) throw(); + void release(anna::timex::TimeEvent*) throw(); + + + public: + + void registerSessionId(const std::string &sessionId, const TestCase *testCase) throw(anna::RuntimeException); + void registerSubscriberId(const std::string &subscriberId, const TestCase *testCase) throw(anna::RuntimeException); + + int tests() const throw() { return a_testPool.size(); } + void setTimerController(anna::timex::Engine *engine) throw() { a_timeController = engine; } + + void setReportsDirectory(const std::string &rd) throw() { a_reportsDirectory = rd; } + const std::string &getReportsDirectory() const throw() { return a_reportsDirectory; } + + void setDumpHex(bool dh) throw() { a_dumpHexMessages = dh; } + bool getDumpHex() const throw() { return a_dumpHexMessages; } + + + void setDumpInitializedReports(bool enable) throw() { a_dumpInitializedReports = enable; } + void setDumpInProgressReports(bool enable) throw() { a_dumpInProgressReports = enable; } + void setDumpFailedReports(bool enable) throw() { a_dumpFailedReports = enable; } + void setDumpSuccessReports(bool enable) throw() { a_dumpSuccessReports = enable; } + void setDumpAllReports(bool enable) throw() { + setDumpInitializedReports(enable); + setDumpInProgressReports(enable); + setDumpFailedReports(enable); + setDumpSuccessReports(enable); + } + + bool getDumpInitializedReports() const throw() { return a_dumpInitializedReports; } + bool getDumpInProgressReports() const throw() { return a_dumpInProgressReports; } + bool getDumpFailedReports() const throw() { return a_dumpFailedReports; } + bool getDumpSuccessReports() const throw() { return a_dumpSuccessReports; } + + // Helper to calculate time interval and synchronous amount of execution tests to guarantee the input rate (tests per second) + // through the time manager. The first call to this method will start the time trigger system and check for new test cases to be launched. + bool configureTTPS(int testTicksPerSecond) throw(); + + bool clearPool() throw(); + bool resetPool(bool hard /* hard reset includes in-progress test cases */) throw(); + void setPoolRepeats(int repeats) throw() { a_poolRepeats = repeats; } + int getPoolRepeats() const throw() { return a_poolRepeats; } + int getPoolCycle() const throw() { return a_poolCycle; } + + unsigned int getInProgressCount() const throw() { return a_statSummary.getInProgressCount(); } + unsigned int getInProgressLimit() const throw() { return a_inProgressLimit; } + void setInProgressLimit(unsigned int limit) throw() { a_inProgressLimit = limit; } // 0 = UINT_MAX (no limit) + + bool gotoTestCase(unsigned int id) throw(); + TestCase *findTestCase(unsigned int id) const throw(); // id = -1 provides current test case triggered + TestCase *getTestCase(unsigned int id) throw(); // creates/reuses a test case + + // Main logic + TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw(); + TestCase *getTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw(); + void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException); + void receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException); + + bool execTestCases(int sync_amount) throw(); + + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + std::string asXMLString() const throw(); + + // stats + void tcsStateStats(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw() { + a_statSummary.newTCState(beginState, endState); + } + + + friend class anna::Singleton ; + friend class TestStepTimeout; // createTimer + friend class TestStepDelay; // createTimer + friend class TestClock; // tick +}; + +} +} + +#endif + diff --git a/include/anna/testing/TestStep.hpp b/include/anna/testing/TestStep.hpp new file mode 100644 index 0000000..34af880 --- /dev/null +++ b/include/anna/testing/TestStep.hpp @@ -0,0 +1,262 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_testing_TestStep_hpp +#define anna_testing_TestStep_hpp + +// Standard +#include +#include +#include + +// Project +#include +#include +#include + + +namespace anna { + class Millisecond; + + namespace xml { + class Node; + } + namespace diameter { + namespace codec { + class Message; + } + namespace comm { + class OriginHost; + class ClientSession; + class ServerSession; + } + } + +namespace testing { + +class TestCase; +class TestTimer; + +class TestStep { + + int a_number; // step number used for xml (informational) + anna::Millisecond a_beginTimestamp; // unix time + anna::Millisecond a_endTimestamp; // unix time + bool a_executed; // used for interactive mode in order to not repeat a execution step if before completing, the user add interactive amount + + void setBeginTimestamp(const anna::Millisecond &t) throw() { a_beginTimestamp = t; } + const anna::Millisecond &getBeginTimestamp() const throw() { return a_beginTimestamp; } + void setEndTimestamp(const anna::Millisecond &t) throw() { a_endTimestamp = t; } + const anna::Millisecond &getEndTimestamp() const throw() { return a_endTimestamp; } + + void initialize(TestCase *testCase); + + public: + struct Type { enum _v { Unconfigured, Timeout, Sendxml2e, Sendxml2c, Delay, Wait, Cmd }; }; + static const char* asText(const Type::_v type) throw(); + + TestStep(TestCase *testCase) : a_message(true), a_messageCodec(NULL), a_executed(false) { initialize(testCase); } + virtual ~TestStep() {;} + + // setter & getters + const Type::_v &getType() const throw() { return a_type; } + const int &getNumber() const throw() { return a_number; } + bool isCompleted() const throw() { return a_completed; } + + bool execute() throw(); + void complete() throw(); + void reset() throw(); + void next() throw(); + virtual anna::xml::Node* asXML(anna::xml::Node* parent) throw(); + std::string asXMLString() throw(); + + protected: + TestCase *a_testCase; + bool a_completed; + Type::_v a_type; + + // Message (not for all step types) + anna::DataBlock a_message; + anna::diameter::codec::Message *a_messageCodec; // used as helper and for traffic logs + bool decodeMessage(bool trust = false) throw(); // If trust=true: decoding the previously encoded message (sendxml sentences). + // The only error would be validation ones, and we are going to ignore them here. + + virtual bool do_execute() throw() = 0; // returns true if next step must be executed + virtual void do_complete() throw() = 0; // end of transaction (delay/timeout expired, wait condition fulfilled, sending done) + // In all cases, the next step will be executed except 'timeout' which is asynchronous + // and will move to the next step just after timer creation (no complete waited) + virtual void do_reset() throw() = 0; +}; + + +class TestStepTimeout : public TestStep { + + anna::Millisecond a_timeout; + TestTimer *a_timer; // just in case i would need to cancel + + public: + TestStepTimeout(TestCase *testCase) : TestStep(testCase), a_timeout(0), a_timer(NULL) { a_type = Type::Timeout; } + + // setter & getters + void setTimeout(const anna::Millisecond &t) throw() { a_timeout = t; } + const anna::Millisecond &getTimeout() const throw() { return a_timeout; } + + // virtuals + bool do_execute() throw(); + void do_complete() throw(); // timeout reached, test case failed + void do_reset() throw(); + anna::xml::Node* asXML(anna::xml::Node* parent) throw(); +}; + + +class TestStepSendxml : public TestStep { + + protected: + // possible end points: + anna::diameter::comm::OriginHost *a_originHost; + + // Step number reference ('wait for request' step) + int a_waitForRequestStepNumber; + + // Expired ? + bool a_expired; // a_endTimestamp will be the expiration reception timestamp + + public: + TestStepSendxml(TestCase *testCase) : TestStep(testCase), + a_expired(false), + a_originHost(NULL), + a_waitForRequestStepNumber(-1) {;} + ~TestStepSendxml() {;} + + // setter & getters + void setOriginHost(anna::diameter::comm::OriginHost *host) throw() { a_originHost = host; } + anna::diameter::comm::OriginHost *getOriginHost() const throw() { return a_originHost; } + void setWaitForRequestStepNumber(int stepNumber) throw() { a_waitForRequestStepNumber = stepNumber; } + int getWaitForRequestStepNumber() const throw() { return a_waitForRequestStepNumber; } + void setMsgDataBlock(const anna::DataBlock &db) throw() { a_message = db; } + const anna::DataBlock &getMsgDataBlock() const throw() { return a_message; } + + // virtuals + bool do_execute() throw(); + void do_complete() throw() {;} + void do_reset() throw(); + anna::xml::Node* asXML(anna::xml::Node* parent) throw(); +}; + +class TestStepSendxml2e : public TestStepSendxml { + public: + TestStepSendxml2e(TestCase *testCase) : TestStepSendxml(testCase) { a_type = Type::Sendxml2e; } +}; + +class TestStepSendxml2c : public TestStepSendxml { + public: + TestStepSendxml2c(TestCase *testCase) : TestStepSendxml(testCase) { a_type = Type::Sendxml2c; } +}; + + +class TestStepDelay : public TestStep { + anna::Millisecond a_delay; + TestTimer *a_timer; // just in case i would need to cancel + + public: + TestStepDelay(TestCase *testCase) : TestStep(testCase), a_delay(0), a_timer(NULL) { a_type = Type::Delay; } + + // setter & getters + void setDelay(const anna::Millisecond &d) throw() { a_delay = d; } + const anna::Millisecond &getDelay() const throw() { return a_delay; } + + // virtuals + bool do_execute() throw(); + void do_complete() throw(); // delay reached + void do_reset() throw(); + anna::xml::Node* asXML(anna::xml::Node* parent) throw(); +}; + + +class TestStepWait : public TestStep { + + TestCondition a_condition; + anna::diameter::comm::ClientSession *a_clientSession; + anna::diameter::comm::ServerSession *a_serverSession; + + public: + TestStepWait(TestCase *testCase) : TestStep(testCase) { + a_type = Type::Wait; + a_clientSession = NULL; + a_serverSession = NULL; + } + ~TestStepWait() {;} + + // setter & getters + void setCondition(bool fromEntity, + 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(); + void setCondition(bool fromEntity, const std::string ®exp) throw(); + + void setClientSession(anna::diameter::comm::ClientSession *cs) throw() { a_clientSession = cs; } + void setServerSession(anna::diameter::comm::ServerSession *ss) throw() { a_serverSession = ss; } + anna::diameter::comm::ClientSession *getClientSession() const throw() { return a_clientSession; } + anna::diameter::comm::ServerSession *getServerSession() const throw() { return a_serverSession; } + + const TestCondition &getCondition() const throw() { return a_condition; } + //void setMsgDataBlock(const anna::DataBlock &db) throw() { a_message = db; } + bool fulfilled(const anna::DataBlock &db/*, bool matchSessionId = true*/) throw(); + const anna::DataBlock &getMsgDataBlock() const throw() { return a_message; } + + + // virtuals + bool do_execute() throw(); // this will be executed when test case starts (at least we could measure the time until condition is fulfilled) + void do_complete() throw(); // condition fulfilled + void do_reset() throw(); + anna::xml::Node* asXML(anna::xml::Node* parent) throw(); +}; + + +class TestStepCmd : public TestStep { + + std::string a_script; + std::thread a_thread; + bool a_threadRunning; + bool a_threadDeprecated; + int a_resultCode; + std::string a_errorMsg; + //std::string a_output; // for POPEN + + pid_t a_childPid; + + public: + TestStepCmd(TestCase *testCase) : TestStep(testCase), a_threadRunning(false), a_threadDeprecated(false), a_resultCode(-2)/*, a_output("")*/, a_errorMsg(""), a_childPid(-1) { a_type = Type::Cmd; } + + // setter & getters + void setThreadRunning(bool running) throw() { a_threadRunning = running; } + + void setResultCode(int rc) throw() { a_resultCode = rc; } + int getResultCode() const throw() { return a_resultCode; } + void setErrorMsg(const std::string &em) throw() { a_errorMsg = em; } + const std::string &getErrorMsg() const throw() { return a_errorMsg; } + //void appendOutput(const std::string &output) throw() { a_output += output; } + //const std::string &getOutput() const throw() { return a_output; } + void setChildPid(pid_t pid) throw() { a_childPid = pid; } + const pid_t &getChildPid() const throw() { return a_childPid; } + + void setScript(const std::string &script) throw() { a_script = script; } + const std::string &getScript() const throw() { return a_script; } + + // virtuals + bool do_execute() throw(); + void do_complete() throw(); + void do_reset() throw(); + anna::xml::Node* asXML(anna::xml::Node* parent) throw(); +}; + +} +} + +#endif + diff --git a/include/anna/testing/TestTimer.hpp b/include/anna/testing/TestTimer.hpp new file mode 100644 index 0000000..54ece7b --- /dev/null +++ b/include/anna/testing/TestTimer.hpp @@ -0,0 +1,55 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_testing_TestTimer_hpp +#define anna_testing_TestTimer_hpp + +#include +#include +#include + + +namespace anna { + +namespace testing { + + +class TestStep; + + +class TestTimer : public anna::timex::Transaction { + +public: + + struct Type { enum _v { TimeLeft, Delay }; }; + static const char* asText(const Type::_v type) throw(); + + + TestStep* getTestCaseStep() throw() { return reinterpret_cast (getContext()); } + const TestStep* getTestCaseStep() const throw() { return reinterpret_cast (getContext()); } + + void setType(const Type::_v type) throw() { a_type = type; } + const Type::_v &getType(const Type::_v type) const throw() { return a_type; } + + std::string asString() const throw(); + +private: + Type::_v a_type; + + TestTimer() {;} + + void expire(anna::timex::Engine*) throw(anna::RuntimeException); + + friend class anna::Allocator; +}; + +} +} + +#endif + diff --git a/include/anna/testing/defines.hpp b/include/anna/testing/defines.hpp new file mode 100644 index 0000000..6dfa42b --- /dev/null +++ b/include/anna/testing/defines.hpp @@ -0,0 +1,18 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +#ifndef anna_testing_defines_hpp +#define anna_testing_defines_hpp + + +#define SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID "##cycleid##" +#define SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID "##testcaseid##" +#define SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID "##teststepid##" + +#endif + diff --git a/source/diameter.comm/Engine.cpp b/source/diameter.comm/Engine.cpp index d43da75..56aa1b6 100644 --- a/source/diameter.comm/Engine.cpp +++ b/source/diameter.comm/Engine.cpp @@ -64,6 +64,7 @@ comm::Engine::Engine(const char *className, const stack::Dictionary *baseProtoco anna::diameter::sccs::activate(); a_originRealm = anna::functions::getDomainname(); a_originHost = anna::functions::getHostname(); + a_ceaPathfile = ""; // Internal base protocol codec engine: a_baseProtocolCodecEngine.setValidationMode(anna::diameter::codec::Engine::ValidationMode::Always); // default was: after decoding @@ -115,8 +116,8 @@ void comm::Engine::setClientCERandDWR(const std::string & cer, const std::string // * [Acct-Application-Id] 259 Unsigned32 anna::diameter::codec::Message diameterCER(getBaseProtocolCodecEngine()); int applicationId = 0 /*anna::diameter::helpers::APPID__3GPP_Rx*/; // Unsigned32 - std::string OH = getOriginHost(); - std::string OR = getOriginRealm(); + std::string OH = getOriginHostName(); + std::string OR = getOriginRealmName(); std::string hostIP = anna::functions::getHostnameIP(); // Address int vendorId = anna::diameter::helpers::VENDORID__tgpp; // Unsigned32 std::string productName = "ANNA Diameter Client"; // UTF8String @@ -768,13 +769,13 @@ int comm::Engine::getOTARequestsForLocalServers() const throw() { } -void comm::Engine::setOriginRealm(const std::string & originRealm) throw() { - a_originRealm = ((originRealm != "") ? originRealm : anna::functions::getDomainname()); +void comm::Engine::setOriginRealmName(const std::string & originRealmName) throw() { + a_originRealm = ((originRealmName != "") ? originRealmName : anna::functions::getDomainname()); } -void comm::Engine::setOriginHost(const std::string & originHost) throw() { - a_originHost = ((originHost != "") ? originHost : anna::functions::getHostname()); +void comm::Engine::setOriginHostName(const std::string & originHostName) throw() { + a_originHost = ((originHostName != "") ? originHostName : anna::functions::getHostname()); } @@ -927,9 +928,9 @@ void comm::Engine::availabilityLostForEntities() throw() { a_availableForEntities = false; LOGDEBUG( std::string msg = "diameter::comm::Engine { Origin-Realm: "; - msg += getOriginRealm(); + msg += getOriginRealmName(); msg += " | Origin-Host: "; - msg += getOriginHost(); + msg += getOriginHostName(); msg += " } has lost its availability for entities"; anna::Logger::debug(msg, ANNA_FILE_LOCATION); ); @@ -946,9 +947,9 @@ void comm::Engine::availabilityRecoveredForEntities() throw() { a_availableForEntities = true; LOGDEBUG( std::string msg = "diameter::comm::Engine { Origin-Realm: "; - msg += getOriginRealm(); + msg += getOriginRealmName(); msg += " | Origin-Host: "; - msg += getOriginHost(); + msg += getOriginHostName(); msg += " } has recovered its availability for entities"; anna::Logger::debug(msg, ANNA_FILE_LOCATION); ); @@ -965,9 +966,9 @@ void comm::Engine::availabilityLostForLocalServers() throw() { a_availableForLocalServers = false; LOGDEBUG( std::string msg = "diameter::comm::Engine { Origin-Realm: "; - msg += getOriginRealm(); + msg += getOriginRealmName(); msg += " | Origin-Host: "; - msg += getOriginHost(); + msg += getOriginHostName(); msg += " } has lost its availability for local servers"; anna::Logger::debug(msg, ANNA_FILE_LOCATION); ); @@ -984,9 +985,9 @@ void comm::Engine::availabilityRecoveredForLocalServers() throw() { a_availableForLocalServers = true; LOGDEBUG( std::string msg = "diameter::comm::Engine { Origin-Realm: "; - msg += getOriginRealm(); + msg += getOriginRealmName(); msg += " | Origin-Host: "; - msg += getOriginHost(); + msg += getOriginHostName(); msg += " } has recovered its availability for local servers"; anna::Logger::debug(msg, ANNA_FILE_LOCATION); ); @@ -1102,11 +1103,30 @@ void comm::Engine::readDPA(anna::DataBlock &dpa, const anna::DataBlock & dpr) th } -void comm::Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock & cer) throw() { +void comm::Engine::readCEA(anna::DataBlock &cea, const anna::DataBlock &cer) throw() { // Check for base protocol codec engine health: assertBaseProtocolHealth(); + if (a_ceaPathfile != "") { + anna::diameter::codec::Message diameterCEA(getBaseProtocolCodecEngine()); + + try { + diameterCEA.loadXML(a_ceaPathfile); + diameterCEA.setHopByHop(anna::diameter::codec::functions::getHopByHop(cer)); + diameterCEA.setEndToEnd(anna::diameter::codec::functions::getEndToEnd(cer)); + cea = diameterCEA.code(); + + } catch(anna::RuntimeException &ex) { + ex.trace(); + LOGWARNING(anna::Logger::warning("CEA file not found or unable to parse. Encoding harcoded default version ...", ANNA_FILE_LOCATION)); + //return anna::diameter::comm::Engine::readCEA(cea, cer); + // will fail with empty cea + } + + return; + } + // Default CEA implementation: // // 'Capabilities-Exchange-Answer' (257,answer) diff --git a/source/diameter.comm/Entity.cpp b/source/diameter.comm/Entity.cpp index 23b8a31..d5b3dd3 100644 --- a/source/diameter.comm/Entity.cpp +++ b/source/diameter.comm/Entity.cpp @@ -39,6 +39,7 @@ void Entity::initialize() throw() { a_description = ""; a_category = 0; a_lastUsedResource = NULL; + a_balance = false; } @@ -74,6 +75,9 @@ throw(anna::RuntimeException) { int Entity::readSocketId(const Message* message, int maxClientSessions) const throw() { + + if(a_sessionBasedModelsType == SessionBasedModelsType::RoundRobin) return -1; // IEC also would return -1 + try { // Service-Context-Id: anna::diameter::helpers::dcca::ChargingContext::_v chargingContext; @@ -88,7 +92,12 @@ int Entity::readSocketId(const Message* message, int maxClientSessions) const th std::string diameterIdentity, optional; anna::U32 high, low; anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional); - return (low % maxClientSessions); + + if(a_sessionBasedModelsType == SessionBasedModelsType::SessionIdLowPart) return (low % maxClientSessions); + + if(a_sessionBasedModelsType == SessionBasedModelsType::SessionIdHighPart) return (high % maxClientSessions); + + if(a_sessionBasedModelsType == SessionBasedModelsType::SessionIdOptionalPart) return (atoi(optional.c_str()) % maxClientSessions); } case anna::diameter::helpers::dcca::ChargingContext::SMS: case anna::diameter::helpers::dcca::ChargingContext::MMS: @@ -108,7 +117,7 @@ int Entity::readSocketId(const Message* message, int maxClientSessions) const th } -bool Entity::send(const Message* message, bool balance) throw(anna::RuntimeException) { +bool Entity::send(const Message* message) throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tttm("diameter::comm::Entity", "send", ANNA_FILE_LOCATION)); assertReady(); // Carried socket id (forwarding/proxy features): @@ -137,7 +146,7 @@ bool Entity::send(const Message* message, bool balance) throw(anna::RuntimeExcep //////////////////////////////////////////////////////////////////////////////////////// // Balance - if(balance) { + if(a_balance) { for(int k = 0; k < getMaxServers(); k++) { // try round-robin only over one cycle, // no matter where you are: don't repeat same server if(a_deliveryIterator == end()) a_deliveryIterator = begin(); @@ -263,6 +272,11 @@ void Entity::close(bool destroy) throw(anna::RuntimeException) { a_engine->closeServer(*it, destroy); } +const char* Entity::asText(const SessionBasedModelsType::_v sbmt) +throw() { + static const char* text [] = { "RoundRobin", "SessionIdOptionalPart", "SessionIdHighPart", "SessionIdLowPart" }; + return text [sbmt]; +} socket_v Entity::getAddressPortList() const throw() { socket_v result; @@ -335,8 +349,8 @@ void Entity::eventRequestRetransmission(const ClientSession* clientSession, Mess std::string Entity::asString() const throw() { std::string result("diameter::comm::Entity { "); - std::string originRealm = a_engine->getOriginRealm(); - std::string originHost = a_engine->getOriginHost(); + std::string originRealm = a_engine->getOriginRealmName(); + std::string originHost = a_engine->getOriginHostName(); result += "Parent Engine Origin-Realm: "; result += (originRealm != "") ? originRealm:""; @@ -366,6 +380,8 @@ std::string Entity::asString() const throw() { result += a_lastOutgoingActivityTime.asString(); result += " | Hidden: "; result += (hidden() ? "yes" : "no"); + result += " | SessionBasedModelsType: "; + result += asText(a_sessionBasedModelsType); result += "\n"; for(std::vector::const_iterator it = begin(); it != end(); it++) { @@ -378,8 +394,8 @@ std::string Entity::asString() const throw() { anna::xml::Node* Entity::asXML(anna::xml::Node* parent) const throw() { anna::xml::Node* result = parent->createChild("diameter.Entity"); - std::string originRealm = a_engine->getOriginRealm(); - std::string originHost = a_engine->getOriginHost(); + std::string originRealm = a_engine->getOriginRealmName(); + std::string originHost = a_engine->getOriginHostName(); if(originRealm != "") result->createAttribute("ParentEngineOriginRealm", originRealm); if(originHost != "") result->createAttribute("ParentEngineOriginHost", originHost); @@ -396,6 +412,7 @@ anna::xml::Node* Entity::asXML(anna::xml::Node* parent) const throw() { result->createAttribute("LastIncomingActivityTime", a_lastIncomingActivityTime.asString()); result->createAttribute("LastOutgoingActivityTime", a_lastOutgoingActivityTime.asString()); result->createAttribute("Hidden", hidden() ? "yes" : "no"); + result->createAttribute("SessionBasedModelsType:", asText(a_sessionBasedModelsType)); anna::xml::Node* servers = result->createChild("Entity.Servers"); for(std::vector::const_iterator it = begin(); it != end(); it++) diff --git a/source/diameter.comm/LocalServer.cpp b/source/diameter.comm/LocalServer.cpp index 0e2bc2e..9e03084 100644 --- a/source/diameter.comm/LocalServer.cpp +++ b/source/diameter.comm/LocalServer.cpp @@ -50,9 +50,9 @@ void LocalServer::initializeStatisticResources() throw() { std::string accName = "local server '"; accName += anna::functions::socketLiteralAsString(a_key.first, a_key.second); accName += "' on origin-realm '"; - accName += a_engine ? a_engine->getOriginRealm() : "unknown"; // it should be known (createServer) + accName += a_engine ? a_engine->getOriginRealmName() : "unknown"; // it should be known (createServer) accName += "' and origin-host '"; - accName += a_engine ? a_engine->getOriginHost() : "unknown"; // it should be known (createServer) + accName += a_engine ? a_engine->getOriginHostName() : "unknown"; // it should be known (createServer) accName += "'"; a_messageStatistics.initialize(accName); } diff --git a/source/diameter.comm/OriginHost.cpp b/source/diameter.comm/OriginHost.cpp new file mode 100644 index 0000000..ed181bd --- /dev/null +++ b/source/diameter.comm/OriginHost.cpp @@ -0,0 +1,415 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +// Standard +#include + +// Project +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace anna::diameter::comm; + + +OriginHost::OriginHost(anna::diameter::comm::Engine* commEngine, unsigned int applicationId) : + a_commEngine(commEngine), a_applicationId(applicationId) { + + a_codecEngine = anna::diameter::codec::EngineManager::instantiate().getCodecEngine(applicationId); // i know, this is going to exist (getCodecEngine is not null) + + a_logFile = ""; + a_burstLogFile = ""; + a_splitLog = false; + a_detailedLog = false; + a_dumpLog = false; + a_entity = NULL; + a_diameterServer = NULL; + + // Comm resources: + a_requestRetransmissions = 0; + + // Burst + a_burstCycle = 1; + a_burstRepeat = false; + a_burstActive = false; + a_burstLoadIndx = 0; + a_burstDeliveryIt = a_burstMessages.begin(); + a_otaRequest = 0; + a_burstPopCounter = 0; +} + +const std::string &OriginHost::getName() const throw() { + return a_commEngine->getOriginHostName(); +} + +void OriginHost::createEntity(const std::string &entityRepresentation, const anna::Millisecond &bindTimeout, const anna::Millisecond &applicationTimeout) throw(anna::RuntimeException) { + + anna::socket_v servers = anna::functions::getSocketVectorFromString(entityRepresentation); + std::string entityDescription = "Launcher diameter entity for "; entityDescription += getName(); + a_entity = (anna::diameter::comm::Entity*)a_commEngine->createEntity(servers, entityDescription); + a_entity->setClassCodeTimeout(anna::diameter::comm::ClassCode::Bind, bindTimeout); + a_entity->setClassCodeTimeout(anna::diameter::comm::ClassCode::ApplicationMessage, applicationTimeout); +} + +void OriginHost::createDiameterServer(const std::string &serverRepresentation, int sessions, const anna::Millisecond &inactivityTimeout, const anna::Millisecond &applicationTimeout, const std::string &ceaPathfile) throw(anna::RuntimeException) { + + //if(sessions <= 0) return; negative implies no limit for accepted connections + + std::string address; int port; + anna::functions::getAddressAndPortFromSocketLiteral(serverRepresentation, address, port); + std::string serverDescription = "Launcher diameter local server for "; serverDescription += getName(); + a_commEngine->setCEA(ceaPathfile); + a_diameterServer = (anna::diameter::comm::LocalServer*)(a_commEngine->createLocalServer(address, port, sessions)); + // we could set sessions = 0, and after application run(), use setMaxConnections(real sessions) + // over the local server in order to start it. + + a_diameterServer->setDescription(serverDescription); + a_diameterServer->setAllowedInactivityTime(inactivityTimeout); + a_diameterServer->setClassCodeTimeout(anna::diameter::comm::ClassCode::ApplicationMessage, applicationTimeout); +} + +anna::diameter::comm::Message *OriginHost::createCommMessage() throw(anna::RuntimeException) { + anna::diameter::comm::Message *result = a_commMessages.create(); + result->setRetries(a_requestRetransmissions); + if (a_requestRetransmissions > 0) result->setOnExpiry(anna::diameter::comm::Message::OnExpiry::Retransmit); + return result; +} + + +void OriginHost::releaseCommMessage(anna::diameter::comm::Message *msg) throw() { + a_commMessages.release(msg); +} + + +void OriginHost::writeLogFile(const anna::DataBlock & db, const std::string &logExtension, const std::string &detail) const throw() { + anna::diameter::codec::Message codecMsg; + try { codecMsg.decode(db); } catch(anna::RuntimeException &ex) { ex.trace(); } + writeLogFile(codecMsg, logExtension, detail); +} + +// Already decoded: +void OriginHost::writeLogFile(const anna::diameter::codec::Message &decodedMessage, const std::string &logExtension, const std::string &detail) const throw() { + // Open target file: + std::string targetFile = a_logFile; + + if(a_splitLog) { + targetFile += "."; + targetFile += logExtension; + } + + std::ofstream out(targetFile.c_str(), std::ifstream::out | std::ifstream::app); + // Set text to dump: + std::string title = "["; + title += logExtension; + title += "]"; + // Build complete log: + std::string log = "\n"; + std::string xml = decodedMessage.asXMLString(); + + + if(a_detailedLog) { + anna::time::Date now; + now.setNow(); + title += " "; + title += now.asString(); + log += anna::functions::highlight(title, anna::functions::TextHighlightMode::OverAndUnderline); + log += xml; + log += "\n"; + log += anna::functions::highlight("Used resource"); + log += detail; + log += "\n"; + } else { + log += title; + log += "\n"; + log += xml; + log += "\n"; + } + + if(a_dumpLog) { + // .......xml + std::string name = anna::functions::asString((anna::Millisecond)anna::functions::millisecond()); + name += "."; + name += getCommEngine()->getOriginHostName(); + name += "."; + name += anna::functions::asString(decodedMessage.getHopByHop()); + name += "."; + name += anna::functions::asString(decodedMessage.getEndToEnd()); + name += "."; + name += anna::functions::asString(decodedMessage.getId().first); + name += "."; + name += ((decodedMessage.getId().second) ? "request.":"answer."); + name += logExtension; + name += ".xml"; + std::ofstream outMsg(name.c_str(), std::ifstream::out | std::ifstream::app); + outMsg.write(xml.c_str(), xml.size()); + outMsg.close(); + } + + // Write and close + out.write(log.c_str(), log.size()); + out.close(); +} + +void OriginHost::writeBurstLogFile(const std::string &buffer) throw() { + std::ofstream out(a_burstLogFile.c_str(), std::ifstream::out | std::ifstream::app); + out.write(buffer.c_str(), buffer.size()); + out.close(); // close() will be called when the object is destructed (i.e., when it goes out of scope). + // you'd call close() only if you indeed for some reason wanted to close the filestream + // earlier than it goes out of scope. +} + +int OriginHost::clearBurst() throw() { + int size = a_burstMessages.size(); + + if(size) { + std::map::const_iterator it; + std::map::const_iterator it_min(a_burstMessages.begin()); + std::map::const_iterator it_max(a_burstMessages.end()); + + for(it = it_min; it != it_max; it++) releaseCommMessage((*it).second); + + a_burstMessages.clear(); + } else { + std::string msg = "Burst list already empty. Nothing done"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + } + + a_burstActive = false; + a_burstLoadIndx = 0; + a_burstDeliveryIt = a_burstMessages.begin(); + return size; +} + +int OriginHost::loadBurstMessage(const anna::DataBlock & db) throw(anna::RuntimeException) { + anna::diameter::comm::Message *msg = createCommMessage(); + msg->setBody(db); + a_burstMessages[a_burstLoadIndx++] = msg; + return (a_burstLoadIndx - 1); +} + +int OriginHost::stopBurst() throw() { + if(!a_burstActive) { + std::string msg = "Burst launch is already stopped. Nothing done"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -1; + } + + a_burstActive = false; + // Remaining on cycle: + return (a_burstMessages.size() - (*a_burstDeliveryIt).first); +} + +int OriginHost::popBurst(int releaseAmount) throw() { + if(!a_burstActive) { + std::string msg = "Burst launch is stopped. Nothing done"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -1; + } + + if(releaseAmount < 1) { + std::string msg = "No valid release amount is specified. Ignoring burst pop"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -2; + } + + int currentOTArequests = a_entity->getOTARequests(); + a_burstPopCounter = (releaseAmount > currentOTArequests) ? currentOTArequests : releaseAmount; + return a_burstPopCounter; +} + +int OriginHost::pushBurst(int loadAmount) throw() { + if(a_burstMessages.size() == 0) { + std::string msg = "Burst data not found (empty list). Ignoring burst launch"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -1; + } + + if(loadAmount < 1) { + std::string msg = "No valid load amount is specified. Ignoring burst push"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -2; + } + + a_burstActive = true; + int count; + + for(count = 0; count < loadAmount; count++) + if(!sendBurstMessage()) break; + + return count; +} + +int OriginHost::sendBurst(int loadAmount) throw() { + if(a_burstMessages.size() == 0) { + std::string msg = "Burst data not found (empty list). Ignoring burst launch"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -1; + } + + if(loadAmount < 1) { + std::string msg = "No valid load amount is specified. Ignoring burst send"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -2; + } + + int count; + + for(count = 0; count < loadAmount; count++) + if(!sendBurstMessage(true /* anyway */)) break; + + return count; +} + +int OriginHost::startBurst(int initialLoad) throw() { + if(initialLoad < 1) { + std::string msg = "No initial load is specified. Ignoring burst start"; + std::cout << msg << std::endl; + LOGWARNING(anna::Logger::warning(msg, ANNA_FILE_LOCATION)); + return -2; + } + + a_burstActive = true; + a_burstCycle = 1; + a_burstDeliveryIt = a_burstMessages.begin(); + return (pushBurst(initialLoad)); +} + +bool OriginHost::sendBurstMessage(bool anyway) throw() { + if(!anyway && !burstActive()) return false; + + if(a_burstPopCounter > 0) { + if(burstLogEnabled()) writeBurstLogFile("x"); + + a_burstPopCounter--; + return false; + } + + if(a_burstDeliveryIt == a_burstMessages.end()) { + a_burstDeliveryIt = a_burstMessages.begin(); + + if(!anyway) { + if(a_burstRepeat) { + a_burstCycle++; + + if(burstLogEnabled()) writeBurstLogFile(anna::functions::asString("\nCompleted burst cycle. Starting again (repeat mode) on cycle %d.\n", a_burstCycle)); + } else { + if(burstLogEnabled()) writeBurstLogFile("\nCompleted burst cycle. Burst finished (repeat mode disabled).\n"); + + stopBurst(); + return false; + } + } + } + + anna::diameter::comm::Message *msg = (*a_burstDeliveryIt).second; + int order = (*a_burstDeliveryIt).first + 1; + a_burstDeliveryIt++; + bool dot = true; + // sending + bool result = a_entity->send(msg); + + if(burstLogEnabled()) { + if(a_burstMessages.size() >= 100) + dot = (order % (a_burstMessages.size() / 100)); + + if(dot) { + writeBurstLogFile("."); + } else { + writeBurstLogFile(anna::functions::asString(" %d", order)); + int otaReqs = a_entity->getOTARequests(); + + if(result && (otaReqs != a_otaRequest)) { + // false if was a sending after an answer received (no OTA change in this case) + // true after push and pop operations + a_otaRequest = otaReqs; + writeBurstLogFile(anna::functions::asString("[OTA %d]", a_otaRequest)); + } + } + } + + // Detailed log: + if(logEnabled()) { + anna::diameter::comm::Server *usedServer = a_entity->getLastUsedResource(); + anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL; + std::string detail = usedClientSession ? usedClientSession->asString() : ""; // esto no deberia ocurrir + writeLogFile(msg->getBody(), (result ? "sent2e" : "send2eError"), detail); // el del nodo de trabajo + } + + return result; +} + +std::string OriginHost::lookBurst(int order) const throw() { + + if (order == -1) order = a_burstDeliveryIt->first; + + std::string result = "No message found for order provided ("; + result += anna::functions::asString(order); + result += ")"; + std::map::const_iterator it = a_burstMessages.find(order - 1); + + if(it != a_burstMessages.end()) { + anna::diameter::codec::Message codecMsg; + try { codecMsg.decode((*it).second->getBody()); result = codecMsg.asXMLString(); } catch(anna::RuntimeException &ex) { ex.trace(); } + } + + return result; +} + +std::string OriginHost::gotoBurst(int order) throw() { + std::string result = "Position not found for order provided ("; + std::map::iterator it = a_burstMessages.find(order - 1); + + if(it != a_burstMessages.end()) { + a_burstDeliveryIt = it; + result = "Position updated for order provided ("; + } + + result += anna::functions::asString(order); + result += ")"; + return result; +} + +anna::xml::Node* OriginHost::asXML(anna::xml::Node* parent) const +throw() { + anna::xml::Node* result = parent->createChild("OriginHost"); + + result->createAttribute("originHost", getName()); + result->createAttribute("ApplicationId", a_applicationId); + result->createAttribute("originRealm", a_commEngine->getOriginRealmName()); + result->createAttribute("LogFile", a_logFile); + result->createAttribute("SplitLog", a_splitLog ? "yes" : "no"); + result->createAttribute("DetailedLog", a_detailedLog ? "yes" : "no"); + result->createAttribute("DumpLog", a_dumpLog ? "yes" : "no"); + result->createAttribute("BurstLogFile", a_burstLogFile); + result->createAttribute("RequestRetransmissions", a_requestRetransmissions); + + anna::xml::Node* commEngine = result->createChild("CommEngine"); + a_commEngine->asXML(commEngine); + + return result; +} + +std::string OriginHost::asXMLString() const throw() { + anna::xml::Node root("root"); + return anna::xml::Compiler().apply(asXML(&root)); +} diff --git a/source/diameter.comm/Server.cpp b/source/diameter.comm/Server.cpp index 1ba8356..3207094 100644 --- a/source/diameter.comm/Server.cpp +++ b/source/diameter.comm/Server.cpp @@ -43,9 +43,9 @@ void Server::initializeStatisticResources() throw() { std::string accName = "remote server '"; accName += anna::functions::socketLiteralAsString(a_socket.first, a_socket.second); accName += "' from origin-realm '"; - accName += a_engine ? a_engine->getOriginRealm() : "unknown"; // it should be known (createServer) + accName += a_engine ? a_engine->getOriginRealmName() : "unknown"; // it should be known (createServer) accName += "' and origin-host '"; - accName += a_engine ? a_engine->getOriginHost() : "unknown"; // it should be known (createServer) + accName += a_engine ? a_engine->getOriginHostName() : "unknown"; // it should be known (createServer) accName += "'"; a_messageStatistics.initialize(accName); } diff --git a/source/diameter/codec/MessagesDeque.cpp b/source/diameter/codec/MessagesDeque.cpp new file mode 100644 index 0000000..2461e19 --- /dev/null +++ b/source/diameter/codec/MessagesDeque.cpp @@ -0,0 +1,150 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + +// Standard +#include +#include + +// Project +#include +#include +#include + +// Process +#include + + +using namespace anna::diameter::codec; + + +void MessagesDeque::clear() throw () { + try { + anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate(); + anna::diameter::codec::Engine *engine; + + for (messages_const_iterator it = a_deques.begin(); it != a_deques.end(); it++) { + anna::diameter::codec::Message *message = *(it->second->begin()); + engine = em.getCodecEngine(message->getApplicationId()); + if (engine) { + engine->releaseMessage(message); + delete (it->second); + } + else { + LOGWARNING(anna::Logger::warning("Cannot release a message for which i don't know the codec engine (check the registered stack id regarding the message application id) !", ANNA_FILE_LOCATION)); + } + } + a_deques.clear(); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + } +} + +void MessagesDeque::dump(const char *filenamePrefix) throw () { + std::string outfilename, xmlString; + for (messages_const_iterator it = a_deques.begin(); + it != a_deques.end(); it++) { + int sequence = 1; + for (codec_messages_deque_const_iterator itm = it->second->begin(); + itm != it->second->end(); itm++) { + // message.. + outfilename = filenamePrefix; + outfilename += "."; + outfilename += anna::functions::asString(it->first); + outfilename += "."; + outfilename += anna::functions::asString(sequence++); + outfilename += ".xml"; + std::ofstream outfile(outfilename.c_str(), std::ifstream::out); + xmlString = (*itm)->asXMLString(); + outfile.write(xmlString.c_str(), xmlString.size()); + outfile.close(); + } + } +} + +void MessagesDeque::addMessage(int code, anna::diameter::codec::Message *message) throw () { + if (!message) + return; // just in case + + messages_const_iterator it = a_deques.find(code); + if (it != a_deques.end()) { + it->second->push_back(message); + } else { + codec_messages_deque *deque = new codec_messages_deque; + a_deques[code] = deque; + deque->push_back(message); + } +} + +anna::diameter::codec::Message* MessagesDeque::getMessage(int code) const throw () { //get the front message (begin()), returns NULL if deque is empty + anna::diameter::codec::Message *result = NULL; + messages_const_iterator it = a_deques.find(code); + if (it != a_deques.end()) { + if (!it->second->empty()) + result = *(it->second->begin()); + } + return result; +} + +void MessagesDeque::nextMessage(int code) throw () { //pops the deque and release the message (when deque is not empty: deque::empty) + anna::diameter::codec::Engine *engine; + + try { + messages_const_iterator it = a_deques.find(code); + if (it != a_deques.end()) { + if (!it->second->empty()) { + anna::diameter::codec::Message *message = *(it->second->begin()); + if (a_rotate) { + addMessage(code, message); + } else { + engine = anna::diameter::codec::EngineManager::instantiate().getCodecEngine(message->getApplicationId()); + if (engine) { + engine->releaseMessage(message); + } + else { + LOGWARNING(anna::Logger::warning("Cannot release a message for which i don't know the codec engine (check the registered stack id regarding the message application id) !", ANNA_FILE_LOCATION)); + return; + } + } + it->second->pop_front(); + } + } + } + catch (anna::RuntimeException &ex) { + ex.trace(); + } +} + +std::string MessagesDeque::asString(const char *queueName) const throw () { + std::string result = ""; + std::string aux = "FIFO QUEUE '"; + aux += queueName; + aux += "', Rotation "; + aux += a_rotate ? "enabled" : "disabled"; + result += anna::functions::highlightJustify(aux); + if (a_deques.size() != 0) { + for (messages_const_iterator it = a_deques.begin(); + it != a_deques.end(); it++) { + if (it->second->size() != 0) { + aux = "Message code "; + aux += anna::functions::asString(it->first); + result += anna::functions::highlightJustify(aux, + anna::functions::TextHighlightMode::OverAndUnderline, + anna::functions::TextJustifyMode::Left, '-'); + for (codec_messages_deque_const_iterator itm = it->second->begin(); + itm != it->second->end(); itm++) { + result += (*itm)->asXMLString(); + result += "\n"; + } + result += "\n"; + } + } + } else { + result = "No ocurrences found\n\n"; + } + return result; +} diff --git a/source/testing/SConscript b/source/testing/SConscript new file mode 100644 index 0000000..e9ea1e5 --- /dev/null +++ b/source/testing/SConscript @@ -0,0 +1,8 @@ +Import ('env') + +sources = Glob('*.cpp') + +result = env.StaticLibrary ('anna_testing', sources); + +Return ('result') + diff --git a/source/testing/SConstruct b/source/testing/SConstruct new file mode 100644 index 0000000..bc482f0 --- /dev/null +++ b/source/testing/SConstruct @@ -0,0 +1,12 @@ +release = ARGUMENTS.get ('release', 0) + +Import ('env') + +# See http://stackoverflow.com/questions/4693826/scons-binary-directory +if int(release): + result = SConscript ('SConscript', exports='env', variant_dir="release", duplicate=0) +else: + result = SConscript ('SConscript', exports='env', variant_dir="debug", duplicate=0) + +Return ('result') + diff --git a/source/testing/TestCase.cpp b/source/testing/TestCase.cpp new file mode 100644 index 0000000..efaba5c --- /dev/null +++ b/source/testing/TestCase.cpp @@ -0,0 +1,410 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +// Standard +#include +#include +#include +#include +#include + +// Project +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace anna::testing; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void TestCase::DebugSummary::addHint(const std::string &hint) throw() { + event_t event; + event.Timestamp = anna::functions::millisecond(); + event.Hint = hint; + a_events.push_back(event); +} + +void TestCase::DebugSummary::clear() throw() { + a_events.clear(); +} + +anna::xml::Node* TestCase::DebugSummary::asXML(anna::xml::Node* parent) const throw() { + anna::xml::Node* result = parent->createChild("DebugSummary"); + + std::vector::const_iterator it; + for (it = a_events.begin(); it != a_events.end(); it++) { + anna::xml::Node* event = result->createChild("Event"); + event->createAttribute("Timestamp", (*it).Timestamp.asString()); + event->createAttribute("Hint", (*it).Hint); + } + + return result; +}; +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +TestCase::TestCase(unsigned int id) : + a_id(id), + a_state(State::Initialized), + a_startTime(0), + a_interactiveAmount(-1) { + + /*a_stepsIt = a_steps.end()*/; + TestManager &testManager = TestManager::instantiate(); + testManager.tcsStateStats(State::Initialized, State::Initialized); +} + +TestCase::~TestCase() { + reset(true); // hard reset + std::vector::const_iterator it; + for (it = a_steps.begin(); it != a_steps.end(); it++) delete (*it); +} + +const char* TestCase::asText(const State::_v state) +throw() { + static const char* text [] = { "Initialized", "InProgress", "Failed", "Success" }; + return text [state]; +} + +anna::xml::Node* TestCase::asXML(anna::xml::Node* parent) const +throw() { + anna::xml::Node* result = parent->createChild("TestCase"); + + result->createAttribute("Id", a_id); + result->createAttribute("State", asText(a_state)); + result->createAttribute("StartTimestamp", a_startTime.asString()); + int steps = a_steps.size(); + if (steps != 0) { + result->createAttribute("NumberOfTestSteps", steps); + std::vector::const_iterator it; + for (it = a_steps.begin(); it != a_steps.end(); it++) { + (*it)->asXML(result); + } + } + + if (a_debugSummary.events()) { + a_debugSummary.asXML(result); + } + + result->createAttribute("Interactive", (a_interactiveAmount != -1) ? "yes":"no"); + + return result; +} + +std::string TestCase::asXMLString() const throw() { + anna::xml::Node root("root"); + return anna::xml::Compiler().apply(asXML(&root)); +} + +bool TestCase::hasSameCondition(const TestCondition &condition) const throw() { + std::vector::const_iterator it; + TestStepWait *step; + for (it = a_steps.begin(); it != a_steps.end(); it++) { + if ((*it)->getType() != TestStep::Type::Wait) continue; + step = (TestStepWait *)(*it); + if (step->getCondition() == condition) return true; + } + return false; +} + + +void TestCase::setState(const State::_v &state) throw() { + + State::_v previousState = a_state; + if (state == previousState) return; + a_state = state; + TestManager &testManager = TestManager::instantiate(); + + // stats: + testManager.tcsStateStats(previousState, state); + + + if (isFinished()) { + if ((getState() == State::Failed) && (!testManager.getDumpFailedReports())) return; + if ((getState() == State::Success) && (!testManager.getDumpSuccessReports())) return; + // report file name: cycle-.testcase-.xml + + // FORMAT: We tabulate the cycle and test case in order to ease ordering of files by mean ls: + int cycles = testManager.getPoolRepeats(); + int tests = testManager.tests(); + int cyclesWidth = (cycles<=0) ? 3 /* 1000 cycles !! */: ((int) log10 ((double) cycles) + 1); + int testsWidth = (tests<=0) ? 9 /* subscribers */: ((int) log10 ((double) tests) + 1); + std::stringstream format; + format << "/cycle-%0" << cyclesWidth << "d.testcase-%0" << testsWidth << "llu.xml"; + + // FILE NAME: + std::string file = testManager.getReportsDirectory() + anna::functions::asString(format.str().c_str(), testManager.getPoolCycle(), a_id); + std::ofstream out; + out.open(file.c_str(), std::ofstream::out | std::ofstream::app); + if(out.is_open() == false) { + std::string msg("Error opening '"); + msg += file; + msg += "' for writting"; + anna::Logger::error(msg, ANNA_FILE_LOCATION); + } + else { + out << asXMLString() << std::endl; + out.close(); + } + } +} + +bool TestCase::done() throw() { + if (a_stepsIt == a_steps.end()) { + setState(State::Success); + return true; + } + + return false; +} + +bool TestCase::process() throw() { + if (steps() == 0) { + LOGWARNING(anna::Logger::warning(anna::functions::asString("Test case %llu is empty, nothing to execute", a_id), ANNA_FILE_LOCATION)); + return false; + } + if (isFinished()) { + LOGDEBUG(anna::Logger::debug(anna::functions::asString("Test case %llu is finished, nothing done until soft-reset", a_id), ANNA_FILE_LOCATION)); + return false; + } + + if (a_state == State::Initialized) { + a_stepsIt = a_steps.begin(); + setState(State::InProgress); + + // For 'wait' steps (not really useful, but better than nothing: begin timestamp on test case start timestamp...): + a_startTime = anna::functions::millisecond(); + } + + // Check end of the test case: + if (done()) return false; + + bool somethingDone = false; + while ((*a_stepsIt)->execute()) { // executes returns 'true' if the next step must be also executed (execute until can't stand no more) + nextStep(); + // Check end of the test case: + if (done()) return false; + somethingDone = true; + } + + return somethingDone; +} + +bool TestCase::reset(bool hard) throw() { + + // Soft reset if finished: + if (!hard /* is soft reset */ && !isFinished()) return false; + + // Clean stage //////////////////////////// + // id is kept + std::vector::iterator it; + for (it = a_steps.begin(); it != a_steps.end(); it++) + (*it)->reset(); + + a_debugSummary.clear(); + a_startTime = 0; + a_interactiveAmount = -1; + + setState(State::Initialized); + + return true; +} + +void TestCase::assertInitialized() const throw(anna::RuntimeException) { + if (isFinished()) + throw anna::RuntimeException(anna::functions::asString("Cannot program anymore. The test case %llu has finished. You must reset it to append new steps (or do it during execution, which is also allowed).", a_id), ANNA_FILE_LOCATION); +} + +void TestCase::assertMessage(const anna::DataBlock &db, bool toEntity) throw(anna::RuntimeException) { + + bool isRequest = anna::diameter::codec::functions::isRequest(db); + bool registerKeys = ((isRequest && toEntity) || (!isRequest && !toEntity) /* (*) */); + // (*) we register answers Session-Id "assuming" that we will know the Session-Id values created by the client. + // This is another solution regarding diameter server testing. No sure about the final implementation. + // We will help registering also subscriber data, because certain messages (i.e. SLR) coming from clients could + // have specific Session-Id value (unknown at test programming), and normally are identified by subscriber. + + // Check hop-by-hop: + if (isRequest) { + anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db); + if (a_hopByHops.find(hbh) != a_hopByHops.end()) + throw anna::RuntimeException(anna::functions::asString("Another request has been programmed with the same hop-by-hop (%llu) in this test case (%llu)", hbh, a_id), ANNA_FILE_LOCATION); + a_hopByHops[hbh] = NULL; // may be assigned to a wait condition + } + + if (registerKeys) { + TestManager &testManager = TestManager::instantiate(); + testManager.registerSessionId(anna::diameter::helpers::base::functions::getSessionId(db), this); + + + std::string subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(db, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164); + if (subscriberId == "") // try with IMSI + subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(db, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI); + + if (subscriberId != "") + testManager.registerSubscriberId(subscriberId, this); + } +} + +void TestCase::addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException) { + assertInitialized(); + TestStepTimeout *step = new TestStepTimeout(this); + step->setTimeout(timeout); + addStep(step); +} + +void TestCase::addSendxml2e(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException) { + assertInitialized(); + assertMessage(db, true /* to entity */); + + if (stepNumber != -1) { + const TestStep *stepReferred = getStep(stepNumber); + if (!stepReferred) + throw anna::RuntimeException(anna::functions::asString("Step number (%d) do not exists (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); + + if (stepReferred->getType() != TestStep::Type::Wait) + throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); + + const TestCondition &tc = (static_cast(stepReferred))->getCondition(); + if (tc.getCode() == "0") { // if regexp used, is not possible to detect this kind of errors + throw anna::RuntimeException(anna::functions::asString("Step number (%d) must refer to a 'wait for request' step (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); + } + } + + TestStepSendxml2e *step = new TestStepSendxml2e(this); + step->setMsgDataBlock(db); + step->setOriginHost(host); + step->setWaitForRequestStepNumber(stepNumber); // -1 means, no reference + addStep(step); +} + +void TestCase::addSendxml2c(const anna::DataBlock &db, anna::diameter::comm::OriginHost *host, int stepNumber) throw(anna::RuntimeException) { + assertInitialized(); + assertMessage(db, false /* to client */); + + TestStepSendxml2c *step = new TestStepSendxml2c(this); + step->setMsgDataBlock(db); + step->setOriginHost(host); + addStep(step); +} + +void TestCase::addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException) { + assertInitialized(); + TestStepDelay *step = new TestStepDelay(this); + step->setDelay(delay); + addStep(step); +} + +void TestCase::addWait(bool fromEntity, + 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(anna::RuntimeException) { + assertInitialized(); + std::string usedHopByHop = hopByHop; + TestStepWait *step = NULL; + + // Check basic conditions: + if (bitR == "1") { + if (resultCode != "") + throw anna::RuntimeException(anna::functions::asString("You cannot specify Result-Code (%s) for a wait condition of a diameter request message (test case %llu)", resultCode.c_str(), a_id), ANNA_FILE_LOCATION); + if (hopByHop != "") + throw anna::RuntimeException(anna::functions::asString("You cannot specify Hop-by-hop (%s) for a wait condition of a diameter request message (test case %llu)", hopByHop.c_str(), a_id), ANNA_FILE_LOCATION); + } + else { + if (hopByHop != "") { + if (hopByHop[0] == '#') { + if (steps() == 0) + throw anna::RuntimeException(anna::functions::asString("No steps has been programmed, step reference is nonsense (test case %llu)", a_id), ANNA_FILE_LOCATION); + + int stepNumber = atoi(hopByHop.substr(1).c_str()); + + const TestStep *stepReferred = getStep(stepNumber); + if (!stepReferred) + throw anna::RuntimeException(anna::functions::asString("Step reference number (%d) do not exists (test case %llu)", stepNumber, a_id), ANNA_FILE_LOCATION); + + if (stepReferred->getType() != TestStep::Type::Sendxml2e && stepReferred->getType() != TestStep::Type::Sendxml2c) + throw anna::RuntimeException(anna::functions::asString("Step number must refer to a 'sendxml2e' or 'sendxml2c' step (test case %llu)", a_id), ANNA_FILE_LOCATION); + + const anna::DataBlock &db = (static_cast(stepReferred))->getMsgDataBlock(); + bool isAnswer = anna::diameter::codec::functions::isAnswer(db); + if (isAnswer) + throw anna::RuntimeException(anna::functions::asString("Step number must refer to a request message (test case %llu)", a_id), ANNA_FILE_LOCATION); + + // Hop-by-hop: + anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(db); + usedHopByHop = anna::functions::asString(hbh); + step = new TestStepWait(this); + a_hopByHops[hbh /* always exists: is the info we calculated above */] = step; + } + } + } + + if (!step) step = new TestStepWait(this); + step->setCondition(fromEntity, code, bitR, usedHopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId); + + LOGINFORMATION( + if (hasSameCondition(step->getCondition())) + anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION); + ); + + addStep(step); +} + +void TestCase::addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException) { + assertInitialized(); + + TestStepWait *step = new TestStepWait(this); + step->setCondition(fromEntity, regexp); + + LOGINFORMATION( + if (hasSameCondition(step->getCondition())) + anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION); + ); + + addStep(step); +} + +void TestCase::addCommand(const std::string &cmd) throw(anna::RuntimeException) { + assertInitialized(); + + TestStepCmd *step = new TestStepCmd(this); + step->setScript(cmd); + + addStep(step); +} + +TestStepWait *TestCase::searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw() { + + TestStepWait *result; + for (std::vector::const_iterator it = a_stepsIt /* current */; it != a_steps.end(); it++) { + if ((*it)->getType() != TestStep::Type::Wait) continue; + if ((*it)->isCompleted()) continue; + result = (TestStepWait*)(*it); + if ((result->getCondition().receivedFromEntity() == waitFromEntity) && (result->fulfilled(message))) + return result; + } + + return NULL; +} + +const TestStep *TestCase::getStep(int stepNumber) const throw() { + if (stepNumber < 1 || stepNumber > steps()) return NULL; +// return a_steps.at(stepNumber-1); // http://stackoverflow.com/questions/3269809/stdvectorat-vs-operator-surprising-results-5-to-10-times-slower-f + return a_steps[stepNumber-1]; +} diff --git a/source/testing/TestClock.cpp b/source/testing/TestClock.cpp new file mode 100644 index 0000000..d949983 --- /dev/null +++ b/source/testing/TestClock.cpp @@ -0,0 +1,20 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +// Standard +#include + +// Process +#include +#include + + +bool anna::testing::TestClock::tick() throw(anna::RuntimeException) { + return a_manager->tick(); +} + diff --git a/source/testing/TestCondition.cpp b/source/testing/TestCondition.cpp new file mode 100644 index 0000000..ba1db97 --- /dev/null +++ b/source/testing/TestCondition.cpp @@ -0,0 +1,182 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +// Standard + +// Project +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +using namespace anna::testing; + + +const char* TestCondition::asText(const Type::_v type) +throw() { + static const char* text [] = { "Generic", "Basic" }; + return text [type]; +} + +bool TestCondition::exists() const throw() { + if (a_type == Type::Generic) + return (a_regexp != ""); + else + return (a_code != "" || a_bitR != "" || a_hopByHop != "" || a_applicationId != "" || a_sessionId != "" || a_resultCode != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != ""); +} + +/* +bool anna::testing::operator==(const TestCondition &c1, const TestCondition &c2) throw() { + + if (c1.getType() != c2.getType()) return false; + + if (c1.getType() == TestCondition::Type::Generic) { + if (c1.getRegexp() != c2.getRegexp()) return false; + } + else { + if (c1.getCode() != c2.getCode()) return false; + if (c1.getBitR() != c2.getBitR()) return false; + if (c1.getHopByHop() != c2.getHopByHop()) return false; + if (c1.getApplicationId() != c2.getApplicationId()) return false; + if (c1.getSessionId() != c2.getSessionId()) return false; + if (c1.getResultCode() != c2.getResultCode()) return false; + if (c1.getMsisdn() != c2.getMsisdn()) return false; + if (c1.getImsi() != c2.getImsi()) return false; + if (c1.getServiceContextId() != c2.getServiceContextId()) return false; + } + + return true; +} +*/ + +bool TestCondition::comply(const anna::DataBlock &message/*, bool matchSessionId*/) const throw() { + + if (a_type == Type::Generic) { + anna::diameter::codec::Message codecMsg; + try { codecMsg.decode(message); } catch (anna::RuntimeException &ex) { ex.trace(); } + return codecMsg.isLike(a_regexp); + } + + // Basic + std::string compare; + anna::diameter::CommandId cid; + + if (a_code != "" || a_bitR != "") { + try { + cid = anna::diameter::codec::functions::getCommandId(message); + } + catch (anna::RuntimeException &) { return false; } + } + + if (a_code != "") { + compare = anna::functions::asString(cid.first); + if (a_code != compare) return false; + } + + if (a_bitR != "") { + compare = (cid.second ? "1":"0"); + if (a_bitR != compare) return false; + } + + if (a_hopByHop != "") { + try { + anna::diameter::HopByHop h = anna::diameter::codec::functions::getHopByHop(message); + compare = anna::functions::asString(h); + } + catch (anna::RuntimeException &) { return false; } + if (a_hopByHop != compare) return false; + } + + if (a_applicationId != "") { + try { + anna::diameter::ApplicationId a = anna::diameter::codec::functions::getApplicationId(message); + compare = anna::functions::asString(a); + } + catch (anna::RuntimeException &) { return false; } + if (a_applicationId != compare) return false; + } + + //if (matchSessionId) { + if (a_sessionId != "") { + try { + compare = anna::diameter::helpers::base::functions::getSessionId(message); + } + catch (anna::RuntimeException &) { return false; } + if (a_sessionId != compare) return false; + } + //} + + if (a_resultCode != "") { + try { + anna::U32 rc = anna::diameter::helpers::base::functions::getResultCode(message); + compare = anna::functions::asString(rc); + } + catch (anna::RuntimeException &) { return false; } + if (a_resultCode != compare) return false; + } + + if (a_msisdn != "") { + try { + compare = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164); + } + catch (anna::RuntimeException &) { return false; } + if (a_msisdn != compare) return false; + } + + if (a_imsi != "") { + try { + compare = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI); + } + catch (anna::RuntimeException &) { return false; } + if (a_imsi != compare) return false; + } + + if (a_serviceContextId != "") { + try { + compare = anna::diameter::helpers::dcca::functions::getServiceContextId(message); + } + catch (anna::RuntimeException &) { return false; } + if (a_serviceContextId != compare) return false; + } + + return true; +} + +anna::xml::Node* TestCondition::asXML(anna::xml::Node* parent) const +throw() { + anna::xml::Node* result = parent->createChild("TestCondition"); + if (!exists()) return result; + + if (a_type == Type::Generic) { + if (a_regexp != "") result->createAttribute("Regexp", a_regexp); + } + else { + if (a_code != "") result->createAttribute("Code", atoi(a_code.c_str())); + if (a_bitR != "") result->createAttribute("BitR", ((a_bitR == "1") ? "yes":"no")); + if (a_hopByHop != "") result->createAttribute("HopByHop", atoll(a_hopByHop.c_str())); + if (a_applicationId != "") result->createAttribute("ApplicationId", atoll(a_applicationId.c_str())); + if (a_sessionId != "") result->createAttribute("SessionId", a_sessionId); + if (a_resultCode != "") result->createAttribute("ResultCode", atoi(a_resultCode.c_str())); + if (a_msisdn != "") result->createAttribute("Msisdn", a_msisdn); + if (a_imsi != "") result->createAttribute("Imsi", a_imsi); + if (a_serviceContextId != "") result->createAttribute("ServiceContextId", a_serviceContextId); + } + + result->createAttribute("ExpectedSource", a_rcvFromEntity ? "entity":"client"); + + return result; +} diff --git a/source/testing/TestManager.cpp b/source/testing/TestManager.cpp new file mode 100644 index 0000000..8f1a728 --- /dev/null +++ b/source/testing/TestManager.cpp @@ -0,0 +1,537 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + +// Standard +#include +#include + +// Project +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//class TestTimer; + + +using namespace anna::testing; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void TestManager::StatSummary::newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw() { + + if ((beginState == TestCase::State::Initialized)&&(endState == TestCase::State::Initialized)) { // special case (new test case provisioning) + a_initializedTcs++; + return; + } + + switch (beginState) { + case TestCase::State::Initialized: a_initializedTcs--; break; + case TestCase::State::InProgress: a_inprogressTcs--; break; + case TestCase::State::Failed: a_failedTcs--; break; + case TestCase::State::Success: a_sucessTcs--; break; + default: break; + } + switch (endState) { + case TestCase::State::Initialized: a_initializedTcs++; break; + case TestCase::State::InProgress: a_inprogressTcs++; break; + case TestCase::State::Failed: a_failedTcs++; break; + case TestCase::State::Success: a_sucessTcs++; break; + default: break; + } +} + +void TestManager::StatSummary::clear() throw() { + a_initializedTcs = 0; + a_inprogressTcs = 0; + a_failedTcs = 0; + a_sucessTcs = 0; +} + +anna::xml::Node *TestManager::StatSummary::asXML(anna::xml::Node* parent) const throw() { + anna::xml::Node* result = parent->createChild("StatSummary"); + + anna::xml::Node* tcs = result->createChild("TestCasesCounts"); + tcs->createAttribute("Total", a_initializedTcs + a_inprogressTcs + a_failedTcs + a_sucessTcs); + tcs->createAttribute("Initialized", a_initializedTcs); + tcs->createAttribute("InProgress", a_inprogressTcs); + tcs->createAttribute("Failed", a_failedTcs); + tcs->createAttribute("Success", a_sucessTcs); + + return result; +} +/////////////////////////////////////////////////////////////////////////////////////////////////// + + + +TestManager::TestManager() : + anna::timex::TimeEventObserver("TestManager") { + a_timeController = NULL; + a_reportsDirectory = "./"; + + a_dumpInProgressReports = false; + a_dumpInitializedReports = false; + a_dumpFailedReports = false; + a_dumpSuccessReports = false; + + a_dumpHexMessages = false; + a_synchronousAmount = 1; + a_poolRepeats = 0; // repeat disabled by default + a_poolCycle = 1; + a_inProgressLimit = UINT_MAX; // no limit + a_clock = NULL; + //a_testPool.clear(); + //a_statSummary.clear(); + a_currentTestIt = a_testPool.end(); +} + +void TestManager::registerSessionId(const std::string &sessionId, const TestCase *testCase) throw(anna::RuntimeException) { + + std::map::const_iterator it = a_sessionIdTestCaseMap.find(sessionId); + if (it != a_sessionIdTestCaseMap.end()) { // found + unsigned int id = it->second->getId(); + if (id != testCase->getId()) { + throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such sessionId: %s", id, sessionId.c_str()), ANNA_FILE_LOCATION); + } + } + else { + a_sessionIdTestCaseMap[sessionId] = const_cast(testCase); + LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerSessionId for test case (id = %llu): %s)", testCase->getId(), sessionId.c_str()), ANNA_FILE_LOCATION)); + } +} + +void TestManager::registerSubscriberId(const std::string &subscriberId, const TestCase *testCase) throw(anna::RuntimeException) { + + std::map::const_iterator it = a_subscriberIdTestCaseMap.find(subscriberId); + if (it != a_subscriberIdTestCaseMap.end()) { // found + unsigned int id = it->second->getId(); + if (id != testCase->getId()) { + throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such subscriberId: %s", id, subscriberId.c_str()), ANNA_FILE_LOCATION); + } + } + else { + a_subscriberIdTestCaseMap[subscriberId] = const_cast(testCase); + LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerSubscriberId for test case (id = %llu): %s)", testCase->getId(), subscriberId.c_str()), ANNA_FILE_LOCATION)); + } +} + +TestTimer* TestManager::createTimer(TestCaseStep* testCaseStep, const anna::Millisecond &timeout, const TestTimer::Type::_v type) +throw(anna::RuntimeException) { + TestTimer* result(NULL); + + if(a_timeController == NULL) + throw anna::RuntimeException("You must invoke 'setTimerController' with a not NULL timex engine", ANNA_FILE_LOCATION); + + anna::Guard guard(a_timeController, "TestManager::createTimer"); // avoid interblocking + result = a_timers.create(); + result->setType(type); + result->setId((anna::timex::TimeEvent::Id) testCaseStep); + result->setObserver(this); + result->setContext(testCaseStep); + result->setTimeout(timeout); + + LOGDEBUG( + std::string msg("TestManager::createTimer | "); + msg += result->asString(); + anna::Logger::debug(msg, ANNA_FILE_LOCATION); + ); + + a_timeController->activate(result); + return result; +} + +void TestManager::cancelTimer(TestTimer* timer) +throw() { + if(timer == NULL) + return; + + LOGDEBUG( + std::string msg("TestManager::cancel | "); + msg += timer->asString(); + anna::Logger::debug(msg, ANNA_FILE_LOCATION); + ); + + try { + if(a_timeController == NULL) + a_timeController = anna::app::functions::component (ANNA_FILE_LOCATION); + + a_timeController->cancel(timer); + } catch(anna::RuntimeException& ex) { + ex.trace(); + } +} + +//------------------------------------------------------------------------------------------ +// Se invoca automaticamente desde anna::timex::Engine +//------------------------------------------------------------------------------------------ +void TestManager::release(anna::timex::TimeEvent* timeEvent) +throw() { + TestTimer* timer = static_cast (timeEvent); + timer->setContext(NULL); + a_timers.release(timer); +} + +bool TestManager::configureTTPS(int testTicksPerSecond) throw() { + + if (testTicksPerSecond == 0) { + if (a_clock) { + a_timeController->cancel(a_clock); + LOGDEBUG(anna::Logger::debug("Testing timer clock stopped !", ANNA_FILE_LOCATION)); + } + else { + LOGDEBUG(anna::Logger::debug("No testing timer started yet !", ANNA_FILE_LOCATION)); + } + return true; + } + else if (testTicksPerSecond < 0) { + LOGWARNING(anna::Logger::warning("Invalid 'ttps' provided", ANNA_FILE_LOCATION)); + return false; + } + + anna::Millisecond applicationTimeInterval = anna::Millisecond(1000 / testTicksPerSecond); + a_synchronousAmount = 1; + + if (applicationTimeInterval < anna::Millisecond(1)) { + LOGWARNING(anna::Logger::warning("Not allowed to configure more than 1000 events per second for for triggering testing system", ANNA_FILE_LOCATION)); + return false; + } + + if (applicationTimeInterval < a_timeController->getResolution()) { + int maximumObtained = 1000 / (int)(a_timeController->getResolution()); + a_synchronousAmount = ceil((double)testTicksPerSecond/maximumObtained); + // calculate again: + applicationTimeInterval = anna::Millisecond(a_synchronousAmount * 1000 / testTicksPerSecond); + } + + if (a_synchronousAmount > 1) { + LOGWARNING( + std::string msg = anna::functions::asString("Desired testing time trigger rate (%d events per second) requires more than one sending per event (%d every %lld milliseconds). Consider launch more instances with lower rate (for example %d ADML processes with %d ttps), or configure %d or more sockets to the remote endpoints to avoid burst sendings", + testTicksPerSecond, + a_synchronousAmount, + applicationTimeInterval.getValue(), + a_synchronousAmount, + 1000/applicationTimeInterval, + a_synchronousAmount); + + anna::Logger::warning(msg, ANNA_FILE_LOCATION); + ); + } + + if (a_clock) { + a_clock->setTimeout(applicationTimeInterval); + } + else { + a_clock = new TestClock("Testing clock", applicationTimeInterval, this); // clock + } + + if (!a_clock->isActive()) a_timeController->activate(a_clock); + + return true; +} + +bool TestManager::gotoTestCase(unsigned int id) throw() { + test_pool_it it = a_testPool.find(id); + if (it != a_testPool.end()) { + a_currentTestIt = it; + return true; + } + + return false; +} + +TestCase *TestManager::findTestCase(unsigned int id) const throw() { // id = -1 provides current test case triggered + + if (!tests()) return NULL; + test_pool_it it = ((id != -1) ? a_testPool.find(id) : a_currentTestIt); + if (it != a_testPool.end()) return const_cast(it->second); + return NULL; +} + +TestCase *TestManager::getTestCase(unsigned int id) throw() { + + test_pool_nc_it it = a_testPool.find(id); + if (it != a_testPool.end()) return it->second; + + TestCase *result = new TestCase(id); + a_testPool[id] = result; + return result; +} + +bool TestManager::clearPool() throw() { + if (!tests()) return false; + for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) delete it->second; + // TODO: stop the possible command threads or there will be a core dump + + a_testPool.clear(); + a_sessionIdTestCaseMap.clear(); + a_subscriberIdTestCaseMap.clear(); + a_currentTestIt = a_testPool.end(); + a_poolCycle = 1; + configureTTPS(0); // stop + a_statSummary.clear(); + return true; +} + +bool TestManager::resetPool(bool hard) throw() { + bool result = false; // any reset + + if (!tests()) return result; + for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) { + if (it->second->reset(hard)) + result = true; + } + //a_sessionIdTestCaseMap.clear(); + //a_subscriberIdTestCaseMap.clear(); + return result; +} + +bool TestManager::tick() throw() { + LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION)); + return execTestCases(a_synchronousAmount); +} + +bool TestManager::execTestCases(int sync_amount) throw() { + + if (!tests()) { + LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION)); + return false; + } + + // Synchronous sendings per tick: + int count = sync_amount; + while (count > 0) { + if (!nextTestCase()) return false; // stop the clock + count--; + } + + return true; +} + +bool TestManager::nextTestCase() throw() { + + while (true) { + + // Limit for in-progress test cases: + if (getInProgressCount() >= a_inProgressLimit) { + LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION)); + return true; // wait next tick to release OTA test cases + } + + // Next test case: + if (a_currentTestIt == a_testPool.end()) + a_currentTestIt = a_testPool.begin(); + else + a_currentTestIt++; + + // Completed: + if (a_currentTestIt == a_testPool.end()) { + if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) { + LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION)); + a_poolCycle = 1; + return false; + } + else { + LOGWARNING( + std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]"; + anna::Logger::warning(anna::functions::asString("Testing pool cycle %d completed (repeats configured: %d%s). Restarting for the %s cycle", a_poolCycle, a_poolRepeats, nolimit.c_str(), (a_poolRepeats == a_poolCycle) ? "last":"next"), ANNA_FILE_LOCATION); + ); + a_poolCycle++; + //a_currentTestIt = a_testPool.begin(); + return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state + // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed. + } + } + + // Soft reset to initialize already finished (in previous cycle) test cases: + a_currentTestIt->second->reset(false); + + // Process test case: + LOGDEBUG(anna::Logger::debug(anna::functions::asString("Processing test case id = %llu, currently '%s' state", a_currentTestIt->first, TestCase::asText(a_currentTestIt->second->getState())), ANNA_FILE_LOCATION)); + if (a_currentTestIt->second->getState() != TestCase::State::InProgress) { + a_currentTestIt->second->process(); + return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole + // time for complete the test cases pool regarding the single test case lifetime. You shouldn't + // forget to programm a test case timeout with a reasonable value + } + } +} + +TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() { + try { + sessionId = anna::diameter::helpers::base::functions::getSessionId(message); + } + catch (anna::RuntimeException &ex) { + //ex.trace(); + LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION)); + return NULL; + } + std::map::const_iterator sessionIdIt = a_sessionIdTestCaseMap.find(sessionId); + if (sessionIdIt != a_sessionIdTestCaseMap.end()) + return sessionIdIt->second; + + LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION)); + return NULL; +} + +TestCase *TestManager::getTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() { + try { + subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164); + if (subscriberId == "") // try with IMSI + subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI); + } + catch (anna::RuntimeException &ex) { + //ex.trace(); + LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION)); + return NULL; + } + std::map::const_iterator subscriberIdIt = a_subscriberIdTestCaseMap.find(subscriberId); + if (subscriberIdIt != a_subscriberIdTestCaseMap.end()) + return subscriberIdIt->second; + + LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION)); + return NULL; +} + +void TestManager::receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) { + + // Testing disabled: + if (!tests()) return; + + // Identify the test case: + std::string sessionId, subscriberId; + TestCase *tc; + tc = getTestCaseFromSessionId(message, sessionId); + if (!tc) + tc = getTestCaseFromSubscriberId(message, subscriberId); + if (!tc) { + LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from server: ", message), ANNA_FILE_LOCATION)); // this should not appear + return; + } + + // Work with Test case: + TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */); + if (!tsw) { // store as 'uncovered' + std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "':"; + + try { + static anna::diameter::codec::Message codecMsg; + codecMsg.decode(message); + hint += "\n"; hint += codecMsg.asXMLString(); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + hint += "\n"; hint += ex.asString(); + } + hint += "\n"; hint += clientSession->asString(); + + tc->addDebugSummaryHint(hint); + } + else { + tsw->setClientSession(const_cast(clientSession)); + tc->process(); + } +} + +void TestManager::receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) { + + // Testing disabled: + if (!tests()) return; + + // Identify the test case: + std::string sessionId, subscriberId; + TestCase *tc; + tc = getTestCaseFromSessionId(message, sessionId); + if (!tc) + tc = getTestCaseFromSubscriberId(message, subscriberId); + if (!tc) { + LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from client: ", message), ANNA_FILE_LOCATION)); // this should not appear + return; + } + + // Work with Test case: + TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */); + if (!tsw) { // store as 'uncovered' + std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "':"; + + try { + static anna::diameter::codec::Message codecMsg; + codecMsg.decode(message); + hint += "\n"; hint += codecMsg.asXMLString(); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + hint += "\n"; hint += ex.asString(); + } + hint += "\n"; hint += serverSession->asString(); + + tc->addDebugSummaryHint(hint); + } + else { + tsw->setServerSession(const_cast(serverSession)); + tc->process(); + } +} + +anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const +throw() { + anna::xml::Node* result = parent->createChild("TestManager"); + + int poolSize = a_testPool.size(); + result->createAttribute("NumberOfTestCases", poolSize); + if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats); + else result->createAttribute("PoolRepeats", "disabled"); + result->createAttribute("PoolCycle", a_poolCycle); + a_statSummary.asXML(result); + if (a_inProgressLimit == UINT_MAX) + result->createAttribute("InProgressLimit", ""); + else + result->createAttribute("InProgressLimit", a_inProgressLimit); + result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no")); + result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no")); + result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no")); + result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no")); + result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no")); + result->createAttribute("ReportsDirectory", a_reportsDirectory); + if (a_clock) { + result->createAttribute("AsynchronousSendings", a_synchronousAmount); + int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout(); + result->createAttribute("TicksPerSecond", ticksPerSecond); + } + if (a_currentTestIt != a_testPool.end()) { + result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first); + } + if (poolSize != 0) { + anna::xml::Node* testCases = result->createChild("TestCases"); + for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) { + if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue; + if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue; + if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue; + if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue; + (*it).second->asXML(testCases); + } + } + + return result; +} + +std::string TestManager::asXMLString() const throw() { + anna::xml::Node root("root"); + return anna::xml::Compiler().apply(asXML(&root)); +} + diff --git a/source/testing/TestStep.cpp b/source/testing/TestStep.cpp new file mode 100644 index 0000000..7e96926 --- /dev/null +++ b/source/testing/TestStep.cpp @@ -0,0 +1,711 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +// Standard +#include +#include +#include + +#include // sigaction, sigemptyset, struct sigaction, SIGCHLD, SA_RESTART, SA_NOCLDSTOP +#include // perror +#include // exit +#include // waitpid, pid_t, WNOHANG + +// cmd with fork: +#include +#include + + +// Project +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +using namespace anna::testing; + + +namespace { + + void handle_sigchld(int sig) { + while (waitpid((pid_t)(-1 /* any child (the only) */), 0, WNOHANG|WNOWAIT) > 0) {} + } + + void cmdRunOnThread (TestStepCmd *step, const std::string &cmd) { + + // Thread running: + step->setThreadRunning(true); + + int status = -2; + + struct sigaction sa; + sa.sa_handler = &handle_sigchld; + sigemptyset(&sa.sa_mask); + 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; + } + */ + } + else { + perror(0); + } + // This can be implemented portably and somewhat more concisely with the signal function if you prefer: + // if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) { + // perror(0); + // exit(1); + // } + + if (status < 0) { + char buf[256]; + char const * str = strerror_r(errno, buf, 256); + step->setErrorMsg(anna::functions::asString("errno = %d (%s)", errno, str)); + } + + step->setResultCode(WEXITSTATUS(status)); // rc = status >>= 8; // divide by 256 + step->complete(); + // TODO: terminate thread when deprecated (RT signal ?) + // TODO: mutex the step while setting data here !! + } + + 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 { + messageCodec.clear(); + messageCodec.decode(message); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + result = false; + } + + return result; + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// TestStep +//////////////////////////////////////////////////////////////////////////////////////////////////////// +void TestStep::initialize(TestCase *testCase) { + a_testCase = testCase; + a_completed = false; + a_type = Type::Unconfigured; + a_beginTimestamp = 0; + a_endTimestamp = 0; + 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", "Command" }; + return text [type]; +} + +anna::xml::Node* TestStep::asXML(anna::xml::Node* parent) +throw() { + anna::xml::Node* result = parent->createChild("TestStep"); + + result->createAttribute("Number", a_number); + result->createAttribute("Type", asText(a_type)); + result->createAttribute("Completed", (a_completed ? "yes":"no")); + + // 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); + 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); + result->createAttribute("EndTimestamp", s_aux); + + return result; +} + +std::string TestStep::asXMLString() throw() { + anna::xml::Node root("root"); + return anna::xml::Compiler().apply(asXML(&root)); +} + +bool TestStep::execute() throw() { + + 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)", 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)", 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(); +} + +void TestStep::next() throw() { + a_testCase->nextStep(); + a_testCase->process(); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// TestStepTimeout +//////////////////////////////////////////////////////////////////////////////////////////////////////// +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"); + result->createAttribute("Timeout", a_timeout.asString()); + + return result; +} + +bool TestStepTimeout::do_execute() throw() { + try { + a_timer = TestManager::instantiate().createTimer((TestCaseStep*)this, a_timeout, TestTimer::Type::TimeLeft); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + a_testCase->addDebugSummaryHint(ex.asString()); // before report (when set Failed state) + a_testCase->setState(TestCase::State::Failed); + } + + return true; // go next +} + +void TestStepTimeout::do_complete() throw() { + int stepNumber = getNumber(); + if (stepNumber == a_testCase->steps()) { + 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) + a_testCase->setState(TestCase::State::Success); + } + else if (a_testCase->getState() == TestCase::State::InProgress) { // sure + a_testCase->addDebugSummaryHint(anna::functions::asString("Timeout expired (step number %d) before test case finished", stepNumber)); // before report (when set Failed state) + a_testCase->setState(TestCase::State::Failed); + } + + a_timer = NULL; +} + +void TestStepTimeout::do_reset() throw() { + try { + TestManager::instantiate().cancelTimer(a_timer); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + } + a_timer = NULL; + //a_timeout = 0; THIS IS CONFIGURATION INFO +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// TestStepSendxml +//////////////////////////////////////////////////////////////////////////////////////////////////////// +anna::xml::Node* TestStepSendxml::asXML(anna::xml::Node* parent) +throw() { + anna::xml::Node* result = TestStep::asXML(parent); + //parent->createChild("TestStepSendxml"); + std::string msg = "", xmlmsg = ""; + + // Message + if (TestManager::instantiate().getDumpHex()) { + if (a_message.isEmpty()) { + msg = ""; + } + else { + msg = "\n"; msg += a_message.asString(); msg += "\n"; + } + } + + if (decodeMessage()) { + xmlmsg = "\n"; + xmlmsg += a_messageCodec->asXMLString(); + xmlmsg += "\n"; + } + else { + xmlmsg = ""; + } + + if (msg != "") result->createAttribute("Message", msg); + if (xmlmsg != "") result->createAttribute("XMLMessage", xmlmsg); + result->createAttribute("Expired", (a_expired ? "yes":"no")); + if (a_waitForRequestStepNumber != -1) + result->createAttribute("WaitForRequestStepNumber", a_waitForRequestStepNumber); + + return result; +} + +bool TestStepSendxml::do_execute() throw() { + bool success = false; + std::string failReason; + anna::diameter::comm::Entity *entity = a_originHost->getEntity(); // by default + anna::diameter::comm::LocalServer *localServer = a_originHost->getDiameterServer(); // by default + const TestStepWait *tsw = NULL; + anna::diameter::comm::Message *msg = a_originHost->createCommMessage(); + + try { + + 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; + + if (tsw) { // is an answer for a received request on wait condition + anna::diameter::comm::ClientSession *clientSession = tsw->getClientSession(); + if (clientSession) { + /* response NULL (is an answer) */clientSession->send(msg); + success = true; + usedClientSession = clientSession; + } + else { + failReason = "Reference wait step didn't store a valid client session. Unable to send the message"; + LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); + } + } + else { + if (entity) { + success = entity->send(msg); + anna::diameter::comm::Server *usedServer = entity->getLastUsedResource(); + usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL; + } + else { + failReason = "There is no diameter entity currently configured. Unable to send the message"; + LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); + } + } // else (normal sending) + + // Detailed log: + if(a_originHost->logEnabled()) { + if (decodeMessage(true /* trust */)) { + std::string detail = usedClientSession ? usedClientSession->asString() : ""; // shouldn't happen + a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2e" : "send2eError"), detail); + } + } + } + else if (getType() == Type::Sendxml2c) { + anna::diameter::comm::ServerSession *usedServerSession = NULL; + + if (tsw) { // is an answer for a received request on wait condition + anna::diameter::comm::ServerSession *serverSession = tsw->getServerSession(); + if (serverSession) { + /* response NULL (is an answer) */serverSession->send(msg); + success = true; + usedServerSession = serverSession; + } + else { + failReason = "Reference wait step didn't store a valid server session. Unable to send the message"; + LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); + } + } + else { + if (localServer) { + success = localServer->send(msg); + usedServerSession = localServer->getLastUsedResource(); + } + else { + failReason = "There is no diameter local server currently configured. Unable to send the message"; + LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); + } + } // else (normal sending) + + // Detailed log: + if(a_originHost->logEnabled()) { + if (decodeMessage(true /* trust */)) { + std::string detail = usedServerSession ? usedServerSession->asString() : ""; // shouldn't happen + a_originHost->writeLogFile(*a_messageCodec, (success ? "sent2c" : "send2cError"), detail); + } + } + } + + } catch(anna::RuntimeException &ex) { + failReason = ex.asString(); + } + + // release msg + a_originHost->releaseCommMessage(msg); + + if (!success) { + a_testCase->addDebugSummaryHint(failReason); // before report (when set Failed state); + a_testCase->setState(TestCase::State::Failed); + } + else { + complete(); + } + + return success; // go next if sent was OK +} + +void TestStepSendxml::do_reset() throw() { + a_expired = false; + //a_message.clear(); + //a_messageAlreadyDecoded = false; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// TestStepDelay +//////////////////////////////////////////////////////////////////////////////////////////////////////// +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 == 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); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + a_testCase->addDebugSummaryHint(ex.asString()); // before report (when set Failed state) + a_testCase->setState(TestCase::State::Failed); + } + + return false; // don't go next (wait complete) +} + +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); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + } + a_timer = NULL; + //a_delay = 0; THIS IS CONFIGURATION INFO +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// TestStepWait +//////////////////////////////////////////////////////////////////////////////////////////////////////// +void TestStepWait::setCondition(bool fromEntity, + 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); + a_condition.setBitR(bitR); + a_condition.setResultCode(resultCode); + a_condition.setSessionId(sessionId); + a_condition.setHopByHop(hopByHop); + a_condition.setMsisdn(msisdn); + a_condition.setImsi(imsi); + a_condition.setServiceContextId(serviceContextId); +} + +void TestStepWait::setCondition(bool fromEntity, const std::string ®exp) throw() { + + a_condition.setReceivedFromEntity(fromEntity); + a_condition.setRegexp(regexp); +} + +anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent) +throw() { + anna::xml::Node* result = TestStep::asXML(parent); + //parent->createChild("TestStepWait"); + std::string msg = "", xmlmsg = ""; + + // Condition + a_condition.asXML(result); + + // Message + if (TestManager::instantiate().getDumpHex()) { + if (a_message.isEmpty()) { + msg = ""; + } + else { + msg = "\n"; msg += a_message.asString(); msg += "\n"; + } + } + + 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; +} + +bool TestStepWait::do_execute() throw() { + return a_completed; +} + +void TestStepWait::do_complete() throw() { + //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 + complete(); + return true; + } + + return false; +} + +void TestStepWait::do_reset() throw() { + a_message.clear(); + a_clientSession = NULL; + a_serverSession = NULL; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// TestStepCmd +//////////////////////////////////////////////////////////////////////////////////////////////////////// +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:""); + if (a_errorMsg != "") result->createAttribute("ErrorMessage", a_errorMsg); + 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; +} + +bool TestStepCmd::do_execute() throw() { + if (!a_threadRunning /* || a_threadDeprecated DO NOT WANT TO OVERLAP ... */) { + // Special tags to replace: + std::string cmd = getScript(); + 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())); + while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID)) != std::string::npos) + cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID), anna::functions::asString(a_testCase->getId())); + while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID)) != std::string::npos) + 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 ! +} + +void TestStepCmd::do_complete() throw() { + + a_threadRunning = false; + if (a_threadDeprecated) { + a_threadDeprecated = false; + do_reset(); + setErrorMsg(anna::functions::asString("Step %d deprecated due to previous reset for Test Case %llu", getNumber(), a_testCase->getId())); + a_testCase->setState(TestCase::State::Failed); + return; // ignore TODO: interrupt the thread to avoid execution of the script + } + + if (getResultCode() != 0) + 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: 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; +} + diff --git a/source/testing/TestTimer.cpp b/source/testing/TestTimer.cpp new file mode 100644 index 0000000..47c4ccc --- /dev/null +++ b/source/testing/TestTimer.cpp @@ -0,0 +1,73 @@ +// ANNA - Anna is Not Nothingness Anymore // +// // +// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo // +// // +// See project site at http://redmine.teslayout.com/projects/anna-suite // +// See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE // + + +// Project +#include +#include + +// Standard +#include + +// Project +#include + + +using namespace std; +using namespace anna; +using namespace anna::testing; + +void TestTimer::expire(anna::timex::Engine*) +throw(anna::RuntimeException) { + + TestStep *step = getTestCaseStep(); + + // action: + step->complete(); + + LOGDEBUG( + string msg("TestTimer::expire | Completed TestStep:\n\n"); + msg += step->asXMLString(); + anna::Logger::debug(msg, ANNA_FILE_LOCATION); + ); +} + +const char* TestTimer::asText(const Type::_v type) +throw() { + static const char* text [] = { "TimeLeft", "Delay" }; + return text [type]; +} + + +string TestTimer::asString() const +throw() { + string result("TestTimer { "); + result += anna::timex::Transaction::asString(); + result += " Type: "; + result += asText(a_type); + +// const TestStep *step = getTestCaseStep(); +// +// if(step != NULL) { +// result += " | "; +// result += step->asXMLString(); +// } else +// result += " | step: "; +// } + +// switch(getType()) { +// case Type::TimeLeft: +// //result += " | xxxxx: "; +// break; +// case Type::Delay: +// //result += " | xxxxx: "; +// break; +// } + + return result += " }"; +} +