Fix T9050_yaca_rsa_encryption_paddings test
[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         case TestResult::FailStatus::NONE:
278             if (!cleanupReason.empty()) {
279                 if (!testReason.empty())
280                     testReason += "\n";
281                 testReason += cleanupReason;
282                 if (testStatus == TestResult::FailStatus::NONE)
283                     testStatus = TestResult::FailStatus::FAILED;
284             }
285             break;
286         default:
287             Assert(false && "Unhandled fail status");
288     }
289
290     CollectResult(testCase->GetName(),
291                   TestResult(testStatus, testReason, testCase->GetPerformance()));
292     setCurrentTestCase(nullptr);
293 }
294
295 void TestRunner::RunTests()
296 {
297     using namespace DPL::Colors::Text;
298
299     Banner();
300     for (auto &collector : m_collectors) {
301         collector.second->Start();
302     }
303
304     unsigned count = 0;
305     for (auto &group : m_testGroups) {
306         count += group.second->GetTests().size();
307     }
308     fprintf(stderr, "%sFound %d testcases...%s\n", GREEN_BEGIN, count, GREEN_END);
309     fprintf(stderr, "%s%s%s\n", GREEN_BEGIN, "Running tests...", GREEN_END);
310     for (auto &group : m_testGroups) {
311         const TestCaseSet& set = group.second->GetTests();
312         if (!set.empty()) {
313             group.second->Init();
314
315             for (auto &collector : m_collectors) {
316                 collector.second->CollectCurrentTestGroupName(group.first);
317             }
318
319             for (TestCaseSet::const_iterator iterator = set.begin();
320                  iterator != set.end();
321                  ++iterator)
322             {
323                 TestCasePtr test = *iterator;
324                 if (m_startTestId == test->GetName()) {
325                     m_startTestId = "";
326                 }
327
328                 if (m_startTestId.empty()) {
329                     RunTestCase(test);
330                 }
331                 if (m_terminate == true) {
332                     // Terminate quietly without any logs
333                     return;
334                 }
335             }
336             group.second->Finish();
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 &g : m_testGroups)
419         delete g.second;
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->GetTests()) {
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             for (auto it = m_testGroups.begin(); it != m_testGroups.end();) {
576                 if (it->first == arg)
577                     it++;
578                 else
579                     it = m_testGroups.erase(it);
580             }
581
582             if (m_testGroups.empty()) {
583                 fprintf(stderr, "Group %s not found\n", arg.c_str());
584                 InvalidArgs();
585                 Usage();
586                 return -1;
587             }
588         } else if (arg == runIgnored) {
589             m_runIgnored = true;
590         } else if (arg == listCmd) {
591             justList = true;
592         } else if (arg == listGroupsCmd) {
593             for (auto &group : m_testGroups) {
594                 printf("GR:%s\n", group.first.c_str());
595             }
596             return 0;
597         } else if (arg.find(listInGroup) == 0) {
598             arg.erase(0, listInGroup.length());
599             auto it = m_testGroups.find(arg);
600             if (it == m_testGroups.end()) {
601                 fprintf(stderr, "Group %s not found\n", arg.c_str());
602                 InvalidArgs();
603                 Usage();
604                 return -1;
605             }
606             for (auto &test : it->second->GetTests()) {
607                 printf("ID:%s\n", test->GetName().c_str());
608             }
609             return 0;
610         } else if (arg.find(allowChildLogs) == 0) {
611             arg.erase(0, allowChildLogs.length());
612             m_allowChildLogs = true;
613         } else if (arg == "--help") {
614             showHelp = true;
615         } else if (arg.find(output) == 0) {
616             arg.erase(0, output.length());
617             if (m_collectors.find(arg) != m_collectors.end()) {
618                 InvalidArgs(
619                     "Multiple outputs of the same type are not supported!");
620                 Usage();
621                 return -1;
622             }
623             currentCollector.reset(TestResultsCollectorBase::Create(arg));
624             if (!currentCollector) {
625                 InvalidArgs("Unsupported output type!");
626                 Usage();
627                 return -1;
628             }
629             m_collectors[arg] = currentCollector;
630         } else if (arg.find(regexp) == 0) {
631             arg.erase(0, regexp.length());
632             if (arg.length() == 0) {
633                 InvalidArgs();
634                 Usage();
635                 return -1;
636             }
637
638             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
639                 arg.erase(0);
640                 arg.erase(arg.length() - 1);
641             }
642
643             if (arg.length() == 0) {
644                 InvalidArgs();
645                 Usage();
646                 return -1;
647             }
648
649             pcrecpp::RE re(arg.c_str());
650             for (auto &group : m_testGroups) {
651                 group.second->RemoveIf([&](const TestCasePtr& test){
652                     return !re.PartialMatch(test->GetName());
653                 });
654             }
655         } else if (arg.find(test) == 0) {
656             arg.erase(0, test.length());
657             if (arg.length() == 0) {
658                 InvalidArgs();
659                 Usage();
660                 return -1;
661             }
662
663             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
664                 arg.erase(0);
665                 arg.erase(arg.length() - 1);
666             }
667
668             if (arg.length() == 0) {
669                 InvalidArgs();
670                 Usage();
671                 return -1;
672             }
673
674             pcrecpp::RE re(arg.c_str());
675             for (auto &group : m_testGroups) {
676                 group.second->RemoveIf([&](const TestCasePtr& test){
677                     return !re.FullMatch(test->GetName());
678                 });
679             }
680         } else if(arg.find(onlyFromXML) == 0) {
681             arg.erase(0, onlyFromXML.length());
682             if (arg.length() == 0) {
683                 InvalidArgs();
684                 Usage();
685                 return -1;
686             }
687
688             if (arg[0] == '\'' && arg[arg.length() - 1] == '\'') {
689                 arg.erase(0);
690                 arg.erase(arg.length() - 1);
691             }
692
693             if (arg.length() == 0) {
694                 InvalidArgs();
695                 Usage();
696                 return -1;
697             }
698
699             xmlFiles.push_back(arg);
700         } else {
701             InvalidArgs();
702             Usage();
703             return -1;
704         }
705     }
706
707     if(!xmlFiles.empty())
708     {
709         if(!filterGroupsByXmls(xmlFiles))
710         {
711             fprintf(stderr, "XML file is not correct\n");
712             return 0;
713         }
714     }
715
716     if(justList)
717     {
718         for (auto &group : m_testGroups) {
719             for (auto &tc : group.second->GetTests()) {
720                 printf("ID:%s:%s\n", group.first.c_str(), tc->GetName().c_str());
721             }
722         }
723         return 0;
724     }
725
726     currentCollector.reset();
727
728     // Show help
729     if (showHelp) {
730         Usage();
731         return 0;
732     }
733
734     if (m_collectors.empty()) {
735         TestResultsCollectorBasePtr collector(
736             TestResultsCollectorBase::Create("text"));
737         m_collectors["text"] = collector;
738     }
739
740     for (auto &collector : m_collectors) {
741         if (!collector.second->Configure()) {
742             fprintf(stderr, "Could not configure selected output");
743             return 0;
744         }
745     }
746
747     // Run tests
748     RunTests();
749
750     return 0;
751 }
752
753 void TestRunner::Terminate()
754 {
755     m_terminate = true;
756 }
757
758 bool TestRunner::GetAllowChildLogs()
759 {
760     return m_allowChildLogs;
761 }
762
763 bool TestRunner::GetRunIgnored() const
764 {
765     return m_runIgnored;
766 }
767
768 void TestRunner::deferFailedException(const DPL::Test::TestFailed &ex)
769 {
770     if (m_deferDeepness <= 0)
771         throw ex;
772
773     if (m_deferredExceptionsMessages.empty()) {
774         m_firstDeferredFail = ex;
775         m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_FAILED;
776     }
777     m_deferredExceptionsMessages.push_back(ex.GetMessage());
778 }
779
780 void TestRunner::deferIgnoredException(const DPL::Test::TestIgnored &ex)
781 {
782     if (m_deferDeepness <= 0)
783         throw ex;
784
785     if (m_deferredExceptionsMessages.empty()) {
786         m_firstDeferredIgnore = ex;
787         m_firstDeferredExceptionType = DeferredExceptionType::DEFERRED_IGNORED;
788     }
789     m_deferredExceptionsMessages.push_back(ex.GetMessage());
790 }
791
792 void TestRunner::deferBegin()
793 {
794     m_deferDeepness++;
795 }
796
797 void TestRunner::deferEnd()
798 {
799     if (m_deferDeepness > 0)
800         m_deferDeepness--;
801
802     if (m_deferDeepness > 0)
803         return;
804
805     for (size_t i = 0; i < m_deferredExceptionsMessages.size(); ++i) {
806         addFailReason(m_deferredExceptionsMessages[i]);
807     }
808
809     if (!m_deferredExceptionsMessages.empty())
810     {
811         m_deferredExceptionsMessages.clear();
812         switch (m_firstDeferredExceptionType) {
813             case DeferredExceptionType::DEFERRED_FAILED:
814                 throw m_firstDeferredFail;
815             case DeferredExceptionType::DEFERRED_IGNORED:
816                 throw m_firstDeferredIgnore;
817         }
818     }
819     m_deferredExceptionsMessages.clear();
820 }
821
822 } // namespace Test
823 } // namespace DPL