Home | History | Annotate | Line # | Download | only in atf-c
tc.c revision 1.1.1.3
      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 <sys/types.h>
     31 #include <sys/time.h>
     32 #include <sys/stat.h>
     33 #include <sys/wait.h>
     34 
     35 #include <errno.h>
     36 #include <fcntl.h>
     37 #include <limits.h>
     38 #include <stdarg.h>
     39 #include <stdbool.h>
     40 #include <stdio.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 #include <unistd.h>
     44 
     45 #include "atf-c/config.h"
     46 #include "atf-c/defs.h"
     47 #include "atf-c/env.h"
     48 #include "atf-c/error.h"
     49 #include "atf-c/fs.h"
     50 #include "atf-c/process.h"
     51 #include "atf-c/sanity.h"
     52 #include "atf-c/tc.h"
     53 #include "atf-c/tcr.h"
     54 #include "atf-c/text.h"
     55 #include "atf-c/user.h"
     56 
     57 /* ---------------------------------------------------------------------
     58  * Auxiliary types and functions.
     59  * --------------------------------------------------------------------- */
     60 
     61 static atf_error_t check_prog(const char *, void *);
     62 static atf_error_t check_prog_in_dir(const char *, void *);
     63 static void fail_internal(const char *, int, const char *, const char *,
     64                           const char *, va_list,
     65                           void (*)(atf_dynstr_t *),
     66                           void (*)(const char *, ...));
     67 static void tc_fail(atf_dynstr_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
     68 static void tc_fail_nonfatal(atf_dynstr_t *);
     69 
     70 /* ---------------------------------------------------------------------
     71  * The "atf_tc" type.
     72  * --------------------------------------------------------------------- */
     73 
     74 /*
     75  * Constructors/destructors.
     76  */
     77 
     78 atf_error_t
     79 atf_tc_init(atf_tc_t *tc, const char *ident, atf_tc_head_t head,
     80             atf_tc_body_t body, atf_tc_cleanup_t cleanup,
     81             const atf_map_t *config)
     82 {
     83     atf_error_t err;
     84 
     85     atf_object_init(&tc->m_object);
     86 
     87     tc->m_ident = ident;
     88     tc->m_head = head;
     89     tc->m_body = body;
     90     tc->m_cleanup = cleanup;
     91     tc->m_config = config;
     92 
     93     err = atf_map_init(&tc->m_vars);
     94     if (atf_is_error(err))
     95         goto err_object;
     96 
     97     err = atf_tc_set_md_var(tc, "ident", ident);
     98     if (atf_is_error(err))
     99         goto err_map;
    100 
    101     /* XXX Should the head be able to return error codes? */
    102     tc->m_head(tc);
    103 
    104     if (strcmp(atf_tc_get_md_var(tc, "ident"), ident) != 0)
    105         atf_tc_fail("Test case head modified the read-only 'ident' "
    106                     "property");
    107 
    108     INV(!atf_is_error(err));
    109     return err;
    110 
    111 err_map:
    112     atf_map_fini(&tc->m_vars);
    113 err_object:
    114     atf_object_fini(&tc->m_object);
    115 
    116     return err;
    117 }
    118 
    119 atf_error_t
    120 atf_tc_init_pack(atf_tc_t *tc, const atf_tc_pack_t *pack,
    121                  const atf_map_t *config)
    122 {
    123     return atf_tc_init(tc, pack->m_ident, pack->m_head, pack->m_body,
    124                        pack->m_cleanup, config);
    125 }
    126 
    127 void
    128 atf_tc_fini(atf_tc_t *tc)
    129 {
    130     atf_map_fini(&tc->m_vars);
    131 
    132     atf_object_fini(&tc->m_object);
    133 }
    134 
    135 /*
    136  * Getters.
    137  */
    138 
    139 const char *
    140 atf_tc_get_ident(const atf_tc_t *tc)
    141 {
    142     return tc->m_ident;
    143 }
    144 
    145 const char *
    146 atf_tc_get_config_var(const atf_tc_t *tc, const char *name)
    147 {
    148     const char *val;
    149     atf_map_citer_t iter;
    150 
    151     PRE(atf_tc_has_config_var(tc, name));
    152     iter = atf_map_find_c(tc->m_config, name);
    153     val = atf_map_citer_data(iter);
    154     INV(val != NULL);
    155 
    156     return val;
    157 }
    158 
    159 const char *
    160 atf_tc_get_config_var_wd(const atf_tc_t *tc, const char *name,
    161                          const char *defval)
    162 {
    163     const char *val;
    164 
    165     if (!atf_tc_has_config_var(tc, name))
    166         val = defval;
    167     else
    168         val = atf_tc_get_config_var(tc, name);
    169 
    170     return val;
    171 }
    172 
    173 const char *
    174 atf_tc_get_md_var(const atf_tc_t *tc, const char *name)
    175 {
    176     const char *val;
    177     atf_map_citer_t iter;
    178 
    179     PRE(atf_tc_has_md_var(tc, name));
    180     iter = atf_map_find_c(&tc->m_vars, name);
    181     val = atf_map_citer_data(iter);
    182     INV(val != NULL);
    183 
    184     return val;
    185 }
    186 
    187 const atf_map_t *
    188 atf_tc_get_md_vars(const atf_tc_t *tc)
    189 {
    190     return &tc->m_vars;
    191 }
    192 
    193 bool
    194 atf_tc_has_config_var(const atf_tc_t *tc, const char *name)
    195 {
    196     bool found;
    197     atf_map_citer_t end, iter;
    198 
    199     if (tc->m_config == NULL)
    200         found = false;
    201     else {
    202         iter = atf_map_find_c(tc->m_config, name);
    203         end = atf_map_end_c(tc->m_config);
    204         found = !atf_equal_map_citer_map_citer(iter, end);
    205     }
    206 
    207     return found;
    208 }
    209 
    210 bool
    211 atf_tc_has_md_var(const atf_tc_t *tc, const char *name)
    212 {
    213     atf_map_citer_t end, iter;
    214 
    215     iter = atf_map_find_c(&tc->m_vars, name);
    216     end = atf_map_end_c(&tc->m_vars);
    217     return !atf_equal_map_citer_map_citer(iter, end);
    218 }
    219 
    220 /*
    221  * Modifiers.
    222  */
    223 
    224 atf_error_t
    225 atf_tc_set_md_var(atf_tc_t *tc, const char *name, const char *fmt, ...)
    226 {
    227     atf_error_t err;
    228     char *value;
    229     va_list ap;
    230 
    231     va_start(ap, fmt);
    232     err = atf_text_format_ap(&value, fmt, ap);
    233     va_end(ap);
    234 
    235     if (!atf_is_error(err))
    236         err = atf_map_insert(&tc->m_vars, name, value, true);
    237     else
    238         free(value);
    239 
    240     return err;
    241 }
    242 
    243 /* ---------------------------------------------------------------------
    244  * Free functions.
    245  * --------------------------------------------------------------------- */
    246 
    247 static const atf_tc_t *current_tc = NULL;
    248 static const atf_fs_path_t *current_resfile = NULL;
    249 static size_t current_tc_fail_count = 0;
    250 
    251 atf_error_t
    252 atf_tc_run(const atf_tc_t *tc, const atf_fs_path_t *resfile)
    253 {
    254     atf_reset_exit_checks(); /* XXX */
    255 
    256     current_tc = tc;
    257     current_resfile = resfile;
    258     current_tc_fail_count = 0;
    259 
    260     tc->m_body(tc);
    261 
    262     if (current_tc_fail_count == 0)
    263         atf_tc_pass();
    264     else
    265         atf_tc_fail("%d checks failed; see output for more details",
    266                     current_tc_fail_count);
    267 
    268     current_tc = NULL;
    269     current_resfile = NULL;
    270     current_tc_fail_count = 0;
    271 
    272     return atf_no_error();
    273 }
    274 
    275 atf_error_t
    276 atf_tc_cleanup(const atf_tc_t *tc)
    277 {
    278     if (tc->m_cleanup != NULL)
    279         tc->m_cleanup(tc);
    280     return atf_no_error(); /* XXX */
    281 }
    282 
    283 struct prog_found_pair {
    284     const char *prog;
    285     bool found;
    286 };
    287 
    288 static
    289 atf_error_t
    290 check_prog(const char *prog, void *data)
    291 {
    292     atf_error_t err;
    293     atf_fs_path_t p;
    294 
    295     err = atf_fs_path_init_fmt(&p, "%s", prog);
    296     if (atf_is_error(err))
    297         goto out;
    298 
    299     if (atf_fs_path_is_absolute(&p)) {
    300         err = atf_fs_eaccess(&p, atf_fs_access_x);
    301         if (atf_is_error(err)) {
    302             atf_error_free(err);
    303             atf_fs_path_fini(&p);
    304             atf_tc_skip("The required program %s could not be found", prog);
    305         }
    306     } else {
    307         const char *path = atf_env_get("PATH");
    308         struct prog_found_pair pf;
    309         atf_fs_path_t bp;
    310 
    311         err = atf_fs_path_branch_path(&p, &bp);
    312         if (atf_is_error(err))
    313             goto out_p;
    314 
    315         if (strcmp(atf_fs_path_cstring(&bp), ".") != 0) {
    316             atf_fs_path_fini(&bp);
    317             atf_fs_path_fini(&p);
    318             atf_tc_fail("Relative paths are not allowed when searching for "
    319                         "a program (%s)", prog);
    320         }
    321 
    322         pf.prog = prog;
    323         pf.found = false;
    324         err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf);
    325         if (atf_is_error(err))
    326             goto out_bp;
    327 
    328         if (!pf.found) {
    329             atf_fs_path_fini(&bp);
    330             atf_fs_path_fini(&p);
    331             atf_tc_skip("The required program %s could not be found in "
    332                         "the PATH", prog);
    333         }
    334 
    335 out_bp:
    336         atf_fs_path_fini(&bp);
    337     }
    338 
    339 out_p:
    340     atf_fs_path_fini(&p);
    341 out:
    342     return err;
    343 }
    344 
    345 static
    346 atf_error_t
    347 check_prog_in_dir(const char *dir, void *data)
    348 {
    349     struct prog_found_pair *pf = data;
    350     atf_error_t err;
    351 
    352     if (pf->found)
    353         err = atf_no_error();
    354     else {
    355         atf_fs_path_t p;
    356 
    357         err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog);
    358         if (atf_is_error(err))
    359             goto out_p;
    360 
    361         err = atf_fs_eaccess(&p, atf_fs_access_x);
    362         if (!atf_is_error(err))
    363             pf->found = true;
    364         else {
    365             atf_error_free(err);
    366             INV(!pf->found);
    367             err = atf_no_error();
    368         }
    369 
    370 out_p:
    371         atf_fs_path_fini(&p);
    372     }
    373 
    374     return err;
    375 }
    376 
    377 static
    378 void
    379 tc_fail(atf_dynstr_t *msg)
    380 {
    381     atf_tcr_t tcr;
    382     atf_error_t err;
    383 
    384     PRE(current_tc != NULL);
    385 
    386     err = atf_tcr_init_reason_fmt(&tcr, atf_tcr_failed_state, "%s",
    387                                   atf_dynstr_cstring(msg));
    388     if (atf_is_error(err))
    389         abort();
    390 
    391     atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
    392 
    393     atf_tcr_fini(&tcr);
    394     atf_dynstr_fini(msg);
    395 
    396     exit(EXIT_FAILURE);
    397 }
    398 
    399 static
    400 void
    401 tc_fail_nonfatal(atf_dynstr_t *msg)
    402 {
    403     fprintf(stderr, "%s\n", atf_dynstr_cstring(msg));
    404     atf_dynstr_fini(msg);
    405 
    406     current_tc_fail_count++;
    407 }
    408 
    409 void
    410 atf_tc_fail(const char *fmt, ...)
    411 {
    412     va_list ap;
    413     atf_tcr_t tcr;
    414     atf_error_t err;
    415 
    416     PRE(current_tc != NULL);
    417 
    418     va_start(ap, fmt);
    419     err = atf_tcr_init_reason_ap(&tcr, atf_tcr_failed_state, fmt, ap);
    420     va_end(ap);
    421     if (atf_is_error(err))
    422         abort();
    423 
    424     atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
    425 
    426     atf_tcr_fini(&tcr);
    427 
    428     exit(EXIT_FAILURE);
    429 }
    430 
    431 void
    432 atf_tc_fail_nonfatal(const char *fmt, ...)
    433 {
    434     va_list ap;
    435 
    436     va_start(ap, fmt);
    437     vfprintf(stderr, fmt, ap);
    438     va_end(ap);
    439     fprintf(stderr, "\n");
    440 
    441     current_tc_fail_count++;
    442 }
    443 
    444 void
    445 atf_tc_fail_check(const char *file, int line, const char *fmt, ...)
    446 {
    447     va_list ap;
    448 
    449     va_start(ap, fmt);
    450     fail_internal(file, line, "Check failed", "*** ", fmt, ap,
    451                   tc_fail_nonfatal, atf_tc_fail_nonfatal);
    452     va_end(ap);
    453 }
    454 
    455 void
    456 atf_tc_fail_requirement(const char *file, int line, const char *fmt, ...)
    457 {
    458     va_list ap;
    459 
    460     atf_reset_exit_checks();
    461 
    462     va_start(ap, fmt);
    463     fail_internal(file, line, "Requirement failed", "", fmt, ap,
    464                   tc_fail, atf_tc_fail);
    465     va_end(ap);
    466 
    467     UNREACHABLE;
    468     abort();
    469 }
    470 
    471 static
    472 void
    473 fail_internal(const char *file, int line, const char *reason,
    474               const char *prefix, const char *fmt, va_list ap,
    475               void (*failfunc)(atf_dynstr_t *),
    476               void (*backupfunc)(const char *, ...))
    477 {
    478     va_list ap2;
    479     atf_error_t err;
    480     atf_dynstr_t msg;
    481 
    482     err = atf_dynstr_init_fmt(&msg, "%s%s:%d: %s: ", prefix, file, line,
    483                               reason);
    484     if (atf_is_error(err))
    485         goto backup;
    486 
    487     va_copy(ap2, ap);
    488     err = atf_dynstr_append_ap(&msg, fmt, ap2);
    489     va_end(ap2);
    490     if (atf_is_error(err)) {
    491         atf_dynstr_fini(&msg);
    492         goto backup;
    493     }
    494 
    495     va_copy(ap2, ap);
    496     failfunc(&msg);
    497     return;
    498 
    499 backup:
    500     atf_error_free(err);
    501     va_copy(ap2, ap);
    502     backupfunc(fmt, ap2);
    503     va_end(ap2);
    504 }
    505 
    506 void
    507 atf_tc_pass(void)
    508 {
    509     atf_tcr_t tcr;
    510     atf_error_t err;
    511 
    512     PRE(current_tc != NULL);
    513 
    514     err = atf_tcr_init(&tcr, atf_tcr_passed_state);
    515     if (atf_is_error(err))
    516         abort();
    517 
    518     atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
    519 
    520     atf_tcr_fini(&tcr);
    521 
    522     exit(EXIT_SUCCESS);
    523 }
    524 
    525 void
    526 atf_tc_require_prog(const char *prog)
    527 {
    528     atf_error_t err;
    529 
    530     err = check_prog(prog, NULL);
    531     if (atf_is_error(err)) {
    532         atf_error_free(err);
    533         atf_tc_fail("atf_tc_require_prog failed"); /* XXX Correct? */
    534     }
    535 }
    536 
    537 void
    538 atf_tc_skip(const char *fmt, ...)
    539 {
    540     va_list ap;
    541     atf_tcr_t tcr;
    542     atf_error_t err;
    543 
    544     PRE(current_tc != NULL);
    545 
    546     va_start(ap, fmt);
    547     err = atf_tcr_init_reason_ap(&tcr, atf_tcr_skipped_state, fmt, ap);
    548     va_end(ap);
    549     if (atf_is_error(err))
    550         abort();
    551 
    552     atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
    553 
    554     atf_tcr_fini(&tcr);
    555 
    556     exit(EXIT_SUCCESS);
    557 }
    558