Update wrt-commons_0.2.54
[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
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         }
431
432         size_t posBegin = m_outputBuffer.find("<testcase name=\"unknown\"");
433         size_t posEnd = m_outputBuffer.find("</testcase>", posBegin);
434         m_outputBuffer.erase(posBegin - 3, posEnd - posBegin + sizeof("</testcase>") + 2);
435         remove(m_filename.c_str());
436         m_fp.Reset(fopen (m_filename.c_str(), "w"));
437         Assert(!!m_fp && "File handle must not be null");
438         fseek(m_fp.Get(), 0L, SEEK_SET);
439         fprintf(m_fp.Get(),"%s", m_outputBuffer.c_str());
440         fflush(m_fp.Get());
441     }
442
443     virtual bool ParseCollectorSpecificArg(const std::string& arg)
444     {
445         const std::string argname = "--file=";
446         if (0 == arg.find(argname)) {
447             m_filename = arg.substr(argname.size());
448             return true;
449         } else {
450             return false;
451         }
452     }
453
454     virtual void CollectResult(const std::string& id,
455                                const std::string& /*description*/,
456                                const FailStatus::Type status = FailStatus::NONE,
457                                const std::string& reason = "")
458     {
459         m_resultBuffer.erase();
460         m_resultBuffer.append("\t\t<testcase name=\"");
461         m_resultBuffer.append(EscapeSpecialCharacters(id));
462         m_resultBuffer.append("\"");
463         switch(status) {
464             case TestResultsCollectorBase::FailStatus::NONE:
465                 m_resultBuffer.append(" status=\"OK\"/>\n");
466                 break;
467             case TestResultsCollectorBase::FailStatus::FAILED:
468                 m_resultBuffer.append(" status=\"FAILED\">\n\t\t\t<failure");
469                 PrintfErrorMessage("FAILED", EscapeSpecialCharacters(reason), true);
470                 m_resultBuffer.append("\t\t</testcase>\n");
471                 break;
472             case TestResultsCollectorBase::FailStatus::IGNORED:
473                 m_resultBuffer.append(" status=\"Ignored\">\n");
474                 PrintfIgnoredMessage("Ignored", EscapeSpecialCharacters(reason), true);
475                 m_resultBuffer.append("\t\t</testcase>\n");
476                 break;
477             case TestResultsCollectorBase::FailStatus::INTERNAL:
478                 m_resultBuffer.append(" status=\"INTERNAL\">\n\t\t\t<failure");
479                 PrintfErrorMessage("INTERNAL", EscapeSpecialCharacters(reason), true);
480                 m_resultBuffer.append("\t\t</testcase>");
481                 break;
482             default:
483                 Assert(false && "Bad status");
484         }
485         size_t pos = m_outputBuffer.find("<testcase name=\"unknown\"");
486         m_outputBuffer.insert(pos - 2, m_resultBuffer);
487         fseek(m_fp.Get(), 0L, SEEK_SET);
488         fprintf(m_fp.Get(), "%s", m_outputBuffer.c_str());
489         fflush(m_fp.Get());
490         m_groupsStats[m_currentGroup].AddTest(status);
491         m_stats.AddTest(status);
492
493     }
494
495     void PrintfErrorMessage(const char* type,
496                             const std::string& message,
497                             bool verbosity)
498     {
499         if (verbosity) {
500             m_resultBuffer.append(" type=\"");
501             m_resultBuffer.append(EscapeSpecialCharacters(type));
502             m_resultBuffer.append("\" message=\"");
503             m_resultBuffer.append(EscapeSpecialCharacters(message));
504             m_resultBuffer.append("\"/>\n");
505
506         } else {
507
508             m_resultBuffer.append(" type=\"");
509             m_resultBuffer.append(EscapeSpecialCharacters(type));
510             m_resultBuffer.append("\"/>\n");
511         }
512     }
513
514     void PrintfIgnoredMessage(const char* type,
515                               const std::string& message,
516                               bool verbosity)
517     {
518         if (verbosity) {
519             m_resultBuffer.append("\t\t\t<skipped type=\"");
520             m_resultBuffer.append(EscapeSpecialCharacters(type));
521             m_resultBuffer.append("\" message=\"");
522             m_resultBuffer.append(EscapeSpecialCharacters(message));
523             m_resultBuffer.append("\"/>\n");
524         } else {
525
526             m_resultBuffer.append("\t\t\t<skipped type=\"");
527             m_resultBuffer.append(EscapeSpecialCharacters(type));
528             m_resultBuffer.append("\"/>\n");
529         }
530     }
531
532     void PrintStats(const std::string& name, const Statistic& stats)
533     {
534         std::stringstream totalStats;
535         totalStats << " tests=\"";
536         totalStats << stats.GetTotal();
537         totalStats << "\" failures=\"";
538         totalStats << stats.GetFailed();
539         totalStats << "\" skipped=\"";
540         totalStats << stats.GetIgnored();
541         totalStats << "\"";
542         std::string suiteHeader;
543         suiteHeader.append("<testsuite name=\"");
544         suiteHeader.append(EscapeSpecialCharacters(name));
545         size_t pos = m_outputBuffer.find(suiteHeader);
546         m_outputBuffer.insert(pos+suiteHeader.size()+1,totalStats.str());
547     }
548
549     std::string EscapeSpecialCharacters(std::string s)
550     {
551         for(unsigned int i = 0; i < s.size();){
552             switch(s[i]){
553             case '"':
554                 s.erase(i,1);
555                 s.insert(i, "&quot;");
556                 i+=6;
557                 break;
558
559             case '&':
560                 s.erase(i,1);
561                 s.insert(i, "&amp;");
562                 i+=5;
563                 break;
564
565             case '<':
566                 s.erase(i,1);
567                 s.insert(i, "&lt;");
568                 i+=4;
569                 break;
570
571             case '>':
572                 s.erase(i,1);
573                 s.insert(i, "&gt;");
574                 i+=4;
575                 break;
576
577             case '\'':
578                 s.erase(i,1);
579                 s.insert(i, "&#39;");
580                 i+=5;
581                 break;
582             default:
583                 ++i;
584                 break;
585             }
586         }
587         return s;
588     }
589
590     std::string m_filename;
591     ScopedFClose m_fp;
592     Statistic m_stats;
593     std::string m_currentGroup;
594     std::map<std::string, Statistic> m_groupsStats;
595     std::string m_outputBuffer;
596     std::string m_resultBuffer;
597 };
598
599 TestResultsCollectorBase* XmlCollector::Constructor()
600 {
601     return new XmlCollector();
602 }
603
604
605
606 class CSVCollector
607     : public TestResultsCollectorBase
608 {
609   public:
610     static TestResultsCollectorBase* Constructor();
611
612   private:
613     CSVCollector() {}
614
615     virtual void Start() {
616         printf("GROUP;ID;RESULT;REASON\n");
617     }
618
619     virtual void CollectCurrentTestGroupName(const std::string& name)
620     {
621         m_currentGroup = name;
622     }
623
624     virtual void CollectResult(const std::string& id,
625                                const std::string& /*description*/,
626                                const FailStatus::Type status = FailStatus::NONE,
627                                const std::string& reason = "")
628     {
629         std::string statusMsg = "";
630         switch(status) {
631             case TestResultsCollectorBase::FailStatus::NONE: statusMsg = "OK"; break;
632             case TestResultsCollectorBase::FailStatus::FAILED: statusMsg = "FAILED"; break;
633             case TestResultsCollectorBase::FailStatus::IGNORED: statusMsg = "IGNORED"; break;
634             case TestResultsCollectorBase::FailStatus::INTERNAL: statusMsg = "FAILED"; break;
635             default:
636                 Assert(false && "Bad status");
637         }
638         printf("%s;%s;%s;%s\n",
639                m_currentGroup.c_str(),
640                id.c_str(),
641                statusMsg.c_str(),
642                reason.c_str());
643     }
644
645     std::string m_currentGroup;
646 };
647
648
649 TestResultsCollectorBase* CSVCollector::Constructor()
650 {
651     return new CSVCollector();
652 }
653
654 }
655
656 class TAPCollector
657     : public TestResultsCollectorBase
658 {
659   public:
660     static TestResultsCollectorBase* Constructor();
661
662   private:
663     TAPCollector() : m_filename(DEFAULT_TAP_FILE_NAME)  {}
664
665     virtual bool Configure()
666     {
667         m_output.open(m_filename.c_str(), std::ios_base::trunc);
668         if (m_output.fail()) {
669             LogError("Can't open output file: " << m_filename);
670             return false;
671         }
672         return true;
673     }
674     virtual std::string CollectorSpecificHelp() const
675     {
676         std::string retVal = "--file=<filename> - name of file for output\n"
677                              "                    default - ";
678         retVal += DEFAULT_TAP_FILE_NAME;
679         retVal += "\n";
680         return retVal;
681     }
682
683     virtual void Start()
684     {
685         Assert(m_output.good() && "Output file must be opened.");
686         m_output << "TAP version 13" << std::endl;
687         m_testIndex = 0;
688     }
689
690     virtual void Finish()
691     {
692         m_output << "1.." << m_testIndex << std::endl;
693         m_output << m_collectedData.rdbuf();
694         m_output.close();
695     }
696
697     virtual bool ParseCollectorSpecificArg(const std::string& arg)
698     {
699         const std::string argname = "--file=";
700         if (0 == arg.find(argname)) {
701             m_filename = arg.substr(argname.size());
702             return true;
703         } else {
704             return false;
705         }
706     }
707
708
709
710     virtual void CollectResult(const std::string& id,
711                                const std::string& description,
712                                const FailStatus::Type status = FailStatus::NONE,
713                                const std::string& reason = "")
714     {
715         m_testIndex++;
716         switch(status) {
717             case TestResultsCollectorBase::FailStatus::NONE:
718                 LogBasicTAP(true, id, description);
719                 endTAPLine();
720                 break;
721             case TestResultsCollectorBase::FailStatus::FAILED:
722                 LogBasicTAP(false, id, description);
723                 endTAPLine();
724                 break;
725             case TestResultsCollectorBase::FailStatus::IGNORED:
726                 LogBasicTAP(true, id, description);
727                 m_collectedData << " # skip " << reason;
728                 endTAPLine();
729                 break;
730             case TestResultsCollectorBase::FailStatus::INTERNAL:
731                 LogBasicTAP(true, id, description);
732                 endTAPLine();
733                 m_collectedData << "    ---" << std::endl;
734                 m_collectedData << "    message: " << reason << std::endl;
735                 m_collectedData << "    severity: Internal" << std::endl;
736                 m_collectedData << "    ..." << std::endl;
737                 break;
738             default:
739                 Assert(false && "Bad status");
740         }
741     }
742
743     void LogBasicTAP(bool isOK, const std::string& id,
744             const std::string& description)
745     {
746         if (!isOK) {
747             m_collectedData << "not ";
748         }
749         m_collectedData << "ok " << m_testIndex << " [" <<
750                             id << "] " << description;
751     }
752
753     void endTAPLine()
754     {
755         m_collectedData << std::endl;
756     }
757
758
759     std::string m_filename;
760     std::stringstream m_collectedData;
761     std::ofstream m_output;
762     int m_testIndex;
763 };
764
765
766 TestResultsCollectorBase* TAPCollector::Constructor()
767 {
768     return new TAPCollector();
769 }
770
771
772 void TestResultsCollectorBase::RegisterCollectorConstructor(
773     const std::string& name,
774     TestResultsCollectorBase::CollectorConstructorFunc func)
775 {
776     Assert(m_constructorsMap.find(name) == m_constructorsMap.end());
777     m_constructorsMap[name] = func;
778 }
779
780 TestResultsCollectorBase* TestResultsCollectorBase::Create(
781     const std::string& name)
782 {
783     ConstructorsMap::iterator found = m_constructorsMap.find(name);
784     if (found != m_constructorsMap.end())
785         return found->second();
786     else
787         return NULL;
788 }
789
790 std::vector<std::string> TestResultsCollectorBase::GetCollectorsNames()
791 {
792     std::vector<std::string> list;
793     FOREACH(it, m_constructorsMap)
794     {
795         list.push_back(it->first);
796     }
797     return list;
798 }
799
800 TestResultsCollectorBase::ConstructorsMap TestResultsCollectorBase::m_constructorsMap;
801
802 namespace
803 {
804 static int RegisterCollectorConstructors();
805 static const int RegisterHelperVariable = RegisterCollectorConstructors();
806 int RegisterCollectorConstructors()
807 {
808     (void)RegisterHelperVariable;
809
810     TestResultsCollectorBase::RegisterCollectorConstructor(
811         "text",
812         &ConsoleCollector::Constructor);
813     TestResultsCollectorBase::RegisterCollectorConstructor(
814         "html",
815         &HtmlCollector::Constructor);
816     TestResultsCollectorBase::RegisterCollectorConstructor(
817         "csv",
818         &CSVCollector::Constructor);
819     TestResultsCollectorBase::RegisterCollectorConstructor(
820         "tap",
821         &TAPCollector::Constructor);
822     TestResultsCollectorBase::RegisterCollectorConstructor(
823         "xml",
824         &XmlCollector::Constructor);
825
826     return 0;
827 }
828
829 }
830
831 }
832 }