1 /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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. */
26 #include "atf-c/utils.h"
42 #include "atf-c/detail/dynstr.h"
44 /* No prototype in header for this one, it's a little sketchy (internal). */
45 void atf_tc_set_resultsfile(const char *);
47 /** Allocate a filename to be used by atf_utils_{fork,wait}.
49 * In case of a failure, marks the calling test as failed when in_parent is
50 * true, else terminates execution.
52 * \param [out] name String to contain the generated file.
53 * \param pid PID of the process that will write to the file.
54 * \param suffix Either "out" or "err".
55 * \param in_parent If true, fail with atf_tc_fail; else use err(3). */
57 init_out_filename(atf_dynstr_t *name, const pid_t pid, const char *suffix,
62 error = atf_dynstr_init_fmt(name, "atf_utils_fork_%d_%s.txt",
64 if (atf_is_error(error)) {
66 atf_error_format(error, buffer, sizeof(buffer));
68 atf_tc_fail("Failed to create output file: %s", buffer);
70 err(EXIT_FAILURE, "Failed to create output file: %s", buffer);
75 /** Searches for a regexp in a string.
77 * \param regex The regexp to look for.
78 * \param str The string in which to look for the expression.
80 * \return True if there is a match; false otherwise. */
83 grep_string(const char *regex, const char *str)
88 printf("Looking for '%s' in '%s'\n", regex, str);
89 ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0);
91 res = regexec(&preg, str, 0, NULL, 0);
92 ATF_REQUIRE(res == 0 || res == REG_NOMATCH);
99 /** Prints the contents of a file to stdout.
101 * \param name The name of the file to be printed.
102 * \param prefix An string to be prepended to every line of the printed
105 atf_utils_cat_file(const char *name, const char *prefix)
107 const int fd = open(name, O_RDONLY);
108 ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
112 bool continued = false;
113 while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
114 buffer[count] = '\0';
117 printf("%s", prefix);
121 while ((end = strchr(iter, '\n')) != NULL) {
123 printf("%s\n", iter);
126 if (iter != buffer + count)
127 printf("%s", prefix);
131 if (iter < buffer + count) {
136 ATF_REQUIRE(count == 0);
139 /** Compares a file against the given golden contents.
141 * \param name Name of the file to be compared.
142 * \param contents Expected contents of the file.
144 * \return True if the file matches the contents; false otherwise. */
146 atf_utils_compare_file(const char *name, const char *contents)
148 const int fd = open(name, O_RDONLY);
149 ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
151 const char *pos = contents;
152 ssize_t remaining = strlen(contents);
156 while ((count = read(fd, buffer, sizeof(buffer))) > 0 &&
157 count <= remaining) {
158 if (memcmp(pos, buffer, count) != 0) {
166 return count == 0 && remaining == 0;
171 * \param source Path to the source file.
172 * \param destination Path to the destination file. */
174 atf_utils_copy_file(const char *source, const char *destination)
176 const int input = open(source, O_RDONLY);
177 ATF_REQUIRE_MSG(input != -1, "Failed to open source file during "
178 "copy (%s)", source);
180 const int output = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777);
181 ATF_REQUIRE_MSG(output != -1, "Failed to open destination file during "
182 "copy (%s)", destination);
186 while ((length = read(input, buffer, sizeof(buffer))) > 0)
187 ATF_REQUIRE_MSG(write(output, buffer, length) == length,
188 "Failed to write to %s during copy", destination);
189 ATF_REQUIRE_MSG(length != -1, "Failed to read from %s during copy", source);
192 ATF_REQUIRE_MSG(fstat(input, &sb) != -1,
193 "Failed to stat source file %s during copy", source);
194 ATF_REQUIRE_MSG(fchmod(output, sb.st_mode) != -1,
195 "Failed to chmod destination file %s during copy",
204 * \param name Name of the file to create.
205 * \param contents Text to write into the created file.
206 * \param ... Positional parameters to the contents. */
208 atf_utils_create_file(const char *name, const char *contents, ...)
211 atf_dynstr_t formatted;
214 va_start(ap, contents);
215 error = atf_dynstr_init_ap(&formatted, contents, ap);
217 ATF_REQUIRE(!atf_is_error(error));
219 const int fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
220 ATF_REQUIRE_MSG(fd != -1, "Cannot create file %s", name);
221 ATF_REQUIRE(write(fd, atf_dynstr_cstring(&formatted),
222 atf_dynstr_length(&formatted)) != -1);
225 atf_dynstr_fini(&formatted);
228 /** Checks if a file exists.
230 * \param path Location of the file to check for.
232 * \return True if the file exists, false otherwise. */
234 atf_utils_file_exists(const char *path)
236 const int ret = access(path, F_OK);
239 atf_tc_fail("Failed to check the existence of %s: %s", path,
247 /** Spawns a subprocess and redirects its output to files.
249 * Use the atf_utils_wait() function to wait for the completion of the spawned
250 * subprocess and validate its exit conditions.
252 * \return 0 in the new child; the PID of the new child in the parent. Does
253 * not return in error conditions. */
257 const pid_t pid = fork();
259 atf_tc_fail("fork failed");
262 atf_dynstr_t out_name;
263 init_out_filename(&out_name, getpid(), "out", false);
265 atf_dynstr_t err_name;
266 init_out_filename(&err_name, getpid(), "err", false);
268 atf_utils_redirect(STDOUT_FILENO, atf_dynstr_cstring(&out_name));
269 atf_utils_redirect(STDERR_FILENO, atf_dynstr_cstring(&err_name));
271 atf_dynstr_fini(&err_name);
272 atf_dynstr_fini(&out_name);
278 atf_utils_reset_resultsfile(void)
281 atf_tc_set_resultsfile("/dev/null");
284 /** Frees an dynamically-allocated "argv" array.
286 * \param argv A dynamically-allocated array of dynamically-allocated
289 atf_utils_free_charpp(char **argv)
293 for (ptr = argv; *ptr != NULL; ptr++)
299 /** Searches for a regexp in a file.
301 * \param regex The regexp to look for.
302 * \param file The file in which to look for the expression.
303 * \param ... Positional parameters to the regex.
305 * \return True if there is a match; false otherwise. */
307 atf_utils_grep_file(const char *regex, const char *file, ...)
311 atf_dynstr_t formatted;
315 error = atf_dynstr_init_ap(&formatted, regex, ap);
317 ATF_REQUIRE(!atf_is_error(error));
319 ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1);
322 while (!found && (line = atf_utils_readline(fd)) != NULL) {
323 found = grep_string(atf_dynstr_cstring(&formatted), line);
328 atf_dynstr_fini(&formatted);
333 /** Searches for a regexp in a string.
335 * \param regex The regexp to look for.
336 * \param str The string in which to look for the expression.
337 * \param ... Positional parameters to the regex.
339 * \return True if there is a match; false otherwise. */
341 atf_utils_grep_string(const char *regex, const char *str, ...)
345 atf_dynstr_t formatted;
349 error = atf_dynstr_init_ap(&formatted, regex, ap);
351 ATF_REQUIRE(!atf_is_error(error));
353 res = grep_string(atf_dynstr_cstring(&formatted), str);
355 atf_dynstr_fini(&formatted);
360 /** Reads a line of arbitrary length.
362 * \param fd The descriptor from which to read the line.
364 * \return A pointer to the read line, which must be released with free(), or
365 * NULL if there was nothing to read from the file. */
367 atf_utils_readline(const int fd)
374 error = atf_dynstr_init(&temp);
375 ATF_REQUIRE(!atf_is_error(error));
377 while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) &&
379 error = atf_dynstr_append_fmt(&temp, "%c", ch);
380 ATF_REQUIRE(!atf_is_error(error));
382 ATF_REQUIRE(cnt != -1);
384 if (cnt == 0 && atf_dynstr_length(&temp) == 0) {
385 atf_dynstr_fini(&temp);
388 return atf_dynstr_fini_disown(&temp);
391 /** Redirects a file descriptor to a file.
393 * \param target_fd The file descriptor to be replaced.
394 * \param name The name of the file to direct the descriptor to.
396 * \pre Should only be called from the process spawned by fork_for_testing
397 * because this exits uncontrolledly.
398 * \post Terminates execution if the redirection fails. */
400 atf_utils_redirect(const int target_fd, const char *name)
402 if (target_fd == STDOUT_FILENO)
404 else if (target_fd == STDERR_FILENO)
407 const int new_fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
409 err(EXIT_FAILURE, "Cannot create %s", name);
410 if (new_fd != target_fd) {
411 if (dup2(new_fd, target_fd) == -1)
412 err(EXIT_FAILURE, "Cannot redirect to fd %d", target_fd);
417 /** Waits for a subprocess and validates its exit condition.
419 * \param pid The process to be waited for. Must have been started by
421 * \param exitstatus Expected exit status.
422 * \param expout Expected contents of stdout.
423 * \param experr Expected contents of stderr. */
425 atf_utils_wait(const pid_t pid, const int exitstatus, const char *expout,
429 ATF_REQUIRE(waitpid(pid, &status, 0) != -1);
431 atf_dynstr_t out_name;
432 init_out_filename(&out_name, pid, "out", true);
434 atf_dynstr_t err_name;
435 init_out_filename(&err_name, pid, "err", true);
437 atf_utils_cat_file(atf_dynstr_cstring(&out_name), "subprocess stdout: ");
438 atf_utils_cat_file(atf_dynstr_cstring(&err_name), "subprocess stderr: ");
440 ATF_REQUIRE(WIFEXITED(status));
441 ATF_REQUIRE_EQ(exitstatus, WEXITSTATUS(status));
443 const char *save_prefix = "save:";
444 const size_t save_prefix_length = strlen(save_prefix);
446 if (strlen(expout) > save_prefix_length &&
447 strncmp(expout, save_prefix, save_prefix_length) == 0) {
448 atf_utils_copy_file(atf_dynstr_cstring(&out_name),
449 expout + save_prefix_length);
451 ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&out_name),
455 if (strlen(experr) > save_prefix_length &&
456 strncmp(experr, save_prefix, save_prefix_length) == 0) {
457 atf_utils_copy_file(atf_dynstr_cstring(&err_name),
458 experr + save_prefix_length);
460 ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&err_name),
464 ATF_REQUIRE(unlink(atf_dynstr_cstring(&out_name)) != -1);
465 ATF_REQUIRE(unlink(atf_dynstr_cstring(&err_name)) != -1);