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