1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
13 #include <anna/testing/TestManager.hpp>
15 #include <anna/xml/Compiler.hpp>
16 #include <anna/xml/Node.hpp>
17 #include <anna/core/tracing/Logger.hpp>
18 #include <anna/app/functions.hpp>
19 #include <anna/timex/Engine.hpp>
20 #include <anna/diameter/helpers/base/functions.hpp>
21 #include <anna/diameter/helpers/dcca/functions.hpp>
22 #include <anna/comm/functions.hpp>
23 #include <anna/core/functions.hpp>
24 #include <anna/diameter.comm/ClientSession.hpp>
25 #include <anna/diameter.comm/ServerSession.hpp>
26 #include <anna/testing/TestStep.hpp>
27 #include <anna/testing/TestClock.hpp>
28 #include <anna/diameter/codec/Message.hpp>
34 using namespace anna::testing;
37 ///////////////////////////////////////////////////////////////////////////////////////////////////
38 void TestManager::StatSummary::newTCState(const TestCase::State::_v beginState, const TestCase::State::_v endState) {
40 if ((beginState == TestCase::State::Initialized)&&(endState == TestCase::State::Initialized)) { // special case (new test case provisioning)
46 case TestCase::State::Initialized: a_initializedTcs--; break;
47 case TestCase::State::InProgress: a_inprogressTcs--; break;
48 case TestCase::State::Failed: a_failedTcs--; break;
49 case TestCase::State::Success: a_sucessTcs--; break;
53 case TestCase::State::Initialized: a_initializedTcs++; break;
54 case TestCase::State::InProgress: a_inprogressTcs++; break;
55 case TestCase::State::Failed: a_failedTcs++; break;
56 case TestCase::State::Success: a_sucessTcs++; break;
61 void TestManager::StatSummary::clear() {
68 anna::xml::Node *TestManager::StatSummary::asXML(anna::xml::Node* parent) const {
69 anna::xml::Node* result = parent->createChild("StatSummary");
71 anna::xml::Node* tcs = result->createChild("TestCasesCounts");
72 tcs->createAttribute("Total", a_initializedTcs + a_inprogressTcs + a_failedTcs + a_sucessTcs);
73 tcs->createAttribute("Initialized", a_initializedTcs);
74 tcs->createAttribute("InProgress", a_inprogressTcs);
75 tcs->createAttribute("Failed", a_failedTcs);
76 tcs->createAttribute("Success", a_sucessTcs);
80 ///////////////////////////////////////////////////////////////////////////////////////////////////
84 TestManager::TestManager() :
85 anna::timex::TimeEventObserver("TestManager") {
86 a_timeController = NULL;
87 a_reportsDirectory = "./";
89 a_dumpInProgressReports = false;
90 a_dumpInitializedReports = false;
91 a_dumpFailedReports = false;
92 a_dumpSuccessReports = false;
94 a_dumpHexMessages = false;
96 a_synchronousAmount = 1;
97 a_poolRepeats = 0; // repeat disabled by default
99 a_inProgressLimit = UINT_MAX; // no limit
101 //a_testPool.clear();
102 //a_statSummary.clear();
104 a_autoResetHard = false;
106 a_currentTestIt = a_testPool.end();
109 void TestManager::registerKey1(const std::string &key, const TestCase *testCase) noexcept(false) {
111 auto it = a_key1TestCaseMap.find(key);
112 if (it != a_key1TestCaseMap.end()) { // found
113 unsigned int id = it->second->getId();
114 if (id != testCase->getId()) {
115 throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such key1: %s", id, key.c_str()), ANNA_FILE_LOCATION);
119 a_key1TestCaseMap[key] = const_cast<TestCase*>(testCase);
120 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerKey1 for test case (id = %llu): %s)", testCase->getId(), key.c_str()), ANNA_FILE_LOCATION));
124 void TestManager::registerKey2(const std::string &key, const TestCase *testCase) noexcept(false) {
126 auto it = a_key2TestCaseMap.find(key);
127 if (it != a_key2TestCaseMap.end()) { // found
128 unsigned int id = it->second->getId();
129 if (id != testCase->getId()) {
130 throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such key2: %s", id, key.c_str()), ANNA_FILE_LOCATION);
134 a_key2TestCaseMap[key] = const_cast<TestCase*>(testCase);
135 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerKey2 for test case (id = %llu): %s)", testCase->getId(), key.c_str()), ANNA_FILE_LOCATION));
139 TestTimer* TestManager::createTimer(TestCaseStep* testCaseStep, const anna::Millisecond &timeout, const TestTimer::Type::_v type)
141 TestTimer* result(NULL);
143 if(a_timeController == NULL)
144 throw anna::RuntimeException("You must invoke 'setTimerController' with a not NULL timex engine", ANNA_FILE_LOCATION);
146 anna::Guard guard(a_timeController, "TestManager::createTimer"); // avoid interblocking
147 result = a_timers.create();
148 result->setType(type);
149 result->setId((anna::timex::TimeEvent::Id) testCaseStep);
150 result->setObserver(this);
151 result->setContext(testCaseStep);
152 result->setTimeout(timeout);
155 std::string msg("TestManager::createTimer | ");
156 msg += result->asString();
157 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
160 a_timeController->activate(result);
164 void TestManager::cancelTimer(TestTimer* timer)
170 std::string msg("TestManager::cancel | ");
171 msg += timer->asString();
172 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
176 if(a_timeController == NULL)
177 a_timeController = anna::app::functions::component <anna::timex::Engine> (ANNA_FILE_LOCATION);
179 a_timeController->cancel(timer);
180 } catch(anna::RuntimeException& ex) {
185 //------------------------------------------------------------------------------------------
186 // Se invoca automaticamente desde anna::timex::Engine
187 //------------------------------------------------------------------------------------------
188 void TestManager::release(anna::timex::TimeEvent* timeEvent)
190 TestTimer* timer = static_cast <TestTimer*>(timeEvent);
191 timer->setContext(NULL);
192 a_timers.release(timer);
195 bool TestManager::configureTTPS(int testTicksPerSecond) {
197 if (testTicksPerSecond == 0) {
199 a_timeController->cancel(a_clock);
200 LOGDEBUG(anna::Logger::debug("Testing timer clock stopped !", ANNA_FILE_LOCATION));
203 LOGDEBUG(anna::Logger::debug("No testing timer started yet !", ANNA_FILE_LOCATION));
207 else if (testTicksPerSecond < 0) {
208 LOGWARNING(anna::Logger::warning("Invalid 'ttps' provided", ANNA_FILE_LOCATION));
212 anna::Millisecond applicationTimeInterval = anna::Millisecond(1000 / testTicksPerSecond);
213 a_synchronousAmount = 1;
215 if (applicationTimeInterval < anna::Millisecond(1)) {
216 LOGWARNING(anna::Logger::warning("Not allowed to configure more than 1000 events per second for for triggering testing system", ANNA_FILE_LOCATION));
220 if (applicationTimeInterval < a_timeController->getResolution()) {
221 int maximumObtained = 1000 / (int)(a_timeController->getResolution());
222 a_synchronousAmount = ceil((double)testTicksPerSecond/maximumObtained);
224 applicationTimeInterval = anna::Millisecond(a_synchronousAmount * 1000 / testTicksPerSecond);
227 if (a_synchronousAmount > 1) {
229 std::string msg = anna::functions::asString("Desired testing time trigger rate (%d events per second) requires more than one sending per event (%d every %lld milliseconds). Consider launch more instances with lower rate (for example %d ADML processes with %d ttps), or configure %d or more sockets to the remote endpoints to avoid burst sendings",
232 applicationTimeInterval.getValue(),
234 1000/applicationTimeInterval,
235 a_synchronousAmount);
237 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
242 a_clock->setTimeout(applicationTimeInterval);
245 a_clock = new TestClock("Testing clock", applicationTimeInterval, this); // clock
248 if (!a_clock->isActive()) a_timeController->activate(a_clock);
253 bool TestManager::gotoTestCase(unsigned int id) {
254 test_pool_it it = a_testPool.find(id);
255 if (it != a_testPool.end()) {
256 a_currentTestIt = it;
263 bool TestManager::runTestCase(unsigned int id) {
264 test_pool_it it = a_testPool.find(id);
265 if (it != a_testPool.end()) {
266 a_currentTestIt = it;
268 // execTestCases will get the next one, we must return 1 position:
269 if (a_currentTestIt == a_testPool.begin())
270 a_currentTestIt = a_testPool.end();
274 return execTestCases(1);
280 TestCase *TestManager::findTestCase(unsigned int id) const { // id = -1 provides current test case triggered
282 if (!tests()) return NULL;
283 test_pool_it it = ((id != -1) ? a_testPool.find(id) : a_currentTestIt);
284 if (it != a_testPool.end()) return const_cast<TestCase*>(it->second);
288 TestCase *TestManager::getTestCase(unsigned int id, const std::string &description) {
290 if (id == 0) { // 0 is used to sequence automatically and get the value of 'tests() + 1'
294 test_pool_nc_it it = a_testPool.find(id);
295 if (it != a_testPool.end()) return it->second;
297 TestCase *result = new TestCase(id, description);
298 a_testPool[id] = result;
302 bool TestManager::clearTestCase(std::string &result, unsigned int id) {
306 result = "There are not programmed test cases to be removed";
310 test_pool_it it = ((id != -1) ? a_testPool.find(id) : a_currentTestIt);
311 if (it == a_testPool.end()) {
312 result = "Test case id provided not found";
316 if (!it->second->safeToClear()) {
317 result = "Test case id provided has running-thread steps. Check for stuck external procedures or try later.";
321 a_testPool.erase(it);
323 auto key1_it = a_key1TestCaseMap.find(it->second->getKey());
324 if (key1_it != a_key1TestCaseMap.end()) a_key1TestCaseMap.erase(key1_it);
325 auto key2_it = a_key2TestCaseMap.find(it->second->getKey());
326 if (key2_it != a_key2TestCaseMap.end()) a_key2TestCaseMap.erase(key2_it);
328 result = "Provided test case has been dropped";
332 bool TestManager::clearPool(std::string &result) {
337 result = "There are not programmed test cases to be removed";
341 int total = a_testPool.size();
345 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
346 if (!it->second->safeToClear()) { // Check that non pending threads are running (command steps):
352 result = "Some test cases cannot be removed (";
353 result += anna::functions::asString(unsafe);
355 result += anna::functions::asString(total);
356 result += "), mainly those having running-thread steps. Check for stuck external procedures or try later.";
360 // Here is safe to clear:
361 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
366 a_key1TestCaseMap.clear();
367 a_key2TestCaseMap.clear();
368 a_currentTestIt = a_testPool.end();
370 configureTTPS(0); // stop
371 a_statSummary.clear();
373 result = "all the programmed test cases have been dropped";
377 bool TestManager::resetPool(bool hard) {
378 bool result = false; // any reset
380 if (!tests()) return result;
381 for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
382 if (it->second->reset(hard))
385 //a_key1TestCaseMap.clear();
386 //a_key2TestCaseMap.clear();
390 bool TestManager::tick() {
391 LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION));
392 return execTestCases(a_synchronousAmount);
395 bool TestManager::execTestCases(int sync_amount) {
398 LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION));
402 // Synchronous sendings per tick:
403 int count = sync_amount;
405 if (!nextTestCase()) return false; // stop the clock
412 bool TestManager::nextTestCase() {
416 // Limit for in-progress test cases:
417 if (getInProgressCount() >= a_inProgressLimit) {
418 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION));
419 return true; // wait next tick to release OTA test cases
423 if (a_currentTestIt == a_testPool.end())
424 a_currentTestIt = a_testPool.begin();
429 if (a_currentTestIt == a_testPool.end()) {
430 if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) {
431 LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION));
437 std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]";
438 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);
441 //a_currentTestIt = a_testPool.begin();
442 return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state
443 // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed.
447 // Hard reset or soft reset to initialize already finished (in previous cycle) test cases:
448 a_currentTestIt->second->reset(a_autoResetHard);
450 // Process test case:
451 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));
452 if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
453 a_currentTestIt->second->process();
454 return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
455 // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
456 // forget to programm a test case timeout with a reasonable value
461 TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) {
463 sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
465 catch (anna::RuntimeException &ex) {
467 LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
470 auto sessionIdIt = a_key1TestCaseMap.find(sessionId);
471 if (sessionIdIt != a_key1TestCaseMap.end())
472 return sessionIdIt->second;
474 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
478 TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) {
480 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
481 if (subscriberId == "") // try with IMSI
482 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
484 catch (anna::RuntimeException &ex) {
486 LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
489 auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId);
490 if (subscriberIdIt != a_key2TestCaseMap.end())
491 return subscriberIdIt->second;
493 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
497 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) noexcept(false) {
500 if (!tests()) return;
502 // Identify the test case:
503 std::string sessionId, subscriberId;
505 tc = getDiameterTestCaseFromSessionId(message, sessionId);
507 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
509 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
513 // Work with Test case:
514 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
515 if (!tsw) { // store as 'uncovered'
516 std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
519 static anna::diameter::codec::Message codecMsg;
520 codecMsg.decode(message);
521 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
522 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
524 catch (anna::RuntimeException &ex) {
526 hint += ex.asString();
528 hint += "\nClient Session: "; hint += clientSession->asString();
530 tc->addDebugSummaryHint(hint);
533 tsw->setClientSession(const_cast<anna::diameter::comm::ClientSession*>(clientSession));
538 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) noexcept(false) {
541 if (!tests()) return;
543 // Identify the test case:
544 std::string sessionId, subscriberId;
546 tc = getDiameterTestCaseFromSessionId(message, sessionId);
548 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
550 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
554 // Work with Test case:
555 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
556 if (!tsw) { // store as 'uncovered'
557 std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
560 static anna::diameter::codec::Message codecMsg;
561 codecMsg.decode(message);
562 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
563 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
565 catch (anna::RuntimeException &ex) {
567 hint += ex.asString();
569 hint += "\nServer Session: "; hint += serverSession->asString();
571 tc->addDebugSummaryHint(hint);
574 tsw->setServerSession(const_cast<anna::diameter::comm::ServerSession*>(serverSession));
579 anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const
581 anna::xml::Node* result = parent->createChild("TestManager");
583 int poolSize = a_testPool.size();
584 result->createAttribute("NumberOfTestCases", poolSize);
585 if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats);
586 else result->createAttribute("PoolRepeats", "disabled");
587 result->createAttribute("PoolCycle", a_poolCycle);
588 a_statSummary.asXML(result);
589 if (a_inProgressLimit == UINT_MAX)
590 result->createAttribute("InProgressLimit", "[no limit]");
592 result->createAttribute("InProgressLimit", a_inProgressLimit);
593 result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no"));
594 result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no"));
595 result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
596 result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
597 result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
598 result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
599 result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
600 result->createAttribute("ReportsDirectory", a_reportsDirectory);
602 result->createAttribute("AsynchronousSendings", a_synchronousAmount);
603 int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout();
604 result->createAttribute("TicksPerSecond", a_clock->isActive() ? ticksPerSecond : 0);
606 if (a_currentTestIt != a_testPool.end()) {
607 result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first);
610 anna::xml::Node* testCases = result->createChild("TestCases");
611 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
612 if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue;
613 if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue;
614 if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue;
615 if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue;
616 (*it).second->asXML(testCases);
623 anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
625 // if only a single testsuite element is present, the testsuites element can be omitted
626 //anna::xml::Node* result = parent->createChild("testsuites");
627 anna::xml::Node* result = parent->createChild("testsuite");
630 https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
634 https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
638 <?xml version="1.0" encoding="UTF-8"?>
639 <testsuites disabled="" errors="" failures="" name="" tests="" time="">
640 <testsuite disabled="" errors="" failures="" hostname="" id=""
641 name="" package="" skipped="" tests="" time="" timestamp="">
643 <property name="" value=""/>
645 <testcase assertions="" classname="" name="" status="" time="">
647 <error message="" type=""/>
648 <failure message="" type=""/>
657 Some of these items can occur multiple times:
659 There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
660 Each properties element can have multiple property children.
661 Each testsuite element can have multiple testcase children.
662 Each testcase element can have multiple error, failure, system-out, or system-err children:
664 <testsuite tests="3">
665 <testcase classname="foo1" name="ASuccessfulTest"/>
666 <testcase classname="foo2" name="AnotherSuccessfulTest"/>
667 <testcase classname="foo3" name="AFailingTest">
668 <failure type="NotEnoughFoo"> details about failure </failure>
675 <testsuite name="" <!-- Full (class) name of the test for non-aggregated testsuite documents.
676 Class name without the package for aggregated testsuites documents. Required -->
677 tests="" <!-- The total number of tests in the suite, required. -->
679 disabled="" <!-- the total number of disabled tests in the suite. optional -->
680 errors="" <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
681 for example an unchecked throwable; or a problem with the implementation of the test. optional -->
682 failures="" <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
683 by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
684 hostname="" <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
685 id="" <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
686 package="" <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
687 skipped="" <!-- The total number of skipped tests. optional -->
688 time="" <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
689 timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
692 result->createAttribute("name", "ADML Testing Main Testsuite");
693 int poolSize = a_testPool.size();
694 result->createAttribute("tests", poolSize);
696 result->createAttribute("errors", 0);
697 result->createAttribute("failures", a_statSummary.getFailedCount());
698 result->createAttribute("hostname", anna::functions::getHostname());
699 result->createAttribute("skipped", a_statSummary.getInitializedCount());
704 <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
705 <testcase name="" <!-- Name of the test method, required. -->
706 assertions="" <!-- number of assertions in the test case. optional -->
707 classname="" <!-- Full class name for the class the test method is in. required -->
709 time="" <!-- Time taken (in seconds) to execute the test. optional -->
713 int testcasesLapseMs = 0;
714 int startTimestampMs = 0;
717 test_pool_it it_min = a_testPool.begin();
718 test_pool_it it_max = a_testPool.end();
719 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
720 std::string debugSummary;
722 for (test_pool_it it = it_min; it != it_max; it++) {
723 anna::xml::Node* testcase = result->createChild("testcase");
724 auto tc = (*it).second;
726 testcasesLapseMs = (int)(tc->getLapseMs());
727 testcase->createAttribute("classname", "adml_testcase");
728 testcase->createAttribute("description", tc->getDescription());
729 testcase->createAttribute("status", TestCase::asText(tc->getState()));
731 secs = testcasesLapseMs / 1000.0;
732 testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
734 if (tc->isFailed()) {
735 anna::xml::Node* failure = testcase->createChild("failure");
736 std::string text = "";
737 debugSummary = tc->getDebugSummary().asString();
738 if (debugSummary != "") {
739 text += "[Testcase debug summary]";
741 text += debugSummary;
744 text += "[Testcase information (xml dump)]";
746 text += tc->asXMLString();
748 failure->createText(text);
753 time_t startTimestamp = startTimestampMs/1000;
754 time(&startTimestamp);
755 char buf[sizeof "2011-10-08T07:07:09Z"];
756 strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
757 // this will work too, if your compiler doesn't support %F or %T:
758 // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
759 // std::cout << buf << "\n";
760 result->createAttribute("timestamp", buf);
766 std::string TestManager::asXMLString() const {
767 anna::xml::Node root("root");
768 return anna::xml::Compiler().apply(asXML(&root));
771 std::string TestManager::junitAsXMLString() const {
772 anna::xml::Node root("root");
773 return anna::xml::Compiler().apply(junitAsXML(&root));
776 std::string TestManager::summaryCounts() const {
778 std::string result= "\nSummary Counts:\n";
779 unsigned int total = a_statSummary.getTotal();
780 result += "\nTotal: " + anna::functions::asString(total);
781 result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
782 result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
783 unsigned int failed = a_statSummary.getFailedCount();
784 unsigned int success = a_statSummary.getSuccessCount();
785 result += "\nFailed: " + anna::functions::asString(failed);
786 result += "\nSuccess: " + anna::functions::asString(success);
787 std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
789 result += std::string("\n\nVERDICT: ") + verdict;
795 std::string TestManager::summaryStates() const {
797 std::string result = "\nSummary States:\n";
798 const char *literal = "\n[%s] %s";
799 const char *secsLiteral = " (%.2f secs)";
801 int testcasesLapseMs = 0;
802 int startTimestampMs = 0;
804 int poolSize = a_testPool.size();
806 test_pool_it it_min = a_testPool.begin();
807 test_pool_it it_max = a_testPool.end();
808 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
810 for (test_pool_it it = it_min; it != it_max; it++) {
811 auto tc = (*it).second;
813 testcasesLapseMs = (int)(tc->getLapseMs());
815 result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
817 if (testcasesLapseMs >= 0) {
818 secs = testcasesLapseMs / 1000.0;
819 result += anna::functions::asString(secsLiteral, secs);
824 result +="\nNo test cases programmed !";