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