#include <math.h> // ceil
#include <climits>
#include <unistd.h> // chdir
+#include <regex>
// Project
#include <anna/timex/Engine.hpp>
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);
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;
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();
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";
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";
// 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";
// 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:
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:
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;
};
--- /dev/null
+010000c400000109010000140000000000000000000001074000003874657374313b61664e6f6465486f73746e616d652e6e6f6465486f73745265616c6d2e636f6d3b313b38303333343530000001024000000c0100001400000108400000236f776e486f737449642e6f70657261746f725265616c6d2e636f6d0000000128400000196f70657261746f725265616c6d2e636f6d00000000000129400000200000010a4000000c000028af0000012a4000000c000013c7000001164000000c55273844
-01000150c000010901000014000000000000000000000107400000246f6373333b313333323737343433303b313b31333332373734343330000001024000000c01000014000001084000000c4f435333000001284000000c4f4353330000011b4000000c4f435333000001254000000c4f435333000001f8c000000f000028af3132320000000205c000006c000028af00000206c0000010000028af00000000000001f8c000000f000028af3132320000000204c0000010000028af0000007f00000203c0000010000028af00000085000001ffc0000010000028af00000002000001ca80000010000032db000000000000020fc0000010000028af00000000000001bb40000028000001c24000000c00000000000001bc4000001136323630333730393900000000000008400000133139322e3136382e302e31000000001e400000145741502e4d4f564953544152
+01000150c000010901000014000000000000000000000107400000246f6373333b313333323737343433303b313b31333332373734343330000001024000000c01000014000001084000000c4f435333000001284000000c4f4353330000011b4000000c4f435333000001254000000c4f435333000001f8c000000f000028af3132320000000205c000006c000028af00000206c0000010000028af00000000000001f8c000000f000028af3132320000000204c0000010000028af0000007f00000203c0000010000028af00000085000001ffc0000010000028af00000002000001ca80000010000032db000000000000020fc0000010000028af00000000000001bb40000028000001c24000000c00000000000001bc4000001136323630333730393900000000000008000000133139322e3136382e302e31000000001e000000145741502e4d4f564953544152
+
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"
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):
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"
# 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))
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"/>
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"/>
<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>
<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>
--- /dev/null
+<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>
const std::string &sessionId, const std::string &resultCode,
const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException);
- void addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException);
+ void addWaitRegexpHex(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException);
+ void addWaitRegexpXml(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException);
void addCommand(const std::string &cmd) throw(anna::RuntimeException);
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 ®exp) throw() { a_regexp = regexp; }
- // Basic
- void setCode(const std::string &value) throw() { a_code = value; }
- void setBitR(const std::string &value) throw() { a_bitR = value; }
- void setHopByHop(const std::string &value) throw() { a_hopByHop = value; }
- void setApplicationId(const std::string &value) throw() { a_applicationId = value; }
- void setSessionId(const std::string &value) throw() { a_sessionId = value; }
- void setResultCode(const std::string &value) throw() { a_resultCode = value; }
- void setMsisdn(const std::string &value) throw() { a_msisdn = value; }
- void setImsi(const std::string &value) throw() { a_imsi = value; }
- void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; }
+ // Regexp
+ void setRegexpXml(const std::string ®exp) throw() { a_regexp = regexp; a_type = Type::RegexpXml; }
+ void setRegexpHex(const std::string ®exp) 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 {
}
-
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; }
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();
const std::string &code, const std::string &bitR, const std::string &hopByHop, const std::string &applicationId,
const std::string &sessionId, const std::string &resultCode,
const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw();
- void setCondition(bool fromEntity, const std::string ®exp) throw();
+ void setConditionRegexpXml(bool fromEntity, const std::string ®exp) throw();
+ void setConditionRegexpHex(bool fromEntity, const std::string ®exp) throw();
void setClientSession(anna::diameter::comm::ClientSession *cs) throw() { a_clientSession = cs; }
void setServerSession(anna::diameter::comm::ServerSession *ss) throw() { a_serverSession = ss; }
addStep(step);
}
-void TestCase::addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException) {
+void TestCase::addWaitRegexpHex(bool fromEntity, const std::string ®exp) 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 ®exp) throw(anna::RuntimeException) {
+ assertInitialized();
+
+ TestStepWait *step = new TestStepWait(this);
+ step->setConditionRegexpXml(fromEntity, regexp);
LOGINFORMATION(
if (hasSameCondition(step->getCondition()))
// Standard
+#include <algorithm>
// Project
#include <anna/testing/TestCondition.hpp>
#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;
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
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()));
// 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);
}
// 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);
}
a_condition.setServiceContextId(serviceContextId);
}
-void TestStepWait::setCondition(bool fromEntity, const std::string ®exp) throw() {
+void TestStepWait::setConditionRegexpHex(bool fromEntity, const std::string ®exp) throw() {
+ a_condition.setReceivedFromEntity(fromEntity);
+ a_condition.setRegexpHex(regexp);
+}
+void TestStepWait::setConditionRegexpXml(bool fromEntity, const std::string ®exp) throw() {
a_condition.setReceivedFromEntity(fromEntity);
- a_condition.setRegexp(regexp);
+ a_condition.setRegexpXml(regexp);
}
anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent)
}
}
- 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);