From: Eduardo Ramos Testillano Date: Thu, 27 Aug 2015 14:05:12 +0000 (+0200) Subject: System test feature X-Git-Tag: REFACTORING_TESTING_LIBRARY~118 X-Git-Url: https://git.teslayout.com/public/public/public/?a=commitdiff_plain;h=4c3f0a4d7e4db76996404d80c6f939548fca656f;p=anna.git System test feature --- diff --git a/example/comm/rrkClient/main.cpp b/example/comm/rrkClient/main.cpp index e0ebc50..d87014f 100644 --- a/example/comm/rrkClient/main.cpp +++ b/example/comm/rrkClient/main.cpp @@ -82,7 +82,7 @@ RRKClient::RRKClient () : { CommandLine& commandLine (CommandLine::instantiate ()); - commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que el servidor atiende respuestas."); + commandLine.add ("p", CommandLine::Argument::Mandatory, "Puertos en los que el servidor atiende respuestas."); commandLine.add ("a", CommandLine::Argument::Mandatory, "Puertos en los que hay servidores atendiendo peticiones"); commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)"); } diff --git a/example/diameter/launcher/DEPLOY.sh b/example/diameter/launcher/DEPLOY.sh index f83ec69..3e3cbd9 100755 --- a/example/diameter/launcher/DEPLOY.sh +++ b/example/diameter/launcher/DEPLOY.sh @@ -65,9 +65,9 @@ STARTED=\`pgrep \$EXE 2>/dev/null\` [ \$? -eq 0 ] && { echo "Already started!"; echo "\$STARTED" ; exit 1 ; } ./pre-start.sh 0> launcher.trace -rm -f counters/* +rm -f counters/* test-reports/* # Execution line: -./\$EXE --services services.xml --cntDir counters $other & +./\$EXE --services services.xml --cntDir counters --tmDir test-reports $other & echo \$! > .pid EOF @@ -138,6 +138,7 @@ for STACKMGMT_EXEC in ${STACKMGMT_EXECS[@]}; do echo "not found" done [ -z "$available" ] && _exit "Anna Diameter Stack Management Tool is not installed neither linked. See README.md (Install section)." +STACKMGMT_EXEC=`readlink -f $STACKMGMT_EXEC` [ ! -d $SETUPS_DIR ] && _exit "Diameter stacks not found ($SETUPS_DIR)." @@ -199,6 +200,7 @@ mkdir -p $DPATH mkdir -p $DPATH/stacks mkdir -p $DPATH/DTDs mkdir -p $DPATH/counters +mkdir -p $DPATH/test-reports [ "$option" = "b" ] && mkdir -p $DPATH/services if [ "$EXEC" = "$EXEC_installed" ] then diff --git a/example/diameter/launcher/DEPLOY_clientAndServer.sh b/example/diameter/launcher/DEPLOY_clientAndServer.sh deleted file mode 100755 index 628c223..0000000 --- a/example/diameter/launcher/DEPLOY_clientAndServer.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -############# -# VARIABLES # -############# -DEPLOY_SCR=`dirname $0`/DEPLOY.sh -DEPLOY_DIR__dflt=$HOME/ADML - -############# -# FUNCTIONS # -############# -usage() { - echo - echo "Usage: $0 [empty directory: '$DEPLOY_DIR__dflt' by default]" - echo - exit 1 -} - -############# -# EXECUTION # -############# -DIR=$1 -[ "$DIR" = "" ] && DIR=$DEPLOY_DIR__dflt -[ -d $DIR ] && usage - -$DEPLOY_SCR b $DIR/client -$DEPLOY_SCR b $DIR/server - -cd $DIR/client -echo c | ./configure.sh - -cd - >/dev/null - -cd $DIR/server -echo s | ./configure.sh - -echo -echo "Done!" -echo - diff --git a/example/diameter/launcher/DEPLOY_setups.sh b/example/diameter/launcher/DEPLOY_setups.sh new file mode 100755 index 0000000..3457bbf --- /dev/null +++ b/example/diameter/launcher/DEPLOY_setups.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +############# +# VARIABLES # +############# +DEPLOY_SCR=`dirname $0`/DEPLOY.sh + +############# +# FUNCTIONS # +############# +_exit() { + echo + echo $1 + echo + exit 1 +} + +############# +# EXECUTION # +############# +echo +echo "Which one you want to deploy [1]:" +echo +echo " 1. Client and server" +echo " 2. Ft-client and server" +echo +read type +[ "$type" = "" ] && type=1 + +case $type in + 1) + DEPLOY_DIR__dflt=$HOME/ADML-clientAndServer + ;; + + 2) + DEPLOY_DIR__dflt=$HOME/ADML-ftclientAndServer + ;; + + *) + echo "Unknown option!" + exit 1 + ;; +esac + +echo +echo "Directory for deploy [$DEPLOY_DIR__dflt]:" +read DIR +[ "$DIR" = "" ] && DIR=$DEPLOY_DIR__dflt +[ -d $DIR ] && _exit "The directory '$DIR' already exists !" + +case $type in + 1) + $DEPLOY_SCR b $DIR/client + $DEPLOY_SCR b $DIR/server + cd $DIR/client + echo c | ./configure.sh + cd - >/dev/null + cd $DIR/server + echo s | ./configure.sh + cd - >/dev/null + ;; + + 2) + $DEPLOY_SCR f $DIR/ft-client + $DEPLOY_SCR b $DIR/server + cd $DIR/server + echo s | ./configure.sh + cd - >/dev/null + ;; +esac + +echo +echo "Done!" +echo + diff --git a/example/diameter/launcher/Launcher.cpp b/example/diameter/launcher/Launcher.cpp index a21466a..eb4dfe7 100644 --- a/example/diameter/launcher/Launcher.cpp +++ b/example/diameter/launcher/Launcher.cpp @@ -9,6 +9,8 @@ // Standard #include // std::istringstream #include // std::cout +#include // ceil +#include // Project #include @@ -22,15 +24,18 @@ #include // Process -#include "Launcher.hpp" -#include "RealmNode.hpp" -#include "MyDiameterEngine.hpp" +#include +#include +#include +#include +#include #define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.tasks.input" #define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.tasks.output" + const char *ServicesDTD = "\ \n\ \n\ @@ -109,6 +114,8 @@ Launcher::Launcher() : anna::comm::Application("launcher", "DiameterLauncher", " a_codecEngine = new anna::diameter::codec::Engine("MyCodecEngine"); a_timeEngine = NULL; a_counterRecorder = NULL; + a_admlMinResolution = 2 * anna::timex::Engine::minResolution; // 2*10 = 20 ms; 1000/20 = 50 ticks per second; + //a_admlMinResolution = (anna::Millisecond)100; a_counterRecorderClock = NULL; // a_nodes.clear(); @@ -430,9 +437,8 @@ throw(anna::RuntimeException) { CommandLine& cl(anna::CommandLine::instantiate()); 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, anna::timex::Engine::minResolution); - a_timeEngine = new anna::timex::Engine((anna::Millisecond)600000, (anna::Millisecond)100); // puedo bajar hasta 10 + a_timeEngine = new anna::timex::Engine((anna::Millisecond)600000, a_admlMinResolution); + TestManager::instantiate().setTimerController(a_timeEngine); // Counters record procedure: const char *varname = "cntRecordPeriod"; @@ -444,6 +450,11 @@ throw(anna::RuntimeException) { a_counterRecorder = new MyCounterRecorder(cntDir + anna::functions::asString("/Counters.Pid%d", (int)getPid())); } + // Testing framework: + std::string tmDir = "."; + if(cl.exists("tmDir")) tmDir = cl.getValue("tmDir"); + TestManager::instantiate().setReportsDirectory(tmDir); + // Tracing: if(cl.exists("trace")) anna::Logger::setLevel(anna::Logger::asLevel(cl.getValue("trace"))); @@ -563,9 +574,12 @@ throw(anna::RuntimeException) { oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentExpired, "", 55); oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnClientSessionExpired, "", 56); oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestSentOnServerSessionExpired, "", 57); - oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedUnknown, "", 58); - oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSessionUnknown, "", 59); - oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSessionUnknown, "", 60); + oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmitted, "", 58); + oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnClientSession, "", 59); + oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::RequestRetransmittedOnServerSession, "", 60); + oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedUnknown, "", 61); + oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnClientSessionUnknown, "", 62); + oamDiameterComm.registerCounter(anna::diameter::comm::OamModule::Counter::AnswerReceivedOnServerSessionUnknown, "", 63); ////////////////// // CODEC MODULE // ////////////////// @@ -672,6 +686,7 @@ throw(anna::RuntimeException) { a_communicator->accept(); } + bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw() { // Get hex string static char buffer[8192]; @@ -939,7 +954,7 @@ std::string Launcher::help() const throw() { result += "\n has been logged)"; result += "\n [retry] Request retransmission"; result += "\n"; - result += "\n-------------------------------------------------------------------------------------------- Load tests"; + result += "\n------------------------------------------------------------------------------------------- Burst tests"; result += "\n"; result += "\nburst|[|parameter] Used for performance testing, we first program diameter requests"; result += "\n messages in order to launch them from client side to the configured"; @@ -966,7 +981,230 @@ std::string Launcher::help() const throw() { result += "\n operations is that burst won't be awaken. Externally we could control"; result += "\n sending time (no request will be sent for answers)."; result += "\n burst|goto| Updates current burst pointer position."; - result += "\n burst|look| Show programmed burst message for order provided."; + result += "\n burst|look[|order] Show programmed burst message for order provided, current when missing."; + result += "\n"; + result += "\n-------------------------------------------------------------------------------------- Advanced testing"; + result += "\n"; + result += "\n Burst mode only allows single interface interaction. For multiple interface"; + result += "\n (realm) coordination, you could use the advanced test cases programming:"; + result += "\n"; + result += "\n"; + result += "\n test||[|parameters]"; + result += "\n"; + result += "\n Adds a new step to the test case with provided identifier. If provided identifier"; + result += "\n is not registered yet, a new test case will be created with that value and the"; + result += "\n step will be added as the first. For a specific 'id', the steps are stored in"; + result += "\n order as they are programmed"; + result += "\n"; + result += "\n : integer number, normally monotonically increased for each test case. Some external"; + result += "\n script/procedure shall clone a test case template in order to build a collection"; + result += "\n of independent and coherent test cases (normally same type) with different context"; + result += "\n values (Session-Id, Subscriber-Id, etc.)."; + result += "\n"; + result += "\n : commands to be executed for the test id provided. Each command programmed"; + result += "\n constitutes a test case 'step', numbered from 1 to N."; + result += "\n"; + result += "\n timeout| Sets an asynchronous timer to restrict the maximum timeout"; + result += "\n until last test step. Normally, this command is invoked"; + result += "\n in the first step, anyway it measures the time from the"; + result += "\n execution point whatever it is. The expiration will abort"; + result += "\n the test if still running. One or more timeouts could be"; + result += "\n programmed (not usual), but the more restrict will apply."; + result += "\n It is highly recommended to program a initial timeout step,"; + result += "\n or the test case could be eternally in-progress."; + result += "\n"; + result += "\n sendxml2e|[|]"; + result += "\n Sends xml source file (pathfile) to entity (it would be a"; + result += "\n 'forward' event if it came through local server endpoint)."; + result += "\n The step number should be provided for answers to indicate"; + result += "\n the 'wait for request' corresponding step. If you miss this"; + result += "\n reference, the sequence information (hop-by-hop, end-to-end)"; + result += "\n will be sent as they are in the answer xml message (realize"; + result += "\n the difficulty of predicting these information). Be sure to"; + result += "\n refer to a 'wait for request' step. Conditions like 'regexp'"; + result += "\n (as we will see later) are not verified."; + result += "\n"; + result += "\n sendxml2c|[|]"; + result += "\n Sends xml source file (pathfile) to client (it would be a"; + result += "\n 'forward' event if it came through remote server endpoint)."; + result += "\n Same commented for 'sendxml2e' regarding the step number."; + result += "\n"; + result += "\n delay| Blocking step until the time lapse expires. Useful to give "; + result += "\n some cadence control and time schedule for a specific case."; + result += "\n wait| Blocking step until condition is fulfilled. The message could"; + result += "\n received from entity (waitfe) or from client (waitfc)."; + result += "\n"; + result += "\n wait-regexp|"; + result += "\n Wait condition, from entity (waitfe-regexp) or client (waitfc-regexp)"; + result += "\n to match the serialized xml content for received messages. CPU cost"; + result += "\n is bigger than the former ones because the whole message must be"; + result += "\n decoded and converted to xml instead of doing a direct hexadecimal"; + result += "\n buffer search. The main advantage is the great flexibility to identify"; + result += "\n any content with a regular expression."; + result += "\n"; + result += "\n : Optional parameters which must be fulfilled to continue through the next step."; + result += "\n Any received message over diameter interfaces will be evaluated against the"; + result += "\n corresponding test case starting from the current step until the first one"; + result += "\n whose condition is fulfilled. If no condition is fulfilled the event will be"; + result += "\n classified as 'uncovered' (normally a test case bad configuration, or perhaps"; + result += "\n a real unexpected message)."; + + // TODO(***) +// result += "\n The way to identify the test case, is through registered Session-Id values for"; +// result += "\n programmed requests. But this depends on the type of node. Acting as clients,"; +// result += "\n requests received have Session-Id values which are already registered with"; +// result += "\n one test case, causing an error if not found. Acting as servers, requests are"; +// result += "\n received over a diameter local server from a client which are generating that"; +// result += "\n Session-Id values. Then we know nothing about such values. The procedure in"; +// result += "\n this case is find out a test case not-started containing a condition which"; +// result += "\n comply with the incoming message, and reactivates it."; + // The other solution: register Session-Id values for answers send to client from a local diameter server. + + result += "\n How to answer: a wait condition for a request will store the incoming message"; + result += "\n which fulfills that condition. This message is useful together with the peer"; + result += "\n connection source in a further send step configured with the corresponding"; + result += "\n response. You could also insert a delay between wait and send steps to be"; + result += "\n more realistic (processing time simulation in a specific ADML realm node)."; + result += "\n Always, a response send step will get the needed information from the most"; + result += "\n recent wait step finding in reverse order (note that some race conditions"; + result += "\n could happen if your condition is not specific enough)."; + + result += "\n"; + result += "\n Condition format:"; + result += "\n"; + result += "\n [code]|[bitR]|[ResultCode]|[sessionId]|[hopByHop]|[msisdn]|[imsi]|[serviceContextId]"; + result += "\n"; + result += "\n code: integer number"; + result += "\n bitR: 1 (request), 0 (answer)"; + result += "\n ResultCode: integer number"; + result += "\n sessionId: string"; + result += "\n hopByHop: integer number or request send step reference: #"; + result += "\n"; + result += "\n Using the hash reference, you would indicate a specific wait condition"; + result += "\n for answers. The step number provided must correspond to any of the"; + result += "\n previous send commands (sendxml2e/sendxml2c) configured for a request."; + result += "\n This 'hop-by-hop' variant eases the wait condition for answers in the"; + result += "\n safest way."; + result += "\n"; + result += "\n msisdn: string"; + result += "\n imsi: string"; + result += "\n serviceContextId: string"; + result += "\n"; + result += "\n Take into account these rules, useful in general:"; + result += "\n"; + result += "\n - Be as much specific as possible defining conditions to avoid ambiguity sending"; + result += "\n messages out of context due to race conditions. Although you could program several"; + result += "\n times similar conditions, some risky practices will throw a warning trace (if you"; + result += "\n repeat the same condition within the same test case)."; + result += "\n - Adding a ResultCode and/or HopByHop to the condition are only valid waiting answers."; + result += "\n - Requests hop-by-hop values must be different for all the test case requests."; + result += "\n RFC says that a hop by hop must be unique for a specific connection, something that"; + result += "\n could be difficult to manage if we have multiple available connections from client"; + result += "\n side endpoint (entity or local server), even if we would have only one connection but"; + result += "\n several realm interfaces. It is enough to configure different hop-by-hop values within"; + result += "\n each test case, because on reception, the Session-Id is used to identify that test case."; + result += "\n"; + result += "\n"; + result += "\n"; + result += "\n"; + result += "\n Programming example:"; + result += "\n"; + result += "\n Basic Rx/Gx scenary: PCEF (Gx) - PCRF - AF (Rx)"; + result += "\n"; + result += "\n test|1|timeout|5000 (step 1: whole time requirement is 5 seconds)"; + result += "\n test|1|sendxml2e|CCR-I.xml (step 2: imagine this xml uses the Session-Id 'SGx')"; + result += "\n test|1|waitfe|272|0|2001|SGx (step 3: waits the CCA for the CCR-I with Result-Code = DIAMETER_SUCCESS)"; + result += "\n test|1|sendxml2e|AAR-flows.xml (step 4: imagine this xml uses the Session-Id 'SRx')"; + result += "\n test|1|waitfe|265|0|2001|SRx (step 5: waits the AAA for the AAR-flows with Result-Code = DIAMETER_SUCCESS)"; + result += "\n test|1|waitfe|258|1||SGx (step 6: waits the RAR (install policies) from the PCRF server)"; + result += "\n test|1|sendxml2e|RAA-install.xml|6 (step 7: sends the response for the RAR)"; + result += "\n test|1|sendxml2e|CCR-T.xml (step 8: termination of the Gx session, imagine this xml puts hop-by-hop 'H1')"; + result += "\n test|1|waitfe|272|0|2001|SGx|H1 (step 9: waits the CCA for the CCR-T with Result-Code = DIAMETER_SUCCESS and hop-by-hop 'H1')"; + result += "\n test|1|waitfe|258|1||SGx (step 10: waits the RAR (remove policies) from the PCRF server)"; + result += "\n test|1|sendxml2e|RAA-remove.xml|10 (step 11: sends the response for the RAR)"; + result += "\n"; + result += "\n Notes: We added an additional condition in step 9: the hop-by-hop. When we program the corresponding"; + result += "\n source request (CCR-T), we configured the value 'H1' for the hop-by-hop. This is an 'application"; + result += "\n value' because the real hop-by-hop transported through the client connection is managed by the"; + result += "\n diameter stack. But when returned, the transaction pool resolve the original value. This feature"; + result += "\n is necessary to ease the implementation of certain diameter agents (proxies for example). In our"; + result += "\n case, we could format the hop-by-hop values within the request templates with total freedom to"; + result += "\n improve the programmed conditions."; + result += "\n"; + result += "\n In the case of 'waiting for requests' is not such easy. Indeed, steps 6 and 10 will write a warning"; + result += "\n because they are the same condition. We know that we are not going to have any problem because"; + result += "\n such events are blocking-protected regarding logic-dependent messages (CCR-T), and race condition"; + result += "\n is absolutely strange in this case."; + result += "\n"; + result += "\n You could speed up the test case moving forward steps like 3 & 5, understood as non-strict requirements"; + result += "\n to continue testing. Anyway, remember that test cases should be as real as possible, and that there"; + result += "\n are many ways to increase the load rate as we will see in next section (test cases execution)."; + result += "\n"; + result += "\n Other simplifications: the steps 3, 5 and 9 can be replaced by"; + result += "\n"; + result += "\n test|1|waitfe||0|||#2"; + result += "\n test|1|waitfe||0|||#4"; + result += "\n test|1|waitfe||0|||#8"; + result += "\n"; + result += "\n which means that hop-by-hop must be retrieved from steps 2, 4 and 8 respectively,"; + result += "\n and the expected message shall be an answer. Normally you will add other conditions,"; + result += "\n for example a DIAMETER_SUCCESS result (adding 2001 as Result-Code)."; + result += "\n"; + result += "\nTest cases execution:"; + result += "\n"; + result += "\n"; + result += "\n test|ttps| Starts/resume the provided number of test ticks per second (ttps). The ADML starts"; + result += "\n with the event trigger system suspended, and this operation is neccessary to begin"; + result += "\n those cases which need this time event (internal triggering). Some other test cases"; + result += "\n could be started through external events (first test case event could be programmed"; + result += "\n to wait specific message), but is not usual this external mode and neither usual to"; + result += "\n mix triggering types. Normally, you will pause/stop new test launchs providing 0 as"; + result += "\n ttps value, and also you could dynamically modify the load rate updating that value."; + result += "\n If a test case has N messages then 'ttps * N' will be the virtual number of messages"; + result += "\n managed per second when no bottleneck exists."; + result += "\n"; + result += "\n Provide 0 in order to stop the timer triggering."; + result += "\n"; + result += "\n There timer manager resolution currently harcoded allows a maximum of "; + result += anna::functions::asString(1000/a_admlMinResolution); result += " events"; + result += "\n per second. To reach greater rates ADML will join synchronously the needed number of"; + result += "\n new time-triggered test cases per a single event, writting a warning-level trace to"; + result += "\n advice about the risk of burst sendings and recommend launching multiple instances"; + result += "\n to achieve such load with a lower rate per instance."; + result += "\n"; + result += "\n test|ip-limit[|amount] In-progress limit of test cases. No new test cases will be launched over this value"; + result += "\n (test Manager tick work will be ignored). Zero-value is equivalent to stop the clock."; + result += "\n tick, -1 is used to specify 'no limit' which is the default. If missing amount, the"; + result += "\n limit and current amount of in-progress test cases will be shown."; + result += "\n"; + result += "\n test|goto| Updates current test pointer position."; + result += "\n"; + result += "\n test|look[|id] Show programmed test case for id provided, current when missing. Test cases programmed"; + result += "\n are not dumped on process context (too many information in general). When the test case"; + result += "\n has been completed or initiated, it will contain context information acting as a report."; + result += "\n"; + result += "\n test|report[|[yes]|no] Every time a test case is finished, its xml representation will be dump on a file under"; + result += "\n the execution directory (or the one configured in process command-line 'tmDir') with"; + result += "\n the name 'testcase..xml'. If repeat mode is active, new test case executions will"; + result += "\n append its reports into the same file. This option is disabled by default to improve"; + result += "\n performance (reducing IO). ADML process context will show test manager whole information"; + result += "\n and test case reports will be written depending on this report option. Anyway, you could"; + result += "\n use the 'look' operation to see the report."; + result += "\n"; + result += "\n test|reset|[|id] Reset the test case for id provided, all the tests when missing. It could be hard/soft:"; + result += "\n - hard: you probably may need to stop the load rate before. This operation initializes"; + result += "\n all test cases regardless their states."; + result += "\n - soft: only for finished cases (those with 'Success' or 'Failed' states). It does not"; + result += "\n affect to test cases with 'InProgress' state."; + result += "\n"; + result += "\n test|repeat[|[yes]|no] Restarts the whole programmed test list when finished, disabled by default (testing"; + result += "\n time trigger system will enter suspended state until new ttps operation is received)."; + result += "\n Test cases state & data will be reset (when achieved again), but general statistics"; + result += "\n and counters will continue measuring until reset with 'collect' operation."; + result += "\n When the test cases pool has been processed (and this repeat option is disabled), you"; + result += "\n could reactivate the testing by mean 'test|reset|soft' and then 'test|ttps|'."; + result += "\n"; + result += "\n test|clear Clears all the programmed test cases and stop testing (if in progress)."; result += "\n"; result += "\n"; result += "\nUSING OPERATIONS INTERFACE"; @@ -1009,11 +1247,18 @@ std::string Launcher::help() const throw() { void Launcher::eventOperation(const std::string &operation, std::string &response_content) throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("Launcher", "eventOperation", ANNA_FILE_LOCATION)); CommandLine& cl(anna::CommandLine::instantiate()); + TestManager &testManager = TestManager::instantiate(); LOGDEBUG(anna::Logger::debug(operation, ANNA_FILE_LOCATION)); - response_content = "Operation processed with exception. See traces\n"; // supposed - std::string result = ""; + + // Default response: + response_content = "Operation processed with exception (see traces): "; + response_content += operation; + response_content += "\n"; + + std::string result_msg = ""; anna::DataBlock db_aux(true); + /////////////////////////////////////////////////////////////////// // Simple operations without arguments: @@ -1022,7 +1267,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons std::string s_help = help(); std::cout << s_help << std::endl; LOGINFORMATION(anna::Logger::information(s_help, ANNA_FILE_LOCATION)); - response_content = "Help dumped on stdout and information-level traces (launcher.trace file)\n"; + response_content = "Help dumped on stdout and information-level traces\n"; return; } @@ -1044,18 +1289,41 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons /////////////////////////////////////////////////////////////////// // Tokenize operation Tokenizer params; - params.apply(operation, "|"); + params.apply(operation, "|", "" /* allow contiguous separators */); int numParams = params.size() - 1; - // No operation has more than 2 arguments ... - if(numParams > 2) { - LOGWARNING(anna::Logger::warning(help(), ANNA_FILE_LOCATION)); - throw anna::RuntimeException("Wrong body content format on HTTP Request", ANNA_FILE_LOCATION); - } - - // Get the operation type: + // Get the operation type and parameters: Tokenizer::const_iterator tok_iter = params.begin(); std::string opType = Tokenizer::data(tok_iter); + std::string param1, param2, param3, param4, param5, param6, param7, param8, param9, param10; + if(numParams >= 1) { tok_iter++; param1 = Tokenizer::data(tok_iter); } + if(numParams >= 2) { tok_iter++; param2 = Tokenizer::data(tok_iter); } + if(numParams >= 3) { tok_iter++; param3 = Tokenizer::data(tok_iter); } + // Tests conditions + if(numParams >= 4) { tok_iter++; param4 = Tokenizer::data(tok_iter); } + if(numParams >= 5) { tok_iter++; param5 = Tokenizer::data(tok_iter); } + if(numParams >= 6) { tok_iter++; param6 = Tokenizer::data(tok_iter); } + if(numParams >= 7) { tok_iter++; param7 = Tokenizer::data(tok_iter); } + if(numParams >= 8) { tok_iter++; param8 = Tokenizer::data(tok_iter); } + if(numParams >= 9) { tok_iter++; param9 = Tokenizer::data(tok_iter); } + if(numParams >= 10) { tok_iter++; param10 = Tokenizer::data(tok_iter); } + // Remove '' artificial token to ease further checkings: + if (param1 == "") param1 = ""; + if (param2 == "") param2 = ""; + if (param3 == "") param3 = ""; + if (param4 == "") param4 = ""; + if (param5 == "") param5 = ""; + if (param6 == "") param6 = ""; + if (param7 == "") param7 = ""; + if (param8 == "") param8 = ""; + if (param9 == "") param9 = ""; + if (param10 == "") param10 = ""; + + // No operation has more than 2 arguments except 'test' ... + if(opType != "test" && numParams > 2) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + // Check the number of parameters: bool wrongBody = false; @@ -1067,23 +1335,18 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons if((opType == "burst") && (numParams < 1)) wrongBody = true; + if((opType == "test") && (numParams < 1)) wrongBody = true; + if(((opType == "sendxml2c") || (opType == "sendhex2c") || (opType == "loadxml") || (opType == "diameterServerSessions")) && (numParams != 1)) wrongBody = true; if(wrongBody) { // Launch exception std::string msg = "Wrong body content format on HTTP Request for '"; msg += opType; - msg += "' operation (missing parameter/s)"; + msg += "' operation (missing parameter/s). Use 'help' management command to see more information."; throw anna::RuntimeException(msg, ANNA_FILE_LOCATION); } - // All seems ok: - std::string param1, param2; - - if(numParams >= 1) { tok_iter++; param1 = Tokenizer::data(tok_iter); } - - if(numParams == 2) { tok_iter++; param2 = Tokenizer::data(tok_iter); } - // Operations: if(opType == "context") { std::string contextFile = ((numParams == 1) ? param1 : anna::functions::asString("/var/tmp/anna.context.%05d", getPid())); @@ -1157,9 +1420,9 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons if(opType == "show") commEngine->findClientSession(key)->show(); - if(opType == "hidden") result = commEngine->findClientSession(key)->hidden() ? "true" : "false"; + if(opType == "hidden") result_msg = commEngine->findClientSession(key)->hidden() ? "true" : "false"; - if(opType == "shown") result = commEngine->findClientSession(key)->shown() ? "true" : "false"; + if(opType == "shown") result_msg = commEngine->findClientSession(key)->shown() ? "true" : "false"; } else { std::string address; int port; @@ -1169,18 +1432,18 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons if(opType == "show") commEngine->findServer(address, port)->show(); - if(opType == "hidden") result = commEngine->findServer(address, port)->hidden() ? "true" : "false"; + if(opType == "hidden") result_msg = commEngine->findServer(address, port)->hidden() ? "true" : "false"; - if(opType == "shown") result = commEngine->findServer(address, port)->shown() ? "true" : "false"; + if(opType == "shown") result_msg = commEngine->findServer(address, port)->shown() ? "true" : "false"; } } else { if(opType == "hide") entity->hide(); if(opType == "show") entity->show(); - if(opType == "hidden") result = entity->hidden() ? "true" : "false"; + if(opType == "hidden") result_msg = entity->hidden() ? "true" : "false"; - if(opType == "shown") result = entity->shown() ? "true" : "false"; + if(opType == "shown") result_msg = entity->shown() ? "true" : "false"; } } else if((opType == "sendxml") || (opType == "sendxml2e") || (opType == "sendhex") || (opType == "sendhex2e")) { if(!entity) throw anna::RuntimeException("No entity configured to send the message", ANNA_FILE_LOCATION); @@ -1224,12 +1487,12 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons // Externally we could control sending time (no request // will be sent for answers). // burst|goto| Updates current burst pointer position. - // burst|look| Show programmed burst message for order provided. + // burst|look| Show programmed burst message for order provided, current when missing. if(param1 == "clear") { - result = "Removed "; - result += anna::functions::asString(getWorkingNode()->clearBurst()); - result += " elements."; + result_msg = "removed "; + result_msg += anna::functions::asString(getWorkingNode()->clearBurst()); + result_msg += " elements"; } else if(param1 == "load") { if(param2 == "") throw anna::RuntimeException("Missing xml path file for burst load operation", ANNA_FILE_LOCATION); @@ -1239,10 +1502,10 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons try { codecMsg.valid(); } catch(anna::RuntimeException &ex) { ex.trace(); } // at least we need to see validation errors although it will continue loading (see validation mode configured in launcher) int position = getWorkingNode()->loadBurstMessage(codecMsg.code()); - result = "Loaded '"; - result += param2; - result += "' file into burst list position "; - result += anna::functions::asString(position); + result_msg = "loaded '"; + result_msg += param2; + result_msg += "' file into burst list position "; + result_msg += anna::functions::asString(position); } else if(param1 == "start") { if(param2 == "") throw anna::RuntimeException("Missing initial load for burst start operation", ANNA_FILE_LOCATION); @@ -1250,9 +1513,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons int processed = getWorkingNode()->startBurst(initialLoad); if(processed > 0) { - result = "Initial load completed for "; - result += anna::functions::entriesAsString(processed, "message"); - result += "."; + result_msg = "initial load completed for "; + result_msg += anna::functions::entriesAsString(processed, "message"); } } else if(param1 == "push") { if(param2 == "") throw anna::RuntimeException("Missing load amount for burst push operation", ANNA_FILE_LOCATION); @@ -1260,9 +1522,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons int pushed = getWorkingNode()->pushBurst(atoi(param2.c_str())); if(pushed > 0) { - result = "Pushed "; - result += anna::functions::entriesAsString(pushed, "message"); - result += "."; + result_msg = "pushed "; + result_msg += anna::functions::entriesAsString(pushed, "message"); } } else if(param1 == "pop") { if(param2 == "") throw anna::RuntimeException("Missing amount for burst pop operation", ANNA_FILE_LOCATION); @@ -1271,47 +1532,266 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons int popped = getWorkingNode()->popBurst(releaseLoad); if(popped > 0) { - result = "Burst popped for "; - result += anna::functions::entriesAsString(popped, "message"); - result += "."; + result_msg = "burst popped for "; + result_msg += anna::functions::entriesAsString(popped, "message"); } } else if(param1 == "stop") { int left = getWorkingNode()->stopBurst(); if(left != -1) { - result += anna::functions::entriesAsString(left, "message"); - result += " left to the end of the cycle."; + result_msg += anna::functions::entriesAsString(left, "message"); + result_msg += " left to the end of the cycle"; } } else if(param1 == "repeat") { if(param2 == "") param2 = "yes"; bool repeat = (param2 == "yes"); getWorkingNode()->repeatBurst(repeat); - result += (repeat ? "Mode on." : "Mode off."); + result_msg += (repeat ? "repeat enabled" : "repeat disabled"); } else if(param1 == "send") { if(param2 == "") throw anna::RuntimeException("Missing amount for burst send operation", ANNA_FILE_LOCATION); int sent = getWorkingNode()->sendBurst(atoi(param2.c_str())); if(sent > 0) { - result = "Sent "; - result += anna::functions::entriesAsString(sent, "message"); - result += "."; + result_msg = "sent "; + result_msg += anna::functions::entriesAsString(sent, "message"); } } else if(param1 == "goto") { if(param2 == "") throw anna::RuntimeException("Missing order position for burst goto operation", ANNA_FILE_LOCATION); - result = getWorkingNode()->gotoBurst(atoi(param2.c_str())); - result += "."; + result_msg = getWorkingNode()->gotoBurst(atoi(param2.c_str())); } else if(param1 == "look") { - if(param2 == "") throw anna::RuntimeException("Missing order position for burst look operation", ANNA_FILE_LOCATION); - - result = "\n\n"; - result += getWorkingNode()->lookBurst(atoi(param2.c_str())); - result += "\n\n"; + int order = ((param2 != "") ? atoi(param2.c_str()) : -1); + result_msg = "\n\n"; + result_msg += getWorkingNode()->lookBurst(order); } else { throw anna::RuntimeException("Wrong body content format on HTTP Request for 'burst' operation (unexpected action parameter). See help", ANNA_FILE_LOCATION); } + + } else if((opType == "test")) { + // test||[|parameters] Add a new step to the test case ... + // test|ttps| Starts/resume the provided number of time ticks per second (ttps). The ADML starts ... + // test|ip-limit[|amount] In-progress limit of test cases. No new test cases will be launched over this value ... + // test|repeat[|[yes]|no] Restarts the programmed test cases when finished. Disabled by default: the testing ... + // test|report[|[yes]|no] Every time a test case is finished a report file in xml format will be created under ... + // test|goto| Updates current test pointer position. + // test|look[|id] Show programmed test case for id provided, current when missing ... + // test|reset|[|id] Reset the test case for id provided, all the tests when missing ... + // test|clear Clears all the programmed test cases. + + if(param1 == "ttps") { + if (numParams > 2) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + bool success = ((param2 != "") ? testManager.configureTTPS(atoi(param2.c_str())) : false); + if (success) { + result_msg = "assigned new test launch rate to "; + result_msg += anna::functions::asString(atoi(param2.c_str())); + result_msg += " events per second"; + } + else { + result_msg += "unable to configure the test rate provided"; + } + } + else if(param1 == "ip-limit") { + if (numParams > 2) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + unsigned int limit; + if (param2 != "") { + limit = atoi(param2.c_str()); + testManager.setInProgressLimit(limit); + result_msg = "new in-progress limit: "; + result_msg += (limit != UINT_MAX) ? anna::functions::asString(limit) : ""; + } + else { + result_msg = "in-progress limit amount: "; + limit = testManager.getInProgressLimit(); + result_msg += (limit != UINT_MAX) ? anna::functions::asString(limit) : ""; + result_msg += "; currently there are "; + result_msg += anna::functions::asString(testManager.getInProgressCount()); + result_msg += " test cases running"; + } + } + else if(param1 == "repeat") { + if (numParams > 2) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + if(param2 == "") param2 = "yes"; + testManager.setPoolRepeat((param2 == "yes")); + result_msg += (testManager.getPoolRepeat() ? "repeat enabled" : "repeat disabled"); + } + else if(param1 == "report") { + if (numParams > 2) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + if(param2 == "") param2 = "yes"; + testManager.setDumpReports((param2 == "yes")); + result_msg += (testManager.getDumpReports() ? "report enabled" : "report disabled"); + } + else if(param1 == "goto") { + if (numParams > 2) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + if(param2 == "") throw anna::RuntimeException("Missing id for test goto operation", ANNA_FILE_LOCATION); + int id = atoi(param2.c_str()); + if (testManager.gotoTestCase(id)) { + result_msg = "position updated for id provided ("; + } + else { + result_msg = "cannot found test id ("; + } + result_msg += anna::functions::asString(id); + result_msg += ")"; + } + else if(param1 == "look") { + if (numParams > 2) + 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); + + if (testCase) { + result_msg = "\n\n"; + result_msg += testCase->asXMLString(); + } + else { + if (id == -1) { + result_msg = "no current test case detected (testing started ?)"; + } + else { + result_msg = "cannot found test id ("; + result_msg += anna::functions::asString(id); + result_msg += ")"; + } + } + } + else if(param1 == "reset") { + if (numParams > 3) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + if (param2 != "soft" && param2 != "hard") + 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); + + if (testCase) { + bool done = testCase->reset((param2 == "hard") ? true:false); + result_msg = "test "; + result_msg += param2; + result_msg += " reset for id "; + result_msg += anna::functions::asString(id); + result_msg += done ? ": done": ": not done"; + } + else { + if (id == -1) { + bool anyReset = testManager.resetPool((param2 == "hard") ? true:false); + result_msg = "reset have been sent to all programmed tests: "; result_msg += anyReset ? "some/all was actually reset" : "nothing was reset"; + } + else { + result_msg = "cannot found test id ("; + result_msg += anna::functions::asString(id); + result_msg += ")"; + } + } + } + else if(param1 == "clear") { + if (numParams > 1) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + + if (testManager.clearPool()) { + result_msg = "all the programmed test cases have been dropped"; + } + else { + result_msg = "there are not programmed test cases to be removed"; + } + } + else { + int id = atoi(param1.c_str()); + if(id < 0) + throw anna::RuntimeException("Invalid test case identifier: must be a non-negative number", ANNA_FILE_LOCATION); + + // PARAM: 1 2 3 4 5 6 7 8 9 10 + // test|| + // timeout| + // sendxml2e| [|] + // sendxml2c| [|] + // delay| [msecs] + // wait|[code]|[bitR]|[ResultCode]|[sessionId]|[hopByHop]|[msisdn]|[imsi]|[serviceContextId] + // wait-answer| + // wait-regexp| + if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION); + + // Commands: + if (param2 == "timeout") { + if (numParams > 3) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'timeout' command in test id operation", ANNA_FILE_LOCATION); + anna::Millisecond timeout = checkTimeMeasure("Test case timeout", param3); + testManager.getTestCase(id)->addTimeout(timeout); // creates / reuses + } + else if ((param2 == "sendxml2e")||(param2 == "sendxml2c")) { + if (numParams > 4) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing xml file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION); + codecMsg.loadXML(param3); + if (codecMsg.isRequest()) { + if (param4 != "") + throw anna::RuntimeException("Step number is provided with answers (to resolve the corresponding 'wait for request' step), but NOT with requests", ANNA_FILE_LOCATION); + } + else { + if (param4 == "") LOGWARNING(anna::Logger::warning("Step number has not been provided. Take into account that this answer message will be sent 'as is' and sequence information could be wrong at the remote peer", ANNA_FILE_LOCATION)); + } + int stepNumber = ((param4 != "") ? atoi(param4.c_str()):-1); + std::string originRealm = codecMsg.getAvp(anna::diameter::helpers::base::AVPID__Origin_Realm)->getDiameterIdentity()->getValue(); + RealmNode *realm = getRealmNode(originRealm); + if (!realm) + throw anna::RuntimeException("Cannot identify the realm node for the manager message. Check the Origin-Realm avp value (use the realm node name)", ANNA_FILE_LOCATION); + + if (param2 == "sendxml2e") + testManager.getTestCase(id)->addSendxml2e(codecMsg.code(), realm, stepNumber); // creates / reuses + else + testManager.getTestCase(id)->addSendxml2c(codecMsg.code(), realm, stepNumber); // creates / reuses + } + else if (param2 == "delay") { + if (numParams > 3) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + if(param3 == "") throw anna::RuntimeException("Missing milliseconds for 'delay' command in test id operation", ANNA_FILE_LOCATION); + anna::Millisecond delay = checkTimeMeasure("Test case delay step", param3); + testManager.getTestCase(id)->addDelay(delay); // creates / reuses + } + else if ((param2 == "waitfe")||(param2 == "waitfc")) { + if (numParams > 10) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + if (param3 != "" || param4 != "" || param5 != "" || param6 != "" || param7 != "" || param8 != "" || param9 != "" || param10 != "") { + bool fromEntity = (param2.substr(4,2) == "fe"); + testManager.getTestCase(id)->addWait(fromEntity, param3, param4, param5, param6, param7, param8, param9, param10); + } + else { + throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION); + } + } + else if ((param2 == "waitfe-regexp")||(param2 == "waitfc-regexp")) { + if (numParams > 3) + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + if (param3 != "") { + bool fromEntity = (param2.substr(4,2) == "fe"); + testManager.getTestCase(id)->addWaitRegexp(fromEntity, param3); + } + else { + throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION); + } + } + else { + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); + } + + result_msg = "new step added to test id "; + result_msg += anna::functions::asString(id); + } + } else if((opType == "sendxml2c") || (opType == "sendhex2c")) { if(!localServer) throw anna::RuntimeException("No local server configured to send the message", ANNA_FILE_LOCATION); anna::diameter::comm::Message *msg = getWorkingNode()->createCommMessage(); @@ -1412,75 +1892,12 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons entity->getReactingAnswers()->addMessage(code, message); } } else { - LOGWARNING(anna::Logger::warning(help(), ANNA_FILE_LOCATION)); - throw anna::RuntimeException("Wrong body content format on HTTP Request. Unsupported/unrecognized operation type", ANNA_FILE_LOCATION); + throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION); } // HTTP response - response_content = "Operation processed; "; - - if((opType == "decode") || (opType == "code")) { - response_content += "File '"; - response_content += param2; - response_content += "' created."; - response_content += "\n"; - } else if((opType == "hide") || (opType == "show")) { - response_content += "Resource '"; - response_content += ((param1 != "") ? param1 : "Entity"); - - if(param2 != "") { - response_content += "|"; - response_content += param2; - } - - response_content += "' "; - - if(opType == "hide") response_content += "has been hidden."; - - if(opType == "show") response_content += "has been shown."; - - response_content += "\n"; - } else if((opType == "hidden") || (opType == "shown")) { - response_content += "Result: "; - response_content += result; - response_content += "\n"; - } else if((opType == "sendxml") || (opType == "sendxml2e") || (opType == "sendhex") || (opType == "sendhex2e")) { - response_content += "Message '"; - response_content += param1; - response_content += "' sent to entity."; - response_content += "\n"; - } else if(opType == "burst") { - response_content += "Burst '"; - response_content += param1; - response_content += "' executed. "; - response_content += result; - response_content += "\n"; - } else if((opType == "sendxml2c") || (opType == "sendhex2c")) { - response_content += "Message '"; - response_content += param1; - response_content += "' sent to client."; - response_content += "\n"; - } else if(opType == "loadxml") { - response_content += "Message '"; - response_content += param1; - response_content += "' loaded."; - response_content += "\n"; - } else if((opType == "answerxml") || (opType == "answerxml2c")) { - response_content += "'"; - response_content += param1; - response_content += "' applied on server FIFO queue"; - response_content += "\n"; - } else if(opType == "answerxml2e") { - response_content += "'"; - response_content += param1; - response_content += "' applied on client FIFO queue"; - response_content += "\n"; - } else if(opType == "diameterServerSessions") { - response_content += "Maximum server socket connections updated to '"; - response_content += param1; - response_content += "'."; - response_content += "\n"; - } + response_content = "Operation correctly processed: "; response_content += operation; response_content += " => "; + response_content += result_msg; } anna::xml::Node* Launcher::asXML(anna::xml::Node* parent) const @@ -1502,5 +1919,9 @@ throw() { anna::diameter::codec::OamModule::instantiate().asXML(result); // Statistics: anna::statistics::Engine::instantiate().asXML(result); + + // Testing: could be heavy if test case reports are enabled + TestManager::instantiate().asXML(result); + return result; } diff --git a/example/diameter/launcher/Launcher.hpp b/example/diameter/launcher/Launcher.hpp index c8417fb..21c3d25 100644 --- a/example/diameter/launcher/Launcher.hpp +++ b/example/diameter/launcher/Launcher.hpp @@ -20,8 +20,9 @@ #include // Process -#include "MyCommunicator.hpp" -#include "MyCounterRecorder.hpp" +#include +#include +#include namespace anna { @@ -35,6 +36,8 @@ namespace anna { } } +class TestManager; + // RealmNode resources class RealmNode; typedef std::map realm_nodes_t; @@ -52,6 +55,7 @@ class Launcher : public anna::comm::Application { anna::diameter::codec::Engine *a_codecEngine; anna::timex::Engine* a_timeEngine; MyCounterRecorder *a_counterRecorder; + anna::Millisecond a_admlMinResolution; MyCounterRecorderClock *a_counterRecorderClock; // Nodes deployment: @@ -61,13 +65,13 @@ class Launcher : public anna::comm::Application { // comm resources: anna::comm::ServerSocket* a_httpServerSocket; // HTTP + const anna::Millisecond &getADMLMinResolution() const throw() { return a_admlMinResolution; } void servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException); anna::Millisecond checkTimeMeasure(const std::string ¶meter, const std::string &value) throw(anna::RuntimeException); void initialize() throw(anna::RuntimeException); // HTTP void run() throw(anna::RuntimeException); - public: Launcher(); @@ -94,6 +98,8 @@ public: // helpers bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw(); + + friend class TestManager; }; #endif diff --git a/example/diameter/launcher/MyCommunicator.cpp b/example/diameter/launcher/MyCommunicator.cpp index 6a85871..d3c1370 100644 --- a/example/diameter/launcher/MyCommunicator.cpp +++ b/example/diameter/launcher/MyCommunicator.cpp @@ -12,7 +12,7 @@ #include // Process -#include "MyCommunicator.hpp" +#include void MyCommunicator::prepareAnswer(anna::diameter::codec::Message *answer, const anna::DataBlock &request) const throw() { diff --git a/example/diameter/launcher/MyCommunicator.hpp b/example/diameter/launcher/MyCommunicator.hpp index d336c7d..4b2677c 100644 --- a/example/diameter/launcher/MyCommunicator.hpp +++ b/example/diameter/launcher/MyCommunicator.hpp @@ -15,7 +15,7 @@ #include // Process -#include "MyHandler.hpp" +#include class MyCommunicator : public anna::comm::Communicator { diff --git a/example/diameter/launcher/MyCounterRecorder.cpp b/example/diameter/launcher/MyCounterRecorder.cpp index 4f62f72..dd32652 100644 --- a/example/diameter/launcher/MyCounterRecorder.cpp +++ b/example/diameter/launcher/MyCounterRecorder.cpp @@ -7,7 +7,7 @@ // Process -#include "MyCounterRecorder.hpp" +#include MyCounterRecorder::MyCounterRecorder(const std::string &fnp) : a_stream(-1), a_fileNamePrefix(fnp) { diff --git a/example/diameter/launcher/MyCounterRecorder.hpp b/example/diameter/launcher/MyCounterRecorder.hpp index d283125..708c408 100644 --- a/example/diameter/launcher/MyCounterRecorder.hpp +++ b/example/diameter/launcher/MyCounterRecorder.hpp @@ -15,7 +15,7 @@ #include // Process -#include "MyCounterRecorderClock.hpp" +#include class MyCounterRecorder : public anna::oam::CounterRecorder { diff --git a/example/diameter/launcher/MyDiameterEngine.hpp b/example/diameter/launcher/MyDiameterEngine.hpp index 13aae6c..29b3c1b 100644 --- a/example/diameter/launcher/MyDiameterEngine.hpp +++ b/example/diameter/launcher/MyDiameterEngine.hpp @@ -13,8 +13,8 @@ #include // Process -#include "MyDiameterEntity.hpp" -#include "MyLocalServer.hpp" +#include +#include class MyDiameterEngine : public anna::diameter::comm::Engine { diff --git a/example/diameter/launcher/MyDiameterEntity.cpp b/example/diameter/launcher/MyDiameterEntity.cpp index ad47b6f..50a9663 100644 --- a/example/diameter/launcher/MyDiameterEntity.cpp +++ b/example/diameter/launcher/MyDiameterEntity.cpp @@ -16,11 +16,12 @@ #include // Process -#include "MyDiameterEngine.hpp" -#include "MyDiameterEntity.hpp" -#include "MyLocalServer.hpp" -#include "Launcher.hpp" -#include "RealmNode.hpp" +#include +#include +#include +#include +#include +#include void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::ClientSession* clientSession, anna::diameter::comm::Message *request) throw() { @@ -99,6 +100,10 @@ throw(anna::RuntimeException) { // Performance stats: Launcher& my_app = static_cast (anna::app::functions::getApp()); RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm()); + + // Testing: + TestManager::instantiate().receiveMessage(message, clientSession); + // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message); LOGDEBUG @@ -121,12 +126,12 @@ throw(anna::RuntimeException) { if (answer_message) { // Prepare answer: my_app.getCommunicator()->prepareAnswer(answer_message, message); + anna::diameter::comm::Message *msg; try { - anna::diameter::comm::Message *msg = my_node->createCommMessage(); + msg = my_node->createCommMessage(); msg->setBody(answer_message->code()); /* response = NULL =*/clientSession->send(msg); - my_node->releaseCommMessage(msg); if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "sent2e", clientSession->asString()); } catch(anna::RuntimeException &ex) { @@ -135,6 +140,9 @@ throw(anna::RuntimeException) { if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "send2eError", clientSession->asString()); } + // release msg + my_node->releaseCommMessage(msg); + // Pop front the reacting answer: a_reactingAnswers.nextMessage(code); return; @@ -186,6 +194,10 @@ throw(anna::RuntimeException) { bool contextExpired = (result == anna::diameter::comm::Response::ResultCode::Timeout); bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable); bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success); + + // Testing: + TestManager::instantiate().receiveMessage(*message, clientSession); + // CommandId: anna::diameter::CommandId request_cid = request->getCommandId(); LOGDEBUG @@ -233,13 +245,13 @@ throw(anna::RuntimeException) { MyLocalServer *localServer = my_node->getDiameterServer(); if(localServer && (request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CEA */) { + anna::diameter::comm::Message *msg; + try { - anna::diameter::comm::Message *msg = my_node->createCommMessage(); + msg = my_node->createCommMessage(); msg->forwardEndToEnd(); // end-to-end will be kept msg->setBody(*message); bool success = localServer->send(msg, request->getRequestServerSessionKey()); - my_node->releaseCommMessage(msg); - my_node->releaseCommMessage(request); // Detailed log: anna::diameter::comm::ServerSession *usedServerSession = my_node->getMyDiameterEngine()->findServerSession(request->getRequestServerSessionKey()); @@ -251,6 +263,10 @@ throw(anna::RuntimeException) { } catch(anna::RuntimeException &ex) { ex.trace(); } + + // release msgs + my_node->releaseCommMessage(msg); + my_node->releaseCommMessage(request); } } diff --git a/example/diameter/launcher/MyDiameterEntity.hpp b/example/diameter/launcher/MyDiameterEntity.hpp index a9ad2c4..9476883 100644 --- a/example/diameter/launcher/MyDiameterEntity.hpp +++ b/example/diameter/launcher/MyDiameterEntity.hpp @@ -13,7 +13,7 @@ #include // Process -#include "ProgrammedAnswers.hpp" +#include namespace anna { namespace diameter { diff --git a/example/diameter/launcher/MyHandler.cpp b/example/diameter/launcher/MyHandler.cpp index f35c89f..9f893a7 100644 --- a/example/diameter/launcher/MyHandler.cpp +++ b/example/diameter/launcher/MyHandler.cpp @@ -13,8 +13,8 @@ #include // Process -#include "MyHandler.hpp" -#include "Launcher.hpp" +#include +#include void MyHandler::evRequest(anna::comm::ClientSocket& clientSocket, const anna::http::Request& request) diff --git a/example/diameter/launcher/MyLocalServer.cpp b/example/diameter/launcher/MyLocalServer.cpp index 4bc8eba..8127d84 100644 --- a/example/diameter/launcher/MyLocalServer.cpp +++ b/example/diameter/launcher/MyLocalServer.cpp @@ -16,11 +16,12 @@ #include // Process -#include "MyLocalServer.hpp" -#include "MyDiameterEngine.hpp" -#include "MyDiameterEntity.hpp" -#include "Launcher.hpp" -#include "RealmNode.hpp" +#include +#include +#include +#include +#include +#include void MyLocalServer::eventRequest(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message) @@ -30,6 +31,9 @@ throw(anna::RuntimeException) { Launcher& my_app = static_cast (anna::app::functions::getApp()); RealmNode * my_node = my_app.getRealmNode(getEngine()->getRealm()); + // Testing: + TestManager::instantiate().receiveMessage(message, serverSession); + // CommandId: anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message); LOGDEBUG @@ -102,11 +106,11 @@ throw(anna::RuntimeException) { if(!analysisOK) a_codecEngine->setValidationMode(anna::diameter::codec::Engine::ValidationMode::Never); + anna::diameter::comm::Message *msg; try { - anna::diameter::comm::Message *msg = my_node->createCommMessage(); + msg = my_node->createCommMessage(); msg->setBody(answer_message->code()); /* response = NULL =*/serverSession->send(msg); - my_node->releaseCommMessage(msg); if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "sent2c", serverSession->asString()); } catch(anna::RuntimeException &ex) { @@ -114,6 +118,9 @@ throw(anna::RuntimeException) { if(my_node->logEnabled()) my_node->writeLogFile(*answer_message, "send2cError", serverSession->asString()); } + // release msg + my_node->releaseCommMessage(msg); + // Restore validation mode a_codecEngine->setValidationMode(backupVM); @@ -136,6 +143,10 @@ throw(anna::RuntimeException) { bool contextExpired = (result == anna::diameter::comm::Response::ResultCode::Timeout); bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable); bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success); + + // Testing: + TestManager::instantiate().receiveMessage(*message, serverSession); + // CommandId: anna::diameter::CommandId request_cid = request->getCommandId(); LOGDEBUG @@ -183,9 +194,10 @@ throw(anna::RuntimeException) { std::string detail; if(my_node->logEnabled()) detail = usedClientSession ? usedClientSession->asString() : ""; // this should not happen + anna::diameter::comm::Message *msg; try { - anna::diameter::comm::Message *msg = my_node->createCommMessage(); + msg = my_node->createCommMessage(); msg->forwardEndToEnd(); // end-to-end will be kept msg->setBody(*message); @@ -196,14 +208,15 @@ throw(anna::RuntimeException) { //msg->setRequestClientSessionKey(request->getRequestClientSessionKey()); //bool success = entity->send(msg); - my_node->releaseCommMessage(msg); - my_node->releaseCommMessage(request); - if(my_node->logEnabled()) my_node->writeLogFile(*message, "fwd2e", detail); // forwarded } catch(anna::RuntimeException &ex) { ex.trace(); if(my_node->logEnabled()) my_node->writeLogFile(*message, "fwd2eError", detail); // forwarded } + + // release msgs + my_node->releaseCommMessage(msg); + my_node->releaseCommMessage(request); } } } diff --git a/example/diameter/launcher/MyLocalServer.hpp b/example/diameter/launcher/MyLocalServer.hpp index c90d8f2..da4ed97 100644 --- a/example/diameter/launcher/MyLocalServer.hpp +++ b/example/diameter/launcher/MyLocalServer.hpp @@ -13,7 +13,7 @@ #include // Process -#include "ProgrammedAnswers.hpp" +#include namespace anna { namespace diameter { diff --git a/example/diameter/launcher/ProgrammedAnswers.cpp b/example/diameter/launcher/ProgrammedAnswers.cpp index 8e310d7..9356dac 100644 --- a/example/diameter/launcher/ProgrammedAnswers.cpp +++ b/example/diameter/launcher/ProgrammedAnswers.cpp @@ -14,7 +14,7 @@ #include // Process -#include "ProgrammedAnswers.hpp" +#include void ProgrammedAnswers::clear () throw() { diff --git a/example/diameter/launcher/RealmNode.cpp b/example/diameter/launcher/RealmNode.cpp index c61bbe4..630ab8f 100644 --- a/example/diameter/launcher/RealmNode.cpp +++ b/example/diameter/launcher/RealmNode.cpp @@ -16,8 +16,8 @@ #include // Process -#include "RealmNode.hpp" -#include "MyDiameterEngine.hpp" +#include +#include @@ -365,6 +365,9 @@ bool RealmNode::sendBurstMessage(bool anyway) throw() { } std::string RealmNode::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 += ")"; diff --git a/example/diameter/launcher/RealmNode.hpp b/example/diameter/launcher/RealmNode.hpp index cf2af40..a8a49fd 100644 --- a/example/diameter/launcher/RealmNode.hpp +++ b/example/diameter/launcher/RealmNode.hpp @@ -77,8 +77,6 @@ public: RealmNode(const std::string &originRealm, unsigned int applicationId, anna::diameter::codec::Engine *codecEngine); ~RealmNode() {;} - std::string asString() const throw(); - // Core resources: MyDiameterEngine* getMyDiameterEngine() const throw() { return a_commEngine; } anna::diameter::codec::Engine *getCodecEngine() const throw() { return a_codecEngine; } @@ -116,7 +114,7 @@ public: 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) const 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(); diff --git a/example/diameter/launcher/SConscript b/example/diameter/launcher/SConscript index c0dc64d..249a878 100644 --- a/example/diameter/launcher/SConscript +++ b/example/diameter/launcher/SConscript @@ -1,3 +1,4 @@ +import os Import ('env') # Process ################################################################# @@ -32,7 +33,12 @@ localEnv.MergeFlags (system_library) localEnv.Append(LIBPATH = anna_libpaths) # Linking ################################################################# -result = localEnv.Program (pName, Glob ('*.cpp')) +# Process includes (avoid mandatory using of quoted includes and ../ paths from testing directory): +current_directory = Dir ('.').abspath +testing_include = os.path.join (current_directory, "testing") +localEnv.Append (CPPPATH = [current_directory, testing_include]) + +result = localEnv.Program (pName, Glob ('*.cpp') + Glob ('testing/*.cpp')) Return ('result') diff --git a/example/diameter/launcher/deployments/basic/configure.sh b/example/diameter/launcher/deployments/basic/configure.sh index 07ee3b1..7a3705b 100755 --- a/example/diameter/launcher/deployments/basic/configure.sh +++ b/example/diameter/launcher/deployments/basic/configure.sh @@ -27,7 +27,7 @@ cd \`dirname \$0\` STARTED=\`pgrep $1 2>/dev/null\` [ \$? -eq 0 ] && { echo "Already started!"; echo "\$STARTED" ; exit 1 ; } 0> launcher.trace -rm -f counters/* +rm -f counters/* test-reports/* # Execution line: ./$@ --services services.xml & echo \$! > .pid @@ -62,7 +62,7 @@ tol=$(get_tol $option) ln -sf services/${tol}.xml services.xml # Tracing -DEBUG="--cntDir counters" +DEBUG="--cntDir counters --tmDir test-reports" echo echo "Enable debug traces ? (y/n) [n]:" read enable diff --git a/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar-bad.hex b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar-bad.hex new file mode 120000 index 0000000..97766e8 --- /dev/null +++ b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar-bad.hex @@ -0,0 +1 @@ +../../../hex_examples/aar-bad.hex \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar2-bad.hex b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar2-bad.hex new file mode 120000 index 0000000..7c39b1a --- /dev/null +++ b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar2-bad.hex @@ -0,0 +1 @@ +../../../hex_examples/aar2-bad.hex \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar3-bad.hex b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar3-bad.hex new file mode 120000 index 0000000..5ba5f5d --- /dev/null +++ b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar3-bad.hex @@ -0,0 +1 @@ +../../../hex_examples/aar3-bad.hex \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_1.tc b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_1.tc new file mode 100644 index 0000000..693bc94 --- /dev/null +++ b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_1.tc @@ -0,0 +1,11 @@ +# Bad length encoded on grouped Subscription-Id length field (not multiple of 4). + +# Send malformed AAR to the server: +SENDHEX2E aar-bad.hex + +# Wait for message: +WAIT4MESSAGE + +# Check Failed-AVP & Subscription-Id within: +CHECKPATTERN +CHECKPATTERN ( *) Media-Sub-Component -> Flow-Status) + +# Send malformed AAR to the server: +SENDHEX2E aar2-bad.hex + +# Wait for message: +WAIT4MESSAGE + +# Check Failed-AVP & Subscription-Id within: +CHECKPATTERN +CHECKPATTERN ( *) Media-Sub-Component -> Flow-Status) + +# Send malformed AAR to the server: +SENDHEX2E aar3-bad.hex + +# Wait for message: +WAIT4MESSAGE + +# Check Failed-AVP & Subscription-Id within: +CHECKPATTERN +CHECKPATTERN ( *)( *)( *) diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment1/case_1.sh b/example/diameter/launcher/deployments/ft-client/tests/experiment1/case_1.sh deleted file mode 100755 index c610b4d..0000000 --- a/example/diameter/launcher/deployments/ft-client/tests/experiment1/case_1.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -# In this test we will send a incorrect message (AAR) to the server peer: -# Warning | source/diameter/codec/Message.cpp (387) | Internal Avp decoding error (avp code = 443): Avp format error, the avp length is incorrect (must be multiple of 4 on grouped type) -# -# The server shall answer with the FailedAVP and we will check this to validate the test. - -############# -# VARIABLES # -############# -# Paths from the point of view of ADML executable: -REQ1_HEX=./hex_examples/aar-bad.hex - -############# -# FUNCTIONS # -############# - - -############# -# EXECUTION # -############# - -# Goto working directory -cd `dirname $0` - -# Source utils: -source common.sh - -# Clean traffic traces: -rm -f ../../launcher.log* - -# Send incorrect AAR: -send_hex $REQ1_HEX & - -# Monitor activity: -monitor_4_message ../../launcher.log.recvfe 10 - -# Check if message was received: -[ $? -ne 0 ] && test_failed "Missing answer from the server" - -# Check launcher.log.recvfe: -check_pattern "" ../../launcher.log.recvfe -check_pattern "( *)/dev/null - res=${PIPESTATUS[2]} - [ $? -ne 0 ] && _exit "Test failed: miss pattern !!" -} - -test_ok () { - echo - echo $1 - echo "Test OK !" - echo - exit 0 -} - -test_failed () { - echo - echo $1 - echo "Test FAILED !" - echo - exit 1 -} - - -# $1: traffic log to monitor -# $2: timeout (request expiration) in seconds -# Result: cats the message when completed -monitor_4_message () { - 0>$1 - rm -f .msg_received - - [ -z "$2" ] && _exit "ERROR: must provide '$FUNCNAME' timeout (second argument)" - sleep $2 & - local timerPid=$! - - # Monitor for incoming message: - tail -n0 -F --pid=$timerPid $1 | while read line - do - if echo $line | grep "^" >/dev/null; then - echo "Message received" - # stop the timer - kill -13 $timerPid - touch .msg_received - fi - done - - if [ ! -f .msg_received ]; then - echo "Timeout expired" - return 1 - fi - - return 0 -} - -############# -# EXECUTION # -############# - -# Trap sigint signal: -trap sigint_handler SIGINT - diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar-bad.hex b/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar-bad.hex deleted file mode 120000 index 4c9a36f..0000000 --- a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar-bad.hex +++ /dev/null @@ -1 +0,0 @@ -../../../../hex_examples/aar-bad.hex \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar2-bad.hex b/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar2-bad.hex deleted file mode 120000 index 5a6586d..0000000 --- a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar2-bad.hex +++ /dev/null @@ -1 +0,0 @@ -../../../../hex_examples/aar2-bad.hex \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar3-bad.hex b/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar3-bad.hex deleted file mode 120000 index 6705cc4..0000000 --- a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar3-bad.hex +++ /dev/null @@ -1 +0,0 @@ -../../../../hex_examples/aar3-bad.hex \ No newline at end of file diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_1.tc b/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_1.tc deleted file mode 100644 index 693bc94..0000000 --- a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_1.tc +++ /dev/null @@ -1,11 +0,0 @@ -# Bad length encoded on grouped Subscription-Id length field (not multiple of 4). - -# Send malformed AAR to the server: -SENDHEX2E aar-bad.hex - -# Wait for message: -WAIT4MESSAGE - -# Check Failed-AVP & Subscription-Id within: -CHECKPATTERN -CHECKPATTERN ( *) Media-Sub-Component -> Flow-Status) - -# Send malformed AAR to the server: -SENDHEX2E aar2-bad.hex - -# Wait for message: -WAIT4MESSAGE - -# Check Failed-AVP & Subscription-Id within: -CHECKPATTERN -CHECKPATTERN ( *) Media-Sub-Component -> Flow-Status) - -# Send malformed AAR to the server: -SENDHEX2E aar3-bad.hex - -# Wait for message: -WAIT4MESSAGE - -# Check Failed-AVP & Subscription-Id within: -CHECKPATTERN -CHECKPATTERN ( *)( *)( *) diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment2/go.sh b/example/diameter/launcher/deployments/ft-client/tests/experiment2/go.sh deleted file mode 100755 index 44bd53f..0000000 --- a/example/diameter/launcher/deployments/ft-client/tests/experiment2/go.sh +++ /dev/null @@ -1,194 +0,0 @@ -#!/bin/bash - -############# -# VARIABLES # -############# -SCR_DIR=`dirname $0` -tmpdir=$(mktemp -d) -SCR_BN=`basename $0` -OPER_SCR=../../operation.sh -RESULT_LOG=result.log -RECV_LOG=../../launcher.log.recvfe - -############# -# FUNCTIONS # -############# - -sigint_handler () { - _exit "Script interrupted. Cleanup and exit ..." -} - -# $1: message; $2: optional rc (1 by default) -_exit () { - rc=1 - [ -n "$2" ] && rc=$2 - echo - echo -e $1 - echo - rm -rf $tmpdir - exit $rc -} - -# $1: hex formatted file to send to the server -operation_sendxml2e () { - sleep 0.5 - $OPER_SCR "sendxml2e|$1" - return $? -} - -# $1: hex formatted file to send to the server -operation_sendhex2e () { - sleep 0.5 - $OPER_SCR "sendhex2e|$1" - return $? -} - -# $1: file to monitor -# $2: timeout (request expiration) in seconds -# Result: cats the message when completed -wait4message () { - 0>$1 - rm -f .msg_received - - [ -z "$2" ] && _exit "ERROR: must provide '$FUNCNAME' timeout (second argument)" - sleep $2 & - local timerPid=$! - - # Monitor for incoming message: - tail -n0 -F --pid=$timerPid $1 | while read line - do - if echo $line | grep "^" >/dev/null; then - echo "Message received:" - echo - cat $1 - # stop the timer - kill -13 $timerPid - touch .msg_received - fi - done - - if [ ! -f .msg_received ]; then - echo "Timeout expired" - return 1 - fi - - return 0 -} - -# xml content is serialized (removing CR's) to ease the pattern matching: -# $1: pattern; $2: file -check_pattern () { - echo -n "Matching pattern '$1' in file '$2' ... " - cat $2 | tr '\n' ' ' | egrep "$1" >/dev/null - [ $? -ne 0 ] && { echo "ERROR" ; test_failed ; return 1 ; } - echo "OK" - return 0 -} - -# $1: pattern file; $2: file -check_pattern_file () { - echo -n "Matching whole file '$1' in file '$2' ... " - grep -f $1 $2 >/dev/null - [ $? -ne 0 ] && { echo "ERROR" ; test_failed ; return 1 ; } - echo "OK" - return 0 -} - -# Tests result: -test_ok () { - echo "Verdict: $TC Ok" | tee -a $RESULT_LOG -} - -test_failed () { - echo "Verdict: $TC Failed" | tee -a $RESULT_LOG -} - -process_tc () { - local tc_dn=`dirname $TC` - - echo - echo "***** Executing '$TC' ..." - - grep -v ^# $TC > $tmpdir/tc - while read -r line - do - oper=$(echo $line | awk '{ print $1 }') - param1="$(echo $line | cut -d' ' -f2-)" - - case $oper in - SENDXML2E) - operation_sendxml2e $tc_dn/$param1 & - [ $? -ne 0 ] && return 1 - ;; - - SENDHEX2E) - operation_sendhex2e $tc_dn/$param1 & - [ $? -ne 0 ] && return 1 - ;; - - WAIT4MESSAGE) - wait4message $RECV_LOG 5 - [ $? -ne 0 ] && return 1 - ;; - - CHECKPATTERN) - check_pattern "$param1" $RECV_LOG - [ $? -ne 0 ] && return 1 - ;; - - CHECKPATTERNFILE) - check_pattern_file "$param1" $RECV_LOG - [ $? -ne 0 ] && return 1 - ;; - - esac - - done < $tmpdir/tc - - return 0 -} - - -############# -# EXECUTION # -############# - -# Trap sigint: -trap sigint_handler SIGINT - -# Working directory is script dirname: -cd $SCR_DIR - -# Intro: -echo -echo "------------------" -echo "Test cases manager" -echo "------------------" -# Mandatory parameter: -[ -z "$1" ] && _exit "Usage: $SCR_BN " -[ ! -f "$1" -a ! -d "$1" ] && _exit "Invalid file or directory '$1' !!" - -# Gather .tc files to be processed: -[ ! -f $1 ] && echo -e "\nGathering list of test cases from '`readlink -f $1`' ..." -echo -find $1 -name *.tc | xargs -L1 readlink -f | tee -a $tmpdir/tc_list -echo -echo - -echo "Start processing test cases:" -echo "----------------------------" -# Process test cases: -0> $RESULT_LOG -for TC in `cat $tmpdir/tc_list` -do - process_tc - if [ $? -eq 0 ]; then - test_ok - else - test_failed - cat $RECV_LOG - fi -done - -_exit "Done !" 0 - diff --git a/example/diameter/launcher/deployments/ft-client/tests/go.sh b/example/diameter/launcher/deployments/ft-client/tests/go.sh new file mode 100755 index 0000000..0bf18a2 --- /dev/null +++ b/example/diameter/launcher/deployments/ft-client/tests/go.sh @@ -0,0 +1,202 @@ +#!/bin/bash + +############# +# VARIABLES # +############# +SCR_DIR=`dirname $0` +tmpdir=$(mktemp -d) +SCR_BN=`basename $0` +OPER_SCR=../operation.sh +RESULT_LOG=result.log +realm=`cat ../services.xml | grep originRealm | awk -F'originRealm=' '{ print $2 }' | cut -d\" -f2` +RECV_LOG=../${realm}.launcher.log.recvfe + +############# +# FUNCTIONS # +############# + +sigint_handler () { + _exit "Script interrupted. Cleanup and exit ..." +} + +# $1: message; $2: optional rc (1 by default) +_exit () { + rc=1 + [ -n "$2" ] && rc=$2 + echo + echo -e $1 + echo + rm -rf $tmpdir + exit $rc +} + +# $1: hex formatted file to send to the server +operation_sendxml2e () { + sleep 0.5 + $OPER_SCR "sendxml2e|$1" + return $? +} + +# $1: hex formatted file to send to the server +operation_sendhex2e () { + sleep 0.5 + $OPER_SCR "sendhex2e|$1" + return $? +} + +# $1: file to monitor +# $2: timeout (request expiration) in seconds +# Result: cats the message when completed +wait4message () { + 0>$1 + rm -f .msg_received + + [ -z "$2" ] && _exit "ERROR: must provide '$FUNCNAME' timeout (second argument)" + sleep $2 & + local timerPid=$! + + # Monitor for incoming message: + tail -n0 -F --pid=$timerPid $1 | while read line + do + if echo $line | grep "^" >/dev/null; then + echo "Message received:" + echo + cat $1 + # stop the timer + kill -13 $timerPid + touch .msg_received + fi + done + + if [ ! -f .msg_received ]; then + echo "Timeout expired" + return 1 + fi + + return 0 +} + +# xml content is serialized (removing CR's) to ease the pattern matching: +# $1: pattern; $2: file +check_pattern () { + echo -n "Matching pattern '$1' in file '$2' ... " + cat $2 | tr '\n' ' ' | egrep "$1" >/dev/null + [ $? -ne 0 ] && { echo "ERROR" ; test_failed ; return 1 ; } + echo "OK" + return 0 +} + +# $1: pattern file; $2: file +check_pattern_file () { + echo -n "Matching whole file '$1' in file '$2' ... " + grep -f $1 $2 >/dev/null + [ $? -ne 0 ] && { echo "ERROR" ; test_failed ; return 1 ; } + echo "OK" + return 0 +} + +# Tests result: +test_ok () { + echo "Verdict: $TC Ok" | tee -a $RESULT_LOG +} + +test_failed () { + echo "Verdict: $TC Failed" | tee -a $RESULT_LOG +} + +process_tc () { + local tc_dn=`dirname $TC` + + echo + echo "***** Executing '$TC' ..." + + grep -v ^# $TC > $tmpdir/tc + while read -r line + do + oper=$(echo $line | awk '{ print $1 }') + param1="$(echo $line | cut -d' ' -f2-)" + + case $oper in + SENDXML2E) + operation_sendxml2e $tc_dn/$param1 & + [ $? -ne 0 ] && return 1 + ;; + + SENDHEX2E) + operation_sendhex2e $tc_dn/$param1 & + [ $? -ne 0 ] && return 1 + ;; + + WAIT4MESSAGE) + wait4message $RECV_LOG 5 + [ $? -ne 0 ] && return 1 + ;; + + CHECKPATTERN) + check_pattern "$param1" $RECV_LOG + [ $? -ne 0 ] && return 1 + ;; + + CHECKPATTERNFILE) + check_pattern_file "$param1" $RECV_LOG + [ $? -ne 0 ] && return 1 + ;; + + esac + + done < $tmpdir/tc + + return 0 +} + + +############# +# EXECUTION # +############# + +# Trap sigint: +trap sigint_handler SIGINT + +# Working directory is script dirname: +cd $SCR_DIR + +# Intro: +echo +echo "------------------" +echo "Test cases manager" +echo "------------------" +# Mandatory parameter: +[ -z "$1" ] && _exit "Usage: $SCR_BN " +[ ! -f "$1" -a ! -d "$1" ] && _exit "Invalid file or directory '$1' !!" + +# Gather .tc files to be processed: +[ ! -f $1 ] && echo -e "\nGathering list of test cases from '`readlink -f $1`' ..." +echo +find $1 -name *.tc | xargs -L1 readlink -f | tee -a $tmpdir/tc_list +echo +echo + +echo "Start processing test cases:" +echo "----------------------------" +# Process test cases: +0> $RESULT_LOG +for TC in `cat $tmpdir/tc_list` +do + process_tc + if [ $? -eq 0 ]; then + test_ok + else + test_failed + cat $RECV_LOG + fi +done + +echo "==========================================" +echo " TEST SUMMARY " +echo "==========================================" +echo +cat $RESULT_LOG +echo + +_exit "Done !" 0 + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/01.CER_Gx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/01.CER_Gx.xml new file mode 100644 index 0000000..b5caf20 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/01.CER_Gx.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/02.CEA_Gx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/02.CEA_Gx.xml new file mode 100644 index 0000000..06ce804 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/02.CEA_Gx.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/03.CER_Rx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/03.CER_Rx.xml new file mode 100644 index 0000000..e79ce98 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/03.CER_Rx.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/04.CEA_Rx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/04.CEA_Rx.xml new file mode 100644 index 0000000..7b9dd52 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/04.CEA_Rx.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/05.CCR-I.xml b/example/diameter/launcher/deployments/test_examples/RxGx/05.CCR-I.xml new file mode 100644 index 0000000..96a031a --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/05.CCR-I.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/06.CCA-I.xml b/example/diameter/launcher/deployments/test_examples/RxGx/06.CCA-I.xml new file mode 100644 index 0000000..885b867 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/06.CCA-I.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/07.AAR-flows.xml b/example/diameter/launcher/deployments/test_examples/RxGx/07.AAR-flows.xml new file mode 100644 index 0000000..3e11df3 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/07.AAR-flows.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/08.AAA-flows.xml b/example/diameter/launcher/deployments/test_examples/RxGx/08.AAA-flows.xml new file mode 100644 index 0000000..1392535 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/08.AAA-flows.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/09.RAR-install.xml b/example/diameter/launcher/deployments/test_examples/RxGx/09.RAR-install.xml new file mode 100644 index 0000000..5c1d643 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/09.RAR-install.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/10.RAA-install.xml b/example/diameter/launcher/deployments/test_examples/RxGx/10.RAA-install.xml new file mode 100644 index 0000000..8c7f5b5 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/10.RAA-install.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/11.CCR-U.xml b/example/diameter/launcher/deployments/test_examples/RxGx/11.CCR-U.xml new file mode 100644 index 0000000..29aad50 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/11.CCR-U.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/12.CCA-U.xml b/example/diameter/launcher/deployments/test_examples/RxGx/12.CCA-U.xml new file mode 100644 index 0000000..27f8e3b --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/12.CCA-U.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/13.STR_Rx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/13.STR_Rx.xml new file mode 100644 index 0000000..0f823dc --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/13.STR_Rx.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/14.STA_Rx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/14.STA_Rx.xml new file mode 100644 index 0000000..24038dc --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/14.STA_Rx.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/15.RAR-remove.xml b/example/diameter/launcher/deployments/test_examples/RxGx/15.RAR-remove.xml new file mode 100644 index 0000000..3840a18 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/15.RAR-remove.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/16.RAA-remove.xml b/example/diameter/launcher/deployments/test_examples/RxGx/16.RAA-remove.xml new file mode 100644 index 0000000..53a2386 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/16.RAA-remove.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/17.CCR-T.xml b/example/diameter/launcher/deployments/test_examples/RxGx/17.CCR-T.xml new file mode 100644 index 0000000..84a790a --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/17.CCR-T.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/18.CCA-T.xml b/example/diameter/launcher/deployments/test_examples/RxGx/18.CCA-T.xml new file mode 100644 index 0000000..f6a6808 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/18.CCA-T.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/19.DPR_Gx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/19.DPR_Gx.xml new file mode 100644 index 0000000..b277506 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/19.DPR_Gx.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/20.DPA_Gx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/20.DPA_Gx.xml new file mode 100644 index 0000000..f4a2435 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/20.DPA_Gx.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/21.DPR_Rx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/21.DPR_Rx.xml new file mode 100644 index 0000000..17896d0 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/21.DPR_Rx.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/22.DPA_Rx.xml b/example/diameter/launcher/deployments/test_examples/RxGx/22.DPA_Rx.xml new file mode 100644 index 0000000..76fcc1c --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/22.DPA_Rx.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/example/diameter/launcher/deployments/test_examples/RxGx/simple.sh b/example/diameter/launcher/deployments/test_examples/RxGx/simple.sh new file mode 100755 index 0000000..765401c --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/RxGx/simple.sh @@ -0,0 +1,23 @@ +#!/bin/sh +pkill ADML +sleep 1 +./run.sh +sleep 1 + +SCR_DIR=`dirname $0` +./operation.sh "test|report|yes" + +./operation.sh "test|1|timeout|5000" +./operation.sh "test|1|sendxml2e|$SCR_DIR/05.CCR-I.xml" +./operation.sh "test|1|waitfe|272|0|2001|tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571" +./operation.sh "test|1|sendxml2e|$SCR_DIR/07.AAR-flows.xml" +./operation.sh "test|1|waitfe|265|0|2001|tc_01_MMTel_DynamicQualification;afNodeHostname.afNodeHostRealm.com;1;690434" +./operation.sh "test|1|waitfe|258|1||tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571" +./operation.sh "test|1|sendxml2e|$SCR_DIR/10.RAA-install.xml|6" +./operation.sh "test|1|sendxml2e|$SCR_DIR/17.CCR-T.xml" +./operation.sh "test|1|waitfe|272|0|2001|tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571|H1" +./operation.sh "test|1|waitfe|258|1||tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571" +./operation.sh "test|1|sendxml2e|$SCR_DIR/16.RAA-remove.xml|10" + +#./operation.sh "test|ttps|1" + diff --git a/example/diameter/launcher/deployments/test_examples/checkings.sh b/example/diameter/launcher/deployments/test_examples/checkings.sh new file mode 100755 index 0000000..11d0c70 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/checkings.sh @@ -0,0 +1,24 @@ +#!/bin/sh +pkill ADML +sleep 1 +./run.sh +sleep 1 + +./operation.sh "test|1|timeout|5000" +./operation.sh "test|1|delay|5000" +./operation.sh "test|1|waitfe|258|1" +./operation.sh "test|1|waitfc|258|1" +./operation.sh "test|1|sendxml2e|aar.xml|7" + +#for id in `seq 1 2` +#do +# ./operation.sh "test|$id|timeout|5000" +# ./operation.sh "test|$id|delay|5000" +# ./operation.sh "test|$id|sendxml2e|aar${id}.xml" +## ./operation.sh "test|$id|waitfe-answer|2" +## ./operation.sh "test|$id|waitfe|258|1||Session|hbhx" +## ./operation.sh "test|$id|waitfe|258|1|rcxxx|Session" +#done + +#./operation.sh "test|ttps|40" + diff --git a/example/diameter/launcher/deployments/test_examples/cycle.sh b/example/diameter/launcher/deployments/test_examples/cycle.sh new file mode 100755 index 0000000..a16a62e --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/cycle.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Checking the correct cycle repeat and reports appending +program () { + + if [ "$2" = "ok" ] + then + ./operation.sh "test|$1|timeout|5000" + ./operation.sh "test|$1|delay|2000" + ./operation.sh "test|$1|delay|2000" + else + ./operation.sh "test|$1|timeout|5000" + ./operation.sh "test|$1|delay|2000" + ./operation.sh "test|$1|delay|4000" + fi +} + +pkill ADML +sleep 1 +./run.sh +sleep 1 +./operation.sh "test|repeat|yes" +./operation.sh "test|report|yes" + +COVERED_SECONDS=1 +for id in `seq 0 $((COVERED_SECONDS/2))` +do + program $((2*id + 1)) ok + program $((2*id + 2)) nok +done + +echo "Press ENTER to continue, CTRL+C to abort ..." +read dummy +./operation.sh "test|ttps|1" + +while true +do + sleep 1 + ./operation.sh "test|look" +done + diff --git a/example/diameter/launcher/deployments/test_examples/endsOk.sh b/example/diameter/launcher/deployments/test_examples/endsOk.sh new file mode 100755 index 0000000..b355ea1 --- /dev/null +++ b/example/diameter/launcher/deployments/test_examples/endsOk.sh @@ -0,0 +1,22 @@ +#!/bin/sh +pkill ADML +sleep 1 +./run.sh +sleep 1 +./operation.sh "test|report|yes" +./operation.sh "test|1|timeout|5000" +./operation.sh "test|1|delay|2000" +./operation.sh "test|1|delay|2000" +# Last timeout ignored: +./operation.sh "test|1|timeout|3000" + +echo "Press ENTER to continue, CTRL+C to abort ..." +read dummy +./operation.sh "test|ttps|1" + +while true +do + sleep 1 + ./operation.sh "test|look|1" +done + diff --git a/example/diameter/launcher/main.cpp b/example/diameter/launcher/main.cpp index 8054a3b..309a7ef 100644 --- a/example/diameter/launcher/main.cpp +++ b/example/diameter/launcher/main.cpp @@ -15,7 +15,7 @@ #include // Process -#include "Launcher.hpp" +#include int main(int argc, const char** argv) { @@ -32,6 +32,7 @@ int main(int argc, const char** argv) { commandLine.add("services", anna::CommandLine::Argument::Mandatory, "Services xml path file. Shall be validated against dtd schema written on warning traces: 'Services DTD schema'. Empty string or \"null\" name, to start without services (see help for management operation 'services')."); commandLine.add("trace", anna::CommandLine::Argument::Optional, "Trace level (emergency, alert, critical, error, warning, notice, information, debug, local0..local7)"); commandLine.add("cntDir", anna::CommandLine::Argument::Optional, "Counters directory. By default is the current execution directory. Warning: a counter file will be dump per record period; take care about the possible accumulation of files"); + commandLine.add("tmDir", anna::CommandLine::Argument::Optional, "Test manager directory. By default is the current execution directory. Warning: report files could be dump during system testing (see help for management operation 'test|report'); take care about the possible accumulation of files"); commandLine.add("cntRecordPeriod", anna::CommandLine::Argument::Optional, "Counters record procedure period in milliseconds. If missing, default value of 300000 (5 minutes) will be assigned. Value of 0 disables the record procedure."); commandLine.add("logStatisticSamples", anna::CommandLine::Argument::Optional, "Log statistics samples for the provided concept id list, over './sample..csv' files. For example: \"1,2\" will log concepts 1 and 2. Reserved word \"all\" activates all registered statistics concept identifiers. That ids are shown at context dump (see help to get it)."); diff --git a/example/diameter/launcher/resources/scripts/operation_signal.sh b/example/diameter/launcher/resources/scripts/operation_signal.sh index c462a7e..8c9ce85 100755 --- a/example/diameter/launcher/resources/scripts/operation_signal.sh +++ b/example/diameter/launcher/resources/scripts/operation_signal.sh @@ -12,7 +12,6 @@ _exit () { # EXECUTION # ############# cd `dirname $0` -echo # Get the PID: [ ! -f .pid ] && _exit "Can't found '`pwd`/.pid'.\nTry to pgrep your process name and dump pid to that file." PID=`cat .pid` @@ -22,10 +21,7 @@ PID=`cat .pid` echo $1 > sigusr2.tasks.input kill -s SIGUSR2 $PID -sleep 1 -echo -echo -echo "You could see results on '`pwd`/sigusr2.tasks.output' file." -echo +#echo "You could see results on '`pwd`/sigusr2.tasks.output' file." +cat `pwd`/sigusr2.tasks.output 2>/dev/null echo diff --git a/example/diameter/launcher/testing/TestCase.cpp b/example/diameter/launcher/testing/TestCase.cpp new file mode 100644 index 0000000..ccb9b42 --- /dev/null +++ b/example/diameter/launcher/testing/TestCase.cpp @@ -0,0 +1,359 @@ +// 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 + +// 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() { + 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); + } + + 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(); + if (isFinished()) { + if (!testManager.getDumpReports()) return; + std::string file = testManager.getReportsDirectory() + anna::functions::asString("/testcase.%llu.xml", 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(); + } + } + + // Count in-progress test cases: + if (inProgress()) { + testManager.setInProgressCountDelta(1); + } + else if (previousState == State::InProgress){ + testManager.setInProgressCountDelta(-1); + } +} + +bool TestCase::done() throw() { + if (a_stepsIt == a_steps.end()) { + setState(State::Success); + return true; + } + + return false; +} + +bool TestCase::process() throw() { + if (a_steps.size() == 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; + + setState(State::Initialized); + + return true; +} + +void TestCase::assertInitialized() const throw(anna::RuntimeException) { + if (a_state != State::Initialized) + throw anna::RuntimeException(anna::functions::asString("Cannot program anymore. The test case %llu was started. You must reset it to append new steps.", 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 registerSessionId = ((isRequest && toEntity) || (!isRequest && !toEntity) /* (*) */); + // (*) we register answers Session-Id assuming that we will know the Session-Id values created by the client (OCS) + // This is another solution for TODO(***) regarding diameter server testing. No tsure about the final implementation. + + // 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 (registerSessionId) + TestManager::instantiate().registerSessionId(anna::diameter::helpers::base::functions::getSessionId(db), this); +} + +void TestCase::addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException) { + assertInitialized(); + TestStepTimeout *step = new TestStepTimeout(this); + step->setTimeout(timeout); + a_steps.push_back(step); +} + +void TestCase::addSendxml2e(const anna::DataBlock &db, RealmNode *realm, int stepNumber) throw(anna::RuntimeException) { + assertInitialized(); + assertMessage(db, true /* to entity */); + + if (stepNumber != -1) { + int steps = a_steps.size(); + int stepIndx = stepNumber - 1; + if ((stepIndx < 0) || (stepIndx > (a_steps.size()-1))) + throw anna::RuntimeException(anna::functions::asString("Step number out of range (test case %llu)", a_id), ANNA_FILE_LOCATION); + + TestStep *stepReferred = a_steps[stepIndx]; + if (stepReferred->getType() != TestStep::Type::Wait) + throw anna::RuntimeException(anna::functions::asString("Step number must refer to a 'wait' step (test case %llu)", 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 must refer to a 'wait for request' step (test case %llu)", a_id), ANNA_FILE_LOCATION); + } + } + + TestStepSendxml2e *step = new TestStepSendxml2e(this); + step->setMsgDataBlock(db); + step->setRealmNode(realm); + step->setWaitForRequestStepNumber(stepNumber); // -1 means, no reference + a_steps.push_back(step); +} + +void TestCase::addSendxml2c(const anna::DataBlock &db, RealmNode *realm, int stepNumber) throw(anna::RuntimeException) { + assertInitialized(); + assertMessage(db, false /* to client */); + + TestStepSendxml2c *step = new TestStepSendxml2c(this); + step->setMsgDataBlock(db); + step->setRealmNode(realm); + a_steps.push_back(step); +} + +void TestCase::addDelay(const anna::Millisecond &delay) throw(anna::RuntimeException) { + assertInitialized(); + TestStepDelay *step = new TestStepDelay(this); + step->setDelay(delay); + a_steps.push_back(step); +} + +void TestCase::addWait(bool fromEntity, + const std::string &code, const std::string &bitR, const std::string &resultCode, const std::string &sessionId, + const std::string &hopByHop, const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(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] == '#') { + int steps = a_steps.size(); + 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()); + int stepIndx = stepNumber - 1; + if ((stepIndx < 0) || (stepIndx > (steps-1))) + throw anna::RuntimeException(anna::functions::asString("Step reference number %d out of range [1-%d]", stepNumber, steps), ANNA_FILE_LOCATION); + + TestStep *stepReferred = a_steps[stepIndx]; + 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, resultCode, sessionId, usedHopByHop, msisdn, imsi, serviceContextId); + + LOGWARNING( + if (hasSameCondition(step->getCondition())) + anna::Logger::warning(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION); + ); + + a_steps.push_back(step); +} + +void TestCase::addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException) { + assertInitialized(); + + TestStepWait *step = new TestStepWait(this); + step->setCondition(fromEntity, regexp); + + LOGWARNING( + if (hasSameCondition(step->getCondition())) + anna::Logger::warning(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION); + ); + + a_steps.push_back(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; + result = (TestStepWait*)(*it); + if ((result->getCondition().receivedFromEntity() == waitFromEntity) && (result->fulfilled(message))) + return result; + } + + return NULL; +} + +const TestStep *TestCase::getStep(int stepNumber) const throw() { + int stepIndx = stepNumber - 1; + if ((stepIndx < 0) || (stepIndx > (a_steps.size()-1))) return NULL; + std::vector::const_iterator it = (a_steps.begin() + stepNumber); + return (*it); +} diff --git a/example/diameter/launcher/testing/TestCase.hpp b/example/diameter/launcher/testing/TestCase.hpp new file mode 100644 index 0000000..a3c48f0 --- /dev/null +++ b/example/diameter/launcher/testing/TestCase.hpp @@ -0,0 +1,119 @@ +// 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 + +// Project +#include +#include +#include + +// Process +#include + + +namespace anna { + class Millisecond; + + namespace xml { + class Node; + } +} + +class RealmNode; + + +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) : a_id(id), a_state(State::Initialized), a_startTime(0) { /*a_stepsIt = a_steps.end()*/;} + ~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(); + + // Step type & information + void addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException); + void addSendxml2e(const anna::DataBlock &db, RealmNode *realm, int stepNumber) throw(anna::RuntimeException); + void addSendxml2c(const anna::DataBlock &db, RealmNode *realm, 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 &resultCode, const std::string &sessionId, + const std::string &hopByHop, 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); + + // 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(); } + 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. + + friend class TestStep; +}; + +#endif diff --git a/example/diameter/launcher/testing/TestClock.cpp b/example/diameter/launcher/testing/TestClock.cpp new file mode 100644 index 0000000..885e87e --- /dev/null +++ b/example/diameter/launcher/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 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 new file mode 100644 index 0000000..e007cb6 --- /dev/null +++ b/example/diameter/launcher/testing/TestClock.hpp @@ -0,0 +1,29 @@ +// 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 new file mode 100644 index 0000000..a772ab0 --- /dev/null +++ b/example/diameter/launcher/testing/TestCondition.cpp @@ -0,0 +1,174 @@ +// 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 + +// Process +#include +#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_resultCode != "" || a_sessionId != "" || a_hopByHop != "" || 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_resultCode != c2.a_resultCode) return false; + if (c1.a_sessionId != c2.a_sessionId) return false; + if (c1.a_hopByHop != c2.a_hopByHop) 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) { + Launcher& my_app = static_cast (anna::app::functions::getApp()); + static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine()); + 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.first ? "1":"0"); + if (a_bitR != 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 (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_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_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_sessionId != "") result->createAttribute("SessionId", a_sessionId); + if (a_resultCode != "") result->createAttribute("ResultCode", atoi(a_resultCode.c_str())); + if (a_hopByHop != "") result->createAttribute("HopByHop", atoll(a_hopByHop.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 new file mode 100644 index 0000000..7ff58d9 --- /dev/null +++ b/example/diameter/launcher/testing/TestCondition.hpp @@ -0,0 +1,100 @@ +// 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_resultCode(""), a_sessionId(""), + a_hopByHop(""), 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 setResultCode(const std::string &value) throw() { a_resultCode = value; } + void setSessionId(const std::string &value) throw() { a_sessionId = value; } + void setHopByHop(const std::string &value) throw() { a_hopByHop = 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 & getResultCode() const throw() { return a_resultCode; } + const std::string & getSessionId() const throw() { return a_sessionId; } + const std::string & getHopByHop() const throw() { return a_hopByHop; } + 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_resultCode; + std::string a_sessionId; + std::string a_hopByHop; + 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 new file mode 100644 index 0000000..40d8286 --- /dev/null +++ b/example/diameter/launcher/testing/TestManager.cpp @@ -0,0 +1,400 @@ +// 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 + +// Process +#include +#include +#include +#include + + +class TestTimer; + + +TestManager::TestManager() : + anna::timex::TimeEventObserver("TestManager") { + a_timeController = NULL; + a_reportsDirectory = "./"; + a_dumpReports = false; + a_synchronousAmount = 1; + a_poolRepeat = false; + a_inProgressCount = 0; + a_inProgressLimit = UINT_MAX; // no limit + a_clock = NULL; + //a_testPool.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); + } +} + +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 the time trigger 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; + a_testPool.clear(); + a_sessionIdTestCaseMap.clear(); + a_currentTestIt = a_testPool.end(); + configureTTPS(0); // stop + 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++) { + it->second->reset(hard); + result = true; + } + //a_sessionIdTestCaseMap.clear(); + return result; +} + +bool TestManager::tick() throw() { + LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION)); + + if (!tests()) { + LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming. Stopping test clock", ANNA_FILE_LOCATION)); + return false; + } + + int count = a_synchronousAmount; + 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 (a_inProgressCount >= 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_poolRepeat) { + LOGWARNING(anna::Logger::warning("Testing pool cycle completed. Repeat mode on. Restarting", ANNA_FILE_LOCATION)); + //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. + } + else { + LOGWARNING(anna::Logger::warning("Testing pool cycle completed. Repeat mode off. Suspending", ANNA_FILE_LOCATION)); + return false; + } + } + + // Hard reset, because normally a cycle takes more time that a single test case lifetime. We can consider that never + // going to break a in-progress test case due to cycle repeat + 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; + } + } +} + +TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw(anna::RuntimeException) { + sessionId = anna::diameter::helpers::base::functions::getSessionId(message); + std::map::const_iterator sessionIdIt = a_sessionIdTestCaseMap.find(sessionId); + if (sessionIdIt != a_sessionIdTestCaseMap.end()) + return sessionIdIt->second; + + LOGWARNING(anna::Logger::warning(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.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; + TestCase *tc = getTestCaseFromSessionId(message, sessionId); + if (!tc) 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 { + Launcher& my_app = static_cast (anna::app::functions::getApp()); + static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine()); + 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)); + } +} + +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; + TestCase *tc = getTestCaseFromSessionId(message, sessionId); + if (!tc) 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 { + Launcher& my_app = static_cast (anna::app::functions::getApp()); + static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine()); + 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)); + } +} + +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); + result->createAttribute("PoolRepeat", (a_poolRepeat ? "yes":"no")); + result->createAttribute("InProgressCount", a_inProgressCount); + if (a_inProgressLimit == UINT_MAX) + result->createAttribute("InProgressLimit", ""); + else + result->createAttribute("InProgressLimit", a_inProgressLimit); + result->createAttribute("DumpReports", (a_dumpReports ? "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 (a_dumpReports && poolSize != 0) { + anna::xml::Node* testCases = result->createChild("TestCases"); + for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) { + (*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 new file mode 100644 index 0000000..313e15f --- /dev/null +++ b/example/diameter/launcher/testing/TestManager.hpp @@ -0,0 +1,134 @@ +// 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 + + +namespace anna { + class Millisecond; + + namespace timex { + class Engine; + } + namespace diameter { + namespace comm { + class ClientSession; + class ServerSession; + } + } +} + + +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 { + typedef anna::Recycler timer_container; + + anna::timex::Engine* a_timeController; + + // reports + std::string a_reportsDirectory; + bool a_dumpReports; + + // Pool of test cases + test_pool_t a_testPool; + test_pool_it a_currentTestIt; + bool a_poolRepeat; // repeat pool when finish + unsigned int a_inProgressCount; + 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; + + // 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. + + + 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); + + 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 setDumpReports(bool dr) throw() { a_dumpReports = dr; } + bool getDumpReports() const throw() { return a_dumpReports; } + + // 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 setPoolRepeat(bool repeat) throw() { a_poolRepeat = repeat; } + bool getPoolRepeat() const throw() { return a_poolRepeat; } + unsigned int getInProgressCount() const throw() { return a_inProgressCount; } + void setInProgressCountDelta(unsigned int delta) throw() { a_inProgressCount += delta; } + 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(anna::RuntimeException); + 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); + + anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); + std::string asXMLString() const throw(); + + + friend class anna::Singleton ; + friend class TestStepTimeout; // createTimer + friend class TestStepDelay; // createTimer + friend class TestClock; // tick +}; + +#endif diff --git a/example/diameter/launcher/testing/TestStep.cpp b/example/diameter/launcher/testing/TestStep.cpp new file mode 100644 index 0000000..076a227 --- /dev/null +++ b/example/diameter/launcher/testing/TestStep.cpp @@ -0,0 +1,392 @@ +// 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 +#include +#include +#include +#include +#include +#include + + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// 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 +} + +const char* TestStep::asText(const Type::_v type) +throw() { + static const char* text [] = { "Unconfigured", "Timeout", "Sendxml2e", "Sendxml2c", "Delay", "Wait" }; + return text [type]; +} + +anna::xml::Node* TestStep::asXML(anna::xml::Node* parent) const +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() const throw() { + anna::xml::Node root("root"); + return anna::xml::Compiler().apply(asXML(&root)); +} + +bool TestStep::execute() throw() { + LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION)); + setBeginTimestamp(anna::functions::millisecond()); + return do_execute(); +} + +void TestStep::complete() throw() { + LOGDEBUG(anna::Logger::debug(anna::functions::asString("COMPLETE %s for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, 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 for Test Case %llu (%p) (%p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION)); + // type and testCase kept + a_completed = 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) const +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() { + LOGDEBUG(anna::Logger::debug(anna::functions::asString("REXXXXXET %s for Test Case %llu (%p) (%p) (a_timer %p)", asText(a_type), a_testCase->getId(), (TestCaseStep*)this, this, a_timer), ANNA_FILE_LOCATION)); + + try { + TestManager::instantiate().cancelTimer(a_timer); + } + 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) const +throw() { + anna::xml::Node* result = TestStep::asXML(parent); + //parent->createChild("TestStepSendxml"); + + // Message + std::string msg = "", xmlmsg = ""; + if (a_message.isEmpty()) { + msg = ""; + } + else { + msg = "\n"; msg += a_message.asString(); msg += "\n"; + // Helper + try { + Launcher& my_app = static_cast (anna::app::functions::getApp()); + static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine()); + codecMsg.decode(a_message); + xmlmsg = "\n"; xmlmsg += codecMsg.asXMLString(); xmlmsg += "\n"; + } + catch (anna::RuntimeException &ex) { + ex.trace(); + } + } + + result->createAttribute("Message", msg); + if (xmlmsg != "") result->createAttribute("XMLMessage", xmlmsg); + result->createAttribute("Expired", (a_expired ? "yes":"no")); + + return result; +} + +bool TestStepSendxml::do_execute() throw() { + anna::diameter::comm::Message *msg = a_realmNode->createCommMessage(); + bool success = false; + std::string failReason, s_warn; + + // Update sequence for answers: + if (a_waitForRequestStepNumber != -1) { // is an answer: try to copy sequence information; alert about Session-Id discrepance + // Request which was received: + const TestStepWait *tsw = (const TestStepWait*)(a_testCase->getStep(a_waitForRequestStepNumber)); + const anna::DataBlock &request = tsw->getMsgDataBlock(); + anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(request); + anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(request); + // Update sequence: + anna::diameter::codec::functions::setHopByHop(a_message, hbh); + anna::diameter::codec::functions::setEndToEnd(a_message, ete); + + // Check Session-Id for warning ... + std::string sessionIdAnswer = anna::diameter::helpers::base::functions::getSessionId(a_message); + std::string sessionIdRequest = anna::diameter::helpers::base::functions::getSessionId(request); + if (sessionIdRequest != sessionIdAnswer) { + s_warn = anna::functions::asString("Sending an answer which Session-Id (%s) is different than supposed corresponding request (%s)", sessionIdAnswer.c_str(), sessionIdRequest.c_str()); + LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION)); + a_testCase->addDebugSummaryHint(s_warn); + } + } + + if (getType() == Type::Sendxml2e) { + MyDiameterEntity *entity = a_realmNode->getEntity(); + if (entity) { + try { + //msg->clearBody(); + msg->setBody(a_message); + /* response = NULL =*/entity->send(msg); + success = true; + } catch(anna::RuntimeException &ex) { + ex.trace(); + failReason = ex.asString(); + } + } + else { + failReason = "There is no diameter entity currently configured. Unable to send the message"; + LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); + } + } + else if (getType() == Type::Sendxml2c) { + MyLocalServer *localServer = a_realmNode->getDiameterServer(); + if (localServer) { + try { + //msg->clearBody(); + msg->setBody(a_message); + /* response = NULL =*/localServer->send(msg); + success = true; + } catch(anna::RuntimeException &ex) { + ex.trace(); + failReason = ex.asString(); + } + } + else { + failReason = "There is no diameter local server currently configured. Unable to send the message"; + LOGWARNING(anna::Logger::warning(failReason, ANNA_FILE_LOCATION)); + } + } + + // release msg + a_realmNode->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_complete() throw() { + next(); +} + +void TestStepSendxml::do_reset() throw() { + a_expired = false; + //a_message.clear(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////// +// TestStepDelay +//////////////////////////////////////////////////////////////////////////////////////////////////////// +anna::xml::Node* TestStepDelay::asXML(anna::xml::Node* parent) const +throw() { + anna::xml::Node* result = TestStep::asXML(parent); + //parent->createChild("TestStepDelay"); + + result->createAttribute("Delay", a_delay.asString()); + + return result; +} + +bool TestStepDelay::do_execute() throw() { + 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() { + a_timer = NULL; + next(); +} + +void TestStepDelay::do_reset() throw() { + 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 &resultCode, const std::string &sessionId, + const std::string &hopByHop, 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) const +throw() { + anna::xml::Node* result = TestStep::asXML(parent); + //parent->createChild("TestStepWait"); + + // Condition + a_condition.asXML(result); + + if (!a_message.isEmpty()) { + // Message + result->createAttribute("MatchedMessage", a_message.asString()); + // Helper + try { + Launcher& my_app = static_cast (anna::app::functions::getApp()); + static anna::diameter::codec::Message codecMsg(my_app.getCodecEngine()); + codecMsg.decode(a_message); + result->createAttribute("MatchedXMLMessage", codecMsg.asXMLString()); + } + catch (anna::RuntimeException &ex) { + ex.trace(); + } + } + + return result; +} + +bool TestStepWait::do_execute() throw() { + return a_completed; +} + +void TestStepWait::do_complete() throw() { + next(); +} + +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; +} + diff --git a/example/diameter/launcher/testing/TestStep.hpp b/example/diameter/launcher/testing/TestStep.hpp new file mode 100644 index 0000000..90b64aa --- /dev/null +++ b/example/diameter/launcher/testing/TestStep.hpp @@ -0,0 +1,204 @@ +// 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 + +// Project +#include +#include + +// Process +#include + + +namespace anna { + class Millisecond; + + namespace xml { + class Node; + } + namespace diameter { + namespace comm { + class ClientSession; + class ServerSession; + } + } +} + +class TestCase; +class TestTimer; +class RealmNode; + +class TestStep { + + int a_number; // step number used for xml (informational) + anna::Millisecond a_beginTimestamp; // unix time + anna::Millisecond a_endTimestamp; // unix time + + 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 }; }; + static const char* asText(const Type::_v type) throw(); + + TestStep(TestCase *testCase) { 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) const throw(); + std::string asXMLString() const throw(); + + protected: + TestCase *a_testCase; + bool a_completed; + Type::_v a_type; + + 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) const throw(); +}; + + +class TestStepSendxml : public TestStep { + + protected: + // possible end points: + RealmNode *a_realmNode; + + // Step number reference ('wait for request' step) + int a_waitForRequestStepNumber; + + // Message + anna::DataBlock a_message; + + // Expired ? + bool a_expired; // a_endTimestamp will be the expiration reception timestamp + + public: + TestStepSendxml(TestCase *testCase) : TestStep(testCase), a_message(true), a_expired(false), a_realmNode(NULL), a_waitForRequestStepNumber(-1) {;} + + // setter & getters + void setRealmNode(RealmNode *realm) throw() { a_realmNode = realm; } + RealmNode *getRealmNode() const throw() { return a_realmNode; } + 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) const 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) const throw(); +}; + + +class TestStepWait : public TestStep { + + TestCondition a_condition; + anna::DataBlock a_message; // message which complies with 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 &resultCode, const std::string &sessionId, + const std::string &hopByHop, 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; } + + 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) const throw(); +}; + + +#endif diff --git a/example/diameter/launcher/testing/TestTimer.cpp b/example/diameter/launcher/testing/TestTimer.cpp new file mode 100644 index 0000000..abe8b4d --- /dev/null +++ b/example/diameter/launcher/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 + +// 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 new file mode 100644 index 0000000..3f1b5eb --- /dev/null +++ b/example/diameter/launcher/testing/TestTimer.hpp @@ -0,0 +1,48 @@ +// 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/example/diameter/pcapDecoder/main.cpp b/example/diameter/pcapDecoder/main.cpp index 32e9dcb..377b317 100644 --- a/example/diameter/pcapDecoder/main.cpp +++ b/example/diameter/pcapDecoder/main.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include #include diff --git a/include/anna/core/util/Tokenizer.hpp b/include/anna/core/util/Tokenizer.hpp index 693b2cf..8290775 100644 --- a/include/anna/core/util/Tokenizer.hpp +++ b/include/anna/core/util/Tokenizer.hpp @@ -18,9 +18,12 @@ namespace anna { class RuntimeException; /** - Separa la cadena recibida en distintos elementos. + Tokenize the input string into several elements */ class Tokenizer { + + int _apply(const char* str, const char* separator) throw(RuntimeException); + public: typedef char* const* const_iterator; @@ -93,30 +96,28 @@ public: const char* operator [](const int i) const throw(RuntimeException) { return at(i); } /** - Aplica la separacion sobre la cadena str con el separador recibido como parametro. + Process the separation over the string str with the separator provided. - @param str Cadena sobre la que aplicar la separacion. - @param separator Caracteres que van a actuar como separador de las subcadenas contenidas en el - primer parametro. + Internally used strtok_r has these imitations: sequence of two or more contiguous delimiter + bytes in the parsed string is considered to be a single delimiter. Delimiter bytes at the start + or end of the string are ignored. Put another way: the tokens returned by strtok() are always + nonempty strings. To override these limitations, the string provided can be internally modified + inserting a artificial token in order to cheat on strtok_r. For this feature, you may provide + '' or whatever string (non-empty) you prefer. - @return Numero de elementos obtenidos al aplicar la separacion. + @param str String to apply the separation. + @param separator Characters used as separator within the string tokenized + @param tokenizeContiguous If provided, it will be the artificial token used internally. The + resulting tokens shall store this string in case of contiguous separators. NULL by + default (original strtok_r behaviour). + @return Number of tokens */ - int apply(const std::string& str, const char* separator) throw(RuntimeException) { - return apply(str.c_str(), separator); + int apply(const char* str, const char* separator, const char *tokenizeContiguous = NULL) throw(RuntimeException); + int apply(const std::string& str, const char* separator, const char *tokenizeContiguous = NULL) throw(RuntimeException) { + return apply(str.c_str(), separator, tokenizeContiguous); } - /** - Aplica la separacion sobre la cadena str con el separador recibido como parametro. - - @param str Cadena sobre la que aplicar la separacion. - @param separator Caracteres que van a actuar como separador de las subcadenas contenidas en el - primer parametro. - - @return Numero de elementos obtenidos al aplicar la separacion. - - */ - int apply(const char* str, const char* separator) throw(RuntimeException); // Metodos /** diff --git a/include/anna/diameter.comm/OamModule.hpp b/include/anna/diameter.comm/OamModule.hpp index 6566180..7567eff 100644 --- a/include/anna/diameter.comm/OamModule.hpp +++ b/include/anna/diameter.comm/OamModule.hpp @@ -106,6 +106,11 @@ public: RequestSentOnClientSessionExpired, RequestSentOnServerSessionExpired, + // retransmissions + RequestRetransmitted, + RequestRetransmittedOnClientSession, + RequestRetransmittedOnServerSession, + // unknown received answers AnswerReceivedUnknown, AnswerReceivedOnClientSessionUnknown, diff --git a/include/anna/diameter/codec/Message.hpp b/include/anna/diameter/codec/Message.hpp index 6ec6e3a..a3916f7 100644 --- a/include/anna/diameter/codec/Message.hpp +++ b/include/anna/diameter/codec/Message.hpp @@ -244,18 +244,20 @@ public: /** Sets/unsets E bit activation. Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored. + This flag MUST NOT be set in request messages (in this case, it will be ignored). @param activate Activates/deactivates the bit. True by default. */ - void setErrorBit(bool activate = true) throw() { if(activate) a_flags |= EBitMask; else a_flags &= (~EBitMask); } + void setErrorBit(bool activate = true) throw() { if(isRequest()) return; if(activate) a_flags |= EBitMask; else a_flags &= (~EBitMask); } /** Sets/unsets T bit activation. Application should not have to use this because dictionary information is used in order to configure flags when Message identifier is stored. + This flag MUST NOT be set in answer messages (in this case, it will be ignored). @param activate Activates/deactivates the bit. True by default. */ - void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); } + void setPotentiallyReTransmittedMessageBit(bool activate = true) throw() { if(isAnswer()) return; if(activate) a_flags |= TBitMask; else a_flags &= (~TBitMask); } /** Sets the message application id. diff --git a/include/anna/diameter/codec/functions.hpp b/include/anna/diameter/codec/functions.hpp index 770fcc1..44a8f15 100644 --- a/include/anna/diameter/codec/functions.hpp +++ b/include/anna/diameter/codec/functions.hpp @@ -107,26 +107,50 @@ struct functions { static void decodeAVP(const char *start, AvpId & id, char & flags, int & length, std::string & data) throw(anna::RuntimeException); /** - * Gets the next AVP pointer reference starting from a first-avp datablock. It could be the first avp within + * Gets the next AVP pointer reference starting from a first-avp data block. It could be the first avp within * a command, or within an grouped avp. * - * @param avpsDB AVPs set as datablock + * @param avpsDB AVP data block buffer pointer + * @param avpsLen AVP data block buffer length * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word). * * @return Pointer to the next AVP found. NULL if no more. */ - static const char * nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException); + static const char * nextAVP(const char *avpsDB, int avpsLen, const char *start) throw(anna::RuntimeException); + +// /** +// * Gets the next AVP pointer reference starting from a first-avp datablock. It could be the first avp within +// * a command, or within an grouped avp. +// * +// * @param avpsDB AVPs set as datablock +// * @param start Point to start the search. Must be a valid avp start (point to the 32-bits avp code word). +// * +// * @return Pointer to the next AVP found. NULL if no more. +// */ +// static const char * nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException); /** - * Gets the next AVP pointer reference within an AVPs set datablock with a certain AVP identification. + * Gets the next AVP pointer reference within an AVPs set data block with a certain AVP identification. * - * @param avpsDB AVPs set as datablock + * @param avpsDB AVP data block buffer pointer + * @param avpsLen AVP data block buffer length * @param id Avp identification (code, vendorId). - * @param n Ocurrence number (first avp, second avp, etc.) + * @param n Ocurrence number (first avp, second avp, etc.). 1 by default. * * @return Pointer to first AVP found with identification provided. NULL if not found. */ - static const char * findAVP(const anna::DataBlock & avpsDB, const AvpId & id, int n = 1) throw(anna::RuntimeException); + static const char *findAVP(const char *avpsDB, int avpsLen, const diameter::AvpId & id, int n = 1) throw(anna::RuntimeException); + +// /** +// * Gets the next AVP pointer reference within an AVPs set datablock with a certain AVP identification. +// * +// * @param avpsDB AVPs set as datablock +// * @param id Avp identification (code, vendorId). +// * @param n Ocurrence number (first avp, second avp, etc.). 1 by default. +// * +// * @return Pointer to first AVP found with identification provided. NULL if not found. +// */ +// static const char * findAVP(const anna::DataBlock & avpsDB, const AvpId & id, int n = 1) throw(anna::RuntimeException); diff --git a/source/core/functions.cpp b/source/core/functions.cpp index 6a660df..f75902d 100644 --- a/source/core/functions.cpp +++ b/source/core/functions.cpp @@ -94,13 +94,12 @@ throw() { string functions::asString(const S64 number) throw() { char aux [24]; - sprintf(aux, "%lld", number); - /*#ifdef __anna64__ + //sprintf(aux, "%lld", number); + #ifdef __anna64__ sprintf (aux, "%ld", number); #else sprintf (aux, "%lld", number); #endif - */ return string(aux); } @@ -114,14 +113,12 @@ throw() { string functions::asString(const U64 number) throw() { char aux [16]; - sprintf(aux, "%llu", number); - /* + //sprintf(aux, "%llu", number); #ifdef __anna64__ sprintf (aux, "%lu", number); #else sprintf (aux, "%llu", number); #endif - */ return string(aux); } @@ -171,14 +168,12 @@ throw() { string functions::asHexString(const S64 number) throw() { char aux [32]; - sprintf(aux, "0x%llx", number); - /* + //sprintf(aux, "0x%llx", number); #ifdef __anna64__ sprintf (aux, "0x%lx", number); #else sprintf (aux, "0x%llx", number); #endif - */ return string(aux); } @@ -310,14 +305,12 @@ throw(RuntimeException) { S64 functions::asInteger64(const char* str) throw() { S64 number = 0; - sscanf(str, "%lld", &number); - /* + //sscanf(str, "%lld", &number); #ifdef __anna64__ sscanf (str, "%ld", &number); #else sscanf (str, "%lld", &number); #endif - */ return number; } @@ -416,8 +409,8 @@ throw() { int r = -1; // r will be lg(v) unsigned int t, tt; // temporaries - if(tt = v >> 16) { - r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]; + if((tt = v >> 16)) { + r = ((t = tt >> 8)) ? 24 + LogTable256[t] : 16 + LogTable256[tt]; } else { r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v]; } diff --git a/source/core/util/String.cpp b/source/core/util/String.cpp index 8f94116..30a7ce5 100644 --- a/source/core/util/String.cpp +++ b/source/core/util/String.cpp @@ -57,14 +57,12 @@ throw() { String& String::operator<< (const S64 vv) throw() { char aux [32]; - sprintf(aux, "%lld", vv); - /* + //sprintf(aux, "%lld", vv); #ifdef __anna64__ - sprintf (aux, "%ld", vv); + sprintf (aux, "%ld", vv); #else - sprintf (aux, "%lld", vv); + sprintf (aux, "%lld", vv); #endif - */ string::operator+= (aux); return *this; } @@ -72,14 +70,12 @@ throw() { String& String::operator<< (const U64 vv) throw() { char aux [32]; - sprintf(aux, "%llu", vv); - /* - #ifdef __anna64__ - sprintf (aux, "%lu", vv); - #else - sprintf (aux, "%llu", vv); - #endif - */ + //sprintf(aux, "%llu", vv); + #ifdef __anna64__ + sprintf (aux, "%lu", vv); + #else + sprintf (aux, "%llu", vv); + #endif string::operator+= (aux); return *this; } diff --git a/source/core/util/Tokenizer.cpp b/source/core/util/Tokenizer.cpp index b4f6d12..d49f6ed 100644 --- a/source/core/util/Tokenizer.cpp +++ b/source/core/util/Tokenizer.cpp @@ -10,12 +10,16 @@ #include #include +// temporary +#include + using namespace std; using namespace anna; //static const int Tokenizer::MaxItem = 64; + Tokenizer::Tokenizer() : a_dataBlock(true), a_activateStrip(false) { @@ -53,12 +57,13 @@ Tokenizer::~Tokenizer() { delete [] a_items; } -int Tokenizer::apply(const char* str, const char* separator) +int Tokenizer::_apply(const char* str, const char* separator) throw(RuntimeException) { + a_maxItem = 0; - if(str == NULL) - return 0; + //if(str == NULL) + // return 0; DataBlock mb(str, anna_strlen(str) + 1, false); a_dataBlock = mb; @@ -86,6 +91,32 @@ throw(RuntimeException) { return a_maxItem; } +int Tokenizer::apply(const char *str, const char* separator, const char *tokenizeContiguous) throw(RuntimeException) { + + if(str == NULL) + return 0; + + if (!separator) + throw RuntimeException("Cannot tokenize with a NULL separator", ANNA_FILE_LOCATION); + + if (!tokenizeContiguous) return _apply(str, separator); + + std::string _str = str; + std::string _sep = separator; + std::string _tok = tokenizeContiguous; + if (_sep == _tok) + throw RuntimeException("Using the separator as artifial token is a nonsense (original behaviour)", ANNA_FILE_LOCATION); + if (_tok == "") + throw RuntimeException("Use another artifial token. Empty is a nonsense (original behaviour)", ANNA_FILE_LOCATION); + + std::string seps = _sep + _sep; + std::size_t pos, sepsL = seps.size(); + std::string artificialToken = _sep + _tok + _sep; + + while ((pos = _str.find(seps)) != std::string::npos) _str.replace(pos, sepsL, artificialToken); + return _apply(_str.c_str(), separator); +} + const char* Tokenizer::at(const int i) throw(RuntimeException) { if(i >= a_maxItem) diff --git a/source/diameter.comm/ClientSession.cpp b/source/diameter.comm/ClientSession.cpp index 6a535a3..83c286c 100644 --- a/source/diameter.comm/ClientSession.cpp +++ b/source/diameter.comm/ClientSession.cpp @@ -448,6 +448,12 @@ void ClientSession::eventPeerShutdown() throw() { } void ClientSession::eventRequestRetransmission(Message *request) throw() { + + // OAM + OamModule &oamModule = OamModule::instantiate(); + oamModule.count(OamModule::Counter::RequestRetransmitted); + oamModule.count(OamModule::Counter::RequestRetransmittedOnClientSession); + // Inform father server: a_parent->eventRequestRetransmission(this, request); } diff --git a/source/diameter.comm/OamModule.cpp b/source/diameter.comm/OamModule.cpp index 52a37b6..eab433c 100644 --- a/source/diameter.comm/OamModule.cpp +++ b/source/diameter.comm/OamModule.cpp @@ -54,6 +54,9 @@ anna_assign_enum(anna::diameter::comm::OamModule::Counter) = { \ "RequestSentExpired", \ "RequestSentOnClientSessionExpired", \ "RequestSentOnServerSessionExpired", \ + "RequestRetransmitted", \ + "RequestRetransmittedOnClientSession", \ + "RequestRetransmittedOnServerSession", \ "AnswerReceivedUnknown", \ "AnswerReceivedOnClientSessionUnknown", \ "AnswerReceivedOnServerSessionUnknown", \ diff --git a/source/diameter.comm/ServerSession.cpp b/source/diameter.comm/ServerSession.cpp index a615c78..1e2728c 100644 --- a/source/diameter.comm/ServerSession.cpp +++ b/source/diameter.comm/ServerSession.cpp @@ -314,6 +314,12 @@ void ServerSession::eventPeerShutdown() throw() { } void ServerSession::eventRequestRetransmission(Message *request) throw() { + + // OAM + OamModule &oamModule = OamModule::instantiate(); + oamModule.count(OamModule::Counter::RequestRetransmitted); + oamModule.count(OamModule::Counter::RequestRetransmittedOnServerSession); + // Inform father server: a_parent->eventRequestRetransmission(this, request); } diff --git a/source/diameter/codec/functions.cpp b/source/diameter/codec/functions.cpp index a58372d..767d960 100644 --- a/source/diameter/codec/functions.cpp +++ b/source/diameter/codec/functions.cpp @@ -214,9 +214,11 @@ void functions::decodeAVP(const char *start, diameter::AvpId & id, char & flags, ); } -const char * functions::nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException) { +const char * functions::nextAVP(const char *avpsDB, int avpsLen, const char *start) throw(anna::RuntimeException) { if(start == NULL) throw anna::RuntimeException("NULL provided start pointer", ANNA_FILE_LOCATION); + if(avpsDB == NULL) + throw anna::RuntimeException("NULL provided avpsDB pointer", ANNA_FILE_LOCATION); const char *result; // LOGDEBUG( @@ -227,18 +229,21 @@ const char * functions::nextAVP(const anna::DataBlock & avpsDB, const char *star //int avpLength = (start[5] << 16) + (start[6] << 8) + start[7]; // AVP Length int avpLength = DECODE3BYTES_INDX_VALUETYPE(start, 5, int); result = start + 4 * REQUIRED_WORDS(avpLength); - const char * first = avpsDB.getData(); - int offset = (result - first); + int offset = (result - avpsDB); - if(offset > (avpsDB.getSize() - 1)) - //throw anna::RuntimeException("Start pointer out of boundaries for DataBlock", ANNA_FILE_LOCATION); + if(offset > (avpsLen - 1)) + //throw anna::RuntimeException("Start pointer out of boundaries for block(avpsDB, avpsLen)", ANNA_FILE_LOCATION); return NULL; // (*) return result; } -const char * functions::findAVP(const anna::DataBlock & avpsDB, const diameter::AvpId & id, int n) throw(anna::RuntimeException) { - const char * result = avpsDB.getData(); // first avp +//const char * functions::nextAVP(const anna::DataBlock & avpsDB, const char *start) throw(anna::RuntimeException) { +// return nextAVP(avpsDB.getData(), avpsDB.getSize(), start); +//} + +const char * functions::findAVP(const char *avpsDB, int avpsLen, const diameter::AvpId & id, int n) throw(anna::RuntimeException) { + const char *result = avpsDB; // first avp int positives = 0; // Decoded avp information: diameter::AvpId _id; @@ -250,7 +255,7 @@ const char * functions::findAVP(const anna::DataBlock & avpsDB, const diameter:: if(_id == id) positives++; while((_id != id) || (positives != n)) { // next search if not found or not ocurrence number reached - result = nextAVP(avpsDB, result); + result = nextAVP(avpsDB, avpsLen, result); if(result == NULL) { // (*) LOGDEBUG( @@ -270,6 +275,9 @@ const char * functions::findAVP(const anna::DataBlock & avpsDB, const diameter:: return result; } +//const char * functions::findAVP(const anna::DataBlock & avpsDB, const diameter::AvpId & id, int n) throw(anna::RuntimeException) { +// return findAVP(avpsDB.getData(), avpsDB.getSize(), id, n); +//} // modifiers void functions::setHopByHop(anna::DataBlock & db, diameter::HopByHop hbh) throw(anna::RuntimeException) { diff --git a/source/diameter/helpers/base/functions.cpp b/source/diameter/helpers/base/functions.cpp index 487319d..20a0257 100644 --- a/source/diameter/helpers/base/functions.cpp +++ b/source/diameter/helpers/base/functions.cpp @@ -30,8 +30,12 @@ U32 anna::diameter::helpers::base::functions::getResultCode(const anna::DataBloc if(db.getSize() < Message::HeaderLength) throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION); - anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); - const char * resultCodePtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__Result_Code); + //anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); + //const char * resultCodePtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__Result_Code); + + const char *avpsDB = db.getData() + Message::HeaderLength; + int avpsLen = db.getSize() - Message::HeaderLength; + const char * resultCodePtr = anna::diameter::codec::functions::findAVP(avpsDB, avpsLen, AVPID__Result_Code); if(resultCodePtr == NULL) throw anna::RuntimeException("Result-Code AVP not found in DataBlock provided", ANNA_FILE_LOCATION); @@ -125,8 +129,12 @@ std::string anna::diameter::helpers::base::functions::getSessionId(const anna::D if(db.getSize() < Message::HeaderLength) throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION); - anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); - const char * sessionIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__Session_Id); + //anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); + //const char * sessionIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__Session_Id); + const char *avpsDB = db.getData() + Message::HeaderLength; + int avpsLen = db.getSize() - Message::HeaderLength; + const char * sessionIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, avpsLen, AVPID__Session_Id); + if(sessionIdPtr == NULL) throw anna::RuntimeException("Session-Id AVP not found in DataBlock provided", ANNA_FILE_LOCATION); diff --git a/source/diameter/helpers/dcca/functions.cpp b/source/diameter/helpers/dcca/functions.cpp index b289889..89850a9 100644 --- a/source/diameter/helpers/dcca/functions.cpp +++ b/source/diameter/helpers/dcca/functions.cpp @@ -39,7 +39,9 @@ std::string anna::diameter::helpers::dcca::functions::getSubscriptionIdData(cons if(db.getSize() < Message::HeaderLength) throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION); - anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); + //anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); + const char *avpsDB = db.getData() + Message::HeaderLength; + int avpsLen = db.getSize() - Message::HeaderLength; std::string result = ""; bool found = false; int pos = 1; // first avp @@ -53,7 +55,8 @@ std::string anna::diameter::helpers::dcca::functions::getSubscriptionIdData(cons std::string _dataG /* grouped */, _data; while(!found) { - subscriptionIdPtr = diameter::codec::functions::findAVP(avpsDB, AVPID__Subscription_Id, pos); + //subscriptionIdPtr = diameter::codec::functions::findAVP(avpsDB, AVPID__Subscription_Id, pos); + subscriptionIdPtr = diameter::codec::functions::findAVP(avpsDB, avpsLen, AVPID__Subscription_Id, pos); if(!subscriptionIdPtr) return result; @@ -80,8 +83,12 @@ std::string anna::diameter::helpers::dcca::functions::getServiceContextId(const if(db.getSize() < Message::HeaderLength) throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION); - anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); - const char * serviceContextIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__Service_Context_Id); + //anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); + //const char * serviceContextIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__Service_Context_Id); + + const char *avpsDB = db.getData() + Message::HeaderLength; + int avpsLen = db.getSize() - Message::HeaderLength; + const char * serviceContextIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, avpsLen, AVPID__Service_Context_Id); if(serviceContextIdPtr == NULL) throw anna::RuntimeException("Service-Context-Id AVP not found in DataBlock provided", ANNA_FILE_LOCATION); diff --git a/source/diameter/helpers/ericsson/functions.cpp b/source/diameter/helpers/ericsson/functions.cpp index 77b899a..97cc551 100644 --- a/source/diameter/helpers/ericsson/functions.cpp +++ b/source/diameter/helpers/ericsson/functions.cpp @@ -29,7 +29,9 @@ std::string anna::diameter::helpers::ericsson::functions::getSCAPSubscriptionIdD if(db.getSize() < Message::HeaderLength) throw anna::RuntimeException("Not enough bytes to cover command header length", ANNA_FILE_LOCATION); - anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); + //anna::DataBlock avpsDB(db.getData() + Message::HeaderLength, db.getSize() - Message::HeaderLength); + const char *avpsDB = db.getData() + Message::HeaderLength; + int avpsLen = db.getSize() - Message::HeaderLength; std::string result = ""; bool found = false; int pos = 1; // first avp @@ -43,7 +45,8 @@ std::string anna::diameter::helpers::ericsson::functions::getSCAPSubscriptionIdD std::string _dataG /* grouped */, _data; while(!found) { - subscriptionIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__SCAP_Subscription_Id, pos); + //subscriptionIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, AVPID__SCAP_Subscription_Id, pos); + subscriptionIdPtr = anna::diameter::codec::functions::findAVP(avpsDB, avpsLen, AVPID__SCAP_Subscription_Id, pos); if(!subscriptionIdPtr) return result; diff --git a/source/diameter/stack/Dictionary.cpp b/source/diameter/stack/Dictionary.cpp index b94d46d..e369d43 100644 --- a/source/diameter/stack/Dictionary.cpp +++ b/source/diameter/stack/Dictionary.cpp @@ -201,7 +201,7 @@ void Dictionary::addAvp(const Avp & avp) throw(anna::RuntimeException) { ITEM_OVERWRITE("avp", avp, a_avps, a_avpNames); } - if(found = getAvp(avp.getName())) { + if((found = getAvp(avp.getName()))) { if(!a_allowUpdates) { std::string s_ex = "Cannot add an avp with an existing name:\n"; s_ex += avp.asString(); diff --git a/source/http/wims20/ServerSide.cpp b/source/http/wims20/ServerSide.cpp index b9caab6..64a6fbe 100644 --- a/source/http/wims20/ServerSide.cpp +++ b/source/http/wims20/ServerSide.cpp @@ -132,7 +132,7 @@ throw(RuntimeException) { return (anna_strncmp(value, "0x", 2) == 0) ? strtol(value + 2, NULL, 16) : atoi(value); } -/* Nos ha debido llegar algo así como: serviceID/guid/*{other_possible_levels} +/* Nos ha debido llegar algo asi como: serviceID/guid {other_possible_levels} */ void http::wims20::ServerSide::decodeHierarchy(const std::string& hierarchy) throw(RuntimeException) {