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
test_set_debug(void)48 test_set_debug(void)
49 {
50 debug++;
51 }
52
53 void
test_set_force(void)54 test_set_force(void)
55 {
56 force++;
57 }
58
59 test_t
test_start(const char * format,...)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
test_failed(test_t t,const char * format,...)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
test_passed(test_t t)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
test_summary(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
test_debugf(test_t t,const char * format,...)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 *
test_thr_one(void * arg)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
test_run(int nthr,void (* func)(test_t,void *),void * arg,const char * tname,...)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
test_trim(char ** ptr)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
test_load_config(test_t t,const char * fname,...)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 }
407