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 free(p->m_tcname); 202 p->m_tcname = NULL; 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 **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 free(tcs); 284 tcs = NULL; 285 } 286 287 /* --------------------------------------------------------------------- 288 * Main. 289 * --------------------------------------------------------------------- */ 290 291 static 292 atf_error_t 293 handle_tcarg(const char *tcarg, char **tcname_out, enum tc_part *tcpart) 294 { 295 char *delim, *tcname; 296 atf_error_t err; 297 298 *tcname_out = NULL; 299 tcname = strdup(tcarg); 300 if (tcname == NULL) { 301 return atf_no_memory_error(); 302 } 303 304 err = atf_no_error(); 305 306 delim = strchr(tcname, ':'); 307 if (delim != NULL) { 308 *delim = '\0'; 309 310 delim++; 311 if (strcmp(delim, "body") == 0) { 312 *tcpart = BODY; 313 } else if (strcmp(delim, "cleanup") == 0) { 314 *tcpart = CLEANUP; 315 } else { 316 err = usage_error("Invalid test case part `%s'", delim); 317 free(tcname); 318 } 319 } 320 321 if (!atf_is_error(err)) 322 *tcname_out = tcname; 323 return err; 324 } 325 326 static 327 atf_error_t 328 process_params(int argc, char **argv, struct params *p) 329 { 330 atf_error_t err; 331 int ch; 332 int old_opterr; 333 334 err = atf_no_error(); 335 336 old_opterr = opterr; 337 opterr = 0; 338 while (!atf_is_error(err) && 339 (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) { 340 switch (ch) { 341 case 'l': 342 p->m_do_list = true; 343 break; 344 345 case 'r': 346 err = replace_path_param(&p->m_resfile, optarg); 347 break; 348 349 case 's': 350 err = replace_path_param(&p->m_srcdir, optarg); 351 break; 352 353 case 'v': 354 err = parse_vflag(optarg, &p->m_config); 355 break; 356 357 case ':': 358 err = usage_error("Option -%c requires an argument.", optopt); 359 break; 360 361 case '?': 362 default: 363 err = usage_error("Unknown option -%c.", optopt); 364 } 365 } 366 argc -= optind; 367 argv += optind; 368 369 /* Clear getopt state just in case the test wants to use it. */ 370 opterr = old_opterr; 371 optind = 1; 372 #if defined(HAVE_OPTRESET) 373 optreset = 1; 374 #endif 375 376 if (!atf_is_error(err)) { 377 if (p->m_do_list) { 378 if (argc > 0) 379 err = usage_error("Cannot provide test case names with -l"); 380 } else { 381 if (argc == 0) 382 err = usage_error("Must provide a test case name"); 383 else if (argc == 1) 384 err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart); 385 else if (argc > 1) { 386 err = usage_error("Cannot provide more than one test case " 387 "name"); 388 } 389 } 390 } 391 392 return err; 393 } 394 395 static 396 atf_error_t 397 srcdir_strip_libtool(atf_fs_path_t *srcdir) 398 { 399 atf_error_t err; 400 atf_fs_path_t parent; 401 402 err = atf_fs_path_branch_path(srcdir, &parent); 403 if (atf_is_error(err)) 404 goto out; 405 406 atf_fs_path_fini(srcdir); 407 *srcdir = parent; 408 409 INV(!atf_is_error(err)); 410 out: 411 return err; 412 } 413 414 static 415 atf_error_t 416 handle_srcdir(struct params *p) 417 { 418 atf_error_t err; 419 atf_dynstr_t leafname; 420 atf_fs_path_t exe, srcdir; 421 bool b; 422 423 err = atf_fs_path_copy(&srcdir, &p->m_srcdir); 424 if (atf_is_error(err)) 425 goto out; 426 427 if (!atf_fs_path_is_absolute(&srcdir)) { 428 atf_fs_path_t srcdirabs; 429 430 err = atf_fs_path_to_absolute(&srcdir, &srcdirabs); 431 if (atf_is_error(err)) 432 goto out_srcdir; 433 434 atf_fs_path_fini(&srcdir); 435 srcdir = srcdirabs; 436 } 437 438 err = atf_fs_path_leaf_name(&srcdir, &leafname); 439 if (atf_is_error(err)) 440 goto out_srcdir; 441 else { 442 const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs"); 443 atf_dynstr_fini(&leafname); 444 445 if (libs) { 446 err = srcdir_strip_libtool(&srcdir); 447 if (atf_is_error(err)) 448 goto out; 449 } 450 } 451 452 err = atf_fs_path_copy(&exe, &srcdir); 453 if (atf_is_error(err)) 454 goto out_srcdir; 455 456 err = atf_fs_path_append_fmt(&exe, "%s", progname); 457 if (atf_is_error(err)) 458 goto out_exe; 459 460 err = atf_fs_exists(&exe, &b); 461 if (!atf_is_error(err)) { 462 if (b) { 463 err = atf_map_insert(&p->m_config, "srcdir", 464 strdup(atf_fs_path_cstring(&srcdir)), true); 465 } else { 466 err = user_error("Cannot find the test program in the source " 467 "directory `%s'", atf_fs_path_cstring(&srcdir)); 468 } 469 } 470 471 out_exe: 472 atf_fs_path_fini(&exe); 473 out_srcdir: 474 atf_fs_path_fini(&srcdir); 475 out: 476 return err; 477 } 478 479 static 480 atf_error_t 481 run_tc(const atf_tp_t *tp, struct params *p, int *exitcode) 482 { 483 atf_error_t err; 484 485 if (!atf_tp_has_tc(tp, p->m_tcname)) 486 return usage_error("Unknown test case `%s'", p->m_tcname); 487 488 err = atf_no_error(); 489 490 if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get( 491 "__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0) 492 { 493 print_warning("Running test cases outside of kyua(1) is unsupported"); 494 print_warning("No isolation nor timeout control is being applied; you " 495 "may get unexpected failures; see atf-test-case(4)"); 496 } 497 498 switch (p->m_tcpart) { 499 case BODY: 500 err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile)); 501 if (atf_is_error(err)) { 502 /* TODO: Handle error */ 503 *exitcode = EXIT_FAILURE; 504 atf_error_free(err); 505 } else { 506 *exitcode = EXIT_SUCCESS; 507 } 508 509 break; 510 511 case CLEANUP: 512 err = atf_tp_cleanup(tp, p->m_tcname); 513 if (atf_is_error(err)) { 514 /* TODO: Handle error */ 515 *exitcode = EXIT_FAILURE; 516 atf_error_free(err); 517 } else { 518 *exitcode = EXIT_SUCCESS; 519 } 520 521 break; 522 523 default: 524 UNREACHABLE; 525 } 526 527 INV(!atf_is_error(err)); 528 return err; 529 } 530 531 static 532 atf_error_t 533 controlled_main(int argc, char **argv, 534 atf_error_t (*add_tcs_hook)(atf_tp_t *), 535 int *exitcode) 536 { 537 atf_error_t err; 538 struct params p; 539 atf_tp_t tp; 540 char **raw_config; 541 542 err = params_init(&p, argv[0]); 543 if (atf_is_error(err)) 544 goto out; 545 546 err = process_params(argc, argv, &p); 547 if (atf_is_error(err)) 548 goto out_p; 549 550 err = handle_srcdir(&p); 551 if (atf_is_error(err)) 552 goto out_p; 553 554 raw_config = atf_map_to_charpp(&p.m_config); 555 if (raw_config == NULL) { 556 err = atf_no_memory_error(); 557 goto out_p; 558 } 559 err = atf_tp_init(&tp, (const char* const*)raw_config); 560 atf_utils_free_charpp(raw_config); 561 if (atf_is_error(err)) 562 goto out_p; 563 564 err = add_tcs_hook(&tp); 565 if (atf_is_error(err)) 566 goto out_tp; 567 568 if (p.m_do_list) { 569 list_tcs(&tp); 570 INV(!atf_is_error(err)); 571 *exitcode = EXIT_SUCCESS; 572 } else { 573 err = run_tc(&tp, &p, exitcode); 574 } 575 576 out_tp: 577 atf_tp_fini(&tp); 578 out_p: 579 params_fini(&p); 580 out: 581 return err; 582 } 583 584 int 585 atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *)) 586 { 587 atf_error_t err; 588 int exitcode; 589 590 progname = strrchr(argv[0], '/'); 591 if (progname == NULL) 592 progname = argv[0]; 593 else 594 progname++; 595 596 exitcode = EXIT_FAILURE; /* Silence GCC warning. */ 597 err = controlled_main(argc, argv, add_tcs_hook, &exitcode); 598 if (atf_is_error(err)) { 599 print_error(err); 600 atf_error_free(err); 601 exitcode = EXIT_FAILURE; 602 } 603 604 return exitcode; 605 } 606