X-Git-Url: https://git.teslayout.com/public/public/public/?a=blobdiff_plain;f=source%2Ftesting%2FTestManager.cpp;h=280a1988ebe17b2f17b8165fc47b6fe739932865;hb=21054b5919e21b14627fac09e718262ae2812af4;hp=6aeae9f9d16cc8b5210ff62cd862ac1f610d9371;hpb=bfedf5aed2670ccd65405c0ff96539ccb88b8f23;p=anna.git diff --git a/source/testing/TestManager.cpp b/source/testing/TestManager.cpp index 6aeae9f..280a198 100644 --- a/source/testing/TestManager.cpp +++ b/source/testing/TestManager.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,7 @@ TestManager::TestManager() : a_dumpSuccessReports = false; a_dumpHexMessages = false; + a_dumpStdout = false; a_synchronousAmount = 1; a_poolRepeats = 0; // repeat disabled by default a_poolCycle = 1; @@ -104,33 +106,33 @@ TestManager::TestManager() : a_currentTestIt = a_testPool.end(); } -void TestManager::registerSessionId(const std::string &sessionId, const TestCase *testCase) throw(anna::RuntimeException) { +void TestManager::registerKey1(const std::string &key, const TestCase *testCase) throw(anna::RuntimeException) { - std::map::const_iterator it = a_sessionIdTestCaseMap.find(sessionId); - if (it != a_sessionIdTestCaseMap.end()) { // found + auto it = a_key1TestCaseMap.find(key); + if (it != a_key1TestCaseMap.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); + throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such key1: %s", id, key.c_str()), ANNA_FILE_LOCATION); } } else { - a_sessionIdTestCaseMap[sessionId] = const_cast(testCase); - LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerSessionId for test case (id = %llu): %s)", testCase->getId(), sessionId.c_str()), ANNA_FILE_LOCATION)); + a_key1TestCaseMap[key] = const_cast(testCase); + LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerKey1 for test case (id = %llu): %s)", testCase->getId(), key.c_str()), ANNA_FILE_LOCATION)); } } -void TestManager::registerSubscriberId(const std::string &subscriberId, const TestCase *testCase) throw(anna::RuntimeException) { +void TestManager::registerKey2(const std::string &key, const TestCase *testCase) throw(anna::RuntimeException) { - std::map::const_iterator it = a_subscriberIdTestCaseMap.find(subscriberId); - if (it != a_subscriberIdTestCaseMap.end()) { // found + auto it = a_key2TestCaseMap.find(key); + if (it != a_key2TestCaseMap.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 subscriberId: %s", id, subscriberId.c_str()), ANNA_FILE_LOCATION); + throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such key2: %s", id, key.c_str()), ANNA_FILE_LOCATION); } } else { - a_subscriberIdTestCaseMap[subscriberId] = const_cast(testCase); - LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerSubscriberId for test case (id = %llu): %s)", testCase->getId(), subscriberId.c_str()), ANNA_FILE_LOCATION)); + a_key2TestCaseMap[key] = const_cast(testCase); + LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerKey2 for test case (id = %llu): %s)", testCase->getId(), key.c_str()), ANNA_FILE_LOCATION)); } } @@ -192,7 +194,7 @@ throw() { bool TestManager::configureTTPS(int testTicksPerSecond) throw() { - if (testTicksPerSecond == 0) { + if (testTicksPerSecond == 0) { if (a_clock) { a_timeController->cancel(a_clock); LOGDEBUG(anna::Logger::debug("Testing timer clock stopped !", ANNA_FILE_LOCATION)); @@ -258,6 +260,23 @@ bool TestManager::gotoTestCase(unsigned int id) throw() { return false; } +bool TestManager::runTestCase(unsigned int id) throw() { + test_pool_it it = a_testPool.find(id); + if (it != a_testPool.end()) { + a_currentTestIt = it; + + // execTestCases will get the next one, we must return 1 position: + if (a_currentTestIt == a_testPool.begin()) + a_currentTestIt = a_testPool.end(); + else + a_currentTestIt--; + + return execTestCases(1); + } + + return false; +} + TestCase *TestManager::findTestCase(unsigned int id) const throw() { // id = -1 provides current test case triggered if (!tests()) return NULL; @@ -266,28 +285,57 @@ TestCase *TestManager::findTestCase(unsigned int id) const throw() { // id = -1 return NULL; } -TestCase *TestManager::getTestCase(unsigned int id) throw() { +TestCase *TestManager::getTestCase(unsigned int id, const std::string &description) throw() { test_pool_nc_it it = a_testPool.find(id); if (it != a_testPool.end()) return it->second; - TestCase *result = new TestCase(id); + TestCase *result = new TestCase(id, description); 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; - // TODO: stop the possible command threads or there will be a core dump +bool TestManager::clearPool(std::string &result) throw() { + + result = ""; + + if (!tests()) { + result = "there are not programmed test cases to be removed"; + return false; + } + + int total = a_testPool.size(); + int unsafe = 0; + + test_pool_it it; + for (it = a_testPool.begin(); it != a_testPool.end(); it++) { + if (!it->second->safeToClear()) { // Check that non pending threads are running (command steps): + unsafe++; + } + } + + if (unsafe > 0) { + result = "some test cases cannot be removed ("; + result += anna::functions::asString(unsafe); + result += "/"; + result += anna::functions::asString(total); + result += "), mainly those having running-thread steps. Check for stuck external procedures or try later."; + return false; + } + + // Here is safe to clear: + for (it = a_testPool.begin(); it != a_testPool.end(); it++) { + delete it->second; + } a_testPool.clear(); - a_sessionIdTestCaseMap.clear(); - a_subscriberIdTestCaseMap.clear(); + a_key1TestCaseMap.clear(); + a_key2TestCaseMap.clear(); a_currentTestIt = a_testPool.end(); a_poolCycle = 1; configureTTPS(0); // stop a_statSummary.clear(); + return true; } @@ -299,8 +347,8 @@ bool TestManager::resetPool(bool hard) throw() { if (it->second->reset(hard)) result = true; } - //a_sessionIdTestCaseMap.clear(); - //a_subscriberIdTestCaseMap.clear(); + //a_key1TestCaseMap.clear(); + //a_key2TestCaseMap.clear(); return result; } @@ -375,7 +423,7 @@ bool TestManager::nextTestCase() throw() { } } -TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() { +TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() { try { sessionId = anna::diameter::helpers::base::functions::getSessionId(message); } @@ -384,15 +432,15 @@ TestCase *TestManager::getTestCaseFromSessionId(const anna::DataBlock &message, LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION)); return NULL; } - std::map::const_iterator sessionIdIt = a_sessionIdTestCaseMap.find(sessionId); - if (sessionIdIt != a_sessionIdTestCaseMap.end()) + auto sessionIdIt = a_key1TestCaseMap.find(sessionId); + if (sessionIdIt != a_key1TestCaseMap.end()) return sessionIdIt->second; LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION)); return NULL; } -TestCase *TestManager::getTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() { +TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() { try { subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164); if (subscriberId == "") // try with IMSI @@ -403,15 +451,15 @@ TestCase *TestManager::getTestCaseFromSubscriberId(const anna::DataBlock &messag LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION)); return NULL; } - std::map::const_iterator subscriberIdIt = a_subscriberIdTestCaseMap.find(subscriberId); - if (subscriberIdIt != a_subscriberIdTestCaseMap.end()) + auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId); + if (subscriberIdIt != a_key2TestCaseMap.end()) return subscriberIdIt->second; LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION)); return NULL; } -void TestManager::receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) { +void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) { // Testing disabled: if (!tests()) return; @@ -419,16 +467,16 @@ void TestManager::receiveMessage(const anna::DataBlock &message, const anna::dia // Identify the test case: std::string sessionId, subscriberId; TestCase *tc; - tc = getTestCaseFromSessionId(message, sessionId); + tc = getDiameterTestCaseFromSessionId(message, sessionId); if (!tc) - tc = getTestCaseFromSubscriberId(message, subscriberId); + tc = getDiameterTestCaseFromSubscriberId(message, subscriberId); if (!tc) { LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from server: ", message), ANNA_FILE_LOCATION)); // this should not appear return; } // Work with Test case: - TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */); + TestStepWaitDiameter *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 += "'; "; @@ -452,7 +500,7 @@ void TestManager::receiveMessage(const anna::DataBlock &message, const anna::dia } } -void TestManager::receiveMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) { +void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) { // Testing disabled: if (!tests()) return; @@ -460,16 +508,16 @@ void TestManager::receiveMessage(const anna::DataBlock &message, const anna::dia // Identify the test case: std::string sessionId, subscriberId; TestCase *tc; - tc = getTestCaseFromSessionId(message, sessionId); + tc = getDiameterTestCaseFromSessionId(message, sessionId); if (!tc) - tc = getTestCaseFromSubscriberId(message, subscriberId); + tc = getDiameterTestCaseFromSubscriberId(message, subscriberId); if (!tc) { LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from client: ", message), ANNA_FILE_LOCATION)); // this should not appear return; } // Work with Test case: - TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */); + TestStepWaitDiameter *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 += "'; "; @@ -512,12 +560,13 @@ throw() { result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no")); result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no")); result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no")); + result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no")); result->createAttribute("AutoResetHard", (a_autoResetHard ? "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); + result->createAttribute("TicksPerSecond", a_clock->isActive() ? ticksPerSecond : 0); } if (a_currentTestIt != a_testPool.end()) { result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first); @@ -536,8 +585,210 @@ throw() { return result; } +anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const +throw() { + // if only a single testsuite element is present, the testsuites element can be omitted + //anna::xml::Node* result = parent->createChild("testsuites"); + anna::xml::Node* result = parent->createChild("testsuite"); + +/* +https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports + +JUnit XSD file +============== +https://windyroad.com.au/dl/Open%20Source/JUnit.xsd + +EXAMPLES +======== + + + + + + + + + + + + + + + + + + +Some of these items can occur multiple times: + +There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element. +Each properties element can have multiple property children. +Each testsuite element can have multiple testcase children. +Each testcase element can have multiple error, failure, system-out, or system-err children: + + + + + + details about failure + + + +*/ + +/* + + tests="" + + disabled="" + errors="" + failures="" + hostname="" + id="" + package="" + skipped="" + time="" + timestamp="" + > +*/ + result->createAttribute("name", "ADML Testing Main Testsuite"); + int poolSize = a_testPool.size(); + result->createAttribute("tests", poolSize); + + result->createAttribute("errors", 0); + result->createAttribute("failures", a_statSummary.getFailedCount()); + result->createAttribute("hostname", anna::functions::getHostname()); + result->createAttribute("skipped", a_statSummary.getInitializedCount()); + + // Testcases: + +/* + + + assertions="" + classname="" + status="" + time="" + > + +*/ + int testcasesLapseMs = 0; + int startTimestampMs = 0; + double secs; + if (poolSize != 0) { + test_pool_it it_min = a_testPool.begin(); + test_pool_it it_max = a_testPool.end(); + startTimestampMs = (int)((*it_min).second->getStartTimestamp()); + std::string debugSummary; + + for (test_pool_it it = it_min; it != it_max; it++) { + anna::xml::Node* testcase = result->createChild("testcase"); + auto tc = (*it).second; + + testcasesLapseMs = (int)(tc->getLapseMs()); + testcase->createAttribute("classname", "adml_testcase"); + testcase->createAttribute("description", tc->getDescription()); + testcase->createAttribute("status", TestCase::asText(tc->getState())); + + secs = testcasesLapseMs / 1000.0; + testcase->createAttribute("time", anna::functions::asString(secs, "%.2f")); + + if (tc->isFailed()) { + anna::xml::Node* failure = testcase->createChild("failure"); + std::string text = ""; + debugSummary = tc->getDebugSummary().asString(); + if (debugSummary != "") { + text += "[Testcase debug summary]"; + text += ""; + } + text += "[Testcase information (xml dump)]"; + text += ""; + failure->createText(text); + } + } + } + + time_t startTimestamp = startTimestampMs/1000; + time(&startTimestamp); + char buf[sizeof "2011-10-08T07:07:09Z"]; + strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp)); + // this will work too, if your compiler doesn't support %F or %T: + // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + // std::cout << buf << "\n"; + result->createAttribute("timestamp", buf); + + + return result; +} + std::string TestManager::asXMLString() const throw() { anna::xml::Node root("root"); return anna::xml::Compiler().apply(asXML(&root)); } +std::string TestManager::junitAsXMLString() const throw() { + anna::xml::Node root("root"); + return anna::xml::Compiler().apply(junitAsXML(&root)); +} + +std::string TestManager::summaryCounts() const throw() { + + std::string result= "\nSummary Counts:\n"; + unsigned int total = a_statSummary.getTotal(); + result += "\nTotal: " + anna::functions::asString(total); + result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount()); + result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount()); + unsigned int failed = a_statSummary.getFailedCount(); + unsigned int success = a_statSummary.getSuccessCount(); + result += "\nFailed: " + anna::functions::asString(failed); + result += "\nSuccess: " + anna::functions::asString(success); + std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL"; + if (total != 0) { + result += std::string("\n\nVERDICT: ") + verdict; + } + + return result; +} + +std::string TestManager::summaryStates() const throw() { + + std::string result = "\nSummary States:\n"; + const char *literal = "\n[%s] %s"; + const char *secsLiteral = " (%.2f secs)"; + + int testcasesLapseMs = 0; + int startTimestampMs = 0; + double secs; + int poolSize = a_testPool.size(); + if (poolSize != 0) { + test_pool_it it_min = a_testPool.begin(); + test_pool_it it_max = a_testPool.end(); + startTimestampMs = (int)((*it_min).second->getStartTimestamp()); + + for (test_pool_it it = it_min; it != it_max; it++) { + auto tc = (*it).second; + + testcasesLapseMs = (int)(tc->getLapseMs()); + + result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str()); + + if (testcasesLapseMs >= 0) { + secs = testcasesLapseMs / 1000.0; + result += anna::functions::asString(secsLiteral, secs); + } + } + } + else { + result +="\nNo test cases programmed !"; + } + + return result; +} +