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