X-Git-Url: https://git.teslayout.com/public/public/public/?a=blobdiff_plain;f=source%2Ftesting%2FTestManager.cpp;h=87278f14ce768aa66fccaf82daf59c81f38e5897;hb=5a6cba5fde2b2f538a7515f8293cc0a8d9589dfa;hp=8f1a72812a1c8c8a5e344ea80fffebba7469d14a;hpb=d723d5bf571eb48c641b092058eaa38bb6c4fcc8;p=anna.git diff --git a/source/testing/TestManager.cpp b/source/testing/TestManager.cpp index 8f1a728..87278f1 100644 --- a/source/testing/TestManager.cpp +++ b/source/testing/TestManager.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,7 @@ using namespace anna::testing; /////////////////////////////////////////////////////////////////////////////////////////////////// -void TestManager::StatSummary::newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) throw() { +void TestManager::StatSummary::newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) { if ((beginState == TestCase::State::Initialized)&&(endState == TestCase::State::Initialized)) { // special case (new test case provisioning) a_initializedTcs++; @@ -57,14 +58,14 @@ void TestManager::StatSummary::newTCState(const TestCase::State::_v beginState, } } -void TestManager::StatSummary::clear() throw() { +void TestManager::StatSummary::clear() { a_initializedTcs = 0; a_inprogressTcs = 0; a_failedTcs = 0; a_sucessTcs = 0; } -anna::xml::Node *TestManager::StatSummary::asXML(anna::xml::Node* parent) const throw() { +anna::xml::Node *TestManager::StatSummary::asXML(anna::xml::Node* parent) const { anna::xml::Node* result = parent->createChild("StatSummary"); anna::xml::Node* tcs = result->createChild("TestCasesCounts"); @@ -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; @@ -98,41 +100,44 @@ TestManager::TestManager() : a_clock = NULL; //a_testPool.clear(); //a_statSummary.clear(); + + a_autoResetHard = false; + 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) noexcept(false) { - 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) noexcept(false) { - 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)); } } TestTimer* TestManager::createTimer(TestCaseStep* testCaseStep, const anna::Millisecond &timeout, const TestTimer::Type::_v type) -throw(anna::RuntimeException) { +noexcept(false) { TestTimer* result(NULL); if(a_timeController == NULL) @@ -157,7 +162,7 @@ throw(anna::RuntimeException) { } void TestManager::cancelTimer(TestTimer* timer) -throw() { +{ if(timer == NULL) return; @@ -181,15 +186,15 @@ throw() { // Se invoca automaticamente desde anna::timex::Engine //------------------------------------------------------------------------------------------ void TestManager::release(anna::timex::TimeEvent* timeEvent) -throw() { +{ TestTimer* timer = static_cast (timeEvent); timer->setContext(NULL); a_timers.release(timer); } -bool TestManager::configureTTPS(int testTicksPerSecond) throw() { +bool TestManager::configureTTPS(int testTicksPerSecond) { - 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)); @@ -245,7 +250,7 @@ bool TestManager::configureTTPS(int testTicksPerSecond) throw() { return true; } -bool TestManager::gotoTestCase(unsigned int id) throw() { +bool TestManager::gotoTestCase(unsigned int id) { test_pool_it it = a_testPool.find(id); if (it != a_testPool.end()) { a_currentTestIt = it; @@ -255,7 +260,24 @@ bool TestManager::gotoTestCase(unsigned int id) throw() { return false; } -TestCase *TestManager::findTestCase(unsigned int id) const throw() { // id = -1 provides current test case triggered +bool TestManager::runTestCase(unsigned int id) { + 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 { // id = -1 provides current test case triggered if (!tests()) return NULL; test_pool_it it = ((id != -1) ? a_testPool.find(id) : a_currentTestIt); @@ -263,32 +285,62 @@ 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) { 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) { + + 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(); + + result = "all the programmed test cases have been dropped"; return true; } -bool TestManager::resetPool(bool hard) throw() { +bool TestManager::resetPool(bool hard) { bool result = false; // any reset if (!tests()) return result; @@ -296,17 +348,17 @@ 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; } -bool TestManager::tick() throw() { +bool TestManager::tick() { LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION)); return execTestCases(a_synchronousAmount); } -bool TestManager::execTestCases(int sync_amount) throw() { +bool TestManager::execTestCases(int sync_amount) { if (!tests()) { LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION)); @@ -323,7 +375,7 @@ bool TestManager::execTestCases(int sync_amount) throw() { return true; } -bool TestManager::nextTestCase() throw() { +bool TestManager::nextTestCase() { while (true) { @@ -358,8 +410,8 @@ bool TestManager::nextTestCase() throw() { } } - // Soft reset to initialize already finished (in previous cycle) test cases: - a_currentTestIt->second->reset(false); + // Hard reset or soft reset to initialize already finished (in previous cycle) test cases: + a_currentTestIt->second->reset(a_autoResetHard); // 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)); @@ -372,7 +424,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) { try { sessionId = anna::diameter::helpers::base::functions::getSessionId(message); } @@ -381,15 +433,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) { 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 @@ -400,15 +452,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) noexcept(false) { // Testing disabled: if (!tests()) return; @@ -416,29 +468,30 @@ 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 += "':"; + std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; "; try { static anna::diameter::codec::Message codecMsg; codecMsg.decode(message); - hint += "\n"; hint += codecMsg.asXMLString(); + hint += "HEX Message: '"; hint += anna::functions::asHexString(message); + hint += "'; XML Message:\n"; hint += codecMsg.asXMLString(); } catch (anna::RuntimeException &ex) { ex.trace(); - hint += "\n"; hint += ex.asString(); + hint += ex.asString(); } - hint += "\n"; hint += clientSession->asString(); + hint += "\nClient Session: "; hint += clientSession->asString(); tc->addDebugSummaryHint(hint); } @@ -448,7 +501,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) noexcept(false) { // Testing disabled: if (!tests()) return; @@ -456,29 +509,30 @@ 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 += "':"; + std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; "; try { static anna::diameter::codec::Message codecMsg; codecMsg.decode(message); - hint += "\n"; hint += codecMsg.asXMLString(); + hint += "HEX Message: '"; hint += anna::functions::asHexString(message); + hint += "'; XML Message:\n"; hint += codecMsg.asXMLString(); } catch (anna::RuntimeException &ex) { ex.trace(); - hint += "\n"; hint += ex.asString(); + hint += ex.asString(); } - hint += "\n"; hint += serverSession->asString(); + hint += "\nServer Session: "; hint += serverSession->asString(); tc->addDebugSummaryHint(hint); } @@ -489,7 +543,7 @@ void TestManager::receiveMessage(const anna::DataBlock &message, const anna::dia } anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const -throw() { +{ anna::xml::Node* result = parent->createChild("TestManager"); int poolSize = a_testPool.size(); @@ -499,7 +553,7 @@ throw() { result->createAttribute("PoolCycle", a_poolCycle); a_statSummary.asXML(result); if (a_inProgressLimit == UINT_MAX) - result->createAttribute("InProgressLimit", ""); + result->createAttribute("InProgressLimit", "[no limit]"); else result->createAttribute("InProgressLimit", a_inProgressLimit); result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no")); @@ -507,11 +561,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); @@ -530,8 +586,210 @@ throw() { return result; } -std::string TestManager::asXMLString() const throw() { +anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const +{ + // 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 { anna::xml::Node root("root"); return anna::xml::Compiler().apply(asXML(&root)); } +std::string TestManager::junitAsXMLString() const { + anna::xml::Node root("root"); + return anna::xml::Compiler().apply(junitAsXML(&root)); +} + +std::string TestManager::summaryCounts() const { + + 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 { + + 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; +} +