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 "utils/process/child.ipp" 30 31extern "C" { 32#include <sys/stat.h> 33#include <sys/wait.h> 34 35#include <fcntl.h> 36#include <signal.h> 37#include <unistd.h> 38} 39 40#include <cstdarg> 41#include <cerrno> 42#include <cstdlib> 43#include <cstring> 44#include <fstream> 45#include <iostream> 46 47#include <atf-c++.hpp> 48 49#include "utils/defs.hpp" 50#include "utils/env.hpp" 51#include "utils/format/macros.hpp" 52#include "utils/fs/operations.hpp" 53#include "utils/logging/macros.hpp" 54#include "utils/process/exceptions.hpp" 55#include "utils/process/system.hpp" 56#include "utils/sanity.hpp" 57 58namespace fs = utils::fs; 59namespace logging = utils::logging; 60namespace process = utils::process; 61 62 63namespace { 64 65 66/// Body for a process that prints a simple message and exits. 67/// 68/// \tparam ExitStatus The exit status for the subprocess. 69/// \tparam Message A single character that will be prepended to the printed 70/// messages. This would ideally be a string, but we cannot templatize a 71/// function with an object nor a pointer. 72template< int ExitStatus, char Message > 73static void 74child_simple_function(void) 75{ 76 std::cout << "To stdout: " << Message << "\n"; 77 std::cerr << "To stderr: " << Message << "\n"; 78 std::exit(ExitStatus); 79} 80 81 82/// Functor for the body of a process that prints a simple message and exits. 83class child_simple_functor { 84 /// The exit status that the subprocess will yield. 85 int _exitstatus; 86 87 /// The message to print on stdout and stderr. 88 std::string _message; 89 90public: 91 /// Constructs a new functor. 92 /// 93 /// \param exitstatus The exit status that the subprocess will yield. 94 /// \param message The message to print on stdout and stderr. 95 child_simple_functor(const int exitstatus, const std::string& message) : 96 _exitstatus(exitstatus), 97 _message(message) 98 { 99 } 100 101 /// Body for the subprocess. 102 void 103 operator()(void) 104 { 105 std::cout << "To stdout: " << _message << "\n"; 106 std::cerr << "To stderr: " << _message << "\n"; 107 std::exit(_exitstatus); 108 } 109}; 110 111 112/// Body for a process that prints many messages to stdout and exits. 113/// 114/// The goal of this body is to validate that any buffering performed on the 115/// parent process to read the output of the subprocess works correctly. 116static void 117child_printer_function(void) 118{ 119 for (std::size_t i = 0; i < 100; i++) 120 std::cout << "This is a message to stdout, sequence " << i << "\n"; 121 std::cout.flush(); 122 std::cerr << "Exiting\n"; 123 std::exit(EXIT_SUCCESS); 124} 125 126 127/// Functor for the body of a process that runs child_printer_function. 128class child_printer_functor { 129public: 130 /// Body for the subprocess. 131 void 132 operator()(void) 133 { 134 child_printer_function(); 135 } 136}; 137 138 139/// Body for a child process that creates a pidfile. 140static void 141child_write_pid(void) 142{ 143 std::ofstream output("pidfile"); 144 output << ::getpid() << "\n"; 145 output.close(); 146 std::exit(EXIT_SUCCESS); 147} 148 149 150/// A child process that returns. 151/// 152/// The fork() wrappers are supposed to capture this condition and terminate the 153/// child before the code returns to the fork() call point. 154static void 155child_return(void) 156{ 157} 158 159 160/// A child process that raises an exception. 161/// 162/// The fork() wrappers are supposed to capture this condition and terminate the 163/// child before the code returns to the fork() call point. 164/// 165/// \tparam Type The type of the exception to raise. 166/// \tparam Value The value passed to the constructor of the exception type. In 167/// general, this only makes sense if Type is a primitive type so that, in 168/// the end, the code becomes "throw int(123)". 169/// 170/// \throw Type An exception of the provided type. 171template< class Type, Type Value > 172void 173child_raise_exception(void) 174{ 175 throw Type(Value); 176} 177 178 179/// Calculates the path to the test helpers binary. 180/// 181/// \param tc A pointer to the caller test case, needed to extract the value of 182/// the "srcdir" property. 183/// 184/// \return The path to the helpers binary. 185static fs::path 186get_helpers(const atf::tests::tc* tc) 187{ 188 return fs::path(tc->get_config_var("srcdir")) / "helpers"; 189} 190 191 192/// Mock fork(2) that just returns an error. 193/// 194/// \tparam Errno The value to set as the errno of the failed call. 195/// 196/// \return Always -1. 197template< int Errno > 198static pid_t 199fork_fail(void) throw() 200{ 201 errno = Errno; 202 return -1; 203} 204 205 206/// Mock open(2) that fails if the 'raise-error' file is opened. 207/// 208/// \tparam Errno The value to set as the errno if the known failure triggers. 209/// \param path The path to the file to be opened. 210/// \param flags The open flags. 211/// \param ... The file mode creation, if flags contains O_CREAT. 212/// 213/// \return The opened file handle or -1 on error. 214template< int Errno > 215static int 216open_fail(const char* path, const int flags, ...) throw() 217{ 218 if (std::strcmp(path, "raise-error") == 0) { 219 errno = Errno; 220 return -1; 221 } else { 222 va_list ap; 223 va_start(ap, flags); 224 const int mode = va_arg(ap, int); 225 va_end(ap); 226 return ::open(path, flags, mode); 227 } 228} 229 230 231/// Mock pipe(2) that just returns an error. 232/// 233/// \tparam Errno The value to set as the errno of the failed call. 234/// \param [out] unused_fildes A pointer to a 2-integer array. 235/// 236/// \return Always -1. 237template< int Errno > 238static pid_t 239pipe_fail(int* UTILS_UNUSED_PARAM(fildes)) throw() 240{ 241 errno = Errno; 242 return -1; 243} 244 245 246/// Helper for child tests to validate inheritance of stdout/stderr. 247/// 248/// This function ensures that passing one of /dev/stdout or /dev/stderr to 249/// the child__fork_files fork method does the right thing. The idea is that we 250/// call fork with the given parameters and then make our child redirect one of 251/// its file descriptors to a specific file without going through the process 252/// library. We then validate if this redirection worked and got the expected 253/// output. 254/// 255/// \param fork_stdout The path to pass to the fork call as the stdout file. 256/// \param fork_stderr The path to pass to the fork call as the stderr file. 257/// \param child_file The file to explicitly in the subchild. 258/// \param child_fd The file descriptor to which to attach child_file. 259static void 260do_inherit_test(const char* fork_stdout, const char* fork_stderr, 261 const char* child_file, const int child_fd) 262{ 263 const pid_t pid = ::fork(); 264 ATF_REQUIRE(pid != -1); 265 if (pid == 0) { 266 logging::set_inmemory(); 267 268 const int fd = ::open(child_file, O_CREAT | O_WRONLY | O_TRUNC, 0644); 269 if (fd != child_fd) { 270 if (::dup2(fd, child_fd) == -1) 271 std::abort(); 272 ::close(fd); 273 } 274 275 std::unique_ptr< process::child > child = process::child::fork_files( 276 child_simple_function< 123, 'Z' >, 277 fs::path(fork_stdout), fs::path(fork_stderr)); 278 const process::status status = child->wait(); 279 if (!status.exited() || status.exitstatus() != 123) 280 std::abort(); 281 std::exit(EXIT_SUCCESS); 282 } else { 283 int status; 284 ATF_REQUIRE(::waitpid(pid, &status, 0) != -1); 285 ATF_REQUIRE(WIFEXITED(status)); 286 ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); 287 ATF_REQUIRE(atf::utils::grep_file("stdout: Z", "stdout.txt")); 288 ATF_REQUIRE(atf::utils::grep_file("stderr: Z", "stderr.txt")); 289 } 290} 291 292 293/// Performs a "child__fork_capture__ok_*" test. 294/// 295/// This test basically ensures that the child__fork_capture class spawns a 296/// process whose output is captured in an input stream. 297/// 298/// \tparam Hook The type of the fork hook to use. 299/// \param hook The hook to the fork call. 300template< class Hook > 301static void 302child__fork_capture__ok(Hook hook) 303{ 304 std::cout << "This unflushed message should not propagate to the child"; 305 std::cerr << "This unflushed message should not propagate to the child"; 306 std::unique_ptr< process::child > child = process::child::fork_capture(hook); 307 std::cout << std::endl; 308 std::cerr << std::endl; 309 310 std::istream& output = child->output(); 311 for (std::size_t i = 0; i < 100; i++) { 312 std::string line; 313 ATF_REQUIRE(std::getline(output, line).good()); 314 ATF_REQUIRE_EQ((F("This is a message to stdout, " 315 "sequence %s") % i).str(), line); 316 } 317 318 std::string line; 319 ATF_REQUIRE(std::getline(output, line).good()); 320 ATF_REQUIRE_EQ("Exiting", line); 321 322 process::status status = child->wait(); 323 ATF_REQUIRE(status.exited()); 324 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 325} 326 327 328} // anonymous namespace 329 330 331ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_function); 332ATF_TEST_CASE_BODY(child__fork_capture__ok_function) 333{ 334 child__fork_capture__ok(child_printer_function); 335} 336 337 338ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_functor); 339ATF_TEST_CASE_BODY(child__fork_capture__ok_functor) 340{ 341 child__fork_capture__ok(child_printer_functor()); 342} 343 344 345ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__pipe_fail); 346ATF_TEST_CASE_BODY(child__fork_capture__pipe_fail) 347{ 348 process::detail::syscall_pipe = pipe_fail< 23 >; 349 try { 350 process::child::fork_capture(child_simple_function< 1, 'A' >); 351 fail("Expected exception but none raised"); 352 } catch (const process::system_error& e) { 353 ATF_REQUIRE(atf::utils::grep_string("pipe.*failed", e.what())); 354 ATF_REQUIRE_EQ(23, e.original_errno()); 355 } 356} 357 358 359ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_exit); 360ATF_TEST_CASE_BODY(child__fork_capture__fork_cannot_exit) 361{ 362 const pid_t parent_pid = ::getpid(); 363 atf::utils::create_file("to-not-be-deleted", ""); 364 365 std::unique_ptr< process::child > child = process::child::fork_capture( 366 child_return); 367 if (::getpid() != parent_pid) { 368 // If we enter this clause, it is because the hook returned. 369 ::unlink("to-not-be-deleted"); 370 std::exit(EXIT_SUCCESS); 371 } 372 373 const process::status status = child->wait(); 374 ATF_REQUIRE(status.signaled()); 375 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 376} 377 378 379ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_unwind); 380ATF_TEST_CASE_BODY(child__fork_capture__fork_cannot_unwind) 381{ 382 const pid_t parent_pid = ::getpid(); 383 atf::utils::create_file("to-not-be-deleted", ""); 384 try { 385 std::unique_ptr< process::child > child = process::child::fork_capture( 386 child_raise_exception< int, 123 >); 387 const process::status status = child->wait(); 388 ATF_REQUIRE(status.signaled()); 389 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 390 } catch (const int i) { 391 // If we enter this clause, it is because an exception leaked from the 392 // hook. 393 INV(parent_pid != ::getpid()); 394 INV(i == 123); 395 ::unlink("to-not-be-deleted"); 396 std::exit(EXIT_SUCCESS); 397 } 398} 399 400 401ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_fail); 402ATF_TEST_CASE_BODY(child__fork_capture__fork_fail) 403{ 404 process::detail::syscall_fork = fork_fail< 89 >; 405 try { 406 process::child::fork_capture(child_simple_function< 1, 'A' >); 407 fail("Expected exception but none raised"); 408 } catch (const process::system_error& e) { 409 ATF_REQUIRE(atf::utils::grep_string("fork.*failed", e.what())); 410 ATF_REQUIRE_EQ(89, e.original_errno()); 411 } 412} 413 414 415ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_function); 416ATF_TEST_CASE_BODY(child__fork_files__ok_function) 417{ 418 const fs::path file1("file1.txt"); 419 const fs::path file2("file2.txt"); 420 421 std::unique_ptr< process::child > child = process::child::fork_files( 422 child_simple_function< 15, 'Z' >, file1, file2); 423 const process::status status = child->wait(); 424 ATF_REQUIRE(status.exited()); 425 ATF_REQUIRE_EQ(15, status.exitstatus()); 426 427 ATF_REQUIRE( atf::utils::grep_file("^To stdout: Z$", file1.str())); 428 ATF_REQUIRE(!atf::utils::grep_file("^To stdout: Z$", file2.str())); 429 430 ATF_REQUIRE( atf::utils::grep_file("^To stderr: Z$", file2.str())); 431 ATF_REQUIRE(!atf::utils::grep_file("^To stderr: Z$", file1.str())); 432} 433 434 435ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_functor); 436ATF_TEST_CASE_BODY(child__fork_files__ok_functor) 437{ 438 const fs::path filea("fileA.txt"); 439 const fs::path fileb("fileB.txt"); 440 441 atf::utils::create_file(filea.str(), "Initial stdout\n"); 442 atf::utils::create_file(fileb.str(), "Initial stderr\n"); 443 444 std::unique_ptr< process::child > child = process::child::fork_files( 445 child_simple_functor(16, "a functor"), filea, fileb); 446 const process::status status = child->wait(); 447 ATF_REQUIRE(status.exited()); 448 ATF_REQUIRE_EQ(16, status.exitstatus()); 449 450 ATF_REQUIRE( atf::utils::grep_file("^Initial stdout$", filea.str())); 451 ATF_REQUIRE(!atf::utils::grep_file("^Initial stdout$", fileb.str())); 452 453 ATF_REQUIRE( atf::utils::grep_file("^To stdout: a functor$", filea.str())); 454 ATF_REQUIRE(!atf::utils::grep_file("^To stdout: a functor$", fileb.str())); 455 456 ATF_REQUIRE( atf::utils::grep_file("^Initial stderr$", fileb.str())); 457 ATF_REQUIRE(!atf::utils::grep_file("^Initial stderr$", filea.str())); 458 459 ATF_REQUIRE( atf::utils::grep_file("^To stderr: a functor$", fileb.str())); 460 ATF_REQUIRE(!atf::utils::grep_file("^To stderr: a functor$", filea.str())); 461} 462 463 464ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stdout); 465ATF_TEST_CASE_BODY(child__fork_files__inherit_stdout) 466{ 467 do_inherit_test("/dev/stdout", "stderr.txt", "stdout.txt", STDOUT_FILENO); 468} 469 470 471ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stderr); 472ATF_TEST_CASE_BODY(child__fork_files__inherit_stderr) 473{ 474 do_inherit_test("stdout.txt", "/dev/stderr", "stderr.txt", STDERR_FILENO); 475} 476 477 478ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_exit); 479ATF_TEST_CASE_BODY(child__fork_files__fork_cannot_exit) 480{ 481 const pid_t parent_pid = ::getpid(); 482 atf::utils::create_file("to-not-be-deleted", ""); 483 484 std::unique_ptr< process::child > child = process::child::fork_files( 485 child_return, fs::path("out"), fs::path("err")); 486 if (::getpid() != parent_pid) { 487 // If we enter this clause, it is because the hook returned. 488 ::unlink("to-not-be-deleted"); 489 std::exit(EXIT_SUCCESS); 490 } 491 492 const process::status status = child->wait(); 493 ATF_REQUIRE(status.signaled()); 494 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 495} 496 497 498ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_unwind); 499ATF_TEST_CASE_BODY(child__fork_files__fork_cannot_unwind) 500{ 501 const pid_t parent_pid = ::getpid(); 502 atf::utils::create_file("to-not-be-deleted", ""); 503 try { 504 std::unique_ptr< process::child > child = process::child::fork_files( 505 child_raise_exception< int, 123 >, fs::path("out"), 506 fs::path("err")); 507 const process::status status = child->wait(); 508 ATF_REQUIRE(status.signaled()); 509 ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted"))); 510 } catch (const int i) { 511 // If we enter this clause, it is because an exception leaked from the 512 // hook. 513 INV(parent_pid != ::getpid()); 514 INV(i == 123); 515 ::unlink("to-not-be-deleted"); 516 std::exit(EXIT_SUCCESS); 517 } 518} 519 520 521ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_fail); 522ATF_TEST_CASE_BODY(child__fork_files__fork_fail) 523{ 524 process::detail::syscall_fork = fork_fail< 1234 >; 525 try { 526 process::child::fork_files(child_simple_function< 1, 'A' >, 527 fs::path("a.txt"), fs::path("b.txt")); 528 fail("Expected exception but none raised"); 529 } catch (const process::system_error& e) { 530 ATF_REQUIRE(atf::utils::grep_string("fork.*failed", e.what())); 531 ATF_REQUIRE_EQ(1234, e.original_errno()); 532 } 533 ATF_REQUIRE(!fs::exists(fs::path("a.txt"))); 534 ATF_REQUIRE(!fs::exists(fs::path("b.txt"))); 535} 536 537 538ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stdout_fail); 539ATF_TEST_CASE_BODY(child__fork_files__create_stdout_fail) 540{ 541 process::detail::syscall_open = open_fail< ENOENT >; 542 std::unique_ptr< process::child > child = process::child::fork_files( 543 child_simple_function< 1, 'A' >, fs::path("raise-error"), 544 fs::path("created")); 545 const process::status status = child->wait(); 546 ATF_REQUIRE(status.signaled()); 547 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 548 ATF_REQUIRE(!fs::exists(fs::path("raise-error"))); 549 ATF_REQUIRE(!fs::exists(fs::path("created"))); 550} 551 552 553ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stderr_fail); 554ATF_TEST_CASE_BODY(child__fork_files__create_stderr_fail) 555{ 556 process::detail::syscall_open = open_fail< ENOENT >; 557 std::unique_ptr< process::child > child = process::child::fork_files( 558 child_simple_function< 1, 'A' >, fs::path("created"), 559 fs::path("raise-error")); 560 const process::status status = child->wait(); 561 ATF_REQUIRE(status.signaled()); 562 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 563 ATF_REQUIRE(fs::exists(fs::path("created"))); 564 ATF_REQUIRE(!fs::exists(fs::path("raise-error"))); 565} 566 567 568ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__absolute_path); 569ATF_TEST_CASE_BODY(child__spawn__absolute_path) 570{ 571 std::vector< std::string > args; 572 args.push_back("return-code"); 573 args.push_back("12"); 574 575 const fs::path program = get_helpers(this); 576 INV(program.is_absolute()); 577 std::unique_ptr< process::child > child = process::child::spawn_files( 578 program, args, fs::path("out"), fs::path("err")); 579 580 const process::status status = child->wait(); 581 ATF_REQUIRE(status.exited()); 582 ATF_REQUIRE_EQ(12, status.exitstatus()); 583} 584 585 586ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__relative_path); 587ATF_TEST_CASE_BODY(child__spawn__relative_path) 588{ 589 std::vector< std::string > args; 590 args.push_back("return-code"); 591 args.push_back("13"); 592 593 ATF_REQUIRE(::mkdir("root", 0755) != -1); 594 ATF_REQUIRE(::symlink(get_helpers(this).c_str(), "root/helpers") != -1); 595 596 std::unique_ptr< process::child > child = process::child::spawn_files( 597 fs::path("root/helpers"), args, fs::path("out"), fs::path("err")); 598 599 const process::status status = child->wait(); 600 ATF_REQUIRE(status.exited()); 601 ATF_REQUIRE_EQ(13, status.exitstatus()); 602} 603 604 605ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__basename_only); 606ATF_TEST_CASE_BODY(child__spawn__basename_only) 607{ 608 std::vector< std::string > args; 609 args.push_back("return-code"); 610 args.push_back("14"); 611 612 ATF_REQUIRE(::symlink(get_helpers(this).c_str(), "helpers") != -1); 613 614 std::unique_ptr< process::child > child = process::child::spawn_files( 615 fs::path("helpers"), args, fs::path("out"), fs::path("err")); 616 617 const process::status status = child->wait(); 618 ATF_REQUIRE(status.exited()); 619 ATF_REQUIRE_EQ(14, status.exitstatus()); 620} 621 622 623ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_path); 624ATF_TEST_CASE_BODY(child__spawn__no_path) 625{ 626 logging::set_inmemory(); 627 628 std::vector< std::string > args; 629 args.push_back("return-code"); 630 args.push_back("14"); 631 632 const fs::path helpers = get_helpers(this); 633 utils::setenv("PATH", helpers.branch_path().c_str()); 634 std::unique_ptr< process::child > child = process::child::spawn_capture( 635 fs::path(helpers.leaf_name()), args); 636 637 std::string line; 638 ATF_REQUIRE(std::getline(child->output(), line).good()); 639 ATF_REQUIRE_MATCH("Failed to execute", line); 640 ATF_REQUIRE(!std::getline(child->output(), line)); 641 642 const process::status status = child->wait(); 643 ATF_REQUIRE(status.signaled()); 644 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 645} 646 647 648ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_args); 649ATF_TEST_CASE_BODY(child__spawn__no_args) 650{ 651 std::vector< std::string > args; 652 std::unique_ptr< process::child > child = process::child::spawn_capture( 653 get_helpers(this), args); 654 655 std::string line; 656 ATF_REQUIRE(std::getline(child->output(), line).good()); 657 ATF_REQUIRE_EQ("Must provide a helper name", line); 658 ATF_REQUIRE(!std::getline(child->output(), line)); 659 660 const process::status status = child->wait(); 661 ATF_REQUIRE(status.exited()); 662 ATF_REQUIRE_EQ(EXIT_FAILURE, status.exitstatus()); 663} 664 665 666ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__some_args); 667ATF_TEST_CASE_BODY(child__spawn__some_args) 668{ 669 std::vector< std::string > args; 670 args.push_back("print-args"); 671 args.push_back("foo"); 672 args.push_back(" bar baz "); 673 std::unique_ptr< process::child > child = process::child::spawn_capture( 674 get_helpers(this), args); 675 676 std::string line; 677 ATF_REQUIRE(std::getline(child->output(), line).good()); 678 ATF_REQUIRE_EQ("argv[0] = " + get_helpers(this).str(), line); 679 ATF_REQUIRE(std::getline(child->output(), line).good()); 680 ATF_REQUIRE_EQ("argv[1] = print-args", line); 681 ATF_REQUIRE(std::getline(child->output(), line)); 682 ATF_REQUIRE_EQ("argv[2] = foo", line); 683 ATF_REQUIRE(std::getline(child->output(), line)); 684 ATF_REQUIRE_EQ("argv[3] = bar baz ", line); 685 ATF_REQUIRE(std::getline(child->output(), line)); 686 ATF_REQUIRE_EQ("argv[4] = NULL", line); 687 ATF_REQUIRE(!std::getline(child->output(), line)); 688 689 const process::status status = child->wait(); 690 ATF_REQUIRE(status.exited()); 691 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 692} 693 694 695ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__missing_program); 696ATF_TEST_CASE_BODY(child__spawn__missing_program) 697{ 698 std::vector< std::string > args; 699 std::unique_ptr< process::child > child = process::child::spawn_capture( 700 fs::path("a/b/c"), args); 701 702 std::string line; 703 ATF_REQUIRE(std::getline(child->output(), line).good()); 704 const std::string exp = "Failed to execute a/b/c: "; 705 ATF_REQUIRE_EQ(exp, line.substr(0, exp.length())); 706 ATF_REQUIRE(!std::getline(child->output(), line)); 707 708 const process::status status = child->wait(); 709 ATF_REQUIRE(status.signaled()); 710 ATF_REQUIRE_EQ(SIGABRT, status.termsig()); 711} 712 713 714ATF_TEST_CASE_WITHOUT_HEAD(child__pid); 715ATF_TEST_CASE_BODY(child__pid) 716{ 717 std::unique_ptr< process::child > child = process::child::fork_capture( 718 child_write_pid); 719 720 const int pid = child->pid(); 721 722 const process::status status = child->wait(); 723 ATF_REQUIRE(status.exited()); 724 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus()); 725 726 std::ifstream input("pidfile"); 727 ATF_REQUIRE(input); 728 int read_pid; 729 input >> read_pid; 730 input.close(); 731 732 ATF_REQUIRE_EQ(read_pid, pid); 733} 734 735 736ATF_INIT_TEST_CASES(tcs) 737{ 738 ATF_ADD_TEST_CASE(tcs, child__fork_capture__ok_function); 739 ATF_ADD_TEST_CASE(tcs, child__fork_capture__ok_functor); 740 ATF_ADD_TEST_CASE(tcs, child__fork_capture__pipe_fail); 741 ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_cannot_exit); 742 ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_cannot_unwind); 743 ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_fail); 744 745 ATF_ADD_TEST_CASE(tcs, child__fork_files__ok_function); 746 ATF_ADD_TEST_CASE(tcs, child__fork_files__ok_functor); 747 ATF_ADD_TEST_CASE(tcs, child__fork_files__inherit_stdout); 748 ATF_ADD_TEST_CASE(tcs, child__fork_files__inherit_stderr); 749 ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_cannot_exit); 750 ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_cannot_unwind); 751 ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_fail); 752 ATF_ADD_TEST_CASE(tcs, child__fork_files__create_stdout_fail); 753 ATF_ADD_TEST_CASE(tcs, child__fork_files__create_stderr_fail); 754 755 ATF_ADD_TEST_CASE(tcs, child__spawn__absolute_path); 756 ATF_ADD_TEST_CASE(tcs, child__spawn__relative_path); 757 ATF_ADD_TEST_CASE(tcs, child__spawn__basename_only); 758 ATF_ADD_TEST_CASE(tcs, child__spawn__no_path); 759 ATF_ADD_TEST_CASE(tcs, child__spawn__no_args); 760 ATF_ADD_TEST_CASE(tcs, child__spawn__some_args); 761 ATF_ADD_TEST_CASE(tcs, child__spawn__missing_program); 762 763 ATF_ADD_TEST_CASE(tcs, child__pid); 764} 765