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();
342 bool TestManager::resetPool(bool hard) throw() {
343 bool result = false; // any reset
345 if (!tests()) return result;
346 for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
347 if (it->second->reset(hard))
350 //a_key1TestCaseMap.clear();
351 //a_key2TestCaseMap.clear();
355 bool TestManager::tick() throw() {
356 LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION));
357 return execTestCases(a_synchronousAmount);
360 bool TestManager::execTestCases(int sync_amount) throw() {
363 LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION));
367 // Synchronous sendings per tick:
368 int count = sync_amount;
370 if (!nextTestCase()) return false; // stop the clock
377 bool TestManager::nextTestCase() throw() {
381 // Limit for in-progress test cases:
382 if (getInProgressCount() >= a_inProgressLimit) {
383 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION));
384 return true; // wait next tick to release OTA test cases
388 if (a_currentTestIt == a_testPool.end())
389 a_currentTestIt = a_testPool.begin();
394 if (a_currentTestIt == a_testPool.end()) {
395 if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) {
396 LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION));
402 std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]";
403 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);
406 //a_currentTestIt = a_testPool.begin();
407 return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state
408 // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed.
412 // Hard reset or soft reset to initialize already finished (in previous cycle) test cases:
413 a_currentTestIt->second->reset(a_autoResetHard);
415 // Process test case:
416 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));
417 if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
418 a_currentTestIt->second->process();
419 return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
420 // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
421 // forget to programm a test case timeout with a reasonable value
426 TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() {
428 sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
430 catch (anna::RuntimeException &ex) {
432 LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
435 auto sessionIdIt = a_key1TestCaseMap.find(sessionId);
436 if (sessionIdIt != a_key1TestCaseMap.end())
437 return sessionIdIt->second;
439 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
443 TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() {
445 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
446 if (subscriberId == "") // try with IMSI
447 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
449 catch (anna::RuntimeException &ex) {
451 LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
454 auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId);
455 if (subscriberIdIt != a_key2TestCaseMap.end())
456 return subscriberIdIt->second;
458 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
462 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) {
465 if (!tests()) return;
467 // Identify the test case:
468 std::string sessionId, subscriberId;
470 tc = getDiameterTestCaseFromSessionId(message, sessionId);
472 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
474 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
478 // Work with Test case:
479 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
480 if (!tsw) { // store as 'uncovered'
481 std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
484 static anna::diameter::codec::Message codecMsg;
485 codecMsg.decode(message);
486 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
487 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
489 catch (anna::RuntimeException &ex) {
491 hint += ex.asString();
493 hint += "\nClient Session: "; hint += clientSession->asString();
495 tc->addDebugSummaryHint(hint);
498 tsw->setClientSession(const_cast<anna::diameter::comm::ClientSession*>(clientSession));
503 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) {
506 if (!tests()) return;
508 // Identify the test case:
509 std::string sessionId, subscriberId;
511 tc = getDiameterTestCaseFromSessionId(message, sessionId);
513 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
515 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
519 // Work with Test case:
520 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
521 if (!tsw) { // store as 'uncovered'
522 std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
525 static anna::diameter::codec::Message codecMsg;
526 codecMsg.decode(message);
527 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
528 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
530 catch (anna::RuntimeException &ex) {
532 hint += ex.asString();
534 hint += "\nServer Session: "; hint += serverSession->asString();
536 tc->addDebugSummaryHint(hint);
539 tsw->setServerSession(const_cast<anna::diameter::comm::ServerSession*>(serverSession));
544 anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const
546 anna::xml::Node* result = parent->createChild("TestManager");
548 int poolSize = a_testPool.size();
549 result->createAttribute("NumberOfTestCases", poolSize);
550 if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats);
551 else result->createAttribute("PoolRepeats", "disabled");
552 result->createAttribute("PoolCycle", a_poolCycle);
553 a_statSummary.asXML(result);
554 if (a_inProgressLimit == UINT_MAX)
555 result->createAttribute("InProgressLimit", "<no limit>");
557 result->createAttribute("InProgressLimit", a_inProgressLimit);
558 result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no"));
559 result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no"));
560 result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
561 result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
562 result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
563 result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
564 result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
565 result->createAttribute("ReportsDirectory", a_reportsDirectory);
567 result->createAttribute("AsynchronousSendings", a_synchronousAmount);
568 int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout();
569 result->createAttribute("TicksPerSecond", a_clock->isActive() ? ticksPerSecond : 0);
571 if (a_currentTestIt != a_testPool.end()) {
572 result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first);
575 anna::xml::Node* testCases = result->createChild("TestCases");
576 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
577 if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue;
578 if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue;
579 if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue;
580 if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue;
581 (*it).second->asXML(testCases);
588 anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
590 // if only a single testsuite element is present, the testsuites element can be omitted
591 //anna::xml::Node* result = parent->createChild("testsuites");
592 anna::xml::Node* result = parent->createChild("testsuite");
595 https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
599 https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
603 <?xml version="1.0" encoding="UTF-8"?>
604 <testsuites disabled="" errors="" failures="" name="" tests="" time="">
605 <testsuite disabled="" errors="" failures="" hostname="" id=""
606 name="" package="" skipped="" tests="" time="" timestamp="">
608 <property name="" value=""/>
610 <testcase assertions="" classname="" name="" status="" time="">
612 <error message="" type=""/>
613 <failure message="" type=""/>
622 Some of these items can occur multiple times:
624 There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
625 Each properties element can have multiple property children.
626 Each testsuite element can have multiple testcase children.
627 Each testcase element can have multiple error, failure, system-out, or system-err children:
629 <testsuite tests="3">
630 <testcase classname="foo1" name="ASuccessfulTest"/>
631 <testcase classname="foo2" name="AnotherSuccessfulTest"/>
632 <testcase classname="foo3" name="AFailingTest">
633 <failure type="NotEnoughFoo"> details about failure </failure>
640 <testsuite name="" <!-- Full (class) name of the test for non-aggregated testsuite documents.
641 Class name without the package for aggregated testsuites documents. Required -->
642 tests="" <!-- The total number of tests in the suite, required. -->
644 disabled="" <!-- the total number of disabled tests in the suite. optional -->
645 errors="" <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
646 for example an unchecked throwable; or a problem with the implementation of the test. optional -->
647 failures="" <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
648 by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
649 hostname="" <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
650 id="" <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
651 package="" <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
652 skipped="" <!-- The total number of skipped tests. optional -->
653 time="" <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
654 timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
657 result->createAttribute("name", "ADML Testing Main Testsuite");
658 int poolSize = a_testPool.size();
659 result->createAttribute("tests", poolSize);
661 result->createAttribute("errors", 0);
662 result->createAttribute("failures", a_statSummary.getFailedCount());
663 result->createAttribute("hostname", anna::functions::getHostname());
664 result->createAttribute("skipped", a_statSummary.getInitializedCount());
669 <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
670 <testcase name="" <!-- Name of the test method, required. -->
671 assertions="" <!-- number of assertions in the test case. optional -->
672 classname="" <!-- Full class name for the class the test method is in. required -->
674 time="" <!-- Time taken (in seconds) to execute the test. optional -->
678 int testcasesLapseMs = 0;
679 int startTimestampMs = 0;
682 test_pool_it it_min = a_testPool.begin();
683 test_pool_it it_max = a_testPool.end();
684 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
685 std::string debugSummary;
687 for (test_pool_it it = it_min; it != it_max; it++) {
688 anna::xml::Node* testcase = result->createChild("testcase");
689 auto tc = (*it).second;
691 testcasesLapseMs = (int)(tc->getLapseMs());
692 testcase->createAttribute("classname", "adml_testcase");
693 testcase->createAttribute("description", tc->getDescription());
694 testcase->createAttribute("status", TestCase::asText(tc->getState()));
696 secs = testcasesLapseMs / 1000.0;
697 testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
699 if (tc->isFailed()) {
700 anna::xml::Node* failure = testcase->createChild("failure");
701 std::string text = "";
702 debugSummary = tc->getDebugSummary().asString();
703 if (debugSummary != "") {
704 text += "[Testcase debug summary]";
706 text += debugSummary;
709 text += "[Testcase information (xml dump)]";
711 text += tc->asXMLString();
713 failure->createText(text);
718 time_t startTimestamp = startTimestampMs/1000;
719 time(&startTimestamp);
720 char buf[sizeof "2011-10-08T07:07:09Z"];
721 strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
722 // this will work too, if your compiler doesn't support %F or %T:
723 // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
724 // std::cout << buf << "\n";
725 result->createAttribute("timestamp", buf);
731 std::string TestManager::asXMLString() const throw() {
732 anna::xml::Node root("root");
733 return anna::xml::Compiler().apply(asXML(&root));
736 std::string TestManager::junitAsXMLString() const throw() {
737 anna::xml::Node root("root");
738 return anna::xml::Compiler().apply(junitAsXML(&root));
741 std::string TestManager::summaryCounts() const throw() {
743 std::string result= "\nSummary Counts:\n";
744 unsigned int total = a_statSummary.getTotal();
745 result += "\nTotal: " + anna::functions::asString(total);
746 result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
747 result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
748 unsigned int failed = a_statSummary.getFailedCount();
749 unsigned int success = a_statSummary.getSuccessCount();
750 result += "\nFailed: " + anna::functions::asString(failed);
751 result += "\nSuccess: " + anna::functions::asString(success);
752 std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
754 result += std::string("\n\nVERDICT: ") + verdict;
760 std::string TestManager::summaryStates() const throw() {
762 std::string result = "\nSummary States:\n";
763 const char *literal = "\n[%s] %s";
764 const char *secsLiteral = " (%.2f secs)";
766 int testcasesLapseMs = 0;
767 int startTimestampMs = 0;
769 int poolSize = a_testPool.size();
771 test_pool_it it_min = a_testPool.begin();
772 test_pool_it it_max = a_testPool.end();
773 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
775 for (test_pool_it it = it_min; it != it_max; it++) {
776 auto tc = (*it).second;
778 testcasesLapseMs = (int)(tc->getLapseMs());
780 result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
782 if (testcasesLapseMs >= 0) {
783 secs = testcasesLapseMs / 1000.0;
784 result += anna::functions::asString(secsLiteral, secs);
789 result +="\nNo test cases programmed !";