1/*
2 * Automated Testing Framework (atf)
3 *
4 * Copyright (c) 2008, 2009, 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <fcntl.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <regex.h>
34#include <unistd.h>
35
36#include "atf-c/build.h"
37#include "atf-c/check.h"
38#include "atf-c/config.h"
39#include "atf-c/error.h"
40#include "atf-c/macros.h"
41
42#include "dynstr.h"
43#include "fs.h"
44#include "process.h"
45#include "test_helpers.h"
46
47static
48void
49build_check_c_o_aux(const char *path, const char *failmsg)
50{
51    bool success;
52    atf_dynstr_t iflag;
53    const char *optargs[2];
54
55    RE(atf_dynstr_init_fmt(&iflag, "-I%s", atf_config_get("atf_includedir")));
56
57    optargs[0] = atf_dynstr_cstring(&iflag);
58    optargs[1] = NULL;
59
60    RE(atf_check_build_c_o(path, "test.o", optargs, &success));
61
62    atf_dynstr_fini(&iflag);
63
64    if (!success)
65        atf_tc_fail(failmsg);
66}
67
68void
69build_check_c_o(const atf_tc_t *tc, const char *sfile, const char *failmsg)
70{
71    atf_fs_path_t path;
72
73    RE(atf_fs_path_init_fmt(&path, "%s/%s",
74                            atf_tc_get_config_var(tc, "srcdir"), sfile));
75    build_check_c_o_aux(atf_fs_path_cstring(&path), failmsg);
76    atf_fs_path_fini(&path);
77}
78
79void
80header_check(const atf_tc_t *tc, const char *hdrname)
81{
82    FILE *srcfile;
83    char failmsg[128];
84
85    srcfile = fopen("test.c", "w");
86    ATF_REQUIRE(srcfile != NULL);
87    fprintf(srcfile, "#include <%s>\n", hdrname);
88    fclose(srcfile);
89
90    snprintf(failmsg, sizeof(failmsg),
91             "Header check failed; %s is not self-contained", hdrname);
92
93    build_check_c_o_aux("test.c", failmsg);
94}
95
96void
97get_process_helpers_path(const atf_tc_t *tc, const bool is_detail,
98                         atf_fs_path_t *path)
99{
100    RE(atf_fs_path_init_fmt(path, "%s/%sprocess_helpers",
101                            atf_tc_get_config_var(tc, "srcdir"),
102                            is_detail ? "" : "detail/"));
103}
104
105bool
106grep_string(const atf_dynstr_t *str, const char *regex)
107{
108    int res;
109    regex_t preg;
110
111    printf("Looking for '%s' in '%s'\n", regex, atf_dynstr_cstring(str));
112    ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0);
113
114    res = regexec(&preg, atf_dynstr_cstring(str), 0, NULL, 0);
115    ATF_REQUIRE(res == 0 || res == REG_NOMATCH);
116
117    regfree(&preg);
118
119    return res == 0;
120}
121
122bool
123grep_file(const char *file, const char *regex, ...)
124{
125    bool done, found;
126    int fd;
127    va_list ap;
128    atf_dynstr_t formatted;
129
130    va_start(ap, regex);
131    RE(atf_dynstr_init_ap(&formatted, regex, ap));
132    va_end(ap);
133
134    done = false;
135    found = false;
136    ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1);
137    do {
138        atf_dynstr_t line;
139
140        RE(atf_dynstr_init(&line));
141
142        done = read_line(fd, &line);
143        if (!done)
144            found = grep_string(&line, atf_dynstr_cstring(&formatted));
145
146        atf_dynstr_fini(&line);
147    } while (!found && !done);
148    close(fd);
149
150    atf_dynstr_fini(&formatted);
151
152    return found;
153}
154
155bool
156read_line(int fd, atf_dynstr_t *dest)
157{
158    char ch;
159    ssize_t cnt;
160
161    while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) &&
162           ch != '\n') {
163        const atf_error_t err = atf_dynstr_append_fmt(dest, "%c", ch);
164        ATF_REQUIRE(!atf_is_error(err));
165    }
166    ATF_REQUIRE(cnt != -1);
167
168    return cnt == 0;
169}
170
171struct run_h_tc_data {
172    atf_tc_t *m_tc;
173    const char *m_resname;
174};
175
176static
177void
178run_h_tc_child(void *v)
179{
180    struct run_h_tc_data *data = (struct run_h_tc_data *)v;
181
182    RE(atf_tc_run(data->m_tc, data->m_resname));
183}
184
185/* TODO: Investigate if it's worth to add this functionality as part of
186 * the public API.  I.e. a function to easily run a test case body in a
187 * subprocess. */
188void
189run_h_tc(atf_tc_t *tc, const char *outname, const char *errname,
190         const char *resname)
191{
192    atf_fs_path_t outpath, errpath;
193    atf_process_stream_t outb, errb;
194    atf_process_child_t child;
195    atf_process_status_t status;
196
197    RE(atf_fs_path_init_fmt(&outpath, outname));
198    RE(atf_fs_path_init_fmt(&errpath, errname));
199
200    struct run_h_tc_data data = { tc, resname };
201
202    RE(atf_process_stream_init_redirect_path(&outb, &outpath));
203    RE(atf_process_stream_init_redirect_path(&errb, &errpath));
204    RE(atf_process_fork(&child, run_h_tc_child, &outb, &errb, &data));
205    atf_process_stream_fini(&errb);
206    atf_process_stream_fini(&outb);
207
208    RE(atf_process_child_wait(&child, &status));
209    ATF_CHECK(atf_process_status_exited(&status));
210    atf_process_status_fini(&status);
211
212    atf_fs_path_fini(&errpath);
213    atf_fs_path_fini(&outpath);
214}
215