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