vis

a vi-like editor based on Plan 9's structural regular expressions

git clone https://9o.is/git/vis.git

tap.c

(9292B)


      1 /*-
      2  * Copyright (c) 2004 Nik Clayton
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     24  * SUCH DAMAGE.
     25  */
     26 #include "config.h"
     27 #include <ctype.h>
     28 #include <stdarg.h>
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <unistd.h>
     32 
     33 #include "tap.h"
     34 
     35 void (*tap_fail_callback)(void) = NULL;
     36 
     37 static int no_plan = 0;
     38 static int skip_all = 0;
     39 static int have_plan = 0;
     40 static unsigned int test_count = 0; /* Number of tests that have been run */
     41 static unsigned int e_tests = 0; /* Expected number of tests to run */
     42 static unsigned int failures = 0; /* Number of tests that failed */
     43 static char *todo_msg = NULL;
     44 static const char *todo_msg_fixed = "libtap malloc issue";
     45 static int todo = 0;
     46 static int test_died = 0;
     47 static int test_pid;
     48 
     49 /* Encapsulate the pthread code in a conditional.  In the absence of
     50    libpthread the code does nothing.
     51 
     52    If you have multiple threads calling ok() etc. at the same time you would
     53    need this, but in that case your test numbers will be random and I'm not
     54    sure it makes sense. --RR
     55 */
     56 #ifdef WANT_PTHREAD
     57 #include <pthread.h>
     58 static pthread_mutex_t M = PTHREAD_MUTEX_INITIALIZER;
     59 # define LOCK pthread_mutex_lock(&M)
     60 # define UNLOCK pthread_mutex_unlock(&M)
     61 #else
     62 # define LOCK
     63 # define UNLOCK
     64 #endif
     65 
     66 static void
     67 _expected_tests(unsigned int tests)
     68 {
     69 
     70 	printf("1..%d\n", tests);
     71 	e_tests = tests;
     72 }
     73 
     74 static void
     75 diagv(const char *fmt, va_list ap)
     76 {
     77 	fputs("# ", stdout);
     78 	vfprintf(stdout, fmt, ap);
     79 	fputs("\n", stdout);
     80 }
     81 
     82 static void
     83 _diag(const char *fmt, ...)
     84 {
     85 	va_list ap;
     86 	va_start(ap, fmt);
     87  	diagv(fmt, ap);
     88 	va_end(ap);
     89 }
     90 
     91 /*
     92  * Generate a test result.
     93  *
     94  * ok -- boolean, indicates whether or not the test passed.
     95  * test_name -- the name of the test, may be NULL
     96  * test_comment -- a comment to print afterwards, may be NULL
     97  */
     98 unsigned int
     99 _gen_result(int ok, const char *func, const char *file, unsigned int line,
    100 	    const char *test_name, ...)
    101 {
    102 	va_list ap;
    103 	char *local_test_name = NULL;
    104 	char *c;
    105 	int name_is_digits;
    106 
    107 	LOCK;
    108 
    109 	test_count++;
    110 
    111 	/* Start by taking the test name and performing any printf()
    112 	   expansions on it */
    113 	if(test_name != NULL) {
    114 		va_start(ap, test_name);
    115 		if (vasprintf(&local_test_name, test_name, ap) < 0)
    116 			local_test_name = NULL;
    117 		va_end(ap);
    118 
    119 		/* Make sure the test name contains more than digits
    120 		   and spaces.  Emit an error message and exit if it
    121 		   does */
    122 		if(local_test_name) {
    123 			name_is_digits = 1;
    124 			for(c = local_test_name; *c != '\0'; c++) {
    125 				if(!isdigit((unsigned char)*c)
    126 				   && !isspace((unsigned char)*c)) {
    127 					name_is_digits = 0;
    128 					break;
    129 				}
    130 			}
    131 
    132 			if(name_is_digits) {
    133 				_diag("    You named your test '%s'.  You shouldn't use numbers for your test names.", local_test_name);
    134 				_diag("    Very confusing.");
    135 			}
    136 		}
    137 	}
    138 
    139 	if(!ok) {
    140 		printf("not ");
    141 		failures++;
    142 	}
    143 
    144 	printf("ok %d", test_count);
    145 
    146 	if(test_name != NULL) {
    147 		printf(" - ");
    148 
    149 		/* Print the test name, escaping any '#' characters it
    150 		   might contain */
    151 		if(local_test_name != NULL) {
    152 			flockfile(stdout);
    153 			for(c = local_test_name; *c != '\0'; c++) {
    154 				if(*c == '#')
    155 					fputc('\\', stdout);
    156 				fputc((int)*c, stdout);
    157 			}
    158 			funlockfile(stdout);
    159 		} else {	/* vasprintf() failed, use a fixed message */
    160 			printf("%s", todo_msg_fixed);
    161 		}
    162 	}
    163 
    164 	/* If we're in a todo_start() block then flag the test as being
    165 	   TODO.  todo_msg should contain the message to print at this
    166 	   point.  If it's NULL then asprintf() failed, and we should
    167 	   use the fixed message.
    168 
    169 	   This is not counted as a failure, so decrement the counter if
    170 	   the test failed. */
    171 	if(todo) {
    172 		printf(" # TODO %s", todo_msg ? todo_msg : todo_msg_fixed);
    173 		if(!ok)
    174 			failures--;
    175 	}
    176 
    177 	printf("\n");
    178 
    179 	if(!ok)
    180 		_diag("    Failed %stest (%s:%s() at line %d)",
    181 		      todo ? "(TODO) " : "", file, func, line);
    182 
    183 	free(local_test_name);
    184 
    185 	UNLOCK;
    186 
    187 	if (!ok && tap_fail_callback)
    188 		tap_fail_callback();
    189 
    190 	/* We only care (when testing) that ok is positive, but here we
    191 	   specifically only want to return 1 or 0 */
    192 	return ok ? 1 : 0;
    193 }
    194 
    195 /*
    196  * Cleanup at the end of the run, produce any final output that might be
    197  * required.
    198  */
    199 static void
    200 _cleanup(void)
    201 {
    202 	/* If we forked, don't do cleanup in child! */
    203 	if (getpid() != test_pid)
    204 		return;
    205 
    206 	LOCK;
    207 
    208 	/* If plan_no_plan() wasn't called, and we don't have a plan,
    209 	   and we're not skipping everything, then something happened
    210 	   before we could produce any output */
    211 	if(!no_plan && !have_plan && !skip_all) {
    212 		_diag("Looks like your test died before it could output anything.");
    213 		UNLOCK;
    214 		return;
    215 	}
    216 
    217 	if(test_died) {
    218 		_diag("Looks like your test died just after %d.", test_count);
    219 		UNLOCK;
    220 		return;
    221 	}
    222 
    223 
    224 	/* No plan provided, but now we know how many tests were run, and can
    225 	   print the header at the end */
    226 	if(!skip_all && (no_plan || !have_plan)) {
    227 		printf("1..%d\n", test_count);
    228 	}
    229 
    230 	if((have_plan && !no_plan) && e_tests < test_count) {
    231 		_diag("Looks like you planned %d tests but ran %d extra.",
    232 		      e_tests, test_count - e_tests);
    233 		UNLOCK;
    234 		return;
    235 	}
    236 
    237 	if((have_plan || !no_plan) && e_tests > test_count) {
    238 		_diag("Looks like you planned %d tests but only ran %d.",
    239 		      e_tests, test_count);
    240 		if(failures) {
    241 			_diag("Looks like you failed %d tests of %d run.",
    242 			      failures, test_count);
    243 		}
    244 		UNLOCK;
    245 		return;
    246 	}
    247 
    248 	if(failures)
    249 		_diag("Looks like you failed %d tests of %d.",
    250 		      failures, test_count);
    251 
    252 	UNLOCK;
    253 }
    254 
    255 /*
    256  * Initialise the TAP library.  Will only do so once, however many times it's
    257  * called.
    258  */
    259 static void
    260 _tap_init(void)
    261 {
    262 	static int run_once = 0;
    263 
    264 	if(!run_once) {
    265 		test_pid = getpid();
    266 		atexit(_cleanup);
    267 
    268 		/* stdout needs to be unbuffered so that the output appears
    269 		   in the same place relative to stderr output as it does
    270 		   with Test::Harness */
    271 //		setbuf(stdout, 0);
    272 		run_once = 1;
    273 	}
    274 }
    275 
    276 /*
    277  * Note that there's no plan.
    278  */
    279 void
    280 plan_no_plan(void)
    281 {
    282 
    283 	LOCK;
    284 
    285 	_tap_init();
    286 
    287 	if(have_plan != 0) {
    288 		fprintf(stderr, "You tried to plan twice!\n");
    289 		test_died = 1;
    290 		UNLOCK;
    291 		exit(255);
    292 	}
    293 
    294 	have_plan = 1;
    295 	no_plan = 1;
    296 
    297 	UNLOCK;
    298 }
    299 
    300 /*
    301  * Note that the plan is to skip all tests
    302  */
    303 void
    304 plan_skip_all(const char *reason)
    305 {
    306 
    307 	LOCK;
    308 
    309 	_tap_init();
    310 
    311 	skip_all = 1;
    312 
    313 	printf("1..0");
    314 
    315 	if(reason != NULL)
    316 		printf(" # Skip %s", reason);
    317 
    318 	printf("\n");
    319 
    320 	UNLOCK;
    321 }
    322 
    323 /*
    324  * Note the number of tests that will be run.
    325  */
    326 void
    327 plan_tests(unsigned int tests)
    328 {
    329 
    330 	LOCK;
    331 
    332 	_tap_init();
    333 
    334 	if(have_plan != 0) {
    335 		fprintf(stderr, "You tried to plan twice!\n");
    336 		test_died = 1;
    337 		UNLOCK;
    338 		exit(255);
    339 	}
    340 
    341 	if(tests == 0) {
    342 		fprintf(stderr, "You said to run 0 tests!  You've got to run something.\n");
    343 		test_died = 1;
    344 		UNLOCK;
    345 		exit(255);
    346 	}
    347 
    348 	have_plan = 1;
    349 
    350 	_expected_tests(tests);
    351 
    352 	UNLOCK;
    353 }
    354 
    355 void
    356 diag(const char *fmt, ...)
    357 {
    358 	va_list ap;
    359 
    360 	LOCK;
    361 
    362 	va_start(ap, fmt);
    363  	diagv(fmt, ap);
    364 	va_end(ap);
    365 
    366 	UNLOCK;
    367 }
    368 
    369 void
    370 skip(unsigned int n, const char *fmt, ...)
    371 {
    372 	va_list ap;
    373 	char *skip_msg;
    374 
    375 	LOCK;
    376 
    377 	va_start(ap, fmt);
    378 	if (vasprintf(&skip_msg, fmt, ap) < 0)
    379 		skip_msg = NULL;
    380 	va_end(ap);
    381 
    382 	while(n-- > 0) {
    383 		test_count++;
    384 		printf("ok %d # skip %s\n", test_count,
    385 		       skip_msg != NULL ?
    386 		       skip_msg : "libtap():malloc() failed");
    387 	}
    388 
    389 	free(skip_msg);
    390 
    391 	UNLOCK;
    392 }
    393 
    394 void
    395 todo_start(const char *fmt, ...)
    396 {
    397 	va_list ap;
    398 
    399 	LOCK;
    400 
    401 	va_start(ap, fmt);
    402 	if (vasprintf(&todo_msg, fmt, ap) < 0)
    403 		todo_msg = NULL;
    404 	va_end(ap);
    405 
    406 	todo = 1;
    407 
    408 	UNLOCK;
    409 }
    410 
    411 void
    412 todo_end(void)
    413 {
    414 
    415 	LOCK;
    416 
    417 	todo = 0;
    418 	free(todo_msg);
    419 
    420 	UNLOCK;
    421 }
    422 
    423 static int
    424 exit_status_(void)
    425 {
    426 	int r;
    427 
    428 	LOCK;
    429 
    430 	/* If there's no plan, just return the number of failures */
    431 	if(no_plan || !have_plan) {
    432 		UNLOCK;
    433 		return failures;
    434 	}
    435 
    436 	/* Ran too many tests?  Return the number of tests that were run
    437 	   that shouldn't have been */
    438 	if(e_tests < test_count) {
    439 		r = test_count - e_tests;
    440 		UNLOCK;
    441 		return r;
    442 	}
    443 
    444 	/* Return the number of tests that failed + the number of tests
    445 	   that weren't run */
    446 	r = failures + e_tests - test_count;
    447 	UNLOCK;
    448 
    449 	return r;
    450 }
    451 
    452 int
    453 exit_status(void)
    454 {
    455 	int r = exit_status_();
    456 	if (r > 255)
    457 		r = 255;
    458 	return r;
    459 }