Remove INTERNAL type from FailStatus enum
[platform/core/test/security-tests.git] / tests / framework / src / test_results_collector_xml.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /*
17  * @file        test_results_collector_xml.cpp
18  * @author      Lukasz Wrzosek (l.wrzosek@samsung.com)
19  * @author      Marcin Niesluchowski (m.niesluchow@samsung.com)
20  * @version     1.0
21  * @brief       Source file containing XmlCollector class definition
22  */
23
24 #include <cerrno>
25 #include <cstdio>
26 #include <cstring>
27 #include <sstream>
28
29 #include <dpl/assert.h>
30 #include <dpl/exception.h>
31 #include <dpl/test/test_results_collector_commons.h>
32
33 #include "dpl/test/test_results_collector_xml.h"
34
35 namespace DPL {
36 namespace Test {
37
38 namespace {
39
40 const char *DEFAULT_XML_FILE_NAME = "results.xml";
41
42 }
43
44 XmlCollector::XmlCollector()
45     : m_filename(DEFAULT_XML_FILE_NAME)
46 {
47 }
48
49 TestResultsCollectorBase* XmlCollector::Constructor()
50 {
51      return new XmlCollector();
52 }
53
54 void XmlCollector::CollectCurrentTestGroupName(const std::string& name)
55 {
56     std::size_t pos = GetCurrentGroupPosition();
57     if (std::string::npos != pos) {
58         GroupFinish(pos);
59         FlushOutput();
60         m_stats = Statistic();
61      }
62
63     pos = m_outputBuffer.find("</testsuites>");
64     if (std::string::npos == pos) {
65         ThrowMsg(DPL::Exception, "Could not find test suites closing tag");
66     }
67     GroupStart(pos, name);
68 }
69
70 void XmlCollector::GroupStart(const std::size_t pos, const std::string& name)
71 {
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")";
78     groupHeader << ">";
79
80     groupHeader << "\n\t\t<testcase name=\"unknown\" status=\"FAILED\">";
81     groupHeader <<
82     "\n\t\t\t<failure type=\"FAILED\" message=\"segmentation fault\"/>";
83     groupHeader << "\n\t\t</testcase>";
84
85     groupHeader << "\n\t</testsuite>";
86
87     m_outputBuffer.insert(pos - 1, groupHeader.str());
88 }
89
90 bool XmlCollector::Configure()
91 {
92     m_fp.Reset(fopen(m_filename.c_str(), "w"));
93     if (!m_fp) {
94         LogPedantic("Could not open file " << m_filename << " for writing");
95         return false;
96     }
97     return true;
98 }
99
100 std::string XmlCollector::CollectorSpecificHelp() const
101 {
102     return "--file=<filename> - name of file for output\n"
103            "                    default - results.xml\n";
104 }
105
106 void XmlCollector::Start()
107 {
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>");
111     FlushOutput();
112 }
113
114 void XmlCollector::Finish()
115 {
116     std::size_t pos = GetCurrentGroupPosition();
117     if (std::string::npos != pos) {
118         GroupFinish(pos);
119         FlushOutput();
120     }
121 }
122
123 bool XmlCollector::ParseCollectorSpecificArg(const std::string& arg)
124 {
125     return ParseCollectorFileArg(arg, m_filename);
126 }
127
128 void XmlCollector::CollectResult(const std::string& id,
129                                  const std::string&,
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)
135 {
136     m_resultBuffer.erase();
137     m_resultBuffer.append("\t\t<testcase name=\"");
138     m_resultBuffer.append(EscapeSpecialCharacters(id));
139     m_resultBuffer.append("\"");
140     switch (status) {
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");
149                 break;
150             } else {
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=\"");
156                 ostr.str("");
157                 ostr << performanceMaxTime.count();
158                 m_resultBuffer.append(ostr.str());
159                 m_resultBuffer.append("\"/>\n");
160                 break;
161             }
162         }
163         m_resultBuffer.append(" status=\"OK\"/>\n");
164         break;
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");
169         break;
170     case TestResultsCollectorBase::FailStatus::IGNORED:
171         m_resultBuffer.append(" status=\"Ignored\">\n");
172         PrintfIgnoredMessage("Ignored", EscapeSpecialCharacters(
173                                  reason), true);
174         m_resultBuffer.append("\t\t</testcase>\n");
175         break;
176     default:
177         Assert(false && "Bad status");
178     }
179     std::size_t group_pos = GetCurrentGroupPosition();
180     if (std::string::npos == group_pos) {
181         ThrowMsg(DPL::Exception, "No current group set");
182     }
183
184     std::size_t last_case_pos = m_outputBuffer.find(
185             "<testcase name=\"unknown\"",
186             group_pos);
187     if (std::string::npos == last_case_pos) {
188         ThrowMsg(DPL::Exception, "Could not find SegFault test case");
189     }
190     m_outputBuffer.insert(last_case_pos - 2, m_resultBuffer);
191
192     m_stats.AddTest(status);
193
194     UpdateGroupHeader(group_pos,
195                       m_stats.GetTotal() + 1, // include SegFault
196                       m_stats.GetFailed() + 1, // include SegFault
197                       m_stats.GetIgnored());
198     FlushOutput();
199 }
200
201 std::size_t XmlCollector::GetCurrentGroupPosition() const
202 {
203     return m_outputBuffer.rfind("<testsuite ");
204 }
205
206 void XmlCollector::UpdateGroupHeader(const std::size_t groupPosition,
207                                      const unsigned int tests,
208                                      const unsigned int failures,
209                                      const unsigned int skipped)
210 {
211     UpdateElementAttribute(groupPosition, "tests", UIntToString(tests));
212     UpdateElementAttribute(groupPosition, "failures", UIntToString(failures));
213     UpdateElementAttribute(groupPosition, "skipped", UIntToString(skipped));
214 }
215
216 void XmlCollector::UpdateElementAttribute(const std::size_t elementPosition,
217                                           const std::string& name,
218                                           const std::string& value)
219 {
220     std::string pattern = name + "=\"";
221
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");
226     }
227
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");
232     }
233
234     m_outputBuffer.replace(start + pattern.length(),
235                            end - start - pattern.length(),
236                            value);
237 }
238
239 std::string XmlCollector::UIntToString(const unsigned int value)
240 {
241     std::stringstream result;
242     result << value;
243     return result.str();
244 }
245
246 void XmlCollector::GroupFinish(const std::size_t groupPosition)
247 {
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");
253     }
254     segFaultStart -= 2; // to erase tabs
255
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");
261     }
262     segFaultEnd += closeTag.length() + 1; // to erase new line
263
264     m_outputBuffer.erase(segFaultStart, segFaultEnd - segFaultStart);
265
266     UpdateGroupHeader(groupPosition,
267                       m_stats.GetTotal(),
268                       m_stats.GetFailed(),
269                       m_stats.GetIgnored());
270 }
271
272 void XmlCollector::FlushOutput()
273 {
274     int fd = fileno(m_fp.Get());
275     if (-1 == fd) {
276         const char* errStr = strerror(errno);
277         ThrowMsg(DPL::Exception, errStr);
278     }
279
280     if (-1 == TEMP_FAILURE_RETRY(ftruncate(fd, 0L))) {
281         const char* errStr = strerror(errno);
282         ThrowMsg(DPL::Exception, errStr);
283     }
284
285     if (-1 == TEMP_FAILURE_RETRY(fseek(m_fp.Get(), 0L, SEEK_SET))) {
286         const char* errStr = strerror(errno);
287         ThrowMsg(DPL::Exception, errStr);
288     }
289
290     if (m_outputBuffer.size() !=
291         fwrite(m_outputBuffer.c_str(), 1, m_outputBuffer.size(),
292                m_fp.Get()))
293     {
294         const char* errStr = strerror(errno);
295         ThrowMsg(DPL::Exception, errStr);
296     }
297
298     if (-1 == TEMP_FAILURE_RETRY(fflush(m_fp.Get()))) {
299         const char* errStr = strerror(errno);
300         ThrowMsg(DPL::Exception, errStr);
301     }
302 }
303
304 void XmlCollector::PrintfErrorMessage(const char* type,
305                                       const std::string& message,
306                                       bool verbosity)
307 {
308     if (verbosity) {
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");
314     } else {
315         m_resultBuffer.append("\t\t\t<failure type=\"");
316         m_resultBuffer.append(EscapeSpecialCharacters(type));
317         m_resultBuffer.append("\"/>\n");
318     }
319 }
320
321 void XmlCollector::PrintfIgnoredMessage(const char* type,
322                                         const std::string& message,
323                                         bool verbosity)
324 {
325     if (verbosity) {
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");
331     } else {
332         m_resultBuffer.append("\t\t\t<skipped type=\"");
333         m_resultBuffer.append(EscapeSpecialCharacters(type));
334         m_resultBuffer.append("\"/>\n");
335     }
336 }
337
338 std::string XmlCollector::EscapeSpecialCharacters(std::string s)
339 {
340     for (unsigned int i = 0; i < s.size();) {
341         switch (s[i]) {
342         case '"':
343             s.erase(i, 1);
344             s.insert(i, "&quot;");
345             i += 6;
346             break;
347
348         case '&':
349             s.erase(i, 1);
350             s.insert(i, "&amp;");
351             i += 5;
352             break;
353
354         case '<':
355             s.erase(i, 1);
356             s.insert(i, "&lt;");
357             i += 4;
358             break;
359
360         case '>':
361             s.erase(i, 1);
362             s.insert(i, "&gt;");
363             i += 4;
364             break;
365
366         case '\'':
367             s.erase(i, 1);
368             s.insert(i, "&#39;");
369             i += 5;
370             break;
371         default:
372             ++i;
373             break;
374         }
375     }
376     return s;
377 }
378
379 } // namespace Test
380 } // namespace DPL