Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / bsd / atf / dist / atf-c / tp_main.c
blob91be91d8d5dad2545317a8e67665e1e3555f0a8b
1 /*
2 * Automated Testing Framework (atf)
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
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.
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.
30 #if defined(HAVE_CONFIG_H)
31 #include "bconfig.h"
32 #endif
34 #include <ctype.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
41 #include "atf-c/config.h"
42 #include "atf-c/dynstr.h"
43 #include "atf-c/error.h"
44 #include "atf-c/expand.h"
45 #include "atf-c/fs.h"
46 #include "atf-c/object.h"
47 #include "atf-c/map.h"
48 #include "atf-c/sanity.h"
49 #include "atf-c/tc.h"
50 #include "atf-c/tp.h"
51 #include "atf-c/ui.h"
53 #if defined(HAVE_GNU_GETOPT)
54 # define GETOPT_POSIX "+"
55 #else
56 # define GETOPT_POSIX ""
57 #endif
59 static const char *progname = NULL;
61 /* This prototype is provided by macros.h during instantiation of the test
62 * program, so it can be kept private. Don't know if that's the best idea
63 * though. */
64 int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *));
66 /* ---------------------------------------------------------------------
67 * The "usage" and "user" error types.
68 * --------------------------------------------------------------------- */
70 #define FREE_FORM_ERROR(name) \
71 struct name ## _error_data { \
72 char m_what[2048]; \
73 }; \
75 static \
76 void \
77 name ## _format(const atf_error_t err, char *buf, size_t buflen) \
78 { \
79 const struct name ## _error_data *data; \
81 PRE(atf_error_is(err, #name)); \
83 data = atf_error_data(err); \
84 snprintf(buf, buflen, "%s", data->m_what); \
85 } \
87 static \
88 atf_error_t \
89 name ## _error(const char *fmt, ...) \
90 { \
91 atf_error_t err; \
92 struct name ## _error_data data; \
93 va_list ap; \
95 va_start(ap, fmt); \
96 vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \
97 va_end(ap); \
99 err = atf_error_new(#name, &data, sizeof(data), name ## _format); \
101 return err; \
104 FREE_FORM_ERROR(usage);
105 FREE_FORM_ERROR(user);
107 /* ---------------------------------------------------------------------
108 * Printing functions.
109 * --------------------------------------------------------------------- */
111 /* XXX: Why are these functions here? We have got a ui module, and it
112 * seems the correct place to put these. Otherwise, the functions that
113 * currently live there only format text, so they'd be moved to the text
114 * module instead and kill ui completely. */
116 static
117 atf_error_t
118 print_tag(FILE *f, const char *tag, bool repeat, size_t col,
119 const char *fmt, ...)
121 atf_error_t err;
122 va_list ap;
123 atf_dynstr_t dest;
125 err = atf_dynstr_init(&dest);
126 if (atf_is_error(err))
127 goto out;
129 va_start(ap, fmt);
130 err = atf_ui_format_ap(&dest, tag, repeat, col, fmt, ap);
131 va_end(ap);
132 if (atf_is_error(err))
133 goto out_dest;
135 fprintf(f, "%s\n", atf_dynstr_cstring(&dest));
137 out_dest:
138 atf_dynstr_fini(&dest);
139 out:
140 return err;
143 static
144 void
145 print_error(const atf_error_t err)
147 PRE(atf_is_error(err));
149 if (atf_error_is(err, "no_memory")) {
150 char buf[1024];
152 atf_error_format(err, buf, sizeof(buf));
154 fprintf(stderr, "%s: %s\n", progname, buf);
155 } else {
156 atf_dynstr_t tag;
157 char buf[4096];
159 atf_error_format(err, buf, sizeof(buf));
161 atf_dynstr_init_fmt(&tag, "%s: ", progname);
162 print_tag(stderr, atf_dynstr_cstring(&tag), true, 0,
163 "ERROR: %s", buf);
165 if (atf_error_is(err, "usage"))
166 print_tag(stderr, atf_dynstr_cstring(&tag), true, 0,
167 "Type `%s -h' for more details.", progname);
169 atf_dynstr_fini(&tag);
173 /* ---------------------------------------------------------------------
174 * Options handling.
175 * --------------------------------------------------------------------- */
177 struct params {
178 bool m_do_list;
179 bool m_do_usage;
180 int m_fd;
181 const char *m_srcdir;
182 const char *m_workdir;
183 atf_list_t m_tcglobs;
184 atf_map_t m_config;
187 static
188 atf_error_t
189 params_init(struct params *p)
191 atf_error_t err;
193 p->m_do_list = false;
194 p->m_do_usage = false;
195 p->m_fd = STDOUT_FILENO;
196 p->m_srcdir = ".";
197 p->m_workdir = atf_config_get("atf_workdir");
199 err = atf_list_init(&p->m_tcglobs);
200 if (atf_is_error(err))
201 goto out;
203 err = atf_map_init(&p->m_config);
204 if (atf_is_error(err))
205 atf_list_fini(&p->m_tcglobs);
207 out:
208 return err;
211 static
212 void
213 params_fini(struct params *p)
215 atf_map_fini(&p->m_config);
216 atf_list_fini(&p->m_tcglobs);
219 static
220 atf_error_t
221 parse_rflag(const char *arg, int *value)
223 atf_error_t err;
225 if (strlen(arg) != 1 || !isdigit((int)arg[0])) {
226 err = usage_error("Invalid value for -r; must be a single digit.");
227 goto out;
230 *value = arg[0] - '0';
231 INV(*value >= 0 && *value <= 9);
232 err = atf_no_error();
234 out:
235 return err;
238 static
239 atf_error_t
240 parse_vflag(char *arg, atf_map_t *config)
242 atf_error_t err;
243 char *split;
245 split = strchr(arg, '=');
246 if (split == NULL) {
247 err = usage_error("-v requires an argument of the form var=value");
248 goto out;
251 *split = '\0';
252 split++;
254 err = atf_map_insert(config, arg, split, false);
256 out:
257 return err;
260 /* ---------------------------------------------------------------------
261 * Test case filtering.
262 * --------------------------------------------------------------------- */
264 static
265 atf_error_t
266 match_tcs(const atf_tp_t *tp, const char *glob, atf_list_t *ids)
268 atf_error_t err;
269 bool found;
270 atf_list_citer_t iter;
272 err = atf_no_error();
273 found = false;
274 atf_list_for_each_c(iter, atf_tp_get_tcs(tp)) {
275 const atf_tc_t *tc = atf_list_citer_data(iter);
276 const char *ident = atf_tc_get_ident(tc);
278 if (atf_expand_is_glob(glob)) {
279 bool matches;
281 err = atf_expand_matches_glob(glob, ident, &matches);
282 if (!atf_is_error(err) && matches) {
283 err = atf_list_append(ids, strdup(ident), true);
284 found = true;
286 } else {
287 if (strcmp(glob, tc->m_ident) == 0) {
288 err = atf_list_append(ids, strdup(ident), true);
289 found = true;
293 if (atf_is_error(err))
294 break;
297 if (!atf_is_error(err) && !found)
298 err = user_error("Unknown test case `%s'", glob);
300 return err;
303 static
304 atf_error_t
305 filter_tcs(const atf_tp_t *tp, const atf_list_t *globs, atf_list_t *ids)
307 atf_error_t err;
308 atf_list_citer_t iter;
310 err = atf_list_init(ids);
311 if (atf_is_error(err))
312 goto out;
314 atf_list_for_each_c(iter, globs) {
315 const char *glob = atf_list_citer_data(iter);
316 err = match_tcs(tp, glob, ids);
317 if (atf_is_error(err)) {
318 atf_list_fini(ids);
319 goto out;
323 out:
324 return err;
327 static
328 atf_error_t
329 list_tcs(const atf_tp_t *tp, const atf_list_t *tcids)
331 atf_error_t err;
332 size_t col;
333 atf_list_citer_t iter;
335 PRE(atf_list_size(tcids) > 0);
337 err = atf_no_error();
339 /* Calculate column where to start descriptions. */
340 col = 0;
341 atf_list_for_each_c(iter, tcids) {
342 const char *id = atf_list_citer_data(iter);
343 const atf_tc_t *tc = atf_tp_get_tc(tp, id);
344 const size_t len = strlen(atf_tc_get_ident(tc));
346 if (col < len)
347 col = len;
349 col += 4;
351 /* Pretty-print test case identifiers and descriptions. */
352 atf_list_for_each_c(iter, tcids) {
353 const char *id = atf_list_citer_data(iter);
354 const atf_tc_t *tc = atf_tp_get_tc(tp, id);
355 const char *descr = atf_tc_get_md_var(tc, "descr");
357 err = print_tag(stdout, id, false, col, "%s", descr);
358 if (atf_is_error(err))
359 break;
362 return err;
365 /* ---------------------------------------------------------------------
366 * Main.
367 * --------------------------------------------------------------------- */
369 static
370 void
371 usage(void)
373 print_tag(stdout, "Usage: ", false, 0,
374 "%s [options] [test_case1 [.. test_caseN]]", progname);
375 printf("\n");
376 print_tag(stdout, "", false, 0, "This is an independent atf test "
377 "program.");
378 printf("\n");
379 print_tag(stdout, "", false, 0, "Available options:");
380 print_tag(stdout, " -h ", false, 0,
381 "Shows this help message");
382 print_tag(stdout, " -l ", false, 0,
383 "List test cases and their purpose");
384 print_tag(stdout, " -r fd ", false, 0,
385 "The file descriptor to which the test program "
386 "will send the results of the test cases");
387 print_tag(stdout, " -s srcdir ", false, 0,
388 "Directory where the test's data files are "
389 "located");
390 print_tag(stdout, " -v var=value ", false, 0,
391 "Sets the configuration variable `var' to `value'");
392 print_tag(stdout, " -w workdir ", false, 0,
393 "Directory where the test's temporary files are "
394 "located");
395 printf("\n");
396 print_tag(stdout, "", false, 0, "For more details please see "
397 "atf-test-program(1) and atf(7).");
400 static
401 atf_error_t
402 process_params(int argc, char **argv, struct params *p)
404 atf_error_t err;
405 int ch;
407 err = params_init(p);
408 if (atf_is_error(err))
409 goto out;
411 opterr = 0;
412 while (!atf_is_error(err) &&
413 (ch = getopt(argc, argv, GETOPT_POSIX ":hlr:s:v:w:")) != -1) {
414 switch (ch) {
415 case 'h':
416 p->m_do_usage = true;
417 break;
419 case 'l':
420 p->m_do_list = true;
421 break;
423 case 'r':
424 err = parse_rflag(optarg, &p->m_fd);
425 break;
427 case 's':
428 p->m_srcdir = optarg;
429 break;
431 case 'v':
432 err = parse_vflag(optarg, &p->m_config);
433 break;
435 case 'w':
436 p->m_workdir = optarg;
437 break;
439 case ':':
440 err = usage_error("Option -%c requires an argument.", optopt);
441 break;
443 case '?':
444 default:
445 err = usage_error("Unknown option -%c.", optopt);
448 argc -= optind;
449 argv += optind;
451 if (!atf_is_error(err)) {
452 char **arg;
453 for (arg = argv; !atf_is_error(err) && *arg != NULL; arg++)
454 err = atf_list_append(&p->m_tcglobs, strdup(*arg), true);
456 if (!atf_is_error(err) && atf_list_size(&p->m_tcglobs) == 0)
457 err = atf_list_append(&p->m_tcglobs, strdup("*"), true);
460 if (atf_is_error(err))
461 params_fini(p);
463 out:
464 return err;
467 static
468 atf_error_t
469 handle_srcdir(struct params *p)
471 atf_error_t err;
472 atf_fs_path_t exe, srcdir;
473 bool b;
475 err = atf_fs_path_init_fmt(&srcdir, "%s", p->m_srcdir);
476 if (atf_is_error(err))
477 goto out;
479 if (!atf_fs_path_is_absolute(&srcdir)) {
480 atf_fs_path_t srcdirabs;
482 err = atf_fs_path_to_absolute(&srcdir, &srcdirabs);
483 if (atf_is_error(err))
484 goto out_srcdir;
486 atf_fs_path_fini(&srcdir);
487 srcdir = srcdirabs;
490 err = atf_fs_path_copy(&exe, &srcdir);
491 if (atf_is_error(err))
492 goto out_srcdir;
494 err = atf_fs_path_append_fmt(&exe, "%s", progname);
495 if (atf_is_error(err))
496 goto out_exe;
498 err = atf_fs_exists(&exe, &b);
499 if (!atf_is_error(err)) {
500 if (b) {
501 err = atf_map_insert(&p->m_config, "srcdir",
502 strdup(atf_fs_path_cstring(&srcdir)), true);
503 } else {
504 err = user_error("Cannot find the test program in the source "
505 "directory `%s'", p->m_srcdir);
509 out_exe:
510 atf_fs_path_fini(&exe);
511 out_srcdir:
512 atf_fs_path_fini(&srcdir);
513 out:
514 return err;
517 static
518 atf_error_t
519 handle_workdir(struct params *p, atf_fs_path_t *workdir)
521 atf_error_t err;
522 bool b;
524 err = atf_fs_path_init_fmt(workdir, "%s", p->m_workdir);
525 if (atf_is_error(err))
526 goto out;
528 err = atf_fs_exists(workdir, &b);
529 if (atf_is_error(err)) {
530 atf_fs_path_fini(workdir);
531 goto out;
534 if (!b) {
535 atf_fs_path_fini(workdir);
536 err = user_error("Cannot find the work directory `%s'",
537 p->m_workdir);
538 } else
539 INV(!atf_is_error(err));
541 out:
542 return err;
545 static
546 atf_error_t
547 controlled_main(int argc, char **argv,
548 atf_error_t (*add_tcs_hook)(atf_tp_t *),
549 int *exitcode)
551 atf_error_t err;
552 struct params p;
553 atf_tp_t tp;
554 atf_list_t tcids;
555 atf_fs_path_t workdir;
557 err = process_params(argc, argv, &p);
558 if (atf_is_error(err))
559 goto out;
561 if (p.m_do_usage) {
562 if (argc != 2) {
563 err = usage_error("-h must be given alone.");
564 } else {
565 usage();
566 *exitcode = EXIT_SUCCESS;
568 goto out_p;
571 err = handle_srcdir(&p);
572 if (atf_is_error(err))
573 goto out_p;
575 err = handle_workdir(&p, &workdir);
576 if (atf_is_error(err))
577 goto out_p;
579 err = atf_tp_init(&tp, &p.m_config);
580 if (atf_is_error(err))
581 goto out_workdir;
583 err = add_tcs_hook(&tp);
584 if (atf_is_error(err))
585 goto out_tp;
587 err = filter_tcs(&tp, &p.m_tcglobs, &tcids);
588 if (atf_is_error(err))
589 goto out_tp;
591 if (p.m_do_list) {
592 err = list_tcs(&tp, &tcids);
593 if (!atf_is_error(err))
594 *exitcode = EXIT_SUCCESS;
595 } else {
596 size_t failed;
597 err = atf_tp_run(&tp, &tcids, p.m_fd, &workdir, &failed);
598 if (!atf_is_error(err))
599 *exitcode = failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
602 atf_list_fini(&tcids);
603 out_tp:
604 atf_tp_fini(&tp);
605 out_workdir:
606 atf_fs_path_fini(&workdir);
607 out_p:
608 params_fini(&p);
609 out:
610 return err;
614 atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *))
616 atf_error_t err;
617 int exitcode;
619 atf_init_objects();
621 progname = strrchr(argv[0], '/');
622 if (progname == NULL)
623 progname = argv[0];
624 else
625 progname++;
627 exitcode = EXIT_FAILURE; /* Silence GCC warning. */
628 err = controlled_main(argc, argv, add_tcs_hook, &exitcode);
629 if (atf_is_error(err)) {
630 print_error(err);
631 atf_error_free(err);
632 exitcode = EXIT_FAILURE;
635 return exitcode;