utils_test.c revision 275988
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. */
55static ssize_t
56read_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
67ATF_TC_WITHOUT_HEAD(cat_file__empty);
68ATF_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
81ATF_TC_WITHOUT_HEAD(cat_file__one_line);
82ATF_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
95ATF_TC_WITHOUT_HEAD(cat_file__several_lines);
96ATF_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
109ATF_TC_WITHOUT_HEAD(cat_file__no_newline_eof);
110ATF_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
123ATF_TC_WITHOUT_HEAD(compare_file__empty__match);
124ATF_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
130ATF_TC_WITHOUT_HEAD(compare_file__empty__not_match);
131ATF_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
139ATF_TC_WITHOUT_HEAD(compare_file__short__match);
140ATF_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
146ATF_TC_WITHOUT_HEAD(compare_file__short__not_match);
147ATF_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
157ATF_TC_WITHOUT_HEAD(compare_file__long__match);
158ATF_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
170ATF_TC_WITHOUT_HEAD(compare_file__long__not_match);
171ATF_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
187ATF_TC_WITHOUT_HEAD(copy_file__empty);
188ATF_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
200ATF_TC_WITHOUT_HEAD(copy_file__some_contents);
201ATF_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
208ATF_TC_WITHOUT_HEAD(create_file);
209ATF_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
218ATF_TC_WITHOUT_HEAD(file_exists);
219ATF_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
229ATF_TC_WITHOUT_HEAD(fork);
230ATF_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
261ATF_TC_WITHOUT_HEAD(free_charpp__empty);
262ATF_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
270ATF_TC_WITHOUT_HEAD(free_charpp__some);
271ATF_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
282ATF_TC_WITHOUT_HEAD(grep_file);
283ATF_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
296ATF_TC_WITHOUT_HEAD(grep_string);
297ATF_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
309ATF_TC_WITHOUT_HEAD(readline__none);
310ATF_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
320ATF_TC_WITHOUT_HEAD(readline__some);
321ATF_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
349ATF_TC_WITHOUT_HEAD(redirect__stdout);
350ATF_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
362ATF_TC_WITHOUT_HEAD(redirect__stderr);
363ATF_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
375ATF_TC_WITHOUT_HEAD(redirect__other);
376ATF_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
388static void
389fork_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_wait(pid, exitstatus, expout, experr);
399    exit(EXIT_SUCCESS);
400}
401
402ATF_TC_WITHOUT_HEAD(wait__ok);
403ATF_TC_BODY(wait__ok, tc)
404{
405    const pid_t control = fork();
406    ATF_REQUIRE(control != -1);
407    if (control == 0)
408        fork_and_wait(123, "Some output\n", "Some error\n");
409    else {
410        int status;
411        ATF_REQUIRE(waitpid(control, &status, 0) != -1);
412        ATF_REQUIRE(WIFEXITED(status));
413        ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
414    }
415}
416
417ATF_TC_WITHOUT_HEAD(wait__ok_nested);
418ATF_TC_BODY(wait__ok_nested, tc)
419{
420    const pid_t parent = atf_utils_fork();
421    ATF_REQUIRE(parent != -1);
422    if (parent == 0) {
423        const pid_t child = atf_utils_fork();
424        ATF_REQUIRE(child != -1);
425        if (child == 0) {
426            fflush(stderr);
427            fprintf(stdout, "Child output\n");
428            fflush(stdout);
429            fprintf(stderr, "Child error\n");
430            exit(50);
431        } else {
432            fprintf(stdout, "Parent output\n");
433            fprintf(stderr, "Parent error\n");
434            atf_utils_wait(child, 50, "Child output\n", "Child error\n");
435            exit(40);
436        }
437    } else {
438        atf_utils_wait(parent, 40,
439                       "Parent output\n"
440                       "subprocess stdout: Child output\n"
441                       "subprocess stderr: Child error\n",
442                       "Parent error\n");
443    }
444}
445
446ATF_TC_WITHOUT_HEAD(wait__invalid_exitstatus);
447ATF_TC_BODY(wait__invalid_exitstatus, tc)
448{
449    const pid_t control = fork();
450    ATF_REQUIRE(control != -1);
451    if (control == 0)
452        fork_and_wait(120, "Some output\n", "Some error\n");
453    else {
454        int status;
455        ATF_REQUIRE(waitpid(control, &status, 0) != -1);
456        ATF_REQUIRE(WIFEXITED(status));
457        ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
458    }
459}
460
461ATF_TC_WITHOUT_HEAD(wait__invalid_stdout);
462ATF_TC_BODY(wait__invalid_stdout, tc)
463{
464    const pid_t control = fork();
465    ATF_REQUIRE(control != -1);
466    if (control == 0)
467        fork_and_wait(123, "Some output foo\n", "Some error\n");
468    else {
469        int status;
470        ATF_REQUIRE(waitpid(control, &status, 0) != -1);
471        ATF_REQUIRE(WIFEXITED(status));
472        ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
473    }
474}
475
476ATF_TC_WITHOUT_HEAD(wait__invalid_stderr);
477ATF_TC_BODY(wait__invalid_stderr, tc)
478{
479    const pid_t control = fork();
480    ATF_REQUIRE(control != -1);
481    if (control == 0)
482        fork_and_wait(123, "Some output\n", "Some error foo\n");
483    else {
484        int status;
485        ATF_REQUIRE(waitpid(control, &status, 0) != -1);
486        ATF_REQUIRE(WIFEXITED(status));
487        ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
488    }
489}
490
491ATF_TC_WITHOUT_HEAD(wait__save_stdout);
492ATF_TC_BODY(wait__save_stdout, tc)
493{
494    const pid_t control = fork();
495    ATF_REQUIRE(control != -1);
496    if (control == 0)
497        fork_and_wait(123, "save:my-output.txt", "Some error\n");
498    else {
499        int status;
500        ATF_REQUIRE(waitpid(control, &status, 0) != -1);
501        ATF_REQUIRE(WIFEXITED(status));
502        ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
503
504        ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some output\n"));
505    }
506}
507
508ATF_TC_WITHOUT_HEAD(wait__save_stderr);
509ATF_TC_BODY(wait__save_stderr, tc)
510{
511    const pid_t control = fork();
512    ATF_REQUIRE(control != -1);
513    if (control == 0)
514        fork_and_wait(123, "Some output\n", "save:my-output.txt");
515    else {
516        int status;
517        ATF_REQUIRE(waitpid(control, &status, 0) != -1);
518        ATF_REQUIRE(WIFEXITED(status));
519        ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
520
521        ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some error\n"));
522    }
523}
524
525ATF_TP_ADD_TCS(tp)
526{
527    ATF_TP_ADD_TC(tp, cat_file__empty);
528    ATF_TP_ADD_TC(tp, cat_file__one_line);
529    ATF_TP_ADD_TC(tp, cat_file__several_lines);
530    ATF_TP_ADD_TC(tp, cat_file__no_newline_eof);
531
532    ATF_TP_ADD_TC(tp, compare_file__empty__match);
533    ATF_TP_ADD_TC(tp, compare_file__empty__not_match);
534    ATF_TP_ADD_TC(tp, compare_file__short__match);
535    ATF_TP_ADD_TC(tp, compare_file__short__not_match);
536    ATF_TP_ADD_TC(tp, compare_file__long__match);
537    ATF_TP_ADD_TC(tp, compare_file__long__not_match);
538
539    ATF_TP_ADD_TC(tp, copy_file__empty);
540    ATF_TP_ADD_TC(tp, copy_file__some_contents);
541
542    ATF_TP_ADD_TC(tp, create_file);
543
544    ATF_TP_ADD_TC(tp, file_exists);
545
546    ATF_TP_ADD_TC(tp, fork);
547
548    ATF_TP_ADD_TC(tp, free_charpp__empty);
549    ATF_TP_ADD_TC(tp, free_charpp__some);
550
551    ATF_TP_ADD_TC(tp, grep_file);
552    ATF_TP_ADD_TC(tp, grep_string);
553
554    ATF_TP_ADD_TC(tp, readline__none);
555    ATF_TP_ADD_TC(tp, readline__some);
556
557    ATF_TP_ADD_TC(tp, redirect__stdout);
558    ATF_TP_ADD_TC(tp, redirect__stderr);
559    ATF_TP_ADD_TC(tp, redirect__other);
560
561    ATF_TP_ADD_TC(tp, wait__ok);
562    ATF_TP_ADD_TC(tp, wait__ok_nested);
563    ATF_TP_ADD_TC(tp, wait__save_stdout);
564    ATF_TP_ADD_TC(tp, wait__save_stderr);
565    ATF_TP_ADD_TC(tp, wait__invalid_exitstatus);
566    ATF_TP_ADD_TC(tp, wait__invalid_stdout);
567    ATF_TP_ADD_TC(tp, wait__invalid_stderr);
568
569    return atf_no_error();
570}
571