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 31 extern "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 58 namespace fs = utils::fs; 59 namespace logging = utils::logging; 60 namespace process = utils::process; 61 62 63 namespace { 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. 72 template< int ExitStatus, char Message > 73 static void 74 child_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. 83 class 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 90 public: 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. 116 static void 117 child_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. 128 class child_printer_functor { 129 public: 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. 140 static void 141 child_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. 154 static void 155 child_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. 171 template< class Type, Type Value > 172 void 173 child_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. 185 static fs::path 186 get_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. 197 template< int Errno > 198 static pid_t 199 fork_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. 214 template< int Errno > 215 static int 216 open_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. 237 template< int Errno > 238 static pid_t 239 pipe_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. 259 static void 260 do_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. 300 template< class Hook > 301 static void 302 child__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 331 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_function); 332 ATF_TEST_CASE_BODY(child__fork_capture__ok_function) 333 { 334 child__fork_capture__ok(child_printer_function); 335 } 336 337 338 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_functor); 339 ATF_TEST_CASE_BODY(child__fork_capture__ok_functor) 340 { 341 child__fork_capture__ok(child_printer_functor()); 342 } 343 344 345 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__pipe_fail); 346 ATF_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 359 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_exit); 360 ATF_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 379 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_unwind); 380 ATF_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 401 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_fail); 402 ATF_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 415 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_function); 416 ATF_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 435 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_functor); 436 ATF_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 464 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stdout); 465 ATF_TEST_CASE_BODY(child__fork_files__inherit_stdout) 466 { 467 do_inherit_test("/dev/stdout", "stderr.txt", "stdout.txt", STDOUT_FILENO); 468 } 469 470 471 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stderr); 472 ATF_TEST_CASE_BODY(child__fork_files__inherit_stderr) 473 { 474 do_inherit_test("stdout.txt", "/dev/stderr", "stderr.txt", STDERR_FILENO); 475 } 476 477 478 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_exit); 479 ATF_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 498 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_unwind); 499 ATF_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 521 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_fail); 522 ATF_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 538 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stdout_fail); 539 ATF_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 553 ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stderr_fail); 554 ATF_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 568 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__absolute_path); 569 ATF_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 586 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__relative_path); 587 ATF_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 605 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__basename_only); 606 ATF_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 623 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_path); 624 ATF_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 648 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_args); 649 ATF_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 666 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__some_args); 667 ATF_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 695 ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__missing_program); 696 ATF_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 714 ATF_TEST_CASE_WITHOUT_HEAD(child__pid); 715 ATF_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 736 ATF_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