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