1// 2// Automated Testing Framework (atf) 3// 4// Copyright (c) 2007, 2008, 2009, 2010 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. 15// 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. 28// 29 30#include <map> 31#include <sstream> 32#include <utility> 33 34#include "atf-c++/detail/parser.hpp" 35#include "atf-c++/detail/sanity.hpp" 36#include "atf-c++/detail/text.hpp" 37 38#include "reader.hpp" 39 40namespace impl = atf::atf_report; 41#define IMPL_NAME "atf::atf_report" 42 43// ------------------------------------------------------------------------ 44// Auxiliary functions. 45// ------------------------------------------------------------------------ 46 47static 48size_t 49string_to_size_t(const std::string& str) 50{ 51 std::istringstream ss(str); 52 size_t s; 53 ss >> s; 54 55 return s; 56} 57 58// ------------------------------------------------------------------------ 59// The "atf_tps" auxiliary parser. 60// ------------------------------------------------------------------------ 61 62namespace atf_tps { 63 64static const atf::parser::token_type eof_type = 0; 65static const atf::parser::token_type nl_type = 1; 66static const atf::parser::token_type text_type = 2; 67static const atf::parser::token_type colon_type = 3; 68static const atf::parser::token_type comma_type = 4; 69static const atf::parser::token_type tps_count_type = 5; 70static const atf::parser::token_type tp_start_type = 6; 71static const atf::parser::token_type tp_end_type = 7; 72static const atf::parser::token_type tc_start_type = 8; 73static const atf::parser::token_type tc_so_type = 9; 74static const atf::parser::token_type tc_se_type = 10; 75static const atf::parser::token_type tc_end_type = 11; 76static const atf::parser::token_type passed_type = 12; 77static const atf::parser::token_type failed_type = 13; 78static const atf::parser::token_type skipped_type = 14; 79static const atf::parser::token_type info_type = 16; 80static const atf::parser::token_type expected_death_type = 17; 81static const atf::parser::token_type expected_exit_type = 18; 82static const atf::parser::token_type expected_failure_type = 19; 83static const atf::parser::token_type expected_signal_type = 20; 84static const atf::parser::token_type expected_timeout_type = 21; 85 86class tokenizer : public atf::parser::tokenizer< std::istream > { 87public: 88 tokenizer(std::istream& is, size_t curline) : 89 atf::parser::tokenizer< std::istream > 90 (is, true, eof_type, nl_type, text_type, curline) 91 { 92 add_delim(':', colon_type); 93 add_delim(',', comma_type); 94 add_keyword("tps-count", tps_count_type); 95 add_keyword("tp-start", tp_start_type); 96 add_keyword("tp-end", tp_end_type); 97 add_keyword("tc-start", tc_start_type); 98 add_keyword("tc-so", tc_so_type); 99 add_keyword("tc-se", tc_se_type); 100 add_keyword("tc-end", tc_end_type); 101 add_keyword("passed", passed_type); 102 add_keyword("failed", failed_type); 103 add_keyword("skipped", skipped_type); 104 add_keyword("info", info_type); 105 add_keyword("expected_death", expected_death_type); 106 add_keyword("expected_exit", expected_exit_type); 107 add_keyword("expected_failure", expected_failure_type); 108 add_keyword("expected_signal", expected_signal_type); 109 add_keyword("expected_timeout", expected_timeout_type); 110 } 111}; 112 113} // namespace atf_tps 114 115// ------------------------------------------------------------------------ 116// The "atf_tps_reader" class. 117// ------------------------------------------------------------------------ 118 119impl::atf_tps_reader::atf_tps_reader(std::istream& is) : 120 m_is(is) 121{ 122} 123 124impl::atf_tps_reader::~atf_tps_reader(void) 125{ 126} 127 128void 129impl::atf_tps_reader::got_info(const std::string& what, 130 const std::string& val) 131{ 132} 133 134void 135impl::atf_tps_reader::got_ntps(size_t ntps) 136{ 137} 138 139void 140impl::atf_tps_reader::got_tp_start(const std::string& tp, size_t ntcs) 141{ 142} 143 144void 145impl::atf_tps_reader::got_tp_end(const std::string& reason) 146{ 147} 148 149void 150impl::atf_tps_reader::got_tc_start(const std::string& tcname) 151{ 152} 153 154void 155impl::atf_tps_reader::got_tc_stdout_line(const std::string& line) 156{ 157} 158 159void 160impl::atf_tps_reader::got_tc_stderr_line(const std::string& line) 161{ 162} 163 164void 165impl::atf_tps_reader::got_tc_end(const std::string& state, 166 const std::string& reason) 167{ 168} 169 170void 171impl::atf_tps_reader::got_eof(void) 172{ 173} 174 175void 176impl::atf_tps_reader::read_info(void* pptr) 177{ 178 using atf::parser::parse_error; 179 using namespace atf_tps; 180 181 atf::parser::parser< tokenizer >& p = 182 *reinterpret_cast< atf::parser::parser< tokenizer >* > 183 (pptr); 184 185 (void)p.expect(colon_type, "`:'"); 186 187 atf::parser::token t = p.expect(text_type, "info property name"); 188 (void)p.expect(comma_type, "`,'"); 189 got_info(t.text(), atf::text::trim(p.rest_of_line())); 190 191 (void)p.expect(nl_type, "new line"); 192} 193 194void 195impl::atf_tps_reader::read_tp(void* pptr) 196{ 197 using atf::parser::parse_error; 198 using namespace atf_tps; 199 200 atf::parser::parser< tokenizer >& p = 201 *reinterpret_cast< atf::parser::parser< tokenizer >* > 202 (pptr); 203 204 atf::parser::token t = p.expect(tp_start_type, 205 "start of test program"); 206 207 t = p.expect(colon_type, "`:'"); 208 209 t = p.expect(text_type, "test program name"); 210 std::string tpname = t.text(); 211 212 t = p.expect(comma_type, "`,'"); 213 214 t = p.expect(text_type, "number of test programs"); 215 size_t ntcs = string_to_size_t(t.text()); 216 217 t = p.expect(nl_type, "new line"); 218 219 ATF_PARSER_CALLBACK(p, got_tp_start(tpname, ntcs)); 220 221 size_t i = 0; 222 while (p.good() && i < ntcs) { 223 try { 224 read_tc(&p); 225 i++; 226 } catch (const parse_error& pe) { 227 p.add_error(pe); 228 p.reset(nl_type); 229 } 230 } 231 t = p.expect(tp_end_type, "end of test program"); 232 233 t = p.expect(colon_type, "`:'"); 234 235 t = p.expect(text_type, "test program name"); 236 if (t.text() != tpname) 237 throw parse_error(t.lineno(), "Test program name used in " 238 "terminator does not match " 239 "opening"); 240 241 t = p.expect(nl_type, comma_type, 242 "new line or comma_type"); 243 std::string reason; 244 if (t.type() == comma_type) { 245 reason = text::trim(p.rest_of_line()); 246 if (reason.empty()) 247 throw parse_error(t.lineno(), 248 "Empty reason for failed test program"); 249 t = p.next(); 250 } 251 252 ATF_PARSER_CALLBACK(p, got_tp_end(reason)); 253} 254 255void 256impl::atf_tps_reader::read_tc(void* pptr) 257{ 258 using atf::parser::parse_error; 259 using namespace atf_tps; 260 261 atf::parser::parser< tokenizer >& p = 262 *reinterpret_cast< atf::parser::parser< tokenizer >* > 263 (pptr); 264 265 atf::parser::token t = p.expect(tc_start_type, "start of test case"); 266 267 t = p.expect(colon_type, "`:'"); 268 269 t = p.expect(text_type, "test case name"); 270 std::string tcname = t.text(); 271 ATF_PARSER_CALLBACK(p, got_tc_start(tcname)); 272 273 t = p.expect(nl_type, "new line"); 274 275 t = p.expect(tc_end_type, tc_so_type, tc_se_type, 276 "end of test case or test case's stdout/stderr line"); 277 while (t.type() != tc_end_type && 278 (t.type() == tc_so_type || t.type() == tc_se_type)) { 279 atf::parser::token t2 = t; 280 281 t = p.expect(colon_type, "`:'"); 282 283 std::string line = p.rest_of_line(); 284 285 if (t2.type() == tc_so_type) { 286 ATF_PARSER_CALLBACK(p, got_tc_stdout_line(line)); 287 } else { 288 INV(t2.type() == tc_se_type); 289 ATF_PARSER_CALLBACK(p, got_tc_stderr_line(line)); 290 } 291 292 t = p.expect(nl_type, "new line"); 293 294 t = p.expect(tc_end_type, tc_so_type, tc_se_type, 295 "end of test case or test case's stdout/stderr line"); 296 } 297 298 t = p.expect(colon_type, "`:'"); 299 300 t = p.expect(text_type, "test case name"); 301 if (t.text() != tcname) 302 throw parse_error(t.lineno(), 303 "Test case name used in terminator does not " 304 "match opening"); 305 306 t = p.expect(comma_type, "`,'"); 307 308 t = p.expect(expected_death_type, expected_exit_type, expected_failure_type, 309 expected_signal_type, expected_timeout_type, passed_type, failed_type, 310 skipped_type, "expected_{death,exit,failure,signal,timeout}, failed, " 311 "passed or skipped"); 312 if (t.type() == passed_type) { 313 ATF_PARSER_CALLBACK(p, got_tc_end("passed", "")); 314 } else { 315 std::string state; 316 if (t.type() == expected_death_type) state = "expected_death"; 317 else if (t.type() == expected_exit_type) state = "expected_exit"; 318 else if (t.type() == expected_failure_type) state = "expected_failure"; 319 else if (t.type() == expected_signal_type) state = "expected_signal"; 320 else if (t.type() == expected_timeout_type) state = "expected_timeout"; 321 else if (t.type() == failed_type) state = "failed"; 322 else if (t.type() == skipped_type) state = "skipped"; 323 else UNREACHABLE; 324 325 t = p.expect(comma_type, "`,'"); 326 std::string reason = text::trim(p.rest_of_line()); 327 if (reason.empty()) 328 throw parse_error(t.lineno(), "Empty reason for " + state + 329 " test case result"); 330 ATF_PARSER_CALLBACK(p, got_tc_end(state, reason)); 331 } 332 333 t = p.expect(nl_type, "new line"); 334} 335 336void 337impl::atf_tps_reader::read(void) 338{ 339 using atf::parser::parse_error; 340 using namespace atf_tps; 341 342 std::pair< size_t, atf::parser::headers_map > hml = 343 atf::parser::read_headers(m_is, 1); 344 atf::parser::validate_content_type(hml.second, "application/X-atf-tps", 2); 345 346 tokenizer tkz(m_is, hml.first); 347 atf::parser::parser< tokenizer > p(tkz); 348 349 try { 350 atf::parser::token t; 351 352 while ((t = p.expect(tps_count_type, info_type, "tps-count or info " 353 "field")).type() == info_type) 354 read_info(&p); 355 356 t = p.expect(colon_type, "`:'"); 357 358 t = p.expect(text_type, "number of test programs"); 359 size_t ntps = string_to_size_t(t.text()); 360 ATF_PARSER_CALLBACK(p, got_ntps(ntps)); 361 362 t = p.expect(nl_type, "new line"); 363 364 size_t i = 0; 365 while (p.good() && i < ntps) { 366 try { 367 read_tp(&p); 368 i++; 369 } catch (const parse_error& pe) { 370 p.add_error(pe); 371 p.reset(nl_type); 372 } 373 } 374 375 while ((t = p.expect(eof_type, info_type, "end of stream or info " 376 "field")).type() == info_type) 377 read_info(&p); 378 ATF_PARSER_CALLBACK(p, got_eof()); 379 } catch (const parse_error& pe) { 380 p.add_error(pe); 381 p.reset(nl_type); 382 } 383} 384