+
+class XmlCollector
+ : public TestResultsCollectorBase
+{
+ public:
+ static TestResultsCollectorBase* Constructor();
+
+ private:
+ XmlCollector() : m_filename(DEFAULT_XML_FILE_NAME) {}
+
+ virtual void CollectCurrentTestGroupName(const std::string& name)
+ {
+ std::string groupHeader;
+ groupHeader.append("\n\t<testsuite name=\"");
+ groupHeader.append(EscapeSpecialCharacters(name));
+ groupHeader.append("\">\n\t\t<testcase name=\"unknown\" status=\"FAILED\">\n");
+ groupHeader.append("\t\t\t<failure type=\"FAILED\" message=\"segmentation fault\"/>\n");
+ groupHeader.append("\t\t</testcase>\n\t</testsuite>");
+ size_t pos = m_outputBuffer.find("</testsuites>");
+ m_outputBuffer.insert(pos - 1, groupHeader);
+ m_currentGroup = name;
+ fseek(m_fp.Get(), 0L, SEEK_SET);
+ fprintf(m_fp.Get(), "%s", m_outputBuffer.c_str());
+ fflush(m_fp.Get());
+
+ }
+
+ virtual bool Configure()
+ {
+ m_fp.Reset(fopen (m_filename.c_str(), "w"));
+ if (!m_fp) {
+ LogPedantic("Could not open file " << m_filename << " for writing");
+ return false;
+ }
+ return true;
+ }
+
+ virtual std::string CollectorSpecificHelp() const
+ {
+ return "--file=<filename> - name of file for output\n"
+ " default - results.xml\n";
+ }
+
+ virtual void Start()
+ {
+ Assert(!!m_fp && "File handle must not be null");
+ m_outputBuffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ m_outputBuffer.append("<testsuites>\n</testsuites>");
+ fseek(m_fp.Get(), 0L, SEEK_SET);
+ fprintf(m_fp.Get(), "%s", m_outputBuffer.c_str());
+ fflush(m_fp.Get());
+
+ }
+
+ virtual void Finish()
+ {
+ // Show result
+ FOREACH(group, m_groupsStats) {
+ PrintStats(group->first, group->second);
+ }
+
+ size_t posBegin = m_outputBuffer.find("<testcase name=\"unknown\"");
+ size_t posEnd = m_outputBuffer.find("</testcase>", posBegin);
+ m_outputBuffer.erase(posBegin - 3, posEnd - posBegin + sizeof("</testcase>") + 2);
+ remove(m_filename.c_str());
+ m_fp.Reset(fopen (m_filename.c_str(), "w"));
+ Assert(!!m_fp && "File handle must not be null");
+ fseek(m_fp.Get(), 0L, SEEK_SET);
+ fprintf(m_fp.Get(),"%s", m_outputBuffer.c_str());
+ fflush(m_fp.Get());
+ }
+
+ virtual bool ParseCollectorSpecificArg(const std::string& arg)
+ {
+ const std::string argname = "--file=";
+ if (0 == arg.find(argname)) {
+ m_filename = arg.substr(argname.size());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ virtual void CollectResult(const std::string& id,
+ const std::string& /*description*/,
+ const FailStatus::Type status = FailStatus::NONE,
+ const std::string& reason = "")
+ {
+ m_resultBuffer.erase();
+ m_resultBuffer.append("\t\t<testcase name=\"");
+ m_resultBuffer.append(EscapeSpecialCharacters(id));
+ m_resultBuffer.append("\"");
+ switch(status) {
+ case TestResultsCollectorBase::FailStatus::NONE:
+ m_resultBuffer.append(" status=\"OK\"/>\n");
+ break;
+ case TestResultsCollectorBase::FailStatus::FAILED:
+ m_resultBuffer.append(" status=\"FAILED\">\n\t\t\t<failure");
+ PrintfErrorMessage("FAILED", EscapeSpecialCharacters(reason), true);
+ m_resultBuffer.append("\t\t</testcase>\n");
+ break;
+ case TestResultsCollectorBase::FailStatus::IGNORED:
+ m_resultBuffer.append(" status=\"Ignored\">\n");
+ PrintfIgnoredMessage("Ignored", EscapeSpecialCharacters(reason), true);
+ m_resultBuffer.append("\t\t</testcase>\n");
+ break;
+ case TestResultsCollectorBase::FailStatus::INTERNAL:
+ m_resultBuffer.append(" status=\"INTERNAL\">\n\t\t\t<failure");
+ PrintfErrorMessage("INTERNAL", EscapeSpecialCharacters(reason), true);
+ m_resultBuffer.append("\t\t</testcase>");
+ break;
+ default:
+ Assert(false && "Bad status");
+ }
+ size_t pos = m_outputBuffer.find("<testcase name=\"unknown\"");
+ m_outputBuffer.insert(pos - 2, m_resultBuffer);
+ fseek(m_fp.Get(), 0L, SEEK_SET);
+ fprintf(m_fp.Get(), "%s", m_outputBuffer.c_str());
+ fflush(m_fp.Get());
+ m_groupsStats[m_currentGroup].AddTest(status);
+ m_stats.AddTest(status);
+
+ }
+
+ void PrintfErrorMessage(const char* type,
+ const std::string& message,
+ bool verbosity)
+ {
+ if (verbosity) {
+ m_resultBuffer.append(" type=\"");
+ m_resultBuffer.append(EscapeSpecialCharacters(type));
+ m_resultBuffer.append("\" message=\"");
+ m_resultBuffer.append(EscapeSpecialCharacters(message));
+ m_resultBuffer.append("\"/>\n");
+
+ } else {
+
+ m_resultBuffer.append(" type=\"");
+ m_resultBuffer.append(EscapeSpecialCharacters(type));
+ m_resultBuffer.append("\"/>\n");
+ }
+ }
+
+ void PrintfIgnoredMessage(const char* type,
+ const std::string& message,
+ bool verbosity)
+ {
+ if (verbosity) {
+ m_resultBuffer.append("\t\t\t<skipped type=\"");
+ m_resultBuffer.append(EscapeSpecialCharacters(type));
+ m_resultBuffer.append("\" message=\"");
+ m_resultBuffer.append(EscapeSpecialCharacters(message));
+ m_resultBuffer.append("\"/>\n");
+ } else {
+
+ m_resultBuffer.append("\t\t\t<skipped type=\"");
+ m_resultBuffer.append(EscapeSpecialCharacters(type));
+ m_resultBuffer.append("\"/>\n");
+ }
+ }
+
+ void PrintStats(const std::string& name, const Statistic& stats)
+ {
+ std::stringstream totalStats;
+ totalStats << " tests=\"";
+ totalStats << stats.GetTotal();
+ totalStats << "\" failures=\"";
+ totalStats << stats.GetFailed();
+ totalStats << "\" skipped=\"";
+ totalStats << stats.GetIgnored();
+ totalStats << "\"";
+ std::string suiteHeader;
+ suiteHeader.append("<testsuite name=\"");
+ suiteHeader.append(EscapeSpecialCharacters(name));
+ size_t pos = m_outputBuffer.find(suiteHeader);
+ m_outputBuffer.insert(pos+suiteHeader.size()+1,totalStats.str());
+ }
+
+ std::string EscapeSpecialCharacters(std::string s)
+ {
+ for(unsigned int i = 0; i < s.size();){
+ switch(s[i]){
+ case '"':
+ s.erase(i,1);
+ s.insert(i, """);
+ i+=6;
+ break;
+
+ case '&':
+ s.erase(i,1);
+ s.insert(i, "&");
+ i+=5;
+ break;
+
+ case '<':
+ s.erase(i,1);
+ s.insert(i, "<");
+ i+=4;
+ break;
+
+ case '>':
+ s.erase(i,1);
+ s.insert(i, ">");
+ i+=4;
+ break;
+
+ case '\'':
+ s.erase(i,1);
+ s.insert(i, "'");
+ i+=5;
+ break;
+ default:
+ ++i;
+ break;
+ }
+ }
+ return s;
+ }
+
+ std::string m_filename;
+ ScopedFClose m_fp;
+ Statistic m_stats;
+ std::string m_currentGroup;
+ std::map<std::string, Statistic> m_groupsStats;
+ std::string m_outputBuffer;
+ std::string m_resultBuffer;
+};
+
+TestResultsCollectorBase* XmlCollector::Constructor()
+{
+ return new XmlCollector();
+}
+
+
+