Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / atf / dist / atf-c++ / formats.cpp
blobb4035675df1fea86f54a5b218b6602e49b4e45c4
1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2007, 2008 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 <poll.h>
34 #include <map>
35 #include <sstream>
36 #include <utility>
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) \
47 do { \
48 if (!(parser).has_errors()) \
49 func; \
50 } while (false);
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;
67 class header_entry {
68 std::string m_name;
69 std::string m_value;
70 attrs_map m_attrs;
72 public:
73 header_entry(void);
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,
91 const std::string& v,
92 attrs_map as) :
93 m_name(n),
94 m_value(v),
95 m_attrs(as)
99 const std::string&
100 header_entry::name(void)
101 const
103 return m_name;
106 const std::string&
107 header_entry::value(void)
108 const
110 return m_value;
113 const attrs_map&
114 header_entry::attrs(void)
115 const
117 return m_attrs;
120 bool
121 header_entry::has_attr(const std::string& n)
122 const
124 return m_attrs.find(n) != m_attrs.end();
127 const std::string&
128 header_entry::get_attr(const std::string& n)
129 const
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 // ------------------------------------------------------------------------
140 namespace header {
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 > {
151 public:
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);
163 static
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) {
171 he = header_entry();
172 return p;
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();
181 attrs_map attrs;
183 for (;;) {
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)
187 break;
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);
201 return p;
204 static
205 std::ostream&
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;
218 return os;
221 } // namespace header
223 // ------------------------------------------------------------------------
224 // Auxiliary functions.
225 // ------------------------------------------------------------------------
227 static
228 size_t
229 string_to_size_t(const std::string& str)
231 std::istringstream ss(str);
232 size_t s;
233 ss >> s;
235 return s;
238 static
239 std::pair< size_t, headers_map >
240 read_headers(std::istream& is, size_t curline)
242 using impl::format_error;
244 headers_map hm;
247 // Grammar
249 // header = entry+ nl
250 // entry = line 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);
259 bool first = true;
260 for (;;) {
261 try {
262 header_entry he;
263 if (!header::read(p, he).good() || he.name().empty())
264 break;
266 if (first && he.name() != "Content-Type")
267 throw format_error("Could not determine content type");
268 else
269 first = false;
271 hm[he.name()] = he;
272 } catch (const atf::parser::parse_error& pe) {
273 p.add_error(pe);
274 p.reset(header::nl_type);
278 if (!is.good())
279 throw format_error("Unexpected end of stream");
281 return std::pair< size_t, headers_map >(tkz.lineno(), hm);
284 static
285 void
286 write_headers(const headers_map& hm,
287 std::ostream& os)
289 PRE(!hm.empty());
290 headers_map::const_iterator ct = hm.find("Content-Type");
291 PRE(ct != hm.end());
292 header::write(os, (*ct).second);
293 for (headers_map::const_iterator iter = hm.begin(); iter != hm.end();
294 iter++) {
295 if ((*iter).first != "Content-Type")
296 header::write(os, (*iter).second);
298 os << std::endl;
301 static
302 void
303 validate_content_type(const headers_map& hm,
304 const std::string& fmt,
305 int version)
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 > {
346 public:
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 > {
378 public:
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 // ------------------------------------------------------------------------
395 namespace atf_tcs {
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 > {
410 public:
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 // ------------------------------------------------------------------------
432 namespace atf_tps {
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 > {
452 public:
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) :
480 m_is(is)
484 impl::atf_atffile_reader::~atf_atffile_reader(void)
488 void
489 impl::atf_atffile_reader::got_conf(const std::string& name,
490 const std::string& val)
494 void
495 impl::atf_atffile_reader::got_prop(const std::string& name,
496 const std::string& val)
500 void
501 impl::atf_atffile_reader::got_tp(const std::string& name, bool isglob)
505 void
506 impl::atf_atffile_reader::got_eof(void)
510 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);
522 for (;;) {
523 try {
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)
529 break;
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) {
564 continue;
565 } else
566 UNREACHABLE;
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();
572 t = p.next();
573 } else if (t.type() == eof_type)
574 break;
575 } catch (const parse_error& pe) {
576 p.add_error(pe);
577 p.reset(nl_type);
581 CALLBACK(p, got_eof());
584 // ------------------------------------------------------------------------
585 // The "atf_config_reader" class.
586 // ------------------------------------------------------------------------
588 impl::atf_config_reader::atf_config_reader(std::istream& is) :
589 m_is(is)
593 impl::atf_config_reader::~atf_config_reader(void)
597 void
598 impl::atf_config_reader::got_var(const std::string& var,
599 const std::string& val)
603 void
604 impl::atf_config_reader::got_eof(void)
608 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);
620 for (;;) {
621 try {
622 atf::parser::token t = p.expect(eof_type, hash_type, text_type,
623 nl_type,
624 "eof, #, new line or text");
625 if (t.type() == eof_type)
626 break;
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) {
645 } else
646 UNREACHABLE;
647 } catch (const parse_error& pe) {
648 p.add_error(pe);
649 p.reset(nl_type);
653 CALLBACK(p, got_eof());
656 // ------------------------------------------------------------------------
657 // The "atf_tcs_reader" class.
658 // ------------------------------------------------------------------------
660 impl::atf_tcs_reader::atf_tcs_reader(std::istream& is) :
661 m_is(is)
665 impl::atf_tcs_reader::~atf_tcs_reader(void)
669 void
670 impl::atf_tcs_reader::got_ntcs(size_t ntcs)
674 void
675 impl::atf_tcs_reader::got_tc_start(const std::string& tcname)
679 void
680 impl::atf_tcs_reader::got_tc_end(const atf::tests::tcr& tcr)
684 void
685 impl::atf_tcs_reader::got_stdout_line(const std::string& line)
689 void
690 impl::atf_tcs_reader::got_stderr_line(const std::string& line)
694 void
695 impl::atf_tcs_reader::got_eof(void)
699 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 >* >
708 (pptr);
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;
716 do {
717 fds[0].revents = 0;
718 fds[1].revents = 0;
719 if (::poll(fds, 2, -1) == -1)
720 break;
722 if (fds[0].revents & POLLIN) {
723 std::string line;
724 if (atf::io::getline(out, line).good()) {
725 if (line == "__atf_tc_separator__")
726 fds[0].events &= ~POLLIN;
727 else
728 CALLBACK(p, got_stdout_line(line));
729 } else
730 fds[0].events &= ~POLLIN;
731 } else if (fds[0].revents & POLLHUP)
732 fds[0].events &= ~POLLIN;
734 if (fds[1].revents & POLLIN) {
735 std::string line;
736 if (atf::io::getline(err, line).good()) {
737 if (line == "__atf_tc_separator__")
738 fds[1].events &= ~POLLIN;
739 else
740 CALLBACK(p, got_stderr_line(line));
741 } else
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);
748 void
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);
762 try {
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");
772 size_t i = 0;
773 while (m_is.good() && i < ntcs) {
774 try {
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 "
798 "opening");
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());
809 if (reason.empty())
810 throw parse_error(t.lineno(),
811 "Empty reason for failed "
812 "test case result");
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());
817 if (reason.empty())
818 throw parse_error(t.lineno(),
819 "Empty reason for skipped "
820 "test case result");
821 CALLBACK(p, got_tc_end(tcr(tcr::skipped_state, reason)));
822 } else
823 UNREACHABLE;
825 t = p.expect(nl_type, "new line");
826 i++;
827 } catch (const parse_error& pe) {
828 p.add_error(pe);
829 p.reset(nl_type);
833 t = p.expect(eof_type, "end of stream");
834 CALLBACK(p, got_eof());
835 } catch (const parse_error& pe) {
836 p.add_error(pe);
837 p.reset(nl_type);
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,
848 size_t ntcs) :
849 m_os(os),
850 m_cout(p_cout),
851 m_cerr(p_cerr),
852 m_ntcs(ntcs),
853 m_curtc(0)
855 headers_map hm;
856 attrs_map ct_attrs;
857 ct_attrs["version"] = "1";
858 hm["Content-Type"] =
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;
863 m_os.flush();
866 void
867 impl::atf_tcs_writer::start_tc(const std::string& tcname)
869 m_tcname = tcname;
870 m_os << "tc-start: " << tcname << std::endl;
871 m_os.flush();
874 void
875 impl::atf_tcs_writer::end_tc(const atf::tests::tcr& tcr)
877 PRE(m_curtc < m_ntcs);
878 m_curtc++;
879 if (m_curtc < m_ntcs) {
880 m_cout << "__atf_tc_separator__\n";
881 m_cerr << "__atf_tc_separator__\n";
883 m_cout.flush();
884 m_cerr.flush();
886 std::string end = "tc-end: " + m_tcname + ", ";
887 if (tcr.get_state() == tests::tcr::passed_state)
888 end += "passed";
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();
893 else
894 UNREACHABLE;
895 m_os << end << std::endl;
896 m_os.flush();
899 // ------------------------------------------------------------------------
900 // The "atf_tps_reader" class.
901 // ------------------------------------------------------------------------
903 impl::atf_tps_reader::atf_tps_reader(std::istream& is) :
904 m_is(is)
908 impl::atf_tps_reader::~atf_tps_reader(void)
912 void
913 impl::atf_tps_reader::got_info(const std::string& what,
914 const std::string& val)
918 void
919 impl::atf_tps_reader::got_ntps(size_t ntps)
923 void
924 impl::atf_tps_reader::got_tp_start(const std::string& tp, size_t ntcs)
928 void
929 impl::atf_tps_reader::got_tp_end(const std::string& reason)
933 void
934 impl::atf_tps_reader::got_tc_start(const std::string& tcname)
938 void
939 impl::atf_tps_reader::got_tc_stdout_line(const std::string& line)
943 void
944 impl::atf_tps_reader::got_tc_stderr_line(const std::string& line)
948 void
949 impl::atf_tps_reader::got_tc_end(const atf::tests::tcr& tcr)
953 void
954 impl::atf_tps_reader::got_eof(void)
958 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 >* >
966 (pptr);
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");
977 void
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 >* >
985 (pptr);
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));
1004 size_t i = 0;
1005 while (p.good() && i < ntcs) {
1006 try {
1007 read_tc(&p);
1008 i++;
1009 } catch (const parse_error& pe) {
1010 p.add_error(pe);
1011 p.reset(nl_type);
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 "
1022 "opening");
1024 t = p.expect(nl_type, comma_type,
1025 "new line or comma_type");
1026 std::string reason;
1027 if (t.type() == comma_type) {
1028 reason = text::trim(p.rest_of_line());
1029 if (reason.empty())
1030 throw parse_error(t.lineno(),
1031 "Empty reason for failed test program");
1032 t = p.next();
1035 CALLBACK(p, got_tp_end(reason));
1038 void
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 >* >
1047 (pptr);
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));
1071 } else {
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 "
1088 "match opening");
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());
1099 if (reason.empty())
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());
1106 if (reason.empty())
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)));
1110 } else
1111 UNREACHABLE;
1113 t = p.expect(nl_type, "new line");
1116 void
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);
1128 try {
1129 atf::parser::token t;
1131 while ((t = p.expect(tps_count_type, info_type, "tps-count or info "
1132 "field")).type() == info_type)
1133 read_info(&p);
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");
1143 size_t i = 0;
1144 while (p.good() && i < ntps) {
1145 try {
1146 read_tp(&p);
1147 i++;
1148 } catch (const parse_error& pe) {
1149 p.add_error(pe);
1150 p.reset(nl_type);
1154 while ((t = p.expect(eof_type, info_type, "end of stream or info "
1155 "field")).type() == info_type)
1156 read_info(&p);
1157 CALLBACK(p, got_eof());
1158 } catch (const parse_error& pe) {
1159 p.add_error(pe);
1160 p.reset(nl_type);
1164 // ------------------------------------------------------------------------
1165 // The "atf_tps_writer" class.
1166 // ------------------------------------------------------------------------
1168 impl::atf_tps_writer::atf_tps_writer(std::ostream& os) :
1169 m_os(os)
1171 headers_map hm;
1172 attrs_map ct_attrs;
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);
1179 void
1180 impl::atf_tps_writer::info(const std::string& what, const std::string& val)
1182 m_os << "info: " << what << ", " << val << std::endl;
1183 m_os.flush();
1186 void
1187 impl::atf_tps_writer::ntps(size_t p_ntps)
1189 m_os << "tps-count: " << p_ntps << std::endl;
1190 m_os.flush();
1193 void
1194 impl::atf_tps_writer::start_tp(const std::string& tp, size_t ntcs)
1196 m_tpname = tp;
1197 m_os << "tp-start: " << tp << ", " << ntcs << std::endl;
1198 m_os.flush();
1201 void
1202 impl::atf_tps_writer::end_tp(const std::string& reason)
1204 PRE(reason.find('\n') == std::string::npos);
1205 if (reason.empty())
1206 m_os << "tp-end: " << m_tpname << std::endl;
1207 else
1208 m_os << "tp-end: " << m_tpname << ", " << reason << std::endl;
1209 m_os.flush();
1212 void
1213 impl::atf_tps_writer::start_tc(const std::string& tcname)
1215 m_tcname = tcname;
1216 m_os << "tc-start: " << tcname << std::endl;
1217 m_os.flush();
1220 void
1221 impl::atf_tps_writer::stdout_tc(const std::string& line)
1223 m_os << "tc-so:" << line << std::endl;
1224 m_os.flush();
1227 void
1228 impl::atf_tps_writer::stderr_tc(const std::string& line)
1230 m_os << "tc-se:" << line << std::endl;
1231 m_os.flush();
1234 void
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)
1239 str += "passed";
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();
1244 else
1245 UNREACHABLE;
1246 m_os << str << std::endl;
1247 m_os.flush();