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