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);
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());
443 virtual bool ParseCollectorSpecificArg(const std::string& arg)
445 const std::string argname = "--file=";
446 if (0 == arg.find(argname)) {
447 m_filename = arg.substr(argname.size());
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 = "")
459 m_resultBuffer.erase();
460 m_resultBuffer.append("\t\t<testcase name=\"");
461 m_resultBuffer.append(EscapeSpecialCharacters(id));
462 m_resultBuffer.append("\"");
464 case TestResultsCollectorBase::FailStatus::NONE:
465 m_resultBuffer.append(" status=\"OK\"/>\n");
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");
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");
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>");
483 Assert(false && "Bad status");
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());
490 m_groupsStats[m_currentGroup].AddTest(status);
491 m_stats.AddTest(status);
495 void PrintfErrorMessage(const char* type,
496 const std::string& message,
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");
508 m_resultBuffer.append(" type=\"");
509 m_resultBuffer.append(EscapeSpecialCharacters(type));
510 m_resultBuffer.append("\"/>\n");
514 void PrintfIgnoredMessage(const char* type,
515 const std::string& message,
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");
526 m_resultBuffer.append("\t\t\t<skipped type=\"");
527 m_resultBuffer.append(EscapeSpecialCharacters(type));
528 m_resultBuffer.append("\"/>\n");
532 void PrintStats(const std::string& name, const Statistic& stats)
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();
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());
549 std::string EscapeSpecialCharacters(std::string s)
551 for(unsigned int i = 0; i < s.size();){
555 s.insert(i, """);
561 s.insert(i, "&");
579 s.insert(i, "'");
590 std::string m_filename;
593 std::string m_currentGroup;
594 std::map<std::string, Statistic> m_groupsStats;
595 std::string m_outputBuffer;
596 std::string m_resultBuffer;
599 TestResultsCollectorBase* XmlCollector::Constructor()
601 return new XmlCollector();
607 : public TestResultsCollectorBase
610 static TestResultsCollectorBase* Constructor();
615 virtual void Start() {
616 printf("GROUP;ID;RESULT;REASON\n");
619 virtual void CollectCurrentTestGroupName(const std::string& name)
621 m_currentGroup = name;
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 = "")
629 std::string statusMsg = "";
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;
636 Assert(false && "Bad status");
638 printf("%s;%s;%s;%s\n",
639 m_currentGroup.c_str(),
645 std::string m_currentGroup;
649 TestResultsCollectorBase* CSVCollector::Constructor()
651 return new CSVCollector();
657 : public TestResultsCollectorBase
660 static TestResultsCollectorBase* Constructor();
663 TAPCollector() : m_filename(DEFAULT_TAP_FILE_NAME) {}
665 virtual bool Configure()
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);
674 virtual std::string CollectorSpecificHelp() const
676 std::string retVal = "--file=<filename> - name of file for output\n"
678 retVal += DEFAULT_TAP_FILE_NAME;
685 Assert(m_output.good() && "Output file must be opened.");
686 m_output << "TAP version 13" << std::endl;
690 virtual void Finish()
692 m_output << "1.." << m_testIndex << std::endl;
693 m_output << m_collectedData.rdbuf();
697 virtual bool ParseCollectorSpecificArg(const std::string& arg)
699 const std::string argname = "--file=";
700 if (0 == arg.find(argname)) {
701 m_filename = arg.substr(argname.size());
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 = "")
717 case TestResultsCollectorBase::FailStatus::NONE:
718 LogBasicTAP(true, id, description);
721 case TestResultsCollectorBase::FailStatus::FAILED:
722 LogBasicTAP(false, id, description);
725 case TestResultsCollectorBase::FailStatus::IGNORED:
726 LogBasicTAP(true, id, description);
727 m_collectedData << " # skip " << reason;
730 case TestResultsCollectorBase::FailStatus::INTERNAL:
731 LogBasicTAP(true, id, description);
733 m_collectedData << " ---" << std::endl;
734 m_collectedData << " message: " << reason << std::endl;
735 m_collectedData << " severity: Internal" << std::endl;
736 m_collectedData << " ..." << std::endl;
739 Assert(false && "Bad status");
743 void LogBasicTAP(bool isOK, const std::string& id,
744 const std::string& description)
747 m_collectedData << "not ";
749 m_collectedData << "ok " << m_testIndex << " [" <<
750 id << "] " << description;
755 m_collectedData << std::endl;
759 std::string m_filename;
760 std::stringstream m_collectedData;
761 std::ofstream m_output;
766 TestResultsCollectorBase* TAPCollector::Constructor()
768 return new TAPCollector();
772 void TestResultsCollectorBase::RegisterCollectorConstructor(
773 const std::string& name,
774 TestResultsCollectorBase::CollectorConstructorFunc func)
776 Assert(m_constructorsMap.find(name) == m_constructorsMap.end());
777 m_constructorsMap[name] = func;
780 TestResultsCollectorBase* TestResultsCollectorBase::Create(
781 const std::string& name)
783 ConstructorsMap::iterator found = m_constructorsMap.find(name);
784 if (found != m_constructorsMap.end())
785 return found->second();
790 std::vector<std::string> TestResultsCollectorBase::GetCollectorsNames()
792 std::vector<std::string> list;
793 FOREACH(it, m_constructorsMap)
795 list.push_back(it->first);
800 TestResultsCollectorBase::ConstructorsMap TestResultsCollectorBase::m_constructorsMap;
804 static int RegisterCollectorConstructors();
805 static const int RegisterHelperVariable = RegisterCollectorConstructors();
806 int RegisterCollectorConstructors()
808 (void)RegisterHelperVariable;
810 TestResultsCollectorBase::RegisterCollectorConstructor(
812 &ConsoleCollector::Constructor);
813 TestResultsCollectorBase::RegisterCollectorConstructor(
815 &HtmlCollector::Constructor);
816 TestResultsCollectorBase::RegisterCollectorConstructor(
818 &CSVCollector::Constructor);
819 TestResultsCollectorBase::RegisterCollectorConstructor(
821 &TAPCollector::Constructor);
822 TestResultsCollectorBase::RegisterCollectorConstructor(
824 &XmlCollector::Constructor);