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