2 * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * @file test_runner.cpp
18 * @author Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
19 * @author Lukasz Wrzosek (l.wrzosek@samsung.com)
21 * @brief This file is the implementation file of test runner
24 #include <dpl/test/test_failed.h>
25 #include <dpl/test/test_ignored.h>
26 #include <dpl/test/test_runner.h>
27 #include <dpl/test/test_results_collector.h>
28 #include <dpl/exception.h>
29 #include <dpl/scoped_free.h>
30 #include <dpl/log/log.h>
31 #include <dpl/colors.h>
37 #include <libxml/xpath.h>
38 #include <libxml/xpathInternals.h>
39 #include <libxml/parser.h>
40 #include <libxml/tree.h>
42 #include <dpl/singleton_impl.h>
43 IMPLEMENT_SINGLETON(DPL::Test::TestRunner)
47 std::string getXMLNode(xmlNodePtr node)
50 xmlChar * value = xmlNodeGetContent(node);
51 ret = std::string(reinterpret_cast<char*>(value));
62 void TestRunner::RegisterTest(const char *testName, TestCase proc)
64 m_testGroups[m_currentGroup].push_back(TestCaseStruct(testName, proc));
67 void TestRunner::InitGroup(const char* name)
69 m_currentGroup = name;
72 void TestRunner::normalizeXMLTag(std::string& str, const std::string& testcase)
74 //Add testcase if missing
75 std::string::size_type pos = str.find(testcase);
78 str = testcase + "_" + str;
81 //dpl test runner cannot have '-' character in name so it have to be replaced
82 // for TCT case to make comparision works
83 std::replace(str.begin(), str.end(), '-', '_');
86 bool TestRunner::filterGroupsByXmls(const std::vector<std::string> & files)
88 DECLARE_EXCEPTION_TYPE(DPL::Exception, XMLError)
90 const std::string idPath = "/test_definition/suite/set/testcase/@id";
93 std::map<std::string, bool> casesMap;
95 std::string testsuite;
96 if(!m_testGroups.empty())
98 for(TestCaseGroupMap::const_iterator cit = m_testGroups.begin(); cit != m_testGroups.end(); ++cit)
100 if(!cit->second.empty())
102 for(TestCaseStructList::const_iterator cj = cit->second.begin(); cj != cit->second.end(); ++cj)
104 std::string name = cj->name;
105 std::string::size_type st = name.find('_');
106 if(st != std::string::npos)
108 name = name.substr(0, st);
113 if(!testsuite.empty()) break;
124 for (const std::string &file : files)
127 xmlXPathContextPtr xpathCtx;
129 doc = xmlReadFile(file.c_str(), nullptr, 0);
130 if (doc == nullptr) {
131 ThrowMsg(XMLError, "File Problem");
134 xpathCtx = xmlXPathNewContext(doc);
135 if (xpathCtx == nullptr) {
137 "Error: unable to create new XPath context\n");
139 xpathCtx->node = xmlDocGetRootElement(doc);
143 xmlXPathObjectPtr xpathObject;
144 //get requested node's values
145 xpathObject = xmlXPathEvalExpression(BAD_CAST idPath.c_str(), xpathCtx);
146 if (xpathObject == nullptr)
148 ThrowMsg(XMLError, "XPath evaluation failure: " << idPath);
150 xmlNodeSetPtr nodes = xpathObject->nodesetval;
151 unsigned size = (nodes) ? nodes->nodeNr : 0;
152 LogDebug("Found " << size << " nodes matching xpath");
153 for(unsigned i = 0; i < size; ++i)
155 LogPedantic("Type: " << nodes->nodeTab[i]->type);
156 if (nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
157 xmlNodePtr curNode = nodes->nodeTab[i];
158 result = getXMLNode(curNode);
159 LogPedantic("Result: " << result);
160 normalizeXMLTag(result, testsuite);
161 casesMap.insert(make_pair(result, false));
164 //Cleanup of XPath data
165 xmlXPathFreeObject(xpathObject);
166 xmlXPathFreeContext(xpathCtx);
172 LogError("Libxml error: " << _rethrown_exception.DumpToString());
177 if(!filterByXML(casesMap))
185 bool TestRunner::filterByXML(std::map<std::string, bool> & casesMap)
187 for (auto &group : m_testGroups) {
188 TestCaseStructList newList;
189 for (auto &tc : group.second)
191 if (casesMap.find(tc.name) != casesMap.end()) {
192 casesMap[tc.name] = true;
193 newList.push_back(tc);
196 group.second = newList;
198 for (auto &cs : casesMap)
200 if(cs.second == false)
202 LogError("Cannot find testcase from XML file: " << cs.first);
209 TestRunner::Status TestRunner::RunTestCase(const TestCaseStruct& testCase)
211 setCurrentTestCase(&(const_cast<TestCaseStruct &>(testCase)));
212 m_deferDeepness = 0U;
215 } catch (const TestFailed &e) {
216 // Simple test failure
217 CollectResult(testCase.name,
218 TestResultsCollectorBase::FailStatus::FAILED,
219 getConcatedFailReason(e.GetMessage()));
221 setCurrentTestCase(nullptr);
223 } catch (const TestIgnored &e) {
225 // Simple test have to be implemented
226 CollectResult(testCase.name,
227 TestResultsCollectorBase::FailStatus::IGNORED,
231 setCurrentTestCase(nullptr);
233 } catch (const std::exception &) {
234 // std exception failure
235 CollectResult(testCase.name,
236 TestResultsCollectorBase::FailStatus::FAILED,
239 setCurrentTestCase(nullptr);
242 // Unknown exception failure
243 CollectResult(testCase.name,
244 TestResultsCollectorBase::FailStatus::FAILED,
245 "unknown exception");
247 setCurrentTestCase(nullptr);
251 CollectResult(testCase.name,
252 TestResultsCollectorBase::FailStatus::NONE,
254 testCase.m_isPerformanceTest,
255 testCase.m_performanceTestDurationTime,
256 testCase.m_performanceMaxTime);
257 setCurrentTestCase(nullptr);
263 void TestRunner::RunTests()
265 using namespace DPL::Colors::Text;
268 for (auto &collector : m_collectors) {
269 collector.second->Start();
273 for (auto &group : m_testGroups) {
274 count += group.second.size();
276 fprintf(stderr, "%sFound %d testcases...%s\n", GREEN_BEGIN, count, GREEN_END);
277 fprintf(stderr, "%s%s%s\n", GREEN_BEGIN, "Running tests...", GREEN_END);
278 for (auto &group : m_testGroups) {
279 TestCaseStructList list = group.second;
281 for (auto &collector : m_collectors) {
282 collector.second->CollectCurrentTestGroupName(group.first);
286 for (TestCaseStructList::const_iterator iterator = list.begin();
287 iterator != list.end();
290 TestCaseStruct test = *iterator;
291 if (m_startTestId == test.name) {
295 if (m_startTestId.empty()) {
298 if (m_terminate == true) {
299 // Terminate quietly without any logs
306 std::for_each(m_collectors.begin(),
308 [] (const TestResultsCollectors::value_type & collector)
310 collector.second->Finish();
314 fprintf(stderr, "%s%s%s\n\n", GREEN_BEGIN, "Finished", GREEN_END);
317 TestRunner::TestCaseStruct *TestRunner::getCurrentTestCase()
319 return m_currentTestCase;
322 void TestRunner::setCurrentTestCase(TestCaseStruct* testCase)
324 m_currentTestCase = testCase;
327 void TestRunner::beginPerformanceTestTime(std::chrono::system_clock::duration maxTimeInMicroseconds)
329 TestCaseStruct* testCase = getCurrentTestCase();
333 testCase->m_isPerformanceTest = true;
334 testCase->m_performanceMaxTime = maxTimeInMicroseconds;
335 testCase->m_performanceTestStartTime = std::chrono::system_clock::now();
337 // Set result to 0 microseconds. Display 0ms result when end macro is missing.
338 testCase->m_performanceTestDurationTime = std::chrono::microseconds::zero();
341 void TestRunner::endPerformanceTestTime()
343 TestCaseStruct* testCase = getCurrentTestCase();
347 testCase->m_performanceTestDurationTime = std::chrono::system_clock::now() -
348 testCase->m_performanceTestStartTime;
351 void TestRunner::getCurrentTestCasePerformanceResult(bool& isPerformanceTest,
352 std::chrono::system_clock::duration& result,
353 std::chrono::system_clock::duration& resultMax)
355 TestCaseStruct* testCase = getCurrentTestCase();
356 if (!testCase || !(testCase->m_isPerformanceTest)){
357 isPerformanceTest = false;
361 isPerformanceTest = testCase->m_isPerformanceTest;
362 result = testCase->m_performanceTestDurationTime;
363 resultMax = testCase->m_performanceMaxTime;
366 void TestRunner::setCurrentTestCasePerformanceResult(bool isPerformanceTest,
367 std::chrono::system_clock::duration result,
368 std::chrono::system_clock::duration resultMax)
370 TestCaseStruct* testCase = getCurrentTestCase();
374 testCase->m_isPerformanceTest = isPerformanceTest;
375 testCase->m_performanceTestDurationTime = result;
376 testCase->m_performanceMaxTime = resultMax;
379 void TestRunner::addFailReason(const std::string &reason)
381 m_failReason.push(reason);
384 std::string TestRunner::getConcatedFailReason(const std::string &reason)
387 while (!m_failReason.empty())
389 ret += m_failReason.front();
395 void TestRunner::CollectResult(
396 const std::string& id,
397 const TestResultsCollectorBase::FailStatus status,
398 const std::string& reason,
399 const bool& isPerformanceTest,
400 const std::chrono::system_clock::duration& performanceTestDurationTime,
401 const std::chrono::system_clock::duration& performanceMaxTime)
403 std::for_each(m_collectors.begin(),
405 [&](const TestResultsCollectors::value_type & collector)
407 collector.second->CollectResult(id,
411 performanceTestDurationTime,
416 void TestRunner::Banner()
418 using namespace DPL::Colors::Text;
432 void TestRunner::InvalidArgs(const std::string& message)
434 using namespace DPL::Colors::Text;
442 void TestRunner::Usage()
444 fprintf(stderr, "Usage: runner [options]\n\n");
445 fprintf(stderr, "Output type:\n");
446 fprintf(stderr, " --output=<output type> --output=<output type> ...\n");
447 fprintf(stderr, "\n possible output types:\n");
448 for (std::string &type : TestResultsCollectorBase::GetCollectorsNames()) {
449 fprintf(stderr, " --output=%s\n", type.c_str());
451 fprintf(stderr, "\n example:\n");
453 " test-binary --output=text --output=xml --file=output.xml\n\n");
454 fprintf(stderr, "Other parameters:\n");
456 " --regexp='regexp'\t Only selected tests"
457 " which names match regexp run\n\n");
458 fprintf(stderr, " --start=<test id>\tStart from concrete test id");
459 fprintf(stderr, " --group=<group name>\t Run tests only from one group\n");
460 fprintf(stderr, " --runignored\t Run also ignored tests\n");
461 fprintf(stderr, " --list\t Show a list of Test IDs\n");
462 fprintf(stderr, " --listgroups\t Show a list of Test Group names \n");
463 fprintf(stderr, " --only-from-xml=<xml file>\t Run only testcases specified in XML file \n"
464 " XML name is taken from attribute id=\"part1_part2\" as whole.\n"
465 " If part1 is not found (no _) then it is implicitily "
466 "set according to suite part1 from binary tests\n");
469 " --listingroup=<group name>\t Show a list of Test IDS in one group\n");
470 fprintf(stderr, " --allowchildlogs\t Allow to print logs from child process on screen.\n");
471 fprintf(stderr, " When active child process will be able to print logs on stdout and stderr.\n");
472 fprintf(stderr, " Both descriptors will be closed after test.\n");
473 fprintf(stderr, " --help\t This help\n\n");
474 std::for_each(m_collectors.begin(),
476 [] (const TestResultsCollectors::value_type & collector)
479 "Output %s has specific args:\n",
480 collector.first.c_str());
484 CollectorSpecificHelp().c_str());
486 fprintf(stderr, "For bug reporting, please write to:\n");
487 fprintf(stderr, "<p.dobrowolsk@samsung.com>\n");
490 int TestRunner::ExecTestRunner(int argc, char *argv[])
492 std::vector<std::string> args;
493 for (int i = 0; i < argc; ++i) {
494 args.push_back(argv[i]);
496 return ExecTestRunner(args);
499 void TestRunner::MarkAssertion()
504 int TestRunner::ExecTestRunner(ArgsList args)
506 m_runIgnored = false;
507 // Parse command line
509 args.erase(args.begin());
511 bool showHelp = false;
512 bool justList = false;
513 std::vector<std::string> xmlFiles;
515 TestResultsCollectorBasePtr currentCollector;
517 // Parse each argument
518 for(std::string &arg : args)
520 const std::string regexp = "--regexp=";
521 const std::string output = "--output=";
522 const std::string groupId = "--group=";
523 const std::string runIgnored = "--runignored";
524 const std::string listCmd = "--list";
525 const std::string startCmd = "--start=";
526 const std::string listGroupsCmd = "--listgroups";
527 const std::string listInGroup = "--listingroup=";
528 const std::string allowChildLogs = "--allowchildlogs";
529 const std::string onlyFromXML = "--only-from-xml=";
531 if (currentCollector) {
532 if (currentCollector->ParseCollectorSpecificArg(arg)) {
537 if (arg.find(startCmd) == 0) {
538 arg.erase(0, startCmd.length());
539 for (auto &group : m_testGroups) {
540 for (auto &tc : group.second) {
541 if (tc.name == arg) {
546 if (!m_startTestId.empty()) {
550 if (!m_startTestId.empty()) {
554 fprintf(stderr, "Start test id has not been found\n");
557 } else if (arg.find(groupId) == 0) {
558 arg.erase(0, groupId.length());
559 TestCaseGroupMap::iterator found = m_testGroups.find(arg);
560 if (found != m_testGroups.end()) {
561 std::string name = found->first;
562 TestCaseStructList newList = found->second;
563 m_testGroups.clear();
564 m_testGroups[name] = newList;
566 fprintf(stderr, "Group %s not found\n", arg.c_str());
571 } else if (arg == runIgnored) {
573 } else if (arg == listCmd) {
575 } else if (arg == listGroupsCmd) {
576 for (auto &group : m_testGroups) {
577 printf("GR:%s\n", group.first.c_str());
580 } else if (arg.find(listInGroup) == 0) {
581 arg.erase(0, listInGroup.length());
582 for (auto &test : m_testGroups[arg]) {
583 printf("ID:%s\n", test.name.c_str());
586 } else if (arg.find(allowChildLogs) == 0) {
587 arg.erase(0, allowChildLogs.length());
588 m_allowChildLogs = true;
589 } else if (arg == "--help") {
591 } else if (arg.find(output) == 0) {
592 arg.erase(0, output.length());
593 if (m_collectors.find(arg) != m_collectors.end()) {
595 "Multiple outputs of the same type are not supported!");
599 currentCollector.reset(TestResultsCollectorBase::Create(arg));
600 if (!currentCollector) {
601 InvalidArgs("Unsupported output type!");
605 m_collectors[arg] = currentCollector;
606 } else if (arg.find(regexp) == 0) {
607 arg.erase(0, regexp.length());
608 if (arg.length() == 0) {
614 if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
616 arg.erase(arg.length() - 1);
619 if (arg.length() == 0) {
625 pcrecpp::RE re(arg.c_str());
626 for (auto &group : m_testGroups) {
627 TestCaseStructList newList;
628 for (auto &tc : group.second)
630 if (re.PartialMatch(tc.name)) {
631 newList.push_back(tc);
634 group.second = newList;
636 } else if(arg.find(onlyFromXML) == 0) {
637 arg.erase(0, onlyFromXML.length());
638 if (arg.length() == 0) {
644 if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
646 arg.erase(arg.length() - 1);
649 if (arg.length() == 0) {
655 xmlFiles.push_back(arg);
663 if(!xmlFiles.empty())
665 if(!filterGroupsByXmls(xmlFiles))
667 fprintf(stderr, "XML file is not correct\n");
674 for (auto &group : m_testGroups) {
675 for (auto &tc : group.second) {
676 printf("ID:%s:%s\n", group.first.c_str(), tc.name.c_str());
682 currentCollector.reset();
690 if (m_collectors.empty()) {
691 TestResultsCollectorBasePtr collector(
692 TestResultsCollectorBase::Create("text"));
693 m_collectors["text"] = collector;
696 for (auto &collector : m_collectors) {
697 if (!collector.second->Configure()) {
698 fprintf(stderr, "Could not configure selected output");
709 bool TestRunner::getRunIgnored() const
714 void TestRunner::Terminate()
719 bool TestRunner::GetAllowChildLogs()
721 return m_allowChildLogs;
724 void TestRunner::deferFailedException(const DPL::Test::TestFailed &ex)
726 if (m_deferDeepness <= 0)
729 if (m_deferredExceptionsMessages.empty()) {
730 m_firstDeferredFail = ex;
731 m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_FAILED;
733 m_deferredExceptionsMessages.push_back(ex.GetMessage());
736 void TestRunner::deferIgnoredException(const DPL::Test::TestIgnored &ex)
738 if (m_deferDeepness <= 0)
741 if (m_deferredExceptionsMessages.empty()) {
742 m_firstDeferredIgnore = ex;
743 m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_IGNORED;
745 m_deferredExceptionsMessages.push_back(ex.GetMessage());
748 void TestRunner::deferBegin()
753 void TestRunner::deferEnd()
755 if (m_deferDeepness > 0)
758 if (m_deferDeepness > 0)
761 bool oops = std::uncaught_exception();
762 size_t additionalExceptions = oops ? 0 : 1;
763 for (size_t i = additionalExceptions; i < m_deferredExceptionsMessages.size(); ++i)
764 addFailReason(m_deferredExceptionsMessages[i]);
766 if (!oops && !m_deferredExceptionsMessages.empty())
768 m_deferredExceptionsMessages.clear();
769 switch (m_firstDeferredExceptionType) {
770 case DeferredExceptionType::DEFERRED_FAILED:
771 throw m_firstDeferredFail;
772 case DeferredExceptionType::DEFERRED_IGNORED:
773 throw m_firstDeferredIgnore;
776 m_deferredExceptionsMessages.clear();