1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
13 #include <anna/testing/TestManager.hpp>
15 #include <anna/xml/Compiler.hpp>
16 #include <anna/xml/Node.hpp>
17 #include <anna/core/tracing/Logger.hpp>
18 #include <anna/app/functions.hpp>
19 #include <anna/timex/Engine.hpp>
20 #include <anna/diameter/helpers/base/functions.hpp>
21 #include <anna/diameter/helpers/dcca/functions.hpp>
22 #include <anna/comm/functions.hpp>
23 #include <anna/core/functions.hpp>
24 #include <anna/diameter.comm/ClientSession.hpp>
25 #include <anna/diameter.comm/ServerSession.hpp>
26 #include <anna/testing/TestStep.hpp>
27 #include <anna/testing/TestClock.hpp>
28 #include <anna/diameter/codec/Message.hpp>
34 using namespace anna::testing;
37 ///////////////////////////////////////////////////////////////////////////////////////////////////
38 void TestManager::StatSummary::newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) {
40 if ((beginState == TestCase::State::Initialized)&&(endState == TestCase::State::Initialized)) { // special case (new test case provisioning)
46 case TestCase::State::Initialized: a_initializedTcs--; break;
47 case TestCase::State::InProgress: a_inprogressTcs--; break;
48 case TestCase::State::Failed: a_failedTcs--; break;
49 case TestCase::State::Success: a_sucessTcs--; break;
53 case TestCase::State::Initialized: a_initializedTcs++; break;
54 case TestCase::State::InProgress: a_inprogressTcs++; break;
55 case TestCase::State::Failed: a_failedTcs++; break;
56 case TestCase::State::Success: a_sucessTcs++; break;
61 void TestManager::StatSummary::clear() {
68 anna::xml::Node *TestManager::StatSummary::asXML(anna::xml::Node* parent) const {
69 anna::xml::Node* result = parent->createChild("StatSummary");
71 anna::xml::Node* tcs = result->createChild("TestCasesCounts");
72 tcs->createAttribute("Total", a_initializedTcs + a_inprogressTcs + a_failedTcs + a_sucessTcs);
73 tcs->createAttribute("Initialized", a_initializedTcs);
74 tcs->createAttribute("InProgress", a_inprogressTcs);
75 tcs->createAttribute("Failed", a_failedTcs);
76 tcs->createAttribute("Success", a_sucessTcs);
80 ///////////////////////////////////////////////////////////////////////////////////////////////////
84 TestManager::TestManager() :
85 anna::timex::TimeEventObserver("TestManager") {
86 a_timeController = NULL;
87 a_reportsDirectory = "./";
89 a_dumpInProgressReports = false;
90 a_dumpInitializedReports = false;
91 a_dumpFailedReports = false;
92 a_dumpSuccessReports = false;
94 a_dumpHexMessages = false;
96 a_synchronousAmount = 1;
97 a_poolRepeats = 0; // repeat disabled by default
99 a_inProgressLimit = UINT_MAX; // no limit
101 //a_testPool.clear();
102 //a_statSummary.clear();
104 a_autoResetHard = false;
106 a_currentTestIt = a_testPool.end();
109 void TestManager::registerKey1(const std::string &key, const TestCase *testCase) noexcept(false) {
111 auto it = a_key1TestCaseMap.find(key);
112 if (it != a_key1TestCaseMap.end()) { // found
113 unsigned int id = it->second->getId();
114 if (id != testCase->getId()) {
115 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);
119 a_key1TestCaseMap[key] = const_cast<TestCase*>(testCase);
120 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerKey1 for test case (id = %llu): %s)", testCase->getId(), key.c_str()), ANNA_FILE_LOCATION));
124 void TestManager::registerKey2(const std::string &key, const TestCase *testCase) noexcept(false) {
126 auto it = a_key2TestCaseMap.find(key);
127 if (it != a_key2TestCaseMap.end()) { // found
128 unsigned int id = it->second->getId();
129 if (id != testCase->getId()) {
130 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);
134 a_key2TestCaseMap[key] = const_cast<TestCase*>(testCase);
135 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerKey2 for test case (id = %llu): %s)", testCase->getId(), key.c_str()), ANNA_FILE_LOCATION));
139 TestTimer* TestManager::createTimer(TestCaseStep* testCaseStep, const anna::Millisecond &timeout, const TestTimer::Type::_v type)
141 TestTimer* result(NULL);
143 if(a_timeController == NULL)
144 throw anna::RuntimeException("You must invoke 'setTimerController' with a not NULL timex engine", ANNA_FILE_LOCATION);
146 anna::Guard guard(a_timeController, "TestManager::createTimer"); // avoid interblocking
147 result = a_timers.create();
148 result->setType(type);
149 result->setId((anna::timex::TimeEvent::Id) testCaseStep);
150 result->setObserver(this);
151 result->setContext(testCaseStep);
152 result->setTimeout(timeout);
155 std::string msg("TestManager::createTimer | ");
156 msg += result->asString();
157 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
160 a_timeController->activate(result);
164 void TestManager::cancelTimer(TestTimer* timer)
170 std::string msg("TestManager::cancel | ");
171 msg += timer->asString();
172 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
176 if(a_timeController == NULL)
177 a_timeController = anna::app::functions::component <anna::timex::Engine> (ANNA_FILE_LOCATION);
179 a_timeController->cancel(timer);
180 } catch(anna::RuntimeException& ex) {
185 //------------------------------------------------------------------------------------------
186 // Se invoca automaticamente desde anna::timex::Engine
187 //------------------------------------------------------------------------------------------
188 void TestManager::release(anna::timex::TimeEvent* timeEvent)
190 TestTimer* timer = static_cast <TestTimer*>(timeEvent);
191 timer->setContext(NULL);
192 a_timers.release(timer);
195 bool TestManager::configureTTPS(int testTicksPerSecond) {
197 if (testTicksPerSecond == 0) {
199 a_timeController->cancel(a_clock);
200 LOGDEBUG(anna::Logger::debug("Testing timer clock stopped !", ANNA_FILE_LOCATION));
203 LOGDEBUG(anna::Logger::debug("No testing timer started yet !", ANNA_FILE_LOCATION));
207 else if (testTicksPerSecond < 0) {
208 LOGWARNING(anna::Logger::warning("Invalid 'ttps' provided", ANNA_FILE_LOCATION));
212 anna::Millisecond applicationTimeInterval = anna::Millisecond(1000 / testTicksPerSecond);
213 a_synchronousAmount = 1;
215 if (applicationTimeInterval < anna::Millisecond(1)) {
216 LOGWARNING(anna::Logger::warning("Not allowed to configure more than 1000 events per second for for triggering testing system", ANNA_FILE_LOCATION));
220 if (applicationTimeInterval < a_timeController->getResolution()) {
221 int maximumObtained = 1000 / (int)(a_timeController->getResolution());
222 a_synchronousAmount = ceil((double)testTicksPerSecond/maximumObtained);
224 applicationTimeInterval = anna::Millisecond(a_synchronousAmount * 1000 / testTicksPerSecond);
227 if (a_synchronousAmount > 1) {
229 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",
232 applicationTimeInterval.getValue(),
234 1000/applicationTimeInterval,
235 a_synchronousAmount);
237 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
242 a_clock->setTimeout(applicationTimeInterval);
245 a_clock = new TestClock("Testing clock", applicationTimeInterval, this); // clock
248 if (!a_clock->isActive()) a_timeController->activate(a_clock);
253 bool TestManager::gotoTestCase(unsigned int id) {
254 test_pool_it it = a_testPool.find(id);
255 if (it != a_testPool.end()) {
256 a_currentTestIt = it;
263 bool TestManager::runTestCase(unsigned int id) {
264 test_pool_it it = a_testPool.find(id);
265 if (it != a_testPool.end()) {
266 a_currentTestIt = it;
268 // execTestCases will get the next one, we must return 1 position:
269 if (a_currentTestIt == a_testPool.begin())
270 a_currentTestIt = a_testPool.end();
274 return execTestCases(1);
280 TestCase *TestManager::findTestCase(unsigned int id) const { // id = -1 provides current test case triggered
282 if (!tests()) return NULL;
283 test_pool_it it = ((id != -1) ? a_testPool.find(id) : a_currentTestIt);
284 if (it != a_testPool.end()) return const_cast<TestCase*>(it->second);
288 TestCase *TestManager::getTestCase(unsigned int id, const std::string &description) {
290 if (id == 0) { // 0 is used to sequence automatically and get the value of 'tests() + 1'
294 test_pool_nc_it it = a_testPool.find(id);
295 if (it != a_testPool.end()) return it->second;
297 TestCase *result = new TestCase(id, description);
298 a_testPool[id] = result;
302 bool TestManager::clearPool(std::string &result) {
307 result = "There are not programmed test cases to be removed";
311 int total = a_testPool.size();
315 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
316 if (!it->second->safeToClear()) { // Check that non pending threads are running (command steps):
322 result = "Some test cases cannot be removed (";
323 result += anna::functions::asString(unsafe);
325 result += anna::functions::asString(total);
326 result += "), mainly those having running-thread steps. Check for stuck external procedures or try later.";
330 // Here is safe to clear:
331 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
336 a_key1TestCaseMap.clear();
337 a_key2TestCaseMap.clear();
338 a_currentTestIt = a_testPool.end();
340 configureTTPS(0); // stop
341 a_statSummary.clear();
343 result = "all the programmed test cases have been dropped";
347 bool TestManager::resetPool(bool hard) {
348 bool result = false; // any reset
350 if (!tests()) return result;
351 for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
352 if (it->second->reset(hard))
355 //a_key1TestCaseMap.clear();
356 //a_key2TestCaseMap.clear();
360 bool TestManager::tick() {
361 LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION));
362 return execTestCases(a_synchronousAmount);
365 bool TestManager::execTestCases(int sync_amount) {
368 LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION));
372 // Synchronous sendings per tick:
373 int count = sync_amount;
375 if (!nextTestCase()) return false; // stop the clock
382 bool TestManager::nextTestCase() {
386 // Limit for in-progress test cases:
387 if (getInProgressCount() >= a_inProgressLimit) {
388 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION));
389 return true; // wait next tick to release OTA test cases
393 if (a_currentTestIt == a_testPool.end())
394 a_currentTestIt = a_testPool.begin();
399 if (a_currentTestIt == a_testPool.end()) {
400 if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) {
401 LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION));
407 std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]";
408 anna::Logger::warning(anna::functions::asString("Testing pool cycle %d completed (repeats configured: %d%s). Restarting for the %s cycle", a_poolCycle, a_poolRepeats, nolimit.c_str(), (a_poolRepeats == a_poolCycle) ? "last":"next"), ANNA_FILE_LOCATION);
411 //a_currentTestIt = a_testPool.begin();
412 return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state
413 // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed.
417 // Hard reset or soft reset to initialize already finished (in previous cycle) test cases:
418 a_currentTestIt->second->reset(a_autoResetHard);
420 // Process test case:
421 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));
422 if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
423 a_currentTestIt->second->process();
424 return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
425 // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
426 // forget to programm a test case timeout with a reasonable value
431 TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) {
433 sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
435 catch (anna::RuntimeException &ex) {
437 LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
440 auto sessionIdIt = a_key1TestCaseMap.find(sessionId);
441 if (sessionIdIt != a_key1TestCaseMap.end())
442 return sessionIdIt->second;
444 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
448 TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) {
450 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
451 if (subscriberId == "") // try with IMSI
452 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
454 catch (anna::RuntimeException &ex) {
456 LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
459 auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId);
460 if (subscriberIdIt != a_key2TestCaseMap.end())
461 return subscriberIdIt->second;
463 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
467 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) noexcept(false) {
470 if (!tests()) return;
472 // Identify the test case:
473 std::string sessionId, subscriberId;
475 tc = getDiameterTestCaseFromSessionId(message, sessionId);
477 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
479 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
483 // Work with Test case:
484 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
485 if (!tsw) { // store as 'uncovered'
486 std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
489 static anna::diameter::codec::Message codecMsg;
490 codecMsg.decode(message);
491 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
492 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
494 catch (anna::RuntimeException &ex) {
496 hint += ex.asString();
498 hint += "\nClient Session: "; hint += clientSession->asString();
500 tc->addDebugSummaryHint(hint);
503 tsw->setClientSession(const_cast<anna::diameter::comm::ClientSession*>(clientSession));
508 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) noexcept(false) {
511 if (!tests()) return;
513 // Identify the test case:
514 std::string sessionId, subscriberId;
516 tc = getDiameterTestCaseFromSessionId(message, sessionId);
518 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
520 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
524 // Work with Test case:
525 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
526 if (!tsw) { // store as 'uncovered'
527 std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
530 static anna::diameter::codec::Message codecMsg;
531 codecMsg.decode(message);
532 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
533 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
535 catch (anna::RuntimeException &ex) {
537 hint += ex.asString();
539 hint += "\nServer Session: "; hint += serverSession->asString();
541 tc->addDebugSummaryHint(hint);
544 tsw->setServerSession(const_cast<anna::diameter::comm::ServerSession*>(serverSession));
549 anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const
551 anna::xml::Node* result = parent->createChild("TestManager");
553 int poolSize = a_testPool.size();
554 result->createAttribute("NumberOfTestCases", poolSize);
555 if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats);
556 else result->createAttribute("PoolRepeats", "disabled");
557 result->createAttribute("PoolCycle", a_poolCycle);
558 a_statSummary.asXML(result);
559 if (a_inProgressLimit == UINT_MAX)
560 result->createAttribute("InProgressLimit", "[no limit]");
562 result->createAttribute("InProgressLimit", a_inProgressLimit);
563 result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no"));
564 result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no"));
565 result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
566 result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
567 result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
568 result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
569 result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
570 result->createAttribute("ReportsDirectory", a_reportsDirectory);
572 result->createAttribute("AsynchronousSendings", a_synchronousAmount);
573 int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout();
574 result->createAttribute("TicksPerSecond", a_clock->isActive() ? ticksPerSecond : 0);
576 if (a_currentTestIt != a_testPool.end()) {
577 result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first);
580 anna::xml::Node* testCases = result->createChild("TestCases");
581 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
582 if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue;
583 if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue;
584 if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue;
585 if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue;
586 (*it).second->asXML(testCases);
593 anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
595 // if only a single testsuite element is present, the testsuites element can be omitted
596 //anna::xml::Node* result = parent->createChild("testsuites");
597 anna::xml::Node* result = parent->createChild("testsuite");
600 https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
604 https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
608 <?xml version="1.0" encoding="UTF-8"?>
609 <testsuites disabled="" errors="" failures="" name="" tests="" time="">
610 <testsuite disabled="" errors="" failures="" hostname="" id=""
611 name="" package="" skipped="" tests="" time="" timestamp="">
613 <property name="" value=""/>
615 <testcase assertions="" classname="" name="" status="" time="">
617 <error message="" type=""/>
618 <failure message="" type=""/>
627 Some of these items can occur multiple times:
629 There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
630 Each properties element can have multiple property children.
631 Each testsuite element can have multiple testcase children.
632 Each testcase element can have multiple error, failure, system-out, or system-err children:
634 <testsuite tests="3">
635 <testcase classname="foo1" name="ASuccessfulTest"/>
636 <testcase classname="foo2" name="AnotherSuccessfulTest"/>
637 <testcase classname="foo3" name="AFailingTest">
638 <failure type="NotEnoughFoo"> details about failure </failure>
645 <testsuite name="" <!-- Full (class) name of the test for non-aggregated testsuite documents.
646 Class name without the package for aggregated testsuites documents. Required -->
647 tests="" <!-- The total number of tests in the suite, required. -->
649 disabled="" <!-- the total number of disabled tests in the suite. optional -->
650 errors="" <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
651 for example an unchecked throwable; or a problem with the implementation of the test. optional -->
652 failures="" <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
653 by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
654 hostname="" <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
655 id="" <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
656 package="" <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
657 skipped="" <!-- The total number of skipped tests. optional -->
658 time="" <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
659 timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
662 result->createAttribute("name", "ADML Testing Main Testsuite");
663 int poolSize = a_testPool.size();
664 result->createAttribute("tests", poolSize);
666 result->createAttribute("errors", 0);
667 result->createAttribute("failures", a_statSummary.getFailedCount());
668 result->createAttribute("hostname", anna::functions::getHostname());
669 result->createAttribute("skipped", a_statSummary.getInitializedCount());
674 <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
675 <testcase name="" <!-- Name of the test method, required. -->
676 assertions="" <!-- number of assertions in the test case. optional -->
677 classname="" <!-- Full class name for the class the test method is in. required -->
679 time="" <!-- Time taken (in seconds) to execute the test. optional -->
683 int testcasesLapseMs = 0;
684 int startTimestampMs = 0;
687 test_pool_it it_min = a_testPool.begin();
688 test_pool_it it_max = a_testPool.end();
689 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
690 std::string debugSummary;
692 for (test_pool_it it = it_min; it != it_max; it++) {
693 anna::xml::Node* testcase = result->createChild("testcase");
694 auto tc = (*it).second;
696 testcasesLapseMs = (int)(tc->getLapseMs());
697 testcase->createAttribute("classname", "adml_testcase");
698 testcase->createAttribute("description", tc->getDescription());
699 testcase->createAttribute("status", TestCase::asText(tc->getState()));
701 secs = testcasesLapseMs / 1000.0;
702 testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
704 if (tc->isFailed()) {
705 anna::xml::Node* failure = testcase->createChild("failure");
706 std::string text = "";
707 debugSummary = tc->getDebugSummary().asString();
708 if (debugSummary != "") {
709 text += "[Testcase debug summary]";
711 text += debugSummary;
714 text += "[Testcase information (xml dump)]";
716 text += tc->asXMLString();
718 failure->createText(text);
723 time_t startTimestamp = startTimestampMs/1000;
724 time(&startTimestamp);
725 char buf[sizeof "2011-10-08T07:07:09Z"];
726 strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
727 // this will work too, if your compiler doesn't support %F or %T:
728 // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
729 // std::cout << buf << "\n";
730 result->createAttribute("timestamp", buf);
736 std::string TestManager::asXMLString() const {
737 anna::xml::Node root("root");
738 return anna::xml::Compiler().apply(asXML(&root));
741 std::string TestManager::junitAsXMLString() const {
742 anna::xml::Node root("root");
743 return anna::xml::Compiler().apply(junitAsXML(&root));
746 std::string TestManager::summaryCounts() const {
748 std::string result= "\nSummary Counts:\n";
749 unsigned int total = a_statSummary.getTotal();
750 result += "\nTotal: " + anna::functions::asString(total);
751 result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
752 result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
753 unsigned int failed = a_statSummary.getFailedCount();
754 unsigned int success = a_statSummary.getSuccessCount();
755 result += "\nFailed: " + anna::functions::asString(failed);
756 result += "\nSuccess: " + anna::functions::asString(success);
757 std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
759 result += std::string("\n\nVERDICT: ") + verdict;
765 std::string TestManager::summaryStates() const {
767 std::string result = "\nSummary States:\n";
768 const char *literal = "\n[%s] %s";
769 const char *secsLiteral = " (%.2f secs)";
771 int testcasesLapseMs = 0;
772 int startTimestampMs = 0;
774 int poolSize = a_testPool.size();
776 test_pool_it it_min = a_testPool.begin();
777 test_pool_it it_max = a_testPool.end();
778 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
780 for (test_pool_it it = it_min; it != it_max; it++) {
781 auto tc = (*it).second;
783 testcasesLapseMs = (int)(tc->getLapseMs());
785 result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
787 if (testcasesLapseMs >= 0) {
788 secs = testcasesLapseMs / 1000.0;
789 result += anna::functions::asString(secsLiteral, secs);
794 result +="\nNo test cases programmed !";