etc/protocols - sync with NetBSD-8
[minix.git] / external / bsd / atf / dist / tools / reader.cpp
blob2ba5530333d2cd0f4c64a9ecf04e0afced60d5af
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.
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.
30 extern "C" {
31 #include <sys/time.h>
34 #include <cassert>
35 #include <cstdlib>
36 #include <map>
37 #include <sstream>
38 #include <utility>
40 #include "parser.hpp"
41 #include "reader.hpp"
42 #include "text.hpp"
44 namespace impl = tools::atf_report;
45 #define IMPL_NAME "tools::atf_report"
47 // ------------------------------------------------------------------------
48 // Auxiliary functions.
49 // ------------------------------------------------------------------------
51 template< typename Type >
52 Type
53 string_to_int(const std::string& str)
55 std::istringstream ss(str);
56 Type s;
57 ss >> s;
59 return s;
62 // ------------------------------------------------------------------------
63 // The "atf_tps" auxiliary parser.
64 // ------------------------------------------------------------------------
66 namespace atf_tps {
68 static const tools::parser::token_type eof_type = 0;
69 static const tools::parser::token_type nl_type = 1;
70 static const tools::parser::token_type text_type = 2;
71 static const tools::parser::token_type colon_type = 3;
72 static const tools::parser::token_type comma_type = 4;
73 static const tools::parser::token_type tps_count_type = 5;
74 static const tools::parser::token_type tp_start_type = 6;
75 static const tools::parser::token_type tp_end_type = 7;
76 static const tools::parser::token_type tc_start_type = 8;
77 static const tools::parser::token_type tc_so_type = 9;
78 static const tools::parser::token_type tc_se_type = 10;
79 static const tools::parser::token_type tc_end_type = 11;
80 static const tools::parser::token_type passed_type = 12;
81 static const tools::parser::token_type failed_type = 13;
82 static const tools::parser::token_type skipped_type = 14;
83 static const tools::parser::token_type info_type = 16;
84 static const tools::parser::token_type expected_death_type = 17;
85 static const tools::parser::token_type expected_exit_type = 18;
86 static const tools::parser::token_type expected_failure_type = 19;
87 static const tools::parser::token_type expected_signal_type = 20;
88 static const tools::parser::token_type expected_timeout_type = 21;
90 class tokenizer : public tools::parser::tokenizer< std::istream > {
91 public:
92 tokenizer(std::istream& is, size_t curline) :
93 tools::parser::tokenizer< std::istream >
94 (is, true, eof_type, nl_type, text_type, curline)
96 add_delim(':', colon_type);
97 add_delim(',', comma_type);
98 add_keyword("tps-count", tps_count_type);
99 add_keyword("tp-start", tp_start_type);
100 add_keyword("tp-end", tp_end_type);
101 add_keyword("tc-start", tc_start_type);
102 add_keyword("tc-so", tc_so_type);
103 add_keyword("tc-se", tc_se_type);
104 add_keyword("tc-end", tc_end_type);
105 add_keyword("passed", passed_type);
106 add_keyword("failed", failed_type);
107 add_keyword("skipped", skipped_type);
108 add_keyword("info", info_type);
109 add_keyword("expected_death", expected_death_type);
110 add_keyword("expected_exit", expected_exit_type);
111 add_keyword("expected_failure", expected_failure_type);
112 add_keyword("expected_signal", expected_signal_type);
113 add_keyword("expected_timeout", expected_timeout_type);
117 } // namespace atf_tps
119 struct timeval
120 read_timeval(tools::parser::parser< atf_tps::tokenizer >& parser)
122 using namespace atf_tps;
124 tools::parser::token t = parser.expect(text_type, "timestamp");
125 const std::string::size_type divider = t.text().find('.');
126 if (divider == std::string::npos || divider == 0 ||
127 divider == t.text().length() - 1)
128 throw tools::parser::parse_error(t.lineno(),
129 "Malformed timestamp value " + t.text());
131 struct timeval tv;
132 tv.tv_sec = string_to_int< long >(t.text().substr(0, divider));
133 tv.tv_usec = string_to_int< long >(t.text().substr(divider + 1));
134 return tv;
137 // ------------------------------------------------------------------------
138 // The "atf_tps_reader" class.
139 // ------------------------------------------------------------------------
141 impl::atf_tps_reader::atf_tps_reader(std::istream& is) :
142 m_is(is)
146 impl::atf_tps_reader::~atf_tps_reader(void)
150 void
151 impl::atf_tps_reader::got_info(
152 const std::string& what __attribute__((__unused__)),
153 const std::string& val __attribute__((__unused__)))
157 void
158 impl::atf_tps_reader::got_ntps(size_t ntps __attribute__((__unused__)))
162 void
163 impl::atf_tps_reader::got_tp_start(
164 const std::string& tp __attribute__((__unused__)),
165 size_t ntcs __attribute__((__unused__)))
169 void
170 impl::atf_tps_reader::got_tp_end(
171 struct timeval* tv __attribute__((__unused__)),
172 const std::string& reason __attribute__((__unused__)))
176 void
177 impl::atf_tps_reader::got_tc_start(
178 const std::string& tcname __attribute__((__unused__)))
182 void
183 impl::atf_tps_reader::got_tc_stdout_line(
184 const std::string& line __attribute__((__unused__)))
188 void
189 impl::atf_tps_reader::got_tc_stderr_line(
190 const std::string& line __attribute__((__unused__)))
194 void
195 impl::atf_tps_reader::got_tc_end(
196 const std::string& state __attribute__((__unused__)),
197 struct timeval* tv __attribute__((__unused__)),
198 const std::string& reason __attribute__((__unused__)))
202 void
203 impl::atf_tps_reader::got_eof(void)
207 void
208 impl::atf_tps_reader::read_info(void* pptr)
210 using tools::parser::parse_error;
211 using namespace atf_tps;
213 tools::parser::parser< tokenizer >& p =
214 *reinterpret_cast< tools::parser::parser< tokenizer >* >
215 (pptr);
217 (void)p.expect(colon_type, "`:'");
219 tools::parser::token t = p.expect(text_type, "info property name");
220 (void)p.expect(comma_type, "`,'");
221 got_info(t.text(), tools::text::trim(p.rest_of_line()));
223 (void)p.expect(nl_type, "new line");
226 void
227 impl::atf_tps_reader::read_tp(void* pptr)
229 using tools::parser::parse_error;
230 using namespace atf_tps;
232 tools::parser::parser< tokenizer >& p =
233 *reinterpret_cast< tools::parser::parser< tokenizer >* >
234 (pptr);
236 tools::parser::token t = p.expect(tp_start_type,
237 "start of test program");
239 t = p.expect(colon_type, "`:'");
241 struct timeval s1 = read_timeval(p);
243 t = p.expect(comma_type, "`,'");
245 t = p.expect(text_type, "test program name");
246 std::string tpname = t.text();
248 t = p.expect(comma_type, "`,'");
250 t = p.expect(text_type, "number of test programs");
251 size_t ntcs = string_to_int< std::size_t >(t.text());
253 t = p.expect(nl_type, "new line");
255 ATF_PARSER_CALLBACK(p, got_tp_start(tpname, ntcs));
257 size_t i = 0;
258 while (p.good() && i < ntcs) {
259 try {
260 read_tc(&p);
261 i++;
262 } catch (const parse_error& pe) {
263 p.add_error(pe);
264 p.reset(nl_type);
267 t = p.expect(tp_end_type, "end of test program");
269 t = p.expect(colon_type, "`:'");
271 struct timeval s2 = read_timeval(p);
273 struct timeval s3;
274 timersub(&s2, &s1, &s3);
276 t = p.expect(comma_type, "`,'");
278 t = p.expect(text_type, "test program name");
279 if (t.text() != tpname)
280 throw parse_error(t.lineno(), "Test program name used in "
281 "terminator does not match "
282 "opening");
284 t = p.expect(nl_type, comma_type,
285 "new line or comma_type");
286 std::string reason;
287 if (t.type() == comma_type) {
288 reason = tools::text::trim(p.rest_of_line());
289 if (reason.empty())
290 throw parse_error(t.lineno(),
291 "Empty reason for failed test program");
292 t = p.next();
295 ATF_PARSER_CALLBACK(p, got_tp_end(&s3, reason));
298 void
299 impl::atf_tps_reader::read_tc(void* pptr)
301 using tools::parser::parse_error;
302 using namespace atf_tps;
304 tools::parser::parser< tokenizer >& p =
305 *reinterpret_cast< tools::parser::parser< tokenizer >* >
306 (pptr);
308 tools::parser::token t = p.expect(tc_start_type, "start of test case");
310 t = p.expect(colon_type, "`:'");
312 struct timeval s1 = read_timeval(p);
314 t = p.expect(comma_type, "`,'");
316 t = p.expect(text_type, "test case name");
317 std::string tcname = t.text();
319 ATF_PARSER_CALLBACK(p, got_tc_start(tcname));
321 t = p.expect(nl_type, "new line");
323 t = p.expect(tc_end_type, tc_so_type, tc_se_type,
324 "end of test case or test case's stdout/stderr line");
325 while (t.type() != tc_end_type &&
326 (t.type() == tc_so_type || t.type() == tc_se_type)) {
327 tools::parser::token t2 = t;
329 t = p.expect(colon_type, "`:'");
331 std::string line = p.rest_of_line();
333 if (t2.type() == tc_so_type) {
334 ATF_PARSER_CALLBACK(p, got_tc_stdout_line(line));
335 } else {
336 assert(t2.type() == tc_se_type);
337 ATF_PARSER_CALLBACK(p, got_tc_stderr_line(line));
340 t = p.expect(nl_type, "new line");
342 t = p.expect(tc_end_type, tc_so_type, tc_se_type,
343 "end of test case or test case's stdout/stderr line");
346 t = p.expect(colon_type, "`:'");
348 struct timeval s2 = read_timeval(p);
350 struct timeval s3;
351 timersub(&s2, &s1, &s3);
353 t = p.expect(comma_type, "`,'");
355 t = p.expect(text_type, "test case name");
356 if (t.text() != tcname)
357 throw parse_error(t.lineno(),
358 "Test case name used in terminator does not "
359 "match opening");
361 t = p.expect(comma_type, "`,'");
363 t = p.expect(expected_death_type, expected_exit_type, expected_failure_type,
364 expected_signal_type, expected_timeout_type, passed_type, failed_type,
365 skipped_type, "expected_{death,exit,failure,signal,timeout}, failed, "
366 "passed or skipped");
367 if (t.type() == passed_type) {
368 ATF_PARSER_CALLBACK(p, got_tc_end("passed", &s3, ""));
369 } else {
370 std::string state;
371 if (t.type() == expected_death_type) state = "expected_death";
372 else if (t.type() == expected_exit_type) state = "expected_exit";
373 else if (t.type() == expected_failure_type) state = "expected_failure";
374 else if (t.type() == expected_signal_type) state = "expected_signal";
375 else if (t.type() == expected_timeout_type) state = "expected_timeout";
376 else if (t.type() == failed_type) state = "failed";
377 else if (t.type() == skipped_type) state = "skipped";
378 else std::abort();
380 t = p.expect(comma_type, "`,'");
381 std::string reason = tools::text::trim(p.rest_of_line());
382 if (reason.empty())
383 throw parse_error(t.lineno(), "Empty reason for " + state +
384 " test case result");
385 ATF_PARSER_CALLBACK(p, got_tc_end(state, &s3, reason));
388 t = p.expect(nl_type, "new line");
391 void
392 impl::atf_tps_reader::read(void)
394 using tools::parser::parse_error;
395 using namespace atf_tps;
397 std::pair< size_t, tools::parser::headers_map > hml =
398 tools::parser::read_headers(m_is, 1);
399 tools::parser::validate_content_type(hml.second,
400 "application/X-atf-tps", 3);
402 tokenizer tkz(m_is, hml.first);
403 tools::parser::parser< tokenizer > p(tkz);
405 try {
406 tools::parser::token t;
408 while ((t = p.expect(tps_count_type, info_type, "tps-count or info "
409 "field")).type() == info_type)
410 read_info(&p);
412 t = p.expect(colon_type, "`:'");
414 t = p.expect(text_type, "number of test programs");
415 size_t ntps = string_to_int< std::size_t >(t.text());
416 ATF_PARSER_CALLBACK(p, got_ntps(ntps));
418 t = p.expect(nl_type, "new line");
420 size_t i = 0;
421 while (p.good() && i < ntps) {
422 try {
423 read_tp(&p);
424 i++;
425 } catch (const parse_error& pe) {
426 p.add_error(pe);
427 p.reset(nl_type);
431 while ((t = p.expect(eof_type, info_type, "end of stream or info "
432 "field")).type() == info_type)
433 read_info(&p);
434 ATF_PARSER_CALLBACK(p, got_eof());
435 } catch (const parse_error& pe) {
436 p.add_error(pe);
437 p.reset(nl_type);