2 // Automated Testing Framework (atf)
4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 // All rights reserved.
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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.
44 namespace impl
= tools::atf_report
;
45 #define IMPL_NAME "tools::atf_report"
47 // ------------------------------------------------------------------------
48 // Auxiliary functions.
49 // ------------------------------------------------------------------------
51 template< typename Type
>
53 string_to_int(const std::string
& str
)
55 std::istringstream
ss(str
);
62 // ------------------------------------------------------------------------
63 // The "atf_tps" auxiliary parser.
64 // ------------------------------------------------------------------------
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
> {
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
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());
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));
137 // ------------------------------------------------------------------------
138 // The "atf_tps_reader" class.
139 // ------------------------------------------------------------------------
141 impl::atf_tps_reader::atf_tps_reader(std::istream
& is
) :
146 impl::atf_tps_reader::~atf_tps_reader(void)
151 impl::atf_tps_reader::got_info(
152 const std::string
& what
__attribute__((__unused__
)),
153 const std::string
& val
__attribute__((__unused__
)))
158 impl::atf_tps_reader::got_ntps(size_t ntps
__attribute__((__unused__
)))
163 impl::atf_tps_reader::got_tp_start(
164 const std::string
& tp
__attribute__((__unused__
)),
165 size_t ntcs
__attribute__((__unused__
)))
170 impl::atf_tps_reader::got_tp_end(
171 struct timeval
* tv
__attribute__((__unused__
)),
172 const std::string
& reason
__attribute__((__unused__
)))
177 impl::atf_tps_reader::got_tc_start(
178 const std::string
& tcname
__attribute__((__unused__
)))
183 impl::atf_tps_reader::got_tc_stdout_line(
184 const std::string
& line
__attribute__((__unused__
)))
189 impl::atf_tps_reader::got_tc_stderr_line(
190 const std::string
& line
__attribute__((__unused__
)))
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__
)))
203 impl::atf_tps_reader::got_eof(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
>* >
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");
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
>* >
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
));
258 while (p
.good() && i
< ntcs
) {
262 } catch (const parse_error
& pe
) {
267 t
= p
.expect(tp_end_type
, "end of test program");
269 t
= p
.expect(colon_type
, "`:'");
271 struct timeval s2
= read_timeval(p
);
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 "
284 t
= p
.expect(nl_type
, comma_type
,
285 "new line or comma_type");
287 if (t
.type() == comma_type
) {
288 reason
= tools::text::trim(p
.rest_of_line());
290 throw parse_error(t
.lineno(),
291 "Empty reason for failed test program");
295 ATF_PARSER_CALLBACK(p
, got_tp_end(&s3
, reason
));
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
>* >
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
));
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
);
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 "
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
, ""));
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";
380 t
= p
.expect(comma_type
, "`,'");
381 std::string reason
= tools::text::trim(p
.rest_of_line());
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");
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
);
406 tools::parser::token t
;
408 while ((t
= p
.expect(tps_count_type
, info_type
, "tps-count or info "
409 "field")).type() == info_type
)
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");
421 while (p
.good() && i
< ntps
) {
425 } catch (const parse_error
& pe
) {
431 while ((t
= p
.expect(eof_type
, info_type
, "end of stream or info "
432 "field")).type() == info_type
)
434 ATF_PARSER_CALLBACK(p
, got_eof());
435 } catch (const parse_error
& pe
) {