xref: /freebsd/contrib/atf/atf-c/utils_test.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
1 /* Copyright (c) 2010 The NetBSD Foundation, 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
25 
26 #include "atf-c/utils.h"
27 
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 
31 #include <fcntl.h>
32 #include <stddef.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 
38 #include <atf-c.h>
39 
40 #include "atf-c/detail/dynstr.h"
41 #include "atf-c/detail/test_helpers.h"
42 
43 /** Reads the contents of a file into a buffer.
44  *
45  * Up to buflen-1 characters are read into buffer.  If this function returns,
46  * the contents read into the buffer are guaranteed to be nul-terminated.
47  * Note, however, that if the file contains any nul characters itself,
48  * comparing it "as a string" will not work.
49  *
50  * \param path The file to be read, which must exist.
51  * \param buffer Buffer into which to store the file contents.
52  * \param buflen Size of the target buffer.
53  *
54  * \return The count of bytes read. */
55 static ssize_t
56 read_file(const char *path, void *const buffer, const size_t buflen)
57 {
58     const int fd = open(path, O_RDONLY);
59     ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", path);
60     const ssize_t length = read(fd, buffer, buflen - 1);
61     close(fd);
62     ATF_REQUIRE(length != -1);
63     ((char *)buffer)[length] = '\0';
64     return length;
65 }
66 
67 ATF_TC_WITHOUT_HEAD(cat_file__empty);
68 ATF_TC_BODY(cat_file__empty, tc)
69 {
70     atf_utils_create_file("file.txt", "%s", "");
71     atf_utils_redirect(STDOUT_FILENO, "captured.txt");
72     atf_utils_cat_file("file.txt", "PREFIX");
73     fflush(stdout);
74     close(STDOUT_FILENO);
75 
76     char buffer[1024];
77     read_file("captured.txt", buffer, sizeof(buffer));
78     ATF_REQUIRE_STREQ("", buffer);
79 }
80 
81 ATF_TC_WITHOUT_HEAD(cat_file__one_line);
82 ATF_TC_BODY(cat_file__one_line, tc)
83 {
84     atf_utils_create_file("file.txt", "This is a single line\n");
85     atf_utils_redirect(STDOUT_FILENO, "captured.txt");
86     atf_utils_cat_file("file.txt", "PREFIX");
87     fflush(stdout);
88     close(STDOUT_FILENO);
89 
90     char buffer[1024];
91     read_file("captured.txt", buffer, sizeof(buffer));
92     ATF_REQUIRE_STREQ("PREFIXThis is a single line\n", buffer);
93 }
94 
95 ATF_TC_WITHOUT_HEAD(cat_file__several_lines);
96 ATF_TC_BODY(cat_file__several_lines, tc)
97 {
98     atf_utils_create_file("file.txt", "First\nSecond line\nAnd third\n");
99     atf_utils_redirect(STDOUT_FILENO, "captured.txt");
100     atf_utils_cat_file("file.txt", ">");
101     fflush(stdout);
102     close(STDOUT_FILENO);
103 
104     char buffer[1024];
105     read_file("captured.txt", buffer, sizeof(buffer));
106     ATF_REQUIRE_STREQ(">First\n>Second line\n>And third\n", buffer);
107 }
108 
109 ATF_TC_WITHOUT_HEAD(cat_file__no_newline_eof);
110 ATF_TC_BODY(cat_file__no_newline_eof, tc)
111 {
112     atf_utils_create_file("file.txt", "Foo\n bar baz");
113     atf_utils_redirect(STDOUT_FILENO, "captured.txt");
114     atf_utils_cat_file("file.txt", "PREFIX");
115     fflush(stdout);
116     close(STDOUT_FILENO);
117 
118     char buffer[1024];
119     read_file("captured.txt", buffer, sizeof(buffer));
120     ATF_REQUIRE_STREQ("PREFIXFoo\nPREFIX bar baz", buffer);
121 }
122 
123 ATF_TC_WITHOUT_HEAD(compare_file__empty__match);
124 ATF_TC_BODY(compare_file__empty__match, tc)
125 {
126     atf_utils_create_file("test.txt", "%s", "");
127     ATF_REQUIRE(atf_utils_compare_file("test.txt", ""));
128 }
129 
130 ATF_TC_WITHOUT_HEAD(compare_file__empty__not_match);
131 ATF_TC_BODY(compare_file__empty__not_match, tc)
132 {
133     atf_utils_create_file("test.txt", "%s", "");
134     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n"));
135     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "foo"));
136     ATF_REQUIRE(!atf_utils_compare_file("test.txt", " "));
137 }
138 
139 ATF_TC_WITHOUT_HEAD(compare_file__short__match);
140 ATF_TC_BODY(compare_file__short__match, tc)
141 {
142     atf_utils_create_file("test.txt", "this is a short file");
143     ATF_REQUIRE(atf_utils_compare_file("test.txt", "this is a short file"));
144 }
145 
146 ATF_TC_WITHOUT_HEAD(compare_file__short__not_match);
147 ATF_TC_BODY(compare_file__short__not_match, tc)
148 {
149     atf_utils_create_file("test.txt", "this is a short file");
150     ATF_REQUIRE(!atf_utils_compare_file("test.txt", ""));
151     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n"));
152     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a Short file"));
153     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a short fil"));
154     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a short file "));
155 }
156 
157 ATF_TC_WITHOUT_HEAD(compare_file__long__match);
158 ATF_TC_BODY(compare_file__long__match, tc)
159 {
160     char long_contents[3456];
161     size_t i = 0;
162     for (; i < sizeof(long_contents) - 1; i++)
163         long_contents[i] = '0' + (i % 10);
164     long_contents[i] = '\0';
165     atf_utils_create_file("test.txt", "%s", long_contents);
166 
167     ATF_REQUIRE(atf_utils_compare_file("test.txt", long_contents));
168 }
169 
170 ATF_TC_WITHOUT_HEAD(compare_file__long__not_match);
171 ATF_TC_BODY(compare_file__long__not_match, tc)
172 {
173     char long_contents[3456];
174     size_t i = 0;
175     for (; i < sizeof(long_contents) - 1; i++)
176         long_contents[i] = '0' + (i % 10);
177     long_contents[i] = '\0';
178     atf_utils_create_file("test.txt", "%s", long_contents);
179 
180     ATF_REQUIRE(!atf_utils_compare_file("test.txt", ""));
181     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n"));
182     ATF_REQUIRE(!atf_utils_compare_file("test.txt", "0123456789"));
183     long_contents[i - 1] = 'Z';
184     ATF_REQUIRE(!atf_utils_compare_file("test.txt", long_contents));
185 }
186 
187 ATF_TC_WITHOUT_HEAD(copy_file__empty);
188 ATF_TC_BODY(copy_file__empty, tc)
189 {
190     atf_utils_create_file("src.txt", "%s", "");
191     ATF_REQUIRE(chmod("src.txt", 0520) != -1);
192 
193     atf_utils_copy_file("src.txt", "dest.txt");
194     ATF_REQUIRE(atf_utils_compare_file("dest.txt", ""));
195     struct stat sb;
196     ATF_REQUIRE(stat("dest.txt", &sb) != -1);
197     ATF_REQUIRE_EQ(0520, sb.st_mode & 0xfff);
198 }
199 
200 ATF_TC_WITHOUT_HEAD(copy_file__some_contents);
201 ATF_TC_BODY(copy_file__some_contents, tc)
202 {
203     atf_utils_create_file("src.txt", "This is a\ntest file\n");
204     atf_utils_copy_file("src.txt", "dest.txt");
205     ATF_REQUIRE(atf_utils_compare_file("dest.txt", "This is a\ntest file\n"));
206 }
207 
208 ATF_TC_WITHOUT_HEAD(create_file);
209 ATF_TC_BODY(create_file, tc)
210 {
211     atf_utils_create_file("test.txt", "This is a test with %d", 12345);
212 
213     char buffer[128];
214     read_file("test.txt", buffer, sizeof(buffer));
215     ATF_REQUIRE_STREQ("This is a test with 12345", buffer);
216 }
217 
218 ATF_TC_WITHOUT_HEAD(file_exists);
219 ATF_TC_BODY(file_exists, tc)
220 {
221     atf_utils_create_file("test.txt", "foo");
222 
223     ATF_REQUIRE( atf_utils_file_exists("test.txt"));
224     ATF_REQUIRE( atf_utils_file_exists("./test.txt"));
225     ATF_REQUIRE(!atf_utils_file_exists("./test.tx"));
226     ATF_REQUIRE(!atf_utils_file_exists("test.txt2"));
227 }
228 
229 ATF_TC_WITHOUT_HEAD(fork);
230 ATF_TC_BODY(fork, tc)
231 {
232     fprintf(stdout, "Should not get into child\n");
233     fprintf(stderr, "Should not get into child\n");
234     pid_t pid = atf_utils_fork();
235     if (pid == 0) {
236         fprintf(stdout, "Child stdout\n");
237         fprintf(stderr, "Child stderr\n");
238         exit(EXIT_SUCCESS);
239     }
240 
241     int status;
242     ATF_REQUIRE(waitpid(pid, &status, 0) != -1);
243     ATF_REQUIRE(WIFEXITED(status));
244     ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
245 
246     atf_dynstr_t out_name;
247     RE(atf_dynstr_init_fmt(&out_name, "atf_utils_fork_%d_out.txt", (int)pid));
248     atf_dynstr_t err_name;
249     RE(atf_dynstr_init_fmt(&err_name, "atf_utils_fork_%d_err.txt", (int)pid));
250 
251     char buffer[1024];
252     read_file(atf_dynstr_cstring(&out_name), buffer, sizeof(buffer));
253     ATF_REQUIRE_STREQ("Child stdout\n", buffer);
254     read_file(atf_dynstr_cstring(&err_name), buffer, sizeof(buffer));
255     ATF_REQUIRE_STREQ("Child stderr\n", buffer);
256 
257     atf_dynstr_fini(&err_name);
258     atf_dynstr_fini(&out_name);
259 }
260 
261 ATF_TC_WITHOUT_HEAD(free_charpp__empty);
262 ATF_TC_BODY(free_charpp__empty, tc)
263 {
264     char **array = malloc(sizeof(char *) * 1);
265     array[0] = NULL;
266 
267     atf_utils_free_charpp(array);
268 }
269 
270 ATF_TC_WITHOUT_HEAD(free_charpp__some);
271 ATF_TC_BODY(free_charpp__some, tc)
272 {
273     char **array = malloc(sizeof(char *) * 4);
274     array[0] = strdup("first");
275     array[1] = strdup("second");
276     array[2] = strdup("third");
277     array[3] = NULL;
278 
279     atf_utils_free_charpp(array);
280 }
281 
282 ATF_TC_WITHOUT_HEAD(grep_file);
283 ATF_TC_BODY(grep_file, tc)
284 {
285     atf_utils_create_file("test.txt", "line1\nthe second line\naaaabbbb\n");
286 
287     ATF_CHECK(atf_utils_grep_file("line1", "test.txt"));
288     ATF_CHECK(atf_utils_grep_file("line%d", "test.txt", 1));
289     ATF_CHECK(atf_utils_grep_file("second line", "test.txt"));
290     ATF_CHECK(atf_utils_grep_file("aa.*bb", "test.txt"));
291     ATF_CHECK(!atf_utils_grep_file("foo", "test.txt"));
292     ATF_CHECK(!atf_utils_grep_file("bar", "test.txt"));
293     ATF_CHECK(!atf_utils_grep_file("aaaaa", "test.txt"));
294 }
295 
296 ATF_TC_WITHOUT_HEAD(grep_string);
297 ATF_TC_BODY(grep_string, tc)
298 {
299     const char *str = "a string - aaaabbbb";
300     ATF_CHECK(atf_utils_grep_string("a string", str));
301     ATF_CHECK(atf_utils_grep_string("^a string", str));
302     ATF_CHECK(atf_utils_grep_string("aaaabbbb$", str));
303     ATF_CHECK(atf_utils_grep_string("a%s*bb", str, "a."));
304     ATF_CHECK(!atf_utils_grep_string("foo", str));
305     ATF_CHECK(!atf_utils_grep_string("bar", str));
306     ATF_CHECK(!atf_utils_grep_string("aaaaa", str));
307 }
308 
309 ATF_TC_WITHOUT_HEAD(readline__none);
310 ATF_TC_BODY(readline__none, tc)
311 {
312     atf_utils_create_file("empty.txt", "%s", "");
313 
314     const int fd = open("empty.txt", O_RDONLY);
315     ATF_REQUIRE(fd != -1);
316     ATF_REQUIRE(atf_utils_readline(fd) == NULL);
317     close(fd);
318 }
319 
320 ATF_TC_WITHOUT_HEAD(readline__some);
321 ATF_TC_BODY(readline__some, tc)
322 {
323     const char *l1 = "First line with % formatting % characters %";
324     const char *l2 = "Second line; much longer than the first one";
325     const char *l3 = "Last line, without terminator";
326 
327     atf_utils_create_file("test.txt", "%s\n%s\n%s", l1, l2, l3);
328 
329     const int fd = open("test.txt", O_RDONLY);
330     ATF_REQUIRE(fd != -1);
331 
332     char *line;
333 
334     line = atf_utils_readline(fd);
335     ATF_REQUIRE_STREQ(l1, line);
336     free(line);
337 
338     line = atf_utils_readline(fd);
339     ATF_REQUIRE_STREQ(l2, line);
340     free(line);
341 
342     line = atf_utils_readline(fd);
343     ATF_REQUIRE_STREQ(l3, line);
344     free(line);
345 
346     close(fd);
347 }
348 
349 ATF_TC_WITHOUT_HEAD(redirect__stdout);
350 ATF_TC_BODY(redirect__stdout, tc)
351 {
352     printf("Buffer this");
353     atf_utils_redirect(STDOUT_FILENO, "captured.txt");
354     printf("The printed message");
355     fflush(stdout);
356 
357     char buffer[1024];
358     read_file("captured.txt", buffer, sizeof(buffer));
359     ATF_REQUIRE_STREQ("The printed message", buffer);
360 }
361 
362 ATF_TC_WITHOUT_HEAD(redirect__stderr);
363 ATF_TC_BODY(redirect__stderr, tc)
364 {
365     fprintf(stderr, "Buffer this");
366     atf_utils_redirect(STDERR_FILENO, "captured.txt");
367     fprintf(stderr, "The printed message");
368     fflush(stderr);
369 
370     char buffer[1024];
371     read_file("captured.txt", buffer, sizeof(buffer));
372     ATF_REQUIRE_STREQ("The printed message", buffer);
373 }
374 
375 ATF_TC_WITHOUT_HEAD(redirect__other);
376 ATF_TC_BODY(redirect__other, tc)
377 {
378     const char *message = "Foo bar\nbaz\n";
379     atf_utils_redirect(15, "captured.txt");
380     ATF_REQUIRE(write(15, message, strlen(message)) != -1);
381     close(15);
382 
383     char buffer[1024];
384     read_file("captured.txt", buffer, sizeof(buffer));
385     ATF_REQUIRE_STREQ(message, buffer);
386 }
387 
388 static void
389 fork_and_wait(const int exitstatus, const char* expout, const char* experr)
390 {
391     const pid_t pid = atf_utils_fork();
392     ATF_REQUIRE(pid != -1);
393     if (pid == 0) {
394         fprintf(stdout, "Some output\n");
395         fprintf(stderr, "Some error\n");
396         exit(123);
397     }
398     atf_utils_reset_resultsfile();
399     atf_utils_wait(pid, exitstatus, expout, experr);
400     exit(EXIT_SUCCESS);
401 }
402 
403 ATF_TC_WITHOUT_HEAD(wait__ok);
404 ATF_TC_BODY(wait__ok, tc)
405 {
406     const pid_t control = fork();
407     ATF_REQUIRE(control != -1);
408     if (control == 0)
409         fork_and_wait(123, "Some output\n", "Some error\n");
410     else {
411         int status;
412         ATF_REQUIRE(waitpid(control, &status, 0) != -1);
413         ATF_REQUIRE(WIFEXITED(status));
414         ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
415     }
416 }
417 
418 ATF_TC_WITHOUT_HEAD(wait__ok_nested);
419 ATF_TC_BODY(wait__ok_nested, tc)
420 {
421     const pid_t parent = atf_utils_fork();
422     ATF_REQUIRE(parent != -1);
423     if (parent == 0) {
424         const pid_t child = atf_utils_fork();
425         ATF_REQUIRE(child != -1);
426         if (child == 0) {
427             fflush(stderr);
428             fprintf(stdout, "Child output\n");
429             fflush(stdout);
430             fprintf(stderr, "Child error\n");
431             exit(50);
432         } else {
433             fprintf(stdout, "Parent output\n");
434             fprintf(stderr, "Parent error\n");
435             atf_utils_wait(child, 50, "Child output\n", "Child error\n");
436             exit(40);
437         }
438     } else {
439         atf_utils_wait(parent, 40,
440                        "Parent output\n"
441                        "subprocess stdout: Child output\n"
442                        "subprocess stderr: Child error\n",
443                        "Parent error\n");
444     }
445 }
446 
447 ATF_TC_WITHOUT_HEAD(wait__invalid_exitstatus);
448 ATF_TC_BODY(wait__invalid_exitstatus, tc)
449 {
450     const pid_t control = fork();
451     ATF_REQUIRE(control != -1);
452     if (control == 0)
453         fork_and_wait(120, "Some output\n", "Some error\n");
454     else {
455         int status;
456         ATF_REQUIRE(waitpid(control, &status, 0) != -1);
457         ATF_REQUIRE(WIFEXITED(status));
458         ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
459     }
460 }
461 
462 ATF_TC_WITHOUT_HEAD(wait__invalid_stdout);
463 ATF_TC_BODY(wait__invalid_stdout, tc)
464 {
465     const pid_t control = fork();
466     ATF_REQUIRE(control != -1);
467     if (control == 0)
468         fork_and_wait(123, "Some output foo\n", "Some error\n");
469     else {
470         int status;
471         ATF_REQUIRE(waitpid(control, &status, 0) != -1);
472         ATF_REQUIRE(WIFEXITED(status));
473         ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
474     }
475 }
476 
477 ATF_TC_WITHOUT_HEAD(wait__invalid_stderr);
478 ATF_TC_BODY(wait__invalid_stderr, tc)
479 {
480     const pid_t control = fork();
481     ATF_REQUIRE(control != -1);
482     if (control == 0)
483         fork_and_wait(123, "Some output\n", "Some error foo\n");
484     else {
485         int status;
486         ATF_REQUIRE(waitpid(control, &status, 0) != -1);
487         ATF_REQUIRE(WIFEXITED(status));
488         ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
489     }
490 }
491 
492 ATF_TC_WITHOUT_HEAD(wait__save_stdout);
493 ATF_TC_BODY(wait__save_stdout, tc)
494 {
495     const pid_t control = fork();
496     ATF_REQUIRE(control != -1);
497     if (control == 0)
498         fork_and_wait(123, "save:my-output.txt", "Some error\n");
499     else {
500         int status;
501         ATF_REQUIRE(waitpid(control, &status, 0) != -1);
502         ATF_REQUIRE(WIFEXITED(status));
503         ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
504 
505         ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some output\n"));
506     }
507 }
508 
509 ATF_TC_WITHOUT_HEAD(wait__save_stderr);
510 ATF_TC_BODY(wait__save_stderr, tc)
511 {
512     const pid_t control = fork();
513     ATF_REQUIRE(control != -1);
514     if (control == 0)
515         fork_and_wait(123, "Some output\n", "save:my-output.txt");
516     else {
517         int status;
518         ATF_REQUIRE(waitpid(control, &status, 0) != -1);
519         ATF_REQUIRE(WIFEXITED(status));
520         ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
521 
522         ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some error\n"));
523     }
524 }
525 
526 ATF_TP_ADD_TCS(tp)
527 {
528     ATF_TP_ADD_TC(tp, cat_file__empty);
529     ATF_TP_ADD_TC(tp, cat_file__one_line);
530     ATF_TP_ADD_TC(tp, cat_file__several_lines);
531     ATF_TP_ADD_TC(tp, cat_file__no_newline_eof);
532 
533     ATF_TP_ADD_TC(tp, compare_file__empty__match);
534     ATF_TP_ADD_TC(tp, compare_file__empty__not_match);
535     ATF_TP_ADD_TC(tp, compare_file__short__match);
536     ATF_TP_ADD_TC(tp, compare_file__short__not_match);
537     ATF_TP_ADD_TC(tp, compare_file__long__match);
538     ATF_TP_ADD_TC(tp, compare_file__long__not_match);
539 
540     ATF_TP_ADD_TC(tp, copy_file__empty);
541     ATF_TP_ADD_TC(tp, copy_file__some_contents);
542 
543     ATF_TP_ADD_TC(tp, create_file);
544 
545     ATF_TP_ADD_TC(tp, file_exists);
546 
547     ATF_TP_ADD_TC(tp, fork);
548 
549     ATF_TP_ADD_TC(tp, free_charpp__empty);
550     ATF_TP_ADD_TC(tp, free_charpp__some);
551 
552     ATF_TP_ADD_TC(tp, grep_file);
553     ATF_TP_ADD_TC(tp, grep_string);
554 
555     ATF_TP_ADD_TC(tp, readline__none);
556     ATF_TP_ADD_TC(tp, readline__some);
557 
558     ATF_TP_ADD_TC(tp, redirect__stdout);
559     ATF_TP_ADD_TC(tp, redirect__stderr);
560     ATF_TP_ADD_TC(tp, redirect__other);
561 
562     ATF_TP_ADD_TC(tp, wait__ok);
563     ATF_TP_ADD_TC(tp, wait__ok_nested);
564     ATF_TP_ADD_TC(tp, wait__save_stdout);
565     ATF_TP_ADD_TC(tp, wait__save_stderr);
566     ATF_TP_ADD_TC(tp, wait__invalid_exitstatus);
567     ATF_TP_ADD_TC(tp, wait__invalid_stdout);
568     ATF_TP_ADD_TC(tp, wait__invalid_stderr);
569 
570     return atf_no_error();
571 }
572