1 // 2 // Automated Testing Framework (atf) 3 // 4 // Copyright (c) 2009 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/types.h> 32 #include <sys/stat.h> 33 } 34 35 #include <algorithm> 36 #include <fstream> 37 #include <memory> 38 39 #include "atf-c++/macros.hpp" 40 41 #include "atf-c++/detail/exceptions.hpp" 42 #include "atf-c++/detail/test_helpers.hpp" 43 44 #include "atffile.hpp" 45 46 namespace detail = atf::atf_run::detail; 47 48 // ------------------------------------------------------------------------ 49 // Auxiliary functions. 50 // ------------------------------------------------------------------------ 51 52 namespace { 53 54 static 55 std::auto_ptr< std::ofstream > 56 new_atffile(void) 57 { 58 std::auto_ptr< std::ofstream > os(new std::ofstream("Atffile")); 59 ATF_REQUIRE(*os); 60 61 (*os) << "Content-Type: application/X-atf-atffile; version=\"1\"\n\n"; 62 return os; 63 } 64 65 static 66 void 67 touch_exec(const char* name) 68 { 69 std::ofstream os(name); 70 ATF_REQUIRE(os); 71 os.close(); 72 ATF_REQUIRE(::chmod(name, S_IRWXU) != -1); 73 } 74 75 static inline 76 bool 77 is_in(const std::string& value, const std::vector< std::string >& v) 78 { 79 return std::find(v.begin(), v.end(), value) != v.end(); 80 } 81 82 } // anonymous namespace 83 84 // ------------------------------------------------------------------------ 85 // Tests cases for the "atffile" parser. 86 // ------------------------------------------------------------------------ 87 88 class atffile_reader : protected detail::atf_atffile_reader { 89 void 90 got_conf(const std::string& name, const std::string& val) 91 { 92 m_calls.push_back("got_conf(" + name + ", " + val + ")"); 93 } 94 95 void 96 got_prop(const std::string& name, const std::string& val) 97 { 98 m_calls.push_back("got_prop(" + name + ", " + val + ")"); 99 } 100 101 void 102 got_tp(const std::string& name, bool isglob) 103 { 104 m_calls.push_back("got_tp(" + name + ", " + (isglob ? "true" : "false") 105 + ")"); 106 } 107 108 void 109 got_eof(void) 110 { 111 m_calls.push_back("got_eof()"); 112 } 113 114 public: 115 atffile_reader(std::istream& is) : 116 detail::atf_atffile_reader(is) 117 { 118 } 119 120 void 121 read(void) 122 { 123 atf_atffile_reader::read(); 124 } 125 126 std::vector< std::string > m_calls; 127 }; 128 129 ATF_TEST_CASE_WITHOUT_HEAD(atffile_1); 130 ATF_TEST_CASE_BODY(atffile_1) 131 { 132 const char* input = 133 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 134 "\n" 135 ; 136 137 const char* exp_calls[] = { 138 "got_eof()", 139 NULL 140 }; 141 142 const char* exp_errors[] = { 143 NULL 144 }; 145 146 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 147 } 148 149 ATF_TEST_CASE_WITHOUT_HEAD(atffile_2); 150 ATF_TEST_CASE_BODY(atffile_2) 151 { 152 const char* input = 153 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 154 "\n" 155 "# This is a comment on a line of its own.\n" 156 "# And this is another one.\n" 157 "\n" 158 " # Another after some whitespace.\n" 159 "\n" 160 "# The last one after an empty line.\n" 161 ; 162 163 const char* exp_calls[] = { 164 "got_eof()", 165 NULL 166 }; 167 168 const char* exp_errors[] = { 169 NULL 170 }; 171 172 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 173 } 174 175 ATF_TEST_CASE_WITHOUT_HEAD(atffile_3); 176 ATF_TEST_CASE_BODY(atffile_3) 177 { 178 const char* input = 179 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 180 "\n" 181 "conf: var1=value1\n" 182 "conf: var2 = value2\n" 183 "conf: var3 = value3\n" 184 "conf: var4 = value4\n" 185 "\n" 186 "conf:var5=value5\n" 187 " conf:var6=value6\n" 188 "\n" 189 "conf: var7 = \"This is a long value.\"\n" 190 "conf: var8 = \"Single-word\"\n" 191 "conf: var9 = \" Single-word \"\n" 192 "conf: var10 = Single-word\n" 193 ; 194 195 const char* exp_calls[] = { 196 "got_conf(var1, value1)", 197 "got_conf(var2, value2)", 198 "got_conf(var3, value3)", 199 "got_conf(var4, value4)", 200 "got_conf(var5, value5)", 201 "got_conf(var6, value6)", 202 "got_conf(var7, This is a long value.)", 203 "got_conf(var8, Single-word)", 204 "got_conf(var9, Single-word )", 205 "got_conf(var10, Single-word)", 206 "got_eof()", 207 NULL 208 }; 209 210 const char* exp_errors[] = { 211 NULL 212 }; 213 214 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 215 } 216 217 ATF_TEST_CASE_WITHOUT_HEAD(atffile_4); 218 ATF_TEST_CASE_BODY(atffile_4) 219 { 220 const char* input = 221 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 222 "\n" 223 "prop: var1=value1\n" 224 "prop: var2 = value2\n" 225 "prop: var3 = value3\n" 226 "prop: var4 = value4\n" 227 "\n" 228 "prop:var5=value5\n" 229 " prop:var6=value6\n" 230 "\n" 231 "prop: var7 = \"This is a long value.\"\n" 232 "prop: var8 = \"Single-word\"\n" 233 "prop: var9 = \" Single-word \"\n" 234 "prop: var10 = Single-word\n" 235 ; 236 237 const char* exp_calls[] = { 238 "got_prop(var1, value1)", 239 "got_prop(var2, value2)", 240 "got_prop(var3, value3)", 241 "got_prop(var4, value4)", 242 "got_prop(var5, value5)", 243 "got_prop(var6, value6)", 244 "got_prop(var7, This is a long value.)", 245 "got_prop(var8, Single-word)", 246 "got_prop(var9, Single-word )", 247 "got_prop(var10, Single-word)", 248 "got_eof()", 249 NULL 250 }; 251 252 const char* exp_errors[] = { 253 NULL 254 }; 255 256 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 257 } 258 259 ATF_TEST_CASE_WITHOUT_HEAD(atffile_5); 260 ATF_TEST_CASE_BODY(atffile_5) 261 { 262 const char* input = 263 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 264 "\n" 265 "tp:foo\n" 266 "tp: foo\n" 267 "tp: foo\n" 268 "tp: foo\n" 269 "tp: foo\n" 270 "tp: \"name with spaces\"\n" 271 "tp: \"single-word\"\n" 272 "tp: single-word\n" 273 "\n" 274 "tp-glob:foo*?bar\n" 275 "tp-glob: foo*?bar\n" 276 "tp-glob: foo*?bar\n" 277 "tp-glob: foo*?bar\n" 278 "tp-glob: foo*?bar\n" 279 "tp-glob: \"glob * with ? spaces\"\n" 280 "tp-glob: \"single-*-word\"\n" 281 "tp-glob: single-*-word\n" 282 ; 283 284 const char* exp_calls[] = { 285 "got_tp(foo, false)", 286 "got_tp(foo, false)", 287 "got_tp(foo, false)", 288 "got_tp(foo, false)", 289 "got_tp(foo, false)", 290 "got_tp(name with spaces, false)", 291 "got_tp(single-word, false)", 292 "got_tp(single-word, false)", 293 "got_tp(foo*?bar, true)", 294 "got_tp(foo*?bar, true)", 295 "got_tp(foo*?bar, true)", 296 "got_tp(foo*?bar, true)", 297 "got_tp(foo*?bar, true)", 298 "got_tp(glob * with ? spaces, true)", 299 "got_tp(single-*-word, true)", 300 "got_tp(single-*-word, true)", 301 "got_eof()", 302 NULL 303 }; 304 305 const char* exp_errors[] = { 306 NULL 307 }; 308 309 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 310 } 311 312 ATF_TEST_CASE_WITHOUT_HEAD(atffile_6); 313 ATF_TEST_CASE_BODY(atffile_6) 314 { 315 const char* input = 316 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 317 "\n" 318 "prop: foo = bar # A comment.\n" 319 "conf: foo = bar # A comment.\n" 320 "tp: foo # A comment.\n" 321 "tp-glob: foo # A comment.\n" 322 ; 323 324 const char* exp_calls[] = { 325 "got_prop(foo, bar)", 326 "got_conf(foo, bar)", 327 "got_tp(foo, false)", 328 "got_tp(foo, true)", 329 "got_eof()", 330 NULL 331 }; 332 333 const char* exp_errors[] = { 334 NULL 335 }; 336 337 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 338 } 339 340 ATF_TEST_CASE_WITHOUT_HEAD(atffile_50); 341 ATF_TEST_CASE_BODY(atffile_50) 342 { 343 const char* input = 344 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 345 "\n" 346 "foo\n" 347 ; 348 349 const char* exp_calls[] = { 350 NULL 351 }; 352 353 // NO_CHECK_STYLE_BEGIN 354 const char* exp_errors[] = { 355 "3: Unexpected token `foo'; expected conf, #, prop, tp, tp-glob, a new line or eof", 356 NULL 357 }; 358 // NO_CHECK_STYLE_END 359 360 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 361 } 362 363 ATF_TEST_CASE_WITHOUT_HEAD(atffile_51); 364 ATF_TEST_CASE_BODY(atffile_51) 365 { 366 const char* input = 367 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 368 "\n" 369 "foo bar\n" 370 "baz\n" 371 ; 372 373 const char* exp_calls[] = { 374 NULL 375 }; 376 377 // NO_CHECK_STYLE_BEGIN 378 const char* exp_errors[] = { 379 "3: Unexpected token `foo'; expected conf, #, prop, tp, tp-glob, a new line or eof", 380 "4: Unexpected token `baz'; expected conf, #, prop, tp, tp-glob, a new line or eof", 381 NULL 382 }; 383 // NO_CHECK_STYLE_END 384 385 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 386 } 387 388 ATF_TEST_CASE_WITHOUT_HEAD(atffile_52); 389 ATF_TEST_CASE_BODY(atffile_52) 390 { 391 const char* input = 392 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 393 "\n" 394 "conf\n" 395 "conf:\n" 396 "conf: foo =\n" 397 "conf: bar = # A comment.\n" 398 "\n" 399 "prop\n" 400 "prop:\n" 401 "prop: foo =\n" 402 "prop: bar = # A comment.\n" 403 "\n" 404 "tp\n" 405 "tp:\n" 406 "tp: # A comment.\n" 407 "\n" 408 "tp-glob\n" 409 "tp-glob:\n" 410 "tp-glob: # A comment.\n" 411 ; 412 413 const char* exp_calls[] = { 414 NULL 415 }; 416 417 const char* exp_errors[] = { 418 "3: Unexpected token `<<NEWLINE>>'; expected `:'", 419 "4: Unexpected token `<<NEWLINE>>'; expected variable name", 420 "5: Unexpected token `<<NEWLINE>>'; expected word or quoted string", 421 "6: Unexpected token `#'; expected word or quoted string", 422 "8: Unexpected token `<<NEWLINE>>'; expected `:'", 423 "9: Unexpected token `<<NEWLINE>>'; expected property name", 424 "10: Unexpected token `<<NEWLINE>>'; expected word or quoted string", 425 "11: Unexpected token `#'; expected word or quoted string", 426 "13: Unexpected token `<<NEWLINE>>'; expected `:'", 427 "14: Unexpected token `<<NEWLINE>>'; expected word or quoted string", 428 "15: Unexpected token `#'; expected word or quoted string", 429 "17: Unexpected token `<<NEWLINE>>'; expected `:'", 430 "18: Unexpected token `<<NEWLINE>>'; expected word or quoted string", 431 "19: Unexpected token `#'; expected word or quoted string", 432 NULL 433 }; 434 435 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 436 } 437 438 ATF_TEST_CASE_WITHOUT_HEAD(atffile_53); 439 ATF_TEST_CASE_BODY(atffile_53) 440 { 441 const char* input = 442 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 443 "\n" 444 "prop: foo = \"Correct value\" # With comment.\n" 445 "\n" 446 "prop: bar = # A comment.\n" 447 "\n" 448 "prop: baz = \"Last variable\"\n" 449 "\n" 450 "# End of file.\n" 451 ; 452 453 const char* exp_calls[] = { 454 "got_prop(foo, Correct value)", 455 NULL 456 }; 457 458 const char* exp_errors[] = { 459 "5: Unexpected token `#'; expected word or quoted string", 460 NULL 461 }; 462 463 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 464 } 465 466 ATF_TEST_CASE_WITHOUT_HEAD(atffile_54); 467 ATF_TEST_CASE_BODY(atffile_54) 468 { 469 const char* input = 470 "Content-Type: application/X-atf-atffile; version=\"1\"\n" 471 "\n" 472 "prop: foo = \"\n" 473 "prop: bar = \"text\n" 474 "prop: baz = \"te\\\"xt\n" 475 "prop: last = \"\\\"\n" 476 ; 477 478 const char* exp_calls[] = { 479 NULL 480 }; 481 482 const char* exp_errors[] = { 483 "3: Missing double quotes before end of line", 484 "4: Missing double quotes before end of line", 485 "5: Missing double quotes before end of line", 486 "6: Missing double quotes before end of line", 487 NULL 488 }; 489 490 do_parser_test< atffile_reader >(input, exp_calls, exp_errors); 491 } 492 493 // ------------------------------------------------------------------------ 494 // Tests cases for the "atffile" class. 495 // ------------------------------------------------------------------------ 496 497 ATF_TEST_CASE(atffile_getters); 498 ATF_TEST_CASE_HEAD(atffile_getters) {} 499 ATF_TEST_CASE_BODY(atffile_getters) { 500 atf::tests::vars_map config_vars; 501 config_vars["config-var-1"] = "value 1"; 502 503 std::vector< std::string > test_program_names; 504 test_program_names.push_back("test-program-1"); 505 506 atf::tests::vars_map properties; 507 properties["test-suite"] = "a test name"; 508 509 const atf::atf_run::atffile atffile(config_vars, test_program_names, 510 properties); 511 ATF_REQUIRE(config_vars == atffile.conf()); 512 ATF_REQUIRE(test_program_names == atffile.tps()); 513 ATF_REQUIRE(properties == atffile.props()); 514 } 515 516 // ------------------------------------------------------------------------ 517 // Tests cases for the free functions. 518 // ------------------------------------------------------------------------ 519 520 ATF_TEST_CASE_WITHOUT_HEAD(read_ok_simple); 521 ATF_TEST_CASE_BODY(read_ok_simple) { 522 std::auto_ptr< std::ofstream > os = new_atffile(); 523 (*os) << "prop: test-suite = foo\n"; 524 (*os) << "tp: tp-1\n"; 525 (*os) << "conf: var1 = value1\n"; 526 (*os) << "tp: tp-2\n"; 527 (*os) << "tp: tp-3\n"; 528 (*os) << "prop: prop1 = propvalue1\n"; 529 (*os) << "conf: var2 = value2\n"; 530 (*os).close(); 531 532 touch_exec("tp-1"); 533 touch_exec("tp-2"); 534 touch_exec("tp-3"); 535 536 const atf::atf_run::atffile atffile = atf::atf_run::read_atffile( 537 atf::fs::path("Atffile")); 538 ATF_REQUIRE_EQ(2, atffile.conf().size()); 539 ATF_REQUIRE_EQ("value1", atffile.conf().find("var1")->second); 540 ATF_REQUIRE_EQ("value2", atffile.conf().find("var2")->second); 541 ATF_REQUIRE_EQ(3, atffile.tps().size()); 542 ATF_REQUIRE(is_in("tp-1", atffile.tps())); 543 ATF_REQUIRE(is_in("tp-2", atffile.tps())); 544 ATF_REQUIRE(is_in("tp-3", atffile.tps())); 545 ATF_REQUIRE_EQ(2, atffile.props().size()); 546 ATF_REQUIRE_EQ("foo", atffile.props().find("test-suite")->second); 547 ATF_REQUIRE_EQ("propvalue1", atffile.props().find("prop1")->second); 548 } 549 550 ATF_TEST_CASE_WITHOUT_HEAD(read_ok_some_globs); 551 ATF_TEST_CASE_BODY(read_ok_some_globs) { 552 std::auto_ptr< std::ofstream > os = new_atffile(); 553 (*os) << "prop: test-suite = foo\n"; 554 (*os) << "tp: foo\n"; 555 (*os) << "tp-glob: *K*\n"; 556 (*os) << "tp: bar\n"; 557 (*os) << "tp-glob: t_*\n"; 558 (*os).close(); 559 560 touch_exec("foo"); 561 touch_exec("bar"); 562 touch_exec("aK"); 563 touch_exec("KKKKK"); 564 touch_exec("t_hello"); 565 touch_exec("zzzt_hello"); 566 567 const atf::atf_run::atffile atffile = atf::atf_run::read_atffile( 568 atf::fs::path("Atffile")); 569 ATF_REQUIRE_EQ(5, atffile.tps().size()); 570 ATF_REQUIRE(is_in("foo", atffile.tps())); 571 ATF_REQUIRE(is_in("bar", atffile.tps())); 572 ATF_REQUIRE(is_in("aK", atffile.tps())); 573 ATF_REQUIRE(is_in("KKKKK", atffile.tps())); 574 ATF_REQUIRE(is_in("t_hello", atffile.tps())); 575 } 576 577 ATF_TEST_CASE_WITHOUT_HEAD(read_missing_test_suite); 578 ATF_TEST_CASE_BODY(read_missing_test_suite) { 579 std::auto_ptr< std::ofstream > os = new_atffile(); 580 (*os).close(); 581 582 try { 583 (void)atf::atf_run::read_atffile(atf::fs::path("Atffile")); 584 ATF_FAIL("Missing property 'test-suite' did not raise an error"); 585 } catch (const atf::not_found_error< std::string >& e) { 586 ATF_REQUIRE_EQ("test-suite", e.get_value()); 587 } 588 } 589 590 ATF_TEST_CASE_WITHOUT_HEAD(read_missing_test_program); 591 ATF_TEST_CASE_BODY(read_missing_test_program) { 592 std::auto_ptr< std::ofstream > os = new_atffile(); 593 (*os) << "tp: foo\n"; 594 (*os) << "tp: bar\n"; 595 (*os) << "tp: baz\n"; 596 (*os).close(); 597 598 touch_exec("foo"); 599 touch_exec("baz"); 600 601 try { 602 (void)atf::atf_run::read_atffile(atf::fs::path("Atffile")); 603 ATF_FAIL("Missing file 'bar' did not raise an error"); 604 } catch (const atf::not_found_error< atf::fs::path >& e) { 605 ATF_REQUIRE_EQ("bar", e.get_value().str()); 606 } 607 } 608 609 // ------------------------------------------------------------------------ 610 // Main. 611 // ------------------------------------------------------------------------ 612 613 ATF_INIT_TEST_CASES(tcs) 614 { 615 // Add the test cases for the parser class. 616 ATF_ADD_TEST_CASE(tcs, atffile_1); 617 ATF_ADD_TEST_CASE(tcs, atffile_2); 618 ATF_ADD_TEST_CASE(tcs, atffile_3); 619 ATF_ADD_TEST_CASE(tcs, atffile_4); 620 ATF_ADD_TEST_CASE(tcs, atffile_5); 621 ATF_ADD_TEST_CASE(tcs, atffile_6); 622 ATF_ADD_TEST_CASE(tcs, atffile_50); 623 ATF_ADD_TEST_CASE(tcs, atffile_51); 624 ATF_ADD_TEST_CASE(tcs, atffile_52); 625 ATF_ADD_TEST_CASE(tcs, atffile_53); 626 ATF_ADD_TEST_CASE(tcs, atffile_54); 627 628 // Add the test cases for the atffile class. 629 ATF_ADD_TEST_CASE(tcs, atffile_getters); 630 631 // Add the test cases for the free functions. 632 ATF_ADD_TEST_CASE(tcs, read_ok_simple); 633 ATF_ADD_TEST_CASE(tcs, read_ok_some_globs); 634 ATF_ADD_TEST_CASE(tcs, read_missing_test_suite); 635 ATF_ADD_TEST_CASE(tcs, read_missing_test_program); 636 } 637