// Decodes a diameter message coming from a datablock
void decodeDataBlock(const anna::DataBlock &db/*, unsigned int & detectedApplicationId*/) throw() {
try {
- G_codecMsg.clear(); // perhaps we will need another codec engine ...
+ G_codecMsg.setEngine(NULL); // perhaps we will need another codec engine ...
G_codecMsg.decode(db);
} catch(RuntimeException &ex) {
_exit(ex.asString());
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 Wait condition, from entity (waitfe-regexp) or client (waitfc-regexp)";
result += "\n to match the serialized xml content for received messages. CPU cost";
result += "\n is bigger than the former ones because the whole message must be";
result += "\n decoded and converted to xml instead of doing a direct hexadecimal";
result += "\n buffer search. The main advantage is the great flexibility to identify";
result += "\n any content with a regular expression.";
result += "\n";
- result += "\n sh-command|<script>[|parameters]";
- result += "\n External execution for script/executable via shell through an";
- result += "\n independent thread, providing the script name and the parameters.";
- result += "\n You could use dynamic variables ##<tag> to have more flexibility:";
+ result += "\n sh-command|<script> External execution for script/executable via shell through a dedicated";
+ result += "\n thread, providing the command and parameters. You could use dynamic";
+ result += "\n variables ##<tag> to have more flexibility:";
result += "\n Test pool cycle id: "; result += SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID;
result += "\n Test case id: "; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID;
result += "\n Test step id: "; result += SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID;
result += "\n";
- result += "\n For example, your command could be something like this:";
- result += "\n script: insert_sql.sh";
- result += "\n parameters: -db dbname --verbose > /tmp/cycle-"; result += SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID; result += ".";
- result += "\n testcase-"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID; result += ".teststep-"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID; result += ".out";
- result += "\n";
- result += "\n You could also make substitutions on script name: insert_sql_"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID; result += ".sh";
- result += "\n";
- result += "\n Don't try to redirect stdout and stderr to avoid ADML output contamination";
- result += "\n with the possible outputs from the scripts.";
+ result += "\n For example, your command could be something like this:";
+ result += "\n insert_sql_"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID; result += ".sh -db dbname --verbose";
+ result += "\n > /tmp/cycle-"; result += SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID;
+ result += ".testcase-"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTCASE_ID;
+ result += ".teststep-"; result += SH_COMMAND_TAG_FOR_REPLACE__TESTSTEP_ID;
+ result += ".out";
+ result += "\n Try to redirect stdout and stderr to avoid ADML output contamination";
+ result += "\n with the possible outputs from the scripts. You could also put your";
+ result += "\n job in background although sh-command will return 0-value immediately.";
result += "\n";
result += "\n <condition>: Optional parameters which must be fulfilled to continue through the next step.";
result += "\n Any received message over diameter interfaces will be evaluated against the";
result += "\n";
result += "\n test|report-hex[|[yes]|no] Reports could include the diameter messages in hexadecimal format. Disabled by default.";
result += "\n";
- result += "\n test|report-hex[|[yes]|no] Reports could include the diameter messages in hexadecimal format. Disabled by default.";
+ result += "\n test|interact|amount|id Makes interactive a specific test case id. The amount is the margin of execution steps";
+ result += "\n to be done. Normally, we will execute 'test|interact|0|<test case id>', which means that";
+ result += "\n the test case is selected to be interactive, but no step is executed. Then you have to";
+ result += "\n interact with positive amounts (usually 1), executing the provided number of steps if";
+ result += "\n they are ready and fulfill the needed conditions. The value of 0, implies no execution";
+ result += "\n steps margin, which could be useful to 'freeze' a test in the middle of its execution.";
+ result += "\n You could also provide -1 to make it non-interactive resuming it from the current step.";
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";
// test|report-hex[|[yes]|no] Reports could include the diameter messages in hexadecimal format. Disabled by default.
// test|goto|<id> Updates current test pointer position.
// test|look[|id] Show programmed test case for id provided, current when missing ...
+ // test|interact|amount|id Makes interactive a specific test case id. The amount is the margin of execution steps ...
// 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.
}
}
else if(param1 == "repeats") {
- if (numParams > 2 || numParams < 2)
+ 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 repeats = atoi(param2.c_str());
if (repeats < 0) repeats = -1;
}
}
}
+ else if (param1 == "interact") {
+ if (numParams != 3)
+ throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
+
+ int amount = atoi(param2.c_str());
+ if (amount < -1)
+ throw anna::RuntimeException("Interactive amount must be -1 (to disable interactive mode) or a positive number.", ANNA_FILE_LOCATION);
+
+ int id = atoi(param3.c_str());
+ TestCase *testCase = testManager.findTestCase(id);
+ if (testCase) {
+ if (amount == -1) {
+ testCase->makeInteractive(false);
+ opt_response_content = "interactive mode disabled";
+ }
+ else {
+ testCase->addInteractiveAmount(amount);
+ opt_response_content = "added interactive amount of ";
+ opt_response_content += anna::functions::asString(amount);
+ opt_response_content += " units";
+ if (amount == 0) opt_response_content += " (0: freezing a non-interactive testcase, no effect on already interactive)";
+ }
+ opt_response_content += " for test case id ";
+ opt_response_content += anna::functions::asString(id);
+ }
+ else {
+ opt_response_content = "cannot found test id (";
+ opt_response_content += anna::functions::asString(id);
+ opt_response_content += ")";
+ }
+ }
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);
else if (param2 == "sh-command") {
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("Missing script for 'sh-command' in test id operation", ANNA_FILE_LOCATION);
- testManager.getTestCase(id)->addCmd(param3, param4); // creates / reuses
+ if(param3 == "") throw anna::RuntimeException("Missing script/executable command-line for 'sh-command' in test id operation", ANNA_FILE_LOCATION);
+ testManager.getTestCase(id)->addCommand(param3); // creates / reuses
}
else {
throw anna::RuntimeException("Wrong body content format on HTTP Request. Use 'help' management command to see more information.", ANNA_FILE_LOCATION);
a_debugSummary.asXML(result);
}
+ result->createAttribute("Interactive", (a_interactiveAmount != -1) ? "yes":"no");
+
return result;
}
a_debugSummary.clear();
a_startTime = 0;
+ a_interactiveAmount = -1;
setState(State::Initialized);
a_steps.push_back(step);
}
-void TestCase::addCmd(const std::string &script, const std::string ¶meters) throw(anna::RuntimeException) {
+void TestCase::addCommand(const std::string &cmd) throw(anna::RuntimeException) {
assertInitialized();
TestStepCmd *step = new TestStepCmd(this);
- step->setScript(script);
- step->setParameters(parameters);
+ step->setScript(cmd);
a_steps.push_back(step);
}
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(unsigned int id) : a_id(id), a_state(State::Initialized), a_startTime(0), a_interactiveAmount(-1) { /*a_stepsIt = a_steps.end()*/;}
~TestCase();
struct State { enum _v { Initialized, InProgress, Failed, Success }; };
bool inProgress() const throw() { return (getState() == State::InProgress); }
bool hasSameCondition(const TestCondition &condition) const throw();
+ // Interactivity:
+ void makeInteractive(bool yes = true) throw() { a_interactiveAmount = (yes ? 0:-1); }
+ void addInteractiveAmount(unsigned int amount) throw() {
+ if (a_interactiveAmount == -1) makeInteractive();
+ if (amount == 0) return;
+ a_interactiveAmount += amount;
+ process();
+ }
+ int interactiveAmount() const throw() { return a_interactiveAmount; }
+ void interactiveExecution() throw() { a_interactiveAmount --; }
+
// 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);
const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException);
void addWaitRegexp(bool fromEntity, const std::string ®exp) throw(anna::RuntimeException);
- void addCmd(const std::string &script, const std::string ¶meters) throw(anna::RuntimeException);
+ void addCommand(const std::string &cmd) throw(anna::RuntimeException);
// Process:
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.
+ int a_interactiveAmount;
friend class TestStep;
};
bool result = true;
try {
+ messageCodec.setEngine(NULL); // perhaps we will need another codec engine ...
messageCodec.decode(message);
}
catch (anna::RuntimeException &ex) {
const char* TestStep::asText(const Type::_v type)
throw() {
- static const char* text [] = { "Unconfigured", "Timeout", "Sendxml2e", "Sendxml2c", "Delay", "Wait", "Cmd" };
+ static const char* text [] = { "Unconfigured", "Timeout", "Sendxml2e", "Sendxml2c", "Delay", "Wait", "Command" };
return text [type];
}
}
bool TestStep::execute() throw() {
+
+ int ia = a_testCase->interactiveAmount();
+ if (ia > -1) {
+ if (ia == 0) return false;
+ a_testCase->interactiveExecution();
+ LOGDEBUG(anna::Logger::debug("Interactive execution ...", ANNA_FILE_LOCATION));
+ if (a_executed) return false; // avoid repeating (this implies amount consumption)
+ }
+
LOGDEBUG(anna::Logger::debug(anna::functions::asString("EXECUTING %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
setBeginTimestamp(anna::functions::millisecond());
+ a_executed = true;
return do_execute();
}
LOGDEBUG(anna::Logger::debug(anna::functions::asString("RESET %s (step number %d) for Test Case %llu (%p) (%p)", asText(a_type), a_number, a_testCase->getId(), (TestCaseStep*)this, this), ANNA_FILE_LOCATION));
// type and testCase kept
a_completed = false;
+ a_executed = false;
a_beginTimestamp = 0;
a_endTimestamp = 0;
do_reset();
//parent->createChild("TestStepCmd");
result->createAttribute("Script", (a_script != "") ? a_script:"<no script>");
- result->createAttribute("Parameters", (a_parameters != "") ? a_parameters:"<no parameters>");
if (a_errorMsg != "") result->createAttribute("ErrorMessage", a_errorMsg);
if (!a_threadRunning && a_resultCode != -2) {
result->createAttribute("ResultCode", a_resultCode);
if (!a_threadRunning /* || a_threadDeprecated DO NOT WANT TO OVERLAP ... */) {
// Special tags to replace:
std::string cmd = getScript();
- cmd += " ";
- cmd += getParameters();
size_t index;
while ((index = cmd.find(SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID)) != std::string::npos)
cmd.replace(index, strlen(SH_COMMAND_TAG_FOR_REPLACE__CYCLE_ID), anna::functions::asString(TestManager::instantiate().getPoolCycle()));
int a_number; // step number used for xml (informational)
anna::Millisecond a_beginTimestamp; // unix time
anna::Millisecond a_endTimestamp; // unix time
+ bool a_executed; // used for interactive mode in order to not repeat a execution step if before completing, the user add interactive amount
void setBeginTimestamp(const anna::Millisecond &t) throw() { a_beginTimestamp = t; }
const anna::Millisecond &getBeginTimestamp() const throw() { return a_beginTimestamp; }
struct Type { enum _v { Unconfigured, Timeout, Sendxml2e, Sendxml2c, Delay, Wait, Cmd }; };
static const char* asText(const Type::_v type) throw();
- TestStep(TestCase *testCase) : a_message(true), a_messageCodec(NULL) { initialize(testCase); }
+ TestStep(TestCase *testCase) : a_message(true), a_messageCodec(NULL), a_executed(false) { initialize(testCase); }
virtual ~TestStep() {;}
// setter & getters
class TestStepCmd : public TestStep {
std::string a_script;
- std::string a_parameters;
std::thread a_thread;
bool a_threadRunning;
bool a_threadDeprecated;
// setter & getters
void setThreadRunning(bool running) throw() { a_threadRunning = running; }
- //bool getThreadRunning() const throw() { return a_threadRunning; }
- //void setThreadDeprecated(bool deprecated) throw() { a_threadDeprecated = deprecated; }
- //bool getThreadDeprecated() const throw() { return a_threadDeprecated; }
void setResultCode(int rc) throw() { a_resultCode = rc; }
int getResultCode() const throw() { return a_resultCode; }
void setScript(const std::string &script) throw() { a_script = script; }
const std::string &getScript() const throw() { return a_script; }
- void setParameters(const std::string ¶ms) throw() { a_parameters = params; }
- const std::string &getParameters() const throw() { return a_parameters; }
// virtuals
bool do_execute() throw();
message is valid against all odds then validation will go on). In case that validation is enabled (codec::Engine::ValidationMode) an exception will be launched
in a moment which depends on validation depth (codec::Engine::ValidationDepth).
+ You could decode multiple times over the same object. A basic cleanup is done respecting the codec engine.
+
@param db buffer data block processed. Before decoding, the whole message instance will be cleared (no need to invoke #clear before #decode).
@param ptrAnswer Answer set by application (could be empty or not), who is responsible for its memory reservation,
and automatically built regarding standard. If message analyzed realizes to be an answer, internal reference becomes
/**
Interpret xml data in order to dump over the class content.
+ You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
\param messageNode Message root node obtained from @functions::xmlFileTo
*/
void fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException);
/**
* Interpret a xml file in order to create a diameter message
+ * You could apply this multiple times over the same object. A basic cleanup is done respecting the codec engine.
*
* @see functions::messageXmlDocumentFromXmlFile
* @see fromXML