System test feature
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Thu, 27 Aug 2015 14:05:12 +0000 (16:05 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Mon, 7 Sep 2015 13:49:24 +0000 (15:49 +0200)
94 files changed:
example/comm/rrkClient/main.cpp
example/diameter/launcher/DEPLOY.sh
example/diameter/launcher/DEPLOY_clientAndServer.sh [deleted file]
example/diameter/launcher/DEPLOY_setups.sh [new file with mode: 0755]
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/MyCommunicator.cpp
example/diameter/launcher/MyCommunicator.hpp
example/diameter/launcher/MyCounterRecorder.cpp
example/diameter/launcher/MyCounterRecorder.hpp
example/diameter/launcher/MyDiameterEngine.hpp
example/diameter/launcher/MyDiameterEntity.cpp
example/diameter/launcher/MyDiameterEntity.hpp
example/diameter/launcher/MyHandler.cpp
example/diameter/launcher/MyLocalServer.cpp
example/diameter/launcher/MyLocalServer.hpp
example/diameter/launcher/ProgrammedAnswers.cpp
example/diameter/launcher/RealmNode.cpp
example/diameter/launcher/RealmNode.hpp
example/diameter/launcher/SConscript
example/diameter/launcher/deployments/basic/configure.sh
example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar-bad.hex [new symlink]
example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar2-bad.hex [new symlink]
example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/aar3-bad.hex [new symlink]
example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_1.tc [new file with mode: 0644]
example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_2.tc [new file with mode: 0644]
example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_3.tc [new file with mode: 0644]
example/diameter/launcher/deployments/ft-client/tests/experiment1/case_1.sh [deleted file]
example/diameter/launcher/deployments/ft-client/tests/experiment1/common.sh [deleted file]
example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar-bad.hex [deleted symlink]
example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar2-bad.hex [deleted symlink]
example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/aar3-bad.hex [deleted symlink]
example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_1.tc [deleted file]
example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_2.tc [deleted file]
example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_3.tc [deleted file]
example/diameter/launcher/deployments/ft-client/tests/experiment2/go.sh [deleted file]
example/diameter/launcher/deployments/ft-client/tests/go.sh [new file with mode: 0755]
example/diameter/launcher/deployments/test_examples/RxGx/01.CER_Gx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/02.CEA_Gx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/03.CER_Rx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/04.CEA_Rx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/05.CCR-I.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/06.CCA-I.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/07.AAR-flows.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/08.AAA-flows.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/09.RAR-install.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/10.RAA-install.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/11.CCR-U.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/12.CCA-U.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/13.STR_Rx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/14.STA_Rx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/15.RAR-remove.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/16.RAA-remove.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/17.CCR-T.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/18.CCA-T.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/19.DPR_Gx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/20.DPA_Gx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/21.DPR_Rx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/22.DPA_Rx.xml [new file with mode: 0644]
example/diameter/launcher/deployments/test_examples/RxGx/simple.sh [new file with mode: 0755]
example/diameter/launcher/deployments/test_examples/checkings.sh [new file with mode: 0755]
example/diameter/launcher/deployments/test_examples/cycle.sh [new file with mode: 0755]
example/diameter/launcher/deployments/test_examples/endsOk.sh [new file with mode: 0755]
example/diameter/launcher/main.cpp
example/diameter/launcher/resources/scripts/operation_signal.sh
example/diameter/launcher/testing/TestCase.cpp [new file with mode: 0644]
example/diameter/launcher/testing/TestCase.hpp [new file with mode: 0644]
example/diameter/launcher/testing/TestClock.cpp [new file with mode: 0644]
example/diameter/launcher/testing/TestClock.hpp [new file with mode: 0644]
example/diameter/launcher/testing/TestCondition.cpp [new file with mode: 0644]
example/diameter/launcher/testing/TestCondition.hpp [new file with mode: 0644]
example/diameter/launcher/testing/TestManager.cpp [new file with mode: 0644]
example/diameter/launcher/testing/TestManager.hpp [new file with mode: 0644]
example/diameter/launcher/testing/TestStep.cpp [new file with mode: 0644]
example/diameter/launcher/testing/TestStep.hpp [new file with mode: 0644]
example/diameter/launcher/testing/TestTimer.cpp [new file with mode: 0644]
example/diameter/launcher/testing/TestTimer.hpp [new file with mode: 0644]
example/diameter/pcapDecoder/main.cpp
include/anna/core/util/Tokenizer.hpp
include/anna/diameter.comm/OamModule.hpp
include/anna/diameter/codec/Message.hpp
include/anna/diameter/codec/functions.hpp
source/core/functions.cpp
source/core/util/String.cpp
source/core/util/Tokenizer.cpp
source/diameter.comm/ClientSession.cpp
source/diameter.comm/OamModule.cpp
source/diameter.comm/ServerSession.cpp
source/diameter/codec/functions.cpp
source/diameter/helpers/base/functions.cpp
source/diameter/helpers/dcca/functions.cpp
source/diameter/helpers/ericsson/functions.cpp
source/diameter/stack/Dictionary.cpp
source/http/wims20/ServerSide.cpp

index e0ebc50..d87014f 100644 (file)
@@ -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,...)");
 }
index f83ec69..3e3cbd9 100755 (executable)
@@ -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 (executable)
index 628c223..0000000
+++ /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 (executable)
index 0000000..3457bbf
--- /dev/null
@@ -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
+
index a21466a..eb4dfe7 100644 (file)
@@ -9,6 +9,8 @@
 // Standard
 #include <sstream>      // std::istringstream
 #include <iostream>     // std::cout
+#include <math.h> // ceil
+#include <climits>
 
 // Project
 #include <anna/timex/Engine.hpp>
 #include <anna/xml/xml.hpp>
 
 // Process
-#include "Launcher.hpp"
-#include "RealmNode.hpp"
-#include "MyDiameterEngine.hpp"
+#include <Launcher.hpp>
+#include <RealmNode.hpp>
+#include <MyDiameterEngine.hpp>
+#include <TestManager.hpp>
+#include <TestCase.hpp>
 
 
 #define SIGUSR2_TASKS_INPUT_FILENAME "./sigusr2.tasks.input"
 #define SIGUSR2_TASKS_OUTPUT_FILENAME "./sigusr2.tasks.output"
 
 
