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