029393d5d21f4d75f99ccfd46858571907a6b6f3
[framework/web/wrt-commons.git] / modules / 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 <stddef.h>
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
29 #include <string>
30 #include <cstdio>
31 #include <fstream>
32 #include <sstream>
33 #include <cstdlib>
34
35 namespace DPL
36 {
37 namespace Test
38 {
39
40 namespace
41 {
42 const char *DEFAULT_HTML_FILE_NAME = "index.html";
43 const char *DEFAULT_TAP_FILE_NAME = "results.tap";
44 const char *DEFAULT_XML_FILE_NAME = "results.xml";
45
46 class Statistic
47 {
48   public:
49     Statistic() :
50         m_failed(0),
51         m_ignored(0),
52         m_passed(0),
53         m_count(0)
54     {
55     }
56
57     void AddTest(TestResultsCollectorBase::FailStatus::Type type)
58     {
59         ++m_count;
60         switch (type) {
61             case TestResultsCollectorBase::FailStatus::INTERNAL:
62             case TestResultsCollectorBase::FailStatus::FAILED:   ++m_failed; break;
63             case TestResultsCollectorBase::FailStatus::IGNORED:  ++m_ignored; break;
64             case TestResultsCollectorBase::FailStatus::NONE:     ++m_passed; break;
65             default:
66                 Assert(false && "Bad FailStatus");
67         }
68     }
69
70     size_t GetTotal() const { return m_count; }
71     size_t GetPassed() const { return m_passed; }
72     size_t GetSuccesed() const { return m_passed; }
73     size_t GetFailed() const { return m_failed; }
74     size_t GetIgnored() const { return m_ignored; }
75     float GetPassedOrIgnoredPercend() const
76     {
77         float passIgnoredPercent =
78             100.0f * (static_cast<float>(m_passed)
79                       + static_cast<float>(m_ignored))
80             / static_cast<float>(m_count);
81         return passIgnoredPercent;
82     }
83
84   private:
85     size_t m_failed;
86     size_t m_ignored;
87     size_t m_passed;
88     size_t m_count;
89 };
90
91 class ConsoleCollector
92     : public TestResultsCollectorBase
93 {
94   public:
95     static TestResultsCollectorBase* Constructor();
96
97   private:
98     ConsoleCollector() {}
99
100     virtual void CollectCurrentTestGroupName(const std::string& name)
101     {
102         printf("Starting group %s\n", name.c_str());
103         m_currentGroup = name;
104     }
105
106     virtual void Finish()
107     {
108         using namespace DPL::Colors::Text;
109
110         // Show result
111         FOREACH(group, m_groupsStats) {
112             PrintStats(group->first, group->second);
113         }
114         PrintStats("All tests together", m_stats);
115     }
116
117     virtual void CollectResult(const std::string& id,
118                                const std::string& /*description*/,
119                                const FailStatus::Type status = FailStatus::NONE,
120                                const std::string& reason = "")
121     {
122         using namespace DPL::Colors::Text;
123         std::string tmp = "'" + id + "' ...";
124
125         printf("Running test case %-60s", tmp.c_str());
126         switch(status) {
127             case TestResultsCollectorBase::FailStatus::NONE:
128                 printf("[%s%s%s]\n", BOLD_GREEN_BEGIN, "   OK   ", BOLD_GREEN_END); break;
129             case TestResultsCollectorBase::FailStatus::FAILED:
130                 PrintfErrorMessage(  " FAILED ", reason, true); break;
131             case TestResultsCollectorBase::FailStatus::IGNORED:
132                 PrintfIgnoredMessage("Ignored ", reason, true); break;
133             case TestResultsCollectorBase::FailStatus::INTERNAL:
134                 PrintfErrorMessage(  "INTERNAL", reason, true); break;
135             default:
136                 Assert(false && "Bad status");
137         }
138         m_stats.AddTest(status);
139         m_groupsStats[m_currentGroup].AddTest(status);
140     }
141
142     void PrintfErrorMessage(const char* type,
143                             const std::string& message,
144                             bool verbosity)
145     {
146         using namespace DPL::Colors::Text;
147         if (verbosity) {
148             printf("[%s%s%s] %s%s%s\n",
149                    BOLD_RED_BEGIN,
150                    type,
151                    BOLD_RED_END,
152                    BOLD_YELLOW_BEGIN,
153                    message.c_str(),
154                    BOLD_YELLOW_END);
155         } else {
156             printf("[%s%s%s]\n",
157                     BOLD_RED_BEGIN,
158                     type,
159                     BOLD_RED_END);
160         }
161     }
162
163     void PrintfIgnoredMessage(const char* type,
164                               const std::string& message,
165                               bool verbosity)
166     {
167         using namespace DPL::Colors::Text;
168         if (verbosity) {
169             printf("[%s%s%s] %s%s%s\n",
170                    CYAN_BEGIN,
171                    type,
172                    CYAN_END,
173                    BOLD_GOLD_BEGIN,
174                    message.c_str(),
175                    BOLD_GOLD_END);
176         } else {
177             printf("[%s%s%s]\n",
178                    CYAN_BEGIN ,
179                    type,
180                    CYAN_END);
181         }
182     }
183
184     void PrintStats(const std::string& title, const Statistic& stats)
185     {
186         using namespace DPL::Colors::Text;
187         printf("\n%sResults [%s]: %s\n", BOLD_GREEN_BEGIN, title.c_str(), BOLD_GREEN_END);
188         printf("%s%s%3d%s\n", CYAN_BEGIN,     "Total tests:            ", stats.GetTotal(), CYAN_END);
189         printf("  %s%s%3d%s\n", CYAN_BEGIN,   "Succeeded:            ", stats.GetPassed(), CYAN_END);
190         printf("  %s%s%3d%s\n", CYAN_BEGIN,   "Failed:               ", stats.GetFailed(), CYAN_END);
191         printf("  %s%s%3d%s\n", CYAN_BEGIN,   "Ignored:              ", stats.GetIgnored(), CYAN_END);
192     }
193
194     Statistic m_stats;
195     std::map<std::string, Statistic> m_groupsStats;
196     std::string m_currentGroup;
197 };
198
199
200 TestResultsCollectorBase* ConsoleCollector::Constructor()
201 {
202     return new ConsoleCollector();
203 }
204
205
206 class HtmlCollector
207     : public TestResultsCollectorBase
208 {
209   public:
210     static TestResultsCollectorBase* Constructor();
211
212   private:
213     HtmlCollector() : m_filename(DEFAULT_HTML_FILE_NAME) {}
214
215     virtual void CollectCurrentTestGroupName(const std::string& name)
216     {
217         fprintf(m_fp.Get(),"<b>Starting group %s", name.c_str());
218         m_currentGroup = name;
219     }
220
221     virtual bool Configure()
222     {
223         m_fp.Reset(fopen (m_filename.c_str(), "w"));
224         if (!m_fp) {
225             LogPedantic("Could not open file " << m_filename << " for writing");
226             return false;
227         }
228         return true;
229     }
230     virtual std::string CollectorSpecificHelp() const
231     {
232         return "--file=<filename> - name of file for output\n"
233                "                    default - index.html\n";
234     }
235
236     virtual void Start()
237     {
238         Assert(!!m_fp && "File handle must not be null");
239         fprintf(m_fp.Get(),
240                 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0"
241                 "Transitional//EN\" "
242                 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
243                 ">\n");
244         fprintf(m_fp.Get(),
245                 "<html xmlns=\"http://www.w3.org/1999/xhtml\" "
246                 "lang=\"en\" dir=\"ltr\">\n");
247         fprintf(m_fp.Get(), "<body style=\"background-color: black;\">\n");
248         fprintf(m_fp.Get(), "<pre>\n");
249         fprintf(m_fp.Get(), "<font color=\"white\">\n");
250     }
251
252     virtual void Finish()
253     {
254         using namespace DPL::Colors::Html;
255         // Show result
256         FOREACH(group, m_groupsStats) {
257             PrintStats(group->first, group->second);
258         }
259         PrintStats("All tests together", m_stats);
260         fprintf(m_fp.Get(), "</font>\n");
261         fprintf(m_fp.Get(), "</pre>\n");
262         fprintf(m_fp.Get(), "</body>\n");
263         fprintf(m_fp.Get(), "</html>\n");
264     }
265
266     virtual bool ParseCollectorSpecificArg(const std::string& arg)
267     {
268         const std::string argname = "--file=";
269         if (0 == arg.find(argname)) {
270             m_filename = arg.substr(argname.size());
271             return true;
272         } else {
273             return false;
274         }
275     }
276
277     virtual void CollectResult(const std::string& id,
278                                const std::string& /*description*/,
279                                const FailStatus::Type status = FailStatus::NONE,
280                                const std::string& reason = "")
281     {
282         using namespace DPL::Colors::Html;
283         std::string tmp = "'" + id + "' ...";
284
285         fprintf(m_fp.Get(), "Running test case %-100s", tmp.c_str());
286         switch(status) {
287             case TestResultsCollectorBase::FailStatus::NONE:
288                 fprintf(m_fp.Get(), "[%s%s%s]\n", BOLD_GREEN_BEGIN, "   OK   ", BOLD_GREEN_END); break;
289             case TestResultsCollectorBase::FailStatus::FAILED:
290                 PrintfErrorMessage(  " FAILED ", reason, true); break;
291             case TestResultsCollectorBase::FailStatus::IGNORED:
292                 PrintfIgnoredMessage("Ignored ", reason, true); break;
293             case TestResultsCollectorBase::FailStatus::INTERNAL:
294                 PrintfErrorMessage(  "INTERNAL", reason, true); break;
295             default:
296                 Assert(false && "Bad status");
297         }
298         m_groupsStats[m_currentGroup].AddTest(status);
299         m_stats.AddTest(status);
300     }
301
302     void PrintfErrorMessage(const char* type,
303                             const std::string& message,
304                             bool verbosity)
305     {
306         using namespace DPL::Colors::Html;
307         if (verbosity) {
308             fprintf(m_fp.Get(),
309                     "[%s%s%s] %s%s%s\n",
310                     BOLD_RED_BEGIN,
311                     type,
312                     BOLD_RED_END,
313                     BOLD_YELLOW_BEGIN,
314                     message.c_str(),
315                     BOLD_YELLOW_END);
316         } else {
317             fprintf(m_fp.Get(),
318                     "[%s%s%s]\n",
319                     BOLD_RED_BEGIN,
320                     type,
321                     BOLD_RED_END);
322         }
323     }
324
325     void PrintfIgnoredMessage(const char* type,
326                               const std::string& message,
327                               bool verbosity)
328     {
329         using namespace DPL::Colors::Html;
330
331         if (verbosity) {
332             fprintf(m_fp.Get(),
333                     "[%s%s%s] %s%s%s\n",
334                     CYAN_BEGIN,
335                     type,
336                     CYAN_END,
337                     BOLD_GOLD_BEGIN,
338                     message.c_str(),
339                     BOLD_GOLD_END);
340         } else {
341             fprintf(m_fp.Get(),
342                     "[%s%s%s]\n",
343                     CYAN_BEGIN ,
344                     type,
345                     CYAN_END);
346         }
347     }
348
349     void PrintStats(const std::string& name, const Statistic& stats)
350     {
351         using namespace DPL::Colors::Html;
352         fprintf(m_fp.Get(), "\n%sResults [%s]:%s\n", BOLD_GREEN_BEGIN, name.c_str(), BOLD_GREEN_END);
353         fprintf(m_fp.Get(), "%s%s%3d%s\n", CYAN_BEGIN,     "Total tests:            ", stats.GetTotal(), CYAN_END);
354         fprintf(m_fp.Get(), "  %s%s%3d%s\n", CYAN_BEGIN,   "Succeeded:            ", stats.GetPassed(), CYAN_END);
355         fprintf(m_fp.Get(), "  %s%s%3d%s\n", CYAN_BEGIN,   "Failed:               ", stats.GetFailed(), CYAN_END);
356         fprintf(m_fp.Get(), "  %s%s%3d%s\n", CYAN_BEGIN,   "Ignored:              ", stats.GetIgnored(), CYAN_END);
357     }
358
359     std::string m_filename;
360     ScopedFClose m_fp;
361     Statistic m_stats;
362     std::string m_currentGroup;
363     std::map<std::string, Statistic> m_groupsStats;
364 };
365
366 TestResultsCollectorBase* HtmlCollector::Constructor()
367 {
368     return new HtmlCollector();
369 }
370
371
372 class XmlCollector
373     : public TestResultsCollectorBase
374 {
375   public:
376     static TestResultsCollectorBase* Constructor();
377
378   private:
379     XmlCollector() : m_filename(DEFAULT_XML_FILE_NAME) {}
380
381     virtual void CollectCurrentTestGroupName(const std::string& name)
382     {
383         std::string groupHeader;
384         groupHeader.append("\n\t<testsuite name=\"");
385         groupHeader.append(EscapeSpecialCharacters(name));
386         groupHeader.append("\">\n\t\t<testcase name=\"unknown\" status=\"FAILED\">\n");
387         groupHeader.append("\t\t\t<failure type=\"FAILED\" message=\"segmentation fault\"/>\n");
388         groupHeader.append("\t\t</testcase>\n\t</testsuite>");
389         size_t pos = m_outputBuffer.find("</testsuites>");
390         m_outputBuffer.insert(pos - 1, groupHeader);
391         m_currentGroup = name;
392         fseek(m_fp.Get(), 0L, SEEK_SET);
393         fprintf(m_fp.Get(), "%s", m_outputBuffer.c_str());
394         fflush(m_fp.Get());
395
396     }
397
398     virtual bool Configure()
399     {
400         m_fp.Reset(fopen (m_filename.c_str(), "w"));
401         if (!m_fp) {
402             LogPedantic("Could not open file " << m_filename << " for writing");
403             return false;
404         }
405         return true;
406     }
407
408     virtual std::string CollectorSpecificHelp() const
409     {
410         return "--file=<filename> - name of file for output\n"
411                "                    default - results.xml\n";
412     }
413
414     virtual void Start()
415     {
416         Assert(!!m_fp && "File handle must not be null");
417         m_outputBuffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
418         m_outputBuffer.append("<testsuites>\n</testsuites>");
419         fseek(m_fp.Get(), 0L, SEEK_SET);
420         fprintf(m_fp.Get(), "%s", m_outputBuffer.c_str());
421         fflush(m_fp.Get());
422
423     }
424
425     virtual void Finish()
426     {
427         // Show result
428         FOREACH(group, m_groupsStats) {
429             PrintStats(group->first, group->second);
430             size_t posBegin = m_outputBuffer.find("<testcase name=\"unknown\"");
431             size_t posEnd = m_outputBuffer.find("</testcase>", posBegin);
432             m_outputBuffer.erase(posBegin - 3, posEnd - posBegin + sizeof("</testcase>") + 2);
433         }
434
435
436         remove(m_filename.c_str());
437         m_fp.Reset(fopen (m_filename.c_str(), "w"));
438         Assert(!!m_fp && "File handle must not be null");
439         fseek(m_fp.Get(), 0L, SEEK_SET);
440         fprintf(m_fp.Get(),"%s", m_outputBuffer.c_str());
441         fflush(m_fp.Get());
442     }
443
444     virtual bool ParseCollectorSpecificArg(const std::string& arg)
445     {
446         const std::string argname = "--file=";
447         if (0 == arg.find(argname)) {
448             m_filename = arg.substr(argname.size());
449             return true;
450         } else {
451             return false;
452         }
453     }
454
455     virtual void CollectResult(const std::string& id,
456                                const std::string& /*description*/,
457                                const FailStatus::Type status = FailStatus::NONE,
458                                const std::string& reason = "")
459     {
460         m_resultBuffer.erase();
461         m_resultBuffer.append("\t\t<testcase name=\"");
462         m_resultBuffer.append(EscapeSpecialCharacters(id));
463         m_resultBuffer.append("\"");
464         switch(status) {
465             case TestResultsCollectorBase::FailStatus::NONE:
466                 m_resultBuffer.append(" status=\"OK\"/>\n");
467                 break;
468             case TestResultsCollectorBase::FailStatus::FAILED:
469                 m_resultBuffer.append(" status=\"FAILED\">\n\t\t\t<failure");
470                 PrintfErrorMessage("FAILED", EscapeSpecialCharacters(reason), true);
471                 m_resultBuffer.append("\t\t</testcase>\n");
472                 break;
473             case TestResultsCollectorBase::FailStatus::IGNORED:
474                 m_resultBuffer.append(" status=\"Ignored\">\n");
475                 PrintfIgnoredMessage("Ignored", EscapeSpecialCharacters(reason), true);
476                 m_resultBuffer.append("\t\t</testcase>\n");
477                 break;
478             case TestResultsCollectorBase::FailStatus::INTERNAL:
479                 m_resultBuffer.append(" status=\"INTERNAL\">\n\t\t\t<failure");
480                 PrintfErrorMessage("INTERNAL", EscapeSpecialCharacters(reason), true);
481                 m_resultBuffer.append("\t\t</testcase>");
482                 break;
483             default:
484                 Assert(false && "Bad status");
485         }
486         size_t group_pos = m_outputBuffer.find(m_currentGroup);
487         size_t last_case_pos = m_outputBuffer.find("<testcase name=\"unknown\"", group_pos);
488         m_outputBuffer.insert(last_case_pos - 2, m_resultBuffer);
489         fseek(m_fp.Get(), 0L, SEEK_SET);
490         fprintf(m_fp.Get(), "%s", m_outputBuffer.c_str());
491         fflush(m_fp.Get());
492         m_groupsStats[m_currentGroup].AddTest(status);
493         m_stats.AddTest(status);
494
495     }
496
497     void PrintfErrorMessage(const char* type,
498                             const std::string& message,
499                             bool verbosity)
500     {
501         if (verbosity) {
502             m_resultBuffer.append(" type=\"");
503             m_resultBuffer.append(EscapeSpecialCharacters(type));
504             m_resultBuffer.append("\" message=\"");
505             m_resultBuffer.append(EscapeSpecialCharacters(message));
506             m_resultBuffer.append("\"/>\n");
507
508         } else {
509
510             m_resultBuffer.append(" type=\"");
511             m_resultBuffer.append(EscapeSpecialCharacters(type));
512             m_resultBuffer.append("\"/>\n");
513         }
514     }
515
516     void PrintfIgnoredMessage(const char* type,
517                               const std::string& message,
518                               bool verbosity)
519     {
520         if (verbosity) {
521             m_resultBuffer.append("\t\t\t<skipped type=\"");
522             m_resultBuffer.append(EscapeSpecialCharacters(type));
523             m_resultBuffer.append("\" message=\"");
524             m_resultBuffer.append(EscapeSpecialCharacters(message));
525             m_resultBuffer.append("\"/>\n");
526         } else {
527
528             m_resultBuffer.append("\t\t\t<skipped type=\"");
529             m_resultBuffer.append(EscapeSpecialCharacters(type));
530             m_resultBuffer.append("\"/>\n");
531         }
532     }
533
534     void PrintStats(const std::string& name, const Statistic& stats)
535     {
536         std::stringstream totalStats;
537         totalStats << " tests=\"";
538         totalStats << stats.GetTotal();
539         totalStats << "\" failures=\"";
540         totalStats << stats.GetFailed();
541         totalStats << "\" skipped=\"";
542         totalStats << stats.GetIgnored();
543         totalStats << "\"";
544         std::string suiteHeader;
545         suiteHeader.append("<testsuite name=\"");
546         suiteHeader.append(EscapeSpecialCharacters(name));
547         size_t pos = m_outputBuffer.find(suiteHeader);
548         m_outputBuffer.insert(pos+suiteHeader.size()+1,totalStats.str());
549     }
550
551     std::string EscapeSpecialCharacters(std::string s)
552     {
553         for(unsigned int i = 0; i < s.size();){
554             switch(s[i]){
555             case '"':
556                 s.erase(i,1);
557                 s.insert(i, "&quot;");
558                 i+=6;
559                 break;
560
561             case '&':
562                 s.erase(i,1);
563                 s.insert(i, "&amp;");
564                 i+=5;
565                 break;
566
567             case '<':
568                 s.erase(i,1);
569                 s.insert(i, "&lt;");
570                 i+=4;
571                 break;
572
573             case '>':
574                 s.erase(i,1);
575                 s.insert(i, "&gt;");
576                 i+=4;
577                 break;
578
579             case '\'':
580                 s.erase(i,1);
581                 s.insert(i, "&#39;");
582                 i+=5;
583                 break;
584             default:
585                 ++i;
586                 break;
587             }
588         }
589         return s;
590     }
591
592     std::string m_filename;
593     ScopedFClose m_fp;
594     Statistic m_stats;
595     std::string m_currentGroup;
596     std::map<std::string, Statistic> m_groupsStats;
597     std::string m_outputBuffer;
598     std::string m_resultBuffer;
599 };
600
601 TestResultsCollectorBase* XmlCollector::Constructor()
602 {
603     return new XmlCollector();
604 }
605
606
607
608 class CSVCollector
609     : public TestResultsCollectorBase
610 {
611   public:
612     static TestResultsCollectorBase* Constructor();
613
614   private:
615     CSVCollector() {}
616
617     virtual void Start() {
618         printf("GROUP;ID;RESULT;REASON\n");
619     }
620
621     virtual void CollectCurrentTestGroupName(const std::string& name)
622     {
623         m_currentGroup = name;
624     }
625
626     virtual void CollectResult(const std::string& id,
627                                const std::string& /*description*/,
628                                const FailStatus::Type status = FailStatus::NONE,
629                                const std::string& reason = "")
630     {
631         std::string statusMsg = "";
632         switch(status) {
633             case TestResultsCollectorBase::FailStatus::NONE: statusMsg = "OK"; break;
634             case TestResultsCollectorBase::FailStatus::FAILED: statusMsg = "FAILED"; break;
635             case TestResultsCollectorBase::FailStatus::IGNORED: statusMsg = "IGNORED"; break;
636             case TestResultsCollectorBase::FailStatus::INTERNAL: statusMsg = "FAILED"; break;
637             default:
638                 Assert(false && "Bad status");
639         }
640         printf("%s;%s;%s;%s\n",
641                m_currentGroup.c_str(),
642                id.c_str(),
643                statusMsg.c_str(),
644                reason.c_str());
645     }
646
647     std::string m_currentGroup;
648 };
649
650
651 TestResultsCollectorBase* CSVCollector::Constructor()
652 {
653     return new CSVCollector();
654 }
655
656 }
657
658 class TAPCollector
659     : public TestResultsCollectorBase
660 {
661   public:
662     static TestResultsCollectorBase* Constructor();
663
664   private:
665     TAPCollector() : m_filename(DEFAULT_TAP_FILE_NAME)  {}
666
667     virtual bool Configure()
668     {
669         m_output.open(m_filename.c_str(), std::ios_base::trunc);
670         if (m_output.fail()) {
671             LogError("Can't open output file: " << m_filename);
672             return false;
673         }
674         return true;
675     }
676     virtual std::string CollectorSpecificHelp() const
677     {
678         std::string retVal = "--file=<filename> - name of file for output\n"
679                              "                    default - ";
680         retVal += DEFAULT_TAP_FILE_NAME;
681         retVal += "\n";
682         return retVal;
683     }
684
685     virtual void Start()
686     {
687         Assert(m_output.good() && "Output file must be opened.");
688         m_output << "TAP version 13" << std::endl;
689         m_testIndex = 0;
690     }
691
692     virtual void Finish()
693     {
694         m_output << "1.." << m_testIndex << std::endl;
695         m_output << m_collectedData.rdbuf();
696         m_output.close();
697     }
698
699     virtual bool ParseCollectorSpecificArg(const std::string& arg)
700     {
701         const std::string argname = "--file=";
702         if (0 == arg.find(argname)) {
703             m_filename = arg.substr(argname.size());
704             return true;
705         } else {
706             return false;
707         }
708     }
709
710
711
712     virtual void CollectResult(const std::string& id,
713                                const std::string& description,
714                                const FailStatus::Type status = FailStatus::NONE,
715                                const std::string& reason = "")
716     {
717         m_testIndex++;
718         switch(status) {
719             case TestResultsCollectorBase::FailStatus::NONE:
720                 LogBasicTAP(true, id, description);
721                 endTAPLine();
722                 break;
723             case TestResultsCollectorBase::FailStatus::FAILED:
724                 LogBasicTAP(false, id, description);
725                 endTAPLine();
726                 break;
727             case TestResultsCollectorBase::FailStatus::IGNORED:
728                 LogBasicTAP(true, id, description);
729                 m_collectedData << " # skip " << reason;
730                 endTAPLine();
731                 break;
732             case TestResultsCollectorBase::FailStatus::INTERNAL:
733                 LogBasicTAP(true, id, description);
734                 endTAPLine();
735                 m_collectedData << "    ---" << std::endl;
736                 m_collectedData << "    message: " << reason << std::endl;
737                 m_collectedData << "    severity: Internal" << std::endl;
738                 m_collectedData << "    ..." << std::endl;
739                 break;
740             default:
741                 Assert(false && "Bad status");
742         }
743     }
744
745     void LogBasicTAP(bool isOK, const std::string& id,
746             const std::string& description)
747     {
748         if (!isOK) {
749             m_collectedData << "not ";
750         }
751         m_collectedData << "ok " << m_testIndex << " [" <<
752                             id << "] " << description;
753     }
754
755     void endTAPLine()
756     {
757         m_collectedData << std::endl;
758     }
759
760
761     std::string m_filename;
762     std::stringstream m_collectedData;
763     std::ofstream m_output;
764     int m_testIndex;
765 };
766
767
768 TestResultsCollectorBase* TAPCollector::Constructor()
769 {
770     return new TAPCollector();
771 }
772
773
774 void TestResultsCollectorBase::RegisterCollectorConstructor(
775     const std::string& name,
776     TestResultsCollectorBase::CollectorConstructorFunc func)
777 {
778     Assert(m_constructorsMap.find(name) == m_constructorsMap.end());
779     m_constructorsMap[name] = func;
780 }
781
782 TestResultsCollectorBase* TestResultsCollectorBase::Create(
783     const std::string& name)
784 {
785     ConstructorsMap::iterator found = m_constructorsMap.find(name);
786     if (found != m_constructorsMap.end())
787         return found->second();
788     else
789         return NULL;
790 }
791
792 std::vector<std::string> TestResultsCollectorBase::GetCollectorsNames()
793 {
794     std::vector<std::string> list;
795     FOREACH(it, m_constructorsMap)
796     {
797         list.push_back(it->first);
798     }
799     return list;
800 }
801
802 TestResultsCollectorBase::ConstructorsMap TestResultsCollectorBase::m_constructorsMap;
803
804 namespace
805 {
806 static int RegisterCollectorConstructors();
807 static const int RegisterHelperVariable = RegisterCollectorConstructors();
808 int RegisterCollectorConstructors()
809 {
810     (void)RegisterHelperVariable;
811
812     TestResultsCollectorBase::RegisterCollectorConstructor(
813         "text",
814         &ConsoleCollector::Constructor);
815     TestResultsCollectorBase::RegisterCollectorConstructor(
816         "html",
817         &HtmlCollector::Constructor);
818     TestResultsCollectorBase::RegisterCollectorConstructor(
819         "csv",
820         &CSVCollector::Constructor);
821     TestResultsCollectorBase::RegisterCollectorConstructor(
822         "tap",
823         &TAPCollector::Constructor);
824     TestResultsCollectorBase::RegisterCollectorConstructor(
825         "xml",
826         &XmlCollector::Constructor);
827
828     return 0;
829 }
830
831 }
832
833 }
834 }