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 }