ec67029e6e299df3c91a79153052ea8a6118d88d
[platform/core/test/security-tests.git] / src / framework / src / test_runner.cpp
1 /*
2  * Copyright (c) 2014-2017 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/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>
33 #include <pcrecpp.h>
34 #include <algorithm>
35 #include <cstdio>
36 #include <exception>
37 #include <functional>
38 #include <memory>
39 #include <string>
40
41 #include <libxml/xpath.h>
42 #include <libxml/xpathInternals.h>
43 #include <libxml/parser.h>
44 #include <libxml/tree.h>
45
46 #include <dpl/singleton_impl.h>
47 IMPLEMENT_SINGLETON(DPL::Test::TestRunner)
48
49 namespace {
50
51 std::string getXMLNode(xmlNodePtr node)
52 {
53     std::string ret;
54     xmlChar * value = xmlNodeGetContent(node);
55     ret = std::string(reinterpret_cast<char*>(value));
56     xmlFree(value);
57     return ret;
58 }
59
60 }
61
62 namespace DPL {
63 namespace Test {
64
65 TestResult::FailStatus TryCatch(const std::function<void(void)> &func, std::string &reason) {
66     try {
67         func();
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) {
75         reason = e.what();
76         return TestResult::FailStatus::FAILED;
77     } catch (...) {
78         reason = "Unknown exception";
79         return TestResult::FailStatus::FAILED;
80     }
81     reason = std::string();
82     return TestResult::FailStatus::NONE;
83 }
84
85 void TestRunner::RegisterTest(TestCasePtr testCase)
86 {
87     m_testGroups[m_currentGroup].push_back(testCase);
88     m_testCaseSet.insert(testCase);
89 }
90
91 void TestRunner::InitGroup(const char* name)
92 {
93     m_currentGroup = name;
94 }
95
96 void TestRunner::normalizeXMLTag(std::string& str, const std::string& testcase)
97 {
98     //Add testcase if missing
99     std::string::size_type pos = str.find(testcase);
100     if(pos != 0)
101     {
102         str = testcase + "_" + str;
103     }
104
105     //dpl test runner cannot have '-' character in name so it have to be replaced
106     // for TCT case to make comparision works
107     std::replace(str.begin(), str.end(), '-', '_');
108 }
109
110 bool TestRunner::filterGroupsByXmls(const std::vector<std::string> & files)
111 {
112     DECLARE_EXCEPTION_TYPE(DPL::Exception, XMLError)
113
114     const std::string idPath = "/test_definition/suite/set/testcase/@id";
115
116     bool success = true;
117     std::map<std::string, bool> casesMap;
118
119     std::string testsuite;
120     if(!m_testGroups.empty())
121     {
122         for(TestCaseGroupMap::const_iterator cit = m_testGroups.begin(); cit != m_testGroups.end(); ++cit)
123         {
124             if(!cit->second.empty())
125             {
126                 for(TestCaseList::const_iterator cj = cit->second.begin(); cj != cit->second.end(); ++cj)
127                 {
128                     std::string name = (*cj)->GetName();
129                     std::string::size_type st = name.find('_');
130                     if(st != std::string::npos)
131                     {
132                         name = name.substr(0, st);
133                         testsuite = name;
134                         break;
135                     }
136                 }
137                 if(!testsuite.empty()) break;
138             }
139         }
140     }
141
142     xmlInitParser();
143     LIBXML_TEST_VERSION
144     xmlXPathInit();
145
146     Try
147     {
148         for (const std::string &file : files)
149         {
150             xmlDocPtr doc;
151             xmlXPathContextPtr xpathCtx;
152
153             doc = xmlReadFile(file.c_str(), nullptr, 0);
154             if (doc == nullptr) {
155                 ThrowMsg(XMLError, "File Problem");
156             } else {
157                 //context
158                 xpathCtx = xmlXPathNewContext(doc);
159                 if (xpathCtx == nullptr) {
160                     ThrowMsg(XMLError,
161                              "Error: unable to create new XPath context\n");
162                 }
163                 xpathCtx->node = xmlDocGetRootElement(doc);
164             }
165
166             std::string result;
167             xmlXPathObjectPtr xpathObject;
168             //get requested node's values
169             xpathObject = xmlXPathEvalExpression(BAD_CAST idPath.c_str(), xpathCtx);
170             if (xpathObject == nullptr)
171             {
172                 ThrowMsg(XMLError, "XPath evaluation failure: " << idPath);
173             }
174             xmlNodeSetPtr nodes = xpathObject->nodesetval;
175             unsigned size = (nodes) ? nodes->nodeNr : 0;
176             LogDebug("Found " << size << " nodes matching xpath");
177             for(unsigned i = 0; i < size; ++i)
178             {
179                 LogPedantic("Type: " << nodes->nodeTab[i]->type);
180                 if (nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE) {
181                     xmlNodePtr curNode = nodes->nodeTab[i];
182                     result = getXMLNode(curNode);
183                     LogPedantic("Result: " << result);
184                     normalizeXMLTag(result, testsuite);
185                     casesMap.insert(make_pair(result, false));
186                 }
187             }
188             //Cleanup of XPath data
189             xmlXPathFreeObject(xpathObject);
190             xmlXPathFreeContext(xpathCtx);
191             xmlFreeDoc(doc);
192         }
193     }
194     Catch(XMLError)
195     {
196         LogError("Libxml error: " << _rethrown_exception.DumpToString());
197         success = false;
198     }
199     xmlCleanupParser();
200
201     if(!filterByXML(casesMap))
202     {
203         success = false;
204     }
205
206     return success;
207 }
208
209 bool TestRunner::filterByXML(std::map<std::string, bool> & casesMap)
210 {
211     for (auto &group : m_testGroups) {
212         TestCaseList newList;
213         for (auto &tc : group.second)
214         {
215             if (casesMap.find(tc->GetName()) != casesMap.end()) {
216                 casesMap[tc->GetName()] = true;
217                 newList.push_back(tc);
218             }
219         }
220         group.second = newList;
221     }
222     for (auto &cs : casesMap)
223     {
224         if(cs.second == false)
225         {
226             LogError("Cannot find testcase from XML file: " << cs.first);
227             return false;
228         }
229     }
230     return true;
231 }
232
233 void TestRunner::RunTestCase(TestCasePtr testCase)
234 {
235     setCurrentTestCase(testCase);
236     m_deferDeepness = 0U;
237
238     std::string initReason;
239     TestResult::FailStatus initStatus = TryCatch(std::bind(&TestCase::Init, testCase),
240                                                  initReason);
241     if (initStatus != TestResult::FailStatus::NONE) {
242         CollectResult(testCase->GetName(),
243                       TestResult(initStatus, getConcatedFailReason(initReason), testCase->GetPerformance()));
244         setCurrentTestCase(nullptr);
245         return;
246     }
247
248     SafeCleanup::reset();
249
250     std::string testReason;
251     TestResult::FailStatus testStatus = TryCatch(std::bind(&TestCase::Test, testCase),
252                                                  testReason);
253     testReason = getConcatedFailReason(testReason);
254     std::string finishReason;
255     TestResult::FailStatus finishStatus = TryCatch(std::bind(&TestCase::Finish, testCase),
256                                                    finishReason);
257     finishReason = getConcatedFailReason(finishReason);
258
259     std::string cleanupReason = SafeCleanup::dump();
260
261     switch (finishStatus) {
262         case TestResult::FailStatus::FAILED:
263             testStatus = TestResult::FailStatus::FAILED;
264             if (!testReason.empty())
265                 testReason += "\n";
266             testReason += finishReason;
267             if (!cleanupReason.empty()) {
268                 if (!testReason.empty())
269                     testReason += "\n";
270                 testReason += cleanupReason;
271             }
272             break;
273         case TestResult::FailStatus::IGNORED:
274             if (testStatus == TestResult::FailStatus::NONE)
275                 testStatus = TestResult::FailStatus::IGNORED;
276             if (!testReason.empty())
277                 testReason += "\n";
278             testReason += finishReason;
279         case TestResult::FailStatus::NONE:
280             if (!cleanupReason.empty()) {
281                 if (!testReason.empty())
282                     testReason += "\n";
283                 testReason += cleanupReason;
284                 if (testStatus == TestResult::FailStatus::NONE)
285                     testStatus = TestResult::FailStatus::FAILED;
286             }
287             break;
288         default:
289             Assert(false && "Unhandled fail status");
290     }
291
292     CollectResult(testCase->GetName(),
293                   TestResult(testStatus, testReason, testCase->GetPerformance()));
294     setCurrentTestCase(nullptr);
295 }
296
297 void TestRunner::RunTests()
298 {
299     using namespace DPL::Colors::Text;
300
301     Banner();
302     for (auto &collector : m_collectors) {
303         collector.second->Start();
304     }
305
306     unsigned count = 0;
307     for (auto &group : m_testGroups) {
308         count += group.second.size();
309     }
310     fprintf(stderr, "%sFound %d testcases...%s\n", GREEN_BEGIN, count, GREEN_END);
311     fprintf(stderr, "%s%s%s\n", GREEN_BEGIN, "Running tests...", GREEN_END);
312     for (auto &group : m_testGroups) {
313         TestCaseList list = group.second;
314         if (!list.empty()) {
315             for (auto &collector : m_collectors) {
316                 collector.second->CollectCurrentTestGroupName(group.first);
317             }
318             list.sort([](const TestCasePtr &a, const TestCasePtr &b) { return (*a < *b); });
319
320             for (TestCaseList::const_iterator iterator = list.begin();
321                  iterator != list.end();
322                  ++iterator)
323             {
324                 TestCasePtr test = *iterator;
325                 if (m_startTestId == test->GetName()) {
326                     m_startTestId = "";
327                 }
328
329                 if (m_startTestId.empty()) {
330                     RunTestCase(test);
331                 }
332                 if (m_terminate == true) {
333                     // Terminate quietly without any logs
334                     return;
335                 }
336             }
337         }
338     }
339
340     std::for_each(m_collectors.begin(),
341                   m_collectors.end(),
342                   [] (const TestResultsCollectors::value_type & collector)
343                   {
344                       collector.second->Finish();
345                   });
346
347     // Finished
348     fprintf(stderr, "%s%s%s\n\n", GREEN_BEGIN, "Finished", GREEN_END);
349 }
350
351 TestCasePtr TestRunner::getCurrentTestCase()
352 {
353     return m_currentTestCase;
354 }
355
356 void TestRunner::setCurrentTestCase(TestCasePtr testCase)
357 {
358     m_currentTestCase = testCase;
359 }
360
361 void TestRunner::beginPerformance(std::chrono::system_clock::duration maxDurationInMicroseconds)
362 {
363     TestCasePtr testCase = getCurrentTestCase();
364     if (!testCase)
365         return;
366
367     if (!testCase->GetPerformance())
368         testCase->SetPerformance(
369             std::unique_ptr<PerformanceResult>                  \
370             (new PerformanceResult(maxDurationInMicroseconds)));
371 }
372
373 void TestRunner::endPerformance()
374 {
375     TestCasePtr testCase = getCurrentTestCase();
376     if (!testCase)
377         return;
378
379     testCase->GetPerformance()->Finish();
380 }
381
382 ConstPerformanceResultPtr TestRunner::getCurrentTestCasePerformanceResult()
383 {
384     TestCasePtr testCase = getCurrentTestCase();
385     if (!testCase)
386         return nullptr;
387
388     return testCase->GetPerformance();
389 }
390
391 void TestRunner::setCurrentTestCasePerformanceResult(const PerformanceResultPtr &performance)
392 {
393     TestCasePtr testCase = getCurrentTestCase();
394     if (!testCase)
395         return;
396
397     testCase->SetPerformance(performance);
398 }
399
400 void TestRunner::addFailReason(const std::string &reason)
401 {
402     m_failReason.push(reason);
403 }
404
405 std::string TestRunner::getConcatedFailReason(const std::string &reason)
406 {
407     std::string ret;
408     while (!m_failReason.empty())
409     {
410         ret += m_failReason.front();
411         m_failReason.pop();
412     }
413     return reason + ret;
414 }
415
416 TestRunner::~TestRunner()
417 {
418     for(auto &t : m_testCaseSet)
419         delete t;
420 }
421
422 void TestRunner::CollectResult(const std::string& id, const TestResult& result)
423 {
424     if (result.GetFailStatus() == TestResult::FailStatus::IGNORED && m_runIgnored)
425         return;
426
427     std::for_each(m_collectors.begin(),
428                   m_collectors.end(),
429                   [&](const TestResultsCollectors::value_type & collector)
430                   {
431                       collector.second->CollectResult(id, result);
432                   });
433 }
434
435 void TestRunner::Banner()
436 {
437     using namespace DPL::Colors::Text;
438     fprintf(stderr,
439             "%s%s%s\n",
440             BOLD_GREEN_BEGIN,
441             "DPL tests runner",
442             BOLD_GREEN_END);
443     fprintf(stderr,
444             "%s%s%s%s\n\n",
445             GREEN_BEGIN,
446             "Build: ",
447             __TIMESTAMP__,
448             GREEN_END);
449 }
450
451 void TestRunner::InvalidArgs(const std::string& message)
452 {
453     using namespace DPL::Colors::Text;
454     fprintf(stderr,
455             "%s%s%s\n",
456             BOLD_RED_BEGIN,
457             message.c_str(),
458             BOLD_RED_END);
459 }
460
461 void TestRunner::Usage()
462 {
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());
469     }
470     fprintf(stderr, "\n  example:\n");
471     fprintf(stderr,
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");
475     fprintf(stderr,
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");
487     fprintf(
488         stderr,
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(),
495                   m_collectors.end(),
496                   [] (const TestResultsCollectors::value_type & collector)
497                   {
498                       fprintf(stderr,
499                               "Output %s has specific args:\n",
500                               collector.first.c_str());
501                       fprintf(stderr,
502                               "%s\n",
503                               collector.second->
504                                   CollectorSpecificHelp().c_str());
505                   });
506     fprintf(stderr, "For bug reporting, please write to:\n");
507     fprintf(stderr, "<p.dobrowolsk@samsung.com>\n");
508 }
509
510 int TestRunner::ExecTestRunner(int argc, char *argv[])
511 {
512     std::vector<std::string> args;
513     for (int i = 0; i < argc; ++i) {
514         args.push_back(argv[i]);
515     }
516     return ExecTestRunner(args);
517 }
518
519 int TestRunner::ExecTestRunner(ArgsList args)
520 {
521     m_runIgnored = false;
522     // Parse command line
523
524     args.erase(args.begin());
525
526     bool showHelp = false;
527     bool justList = false;
528     std::vector<std::string> xmlFiles;
529
530     TestResultsCollectorBasePtr currentCollector;
531
532     // Parse each argument
533     for(std::string &arg : args)
534     {
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=";
546
547         if (currentCollector) {
548             if (currentCollector->ParseCollectorSpecificArg(arg)) {
549                 continue;
550             }
551         }
552
553         if (arg.find(startCmd) == 0) {
554             arg.erase(0, startCmd.length());
555             for (auto &group : m_testGroups) {
556                 for (auto &tc : group.second) {
557                     if (tc->GetName() == arg) {
558                         m_startTestId = arg;
559                         break;
560                     }
561                 }
562                 if (!m_startTestId.empty()) {
563                     break;
564                 }
565             }
566             if (!m_startTestId.empty()) {
567                 continue;
568             }
569             InvalidArgs();
570             fprintf(stderr, "Start test id has not been found\n");
571             Usage();
572             return 0;
573         } else if (arg.find(groupId) == 0) {
574             arg.erase(0, groupId.length());
575             TestCaseGroupMap::iterator found = m_testGroups.find(arg);
576             if (found != m_testGroups.end()) {
577                 std::string name = found->first;
578                 TestCaseList newList = found->second;
579                 m_testGroups.clear();
580                 m_testGroups[name] = newList;
581             } else {
582                 fprintf(stderr, "Group %s not found\n", arg.c_str());
583                 InvalidArgs();
584                 Usage();
585                 return -1;
586             }
587         } else if (arg == runIgnored) {
588             m_runIgnored = true;
589         } else if (arg == listCmd) {
590             justList = true;
591         } else if (arg == listGroupsCmd) {
592             for (auto &group : m_testGroups) {
593                 printf("GR:%s\n", group.first.c_str());
594             }
595             return 0;
596         } else if (arg.find(listInGroup) == 0) {
597             arg.erase(0, listInGroup.length());
598             for (auto &test : m_testGroups[arg]) {
599                 printf("ID:%s\n", test->GetName().c_str());
600             }
601             return 0;
602         } else if (arg.find(allowChildLogs) == 0) {
603             arg.erase(0, allowChildLogs.length());
604             m_allowChildLogs = true;
605         } else if (arg == "--help") {
606             showHelp = true;
607         } else if (arg.find(output) == 0) {
608             arg.erase(0, output.length());
609             if (m_collectors.find(arg) != m_collectors.end()) {
610                 InvalidArgs(
611                     "Multiple outputs of the same type are not supported!");
612                 Usage();
613                 return -1;
614             }
615             currentCollector.reset(TestResultsCollectorBase::Create(arg));
616             if (!currentCollector) {
617                 InvalidArgs("Unsupported output type!");
618                 Usage();
619                 return -1;
620             }
621             m_collectors[arg] = currentCollector;
622         } else if (arg.find(regexp) == 0) {
623             arg.erase(0, regexp.length());
624             if (arg.length() == 0) {
625                 InvalidArgs();
626                 Usage();
627                 return -1;
628             }
629
630             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
631                 arg.erase(0);
632                 arg.erase(arg.length() - 1);
633             }
634
635             if (arg.length() == 0) {
636                 InvalidArgs();
637                 Usage();
638                 return -1;
639             }
640
641             pcrecpp::RE re(arg.c_str());
642             for (auto &group : m_testGroups) {
643                 TestCaseList newList;
644                 for (auto &tc : group.second)
645                 {
646                     if (re.PartialMatch(tc->GetName())) {
647                         newList.push_back(tc);
648                     }
649                 }
650                 group.second = newList;
651             }
652         } else if (arg.find(test) == 0) {
653             arg.erase(0, test.length());
654             if (arg.length() == 0) {
655                 InvalidArgs();
656                 Usage();
657                 return -1;
658             }
659
660             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
661                 arg.erase(0);
662                 arg.erase(arg.length() - 1);
663             }
664
665             if (arg.length() == 0) {
666                 InvalidArgs();
667                 Usage();
668                 return -1;
669             }
670
671             pcrecpp::RE re(arg.c_str());
672             for (auto &group : m_testGroups) {
673                 TestCaseList newList;
674                 for (auto &tc : group.second)
675                 {
676                     if (re.FullMatch(tc->GetName())) {
677                         newList.push_back(tc);
678                     }
679                 }
680                 group.second = newList;
681             }
682         } else if(arg.find(onlyFromXML) == 0) {
683             arg.erase(0, onlyFromXML.length());
684             if (arg.length() == 0) {
685                 InvalidArgs();
686                 Usage();
687                 return -1;
688             }
689
690             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
691                 arg.erase(0);
692                 arg.erase(arg.length() - 1);
693             }
694
695             if (arg.length() == 0) {
696                 InvalidArgs();
697                 Usage();
698                 return -1;
699             }
700
701             xmlFiles.push_back(arg);
702         } else {
703             InvalidArgs();
704             Usage();
705             return -1;
706         }
707     }
708
709     if(!xmlFiles.empty())
710     {
711         if(!filterGroupsByXmls(xmlFiles))
712         {
713             fprintf(stderr, "XML file is not correct\n");
714             return 0;
715         }
716     }
717
718     if(justList)
719     {
720         for (auto &group : m_testGroups) {
721             for (auto &tc : group.second) {
722                 printf("ID:%s:%s\n", group.first.c_str(), tc->GetName().c_str());
723             }
724         }
725         return 0;
726     }
727
728     currentCollector.reset();
729
730     // Show help
731     if (showHelp) {
732         Usage();
733         return 0;
734     }
735
736     if (m_collectors.empty()) {
737         TestResultsCollectorBasePtr collector(
738             TestResultsCollectorBase::Create("text"));
739         m_collectors["text"] = collector;
740     }
741
742     for (auto &collector : m_collectors) {
743         if (!collector.second->Configure()) {
744             fprintf(stderr, "Could not configure selected output");
745             return 0;
746         }
747     }
748
749     // Run tests
750     RunTests();
751
752     return 0;
753 }
754
755 void TestRunner::Terminate()
756 {
757     m_terminate = true;
758 }
759
760 bool TestRunner::GetAllowChildLogs()
761 {
762     return m_allowChildLogs;
763 }
764
765 void TestRunner::deferFailedException(const DPL::Test::TestFailed &ex)
766 {
767     if (m_deferDeepness <= 0)
768         throw ex;
769
770     if (m_deferredExceptionsMessages.empty()) {
771         m_firstDeferredFail = ex;
772         m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_FAILED;
773     }
774     m_deferredExceptionsMessages.push_back(ex.GetMessage());
775 }
776
777 void TestRunner::deferIgnoredException(const DPL::Test::TestIgnored &ex)
778 {
779     if (m_deferDeepness <= 0)
780         throw ex;
781
782     if (m_deferredExceptionsMessages.empty()) {
783         m_firstDeferredIgnore = ex;
784         m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_IGNORED;
785     }
786     m_deferredExceptionsMessages.push_back(ex.GetMessage());
787 }
788
789 void TestRunner::deferBegin()
790 {
791     m_deferDeepness++;
792 }
793
794 void TestRunner::deferEnd()
795 {
796     if (m_deferDeepness > 0)
797         m_deferDeepness--;
798
799     if (m_deferDeepness > 0)
800         return;
801
802     for (size_t i = 0; i < m_deferredExceptionsMessages.size(); ++i) {
803         addFailReason(m_deferredExceptionsMessages[i]);
804     }
805
806     if (!m_deferredExceptionsMessages.empty())
807     {
808         m_deferredExceptionsMessages.clear();
809         switch (m_firstDeferredExceptionType) {
810             case DeferredExceptionType::DEFERRED_FAILED:
811                 throw m_firstDeferredFail;
812             case DeferredExceptionType::DEFERRED_IGNORED:
813                 throw m_firstDeferredIgnore;
814         }
815     }
816     m_deferredExceptionsMessages.clear();
817 }
818
819 } // namespace Test
820 } // namespace DPL