1 // Copyright 2010 Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include "engine/test_case.hpp" 30 31 extern "C" { 32 #include <sys/stat.h> 33 34 #include <signal.h> 35 #include <unistd.h> 36 } 37 38 #include <cerrno> 39 #include <cstdlib> 40 #include <fstream> 41 #include <iostream> 42 #include <sstream> 43 #include <stdexcept> 44 #include <string> 45 46 #include <atf-c++.hpp> 47 48 #include "engine/config.hpp" 49 #include "engine/exceptions.hpp" 50 #include "engine/kyuafile.hpp" 51 #include "engine/test_program.hpp" 52 #include "engine/test_result.hpp" 53 #include "utils/config/tree.ipp" 54 #include "utils/datetime.hpp" 55 #include "utils/env.hpp" 56 #include "utils/format/macros.hpp" 57 #include "utils/fs/operations.hpp" 58 #include "utils/noncopyable.hpp" 59 #include "utils/optional.ipp" 60 #include "utils/passwd.hpp" 61 #include "utils/process/child.ipp" 62 #include "utils/sanity.hpp" 63 #include "utils/stream.hpp" 64 65 namespace config = utils::config; 66 namespace datetime = utils::datetime; 67 namespace fs = utils::fs; 68 namespace passwd = utils::passwd; 69 namespace process = utils::process; 70 71 using utils::none; 72 using utils::optional; 73 74 75 namespace { 76 77 78 /// Test case hooks to capture stdout and stderr in memory. 79 class capture_hooks : public engine::test_case_hooks { 80 public: 81 /// Contents of the stdout of the test case. 82 std::string stdout_contents; 83 84 /// Contents of the stderr of the test case. 85 std::string stderr_contents; 86 87 /// Stores the stdout of the test case into stdout_contents. 88 /// 89 /// \param file The path to the file containing the stdout. 90 void 91 got_stdout(const fs::path& file) 92 { 93 atf::utils::cat_file(file.str(), "helper stdout:"); 94 ATF_REQUIRE(stdout_contents.empty()); 95 96 std::ifstream input(file.c_str()); 97 ATF_REQUIRE(input); 98 stdout_contents = utils::read_stream(input); 99 } 100 101 /// Stores the stderr of the test case into stderr_contents. 102 /// 103 /// \param file The path to the file containing the stderr. 104 void 105 got_stderr(const fs::path& file) 106 { 107 atf::utils::cat_file(file.str(), "helper stderr:"); 108 ATF_REQUIRE(stderr_contents.empty()); 109 110 std::ifstream input(file.c_str()); 111 ATF_REQUIRE(input); 112 stderr_contents = utils::read_stream(input); 113 } 114 }; 115 116 117 /// Launcher for the helper test cases. 118 /// 119 /// This builder class can be used to construct the runtime state of the helper 120 /// test cases and later run them. The class also provides other helper methods 121 /// to interact with the helper binary. 122 class atf_helper : utils::noncopyable { 123 /// Path to the test program's source directory. 124 const fs::path _srcdir; 125 126 /// The root of the test suite. 127 fs::path _root; 128 129 /// Path to the helper test program, relative to _root. 130 fs::path _binary_path; 131 132 /// Name of the helper test case to run. 133 const std::string _name; 134 135 /// Metadata of the test case. 136 engine::metadata_builder _mdbuilder; 137 138 /// Run-time configuration for the test case. 139 config::tree _user_config; 140 141 public: 142 /// Constructs a new helper. 143 /// 144 /// \param atf_tc A pointer to the calling test case. Needed to obtain 145 /// run-time configuration variables. 146 /// \param name The name of the helper to run. 147 atf_helper(const atf::tests::tc* atf_tc, const char* name) : 148 _srcdir(atf_tc->get_config_var("srcdir")), 149 _root(_srcdir), 150 _binary_path("test_case_atf_helpers"), 151 _name(name), 152 _user_config(engine::default_config()) 153 { 154 _user_config.set_string("architecture", "mock-architecture"); 155 _user_config.set_string("platform", "mock-platform"); 156 } 157 158 /// Provides raw access to the run-time configuration. 159 /// 160 /// To override test-suite-specific variables, use set_config() as it 161 /// abstracts away the name of the fake test suite. 162 /// 163 /// \returns A reference to the test case configuration. 164 config::tree& 165 config(void) 166 { 167 return _user_config; 168 } 169 170 /// Sets a test-suite-specific configuration variable for the helper. 171 /// 172 /// \param variable The name of the environment variable to set. 173 /// \param value The value of the variable; must be convertible to a string. 174 template< typename T > 175 void 176 set_config(const char* variable, const T& value) 177 { 178 _user_config.set_string(F("test_suites.the-suite.%s") % variable, 179 F("%s") % value); 180 } 181 182 /// Sets a metadata variable for the helper. 183 /// 184 /// \param variable The name of the environment variable to set. 185 /// \param value The value of the variable; must be convertible to a string. 186 template< typename T > 187 void 188 set_metadata(const char* variable, const T& value) 189 { 190 _mdbuilder.set_string(variable, F("%s") % value); 191 } 192 193 /// Places the helper in a different location. 194 /// 195 /// This prepares the helper to be run from a different location than the 196 /// source directory so that the runtime execution can be validated. 197 /// 198 /// \param new_binary_path The new path to the binary, relative to the test 199 /// suite root. 200 /// \param new_root The new test suite root. 201 /// 202 /// \pre The directory holding the target test program must exist. 203 /// Otherwise, the relocation of the binary will fail. 204 void 205 move(const char* new_binary_path, const char* new_root) 206 { 207 _binary_path = fs::path(new_binary_path); 208 _root = fs::path(new_root); 209 210 const fs::path src_path = fs::path(_srcdir / "test_case_atf_helpers"); 211 const fs::path new_path = _root / _binary_path; 212 ATF_REQUIRE( 213 ::symlink(src_path.c_str(), new_path.c_str()) != -1); 214 } 215 216 /// Runs the helper. 217 /// 218 /// \return The result of the execution. 219 engine::test_result 220 run(void) const 221 { 222 engine::test_case_hooks dummy_hooks; 223 return run(dummy_hooks); 224 } 225 226 /// Runs the helper. 227 /// 228 /// \param hooks The hooks to pass to the test case. 229 /// 230 /// \return The result of the execution. 231 engine::test_result 232 run(engine::test_case_hooks& hooks) const 233 { 234 const engine::test_program test_program( 235 "atf", _binary_path, _root, "the-suite", 236 engine::metadata_builder().build()); 237 const engine::test_case test_case("atf", test_program, _name, 238 _mdbuilder.build()); 239 240 const fs::path workdir("work"); 241 fs::mkdir(workdir, 0755); 242 243 const engine::test_result result = engine::run_test_case( 244 &test_case, _user_config, hooks, workdir); 245 ATF_REQUIRE(::rmdir(workdir.c_str()) != -1); 246 return result; 247 } 248 }; 249 250 251 /// Hooks to retrieve stdout and stderr. 252 class fetch_output_hooks : public engine::test_case_hooks { 253 public: 254 /// Copies the stdout of the test case outside of its work directory. 255 /// 256 /// \param file The location of the test case's stdout. 257 void 258 got_stdout(const fs::path& file) 259 { 260 atf::utils::copy_file(file.str(), "helper-stdout.txt"); 261 atf::utils::cat_file("helper-stdout.txt", "helper stdout: "); 262 } 263 264 /// Copies the stderr of the test case outside of its work directory. 265 /// 266 /// \param file The location of the test case's stderr. 267 void 268 got_stderr(const fs::path& file) 269 { 270 atf::utils::copy_file(file.str(), "helper-stderr.txt"); 271 atf::utils::cat_file("helper-stderr.txt", "helper stderr: "); 272 } 273 }; 274 275 276 /// Simplifies the execution of the helper test cases. 277 class plain_helper { 278 /// Path to the test program's source directory. 279 const fs::path _srcdir; 280 281 /// The root of the test suite. 282 fs::path _root; 283 284 /// Path to the helper test program, relative to _root. 285 fs::path _binary_path; 286 287 /// Optional timeout for the test program. 288 optional< datetime::delta > _timeout; 289 290 public: 291 /// Constructs a new helper. 292 /// 293 /// \param atf_tc A pointer to the calling test case. Needed to obtain 294 /// run-time configuration variables. 295 /// \param name The name of the helper to run. 296 /// \param timeout An optional timeout for the test case. 297 plain_helper(const atf::tests::tc* atf_tc, const char* name, 298 const optional< datetime::delta > timeout = none) : 299 _srcdir(atf_tc->get_config_var("srcdir")), 300 _root(_srcdir), 301 _binary_path("test_case_plain_helpers"), 302 _timeout(timeout) 303 { 304 utils::setenv("TEST_CASE", name); 305 } 306 307 /// Sets an environment variable for the helper. 308 /// 309 /// This is simply syntactic sugar for utils::setenv. 310 /// 311 /// \param variable The name of the environment variable to set. 312 /// \param value The value of the variable; must be convertible to a string. 313 template< typename T > 314 void 315 set(const char* variable, const T& value) 316 { 317 utils::setenv(variable, F("%s") % value); 318 } 319 320 /// Places the helper in a different location. 321 /// 322 /// This prepares the helper to be run from a different location than the 323 /// source directory so that the runtime execution can be validated. 324 /// 325 /// \param new_binary_path The new path to the binary, relative to the test 326 /// suite root. 327 /// \param new_root The new test suite root. 328 /// 329 /// \pre The directory holding the target test program must exist. 330 /// Otherwise, the relocation of the binary will fail. 331 void 332 move(const char* new_binary_path, const char* new_root) 333 { 334 _binary_path = fs::path(new_binary_path); 335 _root = fs::path(new_root); 336 337 const fs::path src_path = fs::path(_srcdir) / "test_case_plain_helpers"; 338 const fs::path new_path = _root / _binary_path; 339 ATF_REQUIRE( 340 ::symlink(src_path.c_str(), new_path.c_str()) != -1); 341 } 342 343 /// Runs the helper. 344 /// 345 /// \param user_config The runtime engine configuration, if different to the 346 /// defaults. 347 /// 348 /// \return The result of the execution. 349 engine::test_result 350 run(const config::tree& user_config = engine::default_config()) const 351 { 352 engine::metadata_builder mdbuilder; 353 if (_timeout) 354 mdbuilder.set_timeout(_timeout.get()); 355 const engine::test_program test_program( 356 "plain", _binary_path, _root, "unit-tests", mdbuilder.build()); 357 const engine::test_cases_vector& tcs = test_program.test_cases(); 358 fetch_output_hooks fetcher; 359 const engine::test_result result = engine::run_test_case( 360 tcs[0].get(), user_config, fetcher, fs::path(".")); 361 std::cerr << "Result is: " << result << '\n'; 362 return result; 363 } 364 }; 365 366 367 } // anonymous namespace 368 369 370 ATF_TEST_CASE_WITHOUT_HEAD(test_case__ctor_and_getters) 371 ATF_TEST_CASE_BODY(test_case__ctor_and_getters) 372 { 373 const engine::metadata md = engine::metadata_builder() 374 .add_custom("first", "value") 375 .build(); 376 const engine::test_program test_program( 377 "mock", fs::path("abc"), fs::path("unused-root"), 378 "unused-suite-name", engine::metadata_builder().build()); 379 const engine::test_case test_case("mock", test_program, "foo", md); 380 ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program()); 381 ATF_REQUIRE_EQ("foo", test_case.name()); 382 ATF_REQUIRE(md == test_case.get_metadata()); 383 } 384 385 386 ATF_TEST_CASE_WITHOUT_HEAD(test_case__fake_result) 387 ATF_TEST_CASE_BODY(test_case__fake_result) 388 { 389 const engine::test_result result(engine::test_result::skipped, 390 "Some reason"); 391 const engine::test_program test_program( 392 "mock", fs::path("abc"), fs::path("unused-root"), 393 "unused-suite-name", engine::metadata_builder().build()); 394 const engine::test_case test_case("mock", test_program, "__foo__", 395 "Some description", result); 396 ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program()); 397 ATF_REQUIRE_EQ("__foo__", test_case.name()); 398 ATF_REQUIRE(result == test_case.fake_result().get()); 399 } 400 401 402 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__copy); 403 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__copy) 404 { 405 const engine::test_program tp( 406 "plain", fs::path("non-existent"), fs::path("."), "suite-name", 407 engine::metadata_builder().build()); 408 409 const engine::test_case tc1("plain", tp, "name", 410 engine::metadata_builder().build()); 411 const engine::test_case tc2 = tc1; 412 ATF_REQUIRE( tc1 == tc2); 413 ATF_REQUIRE(!(tc1 != tc2)); 414 } 415 416 417 ATF_TEST_CASE_WITHOUT_HEAD(test_case__output); 418 ATF_TEST_CASE_BODY(test_case__output) 419 { 420 const engine::test_program tp( 421 "plain", fs::path("non-existent"), fs::path("."), "suite-name", 422 engine::metadata_builder().build()); 423 424 const engine::test_case tc1( 425 "plain", tp, "the-name", engine::metadata_builder() 426 .add_allowed_platform("foo").add_custom("X-bar", "baz").build()); 427 std::ostringstream str; 428 str << tc1; 429 ATF_REQUIRE_EQ( 430 "test_case{interface='plain', name='the-name', " 431 "metadata=metadata{allowed_architectures='', allowed_platforms='foo', " 432 "custom.X-bar='baz', description='', has_cleanup='false', " 433 "required_configs='', required_files='', required_memory='0', " 434 "required_programs='', required_user='', timeout='300'}}", 435 str.str()); 436 } 437 438 439 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__not_copy); 440 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__not_copy) 441 { 442 const std::string base_interface("plain"); 443 const engine::test_program base_tp( 444 "plain", fs::path("non-existent"), fs::path("."), "suite-name", 445 engine::metadata_builder().build()); 446 const std::string base_name("name"); 447 const engine::metadata base_metadata = engine::metadata_builder() 448 .add_custom("X-foo", "bar") 449 .build(); 450 451 const engine::test_case base_tc(base_interface, base_tp, base_name, 452 base_metadata); 453 454 // Construct with all same values. 455 { 456 const engine::test_case other_tc(base_interface, base_tp, base_name, 457 base_metadata); 458 459 ATF_REQUIRE( base_tc == other_tc); 460 ATF_REQUIRE(!(base_tc != other_tc)); 461 } 462 463 // Different interface. 464 { 465 const engine::test_case other_tc("atf", base_tp, base_name, 466 base_metadata); 467 468 ATF_REQUIRE(!(base_tc == other_tc)); 469 ATF_REQUIRE( base_tc != other_tc); 470 } 471 472 // Different test program, different identifier. 473 { 474 const engine::test_program other_tp( 475 "plain", fs::path("another-name"), fs::path("."), "suite2-name", 476 engine::metadata_builder().build()); 477 const engine::test_case other_tc(base_interface, other_tp, base_name, 478 base_metadata); 479 480 ATF_REQUIRE(!(base_tc == other_tc)); 481 ATF_REQUIRE( base_tc != other_tc); 482 } 483 484 // Different test program, same identifier. Cannot be detected! 485 { 486 const engine::test_program other_tp( 487 "plain", fs::path("non-existent"), fs::path("."), "suite2-name", 488 engine::metadata_builder().build()); 489 const engine::test_case other_tc(base_interface, other_tp, base_name, 490 base_metadata); 491 492 ATF_REQUIRE( base_tc == other_tc); 493 ATF_REQUIRE(!(base_tc != other_tc)); 494 } 495 496 // Different name. 497 { 498 const engine::test_case other_tc(base_interface, base_tp, "other", 499 base_metadata); 500 501 ATF_REQUIRE(!(base_tc == other_tc)); 502 ATF_REQUIRE( base_tc != other_tc); 503 } 504 505 // Different metadata. 506 { 507 const engine::test_case other_tc(base_interface, base_tp, base_name, 508 engine::metadata_builder().build()); 509 510 ATF_REQUIRE(!(base_tc == other_tc)); 511 ATF_REQUIRE( base_tc != other_tc); 512 } 513 } 514 515 516 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__current_directory); 517 ATF_TEST_CASE_BODY(run_test_case__atf__current_directory) 518 { 519 atf_helper helper(this, "pass"); 520 helper.move("program", "."); 521 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 522 helper.run()); 523 } 524 525 526 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__subdirectory); 527 ATF_TEST_CASE_BODY(run_test_case__atf__subdirectory) 528 { 529 atf_helper helper(this, "pass"); 530 ATF_REQUIRE(::mkdir("dir1", 0755) != -1); 531 ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1); 532 helper.move("dir2/program", "dir1"); 533 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 534 helper.run()); 535 } 536 537 538 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__config_variables); 539 ATF_TEST_CASE_BODY(run_test_case__atf__config_variables) 540 { 541 atf_helper helper(this, "create_cookie_in_control_dir"); 542 helper.set_config("control_dir", fs::current_path()); 543 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 544 helper.run()); 545 546 if (!fs::exists(fs::path("cookie"))) 547 fail("The cookie was not created where we expected; the test program " 548 "probably received an invalid configuration variable"); 549 } 550 551 552 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__cleanup_shares_workdir); 553 ATF_TEST_CASE_BODY(run_test_case__atf__cleanup_shares_workdir) 554 { 555 atf_helper helper(this, "check_cleanup_workdir"); 556 helper.set_metadata("has_cleanup", "true"); 557 helper.set_config("control_dir", fs::current_path()); 558 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, 559 "cookie created"), helper.run()); 560 561 if (fs::exists(fs::path("missing_cookie"))) 562 fail("The cleanup part did not see the cookie; the work directory " 563 "is probably not shared"); 564 if (fs::exists(fs::path("invalid_cookie"))) 565 fail("The cleanup part read an invalid cookie"); 566 if (!fs::exists(fs::path("cookie_ok"))) 567 fail("The cleanup part was not executed"); 568 } 569 570 571 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__false); 572 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__false) 573 { 574 atf_helper helper(this, "create_cookie_from_cleanup"); 575 helper.set_metadata("has_cleanup", "false"); 576 helper.set_config("control_dir", fs::current_path()); 577 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 578 helper.run()); 579 580 if (fs::exists(fs::path("cookie"))) 581 fail("The cleanup part was executed even though the test case set " 582 "has.cleanup to false"); 583 } 584 585 586 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__true); 587 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__true) 588 { 589 atf_helper helper(this, "create_cookie_from_cleanup"); 590 helper.set_metadata("has_cleanup", "true"); 591 helper.set_config("control_dir", fs::current_path()); 592 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 593 helper.run()); 594 595 if (!fs::exists(fs::path("cookie"))) 596 fail("The cleanup part was not executed even though the test case set " 597 "has.cleanup to true"); 598 } 599 600 601 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__kill_children); 602 ATF_TEST_CASE_BODY(run_test_case__atf__kill_children) 603 { 604 atf_helper helper(this, "spawn_blocking_child"); 605 helper.set_config("control_dir", fs::current_path()); 606 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 607 helper.run()); 608 609 if (!fs::exists(fs::path("pid"))) 610 fail("The pid file was not created"); 611 std::ifstream pidfile("pid"); 612 ATF_REQUIRE(pidfile); 613 pid_t pid; 614 pidfile >> pid; 615 pidfile.close(); 616 617 int attempts = 30; 618 retry: 619 if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) { 620 // Looks like the subchild did not die. 621 // 622 // Note that this might be inaccurate for two reasons: 623 // 1) The system may have spawned a new process with the same pid as 624 // our subchild... but in practice, this does not happen because 625 // most systems do not immediately reuse pid numbers. If that 626 // happens... well, we get a false test failure. 627 // 2) We ran so fast that even if the process was sent a signal to 628 // die, it has not had enough time to process it yet. This is why 629 // we retry this a few times. 630 if (attempts > 0) { 631 std::cout << "Subprocess not dead yet; retrying wait\n"; 632 --attempts; 633 ::usleep(100000); 634 goto retry; 635 } 636 fail(F("The subprocess %s of our child was not killed") % pid); 637 } 638 } 639 640 641 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__isolation); 642 ATF_TEST_CASE_BODY(run_test_case__atf__isolation) 643 { 644 atf_helper helper(this, "validate_isolation"); 645 // Simple checks to make sure that the test case has been isolated. 646 utils::setenv("HOME", "fake-value"); 647 utils::setenv("LANG", "C"); 648 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 649 helper.run()); 650 } 651 652 653 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_architectures); 654 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_architectures) 655 { 656 atf_helper helper(this, "create_cookie_in_control_dir"); 657 helper.set_metadata("allowed_architectures", "i386 x86_64"); 658 helper.config().set_string("architecture", "powerpc"); 659 helper.config().set_string("platform", ""); 660 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current " 661 "architecture 'powerpc' not supported"), 662 helper.run()); 663 664 if (fs::exists(fs::path("cookie"))) 665 fail("The test case was not really skipped when the requirements " 666 "check failed"); 667 } 668 669 670 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_platforms); 671 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_platforms) 672 { 673 atf_helper helper(this, "create_cookie_in_control_dir"); 674 helper.set_metadata("allowed_platforms", "i386 amd64"); 675 helper.config().set_string("architecture", ""); 676 helper.config().set_string("platform", "macppc"); 677 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current " 678 "platform 'macppc' not supported"), 679 helper.run()); 680 681 if (fs::exists(fs::path("cookie"))) 682 fail("The test case was not really skipped when the requirements " 683 "check failed"); 684 } 685 686 687 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_configs); 688 ATF_TEST_CASE_BODY(run_test_case__atf__required_configs) 689 { 690 atf_helper helper(this, "create_cookie_in_control_dir"); 691 helper.set_metadata("required_configs", "used-var"); 692 helper.set_config("control_dir", fs::current_path()); 693 helper.set_config("unused-var", "value"); 694 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required " 695 "configuration property 'used-var' not " 696 "defined"), 697 helper.run()); 698 699 if (fs::exists(fs::path("cookie"))) 700 fail("The test case was not really skipped when the requirements " 701 "check failed"); 702 } 703 704 705 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_programs); 706 ATF_TEST_CASE_BODY(run_test_case__atf__required_programs) 707 { 708 atf_helper helper(this, "create_cookie_in_control_dir"); 709 helper.set_metadata("required_programs", "/non-existent/program"); 710 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required " 711 "program '/non-existent/program' not " 712 "found"), 713 helper.run()); 714 715 if (fs::exists(fs::path("cookie"))) 716 fail("The test case was not really skipped when the requirements " 717 "check failed"); 718 } 719 720 721 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__ok); 722 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__ok) 723 { 724 set_md_var("require.user", "root"); 725 } 726 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__ok) 727 { 728 atf_helper helper(this, "create_cookie_in_workdir"); 729 helper.set_metadata("required_user", "root"); 730 ATF_REQUIRE(passwd::current_user().is_root()); 731 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 732 helper.run()); 733 } 734 735 736 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__skip); 737 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__skip) 738 { 739 set_md_var("require.user", "unprivileged"); 740 } 741 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__skip) 742 { 743 atf_helper helper(this, "create_cookie_in_workdir"); 744 helper.set_metadata("required_user", "root"); 745 ATF_REQUIRE(!passwd::current_user().is_root()); 746 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires " 747 "root privileges"), 748 helper.run()); 749 } 750 751 752 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__ok); 753 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__ok) 754 { 755 set_md_var("require.user", "unprivileged"); 756 } 757 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__ok) 758 { 759 atf_helper helper(this, "create_cookie_in_workdir"); 760 helper.set_metadata("required_user", "unprivileged"); 761 ATF_REQUIRE(!helper.config().is_set("unprivileged_user")); 762 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 763 helper.run()); 764 } 765 766 767 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__skip); 768 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__skip) 769 { 770 set_md_var("require.user", "root"); 771 } 772 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__skip) 773 { 774 atf_helper helper(this, "create_cookie_in_workdir"); 775 helper.set_metadata("required_user", "unprivileged"); 776 ATF_REQUIRE(!helper.config().is_set("unprivileged_user")); 777 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires " 778 "an unprivileged user but the " 779 "unprivileged-user configuration " 780 "variable is not defined"), 781 helper.run()); 782 } 783 784 785 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__drop); 786 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__drop) 787 { 788 set_md_var("require.config", "unprivileged-user"); 789 set_md_var("require.user", "root"); 790 } 791 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__drop) 792 { 793 atf_helper helper(this, "check_unprivileged"); 794 helper.set_metadata("required_user", "unprivileged"); 795 helper.config().set< engine::user_node >( 796 "unprivileged_user", 797 passwd::find_user_by_name(get_config_var("unprivileged-user"))); 798 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 799 helper.run()); 800 } 801 802 803 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_body); 804 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_body) 805 { 806 atf_helper helper(this, "timeout_body"); 807 helper.set_metadata("timeout", "1"); 808 helper.set_config("control_dir", fs::current_path()); 809 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken, 810 "Test case body timed out"), 811 helper.run()); 812 813 if (fs::exists(fs::path("cookie"))) 814 fail("It seems that the test case was not killed after it timed out"); 815 } 816 817 818 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_cleanup); 819 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_cleanup) 820 { 821 atf_helper helper(this, "timeout_cleanup"); 822 helper.set_metadata("has_cleanup", "true"); 823 helper.set_metadata("timeout", "1"); 824 helper.set_config("control_dir", fs::current_path()); 825 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken, 826 "Test case cleanup timed out"), 827 helper.run()); 828 829 if (fs::exists(fs::path("cookie"))) 830 fail("It seems that the test case was not killed after it timed out"); 831 } 832 833 834 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__body); 835 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__body) 836 { 837 atf_helper helper(this, "crash"); 838 capture_hooks hooks; 839 const engine::test_result result = helper.run(hooks); 840 ATF_REQUIRE(engine::test_result::broken == result.type()); 841 ATF_REQUIRE_MATCH("received signal.*core dumped", result.reason()); 842 843 ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace", 844 hooks.stdout_contents)); 845 ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace", 846 hooks.stderr_contents)); 847 } 848 849 850 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__cleanup); 851 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__cleanup) 852 { 853 atf_helper helper(this, "crash_cleanup"); 854 helper.set_metadata("has_cleanup", "true"); 855 capture_hooks hooks; 856 const engine::test_result result = helper.run(hooks); 857 ATF_REQUIRE(engine::test_result::broken == result.type()); 858 ATF_REQUIRE_MATCH(F("cleanup received signal %s") % SIGABRT, 859 result.reason()); 860 861 ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace", 862 hooks.stdout_contents)); 863 ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace", 864 hooks.stderr_contents)); 865 } 866 867 868 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_results_file); 869 ATF_TEST_CASE_BODY(run_test_case__atf__missing_results_file) 870 { 871 atf_helper helper(this, "crash"); 872 const engine::test_result result = helper.run(); 873 ATF_REQUIRE(engine::test_result::broken == result.type()); 874 // Need to match instead of doing an explicit comparison because the string 875 // may include the "core dumped" substring. 876 ATF_REQUIRE_MATCH(F("test case received signal %s") % SIGABRT, 877 result.reason()); 878 } 879 880 881 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_test_program); 882 ATF_TEST_CASE_BODY(run_test_case__atf__missing_test_program) 883 { 884 atf_helper helper(this, "crash"); 885 ATF_REQUIRE(::mkdir("dir", 0755) != -1); 886 helper.move("test_case_atf_helpers", "dir"); 887 ATF_REQUIRE(::unlink("dir/test_case_atf_helpers") != -1); 888 const engine::test_result result = helper.run(); 889 ATF_REQUIRE(engine::test_result::broken == result.type()); 890 ATF_REQUIRE_MATCH("Test program does not exist", result.reason()); 891 } 892 893 894 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__output); 895 ATF_TEST_CASE_BODY(run_test_case__atf__output) 896 { 897 atf_helper helper(this, "output"); 898 helper.set_metadata("has_cleanup", "true"); 899 900 capture_hooks hooks; 901 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 902 helper.run(hooks)); 903 904 ATF_REQUIRE_EQ("Body message to stdout\nCleanup message to stdout\n", 905 hooks.stdout_contents); 906 ATF_REQUIRE_EQ("Body message to stderr\nCleanup message to stderr\n", 907 hooks.stderr_contents); 908 } 909 910 911 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_pass); 912 ATF_TEST_CASE_BODY(run_test_case__plain__result_pass) 913 { 914 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 915 plain_helper(this, "pass").run()); 916 } 917 918 919 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_fail); 920 ATF_TEST_CASE_BODY(run_test_case__plain__result_fail) 921 { 922 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::failed, 923 "Returned non-success exit status 8"), 924 plain_helper(this, "fail").run()); 925 } 926 927 928 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_crash); 929 ATF_TEST_CASE_BODY(run_test_case__plain__result_crash) 930 { 931 const engine::test_result result = plain_helper(this, "crash").run(); 932 ATF_REQUIRE(engine::test_result::broken == result.type()); 933 ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason()); 934 } 935 936 937 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__current_directory); 938 ATF_TEST_CASE_BODY(run_test_case__plain__current_directory) 939 { 940 plain_helper helper(this, "pass"); 941 helper.move("program", "."); 942 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 943 helper.run()); 944 } 945 946 947 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__subdirectory); 948 ATF_TEST_CASE_BODY(run_test_case__plain__subdirectory) 949 { 950 plain_helper helper(this, "pass"); 951 ATF_REQUIRE(::mkdir("dir1", 0755) != -1); 952 ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1); 953 helper.move("dir2/program", "dir1"); 954 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 955 helper.run()); 956 } 957 958 959 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__kill_children); 960 ATF_TEST_CASE_BODY(run_test_case__plain__kill_children) 961 { 962 plain_helper helper(this, "spawn_blocking_child"); 963 helper.set("CONTROL_DIR", fs::current_path()); 964 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 965 helper.run()); 966 967 if (!fs::exists(fs::path("pid"))) 968 fail("The pid file was not created"); 969 std::ifstream pidfile("pid"); 970 ATF_REQUIRE(pidfile); 971 pid_t pid; 972 pidfile >> pid; 973 pidfile.close(); 974 975 int attempts = 30; 976 retry: 977 if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) { 978 // Looks like the subchild did not die. 979 // 980 // Note that this might be inaccurate for two reasons: 981 // 1) The system may have spawned a new process with the same pid as 982 // our subchild... but in practice, this does not happen because 983 // most systems do not immediately reuse pid numbers. If that 984 // happens... well, we get a false test failure. 985 // 2) We ran so fast that even if the process was sent a signal to 986 // die, it has not had enough time to process it yet. This is why 987 // we retry this a few times. 988 if (attempts > 0) { 989 std::cout << "Subprocess not dead yet; retrying wait\n"; 990 --attempts; 991 ::usleep(100000); 992 goto retry; 993 } 994 fail(F("The subprocess %s of our child was not killed") % pid); 995 } 996 } 997 998 999 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__isolation); 1000 ATF_TEST_CASE_BODY(run_test_case__plain__isolation) 1001 { 1002 const plain_helper helper(this, "validate_isolation"); 1003 utils::setenv("TEST_CASE", "validate_isolation"); 1004 // Simple checks to make sure that the test case has been isolated. 1005 utils::setenv("HOME", "fake-value"); 1006 utils::setenv("LANG", "C"); 1007 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 1008 helper.run()); 1009 } 1010 1011 1012 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__timeout); 1013 ATF_TEST_CASE_BODY(run_test_case__plain__timeout) 1014 { 1015 plain_helper helper(this, "timeout", 1016 utils::make_optional(datetime::delta(1, 0))); 1017 helper.set("CONTROL_DIR", fs::current_path()); 1018 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken, 1019 "Test case timed out"), 1020 helper.run()); 1021 1022 if (fs::exists(fs::path("cookie"))) 1023 fail("It seems that the test case was not killed after it timed out"); 1024 } 1025 1026 1027 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__stacktrace); 1028 ATF_TEST_CASE_BODY(run_test_case__plain__stacktrace) 1029 { 1030 plain_helper helper(this, "crash"); 1031 helper.set("CONTROL_DIR", fs::current_path()); 1032 1033 const engine::test_result result = plain_helper(this, "crash").run(); 1034 ATF_REQUIRE(engine::test_result::broken == result.type()); 1035 ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason()); 1036 1037 ATF_REQUIRE(!atf::utils::grep_file("attempting to gather stack trace", 1038 "helper-stdout.txt")); 1039 ATF_REQUIRE( atf::utils::grep_file("attempting to gather stack trace", 1040 "helper-stderr.txt")); 1041 } 1042 1043 1044 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__missing_test_program); 1045 ATF_TEST_CASE_BODY(run_test_case__plain__missing_test_program) 1046 { 1047 plain_helper helper(this, "pass"); 1048 ATF_REQUIRE(::mkdir("dir", 0755) != -1); 1049 helper.move("test_case_helpers", "dir"); 1050 ATF_REQUIRE(::unlink("dir/test_case_helpers") != -1); 1051 const engine::test_result result = helper.run(); 1052 ATF_REQUIRE(engine::test_result::broken == result.type()); 1053 ATF_REQUIRE_MATCH("Test program does not exist", result.reason()); 1054 } 1055 1056 1057 ATF_INIT_TEST_CASES(tcs) 1058 { 1059 ATF_ADD_TEST_CASE(tcs, test_case__ctor_and_getters); 1060 ATF_ADD_TEST_CASE(tcs, test_case__fake_result); 1061 1062 ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__copy); 1063 ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__not_copy); 1064 1065 ATF_ADD_TEST_CASE(tcs, test_case__output); 1066 1067 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__current_directory); 1068 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__subdirectory); 1069 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__config_variables); 1070 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__cleanup_shares_workdir); 1071 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__false); 1072 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__true); 1073 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__kill_children); 1074 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__isolation); 1075 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_architectures); 1076 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_platforms); 1077 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_configs); 1078 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_programs); 1079 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__ok); 1080 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__skip); 1081 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__ok); 1082 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__skip); 1083 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__drop); 1084 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_body); 1085 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_cleanup); 1086 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__body); 1087 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__cleanup); 1088 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_results_file); 1089 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_test_program); 1090 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__output); 1091 1092 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_pass); 1093 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_fail); 1094 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_crash); 1095 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__current_directory); 1096 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__subdirectory); 1097 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__kill_children); 1098 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__isolation); 1099 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__timeout); 1100 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__stacktrace); 1101 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__missing_test_program); 1102 1103 // TODO(jmmv): Add test cases for debug. 1104 } 1105