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() throw() {
299 if (!tests()) return false;
300 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) delete it->second;
301 // TODO: stop the possible command threads or there will be a core dump
304 a_key1TestCaseMap.clear();
305 a_key2TestCaseMap.clear();
306 a_currentTestIt = a_testPool.end();
308 configureTTPS(0); // stop
309 a_statSummary.clear();
313 bool TestManager::resetPool(bool hard) throw() {
314 bool result = false; // any reset
316 if (!tests()) return result;
317 for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
318 if (it->second->reset(hard))
321 //a_key1TestCaseMap.clear();
322 //a_key2TestCaseMap.clear();
326 bool TestManager::tick() throw() {
327 LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION));
328 return execTestCases(a_synchronousAmount);
331 bool TestManager::execTestCases(int sync_amount) throw() {
334 LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION));
338 // Synchronous sendings per tick:
339 int count = sync_amount;
341 if (!nextTestCase()) return false; // stop the clock
348 bool TestManager::nextTestCase() throw() {
352 // Limit for in-progress test cases:
353 if (getInProgressCount() >= a_inProgressLimit) {
354 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION));
355 return true; // wait next tick to release OTA test cases
359 if (a_currentTestIt == a_testPool.end())
360 a_currentTestIt = a_testPool.begin();
365 if (a_currentTestIt == a_testPool.end()) {
366 if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) {
367 LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION));
373 std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]";
374 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);
377 //a_currentTestIt = a_testPool.begin();
378 return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state
379 // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed.
383 // Hard reset or soft reset to initialize already finished (in previous cycle) test cases:
384 a_currentTestIt->second->reset(a_autoResetHard);
386 // Process test case:
387 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));
388 if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
389 a_currentTestIt->second->process();
390 return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
391 // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
392 // forget to programm a test case timeout with a reasonable value
397 TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() {
399 sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
401 catch (anna::RuntimeException &ex) {
403 LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
406 auto sessionIdIt = a_key1TestCaseMap.find(sessionId);
407 if (sessionIdIt != a_key1TestCaseMap.end())
408 return sessionIdIt->second;
410 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
414 TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() {
416 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
417 if (subscriberId == "") // try with IMSI
418 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
420 catch (anna::RuntimeException &ex) {
422 LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
425 auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId);
426 if (subscriberIdIt != a_key2TestCaseMap.end())
427 return subscriberIdIt->second;
429 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
433 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) {
436 if (!tests()) return;
438 // Identify the test case:
439 std::string sessionId, subscriberId;
441 tc = getDiameterTestCaseFromSessionId(message, sessionId);
443 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
445 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
449 // Work with Test case:
450 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
451 if (!tsw) { // store as 'uncovered'
452 std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
455 static anna::diameter::codec::Message codecMsg;
456 codecMsg.decode(message);
457 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
458 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
460 catch (anna::RuntimeException &ex) {
462 hint += ex.asString();
464 hint += "\nClient Session: "; hint += clientSession->asString();
466 tc->addDebugSummaryHint(hint);
469 tsw->setClientSession(const_cast<anna::diameter::comm::ClientSession*>(clientSession));
474 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) {
477 if (!tests()) return;
479 // Identify the test case:
480 std::string sessionId, subscriberId;
482 tc = getDiameterTestCaseFromSessionId(message, sessionId);
484 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
486 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
490 // Work with Test case:
491 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
492 if (!tsw) { // store as 'uncovered'
493 std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
496 static anna::diameter::codec::Message codecMsg;
497 codecMsg.decode(message);
498 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
499 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
501 catch (anna::RuntimeException &ex) {
503 hint += ex.asString();
505 hint += "\nServer Session: "; hint += serverSession->asString();
507 tc->addDebugSummaryHint(hint);
510 tsw->setServerSession(const_cast<anna::diameter::comm::ServerSession*>(serverSession));
515 anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const
517 anna::xml::Node* result = parent->createChild("TestManager");
519 int poolSize = a_testPool.size();
520 result->createAttribute("NumberOfTestCases", poolSize);
521 if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats);
522 else result->createAttribute("PoolRepeats", "disabled");
523 result->createAttribute("PoolCycle", a_poolCycle);
524 a_statSummary.asXML(result);
525 if (a_inProgressLimit == UINT_MAX)
526 result->createAttribute("InProgressLimit", "<no limit>");
528 result->createAttribute("InProgressLimit", a_inProgressLimit);
529 result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no"));
530 result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no"));
531 result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
532 result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
533 result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
534 result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
535 result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
536 result->createAttribute("ReportsDirectory", a_reportsDirectory);
538 result->createAttribute("AsynchronousSendings", a_synchronousAmount);
539 int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout();
540 result->createAttribute("TicksPerSecond", ticksPerSecond);
542 if (a_currentTestIt != a_testPool.end()) {
543 result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first);
546 anna::xml::Node* testCases = result->createChild("TestCases");
547 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
548 if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue;
549 if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue;
550 if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue;
551 if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue;
552 (*it).second->asXML(testCases);
559 anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
561 // if only a single testsuite element is present, the testsuites element can be omitted
562 //anna::xml::Node* result = parent->createChild("testsuites");
563 anna::xml::Node* result = parent->createChild("testsuite");
566 https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
570 https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
574 <?xml version="1.0" encoding="UTF-8"?>
575 <testsuites disabled="" errors="" failures="" name="" tests="" time="">
576 <testsuite disabled="" errors="" failures="" hostname="" id=""
577 name="" package="" skipped="" tests="" time="" timestamp="">
579 <property name="" value=""/>
581 <testcase assertions="" classname="" name="" status="" time="">
583 <error message="" type=""/>
584 <failure message="" type=""/>
593 Some of these items can occur multiple times:
595 There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
596 Each properties element can have multiple property children.
597 Each testsuite element can have multiple testcase children.
598 Each testcase element can have multiple error, failure, system-out, or system-err children:
600 <testsuite tests="3">
601 <testcase classname="foo1" name="ASuccessfulTest"/>
602 <testcase classname="foo2" name="AnotherSuccessfulTest"/>
603 <testcase classname="foo3" name="AFailingTest">
604 <failure type="NotEnoughFoo"> details about failure </failure>
611 <testsuite name="" <!-- Full (class) name of the test for non-aggregated testsuite documents.
612 Class name without the package for aggregated testsuites documents. Required -->
613 tests="" <!-- The total number of tests in the suite, required. -->
615 disabled="" <!-- the total number of disabled tests in the suite. optional -->
616 errors="" <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
617 for example an unchecked throwable; or a problem with the implementation of the test. optional -->
618 failures="" <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
619 by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
620 hostname="" <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
621 id="" <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
622 package="" <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
623 skipped="" <!-- The total number of skipped tests. optional -->
624 time="" <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
625 timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
628 result->createAttribute("name", "ADML Testing Main Testsuite");
629 int poolSize = a_testPool.size();
630 result->createAttribute("tests", poolSize);
632 result->createAttribute("errors", 0);
633 result->createAttribute("failures", a_statSummary.getFailedCount());
634 result->createAttribute("hostname", anna::functions::getHostname());
635 result->createAttribute("skipped", a_statSummary.getInitializedCount());
640 <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
641 <testcase name="" <!-- Name of the test method, required. -->
642 assertions="" <!-- number of assertions in the test case. optional -->
643 classname="" <!-- Full class name for the class the test method is in. required -->
645 time="" <!-- Time taken (in seconds) to execute the test. optional -->
649 int testcasesLapseMs = 0;
650 int startTimestampMs = 0;
653 test_pool_it it_min = a_testPool.begin();
654 test_pool_it it_max = a_testPool.end();
655 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
656 std::string debugSummary;
658 for (test_pool_it it = it_min; it != it_max; it++) {
659 anna::xml::Node* testcase = result->createChild("testcase");
660 auto tc = (*it).second;
662 testcasesLapseMs = (int)(tc->getLapseMs());
663 testcase->createAttribute("classname", "adml_testcase");
664 testcase->createAttribute("description", tc->getDescription());
665 testcase->createAttribute("status", TestCase::asText(tc->getState()));
667 secs = testcasesLapseMs / 1000.0;
668 testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
670 if (tc->isFailed()) {
671 anna::xml::Node* failure = testcase->createChild("failure");
672 std::string text = "";
673 debugSummary = tc->getDebugSummary().asString();
674 if (debugSummary != "") {
675 text += "[Testcase debug summary]";
677 text += debugSummary;
680 text += "[Testcase information (xml dump)]";
682 text += tc->asXMLString();
684 failure->createText(text);
689 time_t startTimestamp = startTimestampMs/1000;
690 time(&startTimestamp);
691 char buf[sizeof "2011-10-08T07:07:09Z"];
692 strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
693 // this will work too, if your compiler doesn't support %F or %T:
694 // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
695 // std::cout << buf << "\n";
696 result->createAttribute("timestamp", buf);
702 std::string TestManager::asXMLString() const throw() {
703 anna::xml::Node root("root");
704 return anna::xml::Compiler().apply(asXML(&root));
707 std::string TestManager::junitAsXMLString() const throw() {
708 anna::xml::Node root("root");
709 return anna::xml::Compiler().apply(junitAsXML(&root));
712 std::string TestManager::summaryCounts() const throw() {
714 std::string result= "\nSummary Counts:\n";
715 unsigned int total = a_statSummary.getTotal();
716 result += "\nTotal: " + anna::functions::asString(total);
717 result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
718 result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
719 unsigned int failed = a_statSummary.getFailedCount();
720 unsigned int success = a_statSummary.getSuccessCount();
721 result += "\nFailed: " + anna::functions::asString(failed);
722 result += "\nSuccess: " + anna::functions::asString(success);
723 std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
725 result += std::string("\n\nVERDICT: ") + verdict;
731 std::string TestManager::summaryStates() const throw() {
733 std::string result = "\nSummary States:\n";
734 const char *literal = "\n[%s] %s";
735 const char *secsLiteral = " (%.2f secs)";
737 int testcasesLapseMs = 0;
738 int startTimestampMs = 0;
740 int poolSize = a_testPool.size();
742 test_pool_it it_min = a_testPool.begin();
743 test_pool_it it_max = a_testPool.end();
744 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
746 for (test_pool_it it = it_min; it != it_max; it++) {
747 auto tc = (*it).second;
749 testcasesLapseMs = (int)(tc->getLapseMs());
751 result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
753 if (testcasesLapseMs >= 0) {
754 secs = testcasesLapseMs / 1000.0;
755 result += anna::functions::asString(secsLiteral, secs);
760 result +="\nNo test cases programmed !";