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 result = "Provided test case has been dropped";
324 bool something_removed = false;
326 auto key1_it = a_key1TestCaseMap.find(it->second->getKey1());
327 if (key1_it != a_key1TestCaseMap.end()) {
328 a_key1TestCaseMap.erase(key1_it);
329 result += " | Removed key1 = ";
330 result += it->second->getKey1();
331 something_removed = true;
333 auto key2_it = a_key2TestCaseMap.find(it->second->getKey2());
334 if (key2_it != a_key2TestCaseMap.end()) {
335 a_key2TestCaseMap.erase(key2_it);
336 result += " | Removed key2 = ";
337 result += it->second->getKey2();
338 something_removed = true;
341 if (something_removed) return true;
343 result = "Provided test case has been dropped, but key1 = '";
344 result += it->second->getKey1();
345 result += "' was not found, and also key2 = '";
346 result += it->second->getKey2();
347 result += "' was not found";
352 bool TestManager::clearPool(std::string &result) {
357 result = "There are not programmed test cases to be removed";
361 int total = a_testPool.size();
365 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
366 if (!it->second->safeToClear()) { // Check that non pending threads are running (command steps):
372 result = "Some test cases cannot be removed (";
373 result += anna::functions::asString(unsafe);
375 result += anna::functions::asString(total);
376 result += "), mainly those having running-thread steps. Check for stuck external procedures or try later.";
380 // Here is safe to clear:
381 for (it = a_testPool.begin(); it != a_testPool.end(); it++) {
386 a_key1TestCaseMap.clear();
387 a_key2TestCaseMap.clear();
388 a_currentTestIt = a_testPool.end();
390 configureTTPS(0); // stop
391 a_statSummary.clear();
393 result = "all the programmed test cases have been dropped";
397 bool TestManager::resetPool(bool hard) {
398 bool result = false; // any reset
400 if (!tests()) return result;
401 for (test_pool_nc_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
402 if (it->second->reset(hard))
405 //a_key1TestCaseMap.clear();
406 //a_key2TestCaseMap.clear();
410 bool TestManager::tick() {
411 LOGDEBUG(anna::Logger::debug("New test clock tick !", ANNA_FILE_LOCATION));
412 return execTestCases(a_synchronousAmount);
415 bool TestManager::execTestCases(int sync_amount) {
418 LOGWARNING(anna::Logger::warning("Testing pool is empty. You need programming", ANNA_FILE_LOCATION));
422 // Synchronous sendings per tick:
423 int count = sync_amount;
425 if (!nextTestCase()) return false; // stop the clock
432 bool TestManager::nextTestCase() {
436 // Limit for in-progress test cases:
437 if (getInProgressCount() >= a_inProgressLimit) {
438 LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager next case ignored (over in-progress count limit: %llu)", a_inProgressLimit), ANNA_FILE_LOCATION));
439 return true; // wait next tick to release OTA test cases
443 if (a_currentTestIt == a_testPool.end())
444 a_currentTestIt = a_testPool.begin();
449 if (a_currentTestIt == a_testPool.end()) {
450 if ((a_poolCycle > a_poolRepeats) && (a_poolRepeats != -1)) {
451 LOGWARNING(anna::Logger::warning("Testing pool cycle completed. No remaining repeat cycles left. Suspending", ANNA_FILE_LOCATION));
457 std::string nolimit = (a_poolRepeats != -1) ? "":" [no limit]";
458 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);
461 //a_currentTestIt = a_testPool.begin();
462 return true; // avoids infinite loop: if the cycle takes less time than test cases completion, below reset never will turns state
463 // into Initialized and this while will be infinite. It is preferable to wait one tick when the cycle is completed.
467 // Hard reset or soft reset to initialize already finished (in previous cycle) test cases:
468 a_currentTestIt->second->reset(a_autoResetHard);
470 // Process test case:
471 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));
472 if (a_currentTestIt->second->getState() != TestCase::State::InProgress) {
473 a_currentTestIt->second->process();
474 return true; // is not probably to reach still In-Progress test cases from previous cycles due to the whole
475 // time for complete the test cases pool regarding the single test case lifetime. You shouldn't
476 // forget to programm a test case timeout with a reasonable value
481 TestCase *TestManager::getDiameterTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) {
483 sessionId = anna::diameter::helpers::base::functions::getSessionId(message);
485 catch (anna::RuntimeException &ex) {
487 LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
490 auto sessionIdIt = a_key1TestCaseMap.find(sessionId);
491 if (sessionIdIt != a_key1TestCaseMap.end())
492 return sessionIdIt->second;
494 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
498 TestCase *TestManager::getDiameterTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) {
500 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
501 if (subscriberId == "") // try with IMSI
502 subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
504 catch (anna::RuntimeException &ex) {
506 LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
509 auto subscriberIdIt = a_key2TestCaseMap.find(subscriberId);
510 if (subscriberIdIt != a_key2TestCaseMap.end())
511 return subscriberIdIt->second;
513 LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
517 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ClientSession *clientSession) noexcept(false) {
520 if (!tests()) return;
522 // Identify the test case:
523 std::string sessionId, subscriberId;
525 tc = getDiameterTestCaseFromSessionId(message, sessionId);
527 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
529 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
533 // Work with Test case:
534 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
535 if (!tsw) { // store as 'uncovered'
536 std::string hint = "Uncovered condition for received message from entity over Session-Id '"; hint += sessionId; hint += "'; ";
539 static anna::diameter::codec::Message codecMsg;
540 codecMsg.decode(message);
541 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
542 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
544 catch (anna::RuntimeException &ex) {
546 hint += ex.asString();
548 hint += "\nClient Session: "; hint += clientSession->asString();
550 tc->addDebugSummaryHint(hint);
553 tsw->setClientSession(const_cast<anna::diameter::comm::ClientSession*>(clientSession));
558 void TestManager::receiveDiameterMessage(const anna::DataBlock &message, const anna::diameter::comm::ServerSession *serverSession) noexcept(false) {
561 if (!tests()) return;
563 // Identify the test case:
564 std::string sessionId, subscriberId;
566 tc = getDiameterTestCaseFromSessionId(message, sessionId);
568 tc = getDiameterTestCaseFromSubscriberId(message, subscriberId);
570 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
574 // Work with Test case:
575 TestStepWaitDiameter *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
576 if (!tsw) { // store as 'uncovered'
577 std::string hint = "Uncovered condition for received message from client over Session-Id '"; hint += sessionId; hint += "'; ";
580 static anna::diameter::codec::Message codecMsg;
581 codecMsg.decode(message);
582 hint += "HEX Message: '"; hint += anna::functions::asHexString(message);
583 hint += "'; XML Message:\n"; hint += codecMsg.asXMLString();
585 catch (anna::RuntimeException &ex) {
587 hint += ex.asString();
589 hint += "\nServer Session: "; hint += serverSession->asString();
591 tc->addDebugSummaryHint(hint);
594 tsw->setServerSession(const_cast<anna::diameter::comm::ServerSession*>(serverSession));
599 anna::xml::Node* TestManager::asXML(anna::xml::Node* parent) const
601 anna::xml::Node* result = parent->createChild("TestManager");
603 int poolSize = a_testPool.size();
604 result->createAttribute("NumberOfTestCases", poolSize);
605 if (a_poolRepeats) result->createAttribute("PoolRepeats", a_poolRepeats);
606 else result->createAttribute("PoolRepeats", "disabled");
607 result->createAttribute("PoolCycle", a_poolCycle);
608 a_statSummary.asXML(result);
609 if (a_inProgressLimit == UINT_MAX)
610 result->createAttribute("InProgressLimit", "[no limit]");
612 result->createAttribute("InProgressLimit", a_inProgressLimit);
613 result->createAttribute("DumpInitializedReports", (a_dumpInitializedReports ? "yes":"no"));
614 result->createAttribute("DumpInProgressReports", (a_dumpInProgressReports ? "yes":"no"));
615 result->createAttribute("DumpFailedReports", (a_dumpFailedReports ? "yes":"no"));
616 result->createAttribute("DumpSuccessReports", (a_dumpSuccessReports ? "yes":"no"));
617 result->createAttribute("DumpHexMessages", (a_dumpHexMessages ? "yes":"no"));
618 result->createAttribute("DumpStdout", (a_dumpStdout ? "yes":"no"));
619 result->createAttribute("AutoResetHard", (a_autoResetHard ? "yes":"no"));
620 result->createAttribute("ReportsDirectory", a_reportsDirectory);
622 result->createAttribute("AsynchronousSendings", a_synchronousAmount);
623 int ticksPerSecond = (a_synchronousAmount * 1000) / a_clock->getTimeout();
624 result->createAttribute("TicksPerSecond", a_clock->isActive() ? ticksPerSecond : 0);
626 if (a_currentTestIt != a_testPool.end()) {
627 result->createAttribute("CurrentTestCaseId", (*a_currentTestIt).first);
630 anna::xml::Node* testCases = result->createChild("TestCases");
631 for (test_pool_it it = a_testPool.begin(); it != a_testPool.end(); it++) {
632 if (((*it).second->getState() == TestCase::State::Initialized) && (!getDumpInitializedReports())) continue;
633 if (((*it).second->getState() == TestCase::State::InProgress) && (!getDumpInProgressReports())) continue;
634 if (((*it).second->getState() == TestCase::State::Failed) && (!getDumpFailedReports())) continue;
635 if (((*it).second->getState() == TestCase::State::Success) && (!getDumpSuccessReports())) continue;
636 (*it).second->asXML(testCases);
643 anna::xml::Node* TestManager::junitAsXML(anna::xml::Node* parent) const
645 // if only a single testsuite element is present, the testsuites element can be omitted
646 //anna::xml::Node* result = parent->createChild("testsuites");
647 anna::xml::Node* result = parent->createChild("testsuite");
650 https://stackoverflow.com/questions/4922867/what-is-the-junit-xml-format-specification-that-hudson-supports
654 https://windyroad.com.au/dl/Open%20Source/JUnit.xsd
658 <?xml version="1.0" encoding="UTF-8"?>
659 <testsuites disabled="" errors="" failures="" name="" tests="" time="">
660 <testsuite disabled="" errors="" failures="" hostname="" id=""
661 name="" package="" skipped="" tests="" time="" timestamp="">
663 <property name="" value=""/>
665 <testcase assertions="" classname="" name="" status="" time="">
667 <error message="" type=""/>
668 <failure message="" type=""/>
677 Some of these items can occur multiple times:
679 There can only be one testsuites element, since that's how XML works, but there can be multiple testsuite elements within the testsuites element.
680 Each properties element can have multiple property children.
681 Each testsuite element can have multiple testcase children.
682 Each testcase element can have multiple error, failure, system-out, or system-err children:
684 <testsuite tests="3">
685 <testcase classname="foo1" name="ASuccessfulTest"/>
686 <testcase classname="foo2" name="AnotherSuccessfulTest"/>
687 <testcase classname="foo3" name="AFailingTest">
688 <failure type="NotEnoughFoo"> details about failure </failure>
695 <testsuite name="" <!-- Full (class) name of the test for non-aggregated testsuite documents.
696 Class name without the package for aggregated testsuites documents. Required -->
697 tests="" <!-- The total number of tests in the suite, required. -->
699 disabled="" <!-- the total number of disabled tests in the suite. optional -->
700 errors="" <!-- The total number of tests in the suite that errored. An errored test is one that had an unanticipated problem,
701 for example an unchecked throwable; or a problem with the implementation of the test. optional -->
702 failures="" <!-- The total number of tests in the suite that failed. A failure is a test which the code has explicitly failed
703 by using the mechanisms for that purpose. e.g., via an assertEquals. optional -->
704 hostname="" <!-- Host on which the tests were executed. 'localhost' should be used if the hostname cannot be determined. optional -->
705 id="" <!-- Starts at 0 for the first testsuite and is incremented by 1 for each following testsuite -->
706 package="" <!-- Derived from testsuite/@name in the non-aggregated documents. optional -->
707 skipped="" <!-- The total number of skipped tests. optional -->
708 time="" <!-- Time taken (in seconds) to execute the tests in the suite. optional -->
709 timestamp="" <!-- when the test was executed in ISO 8601 format (2014-01-21T16:17:18). Timezone may not be specified. optional -->
712 result->createAttribute("name", "ADML Testing Main Testsuite");
713 int poolSize = a_testPool.size();
714 result->createAttribute("tests", poolSize);
716 result->createAttribute("errors", 0);
717 result->createAttribute("failures", a_statSummary.getFailedCount());
718 result->createAttribute("hostname", anna::functions::getHostname());
719 result->createAttribute("skipped", a_statSummary.getInitializedCount());
724 <!-- testcase can appear multiple times, see /testsuites/testsuite@tests -->
725 <testcase name="" <!-- Name of the test method, required. -->
726 assertions="" <!-- number of assertions in the test case. optional -->
727 classname="" <!-- Full class name for the class the test method is in. required -->
729 time="" <!-- Time taken (in seconds) to execute the test. optional -->
733 int testcasesLapseMs = 0;
734 int startTimestampMs = 0;
737 test_pool_it it_min = a_testPool.begin();
738 test_pool_it it_max = a_testPool.end();
739 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
740 std::string debugSummary;
742 for (test_pool_it it = it_min; it != it_max; it++) {
743 anna::xml::Node* testcase = result->createChild("testcase");
744 auto tc = (*it).second;
746 testcasesLapseMs = (int)(tc->getLapseMs());
747 testcase->createAttribute("classname", "adml_testcase");
748 testcase->createAttribute("description", tc->getDescription());
749 testcase->createAttribute("status", TestCase::asText(tc->getState()));
751 secs = testcasesLapseMs / 1000.0;
752 testcase->createAttribute("time", anna::functions::asString(secs, "%.2f"));
754 if (tc->isFailed()) {
755 anna::xml::Node* failure = testcase->createChild("failure");
756 std::string text = "";
757 debugSummary = tc->getDebugSummary().asString();
758 if (debugSummary != "") {
759 text += "[Testcase debug summary]";
761 text += debugSummary;
764 text += "[Testcase information (xml dump)]";
766 text += tc->asXMLString();
768 failure->createText(text);
773 time_t startTimestamp = startTimestampMs/1000;
774 time(&startTimestamp);
775 char buf[sizeof "2011-10-08T07:07:09Z"];
776 strftime(buf, sizeof buf, "%FT%TZ", gmtime(&startTimestamp));
777 // this will work too, if your compiler doesn't support %F or %T:
778 // //strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
779 // std::cout << buf << "\n";
780 result->createAttribute("timestamp", buf);
786 std::string TestManager::asXMLString() const {
787 anna::xml::Node root("root");
788 return anna::xml::Compiler().apply(asXML(&root));
791 std::string TestManager::junitAsXMLString() const {
792 anna::xml::Node root("root");
793 return anna::xml::Compiler().apply(junitAsXML(&root));
796 std::string TestManager::summaryCounts() const {
798 std::string result= "\nSummary Counts:\n";
799 unsigned int total = a_statSummary.getTotal();
800 result += "\nTotal: " + anna::functions::asString(total);
801 result += "\nInitialized: " + anna::functions::asString(a_statSummary.getInitializedCount());
802 result += "\nIn Progress: " + anna::functions::asString(a_statSummary.getInProgressCount());
803 unsigned int failed = a_statSummary.getFailedCount();
804 unsigned int success = a_statSummary.getSuccessCount();
805 result += "\nFailed: " + anna::functions::asString(failed);
806 result += "\nSuccess: " + anna::functions::asString(success);
807 std::string verdict = ((failed == 0) && (success > 0) && (total == success)) ? "PASS":"FAIL";
809 result += std::string("\n\nVERDICT: ") + verdict;
815 std::string TestManager::summaryStates() const {
817 std::string result = "\nSummary States:\n";
818 const char *literal = "\n[%s] %s";
819 const char *secsLiteral = " (%.2f secs)";
821 int testcasesLapseMs = 0;
822 int startTimestampMs = 0;
824 int poolSize = a_testPool.size();
826 test_pool_it it_min = a_testPool.begin();
827 test_pool_it it_max = a_testPool.end();
828 startTimestampMs = (int)((*it_min).second->getStartTimestamp());
830 for (test_pool_it it = it_min; it != it_max; it++) {
831 auto tc = (*it).second;
833 testcasesLapseMs = (int)(tc->getLapseMs());
835 result += anna::functions::asString(literal, TestCase::asText(tc->getState()), tc->getDescription().c_str());
837 if (testcasesLapseMs >= 0) {
838 secs = testcasesLapseMs / 1000.0;
839 result += anna::functions::asString(secsLiteral, secs);
844 result +="\nNo test cases programmed !";