${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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
*/
#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
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
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, """);
- 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_outputBuffer;
- std::string m_resultBuffer;
-};
-
-TestResultsCollectorBase* XmlCollector::Constructor()
-{
- return new XmlCollector();
-}
-
}
void TestResultsCollectorBase::RegisterCollectorConstructor(
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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, """);
+ 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;
+}
+
+} // namespace Test
+} // namespace DPL