{
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,...)");
}
[ \$? -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
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)."
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
+++ /dev/null
-#!/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
-
--- /dev/null
+#!/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
+
// 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\
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();
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";
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")));
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 //
//////////////////
a_communicator->accept();
}
+
bool Launcher::getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw() {
// Get hex string
static char buffer[8192];
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";
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";
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:
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;
}
///////////////////////////////////////////////////////////////////
// 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;
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()));
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;
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);
// 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);
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);
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);
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);
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();
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
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;
}
#include <anna/time/Date.hpp>
// Process
-#include "MyCommunicator.hpp"
-#include "MyCounterRecorder.hpp"
+#include <MyCommunicator.hpp>
+#include <MyCounterRecorder.hpp>
+#include <TestManager.hpp>
namespace anna {
}
}
+class TestManager;
+
// RealmNode resources
class RealmNode;
typedef std::map<std::string, RealmNode*> realm_nodes_t;
anna::diameter::codec::Engine *a_codecEngine;
anna::timex::Engine* a_timeEngine;
MyCounterRecorder *a_counterRecorder;
+ anna::Millisecond a_admlMinResolution;
MyCounterRecorderClock *a_counterRecorderClock;
// Nodes deployment:
// comm resources:
anna::comm::ServerSocket* a_httpServerSocket; // HTTP
+ const anna::Millisecond &getADMLMinResolution() const throw() { return a_admlMinResolution; }
void servicesFromXML(const anna::xml::Node* servicesNode, bool eventOperation) throw(anna::RuntimeException);
anna::Millisecond checkTimeMeasure(const std::string ¶meter, const std::string &value) throw(anna::RuntimeException);
void initialize() throw(anna::RuntimeException); // HTTP
void run() throw(anna::RuntimeException);
-
public:
Launcher();
// helpers
bool getDataBlockFromHexFile(const std::string &pathfile, anna::DataBlock &db) const throw();
+
+ friend class TestManager;
};
#endif
#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() {
#include <anna/diameter/codec/Message.hpp>
// Process
-#include "MyHandler.hpp"
+#include <MyHandler.hpp>
class MyCommunicator : public anna::comm::Communicator {
// Process
-#include "MyCounterRecorder.hpp"
+#include <MyCounterRecorder.hpp>
MyCounterRecorder::MyCounterRecorder(const std::string &fnp) : a_stream(-1), a_fileNamePrefix(fnp) {
#include <time.h>
// Process
-#include "MyCounterRecorderClock.hpp"
+#include <MyCounterRecorderClock.hpp>
class MyCounterRecorder : public anna::oam::CounterRecorder {
#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 {
#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() {
// 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
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) {
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;
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
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());
} catch(anna::RuntimeException &ex) {
ex.trace();
}
+
+ // release msgs
+ my_node->releaseCommMessage(msg);
+ my_node->releaseCommMessage(request);
}
}
#include <anna/diameter.comm/Entity.hpp>
// Process
-#include "ProgrammedAnswers.hpp"
+#include <ProgrammedAnswers.hpp>
namespace anna {
namespace diameter {
#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)
#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)
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
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) {
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);
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
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);
//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);
}
}
}
#include <anna/diameter.comm/LocalServer.hpp>
// Process
-#include "ProgrammedAnswers.hpp"
+#include <ProgrammedAnswers.hpp>
namespace anna {
namespace diameter {
#include <anna/diameter/codec/Engine.hpp>
// Process
-#include "ProgrammedAnswers.hpp"
+#include <ProgrammedAnswers.hpp>
void ProgrammedAnswers::clear () throw() {
#include <anna/xml/Compiler.hpp>
// Process
-#include "RealmNode.hpp"
-#include "MyDiameterEngine.hpp"
+#include <RealmNode.hpp>
+#include <MyDiameterEngine.hpp>
}
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 += ")";
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; }
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();
+import os
Import ('env')
# Process #################################################################
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')
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
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
--- /dev/null
+../../../hex_examples/aar-bad.hex
\ No newline at end of file
--- /dev/null
+../../../hex_examples/aar2-bad.hex
\ No newline at end of file
--- /dev/null
+../../../hex_examples/aar3-bad.hex
\ No newline at end of file
--- /dev/null
+# 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"
--- /dev/null
+# 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)
--- /dev/null
+# 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"/>
+++ /dev/null
-#!/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"
-
+++ /dev/null
-#!/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
-
+++ /dev/null
-../../../../hex_examples/aar-bad.hex
\ No newline at end of file
+++ /dev/null
-../../../../hex_examples/aar2-bad.hex
\ No newline at end of file
+++ /dev/null
-../../../../hex_examples/aar3-bad.hex
\ No newline at end of file
+++ /dev/null
-# 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"
+++ /dev/null
-# 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)
+++ /dev/null
-# 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"/>
+++ /dev/null
-#!/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
-
--- /dev/null
+#!/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
+
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+#!/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"
+
--- /dev/null
+#!/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"
+
--- /dev/null
+#!/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
+
--- /dev/null
+#!/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
+
#include <anna/http/functions.hpp>
// Process
-#include "Launcher.hpp"
+#include <Launcher.hpp>
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).");
# 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`
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
--- /dev/null
+// 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 ®exp) throw(anna::RuntimeException) {
+ assertInitialized();
+
+ TestStepWait *step = new TestStepWait(this);
+ step->setCondition(fromEntity, regexp);
+
+ LOGWARNING(
+ if (hasSameCondition(step->getCondition()))
+ anna::Logger::warning(anna::functions::asString("The same wait condition has already been programmed in this test case (%llu). Are you sure ?", a_id), ANNA_FILE_LOCATION);
+ );
+
+ a_steps.push_back(step);
+}
+
+TestStepWait *TestCase::searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw() {
+
+ TestStepWait *result;
+ for (std::vector<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);
+}
--- /dev/null
+// 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 ®exp) throw(anna::RuntimeException);
+
+ // Process:
+ void nextStep() throw() { a_stepsIt++; }
+ bool done() throw();
+ bool process() throw(); // false to stop
+
+ // Reset test case and underlaying information (steps context)
+ bool reset(bool hard /* hard reset includes in-progress test cases */) throw();
+
+ // getters
+ const anna::Millisecond &getStartTime() const throw() { return a_startTime; }
+ const unsigned int &getId() const throw() { return a_id; }
+
+ //helpers
+ int steps() const throw() { return a_steps.size(); }
+ TestStepWait *searchNextWaitConditionFulfilled(const anna::DataBlock &message, bool waitFromEntity) throw();
+ // When a message arrives, we identify the test case by mean the Session-Id. Then, from the current step iterator (included),
+ // we search for a fulfilling condition for that message. The first found, is 'completed' and then breaks the search.
+ const TestStep *getStep(int stepNumber) const throw();
+
+ anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+ std::string asXMLString() const throw();
+
+
+private:
+ // private members:
+ unsigned int a_id;
+ std::vector<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
--- /dev/null
+// 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();
+}
+
--- /dev/null
+// 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
+
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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 ®exp) throw() { a_regexp = regexp; }
+ // Basic
+ void setCode(const std::string &value) throw() { a_code = value; }
+ void setBitR(const std::string &value) throw() { a_bitR = value; }
+ void setResultCode(const std::string &value) throw() { a_resultCode = value; }
+ void setSessionId(const std::string &value) throw() { a_sessionId = value; }
+ void setHopByHop(const std::string &value) throw() { a_hopByHop = value; }
+ void setMsisdn(const std::string &value) throw() { a_msisdn = value; }
+ void setImsi(const std::string &value) throw() { a_imsi = value; }
+ void setServiceContextId(const std::string &value) throw() { a_serviceContextId = value; }
+
+ bool exists() const throw();
+ friend bool operator==(const TestCondition &c1, const TestCondition &c2) throw();
+
+ // Generic
+ const std::string & getRegexp() const throw() { return a_regexp; }
+ // Basic
+ const std::string & getCode() const throw() { return a_code; }
+ const std::string & getBitR() const throw() { return a_bitR; }
+ const std::string & getResultCode() const throw() { return a_resultCode; }
+ const std::string & getSessionId() const throw() { return a_sessionId; }
+ const std::string & getHopByHop() const throw() { return a_hopByHop; }
+ const std::string & getMsisdn() const throw() { return a_msisdn; }
+ const std::string & getImsi() const throw() { return a_imsi; }
+ const std::string & getServiceContextId() const throw() { return a_serviceContextId; }
+
+
+ bool comply (const anna::DataBlock &message/*, bool matchSessionId*/) const throw();
+
+
+ anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+
+
+ private:
+
+ // Source of the received message
+ bool a_rcvFromEntity;
+
+ // Type
+ Type::_v a_type;
+
+ // Generic:
+ std::string a_regexp;
+
+ // Basic:
+ std::string a_code;
+ std::string a_bitR;
+ std::string a_resultCode;
+ std::string a_sessionId;
+ std::string a_hopByHop;
+ std::string a_msisdn;
+ std::string a_imsi;
+ std::string a_serviceContextId;
+};
+
+
+#endif
--- /dev/null
+// 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));
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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 ®exp) throw() {
+
+ a_condition.setReceivedFromEntity(fromEntity);
+ a_condition.setRegexp(regexp);
+}
+
+anna::xml::Node* TestStepWait::asXML(anna::xml::Node* parent) const
+throw() {
+ anna::xml::Node* result = TestStep::asXML(parent);
+ //parent->createChild("TestStepWait");
+
+ // Condition
+ a_condition.asXML(result);
+
+ if (!a_message.isEmpty()) {
+ // Message
+ result->createAttribute("MatchedMessage", a_message.asString());
+ // Helper
+ try {
+ Launcher& my_app = static_cast <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;
+}
+
--- /dev/null
+// 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 ®exp) throw();
+
+ void setClientSession(anna::diameter::comm::ClientSession *cs) throw() { a_clientSession = cs; }
+ void setServerSession(anna::diameter::comm::ServerSession *ss) throw() { a_serverSession = ss; }
+
+ const TestCondition &getCondition() const throw() { return a_condition; }
+ //void setMsgDataBlock(const anna::DataBlock &db) throw() { a_message = db; }
+ bool fulfilled(const anna::DataBlock &db/*, bool matchSessionId = true*/) throw();
+ const anna::DataBlock &getMsgDataBlock() const throw() { return a_message; }
+
+
+ // virtuals
+ bool do_execute() throw(); // this will be executed when test case starts (at least we could measure the time until condition is fulfilled)
+ void do_complete() throw(); // condition fulfilled
+ void do_reset() throw();
+ anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
+};
+
+
+#endif
--- /dev/null
+// 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 += " }";
+}
+
--- /dev/null
+// 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
+
#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>
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;
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
/**
RequestSentOnClientSessionExpired,
RequestSentOnServerSessionExpired,
+ // retransmissions
+ RequestRetransmitted,
+ RequestRetransmittedOnClientSession,
+ RequestRetransmittedOnServerSession,
+
// unknown received answers
AnswerReceivedUnknown,
AnswerReceivedOnClientSessionUnknown,
/**
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.
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);
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);
}
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);
}
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);
}
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;
}
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];
}
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;
}
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;
}
#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) {
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;
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)
}
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);
}
"RequestSentExpired", \
"RequestSentOnClientSessionExpired", \
"RequestSentOnServerSessionExpired", \
+ "RequestRetransmitted", \
+ "RequestRetransmittedOnClientSession", \
+ "RequestRetransmittedOnServerSession", \
"AnswerReceivedUnknown", \
"AnswerReceivedOnClientSessionUnknown", \
"AnswerReceivedOnServerSessionUnknown", \
}
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);
}
);
}
-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(
//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;
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(
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) {
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);
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);
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
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;
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);
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
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;
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();
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) {