2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * @file test_results_collector.h
18 * @author Lukasz Wrzosek (l.wrzosek@samsung.com)
20 * @brief Implementation file some concrete TestResulstsCollector
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>
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";
57 void AddTest(TestResultsCollectorBase::FailStatus::Type 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;
66 Assert(false && "Bad FailStatus");
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
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;
91 class ConsoleCollector
92 : public TestResultsCollectorBase
95 static TestResultsCollectorBase* Constructor();
100 virtual void CollectCurrentTestGroupName(const std::string& name)
102 printf("Starting group %s\n", name.c_str());
103 m_currentGroup = name;
106 virtual void Finish()
108 using namespace DPL::Colors::Text;
111 FOREACH(group, m_groupsStats) {
112 PrintStats(group->first, group->second);
114 PrintStats("All tests together", m_stats);
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 = "")
122 using namespace DPL::Colors::Text;
123 std::string tmp = "'" + id + "' ...";
125 printf("Running test case %-60s", tmp.c_str());
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;
136 Assert(false && "Bad status");
138 m_stats.AddTest(status);
139 m_groupsStats[m_currentGroup].AddTest(status);
142 void PrintfErrorMessage(const char* type,
143 const std::string& message,
146 using namespace DPL::Colors::Text;
148 printf("[%s%s%s] %s%s%s\n",
163 void PrintfIgnoredMessage(const char* type,
164 const std::string& message,
167 using namespace DPL::Colors::Text;
169 printf("[%s%s%s] %s%s%s\n",
184 void PrintStats(const std::string& title, const Statistic& stats)
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);
195 std::map<std::string, Statistic> m_groupsStats;
196 std::string m_currentGroup;
200 TestResultsCollectorBase* ConsoleCollector::Constructor()
202 return new ConsoleCollector();
207 : public TestResultsCollectorBase
210 static TestResultsCollectorBase* Constructor();
213 HtmlCollector() : m_filename(DEFAULT_HTML_FILE_NAME) {}
215 virtual void CollectCurrentTestGroupName(const std::string& name)
217 fprintf(m_fp.Get(),"<b>Starting group %s", name.c_str());
218 m_currentGroup = name;
221 virtual bool Configure()
223 m_fp.Reset(fopen (m_filename.c_str(), "w"));
225 LogPedantic("Could not open file " << m_filename << " for writing");
230 virtual std::string CollectorSpecificHelp() const
232 return "--file=<filename> - name of file for output\n"
233 " default - index.html\n";
238 Assert(!!m_fp && "File handle must not be null");
240 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0"
241 "Transitional//EN\" "
242 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\""
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");
252 virtual void Finish()
254 using namespace DPL::Colors::Html;
256 FOREACH(group, m_groupsStats) {
257 PrintStats(group->first, group->second);
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");
266 virtual bool ParseCollectorSpecificArg(const std::string& arg)
268 const std::string argname = "--file=";
269 if (0 == arg.find(argname)) {
270 m_filename = arg.substr(argname.size());
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 = "")
282 using namespace DPL::Colors::Html;
283 std::string tmp = "'" + id + "' ...";
285 fprintf(m_fp.Get(), "Running test case %-100s", tmp.c_str());
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;
296 Assert(false && "Bad status");
298 m_groupsStats[m_currentGroup].AddTest(status);
299 m_stats.AddTest(status);
302 void PrintfErrorMessage(const char* type,
303 const std::string& message,
306 using namespace DPL::Colors::Html;
325 void PrintfIgnoredMessage(const char* type,
326 const std::string& message,
329 using namespace DPL::Colors::Html;
349 void PrintStats(const std::string& name, const Statistic& stats)
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);
359 std::string m_filename;
362 std::string m_currentGroup;
363 std::map<std::string, Statistic> m_groupsStats;
366 TestResultsCollectorBase* HtmlCollector::Constructor()
368 return new HtmlCollector();
373 : public TestResultsCollectorBase
376 static TestResultsCollectorBase* Constructor();
379 XmlCollector() : m_filename(DEFAULT_XML_FILE_NAME) {}
381 virtual void CollectCurrentTestGroupName(const std::string& name)
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());
398 virtual bool Configure()
400 m_fp.Reset(fopen (m_filename.c_str(), "w"));
402 LogPedantic("Could not open file " << m_filename << " for writing");
408 virtual std::string CollectorSpecificHelp() const
410 return "--file=<filename> - name of file for output\n"
411 " default - results.xml\n";
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());
425 virtual void Finish()
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);
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());
444 virtual bool ParseCollectorSpecificArg(const std::string& arg)
446 const std::string argname = "--file=";
447 if (0 == arg.find(argname)) {
448 m_filename = arg.substr(argname.size());
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 = "")
460 m_resultBuffer.erase();
461 m_resultBuffer.append("\t\t<testcase name=\"");
462 m_resultBuffer.append(EscapeSpecialCharacters(id));
463 m_resultBuffer.append("\"");
465 case TestResultsCollectorBase::FailStatus::NONE:
466 m_resultBuffer.append(" status=\"OK\"/>\n");
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");
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");
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>");
484 Assert(false && "Bad status");
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());
492 m_groupsStats[m_currentGroup].AddTest(status);
493 m_stats.AddTest(status);
497 void PrintfErrorMessage(const char* type,
498 const std::string& message,
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");
510 m_resultBuffer.append(" type=\"");
511 m_resultBuffer.append(EscapeSpecialCharacters(type));
512 m_resultBuffer.append("\"/>\n");
516 void PrintfIgnoredMessage(const char* type,
517 const std::string& message,
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");
528 m_resultBuffer.append("\t\t\t<skipped type=\"");
529 m_resultBuffer.append(EscapeSpecialCharacters(type));
530 m_resultBuffer.append("\"/>\n");
534 void PrintStats(const std::string& name, const Statistic& stats)
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();
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());
551 std::string EscapeSpecialCharacters(std::string s)
553 for(unsigned int i = 0; i < s.size();){
557 s.insert(i, """);
563 s.insert(i, "&");
581 s.insert(i, "'");
592 std::string m_filename;
595 std::string m_currentGroup;
596 std::map<std::string, Statistic> m_groupsStats;
597 std::string m_outputBuffer;
598 std::string m_resultBuffer;
601 TestResultsCollectorBase* XmlCollector::Constructor()
603 return new XmlCollector();
609 : public TestResultsCollectorBase
612 static TestResultsCollectorBase* Constructor();
617 virtual void Start() {
618 printf("GROUP;ID;RESULT;REASON\n");
621 virtual void CollectCurrentTestGroupName(const std::string& name)
623 m_currentGroup = name;
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 = "")
631 std::string statusMsg = "";
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;
638 Assert(false && "Bad status");
640 printf("%s;%s;%s;%s\n",
641 m_currentGroup.c_str(),
647 std::string m_currentGroup;
651 TestResultsCollectorBase* CSVCollector::Constructor()
653 return new CSVCollector();
659 : public TestResultsCollectorBase
662 static TestResultsCollectorBase* Constructor();
665 TAPCollector() : m_filename(DEFAULT_TAP_FILE_NAME) {}
667 virtual bool Configure()
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);
676 virtual std::string CollectorSpecificHelp() const
678 std::string retVal = "--file=<filename> - name of file for output\n"
680 retVal += DEFAULT_TAP_FILE_NAME;
687 Assert(m_output.good() && "Output file must be opened.");
688 m_output << "TAP version 13" << std::endl;
692 virtual void Finish()
694 m_output << "1.." << m_testIndex << std::endl;
695 m_output << m_collectedData.rdbuf();
699 virtual bool ParseCollectorSpecificArg(const std::string& arg)
701 const std::string argname = "--file=";
702 if (0 == arg.find(argname)) {
703 m_filename = arg.substr(argname.size());
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 = "")
719 case TestResultsCollectorBase::FailStatus::NONE:
720 LogBasicTAP(true, id, description);
723 case TestResultsCollectorBase::FailStatus::FAILED:
724 LogBasicTAP(false, id, description);
727 case TestResultsCollectorBase::FailStatus::IGNORED:
728 LogBasicTAP(true, id, description);
729 m_collectedData << " # skip " << reason;
732 case TestResultsCollectorBase::FailStatus::INTERNAL:
733 LogBasicTAP(true, id, description);
735 m_collectedData << " ---" << std::endl;
736 m_collectedData << " message: " << reason << std::endl;
737 m_collectedData << " severity: Internal" << std::endl;
738 m_collectedData << " ..." << std::endl;
741 Assert(false && "Bad status");
745 void LogBasicTAP(bool isOK, const std::string& id,
746 const std::string& description)
749 m_collectedData << "not ";
751 m_collectedData << "ok " << m_testIndex << " [" <<
752 id << "] " << description;
757 m_collectedData << std::endl;
761 std::string m_filename;
762 std::stringstream m_collectedData;
763 std::ofstream m_output;
768 TestResultsCollectorBase* TAPCollector::Constructor()
770 return new TAPCollector();
774 void TestResultsCollectorBase::RegisterCollectorConstructor(
775 const std::string& name,
776 TestResultsCollectorBase::CollectorConstructorFunc func)
778 Assert(m_constructorsMap.find(name) == m_constructorsMap.end());
779 m_constructorsMap[name] = func;
782 TestResultsCollectorBase* TestResultsCollectorBase::Create(
783 const std::string& name)
785 ConstructorsMap::iterator found = m_constructorsMap.find(name);
786 if (found != m_constructorsMap.end())
787 return found->second();
792 std::vector<std::string> TestResultsCollectorBase::GetCollectorsNames()
794 std::vector<std::string> list;
795 FOREACH(it, m_constructorsMap)
797 list.push_back(it->first);
802 TestResultsCollectorBase::ConstructorsMap TestResultsCollectorBase::m_constructorsMap;
806 static int RegisterCollectorConstructors();
807 static const int RegisterHelperVariable = RegisterCollectorConstructors();
808 int RegisterCollectorConstructors()
810 (void)RegisterHelperVariable;
812 TestResultsCollectorBase::RegisterCollectorConstructor(
814 &ConsoleCollector::Constructor);
815 TestResultsCollectorBase::RegisterCollectorConstructor(
817 &HtmlCollector::Constructor);
818 TestResultsCollectorBase::RegisterCollectorConstructor(
820 &CSVCollector::Constructor);
821 TestResultsCollectorBase::RegisterCollectorConstructor(
823 &TAPCollector::Constructor);
824 TestResultsCollectorBase::RegisterCollectorConstructor(
826 &XmlCollector::Constructor);