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