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