726d4342399af1427f86e169e03e1b1692ef27b9
[platform/core/test/security-tests.git] / src / framework / src / test_runner.cpp
1 /*
2  * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /**
17  * @file        test_runner.cpp
18  * @author      Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
19  * @author      Lukasz Wrzosek (l.wrzosek@samsung.com)
20  * @version     1.0
21  * @brief       This file is the implementation file of test runner
22  */
23 #include <stddef.h>
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>
32 #include <pcrecpp.h>
33 #include <algorithm>
34 #include <cstdio>
35 #include <memory.h>
36
37 #include <libxml/xpath.h>
38 #include <libxml/xpathInternals.h>
39 #include <libxml/parser.h>
40 #include <libxml/tree.h>
41
42 #include <dpl/singleton_impl.h>
43 IMPLEMENT_SINGLETON(DPL::Test::TestRunner)
44
45 namespace {
46
47 std::string getXMLNode(xmlNodePtr node)
48 {
49     std::string ret;
50     xmlChar * value = xmlNodeGetContent(node);
51     ret = std::string(reinterpret_cast<char*>(value));
52     xmlFree(value);
53     return ret;
54 }
55
56 }
57
58
59 namespace DPL {
60 namespace Test {
61
62 void TestRunner::RegisterTest(const char *testName, TestCase proc)
63 {
64     m_testGroups[m_currentGroup].push_back(TestCaseStruct(testName, proc));
65 }
66
67 void TestRunner::InitGroup(const char* name)
68 {
69     m_currentGroup = name;
70 }
71
72 void TestRunner::normalizeXMLTag(std::string& str, const std::string& testcase)
73 {
74     //Add testcase if missing
75     std::string::size_type pos = str.find(testcase);
76     if(pos != 0)
77     {
78         str = testcase + "_" + str;
79     }
80
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(), '-', '_');
84 }
85
86 bool TestRunner::filterGroupsByXmls(const std::vector<std::string> & files)
87 {
88     DECLARE_EXCEPTION_TYPE(DPL::Exception, XMLError)
89
90     const std::string idPath = "/test_definition/suite/set/testcase/@id";
91
92     bool success = true;
93     std::map<std::string, bool> casesMap;
94
95     std::string testsuite;
96     if(!m_testGroups.empty())
97     {
98         for(TestCaseGroupMap::const_iterator cit = m_testGroups.begin(); cit != m_testGroups.end(); ++cit)
99         {
100             if(!cit->second.empty())
101             {
102                 for(TestCaseStructList::const_iterator cj = cit->second.begin(); cj != cit->second.end(); ++cj)
103                 {
104                     std::string name = cj->name;
105                     std::string::size_type st = name.find('_');
106                     if(st != std::string::npos)
107                     {
108                         name = name.substr(0, st);
109                         testsuite = name;
110                         break;
111                     }
112                 }
113                 if(!testsuite.empty()) break;
114             }
115         }
116     }
117
118     xmlInitParser();
119     LIBXML_TEST_VERSION
120     xmlXPathInit();
121
122     Try
123     {
124         for (const std::string &file : files)
125         {
126             xmlDocPtr doc;
127             xmlXPathContextPtr xpathCtx;
128
129             doc = xmlReadFile(file.c_str(), nullptr, 0);
130             if (doc == nullptr) {
131                 ThrowMsg(XMLError, "File Problem");
132             } else {
133                 //context
134                 xpathCtx = xmlXPathNewContext(doc);
135                 if (xpathCtx == nullptr) {
136                     ThrowMsg(XMLError,
137                              "Error: unable to create new XPath context\n");
138                 }
139                 xpathCtx->node = xmlDocGetRootElement(doc);
140             }
141
142             std::string result;
143             xmlXPathObjectPtr xpathObject;
144             //get requested node's values
145             xpathObject = xmlXPathEvalExpression(BAD_CAST idPath.c_str(), xpathCtx);
146             if (xpathObject == nullptr)
147             {
148                 ThrowMsg(XMLError, "XPath evaluation failure: " << idPath);
149             }
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)
154             {
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));
162                 }
163             }
164             //Cleanup of XPath data
165             xmlXPathFreeObject(xpathObject);
166             xmlXPathFreeContext(xpathCtx);
167             xmlFreeDoc(doc);
168         }
169     }
170     Catch(XMLError)
171     {
172         LogError("Libxml error: " << _rethrown_exception.DumpToString());
173         success = false;
174     }
175     xmlCleanupParser();
176
177     if(!filterByXML(casesMap))
178     {
179         success = false;
180     }
181
182     return success;
183 }
184
185 bool TestRunner::filterByXML(std::map<std::string, bool> & casesMap)
186 {
187     for (auto &group : m_testGroups) {
188         TestCaseStructList newList;
189         for (auto &tc : group.second)
190         {
191             if (casesMap.find(tc.name) != casesMap.end()) {
192                 casesMap[tc.name] = true;
193                 newList.push_back(tc);
194             }
195         }
196         group.second = newList;
197     }
198     for (auto &cs : casesMap)
199     {
200         if(cs.second == false)
201         {
202             LogError("Cannot find testcase from XML file: " << cs.first);
203             return false;
204         }
205     }
206     return true;
207 }
208
209 void TestRunner::RunTestCase(const TestCaseStruct& testCase)
210 {
211     setCurrentTestCase(&(const_cast<TestCaseStruct &>(testCase)));
212     m_deferDeepness = 0U;
213     try {
214         testCase.proc();
215     } catch (const TestFailed &e) {
216         // Simple test failure
217         CollectResult(testCase.name,
218                       TestResult(TestResult::FailStatus::FAILED,
219                                  getConcatedFailReason(e.GetMessage())));
220
221         setCurrentTestCase(nullptr);
222         return;
223     } catch (const TestIgnored &e) {
224         if (m_runIgnored) {
225             // Simple test have to be implemented
226             CollectResult(testCase.name,
227                           TestResult(TestResult::FailStatus::IGNORED, e.GetMessage()));
228         }
229
230         setCurrentTestCase(nullptr);
231         return;
232     } catch (const std::exception &) {
233         // std exception failure
234         CollectResult(testCase.name,
235                       TestResult(TestResult::FailStatus::FAILED, "std exception"));
236
237         setCurrentTestCase(nullptr);
238         return;
239     } catch (...) {
240         // Unknown exception failure
241         CollectResult(testCase.name,
242                       TestResult(TestResult::FailStatus::FAILED, "unknown exception"));
243         setCurrentTestCase(nullptr);
244         return;
245     }
246
247     // Everything OK
248     CollectResult(testCase.name,
249                   TestResult(TestResult::FailStatus::NONE,
250                              std::string(),
251                              testCase.performance));
252     setCurrentTestCase(nullptr);
253 }
254
255 void TestRunner::RunTests()
256 {
257     using namespace DPL::Colors::Text;
258
259     Banner();
260     for (auto &collector : m_collectors) {
261         collector.second->Start();
262     }
263
264     unsigned count = 0;
265     for (auto &group : m_testGroups) {
266         count += group.second.size();
267     }
268     fprintf(stderr, "%sFound %d testcases...%s\n", GREEN_BEGIN, count, GREEN_END);
269     fprintf(stderr, "%s%s%s\n", GREEN_BEGIN, "Running tests...", GREEN_END);
270     for (auto &group : m_testGroups) {
271         TestCaseStructList list = group.second;
272         if (!list.empty()) {
273             for (auto &collector : m_collectors) {
274                 collector.second->CollectCurrentTestGroupName(group.first);
275             }
276             list.sort();
277
278             for (TestCaseStructList::const_iterator iterator = list.begin();
279                  iterator != list.end();
280                  ++iterator)
281             {
282                 TestCaseStruct test = *iterator;
283                 if (m_startTestId == test.name) {
284                     m_startTestId = "";
285                 }
286
287                 if (m_startTestId.empty()) {
288                     RunTestCase(test);
289                 }
290                 if (m_terminate == true) {
291                     // Terminate quietly without any logs
292                     return;
293                 }
294             }
295         }
296     }
297
298     std::for_each(m_collectors.begin(),
299                   m_collectors.end(),
300                   [] (const TestResultsCollectors::value_type & collector)
301                   {
302                       collector.second->Finish();
303                   });
304
305     // Finished
306     fprintf(stderr, "%s%s%s\n\n", GREEN_BEGIN, "Finished", GREEN_END);
307 }
308
309 TestRunner::TestCaseStruct *TestRunner::getCurrentTestCase()
310 {
311     return m_currentTestCase;
312 }
313
314 void TestRunner::setCurrentTestCase(TestCaseStruct* testCase)
315 {
316     m_currentTestCase = testCase;
317 }
318
319 void TestRunner::beginPerformance(std::chrono::system_clock::duration maxDurationInMicroseconds)
320 {
321     TestCaseStruct* testCase = getCurrentTestCase();
322     if (!testCase)
323         return;
324
325     if (!testCase->performance)
326         testCase->performance.reset(new PerformanceResult(maxDurationInMicroseconds));
327 }
328
329 void TestRunner::endPerformance()
330 {
331     TestCaseStruct* testCase = getCurrentTestCase();
332     if (!testCase)
333         return;
334
335     testCase->performance->Finish();
336 }
337
338 ConstPerformanceResultPtr TestRunner::getCurrentTestCasePerformanceResult()
339 {
340     TestCaseStruct* testCase = getCurrentTestCase();
341     if (!testCase)
342         return nullptr;
343
344     return testCase->performance;
345 }
346
347 void TestRunner::setCurrentTestCasePerformanceResult(const PerformanceResultPtr &performance)
348 {
349     TestCaseStruct* testCase = getCurrentTestCase();
350     if (!testCase)
351         return;
352
353     testCase->performance = performance;
354 }
355
356 void TestRunner::addFailReason(const std::string &reason)
357 {
358     m_failReason.push(reason);
359 }
360
361 std::string TestRunner::getConcatedFailReason(const std::string &reason)
362 {
363     std::string ret;
364     while (!m_failReason.empty())
365     {
366         ret += m_failReason.front();
367         m_failReason.pop();
368     }
369     return reason + ret;
370 }
371
372 void TestRunner::CollectResult(const std::string& id, const TestResult& result)
373 {
374     std::for_each(m_collectors.begin(),
375                   m_collectors.end(),
376                   [&](const TestResultsCollectors::value_type & collector)
377                   {
378                       collector.second->CollectResult(id, result);
379                   });
380 }
381
382 void TestRunner::Banner()
383 {
384     using namespace DPL::Colors::Text;
385     fprintf(stderr,
386             "%s%s%s\n",
387             BOLD_GREEN_BEGIN,
388             "DPL tests runner",
389             BOLD_GREEN_END);
390     fprintf(stderr,
391             "%s%s%s%s\n\n",
392             GREEN_BEGIN,
393             "Build: ",
394             __TIMESTAMP__,
395             GREEN_END);
396 }
397
398 void TestRunner::InvalidArgs(const std::string& message)
399 {
400     using namespace DPL::Colors::Text;
401     fprintf(stderr,
402             "%s%s%s\n",
403             BOLD_RED_BEGIN,
404             message.c_str(),
405             BOLD_RED_END);
406 }
407
408 void TestRunner::Usage()
409 {
410     fprintf(stderr, "Usage: runner [options]\n\n");
411     fprintf(stderr, "Output type:\n");
412     fprintf(stderr, "  --output=<output type> --output=<output type> ...\n");
413     fprintf(stderr, "\n  possible output types:\n");
414     for (std::string &type : TestResultsCollectorBase::GetCollectorsNames()) {
415         fprintf(stderr, "    --output=%s\n", type.c_str());
416     }
417     fprintf(stderr, "\n  example:\n");
418     fprintf(stderr,
419             "    test-binary --output=text --output=xml --file=output.xml\n\n");
420     fprintf(stderr, "Other parameters:\n");
421     fprintf(stderr,
422             "  --regexp='regexp'\t Only selected tests"
423             " which names match regexp run\n\n");
424     fprintf(stderr, "  --start=<test id>\tStart from concrete test id");
425     fprintf(stderr, "  --group=<group name>\t Run tests only from one group\n");
426     fprintf(stderr, "  --runignored\t Run also ignored tests\n");
427     fprintf(stderr, "  --list\t Show a list of Test IDs\n");
428     fprintf(stderr, "  --listgroups\t Show a list of Test Group names \n");
429     fprintf(stderr, "  --only-from-xml=<xml file>\t Run only testcases specified in XML file \n"
430                     "       XML name is taken from attribute id=\"part1_part2\" as whole.\n"
431                     "       If part1 is not found (no _) then it is implicitily "
432                            "set according to suite part1 from binary tests\n");
433     fprintf(
434         stderr,
435         "  --listingroup=<group name>\t Show a list of Test IDS in one group\n");
436     fprintf(stderr, "  --allowchildlogs\t Allow to print logs from child process on screen.\n");
437     fprintf(stderr, "       When active child process will be able to print logs on stdout and stderr.\n");
438     fprintf(stderr, "       Both descriptors will be closed after test.\n");
439     fprintf(stderr, "  --help\t This help\n\n");
440     std::for_each(m_collectors.begin(),
441                   m_collectors.end(),
442                   [] (const TestResultsCollectors::value_type & collector)
443                   {
444                       fprintf(stderr,
445                               "Output %s has specific args:\n",
446                               collector.first.c_str());
447                       fprintf(stderr,
448                               "%s\n",
449                               collector.second->
450                                   CollectorSpecificHelp().c_str());
451                   });
452     fprintf(stderr, "For bug reporting, please write to:\n");
453     fprintf(stderr, "<p.dobrowolsk@samsung.com>\n");
454 }
455
456 int TestRunner::ExecTestRunner(int argc, char *argv[])
457 {
458     std::vector<std::string> args;
459     for (int i = 0; i < argc; ++i) {
460         args.push_back(argv[i]);
461     }
462     return ExecTestRunner(args);
463 }
464
465 void TestRunner::MarkAssertion()
466 {
467     ++m_totalAssertions;
468 }
469
470 int TestRunner::ExecTestRunner(ArgsList args)
471 {
472     m_runIgnored = false;
473     // Parse command line
474
475     args.erase(args.begin());
476
477     bool showHelp = false;
478     bool justList = false;
479     std::vector<std::string> xmlFiles;
480
481     TestResultsCollectorBasePtr currentCollector;
482
483     // Parse each argument
484     for(std::string &arg : args)
485     {
486         const std::string regexp = "--regexp=";
487         const std::string output = "--output=";
488         const std::string groupId = "--group=";
489         const std::string runIgnored = "--runignored";
490         const std::string listCmd = "--list";
491         const std::string startCmd = "--start=";
492         const std::string listGroupsCmd = "--listgroups";
493         const std::string listInGroup = "--listingroup=";
494         const std::string allowChildLogs = "--allowchildlogs";
495         const std::string onlyFromXML = "--only-from-xml=";
496
497         if (currentCollector) {
498             if (currentCollector->ParseCollectorSpecificArg(arg)) {
499                 continue;
500             }
501         }
502
503         if (arg.find(startCmd) == 0) {
504             arg.erase(0, startCmd.length());
505             for (auto &group : m_testGroups) {
506                 for (auto &tc : group.second) {
507                     if (tc.name == arg) {
508                         m_startTestId = arg;
509                         break;
510                     }
511                 }
512                 if (!m_startTestId.empty()) {
513                     break;
514                 }
515             }
516             if (!m_startTestId.empty()) {
517                 continue;
518             }
519             InvalidArgs();
520             fprintf(stderr, "Start test id has not been found\n");
521             Usage();
522             return 0;
523         } else if (arg.find(groupId) == 0) {
524             arg.erase(0, groupId.length());
525             TestCaseGroupMap::iterator found = m_testGroups.find(arg);
526             if (found != m_testGroups.end()) {
527                 std::string name = found->first;
528                 TestCaseStructList newList = found->second;
529                 m_testGroups.clear();
530                 m_testGroups[name] = newList;
531             } else {
532                 fprintf(stderr, "Group %s not found\n", arg.c_str());
533                 InvalidArgs();
534                 Usage();
535                 return -1;
536             }
537         } else if (arg == runIgnored) {
538             m_runIgnored = true;
539         } else if (arg == listCmd) {
540             justList = true;
541         } else if (arg == listGroupsCmd) {
542             for (auto &group : m_testGroups) {
543                 printf("GR:%s\n", group.first.c_str());
544             }
545             return 0;
546         } else if (arg.find(listInGroup) == 0) {
547             arg.erase(0, listInGroup.length());
548             for (auto &test : m_testGroups[arg]) {
549                 printf("ID:%s\n", test.name.c_str());
550             }
551             return 0;
552         } else if (arg.find(allowChildLogs) == 0) {
553             arg.erase(0, allowChildLogs.length());
554             m_allowChildLogs = true;
555         } else if (arg == "--help") {
556             showHelp = true;
557         } else if (arg.find(output) == 0) {
558             arg.erase(0, output.length());
559             if (m_collectors.find(arg) != m_collectors.end()) {
560                 InvalidArgs(
561                     "Multiple outputs of the same type are not supported!");
562                 Usage();
563                 return -1;
564             }
565             currentCollector.reset(TestResultsCollectorBase::Create(arg));
566             if (!currentCollector) {
567                 InvalidArgs("Unsupported output type!");
568                 Usage();
569                 return -1;
570             }
571             m_collectors[arg] = currentCollector;
572         } else if (arg.find(regexp) == 0) {
573             arg.erase(0, regexp.length());
574             if (arg.length() == 0) {
575                 InvalidArgs();
576                 Usage();
577                 return -1;
578             }
579
580             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
581                 arg.erase(0);
582                 arg.erase(arg.length() - 1);
583             }
584
585             if (arg.length() == 0) {
586                 InvalidArgs();
587                 Usage();
588                 return -1;
589             }
590
591             pcrecpp::RE re(arg.c_str());
592             for (auto &group : m_testGroups) {
593                 TestCaseStructList newList;
594                 for (auto &tc : group.second)
595                 {
596                     if (re.PartialMatch(tc.name)) {
597                         newList.push_back(tc);
598                     }
599                 }
600                 group.second = newList;
601             }
602         } else if(arg.find(onlyFromXML) == 0) {
603             arg.erase(0, onlyFromXML.length());
604             if (arg.length() == 0) {
605                 InvalidArgs();
606                 Usage();
607                 return -1;
608             }
609
610             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
611                 arg.erase(0);
612                 arg.erase(arg.length() - 1);
613             }
614
615             if (arg.length() == 0) {
616                 InvalidArgs();
617                 Usage();
618                 return -1;
619             }
620
621             xmlFiles.push_back(arg);
622         } else {
623             InvalidArgs();
624             Usage();
625             return -1;
626         }
627     }
628
629     if(!xmlFiles.empty())
630     {
631         if(!filterGroupsByXmls(xmlFiles))
632         {
633             fprintf(stderr, "XML file is not correct\n");
634             return 0;
635         }
636     }
637
638     if(justList)
639     {
640         for (auto &group : m_testGroups) {
641             for (auto &tc : group.second) {
642                 printf("ID:%s:%s\n", group.first.c_str(), tc.name.c_str());
643             }
644         }
645         return 0;
646     }
647
648     currentCollector.reset();
649
650     // Show help
651     if (showHelp) {
652         Usage();
653         return 0;
654     }
655
656     if (m_collectors.empty()) {
657         TestResultsCollectorBasePtr collector(
658             TestResultsCollectorBase::Create("text"));
659         m_collectors["text"] = collector;
660     }
661
662     for (auto &collector : m_collectors) {
663         if (!collector.second->Configure()) {
664             fprintf(stderr, "Could not configure selected output");
665             return 0;
666         }
667     }
668
669     // Run tests
670     RunTests();
671
672     return 0;
673 }
674
675 bool TestRunner::getRunIgnored() const
676 {
677     return m_runIgnored;
678 }
679
680 void TestRunner::Terminate()
681 {
682     m_terminate = true;
683 }
684
685 bool TestRunner::GetAllowChildLogs()
686 {
687     return m_allowChildLogs;
688 }
689
690 void TestRunner::deferFailedException(const DPL::Test::TestFailed &ex)
691 {
692     if (m_deferDeepness <= 0)
693         throw ex;
694
695     if (m_deferredExceptionsMessages.empty()) {
696         m_firstDeferredFail = ex;
697         m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_FAILED;
698     }
699     m_deferredExceptionsMessages.push_back(ex.GetMessage());
700 }
701
702 void TestRunner::deferIgnoredException(const DPL::Test::TestIgnored &ex)
703 {
704     if (m_deferDeepness <= 0)
705         throw ex;
706
707     if (m_deferredExceptionsMessages.empty()) {
708         m_firstDeferredIgnore = ex;
709         m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_IGNORED;
710     }
711     m_deferredExceptionsMessages.push_back(ex.GetMessage());
712 }
713
714 void TestRunner::deferBegin()
715 {
716     m_deferDeepness++;
717 }
718
719 void TestRunner::deferEnd()
720 {
721     if (m_deferDeepness > 0)
722         m_deferDeepness--;
723
724     if (m_deferDeepness > 0)
725         return;
726
727     bool oops = std::uncaught_exception();
728     size_t additionalExceptions = oops ? 0 : 1;
729     for (size_t i = additionalExceptions; i < m_deferredExceptionsMessages.size(); ++i)
730         addFailReason(m_deferredExceptionsMessages[i]);
731
732     if (!oops && !m_deferredExceptionsMessages.empty())
733     {
734         m_deferredExceptionsMessages.clear();
735         switch (m_firstDeferredExceptionType) {
736             case DeferredExceptionType::DEFERRED_FAILED:
737                 throw m_firstDeferredFail;
738             case DeferredExceptionType::DEFERRED_IGNORED:
739                 throw m_firstDeferredIgnore;
740         }
741     }
742     m_deferredExceptionsMessages.clear();
743 }
744
745 } // namespace Test
746 } // namespace DPL