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 /// Creates a mock tester that receives a signal. 368 /// 369 /// \param interface The name of the interface implemented by the tester. 370 /// \param term_sig Signal to deliver to the tester. If the tester does not 371 /// exit due to this reason, it exits with an arbitrary non-zero code. 372 static void 373 create_mock_tester_signal(const char* interface, const int term_sig) 374 { 375 const std::string tester_name = F("kyua-%s-tester") % interface; 376 377 atf::utils::create_file( 378 tester_name, 379 F("#! /bin/sh\n" 380 "echo 'stdout stuff'\n" 381 "echo 'stderr stuff' 1>&2\n" 382 "kill -%s $$\n" 383 "echo 'not reachable' 1>&2\n" 384 "exit 0\n") % term_sig); 385 ATF_REQUIRE(::chmod(tester_name.c_str(), 0755) != -1); 386 387 utils::setenv("KYUA_TESTERSDIR", fs::current_path().str()); 388 } 389 390 391 } // anonymous namespace 392 393 394 ATF_TEST_CASE_WITHOUT_HEAD(test_case__ctor_and_getters) 395 ATF_TEST_CASE_BODY(test_case__ctor_and_getters) 396 { 397 const engine::metadata md = engine::metadata_builder() 398 .add_custom("first", "value") 399 .build(); 400 const engine::test_program test_program( 401 "mock", fs::path("abc"), fs::path("unused-root"), 402 "unused-suite-name", engine::metadata_builder().build()); 403 const engine::test_case test_case("mock", test_program, "foo", md); 404 ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program()); 405 ATF_REQUIRE_EQ("foo", test_case.name()); 406 ATF_REQUIRE(md == test_case.get_metadata()); 407 } 408 409 410 ATF_TEST_CASE_WITHOUT_HEAD(test_case__fake_result) 411 ATF_TEST_CASE_BODY(test_case__fake_result) 412 { 413 const engine::test_result result(engine::test_result::skipped, 414 "Some reason"); 415 const engine::test_program test_program( 416 "mock", fs::path("abc"), fs::path("unused-root"), 417 "unused-suite-name", engine::metadata_builder().build()); 418 const engine::test_case test_case("mock", test_program, "__foo__", 419 "Some description", result); 420 ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program()); 421 ATF_REQUIRE_EQ("__foo__", test_case.name()); 422 ATF_REQUIRE(result == test_case.fake_result().get()); 423 } 424 425 426 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__copy); 427 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__copy) 428 { 429 const engine::test_program tp( 430 "plain", fs::path("non-existent"), fs::path("."), "suite-name", 431 engine::metadata_builder().build()); 432 433 const engine::test_case tc1("plain", tp, "name", 434 engine::metadata_builder().build()); 435 const engine::test_case tc2 = tc1; 436 ATF_REQUIRE( tc1 == tc2); 437 ATF_REQUIRE(!(tc1 != tc2)); 438 } 439 440 441 ATF_TEST_CASE_WITHOUT_HEAD(test_case__output); 442 ATF_TEST_CASE_BODY(test_case__output) 443 { 444 const engine::test_program tp( 445 "plain", fs::path("non-existent"), fs::path("."), "suite-name", 446 engine::metadata_builder().build()); 447 448 const engine::test_case tc1( 449 "plain", tp, "the-name", engine::metadata_builder() 450 .add_allowed_platform("foo").add_custom("X-bar", "baz").build()); 451 std::ostringstream str; 452 str << tc1; 453 ATF_REQUIRE_EQ( 454 "test_case{interface='plain', name='the-name', " 455 "metadata=metadata{allowed_architectures='', allowed_platforms='foo', " 456 "custom.X-bar='baz', description='', has_cleanup='false', " 457 "required_configs='', required_files='', required_memory='0', " 458 "required_programs='', required_user='', timeout='300'}}", 459 str.str()); 460 } 461 462 463 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__not_copy); 464 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__not_copy) 465 { 466 const std::string base_interface("plain"); 467 const engine::test_program base_tp( 468 "plain", fs::path("non-existent"), fs::path("."), "suite-name", 469 engine::metadata_builder().build()); 470 const std::string base_name("name"); 471 const engine::metadata base_metadata = engine::metadata_builder() 472 .add_custom("X-foo", "bar") 473 .build(); 474 475 const engine::test_case base_tc(base_interface, base_tp, base_name, 476 base_metadata); 477 478 // Construct with all same values. 479 { 480 const engine::test_case other_tc(base_interface, base_tp, base_name, 481 base_metadata); 482 483 ATF_REQUIRE( base_tc == other_tc); 484 ATF_REQUIRE(!(base_tc != other_tc)); 485 } 486 487 // Different interface. 488 { 489 const engine::test_case other_tc("atf", base_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 test program, different identifier. 497 { 498 const engine::test_program other_tp( 499 "plain", fs::path("another-name"), fs::path("."), "suite2-name", 500 engine::metadata_builder().build()); 501 const engine::test_case other_tc(base_interface, other_tp, base_name, 502 base_metadata); 503 504 ATF_REQUIRE(!(base_tc == other_tc)); 505 ATF_REQUIRE( base_tc != other_tc); 506 } 507 508 // Different test program, same identifier. Cannot be detected! 509 { 510 const engine::test_program other_tp( 511 "plain", fs::path("non-existent"), fs::path("."), "suite2-name", 512 engine::metadata_builder().build()); 513 const engine::test_case other_tc(base_interface, other_tp, base_name, 514 base_metadata); 515 516 ATF_REQUIRE( base_tc == other_tc); 517 ATF_REQUIRE(!(base_tc != other_tc)); 518 } 519 520 // Different name. 521 { 522 const engine::test_case other_tc(base_interface, base_tp, "other", 523 base_metadata); 524 525 ATF_REQUIRE(!(base_tc == other_tc)); 526 ATF_REQUIRE( base_tc != other_tc); 527 } 528 529 // Different metadata. 530 { 531 const engine::test_case other_tc(base_interface, base_tp, base_name, 532 engine::metadata_builder().build()); 533 534 ATF_REQUIRE(!(base_tc == other_tc)); 535 ATF_REQUIRE( base_tc != other_tc); 536 } 537 } 538 539 540 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__tester_crashes); 541 ATF_TEST_CASE_BODY(run_test_case__tester_crashes) 542 { 543 atf_helper helper(this, "pass"); 544 helper.move("program", "."); 545 create_mock_tester_signal("atf", SIGSEGV); 546 capture_hooks hooks; 547 const engine::test_result result = helper.run(hooks); 548 549 ATF_REQUIRE(engine::test_result::broken == result.type()); 550 ATF_REQUIRE_MATCH("Tester received signal.*bug", result.reason()); 551 552 ATF_REQUIRE_EQ("stdout stuff\n", hooks.stdout_contents); 553 ATF_REQUIRE_EQ("stderr stuff\n", hooks.stderr_contents); 554 } 555 556 557 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__current_directory); 558 ATF_TEST_CASE_BODY(run_test_case__atf__current_directory) 559 { 560 atf_helper helper(this, "pass"); 561 helper.move("program", "."); 562 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 563 helper.run()); 564 } 565 566 567 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__subdirectory); 568 ATF_TEST_CASE_BODY(run_test_case__atf__subdirectory) 569 { 570 atf_helper helper(this, "pass"); 571 ATF_REQUIRE(::mkdir("dir1", 0755) != -1); 572 ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1); 573 helper.move("dir2/program", "dir1"); 574 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 575 helper.run()); 576 } 577 578 579 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__config_variables); 580 ATF_TEST_CASE_BODY(run_test_case__atf__config_variables) 581 { 582 atf_helper helper(this, "create_cookie_in_control_dir"); 583 helper.set_config("control_dir", fs::current_path()); 584 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 585 helper.run()); 586 587 if (!fs::exists(fs::path("cookie"))) 588 fail("The cookie was not created where we expected; the test program " 589 "probably received an invalid configuration variable"); 590 } 591 592 593 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__cleanup_shares_workdir); 594 ATF_TEST_CASE_BODY(run_test_case__atf__cleanup_shares_workdir) 595 { 596 atf_helper helper(this, "check_cleanup_workdir"); 597 helper.set_metadata("has_cleanup", "true"); 598 helper.set_config("control_dir", fs::current_path()); 599 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, 600 "cookie created"), helper.run()); 601 602 if (fs::exists(fs::path("missing_cookie"))) 603 fail("The cleanup part did not see the cookie; the work directory " 604 "is probably not shared"); 605 if (fs::exists(fs::path("invalid_cookie"))) 606 fail("The cleanup part read an invalid cookie"); 607 if (!fs::exists(fs::path("cookie_ok"))) 608 fail("The cleanup part was not executed"); 609 } 610 611 612 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__false); 613 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__false) 614 { 615 atf_helper helper(this, "create_cookie_from_cleanup"); 616 helper.set_metadata("has_cleanup", "false"); 617 helper.set_config("control_dir", fs::current_path()); 618 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 619 helper.run()); 620 621 if (fs::exists(fs::path("cookie"))) 622 fail("The cleanup part was executed even though the test case set " 623 "has.cleanup to false"); 624 } 625 626 627 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__true); 628 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__true) 629 { 630 atf_helper helper(this, "create_cookie_from_cleanup"); 631 helper.set_metadata("has_cleanup", "true"); 632 helper.set_config("control_dir", fs::current_path()); 633 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 634 helper.run()); 635 636 if (!fs::exists(fs::path("cookie"))) 637 fail("The cleanup part was not executed even though the test case set " 638 "has.cleanup to true"); 639 } 640 641 642 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__kill_children); 643 ATF_TEST_CASE_BODY(run_test_case__atf__kill_children) 644 { 645 atf_helper helper(this, "spawn_blocking_child"); 646 helper.set_config("control_dir", fs::current_path()); 647 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 648 helper.run()); 649 650 if (!fs::exists(fs::path("pid"))) 651 fail("The pid file was not created"); 652 std::ifstream pidfile("pid"); 653 ATF_REQUIRE(pidfile); 654 pid_t pid; 655 pidfile >> pid; 656 pidfile.close(); 657 658 int attempts = 30; 659 retry: 660 if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) { 661 // Looks like the subchild did not die. 662 // 663 // Note that this might be inaccurate for two reasons: 664 // 1) The system may have spawned a new process with the same pid as 665 // our subchild... but in practice, this does not happen because 666 // most systems do not immediately reuse pid numbers. If that 667 // happens... well, we get a false test failure. 668 // 2) We ran so fast that even if the process was sent a signal to 669 // die, it has not had enough time to process it yet. This is why 670 // we retry this a few times. 671 if (attempts > 0) { 672 std::cout << "Subprocess not dead yet; retrying wait\n"; 673 --attempts; 674 ::usleep(100000); 675 goto retry; 676 } 677 fail(F("The subprocess %s of our child was not killed") % pid); 678 } 679 } 680 681 682 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__isolation); 683 ATF_TEST_CASE_BODY(run_test_case__atf__isolation) 684 { 685 atf_helper helper(this, "validate_isolation"); 686 // Simple checks to make sure that the test case has been isolated. 687 utils::setenv("HOME", "fake-value"); 688 utils::setenv("LANG", "C"); 689 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 690 helper.run()); 691 } 692 693 694 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_architectures); 695 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_architectures) 696 { 697 atf_helper helper(this, "create_cookie_in_control_dir"); 698 helper.set_metadata("allowed_architectures", "i386 x86_64"); 699 helper.config().set_string("architecture", "powerpc"); 700 helper.config().set_string("platform", ""); 701 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current " 702 "architecture 'powerpc' not supported"), 703 helper.run()); 704 705 if (fs::exists(fs::path("cookie"))) 706 fail("The test case was not really skipped when the requirements " 707 "check failed"); 708 } 709 710 711 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_platforms); 712 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_platforms) 713 { 714 atf_helper helper(this, "create_cookie_in_control_dir"); 715 helper.set_metadata("allowed_platforms", "i386 amd64"); 716 helper.config().set_string("architecture", ""); 717 helper.config().set_string("platform", "macppc"); 718 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current " 719 "platform 'macppc' not supported"), 720 helper.run()); 721 722 if (fs::exists(fs::path("cookie"))) 723 fail("The test case was not really skipped when the requirements " 724 "check failed"); 725 } 726 727 728 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_configs); 729 ATF_TEST_CASE_BODY(run_test_case__atf__required_configs) 730 { 731 atf_helper helper(this, "create_cookie_in_control_dir"); 732 helper.set_metadata("required_configs", "used-var"); 733 helper.set_config("control_dir", fs::current_path()); 734 helper.set_config("unused-var", "value"); 735 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required " 736 "configuration property 'used-var' not " 737 "defined"), 738 helper.run()); 739 740 if (fs::exists(fs::path("cookie"))) 741 fail("The test case was not really skipped when the requirements " 742 "check failed"); 743 } 744 745 746 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_programs); 747 ATF_TEST_CASE_BODY(run_test_case__atf__required_programs) 748 { 749 atf_helper helper(this, "create_cookie_in_control_dir"); 750 helper.set_metadata("required_programs", "/non-existent/program"); 751 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required " 752 "program '/non-existent/program' not " 753 "found"), 754 helper.run()); 755 756 if (fs::exists(fs::path("cookie"))) 757 fail("The test case was not really skipped when the requirements " 758 "check failed"); 759 } 760 761 762 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__ok); 763 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__ok) 764 { 765 set_md_var("require.user", "root"); 766 } 767 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__ok) 768 { 769 atf_helper helper(this, "create_cookie_in_workdir"); 770 helper.set_metadata("required_user", "root"); 771 ATF_REQUIRE(passwd::current_user().is_root()); 772 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 773 helper.run()); 774 } 775 776 777 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__skip); 778 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__skip) 779 { 780 set_md_var("require.user", "unprivileged"); 781 } 782 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__skip) 783 { 784 atf_helper helper(this, "create_cookie_in_workdir"); 785 helper.set_metadata("required_user", "root"); 786 ATF_REQUIRE(!passwd::current_user().is_root()); 787 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires " 788 "root privileges"), 789 helper.run()); 790 } 791 792 793 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__ok); 794 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__ok) 795 { 796 set_md_var("require.user", "unprivileged"); 797 } 798 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__ok) 799 { 800 atf_helper helper(this, "create_cookie_in_workdir"); 801 helper.set_metadata("required_user", "unprivileged"); 802 ATF_REQUIRE(!helper.config().is_set("unprivileged_user")); 803 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 804 helper.run()); 805 } 806 807 808 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__skip); 809 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__skip) 810 { 811 set_md_var("require.user", "root"); 812 } 813 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__skip) 814 { 815 atf_helper helper(this, "create_cookie_in_workdir"); 816 helper.set_metadata("required_user", "unprivileged"); 817 ATF_REQUIRE(!helper.config().is_set("unprivileged_user")); 818 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires " 819 "an unprivileged user but the " 820 "unprivileged-user configuration " 821 "variable is not defined"), 822 helper.run()); 823 } 824 825 826 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__drop); 827 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__drop) 828 { 829 set_md_var("require.config", "unprivileged-user"); 830 set_md_var("require.user", "root"); 831 } 832 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__drop) 833 { 834 atf_helper helper(this, "check_unprivileged"); 835 helper.set_metadata("required_user", "unprivileged"); 836 helper.config().set< engine::user_node >( 837 "unprivileged_user", 838 passwd::find_user_by_name(get_config_var("unprivileged-user"))); 839 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 840 helper.run()); 841 } 842 843 844 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_body); 845 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_body) 846 { 847 atf_helper helper(this, "timeout_body"); 848 helper.set_metadata("timeout", "1"); 849 helper.set_config("control_dir", fs::current_path()); 850 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken, 851 "Test case body timed out"), 852 helper.run()); 853 854 if (fs::exists(fs::path("cookie"))) 855 fail("It seems that the test case was not killed after it timed out"); 856 } 857 858 859 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_cleanup); 860 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_cleanup) 861 { 862 atf_helper helper(this, "timeout_cleanup"); 863 helper.set_metadata("has_cleanup", "true"); 864 helper.set_metadata("timeout", "1"); 865 helper.set_config("control_dir", fs::current_path()); 866 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken, 867 "Test case cleanup timed out"), 868 helper.run()); 869 870 if (fs::exists(fs::path("cookie"))) 871 fail("It seems that the test case was not killed after it timed out"); 872 } 873 874 875 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__body); 876 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__body) 877 { 878 atf_helper helper(this, "crash"); 879 capture_hooks hooks; 880 const engine::test_result result = helper.run(hooks); 881 ATF_REQUIRE(engine::test_result::broken == result.type()); 882 ATF_REQUIRE_MATCH("received signal.*core dumped", result.reason()); 883 884 ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace", 885 hooks.stdout_contents)); 886 ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace", 887 hooks.stderr_contents)); 888 } 889 890 891 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__cleanup); 892 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__cleanup) 893 { 894 atf_helper helper(this, "crash_cleanup"); 895 helper.set_metadata("has_cleanup", "true"); 896 capture_hooks hooks; 897 const engine::test_result result = helper.run(hooks); 898 ATF_REQUIRE(engine::test_result::broken == result.type()); 899 ATF_REQUIRE_MATCH(F("cleanup received signal %s") % SIGABRT, 900 result.reason()); 901 902 ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace", 903 hooks.stdout_contents)); 904 ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace", 905 hooks.stderr_contents)); 906 } 907 908 909 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_results_file); 910 ATF_TEST_CASE_BODY(run_test_case__atf__missing_results_file) 911 { 912 atf_helper helper(this, "crash"); 913 const engine::test_result result = helper.run(); 914 ATF_REQUIRE(engine::test_result::broken == result.type()); 915 // Need to match instead of doing an explicit comparison because the string 916 // may include the "core dumped" substring. 917 ATF_REQUIRE_MATCH(F("test case received signal %s") % SIGABRT, 918 result.reason()); 919 } 920 921 922 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_test_program); 923 ATF_TEST_CASE_BODY(run_test_case__atf__missing_test_program) 924 { 925 atf_helper helper(this, "crash"); 926 ATF_REQUIRE(::mkdir("dir", 0755) != -1); 927 helper.move("test_case_atf_helpers", "dir"); 928 ATF_REQUIRE(::unlink("dir/test_case_atf_helpers") != -1); 929 const engine::test_result result = helper.run(); 930 ATF_REQUIRE(engine::test_result::broken == result.type()); 931 ATF_REQUIRE_MATCH("Test program does not exist", result.reason()); 932 } 933 934 935 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__output); 936 ATF_TEST_CASE_BODY(run_test_case__atf__output) 937 { 938 atf_helper helper(this, "output"); 939 helper.set_metadata("has_cleanup", "true"); 940 941 capture_hooks hooks; 942 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 943 helper.run(hooks)); 944 945 ATF_REQUIRE_EQ("Body message to stdout\nCleanup message to stdout\n", 946 hooks.stdout_contents); 947 ATF_REQUIRE_EQ("Body message to stderr\nCleanup message to stderr\n", 948 hooks.stderr_contents); 949 } 950 951 952 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_pass); 953 ATF_TEST_CASE_BODY(run_test_case__plain__result_pass) 954 { 955 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 956 plain_helper(this, "pass").run()); 957 } 958 959 960 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_fail); 961 ATF_TEST_CASE_BODY(run_test_case__plain__result_fail) 962 { 963 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::failed, 964 "Returned non-success exit status 8"), 965 plain_helper(this, "fail").run()); 966 } 967 968 969 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_crash); 970 ATF_TEST_CASE_BODY(run_test_case__plain__result_crash) 971 { 972 const engine::test_result result = plain_helper(this, "crash").run(); 973 ATF_REQUIRE(engine::test_result::broken == result.type()); 974 ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason()); 975 } 976 977 978 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__current_directory); 979 ATF_TEST_CASE_BODY(run_test_case__plain__current_directory) 980 { 981 plain_helper helper(this, "pass"); 982 helper.move("program", "."); 983 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 984 helper.run()); 985 } 986 987 988 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__subdirectory); 989 ATF_TEST_CASE_BODY(run_test_case__plain__subdirectory) 990 { 991 plain_helper helper(this, "pass"); 992 ATF_REQUIRE(::mkdir("dir1", 0755) != -1); 993 ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1); 994 helper.move("dir2/program", "dir1"); 995 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 996 helper.run()); 997 } 998 999 1000 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__kill_children); 1001 ATF_TEST_CASE_BODY(run_test_case__plain__kill_children) 1002 { 1003 plain_helper helper(this, "spawn_blocking_child"); 1004 helper.set("CONTROL_DIR", fs::current_path()); 1005 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 1006 helper.run()); 1007 1008 if (!fs::exists(fs::path("pid"))) 1009 fail("The pid file was not created"); 1010 std::ifstream pidfile("pid"); 1011 ATF_REQUIRE(pidfile); 1012 pid_t pid; 1013 pidfile >> pid; 1014 pidfile.close(); 1015 1016 int attempts = 30; 1017 retry: 1018 if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) { 1019 // Looks like the subchild did not die. 1020 // 1021 // Note that this might be inaccurate for two reasons: 1022 // 1) The system may have spawned a new process with the same pid as 1023 // our subchild... but in practice, this does not happen because 1024 // most systems do not immediately reuse pid numbers. If that 1025 // happens... well, we get a false test failure. 1026 // 2) We ran so fast that even if the process was sent a signal to 1027 // die, it has not had enough time to process it yet. This is why 1028 // we retry this a few times. 1029 if (attempts > 0) { 1030 std::cout << "Subprocess not dead yet; retrying wait\n"; 1031 --attempts; 1032 ::usleep(100000); 1033 goto retry; 1034 } 1035 fail(F("The subprocess %s of our child was not killed") % pid); 1036 } 1037 } 1038 1039 1040 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__isolation); 1041 ATF_TEST_CASE_BODY(run_test_case__plain__isolation) 1042 { 1043 const plain_helper helper(this, "validate_isolation"); 1044 utils::setenv("TEST_CASE", "validate_isolation"); 1045 // Simple checks to make sure that the test case has been isolated. 1046 utils::setenv("HOME", "fake-value"); 1047 utils::setenv("LANG", "C"); 1048 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed), 1049 helper.run()); 1050 } 1051 1052 1053 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__timeout); 1054 ATF_TEST_CASE_BODY(run_test_case__plain__timeout) 1055 { 1056 plain_helper helper(this, "timeout", 1057 utils::make_optional(datetime::delta(1, 0))); 1058 helper.set("CONTROL_DIR", fs::current_path()); 1059 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken, 1060 "Test case timed out"), 1061 helper.run()); 1062 1063 if (fs::exists(fs::path("cookie"))) 1064 fail("It seems that the test case was not killed after it timed out"); 1065 } 1066 1067 1068 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__stacktrace); 1069 ATF_TEST_CASE_BODY(run_test_case__plain__stacktrace) 1070 { 1071 plain_helper helper(this, "crash"); 1072 helper.set("CONTROL_DIR", fs::current_path()); 1073 1074 const engine::test_result result = plain_helper(this, "crash").run(); 1075 ATF_REQUIRE(engine::test_result::broken == result.type()); 1076 ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason()); 1077 1078 ATF_REQUIRE(!atf::utils::grep_file("attempting to gather stack trace", 1079 "helper-stdout.txt")); 1080 ATF_REQUIRE( atf::utils::grep_file("attempting to gather stack trace", 1081 "helper-stderr.txt")); 1082 } 1083 1084 1085 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__missing_test_program); 1086 ATF_TEST_CASE_BODY(run_test_case__plain__missing_test_program) 1087 { 1088 plain_helper helper(this, "pass"); 1089 ATF_REQUIRE(::mkdir("dir", 0755) != -1); 1090 helper.move("test_case_helpers", "dir"); 1091 ATF_REQUIRE(::unlink("dir/test_case_helpers") != -1); 1092 const engine::test_result result = helper.run(); 1093 ATF_REQUIRE(engine::test_result::broken == result.type()); 1094 ATF_REQUIRE_MATCH("Test program does not exist", result.reason()); 1095 } 1096 1097 1098 ATF_INIT_TEST_CASES(tcs) 1099 { 1100 ATF_ADD_TEST_CASE(tcs, test_case__ctor_and_getters); 1101 ATF_ADD_TEST_CASE(tcs, test_case__fake_result); 1102 1103 ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__copy); 1104 ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__not_copy); 1105 1106 ATF_ADD_TEST_CASE(tcs, test_case__output); 1107 1108 ATF_ADD_TEST_CASE(tcs, run_test_case__tester_crashes); 1109 1110 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__current_directory); 1111 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__subdirectory); 1112 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__config_variables); 1113 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__cleanup_shares_workdir); 1114 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__false); 1115 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__true); 1116 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__kill_children); 1117 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__isolation); 1118 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_architectures); 1119 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_platforms); 1120 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_configs); 1121 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_programs); 1122 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__ok); 1123 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__skip); 1124 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__ok); 1125 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__skip); 1126 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__drop); 1127 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_body); 1128 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_cleanup); 1129 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__body); 1130 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__cleanup); 1131 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_results_file); 1132 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_test_program); 1133 ATF_ADD_TEST_CASE(tcs, run_test_case__atf__output); 1134 1135 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_pass); 1136 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_fail); 1137 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_crash); 1138 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__current_directory); 1139 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__subdirectory); 1140 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__kill_children); 1141 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__isolation); 1142 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__timeout); 1143 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__stacktrace); 1144 ATF_ADD_TEST_CASE(tcs, run_test_case__plain__missing_test_program); 1145 1146 // TODO(jmmv): Add test cases for debug. 1147 } 1148