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) throw() {
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() throw() {
68 anna::xml::Node *TestManager::StatSummary::asXML(anna::xml::Node* parent) const throw() {
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) throw(anna::RuntimeException) {
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) throw(anna::RuntimeException) {
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)
140 throw(anna::RuntimeException) {
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) throw() {
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) throw() {
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) throw() {
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 throw() { // 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) throw() {
290 test_pool_nc_it it = a_testPool.find(id);
291 if (it != a_testPool.end()) return it->second;
293 TestCase *result = new TestCase(id, description);
294 a_testPool[id] = result;
298 bool TestManager::clearPool(std::string &result) throw() {
303 result = "There are not programmed test cases to be removed";
307 int total = a_testPool.size();
311 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
312 if (!it->second->safeToClear()) { // Check that non pending threads are running (command steps):
318 result = "Some test cases cannot be removed (";
319 result += anna::functions::asString(unsafe);
321 result += anna::functions::asString(total);
322 result += "), mainly those having running-thread steps. Check for stuck external procedures or try later.";
326 // Here is safe to clear:
327 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
332 a_key1TestCaseMap.clear();
333 a_key2TestCaseMap.clear();
334 a_currentTestIt = a_testPool.end();
336 configureTTPS(0); // stop
337 a_statSummary.clear();
339 result = "all the programmed test cases have been dropped";
343 bool TestManager::resetPool(bool hard) throw() {
344 bool result = false; // any reset
346 if (!tests()) return result;
347 for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
348 if (it->second->reset(hard))
351 //a_key1TestCaseMap.clear();
352 //a_key2TestCaseMap.clear();
356 bool TestManager::tick() throw() {
357 LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION));
358 return execTestCases(a_synchronousAmount);
361 bool TestManager::execTestCases(int sync_amount) throw() {
364 LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION));
368 // Synchronous sendings per tick:
369 int count = sync_amount;
371 if (!nextTestCase()) return false; // stop the clock
378 bool TestManager::nextTestCase() throw() {
382 // Limit for in-progress test cases:
383 if (getInProgressCount() >= a_inProgressLimit) {
384 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION));
385 return true; // wait next tick to release OTA test cases
389 if (a_currentTestIt == a_testPool.end())
390 a_currentTestIt = a_testPool.begin();
395 if (a_currentTestIt == a_testPool.end()) {
396 if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) {
397 LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION));
403 std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]";
404 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);
407 //a_currentTestIt = a_testPool.begin();
408 return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state
409 // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed.
413 // Hard reset or soft reset to initialize already finished (in previous cycle) test cases:
414 a_currentTestIt->second->reset(a_autoResetHard);
416 // Process test case:
417 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));
418 if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
419 a_currentTestIt->second->process();
420 return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
421 // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
422 // forget to programm a test case timeout with a reasonable value
427 TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() {
429 sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
431 catch (anna::RuntimeException &ex) {
433 LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
436 auto sessionIdIt = a_key1TestCaseMap.find(sessionId);
437 if (sessionIdIt != a_key1TestCaseMap.end())
438 return sessionIdIt->second;
440 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
444 TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() {
446 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
447 if (subscriberId == "") // try with IMSI
448 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
450 catch (anna::RuntimeException &ex) {
452 LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
455 auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId);
456 if (subscriberIdIt != a_key2TestCaseMap.end())
457 return subscriberIdIt->second;
459 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
463 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) {
466 if (!tests()) return;
468 // Identify the test case:
469 std::string sessionId, subscriberId;
471 tc = getDiameterTestCaseFromSessionId(message, sessionId);
473 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
475 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
479 // Work with Test case:
480 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
481 if (!tsw) { // store as 'uncovered'
482 std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
485 static anna::diameter::codec::Message codecMsg;
486 codecMsg.decode(message);
487 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
488 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
490 catch (anna::RuntimeException &ex) {
492 hint += ex.asString();
494 hint += "\nClient Session: "; hint += clientSession->asString();
496 tc->addDebugSummaryHint(hint);
499 tsw->setClientSession(const_cast<anna::diameter::comm::ClientSession*>(clientSession));
504 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) {
507 if (!tests()) return;
509 // Identify the test case:
510 std::string sessionId, subscriberId;
512 tc = getDiameterTestCaseFromSessionId(message, sessionId);
514 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
516 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
520 // Work with Test case:
521 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
522 if (!tsw) { // store as 'uncovered'
523 std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
526 static anna::diameter::codec::Message codecMsg;
527 codecMsg.decode(message);
528 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
529 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
531 catch (anna::RuntimeException &ex) {
533 hint += ex.asString();
535 hint += "\nServer Session: "; hint += serverSession->asString();
537 tc->addDebugSummaryHint(hint);
540 tsw->setServerSession(const_cast<anna::diameter::comm::ServerSession*>(serverSession));
545 anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const
547 anna::xml::Node* result = parent->createChild("TestManager");
549 int poolSize = a_testPool.size();
550 result->createAttribute("NumberOfTestCases", poolSize);
551 if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats);
552 else result->createAttribute("PoolRepeats", "disabled");
553 result->createAttribute("PoolCycle", a_poolCycle);
554 a_statSummary.asXML(result);
555 if (a_inProgressLimit == UINT_MAX)
556 result->createAttribute("InProgressLimit", "<no limit>");
558 result->createAttribute("InProgressLimit", a_inProgressLimit);
559 result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no"));
560 result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no"));
561 result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
562 result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
563 result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
564 result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
565 result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
566 result->createAttribute("ReportsDirectory", a_reportsDirectory);
568 result->createAttribute("AsynchronousSendings", a_synchronousAmount);
569 int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout();
570 result->createAttribute("TicksPerSecond", a_clock->isActive() ? ticksPerSecond : 0);
572 if (a_currentTestIt != a_testPool.end()) {
573 result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first);
576 anna::xml::Node* testCases = result->createChild("TestCases");
577 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
578 if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue;
579 if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue;
580 if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue;
581 if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue;
582 (*it).second->asXML(testCases);
589 anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
591 // if only a single testsuite element is present, the testsuites element can be omitted
592 //anna::xml::Node* result = parent->createChild("testsuites");
593 anna::xml::Node* result = parent->createChild("testsuite");
596 https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
600 https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
604 <?xml version="1.0" encoding="UTF-8"?>
605 <testsuites disabled="" errors="" failures="" name="" tests="" time="">
606 <testsuite disabled="" errors="" failures="" hostname="" id=""
607 name="" package="" skipped="" tests="" time="" timestamp="">
609 <property name="" value=""/>
611 <testcase assertions="" classname="" name="" status="" time="">
613 <error message="" type=""/>
614 <failure message="" type=""/>
623 Some of these items can occur multiple times:
625 There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
626 Each properties element can have multiple property children.
627 Each testsuite element can have multiple testcase children.
628 Each testcase element can have multiple error, failure, system-out, or system-err children:
630 <testsuite tests="3">
631 <testcase classname="foo1" name="ASuccessfulTest"/>
632 <testcase classname="foo2" name="AnotherSuccessfulTest"/>
633 <testcase classname="foo3" name="AFailingTest">
634 <failure type="NotEnoughFoo"> details about failure </failure>
641 <testsuite name="" <!-- Full (class) name of the test for non-aggregated testsuite documents.
642 Class name without the package for aggregated testsuites documents. Required -->
643 tests="" <!-- The total number of tests in the suite, required. -->
645 disabled="" <!-- the total number of disabled tests in the suite. optional -->
646 errors="" <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
647 for example an unchecked throwable; or a problem with the implementation of the test. optional -->
648 failures="" <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
649 by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
650 hostname="" <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
651 id="" <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
652 package="" <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
653 skipped="" <!-- The total number of skipped tests. optional -->
654 time="" <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
655 timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
658 result->createAttribute("name", "ADML Testing Main Testsuite");
659 int poolSize = a_testPool.size();
660 result->createAttribute("tests", poolSize);
662 result->createAttribute("errors", 0);
663 result->createAttribute("failures", a_statSummary.getFailedCount());
664 result->createAttribute("hostname", anna::functions::getHostname());
665 result->createAttribute("skipped", a_statSummary.getInitializedCount());
670 <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
671 <testcase name="" <!-- Name of the test method, required. -->
672 assertions="" <!-- number of assertions in the test case. optional -->
673 classname="" <!-- Full class name for the class the test method is in. required -->
675 time="" <!-- Time taken (in seconds) to execute the test. optional -->
679 int testcasesLapseMs = 0;
680 int startTimestampMs = 0;
683 test_pool_it it_min = a_testPool.begin();
684 test_pool_it it_max = a_testPool.end();
685 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
686 std::string debugSummary;
688 for (test_pool_it it = it_min; it != it_max; it++) {
689 anna::xml::Node* testcase = result->createChild("testcase");
690 auto tc = (*it).second;
692 testcasesLapseMs = (int)(tc->getLapseMs());
693 testcase->createAttribute("classname", "adml_testcase");
694 testcase->createAttribute("description", tc->getDescription());
695 testcase->createAttribute("status", TestCase::asText(tc->getState()));
697 secs = testcasesLapseMs / 1000.0;
698 testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
700 if (tc->isFailed()) {
701 anna::xml::Node* failure = testcase->createChild("failure");
702 std::string text = "";
703 debugSummary = tc->getDebugSummary().asString();
704 if (debugSummary != "") {
705 text += "[Testcase debug summary]";
707 text += debugSummary;
710 text += "[Testcase information (xml dump)]";
712 text += tc->asXMLString();
714 failure->createText(text);
719 time_t startTimestamp = startTimestampMs/1000;
720 time(&startTimestamp);
721 char buf[sizeof "2011-10-08T07:07:09Z"];
722 strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
723 // this will work too, if your compiler doesn't support %F or %T:
724 // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
725 // std::cout << buf << "\n";
726 result->createAttribute("timestamp", buf);
732 std::string TestManager::asXMLString() const throw() {
733 anna::xml::Node root("root");
734 return anna::xml::Compiler().apply(asXML(&root));
737 std::string TestManager::junitAsXMLString() const throw() {
738 anna::xml::Node root("root");
739 return anna::xml::Compiler().apply(junitAsXML(&root));
742 std::string TestManager::summaryCounts() const throw() {
744 std::string result= "\nSummary Counts:\n";
745 unsigned int total = a_statSummary.getTotal();
746 result += "\nTotal: " + anna::functions::asString(total);
747 result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
748 result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
749 unsigned int failed = a_statSummary.getFailedCount();
750 unsigned int success = a_statSummary.getSuccessCount();
751 result += "\nFailed: " + anna::functions::asString(failed);
752 result += "\nSuccess: " + anna::functions::asString(success);
753 std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
755 result += std::string("\n\nVERDICT: ") + verdict;
761 std::string TestManager::summaryStates() const throw() {
763 std::string result = "\nSummary States:\n";
764 const char *literal = "\n[%s] %s";
765 const char *secsLiteral = " (%.2f secs)";
767 int testcasesLapseMs = 0;
768 int startTimestampMs = 0;
770 int poolSize = a_testPool.size();
772 test_pool_it it_min = a_testPool.begin();
773 test_pool_it it_max = a_testPool.end();
774 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
776 for (test_pool_it it = it_min; it != it_max; it++) {
777 auto tc = (*it).second;
779 testcasesLapseMs = (int)(tc->getLapseMs());
781 result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
783 if (testcasesLapseMs >= 0) {
784 secs = testcasesLapseMs / 1000.0;
785 result += anna::functions::asString(secsLiteral, secs);
790 result +="\nNo test cases programmed !";