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 /* 13de572d98SGarrett 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> 23de572d98SGarrett D'Amore #include <string.h> 24de572d98SGarrett D'Amore #include <errno.h> 25538aa54dSGarrett D'Amore #include <pthread.h> 26de572d98SGarrett D'Amore #include <ctype.h> 27de572d98SGarrett D'Amore #include <unistd.h> 28de572d98SGarrett 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 35de572d98SGarrett D'Amore static int passes; 36de572d98SGarrett D'Amore static int tests; 37de572d98SGarrett 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(); 87de572d98SGarrett D'Amore test_summary(); 88de572d98SGarrett D'Amore (void) test_load_config(t, NULL, NULL); 89538aa54dSGarrett D'Amore #endif 90538aa54dSGarrett D'Amore 91de572d98SGarrett 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); 101de572d98SGarrett D'Amore if (t == NULL) { 102de572d98SGarrett D'Amore (void) printf("FAILURE: "); 103de572d98SGarrett D'Amore va_start(args, format); 104de572d98SGarrett D'Amore (void) vprintf(format, args); 105de572d98SGarrett D'Amore va_end(args); 106de572d98SGarrett D'Amore (void) printf("\n"); 107de572d98SGarrett D'Amore (void) fflush(stdout); 108de572d98SGarrett D'Amore (void) pthread_mutex_unlock(&lk); 109de572d98SGarrett D'Amore return; 110de572d98SGarrett 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 { 137de572d98SGarrett D'Amore if (t == NULL) { 138de572d98SGarrett D'Amore return; 139de572d98SGarrett 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) { 150de572d98SGarrett 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 164538aa54dSGarrett D'Amore void 165de572d98SGarrett D'Amore test_summary(void) 166de572d98SGarrett D'Amore { 167de572d98SGarrett D'Amore if (passes == tests) { 168de572d98SGarrett D'Amore (void) printf("TEST SUMMARY: %d / %d (ok)\n", passes, tests); 169de572d98SGarrett D'Amore } else { 170de572d98SGarrett D'Amore (void) printf("TEST SUMMARY: %d / %d (%d failing)\n", 171de572d98SGarrett D'Amore passes, tests, tests - passes); 172de572d98SGarrett D'Amore } 173de572d98SGarrett D'Amore } 174de572d98SGarrett D'Amore 175de572d98SGarrett 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); 184de572d98SGarrett D'Amore if (t) { 185538aa54dSGarrett D'Amore (void) printf("TEST DEBUG %s: ", t->name); 186de572d98SGarrett D'Amore } else { 187de572d98SGarrett D'Amore (void) printf("TEST DEBUG: "); 188de572d98SGarrett 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 } 245de572d98SGarrett D'Amore 246de572d98SGarrett D'Amore void 247de572d98SGarrett D'Amore test_trim(char **ptr) 248de572d98SGarrett D'Amore { 249de572d98SGarrett D'Amore char *p = *ptr; 250de572d98SGarrett D'Amore while (isspace(*p)) { 251de572d98SGarrett D'Amore p++; 252de572d98SGarrett D'Amore } 253de572d98SGarrett D'Amore *ptr = p; 254de572d98SGarrett D'Amore p += strlen(p); 255de572d98SGarrett D'Amore while ((--p >= *ptr) && (isspace(*p))) { 256*dcdfe824SRobert Mustacchi *p = '\0'; 257de572d98SGarrett D'Amore } 258de572d98SGarrett D'Amore } 259de572d98SGarrett D'Amore 260de572d98SGarrett D'Amore #define MAXCB 20 261de572d98SGarrett D'Amore #define MAXFIELD 20 262de572d98SGarrett D'Amore 263de572d98SGarrett D'Amore int 264de572d98SGarrett D'Amore test_load_config(test_t t, const char *fname, ...) 265de572d98SGarrett D'Amore { 266de572d98SGarrett D'Amore va_list va; 267de572d98SGarrett D'Amore const char *keyws[MAXCB]; 268de572d98SGarrett D'Amore test_cfg_func_t callbs[MAXCB]; 269de572d98SGarrett D'Amore char *fields[MAXFIELD]; 270de572d98SGarrett D'Amore int nfields; 271de572d98SGarrett D'Amore 272de572d98SGarrett D'Amore FILE *cfg; 273de572d98SGarrett D'Amore char line[1024]; 274de572d98SGarrett D'Amore char buf[1024]; 275de572d98SGarrett D'Amore int done; 276de572d98SGarrett D'Amore char *ptr; 277de572d98SGarrett D'Amore char *tok; 278de572d98SGarrett D'Amore char *err; 279de572d98SGarrett D'Amore int lineno; 280de572d98SGarrett D'Amore int rv; 281de572d98SGarrett D'Amore int found; 282de572d98SGarrett D'Amore char path[MAXPATHLEN]; 283de572d98SGarrett D'Amore int i; 284de572d98SGarrett D'Amore 285de572d98SGarrett D'Amore va_start(va, fname); 286de572d98SGarrett D'Amore for (i = 0; i < MAXCB; i++) { 287de572d98SGarrett D'Amore keyws[i] = (const char *)va_arg(va, const char *); 288de572d98SGarrett D'Amore if (keyws[i] == NULL) 289de572d98SGarrett D'Amore break; 290de572d98SGarrett D'Amore callbs[i] = (test_cfg_func_t)va_arg(va, test_cfg_func_t); 291de572d98SGarrett D'Amore } 292de572d98SGarrett D'Amore va_end(va); 293de572d98SGarrett D'Amore if (i == MAXCB) { 294de572d98SGarrett D'Amore test_debugf(t, "too many arguments to function >= %d", MAXCB); 295de572d98SGarrett D'Amore } 296de572d98SGarrett D'Amore 297de572d98SGarrett D'Amore found = 0; 298de572d98SGarrett D'Amore 299de572d98SGarrett D'Amore if (access(fname, F_OK) == 0) { 300de572d98SGarrett D'Amore found++; 301de572d98SGarrett D'Amore } 302de572d98SGarrett D'Amore if (!found && fname[0] != '/') { 303de572d98SGarrett D'Amore char *stf = getenv("STF_SUITE"); 304de572d98SGarrett D'Amore if (stf == NULL) { 305de572d98SGarrett D'Amore stf = "../.."; 306de572d98SGarrett D'Amore } 307de572d98SGarrett D'Amore (void) snprintf(path, sizeof (path), "%s/cfg/%s", stf, fname); 308de572d98SGarrett D'Amore if (access(path, F_OK) == 0) { 309de572d98SGarrett D'Amore fname = path; 310de572d98SGarrett D'Amore found++; 311de572d98SGarrett D'Amore } else { 312de572d98SGarrett D'Amore (void) snprintf(path, sizeof (path), "cfg/%s", fname); 313de572d98SGarrett D'Amore if (access(path, F_OK) == 0) { 314de572d98SGarrett D'Amore fname = path; 315de572d98SGarrett D'Amore found++; 316de572d98SGarrett D'Amore } 317de572d98SGarrett D'Amore } 318de572d98SGarrett D'Amore } 319de572d98SGarrett D'Amore 320de572d98SGarrett D'Amore if ((cfg = fopen(fname, "r")) == NULL) { 321de572d98SGarrett D'Amore test_failed(t, "open(%s): %s", fname, strerror(errno)); 322de572d98SGarrett D'Amore return (-1); 323de572d98SGarrett D'Amore } 324de572d98SGarrett D'Amore 325de572d98SGarrett D'Amore line[0] = 0; 326de572d98SGarrett D'Amore done = 0; 327de572d98SGarrett D'Amore lineno = 0; 328de572d98SGarrett D'Amore 329de572d98SGarrett D'Amore while (!done) { 330de572d98SGarrett D'Amore 331de572d98SGarrett D'Amore lineno++; 332de572d98SGarrett D'Amore 333de572d98SGarrett D'Amore if (fgets(buf, sizeof (buf), cfg) == NULL) { 334de572d98SGarrett D'Amore done++; 335de572d98SGarrett D'Amore } else { 336de572d98SGarrett D'Amore (void) strtok(buf, "\n"); 337de572d98SGarrett D'Amore if ((*buf != 0) && (buf[strlen(buf)-1] == '\\')) { 338de572d98SGarrett D'Amore /* 339de572d98SGarrett D'Amore * Continuation. This isn't quite right, 340de572d98SGarrett D'Amore * as it doesn't allow for a "\" at the 341de572d98SGarrett D'Amore * end of line (no escaping). 342de572d98SGarrett D'Amore */ 343de572d98SGarrett D'Amore buf[strlen(buf)-1] = 0; 344de572d98SGarrett D'Amore (void) strlcat(line, buf, sizeof (line)); 345de572d98SGarrett D'Amore continue; 346de572d98SGarrett D'Amore } 347de572d98SGarrett D'Amore (void) strlcat(line, buf, sizeof (line)); 348de572d98SGarrett D'Amore } 349de572d98SGarrett D'Amore 350de572d98SGarrett D'Amore /* got a line */ 351de572d98SGarrett D'Amore ptr = line; 352de572d98SGarrett D'Amore test_trim(&ptr); 353de572d98SGarrett D'Amore 354de572d98SGarrett D'Amore /* skip comments and empty lines */ 355de572d98SGarrett D'Amore if (ptr[0] == 0 || ptr[0] == '#') { 356de572d98SGarrett D'Amore line[0] = 0; 357de572d98SGarrett D'Amore continue; 358de572d98SGarrett D'Amore } 359de572d98SGarrett D'Amore 360de572d98SGarrett D'Amore tok = strsep(&ptr, "|"); 361de572d98SGarrett D'Amore if (tok == NULL) { 362de572d98SGarrett D'Amore break; 363de572d98SGarrett D'Amore } 364de572d98SGarrett D'Amore test_trim(&tok); 365de572d98SGarrett D'Amore 366de572d98SGarrett D'Amore for (nfields = 0; nfields < MAXFIELD; nfields++) { 367de572d98SGarrett D'Amore fields[nfields] = strsep(&ptr, "|"); 368de572d98SGarrett D'Amore if (fields[nfields] == NULL) { 369de572d98SGarrett D'Amore break; 370de572d98SGarrett D'Amore } 371de572d98SGarrett D'Amore test_trim(&fields[nfields]); 372de572d98SGarrett D'Amore } 373de572d98SGarrett D'Amore 374de572d98SGarrett D'Amore found = 0; 375de572d98SGarrett D'Amore rv = 0; 376de572d98SGarrett D'Amore 377de572d98SGarrett D'Amore for (int i = 0; keyws[i] != NULL; i++) { 378de572d98SGarrett D'Amore if (strcmp(tok, keyws[i]) == 0) { 379de572d98SGarrett D'Amore found++; 380de572d98SGarrett D'Amore err = NULL; 381de572d98SGarrett D'Amore rv = callbs[i](fields, nfields, &err); 382de572d98SGarrett D'Amore } 383de572d98SGarrett D'Amore } 384de572d98SGarrett D'Amore if (!found) { 385de572d98SGarrett D'Amore rv = -1; 386de572d98SGarrett D'Amore err = NULL; 387de572d98SGarrett D'Amore (void) asprintf(&err, "unknown keyword %s", tok); 388de572d98SGarrett D'Amore } 389de572d98SGarrett D'Amore if (rv != 0) { 390de572d98SGarrett D'Amore if (err) { 391de572d98SGarrett D'Amore test_failed(t, "%s:%d: %s", fname, 392de572d98SGarrett D'Amore lineno, err); 393de572d98SGarrett D'Amore free(err); 394de572d98SGarrett D'Amore } else { 395de572d98SGarrett D'Amore test_failed(t, "%s:%d: unknown error", 396de572d98SGarrett D'Amore fname, lineno); 397de572d98SGarrett D'Amore } 398de572d98SGarrett D'Amore (void) fclose(cfg); 399de572d98SGarrett D'Amore return (rv); 400de572d98SGarrett D'Amore } 401de572d98SGarrett D'Amore 402de572d98SGarrett D'Amore line[0] = 0; 403de572d98SGarrett D'Amore } 404de572d98SGarrett D'Amore (void) fclose(cfg); 405de572d98SGarrett D'Amore return (0); 406de572d98SGarrett D'Amore } 407