2 * Automated Testing Framework (atf)
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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)
41 #include "atf-c/config.h"
42 #include "atf-c/dynstr.h"
43 #include "atf-c/error.h"
44 #include "atf-c/expand.h"
46 #include "atf-c/object.h"
47 #include "atf-c/map.h"
48 #include "atf-c/sanity.h"
53 #if defined(HAVE_GNU_GETOPT)
54 # define GETOPT_POSIX "+"
56 # define GETOPT_POSIX ""
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
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 { \
77 name ## _format(const atf_error_t err, char *buf, size_t buflen) \
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); \
89 name ## _error(const char *fmt, ...) \
92 struct name ## _error_data data; \
96 vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \
99 err = atf_error_new(#name, &data, sizeof(data), name ## _format); \
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. */
118 print_tag(FILE *f
, const char *tag
, bool repeat
, size_t col
,
119 const char *fmt
, ...)
125 err
= atf_dynstr_init(&dest
);
126 if (atf_is_error(err
))
130 err
= atf_ui_format_ap(&dest
, tag
, repeat
, col
, fmt
, ap
);
132 if (atf_is_error(err
))
135 fprintf(f
, "%s\n", atf_dynstr_cstring(&dest
));
138 atf_dynstr_fini(&dest
);
145 print_error(const atf_error_t err
)
147 PRE(atf_is_error(err
));
149 if (atf_error_is(err
, "no_memory")) {
152 atf_error_format(err
, buf
, sizeof(buf
));
154 fprintf(stderr
, "%s: %s\n", progname
, buf
);
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,
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 /* ---------------------------------------------------------------------
175 * --------------------------------------------------------------------- */
181 const char *m_srcdir
;
182 const char *m_workdir
;
183 atf_list_t m_tcglobs
;
189 params_init(struct params
*p
)
193 p
->m_do_list
= false;
194 p
->m_do_usage
= false;
195 p
->m_fd
= STDOUT_FILENO
;
197 p
->m_workdir
= atf_config_get("atf_workdir");
199 err
= atf_list_init(&p
->m_tcglobs
);
200 if (atf_is_error(err
))
203 err
= atf_map_init(&p
->m_config
);
204 if (atf_is_error(err
))
205 atf_list_fini(&p
->m_tcglobs
);
213 params_fini(struct params
*p
)
215 atf_map_fini(&p
->m_config
);
216 atf_list_fini(&p
->m_tcglobs
);
221 parse_rflag(const char *arg
, int *value
)
225 if (strlen(arg
) != 1 || !isdigit((int)arg
[0])) {
226 err
= usage_error("Invalid value for -r; must be a single digit.");
230 *value
= arg
[0] - '0';
231 INV(*value
>= 0 && *value
<= 9);
232 err
= atf_no_error();
240 parse_vflag(char *arg
, atf_map_t
*config
)
245 split
= strchr(arg
, '=');
247 err
= usage_error("-v requires an argument of the form var=value");
254 err
= atf_map_insert(config
, arg
, split
, false);
260 /* ---------------------------------------------------------------------
261 * Test case filtering.
262 * --------------------------------------------------------------------- */
266 match_tcs(const atf_tp_t
*tp
, const char *glob
, atf_list_t
*ids
)
270 atf_list_citer_t iter
;
272 err
= atf_no_error();
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
)) {
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);
287 if (strcmp(glob
, tc
->m_ident
) == 0) {
288 err
= atf_list_append(ids
, strdup(ident
), true);
293 if (atf_is_error(err
))
297 if (!atf_is_error(err
) && !found
)
298 err
= user_error("Unknown test case `%s'", glob
);
305 filter_tcs(const atf_tp_t
*tp
, const atf_list_t
*globs
, atf_list_t
*ids
)
308 atf_list_citer_t iter
;
310 err
= atf_list_init(ids
);
311 if (atf_is_error(err
))
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
)) {
329 list_tcs(const atf_tp_t
*tp
, const atf_list_t
*tcids
)
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. */
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
));
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
))
365 /* ---------------------------------------------------------------------
367 * --------------------------------------------------------------------- */
373 print_tag(stdout
, "Usage: ", false, 0,
374 "%s [options] [test_case1 [.. test_caseN]]", progname
);
376 print_tag(stdout
, "", false, 0, "This is an independent atf test "
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 "
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 "
396 print_tag(stdout
, "", false, 0, "For more details please see "
397 "atf-test-program(1) and atf(7).");
402 process_params(int argc
, char **argv
, struct params
*p
)
407 err
= params_init(p
);
408 if (atf_is_error(err
))
412 while (!atf_is_error(err
) &&
413 (ch
= getopt(argc
, argv
, GETOPT_POSIX
":hlr:s:v:w:")) != -1) {
416 p
->m_do_usage
= true;
424 err
= parse_rflag(optarg
, &p
->m_fd
);
428 p
->m_srcdir
= optarg
;
432 err
= parse_vflag(optarg
, &p
->m_config
);
436 p
->m_workdir
= optarg
;
440 err
= usage_error("Option -%c requires an argument.", optopt
);
445 err
= usage_error("Unknown option -%c.", optopt
);
451 if (!atf_is_error(err
)) {
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
))
469 handle_srcdir(struct params
*p
)
472 atf_fs_path_t exe
, srcdir
;
475 err
= atf_fs_path_init_fmt(&srcdir
, "%s", p
->m_srcdir
);
476 if (atf_is_error(err
))
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
))
486 atf_fs_path_fini(&srcdir
);
490 err
= atf_fs_path_copy(&exe
, &srcdir
);
491 if (atf_is_error(err
))
494 err
= atf_fs_path_append_fmt(&exe
, "%s", progname
);
495 if (atf_is_error(err
))
498 err
= atf_fs_exists(&exe
, &b
);
499 if (!atf_is_error(err
)) {
501 err
= atf_map_insert(&p
->m_config
, "srcdir",
502 strdup(atf_fs_path_cstring(&srcdir
)), true);
504 err
= user_error("Cannot find the test program in the source "
505 "directory `%s'", p
->m_srcdir
);
510 atf_fs_path_fini(&exe
);
512 atf_fs_path_fini(&srcdir
);
519 handle_workdir(struct params
*p
, atf_fs_path_t
*workdir
)
524 err
= atf_fs_path_init_fmt(workdir
, "%s", p
->m_workdir
);
525 if (atf_is_error(err
))
528 err
= atf_fs_exists(workdir
, &b
);
529 if (atf_is_error(err
)) {
530 atf_fs_path_fini(workdir
);
535 atf_fs_path_fini(workdir
);
536 err
= user_error("Cannot find the work directory `%s'",
539 INV(!atf_is_error(err
));
547 controlled_main(int argc
, char **argv
,
548 atf_error_t (*add_tcs_hook
)(atf_tp_t
*),
555 atf_fs_path_t workdir
;
557 err
= process_params(argc
, argv
, &p
);
558 if (atf_is_error(err
))
563 err
= usage_error("-h must be given alone.");
566 *exitcode
= EXIT_SUCCESS
;
571 err
= handle_srcdir(&p
);
572 if (atf_is_error(err
))
575 err
= handle_workdir(&p
, &workdir
);
576 if (atf_is_error(err
))
579 err
= atf_tp_init(&tp
, &p
.m_config
);
580 if (atf_is_error(err
))
583 err
= add_tcs_hook(&tp
);
584 if (atf_is_error(err
))
587 err
= filter_tcs(&tp
, &p
.m_tcglobs
, &tcids
);
588 if (atf_is_error(err
))
592 err
= list_tcs(&tp
, &tcids
);
593 if (!atf_is_error(err
))
594 *exitcode
= EXIT_SUCCESS
;
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
);
606 atf_fs_path_fini(&workdir
);
614 atf_tp_main(int argc
, char **argv
, atf_error_t (*add_tcs_hook
)(atf_tp_t
*))
621 progname
= strrchr(argv
[0], '/');
622 if (progname
== NULL
)
627 exitcode
= EXIT_FAILURE
; /* Silence GCC warning. */
628 err
= controlled_main(argc
, argv
, add_tcs_hook
, &exitcode
);
629 if (atf_is_error(err
)) {
632 exitcode
= EXIT_FAILURE
;