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 2014 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 void 175 test_debugf(test_t t, const char *format, ...) 176 { 177 va_list args; 178 179 if (!debug) 180 return; 181 182 (void) pthread_mutex_lock(&lk); 183 if (t) { 184 (void) printf("TEST DEBUG %s: ", t->name); 185 } else { 186 (void) printf("TEST DEBUG: "); 187 } 188 va_start(args, format); 189 (void) vprintf(format, args); 190 va_end(args); 191 (void) printf("\n"); 192 (void) fflush(stdout); 193 (void) pthread_mutex_unlock(&lk); 194 } 195 196 static void * 197 test_thr_one(void *arg) 198 { 199 test_t t = arg; 200 t->func(t, t->arg); 201 return (NULL); 202 } 203 204 void 205 test_run(int nthr, void (*func)(test_t, void *), void *arg, 206 const char *tname, ...) 207 { 208 test_t t; 209 char *s; 210 va_list args; 211 212 t = calloc(1, sizeof (*t)); 213 t->ntids = nthr; 214 t->tids = calloc(nthr, sizeof (pthread_t)); 215 t->func = func; 216 t->arg = arg; 217 218 va_start(args, tname); 219 (void) vasprintf(&s, tname, args); 220 va_end(args); 221 222 (void) asprintf(&t->name, "%s (%s)", s, ARCH); 223 free(s); 224 225 (void) pthread_mutex_lock(&lk); 226 (void) printf("TEST STARTING %s:\n", t->name); 227 (void) fflush(stdout); 228 (void) pthread_mutex_unlock(&lk); 229 230 test_debugf(t, "running %d threads", nthr); 231 232 for (int i = 0; i < nthr; i++) { 233 test_debugf(t, "started thread %d", i); 234 (void) pthread_create(&t->tids[i], NULL, test_thr_one, t); 235 } 236 237 for (int i = 0; i < nthr; i++) { 238 (void) pthread_join(t->tids[i], NULL); 239 test_debugf(t, "thread %d joined", i); 240 t->ntids--; 241 } 242 test_passed(t); 243 } 244 245 void 246 test_trim(char **ptr) 247 { 248 char *p = *ptr; 249 while (isspace(*p)) { 250 p++; 251 } 252 *ptr = p; 253 p += strlen(p); 254 while ((--p >= *ptr) && (isspace(*p))) { 255 *p = 0; 256 } 257 } 258 259 #define MAXCB 20 260 #define MAXFIELD 20 261 262 int 263 test_load_config(test_t t, const char *fname, ...) 264 { 265 va_list va; 266 const char *keyws[MAXCB]; 267 test_cfg_func_t callbs[MAXCB]; 268 char *fields[MAXFIELD]; 269 int nfields; 270 271 FILE *cfg; 272 char line[1024]; 273 char buf[1024]; 274 int done; 275 char *ptr; 276 char *tok; 277 char *err; 278 int lineno; 279 int rv; 280 int found; 281 char path[MAXPATHLEN]; 282 283 va_start(va, fname); 284 for (int i = 0; i < MAXCB; i++) { 285 keyws[i] = (const char *)va_arg(va, const char *); 286 if (keyws[i] == NULL) 287 break; 288 callbs[i] = (test_cfg_func_t)va_arg(va, test_cfg_func_t); 289 } 290 va_end(va); 291 292 found = 0; 293 294 if (access(fname, F_OK) == 0) { 295 found++; 296 } 297 if (!found && fname[0] != '/') { 298 char *stf = getenv("STF_SUITE"); 299 if (stf == NULL) { 300 stf = "../.."; 301 } 302 (void) snprintf(path, sizeof (path), "%s/cfg/%s", stf, fname); 303 if (access(path, F_OK) == 0) { 304 fname = path; 305 found++; 306 } else { 307 (void) snprintf(path, sizeof (path), "cfg/%s", fname); 308 if (access(path, F_OK) == 0) { 309 fname = path; 310 found++; 311 } 312 } 313 } 314 315 if ((cfg = fopen(fname, "r")) == NULL) { 316 test_failed(t, "open(%s): %s", fname, strerror(errno)); 317 return (-1); 318 } 319 320 line[0] = 0; 321 done = 0; 322 lineno = 0; 323 324 while (!done) { 325 326 lineno++; 327 328 if (fgets(buf, sizeof (buf), cfg) == NULL) { 329 done++; 330 } else { 331 (void) strtok(buf, "\n"); 332 if ((*buf != 0) && (buf[strlen(buf)-1] == '\\')) { 333 /* 334 * Continuation. This isn't quite right, 335 * as it doesn't allow for a "\" at the 336 * end of line (no escaping). 337 */ 338 buf[strlen(buf)-1] = 0; 339 (void) strlcat(line, buf, sizeof (line)); 340 continue; 341 } 342 (void) strlcat(line, buf, sizeof (line)); 343 } 344 345 /* got a line */ 346 ptr = line; 347 test_trim(&ptr); 348 349 /* skip comments and empty lines */ 350 if (ptr[0] == 0 || ptr[0] == '#') { 351 line[0] = 0; 352 continue; 353 } 354 355 tok = strsep(&ptr, "|"); 356 if (tok == NULL) { 357 break; 358 } 359 test_trim(&tok); 360 361 for (nfields = 0; nfields < MAXFIELD; nfields++) { 362 fields[nfields] = strsep(&ptr, "|"); 363 if (fields[nfields] == NULL) { 364 break; 365 } 366 test_trim(&fields[nfields]); 367 } 368 369 found = 0; 370 rv = 0; 371 372 for (int i = 0; keyws[i] != NULL; i++) { 373 if (strcmp(tok, keyws[i]) == 0) { 374 found++; 375 err = NULL; 376 rv = callbs[i](fields, nfields, &err); 377 } 378 } 379 if (!found) { 380 rv = -1; 381 err = NULL; 382 (void) asprintf(&err, "unknown keyword %s", tok); 383 } 384 if (rv != 0) { 385 if (err) { 386 test_failed(t, "%s:%d: %s", fname, 387 lineno, err); 388 free(err); 389 } else { 390 test_failed(t, "%s:%d: unknown error", 391 fname, lineno); 392 } 393 (void) fclose(cfg); 394 return (rv); 395 } 396 397 line[0] = 0; 398 } 399 (void) fclose(cfg); 400 return (0); 401 }