2 * Copyright (c) 2014-2017 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/test/safe_cleanup.h>
29 #include <dpl/exception.h>
30 #include <dpl/scoped_free.h>
31 #include <dpl/log/log.h>
32 #include <dpl/colors.h>
41 #include <libxml/xpath.h>
42 #include <libxml/xpathInternals.h>
43 #include <libxml/parser.h>
44 #include <libxml/tree.h>
46 #include <dpl/singleton_impl.h>
47 IMPLEMENT_SINGLETON(DPL::Test::TestRunner)
51 std::string getXMLNode(xmlNodePtr node)
54 xmlChar * value = xmlNodeGetContent(node);
55 ret = std::string(reinterpret_cast<char*>(value));
65 TestResult::FailStatus TryCatch(const std::function<void(void)> &func, std::string &reason) {
68 } catch (const DPL::Test::TestFailed &e) {
69 reason = e.GetMessage();
70 return TestResult::FailStatus::FAILED;
71 } catch (const DPL::Test::TestIgnored &e) {
72 reason = e.GetMessage();
73 return TestResult::FailStatus::IGNORED;
74 } catch (const std::exception &e) {
76 return TestResult::FailStatus::FAILED;
78 reason = "Unknown exception";
79 return TestResult::FailStatus::FAILED;
81 reason = std::string();
82 return TestResult::FailStatus::NONE;
85 void TestRunner::RegisterTest(TestCase* testCase)
87 m_currentGroup->Add(testCase);
90 void TestRunner::InitGroup(const char* name, TestGroup* group)
93 group = new TestGroup();
95 m_testGroups[name] = group;
96 m_currentGroup = group;
99 void TestRunner::normalizeXMLTag(std::string& str, const std::string& testcase)
101 //Add testcase if missing
102 std::string::size_type pos = str.find(testcase);
105 str = testcase + "_" + str;
108 //dpl test runner cannot have '-' character in name so it have to be replaced
109 // for TCT case to make comparision works
110 std::replace(str.begin(), str.end(), '-', '_');
113 bool TestRunner::filterGroupsByXmls(const std::vector<std::string> & files)
115 DECLARE_EXCEPTION_TYPE(DPL::Exception, XMLError)
117 const std::string idPath = "/test_definition/suite/set/testcase/@id";
120 std::map<std::string, bool> casesMap;
122 std::string testsuite;
123 if(!m_testGroups.empty())
125 for(auto cit = m_testGroups.begin(); cit != m_testGroups.end(); ++cit)
127 const TestCaseSet& tl = cit->second->GetTests();
130 for(auto cj = tl.begin(); cj != tl.end(); ++cj)
132 std::string name = (*cj)->GetName();
133 std::string::size_type st = name.find('_');
134 if(st != std::string::npos)
136 name = name.substr(0, st);
141 if(!testsuite.empty()) break;
152 for (const std::string &file : files)
155 xmlXPathContextPtr xpathCtx;
157 doc = xmlReadFile(file.c_str(), nullptr, 0);
158 if (doc == nullptr) {
159 ThrowMsg(XMLError, "File Problem");
162 xpathCtx = xmlXPathNewContext(doc);
163 if (xpathCtx == nullptr) {
165 "Error: unable to create new XPath context\n");
167 xpathCtx->node = xmlDocGetRootElement(doc);
171 xmlXPathObjectPtr xpathObject;
172 //get requested node's values
173 xpathObject = xmlXPathEvalExpression(BAD_CAST idPath.c_str(), xpathCtx);
174 if (xpathObject == nullptr)
176 ThrowMsg(XMLError, "XPath evaluation failure: " << idPath);
178 xmlNodeSetPtr nodes = xpathObject->nodesetval;
179 unsigned size = (nodes) ? nodes->nodeNr : 0;
180 LogDebug("Found " << size << " nodes matching xpath");
181 for(unsigned i = 0; i < size; ++i)
183 LogPedantic("Type: " << nodes->nodeTab[i]->type);
184 if (nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
185 xmlNodePtr curNode = nodes->nodeTab[i];
186 result = getXMLNode(curNode);
187 LogPedantic("Result: " << result);
188 normalizeXMLTag(result, testsuite);
189 casesMap.insert(make_pair(result, false));
192 //Cleanup of XPath data
193 xmlXPathFreeObject(xpathObject);
194 xmlXPathFreeContext(xpathCtx);
200 LogError("Libxml error: " << _rethrown_exception.DumpToString());
205 if(!filterByXML(casesMap))
213 bool TestRunner::filterByXML(std::map<std::string, bool> & casesMap)
215 for (auto &group : m_testGroups) {
216 group.second->RemoveIf([&](const TestCasePtr test){
217 return (casesMap.find(test->GetName()) == casesMap.end());
220 for (auto &cs : casesMap)
222 if(cs.second == false)
224 LogError("Cannot find testcase from XML file: " << cs.first);
231 void TestRunner::RunTestCase(TestCasePtr testCase)
233 setCurrentTestCase(testCase);
234 m_deferDeepness = 0U;
236 std::string initReason;
237 TestResult::FailStatus initStatus = TryCatch(std::bind(&TestCase::Init, testCase),
239 if (initStatus != TestResult::FailStatus::NONE) {
240 CollectResult(testCase->GetName(),
241 TestResult(initStatus, getConcatedFailReason(initReason), testCase->GetPerformance()));
242 setCurrentTestCase(nullptr);
246 SafeCleanup::reset();
248 std::string testReason;
249 TestResult::FailStatus testStatus = TryCatch(std::bind(&TestCase::Test, testCase),
251 testReason = getConcatedFailReason(testReason);
252 std::string finishReason;
253 TestResult::FailStatus finishStatus = TryCatch(std::bind(&TestCase::Finish, testCase),
255 finishReason = getConcatedFailReason(finishReason);
257 std::string cleanupReason = SafeCleanup::dump();
259 switch (finishStatus) {
260 case TestResult::FailStatus::FAILED:
261 testStatus = TestResult::FailStatus::FAILED;
262 if (!testReason.empty())
264 testReason += finishReason;
265 if (!cleanupReason.empty()) {
266 if (!testReason.empty())
268 testReason += cleanupReason;
271 case TestResult::FailStatus::IGNORED:
272 if (testStatus == TestResult::FailStatus::NONE)
273 testStatus = TestResult::FailStatus::IGNORED;
274 if (!testReason.empty())
276 testReason += finishReason;
277 case TestResult::FailStatus::NONE:
278 if (!cleanupReason.empty()) {
279 if (!testReason.empty())
281 testReason += cleanupReason;
282 if (testStatus == TestResult::FailStatus::NONE)
283 testStatus = TestResult::FailStatus::FAILED;
287 Assert(false && "Unhandled fail status");
290 CollectResult(testCase->GetName(),
291 TestResult(testStatus, testReason, testCase->GetPerformance()));
292 setCurrentTestCase(nullptr);
295 void TestRunner::RunTests()
297 using namespace DPL::Colors::Text;
300 for (auto &collector : m_collectors) {
301 collector.second->Start();
305 for (auto &group : m_testGroups) {
306 count += group.second->GetTests().size();
308 fprintf(stderr, "%sFound %d testcases...%s\n", GREEN_BEGIN, count, GREEN_END);
309 fprintf(stderr, "%s%s%s\n", GREEN_BEGIN, "Running tests...", GREEN_END);
310 for (auto &group : m_testGroups) {
311 const TestCaseSet& set = group.second->GetTests();
313 group.second->Init();
315 for (auto &collector : m_collectors) {
316 collector.second->CollectCurrentTestGroupName(group.first);
319 for (TestCaseSet::const_iterator iterator = set.begin();
320 iterator != set.end();
323 TestCasePtr test = *iterator;
324 if (m_startTestId == test->GetName()) {
328 if (m_startTestId.empty()) {
331 if (m_terminate == true) {
332 // Terminate quietly without any logs
336 group.second->Finish();
340 std::for_each(m_collectors.begin(),
342 [] (const TestResultsCollectors::value_type & collector)
344 collector.second->Finish();
348 fprintf(stderr, "%s%s%s\n\n", GREEN_BEGIN, "Finished", GREEN_END);
351 TestCasePtr TestRunner::getCurrentTestCase()
353 return m_currentTestCase;
356 void TestRunner::setCurrentTestCase(TestCasePtr testCase)
358 m_currentTestCase = testCase;
361 void TestRunner::beginPerformance(std::chrono::system_clock::duration maxDurationInMicroseconds)
363 TestCasePtr testCase = getCurrentTestCase();
367 if (!testCase->GetPerformance())
368 testCase->SetPerformance(
369 std::unique_ptr<PerformanceResult> \
370 (new PerformanceResult(maxDurationInMicroseconds)));
373 void TestRunner::endPerformance()
375 TestCasePtr testCase = getCurrentTestCase();
379 testCase->GetPerformance()->Finish();
382 ConstPerformanceResultPtr TestRunner::getCurrentTestCasePerformanceResult()
384 TestCasePtr testCase = getCurrentTestCase();
388 return testCase->GetPerformance();
391 void TestRunner::setCurrentTestCasePerformanceResult(const PerformanceResultPtr &performance)
393 TestCasePtr testCase = getCurrentTestCase();
397 testCase->SetPerformance(performance);
400 void TestRunner::addFailReason(const std::string &reason)
402 m_failReason.push(reason);
405 std::string TestRunner::getConcatedFailReason(const std::string &reason)
408 while (!m_failReason.empty())
410 ret += m_failReason.front();
416 TestRunner::~TestRunner()
418 for(auto &g : m_testGroups)
422 void TestRunner::CollectResult(const std::string& id, const TestResult& result)
424 if (result.GetFailStatus() == TestResult::FailStatus::IGNORED && m_runIgnored)
427 std::for_each(m_collectors.begin(),
429 [&](const TestResultsCollectors::value_type & collector)
431 collector.second->CollectResult(id, result);
435 void TestRunner::Banner()
437 using namespace DPL::Colors::Text;
451 void TestRunner::InvalidArgs(const std::string& message)
453 using namespace DPL::Colors::Text;
461 void TestRunner::Usage()
463 fprintf(stderr, "Usage: runner [options]\n\n");
464 fprintf(stderr, "Output type:\n");
465 fprintf(stderr, " --output=<output type> --output=<output type> ...\n");
466 fprintf(stderr, "\n possible output types:\n");
467 for (std::string &type : TestResultsCollectorBase::GetCollectorsNames()) {
468 fprintf(stderr, " --output=%s\n", type.c_str());
470 fprintf(stderr, "\n example:\n");
472 " test-binary --output=text --output=xml --file=output.xml\n\n");
473 fprintf(stderr, "Other parameters:\n");
474 fprintf(stderr, " --test=<test name> Run only one test\n");
476 " --regexp='regexp'\t Only selected tests"
477 " which names match regexp run\n\n");
478 fprintf(stderr, " --start=<test id>\tStart from concrete test id\n");
479 fprintf(stderr, " --group=<group name>\t Run tests only from one group\n");
480 fprintf(stderr, " --runignored\t Run also ignored tests\n");
481 fprintf(stderr, " --list\t Show a list of Test IDs\n");
482 fprintf(stderr, " --listgroups\t Show a list of Test Group names \n");
483 fprintf(stderr, " --only-from-xml=<xml file>\t Run only testcases specified in XML file \n"
484 " XML name is taken from attribute id=\"part1_part2\" as whole.\n"
485 " If part1 is not found (no _) then it is implicitily "
486 "set according to suite part1 from binary tests\n");
489 " --listingroup=<group name>\t Show a list of Test IDS in one group\n");
490 fprintf(stderr, " --allowchildlogs\t Allow to print logs from child process on screen.\n");
491 fprintf(stderr, " When active child process will be able to print logs on stdout and stderr.\n");
492 fprintf(stderr, " Both descriptors will be closed after test.\n");
493 fprintf(stderr, " --help\t This help\n\n");
494 std::for_each(m_collectors.begin(),
496 [] (const TestResultsCollectors::value_type & collector)
499 "Output %s has specific args:\n",
500 collector.first.c_str());
504 CollectorSpecificHelp().c_str());
506 fprintf(stderr, "For bug reporting, please write to:\n");
507 fprintf(stderr, "<p.dobrowolsk@samsung.com>\n");
510 int TestRunner::ExecTestRunner(int argc, char *argv[])
512 std::vector<std::string> args;
513 for (int i = 0; i < argc; ++i) {
514 args.push_back(argv[i]);
516 return ExecTestRunner(args);
519 int TestRunner::ExecTestRunner(ArgsList args)
521 m_runIgnored = false;
522 // Parse command line
524 args.erase(args.begin());
526 bool showHelp = false;
527 bool justList = false;
528 std::vector<std::string> xmlFiles;
530 TestResultsCollectorBasePtr currentCollector;
532 // Parse each argument
533 for(std::string &arg : args)
535 const std::string test = "--test=";
536 const std::string regexp = "--regexp=";
537 const std::string output = "--output=";
538 const std::string groupId = "--group=";
539 const std::string runIgnored = "--runignored";
540 const std::string listCmd = "--list";
541 const std::string startCmd = "--start=";
542 const std::string listGroupsCmd = "--listgroups";
543 const std::string listInGroup = "--listingroup=";
544 const std::string allowChildLogs = "--allowchildlogs";
545 const std::string onlyFromXML = "--only-from-xml=";
547 if (currentCollector) {
548 if (currentCollector->ParseCollectorSpecificArg(arg)) {
553 if (arg.find(startCmd) == 0) {
554 arg.erase(0, startCmd.length());
555 for (auto &group : m_testGroups) {
556 for (auto &tc : group.second->GetTests()) {
557 if (tc->GetName() == arg) {
562 if (!m_startTestId.empty()) {
566 if (!m_startTestId.empty()) {
570 fprintf(stderr, "Start test id has not been found\n");
573 } else if (arg.find(groupId) == 0) {
574 arg.erase(0, groupId.length());
575 for (auto it = m_testGroups.begin(); it != m_testGroups.end();) {
576 if (it->first == arg)
579 it = m_testGroups.erase(it);
582 if (m_testGroups.empty()) {
583 fprintf(stderr, "Group %s not found\n", arg.c_str());
588 } else if (arg == runIgnored) {
590 } else if (arg == listCmd) {
592 } else if (arg == listGroupsCmd) {
593 for (auto &group : m_testGroups) {
594 printf("GR:%s\n", group.first.c_str());
597 } else if (arg.find(listInGroup) == 0) {
598 arg.erase(0, listInGroup.length());
599 auto it = m_testGroups.find(arg);
600 if (it == m_testGroups.end()) {
601 fprintf(stderr, "Group %s not found\n", arg.c_str());
606 for (auto &test : it->second->GetTests()) {
607 printf("ID:%s\n", test->GetName().c_str());
610 } else if (arg.find(allowChildLogs) == 0) {
611 arg.erase(0, allowChildLogs.length());
612 m_allowChildLogs = true;
613 } else if (arg == "--help") {
615 } else if (arg.find(output) == 0) {
616 arg.erase(0, output.length());
617 if (m_collectors.find(arg) != m_collectors.end()) {
619 "Multiple outputs of the same type are not supported!");
623 currentCollector.reset(TestResultsCollectorBase::Create(arg));
624 if (!currentCollector) {
625 InvalidArgs("Unsupported output type!");
629 m_collectors[arg] = currentCollector;
630 } else if (arg.find(regexp) == 0) {
631 arg.erase(0, regexp.length());
632 if (arg.length() == 0) {
638 if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
640 arg.erase(arg.length() - 1);
643 if (arg.length() == 0) {
649 pcrecpp::RE re(arg.c_str());
650 for (auto &group : m_testGroups) {
651 group.second->RemoveIf([&](const TestCasePtr& test){
652 return !re.PartialMatch(test->GetName());
655 } else if (arg.find(test) == 0) {
656 arg.erase(0, test.length());
657 if (arg.length() == 0) {
663 if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
665 arg.erase(arg.length() - 1);
668 if (arg.length() == 0) {
674 pcrecpp::RE re(arg.c_str());
675 for (auto &group : m_testGroups) {
676 group.second->RemoveIf([&](const TestCasePtr& test){
677 return !re.FullMatch(test->GetName());
680 } else if(arg.find(onlyFromXML) == 0) {
681 arg.erase(0, onlyFromXML.length());
682 if (arg.length() == 0) {
688 if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
690 arg.erase(arg.length() - 1);
693 if (arg.length() == 0) {
699 xmlFiles.push_back(arg);
707 if(!xmlFiles.empty())
709 if(!filterGroupsByXmls(xmlFiles))
711 fprintf(stderr, "XML file is not correct\n");
718 for (auto &group : m_testGroups) {
719 for (auto &tc : group.second->GetTests()) {
720 printf("ID:%s:%s\n", group.first.c_str(), tc->GetName().c_str());
726 currentCollector.reset();
734 if (m_collectors.empty()) {
735 TestResultsCollectorBasePtr collector(
736 TestResultsCollectorBase::Create("text"));
737 m_collectors["text"] = collector;
740 for (auto &collector : m_collectors) {
741 if (!collector.second->Configure()) {
742 fprintf(stderr, "Could not configure selected output");
753 void TestRunner::Terminate()
758 bool TestRunner::GetAllowChildLogs()
760 return m_allowChildLogs;
763 bool TestRunner::GetRunIgnored() const
768 void TestRunner::deferFailedException(const DPL::Test::TestFailed &ex)
770 if (m_deferDeepness <= 0)
773 if (m_deferredExceptionsMessages.empty()) {
774 m_firstDeferredFail = ex;
775 m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_FAILED;
777 m_deferredExceptionsMessages.push_back(ex.GetMessage());
780 void TestRunner::deferIgnoredException(const DPL::Test::TestIgnored &ex)
782 if (m_deferDeepness <= 0)
785 if (m_deferredExceptionsMessages.empty()) {
786 m_firstDeferredIgnore = ex;
787 m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_IGNORED;
789 m_deferredExceptionsMessages.push_back(ex.GetMessage());
792 void TestRunner::deferBegin()
797 void TestRunner::deferEnd()
799 if (m_deferDeepness > 0)
802 if (m_deferDeepness > 0)
805 for (size_t i = 0; i < m_deferredExceptionsMessages.size(); ++i) {
806 addFailReason(m_deferredExceptionsMessages[i]);
809 if (!m_deferredExceptionsMessages.empty())
811 m_deferredExceptionsMessages.clear();
812 switch (m_firstDeferredExceptionType) {
813 case DeferredExceptionType::DEFERRED_FAILED:
814 throw m_firstDeferredFail;
815 case DeferredExceptionType::DEFERRED_IGNORED:
816 throw m_firstDeferredIgnore;
819 m_deferredExceptionsMessages.clear();