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