Improved wait for regexp operations
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Sun, 2 Apr 2017 18:29:06 +0000 (20:29 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Sun, 2 Apr 2017 18:29:06 +0000 (20:29 +0200)
16 files changed:
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/resources/hex_examples/aaa.hex [new file with mode: 0644]
example/diameter/launcher/resources/hex_examples/aar.hex
example/diameter/launcher/resources/scripts/operation_signal.sh
example/diameter/launcher/resources/scripts/tinyTestcase.sh
example/diameter/launcher/resources/xml_examples/aaa.xml
example/diameter/launcher/resources/xml_examples/aar.xml
example/diameter/launcher/resources/xml_examples/regexp_for_aar.xml [new file with mode: 0644]
include/anna/testing/TestCase.hpp
include/anna/testing/TestCondition.hpp
include/anna/testing/TestStep.hpp
source/testing/TestCase.cpp
source/testing/TestCondition.cpp
source/testing/TestManager.cpp
source/testing/TestStep.cpp

index b141865..0fcfd52 100644 (file)
@@ -12,6 +12,7 @@
 #include <math.h> // ceil
 #include <climits>
 #include <unistd.h> // chdir
+#include <regex>
 
 // Project
 #include <anna/timex/Engine.hpp>
@@ -792,8 +793,7 @@ throw(anna::RuntimeException) {
   a_communicator->accept();
 }
 
-
-bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw() {
+bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw(anna::RuntimeException) {
   // Get hex string
   static char buffer[8192];
   std::ifstream infile(pathfile.c_str(), std::ifstream::in);
@@ -808,7 +808,8 @@ bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBl
     msg += hexString;
     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
     );
-    anna::functions::fromHexString(hexString, db);
+
+    anna::functions::fromHexString(hexString, db); // could launch exception
     // Close file
     infile.close();
     return true;
@@ -817,6 +818,21 @@ bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBl
   return false;
 }
 
+bool Launcher::getContentFromFile(const std::string &pathfile, std::string &content) const throw(anna::RuntimeException) {
+
+  std::ifstream inFile(pathfile.c_str(), std::ifstream::in);
+  if(!inFile.good()) {
+    throw RuntimeException(anna::functions::asString("Unable to open file '%s'", pathfile.c_str()), ANNA_FILE_LOCATION);
+  }
+
+  std::stringstream strStream;
+  strStream << inFile.rdbuf(); //read the file
+  content = strStream.str(); // holds the content of the file
+  inFile.close();
+
+  return true;
+}
+
 void Launcher::resetStatistics() throw() {
   if (a_workingNode) {
     a_workingNode->getCommEngine()->resetStatistics();
@@ -1222,16 +1238,6 @@ std::string Launcher::help() const throw() {
   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                                                         A value of 0 could be used as a dummy step.";
-  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                              sh-command|<script>        External execution for script/executable via shell through a dedicated";
   result += "\n                                                          thread, providing the command and parameters. You could use dynamic";
@@ -1250,12 +1256,35 @@ std::string Launcher::help() const throw() {
   result += "\n                                                          with the possible outputs from the scripts. You could also put your";
   result += "\n                                                          job in background although sh-command will return 0-value immediately.";
   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).";
+  result += "\n                              wait<fe/fc>-hex|<source_file>[|strict]";
+  result += "\n                                                         Wait condition, from entity (waitfe-hex) or client (waitfc-hex) to";
+  result += "\n                                                          match the hexadecimal representation for received messages against";
+  result += "\n                                                          source file (hex format). Fix mode must be enabled to avoid unexpected";
+  result += "\n                                                          matching behaviour. Specify 'strict' to use the hex content 'as is'.";
+  result += "\n                                                          If not, the hex content will be understood as whole message and then,";
+  result += "\n                                                          borders will be added (^<content>$) and sequence information bypassed";
+  result += "\n                                                          even for diameter answers.";
+  result += "\n";
+  result += "\n                              wait<fe/fc>-xml|<source_file>[|strict]";
+  result += "\n                                                         Wait condition from entity (waitfe-xml) or client (waitfc-xml) to";
+  result += "\n                                                          match the serialized xml content for received messages against";
+  result += "\n                                                          source file (xml representation). Fix mode must be enabled to avoid";
+  result += "\n                                                          unexpected matching behaviour. If you need a strict matching you";
+  result += "\n                                                          must add parameter 'strict', if not, regexp is built ignoring sequence";
+  result += "\n                                                          information (hop-by-hop-id=\"[0-9]+\" end-to-end-id=\"[0-9]+\").";
+  result += "\n                                                          All LF codes will be internally removed when comparison is executed";
+  result += "\n                                                          in order to ease xml content configuration.";
+  result += "\n";
+  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                                                         CPU cost is lower than former 'wait<fe/fc>-<xml|hex>' variants.";
+  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";
@@ -1268,50 +1297,50 @@ std::string Launcher::help() const throw() {
   //  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 host 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                                                       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 host 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                                                       Condition format:";
   result += "\n";
-  result += "\n                                           [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]";
+  result += "\n                                                          [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]";
   result += "\n";
-  result += "\n                                             code: integer number";
-  result += "\n                                             bitR: 1 (request), 0 (answer)";
-  result += "\n                                             hopByHop: integer number or request send step reference: #<step number>";
+  result += "\n                                                            code: integer number";
+  result += "\n                                                            bitR: 1 (request), 0 (answer)";
+  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                                                                      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                                             applicationId: integer number";
-  result += "\n                                             sessionId: string";
-  result += "\n                                             resultCode: integer number";
-  result += "\n                                             msisdn: string";
-  result += "\n                                             imsi: string";
-  result += "\n                                             serviceContextId: string";
+  result += "\n                                                            applicationId: integer number";
+  result += "\n                                                            sessionId: string";
+  result += "\n                                                            resultCode: integer number";
+  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                                                       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 host 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                                                          - 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 host 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";
@@ -2169,7 +2198,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       //             delay|      [msecs]
       //             wait<fe/fc>|[code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
       //      wait<fe/fc>-answer|<step number>
-      //      wait<fe/fc>-regexp|<regexp>
+      //      wait<fe/fc>-xml   |<source_file>[|strict]
+      //      wait<fe/fc>-hex   |<source_file>[|strict]
       if(param2 == "") throw anna::RuntimeException("Missing command for test id operation", ANNA_FILE_LOCATION);
 
       // Commands:
@@ -2216,16 +2246,52 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
           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)
+      else if ((param2 == "waitfe-hex")||(param2 == "waitfc-hex")) {
+        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 != "") {
-          bool fromEntity = (param2.substr(4,2) == "fe");
-          testManager.getTestCase(id)->addWaitRegexp(fromEntity, param3);
+        if(param3 == "") throw anna::RuntimeException(anna::functions::asString("Missing hex file for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
+
+        // Get DataBlock from file with hex content:
+        if(!getDataBlockFromHexFile(param3, db_aux))
+          throw anna::RuntimeException("Error reading hex content from file provided", ANNA_FILE_LOCATION);
+
+        // Hexadecimal representation read from file:
+        std::string regexp = anna::functions::asHexString(db_aux);
+
+        // optional 'full':
+        if(param4 != "strict") {
+          //// If request, we will ignore sequence data:
+          //if (anna::diameter::codec::functions::requestBit(db_aux))
+            regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
+
+          regexp.insert(0, "^");
+          regexp += "$";
         }
-        else {
-          throw anna::RuntimeException(anna::functions::asString("Missing condition for '%s' command in test id operation", param2.c_str()), ANNA_FILE_LOCATION);
+
+        bool fromEntity = (param2.substr(4,2) == "fe");
+        testManager.getTestCase(id)->addWaitRegexpHex(fromEntity, regexp);
+      }
+      else if ((param2 == "waitfe-xml")||(param2 == "waitfc-xml")) {
+        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);
+
+        // Get xml content from file:
+        std::string regexp;
+        if(!getContentFromFile(param3, regexp))
+          throw anna::RuntimeException("Error reading xml content from file provided", ANNA_FILE_LOCATION);
+
+        // optional 'full':
+        if(param4 != "strict") {
+          std::string s_from = "hop-by-hop-id=\"[0-9]+\" end-to-end-id=\"[0-9]+\"";
+          std::string s_to = s_from;
+          regexp = std::regex_replace (regexp, std::regex(s_from), s_to);
+          //regexp.insert(0, "^");
+          //regexp += "$";
         }
+
+        bool fromEntity = (param2.substr(4,2) == "fe");
+        testManager.getTestCase(id)->addWaitRegexpXml(fromEntity, regexp);
       }
       else if (param2 == "sh-command") {
         // Allow pipes in command:
index 2d5b3a7..7c3596c 100644 (file)
@@ -119,7 +119,8 @@ public:
   anna::xml::Node* statsAsXML(anna::xml::Node* parent) const throw();
 
   // helpers
-  bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw();
+  bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw(anna::RuntimeException);
+  bool getContentFromFile(const std::string &pathfile, std::string &content) const throw(anna::RuntimeException);
 
   friend class TestManager;
 };
diff --git a/example/diameter/launcher/resources/hex_examples/aaa.hex b/example/diameter/launcher/resources/hex_examples/aaa.hex
new file mode 100644 (file)
index 0000000..a6a423b
--- /dev/null
@@ -0,0 +1 @@
+010000c400000109010000140000000000000000000001074000003874657374313b61664e6f6465486f73746e616d652e6e6f6465486f73745265616c6d2e636f6d3b313b38303333343530000001024000000c0100001400000108400000236f776e486f737449642e6f70657261746f725265616c6d2e636f6d0000000128400000196f70657261746f725265616c6d2e636f6d00000000000129400000200000010a4000000c000028af0000012a4000000c000013c7000001164000000c55273844
index 9a2aeb9..150cbfc 100644 (file)
@@ -1 +1,2 @@
-01000150c000010901000014000000000000000000000107400000246f6373333b313333323737343433303b313b31333332373734343330000001024000000c01000014000001084000000c4f435333000001284000000c4f4353330000011b4000000c4f435333000001254000000c4f435333000001f8c000000f000028af3132320000000205c000006c000028af00000206c0000010000028af00000000000001f8c000000f000028af3132320000000204c0000010000028af0000007f00000203c0000010000028af00000085000001ffc0000010000028af00000002000001ca80000010000032db000000000000020fc0000010000028af00000000000001bb40000028000001c24000000c00000000000001bc4000001136323630333730393900000000000008400000133139322e3136382e302e31000000001e400000145741502e4d4f564953544152
+01000150c000010901000014000000000000000000000107400000246f6373333b313333323737343433303b313b31333332373734343330000001024000000c01000014000001084000000c4f435333000001284000000c4f4353330000011b4000000c4f435333000001254000000c4f435333000001f8c000000f000028af3132320000000205c000006c000028af00000206c0000010000028af00000000000001f8c000000f000028af3132320000000204c0000010000028af0000007f00000203c0000010000028af00000085000001ffc0000010000028af00000002000001ca80000010000032db000000000000020fc0000010000028af00000000000001bb40000028000001c24000000c00000000000001bc4000001136323630333730393900000000000008000000133139322e3136382e302e31000000001e000000145741502e4d4f564953544152
+
index eb0eb8e..4a9d0a7 100755 (executable)
@@ -21,7 +21,7 @@ usage() {
   echo "       -h|--help:             this usage help."
   echo "       -t|--timeout <value>:  timeout for operation in seconds."
   echo "                              Defaults to $TIMEOUT__dflt seconds if not provided."
-  echo "       -f|--file <file>:      file with one operation per line."
+  echo "       -f|--file <file>:      file with one operation per line (comments # allowed)."
   echo "       -p|--ping:             Check the target process id."
   echo "                              Returns 1 (dead) or 0 (alive)."
   echo "       operation:             quoted operation string. Will be ignored if file"
index 5beaa12..23ec5a4 100755 (executable)
@@ -93,8 +93,10 @@ update_testcase () {
   local adml_type=$4
 
   local wait_command=waitfe
+  # wait xml variant:
+  local waitxml_command=waitfe-xml
   local send_command=sendxml2e
-  [ "$adml_type" = "server" ] && { wait_command=waitfc ; send_command=sendxml2c ; }
+  [ "$adml_type" = "server" ] && { wait_command=waitfc ; waitxml_command=waitfc-xml ; send_command=sendxml2c ; }
 
 
   # Ignore disconnect peer messages on testcase format (they have no session-id):
@@ -107,6 +109,8 @@ update_testcase () {
   local lines=$(wc -l $TESTCASE_BN | awk '{ print $1 }')
 
   local s_wait="test|1|$wait_command|$code|$((1-isrequest))"
+  # wait xml variant:
+  local s_waitxml="test|1|$waitxml_command|$xml"
   [ -n "$sessionid" ] && s_wait="${s_wait}|||${sessionid}"
 
   local s_send="test|1|$send_command|$xml"
@@ -141,7 +145,8 @@ update_testcase () {
       # Wait the answer:
       s_wait="${s_wait}|${rc}"
     fi
-    echo "$s_wait" >> $TESTCASE_BN
+    echo "$s_waitxml" >> $TESTCASE_BN
+    echo "#$s_wait" >> $TESTCASE_BN
   else
     local next_step_number=$((lines+1))
 
@@ -298,7 +303,7 @@ do
     echo $oh >> cers_4_starting_origin_hosts
     echo "Missing CER: `basename $cer` (a basic template has been created, please edit & fix the unknowns)"
         cat << EOF > $cer
-<message version="1" name="CER" application-id="0" hop-by-hop-id="1" end-by-end-id="1">
+<message version="1" name="CER" application-id="0" hop-by-hop-id="1" end-to-end-id="1">
    <avp name="Origin-Host" data="$oh"/>
    <avp name="Origin-Realm" data="$(echo $oh | cut -d\. -f2-) $WHAT"/>
    <avp name="Auth-Application-Id" data="16777236 $WHAT 16777238 $WHAT"/>
@@ -321,7 +326,7 @@ do
     echo $oh >> ceas_4_establishing_origin_hosts
     echo "Missing CEA: `basename $cea` (a basic template has been created, please edit & fix the unknowns)"
         cat << EOF > $cea
-<message version="1" name="CEA" application-id="0" hop-by-hop-id="1" end-by-end-id="1">
+<message version="1" name="CEA" application-id="0" hop-by-hop-id="1" end-to-end-id="1">
    <avp name="Result-Code" data="2001" alias="DIAMETER_SUCCESS"/>
    <avp name="Origin-Host" data="$oh"/>
    <avp name="Origin-Realm" data="$(echo $oh | cut -d\. -f2-) $WHAT"/>
index 38bd605..9cd059f 100644 (file)
@@ -1,11 +1,11 @@
 <message version="1" name="AA-Answer" application-id="16777236" hop-by-hop-id="202475" end-by-end-id="509476">
-   <avp name="Auth-Application-Id" data="16777236"/>
    <avp name="Session-Id" data="test1;afNodeHostname.nodeHostRealm.com;1;8033450"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
    <avp name="Origin-Host" data="ownHostId.operatorRealm.com"/>
-   <avp name="Origin-State-Id" data="1428633668"/>
    <avp name="Origin-Realm" data="operatorRealm.com"/>
    <avp name="Experimental-Result">
       <avp name="Vendor-Id" data="10415"/>
       <avp name="Experimental-Result-Code" data="5063"/>
    </avp>
+   <avp name="Origin-State-Id" data="1428633668"/>
 </message>
index 1201655..27b631c 100644 (file)
@@ -19,6 +19,6 @@
       <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
       <avp name="Subscription-Id-Data" data="626037099"/>
    </avp>
-   <avp name="Framed-IP-Address" hex-data="3139322e3136382e302e31"/>
-   <avp name="Called-Station-Id" data="WAP.MOVISTAR"/>
+   <avp name="Framed-IP-Address" flags="0" hex-data="3139322e3136382e302e31"/>
+   <avp name="Called-Station-Id" flags="0" data="WAP.MOVISTAR"/>
 </message>
diff --git a/example/diameter/launcher/resources/xml_examples/regexp_for_aar.xml b/example/diameter/launcher/resources/xml_examples/regexp_for_aar.xml
new file mode 100644 (file)
index 0000000..6c06e35
--- /dev/null
@@ -0,0 +1,24 @@
+<message version="1" name="AA-Request" p-bit="yes" application-id="16777236" hop-by-hop-id="[0-9]+" end-by-end-id="[0-9]+">
+   <avp name="Session-Id" data="ocs3;1332774430;1;1332774430"/>
+   <avp name="Auth-Application-Id" data="16777236"/>
+   <avp name="Origin-Host" data="OCS3"/>
+   <avp name="Origin-Realm" data="OCS3"/>
+   <avp name="Destination-Realm" data="OCS3"/>
+   <avp name="Destination-Host" data="OCS3"/>
+   <avp name="AF-Application-Identifier" hex-data="313232"/>
+   <avp name="Media-Component-Description">
+      <avp name="Media-Component-Number" data="0"/>
+      <avp name="AF-Application-Identifier" hex-data="313232"/>
+      <avp name="Max-Requested-Bandwidth-UL" data="127"/>
+      <avp name="Max-Requested-Bandwidth-DL" data="133"/>
+      <avp name="Flow-Status" data="2" alias="ENABLED"/>
+      <avp name="Reservation-Priority" data="0" alias="DEFAULT"/>
+   </avp>
+   <avp name="Service-Info-Status" data="0" alias="FINAL_SERVICE_INFORMATION"/>
+   <avp name="Subscription-Id">
+      <avp name="Subscription-Id-Type" data="0" alias="END_USER_E164"/>
+      <avp name="Subscription-Id-Data" data="626037099"/>
+   </avp>
+   <avp name="Framed-IP-Address" flags="0" hex-data="3139322e3136382e302e31"/>
+   <avp name="Called-Station-Id" flags="0" data="WAP.MOVISTAR"/>
+</message>
index b722cac..acd3a54 100644 (file)
@@ -95,7 +95,8 @@ public:
                 const std::string &sessionId, const std::string &resultCode,
                 const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
   void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException);
-  void addWaitRegexp(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
+  void addWaitRegexpHex(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
+  void addWaitRegexpXml(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
   void addCommand(const std::string &cmd) throw(anna::RuntimeException);
 
 
index 25350c8..ad29dc3 100644 (file)
@@ -28,39 +28,46 @@ class TestCondition {
 
   public:
 
-    struct Type { enum _v { Generic, Basic }; };
+    // RegexpXml = Regexp against XML representation for incoming messages
+    // RegexpHex = Regexp against HEX representation for incoming messages
+    // Fields = Compare specific message fields
+    struct Type { enum _v { RegexpXml, RegexpHex, Fields }; };
     static const char* asText(const Type::_v type) throw();
 
     TestCondition() : a_rcvFromEntity(true),
+
                       a_regexp(""),
+
                       a_code(""), a_bitR(""), a_hopByHop(""), a_applicationId(""),
                       a_sessionId(""), a_resultCode(""),
-                      a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Basic; }
+                      a_msisdn(""), a_imsi(""), a_serviceContextId("") { a_type = Type::Fields; }
 
 
     // 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 setHopByHop(const std::string &value) throw() { a_hopByHop = value; }
-    void setApplicationId(const std::string &value) throw() { a_applicationId = value; }
-    void setSessionId(const std::string &value) throw() { a_sessionId = value; }
-    void setResultCode(const std::string &value) throw() { a_resultCode = value; }
-    void setMsisdn(const std::string &value) throw() { a_msisdn = value; }
-    void setImsi(const std::string &value) throw() { a_imsi = value; }
-    void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; }
+    // Regexp
+    void setRegexpXml(const std::string &regexp) throw() { a_regexp = regexp; a_type = Type::RegexpXml; }
+    void setRegexpHex(const std::string &regexp) throw() { a_regexp = regexp; a_type = Type::RegexpHex; }
+
+    // Fields
+    void setCode(const std::string &value) throw() { a_code = value; a_type = Type::Fields; }
+    void setBitR(const std::string &value) throw() { a_bitR = value; a_type = Type::Fields; }
+    void setHopByHop(const std::string &value) throw() { a_hopByHop = value; a_type = Type::Fields; }
+    void setApplicationId(const std::string &value) throw() { a_applicationId = value; a_type = Type::Fields; }
+    void setSessionId(const std::string &value) throw() { a_sessionId = value; a_type = Type::Fields; }
+    void setResultCode(const std::string &value) throw() { a_resultCode = value; a_type = Type::Fields; }
+    void setMsisdn(const std::string &value) throw() { a_msisdn = value; a_type = Type::Fields; }
+    void setImsi(const std::string &value) throw() { a_imsi = value; a_type = Type::Fields; }
+    void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; a_type = Type::Fields; }
 
     bool exists() const throw();
     friend bool operator==(const TestCondition &c1, const TestCondition &c2) throw() {
 
       if (c1.getType() != c2.getType()) return false;
 
-      if (c1.getType() == TestCondition::Type::Generic) {
+      if (c1.getType() == TestCondition::Type::RegexpXml || c1.getType() == TestCondition::Type::RegexpHex) {
         if (c1.getRegexp() != c2.getRegexp()) return false;
       }
       else {
@@ -79,10 +86,9 @@ class TestCondition {
     }
 
 
-
     const Type::_v &getType() const throw() { return a_type; }
 
-    // Generic
+    // Regexp:
     const std::string & getRegexp() const throw() { return a_regexp; }
     // Basic
     const std::string & getCode() const throw() { return a_code; }
@@ -96,8 +102,7 @@ class TestCondition {
     const std::string & getServiceContextId() const throw() { return a_serviceContextId; }
 
 
-    bool comply (const anna::DataBlock &message/*, bool matchSessionId*/) const throw();
-
+    bool comply (const anna::DataBlock &message) const throw();
 
     anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
 
index 34af880..782ce55 100644 (file)
@@ -197,7 +197,8 @@ class TestStepWait : public TestStep {
                         const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
                         const std::string &sessionId, const std::string &resultCode,
                         const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw();
-    void setCondition(bool fromEntity, const std::string &regexp) throw();
+    void setConditionRegexpXml(bool fromEntity, const std::string &regexp) throw();
+    void setConditionRegexpHex(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; }
index efaba5c..f8d7a98 100644 (file)
@@ -366,11 +366,25 @@ void TestCase::addWait(bool fromEntity,
   addStep(step);
 }
 
-void TestCase::addWaitRegexp(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException) {
+void TestCase::addWaitRegexpHex(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException) {
   assertInitialized();
 
   TestStepWait *step = new TestStepWait(this);
-  step->setCondition(fromEntity, regexp);
+  step->setConditionRegexpHex(fromEntity, regexp);
+
+  LOGINFORMATION(
+    if (hasSameCondition(step->getCondition()))
+      anna::Logger::information(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION);
+  );
+
+  addStep(step);
+}
+
+void TestCase::addWaitRegexpXml(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException) {
+  assertInitialized();
+
+  TestStepWait *step = new TestStepWait(this);
+  step->setConditionRegexpXml(fromEntity, regexp);
 
   LOGINFORMATION(
     if (hasSameCondition(step->getCondition()))
index ba1db97..fcb3c08 100644 (file)
@@ -7,6 +7,7 @@
 
 
 // Standard
+#include <algorithm>
 
 // Project
 #include <anna/testing/TestCondition.hpp>
@@ -21,6 +22,7 @@
 #include <anna/diameter/helpers/dcca/defines.hpp>
 #include <anna/diameter/helpers/dcca/functions.hpp>
 #include <anna/core/util/defines.hpp>
+#include <anna/core/util/RegularExpression.hpp>
 
 
 using namespace anna::testing;
@@ -28,47 +30,36 @@ using namespace anna::testing;
 
 const char* TestCondition::asText(const Type::_v type)
 throw() {
-  static const char* text [] = { "Generic", "Basic" };
+  static const char* text [] = { "RegexpXml", "RegexpHex", "Fields" };
   return text [type];
 }
 
 bool TestCondition::exists() const throw() {
-  if (a_type == Type::Generic)
-    return (a_regexp != "");
-  else
-    return (a_code != "" || a_bitR != "" || a_hopByHop != "" || a_applicationId != "" || a_sessionId != "" || a_resultCode != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != "");
-}
-
-/*
-bool anna::testing::operator==(const TestCondition &c1, const TestCondition &c2) throw() {
-
-  if (c1.getType() != c2.getType()) return false;
-
-  if (c1.getType() == TestCondition::Type::Generic) {
-    if (c1.getRegexp() != c2.getRegexp()) return false;
-  }
-  else {
-    if (c1.getCode() != c2.getCode()) return false;
-    if (c1.getBitR() != c2.getBitR()) return false;
-    if (c1.getHopByHop() != c2.getHopByHop()) return false;
-    if (c1.getApplicationId() != c2.getApplicationId()) return false;
-    if (c1.getSessionId() != c2.getSessionId()) return false;
-    if (c1.getResultCode() != c2.getResultCode()) return false;
-    if (c1.getMsisdn() != c2.getMsisdn()) return false;
-    if (c1.getImsi() != c2.getImsi()) return false;
-    if (c1.getServiceContextId() != c2.getServiceContextId()) return false;
-  }
+  if (a_type != Type::Fields) return (getRegexp() != "");
 
-  return true;
+  return (a_code != "" || a_bitR != "" || a_hopByHop != "" || a_applicationId != "" || a_sessionId != "" || a_resultCode != "" || a_msisdn != "" || a_imsi != "" || a_serviceContextId != "");
 }
-*/
 
-bool TestCondition::comply(const anna::DataBlock &message/*, bool matchSessionId*/) const throw() {
+bool TestCondition::comply(const anna::DataBlock &message) const throw() {
 
-  if (a_type == Type::Generic) {
+  if (a_type == Type::RegexpXml) {
     anna::diameter::codec::Message codecMsg;
     try { codecMsg.decode(message); } catch (anna::RuntimeException &ex) { ex.trace(); }
-    return codecMsg.isLike(a_regexp);
+
+    //return codecMsg.isLike(getRegexp());
+    // We will remove LF from both sides to ease regexp management:
+    std::string regexp = getRegexp();
+    regexp.erase(std::remove(regexp.begin(), regexp.end(), '\n'), regexp.end());
+    anna::RegularExpression re(regexp);
+
+    std::string msgString = codecMsg.asXMLString();
+    msgString.erase(std::remove(msgString.begin(), msgString.end(), '\n'), msgString.end());
+
+    return re.isLike(msgString); 
+  }
+  else if (a_type == Type::RegexpHex) {
+    anna::RegularExpression re(getRegexp());
+    return re.isLike(anna::functions::asHexString(message));
   }
 
   // Basic
@@ -161,8 +152,11 @@ 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);
+  if (a_type == Type::RegexpXml) {
+    if (getRegexp() != "") result->createAttribute("RegexpXml", getRegexp());
+  }
+  else if (a_type == Type::RegexpHex) {
+    if (getRegexp() != "") result->createAttribute("RegexpHex", getRegexp());
   }
   else {
     if (a_code != "") result->createAttribute("Code", atoi(a_code.c_str()));
index 8f1a728..8c0ea81 100644 (file)
@@ -427,18 +427,19 @@ void TestManager::receiveMessage(const anna::DataBlock &message, const anna::dia
   // 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 += "':";
+    std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'";
 
     try {
       static anna::diameter::codec::Message codecMsg;
       codecMsg.decode(message);
-      hint += "\n"; hint += codecMsg.asXMLString();
+      hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
+      hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
     }
     catch (anna::RuntimeException &ex) {
       ex.trace();
-      hint += "\n"; hint += ex.asString();
+      hint += ex.asString();
     }
-    hint += "\n"; hint += clientSession->asString();
+    hint += "\nClient Session: "; hint += clientSession->asString();
 
     tc->addDebugSummaryHint(hint);
   }
@@ -467,18 +468,19 @@ void TestManager::receiveMessage(const anna::DataBlock &message, const anna::dia
   // 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 += "':";
+    std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'";
 
     try {
       static anna::diameter::codec::Message codecMsg;
       codecMsg.decode(message);
-      hint += "\n"; hint += codecMsg.asXMLString();
+      hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
+      hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
     }
     catch (anna::RuntimeException &ex) {
       ex.trace();
-      hint += "\n"; hint += ex.asString();
+      hint += ex.asString();
     }
-    hint += "\n"; hint += serverSession->asString();
+    hint += "\nServer Session: "; hint += serverSession->asString();
 
     tc->addDebugSummaryHint(hint);
   }
index 7e96926..e180437 100644 (file)
@@ -559,10 +559,14 @@ void TestStepWait::setCondition(bool fromEntity,
   a_condition.setServiceContextId(serviceContextId);
 }
 
-void TestStepWait::setCondition(bool fromEntity, const std::string &regexp) throw() {
+void TestStepWait::setConditionRegexpHex(bool fromEntity, const std::string &regexp) throw() {
+  a_condition.setReceivedFromEntity(fromEntity);
+  a_condition.setRegexpHex(regexp);
+}
 
+void TestStepWait::setConditionRegexpXml(bool fromEntity, const std::string &regexp) throw() {
   a_condition.setReceivedFromEntity(fromEntity);
-  a_condition.setRegexp(regexp);
+  a_condition.setRegexpXml(regexp);
 }
 
 anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent)
@@ -584,13 +588,18 @@ throw() {
     }
   }
 
-  if (decodeMessage()) {
-    xmlmsg = "\n";
-    xmlmsg += a_messageCodec->asXMLString();
-    xmlmsg += "\n";
+  if (a_message.isEmpty()) {
+      xmlmsg = "<empty>";
   }
   else {
-    xmlmsg = "<unable to decode, check traces>"; 
+    if (decodeMessage()) {
+      xmlmsg = "\n";
+      xmlmsg += a_messageCodec->asXMLString();
+      xmlmsg += "\n";
+    }
+    else {
+      xmlmsg = "<unable to decode, check traces>"; 
+    }
   }
 
   if (msg != "") result->createAttribute("MatchedMessage", msg);