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