1 /*
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 2004 Nik Clayton
5  * Copyright (C) 2017 Jérémie Galarneau
6  */
7 
8 #include <ctype.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <assert.h>
15 
16 #include "tap.h"
17 
18 static int no_plan = 0;
19 static int skip_all = 0;
20 static int have_plan = 0;
21 static unsigned int test_count = 0; /* Number of tests that have been run */
22 static unsigned int e_tests = 0; /* Expected number of tests to run */
23 static unsigned int failures = 0; /* Number of tests that failed */
24 static char *todo_msg = NULL;
25 static const char *todo_msg_fixed = "libtap malloc issue";
26 static int todo = 0;
27 static int test_died = 0;
28 static int tap_is_disabled = 0;
29 
30 /* Encapsulate the pthread code in a conditional.  In the absence of
31    libpthread the code does nothing */
32 #ifdef HAVE_LIBPTHREAD
33 #include <pthread.h>
34 static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
35 # define LOCK pthread_mutex_lock(&M);
36 # define UNLOCK pthread_mutex_unlock(&M);
37 #else
38 # define LOCK
39 # define UNLOCK
40 #endif
41 
42 static void _expected_tests(unsigned int);
43 static void _tap_init(void);
44 static void _cleanup(void);
45 
46 #ifdef __MINGW32__
47 static inline
flockfile(FILE * filehandle)48 void flockfile (FILE * filehandle) {
49        return;
50 }
51 
52 static inline
funlockfile(FILE * filehandle)53 void funlockfile(FILE * filehandle) {
54        return;
55 }
56 #endif
57 
58 /*
59  * Generate a test result.
60  *
61  * ok -- boolean, indicates whether or not the test passed.
62  * test_name -- the name of the test, may be NULL
63  * test_comment -- a comment to print afterwards, may be NULL
64  */
65 unsigned int
_gen_result(int ok,const char * func,const char * file,unsigned int line,const char * test_name,...)66 _gen_result(int ok, const char *func, const char *file, unsigned int line,
67 	    const char *test_name, ...)
68 {
69 	va_list ap;
70 	char *local_test_name = NULL;
71 	char *c;
72 	int name_is_digits;
73 
74 	LOCK;
75 
76 	test_count++;
77 
78 	/* Start by taking the test name and performing any printf()
79 	   expansions on it */
80 	if(test_name != NULL) {
81 		va_start(ap, test_name);
82 		if (vasprintf(&local_test_name, test_name, ap) == -1) {
83 			local_test_name = NULL;
84 		}
85 		va_end(ap);
86 
87 		/* Make sure the test name contains more than digits
88 		   and spaces.  Emit an error message and exit if it
89 		   does */
90 		if(local_test_name) {
91 			name_is_digits = 1;
92 			for(c = local_test_name; *c != '\0'; c++) {
93 				if(!isdigit((unsigned char) *c) && !isspace((unsigned char) *c)) {
94 					name_is_digits = 0;
95 					break;
96 				}
97 			}
98 
99 			if(name_is_digits) {
100 				diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
101 				diag("    Very confusing.");
102 			}
103 		}
104 	}
105 
106 	if(!ok) {
107 		printf("not ");
108 		failures++;
109 	}
110 
111 	printf("ok %d", test_count);
112 
113 	if(test_name != NULL) {
114 		printf(" - ");
115 
116 		/* Print the test name, escaping any '#' characters it
117 		   might contain */
118 		if(local_test_name != NULL) {
119 			flockfile(stdout);
120 			for(c = local_test_name; *c != '\0'; c++) {
121 				if(*c == '#')
122 					fputc('\\', stdout);
123 				fputc((int)*c, stdout);
124 			}
125 			funlockfile(stdout);
126 		} else {	/* vasprintf() failed, use a fixed message */
127 			printf("%s", todo_msg_fixed);
128 		}
129 	}
130 
131 	/* If we're in a todo_start() block then flag the test as being
132 	   TODO.  todo_msg should contain the message to print at this
133 	   point.  If it's NULL then asprintf() failed, and we should
134 	   use the fixed message.
135 
136 	   This is not counted as a failure, so decrement the counter if
137 	   the test failed. */
138 	if(todo) {
139 		printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
140 		if(!ok)
141 			failures--;
142 	}
143 
144 	printf("\n");
145 
146 	if(!ok) {
147 		if(getenv("HARNESS_ACTIVE") != NULL)
148 			fputs("\n", stderr);
149 
150 		diag("    Failed %stest (%s:%s() at line %d)",
151 		     todo ? "(TODO) " : "", file, func, line);
152 	}
153 	free(local_test_name);
154 
155 	UNLOCK;
156 
157 	/* We only care (when testing) that ok is positive, but here we
158 	   specifically only want to return 1 or 0 */
159 	return ok ? 1 : 0;
160 }
161 
162 /*
163  * Initialise the TAP library.  Will only do so once, however many times it's
164  * called.
165  */
166 void
_tap_init(void)167 _tap_init(void)
168 {
169 	static int run_once = 0;
170 
171 	if(!run_once) {
172 		atexit(_cleanup);
173 
174 		/* stdout needs to be unbuffered so that the output appears
175 		   in the same place relative to stderr output as it does
176 		   with Test::Harness */
177 		setbuf(stdout, 0);
178 		run_once = 1;
179 	}
180 }
181 
182 /*
183  * Note that there's no plan.
184  */
185 int
plan_no_plan(void)186 plan_no_plan(void)
187 {
188 
189 	LOCK;
190 
191 	_tap_init();
192 
193 	if(have_plan != 0) {
194 		fprintf(stderr, "You tried to plan twice!\n");
195 		test_died = 1;
196 		UNLOCK;
197 		exit(255);
198 	}
199 
200 	have_plan = 1;
201 	no_plan = 1;
202 
203 	UNLOCK;
204 
205 	return 1;
206 }
207 
208 /*
209  * Note that the plan is to skip all tests
210  */
211 int
plan_skip_all(const char * reason)212 plan_skip_all(const char *reason)
213 {
214 
215 	LOCK;
216 
217 	_tap_init();
218 
219 	skip_all = 1;
220 
221 	printf("1..0");
222 
223 	if(reason != NULL)
224 		printf(" # Skip %s", reason);
225 
226 	printf("\n");
227 
228 	UNLOCK;
229 
230 	exit(0);
231 }
232 
233 /*
234  * Note the number of tests that will be run.
235  */
236 int
plan_tests(unsigned int tests)237 plan_tests(unsigned int tests)
238 {
239 
240 	LOCK;
241 
242 	_tap_init();
243 
244 	if(have_plan != 0) {
245 		fprintf(stderr, "You tried to plan twice!\n");
246 		test_died = 1;
247 		UNLOCK;
248 		exit(255);
249 	}
250 
251 	if(tests == 0) {
252 		fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
253 		test_died = 1;
254 		UNLOCK;
255 		exit(255);
256 	}
257 
258 	have_plan = 1;
259 
260 	_expected_tests(tests);
261 
262 	UNLOCK;
263 
264 	return e_tests;
265 }
266 
267 unsigned int
diag(const char * fmt,...)268 diag(const char *fmt, ...)
269 {
270 	va_list ap;
271 
272 	fputs("# ", stderr);
273 
274 	va_start(ap, fmt);
275 	vfprintf(stderr, fmt, ap);
276 	va_end(ap);
277 
278 	fputs("\n", stderr);
279 
280 	return 0;
281 }
282 
283 unsigned int
rdiag_start(void)284 rdiag_start(void)
285 {
286 	fputs("# ", stderr);
287 	return 0;
288 }
289 
290 unsigned int
rdiag(const char * fmt,...)291 rdiag(const char *fmt, ...)
292 {
293 	va_list ap;
294 
295 	va_start(ap, fmt);
296 	vfprintf(stderr, fmt, ap);
297 	va_end(ap);
298 
299 	return 0;
300 }
301 
302 unsigned int
rdiag_end(void)303 rdiag_end(void)
304 {
305 	fputs("\n", stderr);
306 	return 0;
307 }
308 
309 void
diag_multiline(const char * val)310 diag_multiline(const char *val)
311 {
312 	size_t len, i, line_start_idx = 0;
313 
314 	assert(val);
315 	len = strlen(val);
316 
317 	for (i = 0; i < len; i++) {
318 		int line_length;
319 
320 		if (val[i] != '\n') {
321 			continue;
322 		}
323 
324 		assert((i - line_start_idx + 1) <= INT_MAX);
325 		line_length = i - line_start_idx + 1;
326 		fprintf(stderr, "# %.*s", line_length, &val[line_start_idx]);
327 		line_start_idx = i + 1;
328 	}
329 }
330 
331 void
_expected_tests(unsigned int tests)332 _expected_tests(unsigned int tests)
333 {
334 
335 	printf("1..%d\n", tests);
336 	e_tests = tests;
337 }
338 
339 int
skip(unsigned int n,const char * fmt,...)340 skip(unsigned int n, const char *fmt, ...)
341 {
342 	va_list ap;
343 	char *skip_msg = NULL;
344 
345 	LOCK;
346 
347 	va_start(ap, fmt);
348 	if (vasprintf(&skip_msg, fmt, ap) == -1) {
349 		skip_msg = NULL;
350 	}
351 	va_end(ap);
352 
353 	while(n-- > 0) {
354 		test_count++;
355 		printf("ok %d # skip %s\n", test_count,
356 		       skip_msg != NULL ?
357 		       skip_msg : "libtap():malloc() failed");
358 	}
359 
360 	free(skip_msg);
361 
362 	UNLOCK;
363 
364 	return 1;
365 }
366 
367 void
todo_start(const char * fmt,...)368 todo_start(const char *fmt, ...)
369 {
370 	va_list ap;
371 
372 	LOCK;
373 
374 	va_start(ap, fmt);
375 	if (vasprintf(&todo_msg, fmt, ap) == -1) {
376 		todo_msg = NULL;
377 	}
378 	va_end(ap);
379 
380 	todo = 1;
381 
382 	UNLOCK;
383 }
384 
385 void
todo_end(void)386 todo_end(void)
387 {
388 
389 	LOCK;
390 
391 	todo = 0;
392 	free(todo_msg);
393 
394 	UNLOCK;
395 }
396 
397 int
exit_status(void)398 exit_status(void)
399 {
400 	int r;
401 
402 	LOCK;
403 
404 	/* If there's no plan, just return the number of failures */
405 	if(no_plan || !have_plan) {
406 		UNLOCK;
407 		return failures;
408 	}
409 
410 	/* Ran too many tests?  Return the number of tests that were run
411 	   that shouldn't have been */
412 	if(e_tests < test_count) {
413 		r = test_count - e_tests;
414 		UNLOCK;
415 		return r;
416 	}
417 
418 	/* Return the number of tests that failed + the number of tests
419 	   that weren't run */
420 	r = failures + e_tests - test_count;
421 	UNLOCK;
422 
423 	return r;
424 }
425 
426 /*
427  * Cleanup at the end of the run, produce any final output that might be
428  * required.
429  */
430 void
_cleanup(void)431 _cleanup(void)
432 {
433 
434 	LOCK;
435 
436 	if (tap_is_disabled) {
437 		UNLOCK;
438 		return;
439 	}
440 
441 	/* If plan_no_plan() wasn't called, and we don't have a plan,
442 	   and we're not skipping everything, then something happened
443 	   before we could produce any output */
444 	if(!no_plan && !have_plan && !skip_all) {
445 		diag("Looks like your test died before it could output anything.");
446 		UNLOCK;
447 		return;
448 	}
449 
450 	if(test_died) {
451 		diag("Looks like your test died just after %d.", test_count);
452 		UNLOCK;
453 		return;
454 	}
455 
456 
457 	/* No plan provided, but now we know how many tests were run, and can
458 	   print the header at the end */
459 	if(!skip_all && (no_plan || !have_plan)) {
460 		printf("1..%d\n", test_count);
461 	}
462 
463 	if((have_plan && !no_plan) && e_tests < test_count) {
464 		diag("Looks like you planned %d %s but ran %d extra.",
465 		     e_tests, e_tests == 1 ? "test" : "tests", test_count - e_tests);
466 		UNLOCK;
467 		return;
468 	}
469 
470 	if((have_plan || !no_plan) && e_tests > test_count) {
471 		diag("Looks like you planned %d %s but only ran %d.",
472 		     e_tests, e_tests == 1 ? "test" : "tests", test_count);
473 		UNLOCK;
474 		return;
475 	}
476 
477 	if(failures)
478 		diag("Looks like you failed %d %s of %d.",
479 		     failures, failures == 1 ? "test" : "tests", test_count);
480 
481 	UNLOCK;
482 }
483 
484 /* Disable tap for this process. */
485 void
tap_disable(void)486 tap_disable(void)
487 {
488 	LOCK;
489 	tap_is_disabled = 1;
490 	UNLOCK;
491 }
492