Path: blob/main/contrib/atf/atf-c/detail/tp_main.c
39507 views
/* Copyright (c) 2008 The NetBSD Foundation, Inc.1* All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions and the following disclaimer.8* 2. Redistributions in binary form must reproduce the above copyright9* notice, this list of conditions and the following disclaimer in the10* documentation and/or other materials provided with the distribution.11*12* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND13* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,14* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF15* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.16* IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY17* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE19* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS20* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER21* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR22* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN23* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */2425#if defined(HAVE_CONFIG_H)26#include "config.h"27#endif2829#include <ctype.h>30#include <stdarg.h>31#include <stdio.h>32#include <stdlib.h>33#include <string.h>34#include <unistd.h>3536#include "atf-c/detail/dynstr.h"37#include "atf-c/detail/env.h"38#include "atf-c/detail/fs.h"39#include "atf-c/detail/map.h"40#include "atf-c/detail/sanity.h"41#include "atf-c/error.h"42#include "atf-c/tc.h"43#include "atf-c/tp.h"44#include "atf-c/utils.h"4546#if defined(HAVE_GNU_GETOPT)47# define GETOPT_POSIX "+"48#else49# define GETOPT_POSIX ""50#endif5152static const char *progname = NULL;5354/* This prototype is provided by macros.h during instantiation of the test55* program, so it can be kept private. Don't know if that's the best idea56* though. */57int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *));5859enum tc_part {60BODY,61CLEANUP,62};6364/* ---------------------------------------------------------------------65* The "usage" and "user" error types.66* --------------------------------------------------------------------- */6768#define FREE_FORM_ERROR(name) \69struct name ## _error_data { \70char m_what[2048]; \71}; \72\73static \74void \75name ## _format(const atf_error_t err, char *buf, size_t buflen) \76{ \77const struct name ## _error_data *data; \78\79PRE(atf_error_is(err, #name)); \80\81data = atf_error_data(err); \82snprintf(buf, buflen, "%s", data->m_what); \83} \84\85static \86atf_error_t \87name ## _error(const char *fmt, ...) \88{ \89atf_error_t err; \90struct name ## _error_data data; \91va_list ap; \92\93va_start(ap, fmt); \94vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \95va_end(ap); \96\97err = atf_error_new(#name, &data, sizeof(data), name ## _format); \98\99return err; \100}101102FREE_FORM_ERROR(usage);103FREE_FORM_ERROR(user);104105/* ---------------------------------------------------------------------106* Printing functions.107* --------------------------------------------------------------------- */108109static110void111print_error(const atf_error_t err)112{113char buf[4096];114115PRE(atf_is_error(err));116117atf_error_format(err, buf, sizeof(buf));118fprintf(stderr, "%s: ERROR: %s\n", progname, buf);119120if (atf_error_is(err, "usage"))121fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n",122progname);123}124125static126void127print_warning(const char *message)128{129fprintf(stderr, "%s: WARNING: %s\n", progname, message);130}131132/* ---------------------------------------------------------------------133* Options handling.134* --------------------------------------------------------------------- */135136struct params {137bool m_do_list;138atf_fs_path_t m_srcdir;139char *m_tcname;140enum tc_part m_tcpart;141atf_fs_path_t m_resfile;142atf_map_t m_config;143};144145static146atf_error_t147argv0_to_dir(const char *argv0, atf_fs_path_t *dir)148{149atf_error_t err;150atf_fs_path_t temp;151152err = atf_fs_path_init_fmt(&temp, "%s", argv0);153if (atf_is_error(err))154goto out;155156err = atf_fs_path_branch_path(&temp, dir);157158atf_fs_path_fini(&temp);159out:160return err;161}162163static164atf_error_t165params_init(struct params *p, const char *argv0)166{167atf_error_t err;168169p->m_do_list = false;170p->m_tcname = NULL;171p->m_tcpart = BODY;172173err = argv0_to_dir(argv0, &p->m_srcdir);174if (atf_is_error(err))175return err;176177err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout");178if (atf_is_error(err)) {179atf_fs_path_fini(&p->m_srcdir);180return err;181}182183err = atf_map_init(&p->m_config);184if (atf_is_error(err)) {185atf_fs_path_fini(&p->m_resfile);186atf_fs_path_fini(&p->m_srcdir);187return err;188}189190return err;191}192193static194void195params_fini(struct params *p)196{197atf_map_fini(&p->m_config);198atf_fs_path_fini(&p->m_resfile);199atf_fs_path_fini(&p->m_srcdir);200if (p->m_tcname != NULL)201free(p->m_tcname);202}203204static205atf_error_t206parse_vflag(char *arg, atf_map_t *config)207{208atf_error_t err;209char *split;210211split = strchr(arg, '=');212if (split == NULL) {213err = usage_error("-v requires an argument of the form var=value");214goto out;215}216217*split = '\0';218split++;219220err = atf_map_insert(config, arg, split, false);221222out:223return err;224}225226static227atf_error_t228replace_path_param(atf_fs_path_t *param, const char *value)229{230atf_error_t err;231atf_fs_path_t temp;232233err = atf_fs_path_init_fmt(&temp, "%s", value);234if (!atf_is_error(err)) {235atf_fs_path_fini(param);236*param = temp;237}238239return err;240}241242/* ---------------------------------------------------------------------243* Test case listing.244* --------------------------------------------------------------------- */245246static247void248list_tcs(const atf_tp_t *tp)249{250const atf_tc_t *const *tcs;251const atf_tc_t *const *tcsptr;252253printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n");254255tcs = atf_tp_get_tcs(tp);256INV(tcs != NULL); /* Should be checked. */257for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) {258const atf_tc_t *tc = *tcsptr;259char **vars = atf_tc_get_md_vars(tc);260char **ptr;261262INV(vars != NULL); /* Should be checked. */263264if (tcsptr != tcs) /* Not first. */265printf("\n");266267for (ptr = vars; *ptr != NULL; ptr += 2) {268if (strcmp(*ptr, "ident") == 0) {269printf("ident: %s\n", *(ptr + 1));270break;271}272}273274for (ptr = vars; *ptr != NULL; ptr += 2) {275if (strcmp(*ptr, "ident") != 0) {276printf("%s: %s\n", *ptr, *(ptr + 1));277}278}279280atf_utils_free_charpp(vars);281}282}283284/* ---------------------------------------------------------------------285* Main.286* --------------------------------------------------------------------- */287288static289atf_error_t290handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart)291{292atf_error_t err;293294err = atf_no_error();295296*tcname = strdup(tcarg);297if (*tcname == NULL) {298err = atf_no_memory_error();299goto out;300}301302char *delim = strchr(*tcname, ':');303if (delim != NULL) {304*delim = '\0';305306delim++;307if (strcmp(delim, "body") == 0) {308*tcpart = BODY;309} else if (strcmp(delim, "cleanup") == 0) {310*tcpart = CLEANUP;311} else {312err = usage_error("Invalid test case part `%s'", delim);313goto out;314}315}316317out:318return err;319}320321static322atf_error_t323process_params(int argc, char **argv, struct params *p)324{325atf_error_t err;326int ch;327int old_opterr;328329err = params_init(p, argv[0]);330if (atf_is_error(err))331goto out;332333old_opterr = opterr;334opterr = 0;335while (!atf_is_error(err) &&336(ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {337switch (ch) {338case 'l':339p->m_do_list = true;340break;341342case 'r':343err = replace_path_param(&p->m_resfile, optarg);344break;345346case 's':347err = replace_path_param(&p->m_srcdir, optarg);348break;349350case 'v':351err = parse_vflag(optarg, &p->m_config);352break;353354case ':':355err = usage_error("Option -%c requires an argument.", optopt);356break;357358case '?':359default:360err = usage_error("Unknown option -%c.", optopt);361}362}363argc -= optind;364argv += optind;365366/* Clear getopt state just in case the test wants to use it. */367opterr = old_opterr;368optind = 1;369#if defined(HAVE_OPTRESET)370optreset = 1;371#endif372373if (!atf_is_error(err)) {374if (p->m_do_list) {375if (argc > 0)376err = usage_error("Cannot provide test case names with -l");377} else {378if (argc == 0)379err = usage_error("Must provide a test case name");380else if (argc == 1)381err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart);382else if (argc > 1) {383err = usage_error("Cannot provide more than one test case "384"name");385}386}387}388389if (atf_is_error(err))390params_fini(p);391392out:393return err;394}395396static397atf_error_t398srcdir_strip_libtool(atf_fs_path_t *srcdir)399{400atf_error_t err;401atf_fs_path_t parent;402403err = atf_fs_path_branch_path(srcdir, &parent);404if (atf_is_error(err))405goto out;406407atf_fs_path_fini(srcdir);408*srcdir = parent;409410INV(!atf_is_error(err));411out:412return err;413}414415static416atf_error_t417handle_srcdir(struct params *p)418{419atf_error_t err;420atf_dynstr_t leafname;421atf_fs_path_t exe, srcdir;422bool b;423424err = atf_fs_path_copy(&srcdir, &p->m_srcdir);425if (atf_is_error(err))426goto out;427428if (!atf_fs_path_is_absolute(&srcdir)) {429atf_fs_path_t srcdirabs;430431err = atf_fs_path_to_absolute(&srcdir, &srcdirabs);432if (atf_is_error(err))433goto out_srcdir;434435atf_fs_path_fini(&srcdir);436srcdir = srcdirabs;437}438439err = atf_fs_path_leaf_name(&srcdir, &leafname);440if (atf_is_error(err))441goto out_srcdir;442else {443const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs");444atf_dynstr_fini(&leafname);445446if (libs) {447err = srcdir_strip_libtool(&srcdir);448if (atf_is_error(err))449goto out;450}451}452453err = atf_fs_path_copy(&exe, &srcdir);454if (atf_is_error(err))455goto out_srcdir;456457err = atf_fs_path_append_fmt(&exe, "%s", progname);458if (atf_is_error(err))459goto out_exe;460461err = atf_fs_exists(&exe, &b);462if (!atf_is_error(err)) {463if (b) {464err = atf_map_insert(&p->m_config, "srcdir",465strdup(atf_fs_path_cstring(&srcdir)), true);466} else {467err = user_error("Cannot find the test program in the source "468"directory `%s'", atf_fs_path_cstring(&srcdir));469}470}471472out_exe:473atf_fs_path_fini(&exe);474out_srcdir:475atf_fs_path_fini(&srcdir);476out:477return err;478}479480static481atf_error_t482run_tc(const atf_tp_t *tp, struct params *p, int *exitcode)483{484atf_error_t err;485486err = atf_no_error();487488if (!atf_tp_has_tc(tp, p->m_tcname)) {489err = usage_error("Unknown test case `%s'", p->m_tcname);490goto out;491}492493if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get(494"__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0)495{496print_warning("Running test cases outside of kyua(1) is unsupported");497print_warning("No isolation nor timeout control is being applied; you "498"may get unexpected failures; see atf-test-case(4)");499}500501switch (p->m_tcpart) {502case BODY:503err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile));504if (atf_is_error(err)) {505/* TODO: Handle error */506*exitcode = EXIT_FAILURE;507atf_error_free(err);508} else {509*exitcode = EXIT_SUCCESS;510}511512break;513514case CLEANUP:515err = atf_tp_cleanup(tp, p->m_tcname);516if (atf_is_error(err)) {517/* TODO: Handle error */518*exitcode = EXIT_FAILURE;519atf_error_free(err);520} else {521*exitcode = EXIT_SUCCESS;522}523524break;525526default:527UNREACHABLE;528}529530INV(!atf_is_error(err));531out:532return err;533}534535static536atf_error_t537controlled_main(int argc, char **argv,538atf_error_t (*add_tcs_hook)(atf_tp_t *),539int *exitcode)540{541atf_error_t err;542struct params p;543atf_tp_t tp;544char **raw_config;545546err = process_params(argc, argv, &p);547if (atf_is_error(err))548goto out;549550err = handle_srcdir(&p);551if (atf_is_error(err))552goto out_p;553554raw_config = atf_map_to_charpp(&p.m_config);555if (raw_config == NULL) {556err = atf_no_memory_error();557goto out_p;558}559err = atf_tp_init(&tp, (const char* const*)raw_config);560atf_utils_free_charpp(raw_config);561if (atf_is_error(err))562goto out_p;563564err = add_tcs_hook(&tp);565if (atf_is_error(err))566goto out_tp;567568if (p.m_do_list) {569list_tcs(&tp);570INV(!atf_is_error(err));571*exitcode = EXIT_SUCCESS;572} else {573err = run_tc(&tp, &p, exitcode);574}575576out_tp:577atf_tp_fini(&tp);578out_p:579params_fini(&p);580out:581return err;582}583584int585atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *))586{587atf_error_t err;588int exitcode;589590progname = strrchr(argv[0], '/');591if (progname == NULL)592progname = argv[0];593else594progname++;595596/* Libtool workaround: if running from within the source tree (binaries597* that are not installed yet), skip the "lt-" prefix added to files in598* the ".libs" directory to show the real (not temporary) name. */599if (strncmp(progname, "lt-", 3) == 0)600progname += 3;601602exitcode = EXIT_FAILURE; /* Silence GCC warning. */603err = controlled_main(argc, argv, add_tcs_hook, &exitcode);604if (atf_is_error(err)) {605print_error(err);606atf_error_free(err);607exitcode = EXIT_FAILURE;608}609610return exitcode;611}612613614