1538aa54dSGarrett D'Amore /* 2538aa54dSGarrett D'Amore * This file and its contents are supplied under the terms of the 3538aa54dSGarrett D'Amore * Common Development and Distribution License ("CDDL"), version 1.0. 4538aa54dSGarrett D'Amore * You may only use this file in accordance with the terms of version 5538aa54dSGarrett D'Amore * 1.0 of the CDDL. 6538aa54dSGarrett D'Amore * 7538aa54dSGarrett D'Amore * A full copy of the text of the CDDL should have accompanied this 8538aa54dSGarrett D'Amore * source. A copy of the CDDL is also available via the Internet at 9538aa54dSGarrett D'Amore * http://www.illumos.org/license/CDDL. 10538aa54dSGarrett D'Amore */ 11538aa54dSGarrett D'Amore 12538aa54dSGarrett D'Amore /* 13*de572d98SGarrett D'Amore * Copyright 2015 Garrett D'Amore <garrett@damore.org> 14538aa54dSGarrett D'Amore */ 15538aa54dSGarrett D'Amore 16538aa54dSGarrett D'Amore /* 17538aa54dSGarrett D'Amore * Common handling for test programs. 18538aa54dSGarrett D'Amore */ 19538aa54dSGarrett D'Amore 20538aa54dSGarrett D'Amore #include <stdio.h> 21538aa54dSGarrett D'Amore #include <stdlib.h> 22538aa54dSGarrett D'Amore #include <stdarg.h> 23*de572d98SGarrett D'Amore #include <string.h> 24*de572d98SGarrett D'Amore #include <errno.h> 25538aa54dSGarrett D'Amore #include <pthread.h> 26*de572d98SGarrett D'Amore #include <ctype.h> 27*de572d98SGarrett D'Amore #include <unistd.h> 28*de572d98SGarrett D'Amore #include <sys/param.h> 29538aa54dSGarrett D'Amore #include "test_common.h" 30538aa54dSGarrett D'Amore 31538aa54dSGarrett D'Amore static int debug = 0; 32538aa54dSGarrett D'Amore static int force = 0; 33538aa54dSGarrett D'Amore static pthread_mutex_t lk; 34538aa54dSGarrett D'Amore 35*de572d98SGarrett D'Amore static int passes; 36*de572d98SGarrett D'Amore static int tests; 37*de572d98SGarrett D'Amore 38538aa54dSGarrett D'Amore struct test { 39538aa54dSGarrett D'Amore char *name; 40538aa54dSGarrett D'Amore int ntids; 41538aa54dSGarrett D'Amore pthread_t *tids; 42538aa54dSGarrett D'Amore int fails; 43538aa54dSGarrett D'Amore void *arg; 44538aa54dSGarrett D'Amore void (*func)(test_t t, void *); 45538aa54dSGarrett D'Amore }; 46538aa54dSGarrett D'Amore 47538aa54dSGarrett D'Amore void 48538aa54dSGarrett D'Amore test_set_debug(void) 49538aa54dSGarrett D'Amore { 50538aa54dSGarrett D'Amore debug++; 51538aa54dSGarrett D'Amore } 52538aa54dSGarrett D'Amore 53538aa54dSGarrett D'Amore void 54538aa54dSGarrett D'Amore test_set_force(void) 55538aa54dSGarrett D'Amore { 56538aa54dSGarrett D'Amore force++; 57538aa54dSGarrett D'Amore } 58538aa54dSGarrett D'Amore 59538aa54dSGarrett D'Amore test_t 60538aa54dSGarrett D'Amore test_start(const char *format, ...) 61538aa54dSGarrett D'Amore { 62538aa54dSGarrett D'Amore va_list args; 63538aa54dSGarrett D'Amore test_t t; 64538aa54dSGarrett D'Amore char *s; 65538aa54dSGarrett D'Amore 66538aa54dSGarrett D'Amore t = calloc(1, sizeof (*t)); 67538aa54dSGarrett D'Amore va_start(args, format); 68538aa54dSGarrett D'Amore (void) vasprintf(&s, format, args); 69538aa54dSGarrett D'Amore va_end(args); 70538aa54dSGarrett D'Amore 71538aa54dSGarrett D'Amore (void) asprintf(&t->name, "%s (%s)", s, ARCH); 72538aa54dSGarrett D'Amore free(s); 73538aa54dSGarrett D'Amore 74538aa54dSGarrett D'Amore (void) pthread_mutex_lock(&lk); 75538aa54dSGarrett D'Amore (void) printf("TEST STARTING %s:\n", t->name); 76538aa54dSGarrett D'Amore (void) fflush(stdout); 77538aa54dSGarrett D'Amore (void) pthread_mutex_unlock(&lk); 78538aa54dSGarrett D'Amore 79538aa54dSGarrett D'Amore #ifdef LINT 80538aa54dSGarrett D'Amore /* We inject references to make avoid name unused warnings */ 81538aa54dSGarrett D'Amore test_run(0, NULL, NULL, NULL); 82538aa54dSGarrett D'Amore test_debugf(t, NULL); 83538aa54dSGarrett D'Amore test_failed(t, NULL); 84538aa54dSGarrett D'Amore test_passed(t); 85538aa54dSGarrett D'Amore test_set_debug(); 86538aa54dSGarrett D'Amore test_set_force(); 87*de572d98SGarrett D'Amore test_summary(); 88*de572d98SGarrett D'Amore (void) test_load_config(t, NULL, NULL); 89538aa54dSGarrett D'Amore #endif 90538aa54dSGarrett D'Amore 91*de572d98SGarrett D'Amore tests++; 92538aa54dSGarrett D'Amore return (t); 93538aa54dSGarrett D'Amore } 94538aa54dSGarrett D'Amore 95538aa54dSGarrett D'Amore void 96538aa54dSGarrett D'Amore test_failed(test_t t, const char *format, ...) 97538aa54dSGarrett D'Amore { 98538aa54dSGarrett D'Amore va_list args; 99538aa54dSGarrett D'Amore 100538aa54dSGarrett D'Amore (void) pthread_mutex_lock(&lk); 101*de572d98SGarrett D'Amore if (t == NULL) { 102*de572d98SGarrett D'Amore (void) printf("FAILURE: "); 103*de572d98SGarrett D'Amore va_start(args, format); 104*de572d98SGarrett D'Amore (void) vprintf(format, args); 105*de572d98SGarrett D'Amore va_end(args); 106*de572d98SGarrett D'Amore (void) printf("\n"); 107*de572d98SGarrett D'Amore (void) fflush(stdout); 108*de572d98SGarrett D'Amore (void) pthread_mutex_unlock(&lk); 109*de572d98SGarrett D'Amore return; 110*de572d98SGarrett D'Amore } 111538aa54dSGarrett D'Amore if (force || (t->ntids > 0)) { 112538aa54dSGarrett D'Amore (void) printf("TEST FAILING %s: ", t->name); 113538aa54dSGarrett D'Amore } else { 114538aa54dSGarrett D'Amore (void) printf("TEST FAILED %s: ", t->name); 115538aa54dSGarrett D'Amore } 116538aa54dSGarrett D'Amore 117538aa54dSGarrett D'Amore va_start(args, format); 118538aa54dSGarrett D'Amore (void) vprintf(format, args); 119538aa54dSGarrett D'Amore va_end(args); 120538aa54dSGarrett D'Amore (void) printf("\n"); 121538aa54dSGarrett D'Amore (void) fflush(stdout); 122538aa54dSGarrett D'Amore (void) pthread_mutex_unlock(&lk); 123538aa54dSGarrett D'Amore 124538aa54dSGarrett D'Amore t->fails++; 125538aa54dSGarrett D'Amore if (!force) { 126538aa54dSGarrett D'Amore if (t->ntids > 0) { 127538aa54dSGarrett D'Amore pthread_exit(NULL); 128538aa54dSGarrett D'Amore } else { 129538aa54dSGarrett D'Amore (void) exit(EXIT_FAILURE); 130538aa54dSGarrett D'Amore } 131538aa54dSGarrett D'Amore } 132538aa54dSGarrett D'Amore } 133538aa54dSGarrett D'Amore 134538aa54dSGarrett D'Amore void 135538aa54dSGarrett D'Amore test_passed(test_t t) 136538aa54dSGarrett D'Amore { 137*de572d98SGarrett D'Amore if (t == NULL) { 138*de572d98SGarrett D'Amore return; 139*de572d98SGarrett D'Amore } 140538aa54dSGarrett D'Amore if (t->ntids > 0) { 141538aa54dSGarrett D'Amore if (debug) { 142538aa54dSGarrett D'Amore (void) pthread_mutex_lock(&lk); 143538aa54dSGarrett D'Amore (void) printf("TEST PASSING: %s\n", t->name); 144538aa54dSGarrett D'Amore (void) pthread_mutex_unlock(&lk); 145538aa54dSGarrett D'Amore } 146538aa54dSGarrett D'Amore return; 147538aa54dSGarrett D'Amore } 148538aa54dSGarrett D'Amore (void) pthread_mutex_lock(&lk); 149538aa54dSGarrett D'Amore if (t->fails == 0) { 150*de572d98SGarrett D'Amore passes++; 151538aa54dSGarrett D'Amore (void) printf("TEST PASS: %s\n", t->name); 152538aa54dSGarrett D'Amore } else { 153538aa54dSGarrett D'Amore (void) printf("TEST FAILED: %d failures\n", t->fails); 154538aa54dSGarrett D'Amore } 155538aa54dSGarrett D'Amore (void) fflush(stdout); 156538aa54dSGarrett D'Amore (void) pthread_mutex_unlock(&lk); 157538aa54dSGarrett D'Amore free(t->name); 158538aa54dSGarrett D'Amore if (t->tids) { 159538aa54dSGarrett D'Amore free(t->tids); 160538aa54dSGarrett D'Amore } 161538aa54dSGarrett D'Amore free(t); 162538aa54dSGarrett D'Amore } 163538aa54dSGarrett D'Amore 164*de572d98SGarrett D'Amore void 165*de572d98SGarrett D'Amore test_summary(void) 166*de572d98SGarrett D'Amore { 167*de572d98SGarrett D'Amore if (passes == tests) { 168*de572d98SGarrett D'Amore (void) printf("TEST SUMMARY: %d / %d (ok)\n", passes, tests); 169*de572d98SGarrett D'Amore } else { 170*de572d98SGarrett D'Amore (void) printf("TEST SUMMARY: %d / %d (%d failing)\n", 171*de572d98SGarrett D'Amore passes, tests, tests - passes); 172*de572d98SGarrett D'Amore } 173*de572d98SGarrett D'Amore } 174*de572d98SGarrett D'Amore 175538aa54dSGarrett D'Amore void 176538aa54dSGarrett D'Amore test_debugf(test_t t, const char *format, ...) 177538aa54dSGarrett D'Amore { 178538aa54dSGarrett D'Amore va_list args; 179538aa54dSGarrett D'Amore 180538aa54dSGarrett D'Amore if (!debug) 181538aa54dSGarrett D'Amore return; 182538aa54dSGarrett D'Amore 183538aa54dSGarrett D'Amore (void) pthread_mutex_lock(&lk); 184*de572d98SGarrett D'Amore if (t) { 185*de572d98SGarrett D'Amore (void) printf("TEST DEBUG %s: ", t->name); 186*de572d98SGarrett D'Amore } else { 187*de572d98SGarrett D'Amore (void) printf("TEST DEBUG: "); 188*de572d98SGarrett D'Amore } 189538aa54dSGarrett D'Amore va_start(args, format); 190538aa54dSGarrett D'Amore (void) vprintf(format, args); 191538aa54dSGarrett D'Amore va_end(args); 192538aa54dSGarrett D'Amore (void) printf("\n"); 193538aa54dSGarrett D'Amore (void) fflush(stdout); 194538aa54dSGarrett D'Amore (void) pthread_mutex_unlock(&lk); 195538aa54dSGarrett D'Amore } 196538aa54dSGarrett D'Amore 197538aa54dSGarrett D'Amore static void * 198538aa54dSGarrett D'Amore test_thr_one(void *arg) 199538aa54dSGarrett D'Amore { 200538aa54dSGarrett D'Amore test_t t = arg; 201538aa54dSGarrett D'Amore t->func(t, t->arg); 202538aa54dSGarrett D'Amore return (NULL); 203538aa54dSGarrett D'Amore } 204538aa54dSGarrett D'Amore 205538aa54dSGarrett D'Amore void 206538aa54dSGarrett D'Amore test_run(int nthr, void (*func)(test_t, void *), void *arg, 207538aa54dSGarrett D'Amore const char *tname, ...) 208538aa54dSGarrett D'Amore { 209538aa54dSGarrett D'Amore test_t t; 210538aa54dSGarrett D'Amore char *s; 211538aa54dSGarrett D'Amore va_list args; 212538aa54dSGarrett D'Amore 213538aa54dSGarrett D'Amore t = calloc(1, sizeof (*t)); 214538aa54dSGarrett D'Amore t->ntids = nthr; 215538aa54dSGarrett D'Amore t->tids = calloc(nthr, sizeof (pthread_t)); 216538aa54dSGarrett D'Amore t->func = func; 217538aa54dSGarrett D'Amore t->arg = arg; 218538aa54dSGarrett D'Amore 219538aa54dSGarrett D'Amore va_start(args, tname); 220538aa54dSGarrett D'Amore (void) vasprintf(&s, tname, args); 221538aa54dSGarrett D'Amore va_end(args); 222538aa54dSGarrett D'Amore 223538aa54dSGarrett D'Amore (void) asprintf(&t->name, "%s (%s)", s, ARCH); 224538aa54dSGarrett D'Amore free(s); 225538aa54dSGarrett D'Amore 226538aa54dSGarrett D'Amore (void) pthread_mutex_lock(&lk); 227538aa54dSGarrett D'Amore (void) printf("TEST STARTING %s:\n", t->name); 228538aa54dSGarrett D'Amore (void) fflush(stdout); 229538aa54dSGarrett D'Amore (void) pthread_mutex_unlock(&lk); 230538aa54dSGarrett D'Amore 231538aa54dSGarrett D'Amore test_debugf(t, "running %d threads", nthr); 232538aa54dSGarrett D'Amore 233538aa54dSGarrett D'Amore for (int i = 0; i < nthr; i++) { 234538aa54dSGarrett D'Amore test_debugf(t, "started thread %d", i); 235538aa54dSGarrett D'Amore (void) pthread_create(&t->tids[i], NULL, test_thr_one, t); 236538aa54dSGarrett D'Amore } 237538aa54dSGarrett D'Amore 238538aa54dSGarrett D'Amore for (int i = 0; i < nthr; i++) { 239538aa54dSGarrett D'Amore (void) pthread_join(t->tids[i], NULL); 240538aa54dSGarrett D'Amore test_debugf(t, "thread %d joined", i); 241538aa54dSGarrett D'Amore t->ntids--; 242538aa54dSGarrett D'Amore } 243538aa54dSGarrett D'Amore test_passed(t); 244538aa54dSGarrett D'Amore } 245*de572d98SGarrett D'Amore 246*de572d98SGarrett D'Amore void 247*de572d98SGarrett D'Amore test_trim(char **ptr) 248*de572d98SGarrett D'Amore { 249*de572d98SGarrett D'Amore char *p = *ptr; 250*de572d98SGarrett D'Amore while (isspace(*p)) { 251*de572d98SGarrett D'Amore p++; 252*de572d98SGarrett D'Amore } 253*de572d98SGarrett D'Amore *ptr = p; 254*de572d98SGarrett D'Amore p += strlen(p); 255*de572d98SGarrett D'Amore while ((--p >= *ptr) && (isspace(*p))) { 256*de572d98SGarrett D'Amore *p = 0; 257*de572d98SGarrett D'Amore } 258*de572d98SGarrett D'Amore } 259*de572d98SGarrett D'Amore 260*de572d98SGarrett D'Amore #define MAXCB 20 261*de572d98SGarrett D'Amore #define MAXFIELD 20 262*de572d98SGarrett D'Amore 263*de572d98SGarrett D'Amore int 264*de572d98SGarrett D'Amore test_load_config(test_t t, const char *fname, ...) 265*de572d98SGarrett D'Amore { 266*de572d98SGarrett D'Amore va_list va; 267*de572d98SGarrett D'Amore const char *keyws[MAXCB]; 268*de572d98SGarrett D'Amore test_cfg_func_t callbs[MAXCB]; 269*de572d98SGarrett D'Amore char *fields[MAXFIELD]; 270*de572d98SGarrett D'Amore int nfields; 271*de572d98SGarrett D'Amore 272*de572d98SGarrett D'Amore FILE *cfg; 273*de572d98SGarrett D'Amore char line[1024]; 274*de572d98SGarrett D'Amore char buf[1024]; 275*de572d98SGarrett D'Amore int done; 276*de572d98SGarrett D'Amore char *ptr; 277*de572d98SGarrett D'Amore char *tok; 278*de572d98SGarrett D'Amore char *err; 279*de572d98SGarrett D'Amore int lineno; 280*de572d98SGarrett D'Amore int rv; 281*de572d98SGarrett D'Amore int found; 282*de572d98SGarrett D'Amore char path[MAXPATHLEN]; 283*de572d98SGarrett D'Amore int i; 284*de572d98SGarrett D'Amore 285*de572d98SGarrett D'Amore va_start(va, fname); 286*de572d98SGarrett D'Amore for (i = 0; i < MAXCB; i++) { 287*de572d98SGarrett D'Amore keyws[i] = (const char *)va_arg(va, const char *); 288*de572d98SGarrett D'Amore if (keyws[i] == NULL) 289*de572d98SGarrett D'Amore break; 290*de572d98SGarrett D'Amore callbs[i] = (test_cfg_func_t)va_arg(va, test_cfg_func_t); 291*de572d98SGarrett D'Amore } 292*de572d98SGarrett D'Amore va_end(va); 293*de572d98SGarrett D'Amore if (i == MAXCB) { 294*de572d98SGarrett D'Amore test_debugf(t, "too many arguments to function >= %d", MAXCB); 295*de572d98SGarrett D'Amore } 296*de572d98SGarrett D'Amore 297*de572d98SGarrett D'Amore found = 0; 298*de572d98SGarrett D'Amore 299*de572d98SGarrett D'Amore if (access(fname, F_OK) == 0) { 300*de572d98SGarrett D'Amore found++; 301*de572d98SGarrett D'Amore } 302*de572d98SGarrett D'Amore if (!found && fname[0] != '/') { 303*de572d98SGarrett D'Amore char *stf = getenv("STF_SUITE"); 304*de572d98SGarrett D'Amore if (stf == NULL) { 305*de572d98SGarrett D'Amore stf = "../.."; 306*de572d98SGarrett D'Amore } 307*de572d98SGarrett D'Amore (void) snprintf(path, sizeof (path), "%s/cfg/%s", stf, fname); 308*de572d98SGarrett D'Amore if (access(path, F_OK) == 0) { 309*de572d98SGarrett D'Amore fname = path; 310*de572d98SGarrett D'Amore found++; 311*de572d98SGarrett D'Amore } else { 312*de572d98SGarrett D'Amore (void) snprintf(path, sizeof (path), "cfg/%s", fname); 313*de572d98SGarrett D'Amore if (access(path, F_OK) == 0) { 314*de572d98SGarrett D'Amore fname = path; 315*de572d98SGarrett D'Amore found++; 316*de572d98SGarrett D'Amore } 317*de572d98SGarrett D'Amore } 318*de572d98SGarrett D'Amore } 319*de572d98SGarrett D'Amore 320*de572d98SGarrett D'Amore if ((cfg = fopen(fname, "r")) == NULL) { 321*de572d98SGarrett D'Amore test_failed(t, "open(%s): %s", fname, strerror(errno)); 322*de572d98SGarrett D'Amore return (-1); 323*de572d98SGarrett D'Amore } 324*de572d98SGarrett D'Amore 325*de572d98SGarrett D'Amore line[0] = 0; 326*de572d98SGarrett D'Amore done = 0; 327*de572d98SGarrett D'Amore lineno = 0; 328*de572d98SGarrett D'Amore 329*de572d98SGarrett D'Amore while (!done) { 330*de572d98SGarrett D'Amore 331*de572d98SGarrett D'Amore lineno++; 332*de572d98SGarrett D'Amore 333*de572d98SGarrett D'Amore if (fgets(buf, sizeof (buf), cfg) == NULL) { 334*de572d98SGarrett D'Amore done++; 335*de572d98SGarrett D'Amore } else { 336*de572d98SGarrett D'Amore (void) strtok(buf, "\n"); 337*de572d98SGarrett D'Amore if ((*buf != 0) && (buf[strlen(buf)-1] == '\\')) { 338*de572d98SGarrett D'Amore /* 339*de572d98SGarrett D'Amore * Continuation. This isn't quite right, 340*de572d98SGarrett D'Amore * as it doesn't allow for a "\" at the 341*de572d98SGarrett D'Amore * end of line (no escaping). 342*de572d98SGarrett D'Amore */ 343*de572d98SGarrett D'Amore buf[strlen(buf)-1] = 0; 344*de572d98SGarrett D'Amore (void) strlcat(line, buf, sizeof (line)); 345*de572d98SGarrett D'Amore continue; 346*de572d98SGarrett D'Amore } 347*de572d98SGarrett D'Amore (void) strlcat(line, buf, sizeof (line)); 348*de572d98SGarrett D'Amore } 349*de572d98SGarrett D'Amore 350*de572d98SGarrett D'Amore /* got a line */ 351*de572d98SGarrett D'Amore ptr = line; 352*de572d98SGarrett D'Amore test_trim(&ptr); 353*de572d98SGarrett D'Amore 354*de572d98SGarrett D'Amore /* skip comments and empty lines */ 355*de572d98SGarrett D'Amore if (ptr[0] == 0 || ptr[0] == '#') { 356*de572d98SGarrett D'Amore line[0] = 0; 357*de572d98SGarrett D'Amore continue; 358*de572d98SGarrett D'Amore } 359*de572d98SGarrett D'Amore 360*de572d98SGarrett D'Amore tok = strsep(&ptr, "|"); 361*de572d98SGarrett D'Amore if (tok == NULL) { 362*de572d98SGarrett D'Amore break; 363*de572d98SGarrett D'Amore } 364*de572d98SGarrett D'Amore test_trim(&tok); 365*de572d98SGarrett D'Amore 366*de572d98SGarrett D'Amore for (nfields = 0; nfields < MAXFIELD; nfields++) { 367*de572d98SGarrett D'Amore fields[nfields] = strsep(&ptr, "|"); 368*de572d98SGarrett D'Amore if (fields[nfields] == NULL) { 369*de572d98SGarrett D'Amore break; 370*de572d98SGarrett D'Amore } 371*de572d98SGarrett D'Amore test_trim(&fields[nfields]); 372*de572d98SGarrett D'Amore } 373*de572d98SGarrett D'Amore 374*de572d98SGarrett D'Amore found = 0; 375*de572d98SGarrett D'Amore rv = 0; 376*de572d98SGarrett D'Amore 377*de572d98SGarrett D'Amore for (int i = 0; keyws[i] != NULL; i++) { 378*de572d98SGarrett D'Amore if (strcmp(tok, keyws[i]) == 0) { 379*de572d98SGarrett D'Amore found++; 380*de572d98SGarrett D'Amore err = NULL; 381*de572d98SGarrett D'Amore rv = callbs[i](fields, nfields, &err); 382*de572d98SGarrett D'Amore } 383*de572d98SGarrett D'Amore } 384*de572d98SGarrett D'Amore if (!found) { 385*de572d98SGarrett D'Amore rv = -1; 386*de572d98SGarrett D'Amore err = NULL; 387*de572d98SGarrett D'Amore (void) asprintf(&err, "unknown keyword %s", tok); 388*de572d98SGarrett D'Amore } 389*de572d98SGarrett D'Amore if (rv != 0) { 390*de572d98SGarrett D'Amore if (err) { 391*de572d98SGarrett D'Amore test_failed(t, "%s:%d: %s", fname, 392*de572d98SGarrett D'Amore lineno, err); 393*de572d98SGarrett D'Amore free(err); 394*de572d98SGarrett D'Amore } else { 395*de572d98SGarrett D'Amore test_failed(t, "%s:%d: unknown error", 396*de572d98SGarrett D'Amore fname, lineno); 397*de572d98SGarrett D'Amore } 398*de572d98SGarrett D'Amore (void) fclose(cfg); 399*de572d98SGarrett D'Amore return (rv); 400*de572d98SGarrett D'Amore } 401*de572d98SGarrett D'Amore 402*de572d98SGarrett D'Amore line[0] = 0; 403*de572d98SGarrett D'Amore } 404*de572d98SGarrett D'Amore (void) fclose(cfg); 405*de572d98SGarrett D'Amore return (0); 406*de572d98SGarrett D'Amore } 407