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