| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458 |
- /*
- Stupidly simple test framework
- Copyright (C) 2001-2009, Joe Orton <[email protected]>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #include "config.h"
- #include <sys/types.h>
- #include <stdio.h>
- #ifdef HAVE_SIGNAL_H
- #include <signal.h>
- #endif
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef HAVE_STRING_H
- #include <string.h>
- #endif
- #ifdef HAVE_STDLIB_H
- #include <stdlib.h>
- #endif
- #ifdef HAVE_ERRNO_H
- #include <errno.h>
- #endif
- #ifdef HAVE_LOCALE_H
- #include <locale.h>
- #endif
- #include "ne_string.h"
- #include "ne_utils.h"
- #include "ne_socket.h"
- #include "ne_i18n.h"
- #include "tests.h"
- #include "child.h"
- char test_context[BUFSIZ];
- int have_context = 0;
- static FILE *child_debug, *debug;
- char **test_argv;
- int test_argc;
- const char *test_suite;
- int test_num;
- static int quiet, count;
- /* statistics for all tests so far */
- static int passes = 0, fails = 0, skipped = 0, warnings = 0;
- /* per-test globals: */
- static int warned, aborted = 0;
- static const char *test_name; /* current test name */
- static int use_colour = 0;
- static int flag_child;
- /* resource for ANSI escape codes:
- * http://www.isthe.com/chongo/tech/comp/ansi_escapes.html */
- #define COL(x) do { if (use_colour) printf("\033[" x "m"); } while (0)
- #define NOCOL COL("00")
- void t_context(const char *context, ...)
- {
- va_list ap;
- va_start(ap, context);
- ne_vsnprintf(test_context, BUFSIZ, context, ap);
- va_end(ap);
- if (flag_child) {
- NE_DEBUG(NE_DBG_HTTP, "context: %s\n", test_context);
- }
- have_context = 1;
- }
- void t_warning(const char *str, ...)
- {
- va_list ap;
- COL("43;01"); printf("WARNING:"); NOCOL;
- putchar(' ');
- va_start(ap, str);
- vprintf(str, ap);
- va_end(ap);
- warnings++;
- warned++;
- putchar('\n');
- }
- #define TEST_DEBUG \
- (NE_DBG_HTTP | NE_DBG_SOCKET | NE_DBG_HTTPBODY | NE_DBG_HTTPAUTH | \
- NE_DBG_LOCKS | NE_DBG_XMLPARSE | NE_DBG_XML | NE_DBG_SSL | \
- NE_DBG_HTTPPLAIN)
- #define W(m) do { if (write(STDOUT_FILENO, m, strlen(m)) < 0) _exit(99); } while(0)
- #define W_RED(m) do { if (use_colour) W("\033[41;37;01m"); \
- W(m); if (use_colour) W("\033[00m\n"); } while (0);
- #ifndef NEON_NO_TEST_CHILD
- /* Signal handler for child processes. */
- static void child_segv(int signo)
- {
- signal(SIGSEGV, SIG_DFL);
- signal(SIGABRT, SIG_DFL);
- W_RED("Fatal signal in child!");
- kill(getpid(), SIGSEGV);
- minisleep();
- }
- /* Signal handler for parent process. */
- static void parent_segv(int signo)
- {
- signal(SIGSEGV, SIG_DFL);
- signal(SIGABRT, SIG_DFL);
- if (signo == SIGSEGV) {
- W_RED("FAILED - Segmentation fault--\n");
- }
- else if (signo == SIGABRT) {
- W_RED("ABORTED\n");
- }
- else {
- W_RED("-- Unexpected signal! --\n");
- }
- reap_server();
- kill(getpid(), signo);
- minisleep();
- }
- void in_child(void)
- {
- ne_debug_init(child_debug, TEST_DEBUG);
- NE_DEBUG(TEST_DEBUG, "**** Child forked for test %s ****\n", test_name);
- signal(SIGSEGV, child_segv);
- signal(SIGABRT, child_segv);
- flag_child = 1;
- }
- #endif
- static const char dots[] = "......................";
- static void print_prefix(int n)
- {
- if (quiet) {
- printf("\r%s%.*s %2u/%2u ", test_suite,
- (int) (strlen(dots) - strlen(test_suite)), dots,
- n + 1, count);
- }
- else {
- if (warned) {
- printf(" %s ", dots);
- }
- else {
- printf("\r%2d. %s%.*s ", n, test_name,
- (int) (strlen(dots) - strlen(test_name)), dots);
- }
- }
- fflush(stdout);
- }
- int main(int argc, char *argv[])
- {
- int n;
- char *tmp;
-
- /* get basename(argv[0]) */
- test_suite = strrchr(argv[0], '/');
- if (test_suite == NULL) {
- test_suite = argv[0];
- } else {
- test_suite++;
- }
- if (strncmp(test_suite, "lt-", 3) == 0)
- test_suite += 3;
- #ifdef HAVE_SETLOCALE
- setlocale(LC_MESSAGES, "");
- #endif
- ne_i18n_init(NULL);
- #if defined(HAVE_ISATTY) && defined(STDOUT_FILENO)
- if (isatty(STDOUT_FILENO)) {
- use_colour = 1;
- }
- #endif
- test_argc = argc;
- test_argv = argv;
- debug = fopen("debug.log", "a");
- if (debug == NULL) {
- fprintf(stderr, "%s: Could not open debug.log: %s\n", test_suite,
- strerror(errno));
- return -1;
- }
- child_debug = fopen("child.log", "a");
- if (child_debug == NULL) {
- fprintf(stderr, "%s: Could not open child.log: %s\n", test_suite,
- strerror(errno));
- fclose(debug);
- return -1;
- }
- if (tests[0].fn == NULL) {
- printf("-> no tests found in `%s'\n", test_suite);
- return -1;
- }
- #ifndef NEON_NO_TEST_CHILD
- /* install special SEGV handler. */
- signal(SIGSEGV, parent_segv);
- signal(SIGABRT, parent_segv);
- #endif
- /* test the "no-debugging" mode of ne_debug. */
- ne_debug_init(NULL, 0);
- NE_DEBUG(TEST_DEBUG, "This message should go to /dev/null");
- /* enable debugging for real. */
- ne_debug_init(debug, TEST_DEBUG);
- NE_DEBUG(TEST_DEBUG | NE_DBG_FLUSH, "Version string: %s\n",
- ne_version_string());
-
- /* another silly test. */
- NE_DEBUG(0, "This message should also go to /dev/null");
- if (ne_sock_init()) {
- COL("43;01"); printf("WARNING:"); NOCOL;
- printf(" Socket library initialization failed.\n");
- }
- if ((tmp = getenv("TEST_QUIET")) != NULL && strcmp(tmp, "1") == 0) {
- quiet = 1;
- }
- if (!quiet)
- printf("-> running `%s':\n", test_suite);
-
- for (count = 0; tests[count].fn; count++)
- /* nullop */;
- for (n = 0; !aborted && tests[n].fn != NULL; n++) {
- int result, is_xfail = 0;
- #ifdef NEON_MEMLEAK
- size_t allocated = ne_alloc_used;
- int is_xleaky = 0;
- #endif
- test_name = tests[n].name;
- print_prefix(n);
- have_context = 0;
- test_num = n;
- warned = 0;
- fflush(stdout);
- NE_DEBUG(TEST_DEBUG, "******* Running test %d: %s ********\n",
- n, test_name);
- /* run the test. */
- result = tests[n].fn();
- #ifdef NEON_MEMLEAK
- /* issue warnings for memory leaks, if requested */
- if ((tests[n].flags & T_CHECK_LEAKS) && result == OK &&
- ne_alloc_used > allocated) {
- t_context("memory leak of %" NE_FMT_SIZE_T " bytes",
- ne_alloc_used - allocated);
- fprintf(debug, "Blocks leaked: ");
- ne_alloc_dump(debug);
- result = FAIL;
- } else if (tests[n].flags & T_EXPECT_LEAKS && result == OK &&
- ne_alloc_used == allocated) {
- t_context("expected memory leak not detected");
- result = FAIL;
- } else if (tests[n].flags & T_EXPECT_LEAKS && result == OK) {
- fprintf(debug, "Blocks leaked (expected): ");
- ne_alloc_dump(debug);
- is_xleaky = 1;
- }
- #endif
- if (tests[n].flags & T_EXPECT_FAIL) {
- if (result == OK) {
- t_context("test passed but expected failure");
- result = FAIL;
- } else if (result == FAIL) {
- result = OK;
- is_xfail = 1;
- }
- }
- print_prefix(n);
- switch (result) {
- case OK:
- passes++;
- if (is_xfail) {
- COL("32;07");
- printf("XFAIL");
- } else if (!quiet) {
- COL("32");
- printf("pass");
- }
- NOCOL;
- if (quiet && is_xfail) {
- printf(" - %s", test_name);
- if (have_context) {
- printf(" (%s)", test_context);
- }
- }
- if (warned && !quiet) {
- printf(" (with %d warning%s)", warned, (warned > 1)?"s":"");
- }
- #ifdef NEON_MEMLEAK
- if (is_xleaky) {
- if (quiet) {
- printf("expected leak - %s: %" NE_FMT_SIZE_T " bytes",
- test_name, ne_alloc_used - allocated);
- }
- else {
- printf(" (expected leak, %" NE_FMT_SIZE_T " bytes)",
- ne_alloc_used - allocated);
- }
- }
- #endif
- if (!quiet || is_xfail) putchar('\n');
- break;
- case FAILHARD:
- aborted = 1;
- COL("41;37;01"); printf("fatal error - "); NOCOL;
- /* fall-through */
- case FAIL:
- COL("41;37;01"); printf("FAIL"); NOCOL;
- if (quiet) {
- printf(" - %s", test_name);
- }
- if (have_context) {
- printf(" (%s)", test_context);
- }
- putchar('\n');
- fails++;
- break;
- case SKIPREST:
- aborted = 1;
- /* fall-through */
- case SKIP:
- COL("44;37;01"); printf("SKIPPED"); NOCOL;
- if (quiet) {
- printf(" - %s", test_name);
- }
- if (have_context) {
- printf(" (%s)", test_context);
- }
- putchar('\n');
- skipped++;
- break;
- default:
- COL("41;37;01"); printf("OOPS"); NOCOL;
- printf(" unexpected test result `%d'\n", result);
- break;
- }
- #ifndef NEON_NO_TEST_CHILD
- reap_server();
- #endif
- if (quiet) {
- print_prefix(n);
- }
- }
- /* discount skipped tests */
- if (skipped) {
- if (!quiet)
- printf("-> %d %s.\n", skipped,
- skipped == 1 ? "test was skipped" : "tests were skipped");
- n -= skipped;
- }
- /* print the summary. */
- if (skipped && n == 0) {
- if (quiet)
- puts("(all skipped)");
- else
- printf("<- all tests skipped for `%s'.\n", test_suite);
- } else {
- if (quiet) {
- printf("\r%s%.*s %2u/%2u ", test_suite,
- (int) (strlen(dots) - strlen(test_suite)), dots,
- passes, count);
- if (fails == 0) {
- COL("32");
- printf("passed");
- NOCOL;
- putchar(' ');
- }
- else {
- printf("passed, %d failed ", fails);
- }
- if (skipped)
- printf("(%d skipped) ", skipped);
- }
- else /* !quiet */
- printf("<- summary for `%s': "
- "of %d tests run: %d passed, %d failed. %.1f%%\n",
- test_suite, n, passes, fails, 100*(float)passes/n);
- if (warnings) {
- if (quiet) {
- printf("(%d warning%s)\n", warnings,
- warnings > 1 ? "s" : "");
- }
- else {
- printf("-> %d warning%s issued.\n", warnings,
- warnings==1?" was":"s were");
- }
- }
- else if (quiet) {
- putchar('\n');
- }
- }
- if (fclose(debug)) {
- fprintf(stderr, "Error closing debug.log: %s\n", strerror(errno));
- fails = 1;
- }
-
- if (fclose(child_debug)) {
- fprintf(stderr, "Error closing child.log: %s\n", strerror(errno));
- fails = 1;
- }
- ne_sock_exit();
-
- return fails;
- }
|