+
 const char *ServicesDTD = "\
 <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
 <!-- Diameter services DTD -->\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|<action>[|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|<order>          Updates current burst pointer position.";
-  result += "\n   burst|look|<order>          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|<id>|<command>[|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                           <id>: 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                           <command>: 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|<msecs>            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|<source_file>[|<step number>]";
+  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|<source_file>[|<step number>]";
+  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|<msecs>              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<fe/fc>|<condition>    Blocking step until condition is fulfilled. The message could";
+  result += "\n                                                          received from entity (waitfe) or from client (waitfc).";
+  result += "\n";
+  result += "\n                              wait<fe/fc>-regexp|<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                           <condition>: 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: #<step number>";
+  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|<amount>            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|<id>                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.<id>.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|<soft/hard>[|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|<value>'.";
+  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, "|", "<null>" /* 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 '<null>' artificial token to ease further checkings:
+  if (param1 == "<null>") param1 = "";
+  if (param2 == "<null>") param2 = "";
+  if (param3 == "<null>") param3 = "";
+  if (param4 == "<null>") param4 = "";
+  if (param5 == "<null>") param5 = "";
+  if (param6 == "<null>") param6 = "";
+  if (param7 == "<null>") param7 = "";
+  if (param8 == "<null>") param8 = "";
+  if (param9 == "<null>") param9 = "";
+  if (param10 == "<null>") 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|<order>              Updates current burst pointer position.
-    // burst|look|<order>              Show programmed burst message for order provided.
+    // burst|look|<order>              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|<id>|<command>[|parameters]   Add a new step to the test case ...
+    // test|ttps|<amount>                 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|<id>                     Updates current test pointer position.
+    // test|look[|id]                     Show programmed test case for id provided, current when missing ...
+    // test|reset|<soft/hard>[|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) : "<no limit>";
+      }
+      else {
+        result_msg = "in-progress limit amount: ";
+        limit = testManager.getInProgressLimit();
+        result_msg += (limit != UINT_MAX) ? anna::functions::asString(limit) : "<no 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|<id>|<command>
+      //             timeout|    <msecs>
+      //             sendxml2e|  <file>[|<step number>]
+      //             sendxml2c|  <file>[|<step number>]
+      //             delay|      [msecs]
+      //             wait<fe/fc>|[code]|[bitR]|[ResultCode]|[sessionId]|[hopByHop]|[msisdn]|[imsi]|[serviceContextId]
+      //      wait<fe/fc>-answer|<step number>
+      //      wait<fe/fc>-regexp|<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;
 }
index c8417fb..21c3d25 100644 (file)
@@ -20,8 +20,9 @@
 #include <anna/time/Date.hpp>
 
 // Process
-#include "MyCommunicator.hpp"
-#include "MyCounterRecorder.hpp"
+#include <MyCommunicator.hpp>
+#include <MyCounterRecorder.hpp>
+#include <TestManager.hpp>
 
 
 namespace anna {
@@ -35,6 +36,8 @@ namespace anna {
   }
 }
 
+class TestManager;
+
 // RealmNode resources
 class RealmNode;
 typedef std::map<std::string, RealmNode*> 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 &parameter, 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
index 6a85871..d3c1370 100644 (file)
@@ -12,7 +12,7 @@
 #include <anna/http/Transport.hpp>
 
 // Process
-#include "MyCommunicator.hpp"
+#include <MyCommunicator.hpp>
 
 
 void MyCommunicator::prepareAnswer(anna::diameter::codec::Message *answer, const anna::DataBlock &request) const throw() {
index d336c7d..4b2677c 100644 (file)
@@ -15,7 +15,7 @@
 #include <anna/diameter/codec/Message.hpp>
 
 // Process
-#include "MyHandler.hpp"
+#include <MyHandler.hpp>
 
 
 class MyCommunicator : public anna::comm::Communicator {
index 4f62f72..dd32652 100644 (file)
@@ -7,7 +7,7 @@
 
 
 // Process
-#include "MyCounterRecorder.hpp"
+#include <MyCounterRecorder.hpp>
 
 
 MyCounterRecorder::MyCounterRecorder(const std::string &fnp) : a_stream(-1), a_fileNamePrefix(fnp) {
index d283125..708c408 100644 (file)
@@ -15,7 +15,7 @@
 #include <time.h>
 
 // Process
-#include "MyCounterRecorderClock.hpp"
+#include <MyCounterRecorderClock.hpp>
 
 
 class MyCounterRecorder : public anna::oam::CounterRecorder {
index 13aae6c..29b3c1b 100644 (file)
@@ -13,8 +13,8 @@
 #include <anna/diameter.comm/Engine.hpp>
 
 // Process
-#include "MyDiameterEntity.hpp"
-#include "MyLocalServer.hpp"
+#include <MyDiameterEntity.hpp>
+#include <MyLocalServer.hpp>
 
 
 class MyDiameterEngine : public anna::diameter::comm::Engine {
index ad47b6f..50a9663 100644 (file)
 #include <anna/diameter/helpers/dcca/functions.hpp>
 
 // Process
-#include "MyDiameterEngine.hpp"
-#include "MyDiameterEntity.hpp"
-#include "MyLocalServer.hpp"
-#include "Launcher.hpp"
-#include "RealmNode.hpp"
+#include <MyDiameterEngine.hpp>
+#include <MyDiameterEntity.hpp>
+#include <MyLocalServer.hpp>
+#include <Launcher.hpp>
+#include <RealmNode.hpp>
+#include <TestManager.hpp>
 
 
 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 <Launcher&>(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);
     }
   }
 
index a9ad2c4..9476883 100644 (file)
@@ -13,7 +13,7 @@
 #include <anna/diameter.comm/Entity.hpp>
 
 // Process
-#include "ProgrammedAnswers.hpp"
+#include <ProgrammedAnswers.hpp>
 
 namespace anna {
   namespace diameter {
index f35c89f..9f893a7 100644 (file)
@@ -13,8 +13,8 @@
 #include <anna/http/Request.hpp>
 
 // Process
-#include "MyHandler.hpp"
-#include "Launcher.hpp"
+#include <MyHandler.hpp>
+#include <Launcher.hpp>
 
 
 void MyHandler::evRequest(anna::comm::ClientSocket& clientSocket, const anna::http::Request& request)
index 4bc8eba..8127d84 100644 (file)
 #include <anna/diameter.comm/Server.hpp>
 
 // Process
-#include "MyLocalServer.hpp"
-#include "MyDiameterEngine.hpp"
-#include "MyDiameterEntity.hpp"
-#include "Launcher.hpp"
-#include "RealmNode.hpp"
+#include <MyLocalServer.hpp>
+#include <MyDiameterEngine.hpp>
+#include <MyDiameterEntity.hpp>
+#include <Launcher.hpp>
+#include <RealmNode.hpp>
+#include <TestManager.hpp>
 
 
 void MyLocalServer::eventRequest(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message)
@@ -30,6 +31,9 @@ throw(anna::RuntimeException) {
   Launcher& my_app = static_cast <Launcher&>(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() : "<null client session>";  // 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);
     }
   }
 }
index c90d8f2..da4ed97 100644 (file)
@@ -13,7 +13,7 @@
 #include <anna/diameter.comm/LocalServer.hpp>
 
 // Process
-#include "ProgrammedAnswers.hpp"
+#include <ProgrammedAnswers.hpp>
 
 namespace anna {
   namespace diameter {
index 8e310d7..9356dac 100644 (file)
@@ -14,7 +14,7 @@
 #include <anna/diameter/codec/Engine.hpp>
 
 // Process
-#include "ProgrammedAnswers.hpp"
+#include <ProgrammedAnswers.hpp>
 
 
 void ProgrammedAnswers::clear () throw() {
index c61bbe4..630ab8f 100644 (file)
@@ -16,8 +16,8 @@
 #include <anna/xml/Compiler.hpp>
 
 // Process
-#include "RealmNode.hpp"
-#include "MyDiameterEngine.hpp"
+#include <RealmNode.hpp>
+#include <MyDiameterEngine.hpp>
 
 
 
@@ -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 += ")";
index cf2af40..a8a49fd 100644 (file)
@@ -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();
index c0dc64d..249a878 100644 (file)
@@ -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')
index 07ee3b1..7a3705b 100755 (executable)
@@ -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 (symlink)
index 0000000..97766e8
--- /dev/null
@@ -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 (symlink)
index 0000000..7c39b1a
--- /dev/null
@@ -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 (symlink)
index 0000000..5ba5f5d
--- /dev/null
@@ -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 (file)
index 0000000..693bc94
--- /dev/null
@@ -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 <avp name="Result-Code" data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
+CHECKPATTERN <avp name="Failed-AVP">( *)<avp name="Subscription-Id"
diff --git a/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_2.tc b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_2.tc
new file mode 100644 (file)
index 0000000..1727638
--- /dev/null
@@ -0,0 +1,13 @@
+# Bad length encoded on enumerated Flow-Status avp 2-level nested (Media-Component-Description -> 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 <avp name="Result-Code" data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
+CHECKPATTERN <avp name="Failed-AVP">( *)<avp name="Media-Component-Description"
+
+# TODO: check that the complete hierarchy to Flow-Status (the real bad avp)
diff --git a/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_3.tc b/example/diameter/launcher/deployments/ft-client/tests/ProtocolErrors/BadAARtoServer/case_3.tc
new file mode 100644 (file)
index 0000000..06b5efc
--- /dev/null
@@ -0,0 +1,11 @@
+# Bad value (15) encoded on enumerated Flow-Status avp (range 0-15) 2-level nested (Media-Component-Description -> 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 <avp name="Result-Code" data="5004" alias="DIAMETER_INVALID_AVP_VALUE"/>
+CHECKPATTERN <avp name="Failed-AVP">( *)<avp name="Media-Component-Description">( *)<avp name="Media-Sub-Component">( *)<avp name="Flow-Status" data="0" alias="ENABLED-UPLINK"/>
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 (executable)
index c610b4d..0000000
+++ /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 "<avp name=\"Result-Code\" data=\"5004\" alias=\"DIAMETER_INVALID_AVP_VALUE\"/>" ../../launcher.log.recvfe
-check_pattern "<avp name=\"Failed-AVP\">( *)<avp name=\"Subscription-Id\"" ../../launcher.log.recvfe
-
-# Test OK
-test_ok "Received answer to bad-aar message indicating Failed-AVP as Subscription-Id"
-
diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment1/common.sh b/example/diameter/launcher/deployments/ft-client/tests/experiment1/common.sh
deleted file mode 100755 (executable)
index d841491..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/bin/bash
-
-#############
-# FUNCTIONS #
-#############
-
-_exit () {
-  echo
-  echo $1
-  echo
-  exit 1
-}
-
-sigint_handler() {
-  _exit "Script interrupted. Cleanup & exit ..."
-}
-
-# $1: hex formatted file to send to the server
-send_xml () {
-  ../../operation.sh "sendxml|$1"
-}
-
-send_hex () {
-  sleep 1
-  ../../operation.sh "sendhex|$1"
-}
-
-# $1: pattern; $2: file
-check_pattern () {
-  echo "Matching pattern '$1' ..." 
-  cat $2 | tr '\n' ' ' | egrep "$1" >/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 "^</message>" >/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 (symlink)
index 4c9a36f..0000000
+++ /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 (symlink)
index 5a6586d..0000000
+++ /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 (symlink)
index 6705cc4..0000000
+++ /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 (file)
index 693bc94..0000000
+++ /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 <avp name="Result-Code" data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
-CHECKPATTERN <avp name="Failed-AVP">( *)<avp name="Subscription-Id"
diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_2.tc b/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_2.tc
deleted file mode 100644 (file)
index 1727638..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# Bad length encoded on enumerated Flow-Status avp 2-level nested (Media-Component-Description -> 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 <avp name="Result-Code" data="5014" alias="DIAMETER_INVALID_AVP_LENGTH"/>
-CHECKPATTERN <avp name="Failed-AVP">( *)<avp name="Media-Component-Description"
-
-# TODO: check that the complete hierarchy to Flow-Status (the real bad avp)
diff --git a/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_3.tc b/example/diameter/launcher/deployments/ft-client/tests/experiment2/ProtocolErrors/BadAARtoServer/case_3.tc
deleted file mode 100644 (file)
index 06b5efc..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# Bad value (15) encoded on enumerated Flow-Status avp (range 0-15) 2-level nested (Media-Component-Description -> 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 <avp name="Result-Code" data="5004" alias="DIAMETER_INVALID_AVP_VALUE"/>
-CHECKPATTERN <avp name="Failed-AVP">( *)<avp name="Media-Component-Description">( *)<avp name="Media-Sub-Component">( *)<avp name="Flow-Status" data="0" alias="ENABLED-UPLINK"/>
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 (executable)
index 44bd53f..0000000
+++ /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 "^</message>" >/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 <test cases parent directory (or .tc file)>"
-[ ! -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 (executable)
index 0000000..0bf18a2
--- /dev/null
@@ -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 "^</message>" >/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 <test cases parent directory (or .tc file)>"
+[ ! -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 (file)
index 0000000..b5caf20
--- /dev/null
@@ -0,0 +1,10 @@
+<message version="1" name="CER" application-id="0" hop-by-hop-id="722470" end-by-end-id="539510">
+   <avp name="Origin-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Host-IP-Address" data="1|192.168.14.42"/>
+   <avp name="Vendor-Id" data="193"/>
+   <avp name="Product-Name" data="ggsnNode"/>
+   <avp name="Firmware-Revision" data="1"/>
+</message>
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 (file)
index 0000000..06ce804
--- /dev/null
@@ -0,0 +1,21 @@
+<message version="1" name="CEA" application-id="0" hop-by-hop-id="722470" end-by-end-id="539510">
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="Host-IP-Address" data="1|192.168.12.40"/>
+   <avp name="Vendor-Id" data="193"/>
+   <avp name="Product-Name" data="SAPC"/>
+   <avp name="Supported-Vendor-Id" data="5535"/>
+   <avp name="Supported-Vendor-Id" data="10415"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Vendor-Specific-Application-Id">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Auth-Application-Id" data="16777238"/>
+   </avp>
+   <avp name="Vendor-Specific-Application-Id">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Auth-Application-Id" data="16777236"/>
+   </avp>
+   <avp name="Firmware-Revision" data="1"/>
+</message>
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 (file)
index 0000000..e79ce98
--- /dev/null
@@ -0,0 +1,10 @@
+<message version="1" name="CER" application-id="0" hop-by-hop-id="956775" end-by-end-id="968247">
+   <avp name="Origin-Host" data="afNodeHostname.afNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="afNodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Host-IP-Address" data="1|192.168.14.42"/>
+   <avp name="Vendor-Id" data="193"/>
+   <avp name="Product-Name" data="afNode"/>
+   <avp name="Firmware-Revision" data="1"/>
+</message>
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 (file)
index 0000000..7b9dd52
--- /dev/null
@@ -0,0 +1,21 @@
+<message version="1" name="CEA" application-id="0" hop-by-hop-id="956775" end-by-end-id="968247">
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="Host-IP-Address" data="1|192.168.12.40"/>
+   <avp name="Vendor-Id" data="193"/>
+   <avp name="Product-Name" data="SAPC"/>
+   <avp name="Supported-Vendor-Id" data="5535"/>
+   <avp name="Supported-Vendor-Id" data="10415"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Vendor-Specific-Application-Id">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Auth-Application-Id" data="16777238"/>
+   </avp>
+   <avp name="Vendor-Specific-Application-Id">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Auth-Application-Id" data="16777236"/>
+   </avp>
+   <avp name="Firmware-Revision" data="1"/>
+</message>
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 (file)
index 0000000..96a031a
--- /dev/null
@@ -0,0 +1,27 @@
+<message version="1" name="CC-Request" application-id="16777238" hop-by-hop-id="529097" end-by-end-id="419342">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="CC-Request-Number" data="0"/>
+   <avp name="CC-Request-Type" data="1" alias="INITIAL_REQUEST"/>
+   <avp name="Framed-IP-Address" hex-data="8132bb64"/>
+   <avp name="Bearer-Usage" data="0" alias="GENERAL"/>
+   <avp name="Subscription-Id">
+      <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
+      <avp name="Subscription-Id-Data" data="34600000001"/>
+   </avp>
+   <avp name="RAT-Type" data="1004" alias="EUTRAN"/>
+   <avp name="Called-Station-Id" data="defaultApnId"/>
+   <avp name="QoS-Negotiation" data="1" alias="QoS_NEGOTIATION_SUPPORTED"/>
+   <avp name="QoS-Upgrade" data="1" alias="QoS_UPGRADE_SUPPORTED"/>
+   <avp name="Supported-Features" flags="192">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="11"/>
+   </avp>
+   <avp name="IP-CAN-Type" data="5" alias="3GPP-EPS"/>
+</message>
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 (file)
index 0000000..885b867
--- /dev/null
@@ -0,0 +1,16 @@
+<message version="1" name="CC-Answer" application-id="16777238" hop-by-hop-id="529097" end-by-end-id="419342">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-State-Id" data="1441363963"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="CC-Request-Number" data="0"/>
+   <avp name="CC-Request-Type" data="1" alias="INITIAL_REQUEST"/>
+   <avp name="Supported-Features" flags="128">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="11"/>
+   </avp>
+   <avp name="Bearer-Control-Mode" data="0" alias="UE_ONLY"/>
+</message>
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 (file)
index 0000000..3e11df3
--- /dev/null
@@ -0,0 +1,68 @@
+<message version="1" name="AA-Request" application-id="16777236" hop-by-hop-id="187793" end-by-end-id="827675">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;afNodeHostname.afNodeHostRealm.com;1;690434"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="afNodeHostname.afNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="afNodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Framed-IP-Address" hex-data="8132bb64"/>
+   <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="1"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 129.50.187.100 2234 to 10.95.130.50 2234"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 129.50.187.100 2234"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 129.50.187.100 2235 to 10.95.130.50 2235"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 129.50.187.100 2235"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="0" alias="OTHER"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="3" alias="PRIORITY-THREE"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="2"/>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="1"/>
+         <avp name="Flow-Description" data="permit in 17 from 129.50.187.100 2236 to 10.95.130.50 2236"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 129.50.187.100 2236"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="0" alias="NO_INFORMATION"/>
+      </avp>
+      <avp name="Media-Sub-Component">
+         <avp name="Flow-Number" data="2"/>
+         <avp name="Flow-Description" data="permit in 17 from 129.50.187.100 2237 to 10.95.130.50 2237"/>
+         <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 129.50.187.100 2237"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Flow-Usage" data="1" alias="RTCP"/>
+      </avp>
+      <avp name="AF-Application-Identifier" hex-data="75726e25334175726e2d787878253341336770702d736572766963652e696d732e696373692e6d6d74656c"/>
+      <avp name="Media-Type" data="1" alias="VIDEO"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="4" alias="PRIORITY-FOUR"/>
+      <avp name="RR-Bandwidth" data="64000"/>
+      <avp name="RS-Bandwidth" data="64000"/>
+   </avp>
+   <avp name="AF-Charging-Identifier" hex-data="41462d4368617267696e674964656e746966696572"/>
+   <avp name="SIP-Forking-Indication" data="0" alias="SINGLE_DIALOGUE"/>
+   <avp name="Supported-Features" flags="192">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="19"/>
+   </avp>
+</message>
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 (file)
index 0000000..1392535
--- /dev/null
@@ -0,0 +1,13 @@
+<message version="1" name="AA-Answer" application-id="16777236" hop-by-hop-id="187793" end-by-end-id="827675">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;afNodeHostname.afNodeHostRealm.com;1;690434"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-State-Id" data="1441363963"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="Supported-Features" flags="128">
+      <avp name="Vendor-Id" data="10415"/>
+      <avp name="Feature-List-ID" data="1"/>
+      <avp name="Feature-List" data="19"/>
+   </avp>
+</message>
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 (file)
index 0000000..5c1d643
--- /dev/null
@@ -0,0 +1,124 @@
+<message version="1" name="RA-Request" p-bit="yes" application-id="16777238" hop-by-hop-id="771961614" end-by-end-id="771961614">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-State-Id" data="1441363963"/>
+   <avp name="Destination-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Re-Auth-Request-Type" data="0" alias="AUTHORIZE_ONLY"/>
+   <avp name="Destination-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="Charging-Rule-Install">
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c317c317c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="0" alias="DURATION"/>
+         <avp name="Offline" data="1" alias="ENABLE_OFFLINE"/>
+         <avp name="Online" data="0" alias="DISABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="Guaranteed-Bitrate-DL" data="32000"/>
+            <avp name="Guaranteed-Bitrate-UL" data="32000"/>
+            <avp name="QoS-Class-Identifier" data="1" alias="QCI_1"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="3"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 129.50.187.100 2234"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 129.50.187.100 2234"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c317c327c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="0" alias="DURATION"/>
+         <avp name="Offline" data="1" alias="ENABLE_OFFLINE"/>
+         <avp name="Online" data="0" alias="DISABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="Guaranteed-Bitrate-DL" data="1600"/>
+            <avp name="Guaranteed-Bitrate-UL" data="1600"/>
+            <avp name="QoS-Class-Identifier" data="1" alias="QCI_1"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="3"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 129.50.187.100 2235"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 129.50.187.100 2235"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c327c317c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="2" alias="DURATION_VOLUME"/>
+         <avp name="Offline" data="0" alias="DISABLE_OFFLINE"/>
+         <avp name="Online" data="1" alias="ENABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="Guaranteed-Bitrate-DL" data="208000"/>
+            <avp name="Guaranteed-Bitrate-UL" data="208000"/>
+            <avp name="QoS-Class-Identifier" data="2" alias="QCI_2"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="4"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 129.50.187.100 2236"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 129.50.187.100 2236"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c327c327c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="2" alias="DURATION_VOLUME"/>
+         <avp name="Offline" data="0" alias="DISABLE_OFFLINE"/>
+         <avp name="Online" data="1" alias="ENABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="Guaranteed-Bitrate-DL" data="10400"/>
+            <avp name="Guaranteed-Bitrate-UL" data="10400"/>
+            <avp name="QoS-Class-Identifier" data="2" alias="QCI_2"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="4"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 129.50.187.100 2237"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 129.50.187.100 2237"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+   </avp>
+</message>
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 (file)
index 0000000..8c7f5b5
--- /dev/null
@@ -0,0 +1,7 @@
+<message version="1" name="RA-Answer" p-bit="yes" application-id="16777238" hop-by-hop-id="771961614" end-by-end-id="771961614">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Origin-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-State-Id" data="1"/>
+</message>
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 (file)
index 0000000..29aad50
--- /dev/null
@@ -0,0 +1,18 @@
+<message version="1" name="CC-Request" application-id="16777238" hop-by-hop-id="771961615" end-by-end-id="771961615">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="CC-Request-Number" data="1"/>
+   <avp name="CC-Request-Type" data="2" alias="UPDATE_REQUEST"/>
+   <avp name="Bearer-Usage" data="0" alias="GENERAL"/>
+   <avp name="Subscription-Id">
+      <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
+      <avp name="Subscription-Id-Data" data="34600000001"/>
+   </avp>
+   <avp name="RAT-Type" data="1004" alias="EUTRAN"/>
+   <avp name="IP-CAN-Type" data="6" alias="Non-3GPP-EPS"/>
+</message>
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 (file)
index 0000000..27f8e3b
--- /dev/null
@@ -0,0 +1,116 @@
+<message version="1" name="CC-Answer" application-id="16777238" hop-by-hop-id="771961615" end-by-end-id="771961615">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-State-Id" data="1441363963"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="CC-Request-Number" data="1"/>
+   <avp name="CC-Request-Type" data="2" alias="UPDATE_REQUEST"/>
+   <avp name="Charging-Rule-Install">
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c317c317c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="0" alias="DURATION"/>
+         <avp name="Offline" data="1" alias="ENABLE_OFFLINE"/>
+         <avp name="Online" data="0" alias="DISABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="QoS-Class-Identifier" data="5" alias="QCI_5"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="5"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 129.50.187.100 2234"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2234 to 129.50.187.100 2234"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c317c327c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="0" alias="DURATION"/>
+         <avp name="Offline" data="1" alias="ENABLE_OFFLINE"/>
+         <avp name="Online" data="0" alias="DISABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="QoS-Class-Identifier" data="5" alias="QCI_5"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="5"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 129.50.187.100 2235"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2235 to 129.50.187.100 2235"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c327c317c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="2" alias="DURATION_VOLUME"/>
+         <avp name="Offline" data="0" alias="DISABLE_OFFLINE"/>
+         <avp name="Online" data="1" alias="ENABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="QoS-Class-Identifier" data="5" alias="QCI_5"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="5"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 129.50.187.100 2236"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2236 to 129.50.187.100 2236"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+      <avp name="Charging-Rule-Definition">
+         <avp name="Rating-Group" data="4"/>
+         <avp name="Flow-Status" data="2" alias="ENABLED"/>
+         <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c327c327c303030303030323235363534343736303436383530333138"/>
+         <avp name="Metering-Method" data="2" alias="DURATION_VOLUME"/>
+         <avp name="Offline" data="0" alias="DISABLE_OFFLINE"/>
+         <avp name="Online" data="1" alias="ENABLE_ONLINE"/>
+         <avp name="Precedence" data="0"/>
+         <avp name="Reporting-Level" data="1" alias="RATING_GROUP_LEVEL"/>
+         <avp name="QoS-Information">
+            <avp name="Max-Requested-Bandwidth-DL" data="128000"/>
+            <avp name="Max-Requested-Bandwidth-UL" data="128000"/>
+            <avp name="QoS-Class-Identifier" data="5" alias="QCI_5"/>
+            <avp name="Allocation-Retention-Priority">
+               <avp name="Priority-Level" data="5"/>
+            </avp>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 129.50.187.100 2237"/>
+            <avp name="Flow-Direction" data="2" alias="UPLINK"/>
+         </avp>
+         <avp name="Flow-Information">
+            <avp name="Flow-Description" data="permit out 17 from 10.95.130.50 2237 to 129.50.187.100 2237"/>
+            <avp name="Flow-Direction" data="1" alias="DOWNLINK"/>
+         </avp>
+      </avp>
+   </avp>
+</message>
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 (file)
index 0000000..0f823dc
--- /dev/null
@@ -0,0 +1,10 @@
+<message version="1" name="ST-Request" application-id="16777236" hop-by-hop-id="187793" end-by-end-id="827675">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;afNodeHostname.afNodeHostRealm.com;1;690434"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="afNodeHostname.afNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="afNodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="Termination-Cause" data="1" alias="LOGOUT"/>
+</message>
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 (file)
index 0000000..24038dc
--- /dev/null
@@ -0,0 +1,7 @@
+<message version="1" name="ST-Answer" application-id="16777236" hop-by-hop-id="187793" end-by-end-id="827675">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;afNodeHostname.afNodeHostRealm.com;1;690434"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-State-Id" data="1441363963"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+</message>
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 (file)
index 0000000..3840a18
--- /dev/null
@@ -0,0 +1,16 @@
+<message version="1" name="RA-Request" p-bit="yes" application-id="16777238" hop-by-hop-id="771961615" end-by-end-id="771961615">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-State-Id" data="1441363963"/>
+   <avp name="Destination-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Re-Auth-Request-Type" data="0" alias="AUTHORIZE_ONLY"/>
+   <avp name="Destination-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="Charging-Rule-Remove">
+      <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c317c317c303030303030323235363534343736303436383530333138"/>
+      <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c317c327c303030303030323235363534343736303436383530333138"/>
+      <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c327c317c303030303030323235363534343736303436383530333138"/>
+      <avp name="Charging-Rule-Name" hex-data="4d4d54656c7c327c327c303030303030323235363534343736303436383530333138"/>
+   </avp>
+</message>
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 (file)
index 0000000..53a2386
--- /dev/null
@@ -0,0 +1,7 @@
+<message version="1" name="RA-Answer" p-bit="yes" application-id="16777238" hop-by-hop-id="771961615" end-by-end-id="771961615">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Origin-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-State-Id" data="1"/>
+</message>
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 (file)
index 0000000..84a790a
--- /dev/null
@@ -0,0 +1,18 @@
+<message version="1" name="CC-Request" application-id="16777238" hop-by-hop-id="771961616" end-by-end-id="771961616">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Destination-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Destination-Realm" data="operatorRealm.com"/>
+   <avp name="Origin-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-State-Id" data="1"/>
+   <avp name="CC-Request-Number" data="2"/>
+   <avp name="CC-Request-Type" data="3" alias="TERMINATION_REQUEST"/>
+   <avp name="Bearer-Usage" data="0" alias="GENERAL"/>
+   <avp name="Subscription-Id">
+      <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
+      <avp name="Subscription-Id-Data" data="34600000001"/>
+   </avp>
+   <avp name="RAT-Type" data="1004" alias="EUTRAN"/>
+   <avp name="Termination-Cause" data="1" alias="LOGOUT"/>
+</message>
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 (file)
index 0000000..f6a6808
--- /dev/null
@@ -0,0 +1,10 @@
+<message version="1" name="CC-Answer" application-id="16777238" hop-by-hop-id="771961616" end-by-end-id="771961616">
+   <avp name="Session-Id" data="tc_01_MMTel_DynamicQualification;ggsnNodeHostname.ggsnNodeHostRealm.com;1;629571"/>
+   <avp name="Auth-Application-Id" data="16777238"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-State-Id" data="1441363963"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+   <avp name="CC-Request-Number" data="2"/>
+   <avp name="CC-Request-Type" data="3" alias="TERMINATION_REQUEST"/>
+</message>
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 (file)
index 0000000..b277506
--- /dev/null
@@ -0,0 +1,5 @@
+<message version="1" name="DPR" application-id="0" hop-by-hop-id="118544" end-by-end-id="901132">
+   <avp name="Origin-Host" data="ggsnNodeHostname.ggsnNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="ggsnNodeHostRealm.com"/>
+   <avp name="Disconnect-Cause" data="2" alias="DO_NOT_WANT_TO_TALK_TO_YOU"/>
+</message>
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 (file)
index 0000000..f4a2435
--- /dev/null
@@ -0,0 +1,5 @@
+<message version="1" name="DPA" application-id="0" hop-by-hop-id="118544" end-by-end-id="901132">
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+</message>
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 (file)
index 0000000..17896d0
--- /dev/null
@@ -0,0 +1,5 @@
+<message version="1" name="DPR" application-id="0" hop-by-hop-id="640659" end-by-end-id="633282">
+   <avp name="Origin-Host" data="afNodeHostname.afNodeHostRealm.com"/>
+   <avp name="Origin-Realm" data="afNodeHostRealm.com"/>
+   <avp name="Disconnect-Cause" data="2" alias="DO_NOT_WANT_TO_TALK_TO_YOU"/>
+</message>
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 (file)
index 0000000..76fcc1c
--- /dev/null
@@ -0,0 +1,5 @@
+<message version="1" name="DPA" application-id="0" hop-by-hop-id="640659" end-by-end-id="633282">
+   <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
+   <avp name="Origin-Host" data="sapcOwnHostId.operatorRealm.com"/>
+   <avp name="Origin-Realm" data="operatorRealm.com"/>
+</message>
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 (executable)
index 0000000..765401c
--- /dev/null
@@ -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 (executable)
index 0000000..11d0c70
--- /dev/null
@@ -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 (executable)
index 0000000..a16a62e
--- /dev/null
@@ -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 (executable)
index 0000000..b355ea1
--- /dev/null
@@ -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
+
index 8054a3b..309a7ef 100644 (file)
@@ -15,7 +15,7 @@
 #include <anna/http/functions.hpp>
 
 // Process
-#include "Launcher.hpp"
+#include <Launcher.hpp>
 
 
 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.<concept id>.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).");
 
index c462a7e..8c9ce85 100755 (executable)
@@ -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 (file)
index 0000000..ccb9b42
--- /dev/null
@@ -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 <string>
+#include <fstream>
+
+// Project
+#include <anna/xml/Compiler.hpp>
+#include <anna/diameter/defines.hpp>
+#include <anna/diameter/helpers/dcca/defines.hpp>
+#include <anna/diameter/codec/functions.hpp>
+#include <anna/diameter/helpers/base/functions.hpp>
+#include <anna/diameter/helpers/dcca/functions.hpp>
+#include <anna/core/util/Millisecond.hpp>
+#include <anna/core/tracing/Logger.hpp>
+
+// Process
+#include <TestCase.hpp>
+#include <TestManager.hpp>
+
+
+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<event_t>::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<TestStep*>::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<TestStep*>::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<TestStep*>::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<TestStep*>::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<TestStepWait*>(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<TestStepSendxml*>(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 &regexp) 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<TestStep*>::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<TestStep*>::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 (file)
index 0000000..a3c48f0
--- /dev/null
@@ -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 <string>
+#include <vector>
+
+// Project
+#include <anna/core/DataBlock.hpp>
+#include <anna/xml/Node.hpp>
+#include <anna/diameter/defines.hpp>
+
+// Process
+#include <TestStep.hpp>
+
+
+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<event_t> 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 &regexp) 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<TestStep*> a_steps;
+  std::vector<TestStep*>::const_iterator a_stepsIt;
+  std::map<anna::diameter::HopByHop, TestStep*> 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 (file)
index 0000000..885e87e
--- /dev/null
@@ -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 <string>
+
+// Process
+#include <TestClock.hpp>
+#include <Launcher.hpp>
+
+
+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 (file)
index 0000000..e007cb6
--- /dev/null
@@ -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 <anna/core/core.hpp>
+#include <anna/timex/Clock.hpp>
+
+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 (file)
index 0000000..a772ab0
--- /dev/null
@@ -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 <anna/xml/Node.hpp>
+#include <anna/xml/Compiler.hpp>
+#include <anna/diameter/defines.hpp>
+#include <anna/diameter/codec/functions.hpp>
+#include <anna/diameter/helpers/base/functions.hpp>
+
+#include <anna/diameter/helpers/dcca/defines.hpp>
+#include <anna/diameter/helpers/dcca/functions.hpp>
+#include <anna/core/util/defines.hpp>
+
+// Process
+#include <TestCondition.hpp>
+#include <Launcher.hpp>
+
+
+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 <Launcher&>(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 (file)
index 0000000..7ff58d9
--- /dev/null
@@ -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 <string>
+
+// Project
+#include <anna/core/DataBlock.hpp>
+
+
+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 &regexp) 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 (file)
index 0000000..40d8286
--- /dev/null
@@ -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 <climits>
+
+// Project
+#include <anna/xml/Compiler.hpp>
+#include <anna/xml/Node.hpp>
+#include <anna/core/tracing/Logger.hpp>
+#include <anna/app/functions.hpp>
+#include <anna/timex/Engine.hpp>
+#include <anna/diameter/helpers/base/functions.hpp>
+#include <anna/diameter.comm/ClientSession.hpp>
+#include <anna/diameter.comm/ServerSession.hpp>
+
+// Process
+#include <TestManager.hpp>
+#include <TestCase.hpp>
+#include <TestClock.hpp>
+#include <Launcher.hpp>
+
+
+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<std::string /* session id's */, TestCase*>::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*>(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::timex::Engine> (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 <TestTimer*>(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 <Launcher&>(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<TestCase*>(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<std::string /* session id's */, TestCase*>::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 <Launcher&>(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<anna::diameter::comm::ClientSession*>(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 <Launcher&>(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<anna::diameter::comm::ServerSession*>(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", "<no limit>");
+  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 (file)
index 0000000..313e15f
--- /dev/null
@@ -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 <anna/config/defines.hpp>
+#include <anna/core/util/Recycler.hpp>
+#include <anna/core/Singleton.hpp>
+#include <anna/timex/TimeEventObserver.hpp>
+#include <anna/core/RuntimeException.hpp>
+#include <anna/core/DataBlock.hpp>
+
+// Process
+#include <TestTimer.hpp>
+
+
+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<unsigned int /* test case id */, TestCase*> test_pool_t;
+typedef std::map<unsigned int /* test case id */, TestCase*>::const_iterator test_pool_it;
+typedef std::map<unsigned int /* test case id */, TestCase*>::iterator test_pool_nc_it;
+
+
+/**
+   Timer Manager for testing system
+*/
+class TestManager : public anna::timex::TimeEventObserver, public anna::Singleton <TestManager> {
+  typedef anna::Recycler <TestTimer> 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<std::string /* session id's */, TestCase*> 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 <TestManager>;
+  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 (file)
index 0000000..076a227
--- /dev/null
@@ -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 <string>
+
+// Project
+#include <anna/xml/Compiler.hpp>
+#include <anna/core/util/Millisecond.hpp>
+#include <anna/diameter.comm/Message.hpp>
+#include <anna/core/tracing/Logger.hpp>
+#include <anna/diameter/codec/functions.hpp>
+#include <anna/diameter/helpers/base/functions.hpp>
+
+// Process
+#include <RealmNode.hpp>
+#include <MyDiameterEntity.hpp>
+#include <MyLocalServer.hpp>
+#include <TestStep.hpp>
+#include <Launcher.hpp>
+#include <TestCase.hpp>
+#include <TestManager.hpp>
+#include <TestTimer.hpp>
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 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 = "<empty>";
+  }
+  else {
+    msg = "\n"; msg += a_message.asString(); msg += "\n";
+    // Helper
+    try {
+      Launcher& my_app = static_cast <Launcher&>(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 &regexp) 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 <Launcher&>(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 (file)
index 0000000..90b64aa
--- /dev/null
@@ -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 <string>
+#include <vector>
+
+// Project
+#include <anna/core/DataBlock.hpp>
+#include <anna/xml/Node.hpp>
+
+// Process
+#include <TestCondition.hpp>
+
+
+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 &regexp) 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 (file)
index 0000000..abe8b4d
--- /dev/null
@@ -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 <anna/core/tracing/Logger.hpp>
+
+// Process
+#include <TestTimer.hpp>
+#include <TestCase.hpp>
+
+// Standard
+#include <iostream>
+
+
+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: <null>";
+//  }
+
+//  switch(getType()) {
+//    case Type::TimeLeft:
+//      //result += " | xxxxx: <null>";
+//      break;
+//    case Type::Delay:
+//      //result += " | xxxxx: <null>";
+//      break;
+//  }
+
+  return result += " }";
+}
+
diff --git a/example/diameter/launcher/testing/TestTimer.hpp b/example/diameter/launcher/testing/TestTimer.hpp
new file mode 100644 (file)
index 0000000..3f1b5eb
--- /dev/null
@@ -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 <anna/core/RuntimeException.hpp>
+#include <anna/core/Allocator.hpp>
+#include <anna/timex/Transaction.hpp>
+
+
+
+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 <TestStep*>(getContext()); }
+  const TestStep* getTestCaseStep() const throw() { return reinterpret_cast <const TestStep*>(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<TestTimer>;
+};
+
+#endif
+
index 32e9dcb..377b317 100644 (file)
@@ -18,7 +18,6 @@
 #include <map>
 
 #include <anna/core/DataBlock.hpp>
-#include <anna/core/util/Tokenizer.hpp>
 #include <anna/core/functions.hpp>
 #include <anna/core/tracing/Logger.hpp>
 #include <anna/core/tracing/TraceWriter.hpp>
index 693b2cf..8290775 100644 (file)
@@ -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
+     '<null>' 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
   /**
index 6566180..7567eff 100644 (file)
@@ -106,6 +106,11 @@ public:
       RequestSentOnClientSessionExpired,
       RequestSentOnServerSessionExpired,
 
+      // retransmissions
+      RequestRetransmitted,
+      RequestRetransmittedOnClientSession,
+      RequestRetransmittedOnServerSession,
+
       // unknown received answers
       AnswerReceivedUnknown,
       AnswerReceivedOnClientSessionUnknown,
index 6ec6e3a..a3916f7 100644 (file)
@@ -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.
index 770fcc1..44a8f15 100644 (file)
@@ -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);
 
 
 
index 6a660df..f75902d 100644 (file)
@@ -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];
   }
index 8f94116..30a7ce5 100644 (file)
@@ -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;
 }
index b4f6d12..d49f6ed 100644 (file)
 #include <anna/core/functions.hpp>
 #include <anna/config/defines.hpp>
 
+// temporary
+#include <iostream>
+
 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)
index 6a535a3..83c286c 100644 (file)
@@ -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);
 }
index 52a37b6..eab433c 100644 (file)
@@ -54,6 +54,9 @@ anna_assign_enum(anna::diameter::comm::OamModule::Counter) = { \
     "RequestSentExpired", \
     "RequestSentOnClientSessionExpired", \
     "RequestSentOnServerSessionExpired", \
+    "RequestRetransmitted", \
+    "RequestRetransmittedOnClientSession", \
+    "RequestRetransmittedOnServerSession", \
     "AnswerReceivedUnknown", \
     "AnswerReceivedOnClientSessionUnknown", \
     "AnswerReceivedOnServerSessionUnknown", \
index a615c78..1e2728c 100644 (file)
@@ -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);
 }
index a58372d..767d960 100644 (file)
@@ -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) {
index 487319d..20a0257 100644 (file)
@@ -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);
index b289889..89850a9 100644 (file)
@@ -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);
index 77b899a..97cc551 100644 (file)
@@ -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;
 
index b94d46d..e369d43 100644 (file)
@@ -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();
index b9caab6..64a6fbe 100644 (file)
@@ -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) {