1/* 2 Stupidly simple test framework 3 Copyright (C) 2001-2009, Joe Orton <joe@manyfish.co.uk> 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 19*/ 20 21#include "config.h" 22 23#include <sys/types.h> 24 25#include <stdio.h> 26#ifdef HAVE_SIGNAL_H 27#include <signal.h> 28#endif 29#ifdef HAVE_UNISTD_H 30#include <unistd.h> 31#endif 32#ifdef HAVE_STRING_H 33#include <string.h> 34#endif 35#ifdef HAVE_STDLIB_H 36#include <stdlib.h> 37#endif 38#ifdef HAVE_ERRNO_H 39#include <errno.h> 40#endif 41#ifdef HAVE_LOCALE_H 42#include <locale.h> 43#endif 44 45#include "ne_string.h" 46#include "ne_utils.h" 47#include "ne_socket.h" 48#include "ne_i18n.h" 49 50#include "tests.h" 51#include "child.h" 52 53char test_context[BUFSIZ]; 54int have_context = 0; 55 56static FILE *child_debug, *debug; 57 58char **test_argv; 59int test_argc; 60 61const char *test_suite; 62int test_num; 63 64static int quiet, count; 65 66/* statistics for all tests so far */ 67static int passes = 0, fails = 0, skipped = 0, warnings = 0; 68 69/* per-test globals: */ 70static int warned, aborted = 0; 71static const char *test_name; /* current test name */ 72 73static int use_colour = 0; 74 75static int flag_child; 76 77/* resource for ANSI escape codes: 78 * http://www.isthe.com/chongo/tech/comp/ansi_escapes.html */ 79#define COL(x) do { if (use_colour) printf("\033[" x "m"); } while (0) 80 81#define NOCOL COL("00") 82 83void t_context(const char *context, ...) 84{ 85 va_list ap; 86 va_start(ap, context); 87 ne_vsnprintf(test_context, BUFSIZ, context, ap); 88 va_end(ap); 89 if (flag_child) { 90 NE_DEBUG(NE_DBG_HTTP, "context: %s\n", test_context); 91 } 92 have_context = 1; 93} 94 95void t_warning(const char *str, ...) 96{ 97 va_list ap; 98 COL("43;01"); printf("WARNING:"); NOCOL; 99 putchar(' '); 100 va_start(ap, str); 101 vprintf(str, ap); 102 va_end(ap); 103 warnings++; 104 warned++; 105 putchar('\n'); 106} 107 108#define TEST_DEBUG \ 109(NE_DBG_HTTP | NE_DBG_SOCKET | NE_DBG_HTTPBODY | NE_DBG_HTTPAUTH | \ 110 NE_DBG_LOCKS | NE_DBG_XMLPARSE | NE_DBG_XML | NE_DBG_SSL | \ 111 NE_DBG_HTTPPLAIN) 112 113#define W(m) do { if (write(0, m, strlen(m)) < 0) exit(99); } while(0) 114 115#define W_RED(m) do { if (use_colour) W("\033[41;37;01m"); \ 116W(m); if (use_colour) W("\033[00m\n"); } while (0); 117 118/* Signal handler for child processes. */ 119static void child_segv(int signo) 120{ 121 signal(SIGSEGV, SIG_DFL); 122 signal(SIGABRT, SIG_DFL); 123 W_RED("Fatal signal in child!"); 124 kill(getpid(), SIGSEGV); 125 minisleep(); 126} 127 128/* Signal handler for parent process. */ 129static void parent_segv(int signo) 130{ 131 signal(SIGSEGV, SIG_DFL); 132 signal(SIGABRT, SIG_DFL); 133 if (signo == SIGSEGV) { 134 W_RED("FAILED - segmentation fault"); 135 } else if (signo == SIGABRT) { 136 W_RED("ABORTED"); 137 } 138 reap_server(); 139 kill(getpid(), SIGSEGV); 140 minisleep(); 141} 142 143void in_child(void) 144{ 145 ne_debug_init(child_debug, TEST_DEBUG); 146 NE_DEBUG(TEST_DEBUG, "**** Child forked for test %s ****\n", test_name); 147 signal(SIGSEGV, child_segv); 148 signal(SIGABRT, child_segv); 149 flag_child = 1; 150} 151 152static const char dots[] = "......................"; 153 154static void print_prefix(int n) 155{ 156 if (quiet) { 157 printf("\r%s%.*s %2u/%2u ", test_suite, 158 (int) (strlen(dots) - strlen(test_suite)), dots, 159 n + 1, count); 160 } 161 else { 162 if (warned) { 163 printf(" %s ", dots); 164 } 165 else { 166 printf("\r%2d. %s%.*s ", n, test_name, 167 (int) (strlen(dots) - strlen(test_name)), dots); 168 } 169 } 170 fflush(stdout); 171} 172 173 174int main(int argc, char *argv[]) 175{ 176 int n; 177 char *tmp; 178 179 /* get basename(argv[0]) */ 180 test_suite = strrchr(argv[0], '/'); 181 if (test_suite == NULL) { 182 test_suite = argv[0]; 183 } else { 184 test_suite++; 185 } 186 187#ifdef HAVE_SETLOCALE 188 setlocale(LC_MESSAGES, ""); 189#endif 190 191 ne_i18n_init(NULL); 192 193#if defined(HAVE_ISATTY) && defined(STDOUT_FILENO) 194 if (isatty(STDOUT_FILENO)) { 195 use_colour = 1; 196 } 197#endif 198 199 test_argc = argc; 200 test_argv = argv; 201 202 debug = fopen("debug.log", "a"); 203 if (debug == NULL) { 204 fprintf(stderr, "%s: Could not open debug.log: %s\n", test_suite, 205 strerror(errno)); 206 return -1; 207 } 208 child_debug = fopen("child.log", "a"); 209 if (child_debug == NULL) { 210 fprintf(stderr, "%s: Could not open child.log: %s\n", test_suite, 211 strerror(errno)); 212 fclose(debug); 213 return -1; 214 } 215 216 if (tests[0].fn == NULL) { 217 printf("-> no tests found in `%s'\n", test_suite); 218 return -1; 219 } 220 221 /* install special SEGV handler. */ 222 signal(SIGSEGV, parent_segv); 223 signal(SIGABRT, parent_segv); 224 225 /* test the "no-debugging" mode of ne_debug. */ 226 ne_debug_init(NULL, 0); 227 NE_DEBUG(TEST_DEBUG, "This message should go to /dev/null"); 228 229 /* enable debugging for real. */ 230 ne_debug_init(debug, TEST_DEBUG); 231 NE_DEBUG(TEST_DEBUG | NE_DBG_FLUSH, "Version string: %s\n", 232 ne_version_string()); 233 234 /* another silly test. */ 235 NE_DEBUG(0, "This message should also go to /dev/null"); 236 237 if (ne_sock_init()) { 238 COL("43;01"); printf("WARNING:"); NOCOL; 239 printf(" Socket library initalization failed.\n"); 240 } 241 242 if ((tmp = getenv("TEST_QUIET")) != NULL && strcmp(tmp, "1") == 0) { 243 quiet = 1; 244 } 245 246 if (!quiet) 247 printf("-> running `%s':\n", test_suite); 248 249 for (count = 0; tests[count].fn; count++) 250 /* nullop */; 251 252 for (n = 0; !aborted && tests[n].fn != NULL; n++) { 253 int result, is_xfail = 0; 254#ifdef NEON_MEMLEAK 255 size_t allocated = ne_alloc_used; 256 int is_xleaky = 0; 257#endif 258 259 test_name = tests[n].name; 260 261 print_prefix(n); 262 263 have_context = 0; 264 test_num = n; 265 warned = 0; 266 fflush(stdout); 267 NE_DEBUG(TEST_DEBUG, "******* Running test %d: %s ********\n", 268 n, test_name); 269 270 /* run the test. */ 271 result = tests[n].fn(); 272 273#ifdef NEON_MEMLEAK 274 /* issue warnings for memory leaks, if requested */ 275 if ((tests[n].flags & T_CHECK_LEAKS) && result == OK && 276 ne_alloc_used > allocated) { 277 t_context("memory leak of %" NE_FMT_SIZE_T " bytes", 278 ne_alloc_used - allocated); 279 fprintf(debug, "Blocks leaked: "); 280 ne_alloc_dump(debug); 281 result = FAIL; 282 } else if (tests[n].flags & T_EXPECT_LEAKS && result == OK && 283 ne_alloc_used == allocated) { 284 t_context("expected memory leak not detected"); 285 result = FAIL; 286 } else if (tests[n].flags & T_EXPECT_LEAKS && result == OK) { 287 fprintf(debug, "Blocks leaked (expected): "); 288 ne_alloc_dump(debug); 289 is_xleaky = 1; 290 } 291#endif 292 293 if (tests[n].flags & T_EXPECT_FAIL) { 294 if (result == OK) { 295 t_context("test passed but expected failure"); 296 result = FAIL; 297 } else if (result == FAIL) { 298 result = OK; 299 is_xfail = 1; 300 } 301 } 302 303 print_prefix(n); 304 305 switch (result) { 306 case OK: 307 passes++; 308 if (is_xfail) { 309 COL("32;07"); 310 printf("XFAIL"); 311 } else if (!quiet) { 312 COL("32"); 313 printf("pass"); 314 } 315 NOCOL; 316 if (quiet && is_xfail) { 317 printf(" - %s", test_name); 318 if (have_context) { 319 printf(" (%s)", test_context); 320 } 321 } 322 if (warned && !quiet) { 323 printf(" (with %d warning%s)", warned, (warned > 1)?"s":""); 324 } 325#ifdef NEON_MEMLEAK 326 if (is_xleaky) { 327 if (quiet) { 328 printf("expected leak - %s: %" NE_FMT_SIZE_T " bytes", 329 test_name, ne_alloc_used - allocated); 330 } 331 else { 332 printf(" (expected leak, %" NE_FMT_SIZE_T " bytes)", 333 ne_alloc_used - allocated); 334 } 335 } 336#endif 337 if (!quiet || is_xfail) putchar('\n'); 338 break; 339 case FAILHARD: 340 aborted = 1; 341 /* fall-through */ 342 case FAIL: 343 COL("41;37;01"); printf("FAIL"); NOCOL; 344 if (quiet) { 345 printf(" - %s", test_name); 346 } 347 if (have_context) { 348 printf(" (%s)", test_context); 349 } 350 putchar('\n'); 351 fails++; 352 break; 353 case SKIPREST: 354 aborted = 1; 355 /* fall-through */ 356 case SKIP: 357 COL("44;37;01"); printf("SKIPPED"); NOCOL; 358 if (quiet) { 359 printf(" - %s", test_name); 360 } 361 if (have_context) { 362 printf(" (%s)", test_context); 363 } 364 putchar('\n'); 365 skipped++; 366 break; 367 default: 368 COL("41;37;01"); printf("OOPS"); NOCOL; 369 printf(" unexpected test result `%d'\n", result); 370 break; 371 } 372 373 reap_server(); 374 375 if (quiet) { 376 print_prefix(n); 377 } 378 } 379 380 /* discount skipped tests */ 381 if (skipped) { 382 if (!quiet) 383 printf("-> %d %s.\n", skipped, 384 skipped == 1 ? "test was skipped" : "tests were skipped"); 385 n -= skipped; 386 } 387 /* print the summary. */ 388 if (skipped && n == 0) { 389 if (quiet) 390 puts("(all skipped)"); 391 else 392 printf("<- all tests skipped for `%s'.\n", test_suite); 393 } else { 394 if (quiet) { 395 printf("\r%s%.*s %2u/%2u ", test_suite, 396 (int) (strlen(dots) - strlen(test_suite)), dots, 397 passes, count); 398 if (fails == 0) { 399 COL("32"); 400 printf("passed"); 401 NOCOL; 402 putchar(' '); 403 } 404 else { 405 printf("passed, %d failed ", fails); 406 } 407 if (skipped) 408 printf("(%d skipped) ", skipped); 409 } 410 else /* !quiet */ 411 printf("<- summary for `%s': " 412 "of %d tests run: %d passed, %d failed. %.1f%%\n", 413 test_suite, n, passes, fails, 100*(float)passes/n); 414 if (warnings) { 415 if (quiet) { 416 printf("(%d warning%s)\n", warnings, 417 warnings==1?"s":""); 418 } 419 else { 420 printf("-> %d warning%s issued.\n", warnings, 421 warnings==1?" was":"s were"); 422 } 423 } 424 else if (quiet) { 425 putchar('\n'); 426 } 427 } 428 429 if (fclose(debug)) { 430 fprintf(stderr, "Error closing debug.log: %s\n", strerror(errno)); 431 fails = 1; 432 } 433 434 if (fclose(child_debug)) { 435 fprintf(stderr, "Error closing child.log: %s\n", strerror(errno)); 436 fails = 1; 437 } 438 439 ne_sock_exit(); 440 441 return fails; 442} 443 444