Move XML collector to separate files 91/28691/5
authorMarcin Niesluchowski <m.niesluchow@samsung.com>
Mon, 13 Oct 2014 09:09:48 +0000 (11:09 +0200)
committerMarcin Niesluchowski <m.niesluchow@samsung.com>
Tue, 21 Oct 2014 09:53:14 +0000 (11:53 +0200)
Change-Id: I7c82a9415bbf9969ab2bc5bc008878b8b61bbcc4

tests/framework/config.cmake
tests/framework/include/dpl/test/test_results_collector_commons.h [new file with mode: 0644]
tests/framework/include/dpl/test/test_results_collector_xml.h [new file with mode: 0644]
tests/framework/src/test_results_collector.cpp
tests/framework/src/test_results_collector_commons.cpp [new file with mode: 0644]
tests/framework/src/test_results_collector_xml.cpp [new file with mode: 0644]

index a2f407c..facbe2c 100644 (file)
@@ -33,6 +33,8 @@ SET(DPL_FRAMEWORK_TEST_SOURCES
     ${PROJECT_SOURCE_DIR}/tests/framework/src/log.cpp
     ${PROJECT_SOURCE_DIR}/tests/framework/src/old_style_log_provider.cpp
     ${PROJECT_SOURCE_DIR}/tests/framework/src/test_results_collector.cpp
+    ${PROJECT_SOURCE_DIR}/tests/framework/src/test_results_collector_commons.cpp
+    ${PROJECT_SOURCE_DIR}/tests/framework/src/test_results_collector_xml.cpp
     ${PROJECT_SOURCE_DIR}/tests/framework/src/test_runner_child.cpp
     ${PROJECT_SOURCE_DIR}/tests/framework/src/test_runner.cpp
     ${PROJECT_SOURCE_DIR}/tests/framework/src/test_runner_multiprocess.cpp
diff --git a/tests/framework/include/dpl/test/test_results_collector_commons.h b/tests/framework/include/dpl/test/test_results_collector_commons.h
new file mode 100644 (file)
index 0000000..09cf740
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        test_results_collector_commons.h
+ * @author      Marcin Niesluchowski (m.niesluchow@samsung.com)
+ * @version     1.0
+ * @brief       Header file containing declarations of collectors common
+ *              functions and macros
+ */
+
+#ifndef DPL_TEST_RESULTS_COLLECTOR_COMMONS_H
+#define DPL_TEST_RESULTS_COLLECTOR_COMMONS_H
+
+#include <string>
+
+namespace DPL {
+namespace Test {
+
+bool ParseCollectorFileArg(const std::string &arg, std::string &filename);
+
+} // namespace Test
+} // namespace DPL
+
+#endif // DPL_TEST_RESULTS_COLLECTOR_COMMONS_H
diff --git a/tests/framework/include/dpl/test/test_results_collector_xml.h b/tests/framework/include/dpl/test/test_results_collector_xml.h
new file mode 100644 (file)
index 0000000..be5fcec
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        test_results_collector_xml.h
+ * @author      Marcin Niesluchowski (m.niesluchow@samsung.com)
+ * @version     1.0
+ * @brief       Header file containing XmlCollector class declaration
+ */
+
+#ifndef DPL_TEST_RESULTS_COLLECTOR_XML_H
+#define DPL_TEST_RESULTS_COLLECTOR_XML_H
+
+#include <dpl/scoped_fclose.h>
+#include <dpl/test/statistic.h>
+#include <dpl/test/test_results_collector.h>
+
+namespace DPL {
+namespace Test {
+
+class XmlCollector :
+    public TestResultsCollectorBase
+{
+public:
+    static TestResultsCollectorBase* Constructor();
+
+private:
+    XmlCollector();
+
+    virtual std::string CollectorSpecificHelp() const;
+    virtual bool ParseCollectorSpecificArg(const std::string& arg);
+    virtual bool Configure();
+    virtual void Start();
+    virtual void CollectCurrentTestGroupName(const std::string& name);
+    virtual void CollectResult(const std::string& id,
+                               const std::string& /*description*/,
+                               const FailStatus::Type status = FailStatus::NONE,
+                               const std::string& reason = "",
+                               const bool& isPerformanceTest = false,
+                               const std::chrono::system_clock::duration& performanceTime
+                                   = std::chrono::microseconds::zero(),
+                               const std::chrono::system_clock::duration& performanceMaxTime
+                                   = std::chrono::microseconds::zero());
+    virtual void Finish();
+
+    void GroupStart(const std::size_t pos, const std::string& name);
+    std::size_t GetCurrentGroupPosition() const;
+    void UpdateGroupHeader(const std::size_t groupPosition,
+                           const unsigned int tests,
+                           const unsigned int failures,
+                           const unsigned int skipped);
+    void UpdateElementAttribute(const std::size_t elementPosition,
+                                const std::string& name,
+                                const std::string& value);
+    std::string UIntToString(const unsigned int value);
+    void GroupFinish(const std::size_t groupPosition);
+    void PrintfErrorMessage(const char* type,
+                            const std::string& message,
+                            bool verbosity);
+    void PrintfIgnoredMessage(const char* type,
+                              const std::string& message,
+                              bool verbosity);
+    void FlushOutput();
+    std::string EscapeSpecialCharacters(std::string s);
+
+    std::string m_filename;
+    ScopedFClose m_fp;
+    Statistic m_stats;
+    std::string m_outputBuffer;
+    std::string m_resultBuffer;
+};
+
+} // namespace Test
+} // namespace DPL
+
+#endif // DPL_TEST_RESULTS_COLLECTOR_XML_H
index 0e274e6..d158a28 100644 (file)
  */
 #include <dpl/test/statistic.h>
 #include <dpl/test/test_results_collector.h>
+#include <dpl/test/test_results_collector_commons.h>
+#include <dpl/test/test_results_collector_xml.h>
 #include <dpl/colors.h>
 #include <dpl/assert.h>
 #include <dpl/scoped_fclose.h>
-#include <dpl/exception.h>
 
 #include <string>
-#include <string.h>
-#include <errno.h>
 #include <cstdio>
-#include <sstream>
 
 #define GREEN_RESULT_OK "[%s%s%s]\n", BOLD_GREEN_BEGIN, "   OK   ", \
     BOLD_GREEN_END
@@ -56,17 +54,6 @@ namespace DPL {
 namespace Test {
 namespace {
 const char *DEFAULT_HTML_FILE_NAME = "index.html";
-const char *DEFAULT_XML_FILE_NAME = "results.xml";
-
-bool ParseCollectorFileArg(const std::string &arg, std::string &filename)
-{
-    const std::string argname = "--file=";
-    if (arg.find(argname) == 0 ) {
-        filename = arg.substr(argname.size());
-        return true;
-    }
-    return false;
-}
 
 class ConsoleCollector :
     public TestResultsCollectorBase
@@ -416,358 +403,6 @@ TestResultsCollectorBase* HtmlCollector::Constructor()
     return new HtmlCollector();
 }
 
-class XmlCollector :
-    public TestResultsCollectorBase
-{
-  public:
-    static TestResultsCollectorBase* Constructor();
-
-  private:
-    XmlCollector() : m_filename(DEFAULT_XML_FILE_NAME) {}
-
-    virtual void CollectCurrentTestGroupName(const std::string& name)
-    {
-        std::size_t pos = GetCurrentGroupPosition();
-        if (std::string::npos != pos) {
-            GroupFinish(pos);
-            FlushOutput();
-            m_stats = Statistic();
-        }
-
-        pos = m_outputBuffer.find("</testsuites>");
-        if (std::string::npos == pos) {
-            ThrowMsg(DPL::Exception, "Could not find test suites closing tag");
-        }
-        GroupStart(pos, name);
-    }
-
-    void GroupStart(const std::size_t pos, const std::string& name)
-    {
-        std::stringstream groupHeader;
-        groupHeader << "\n\t<testsuite";
-        groupHeader << " name=\"" << EscapeSpecialCharacters(name) << "\"";
-        groupHeader << R"( tests="1")"; // include SegFault
-        groupHeader << R"( failures="1")"; // include SegFault
-        groupHeader << R"( skipped="0")";
-        groupHeader << ">";
-
-        groupHeader << "\n\t\t<testcase name=\"unknown\" status=\"FAILED\">";
-        groupHeader <<
-        "\n\t\t\t<failure type=\"FAILED\" message=\"segmentation fault\"/>";
-        groupHeader << "\n\t\t</testcase>";
-
-        groupHeader << "\n\t</testsuite>";
-
-        m_outputBuffer.insert(pos - 1, groupHeader.str());
-    }
-
-    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>");
-        FlushOutput();
-    }
-
-    virtual void Finish()
-    {
-        std::size_t pos = GetCurrentGroupPosition();
-        if (std::string::npos != pos) {
-            GroupFinish(pos);
-            FlushOutput();
-        }
-    }
-
-    virtual bool ParseCollectorSpecificArg(const std::string& arg)
-    {
-        return ParseCollectorFileArg(arg, m_filename);
-    }
-
-    virtual void CollectResult(const std::string& id,
-                               const std::string& /*description*/,
-                               const FailStatus::Type status = FailStatus::NONE,
-                               const std::string& reason = "",
-                               const bool& isPerformanceTest = false,
-                               const std::chrono::system_clock::duration& performanceTime = std::chrono::microseconds::zero(),
-                               const std::chrono::system_clock::duration& performanceMaxTime = std::chrono::microseconds::zero())
-    {
-        m_resultBuffer.erase();
-        m_resultBuffer.append("\t\t<testcase name=\"");
-        m_resultBuffer.append(EscapeSpecialCharacters(id));
-        m_resultBuffer.append("\"");
-        switch (status) {
-        case TestResultsCollectorBase::FailStatus::NONE:
-            if (isPerformanceTest) {
-                if (performanceMaxTime <= std::chrono::microseconds::zero()) {
-                    m_resultBuffer.append(" status=\"OK\" time=\"");
-                    std::ostringstream ostr;
-                    ostr << performanceTime.count();
-                    m_resultBuffer.append(ostr.str());
-                    m_resultBuffer.append("\"/>\n");
-                    break;
-                } else {
-                    m_resultBuffer.append(" status=\"OK\" time=\"");
-                    std::ostringstream ostr;
-                    ostr << performanceTime.count();
-                    m_resultBuffer.append(ostr.str());
-                    m_resultBuffer.append("\" time_expected=\"");
-                    ostr.str("");
-                    ostr << performanceMaxTime.count();
-                    m_resultBuffer.append(ostr.str());
-                    m_resultBuffer.append("\"/>\n");
-                    break;
-                }
-            }
-            m_resultBuffer.append(" status=\"OK\"/>\n");
-            break;
-        case TestResultsCollectorBase::FailStatus::FAILED:
-            m_resultBuffer.append(" status=\"FAILED\">\n");
-            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=\"FAILED\">\n");
-            PrintfErrorMessage("INTERNAL", EscapeSpecialCharacters(
-                                   reason), true);
-            m_resultBuffer.append("\t\t</testcase>");
-            break;
-        default:
-            Assert(false && "Bad status");
-        }
-        std::size_t group_pos = GetCurrentGroupPosition();
-        if (std::string::npos == group_pos) {
-            ThrowMsg(DPL::Exception, "No current group set");
-        }
-
-        std::size_t last_case_pos = m_outputBuffer.find(
-                "<testcase name=\"unknown\"",
-                group_pos);
-        if (std::string::npos == last_case_pos) {
-            ThrowMsg(DPL::Exception, "Could not find SegFault test case");
-        }
-        m_outputBuffer.insert(last_case_pos - 2, m_resultBuffer);
-
-        m_stats.AddTest(status);
-
-        UpdateGroupHeader(group_pos,
-                          m_stats.GetTotal() + 1, // include SegFault
-                          m_stats.GetFailed() + 1, // include SegFault
-                          m_stats.GetIgnored());
-        FlushOutput();
-    }
-
-    std::size_t GetCurrentGroupPosition() const
-    {
-        return m_outputBuffer.rfind("<testsuite ");
-    }
-
-    void UpdateGroupHeader(const std::size_t groupPosition,
-                           const unsigned int tests,
-                           const unsigned int failures,
-                           const unsigned int skipped)
-    {
-        UpdateElementAttribute(groupPosition, "tests", UIntToString(tests));
-        UpdateElementAttribute(groupPosition, "failures", UIntToString(failures));
-        UpdateElementAttribute(groupPosition, "skipped", UIntToString(skipped));
-    }
-
-    void UpdateElementAttribute(const std::size_t elementPosition,
-                                const std::string& name,
-                                const std::string& value)
-    {
-        std::string pattern = name + "=\"";
-
-        std::size_t start = m_outputBuffer.find(pattern, elementPosition);
-        if (std::string::npos == start) {
-            ThrowMsg(DPL::Exception,
-                     "Could not find attribute " << name << " beginning");
-        }
-
-        std::size_t end = m_outputBuffer.find("\"", start + pattern.length());
-        if (std::string::npos == end) {
-            ThrowMsg(DPL::Exception,
-                     "Could not find attribute " << name << " end");
-        }
-
-        m_outputBuffer.replace(start + pattern.length(),
-                               end - start - pattern.length(),
-                               value);
-    }
-
-    std::string UIntToString(const unsigned int value)
-    {
-        std::stringstream result;
-        result << value;
-        return result.str();
-    }
-
-    void GroupFinish(const std::size_t groupPosition)
-    {
-        std::size_t segFaultStart =
-            m_outputBuffer.find("<testcase name=\"unknown\"", groupPosition);
-        if (std::string::npos == segFaultStart) {
-            ThrowMsg(DPL::Exception,
-                     "Could not find SegFault test case start position");
-        }
-        segFaultStart -= 2; // to erase tabs
-
-        std::string closeTag = "</testcase>";
-        std::size_t segFaultEnd = m_outputBuffer.find(closeTag, segFaultStart);
-        if (std::string::npos == segFaultEnd) {
-            ThrowMsg(DPL::Exception,
-                     "Could not find SegFault test case end position");
-        }
-        segFaultEnd += closeTag.length() + 1; // to erase new line
-
-        m_outputBuffer.erase(segFaultStart, segFaultEnd - segFaultStart);
-
-        UpdateGroupHeader(groupPosition,
-                          m_stats.GetTotal(),
-                          m_stats.GetFailed(),
-                          m_stats.GetIgnored());
-    }
-
-    void FlushOutput()
-    {
-        int fd = fileno(m_fp.Get());
-        if (-1 == fd) {
-            const char* errStr = strerror(errno);
-            ThrowMsg(DPL::Exception, errStr);
-        }
-
-        if (-1 == TEMP_FAILURE_RETRY(ftruncate(fd, 0L))) {
-            const char* errStr = strerror(errno);
-            ThrowMsg(DPL::Exception, errStr);
-        }
-
-        if (-1 == TEMP_FAILURE_RETRY(fseek(m_fp.Get(), 0L, SEEK_SET))) {
-            const char* errStr = strerror(errno);
-            ThrowMsg(DPL::Exception, errStr);
-        }
-
-        if (m_outputBuffer.size() !=
-            fwrite(m_outputBuffer.c_str(), 1, m_outputBuffer.size(),
-                   m_fp.Get()))
-        {
-            const char* errStr = strerror(errno);
-            ThrowMsg(DPL::Exception, errStr);
-        }
-
-        if (-1 == TEMP_FAILURE_RETRY(fflush(m_fp.Get()))) {
-            const char* errStr = strerror(errno);
-            ThrowMsg(DPL::Exception, errStr);
-        }
-    }
-
-    void PrintfErrorMessage(const char* type,
-                            const std::string& message,
-                            bool verbosity)
-    {
-        if (verbosity) {
-            m_resultBuffer.append("\t\t\t<failure 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<failure 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");
-        }
-    }
-
-    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, "&quot;");
-                i += 6;
-                break;
-
-            case '&':
-                s.erase(i, 1);
-                s.insert(i, "&amp;");
-                i += 5;
-                break;
-
-            case '<':
-                s.erase(i, 1);
-                s.insert(i, "&lt;");
-                i += 4;
-                break;
-
-            case '>':
-                s.erase(i, 1);
-                s.insert(i, "&gt;");
-                i += 4;
-                break;
-
-            case '\'':
-                s.erase(i, 1);
-                s.insert(i, "&#39;");
-                i += 5;
-                break;
-            default:
-                ++i;
-                break;
-            }
-        }
-        return s;
-    }
-
-    std::string m_filename;
-    ScopedFClose m_fp;
-    Statistic m_stats;
-    std::string m_outputBuffer;
-    std::string m_resultBuffer;
-};
-
-TestResultsCollectorBase* XmlCollector::Constructor()
-{
-    return new XmlCollector();
-}
-
 }
 
 void TestResultsCollectorBase::RegisterCollectorConstructor(
diff --git a/tests/framework/src/test_results_collector_commons.cpp b/tests/framework/src/test_results_collector_commons.cpp
new file mode 100644 (file)
index 0000000..ad2c0a0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        test_results_collector_commons.cpp
+ * @author      Lukasz Wrzosek (l.wrzosek@samsung.com)
+ * @author      Marcin Niesluchowski (m.niesluchow@samsung.com)
+ * @version     1.0
+ * @brief       Source file containing definitions of collectors common
+ *              functions
+ */
+
+#include "dpl/test/test_results_collector_commons.h"
+
+namespace DPL {
+namespace Test {
+
+bool ParseCollectorFileArg(const std::string &arg, std::string &filename)
+{
+    const std::string argname = "--file=";
+    if (arg.find(argname) == 0 ) {
+        filename = arg.substr(argname.size());
+        return true;
+    }
+    return false;
+}
+
+} // namespace Test
+} // namespace DPL
+
diff --git a/tests/framework/src/test_results_collector_xml.cpp b/tests/framework/src/test_results_collector_xml.cpp
new file mode 100644 (file)
index 0000000..28d9bbd
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        test_results_collector_xml.cpp
+ * @author      Lukasz Wrzosek (l.wrzosek@samsung.com)
+ * @author      Marcin Niesluchowski (m.niesluchow@samsung.com)
+ * @version     1.0
+ * @brief       Source file containing XmlCollector class definition
+ */
+
+#include <cerrno>
+#include <cstdio>
+#include <cstring>
+#include <sstream>
+
+#include <dpl/assert.h>
+#include <dpl/exception.h>
+#include <dpl/test/test_results_collector_commons.h>
+
+#include "dpl/test/test_results_collector_xml.h"
+
+namespace DPL {
+namespace Test {
+
+namespace {
+
+const char *DEFAULT_XML_FILE_NAME = "results.xml";
+
+}
+
+XmlCollector::XmlCollector()
+    : m_filename(DEFAULT_XML_FILE_NAME)
+{
+}
+
+TestResultsCollectorBase* XmlCollector::Constructor()
+{
+     return new XmlCollector();
+}
+
+void XmlCollector::CollectCurrentTestGroupName(const std::string& name)
+{
+    std::size_t pos = GetCurrentGroupPosition();
+    if (std::string::npos != pos) {
+        GroupFinish(pos);
+        FlushOutput();
+        m_stats = Statistic();
+     }
+
+    pos = m_outputBuffer.find("</testsuites>");
+    if (std::string::npos == pos) {
+        ThrowMsg(DPL::Exception, "Could not find test suites closing tag");
+    }
+    GroupStart(pos, name);
+}
+
+void XmlCollector::GroupStart(const std::size_t pos, const std::string& name)
+{
+    std::stringstream groupHeader;
+    groupHeader << "\n\t<testsuite";
+    groupHeader << " name=\"" << EscapeSpecialCharacters(name) << "\"";
+    groupHeader << R"( tests="1")"; // include SegFault
+    groupHeader << R"( failures="1")"; // include SegFault
+    groupHeader << R"( skipped="0")";
+    groupHeader << ">";
+
+    groupHeader << "\n\t\t<testcase name=\"unknown\" status=\"FAILED\">";
+    groupHeader <<
+    "\n\t\t\t<failure type=\"FAILED\" message=\"segmentation fault\"/>";
+    groupHeader << "\n\t\t</testcase>";
+
+    groupHeader << "\n\t</testsuite>";
+
+    m_outputBuffer.insert(pos - 1, groupHeader.str());
+}
+
+bool XmlCollector::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;
+}
+
+std::string XmlCollector::CollectorSpecificHelp() const
+{
+    return "--file=<filename> - name of file for output\n"
+           "                    default - results.xml\n";
+}
+
+void XmlCollector::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>");
+    FlushOutput();
+}
+
+void XmlCollector::Finish()
+{
+    std::size_t pos = GetCurrentGroupPosition();
+    if (std::string::npos != pos) {
+        GroupFinish(pos);
+        FlushOutput();
+    }
+}
+
+bool XmlCollector::ParseCollectorSpecificArg(const std::string& arg)
+{
+    return ParseCollectorFileArg(arg, m_filename);
+}
+
+void XmlCollector::CollectResult(const std::string& id,
+                                 const std::string&,
+                                 const FailStatus::Type status,
+                                 const std::string& reason,
+                                 const bool& isPerformanceTest,
+                                 const std::chrono::system_clock::duration& performanceTime,
+                                 const std::chrono::system_clock::duration& performanceMaxTime)
+{
+    m_resultBuffer.erase();
+    m_resultBuffer.append("\t\t<testcase name=\"");
+    m_resultBuffer.append(EscapeSpecialCharacters(id));
+    m_resultBuffer.append("\"");
+    switch (status) {
+    case TestResultsCollectorBase::FailStatus::NONE:
+        if (isPerformanceTest) {
+            if (performanceMaxTime <= std::chrono::microseconds::zero()) {
+                m_resultBuffer.append(" status=\"OK\" time=\"");
+                std::ostringstream ostr;
+                ostr << performanceTime.count();
+                m_resultBuffer.append(ostr.str());
+                m_resultBuffer.append("\"/>\n");
+                break;
+            } else {
+                m_resultBuffer.append(" status=\"OK\" time=\"");
+                std::ostringstream ostr;
+                ostr << performanceTime.count();
+                m_resultBuffer.append(ostr.str());
+                m_resultBuffer.append("\" time_expected=\"");
+                ostr.str("");
+                ostr << performanceMaxTime.count();
+                m_resultBuffer.append(ostr.str());
+                m_resultBuffer.append("\"/>\n");
+                break;
+            }
+        }
+        m_resultBuffer.append(" status=\"OK\"/>\n");
+        break;
+    case TestResultsCollectorBase::FailStatus::FAILED:
+        m_resultBuffer.append(" status=\"FAILED\">\n");
+        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=\"FAILED\">\n");
+        PrintfErrorMessage("INTERNAL", EscapeSpecialCharacters(
+                               reason), true);
+        m_resultBuffer.append("\t\t</testcase>");
+        break;
+    default:
+        Assert(false && "Bad status");
+    }
+    std::size_t group_pos = GetCurrentGroupPosition();
+    if (std::string::npos == group_pos) {
+        ThrowMsg(DPL::Exception, "No current group set");
+    }
+
+    std::size_t last_case_pos = m_outputBuffer.find(
+            "<testcase name=\"unknown\"",
+            group_pos);
+    if (std::string::npos == last_case_pos) {
+        ThrowMsg(DPL::Exception, "Could not find SegFault test case");
+    }
+    m_outputBuffer.insert(last_case_pos - 2, m_resultBuffer);
+
+    m_stats.AddTest(status);
+
+    UpdateGroupHeader(group_pos,
+                      m_stats.GetTotal() + 1, // include SegFault
+                      m_stats.GetFailed() + 1, // include SegFault
+                      m_stats.GetIgnored());
+    FlushOutput();
+}
+
+std::size_t XmlCollector::GetCurrentGroupPosition() const
+{
+    return m_outputBuffer.rfind("<testsuite ");
+}
+
+void XmlCollector::UpdateGroupHeader(const std::size_t groupPosition,
+                                     const unsigned int tests,
+                                     const unsigned int failures,
+                                     const unsigned int skipped)
+{
+    UpdateElementAttribute(groupPosition, "tests", UIntToString(tests));
+    UpdateElementAttribute(groupPosition, "failures", UIntToString(failures));
+    UpdateElementAttribute(groupPosition, "skipped", UIntToString(skipped));
+}
+
+void XmlCollector::UpdateElementAttribute(const std::size_t elementPosition,
+                                          const std::string& name,
+                                          const std::string& value)
+{
+    std::string pattern = name + "=\"";
+
+    std::size_t start = m_outputBuffer.find(pattern, elementPosition);
+    if (std::string::npos == start) {
+        ThrowMsg(DPL::Exception,
+                 "Could not find attribute " << name << " beginning");
+    }
+
+    std::size_t end = m_outputBuffer.find("\"", start + pattern.length());
+    if (std::string::npos == end) {
+        ThrowMsg(DPL::Exception,
+                 "Could not find attribute " << name << " end");
+    }
+
+    m_outputBuffer.replace(start + pattern.length(),
+                           end - start - pattern.length(),
+                           value);
+}
+
+std::string XmlCollector::UIntToString(const unsigned int value)
+{
+    std::stringstream result;
+    result << value;
+    return result.str();
+}
+
+void XmlCollector::GroupFinish(const std::size_t groupPosition)
+{
+    std::size_t segFaultStart =
+        m_outputBuffer.find("<testcase name=\"unknown\"", groupPosition);
+    if (std::string::npos == segFaultStart) {
+        ThrowMsg(DPL::Exception,
+                 "Could not find SegFault test case start position");
+    }
+    segFaultStart -= 2; // to erase tabs
+
+    std::string closeTag = "</testcase>";
+    std::size_t segFaultEnd = m_outputBuffer.find(closeTag, segFaultStart);
+    if (std::string::npos == segFaultEnd) {
+        ThrowMsg(DPL::Exception,
+                 "Could not find SegFault test case end position");
+    }
+    segFaultEnd += closeTag.length() + 1; // to erase new line
+
+    m_outputBuffer.erase(segFaultStart, segFaultEnd - segFaultStart);
+
+    UpdateGroupHeader(groupPosition,
+                      m_stats.GetTotal(),
+                      m_stats.GetFailed(),
+                      m_stats.GetIgnored());
+}
+
+void XmlCollector::FlushOutput()
+{
+    int fd = fileno(m_fp.Get());
+    if (-1 == fd) {
+        const char* errStr = strerror(errno);
+        ThrowMsg(DPL::Exception, errStr);
+    }
+
+    if (-1 == TEMP_FAILURE_RETRY(ftruncate(fd, 0L))) {
+        const char* errStr = strerror(errno);
+        ThrowMsg(DPL::Exception, errStr);
+    }
+
+    if (-1 == TEMP_FAILURE_RETRY(fseek(m_fp.Get(), 0L, SEEK_SET))) {
+        const char* errStr = strerror(errno);
+        ThrowMsg(DPL::Exception, errStr);
+    }
+
+    if (m_outputBuffer.size() !=
+        fwrite(m_outputBuffer.c_str(), 1, m_outputBuffer.size(),
+               m_fp.Get()))
+    {
+        const char* errStr = strerror(errno);
+        ThrowMsg(DPL::Exception, errStr);
+    }
+
+    if (-1 == TEMP_FAILURE_RETRY(fflush(m_fp.Get()))) {
+        const char* errStr = strerror(errno);
+        ThrowMsg(DPL::Exception, errStr);
+    }
+}
+
+void XmlCollector::PrintfErrorMessage(const char* type,
+                                      const std::string& message,
+                                      bool verbosity)
+{
+    if (verbosity) {
+        m_resultBuffer.append("\t\t\t<failure 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<failure type=\"");
+        m_resultBuffer.append(EscapeSpecialCharacters(type));
+        m_resultBuffer.append("\"/>\n");
+    }
+}
+
+void XmlCollector::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");
+    }
+}
+
+std::string XmlCollector::EscapeSpecialCharacters(std::string s)
+{
+    for (unsigned int i = 0; i < s.size();) {
+        switch (s[i]) {
+        case '"':
+            s.erase(i, 1);
+            s.insert(i, "&quot;");
+            i += 6;
+            break;
+
+        case '&':
+            s.erase(i, 1);
+            s.insert(i, "&amp;");
+            i += 5;
+            break;
+
+        case '<':
+            s.erase(i, 1);
+            s.insert(i, "&lt;");
+            i += 4;
+            break;
+
+        case '>':
+            s.erase(i, 1);
+            s.insert(i, "&gt;");
+            i += 4;
+            break;
+
+        case '\'':
+            s.erase(i, 1);
+            s.insert(i, "&#39;");
+            i += 5;
+            break;
+        default:
+            ++i;
+            break;
+        }
+    }
+    return s;
+}
+
+} // namespace Test
+} // namespace DPL