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