1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2015 Garrett D'Amore <garrett@damore.org> 14 */ 15 16 /* 17 * Common handling for test programs. 18 */ 19 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <stdarg.h> 23 #include <string.h> 24 #include <errno.h> 25 #include <pthread.h> 26 #include <ctype.h> 27 #include <unistd.h> 28 #include <sys/param.h> 29 #include "test_common.h" 30 31 static int debug = 0; 32 static int force = 0; 33 static pthread_mutex_t lk; 34 35 static int passes; 36 static int tests; 37 38 struct test { 39 char *name; 40 int ntids; 41 pthread_t *tids; 42 int fails; 43 void *arg; 44 void (*func)(test_t t, void *); 45 }; 46 47 void 48 test_set_debug(void) 49 { 50 debug++; 51 } 52 53 void 54 test_set_force(void) 55 { 56 force++; 57 } 58 59 test_t 60 test_start(const char *format, ...) 61 { 62 va_list args; 63 test_t t; 64 char *s; 65 66 t = calloc(1, sizeof (*t)); 67 va_start(args, format); 68 (void) vasprintf(&s, format, args); 69 va_end(args); 70 71 (void) asprintf(&t->name, "%s (%s)", s, ARCH); 72 free(s); 73 74 (void) pthread_mutex_lock(&lk); 75 (void) printf("TEST STARTING %s:\n", t->name); 76 (void) fflush(stdout); 77 (void) pthread_mutex_unlock(&lk); 78 79 #ifdef LINT 80 /* We inject references to make avoid name unused warnings */ 81 test_run(0, NULL, NULL, NULL); 82 test_debugf(t, NULL); 83 test_failed(t, NULL); 84 test_passed(t); 85 test_set_debug(); 86 test_set_force(); 87 test_summary(); 88 (void) test_load_config(t, NULL, NULL); 89 #endif 90 91 tests++; 92 return (t); 93 } 94 95 void 96 test_failed(test_t t, const char *format, ...) 97 { 98 va_list args; 99 100 (void) pthread_mutex_lock(&lk); 101 if (t == NULL) { 102 (void) printf("FAILURE: "); 103 va_start(args, format); 104 (void) vprintf(format, args); 105 va_end(args); 106 (void) printf("\n"); 107 (void) fflush(stdout); 108 (void) pthread_mutex_unlock(&lk); 109 return; 110 } 111 if (force || (t->ntids > 0)) { 112 (void) printf("TEST FAILING %s: ", t->name); 113 } else { 114 (void) printf("TEST FAILED %s: ", t->name); 115 } 116 117 va_start(args, format); 118 (void) vprintf(format, args); 119 va_end(args); 120 (void) printf("\n"); 121 (void) fflush(stdout); 122 (void) pthread_mutex_unlock(&lk); 123 124 t->fails++; 125 if (!force) { 126 if (t->ntids > 0) { 127 pthread_exit(NULL); 128 } else { 129 (void) exit(EXIT_FAILURE); 130 } 131 } 132 } 133 134 void 135 test_passed(test_t t) 136 { 137 if (t == NULL) { 138 return; 139 } 140 if (t->ntids > 0) { 141 if (debug) { 142 (void) pthread_mutex_lock(&lk); 143 (void) printf("TEST PASSING: %s\n", t->name); 144 (void) pthread_mutex_unlock(&lk); 145 } 146 return; 147 } 148 (void) pthread_mutex_lock(&lk); 149 if (t->fails == 0) { 150 passes++; 151 (void) printf("TEST PASS: %s\n", t->name); 152 } else { 153 (void) printf("TEST FAILED: %d failures\n", t->fails); 154 } 155 (void) fflush(stdout); 156 (void) pthread_mutex_unlock(&lk); 157 free(t->name); 158 if (t->tids) { 159 free(t->tids); 160 } 161 free(t); 162 } 163 164 void 165 test_summary(void) 166 { 167 if (passes == tests) { 168 (void) printf("TEST SUMMARY: %d / %d (ok)\n", passes, tests); 169 } else { 170 (void) printf("TEST SUMMARY: %d / %d (%d failing)\n", 171 passes, tests, tests - passes); 172 } 173 } 174 175 void 176 test_debugf(test_t t, const char *format, ...) 177 { 178 va_list args; 179 180 if (!debug) 181 return; 182 183 (void) pthread_mutex_lock(&lk); 184 if (t) { 185 (void) printf("TEST DEBUG %s: ", t->name); 186 } else { 187 (void) printf("TEST DEBUG: "); 188 } 189 va_start(args, format); 190 (void) vprintf(format, args); 191 va_end(args); 192 (void) printf("\n"); 193 (void) fflush(stdout); 194 (void) pthread_mutex_unlock(&lk); 195 } 196 197 static void * 198 test_thr_one(void *arg) 199 { 200 test_t t = arg; 201 t->func(t, t->arg); 202 return (NULL); 203 } 204 205 void 206 test_run(int nthr, void (*func)(test_t, void *), void *arg, 207 const char *tname, ...) 208 { 209 test_t t; 210 char *s; 211 va_list args; 212 213 t = calloc(1, sizeof (*t)); 214 t->ntids = nthr; 215 t->tids = calloc(nthr, sizeof (pthread_t)); 216 t->func = func; 217 t->arg = arg; 218 219 va_start(args, tname); 220 (void) vasprintf(&s, tname, args); 221 va_end(args); 222 223 (void) asprintf(&t->name, "%s (%s)", s, ARCH); 224 free(s); 225 226 (void) pthread_mutex_lock(&lk); 227 (void) printf("TEST STARTING %s:\n", t->name); 228 (void) fflush(stdout); 229 (void) pthread_mutex_unlock(&lk); 230 231 test_debugf(t, "running %d threads", nthr); 232 233 for (int i = 0; i < nthr; i++) { 234 test_debugf(t, "started thread %d", i); 235 (void) pthread_create(&t->tids[i], NULL, test_thr_one, t); 236 } 237 238 for (int i = 0; i < nthr; i++) { 239 (void) pthread_join(t->tids[i], NULL); 240 test_debugf(t, "thread %d joined", i); 241 t->ntids--; 242 } 243 test_passed(t); 244 } 245 246 void 247 test_trim(char **ptr) 248 { 249 char *p = *ptr; 250 while (isspace(*p)) { 251 p++; 252 } 253 *ptr = p; 254 p += strlen(p); 255 while ((--p >= *ptr) && (isspace(*p))) { 256 *p = 0; 257 } 258 } 259 260 #define MAXCB 20 261 #define MAXFIELD 20 262 263 int 264 test_load_config(test_t t, const char *fname, ...) 265 { 266 va_list va; 267 const char *keyws[MAXCB]; 268 test_cfg_func_t callbs[MAXCB]; 269 char *fields[MAXFIELD]; 270 int nfields; 271 272 FILE *cfg; 273 char line[1024]; 274 char buf[1024]; 275 int done; 276 char *ptr; 277 char *tok; 278 char *err; 279 int lineno; 280 int rv; 281 int found; 282 char path[MAXPATHLEN]; 283 int i; 284 285 va_start(va, fname); 286 for (i = 0; i < MAXCB; i++) { 287 keyws[i] = (const char *)va_arg(va, const char *); 288 if (keyws[i] == NULL) 289 break; 290 callbs[i] = (test_cfg_func_t)va_arg(va, test_cfg_func_t); 291 } 292 va_end(va); 293 if (i == MAXCB) { 294 test_debugf(t, "too many arguments to function >= %d", MAXCB); 295 } 296 297 found = 0; 298 299 if (access(fname, F_OK) == 0) { 300 found++; 301 } 302 if (!found && fname[0] != '/') { 303 char *stf = getenv("STF_SUITE"); 304 if (stf == NULL) { 305 stf = "../.."; 306 } 307 (void) snprintf(path, sizeof (path), "%s/cfg/%s", stf, fname); 308 if (access(path, F_OK) == 0) { 309 fname = path; 310 found++; 311 } else { 312 (void) snprintf(path, sizeof (path), "cfg/%s", fname); 313 if (access(path, F_OK) == 0) { 314 fname = path; 315 found++; 316 } 317 } 318 } 319 320 if ((cfg = fopen(fname, "r")) == NULL) { 321 test_failed(t, "open(%s): %s", fname, strerror(errno)); 322 return (-1); 323 } 324 325 line[0] = 0; 326 done = 0; 327 lineno = 0; 328 329 while (!done) { 330 331 lineno++; 332 333 if (fgets(buf, sizeof (buf), cfg) == NULL) { 334 done++; 335 } else { 336 (void) strtok(buf, "\n"); 337 if ((*buf != 0) && (buf[strlen(buf)-1] == '\\')) { 338 /* 339 * Continuation. This isn't quite right, 340 * as it doesn't allow for a "\" at the 341 * end of line (no escaping). 342 */ 343 buf[strlen(buf)-1] = 0; 344 (void) strlcat(line, buf, sizeof (line)); 345 continue; 346 } 347 (void) strlcat(line, buf, sizeof (line)); 348 } 349 350 /* got a line */ 351 ptr = line; 352 test_trim(&ptr); 353 354 /* skip comments and empty lines */ 355 if (ptr[0] == 0 || ptr[0] == '#') { 356 line[0] = 0; 357 continue; 358 } 359 360 tok = strsep(&ptr, "|"); 361 if (tok == NULL) { 362 break; 363 } 364 test_trim(&tok); 365 366 for (nfields = 0; nfields < MAXFIELD; nfields++) { 367 fields[nfields] = strsep(&ptr, "|"); 368 if (fields[nfields] == NULL) { 369 break; 370 } 371 test_trim(&fields[nfields]); 372 } 373 374 found = 0; 375 rv = 0; 376 377 for (int i = 0; keyws[i] != NULL; i++) { 378 if (strcmp(tok, keyws[i]) == 0) { 379 found++; 380 err = NULL; 381 rv = callbs[i](fields, nfields, &err); 382 } 383 } 384 if (!found) { 385 rv = -1; 386 err = NULL; 387 (void) asprintf(&err, "unknown keyword %s", tok); 388 } 389 if (rv != 0) { 390 if (err) { 391 test_failed(t, "%s:%d: %s", fname, 392 lineno, err); 393 free(err); 394 } else { 395 test_failed(t, "%s:%d: unknown error", 396 fname, lineno); 397 } 398 (void) fclose(cfg); 399 return (rv); 400 } 401 402 line[0] = 0; 403 } 404 (void) fclose(cfg); 405 return (0); 406 }