Home | History | Annotate | Line # | Download | only in utils
tap.c revision 1.1.1.1
      1 /*
      2  * SPDX-License-Identifier: BSD-2-Clause
      3  *
      4  * Copyright (C) 2004 Nik Clayton
      5  * Copyright (C) 2017 Jrmie 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
     48 void flockfile (FILE * filehandle) {
     49        return;
     50 }
     51 
     52 static inline
     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
     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
    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
    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
    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
    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
    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
    284 rdiag_start(void)
    285 {
    286 	fputs("# ", stderr);
    287 	return 0;
    288 }
    289 
    290 unsigned int
    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
    303 rdiag_end(void)
    304 {
    305 	fputs("\n", stderr);
    306 	return 0;
    307 }
    308 
    309 void
    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
    332 _expected_tests(unsigned int tests)
    333 {
    334 
    335 	printf("1..%d\n", tests);
    336 	e_tests = tests;
    337 }
    338 
    339 int
    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
    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
    386 todo_end(void)
    387 {
    388 
    389 	LOCK;
    390 
    391 	todo = 0;
    392 	free(todo_msg);
    393 
    394 	UNLOCK;
    395 }
    396 
    397 int
    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
    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
    486 tap_disable(void)
    487 {
    488 	LOCK;
    489 	tap_is_disabled = 1;
    490 	UNLOCK;
    491 }
    492