1 /* Copyright (c) 2008 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 #if defined(HAVE_CONFIG_H)
37 #include "atf-c/detail/dynstr.h"
38 #include "atf-c/detail/env.h"
39 #include "atf-c/detail/fs.h"
40 #include "atf-c/detail/map.h"
41 #include "atf-c/detail/sanity.h"
42 #include "atf-c/error.h"
45 #include "atf-c/utils.h"
47 #if defined(HAVE_GNU_GETOPT)
48 # define GETOPT_POSIX "+"
50 # define GETOPT_POSIX ""
53 static const char *progname = NULL;
55 /* This prototype is provided by macros.h during instantiation of the test
56 * program, so it can be kept private. Don't know if that's the best idea
58 int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *));
65 /* ---------------------------------------------------------------------
66 * The "usage" and "user" error types.
67 * --------------------------------------------------------------------- */
69 #define FREE_FORM_ERROR(name) \
70 struct name ## _error_data { \
76 name ## _format(const atf_error_t err, char *buf, size_t buflen) \
78 const struct name ## _error_data *data; \
80 PRE(atf_error_is(err, #name)); \
82 data = atf_error_data(err); \
83 snprintf(buf, buflen, "%s", data->m_what); \
88 name ## _error(const char *fmt, ...) \
91 struct name ## _error_data data; \
95 vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \
98 err = atf_error_new(#name, &data, sizeof(data), name ## _format); \
103 FREE_FORM_ERROR(usage);
104 FREE_FORM_ERROR(user);
106 /* ---------------------------------------------------------------------
107 * Printing functions.
108 * --------------------------------------------------------------------- */
112 print_error(const atf_error_t err)
116 PRE(atf_is_error(err));
118 atf_error_format(err, buf, sizeof(buf));
119 fprintf(stderr, "%s: ERROR: %s\n", progname, buf);
121 if (atf_error_is(err, "usage"))
122 fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n",
128 print_warning(const char *message)
130 fprintf(stderr, "%s: WARNING: %s\n", progname, message);
133 /* ---------------------------------------------------------------------
135 * --------------------------------------------------------------------- */
139 atf_fs_path_t m_srcdir;
141 enum tc_part m_tcpart;
142 atf_fs_path_t m_resfile;
148 argv0_to_dir(const char *argv0, atf_fs_path_t *dir)
153 err = atf_fs_path_init_fmt(&temp, "%s", argv0);
154 if (atf_is_error(err))
157 err = atf_fs_path_branch_path(&temp, dir);
159 atf_fs_path_fini(&temp);
166 params_init(struct params *p, const char *argv0)
170 p->m_do_list = false;
174 err = argv0_to_dir(argv0, &p->m_srcdir);
175 if (atf_is_error(err))
178 err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout");
179 if (atf_is_error(err)) {
180 atf_fs_path_fini(&p->m_srcdir);
184 err = atf_map_init(&p->m_config);
185 if (atf_is_error(err)) {
186 atf_fs_path_fini(&p->m_resfile);
187 atf_fs_path_fini(&p->m_srcdir);
196 params_fini(struct params *p)
198 atf_map_fini(&p->m_config);
199 atf_fs_path_fini(&p->m_resfile);
200 atf_fs_path_fini(&p->m_srcdir);
201 if (p->m_tcname != NULL)
207 parse_vflag(char *arg, atf_map_t *config)
212 split = strchr(arg, '=');
214 err = usage_error("-v requires an argument of the form var=value");
221 err = atf_map_insert(config, arg, split, false);
229 replace_path_param(atf_fs_path_t *param, const char *value)
234 err = atf_fs_path_init_fmt(&temp, "%s", value);
235 if (!atf_is_error(err)) {
236 atf_fs_path_fini(param);
243 /* ---------------------------------------------------------------------
245 * --------------------------------------------------------------------- */
249 list_tcs(const atf_tp_t *tp)
251 const atf_tc_t *const *tcs;
252 const atf_tc_t *const *tcsptr;
254 printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n");
256 tcs = atf_tp_get_tcs(tp);
257 INV(tcs != NULL); /* Should be checked. */
258 for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) {
259 const atf_tc_t *tc = *tcsptr;
260 char **vars = atf_tc_get_md_vars(tc);
263 INV(vars != NULL); /* Should be checked. */
265 if (tcsptr != tcs) /* Not first. */
268 for (ptr = vars; *ptr != NULL; ptr += 2) {
269 if (strcmp(*ptr, "ident") == 0) {
270 printf("ident: %s\n", *(ptr + 1));
275 for (ptr = vars; *ptr != NULL; ptr += 2) {
276 if (strcmp(*ptr, "ident") != 0) {
277 printf("%s: %s\n", *ptr, *(ptr + 1));
281 atf_utils_free_charpp(vars);
285 /* ---------------------------------------------------------------------
287 * --------------------------------------------------------------------- */
291 handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart)
295 err = atf_no_error();
297 *tcname = strdup(tcarg);
298 if (*tcname == NULL) {
299 err = atf_no_memory_error();
303 char *delim = strchr(*tcname, ':');
308 if (strcmp(delim, "body") == 0) {
310 } else if (strcmp(delim, "cleanup") == 0) {
313 err = usage_error("Invalid test case part `%s'", delim);
324 process_params(int argc, char **argv, struct params *p)
330 err = params_init(p, argv[0]);
331 if (atf_is_error(err))
336 while (!atf_is_error(err) &&
337 (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
344 err = replace_path_param(&p->m_resfile, optarg);
348 err = replace_path_param(&p->m_srcdir, optarg);
352 err = parse_vflag(optarg, &p->m_config);
356 err = usage_error("Option -%c requires an argument.", optopt);
361 err = usage_error("Unknown option -%c.", optopt);
367 /* Clear getopt state just in case the test wants to use it. */
370 #if defined(HAVE_OPTRESET)
374 if (!atf_is_error(err)) {
377 err = usage_error("Cannot provide test case names with -l");
380 err = usage_error("Must provide a test case name");
382 err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart);
384 err = usage_error("Cannot provide more than one test case "
390 if (atf_is_error(err))
399 srcdir_strip_libtool(atf_fs_path_t *srcdir)
402 atf_fs_path_t parent;
404 err = atf_fs_path_branch_path(srcdir, &parent);
405 if (atf_is_error(err))
408 atf_fs_path_fini(srcdir);
411 INV(!atf_is_error(err));
418 handle_srcdir(struct params *p)
421 atf_dynstr_t leafname;
422 atf_fs_path_t exe, srcdir;
425 err = atf_fs_path_copy(&srcdir, &p->m_srcdir);
426 if (atf_is_error(err))
429 if (!atf_fs_path_is_absolute(&srcdir)) {
430 atf_fs_path_t srcdirabs;
432 err = atf_fs_path_to_absolute(&srcdir, &srcdirabs);
433 if (atf_is_error(err))
436 atf_fs_path_fini(&srcdir);
440 err = atf_fs_path_leaf_name(&srcdir, &leafname);
441 if (atf_is_error(err))
444 const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs");
445 atf_dynstr_fini(&leafname);
448 err = srcdir_strip_libtool(&srcdir);
449 if (atf_is_error(err))
454 err = atf_fs_path_copy(&exe, &srcdir);
455 if (atf_is_error(err))
458 err = atf_fs_path_append_fmt(&exe, "%s", progname);
459 if (atf_is_error(err))
462 err = atf_fs_exists(&exe, &b);
463 if (!atf_is_error(err)) {
465 err = atf_map_insert(&p->m_config, "srcdir",
466 strdup(atf_fs_path_cstring(&srcdir)), true);
468 err = user_error("Cannot find the test program in the source "
469 "directory `%s'", atf_fs_path_cstring(&srcdir));
474 atf_fs_path_fini(&exe);
476 atf_fs_path_fini(&srcdir);
483 run_tc(const atf_tp_t *tp, struct params *p, int *exitcode)
487 err = atf_no_error();
489 if (!atf_tp_has_tc(tp, p->m_tcname)) {
490 err = usage_error("Unknown test case `%s'", p->m_tcname);
494 if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get(
495 "__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0)
497 print_warning("Running test cases outside of kyua(1) is unsupported");
498 print_warning("No isolation nor timeout control is being applied; you "
499 "may get unexpected failures; see atf-test-case(4)");
502 switch (p->m_tcpart) {
504 err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile));
505 if (atf_is_error(err)) {
506 /* TODO: Handle error */
507 *exitcode = EXIT_FAILURE;
510 *exitcode = EXIT_SUCCESS;
516 err = atf_tp_cleanup(tp, p->m_tcname);
517 if (atf_is_error(err)) {
518 /* TODO: Handle error */
519 *exitcode = EXIT_FAILURE;
522 *exitcode = EXIT_SUCCESS;
531 INV(!atf_is_error(err));
538 controlled_main(int argc, char **argv,
539 atf_error_t (*add_tcs_hook)(atf_tp_t *),
547 err = process_params(argc, argv, &p);
548 if (atf_is_error(err))
551 err = handle_srcdir(&p);
552 if (atf_is_error(err))
555 raw_config = atf_map_to_charpp(&p.m_config);
556 if (raw_config == NULL) {
557 err = atf_no_memory_error();
560 err = atf_tp_init(&tp, (const char* const*)raw_config);
561 atf_utils_free_charpp(raw_config);
562 if (atf_is_error(err))
565 err = add_tcs_hook(&tp);
566 if (atf_is_error(err))
571 INV(!atf_is_error(err));
572 *exitcode = EXIT_SUCCESS;
574 err = run_tc(&tp, &p, exitcode);
586 atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *))
591 progname = strrchr(argv[0], '/');
592 if (progname == NULL)
597 /* Libtool workaround: if running from within the source tree (binaries
598 * that are not installed yet), skip the "lt-" prefix added to files in
599 * the ".libs" directory to show the real (not temporary) name. */
600 if (strncmp(progname, "lt-", 3) == 0)
603 exitcode = EXIT_FAILURE; /* Silence GCC warning. */
604 err = controlled_main(argc, argv, add_tcs_hook, &exitcode);
605 if (atf_is_error(err)) {
608 exitcode = EXIT_FAILURE;