2 // Automated Testing Framework (atf)
4 // Copyright (c) 2007, 2008 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++/formats.hpp"
39 #include "atf-c++/parser.hpp"
40 #include "atf-c++/sanity.hpp"
41 #include "atf-c++/text.hpp"
43 namespace impl
= atf::formats
;
44 #define IMPL_NAME "atf::formats"
46 #define CALLBACK(parser, func) \
48 if (!(parser).has_errors()) \
52 // ------------------------------------------------------------------------
53 // The "format_error" class.
54 // ------------------------------------------------------------------------
56 impl::format_error::format_error(const std::string
& w
) :
57 std::runtime_error(w
.c_str())
61 // ------------------------------------------------------------------------
62 // The "header_entry" class.
63 // ------------------------------------------------------------------------
65 typedef std::map
< std::string
, std::string
> attrs_map
;
74 header_entry(const std::string
&, const std::string
&,
75 attrs_map
= attrs_map());
77 const std::string
& name(void) const;
78 const std::string
& value(void) const;
79 const attrs_map
& attrs(void) const;
80 bool has_attr(const std::string
&) const;
81 const std::string
& get_attr(const std::string
&) const;
84 typedef std::map
< std::string
, header_entry
> headers_map
;
86 header_entry::header_entry(void)
90 header_entry::header_entry(const std::string
& n
,
100 header_entry::name(void)
107 header_entry::value(void)
114 header_entry::attrs(void)
121 header_entry::has_attr(const std::string
& n
)
124 return m_attrs
.find(n
) != m_attrs
.end();
128 header_entry::get_attr(const std::string
& n
)
131 attrs_map::const_iterator iter
= m_attrs
.find(n
);
132 PRE(iter
!= m_attrs
.end());
133 return (*iter
).second
;
136 // ------------------------------------------------------------------------
137 // The header tokenizer.
138 // ------------------------------------------------------------------------
142 static const atf::parser::token_type
& eof_type
= 0;
143 static const atf::parser::token_type
& nl_type
= 1;
144 static const atf::parser::token_type
& text_type
= 2;
145 static const atf::parser::token_type
& colon_type
= 3;
146 static const atf::parser::token_type
& semicolon_type
= 4;
147 static const atf::parser::token_type
& dblquote_type
= 5;
148 static const atf::parser::token_type
& equal_type
= 6;
150 class tokenizer
: public atf::parser::tokenizer
< std::istream
> {
152 tokenizer(std::istream
& is
, size_t curline
) :
153 atf::parser::tokenizer
< std::istream
>
154 (is
, true, eof_type
, nl_type
, text_type
, curline
)
156 add_delim(';', semicolon_type
);
157 add_delim(':', colon_type
);
158 add_delim('=', equal_type
);
159 add_quote('"', dblquote_type
);
164 atf::parser::parser
< header::tokenizer
>&
165 read(atf::parser::parser
< header::tokenizer
>& p
, header_entry
& he
)
167 using namespace header
;
169 atf::parser::token t
= p
.expect(text_type
, nl_type
, "a header name");
170 if (t
.type() == nl_type
) {
174 std::string hdr_name
= t
.text();
176 t
= p
.expect(colon_type
, "`:'");
178 t
= p
.expect(text_type
, "a textual value");
179 std::string hdr_value
= t
.text();
184 t
= p
.expect(eof_type
, semicolon_type
, nl_type
,
185 "eof, `;' or new line");
186 if (t
.type() == eof_type
|| t
.type() == nl_type
)
189 t
= p
.expect(text_type
, "an attribute name");
190 std::string attr_name
= t
.text();
192 t
= p
.expect(equal_type
, "`='");
194 t
= p
.expect(text_type
, "word or quoted string");
195 std::string attr_value
= t
.text();
196 attrs
[attr_name
] = attr_value
;
199 he
= header_entry(hdr_name
, hdr_value
, attrs
);
206 write(std::ostream
& os
, const header_entry
& he
)
208 std::string line
= he
.name() + ": " + he
.value();
209 attrs_map as
= he
.attrs();
210 for (attrs_map::const_iterator iter
= as
.begin();
211 iter
!= as
.end(); iter
++) {
212 PRE((*iter
).second
.find('\"') == std::string::npos
);
213 line
+= "; " + (*iter
).first
+ "=\"" + (*iter
).second
+ "\"";
216 os
<< line
<< std::endl
;
221 } // namespace header
223 // ------------------------------------------------------------------------
224 // Auxiliary functions.
225 // ------------------------------------------------------------------------
229 string_to_size_t(const std::string
& str
)
231 std::istringstream
ss(str
);
239 std::pair
< size_t, headers_map
>
240 read_headers(std::istream
& is
, size_t curline
)
242 using impl::format_error
;
249 // header = entry+ nl
251 // line = text colon text
252 // (semicolon (text equal (text | dblquote string dblquote)))*
253 // string = quoted_string
256 header::tokenizer
tkz(is
, curline
);
257 atf::parser::parser
< header::tokenizer
> p(tkz
);
263 if (!header::read(p
, he
).good() || he
.name().empty())
266 if (first
&& he
.name() != "Content-Type")
267 throw format_error("Could not determine content type");
272 } catch (const atf::parser::parse_error
& pe
) {
274 p
.reset(header::nl_type
);
279 throw format_error("Unexpected end of stream");
281 return std::pair
< size_t, headers_map
>(tkz
.lineno(), hm
);
286 write_headers(const headers_map
& hm
,
290 headers_map::const_iterator ct
= hm
.find("Content-Type");
292 header::write(os
, (*ct
).second
);
293 for (headers_map::const_iterator iter
= hm
.begin(); iter
!= hm
.end();
295 if ((*iter
).first
!= "Content-Type")
296 header::write(os
, (*iter
).second
);
303 validate_content_type(const headers_map
& hm
,
304 const std::string
& fmt
,
307 using impl::format_error
;
309 headers_map::const_iterator iter
= hm
.find("Content-Type");
310 if (iter
== hm
.end())
311 throw format_error("Could not determine content type");
313 const header_entry
& he
= (*iter
).second
;
314 if (he
.value() != fmt
)
315 throw format_error("Mismatched content type: expected `" + fmt
+
316 "' but got `" + he
.value() + "'");
318 if (!he
.has_attr("version"))
319 throw format_error("Could not determine version");
320 const std::string
& vstr
= atf::text::to_string(version
);
321 if (he
.get_attr("version") != vstr
)
322 throw format_error("Mismatched version: expected `" +
323 vstr
+ "' but got `" +
324 he
.get_attr("version") + "'");
327 // ------------------------------------------------------------------------
328 // The "atf_atffile" auxiliary parser.
329 // ------------------------------------------------------------------------
331 namespace atf_atffile
{
333 static const atf::parser::token_type eof_type
= 0;
334 static const atf::parser::token_type nl_type
= 1;
335 static const atf::parser::token_type text_type
= 2;
336 static const atf::parser::token_type colon_type
= 3;
337 static const atf::parser::token_type conf_type
= 4;
338 static const atf::parser::token_type dblquote_type
= 5;
339 static const atf::parser::token_type equal_type
= 6;
340 static const atf::parser::token_type hash_type
= 7;
341 static const atf::parser::token_type prop_type
= 8;
342 static const atf::parser::token_type tp_type
= 9;
343 static const atf::parser::token_type tp_glob_type
= 10;
345 class tokenizer
: public atf::parser::tokenizer
< std::istream
> {
347 tokenizer(std::istream
& is
, size_t curline
) :
348 atf::parser::tokenizer
< std::istream
>
349 (is
, true, eof_type
, nl_type
, text_type
, curline
)
351 add_delim(':', colon_type
);
352 add_delim('=', equal_type
);
353 add_delim('#', hash_type
);
354 add_quote('"', dblquote_type
);
355 add_keyword("conf", conf_type
);
356 add_keyword("prop", prop_type
);
357 add_keyword("tp", tp_type
);
358 add_keyword("tp-glob", tp_glob_type
);
362 } // namespace atf_atffile
364 // ------------------------------------------------------------------------
365 // The "atf_config" auxiliary parser.
366 // ------------------------------------------------------------------------
368 namespace atf_config
{
370 static const atf::parser::token_type
& eof_type
= 0;
371 static const atf::parser::token_type
& nl_type
= 1;
372 static const atf::parser::token_type
& text_type
= 2;
373 static const atf::parser::token_type
& dblquote_type
= 3;
374 static const atf::parser::token_type
& equal_type
= 4;
375 static const atf::parser::token_type
& hash_type
= 5;
377 class tokenizer
: public atf::parser::tokenizer
< std::istream
> {
379 tokenizer(std::istream
& is
, size_t curline
) :
380 atf::parser::tokenizer
< std::istream
>
381 (is
, true, eof_type
, nl_type
, text_type
, curline
)
383 add_delim('=', equal_type
);
384 add_delim('#', hash_type
);
385 add_quote('"', dblquote_type
);
389 } // namespace atf_config
391 // ------------------------------------------------------------------------
392 // The "atf_tcs" auxiliary parser.
393 // ------------------------------------------------------------------------
397 static const atf::parser::token_type
& eof_type
= 0;
398 static const atf::parser::token_type
& nl_type
= 1;
399 static const atf::parser::token_type
& text_type
= 2;
400 static const atf::parser::token_type
& colon_type
= 3;
401 static const atf::parser::token_type
& comma_type
= 4;
402 static const atf::parser::token_type
& tcs_count_type
= 5;
403 static const atf::parser::token_type
& tc_start_type
= 6;
404 static const atf::parser::token_type
& tc_end_type
= 7;
405 static const atf::parser::token_type
& passed_type
= 8;
406 static const atf::parser::token_type
& failed_type
= 9;
407 static const atf::parser::token_type
& skipped_type
= 10;
409 class tokenizer
: public atf::parser::tokenizer
< std::istream
> {
411 tokenizer(std::istream
& is
, size_t curline
) :
412 atf::parser::tokenizer
< std::istream
>
413 (is
, true, eof_type
, nl_type
, text_type
, curline
)
415 add_delim(':', colon_type
);
416 add_delim(',', comma_type
);
417 add_keyword("tcs-count", tcs_count_type
);
418 add_keyword("tc-start", tc_start_type
);
419 add_keyword("tc-end", tc_end_type
);
420 add_keyword("passed", passed_type
);
421 add_keyword("failed", failed_type
);
422 add_keyword("skipped", skipped_type
);
426 } // namespace atf_tcs
428 // ------------------------------------------------------------------------
429 // The "atf_tps" auxiliary parser.
430 // ------------------------------------------------------------------------
434 static const atf::parser::token_type
& eof_type
= 0;
435 static const atf::parser::token_type
& nl_type
= 1;
436 static const atf::parser::token_type
& text_type
= 2;
437 static const atf::parser::token_type
& colon_type
= 3;
438 static const atf::parser::token_type
& comma_type
= 4;
439 static const atf::parser::token_type
& tps_count_type
= 5;
440 static const atf::parser::token_type
& tp_start_type
= 6;
441 static const atf::parser::token_type
& tp_end_type
= 7;
442 static const atf::parser::token_type
& tc_start_type
= 8;
443 static const atf::parser::token_type
& tc_so_type
= 9;
444 static const atf::parser::token_type
& tc_se_type
= 10;
445 static const atf::parser::token_type
& tc_end_type
= 11;
446 static const atf::parser::token_type
& passed_type
= 12;
447 static const atf::parser::token_type
& failed_type
= 13;
448 static const atf::parser::token_type
& skipped_type
= 14;
449 static const atf::parser::token_type
& info_type
= 16;
451 class tokenizer
: public atf::parser::tokenizer
< std::istream
> {
453 tokenizer(std::istream
& is
, size_t curline
) :
454 atf::parser::tokenizer
< std::istream
>
455 (is
, true, eof_type
, nl_type
, text_type
, curline
)
457 add_delim(':', colon_type
);
458 add_delim(',', comma_type
);
459 add_keyword("tps-count", tps_count_type
);
460 add_keyword("tp-start", tp_start_type
);
461 add_keyword("tp-end", tp_end_type
);
462 add_keyword("tc-start", tc_start_type
);
463 add_keyword("tc-so", tc_so_type
);
464 add_keyword("tc-se", tc_se_type
);
465 add_keyword("tc-end", tc_end_type
);
466 add_keyword("passed", passed_type
);
467 add_keyword("failed", failed_type
);
468 add_keyword("skipped", skipped_type
);
469 add_keyword("info", info_type
);
473 } // namespace atf_tps
475 // ------------------------------------------------------------------------
476 // The "atf_atffile_reader" class.
477 // ------------------------------------------------------------------------
479 impl::atf_atffile_reader::atf_atffile_reader(std::istream
& is
) :
484 impl::atf_atffile_reader::~atf_atffile_reader(void)
489 impl::atf_atffile_reader::got_conf(const std::string
& name
,
490 const std::string
& val
)
495 impl::atf_atffile_reader::got_prop(const std::string
& name
,
496 const std::string
& val
)
501 impl::atf_atffile_reader::got_tp(const std::string
& name
, bool isglob
)
506 impl::atf_atffile_reader::got_eof(void)
511 impl::atf_atffile_reader::read(void)
513 using atf::parser::parse_error
;
514 using namespace atf_atffile
;
516 std::pair
< size_t, headers_map
> hml
= read_headers(m_is
, 1);
517 validate_content_type(hml
.second
, "application/X-atf-atffile", 1);
519 tokenizer
tkz(m_is
, hml
.first
);
520 atf::parser::parser
< tokenizer
> p(tkz
);
524 atf::parser::token t
=
525 p
.expect(conf_type
, hash_type
, prop_type
, tp_type
,
526 tp_glob_type
, nl_type
, eof_type
,
527 "conf, #, prop, tp, tp-glob, a new line or eof");
528 if (t
.type() == eof_type
)
531 if (t
.type() == conf_type
) {
532 t
= p
.expect(colon_type
, "`:'");
534 t
= p
.expect(text_type
, "variable name");
535 std::string var
= t
.text();
537 t
= p
.expect(equal_type
, "equal sign");
539 t
= p
.expect(text_type
, "word or quoted string");
540 CALLBACK(p
, got_conf(var
, t
.text()));
541 } else if (t
.type() == hash_type
) {
542 (void)p
.rest_of_line();
543 } else if (t
.type() == prop_type
) {
544 t
= p
.expect(colon_type
, "`:'");
546 t
= p
.expect(text_type
, "property name");
547 std::string name
= t
.text();
549 t
= p
.expect(equal_type
, "equale sign");
551 t
= p
.expect(text_type
, "word or quoted string");
552 CALLBACK(p
, got_prop(name
, t
.text()));
553 } else if (t
.type() == tp_type
) {
554 t
= p
.expect(colon_type
, "`:'");
556 t
= p
.expect(text_type
, "word or quoted string");
557 CALLBACK(p
, got_tp(t
.text(), false));
558 } else if (t
.type() == tp_glob_type
) {
559 t
= p
.expect(colon_type
, "`:'");
561 t
= p
.expect(text_type
, "word or quoted string");
562 CALLBACK(p
, got_tp(t
.text(), true));
563 } else if (t
.type() == nl_type
) {
568 t
= p
.expect(nl_type
, hash_type
, eof_type
,
569 "new line or comment");
570 if (t
.type() == hash_type
) {
571 (void)p
.rest_of_line();
573 } else if (t
.type() == eof_type
)
575 } catch (const parse_error
& pe
) {
581 CALLBACK(p
, got_eof());
584 // ------------------------------------------------------------------------
585 // The "atf_config_reader" class.
586 // ------------------------------------------------------------------------
588 impl::atf_config_reader::atf_config_reader(std::istream
& is
) :
593 impl::atf_config_reader::~atf_config_reader(void)
598 impl::atf_config_reader::got_var(const std::string
& var
,
599 const std::string
& val
)
604 impl::atf_config_reader::got_eof(void)
609 impl::atf_config_reader::read(void)
611 using atf::parser::parse_error
;
612 using namespace atf_config
;
614 std::pair
< size_t, headers_map
> hml
= read_headers(m_is
, 1);
615 validate_content_type(hml
.second
, "application/X-atf-config", 1);
617 tokenizer
tkz(m_is
, hml
.first
);
618 atf::parser::parser
< tokenizer
> p(tkz
);
622 atf::parser::token t
= p
.expect(eof_type
, hash_type
, text_type
,
624 "eof, #, new line or text");
625 if (t
.type() == eof_type
)
628 if (t
.type() == hash_type
) {
629 (void)p
.rest_of_line();
630 t
= p
.expect(nl_type
, "new line");
631 } else if (t
.type() == text_type
) {
632 std::string name
= t
.text();
634 t
= p
.expect(equal_type
, "equal sign");
636 t
= p
.expect(text_type
, "word or quoted string");
637 CALLBACK(p
, got_var(name
, t
.text()));
639 t
= p
.expect(nl_type
, hash_type
, "new line or comment");
640 if (t
.type() == hash_type
) {
641 (void)p
.rest_of_line();
642 t
= p
.expect(nl_type
, "new line");
644 } else if (t
.type() == nl_type
) {
647 } catch (const parse_error
& pe
) {
653 CALLBACK(p
, got_eof());
656 // ------------------------------------------------------------------------
657 // The "atf_tcs_reader" class.
658 // ------------------------------------------------------------------------
660 impl::atf_tcs_reader::atf_tcs_reader(std::istream
& is
) :
665 impl::atf_tcs_reader::~atf_tcs_reader(void)
670 impl::atf_tcs_reader::got_ntcs(size_t ntcs
)
675 impl::atf_tcs_reader::got_tc_start(const std::string
& tcname
)
680 impl::atf_tcs_reader::got_tc_end(const atf::tests::tcr
& tcr
)
685 impl::atf_tcs_reader::got_stdout_line(const std::string
& line
)
690 impl::atf_tcs_reader::got_stderr_line(const std::string
& line
)
695 impl::atf_tcs_reader::got_eof(void)
700 impl::atf_tcs_reader::read_out_err(void* pptr
,
701 atf::io::unbuffered_istream
& out
,
702 atf::io::unbuffered_istream
& err
)
704 using namespace atf_tps
;
706 atf::parser::parser
< tokenizer
>& p
=
707 *reinterpret_cast< atf::parser::parser
< tokenizer
>* >
710 struct pollfd fds
[2];
711 fds
[0].fd
= out
.get_fh().get();
712 fds
[0].events
= POLLIN
;
713 fds
[1].fd
= err
.get_fh().get();
714 fds
[1].events
= POLLIN
;
719 if (::poll(fds
, 2, -1) == -1)
722 if (fds
[0].revents
& POLLIN
) {
724 if (atf::io::getline(out
, line
).good()) {
725 if (line
== "__atf_tc_separator__")
726 fds
[0].events
&= ~POLLIN
;
728 CALLBACK(p
, got_stdout_line(line
));
730 fds
[0].events
&= ~POLLIN
;
731 } else if (fds
[0].revents
& POLLHUP
)
732 fds
[0].events
&= ~POLLIN
;
734 if (fds
[1].revents
& POLLIN
) {
736 if (atf::io::getline(err
, line
).good()) {
737 if (line
== "__atf_tc_separator__")
738 fds
[1].events
&= ~POLLIN
;
740 CALLBACK(p
, got_stderr_line(line
));
742 fds
[1].events
&= ~POLLIN
;
743 } else if (fds
[1].revents
& POLLHUP
)
744 fds
[1].events
&= ~POLLIN
;
745 } while (fds
[0].events
& POLLIN
|| fds
[1].events
& POLLIN
);
749 impl::atf_tcs_reader::read(atf::io::unbuffered_istream
& out
,
750 atf::io::unbuffered_istream
& err
)
752 using atf::parser::parse_error
;
753 using namespace atf_tcs
;
754 using atf::tests::tcr
;
756 std::pair
< size_t, headers_map
> hml
= read_headers(m_is
, 1);
757 validate_content_type(hml
.second
, "application/X-atf-tcs", 1);
759 tokenizer
tkz(m_is
, hml
.first
);
760 atf::parser::parser
< tokenizer
> p(tkz
);
763 atf::parser::token t
= p
.expect(tcs_count_type
, "tcs-count field");
764 t
= p
.expect(colon_type
, "`:'");
766 t
= p
.expect(text_type
, "number of test cases");
767 size_t ntcs
= string_to_size_t(t
.text());
768 CALLBACK(p
, got_ntcs(ntcs
));
770 t
= p
.expect(nl_type
, "new line");
773 while (m_is
.good() && i
< ntcs
) {
775 t
= p
.expect(tc_start_type
, "start of test case");
777 t
= p
.expect(colon_type
, "`:'");
779 t
= p
.expect(text_type
, "test case name");
780 std::string tcname
= t
.text();
781 CALLBACK(p
, got_tc_start(tcname
));
783 t
= p
.expect(nl_type
, "new line");
785 read_out_err(&p
, out
, err
);
786 if (i
< ntcs
- 1 && (!out
.good() || !err
.good()))
787 p
.add_error(parse_error(0, "Missing terminators in "
788 "stdout or stderr"));
790 t
= p
.expect(tc_end_type
, "end of test case");
792 t
= p
.expect(colon_type
, "`:'");
794 t
= p
.expect(text_type
, "test case name");
795 if (t
.text() != tcname
)
796 throw parse_error(t
.lineno(), "Test case name used in "
797 "terminator does not match "
800 t
= p
.expect(comma_type
, "`,'");
802 t
= p
.expect(passed_type
, skipped_type
, failed_type
,
803 "passed, failed or skipped");
804 if (t
.type() == passed_type
) {
805 CALLBACK(p
, got_tc_end(tcr(tcr::passed_state
)));
806 } else if (t
.type() == failed_type
) {
807 t
= p
.expect(comma_type
, "`,'");
808 std::string reason
= text::trim(p
.rest_of_line());
810 throw parse_error(t
.lineno(),
811 "Empty reason for failed "
813 CALLBACK(p
, got_tc_end(tcr(tcr::failed_state
, reason
)));
814 } else if (t
.type() == skipped_type
) {
815 t
= p
.expect(comma_type
, "`,'");
816 std::string reason
= text::trim(p
.rest_of_line());
818 throw parse_error(t
.lineno(),
819 "Empty reason for skipped "
821 CALLBACK(p
, got_tc_end(tcr(tcr::skipped_state
, reason
)));
825 t
= p
.expect(nl_type
, "new line");
827 } catch (const parse_error
& pe
) {
833 t
= p
.expect(eof_type
, "end of stream");
834 CALLBACK(p
, got_eof());
835 } catch (const parse_error
& pe
) {
841 // ------------------------------------------------------------------------
842 // The "atf_tcs_writer" class.
843 // ------------------------------------------------------------------------
845 impl::atf_tcs_writer::atf_tcs_writer(std::ostream
& os
,
846 std::ostream
& p_cout
,
847 std::ostream
& p_cerr
,
857 ct_attrs
["version"] = "1";
859 header_entry("Content-Type", "application/X-atf-tcs", ct_attrs
);
860 write_headers(hm
, m_os
);
862 m_os
<< "tcs-count: " << ntcs
<< std::endl
;
867 impl::atf_tcs_writer::start_tc(const std::string
& tcname
)
870 m_os
<< "tc-start: " << tcname
<< std::endl
;
875 impl::atf_tcs_writer::end_tc(const atf::tests::tcr
& tcr
)
877 PRE(m_curtc
< m_ntcs
);
879 if (m_curtc
< m_ntcs
) {
880 m_cout
<< "__atf_tc_separator__\n";
881 m_cerr
<< "__atf_tc_separator__\n";
886 std::string end
= "tc-end: " + m_tcname
+ ", ";
887 if (tcr
.get_state() == tests::tcr::passed_state
)
889 else if (tcr
.get_state() == tests::tcr::failed_state
)
890 end
+= "failed, " + tcr
.get_reason();
891 else if (tcr
.get_state() == tests::tcr::skipped_state
)
892 end
+= "skipped, " + tcr
.get_reason();
895 m_os
<< end
<< std::endl
;
899 // ------------------------------------------------------------------------
900 // The "atf_tps_reader" class.
901 // ------------------------------------------------------------------------
903 impl::atf_tps_reader::atf_tps_reader(std::istream
& is
) :
908 impl::atf_tps_reader::~atf_tps_reader(void)
913 impl::atf_tps_reader::got_info(const std::string
& what
,
914 const std::string
& val
)
919 impl::atf_tps_reader::got_ntps(size_t ntps
)
924 impl::atf_tps_reader::got_tp_start(const std::string
& tp
, size_t ntcs
)
929 impl::atf_tps_reader::got_tp_end(const std::string
& reason
)
934 impl::atf_tps_reader::got_tc_start(const std::string
& tcname
)
939 impl::atf_tps_reader::got_tc_stdout_line(const std::string
& line
)
944 impl::atf_tps_reader::got_tc_stderr_line(const std::string
& line
)
949 impl::atf_tps_reader::got_tc_end(const atf::tests::tcr
& tcr
)
954 impl::atf_tps_reader::got_eof(void)
959 impl::atf_tps_reader::read_info(void* pptr
)
961 using atf::parser::parse_error
;
962 using namespace atf_tps
;
964 atf::parser::parser
< tokenizer
>& p
=
965 *reinterpret_cast< atf::parser::parser
< tokenizer
>* >
968 (void)p
.expect(colon_type
, "`:'");
970 atf::parser::token t
= p
.expect(text_type
, "info property name");
971 (void)p
.expect(comma_type
, "`,'");
972 got_info(t
.text(), atf::text::trim(p
.rest_of_line()));
974 (void)p
.expect(nl_type
, "new line");
978 impl::atf_tps_reader::read_tp(void* pptr
)
980 using atf::parser::parse_error
;
981 using namespace atf_tps
;
983 atf::parser::parser
< tokenizer
>& p
=
984 *reinterpret_cast< atf::parser::parser
< tokenizer
>* >
987 atf::parser::token t
= p
.expect(tp_start_type
,
988 "start of test program");
990 t
= p
.expect(colon_type
, "`:'");
992 t
= p
.expect(text_type
, "test program name");
993 std::string tpname
= t
.text();
995 t
= p
.expect(comma_type
, "`,'");
997 t
= p
.expect(text_type
, "number of test programs");
998 size_t ntcs
= string_to_size_t(t
.text());
1000 t
= p
.expect(nl_type
, "new line");
1002 CALLBACK(p
, got_tp_start(tpname
, ntcs
));
1005 while (p
.good() && i
< ntcs
) {
1009 } catch (const parse_error
& pe
) {
1014 t
= p
.expect(tp_end_type
, "end of test program");
1016 t
= p
.expect(colon_type
, "`:'");
1018 t
= p
.expect(text_type
, "test program name");
1019 if (t
.text() != tpname
)
1020 throw parse_error(t
.lineno(), "Test program name used in "
1021 "terminator does not match "
1024 t
= p
.expect(nl_type
, comma_type
,
1025 "new line or comma_type");
1027 if (t
.type() == comma_type
) {
1028 reason
= text::trim(p
.rest_of_line());
1030 throw parse_error(t
.lineno(),
1031 "Empty reason for failed test program");
1035 CALLBACK(p
, got_tp_end(reason
));
1039 impl::atf_tps_reader::read_tc(void* pptr
)
1041 using atf::parser::parse_error
;
1042 using namespace atf_tps
;
1043 using atf::tests::tcr
;
1045 atf::parser::parser
< tokenizer
>& p
=
1046 *reinterpret_cast< atf::parser::parser
< tokenizer
>* >
1049 atf::parser::token t
= p
.expect(tc_start_type
, "start of test case");
1051 t
= p
.expect(colon_type
, "`:'");
1053 t
= p
.expect(text_type
, "test case name");
1054 std::string tcname
= t
.text();
1055 CALLBACK(p
, got_tc_start(tcname
));
1057 t
= p
.expect(nl_type
, "new line");
1059 t
= p
.expect(tc_end_type
, tc_so_type
, tc_se_type
,
1060 "end of test case or test case's stdout/stderr line");
1061 while (t
.type() != tc_end_type
&&
1062 (t
.type() == tc_so_type
|| t
.type() == tc_se_type
)) {
1063 atf::parser::token t2
= t
;
1065 t
= p
.expect(colon_type
, "`:'");
1067 std::string line
= p
.rest_of_line();
1069 if (t2
.type() == tc_so_type
) {
1070 CALLBACK(p
, got_tc_stdout_line(line
));
1072 INV(t2
.type() == tc_se_type
);
1073 CALLBACK(p
, got_tc_stderr_line(line
));
1076 t
= p
.expect(nl_type
, "new line");
1078 t
= p
.expect(tc_end_type
, tc_so_type
, tc_se_type
,
1079 "end of test case or test case's stdout/stderr line");
1082 t
= p
.expect(colon_type
, "`:'");
1084 t
= p
.expect(text_type
, "test case name");
1085 if (t
.text() != tcname
)
1086 throw parse_error(t
.lineno(),
1087 "Test case name used in terminator does not "
1090 t
= p
.expect(comma_type
, "`,'");
1092 t
= p
.expect(passed_type
, failed_type
, skipped_type
,
1093 "passed, failed or skipped");
1094 if (t
.type() == passed_type
) {
1095 CALLBACK(p
, got_tc_end(tcr(tcr::passed_state
)));
1096 } else if (t
.type() == failed_type
) {
1097 t
= p
.expect(comma_type
, "`,'");
1098 std::string reason
= text::trim(p
.rest_of_line());
1100 throw parse_error(t
.lineno(),
1101 "Empty reason for failed test case result");
1102 CALLBACK(p
, got_tc_end(tcr(tcr::failed_state
, reason
)));
1103 } else if (t
.type() == skipped_type
) {
1104 t
= p
.expect(comma_type
, "`,'");
1105 std::string reason
= text::trim(p
.rest_of_line());
1107 throw parse_error(t
.lineno(),
1108 "Empty reason for skipped test case result");
1109 CALLBACK(p
, got_tc_end(tcr(tcr::skipped_state
, reason
)));
1113 t
= p
.expect(nl_type
, "new line");
1117 impl::atf_tps_reader::read(void)
1119 using atf::parser::parse_error
;
1120 using namespace atf_tps
;
1122 std::pair
< size_t, headers_map
> hml
= read_headers(m_is
, 1);
1123 validate_content_type(hml
.second
, "application/X-atf-tps", 2);
1125 tokenizer
tkz(m_is
, hml
.first
);
1126 atf::parser::parser
< tokenizer
> p(tkz
);
1129 atf::parser::token t
;
1131 while ((t
= p
.expect(tps_count_type
, info_type
, "tps-count or info "
1132 "field")).type() == info_type
)
1135 t
= p
.expect(colon_type
, "`:'");
1137 t
= p
.expect(text_type
, "number of test programs");
1138 size_t ntps
= string_to_size_t(t
.text());
1139 CALLBACK(p
, got_ntps(ntps
));
1141 t
= p
.expect(nl_type
, "new line");
1144 while (p
.good() && i
< ntps
) {
1148 } catch (const parse_error
& pe
) {
1154 while ((t
= p
.expect(eof_type
, info_type
, "end of stream or info "
1155 "field")).type() == info_type
)
1157 CALLBACK(p
, got_eof());
1158 } catch (const parse_error
& pe
) {
1164 // ------------------------------------------------------------------------
1165 // The "atf_tps_writer" class.
1166 // ------------------------------------------------------------------------
1168 impl::atf_tps_writer::atf_tps_writer(std::ostream
& os
) :
1173 ct_attrs
["version"] = "2";
1174 hm
["Content-Type"] =
1175 header_entry("Content-Type", "application/X-atf-tps", ct_attrs
);
1176 write_headers(hm
, m_os
);
1180 impl::atf_tps_writer::info(const std::string
& what
, const std::string
& val
)
1182 m_os
<< "info: " << what
<< ", " << val
<< std::endl
;
1187 impl::atf_tps_writer::ntps(size_t p_ntps
)
1189 m_os
<< "tps-count: " << p_ntps
<< std::endl
;
1194 impl::atf_tps_writer::start_tp(const std::string
& tp
, size_t ntcs
)
1197 m_os
<< "tp-start: " << tp
<< ", " << ntcs
<< std::endl
;
1202 impl::atf_tps_writer::end_tp(const std::string
& reason
)
1204 PRE(reason
.find('\n') == std::string::npos
);
1206 m_os
<< "tp-end: " << m_tpname
<< std::endl
;
1208 m_os
<< "tp-end: " << m_tpname
<< ", " << reason
<< std::endl
;
1213 impl::atf_tps_writer::start_tc(const std::string
& tcname
)
1216 m_os
<< "tc-start: " << tcname
<< std::endl
;
1221 impl::atf_tps_writer::stdout_tc(const std::string
& line
)
1223 m_os
<< "tc-so:" << line
<< std::endl
;
1228 impl::atf_tps_writer::stderr_tc(const std::string
& line
)
1230 m_os
<< "tc-se:" << line
<< std::endl
;
1235 impl::atf_tps_writer::end_tc(const atf::tests::tcr
& tcr
)
1237 std::string str
= "tc-end: " + m_tcname
+ ", ";
1238 if (tcr
.get_state() == atf::tests::tcr::passed_state
)
1240 else if (tcr
.get_state() == atf::tests::tcr::skipped_state
)
1241 str
+= "skipped, " + tcr
.get_reason();
1242 else if (tcr
.get_state() == atf::tests::tcr::failed_state
)
1243 str
+= "failed, " + tcr
.get_reason();
1246 m_os
<< str
<< std::endl
;