Refactor SignatureValidator and reduce interface headers
[platform/core/security/cert-svc.git] / vcore / src / dpl / test / src / test_results_collector.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file        test_results_collector.h
18  * @author      Lukasz Wrzosek (l.wrzosek@samsung.com)
19  * @version     1.0
20  * @brief       Implementation file some concrete TestResulstsCollector
21  */
22 #include <cstddef>
23 #include <dpl/test/test_results_collector.h>
24 #include <dpl/colors.h>
25 #include <dpl/assert.h>
26 #include <dpl/foreach.h>
27 #include <dpl/scoped_fclose.h>
28 #include <dpl/exception.h>
29 #include <dpl/errno_string.h>
30 #include <dpl/lexical_cast.h>
31 #include <dpl/availability.h>
32
33 #include <string>
34 #include <string.h>
35 #include <cstdio>
36 #include <fstream>
37 #include <sstream>
38 #include <cstdlib>
39
40 #define GREEN_RESULT_OK "[%s%s%s]\n", BOLD_GREEN_BEGIN, "   OK   ", \
41     BOLD_GREEN_END
42
43 namespace VcoreDPL {
44 namespace Test {
45 namespace {
46 const char *DEFAULT_HTML_FILE_NAME = "index.html";
47 const char *DEFAULT_TAP_FILE_NAME = "results.tap";
48 const char *DEFAULT_XML_FILE_NAME = "results.xml";
49
50 bool ParseCollectorFileArg(const std::string &arg, std::string &filename)
51 {
52     const std::string argname = "--file=";
53     if (arg.find(argname) == 0 ) {
54         filename = arg.substr(argname.size());
55         return true;
56     }
57     return false;
58 }
59
60 class Statistic
61 {
62   public:
63     Statistic() :
64         m_failed(0),
65         m_ignored(0),
66         m_passed(0),
67         m_count(0)
68     {}
69
70     void AddTest(TestResultsCollectorBase::FailStatus::Type type)
71     {
72         ++m_count;
73         switch (type) {
74         case TestResultsCollectorBase::FailStatus::INTERNAL:
75         case TestResultsCollectorBase::FailStatus::FAILED:   ++m_failed;
76             break;
77         case TestResultsCollectorBase::FailStatus::IGNORED:  ++m_ignored;
78             break;
79         case TestResultsCollectorBase::FailStatus::NONE:     ++m_passed;
80             break;
81         default:
82             Assert(false && "Bad FailStatus");
83         }
84     }
85
86     std::size_t GetTotal() const
87     {
88         return m_count;
89     }
90     std::size_t GetPassed() const
91     {
92         return m_passed;
93     }
94     std::size_t GetSuccesed() const
95     {
96         return m_passed;
97     }
98     std::size_t GetFailed() const
99     {
100         return m_failed;
101     }
102     std::size_t GetIgnored() const
103     {
104         return m_ignored;
105     }
106     float GetPassedOrIgnoredPercend() const
107     {
108         float passIgnoredPercent =
109             100.0f * (static_cast<float>(m_passed)
110                       + static_cast<float>(m_ignored))
111             / static_cast<float>(m_count);
112         return passIgnoredPercent;
113     }
114
115   private:
116     std::size_t m_failed;
117     std::size_t m_ignored;
118     std::size_t m_passed;
119     std::size_t m_count;
120 };
121
122 class ConsoleCollector :
123     public TestResultsCollectorBase
124 {
125   public:
126     static TestResultsCollectorBase* Constructor();
127
128   private:
129     ConsoleCollector() {}
130
131     virtual void CollectCurrentTestGroupName(const std::string& name)
132     {
133         printf("Starting group %s\n", name.c_str());
134         m_currentGroup = name;
135     }
136
137     virtual void Finish()
138     {
139         using namespace VcoreDPL::Colors::Text;
140
141         // Show result
142         FOREACH(group, m_groupsStats) {
143             PrintStats(group->first, group->second);
144         }
145         PrintStats("All tests together", m_stats);
146     }
147
148     virtual void CollectResult(const std::string& id,
149                                const std::string& /*description*/,
150                                const FailStatus::Type status = FailStatus::NONE,
151                                const std::string& reason = "")
152     {
153         using namespace VcoreDPL::Colors::Text;
154         std::string tmp = "'" + id + "' ...";
155
156         printf("Running test case %-60s", tmp.c_str());
157         switch (status) {
158         case TestResultsCollectorBase::FailStatus::NONE:
159             printf(GREEN_RESULT_OK);
160             break;
161         case TestResultsCollectorBase::FailStatus::FAILED:
162             PrintfErrorMessage(" FAILED ", reason, true);
163             break;
164         case TestResultsCollectorBase::FailStatus::IGNORED:
165             PrintfIgnoredMessage("Ignored ", reason, true);
166             break;
167         case TestResultsCollectorBase::FailStatus::INTERNAL:
168             PrintfErrorMessage("INTERNAL", reason, true);
169             break;
170         default:
171             Assert(false && "Bad status");
172         }
173         m_stats.AddTest(status);
174         m_groupsStats[m_currentGroup].AddTest(status);
175     }
176
177     void PrintfErrorMessage(const char* type,
178                             const std::string& message,
179                             bool verbosity)
180     {
181         using namespace VcoreDPL::Colors::Text;
182         if (verbosity) {
183             printf("[%s%s%s] %s%s%s\n",
184                    BOLD_RED_BEGIN,
185                    type,
186                    BOLD_RED_END,
187                    BOLD_YELLOW_BEGIN,
188                    message.c_str(),
189                    BOLD_YELLOW_END);
190         } else {
191             printf("[%s%s%s]\n",
192                    BOLD_RED_BEGIN,
193                    type,
194                    BOLD_RED_END);
195         }
196     }
197
198     void PrintfIgnoredMessage(const char* type,
199                               const std::string& message,
200                               bool verbosity)
201     {
202         using namespace VcoreDPL::Colors::Text;
203         if (verbosity) {
204             printf("[%s%s%s] %s%s%s\n",
205                    CYAN_BEGIN,
206                    type,
207                    CYAN_END,
208                    BOLD_GOLD_BEGIN,
209                    message.c_str(),
210                    BOLD_GOLD_END);
211         } else {
212             printf("[%s%s%s]\n",
213                    CYAN_BEGIN,
214                    type,
215                    CYAN_END);
216         }
217     }
218
219     void PrintStats(const std::string& title, const Statistic& stats)
220     {
221         using namespace VcoreDPL::Colors::Text;
222         printf("\n%sResults [%s]: %s\n", BOLD_GREEN_BEGIN,
223                title.c_str(), BOLD_GREEN_END);
224         printf("%s%s%3d%s\n",
225                CYAN_BEGIN,
226                "Total tests:            ",
227                stats.GetTotal(),
228                CYAN_END);
229         printf("  %s%s%3d%s\n",
230                CYAN_BEGIN,
231                "Succeeded:            ",
232                stats.GetPassed(),
233                CYAN_END);
234         printf("  %s%s%3d%s\n",
235                CYAN_BEGIN,
236                "Failed:               ",
237                stats.GetFailed(),
238                CYAN_END);
239         printf("  %s%s%3d%s\n",
240                CYAN_BEGIN,
241                "Ignored:              ",
242                stats.GetIgnored(),
243                CYAN_END);
244     }
245
246     Statistic m_stats;
247     std::map<std::string, Statistic> m_groupsStats;
248     std::string m_currentGroup;
249 };
250
251 TestResultsCollectorBase* ConsoleCollector::Constructor()
252 {
253     return new ConsoleCollector();
254 }
255
256 class HtmlCollector :
257     public TestResultsCollectorBase
258 {
259   public:
260     static TestResultsCollectorBase* Constructor();
261
262   private:
263     HtmlCollector() : m_filename(DEFAULT_HTML_FILE_NAME) {}
264
265     virtual void CollectCurrentTestGroupName(const std::string& name)
266     {
267         fprintf(m_fp.Get(), "<b>Starting group %s", name.c_str());
268         m_currentGroup = name;
269     }
270
271     virtual bool Configure()
272     {
273         m_fp.Reset(fopen(m_filename.c_str(), "w"));
274         if (!m_fp)
275             return false;
276
277         return true;
278     }
279     virtual std::string CollectorSpecificHelp() const
280     {
281         return "--file=<filename> - name of file for output\n"
282                "                    default - index.html\n";
283     }
284
285     virtual void Start(int count)
286     {
287         DPL_UNUSED_PARAM(count);
288         AssertMsg(!!m_fp, "File handle must not be null");
289         fprintf(m_fp.Get(),
290                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0"
291                 "Transitional//EN\" "
292                 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
293                 ">\n");
294         fprintf(m_fp.Get(),
295                 "<html xmlns=\"http://www.w3.org/1999/xhtml\" "
296                 "lang=\"en\" dir=\"ltr\">\n");
297         fprintf(m_fp.Get(), "<body style=\"background-color: black;\">\n");
298         fprintf(m_fp.Get(), "<pre>\n");
299         fprintf(m_fp.Get(), "<font color=\"white\">\n");
300     }
301
302     virtual void Finish()
303     {
304         using namespace VcoreDPL::Colors::Html;
305         // Show result
306         FOREACH(group, m_groupsStats) {
307             PrintStats(group->first, group->second);
308         }
309         PrintStats("All tests together", m_stats);
310         fprintf(m_fp.Get(), "</font>\n");
311         fprintf(m_fp.Get(), "</pre>\n");
312         fprintf(m_fp.Get(), "</body>\n");
313         fprintf(m_fp.Get(), "</html>\n");
314     }
315
316     virtual bool ParseCollectorSpecificArg(const std::string& arg)
317     {
318         return ParseCollectorFileArg(arg, m_filename);
319     }
320
321     virtual void CollectResult(const std::string& id,
322                                const std::string& /*description*/,
323                                const FailStatus::Type status = FailStatus::NONE,
324                                const std::string& reason = "")
325     {
326         using namespace VcoreDPL::Colors::Html;
327         std::string tmp = "'" + id + "' ...";
328
329         fprintf(m_fp.Get(), "Running test case %-100s", tmp.c_str());
330         switch (status) {
331         case TestResultsCollectorBase::FailStatus::NONE:
332             fprintf(m_fp.Get(), GREEN_RESULT_OK);
333             break;
334         case TestResultsCollectorBase::FailStatus::FAILED:
335             PrintfErrorMessage(" FAILED ", reason, true);
336             break;
337         case TestResultsCollectorBase::FailStatus::IGNORED:
338             PrintfIgnoredMessage("Ignored ", reason, true);
339             break;
340         case TestResultsCollectorBase::FailStatus::INTERNAL:
341             PrintfErrorMessage("INTERNAL", reason, true);
342             break;
343         default:
344             Assert(false && "Bad status");
345         }
346         m_groupsStats[m_currentGroup].AddTest(status);
347         m_stats.AddTest(status);
348     }
349
350     void PrintfErrorMessage(const char* type,
351                             const std::string& message,
352                             bool verbosity)
353     {
354         using namespace VcoreDPL::Colors::Html;
355         if (verbosity) {
356             fprintf(m_fp.Get(),
357                     "[%s%s%s] %s%s%s\n",
358                     BOLD_RED_BEGIN,
359                     type,
360                     BOLD_RED_END,
361                     BOLD_YELLOW_BEGIN,
362                     message.c_str(),
363                     BOLD_YELLOW_END);
364         } else {
365             fprintf(m_fp.Get(),
366                     "[%s%s%s]\n",
367                     BOLD_RED_BEGIN,
368                     type,
369                     BOLD_RED_END);
370         }
371     }
372
373     void PrintfIgnoredMessage(const char* type,
374                               const std::string& message,
375                               bool verbosity)
376     {
377         using namespace VcoreDPL::Colors::Html;
378
379         if (verbosity) {
380             fprintf(m_fp.Get(),
381                     "[%s%s%s] %s%s%s\n",
382                     CYAN_BEGIN,
383                     type,
384                     CYAN_END,
385                     BOLD_GOLD_BEGIN,
386                     message.c_str(),
387                     BOLD_GOLD_END);
388         } else {
389             fprintf(m_fp.Get(),
390                     "[%s%s%s]\n",
391                     CYAN_BEGIN,
392                     type,
393                     CYAN_END);
394         }
395     }
396
397     void PrintStats(const std::string& name, const Statistic& stats)
398     {
399         using namespace VcoreDPL::Colors::Html;
400         fprintf(
401             m_fp.Get(), "\n%sResults [%s]:%s\n", BOLD_GREEN_BEGIN,
402             name.c_str(), BOLD_GREEN_END);
403         fprintf(
404             m_fp.Get(), "%s%s%3d%s\n", CYAN_BEGIN,
405             "Total tests:            ", stats.GetTotal(), CYAN_END);
406         fprintf(
407             m_fp.Get(), "  %s%s%3d%s\n", CYAN_BEGIN,
408             "Succeeded:            ", stats.GetPassed(), CYAN_END);
409         fprintf(
410             m_fp.Get(), "  %s%s%3d%s\n", CYAN_BEGIN,
411             "Failed:               ", stats.GetFailed(), CYAN_END);
412         fprintf(
413             m_fp.Get(), "  %s%s%3d%s\n", CYAN_BEGIN,
414             "Ignored:              ", stats.GetIgnored(), CYAN_END);
415     }
416
417     std::string m_filename;
418     ScopedFClose m_fp;
419     Statistic m_stats;
420     std::string m_currentGroup;
421     std::map<std::string, Statistic> m_groupsStats;
422 };
423
424 TestResultsCollectorBase* HtmlCollector::Constructor()
425 {
426     return new HtmlCollector();
427 }
428
429 class XmlCollector :
430     public TestResultsCollectorBase
431 {
432   public:
433     static TestResultsCollectorBase* Constructor();
434
435   private:
436     XmlCollector() : m_filename(DEFAULT_XML_FILE_NAME) {}
437
438     virtual void CollectCurrentTestGroupName(const std::string& name)
439     {
440         std::size_t pos = GetCurrentGroupPosition();
441         if (std::string::npos != pos) {
442             GroupFinish(pos);
443             FlushOutput();
444             m_stats = Statistic();
445         }
446
447         pos = m_outputBuffer.find("</testsuites>");
448         if (std::string::npos == pos) {
449             ThrowMsg(VcoreDPL::Exception, "Could not find test suites closing tag");
450         }
451         GroupStart(pos, name);
452     }
453
454     void GroupStart(const std::size_t pos, const std::string& name)
455     {
456         std::stringstream groupHeader;
457         groupHeader << "\n\t<testsuite";
458         groupHeader << " name=\"" << EscapeSpecialCharacters(name) << "\"";
459         groupHeader << R"( tests="1")"; // include SegFault
460         groupHeader << R"( failures="1")"; // include SegFault
461         groupHeader << R"( skipped="0")";
462         groupHeader << ">";
463
464         groupHeader << "\n\t\t<testcase name=\"unknown\" status=\"FAILED\">";
465         groupHeader <<
466         "\n\t\t\t<failure type=\"FAILED\" message=\"segmentation fault\"/>";
467         groupHeader << "\n\t\t</testcase>";
468
469         groupHeader << "\n\t</testsuite>";
470
471         m_outputBuffer.insert(pos - 1, groupHeader.str());
472     }
473
474     virtual bool Configure()
475     {
476         m_fp.Reset(fopen(m_filename.c_str(), "w"));
477         if (!m_fp)
478             return false;
479
480         return true;
481     }
482
483     virtual std::string CollectorSpecificHelp() const
484     {
485         return "--file=<filename> - name of file for output\n"
486                "                    default - results.xml\n";
487     }
488
489     virtual void Start(int count)
490     {
491         AssertMsg(!!m_fp, "File handle must not be null");
492         m_outputBuffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
493         m_outputBuffer.append("<testsuites ");
494         if(count >= 0)
495         {
496             m_outputBuffer.append("total=\"");
497             m_outputBuffer.append(VcoreDPL::lexical_cast<std::string>(count));
498             m_outputBuffer.append("\"");
499         }
500         m_outputBuffer.append(" >\n</testsuites>");
501         FlushOutput();
502     }
503
504     virtual void Finish()
505     {
506         std::size_t pos = GetCurrentGroupPosition();
507         if (std::string::npos != pos) {
508             GroupFinish(pos);
509             FlushOutput();
510         }
511     }
512
513     virtual bool ParseCollectorSpecificArg(const std::string& arg)
514     {
515         return ParseCollectorFileArg(arg, m_filename);
516     }
517
518     virtual void CollectResult(const std::string& id,
519                                const std::string& /*description*/,
520                                const FailStatus::Type status = FailStatus::NONE,
521                                const std::string& reason = "")
522     {
523         m_resultBuffer.erase();
524         m_resultBuffer.append("\t\t<testcase name=\"");
525         m_resultBuffer.append(EscapeSpecialCharacters(id));
526         m_resultBuffer.append("\"");
527         switch (status) {
528         case TestResultsCollectorBase::FailStatus::NONE:
529             m_resultBuffer.append(" status=\"OK\"/>\n");
530             break;
531         case TestResultsCollectorBase::FailStatus::FAILED:
532             m_resultBuffer.append(" status=\"FAILED\">\n");
533             PrintfErrorMessage("FAILED", EscapeSpecialCharacters(reason), true);
534             m_resultBuffer.append("\t\t</testcase>\n");
535             break;
536         case TestResultsCollectorBase::FailStatus::IGNORED:
537             m_resultBuffer.append(" status=\"Ignored\">\n");
538             PrintfIgnoredMessage("Ignored", EscapeSpecialCharacters(
539                                      reason), true);
540             m_resultBuffer.append("\t\t</testcase>\n");
541             break;
542         case TestResultsCollectorBase::FailStatus::INTERNAL:
543             m_resultBuffer.append(" status=\"FAILED\">\n");
544             PrintfErrorMessage("INTERNAL", EscapeSpecialCharacters(
545                                    reason), true);
546             m_resultBuffer.append("\t\t</testcase>");
547             break;
548         default:
549             Assert(false && "Bad status");
550         }
551         std::size_t group_pos = GetCurrentGroupPosition();
552         if (std::string::npos == group_pos) {
553             ThrowMsg(VcoreDPL::Exception, "No current group set");
554         }
555
556         std::size_t last_case_pos = m_outputBuffer.find(
557                 "<testcase name=\"unknown\"",
558                 group_pos);
559         if (std::string::npos == last_case_pos) {
560             ThrowMsg(VcoreDPL::Exception, "Could not find SegFault test case");
561         }
562         m_outputBuffer.insert(last_case_pos - 2, m_resultBuffer);
563
564         m_stats.AddTest(status);
565
566         UpdateGroupHeader(group_pos,
567                           m_stats.GetTotal() + 1, // include SegFault
568                           m_stats.GetFailed() + 1, // include SegFault
569                           m_stats.GetIgnored());
570         FlushOutput();
571     }
572
573     std::size_t GetCurrentGroupPosition() const
574     {
575         return m_outputBuffer.rfind("<testsuite ");
576     }
577
578     void UpdateGroupHeader(const std::size_t groupPosition,
579                            const unsigned int tests,
580                            const unsigned int failures,
581                            const unsigned int skipped)
582     {
583         UpdateElementAttribute(groupPosition, "tests", UIntToString(tests));
584         UpdateElementAttribute(groupPosition, "failures", UIntToString(failures));
585         UpdateElementAttribute(groupPosition, "skipped", UIntToString(skipped));
586     }
587
588     void UpdateElementAttribute(const std::size_t elementPosition,
589                                 const std::string& name,
590                                 const std::string& value)
591     {
592         std::string pattern = name + "=\"";
593
594         std::size_t start = m_outputBuffer.find(pattern, elementPosition);
595         if (std::string::npos == start) {
596             ThrowMsg(VcoreDPL::Exception,
597                      "Could not find attribute " << name << " beginning");
598         }
599
600         std::size_t end = m_outputBuffer.find("\"", start + pattern.length());
601         if (std::string::npos == end) {
602             ThrowMsg(VcoreDPL::Exception,
603                      "Could not find attribute " << name << " end");
604         }
605
606         m_outputBuffer.replace(start + pattern.length(),
607                                end - start - pattern.length(),
608                                value);
609     }
610
611     std::string UIntToString(const unsigned int value)
612     {
613         std::stringstream result;
614         result << value;
615         return result.str();
616     }
617
618     void GroupFinish(const std::size_t groupPosition)
619     {
620         std::size_t segFaultStart =
621             m_outputBuffer.find("<testcase name=\"unknown\"", groupPosition);
622         if (std::string::npos == segFaultStart) {
623             ThrowMsg(VcoreDPL::Exception,
624                      "Could not find SegFault test case start position");
625         }
626         segFaultStart -= 2; // to erase tabs
627
628         std::string closeTag = "</testcase>";
629         std::size_t segFaultEnd = m_outputBuffer.find(closeTag, segFaultStart);
630         if (std::string::npos == segFaultEnd) {
631             ThrowMsg(VcoreDPL::Exception,
632                      "Could not find SegFault test case end position");
633         }
634         segFaultEnd += closeTag.length() + 1; // to erase new line
635
636         m_outputBuffer.erase(segFaultStart, segFaultEnd - segFaultStart);
637
638         UpdateGroupHeader(groupPosition,
639                           m_stats.GetTotal(),
640                           m_stats.GetFailed(),
641                           m_stats.GetIgnored());
642     }
643
644     void FlushOutput()
645     {
646         int fd = fileno(m_fp.Get());
647         if (-1 == fd) {
648             int error = errno;
649             ThrowMsg(VcoreDPL::Exception, VcoreDPL::GetErrnoString(error));
650         }
651
652         if (-1 == TEMP_FAILURE_RETRY(ftruncate(fd, 0L))) {
653             int error = errno;
654             ThrowMsg(VcoreDPL::Exception, VcoreDPL::GetErrnoString(error));
655         }
656
657         if (-1 == TEMP_FAILURE_RETRY(fseek(m_fp.Get(), 0L, SEEK_SET))) {
658             int error = errno;
659             ThrowMsg(VcoreDPL::Exception, VcoreDPL::GetErrnoString(error));
660         }
661
662         if (m_outputBuffer.size() !=
663             fwrite(m_outputBuffer.c_str(), 1, m_outputBuffer.size(),
664                    m_fp.Get()))
665         {
666             int error = errno;
667             ThrowMsg(VcoreDPL::Exception, VcoreDPL::GetErrnoString(error));
668         }
669
670         if (-1 == TEMP_FAILURE_RETRY(fflush(m_fp.Get()))) {
671             int error = errno;
672             ThrowMsg(VcoreDPL::Exception, VcoreDPL::GetErrnoString(error));
673         }
674     }
675
676     void PrintfErrorMessage(const char* type,
677                             const std::string& message,
678                             bool verbosity)
679     {
680         if (verbosity) {
681             m_resultBuffer.append("\t\t\t<failure type=\"");
682             m_resultBuffer.append(EscapeSpecialCharacters(type));
683             m_resultBuffer.append("\" message=\"");
684             m_resultBuffer.append(EscapeSpecialCharacters(message));
685             m_resultBuffer.append("\"/>\n");
686         } else {
687             m_resultBuffer.append("\t\t\t<failure type=\"");
688             m_resultBuffer.append(EscapeSpecialCharacters(type));
689             m_resultBuffer.append("\"/>\n");
690         }
691     }
692
693     void PrintfIgnoredMessage(const char* type,
694                               const std::string& message,
695                               bool verbosity)
696     {
697         if (verbosity) {
698             m_resultBuffer.append("\t\t\t<skipped type=\"");
699             m_resultBuffer.append(EscapeSpecialCharacters(type));
700             m_resultBuffer.append("\" message=\"");
701             m_resultBuffer.append(EscapeSpecialCharacters(message));
702             m_resultBuffer.append("\"/>\n");
703         } else {
704             m_resultBuffer.append("\t\t\t<skipped type=\"");
705             m_resultBuffer.append(EscapeSpecialCharacters(type));
706             m_resultBuffer.append("\"/>\n");
707         }
708     }
709
710     std::string EscapeSpecialCharacters(std::string s)
711     {
712         for (unsigned int i = 0; i < s.size();) {
713             switch (s[i]) {
714             case '"':
715                 s.erase(i, 1);
716                 s.insert(i, "&quot;");
717                 i += 6;
718                 break;
719
720             case '&':
721                 s.erase(i, 1);
722                 s.insert(i, "&amp;");
723                 i += 5;
724                 break;
725
726             case '<':
727                 s.erase(i, 1);
728                 s.insert(i, "&lt;");
729                 i += 4;
730                 break;
731
732             case '>':
733                 s.erase(i, 1);
734                 s.insert(i, "&gt;");
735                 i += 4;
736                 break;
737
738             case '\'':
739                 s.erase(i, 1);
740                 s.insert(i, "&#39;");
741                 i += 5;
742                 break;
743             default:
744                 ++i;
745                 break;
746             }
747         }
748         return s;
749     }
750
751     std::string m_filename;
752     ScopedFClose m_fp;
753     Statistic m_stats;
754     std::string m_outputBuffer;
755     std::string m_resultBuffer;
756 };
757
758 TestResultsCollectorBase* XmlCollector::Constructor()
759 {
760     return new XmlCollector();
761 }
762
763 class CSVCollector :
764     public TestResultsCollectorBase
765 {
766   public:
767     static TestResultsCollectorBase* Constructor();
768
769   private:
770     CSVCollector() {}
771
772     virtual void Start(int count)
773     {
774         DPL_UNUSED_PARAM(count);
775         printf("GROUP;ID;RESULT;REASON\n");
776     }
777
778     virtual void CollectCurrentTestGroupName(const std::string& name)
779     {
780         m_currentGroup = name;
781     }
782
783     virtual void CollectResult(const std::string& id,
784                                const std::string& /*description*/,
785                                const FailStatus::Type status = FailStatus::NONE,
786                                const std::string& reason = "")
787     {
788         std::string statusMsg = "";
789         switch (status) {
790         case TestResultsCollectorBase::FailStatus::NONE: statusMsg = "OK";
791             break;
792         case TestResultsCollectorBase::FailStatus::FAILED: statusMsg = "FAILED";
793             break;
794         case TestResultsCollectorBase::FailStatus::IGNORED: statusMsg =
795             "IGNORED";
796             break;
797         case TestResultsCollectorBase::FailStatus::INTERNAL: statusMsg =
798             "FAILED";
799             break;
800         default:
801             Assert(false && "Bad status");
802         }
803         printf("%s;%s;%s;%s\n",
804                m_currentGroup.c_str(),
805                id.c_str(),
806                statusMsg.c_str(),
807                reason.c_str());
808     }
809
810     std::string m_currentGroup;
811 };
812
813 TestResultsCollectorBase* CSVCollector::Constructor()
814 {
815     return new CSVCollector();
816 }
817 }
818
819 class TAPCollector :
820     public TestResultsCollectorBase
821 {
822   public:
823     static TestResultsCollectorBase* Constructor();
824
825   private:
826     TAPCollector() : m_filename(DEFAULT_TAP_FILE_NAME)  {}
827
828     virtual bool Configure()
829     {
830         m_output.open(m_filename.c_str(), std::ios_base::trunc);
831         if (m_output.fail())
832             return false;
833
834         return true;
835     }
836     virtual std::string CollectorSpecificHelp() const
837     {
838         std::string retVal = "--file=<filename> - name of file for output\n"
839                              "                    default - ";
840         retVal += DEFAULT_TAP_FILE_NAME;
841         retVal += "\n";
842         return retVal;
843     }
844
845     virtual void Start(int count)
846     {
847         DPL_UNUSED_PARAM(count);
848         AssertMsg(m_output.good(), "Output file must be opened.");
849         m_output << "TAP version 13" << std::endl;
850         m_testIndex = 0;
851     }
852
853     virtual void Finish()
854     {
855         m_output << "1.." << m_testIndex << std::endl;
856         m_output << m_collectedData.rdbuf();
857         m_output.close();
858     }
859
860     virtual bool ParseCollectorSpecificArg(const std::string& arg)
861     {
862         return ParseCollectorFileArg(arg, m_filename);
863     }
864
865     virtual void CollectResult(const std::string& id,
866                                const std::string& description,
867                                const FailStatus::Type status = FailStatus::NONE,
868                                const std::string& reason = "")
869     {
870         m_testIndex++;
871         switch (status) {
872         case TestResultsCollectorBase::FailStatus::NONE:
873             LogBasicTAP(true, id, description);
874             endTAPLine();
875             break;
876         case TestResultsCollectorBase::FailStatus::FAILED:
877             LogBasicTAP(false, id, description);
878             endTAPLine();
879             break;
880         case TestResultsCollectorBase::FailStatus::IGNORED:
881             LogBasicTAP(true, id, description);
882             m_collectedData << " # skip " << reason;
883             endTAPLine();
884             break;
885         case TestResultsCollectorBase::FailStatus::INTERNAL:
886             LogBasicTAP(true, id, description);
887             endTAPLine();
888             m_collectedData << "    ---" << std::endl;
889             m_collectedData << "    message: " << reason << std::endl;
890             m_collectedData << "    severity: Internal" << std::endl;
891             m_collectedData << "    ..." << std::endl;
892             break;
893         default:
894             Assert(false && "Bad status");
895         }
896     }
897
898     void LogBasicTAP(bool isOK, const std::string& id,
899                      const std::string& description)
900     {
901         if (!isOK) {
902             m_collectedData << "not ";
903         }
904         m_collectedData << "ok " << m_testIndex << " [" <<
905         id << "] " << description;
906     }
907
908     void endTAPLine()
909     {
910         m_collectedData << std::endl;
911     }
912
913     std::string m_filename;
914     std::stringstream m_collectedData;
915     std::ofstream m_output;
916     int m_testIndex;
917 };
918
919 TestResultsCollectorBase* TAPCollector::Constructor()
920 {
921     return new TAPCollector();
922 }
923
924 void TestResultsCollectorBase::RegisterCollectorConstructor(
925     const std::string& name,
926     TestResultsCollectorBase::CollectorConstructorFunc func)
927 {
928     Assert(m_constructorsMap.find(name) == m_constructorsMap.end());
929     m_constructorsMap[name] = func;
930 }
931
932 TestResultsCollectorBase* TestResultsCollectorBase::Create(
933     const std::string& name)
934 {
935     ConstructorsMap::iterator found = m_constructorsMap.find(name);
936     if (found != m_constructorsMap.end()) {
937         return found->second();
938     } else {
939         return NULL;
940     }
941 }
942
943 std::vector<std::string> TestResultsCollectorBase::GetCollectorsNames()
944 {
945     std::vector<std::string> list;
946     FOREACH(it, m_constructorsMap)
947     {
948         list.push_back(it->first);
949     }
950     return list;
951 }
952
953 TestResultsCollectorBase::ConstructorsMap TestResultsCollectorBase::
954     m_constructorsMap;
955
956 namespace {
957 static int RegisterCollectorConstructors();
958 static const int RegisterHelperVariable = RegisterCollectorConstructors();
959 int RegisterCollectorConstructors()
960 {
961     (void)RegisterHelperVariable;
962
963     TestResultsCollectorBase::RegisterCollectorConstructor(
964         "text",
965         &ConsoleCollector::Constructor);
966     TestResultsCollectorBase::RegisterCollectorConstructor(
967         "html",
968         &HtmlCollector::Constructor);
969     TestResultsCollectorBase::RegisterCollectorConstructor(
970         "csv",
971         &CSVCollector::Constructor);
972     TestResultsCollectorBase::RegisterCollectorConstructor(
973         "tap",
974         &TAPCollector::Constructor);
975     TestResultsCollectorBase::RegisterCollectorConstructor(
976         "xml",
977         &XmlCollector::Constructor);
978
979     return 0;
980 }
981 }
982 }
983 }
984 #undef GREEN_RESULT_OK