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
t_sighandler(int sig)149 t_sighandler(int sig) {
150 T_int = sig;
151 }
152
153 int
main(int argc,char ** argv)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
t_assert(const char * component,int anum,int class,const char * what,...)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
t_info(const char * format,...)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
t_result(int result)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 *
t_getenv(const char * name)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
t_initconf(const char * path)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
t_dumpconf(const char * path)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 *
t_fgetbs(FILE * fp)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
t_putinfo(const char * key,const char * info)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 *
t_getdate(char * buf,size_t buflen)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
t_dns_result_fromtext(char * name)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
t_dc_method_fromtext(char * name)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
t_bustline(char * line,char ** toks)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
printhelp(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
printusage(void)776 printusage(void) {
777 printf("Usage:\n%s\n", Usage);
778 }
779
780 int
t_eval(const char * filename,int (* func)(char **),int nargs)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