reader.cpp revision 1.1.1.1 1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // 2. Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29
30 extern "C" {
31 #include <sys/time.h>
32 }
33
34 #include <cassert>
35 #include <cstdlib>
36 #include <map>
37 #include <sstream>
38 #include <utility>
39
40 #include "defs.hpp"
41 #include "parser.hpp"
42 #include "reader.hpp"
43 #include "text.hpp"
44
45 namespace impl = tools::atf_report;
46 #define IMPL_NAME "tools::atf_report"
47
48 // ------------------------------------------------------------------------
49 // Auxiliary functions.
50 // ------------------------------------------------------------------------
51
52 template< typename Type >
53 Type
54 string_to_int(const std::string& str)
55 {
56 std::istringstream ss(str);
57 Type s;
58 ss >> s;
59
60 return s;
61 }
62
63 // ------------------------------------------------------------------------
64 // The "atf_tps" auxiliary parser.
65 // ------------------------------------------------------------------------
66
67 namespace atf_tps {
68
69 static const tools::parser::token_type eof_type = 0;
70 static const tools::parser::token_type nl_type = 1;
71 static const tools::parser::token_type text_type = 2;
72 static const tools::parser::token_type colon_type = 3;
73 static const tools::parser::token_type comma_type = 4;
74 static const tools::parser::token_type tps_count_type = 5;
75 static const tools::parser::token_type tp_start_type = 6;
76 static const tools::parser::token_type tp_end_type = 7;
77 static const tools::parser::token_type tc_start_type = 8;
78 static const tools::parser::token_type tc_so_type = 9;
79 static const tools::parser::token_type tc_se_type = 10;
80 static const tools::parser::token_type tc_end_type = 11;
81 static const tools::parser::token_type passed_type = 12;
82 static const tools::parser::token_type failed_type = 13;
83 static const tools::parser::token_type skipped_type = 14;
84 static const tools::parser::token_type info_type = 16;
85 static const tools::parser::token_type expected_death_type = 17;
86 static const tools::parser::token_type expected_exit_type = 18;
87 static const tools::parser::token_type expected_failure_type = 19;
88 static const tools::parser::token_type expected_signal_type = 20;
89 static const tools::parser::token_type expected_timeout_type = 21;
90
91 class tokenizer : public tools::parser::tokenizer< std::istream > {
92 public:
93 tokenizer(std::istream& is, size_t curline) :
94 tools::parser::tokenizer< std::istream >
95 (is, true, eof_type, nl_type, text_type, curline)
96 {
97 add_delim(':', colon_type);
98 add_delim(',', comma_type);
99 add_keyword("tps-count", tps_count_type);
100 add_keyword("tp-start", tp_start_type);
101 add_keyword("tp-end", tp_end_type);
102 add_keyword("tc-start", tc_start_type);
103 add_keyword("tc-so", tc_so_type);
104 add_keyword("tc-se", tc_se_type);
105 add_keyword("tc-end", tc_end_type);
106 add_keyword("passed", passed_type);
107 add_keyword("failed", failed_type);
108 add_keyword("skipped", skipped_type);
109 add_keyword("info", info_type);
110 add_keyword("expected_death", expected_death_type);
111 add_keyword("expected_exit", expected_exit_type);
112 add_keyword("expected_failure", expected_failure_type);
113 add_keyword("expected_signal", expected_signal_type);
114 add_keyword("expected_timeout", expected_timeout_type);
115 }
116 };
117
118 } // namespace atf_tps
119
120 struct timeval
121 read_timeval(tools::parser::parser< atf_tps::tokenizer >& parser)
122 {
123 using namespace atf_tps;
124
125 tools::parser::token t = parser.expect(text_type, "timestamp");
126 const std::string::size_type divider = t.text().find('.');
127 if (divider == std::string::npos || divider == 0 ||
128 divider == t.text().length() - 1)
129 throw tools::parser::parse_error(t.lineno(),
130 "Malformed timestamp value " + t.text());
131
132 struct timeval tv;
133 tv.tv_sec = string_to_int< long >(t.text().substr(0, divider));
134 tv.tv_usec = string_to_int< long >(t.text().substr(divider + 1));
135 return tv;
136 }
137
138 // ------------------------------------------------------------------------
139 // The "atf_tps_reader" class.
140 // ------------------------------------------------------------------------
141
142 impl::atf_tps_reader::atf_tps_reader(std::istream& is) :
143 m_is(is)
144 {
145 }
146
147 impl::atf_tps_reader::~atf_tps_reader(void)
148 {
149 }
150
151 void
152 impl::atf_tps_reader::got_info(
153 const std::string& what ATF_DEFS_ATTRIBUTE_UNUSED,
154 const std::string& val ATF_DEFS_ATTRIBUTE_UNUSED)
155 {
156 }
157
158 void
159 impl::atf_tps_reader::got_ntps(size_t ntps ATF_DEFS_ATTRIBUTE_UNUSED)
160 {
161 }
162
163 void
164 impl::atf_tps_reader::got_tp_start(
165 const std::string& tp ATF_DEFS_ATTRIBUTE_UNUSED,
166 size_t ntcs ATF_DEFS_ATTRIBUTE_UNUSED)
167 {
168 }
169
170 void
171 impl::atf_tps_reader::got_tp_end(
172 struct timeval* tv ATF_DEFS_ATTRIBUTE_UNUSED,
173 const std::string& reason ATF_DEFS_ATTRIBUTE_UNUSED)
174 {
175 }
176
177 void
178 impl::atf_tps_reader::got_tc_start(
179 const std::string& tcname ATF_DEFS_ATTRIBUTE_UNUSED)
180 {
181 }
182
183 void
184 impl::atf_tps_reader::got_tc_stdout_line(
185 const std::string& line ATF_DEFS_ATTRIBUTE_UNUSED)
186 {
187 }
188
189 void
190 impl::atf_tps_reader::got_tc_stderr_line(
191 const std::string& line ATF_DEFS_ATTRIBUTE_UNUSED)
192 {
193 }
194
195 void
196 impl::atf_tps_reader::got_tc_end(
197 const std::string& state ATF_DEFS_ATTRIBUTE_UNUSED,
198 struct timeval* tv ATF_DEFS_ATTRIBUTE_UNUSED,
199 const std::string& reason ATF_DEFS_ATTRIBUTE_UNUSED)
200 {
201 }
202
203 void
204 impl::atf_tps_reader::got_eof(void)
205 {
206 }
207
208 void
209 impl::atf_tps_reader::read_info(void* pptr)
210 {
211 using tools::parser::parse_error;
212 using namespace atf_tps;
213
214 tools::parser::parser< tokenizer >& p =
215 *reinterpret_cast< tools::parser::parser< tokenizer >* >
216 (pptr);
217
218 (void)p.expect(colon_type, "`:'");
219
220 tools::parser::token t = p.expect(text_type, "info property name");
221 (void)p.expect(comma_type, "`,'");
222 got_info(t.text(), tools::text::trim(p.rest_of_line()));
223
224 (void)p.expect(nl_type, "new line");
225 }
226
227 void
228 impl::atf_tps_reader::read_tp(void* pptr)
229 {
230 using tools::parser::parse_error;
231 using namespace atf_tps;
232
233 tools::parser::parser< tokenizer >& p =
234 *reinterpret_cast< tools::parser::parser< tokenizer >* >
235 (pptr);
236
237 tools::parser::token t = p.expect(tp_start_type,
238 "start of test program");
239
240 t = p.expect(colon_type, "`:'");
241
242 struct timeval s1 = read_timeval(p);
243
244 t = p.expect(comma_type, "`,'");
245
246 t = p.expect(text_type, "test program name");
247 std::string tpname = t.text();
248
249 t = p.expect(comma_type, "`,'");
250
251 t = p.expect(text_type, "number of test programs");
252 size_t ntcs = string_to_int< std::size_t >(t.text());
253
254 t = p.expect(nl_type, "new line");
255
256 ATF_PARSER_CALLBACK(p, got_tp_start(tpname, ntcs));
257
258 size_t i = 0;
259 while (p.good() && i < ntcs) {
260 try {
261 read_tc(&p);
262 i++;
263 } catch (const parse_error& pe) {
264 p.add_error(pe);
265 p.reset(nl_type);
266 }
267 }
268 t = p.expect(tp_end_type, "end of test program");
269
270 t = p.expect(colon_type, "`:'");
271
272 struct timeval s2 = read_timeval(p);
273
274 struct timeval s3;
275 timersub(&s2, &s1, &s3);
276
277 t = p.expect(comma_type, "`,'");
278
279 t = p.expect(text_type, "test program name");
280 if (t.text() != tpname)
281 throw parse_error(t.lineno(), "Test program name used in "
282 "terminator does not match "
283 "opening");
284
285 t = p.expect(nl_type, comma_type,
286 "new line or comma_type");
287 std::string reason;
288 if (t.type() == comma_type) {
289 reason = tools::text::trim(p.rest_of_line());
290 if (reason.empty())
291 throw parse_error(t.lineno(),
292 "Empty reason for failed test program");
293 t = p.next();
294 }
295
296 ATF_PARSER_CALLBACK(p, got_tp_end(&s3, reason));
297 }
298
299 void
300 impl::atf_tps_reader::read_tc(void* pptr)
301 {
302 using tools::parser::parse_error;
303 using namespace atf_tps;
304
305 tools::parser::parser< tokenizer >& p =
306 *reinterpret_cast< tools::parser::parser< tokenizer >* >
307 (pptr);
308
309 tools::parser::token t = p.expect(tc_start_type, "start of test case");
310
311 t = p.expect(colon_type, "`:'");
312
313 struct timeval s1 = read_timeval(p);
314
315 t = p.expect(comma_type, "`,'");
316
317 t = p.expect(text_type, "test case name");
318 std::string tcname = t.text();
319
320 ATF_PARSER_CALLBACK(p, got_tc_start(tcname));
321
322 t = p.expect(nl_type, "new line");
323
324 t = p.expect(tc_end_type, tc_so_type, tc_se_type,
325 "end of test case or test case's stdout/stderr line");
326 while (t.type() != tc_end_type &&
327 (t.type() == tc_so_type || t.type() == tc_se_type)) {
328 tools::parser::token t2 = t;
329
330 t = p.expect(colon_type, "`:'");
331
332 std::string line = p.rest_of_line();
333
334 if (t2.type() == tc_so_type) {
335 ATF_PARSER_CALLBACK(p, got_tc_stdout_line(line));
336 } else {
337 assert(t2.type() == tc_se_type);
338 ATF_PARSER_CALLBACK(p, got_tc_stderr_line(line));
339 }
340
341 t = p.expect(nl_type, "new line");
342
343 t = p.expect(tc_end_type, tc_so_type, tc_se_type,
344 "end of test case or test case's stdout/stderr line");
345 }
346
347 t = p.expect(colon_type, "`:'");
348
349 struct timeval s2 = read_timeval(p);
350
351 struct timeval s3;
352 timersub(&s2, &s1, &s3);
353
354 t = p.expect(comma_type, "`,'");
355
356 t = p.expect(text_type, "test case name");
357 if (t.text() != tcname)
358 throw parse_error(t.lineno(),
359 "Test case name used in terminator does not "
360 "match opening");
361
362 t = p.expect(comma_type, "`,'");
363
364 t = p.expect(expected_death_type, expected_exit_type, expected_failure_type,
365 expected_signal_type, expected_timeout_type, passed_type, failed_type,
366 skipped_type, "expected_{death,exit,failure,signal,timeout}, failed, "
367 "passed or skipped");
368 if (t.type() == passed_type) {
369 ATF_PARSER_CALLBACK(p, got_tc_end("passed", &s3, ""));
370 } else {
371 std::string state;
372 if (t.type() == expected_death_type) state = "expected_death";
373 else if (t.type() == expected_exit_type) state = "expected_exit";
374 else if (t.type() == expected_failure_type) state = "expected_failure";
375 else if (t.type() == expected_signal_type) state = "expected_signal";
376 else if (t.type() == expected_timeout_type) state = "expected_timeout";
377 else if (t.type() == failed_type) state = "failed";
378 else if (t.type() == skipped_type) state = "skipped";
379 else std::abort();
380
381 t = p.expect(comma_type, "`,'");
382 std::string reason = tools::text::trim(p.rest_of_line());
383 if (reason.empty())
384 throw parse_error(t.lineno(), "Empty reason for " + state +
385 " test case result");
386 ATF_PARSER_CALLBACK(p, got_tc_end(state, &s3, reason));
387 }
388
389 t = p.expect(nl_type, "new line");
390 }
391
392 void
393 impl::atf_tps_reader::read(void)
394 {
395 using tools::parser::parse_error;
396 using namespace atf_tps;
397
398 std::pair< size_t, tools::parser::headers_map > hml =
399 tools::parser::read_headers(m_is, 1);
400 tools::parser::validate_content_type(hml.second,
401 "application/X-atf-tps", 3);
402
403 tokenizer tkz(m_is, hml.first);
404 tools::parser::parser< tokenizer > p(tkz);
405
406 try {
407 tools::parser::token t;
408
409 while ((t = p.expect(tps_count_type, info_type, "tps-count or info "
410 "field")).type() == info_type)
411 read_info(&p);
412
413 t = p.expect(colon_type, "`:'");
414
415 t = p.expect(text_type, "number of test programs");
416 size_t ntps = string_to_int< std::size_t >(t.text());
417 ATF_PARSER_CALLBACK(p, got_ntps(ntps));
418
419 t = p.expect(nl_type, "new line");
420
421 size_t i = 0;
422 while (p.good() && i < ntps) {
423 try {
424 read_tp(&p);
425 i++;
426 } catch (const parse_error& pe) {
427 p.add_error(pe);
428 p.reset(nl_type);
429 }
430 }
431
432 while ((t = p.expect(eof_type, info_type, "end of stream or info "
433 "field")).type() == info_type)
434 read_info(&p);
435 ATF_PARSER_CALLBACK(p, got_eof());
436 } catch (const parse_error& pe) {
437 p.add_error(pe);
438 p.reset(nl_type);
439 }
440 }
441