Path: blob/main/contrib/kyua/utils/process/child_test.cpp
48179 views
// Copyright 2010 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "utils/process/child.ipp"2930extern "C" {31#include <sys/stat.h>32#include <sys/wait.h>3334#include <fcntl.h>35#include <signal.h>36#include <unistd.h>37}3839#include <cstdarg>40#include <cerrno>41#include <cstdlib>42#include <cstring>43#include <fstream>44#include <iostream>45#include <stdexcept>4647#include <atf-c++.hpp>4849#include "utils/defs.hpp"50#include "utils/env.hpp"51#include "utils/format/macros.hpp"52#include "utils/fs/operations.hpp"53#include "utils/fs/path.hpp"54#include "utils/logging/macros.hpp"55#include "utils/process/exceptions.hpp"56#include "utils/process/status.hpp"57#include "utils/process/system.hpp"58#include "utils/sanity.hpp"59#include "utils/test_utils.ipp"6061namespace fs = utils::fs;62namespace logging = utils::logging;63namespace process = utils::process;646566namespace {676869/// Checks if the current subprocess is in its own session.70static void71child_check_own_session(void)72{73std::exit((::getsid(::getpid()) == ::getpid()) ?74EXIT_SUCCESS : EXIT_FAILURE);75}767778/// Body for a process that prints a simple message and exits.79///80/// \tparam ExitStatus The exit status for the subprocess.81/// \tparam Message A single character that will be prepended to the printed82/// messages. This would ideally be a string, but we cannot templatize a83/// function with an object nor a pointer.84template< int ExitStatus, char Message >85static void86child_simple_function(void)87{88std::cout << "To stdout: " << Message << "\n";89std::cerr << "To stderr: " << Message << "\n";90std::exit(ExitStatus);91}929394/// Functor for the body of a process that prints a simple message and exits.95class child_simple_functor {96/// The exit status that the subprocess will yield.97int _exitstatus;9899/// The message to print on stdout and stderr.100std::string _message;101102public:103/// Constructs a new functor.104///105/// \param exitstatus The exit status that the subprocess will yield.106/// \param message The message to print on stdout and stderr.107child_simple_functor(const int exitstatus, const std::string& message) :108_exitstatus(exitstatus),109_message(message)110{111}112113/// Body for the subprocess.114void115operator()(void)116{117std::cout << "To stdout: " << _message << "\n";118std::cerr << "To stderr: " << _message << "\n";119std::exit(_exitstatus);120}121};122123124/// Body for a process that prints many messages to stdout and exits.125///126/// The goal of this body is to validate that any buffering performed on the127/// parent process to read the output of the subprocess works correctly.128static void129child_printer_function(void)130{131for (std::size_t i = 0; i < 100; i++)132std::cout << "This is a message to stdout, sequence " << i << "\n";133std::cout.flush();134std::cerr << "Exiting\n";135std::exit(EXIT_SUCCESS);136}137138139/// Functor for the body of a process that runs child_printer_function.140class child_printer_functor {141public:142/// Body for the subprocess.143void144operator()(void)145{146child_printer_function();147}148};149150151/// Body for a child process that throws an exception.152static void153child_throw_exception(void)154{155throw std::runtime_error("A loose exception");156}157158159/// Body for a child process that creates a pidfile.160static void161child_write_pid(void)162{163std::ofstream output("pidfile");164output << ::getpid() << "\n";165output.close();166std::exit(EXIT_SUCCESS);167}168169170/// A child process that returns.171///172/// The fork() wrappers are supposed to capture this condition and terminate the173/// child before the code returns to the fork() call point.174static void175child_return(void)176{177}178179180/// A child process that raises an exception.181///182/// The fork() wrappers are supposed to capture this condition and terminate the183/// child before the code returns to the fork() call point.184///185/// \tparam Type The type of the exception to raise.186/// \tparam Value The value passed to the constructor of the exception type. In187/// general, this only makes sense if Type is a primitive type so that, in188/// the end, the code becomes "throw int(123)".189///190/// \throw Type An exception of the provided type.191template< class Type, Type Value >192void193child_raise_exception(void)194{195throw Type(Value);196}197198199/// Calculates the path to the test helpers binary.200///201/// \param tc A pointer to the caller test case, needed to extract the value of202/// the "srcdir" property.203///204/// \return The path to the helpers binary.205static fs::path206get_helpers(const atf::tests::tc* tc)207{208return fs::path(tc->get_config_var("srcdir")) / "helpers";209}210211212/// Mock fork(2) that just returns an error.213///214/// \tparam Errno The value to set as the errno of the failed call.215///216/// \return Always -1.217template< int Errno >218static pid_t219fork_fail(void) throw()220{221errno = Errno;222return -1;223}224225226/// Mock open(2) that fails if the 'raise-error' file is opened.227///228/// \tparam Errno The value to set as the errno if the known failure triggers.229/// \param path The path to the file to be opened.230/// \param flags The open flags.231/// \param ... The file mode creation, if flags contains O_CREAT.232///233/// \return The opened file handle or -1 on error.234template< int Errno >235static int236open_fail(const char* path, const int flags, ...) throw()237{238if (std::strcmp(path, "raise-error") == 0) {239errno = Errno;240return -1;241} else {242va_list ap;243va_start(ap, flags);244const int mode = va_arg(ap, int);245va_end(ap);246return ::open(path, flags, mode);247}248}249250251/// Mock pipe(2) that just returns an error.252///253/// \tparam Errno The value to set as the errno of the failed call.254///255/// \return Always -1.256template< int Errno >257static pid_t258pipe_fail(int* /* fildes */) throw()259{260errno = Errno;261return -1;262}263264265/// Helper for child tests to validate inheritance of stdout/stderr.266///267/// This function ensures that passing one of /dev/stdout or /dev/stderr to268/// the child__fork_files fork method does the right thing. The idea is that we269/// call fork with the given parameters and then make our child redirect one of270/// its file descriptors to a specific file without going through the process271/// library. We then validate if this redirection worked and got the expected272/// output.273///274/// \param fork_stdout The path to pass to the fork call as the stdout file.275/// \param fork_stderr The path to pass to the fork call as the stderr file.276/// \param child_file The file to explicitly in the subchild.277/// \param child_fd The file descriptor to which to attach child_file.278static void279do_inherit_test(const char* fork_stdout, const char* fork_stderr,280const char* child_file, const int child_fd)281{282const pid_t pid = ::fork();283ATF_REQUIRE(pid != -1);284if (pid == 0) {285logging::set_inmemory();286287const int fd = ::open(child_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);288if (fd != child_fd) {289if (::dup2(fd, child_fd) == -1)290std::abort();291::close(fd);292}293294std::unique_ptr< process::child > child = process::child::fork_files(295child_simple_function< 123, 'Z' >,296fs::path(fork_stdout), fs::path(fork_stderr));297const process::status status = child->wait();298if (!status.exited() || status.exitstatus() != 123)299std::abort();300std::exit(EXIT_SUCCESS);301} else {302int status;303ATF_REQUIRE(::waitpid(pid, &status, 0) != -1);304ATF_REQUIRE(WIFEXITED(status));305ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));306ATF_REQUIRE(atf::utils::grep_file("stdout: Z", "stdout.txt"));307ATF_REQUIRE(atf::utils::grep_file("stderr: Z", "stderr.txt"));308}309}310311312/// Performs a "child__fork_capture__ok_*" test.313///314/// This test basically ensures that the child__fork_capture class spawns a315/// process whose output is captured in an input stream.316///317/// \tparam Hook The type of the fork hook to use.318/// \param hook The hook to the fork call.319template< class Hook >320static void321child__fork_capture__ok(Hook hook)322{323std::cout << "This unflushed message should not propagate to the child";324std::cerr << "This unflushed message should not propagate to the child";325std::unique_ptr< process::child > child = process::child::fork_capture(hook);326std::cout.flush();327std::cerr.flush();328329std::istream& output = child->output();330for (std::size_t i = 0; i < 100; i++) {331std::string line;332ATF_REQUIRE(std::getline(output, line).good());333ATF_REQUIRE_EQ((F("This is a message to stdout, "334"sequence %s") % i).str(), line);335}336337std::string line;338ATF_REQUIRE(std::getline(output, line).good());339ATF_REQUIRE_EQ("Exiting", line);340341process::status status = child->wait();342ATF_REQUIRE(status.exited());343ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());344}345346347} // anonymous namespace348349350ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_function);351ATF_TEST_CASE_BODY(child__fork_capture__ok_function)352{353child__fork_capture__ok(child_printer_function);354}355356357ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__ok_functor);358ATF_TEST_CASE_BODY(child__fork_capture__ok_functor)359{360child__fork_capture__ok(child_printer_functor());361}362363364ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__catch_exceptions);365ATF_TEST_CASE_BODY(child__fork_capture__catch_exceptions)366{367std::unique_ptr< process::child > child = process::child::fork_capture(368child_throw_exception);369370std::string message;371std::istream& output = child->output();372ATF_REQUIRE(std::getline(output, message).good());373374const process::status status = child->wait();375ATF_REQUIRE(status.signaled());376ATF_REQUIRE_EQ(SIGABRT, status.termsig());377378ATF_REQUIRE_MATCH("Caught.*A loose exception", message);379}380381382ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__new_session);383ATF_TEST_CASE_BODY(child__fork_capture__new_session)384{385std::unique_ptr< process::child > child = process::child::fork_capture(386child_check_own_session);387const process::status status = child->wait();388ATF_REQUIRE(status.exited());389ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());390}391392393ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__pipe_fail);394ATF_TEST_CASE_BODY(child__fork_capture__pipe_fail)395{396process::detail::syscall_pipe = pipe_fail< 23 >;397try {398process::child::fork_capture(child_simple_function< 1, 'A' >);399fail("Expected exception but none raised");400} catch (const process::system_error& e) {401ATF_REQUIRE(atf::utils::grep_string("pipe.*failed", e.what()));402ATF_REQUIRE_EQ(23, e.original_errno());403}404}405406407ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_exit);408ATF_TEST_CASE_BODY(child__fork_capture__fork_cannot_exit)409{410const pid_t parent_pid = ::getpid();411atf::utils::create_file("to-not-be-deleted", "");412413std::unique_ptr< process::child > child = process::child::fork_capture(414child_return);415if (::getpid() != parent_pid) {416// If we enter this clause, it is because the hook returned.417::unlink("to-not-be-deleted");418std::exit(EXIT_SUCCESS);419}420421const process::status status = child->wait();422ATF_REQUIRE(status.signaled());423ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted")));424}425426427ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_cannot_unwind);428ATF_TEST_CASE_BODY(child__fork_capture__fork_cannot_unwind)429{430const pid_t parent_pid = ::getpid();431atf::utils::create_file("to-not-be-deleted", "");432try {433std::unique_ptr< process::child > child = process::child::fork_capture(434child_raise_exception< int, 123 >);435const process::status status = child->wait();436ATF_REQUIRE(status.signaled());437ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted")));438} catch (const int i) {439// If we enter this clause, it is because an exception leaked from the440// hook.441INV(parent_pid != ::getpid());442INV(i == 123);443::unlink("to-not-be-deleted");444std::exit(EXIT_SUCCESS);445}446}447448449ATF_TEST_CASE_WITHOUT_HEAD(child__fork_capture__fork_fail);450ATF_TEST_CASE_BODY(child__fork_capture__fork_fail)451{452process::detail::syscall_fork = fork_fail< 89 >;453try {454process::child::fork_capture(child_simple_function< 1, 'A' >);455fail("Expected exception but none raised");456} catch (const process::system_error& e) {457ATF_REQUIRE(atf::utils::grep_string("fork.*failed", e.what()));458ATF_REQUIRE_EQ(89, e.original_errno());459}460}461462463ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_function);464ATF_TEST_CASE_BODY(child__fork_files__ok_function)465{466const fs::path file1("file1.txt");467const fs::path file2("file2.txt");468469std::unique_ptr< process::child > child = process::child::fork_files(470child_simple_function< 15, 'Z' >, file1, file2);471const process::status status = child->wait();472ATF_REQUIRE(status.exited());473ATF_REQUIRE_EQ(15, status.exitstatus());474475ATF_REQUIRE( atf::utils::grep_file("^To stdout: Z$", file1.str()));476ATF_REQUIRE(!atf::utils::grep_file("^To stdout: Z$", file2.str()));477478ATF_REQUIRE( atf::utils::grep_file("^To stderr: Z$", file2.str()));479ATF_REQUIRE(!atf::utils::grep_file("^To stderr: Z$", file1.str()));480}481482483ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__ok_functor);484ATF_TEST_CASE_BODY(child__fork_files__ok_functor)485{486const fs::path filea("fileA.txt");487const fs::path fileb("fileB.txt");488489atf::utils::create_file(filea.str(), "Initial stdout\n");490atf::utils::create_file(fileb.str(), "Initial stderr\n");491492std::unique_ptr< process::child > child = process::child::fork_files(493child_simple_functor(16, "a functor"), filea, fileb);494const process::status status = child->wait();495ATF_REQUIRE(status.exited());496ATF_REQUIRE_EQ(16, status.exitstatus());497498ATF_REQUIRE( atf::utils::grep_file("^Initial stdout$", filea.str()));499ATF_REQUIRE(!atf::utils::grep_file("^Initial stdout$", fileb.str()));500501ATF_REQUIRE( atf::utils::grep_file("^To stdout: a functor$", filea.str()));502ATF_REQUIRE(!atf::utils::grep_file("^To stdout: a functor$", fileb.str()));503504ATF_REQUIRE( atf::utils::grep_file("^Initial stderr$", fileb.str()));505ATF_REQUIRE(!atf::utils::grep_file("^Initial stderr$", filea.str()));506507ATF_REQUIRE( atf::utils::grep_file("^To stderr: a functor$", fileb.str()));508ATF_REQUIRE(!atf::utils::grep_file("^To stderr: a functor$", filea.str()));509}510511512ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__catch_exceptions);513ATF_TEST_CASE_BODY(child__fork_files__catch_exceptions)514{515std::unique_ptr< process::child > child = process::child::fork_files(516child_throw_exception,517fs::path("unused.out"), fs::path("stderr"));518519const process::status status = child->wait();520ATF_REQUIRE(status.signaled());521ATF_REQUIRE_EQ(SIGABRT, status.termsig());522523ATF_REQUIRE(atf::utils::grep_file("Caught.*A loose exception", "stderr"));524}525526527ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__new_session);528ATF_TEST_CASE_BODY(child__fork_files__new_session)529{530std::unique_ptr< process::child > child = process::child::fork_files(531child_check_own_session,532fs::path("unused.out"), fs::path("unused.err"));533const process::status status = child->wait();534ATF_REQUIRE(status.exited());535ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());536}537538539ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stdout);540ATF_TEST_CASE_BODY(child__fork_files__inherit_stdout)541{542do_inherit_test("/dev/stdout", "stderr.txt", "stdout.txt", STDOUT_FILENO);543}544545546ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__inherit_stderr);547ATF_TEST_CASE_BODY(child__fork_files__inherit_stderr)548{549do_inherit_test("stdout.txt", "/dev/stderr", "stderr.txt", STDERR_FILENO);550}551552553ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_exit);554ATF_TEST_CASE_BODY(child__fork_files__fork_cannot_exit)555{556const pid_t parent_pid = ::getpid();557atf::utils::create_file("to-not-be-deleted", "");558559std::unique_ptr< process::child > child = process::child::fork_files(560child_return, fs::path("out"), fs::path("err"));561if (::getpid() != parent_pid) {562// If we enter this clause, it is because the hook returned.563::unlink("to-not-be-deleted");564std::exit(EXIT_SUCCESS);565}566567const process::status status = child->wait();568ATF_REQUIRE(status.signaled());569ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted")));570}571572573ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_cannot_unwind);574ATF_TEST_CASE_BODY(child__fork_files__fork_cannot_unwind)575{576const pid_t parent_pid = ::getpid();577atf::utils::create_file("to-not-be-deleted", "");578try {579std::unique_ptr< process::child > child = process::child::fork_files(580child_raise_exception< int, 123 >, fs::path("out"),581fs::path("err"));582const process::status status = child->wait();583ATF_REQUIRE(status.signaled());584ATF_REQUIRE(fs::exists(fs::path("to-not-be-deleted")));585} catch (const int i) {586// If we enter this clause, it is because an exception leaked from the587// hook.588INV(parent_pid != ::getpid());589INV(i == 123);590::unlink("to-not-be-deleted");591std::exit(EXIT_SUCCESS);592}593}594595596ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__fork_fail);597ATF_TEST_CASE_BODY(child__fork_files__fork_fail)598{599process::detail::syscall_fork = fork_fail< 1234 >;600try {601process::child::fork_files(child_simple_function< 1, 'A' >,602fs::path("a.txt"), fs::path("b.txt"));603fail("Expected exception but none raised");604} catch (const process::system_error& e) {605ATF_REQUIRE(atf::utils::grep_string("fork.*failed", e.what()));606ATF_REQUIRE_EQ(1234, e.original_errno());607}608ATF_REQUIRE(!fs::exists(fs::path("a.txt")));609ATF_REQUIRE(!fs::exists(fs::path("b.txt")));610}611612613ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stdout_fail);614ATF_TEST_CASE_BODY(child__fork_files__create_stdout_fail)615{616process::detail::syscall_open = open_fail< ENOENT >;617std::unique_ptr< process::child > child = process::child::fork_files(618child_simple_function< 1, 'A' >, fs::path("raise-error"),619fs::path("created"));620const process::status status = child->wait();621ATF_REQUIRE(status.signaled());622ATF_REQUIRE_EQ(SIGABRT, status.termsig());623ATF_REQUIRE(!fs::exists(fs::path("raise-error")));624ATF_REQUIRE(!fs::exists(fs::path("created")));625}626627628ATF_TEST_CASE_WITHOUT_HEAD(child__fork_files__create_stderr_fail);629ATF_TEST_CASE_BODY(child__fork_files__create_stderr_fail)630{631process::detail::syscall_open = open_fail< ENOENT >;632std::unique_ptr< process::child > child = process::child::fork_files(633child_simple_function< 1, 'A' >, fs::path("created"),634fs::path("raise-error"));635const process::status status = child->wait();636ATF_REQUIRE(status.signaled());637ATF_REQUIRE_EQ(SIGABRT, status.termsig());638ATF_REQUIRE(fs::exists(fs::path("created")));639ATF_REQUIRE(!fs::exists(fs::path("raise-error")));640}641642643ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__absolute_path);644ATF_TEST_CASE_BODY(child__spawn__absolute_path)645{646std::vector< std::string > args;647args.push_back("return-code");648args.push_back("12");649650const fs::path program = get_helpers(this);651INV(program.is_absolute());652std::unique_ptr< process::child > child = process::child::spawn_files(653program, args, fs::path("out"), fs::path("err"));654655const process::status status = child->wait();656ATF_REQUIRE(status.exited());657ATF_REQUIRE_EQ(12, status.exitstatus());658}659660661ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__relative_path);662ATF_TEST_CASE_BODY(child__spawn__relative_path)663{664std::vector< std::string > args;665args.push_back("return-code");666args.push_back("13");667668ATF_REQUIRE(::mkdir("root", 0755) != -1);669ATF_REQUIRE(::symlink(get_helpers(this).c_str(), "root/helpers") != -1);670671std::unique_ptr< process::child > child = process::child::spawn_files(672fs::path("root/helpers"), args, fs::path("out"), fs::path("err"));673674const process::status status = child->wait();675ATF_REQUIRE(status.exited());676ATF_REQUIRE_EQ(13, status.exitstatus());677}678679680ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__basename_only);681ATF_TEST_CASE_BODY(child__spawn__basename_only)682{683std::vector< std::string > args;684args.push_back("return-code");685args.push_back("14");686687ATF_REQUIRE(::symlink(get_helpers(this).c_str(), "helpers") != -1);688689std::unique_ptr< process::child > child = process::child::spawn_files(690fs::path("helpers"), args, fs::path("out"), fs::path("err"));691692const process::status status = child->wait();693ATF_REQUIRE(status.exited());694ATF_REQUIRE_EQ(14, status.exitstatus());695}696697698ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_path);699ATF_TEST_CASE_BODY(child__spawn__no_path)700{701logging::set_inmemory();702703std::vector< std::string > args;704args.push_back("return-code");705args.push_back("14");706707const fs::path helpers = get_helpers(this);708utils::setenv("PATH", helpers.branch_path().c_str());709std::unique_ptr< process::child > child = process::child::spawn_capture(710fs::path(helpers.leaf_name()), args);711712std::string line;713ATF_REQUIRE(std::getline(child->output(), line).good());714ATF_REQUIRE_MATCH("Failed to execute", line);715ATF_REQUIRE(!std::getline(child->output(), line));716717const process::status status = child->wait();718ATF_REQUIRE(status.signaled());719ATF_REQUIRE_EQ(SIGABRT, status.termsig());720}721722723ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__no_args);724ATF_TEST_CASE_BODY(child__spawn__no_args)725{726std::vector< std::string > args;727std::unique_ptr< process::child > child = process::child::spawn_capture(728get_helpers(this), args);729730std::string line;731ATF_REQUIRE(std::getline(child->output(), line).good());732ATF_REQUIRE_EQ("Must provide a helper name", line);733ATF_REQUIRE(!std::getline(child->output(), line));734735const process::status status = child->wait();736ATF_REQUIRE(status.exited());737ATF_REQUIRE_EQ(EXIT_FAILURE, status.exitstatus());738}739740741ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__some_args);742ATF_TEST_CASE_BODY(child__spawn__some_args)743{744std::vector< std::string > args;745args.push_back("print-args");746args.push_back("foo");747args.push_back(" bar baz ");748std::unique_ptr< process::child > child = process::child::spawn_capture(749get_helpers(this), args);750751std::string line;752ATF_REQUIRE(std::getline(child->output(), line).good());753ATF_REQUIRE_EQ("argv[0] = " + get_helpers(this).str(), line);754ATF_REQUIRE(std::getline(child->output(), line).good());755ATF_REQUIRE_EQ("argv[1] = print-args", line);756ATF_REQUIRE(std::getline(child->output(), line));757ATF_REQUIRE_EQ("argv[2] = foo", line);758ATF_REQUIRE(std::getline(child->output(), line));759ATF_REQUIRE_EQ("argv[3] = bar baz ", line);760ATF_REQUIRE(std::getline(child->output(), line));761ATF_REQUIRE_EQ("argv[4] = NULL", line);762ATF_REQUIRE(!std::getline(child->output(), line));763764const process::status status = child->wait();765ATF_REQUIRE(status.exited());766ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());767}768769770ATF_TEST_CASE_WITHOUT_HEAD(child__spawn__missing_program);771ATF_TEST_CASE_BODY(child__spawn__missing_program)772{773std::vector< std::string > args;774std::unique_ptr< process::child > child = process::child::spawn_capture(775fs::path("a/b/c"), args);776777std::string line;778ATF_REQUIRE(std::getline(child->output(), line).good());779const std::string exp = "Failed to execute a/b/c: ";780ATF_REQUIRE_EQ(exp, line.substr(0, exp.length()));781ATF_REQUIRE(!std::getline(child->output(), line));782783const process::status status = child->wait();784ATF_REQUIRE(status.signaled());785ATF_REQUIRE_EQ(SIGABRT, status.termsig());786}787788789ATF_TEST_CASE_WITHOUT_HEAD(child__pid);790ATF_TEST_CASE_BODY(child__pid)791{792std::unique_ptr< process::child > child = process::child::fork_capture(793child_write_pid);794795const int pid = child->pid();796797const process::status status = child->wait();798ATF_REQUIRE(status.exited());799ATF_REQUIRE_EQ(EXIT_SUCCESS, status.exitstatus());800801std::ifstream input("pidfile");802ATF_REQUIRE(input);803int read_pid;804input >> read_pid;805input.close();806807ATF_REQUIRE_EQ(read_pid, pid);808}809810811ATF_INIT_TEST_CASES(tcs)812{813utils::avoid_coredump_on_crash();814815ATF_ADD_TEST_CASE(tcs, child__fork_capture__ok_function);816ATF_ADD_TEST_CASE(tcs, child__fork_capture__ok_functor);817ATF_ADD_TEST_CASE(tcs, child__fork_capture__catch_exceptions);818ATF_ADD_TEST_CASE(tcs, child__fork_capture__new_session);819ATF_ADD_TEST_CASE(tcs, child__fork_capture__pipe_fail);820ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_cannot_exit);821ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_cannot_unwind);822ATF_ADD_TEST_CASE(tcs, child__fork_capture__fork_fail);823824ATF_ADD_TEST_CASE(tcs, child__fork_files__ok_function);825ATF_ADD_TEST_CASE(tcs, child__fork_files__ok_functor);826ATF_ADD_TEST_CASE(tcs, child__fork_files__catch_exceptions);827ATF_ADD_TEST_CASE(tcs, child__fork_files__new_session);828ATF_ADD_TEST_CASE(tcs, child__fork_files__inherit_stdout);829ATF_ADD_TEST_CASE(tcs, child__fork_files__inherit_stderr);830ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_cannot_exit);831ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_cannot_unwind);832ATF_ADD_TEST_CASE(tcs, child__fork_files__fork_fail);833ATF_ADD_TEST_CASE(tcs, child__fork_files__create_stdout_fail);834ATF_ADD_TEST_CASE(tcs, child__fork_files__create_stderr_fail);835836ATF_ADD_TEST_CASE(tcs, child__spawn__absolute_path);837ATF_ADD_TEST_CASE(tcs, child__spawn__relative_path);838ATF_ADD_TEST_CASE(tcs, child__spawn__basename_only);839ATF_ADD_TEST_CASE(tcs, child__spawn__no_path);840ATF_ADD_TEST_CASE(tcs, child__spawn__no_args);841ATF_ADD_TEST_CASE(tcs, child__spawn__some_args);842ATF_ADD_TEST_CASE(tcs, child__spawn__missing_program);843844ATF_ADD_TEST_CASE(tcs, child__pid);845}846847848