Initialize Tizen 2.3
[framework/web/wrt-commons.git] / modules_mobile / test / src / test_runner.cpp
1 /*
2  * Copyright (c) 2011 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/foreach.h>
29 #include <dpl/log/log.h>
30 #include <dpl/colors.h>
31 #include <pcrecpp.h>
32 #include <algorithm>
33 #include <cstdio>
34 #include <memory.h>
35 #include <libgen.h>
36 #include <cstring>
37 #include <cstdlib>
38 #include <dpl/utils/wrt_global_settings.h>
39
40 #include <libxml/xpath.h>
41 #include <libxml/xpathInternals.h>
42 #include <libxml/parser.h>
43 #include <libxml/tree.h>
44
45 #include <dpl/singleton_impl.h>
46 IMPLEMENT_SINGLETON(DPL::Test::TestRunner)
47
48 namespace {
49
50 std::string getXMLNode(xmlNodePtr node)
51 {
52     std::string ret;
53     xmlChar * value = xmlNodeGetContent(node);
54     ret = std::string(reinterpret_cast<char*>(value));
55     xmlFree(value);
56     return ret;
57 }
58
59 }
60
61
62 namespace DPL {
63 namespace Test {
64 namespace // anonymous
65 {
66 std::string BaseName(std::string aPath)
67 {
68     ScopedFree<char> path(strdup(aPath.c_str()));
69     if (NULL == path.Get()) {
70         throw std::bad_alloc();
71     }
72     char* baseName = basename(path.Get());
73     std::string retValue = baseName;
74     return retValue;
75 }
76 } // namespace anonymous
77
78 //! \brief Failed test message creator
79 //!
80 //! \param[in] aTest string for tested expression
81 //! \param[in] aFile source file name
82 //! \param[in] aLine source file line
83 //! \param[in] aMessage error message
84 TestRunner::TestFailed::TestFailed(const char* aTest,
85                                    const char* aFile,
86                                    int aLine,
87                                    const std::string &aMessage)
88 {
89     std::ostringstream assertMsg;
90     assertMsg << "[" << BaseName(aFile) << ":" << aLine
91               << "] Assertion failed ("
92               << aTest << ") " << aMessage;
93     m_message = assertMsg.str();
94 }
95
96 TestRunner::TestFailed::TestFailed(const std::string &message)
97 {
98     m_message = message;
99 }
100
101 void TestRunner::RegisterTest(const char *testName, TestCase proc)
102 {
103     m_testGroups[m_currentGroup].push_back(TestCaseStruct(testName, proc));
104 }
105
106 void TestRunner::InitGroup(const char* name)
107 {
108     m_currentGroup = name;
109 }
110
111 void TestRunner::normalizeXMLTag(std::string& str, const std::string& testcase)
112 {
113     //Add testcase if missing
114     std::string::size_type pos = str.find(testcase);
115     if(pos != 0)
116     {
117         str = testcase + "_" + str;
118     }
119
120     //dpl test runner cannot have '-' character in name so it have to be replaced
121     // for TCT case to make comparision works
122     std::replace(str.begin(), str.end(), '-', '_');
123 }
124
125 bool TestRunner::filterGroupsByXmls(const std::vector<std::string> & files)
126 {
127     DECLARE_EXCEPTION_TYPE(DPL::Exception, XMLError)
128
129     const std::string idPath = "/test_definition/suite/set/testcase/@id";
130
131     bool success = true;
132     std::map<std::string, bool> casesMap;
133
134     std::string testsuite;
135     if(!m_testGroups.empty())
136     {
137         for(TestCaseGroupMap::const_iterator cit = m_testGroups.begin(); cit != m_testGroups.end(); ++cit)
138         {
139             if(!cit->second.empty())
140             {
141                 for(TestCaseStructList::const_iterator cj = cit->second.begin(); cj != cit->second.end(); ++cj)
142                 {
143                     std::string name = cj->name;
144                     std::string::size_type st = name.find('_');
145                     if(st != std::string::npos)
146                     {
147                         name = name.substr(0, st);
148                         testsuite = name;
149                         break;
150                     }
151                 }
152                 if(!testsuite.empty()) break;
153             }
154         }
155     }
156
157     xmlInitParser();
158     LIBXML_TEST_VERSION
159     xmlXPathInit();
160
161     Try
162     {
163         FOREACH(file, files)
164         {
165             xmlDocPtr doc;
166             xmlXPathContextPtr xpathCtx;
167
168             doc = xmlReadFile(file->c_str(), NULL, 0);
169             if (doc == NULL) {
170                 ThrowMsg(XMLError, "File Problem");
171             } else {
172                 //context
173                 xpathCtx = xmlXPathNewContext(doc);
174                 if (xpathCtx == NULL) {
175                     ThrowMsg(XMLError,
176                              "Error: unable to create new XPath context\n");
177                 }
178                 xpathCtx->node = xmlDocGetRootElement(doc);
179             }
180
181             std::string result;
182             xmlXPathObjectPtr xpathObject;
183             //get requested node's values
184             xpathObject = xmlXPathEvalExpression(BAD_CAST idPath.c_str(), xpathCtx);
185             if (xpathObject == NULL)
186             {
187                 ThrowMsg(XMLError, "XPath evaluation failure: " << idPath);
188             }
189             xmlNodeSetPtr nodes = xpathObject->nodesetval;
190             unsigned size = (nodes) ? nodes->nodeNr : 0;
191             LogDebug("Found " << size << " nodes matching xpath");
192             for(unsigned i = 0; i < size; ++i)
193             {
194                 LogPedantic("Type: " << nodes->nodeTab[i]->type);
195                 if (nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
196                     xmlNodePtr curNode = nodes->nodeTab[i];
197                     result = getXMLNode(curNode);
198                     LogPedantic("Result: " << result);
199                     normalizeXMLTag(result, testsuite);
200                     casesMap.insert(make_pair(result, false));
201                 }
202             }
203             //Cleanup of XPath data
204             xmlXPathFreeObject(xpathObject);
205             xmlXPathFreeContext(xpathCtx);
206             xmlFreeDoc(doc);
207         }
208     }
209     Catch(XMLError)
210     {
211         LogError("Libxml error: " << _rethrown_exception.DumpToString());
212         success = false;
213     }
214     xmlCleanupParser();
215
216     if(!filterByXML(casesMap))
217     {
218         success = false;
219     }
220
221     return success;
222 }
223
224 bool TestRunner::filterByXML(std::map<std::string, bool> & casesMap)
225 {
226     FOREACH(group, m_testGroups) {
227         TestCaseStructList newList;
228         FOREACH(iterator, group->second)
229         {
230             if (casesMap.find(iterator->name) != casesMap.end()) {
231                 casesMap[iterator->name] = true;
232                 newList.push_back(*iterator);
233             }
234         }
235         group->second = newList;
236     }
237     FOREACH(cs, casesMap)
238     {
239         if(cs->second == false)
240         {
241             LogError("Cannot find testcase from XML file: " << cs->first);
242             return false;
243         }
244     }
245     return true;
246 }
247
248 TestRunner::Status TestRunner::RunTestCase(const TestCaseStruct& testCase)
249 {
250     try {
251         testCase.proc();
252     } catch (const TestFailed &e) {
253         // Simple test failure
254         CollectResult(testCase.name,
255                       "",
256                       TestResultsCollectorBase::FailStatus::FAILED,
257                       e.GetMessage());
258         return FAILED;
259     } catch (const Ignored &e) {
260         if (m_runIgnored) {
261             // Simple test have to be implemented
262             CollectResult(testCase.name,
263                           "",
264                           TestResultsCollectorBase::FailStatus::IGNORED,
265                           e.GetMessage());
266         }
267
268         return IGNORED;
269     } catch (const DPL::Exception &e) {
270         // DPL exception failure
271         CollectResult(testCase.name,
272                       "",
273                       TestResultsCollectorBase::FailStatus::INTERNAL,
274                       "DPL exception:" + e.GetMessage());
275
276         return FAILED;
277     } catch (const std::exception &) {
278         // std exception failure
279         CollectResult(testCase.name,
280                       "",
281                       TestResultsCollectorBase::FailStatus::INTERNAL,
282                       "std exception");
283
284         return FAILED;
285     } catch (...) {
286         // Unknown exception failure
287         CollectResult(testCase.name,
288                       "",
289                       TestResultsCollectorBase::FailStatus::INTERNAL,
290                       "unknown exception");
291
292         return FAILED;
293     }
294
295     CollectResult(testCase.name,
296                   "",
297                   TestResultsCollectorBase::FailStatus::NONE);
298
299     // Everything OK
300     return PASS;
301 }
302
303 void TestRunner::RunTests()
304 {
305     using namespace DPL::Colors::Text;
306
307     Banner();
308     std::for_each(m_collectors.begin(),
309                   m_collectors.end(),
310                   [] (const TestResultsCollectors::value_type & collector)
311                   {
312                       collector.second->Start();
313                   });
314
315     unsigned count = 0;
316     FOREACH(group, m_testGroups) {
317         count += group->second.size();
318     }
319     fprintf(stderr, "%sFound %d testcases...%s\n", GREEN_BEGIN, count, GREEN_END);
320     fprintf(stderr, "%s%s%s\n", GREEN_BEGIN, "Running tests...", GREEN_END);
321     FOREACH(group, m_testGroups) {
322         TestCaseStructList list = group->second;
323         if (!list.empty()) {
324             std::for_each(
325                 m_collectors.begin(),
326                 m_collectors.end(),
327                 [&group](const TestResultsCollectors::value_type & collector)
328                 {
329                     collector.second->
330                         CollectCurrentTestGroupName(group->first);
331                 });
332             list.sort();
333
334             for (TestCaseStructList::const_iterator iterator = list.begin();
335                  iterator != list.end();
336                  ++iterator)
337             {
338                 TestCaseStruct test = *iterator;
339                 if (m_startTestId == test.name) {
340                     m_startTestId = "";
341                 }
342
343                 if (m_startTestId.empty()) {
344                     RunTestCase(test);
345                 }
346                 if (m_terminate == true) {
347                     // Terminate quietly without any logs
348                     return;
349                 }
350             }
351         }
352     }
353
354     std::for_each(m_collectors.begin(),
355                   m_collectors.end(),
356                   [] (const TestResultsCollectors::value_type & collector)
357                   {
358                       collector.second->Finish();
359                   });
360
361     // Finished
362     fprintf(stderr, "%s%s%s\n\n", GREEN_BEGIN, "Finished", GREEN_END);
363 }
364
365 void TestRunner::CollectResult(
366     const std::string& id,
367     const std::string& description,
368     const TestResultsCollectorBase::FailStatus::Type status,
369     const std::string& reason)
370 {
371     std::for_each(m_collectors.begin(),
372                   m_collectors.end(),
373                   [&](const TestResultsCollectors::value_type & collector)
374                   {
375                       collector.second->CollectResult(id,
376                                                       description,
377                                                       status,
378                                                       reason);
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     FOREACH(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(const ArgsList& value)
471 {
472     m_runIgnored = false;
473     ArgsList args = value;
474     // Parse command line
475     if (args.size() == 1) {
476         InvalidArgs();
477         Usage();
478         return -1;
479     }
480
481     args.erase(args.begin());
482
483     bool showHelp = false;
484     bool justList = false;
485     std::vector<std::string> xmlFiles;
486
487     TestResultsCollectorBasePtr currentCollector;
488
489     // Parse each argument
490     FOREACH(it, args)
491     {
492         std::string arg = *it;
493         const std::string regexp = "--regexp=";
494         const std::string output = "--output=";
495         const std::string groupId = "--group=";
496         const std::string runIgnored = "--runignored";
497         const std::string listCmd = "--list";
498         const std::string startCmd = "--start=";
499         const std::string listGroupsCmd = "--listgroups";
500         const std::string listInGroup = "--listingroup=";
501         const std::string allowChildLogs = "--allowchildlogs";
502         const std::string onlyFromXML = "--only-from-xml=";
503
504         if (currentCollector) {
505             if (currentCollector->ParseCollectorSpecificArg(arg)) {
506                 continue;
507             }
508         }
509
510         if (arg.find(startCmd) == 0) {
511             arg.erase(0, startCmd.length());
512             FOREACH(group, m_testGroups) {
513                 FOREACH(tc, group->second) {
514                     if (tc->name == arg) {
515                         m_startTestId = arg;
516                         break;
517                     }
518                 }
519                 if (!m_startTestId.empty()) {
520                     break;
521                 }
522             }
523             if (!m_startTestId.empty()) {
524                 continue;
525             }
526             InvalidArgs();
527             fprintf(stderr, "Start test id has not been found\n");
528             Usage();
529             return 0;
530         } else if (arg.find(groupId) == 0) {
531             arg.erase(0, groupId.length());
532             TestCaseGroupMap::iterator found = m_testGroups.find(arg);
533             if (found != m_testGroups.end()) {
534                 std::string name = found->first;
535                 TestCaseStructList newList = found->second;
536                 m_testGroups.clear();
537                 m_testGroups[name] = newList;
538             } else {
539                 fprintf(stderr, "Group %s not found\n", arg.c_str());
540                 InvalidArgs();
541                 Usage();
542                 return -1;
543             }
544         } else if (arg == runIgnored) {
545             m_runIgnored = true;
546         } else if (arg == listCmd) {
547             justList = true;
548         } else if (arg == listGroupsCmd) {
549             FOREACH(group, m_testGroups) {
550                 printf("GR:%s\n", group->first.c_str());
551             }
552             return 0;
553         } else if (arg.find(listInGroup) == 0) {
554             arg.erase(0, listInGroup.length());
555             FOREACH(test, m_testGroups[arg]) {
556                 printf("ID:%s\n", test->name.c_str());
557             }
558             return 0;
559         } else if (arg.find(allowChildLogs) == 0) {
560             arg.erase(0, allowChildLogs.length());
561             m_allowChildLogs = true;
562         } else if (arg == "--help") {
563             showHelp = true;
564         } else if (arg.find(output) == 0) {
565             arg.erase(0, output.length());
566             if (m_collectors.find(arg) != m_collectors.end()) {
567                 InvalidArgs(
568                     "Multiple outputs of the same type are not supported!");
569                 Usage();
570                 return -1;
571             }
572             currentCollector.reset(TestResultsCollectorBase::Create(arg));
573             if (!currentCollector) {
574                 InvalidArgs("Unsupported output type!");
575                 Usage();
576                 return -1;
577             }
578             m_collectors[arg] = currentCollector;
579         } else if (arg.find(regexp) == 0) {
580             arg.erase(0, regexp.length());
581             if (arg.length() == 0) {
582                 InvalidArgs();
583                 Usage();
584                 return -1;
585             }
586
587             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
588                 arg.erase(0);
589                 arg.erase(arg.length() - 1);
590             }
591
592             if (arg.length() == 0) {
593                 InvalidArgs();
594                 Usage();
595                 return -1;
596             }
597
598             pcrecpp::RE re(arg.c_str());
599             FOREACH(group, m_testGroups) {
600                 TestCaseStructList newList;
601                 FOREACH(iterator, group->second)
602                 {
603                     if (re.PartialMatch(iterator->name)) {
604                         newList.push_back(*iterator);
605                     }
606                 }
607                 group->second = newList;
608             }
609         } else if(arg.find(onlyFromXML) == 0) {
610             arg.erase(0, onlyFromXML.length());
611             if (arg.length() == 0) {
612                 InvalidArgs();
613                 Usage();
614                 return -1;
615             }
616
617             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
618                 arg.erase(0);
619                 arg.erase(arg.length() - 1);
620             }
621
622             if (arg.length() == 0) {
623                 InvalidArgs();
624                 Usage();
625                 return -1;
626             }
627
628             xmlFiles.push_back(arg);
629         } else {
630             InvalidArgs();
631             Usage();
632             return -1;
633         }
634     }
635
636     if(!xmlFiles.empty())
637     {
638         if(!filterGroupsByXmls(xmlFiles))
639         {
640             fprintf(stderr, "XML file is not correct\n");
641             return 0;
642         }
643     }
644
645     if(justList)
646     {
647         FOREACH(group, m_testGroups) {
648             FOREACH(test, group->second) {
649                 printf("ID:%s:%s\n", group->first.c_str(), test->name.c_str());
650             }
651         }
652         return 0;
653     }
654
655     currentCollector.reset();
656
657     // Show help
658     if (showHelp) {
659         Usage();
660         return 0;
661     }
662
663     if (m_collectors.empty()) {
664         TestResultsCollectorBasePtr collector(
665             TestResultsCollectorBase::Create("text"));
666         m_collectors["text"] = collector;
667     }
668
669     for (auto it = m_collectors.begin(); it != m_collectors.end(); ++it) {
670         if (!it->second->Configure()) {
671             fprintf(stderr, "Could not configure selected output");
672             return 0;
673         }
674     }
675
676     // Run tests
677     RunTests();
678
679     return 0;
680 }
681
682 bool TestRunner::getRunIgnored() const
683 {
684     return m_runIgnored;
685 }
686
687 void TestRunner::Terminate()
688 {
689     m_terminate = true;
690 }
691
692 bool TestRunner::GetAllowChildLogs()
693 {
694     return m_allowChildLogs;
695 }
696
697 }
698 } // namespace DPL