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