Added XML Escape for junit reports.
[awesomized/libmemcached] / libtest / formatter.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Data Differential YATL (i.e. libtest) library
4 *
5 * Copyright (C) 2012 Data Differential, http://datadifferential.com/
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * * The names of its contributors may not be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 */
36
37 #include "libtest/yatlcon.h"
38
39 #include <libtest/common.h>
40
41 #include <algorithm>
42 #include <fstream>
43 #include <iostream>
44
45 namespace libtest {
46
47 std::string& escape4XML(std::string const& arg, std::string& escaped_string)
48 {
49 escaped_string.clear();
50
51 escaped_string+= '"';
52 for (std::string::const_iterator x= arg.begin(), end= arg.end(); x != end; ++x)
53 {
54 unsigned char c= *x;
55 if (' ' <= c and c <= '~' and c != '\\' and c != '"' and c != '>' and c != '<')
56 {
57 escaped_string+= c;
58 }
59 else if (c == '>')
60 {
61 escaped_string+= '&';
62 escaped_string+= 'g';
63 escaped_string+= 't';
64 escaped_string+= ';';
65 }
66 else if (c == '<')
67 {
68 escaped_string+= '&';
69 escaped_string+= 'l';
70 escaped_string+= 't';
71 escaped_string+= ';';
72 }
73 else
74 {
75 escaped_string+= '\\';
76 switch (c) {
77 case '"': escaped_string+= '"'; break;
78 case '\\': escaped_string+= '\\'; break;
79 case '\t': escaped_string+='t'; break;
80 case '\r': escaped_string+='r'; break;
81 case '\n': escaped_string+='n'; break;
82 default:
83 char const* const hexdig= "0123456789ABCDEF";
84 escaped_string+= 'x';
85 escaped_string+= hexdig[c >> 4];
86 escaped_string+= hexdig[c & 0xF];
87 }
88 }
89 }
90 escaped_string+= '"';
91
92 return escaped_string;
93 }
94
95 class TestCase {
96 public:
97 TestCase(const std::string& arg):
98 _name(arg),
99 _result(TEST_FAILURE)
100 {
101 }
102
103 const std::string& name() const
104 {
105 return _name;
106 }
107
108 test_return_t result() const
109 {
110 return _result;
111 }
112
113 void result(test_return_t arg)
114 {
115 _result= arg;
116 }
117
118 void result(test_return_t arg, const libtest::Timer& timer_)
119 {
120 _result= arg;
121 _timer= timer_;
122 }
123
124 const libtest::Timer& timer() const
125 {
126 return _timer;
127 }
128
129 void timer(libtest::Timer& arg)
130 {
131 _timer= arg;
132 }
133
134 private:
135 std::string _name;
136 test_return_t _result;
137 libtest::Timer _timer;
138 };
139
140 Formatter::Formatter(const std::string& frame_name, const std::string& arg)
141 {
142 _suite_name= frame_name;
143 _suite_name+= ".";
144 _suite_name+= arg;
145 }
146
147 Formatter::~Formatter()
148 {
149 std::for_each(_testcases.begin(), _testcases.end(), DeleteFromVector());
150 _testcases.clear();
151 }
152
153 TestCase* Formatter::current()
154 {
155 return _testcases.back();
156 }
157
158 void Formatter::skipped()
159 {
160 current()->result(TEST_SKIPPED);
161 Out << name() << "."
162 << current()->name()
163 << "\t\t\t\t\t"
164 << "[ " << test_strerror(current()->result()) << " ]";
165
166 reset();
167 }
168
169 void Formatter::failed()
170 {
171 assert(current());
172 current()->result(TEST_FAILURE);
173
174 Out << name()
175 << "." << current()->name() << "\t\t\t\t\t"
176 << "[ " << test_strerror(current()->result()) << " ]";
177
178 reset();
179 }
180
181 void Formatter::success(const libtest::Timer& timer_)
182 {
183 assert(current());
184 current()->result(TEST_SUCCESS, timer_);
185 std::string escaped_string;
186
187 Out << name() << "."
188 << current()->name()
189 << "\t\t\t\t\t"
190 << current()->timer()
191 << " [ " << test_strerror(current()->result()) << " ]";
192
193 reset();
194 }
195
196 void Formatter::xml(libtest::Framework& framework_, std::ofstream& output)
197 {
198 std::string escaped_string;
199
200 output << "<testsuites name="
201 << escape4XML(framework_.name(), escaped_string) << ">" << std::endl;
202
203 for (Suites::iterator framework_iter= framework_.suites().begin();
204 framework_iter != framework_.suites().end();
205 ++framework_iter)
206 {
207 output << "\t<testsuite name="
208 << escape4XML((*framework_iter)->name(), escaped_string)
209 << " classname=\"\" package=\"\">"
210 << std::endl;
211
212 for (TestCases::iterator case_iter= (*framework_iter)->formatter()->testcases().begin();
213 case_iter != (*framework_iter)->formatter()->testcases().end();
214 ++case_iter)
215 {
216 output << "\t\t<testcase name="
217 << escape4XML((*case_iter)->name(), escaped_string)
218 << " time=\""
219 << (*case_iter)->timer().elapsed_milliseconds()
220 << "\">"
221 << std::endl;
222
223 switch ((*case_iter)->result())
224 {
225 case TEST_SKIPPED:
226 output << "\t\t <skipped/>" << std::endl;
227 break;
228
229 case TEST_FAILURE:
230 output << "\t\t <failure message=\"\" type=\"\"/>"<< std::endl;
231 break;
232
233 case TEST_SUCCESS:
234 break;
235 }
236 output << "\t\t</testcase>" << std::endl;
237 }
238 output << "\t</testsuite>" << std::endl;
239 }
240 output << "</testsuites>" << std::endl;
241 }
242
243 void Formatter::push_testcase(const std::string& arg)
244 {
245 assert(_suite_name.empty() == false);
246 TestCase* _current_testcase= new TestCase(arg);
247 _testcases.push_back(_current_testcase);
248 }
249
250 void Formatter::reset()
251 {
252 }
253 } // namespace libtest