libtest: improve output format; makes eyes hurt less
[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 #include <ostream>
45
46 namespace libtest {
47
48 std::string& escape4XML(std::string const& arg, std::string& escaped_string)
49 {
50 escaped_string.clear();
51
52 escaped_string+= '"';
53 for (std::string::const_iterator x= arg.begin(), end= arg.end(); x != end; ++x)
54 {
55 unsigned char c= *x;
56 if (c == '&')
57 {
58 escaped_string+= "&amp;";
59 }
60 else if (c == '>')
61 {
62 escaped_string+= "&gt;";
63 }
64 else if (c == '<')
65 {
66 escaped_string+= "&lt;";
67 }
68 else if (c == '\'')
69 {
70 escaped_string+= "&apos;"; break;
71 }
72 else if (c == '"')
73 {
74 escaped_string+= "&quot;";
75 }
76 else if (c == ' ')
77 {
78 escaped_string+= ' ';
79 }
80 else if (isalnum(c))
81 {
82 escaped_string+= c;
83 }
84 else
85 {
86 char const* const hexdig= "0123456789ABCDEF";
87 escaped_string+= "&#x";
88 escaped_string+= hexdig[c >> 4];
89 escaped_string+= hexdig[c & 0xF];
90 escaped_string+= ';';
91 }
92 }
93 escaped_string+= '"';
94
95 return escaped_string;
96 }
97
98 class TestCase {
99 public:
100 TestCase(const std::string& arg):
101 _name(arg),
102 _result(TEST_FAILURE)
103 {
104 }
105
106 const std::string& name() const
107 {
108 return _name;
109 }
110
111 test_return_t result() const
112 {
113 return _result;
114 }
115
116 void result(test_return_t arg)
117 {
118 _result= arg;
119 }
120
121 void result(test_return_t arg, const libtest::Timer& timer_)
122 {
123 _result= arg;
124 _timer= timer_;
125 }
126
127 const libtest::Timer& timer() const
128 {
129 return _timer;
130 }
131
132 void timer(libtest::Timer& arg)
133 {
134 _timer= arg;
135 }
136
137 private:
138 std::string _name;
139 test_return_t _result;
140 libtest::Timer _timer;
141 };
142
143 Formatter::Formatter(const std::string& frame_name, const std::string& arg)
144 {
145 _suite_name= frame_name;
146 _suite_name+= ".";
147 _suite_name+= arg;
148 }
149
150 Formatter::~Formatter()
151 {
152 std::for_each(_testcases.begin(), _testcases.end(), DeleteFromVector());
153 _testcases.clear();
154 }
155
156 TestCase* Formatter::current()
157 {
158 return _testcases.back();
159 }
160
161 void Formatter::skipped()
162 {
163 assert(current());
164 current()->result(TEST_SKIPPED);
165
166 Out
167 << "[ " << test_strerror(current()->result()) << " ]"
168 << "\t\t"
169 << name() << "." << current()->name()
170 ;
171
172 reset();
173 }
174
175 void Formatter::failed()
176 {
177 assert(current());
178 current()->result(TEST_FAILURE);
179
180 Out
181 << "[ " << test_strerror(current()->result()) << " ]"
182 << "\t\t"
183 << name() << "." << current()->name()
184 ;
185
186 reset();
187 }
188
189 void Formatter::success(const libtest::Timer& timer_)
190 {
191 assert(current());
192 current()->result(TEST_SUCCESS, timer_);
193
194 Out
195 << "[ " << test_strerror(current()->result()) << " ]"
196 << "\t"
197 << current()->timer()
198 << "\t"
199 << name() << "." << current()->name()
200 ;
201
202 reset();
203 }
204
205 void Formatter::xml(libtest::Framework& framework_, std::ofstream& output)
206 {
207 std::string escaped_string;
208
209 output << "<testsuites name="
210 << escape4XML(framework_.name(), escaped_string) << ">" << std::endl;
211
212 for (Suites::iterator framework_iter= framework_.suites().begin();
213 framework_iter != framework_.suites().end();
214 ++framework_iter)
215 {
216 output << "\t<testsuite name="
217 << escape4XML((*framework_iter)->name(), escaped_string)
218 #if 0
219 << " classname=\"\" package=\"\""
220 #endif
221 << ">" << std::endl;
222
223 for (TestCases::iterator case_iter= (*framework_iter)->formatter()->testcases().begin();
224 case_iter != (*framework_iter)->formatter()->testcases().end();
225 ++case_iter)
226 {
227 output << "\t\t<testcase name="
228 << escape4XML((*case_iter)->name(), escaped_string)
229 << " time=\""
230 << (*case_iter)->timer().elapsed_milliseconds()
231 << "\"";
232
233 switch ((*case_iter)->result())
234 {
235 case TEST_SKIPPED:
236 output << ">" << std::endl;
237 output << "\t\t <skipped/>" << std::endl;
238 output << "\t\t</testcase>" << std::endl;
239 break;
240
241 case TEST_FAILURE:
242 output << ">" << std::endl;
243 output << "\t\t <failure message=\"\" type=\"\"/>"<< std::endl;
244 output << "\t\t</testcase>" << std::endl;
245 break;
246
247 case TEST_SUCCESS:
248 output << "/>" << std::endl;
249 break;
250 }
251 }
252 output << "\t</testsuite>" << std::endl;
253 }
254 output << "</testsuites>" << std::endl;
255 }
256
257 void Formatter::push_testcase(const std::string& arg)
258 {
259 assert(_suite_name.empty() == false);
260 TestCase* _current_testcase= new TestCase(arg);
261 _testcases.push_back(_current_testcase);
262
263 assert(current());
264
265 Echo
266 << "\t\t\t"
267 << name() << "." << current()->name()
268 << "... \r"
269 ;
270 }
271
272 void Formatter::reset()
273 {
274 }
275 } // namespace libtest