diff options
Diffstat (limited to 'lib/tests/t_api.c')
-rw-r--r-- | lib/tests/t_api.c | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/lib/tests/t_api.c b/lib/tests/t_api.c new file mode 100644 index 0000000..09cea48 --- /dev/null +++ b/lib/tests/t_api.c @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: t_api.c,v 1.63 2008/01/18 23:46:58 tbox Exp $ */ + +/*! \file */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include <sys/wait.h> + +#include <isc/boolean.h> +#include <isc/commandline.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/mem.h> + +#include <dns/compress.h> +#include <dns/result.h> + +#include "include/tests/t_api.h" + +static const char *Usage = + "\t-a : run all tests\n" + "\t-b <dir> : chdir to dir before running tests" + "\t-c <config_file> : use specified config file\n" + "\t-d <debug_level> : set debug level to debug_level\n" + "\t-h : print test info\n" + "\t-u : print usage info\n" + "\t-n <test_name> : run specified test name\n" + "\t-t <test_number> : run specified test number\n" + "\t-x : don't execute tests in a subproc\n" + "\t-q <timeout> : use 'timeout' as the timeout value\n"; +/*!< + * -a --> run all tests + * -b dir --> chdir to dir before running tests + * -c config --> use config file 'config' + * -d --> turn on api debugging + * -h --> print out available test names + * -u --> print usage info + * -n name --> run test named name + * -tn --> run test n + * -x --> don't execute testcases in a subproc + * -q timeout --> use 'timeout' as the timeout value + */ + +#define T_MAXTESTS 256 /*% must be 0 mod 8 */ +#define T_MAXENV 256 +#define T_DEFAULT_CONFIG "t_config" +#define T_BUFSIZ 256 +#define T_BIGBUF 4096 + +#define T_TCTOUT 60 + +int T_debug; +int T_timeout; +pid_t T_pid; +static const char * T_config; +static char T_tvec[T_MAXTESTS / 8]; +static char * T_env[T_MAXENV + 1]; +static char T_buf[T_BIGBUF]; +static char * T_dir; + +static int +t_initconf(const char *path); + +static int +t_dumpconf(const char *path); + +static int +t_putinfo(const char *key, const char *info); + +static char * +t_getdate(char *buf, size_t buflen); + +static void +printhelp(void); + +static void +printusage(void); + +static int T_int; + +static void +t_sighandler(int sig) { + T_int = sig; +} + +int +main(int argc, char **argv) { + int c; + int tnum; + int subprocs; + pid_t deadpid; + int status; + int len; + isc_boolean_t first; + testspec_t *pts; + struct sigaction sa; + + isc_mem_debugging = ISC_MEM_DEBUGRECORD; + first = ISC_TRUE; + subprocs = 1; + T_timeout = T_TCTOUT; + + /* + * -a option is now default. + */ + memset(T_tvec, 0xff, sizeof(T_tvec)); + + /* + * Parse args. + */ + while ((c = isc_commandline_parse(argc, argv, ":at:c:d:n:huxq:b:")) + != -1) { + if (c == 'a') { + /* + * Flag all tests to be run. + */ + memset(T_tvec, 0xff, sizeof(T_tvec)); + } + else if (c == 'b') { + T_dir = isc_commandline_argument; + } + else if (c == 't') { + tnum = atoi(isc_commandline_argument); + if ((tnum > 0) && (tnum < T_MAXTESTS)) { + if (first) { + /* + * Turn off effect of -a default + * and allow multiple -t and -n + * options. + */ + memset(T_tvec, 0, sizeof(T_tvec)); + first = ISC_FALSE; + } + /* + * Flag test tnum to be run. + */ + tnum -= 1; + T_tvec[tnum / 8] |= (0x01 << (tnum % 8)); + } + } + else if (c == 'c') { + T_config = isc_commandline_argument; + } + else if (c == 'd') { + T_debug = atoi(isc_commandline_argument); + } + else if (c == 'n') { + pts = &T_testlist[0]; + tnum = 0; + while (pts->pfv != NULL) { + if (! strcmp(pts->func_name, + isc_commandline_argument)) { + if (first) { + memset(T_tvec, 0, + sizeof(T_tvec)); + first = ISC_FALSE; + } + T_tvec[tnum/8] |= (0x01 << (tnum%8)); + break; + } + ++pts; + ++tnum; + } + if (pts->pfv == NULL) { + fprintf(stderr, "no such test %s\n", + isc_commandline_argument); + exit(1); + } + } + else if (c == 'h') { + printhelp(); + exit(0); + } + else if (c == 'u') { + printusage(); + exit(0); + } + else if (c == 'x') { + subprocs = 0; + } + else if (c == 'q') { + T_timeout = atoi(isc_commandline_argument); + } + else if (c == ':') { + fprintf(stderr, "Option -%c requires an argument\n", + isc_commandline_option); + exit(1); + } + else if (c == '?') { + fprintf(stderr, "Unrecognized option -%c\n", + isc_commandline_option); + exit(1); + } + } + + /* + * Set cwd. + */ + + if (T_dir != NULL) + (void) chdir(T_dir); + + /* + * We don't want buffered output. + */ + + (void)setbuf(stdout, NULL); + (void)setbuf(stderr, NULL); + + /* + * Setup signals. + */ + + sa.sa_flags = 0; + sigfillset(&sa.sa_mask); + +#ifdef SIGCHLD + /* + * This is mostly here for NetBSD's pthread implementation, until + * people catch up to the latest unproven-pthread package. + */ + sa.sa_handler = SIG_DFL; + (void)sigaction(SIGCHLD, &sa, NULL); +#endif + + sa.sa_handler = t_sighandler; + (void)sigaction(SIGINT, &sa, NULL); + (void)sigaction(SIGALRM, &sa, NULL); + + /* + * Output start stanza to journal. + */ + + snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); + len = strlen(T_buf); + (void) t_getdate(T_buf + len, T_BIGBUF - len); + t_putinfo("S", T_buf); + + /* + * Setup the test environment using the config file. + */ + + if (T_config == NULL) + T_config = T_DEFAULT_CONFIG; + + t_initconf(T_config); + if (T_debug) + t_dumpconf(T_config); + + /* + * Now invoke all the test cases. + */ + + tnum = 0; + pts = &T_testlist[0]; + while (*pts->pfv != NULL) { + if (T_tvec[tnum / 8] & (0x01 << (tnum % 8))) { + if (subprocs) { + T_pid = fork(); + if (T_pid == 0) { + (*pts->pfv)(); + exit(0); + } else if (T_pid > 0) { + + T_int = 0; + sa.sa_handler = t_sighandler; + (void)sigaction(SIGALRM, &sa, NULL); + alarm(T_timeout); + + deadpid = (pid_t) -1; + while (deadpid != T_pid) { + deadpid = + waitpid(T_pid, &status, 0); + if (deadpid == T_pid) { + if (WIFSIGNALED(status)) { + if (WTERMSIG(status) == + SIGTERM) + t_info( + "the test case timed out\n"); + else + t_info( + "the test case caused exception %d\n", + WTERMSIG(status)); + t_result(T_UNRESOLVED); + } + } else if ((deadpid == -1) && + (errno == EINTR) && + T_int) { + kill(T_pid, SIGTERM); + T_int = 0; + } + else if ((deadpid == -1) && + ((errno == ECHILD) || + (errno == ESRCH))) + break; + } + + alarm(0); + sa.sa_handler = SIG_IGN; + (void)sigaction(SIGALRM, &sa, NULL); + } else { + t_info("fork failed, errno == %d\n", + errno); + t_result(T_UNRESOLVED); + } + } + else { + (*pts->pfv)(); + } + } + ++pts; + ++tnum; + } + + snprintf(T_buf, sizeof(T_buf), "%s:", argv[0]); + len = strlen(T_buf); + (void) t_getdate(T_buf + len, T_BIGBUF - len); + t_putinfo("E", T_buf); + + return(0); +} + +void +t_assert(const char *component, int anum, int class, const char *what, ...) { + va_list args; + + (void)printf("T:%s:%d:%s\n", component, anum, class == T_REQUIRED ? + "A" : "C"); + + /* + * Format text to a buffer. + */ + va_start(args, what); + (void)vsnprintf(T_buf, sizeof(T_buf), what, args); + va_end(args); + + (void)t_putinfo("A", T_buf); + (void)printf("\n"); +} + +void +t_info(const char *format, ...) { + va_list args; + + va_start(args, format); + (void) vsnprintf(T_buf, sizeof(T_buf), format, args); + va_end(args); + (void) t_putinfo("I", T_buf); +} + +void +t_result(int result) { + const char *p; + + switch (result) { + case T_PASS: + p = "PASS"; + break; + case T_FAIL: + p = "FAIL"; + break; + case T_UNRESOLVED: + p = "UNRESOLVED"; + break; + case T_UNSUPPORTED: + p = "UNSUPPORTED"; + break; + case T_UNTESTED: + p = "UNTESTED"; + break; + case T_THREADONLY: + p = "THREADONLY"; + break; + default: + p = "UNKNOWN"; + break; + } + printf("R:%s\n", p); +} + +char * +t_getenv(const char *name) { + char *n; + char **p; + size_t len; + + n = NULL; + if (name && *name) { + + p = &T_env[0]; + len = strlen(name); + + while (*p != NULL) { + if (strncmp(*p, name, len) == 0) { + if ( *(*p + len) == '=') { + n = *p + len + 1; + break; + } + } + ++p; + } + } + return(n); +} + +/* + * + * Read in the config file at path, initializing T_env. + * + * note: no format checking for now ... + * + */ + +static int +t_initconf(const char *path) { + + int n; + int rval; + char **p; + FILE *fp; + + rval = -1; + + fp = fopen(path, "r"); + if (fp != NULL) { + n = 0; + p = &T_env[0]; + while (n < T_MAXENV) { + *p = t_fgetbs(fp); + if (*p == NULL) + break; + if ((**p == '#') || (strchr(*p, '=') == NULL)) { + /* + * Skip comments and other junk. + */ + (void)free(*p); + continue; + } + ++p; ++n; + } + (void)fclose(fp); + rval = 0; + } + + return (rval); +} + +/* + * + * Dump T_env to stdout. + * + */ + +static int +t_dumpconf(const char *path) { + int rval; + char **p; + FILE *fp; + + rval = -1; + fp = fopen(path, "r"); + if (fp != NULL) { + p = &T_env[0]; + while (*p != NULL) { + printf("C:%s\n", *p); + ++p; + } + (void) fclose(fp); + rval = 0; + } + return(rval); +} + +/* + * + * Read a newline or EOF terminated string from fp. + * On success: + * return a malloc'd buf containing the string with + * the newline converted to a '\0'. + * On error: + * return NULL. + * + * Caller is responsible for freeing buf. + * + */ + +char * +t_fgetbs(FILE *fp) { + int c; + size_t n; + size_t size; + char *buf; + char *p; + + n = 0; + size = T_BUFSIZ; + buf = (char *) malloc(T_BUFSIZ * sizeof(char)); + + if (buf != NULL) { + p = buf; + while ((c = fgetc(fp)) != EOF) { + + if (c == '\n') + break; + + *p++ = c; + ++n; + if ( n >= size ) { + size += T_BUFSIZ; + buf = (char *)realloc(buf, + size * sizeof(char)); + if (buf == NULL) + break; + p = buf + n; + } + } + *p = '\0'; + if (c == EOF && n == 0U) { + free(buf); + return (NULL); + } + return (buf); + } else { + fprintf(stderr, "malloc failed %d", errno); + return(NULL); + } +} + +/* + * + * Put info to log, using key. + * For now, just dump it out. + * Later format into pretty lines. + * + */ + +static int +t_putinfo(const char *key, const char *info) { + int rval; + + /* + * For now. + */ + rval = printf("%s:%s", key, info); + return(rval); +} + +static char * +t_getdate(char *buf, size_t buflen) { + size_t n; + time_t t; + struct tm *p; + + t = time(NULL); + p = localtime(&t); + n = strftime(buf, buflen - 1, "%A %d %B %H:%M:%S %Y\n", p); + return(n != 0U ? buf : NULL); +} + +/* + * Some generally used utilities. + */ +struct dns_errormap { + isc_result_t result; + const char *text; +} dns_errormap[] = { + { ISC_R_SUCCESS, "ISC_R_SUCCESS" }, + { ISC_R_EXISTS, "ISC_R_EXISTS" }, + { ISC_R_NOTFOUND, "ISC_R_NOTFOUND" }, + { ISC_R_NOSPACE, "ISC_R_NOSPACE" }, + { ISC_R_UNEXPECTED, "ISC_R_UNEXPECTED" }, + { ISC_R_UNEXPECTEDEND, "ISC_R_UNEXPECTEDEND" }, + { ISC_R_RANGE, "ISC_R_RANGE" }, + { DNS_R_LABELTOOLONG, "DNS_R_LABELTOOLONG" }, + { DNS_R_BADESCAPE, "DNS_R_BADESCAPE" }, + /* { DNS_R_BADBITSTRING, "DNS_R_BADBITSTRING" }, */ + /* { DNS_R_BITSTRINGTOOLONG, "DNS_R_BITSTRINGTOOLONG"}, */ + { DNS_R_EMPTYLABEL, "DNS_R_EMPTYLABEL" }, + { DNS_R_BADDOTTEDQUAD, "DNS_R_BADDOTTEDQUAD" }, + { DNS_R_UNKNOWN, "DNS_R_UNKNOWN" }, + { DNS_R_BADLABELTYPE, "DNS_R_BADLABELTYPE" }, + { DNS_R_BADPOINTER, "DNS_R_BADPOINTER" }, + { DNS_R_TOOMANYHOPS, "DNS_R_TOOMANYHOPS" }, + { DNS_R_DISALLOWED, "DNS_R_DISALLOWED" }, + { DNS_R_EXTRATOKEN, "DNS_R_EXTRATOKEN" }, + { DNS_R_EXTRADATA, "DNS_R_EXTRADATA" }, + { DNS_R_TEXTTOOLONG, "DNS_R_TEXTTOOLONG" }, + { DNS_R_SYNTAX, "DNS_R_SYNTAX" }, + { DNS_R_BADCKSUM, "DNS_R_BADCKSUM" }, + { DNS_R_BADAAAA, "DNS_R_BADAAAA" }, + { DNS_R_NOOWNER, "DNS_R_NOOWNER" }, + { DNS_R_NOTTL, "DNS_R_NOTTL" }, + { DNS_R_BADCLASS, "DNS_R_BADCLASS" }, + { DNS_R_PARTIALMATCH, "DNS_R_PARTIALMATCH" }, + { DNS_R_NEWORIGIN, "DNS_R_NEWORIGIN" }, + { DNS_R_UNCHANGED, "DNS_R_UNCHANGED" }, + { DNS_R_BADTTL, "DNS_R_BADTTL" }, + { DNS_R_NOREDATA, "DNS_R_NOREDATA" }, + { DNS_R_CONTINUE, "DNS_R_CONTINUE" }, + { DNS_R_DELEGATION, "DNS_R_DELEGATION" }, + { DNS_R_GLUE, "DNS_R_GLUE" }, + { DNS_R_DNAME, "DNS_R_DNAME" }, + { DNS_R_CNAME, "DNS_R_CNAME" }, + { DNS_R_NXDOMAIN, "DNS_R_NXDOMAIN" }, + { DNS_R_NXRRSET, "DNS_R_NXRRSET" }, + { DNS_R_BADDB, "DNS_R_BADDB" }, + { DNS_R_ZONECUT, "DNS_R_ZONECUT" }, + { DNS_R_NOTZONETOP, "DNS_R_NOTZONETOP" }, + { DNS_R_SEENINCLUDE, "DNS_R_SEENINCLUDE" }, + { DNS_R_SINGLETON, "DNS_R_SINGLETON" }, + { (isc_result_t)0, NULL } +}; + +isc_result_t +t_dns_result_fromtext(char *name) { + + isc_result_t result; + struct dns_errormap *pmap; + + result = ISC_R_UNEXPECTED; + + pmap = dns_errormap; + while (pmap->text != NULL) { + if (strcmp(name, pmap->text) == 0) + break; + ++pmap; + } + + if (pmap->text != NULL) + result = pmap->result; + + return (result); +} + +struct dc_method_map { + unsigned int dc_method; + const char *text; +} dc_method_map[] = { + + { DNS_COMPRESS_NONE, "DNS_COMPRESS_NONE" }, + { DNS_COMPRESS_GLOBAL14, "DNS_COMPRESS_GLOBAL14" }, + { DNS_COMPRESS_ALL, "DNS_COMPRESS_ALL" }, + { 0, NULL } +}; + +unsigned int +t_dc_method_fromtext(char *name) { + unsigned int dc_method; + struct dc_method_map *pmap; + + dc_method = DNS_COMPRESS_NONE; + + pmap = dc_method_map; + while (pmap->text != NULL) { + if (strcmp(name, pmap->text) == 0) + break; + ++pmap; + } + + if (pmap->text != NULL) + dc_method = pmap->dc_method; + + return(dc_method); +} + +int +t_bustline(char *line, char **toks) { + int cnt; + char *p; + + cnt = 0; + if (line && *line) { + while ((p = strtok(line, "\t")) && (cnt < T_MAXTOKS)) { + *toks++ = p; + line = NULL; + ++cnt; + } + } + return(cnt); +} + +static void +printhelp(void) { + int cnt; + testspec_t *pts; + + cnt = 1; + pts = &T_testlist[0]; + + printf("Available tests:\n"); + while (pts->func_name) { + printf("\t%d\t%s\n", cnt, pts->func_name); + ++pts; + ++cnt; + } +} + +static void +printusage(void) { + printf("Usage:\n%s\n", Usage); +} + +int +t_eval(const char *filename, int (*func)(char **), int nargs) { + FILE *fp; + char *p; + int line; + int cnt; + int result; + int nfails; + int nprobs; + int npass; + char *tokens[T_MAXTOKS + 1]; + + npass = 0; + nfails = 0; + nprobs = 0; + + fp = fopen(filename, "r"); + if (fp != NULL) { + line = 0; + while ((p = t_fgetbs(fp)) != NULL) { + + ++line; + + /* + * Skip comment lines. + */ + if ((isspace((unsigned char)*p)) || (*p == '#')) { + (void)free(p); + continue; + } + + cnt = t_bustline(p, tokens); + if (cnt == nargs) { + result = func(tokens); + switch (result) { + case T_PASS: + ++npass; + break; + case T_FAIL: + ++nfails; + break; + case T_UNTESTED: + break; + default: + ++nprobs; + break; + } + } else { + t_info("bad format in %s at line %d\n", + filename, line); + ++nprobs; + } + + (void)free(p); + } + (void)fclose(fp); + } else { + t_info("Missing datafile %s\n", filename); + ++nprobs; + } + + result = T_UNRESOLVED; + + if (nfails == 0 && nprobs == 0 && npass > 0) + result = T_PASS; + else if (nfails > 0) + result = T_FAIL; + else if (npass == 0) + result = T_UNTESTED; + + return (result); +} |