Simplify command (no parameter field, all inside command). Interactive mode.
authorEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Fri, 2 Oct 2015 00:52:20 +0000 (02:52 +0200)
committerEduardo Ramos Testillano <eduardo.ramos.testillano@ericsson.com>
Fri, 2 Oct 2015 00:52:20 +0000 (02:52 +0200)
example/diameter/batchConverter/main.cpp
example/diameter/launcher/Launcher.cpp
example/diameter/launcher/testing/TestCase.cpp
example/diameter/launcher/testing/TestCase.hpp
example/diameter/launcher/testing/TestStep.cpp
example/diameter/launcher/testing/TestStep.hpp
include/anna/diameter/codec/Message.hpp

index f909d89..0e7eff4 100644 (file)
@@ -70,7 +70,7 @@ void _exit(const std::string &message, int resultCode = 1) {
 // 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());
index f7921e7..eac8b26 100644 (file)
@@ -1159,30 +1159,29 @@ std::string Launcher::help() const throw() {
   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";
@@ -1335,7 +1334,13 @@ std::string Launcher::help() const throw() {
   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";
@@ -1739,6 +1744,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
     // 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.
 
@@ -1777,7 +1783,7 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       }
     }
     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;
@@ -1838,6 +1844,37 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
         }
       }
     }
+    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);
@@ -1955,8 +1992,8 @@ void Launcher::eventOperation(const std::string &operation, std::string &respons
       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);
index 32ecba1..63851f7 100644 (file)
@@ -86,6 +86,8 @@ throw() {
     a_debugSummary.asXML(result);
   }
 
+  result->createAttribute("Interactive", (a_interactiveAmount != -1) ? "yes":"no");
+
   return result;
 }
 
@@ -203,6 +205,7 @@ bool TestCase::reset(bool hard) throw() {
 
   a_debugSummary.clear();
   a_startTime = 0;
+  a_interactiveAmount = -1;
 
   setState(State::Initialized);
 
@@ -354,12 +357,11 @@ void TestCase::addWaitRegexp(bool fromEntity, const std::string &regexp) throw(a
   a_steps.push_back(step);
 }
 
-void TestCase::addCmd(const std::string &script, const std::string &parameters) 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);
 }
index 0a2c93b..a3b653d 100644 (file)
@@ -56,7 +56,7 @@ public:
     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 }; };
@@ -69,6 +69,17 @@ public:
   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);
@@ -80,7 +91,7 @@ public:
                 const std::string &msisdn, const std::string &imsi, const std::string &serviceContextId) throw(anna::RuntimeException);
   void addWaitAnswer(bool fromEntity, int stepNumber) throw(anna::RuntimeException);
   void addWaitRegexp(bool fromEntity, const std::string &regexp) throw(anna::RuntimeException);
-  void addCmd(const std::string &script, const std::string &parameters) throw(anna::RuntimeException);
+  void addCommand(const std::string &cmd) throw(anna::RuntimeException);
 
 
   // Process:
@@ -115,6 +126,7 @@ private:
   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;
 };
index 8a5e2b0..c1f27d8 100644 (file)
@@ -99,6 +99,7 @@ namespace {
 
     bool result = true;
     try {
+      messageCodec.setEngine(NULL); // perhaps we will need another codec engine ...
       messageCodec.decode(message);
     }
     catch (anna::RuntimeException &ex) {
@@ -135,7 +136,7 @@ bool TestStep::decodeMessage() throw() {
 
 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];
 }
 
@@ -168,8 +169,18 @@ std::string TestStep::asXMLString() throw() {
 }
 
 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();
 }
 
@@ -184,6 +195,7 @@ void TestStep::reset() throw() {
   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();
@@ -541,7 +553,6 @@ throw() {
   //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);
@@ -555,8 +566,6 @@ bool TestStepCmd::do_execute() throw() {
   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()));
index 9b69579..4f7e6e8 100644 (file)
@@ -51,6 +51,7 @@ class TestStep {
     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; }
@@ -63,7 +64,7 @@ class TestStep {
     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
@@ -223,7 +224,6 @@ class TestStepWait : public TestStep {
 class TestStepCmd : public TestStep {
 
   std::string a_script;
-  std::string a_parameters;
   std::thread a_thread;
   bool a_threadRunning;
   bool a_threadDeprecated;
@@ -236,9 +236,6 @@ class TestStepCmd : public TestStep {
 
     // 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; }
@@ -249,8 +246,6 @@ class TestStepCmd : public TestStep {
 
     void setScript(const std::string &script) throw() { a_script = script; }
     const std::string &getScript() const throw() { return a_script; }
-    void setParameters(const std::string &params) throw() { a_parameters = params; }
-    const std::string &getParameters() const throw() { return a_parameters; }
 
     // virtuals
     bool do_execute() throw();
index 27ee6da..32aa932 100644 (file)
@@ -514,6 +514,8 @@ public:
      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
@@ -540,12 +542,14 @@ public:
 
   /**
      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