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#if defined(HAVE_CONFIG_H) 31#include "bconfig.h" 32#endif 33 34#include <ctype.h> 35#include <stdarg.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <string.h> 39#include <unistd.h> 40 41#include "atf-c/error.h" 42#include "atf-c/tc.h" 43#include "atf-c/tp.h" 44#include "atf-c/utils.h" 45 46#include "dynstr.h" 47#include "fs.h" 48#include "map.h" 49#include "sanity.h" 50 51#if defined(HAVE_GNU_GETOPT) 52# define GETOPT_POSIX "+" 53#else 54# define GETOPT_POSIX "" 55#endif 56 57static const char *progname = NULL; 58 59/* This prototype is provided by macros.h during instantiation of the test 60 * program, so it can be kept private. Don't know if that's the best idea 61 * though. */ 62int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *)); 63 64enum tc_part { 65 BODY, 66 CLEANUP, 67}; 68 69/* --------------------------------------------------------------------- 70 * The "usage" and "user" error types. 71 * --------------------------------------------------------------------- */ 72 73#define FREE_FORM_ERROR(name) \ 74 struct name ## _error_data { \ 75 char m_what[2048]; \ 76 }; \ 77 \ 78 static \ 79 void \ 80 name ## _format(const atf_error_t err, char *buf, size_t buflen) \ 81 { \ 82 const struct name ## _error_data *data; \ 83 \ 84 PRE(atf_error_is(err, #name)); \ 85 \ 86 data = atf_error_data(err); \ 87 snprintf(buf, buflen, "%s", data->m_what); \ 88 } \ 89 \ 90 static \ 91 atf_error_t \ 92 name ## _error(const char *fmt, ...) \ 93 { \ 94 atf_error_t err; \ 95 struct name ## _error_data data; \ 96 va_list ap; \ 97 \ 98 va_start(ap, fmt); \ 99 vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \ 100 va_end(ap); \ 101 \ 102 err = atf_error_new(#name, &data, sizeof(data), name ## _format); \ 103 \ 104 return err; \ 105 } 106 107FREE_FORM_ERROR(usage); 108FREE_FORM_ERROR(user); 109 110/* --------------------------------------------------------------------- 111 * Printing functions. 112 * --------------------------------------------------------------------- */ 113 114static 115void 116print_error(const atf_error_t err) 117{ 118 char buf[4096]; 119 120 PRE(atf_is_error(err)); 121 122 atf_error_format(err, buf, sizeof(buf)); 123 fprintf(stderr, "%s: ERROR: %s\n", progname, buf); 124 125 if (atf_error_is(err, "usage")) 126 fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n", 127 progname); 128} 129 130/* --------------------------------------------------------------------- 131 * Options handling. 132 * --------------------------------------------------------------------- */ 133 134struct params { 135 bool m_do_list; 136 atf_fs_path_t m_srcdir; 137 char *m_tcname; 138 enum tc_part m_tcpart; 139 atf_fs_path_t m_resfile; 140 atf_map_t m_config; 141}; 142 143static 144atf_error_t 145argv0_to_dir(const char *argv0, atf_fs_path_t *dir) 146{ 147 atf_error_t err; 148 atf_fs_path_t temp; 149 150 err = atf_fs_path_init_fmt(&temp, "%s", argv0); 151 if (atf_is_error(err)) 152 goto out; 153 154 err = atf_fs_path_branch_path(&temp, dir); 155 156 atf_fs_path_fini(&temp); 157out: 158 return err; 159} 160 161static 162atf_error_t 163params_init(struct params *p, const char *argv0) 164{ 165 atf_error_t err; 166 167 p->m_do_list = false; 168 p->m_tcname = NULL; 169 p->m_tcpart = BODY; 170 171 err = argv0_to_dir(argv0, &p->m_srcdir); 172 if (atf_is_error(err)) 173 return err; 174 175 err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout"); 176 if (atf_is_error(err)) { 177 atf_fs_path_fini(&p->m_srcdir); 178 return err; 179 } 180 181 err = atf_map_init(&p->m_config); 182 if (atf_is_error(err)) { 183 atf_fs_path_fini(&p->m_resfile); 184 atf_fs_path_fini(&p->m_srcdir); 185 return err; 186 } 187 188 return err; 189} 190 191static 192void 193params_fini(struct params *p) 194{ 195 atf_map_fini(&p->m_config); 196 atf_fs_path_fini(&p->m_resfile); 197 atf_fs_path_fini(&p->m_srcdir); 198 if (p->m_tcname != NULL) 199 free(p->m_tcname); 200} 201 202static 203atf_error_t 204parse_vflag(char *arg, atf_map_t *config) 205{ 206 atf_error_t err; 207 char *split; 208 209 split = strchr(arg, '='); 210 if (split == NULL) { 211 err = usage_error("-v requires an argument of the form var=value"); 212 goto out; 213 } 214 215 *split = '\0'; 216 split++; 217 218 err = atf_map_insert(config, arg, split, false); 219 220out: 221 return err; 222} 223 224static 225atf_error_t 226replace_path_param(atf_fs_path_t *param, const char *value) 227{ 228 atf_error_t err; 229 atf_fs_path_t temp; 230 231 err = atf_fs_path_init_fmt(&temp, "%s", value); 232 if (!atf_is_error(err)) { 233 atf_fs_path_fini(param); 234 *param = temp; 235 } 236 237 return err; 238} 239 240/* --------------------------------------------------------------------- 241 * Test case listing. 242 * --------------------------------------------------------------------- */ 243 244static 245void 246list_tcs(const atf_tp_t *tp) 247{ 248 const atf_tc_t *const *tcs; 249 const atf_tc_t *const *tcsptr; 250 251 printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n"); 252 253 tcs = atf_tp_get_tcs(tp); 254 INV(tcs != NULL); /* Should be checked. */ 255 for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) { 256 const atf_tc_t *tc = *tcsptr; 257 char **vars = atf_tc_get_md_vars(tc); 258 char **ptr; 259 260 INV(vars != NULL); /* Should be checked. */ 261 262 if (tcsptr != tcs) /* Not first. */ 263 printf("\n"); 264 265 for (ptr = vars; *ptr != NULL; ptr += 2) { 266 if (strcmp(*ptr, "ident") == 0) { 267 printf("ident: %s\n", *(ptr + 1)); 268 break; 269 } 270 } 271 272 for (ptr = vars; *ptr != NULL; ptr += 2) { 273 if (strcmp(*ptr, "ident") != 0) { 274 printf("%s: %s\n", *ptr, *(ptr + 1)); 275 } 276 } 277 278 atf_utils_free_charpp(vars); 279 } 280} 281 282/* --------------------------------------------------------------------- 283 * Main. 284 * --------------------------------------------------------------------- */ 285 286static 287atf_error_t 288handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart) 289{ 290 atf_error_t err; 291 292 err = atf_no_error(); 293 294 *tcname = strdup(tcarg); 295 if (*tcname == NULL) { 296 err = atf_no_memory_error(); 297 goto out; 298 } 299 300 char *delim = strchr(*tcname, ':'); 301 if (delim != NULL) { 302 *delim = '\0'; 303 304 delim++; 305 if (strcmp(delim, "body") == 0) { 306 *tcpart = BODY; 307 } else if (strcmp(delim, "cleanup") == 0) { 308 *tcpart = CLEANUP; 309 } else { 310 err = usage_error("Invalid test case part `%s'", delim); 311 goto out; 312 } 313 } 314 315out: 316 return err; 317} 318 319static 320atf_error_t 321process_params(int argc, char **argv, struct params *p) 322{ 323 atf_error_t err; 324 int ch; 325 326 err = params_init(p, argv[0]); 327 if (atf_is_error(err)) 328 goto out; 329 330 opterr = 0; 331 while (!atf_is_error(err) && 332 (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) { 333 switch (ch) { 334 case 'l': 335 p->m_do_list = true; 336 break; 337 338 case 'r': 339 err = replace_path_param(&p->m_resfile, optarg); 340 break; 341 342 case 's': 343 err = replace_path_param(&p->m_srcdir, optarg); 344 break; 345 346 case 'v': 347 err = parse_vflag(optarg, &p->m_config); 348 break; 349 350 case ':': 351 err = usage_error("Option -%c requires an argument.", optopt); 352 break; 353 354 case '?': 355 default: 356 err = usage_error("Unknown option -%c.", optopt); 357 } 358 } 359 argc -= optind; 360 argv += optind; 361 362 /* Clear getopt state just in case the test wants to use it. */ 363 optind = 1; 364#if defined(HAVE_OPTRESET) 365 optreset = 1; 366#endif 367 368 if (!atf_is_error(err)) { 369 if (p->m_do_list) { 370 if (argc > 0) 371 err = usage_error("Cannot provide test case names with -l"); 372 } else { 373 if (argc == 0) 374 err = usage_error("Must provide a test case name"); 375 else if (argc == 1) 376 err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart); 377 else if (argc > 1) { 378 err = usage_error("Cannot provide more than one test case " 379 "name"); 380 } 381 } 382 } 383 384 if (atf_is_error(err)) 385 params_fini(p); 386 387out: 388 return err; 389} 390 391static 392atf_error_t 393srcdir_strip_libtool(atf_fs_path_t *srcdir) 394{ 395 atf_error_t err; 396 atf_fs_path_t parent; 397 398 err = atf_fs_path_branch_path(srcdir, &parent); 399 if (atf_is_error(err)) 400 goto out; 401 402 atf_fs_path_fini(srcdir); 403 *srcdir = parent; 404 405 INV(!atf_is_error(err)); 406out: 407 return err; 408} 409 410static 411atf_error_t 412handle_srcdir(struct params *p) 413{ 414 atf_error_t err; 415 atf_dynstr_t leafname; 416 atf_fs_path_t exe, srcdir; 417 bool b; 418 419 err = atf_fs_path_copy(&srcdir, &p->m_srcdir); 420 if (atf_is_error(err)) 421 goto out; 422 423 if (!atf_fs_path_is_absolute(&srcdir)) { 424 atf_fs_path_t srcdirabs; 425 426 err = atf_fs_path_to_absolute(&srcdir, &srcdirabs); 427 if (atf_is_error(err)) 428 goto out_srcdir; 429 430 atf_fs_path_fini(&srcdir); 431 srcdir = srcdirabs; 432 } 433 434 err = atf_fs_path_leaf_name(&srcdir, &leafname); 435 if (atf_is_error(err)) 436 goto out_srcdir; 437 else { 438 const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs"); 439 atf_dynstr_fini(&leafname); 440 441 if (libs) { 442 err = srcdir_strip_libtool(&srcdir); 443 if (atf_is_error(err)) 444 goto out; 445 } 446 } 447 448 err = atf_fs_path_copy(&exe, &srcdir); 449 if (atf_is_error(err)) 450 goto out_srcdir; 451 452 err = atf_fs_path_append_fmt(&exe, "%s", progname); 453 if (atf_is_error(err)) 454 goto out_exe; 455 456 err = atf_fs_exists(&exe, &b); 457 if (!atf_is_error(err)) { 458 if (b) { 459 err = atf_map_insert(&p->m_config, "srcdir", 460 strdup(atf_fs_path_cstring(&srcdir)), true); 461 } else { 462 err = user_error("Cannot find the test program in the source " 463 "directory `%s'", atf_fs_path_cstring(&srcdir)); 464 } 465 } 466 467out_exe: 468 atf_fs_path_fini(&exe); 469out_srcdir: 470 atf_fs_path_fini(&srcdir); 471out: 472 return err; 473} 474 475static 476atf_error_t 477run_tc(const atf_tp_t *tp, struct params *p, int *exitcode) 478{ 479 atf_error_t err; 480 481 err = atf_no_error(); 482 483 if (!atf_tp_has_tc(tp, p->m_tcname)) { 484 err = usage_error("Unknown test case `%s'", p->m_tcname); 485 goto out; 486 } 487 488 switch (p->m_tcpart) { 489 case BODY: 490 err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile)); 491 if (atf_is_error(err)) { 492 /* TODO: Handle error */ 493 *exitcode = EXIT_FAILURE; 494 atf_error_free(err); 495 } else { 496 *exitcode = EXIT_SUCCESS; 497 } 498 499 break; 500 501 case CLEANUP: 502 err = atf_tp_cleanup(tp, p->m_tcname); 503 if (atf_is_error(err)) { 504 /* TODO: Handle error */ 505 *exitcode = EXIT_FAILURE; 506 atf_error_free(err); 507 } else { 508 *exitcode = EXIT_SUCCESS; 509 } 510 511 break; 512 513 default: 514 UNREACHABLE; 515 } 516 517 INV(!atf_is_error(err)); 518out: 519 return err; 520} 521 522static 523atf_error_t 524controlled_main(int argc, char **argv, 525 atf_error_t (*add_tcs_hook)(atf_tp_t *), 526 int *exitcode) 527{ 528 atf_error_t err; 529 struct params p; 530 atf_tp_t tp; 531 char **raw_config; 532 533 err = process_params(argc, argv, &p); 534 if (atf_is_error(err)) 535 goto out; 536 537 err = handle_srcdir(&p); 538 if (atf_is_error(err)) 539 goto out_p; 540 541 raw_config = atf_map_to_charpp(&p.m_config); 542 if (raw_config == NULL) { 543 err = atf_no_memory_error(); 544 goto out_p; 545 } 546 err = atf_tp_init(&tp, (const char* const*)raw_config); 547 atf_utils_free_charpp(raw_config); 548 if (atf_is_error(err)) 549 goto out_p; 550 551 err = add_tcs_hook(&tp); 552 if (atf_is_error(err)) 553 goto out_tp; 554 555 if (p.m_do_list) { 556 list_tcs(&tp); 557 INV(!atf_is_error(err)); 558 *exitcode = EXIT_SUCCESS; 559 } else { 560 err = run_tc(&tp, &p, exitcode); 561 } 562 563out_tp: 564 atf_tp_fini(&tp); 565out_p: 566 params_fini(&p); 567out: 568 return err; 569} 570 571int 572atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *)) 573{ 574 atf_error_t err; 575 int exitcode; 576 577 progname = strrchr(argv[0], '/'); 578 if (progname == NULL) 579 progname = argv[0]; 580 else 581 progname++; 582 583 /* Libtool workaround: if running from within the source tree (binaries 584 * that are not installed yet), skip the "lt-" prefix added to files in 585 * the ".libs" directory to show the real (not temporary) name. */ 586 if (strncmp(progname, "lt-", 3) == 0) 587 progname += 3; 588 589 exitcode = EXIT_FAILURE; /* Silence GCC warning. */ 590 err = controlled_main(argc, argv, add_tcs_hook, &exitcode); 591 if (atf_is_error(err)) { 592 print_error(err); 593 atf_error_free(err); 594 exitcode = EXIT_FAILURE; 595 } 596 597 return exitcode; 598} 599