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 TestCase *TestManager::findTestCase(unsigned int id) const throw() { // id = -1 provides current test case triggered
265 if (!tests()) return NULL;
266 test_pool_it it = ((id != -1) ? a_testPool.find(id) : a_currentTestIt);
267 if (it != a_testPool.end()) return const_cast<TestCase*>(it->second);
271 TestCase *TestManager::getTestCase(unsigned int id, const std::string &description) throw() {
273 test_pool_nc_it it = a_testPool.find(id);
274 if (it != a_testPool.end()) return it->second;
276 TestCase *result = new TestCase(id, description);
277 a_testPool[id] = result;
281 bool TestManager::clearPool() throw() {
282 if (!tests()) return false;
283 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) delete it->second;
284 // TODO: stop the possible command threads or there will be a core dump
287 a_key1TestCaseMap.clear();
288 a_key2TestCaseMap.clear();
289 a_currentTestIt = a_testPool.end();
291 configureTTPS(0); // stop
292 a_statSummary.clear();
296 bool TestManager::resetPool(bool hard) throw() {
297 bool result = false; // any reset
299 if (!tests()) return result;
300 for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
301 if (it->second->reset(hard))
304 //a_key1TestCaseMap.clear();
305 //a_key2TestCaseMap.clear();
309 bool TestManager::tick() throw() {
310 LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION));
311 return execTestCases(a_synchronousAmount);
314 bool TestManager::execTestCases(int sync_amount) throw() {
317 LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION));
321 // Synchronous sendings per tick:
322 int count = sync_amount;
324 if (!nextTestCase()) return false; // stop the clock
331 bool TestManager::nextTestCase() throw() {
335 // Limit for in-progress test cases:
336 if (getInProgressCount() >= a_inProgressLimit) {
337 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION));
338 return true; // wait next tick to release OTA test cases
342 if (a_currentTestIt == a_testPool.end())
343 a_currentTestIt = a_testPool.begin();
348 if (a_currentTestIt == a_testPool.end()) {
349 if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) {
350 LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION));
356 std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]";
357 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);
360 //a_currentTestIt = a_testPool.begin();
361 return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state
362 // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed.
366 // Hard reset or soft reset to initialize already finished (in previous cycle) test cases:
367 a_currentTestIt->second->reset(a_autoResetHard);
369 // Process test case:
370 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));
371 if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
372 a_currentTestIt->second->process();
373 return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
374 // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
375 // forget to programm a test case timeout with a reasonable value
380 TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw() {
382 sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
384 catch (anna::RuntimeException &ex) {
386 LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
389 auto sessionIdIt = a_key1TestCaseMap.find(sessionId);
390 if (sessionIdIt != a_key1TestCaseMap.end())
391 return sessionIdIt->second;
393 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
397 TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() {
399 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
400 if (subscriberId == "") // try with IMSI
401 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
403 catch (anna::RuntimeException &ex) {
405 LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
408 auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId);
409 if (subscriberIdIt != a_key2TestCaseMap.end())
410 return subscriberIdIt->second;
412 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
416 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException) {
419 if (!tests()) return;
421 // Identify the test case:
422 std::string sessionId, subscriberId;
424 tc = getDiameterTestCaseFromSessionId(message, sessionId);
426 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
428 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
432 // Work with Test case:
433 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
434 if (!tsw) { // store as 'uncovered'
435 std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
438 static anna::diameter::codec::Message codecMsg;
439 codecMsg.decode(message);
440 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
441 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
443 catch (anna::RuntimeException &ex) {
445 hint += ex.asString();
447 hint += "\nClient Session: "; hint += clientSession->asString();
449 tc->addDebugSummaryHint(hint);
452 tsw->setClientSession(const_cast<anna::diameter::comm::ClientSession*>(clientSession));
457 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException) {
460 if (!tests()) return;
462 // Identify the test case:
463 std::string sessionId, subscriberId;
465 tc = getDiameterTestCaseFromSessionId(message, sessionId);
467 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
469 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
473 // Work with Test case:
474 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
475 if (!tsw) { // store as 'uncovered'
476 std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
479 static anna::diameter::codec::Message codecMsg;
480 codecMsg.decode(message);
481 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
482 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
484 catch (anna::RuntimeException &ex) {
486 hint += ex.asString();
488 hint += "\nServer Session: "; hint += serverSession->asString();
490 tc->addDebugSummaryHint(hint);
493 tsw->setServerSession(const_cast<anna::diameter::comm::ServerSession*>(serverSession));
498 anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const
500 anna::xml::Node* result = parent->createChild("TestManager");
502 int poolSize = a_testPool.size();
503 result->createAttribute("NumberOfTestCases", poolSize);
504 if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats);
505 else result->createAttribute("PoolRepeats", "disabled");
506 result->createAttribute("PoolCycle", a_poolCycle);
507 a_statSummary.asXML(result);
508 if (a_inProgressLimit == UINT_MAX)
509 result->createAttribute("InProgressLimit", "<no limit>");
511 result->createAttribute("InProgressLimit", a_inProgressLimit);
512 result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no"));
513 result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no"));
514 result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
515 result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
516 result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
517 result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
518 result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
519 result->createAttribute("ReportsDirectory", a_reportsDirectory);
521 result->createAttribute("AsynchronousSendings", a_synchronousAmount);
522 int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout();
523 result->createAttribute("TicksPerSecond", ticksPerSecond);
525 if (a_currentTestIt != a_testPool.end()) {
526 result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first);
529 anna::xml::Node* testCases = result->createChild("TestCases");
530 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
531 if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue;
532 if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue;
533 if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue;
534 if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue;
535 (*it).second->asXML(testCases);
542 anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
544 // if only a single testsuite element is present, the testsuites element can be omitted
545 //anna::xml::Node* result = parent->createChild("testsuites");
546 anna::xml::Node* result = parent->createChild("testsuite");
549 https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
553 https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
557 <?xml version="1.0" encoding="UTF-8"?>
558 <testsuites disabled="" errors="" failures="" name="" tests="" time="">
559 <testsuite disabled="" errors="" failures="" hostname="" id=""
560 name="" package="" skipped="" tests="" time="" timestamp="">
562 <property name="" value=""/>
564 <testcase assertions="" classname="" name="" status="" time="">
566 <error message="" type=""/>
567 <failure message="" type=""/>
576 Some of these items can occur multiple times:
578 There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
579 Each properties element can have multiple property children.
580 Each testsuite element can have multiple testcase children.
581 Each testcase element can have multiple error, failure, system-out, or system-err children:
583 <testsuite tests="3">
584 <testcase classname="foo1" name="ASuccessfulTest"/>
585 <testcase classname="foo2" name="AnotherSuccessfulTest"/>
586 <testcase classname="foo3" name="AFailingTest">
587 <failure type="NotEnoughFoo"> details about failure </failure>
594 <testsuite name="" <!-- Full (class) name of the test for non-aggregated testsuite documents.
595 Class name without the package for aggregated testsuites documents. Required -->
596 tests="" <!-- The total number of tests in the suite, required. -->
598 disabled="" <!-- the total number of disabled tests in the suite. optional -->
599 errors="" <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
600 for example an unchecked throwable; or a problem with the implementation of the test. optional -->
601 failures="" <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
602 by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
603 hostname="" <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
604 id="" <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
605 package="" <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
606 skipped="" <!-- The total number of skipped tests. optional -->
607 time="" <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
608 timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
611 result->createAttribute("name", "ADML Testing Main Testsuite");
612 int poolSize = a_testPool.size();
613 result->createAttribute("tests", poolSize);
615 result->createAttribute("errors", 0);
616 result->createAttribute("failures", a_statSummary.getFailedCount());
617 result->createAttribute("hostname", anna::functions::getHostname());
618 result->createAttribute("skipped", a_statSummary.getInitializedCount());
623 <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
624 <testcase name="" <!-- Name of the test method, required. -->
625 assertions="" <!-- number of assertions in the test case. optional -->
626 classname="" <!-- Full class name for the class the test method is in. required -->
628 time="" <!-- Time taken (in seconds) to execute the test. optional -->
632 int testcasesLapseMs = 0;
633 int startTimestampMs = 0;
636 test_pool_it it_min = a_testPool.begin();
637 test_pool_it it_max = a_testPool.end();
638 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
639 std::string debugSummary;
641 for (test_pool_it it = it_min; it != it_max; it++) {
642 anna::xml::Node* testcase = result->createChild("testcase");
643 auto tc = (*it).second;
645 testcasesLapseMs = (int)(tc->getLapseMs());
646 testcase->createAttribute("classname", "adml_testcase");
647 testcase->createAttribute("description", tc->getDescription());
648 testcase->createAttribute("status", TestCase::asText(tc->getState()));
650 secs = testcasesLapseMs / 1000.0;
651 testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
653 if (tc->isFailed()) {
654 anna::xml::Node* failure = testcase->createChild("failure");
655 std::string text = "";
656 debugSummary = tc->getDebugSummary().asString();
657 if (debugSummary != "") {
658 text += "[Testcase debug summary]";
660 text += debugSummary;
663 text += "[Testcase information (xml dump)]";
665 text += tc->asXMLString();
667 failure->createText(text);
672 time_t startTimestamp = startTimestampMs/1000;
673 time(&startTimestamp);
674 char buf[sizeof "2011-10-08T07:07:09Z"];
675 strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
676 // this will work too, if your compiler doesn't support %F or %T:
677 // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
678 // std::cout << buf << "\n";
679 result->createAttribute("timestamp", buf);
685 std::string TestManager::asXMLString() const throw() {
686 anna::xml::Node root("root");
687 return anna::xml::Compiler().apply(asXML(&root));
690 std::string TestManager::junitAsXMLString() const throw() {
691 anna::xml::Node root("root");
692 return anna::xml::Compiler().apply(junitAsXML(&root));
695 std::string TestManager::summaryCounts() const throw() {
697 std::string result= "\nSummary Counts:\n";
698 unsigned int total = a_statSummary.getTotal();
699 result += "\nTotal: " + anna::functions::asString(total);
700 result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
701 result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
702 unsigned int failed = a_statSummary.getFailedCount();
703 unsigned int success = a_statSummary.getSuccessCount();
704 result += "\nFailed: " + anna::functions::asString(failed);
705 result += "\nSuccess: " + anna::functions::asString(success);
706 std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
708 result += std::string("\n\nVERDICT: ") + verdict;
714 std::string TestManager::summaryStates() const throw() {
716 std::string result = "\nSummary States:\n";
717 const char *literal = "\n[%s] %s";
718 const char *secsLiteral = " (%.2f secs)";
720 int testcasesLapseMs = 0;
721 int startTimestampMs = 0;
723 int poolSize = a_testPool.size();
725 test_pool_it it_min = a_testPool.begin();
726 test_pool_it it_max = a_testPool.end();
727 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
729 for (test_pool_it it = it_min; it != it_max; it++) {
730 auto tc = (*it).second;
732 testcasesLapseMs = (int)(tc->getLapseMs());
734 result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
736 if (testcasesLapseMs >= 0) {
737 secs = testcasesLapseMs / 1000.0;
738 result += anna::functions::asString(secsLiteral, secs);
743 result +="\nNo test cases programmed !";