1 // 2 // Automated Testing Framework (atf) 3 // 4 // Copyright (c) 2007 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 extern "C" { 31 #include <sys/time.h> 32 } 33 34 #include <cassert> 35 #include <cstdlib> 36 #include <map> 37 #include <sstream> 38 #include <utility> 39 40 #include "parser.hpp" 41 #include "reader.hpp" 42 #include "text.hpp" 43 44 namespace impl = tools::atf_report; 45 #define IMPL_NAME "tools::atf_report" 46 47 // ------------------------------------------------------------------------ 48 // Auxiliary functions. 49 // ------------------------------------------------------------------------ 50 51 template< typename Type > 52 Type 53 string_to_int(const std::string& str) 54 { 55 std::istringstream ss(str); 56 Type s; 57 ss >> s; 58 59 return s; 60 } 61 62 // ------------------------------------------------------------------------ 63 // The "atf_tps" auxiliary parser. 64 // ------------------------------------------------------------------------ 65 66 namespace atf_tps { 67 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; 89 90 class tokenizer : public tools::parser::tokenizer< std::istream > { 91 public: 92 tokenizer(std::istream& is, size_t curline) : 93 tools::parser::tokenizer< std::istream > 94 (is, true, eof_type, nl_type, text_type, curline) 95 { 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); 114 } 115 }; 116 117 } // namespace atf_tps 118 119 struct timeval 120 read_timeval(tools::parser::parser< atf_tps::tokenizer >& parser) 121 { 122 using namespace atf_tps; 123 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()); 130 131 struct timeval tv; 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)); 134 return tv; 135 } 136 137 // ------------------------------------------------------------------------ 138 // The "atf_tps_reader" class. 139 // ------------------------------------------------------------------------ 140 141 impl::atf_tps_reader::atf_tps_reader(std::istream& is) : 142 m_is(is) 143 { 144 } 145 146 impl::atf_tps_reader::~atf_tps_reader(void) 147 { 148 } 149 150 void 151 impl::atf_tps_reader::got_info( 152 const std::string& what __attribute__((__unused__)), 153 const std::string& val __attribute__((__unused__))) 154 { 155 } 156 157 void 158 impl::atf_tps_reader::got_ntps(size_t ntps __attribute__((__unused__))) 159 { 160 } 161 162 void 163 impl::atf_tps_reader::got_tp_start( 164 const std::string& tp __attribute__((__unused__)), 165 size_t ntcs __attribute__((__unused__))) 166 { 167 } 168 169 void 170 impl::atf_tps_reader::got_tp_end( 171 struct timeval* tv __attribute__((__unused__)), 172 const std::string& reason __attribute__((__unused__))) 173 { 174 } 175 176 void 177 impl::atf_tps_reader::got_tc_start( 178 const std::string& tcname __attribute__((__unused__))) 179 { 180 } 181 182 void 183 impl::atf_tps_reader::got_tc_stdout_line( 184 const std::string& line __attribute__((__unused__))) 185 { 186 } 187 188 void 189 impl::atf_tps_reader::got_tc_stderr_line( 190 const std::string& line __attribute__((__unused__))) 191 { 192 } 193 194 void 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__))) 199 { 200 } 201 202 void 203 impl::atf_tps_reader::got_eof(void) 204 { 205 } 206 207 void 208 impl::atf_tps_reader::read_info(void* pptr) 209 { 210 using tools::parser::parse_error; 211 using namespace atf_tps; 212 213 tools::parser::parser< tokenizer >& p = 214 *reinterpret_cast< tools::parser::parser< tokenizer >* > 215 (pptr); 216 217 (void)p.expect(colon_type, "`:'"); 218 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())); 222 223 (void)p.expect(nl_type, "new line"); 224 } 225 226 void 227 impl::atf_tps_reader::read_tp(void* pptr) 228 { 229 using tools::parser::parse_error; 230 using namespace atf_tps; 231 232 tools::parser::parser< tokenizer >& p = 233 *reinterpret_cast< tools::parser::parser< tokenizer >* > 234 (pptr); 235 236 tools::parser::token t = p.expect(tp_start_type, 237 "start of test program"); 238 239 t = p.expect(colon_type, "`:'"); 240 241 struct timeval s1 = read_timeval(p); 242 243 t = p.expect(comma_type, "`,'"); 244 245 t = p.expect(text_type, "test program name"); 246 std::string tpname = t.text(); 247 248 t = p.expect(comma_type, "`,'"); 249 250 t = p.expect(text_type, "number of test programs"); 251 size_t ntcs = string_to_int< std::size_t >(t.text()); 252 253 t = p.expect(nl_type, "new line"); 254 255 ATF_PARSER_CALLBACK(p, got_tp_start(tpname, ntcs)); 256 257 size_t i = 0; 258 while (p.good() && i < ntcs) { 259 try { 260 read_tc(&p); 261 i++; 262 } catch (const parse_error& pe) { 263 p.add_error(pe); 264 p.reset(nl_type); 265 } 266 } 267 t = p.expect(tp_end_type, "end of test program"); 268 269 t = p.expect(colon_type, "`:'"); 270 271 struct timeval s2 = read_timeval(p); 272 273 struct timeval s3; 274 timersub(&s2, &s1, &s3); 275 276 t = p.expect(comma_type, "`,'"); 277 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 " 282 "opening"); 283 284 t = p.expect(nl_type, comma_type, 285 "new line or comma_type"); 286 std::string reason; 287 if (t.type() == comma_type) { 288 reason = tools::text::trim(p.rest_of_line()); 289 if (reason.empty()) 290 throw parse_error(t.lineno(), 291 "Empty reason for failed test program"); 292 t = p.next(); 293 } 294 295 ATF_PARSER_CALLBACK(p, got_tp_end(&s3, reason)); 296 } 297 298 void 299 impl::atf_tps_reader::read_tc(void* pptr) 300 { 301 using tools::parser::parse_error; 302 using namespace atf_tps; 303 304 tools::parser::parser< tokenizer >& p = 305 *reinterpret_cast< tools::parser::parser< tokenizer >* > 306 (pptr); 307 308 tools::parser::token t = p.expect(tc_start_type, "start of test case"); 309 310 t = p.expect(colon_type, "`:'"); 311 312 struct timeval s1 = read_timeval(p); 313 314 t = p.expect(comma_type, "`,'"); 315 316 t = p.expect(text_type, "test case name"); 317 std::string tcname = t.text(); 318 319 ATF_PARSER_CALLBACK(p, got_tc_start(tcname)); 320 321 t = p.expect(nl_type, "new line"); 322 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; 328 329 t = p.expect(colon_type, "`:'"); 330 331 std::string line = p.rest_of_line(); 332 333 if (t2.type() == tc_so_type) { 334 ATF_PARSER_CALLBACK(p, got_tc_stdout_line(line)); 335 } else { 336 assert(t2.type() == tc_se_type); 337 ATF_PARSER_CALLBACK(p, got_tc_stderr_line(line)); 338 } 339 340 t = p.expect(nl_type, "new line"); 341 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"); 344 } 345 346 t = p.expect(colon_type, "`:'"); 347 348 struct timeval s2 = read_timeval(p); 349 350 struct timeval s3; 351 timersub(&s2, &s1, &s3); 352 353 t = p.expect(comma_type, "`,'"); 354 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 " 359 "match opening"); 360 361 t = p.expect(comma_type, "`,'"); 362 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, "")); 369 } else { 370 std::string state; 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"; 378 else std::abort(); 379 380 t = p.expect(comma_type, "`,'"); 381 std::string reason = tools::text::trim(p.rest_of_line()); 382 if (reason.empty()) 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)); 386 } 387 388 t = p.expect(nl_type, "new line"); 389 } 390 391 void 392 impl::atf_tps_reader::read(void) 393 { 394 using tools::parser::parse_error; 395 using namespace atf_tps; 396 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); 401 402 tokenizer tkz(m_is, hml.first); 403 tools::parser::parser< tokenizer > p(tkz); 404 405 try { 406 tools::parser::token t; 407 408 while ((t = p.expect(tps_count_type, info_type, "tps-count or info " 409 "field")).type() == info_type) 410 read_info(&p); 411 412 t = p.expect(colon_type, "`:'"); 413 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)); 417 418 t = p.expect(nl_type, "new line"); 419 420 size_t i = 0; 421 while (p.good() && i < ntps) { 422 try { 423 read_tp(&p); 424 i++; 425 } catch (const parse_error& pe) { 426 p.add_error(pe); 427 p.reset(nl_type); 428 } 429 } 430 431 while ((t = p.expect(eof_type, info_type, "end of stream or info " 432 "field")).type() == info_type) 433 read_info(&p); 434 ATF_PARSER_CALLBACK(p, got_eof()); 435 } catch (const parse_error& pe) { 436 p.add_error(pe); 437 p.reset(nl_type); 438 } 439 } 440