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.
38 #include "atf-c/defs.h"
40 #include "atf-c++/detail/parser.hpp"
41 #include "atf-c++/detail/sanity.hpp"
42 #include "atf-c++/detail/text.hpp"
46 namespace impl
= atf::atf_report
;
47 #define IMPL_NAME "atf::atf_report"
49 // ------------------------------------------------------------------------
50 // Auxiliary functions.
51 // ------------------------------------------------------------------------
53 template< typename Type
>
55 string_to_int(const std::string
& str
)
57 std::istringstream
ss(str
);
64 // ------------------------------------------------------------------------
65 // The "atf_tps" auxiliary parser.
66 // ------------------------------------------------------------------------
70 static const atf::parser::token_type eof_type
= 0;
71 static const atf::parser::token_type nl_type
= 1;
72 static const atf::parser::token_type text_type
= 2;
73 static const atf::parser::token_type colon_type
= 3;
74 static const atf::parser::token_type comma_type
= 4;
75 static const atf::parser::token_type tps_count_type
= 5;
76 static const atf::parser::token_type tp_start_type
= 6;
77 static const atf::parser::token_type tp_end_type
= 7;
78 static const atf::parser::token_type tc_start_type
= 8;
79 static const atf::parser::token_type tc_so_type
= 9;
80 static const atf::parser::token_type tc_se_type
= 10;
81 static const atf::parser::token_type tc_end_type
= 11;
82 static const atf::parser::token_type passed_type
= 12;
83 static const atf::parser::token_type failed_type
= 13;
84 static const atf::parser::token_type skipped_type
= 14;
85 static const atf::parser::token_type info_type
= 16;
86 static const atf::parser::token_type expected_death_type
= 17;
87 static const atf::parser::token_type expected_exit_type
= 18;
88 static const atf::parser::token_type expected_failure_type
= 19;
89 static const atf::parser::token_type expected_signal_type
= 20;
90 static const atf::parser::token_type expected_timeout_type
= 21;
92 class tokenizer
: public atf::parser::tokenizer
< std::istream
> {
94 tokenizer(std::istream
& is
, size_t curline
) :
95 atf::parser::tokenizer
< std::istream
>
96 (is
, true, eof_type
, nl_type
, text_type
, curline
)
98 add_delim(':', colon_type
);
99 add_delim(',', comma_type
);
100 add_keyword("tps-count", tps_count_type
);
101 add_keyword("tp-start", tp_start_type
);
102 add_keyword("tp-end", tp_end_type
);
103 add_keyword("tc-start", tc_start_type
);
104 add_keyword("tc-so", tc_so_type
);
105 add_keyword("tc-se", tc_se_type
);
106 add_keyword("tc-end", tc_end_type
);
107 add_keyword("passed", passed_type
);
108 add_keyword("failed", failed_type
);
109 add_keyword("skipped", skipped_type
);
110 add_keyword("info", info_type
);
111 add_keyword("expected_death", expected_death_type
);
112 add_keyword("expected_exit", expected_exit_type
);
113 add_keyword("expected_failure", expected_failure_type
);
114 add_keyword("expected_signal", expected_signal_type
);
115 add_keyword("expected_timeout", expected_timeout_type
);
119 } // namespace atf_tps
122 read_timeval(atf::parser::parser
< atf_tps::tokenizer
>& parser
)
124 using namespace atf_tps
;
126 atf::parser::token t
= parser
.expect(text_type
, "timestamp");
127 const std::string::size_type divider
= t
.text().find('.');
128 if (divider
== std::string::npos
|| divider
== 0 ||
129 divider
== t
.text().length() - 1)
130 throw atf::parser::parse_error(t
.lineno(),
131 "Malformed timestamp value " + t
.text());
134 tv
.tv_sec
= string_to_int
< long >(t
.text().substr(0, divider
));
135 tv
.tv_usec
= string_to_int
< long >(t
.text().substr(divider
+ 1));
139 // ------------------------------------------------------------------------
140 // The "atf_tps_reader" class.
141 // ------------------------------------------------------------------------
143 impl::atf_tps_reader::atf_tps_reader(std::istream
& is
) :
148 impl::atf_tps_reader::~atf_tps_reader(void)
153 impl::atf_tps_reader::got_info(
154 const std::string
& what ATF_DEFS_ATTRIBUTE_UNUSED
,
155 const std::string
& val ATF_DEFS_ATTRIBUTE_UNUSED
)
160 impl::atf_tps_reader::got_ntps(size_t ntps ATF_DEFS_ATTRIBUTE_UNUSED
)
165 impl::atf_tps_reader::got_tp_start(
166 const std::string
& tp ATF_DEFS_ATTRIBUTE_UNUSED
,
167 size_t ntcs ATF_DEFS_ATTRIBUTE_UNUSED
)
172 impl::atf_tps_reader::got_tp_end(
173 struct timeval
* tv ATF_DEFS_ATTRIBUTE_UNUSED
,
174 const std::string
& reason ATF_DEFS_ATTRIBUTE_UNUSED
)
179 impl::atf_tps_reader::got_tc_start(
180 const std::string
& tcname ATF_DEFS_ATTRIBUTE_UNUSED
)
185 impl::atf_tps_reader::got_tc_stdout_line(
186 const std::string
& line ATF_DEFS_ATTRIBUTE_UNUSED
)
191 impl::atf_tps_reader::got_tc_stderr_line(
192 const std::string
& line ATF_DEFS_ATTRIBUTE_UNUSED
)
197 impl::atf_tps_reader::got_tc_end(
198 const std::string
& state ATF_DEFS_ATTRIBUTE_UNUSED
,
199 struct timeval
* tv ATF_DEFS_ATTRIBUTE_UNUSED
,
200 const std::string
& reason ATF_DEFS_ATTRIBUTE_UNUSED
)
205 impl::atf_tps_reader::got_eof(void)
210 impl::atf_tps_reader::read_info(void* pptr
)
212 using atf::parser::parse_error
;
213 using namespace atf_tps
;
215 atf::parser::parser
< tokenizer
>& p
=
216 *reinterpret_cast< atf::parser::parser
< tokenizer
>* >
219 (void)p
.expect(colon_type
, "`:'");
221 atf::parser::token t
= p
.expect(text_type
, "info property name");
222 (void)p
.expect(comma_type
, "`,'");
223 got_info(t
.text(), atf::text::trim(p
.rest_of_line()));
225 (void)p
.expect(nl_type
, "new line");
229 impl::atf_tps_reader::read_tp(void* pptr
)
231 using atf::parser::parse_error
;
232 using namespace atf_tps
;
234 atf::parser::parser
< tokenizer
>& p
=
235 *reinterpret_cast< atf::parser::parser
< tokenizer
>* >
238 atf::parser::token t
= p
.expect(tp_start_type
,
239 "start of test program");
241 t
= p
.expect(colon_type
, "`:'");
243 struct timeval s1
= read_timeval(p
);
245 t
= p
.expect(comma_type
, "`,'");
247 t
= p
.expect(text_type
, "test program name");
248 std::string tpname
= t
.text();
250 t
= p
.expect(comma_type
, "`,'");
252 t
= p
.expect(text_type
, "number of test programs");
253 size_t ntcs
= string_to_int
< std::size_t >(t
.text());
255 t
= p
.expect(nl_type
, "new line");
257 ATF_PARSER_CALLBACK(p
, got_tp_start(tpname
, ntcs
));
260 while (p
.good() && i
< ntcs
) {
264 } catch (const parse_error
& pe
) {
269 t
= p
.expect(tp_end_type
, "end of test program");
271 t
= p
.expect(colon_type
, "`:'");
273 struct timeval s2
= read_timeval(p
);
276 timersub(&s2
, &s1
, &s3
);
278 t
= p
.expect(comma_type
, "`,'");
280 t
= p
.expect(text_type
, "test program name");
281 if (t
.text() != tpname
)
282 throw parse_error(t
.lineno(), "Test program name used in "
283 "terminator does not match "
286 t
= p
.expect(nl_type
, comma_type
,
287 "new line or comma_type");
289 if (t
.type() == comma_type
) {
290 reason
= text::trim(p
.rest_of_line());
292 throw parse_error(t
.lineno(),
293 "Empty reason for failed test program");
297 ATF_PARSER_CALLBACK(p
, got_tp_end(&s3
, reason
));
301 impl::atf_tps_reader::read_tc(void* pptr
)
303 using atf::parser::parse_error
;
304 using namespace atf_tps
;
306 atf::parser::parser
< tokenizer
>& p
=
307 *reinterpret_cast< atf::parser::parser
< tokenizer
>* >
310 atf::parser::token t
= p
.expect(tc_start_type
, "start of test case");
312 t
= p
.expect(colon_type
, "`:'");
314 struct timeval s1
= read_timeval(p
);
316 t
= p
.expect(comma_type
, "`,'");
318 t
= p
.expect(text_type
, "test case name");
319 std::string tcname
= t
.text();
321 ATF_PARSER_CALLBACK(p
, got_tc_start(tcname
));
323 t
= p
.expect(nl_type
, "new line");
325 t
= p
.expect(tc_end_type
, tc_so_type
, tc_se_type
,
326 "end of test case or test case's stdout/stderr line");
327 while (t
.type() != tc_end_type
&&
328 (t
.type() == tc_so_type
|| t
.type() == tc_se_type
)) {
329 atf::parser::token t2
= t
;
331 t
= p
.expect(colon_type
, "`:'");
333 std::string line
= p
.rest_of_line();
335 if (t2
.type() == tc_so_type
) {
336 ATF_PARSER_CALLBACK(p
, got_tc_stdout_line(line
));
338 INV(t2
.type() == tc_se_type
);
339 ATF_PARSER_CALLBACK(p
, got_tc_stderr_line(line
));
342 t
= p
.expect(nl_type
, "new line");
344 t
= p
.expect(tc_end_type
, tc_so_type
, tc_se_type
,
345 "end of test case or test case's stdout/stderr line");
348 t
= p
.expect(colon_type
, "`:'");
350 struct timeval s2
= read_timeval(p
);
353 timersub(&s2
, &s1
, &s3
);
355 t
= p
.expect(comma_type
, "`,'");
357 t
= p
.expect(text_type
, "test case name");
358 if (t
.text() != tcname
)
359 throw parse_error(t
.lineno(),
360 "Test case name used in terminator does not "
363 t
= p
.expect(comma_type
, "`,'");
365 t
= p
.expect(expected_death_type
, expected_exit_type
, expected_failure_type
,
366 expected_signal_type
, expected_timeout_type
, passed_type
, failed_type
,
367 skipped_type
, "expected_{death,exit,failure,signal,timeout}, failed, "
368 "passed or skipped");
369 if (t
.type() == passed_type
) {
370 ATF_PARSER_CALLBACK(p
, got_tc_end("passed", &s3
, ""));
373 if (t
.type() == expected_death_type
) state
= "expected_death";
374 else if (t
.type() == expected_exit_type
) state
= "expected_exit";
375 else if (t
.type() == expected_failure_type
) state
= "expected_failure";
376 else if (t
.type() == expected_signal_type
) state
= "expected_signal";
377 else if (t
.type() == expected_timeout_type
) state
= "expected_timeout";
378 else if (t
.type() == failed_type
) state
= "failed";
379 else if (t
.type() == skipped_type
) state
= "skipped";
382 t
= p
.expect(comma_type
, "`,'");
383 std::string reason
= text::trim(p
.rest_of_line());
385 throw parse_error(t
.lineno(), "Empty reason for " + state
+
386 " test case result");
387 ATF_PARSER_CALLBACK(p
, got_tc_end(state
, &s3
, reason
));
390 t
= p
.expect(nl_type
, "new line");
394 impl::atf_tps_reader::read(void)
396 using atf::parser::parse_error
;
397 using namespace atf_tps
;
399 std::pair
< size_t, atf::parser::headers_map
> hml
=
400 atf::parser::read_headers(m_is
, 1);
401 atf::parser::validate_content_type(hml
.second
, "application/X-atf-tps", 3);
403 tokenizer
tkz(m_is
, hml
.first
);
404 atf::parser::parser
< tokenizer
> p(tkz
);
407 atf::parser::token t
;
409 while ((t
= p
.expect(tps_count_type
, info_type
, "tps-count or info "
410 "field")).type() == info_type
)
413 t
= p
.expect(colon_type
, "`:'");
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
));
419 t
= p
.expect(nl_type
, "new line");
422 while (p
.good() && i
< ntps
) {
426 } catch (const parse_error
& pe
) {
432 while ((t
= p
.expect(eof_type
, info_type
, "end of stream or info "
433 "field")).type() == info_type
)
435 ATF_PARSER_CALLBACK(p
, got_eof());
436 } catch (const parse_error
& pe
) {