Home | History | Annotate | Line # | Download | only in atf-c
      1 /*
      2  * Automated Testing Framework (atf)
      3  *
      4  * Copyright (c) 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 "atf-c/utils.h"
     31 
     32 #include <sys/stat.h>
     33 #include <sys/wait.h>
     34 
     35 #include <err.h>
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 #include <regex.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <unistd.h>
     43 
     44 #include <atf-c.h>
     45 
     46 #include "detail/dynstr.h"
     47 
     48 /** Searches for a regexp in a string.
     49  *
     50  * \param regex The regexp to look for.
     51  * \param str The string in which to look for the expression.
     52  *
     53  * \return True if there is a match; false otherwise. */
     54 static
     55 bool
     56 grep_string(const char *regex, const char *str)
     57 {
     58     int res;
     59     regex_t preg;
     60 
     61     printf("Looking for '%s' in '%s'\n", regex, str);
     62     ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0);
     63 
     64     res = regexec(&preg, str, 0, NULL, 0);
     65     ATF_REQUIRE(res == 0 || res == REG_NOMATCH);
     66 
     67     regfree(&preg);
     68 
     69     return res == 0;
     70 }
     71 
     72 /** Prints the contents of a file to stdout.
     73  *
     74  * \param name The name of the file to be printed.
     75  * \param prefix An string to be prepended to every line of the printed
     76  *     file. */
     77 void
     78 atf_utils_cat_file(const char *name, const char *prefix)
     79 {
     80     const int fd = open(name, O_RDONLY);
     81     ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
     82 
     83     char buffer[1024];
     84     ssize_t count;
     85     bool continued = false;
     86     while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
     87         buffer[count] = '\0';
     88 
     89         if (!continued)
     90             printf("%s", prefix);
     91 
     92         char *iter = buffer;
     93         char *end;
     94         while ((end = strchr(iter, '\n')) != NULL) {
     95             *end = '\0';
     96             printf("%s\n", iter);
     97 
     98             iter = end + 1;
     99             if (iter != buffer + count)
    100                 printf("%s", prefix);
    101             else
    102                 continued = false;
    103         }
    104         if (iter < buffer + count) {
    105             printf("%s", iter);
    106             continued = true;
    107         }
    108     }
    109     ATF_REQUIRE(count == 0);
    110 }
    111 
    112 /** Compares a file against the given golden contents.
    113  *
    114  * \param name Name of the file to be compared.
    115  * \param contents Expected contents of the file.
    116  *
    117  * \return True if the file matches the contents; false otherwise. */
    118 bool
    119 atf_utils_compare_file(const char *name, const char *contents)
    120 {
    121     const int fd = open(name, O_RDONLY);
    122     ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
    123 
    124     const char *pos = contents;
    125     ssize_t remaining = strlen(contents);
    126 
    127     char buffer[1024];
    128     ssize_t count;
    129     while ((count = read(fd, buffer, sizeof(buffer))) > 0 &&
    130            count <= remaining) {
    131         if (memcmp(pos, buffer, count) != 0) {
    132             close(fd);
    133             return false;
    134         }
    135         remaining -= count;
    136         pos += count;
    137     }
    138     close(fd);
    139     return count == 0 && remaining == 0;
    140 }
    141 
    142 /** Copies a file.
    143  *
    144  * \param source Path to the source file.
    145  * \param destination Path to the destination file. */
    146 void
    147 atf_utils_copy_file(const char *source, const char *destination)
    148 {
    149     const int input = open(source, O_RDONLY);
    150     ATF_REQUIRE_MSG(input != -1, "Failed to open source file during "
    151                     "copy (%s)", source);
    152 
    153     const int output = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777);
    154     ATF_REQUIRE_MSG(output != -1, "Failed to open destination file during "
    155                     "copy (%s)", destination);
    156 
    157     char buffer[1024];
    158     ssize_t length;
    159     while ((length = read(input, buffer, sizeof(buffer))) > 0)
    160         ATF_REQUIRE_MSG(write(output, buffer, length) == length,
    161                         "Failed to write to %s during copy", destination);
    162     ATF_REQUIRE_MSG(length != -1, "Failed to read from %s during copy", source);
    163 
    164     struct stat sb;
    165     ATF_REQUIRE_MSG(fstat(input, &sb) != -1,
    166                     "Failed to stat source file %s during copy", source);
    167     ATF_REQUIRE_MSG(fchmod(output, sb.st_mode) != -1,
    168                     "Failed to chmod destination file %s during copy",
    169                     destination);
    170 
    171     close(output);
    172     close(input);
    173 }
    174 
    175 /** Creates a file.
    176  *
    177  * \param name Name of the file to create.
    178  * \param contents Text to write into the created file.
    179  * \param ... Positional parameters to the contents. */
    180 void
    181 atf_utils_create_file(const char *name, const char *contents, ...)
    182 {
    183     va_list ap;
    184     atf_dynstr_t formatted;
    185     atf_error_t error;
    186 
    187     va_start(ap, contents);
    188     error = atf_dynstr_init_ap(&formatted, contents, ap);
    189     va_end(ap);
    190     ATF_REQUIRE(!atf_is_error(error));
    191 
    192     const int fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    193     ATF_REQUIRE_MSG(fd != -1, "Cannot create file %s", name);
    194     ATF_REQUIRE(write(fd, atf_dynstr_cstring(&formatted),
    195                       atf_dynstr_length(&formatted)) != -1);
    196     close(fd);
    197 
    198     atf_dynstr_fini(&formatted);
    199 }
    200 
    201 /** Checks if a file exists.
    202  *
    203  * \param path Location of the file to check for.
    204  *
    205  * \return True if the file exists, false otherwise. */
    206 bool
    207 atf_utils_file_exists(const char *path)
    208 {
    209     const int ret = access(path, F_OK);
    210     if (ret == -1) {
    211         if (errno != ENOENT)
    212             atf_tc_fail("Failed to check the existence of %s: %s", path,
    213                         strerror(errno));
    214         else
    215             return false;
    216     } else
    217         return true;
    218 }
    219 
    220 /** Spawns a subprocess and redirects its output to files.
    221  *
    222  * Use the atf_utils_wait() function to wait for the completion of the spawned
    223  * subprocess and validate its exit conditions.
    224  *
    225  * \return 0 in the new child; the PID of the new child in the parent.  Does
    226  * not return in error conditions. */
    227 pid_t
    228 atf_utils_fork(void)
    229 {
    230     const pid_t pid = fork();
    231     if (pid == -1)
    232         atf_tc_fail("fork failed");
    233 
    234     if (pid == 0) {
    235         atf_utils_redirect(STDOUT_FILENO, "atf_utils_fork_out.txt");
    236         atf_utils_redirect(STDERR_FILENO, "atf_utils_fork_err.txt");
    237     }
    238     return pid;
    239 }
    240 
    241 /** Frees an dynamically-allocated "argv" array.
    242  *
    243  * \param argv A dynamically-allocated array of dynamically-allocated
    244  *     strings. */
    245 void
    246 atf_utils_free_charpp(char **argv)
    247 {
    248     char **ptr;
    249 
    250     for (ptr = argv; *ptr != NULL; ptr++)
    251         free(*ptr);
    252 
    253     free(argv);
    254 }
    255 
    256 /** Searches for a regexp in a file.
    257  *
    258  * \param regex The regexp to look for.
    259  * \param file The file in which to look for the expression.
    260  * \param ... Positional parameters to the regex.
    261  *
    262  * \return True if there is a match; false otherwise. */
    263 bool
    264 atf_utils_grep_file(const char *regex, const char *file, ...)
    265 {
    266     int fd;
    267     va_list ap;
    268     atf_dynstr_t formatted;
    269     atf_error_t error;
    270 
    271     va_start(ap, file);
    272     error = atf_dynstr_init_ap(&formatted, regex, ap);
    273     va_end(ap);
    274     ATF_REQUIRE(!atf_is_error(error));
    275 
    276     ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1);
    277     bool found = false;
    278     char *line = NULL;
    279     while (!found && (line = atf_utils_readline(fd)) != NULL) {
    280         found = grep_string(atf_dynstr_cstring(&formatted), line);
    281         free(line);
    282     }
    283     close(fd);
    284 
    285     atf_dynstr_fini(&formatted);
    286 
    287     return found;
    288 }
    289 
    290 /** Searches for a regexp in a string.
    291  *
    292  * \param regex The regexp to look for.
    293  * \param str The string in which to look for the expression.
    294  * \param ... Positional parameters to the regex.
    295  *
    296  * \return True if there is a match; false otherwise. */
    297 bool
    298 atf_utils_grep_string(const char *regex, const char *str, ...)
    299 {
    300     bool res;
    301     va_list ap;
    302     atf_dynstr_t formatted;
    303     atf_error_t error;
    304 
    305     va_start(ap, str);
    306     error = atf_dynstr_init_ap(&formatted, regex, ap);
    307     va_end(ap);
    308     ATF_REQUIRE(!atf_is_error(error));
    309 
    310     res = grep_string(atf_dynstr_cstring(&formatted), str);
    311 
    312     atf_dynstr_fini(&formatted);
    313 
    314     return res;
    315 }
    316 
    317 /** Reads a line of arbitrary length.
    318  *
    319  * \param fd The descriptor from which to read the line.
    320  *
    321  * \return A pointer to the read line, which must be released with free(), or
    322  * NULL if there was nothing to read from the file. */
    323 char *
    324 atf_utils_readline(const int fd)
    325 {
    326     char ch;
    327     ssize_t cnt;
    328     atf_dynstr_t temp;
    329     atf_error_t error;
    330 
    331     error = atf_dynstr_init(&temp);
    332     ATF_REQUIRE(!atf_is_error(error));
    333 
    334     while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) &&
    335            ch != '\n') {
    336         error = atf_dynstr_append_fmt(&temp, "%c", ch);
    337         ATF_REQUIRE(!atf_is_error(error));
    338     }
    339     ATF_REQUIRE(cnt != -1);
    340 
    341     if (cnt == 0 && atf_dynstr_length(&temp) == 0) {
    342         atf_dynstr_fini(&temp);
    343         return NULL;
    344     } else
    345         return atf_dynstr_fini_disown(&temp);
    346 }
    347 
    348 /** Redirects a file descriptor to a file.
    349  *
    350  * \param target_fd The file descriptor to be replaced.
    351  * \param name The name of the file to direct the descriptor to.
    352  *
    353  * \pre Should only be called from the process spawned by fork_for_testing
    354  * because this exits uncontrolledly.
    355  * \post Terminates execution if the redirection fails. */
    356 void
    357 atf_utils_redirect(const int target_fd, const char *name)
    358 {
    359     if (target_fd == STDOUT_FILENO)
    360         fflush(stdout);
    361     else if (target_fd == STDERR_FILENO)
    362         fflush(stderr);
    363 
    364     const int new_fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    365     if (new_fd == -1)
    366         err(EXIT_FAILURE, "Cannot create %s", name);
    367     if (new_fd != target_fd) {
    368         if (dup2(new_fd, target_fd) == -1)
    369             err(EXIT_FAILURE, "Cannot redirect to fd %d", target_fd);
    370     }
    371     close(new_fd);
    372 }
    373 
    374 /** Waits for a subprocess and validates its exit condition.
    375  *
    376  * \param pid The process to be waited for.  Must have been started by
    377  *     testutils_fork().
    378  * \param exitstatus Expected exit status.
    379  * \param expout Expected contents of stdout.
    380  * \param experr Expected contents of stderr. */
    381 void
    382 atf_utils_wait(const pid_t pid, const int exitstatus, const char *expout,
    383                const char *experr)
    384 {
    385     int status;
    386     ATF_REQUIRE(waitpid(pid, &status, 0) != -1);
    387 
    388     atf_utils_cat_file("atf_utils_fork_out.txt", "subprocess stdout: ");
    389     atf_utils_cat_file("atf_utils_fork_err.txt", "subprocess stderr: ");
    390 
    391     ATF_REQUIRE(WIFEXITED(status));
    392     ATF_REQUIRE_EQ(exitstatus, WEXITSTATUS(status));
    393 
    394     const char *save_prefix = "save:";
    395     const size_t save_prefix_length = strlen(save_prefix);
    396 
    397     if (strlen(expout) > save_prefix_length &&
    398         strncmp(expout, save_prefix, save_prefix_length) == 0) {
    399         atf_utils_copy_file("atf_utils_fork_out.txt",
    400                             expout + save_prefix_length);
    401     } else {
    402         ATF_REQUIRE(atf_utils_compare_file("atf_utils_fork_out.txt", expout));
    403     }
    404 
    405     if (strlen(experr) > save_prefix_length &&
    406         strncmp(experr, save_prefix, save_prefix_length) == 0) {
    407         atf_utils_copy_file("atf_utils_fork_err.txt",
    408                             experr + save_prefix_length);
    409     } else {
    410         ATF_REQUIRE(atf_utils_compare_file("atf_utils_fork_err.txt", experr));
    411     }
    412 
    413     ATF_REQUIRE(unlink("atf_utils_fork_out.txt") != -1);
    414     ATF_REQUIRE(unlink("atf_utils_fork_err.txt") != -1);
    415 }
    416