2 * Copyright (c) 2014 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_xml.cpp
18 * @author Lukasz Wrzosek (l.wrzosek@samsung.com)
19 * @author Marcin Niesluchowski (m.niesluchow@samsung.com)
21 * @brief Source file containing XmlCollector class definition
29 #include <dpl/assert.h>
30 #include <dpl/exception.h>
31 #include <dpl/test/test_results_collector_commons.h>
33 #include "dpl/test/test_results_collector_xml.h"
40 const char *DEFAULT_XML_FILE_NAME = "results.xml";
44 XmlCollector::XmlCollector()
45 : m_filename(DEFAULT_XML_FILE_NAME)
49 TestResultsCollectorBase* XmlCollector::Constructor()
51 return new XmlCollector();
54 void XmlCollector::CollectCurrentTestGroupName(const std::string& name)
56 std::size_t pos = GetCurrentGroupPosition();
57 if (std::string::npos != pos) {
60 m_stats = Statistic();
63 pos = m_outputBuffer.find("</testsuites>");
64 if (std::string::npos == pos) {
65 ThrowMsg(DPL::Exception, "Could not find test suites closing tag");
67 GroupStart(pos, name);
70 void XmlCollector::GroupStart(const std::size_t pos, const std::string& name)
72 std::stringstream groupHeader;
73 groupHeader << "\n\t<testsuite";
74 groupHeader << " name=\"" << EscapeSpecialCharacters(name) << "\"";
75 groupHeader << R"( tests="1")"; // include SegFault
76 groupHeader << R"( failures="1")"; // include SegFault
77 groupHeader << R"( skipped="0")";
80 groupHeader << "\n\t\t<testcase name=\"unknown\" status=\"FAILED\">";
82 "\n\t\t\t<failure type=\"FAILED\" message=\"segmentation fault\"/>";
83 groupHeader << "\n\t\t</testcase>";
85 groupHeader << "\n\t</testsuite>";
87 m_outputBuffer.insert(pos - 1, groupHeader.str());
90 bool XmlCollector::Configure()
92 m_fp.Reset(fopen(m_filename.c_str(), "w"));
94 LogPedantic("Could not open file " << m_filename << " for writing");
100 std::string XmlCollector::CollectorSpecificHelp() const
102 return "--file=<filename> - name of file for output\n"
103 " default - results.xml\n";
106 void XmlCollector::Start()
108 Assert(!!m_fp && "File handle must not be null");
109 m_outputBuffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
110 m_outputBuffer.append("<testsuites>\n</testsuites>");
114 void XmlCollector::Finish()
116 std::size_t pos = GetCurrentGroupPosition();
117 if (std::string::npos != pos) {
123 bool XmlCollector::ParseCollectorSpecificArg(const std::string& arg)
125 return ParseCollectorFileArg(arg, m_filename);
128 void XmlCollector::CollectResult(const std::string& id,
130 const FailStatus::Type status,
131 const std::string& reason,
132 const bool& isPerformanceTest,
133 const std::chrono::system_clock::duration& performanceTime,
134 const std::chrono::system_clock::duration& performanceMaxTime)
136 m_resultBuffer.erase();
137 m_resultBuffer.append("\t\t<testcase name=\"");
138 m_resultBuffer.append(EscapeSpecialCharacters(id));
139 m_resultBuffer.append("\"");
141 case TestResultsCollectorBase::FailStatus::NONE:
142 if (isPerformanceTest) {
143 if (performanceMaxTime <= std::chrono::microseconds::zero()) {
144 m_resultBuffer.append(" status=\"OK\" time=\"");
145 std::ostringstream ostr;
146 ostr << performanceTime.count();
147 m_resultBuffer.append(ostr.str());
148 m_resultBuffer.append("\"/>\n");
151 m_resultBuffer.append(" status=\"OK\" time=\"");
152 std::ostringstream ostr;
153 ostr << performanceTime.count();
154 m_resultBuffer.append(ostr.str());
155 m_resultBuffer.append("\" time_expected=\"");
157 ostr << performanceMaxTime.count();
158 m_resultBuffer.append(ostr.str());
159 m_resultBuffer.append("\"/>\n");
163 m_resultBuffer.append(" status=\"OK\"/>\n");
165 case TestResultsCollectorBase::FailStatus::FAILED:
166 m_resultBuffer.append(" status=\"FAILED\">\n");
167 PrintfErrorMessage("FAILED", EscapeSpecialCharacters(reason), true);
168 m_resultBuffer.append("\t\t</testcase>\n");
170 case TestResultsCollectorBase::FailStatus::IGNORED:
171 m_resultBuffer.append(" status=\"Ignored\">\n");
172 PrintfIgnoredMessage("Ignored", EscapeSpecialCharacters(
174 m_resultBuffer.append("\t\t</testcase>\n");
177 Assert(false && "Bad status");
179 std::size_t group_pos = GetCurrentGroupPosition();
180 if (std::string::npos == group_pos) {
181 ThrowMsg(DPL::Exception, "No current group set");
184 std::size_t last_case_pos = m_outputBuffer.find(
185 "<testcase name=\"unknown\"",
187 if (std::string::npos == last_case_pos) {
188 ThrowMsg(DPL::Exception, "Could not find SegFault test case");
190 m_outputBuffer.insert(last_case_pos - 2, m_resultBuffer);
192 m_stats.AddTest(status);
194 UpdateGroupHeader(group_pos,
195 m_stats.GetTotal() + 1, // include SegFault
196 m_stats.GetFailed() + 1, // include SegFault
197 m_stats.GetIgnored());
201 std::size_t XmlCollector::GetCurrentGroupPosition() const
203 return m_outputBuffer.rfind("<testsuite ");
206 void XmlCollector::UpdateGroupHeader(const std::size_t groupPosition,
207 const unsigned int tests,
208 const unsigned int failures,
209 const unsigned int skipped)
211 UpdateElementAttribute(groupPosition, "tests", UIntToString(tests));
212 UpdateElementAttribute(groupPosition, "failures", UIntToString(failures));
213 UpdateElementAttribute(groupPosition, "skipped", UIntToString(skipped));
216 void XmlCollector::UpdateElementAttribute(const std::size_t elementPosition,
217 const std::string& name,
218 const std::string& value)
220 std::string pattern = name + "=\"";
222 std::size_t start = m_outputBuffer.find(pattern, elementPosition);
223 if (std::string::npos == start) {
224 ThrowMsg(DPL::Exception,
225 "Could not find attribute " << name << " beginning");
228 std::size_t end = m_outputBuffer.find("\"", start + pattern.length());
229 if (std::string::npos == end) {
230 ThrowMsg(DPL::Exception,
231 "Could not find attribute " << name << " end");
234 m_outputBuffer.replace(start + pattern.length(),
235 end - start - pattern.length(),
239 std::string XmlCollector::UIntToString(const unsigned int value)
241 std::stringstream result;
246 void XmlCollector::GroupFinish(const std::size_t groupPosition)
248 std::size_t segFaultStart =
249 m_outputBuffer.find("<testcase name=\"unknown\"", groupPosition);
250 if (std::string::npos == segFaultStart) {
251 ThrowMsg(DPL::Exception,
252 "Could not find SegFault test case start position");
254 segFaultStart -= 2; // to erase tabs
256 std::string closeTag = "</testcase>";
257 std::size_t segFaultEnd = m_outputBuffer.find(closeTag, segFaultStart);
258 if (std::string::npos == segFaultEnd) {
259 ThrowMsg(DPL::Exception,
260 "Could not find SegFault test case end position");
262 segFaultEnd += closeTag.length() + 1; // to erase new line
264 m_outputBuffer.erase(segFaultStart, segFaultEnd - segFaultStart);
266 UpdateGroupHeader(groupPosition,
269 m_stats.GetIgnored());
272 void XmlCollector::FlushOutput()
274 int fd = fileno(m_fp.Get());
276 const char* errStr = strerror(errno);
277 ThrowMsg(DPL::Exception, errStr);
280 if (-1 == TEMP_FAILURE_RETRY(ftruncate(fd, 0L))) {
281 const char* errStr = strerror(errno);
282 ThrowMsg(DPL::Exception, errStr);
285 if (-1 == TEMP_FAILURE_RETRY(fseek(m_fp.Get(), 0L, SEEK_SET))) {
286 const char* errStr = strerror(errno);
287 ThrowMsg(DPL::Exception, errStr);
290 if (m_outputBuffer.size() !=
291 fwrite(m_outputBuffer.c_str(), 1, m_outputBuffer.size(),
294 const char* errStr = strerror(errno);
295 ThrowMsg(DPL::Exception, errStr);
298 if (-1 == TEMP_FAILURE_RETRY(fflush(m_fp.Get()))) {
299 const char* errStr = strerror(errno);
300 ThrowMsg(DPL::Exception, errStr);
304 void XmlCollector::PrintfErrorMessage(const char* type,
305 const std::string& message,
309 m_resultBuffer.append("\t\t\t<failure type=\"");
310 m_resultBuffer.append(EscapeSpecialCharacters(type));
311 m_resultBuffer.append("\" message=\"");
312 m_resultBuffer.append(EscapeSpecialCharacters(message));
313 m_resultBuffer.append("\"/>\n");
315 m_resultBuffer.append("\t\t\t<failure type=\"");
316 m_resultBuffer.append(EscapeSpecialCharacters(type));
317 m_resultBuffer.append("\"/>\n");
321 void XmlCollector::PrintfIgnoredMessage(const char* type,
322 const std::string& message,
326 m_resultBuffer.append("\t\t\t<skipped type=\"");
327 m_resultBuffer.append(EscapeSpecialCharacters(type));
328 m_resultBuffer.append("\" message=\"");
329 m_resultBuffer.append(EscapeSpecialCharacters(message));
330 m_resultBuffer.append("\"/>\n");
332 m_resultBuffer.append("\t\t\t<skipped type=\"");
333 m_resultBuffer.append(EscapeSpecialCharacters(type));
334 m_resultBuffer.append("\"/>\n");
338 std::string XmlCollector::EscapeSpecialCharacters(std::string s)
340 for (unsigned int i = 0; i < s.size();) {
344 s.insert(i, """);
350 s.insert(i, "&");
368 s.insert(i, "'");