1 /* $NetBSD: t_api.c,v 1.2 2018/04/07 22:37:30 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2004-2017 Internet Systems Consortium, Inc. ("ISC") 5 * Copyright (C) 1999-2003 Internet Software Consortium. 6 * 7 * This Source Code Form is subject to the terms of the Mozilla Public 8 * License, v. 2.0. If a copy of the MPL was not distributed with this 9 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 /* Id: t_api.c,v 1.4 2009/10/28 04:12:30 sar Exp */ 21 22 /*! \file */ 23 24 /* 25 * This test API framework is taken from the BIND 9 code. It has been 26 * modified to remove the DNS-specific parts, and the BIND-specific 27 * parts. 28 * 29 * The DNS-specific parts are now wrapped with the DNS_SUPPORT macro, 30 * and the BIND-specific parts are now wrapped with the BIND_SUPPORT 31 * macro. 32 */ 33 #include <sys/cdefs.h> 34 __RCSID("$NetBSD: t_api.c,v 1.2 2018/04/07 22:37:30 christos Exp $"); 35 36 #include <config.h> 37 38 #include <ctype.h> 39 #include <errno.h> 40 #include <limits.h> 41 #include <signal.h> 42 #include <stdarg.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <time.h> 46 #include <unistd.h> 47 48 #include <sys/wait.h> 49 50 #include <isc/boolean.h> 51 #include <isc/commandline.h> 52 #include <isc/print.h> 53 #include <isc/string.h> 54 #include <isc/mem.h> 55 56 #ifdef DNS_SUPPORT 57 #include <dns/compress.h> 58 #include <omapip/result.h> 59 #endif /* DNS_SUPPORT */ 60 61 #ifndef BIND_SUPPORT 62 #define isc_commandline_parse getopt 63 #define isc_commandline_argument optarg 64 #define isc_commandline_option optopt 65 #endif /* BIND_SUPPORT */ 66 67 #include "t_api.h" 68 #include "cdefs.h" 69 70 static const char *Usage = 71 "\t-a : run all tests\n" 72 "\t-b <dir> : chdir to dir before running tests" 73 "\t-c <config_file> : use specified config file\n" 74 "\t-d <debug_level> : set debug level to debug_level\n" 75 "\t-h : print test info\n" 76 "\t-u : print usage info\n" 77 "\t-n <test_name> : run specified test name\n" 78 "\t-t <test_number> : run specified test number\n" 79 "\t-x : don't execute tests in a subproc\n" 80 "\t-q <timeout> : use 'timeout' as the timeout value\n"; 81 /*!< 82 * -a --> run all tests 83 * -b dir --> chdir to dir before running tests 84 * -c config --> use config file 'config' 85 * -d --> turn on api debugging 86 * -h --> print out available test names 87 * -u --> print usage info 88 * -n name --> run test named name 89 * -tn --> run test n 90 * -x --> don't execute testcases in a subproc 91 * -q timeout --> use 'timeout' as the timeout value 92 */ 93 94 #define T_MAXTESTS 256 /*% must be 0 mod 8 */ 95 #define T_MAXENV 256 96 #define T_DEFAULT_CONFIG "t_config" 97 #define T_BUFSIZ 256 98 #define T_BIGBUF 4096 99 100 #define T_TCTOUT 60 101 102 int T_debug; 103 int T_timeout; 104 pid_t T_pid; 105 static const char * T_config; 106 static char T_tvec[T_MAXTESTS / 8]; 107 static char * T_env[T_MAXENV + 1]; 108 static char T_buf[T_BIGBUF]; 109 static char * T_dir; 110 111 static int 112 t_initconf(const char *path); 113 114 static int 115 t_dumpconf(const char *path); 116 117 static int 118 t_putinfo(const char *key, const char *info); 119 120 static char * 121 t_getdate(char *buf, size_t buflen); 122 123 static void 124 printhelp(void); 125 126 static void 127 printusage(void); 128 129 static int T_int; 130 131 uint16_t local_port = 0; 132 uint16_t remote_port = 0; 133 libdhcp_callbacks_t t_api_callbacks = { 134 &local_port, 135 &remote_port, 136 classify, 137 check_collection, 138 dhcp, 139 #ifdef DHCPv6 140 dhcpv6, 141 #endif /* DHCPv6 */ 142 bootp, 143 find_class, 144 parse_allow_deny, 145 dhcp_set_control_state, 146 }; 147 148 static void 149 t_sighandler(int sig) { 150 T_int = sig; 151 } 152 153 int 154 main(int argc, char **argv) { 155 int c; 156 int tnum; 157 int subprocs; 158 pid_t deadpid; 159 int status; 160 int len; 161 isc_boolean_t first; 162 testspec_t *pts; 163 struct sigaction sa; 164 165 #ifdef BIND_SUPPORT 166 isc_mem_debugging = ISC_MEM_DEBUGRECORD; 167 #endif /* BIND_SUPPORT */ 168 first = ISC_TRUE; 169 subprocs = 1; 170 T_timeout = T_TCTOUT; 171 172 libdhcp_callbacks_register(&t_api_callbacks); 173 174 /* 175 * -a option is now default. 176 */ 177 memset(T_tvec, 0xffff, sizeof(T_tvec)); 178 179 /* 180 * Parse args. 181 */ 182 while ((c = isc_commandline_parse(argc, argv, ":at:c:d:n:huxq:b:")) 183 != -1) { 184 if (c == 'a') { 185 /* 186 * Flag all tests to be run. 187 */ 188 memset(T_tvec, 0xffff, sizeof(T_tvec)); 189 } 190 else if (c == 'b') { 191 T_dir = isc_commandline_argument; 192 } 193 else if (c == 't') { 194 tnum = atoi(isc_commandline_argument); 195 if ((tnum > 0) && (tnum < T_MAXTESTS)) { 196 if (first) { 197 /* 198 * Turn off effect of -a default 199 * and allow multiple -t and -n 200 * options. 201 */ 202 memset(T_tvec, 0, sizeof(T_tvec)); 203 first = ISC_FALSE; 204 } 205 /* 206 * Flag test tnum to be run. 207 */ 208 tnum -= 1; 209 T_tvec[tnum / 8] |= (0x01 << (tnum % 8)); 210 } 211 } 212 else if (c == 'c') { 213 T_config = isc_commandline_argument; 214 } 215 else if (c == 'd') { 216 T_debug = atoi(isc_commandline_argument); 217 } 218 else if (c == 'n') { 219 pts = &T_testlist[0]; 220 tnum = 0; 221 while (pts->pfv != NULL) { 222 if (! strcmp(pts->func_name, 223 isc_commandline_argument)) { 224 if (first) { 225 memset(T_tvec, 0, 226 sizeof(T_tvec)); 227 first = ISC_FALSE; 228 } 229 T_tvec[tnum/8] |= (0x01 << (tnum%8)); 230 break; 231 } 232 ++pts; 233 ++tnum; 234 } 235 if (pts->pfv == NULL) { 236 fprintf(stderr, "no such test %s\n", 237 isc_commandline_argument); 238 exit(1); 239 } 240 } 241 else if (c == 'h') { 242 printhelp(); 243 exit(0); 244 } 245 else if (c == 'u') { 246 printusage(); 247 exit(0); 248 } 249 else if (c == 'x') { 250 subprocs = 0; 251 } 252 else if (c == 'q') { 253 T_timeout = atoi(isc_commandline_argument); 254 } 255 else if (c == ':') { 256 fprintf(stderr, "Option -%c requires an argument\n", 257 isc_commandline_option); 258 exit(1); 259 } 260 else if (c == '?') { 261 fprintf(stderr, "Unrecognized option -%c\n", 262 isc_commandline_option); 263 exit(1); 264 } 265 } 266 267 /* 268 * Set cwd. 269 */ 270 271 if (T_dir != NULL) 272 IGNORE_RET (chdir(T_dir)); 273 274 /* 275 * We don't want buffered output. 276 */ 277 278 (void)setbuf(stdout, NULL); 279 (void)setbuf(stderr, NULL); 280 281 /* 282 * Setup signals. 283 */ 284 285 sa.sa_flags = 0; 286 sigfillset(&sa.sa_mask); 287 288 #ifdef SIGCHLD 289 /* 290 * This is mostly here for NetBSD's pthread implementation, until 291 * people catch up to the latest unproven-pthread package. 292 */ 293 sa.sa_handler = SIG_DFL; 294 (void)sigaction(SIGCHLD, &sa, NULL); 295 #endif 296 297 sa.sa_handler = t_sighandler; 298 (void)sigaction(SIGINT, &sa, NULL); 299 (void)sigaction(SIGALRM, &sa, NULL); 300 301 /* 302 * Output start stanza to journal. 303 */ 304 305 snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); 306 len = strlen(T_buf); 307 (void) t_getdate(T_buf + len, T_BIGBUF - len); 308 t_putinfo("S", T_buf); 309 310 /* 311 * Setup the test environment using the config file. 312 */ 313 314 if (T_config == NULL) 315 T_config = T_DEFAULT_CONFIG; 316 317 t_initconf(T_config); 318 if (T_debug) 319 t_dumpconf(T_config); 320 321 /* 322 * Now invoke all the test cases. 323 */ 324 325 tnum = 0; 326 pts = &T_testlist[0]; 327 while (*pts->pfv != NULL) { 328 if (T_tvec[tnum / 8] & (0x01 << (tnum % 8))) { 329 if (subprocs) { 330 T_pid = fork(); 331 if (T_pid == 0) { 332 (*pts->pfv)(); 333 exit(0); 334 } else if (T_pid > 0) { 335 336 T_int = 0; 337 sa.sa_handler = t_sighandler; 338 (void)sigaction(SIGALRM, &sa, NULL); 339 alarm(T_timeout); 340 341 deadpid = (pid_t) -1; 342 while (deadpid != T_pid) { 343 deadpid = 344 waitpid(T_pid, &status, 0); 345 if (deadpid == T_pid) { 346 if (WIFSIGNALED(status)) { 347 if (WTERMSIG(status) == 348 SIGTERM) 349 t_info( 350 "the test case timed out\n"); 351 else 352 t_info( 353 "the test case caused exception %d\n", 354 WTERMSIG(status)); 355 t_result(T_UNRESOLVED); 356 } 357 } else if ((deadpid == -1) && 358 (errno == EINTR) && 359 T_int) { 360 kill(T_pid, SIGTERM); 361 T_int = 0; 362 } 363 else if ((deadpid == -1) && 364 ((errno == ECHILD) || 365 (errno == ESRCH))) 366 break; 367 } 368 369 alarm(0); 370 sa.sa_handler = SIG_IGN; 371 (void)sigaction(SIGALRM, &sa, NULL); 372 } else { 373 t_info("fork failed, errno == %d\n", 374 errno); 375 t_result(T_UNRESOLVED); 376 } 377 } 378 else { 379 (*pts->pfv)(); 380 } 381 } 382 ++pts; 383 ++tnum; 384 } 385 386 snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); 387 len = strlen(T_buf); 388 (void) t_getdate(T_buf + len, T_BIGBUF - len); 389 t_putinfo("E", T_buf); 390 391 return(0); 392 } 393 394 void 395 t_assert(const char *component, int anum, int class, const char *what, ...) { 396 va_list args; 397 398 (void)printf("T:%s:%d:%s\n", component, anum, class == T_REQUIRED ? 399 "A" : "C"); 400 401 /* 402 * Format text to a buffer. 403 */ 404 va_start(args, what); 405 (void)vsnprintf(T_buf, sizeof(T_buf), what, args); 406 va_end(args); 407 408 (void)t_putinfo("A", T_buf); 409 (void)printf("\n"); 410 } 411 412 void 413 t_info(const char *format, ...) { 414 va_list args; 415 416 va_start(args, format); 417 (void) vsnprintf(T_buf, sizeof(T_buf), format, args); 418 va_end(args); 419 (void) t_putinfo("I", T_buf); 420 } 421 422 void 423 t_result(int result) { 424 const char *p; 425 426 switch (result) { 427 case T_PASS: 428 p = "PASS"; 429 break; 430 case T_FAIL: 431 p = "FAIL"; 432 break; 433 case T_UNRESOLVED: 434 p = "UNRESOLVED"; 435 break; 436 case T_UNSUPPORTED: 437 p = "UNSUPPORTED"; 438 break; 439 case T_UNTESTED: 440 p = "UNTESTED"; 441 break; 442 case T_THREADONLY: 443 p = "THREADONLY"; 444 break; 445 default: 446 p = "UNKNOWN"; 447 break; 448 } 449 printf("R:%s\n", p); 450 } 451 452 char * 453 t_getenv(const char *name) { 454 char *n; 455 char **p; 456 size_t len; 457 458 n = NULL; 459 if (name && *name) { 460 461 p = &T_env[0]; 462 len = strlen(name); 463 464 while (*p != NULL) { 465 if (strncmp(*p, name, len) == 0) { 466 if ( *(*p + len) == '=') { 467 n = *p + len + 1; 468 break; 469 } 470 } 471 ++p; 472 } 473 } 474 return(n); 475 } 476 477 /* 478 * 479 * Read in the config file at path, initializing T_env. 480 * 481 * note: no format checking for now ... 482 * 483 */ 484 485 static int 486 t_initconf(const char *path) { 487 488 int n; 489 int rval; 490 char **p; 491 FILE *fp; 492 493 rval = -1; 494 495 fp = fopen(path, "r"); 496 if (fp != NULL) { 497 n = 0; 498 p = &T_env[0]; 499 while (n < T_MAXENV) { 500 *p = t_fgetbs(fp); 501 if (*p == NULL) 502 break; 503 if ((**p == '#') || (strchr(*p, '=') == NULL)) { 504 /* 505 * Skip comments and other junk. 506 */ 507 (void)free(*p); 508 continue; 509 } 510 ++p; ++n; 511 } 512 (void)fclose(fp); 513 rval = 0; 514 } 515 516 return (rval); 517 } 518 519 /* 520 * 521 * Dump T_env to stdout. 522 * 523 */ 524 525 static int 526 t_dumpconf(const char *path) { 527 int rval; 528 char **p; 529 FILE *fp; 530 531 rval = -1; 532 fp = fopen(path, "r"); 533 if (fp != NULL) { 534 p = &T_env[0]; 535 while (*p != NULL) { 536 printf("C:%s\n", *p); 537 ++p; 538 } 539 (void) fclose(fp); 540 rval = 0; 541 } 542 return(rval); 543 } 544 545 /* 546 * 547 * Read a newline or EOF terminated string from fp. 548 * On success: 549 * return a malloc'd buf containing the string with 550 * the newline converted to a '\0'. 551 * On error: 552 * return NULL. 553 * 554 * Caller is responsible for freeing buf. 555 * 556 */ 557 558 char * 559 t_fgetbs(FILE *fp) { 560 int c; 561 size_t n; 562 size_t size; 563 char *buf, *old; 564 char *p; 565 566 n = 0; 567 size = T_BUFSIZ; 568 old = buf = (char *) malloc(T_BUFSIZ * sizeof(char)); 569 570 if (buf != NULL) { 571 p = buf; 572 while ((c = fgetc(fp)) != EOF) { 573 574 if (c == '\n') 575 break; 576 577 *p++ = c; 578 ++n; 579 if ( n >= size ) { 580 size += T_BUFSIZ; 581 buf = (char *)realloc(buf, 582 size * sizeof(char)); 583 if (buf == NULL) 584 goto err; 585 old = buf; 586 p = buf + n; 587 } 588 } 589 *p = '\0'; 590 if (c == EOF && n == 0U) { 591 free(buf); 592 return (NULL); 593 } 594 return (buf); 595 } else { 596 err: 597 if (old != NULL) 598 free(old); 599 fprintf(stderr, "malloc/realloc failed %d", errno); 600 return(NULL); 601 } 602 } 603 604 /* 605 * 606 * Put info to log, using key. 607 * For now, just dump it out. 608 * Later format into pretty lines. 609 * 610 */ 611 612 static int 613 t_putinfo(const char *key, const char *info) { 614 int rval; 615 616 /* 617 * For now. 618 */ 619 rval = printf("%s:%s", key, info); 620 return(rval); 621 } 622 623 static char * 624 t_getdate(char *buf, size_t buflen) { 625 size_t n; 626 time_t t; 627 struct tm *p; 628 629 t = time(NULL); 630 p = localtime(&t); 631 n = strftime(buf, buflen - 1, "%A %d %B %H:%M:%S %Y\n", p); 632 return(n != 0U ? buf : NULL); 633 } 634 635 /* 636 * Some generally used utilities. 637 */ 638 #ifdef DNS_SUPPORT 639 struct dns_errormap { 640 isc_result_t result; 641 const char *text; 642 } dns_errormap[] = { 643 { ISC_R_SUCCESS, "ISC_R_SUCCESS" }, 644 { ISC_R_EXISTS, "ISC_R_EXISTS" }, 645 { ISC_R_NOTFOUND, "ISC_R_NOTFOUND" }, 646 { ISC_R_NOSPACE, "ISC_R_NOSPACE" }, 647 { ISC_R_UNEXPECTED, "ISC_R_UNEXPECTED" }, 648 { ISC_R_UNEXPECTEDEND, "ISC_R_UNEXPECTEDEND" }, 649 { ISC_R_RANGE, "ISC_R_RANGE" }, 650 { DNS_R_LABELTOOLONG, "DNS_R_LABELTOOLONG" }, 651 { DNS_R_BADESCAPE, "DNS_R_BADESCAPE" }, 652 /* { DNS_R_BADBITSTRING, "DNS_R_BADBITSTRING" }, */ 653 /* { DNS_R_BITSTRINGTOOLONG, "DNS_R_BITSTRINGTOOLONG"}, */ 654 { DNS_R_EMPTYLABEL, "DNS_R_EMPTYLABEL" }, 655 { DNS_R_BADDOTTEDQUAD, "DNS_R_BADDOTTEDQUAD" }, 656 { DNS_R_UNKNOWN, "DNS_R_UNKNOWN" }, 657 { DNS_R_BADLABELTYPE, "DNS_R_BADLABELTYPE" }, 658 { DNS_R_BADPOINTER, "DNS_R_BADPOINTER" }, 659 { DNS_R_TOOMANYHOPS, "DNS_R_TOOMANYHOPS" }, 660 { DNS_R_DISALLOWED, "DNS_R_DISALLOWED" }, 661 { DNS_R_EXTRATOKEN, "DNS_R_EXTRATOKEN" }, 662 { DNS_R_EXTRADATA, "DNS_R_EXTRADATA" }, 663 { DNS_R_TEXTTOOLONG, "DNS_R_TEXTTOOLONG" }, 664 { DNS_R_SYNTAX, "DNS_R_SYNTAX" }, 665 { DNS_R_BADCKSUM, "DNS_R_BADCKSUM" }, 666 { DNS_R_BADAAAA, "DNS_R_BADAAAA" }, 667 { DNS_R_NOOWNER, "DNS_R_NOOWNER" }, 668 { DNS_R_NOTTL, "DNS_R_NOTTL" }, 669 { DNS_R_BADCLASS, "DNS_R_BADCLASS" }, 670 { DNS_R_PARTIALMATCH, "DNS_R_PARTIALMATCH" }, 671 { DNS_R_NEWORIGIN, "DNS_R_NEWORIGIN" }, 672 { DNS_R_UNCHANGED, "DNS_R_UNCHANGED" }, 673 { DNS_R_BADTTL, "DNS_R_BADTTL" }, 674 { DNS_R_NOREDATA, "DNS_R_NOREDATA" }, 675 { DNS_R_CONTINUE, "DNS_R_CONTINUE" }, 676 { DNS_R_DELEGATION, "DNS_R_DELEGATION" }, 677 { DNS_R_GLUE, "DNS_R_GLUE" }, 678 { DNS_R_DNAME, "DNS_R_DNAME" }, 679 { DNS_R_CNAME, "DNS_R_CNAME" }, 680 { DNS_R_NXDOMAIN, "DNS_R_NXDOMAIN" }, 681 { DNS_R_NXRRSET, "DNS_R_NXRRSET" }, 682 { DNS_R_BADDB, "DNS_R_BADDB" }, 683 { DNS_R_ZONECUT, "DNS_R_ZONECUT" }, 684 { DNS_R_NOTZONETOP, "DNS_R_NOTZONETOP" }, 685 { DNS_R_SEENINCLUDE, "DNS_R_SEENINCLUDE" }, 686 { DNS_R_SINGLETON, "DNS_R_SINGLETON" }, 687 { (isc_result_t)0, NULL } 688 }; 689 690 isc_result_t 691 t_dns_result_fromtext(char *name) { 692 693 isc_result_t result; 694 struct dns_errormap *pmap; 695 696 result = ISC_R_UNEXPECTED; 697 698 pmap = dns_errormap; 699 while (pmap->text != NULL) { 700 if (strcmp(name, pmap->text) == 0) 701 break; 702 ++pmap; 703 } 704 705 if (pmap->text != NULL) 706 result = pmap->result; 707 708 return (result); 709 } 710 711 struct dc_method_map { 712 unsigned int dc_method; 713 const char *text; 714 } dc_method_map[] = { 715 716 { DNS_COMPRESS_NONE, "DNS_COMPRESS_NONE" }, 717 { DNS_COMPRESS_GLOBAL14, "DNS_COMPRESS_GLOBAL14" }, 718 { DNS_COMPRESS_ALL, "DNS_COMPRESS_ALL" }, 719 { 0, NULL } 720 }; 721 722 unsigned int 723 t_dc_method_fromtext(char *name) { 724 unsigned int dc_method; 725 struct dc_method_map *pmap; 726 727 dc_method = DNS_COMPRESS_NONE; 728 729 pmap = dc_method_map; 730 while (pmap->text != NULL) { 731 if (strcmp(name, pmap->text) == 0) 732 break; 733 ++pmap; 734 } 735 736 if (pmap->text != NULL) 737 dc_method = pmap->dc_method; 738 739 return(dc_method); 740 } 741 #endif /* DNS_SUPPORT */ 742 743 int 744 t_bustline(char *line, char **toks) { 745 int cnt; 746 char *p; 747 748 cnt = 0; 749 if (line && *line) { 750 while ((p = strtok(line, "\t")) && (cnt < T_MAXTOKS)) { 751 *toks++ = p; 752 line = NULL; 753 ++cnt; 754 } 755 } 756 return(cnt); 757 } 758 759 static void 760 printhelp(void) { 761 int cnt; 762 testspec_t *pts; 763 764 cnt = 1; 765 pts = &T_testlist[0]; 766 767 printf("Available tests:\n"); 768 while (pts->func_name) { 769 printf("\t%d\t%s\n", cnt, pts->func_name); 770 ++pts; 771 ++cnt; 772 } 773 } 774 775 static void 776 printusage(void) { 777 printf("Usage:\n%s\n", Usage); 778 } 779 780 int 781 t_eval(const char *filename, int (*func)(char **), int nargs) { 782 FILE *fp; 783 char *p; 784 int line; 785 int cnt; 786 int result; 787 int nfails; 788 int nprobs; 789 int npass; 790 char *tokens[T_MAXTOKS + 1]; 791 792 npass = 0; 793 nfails = 0; 794 nprobs = 0; 795 796 fp = fopen(filename, "r"); 797 if (fp != NULL) { 798 line = 0; 799 while ((p = t_fgetbs(fp)) != NULL) { 800 801 ++line; 802 803 /* 804 * Skip comment lines. 805 */ 806 if ((isspace((unsigned char)*p)) || (*p == '#')) { 807 (void)free(p); 808 continue; 809 } 810 811 cnt = t_bustline(p, tokens); 812 if (cnt == nargs) { 813 result = func(tokens); 814 switch (result) { 815 case T_PASS: 816 ++npass; 817 break; 818 case T_FAIL: 819 ++nfails; 820 break; 821 case T_UNTESTED: 822 break; 823 default: 824 ++nprobs; 825 break; 826 } 827 } else { 828 t_info("bad format in %s at line %d\n", 829 filename, line); 830 ++nprobs; 831 } 832 833 (void)free(p); 834 } 835 (void)fclose(fp); 836 } else { 837 t_info("Missing datafile %s\n", filename); 838 ++nprobs; 839 } 840 841 result = T_UNRESOLVED; 842 843 if (nfails == 0 && nprobs == 0 && npass > 0) 844 result = T_PASS; 845 else if (nfails > 0) 846 result = T_FAIL; 847 else if (npass == 0) 848 result = T_UNTESTED; 849 850 return (result); 851 } 852