Merge branch 'master' of ssh://apples.lambdacomplex.org/git/bus
[bus.git] / origin-src / wvtest.cc
<
1 /*
2 * WvTest:
3 * Copyright (C) 1997-2009 Net Integration Technologies, Inc.
4 * Licensed under the GNU Library General Public License, version 2.
5 * See the included file named LICENSE for license information.
6 */
7 #include "wvtest.h"
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdlib.h>
11 #include <ctype.h>
12 #ifdef _WIN32
13 #include <direct.h>
14 #else
15 #include <unistd.h>
16 #include <sys/wait.h>
17 #endif
18 #include <errno.h>
19 #include <signal.h>
20
21 #include <cstdlib>
22
23 #ifdef HAVE_VALGRIND_MEMCHECK_H
24 # include <valgrind/memcheck.h>
25 # include <valgrind/valgrind.h>
26 #else
27 # define VALGRIND_COUNT_ERRORS 0
28 # define VALGRIND_DO_LEAK_CHECK
29 # define VALGRIND_COUNT_LEAKS(a,b,c,d) (a=b=c=d=0)
30 #endif
31
32 #define MAX_TEST_TIME 40 // max seconds for a single test to run
33 #define MAX_TOTAL_TIME 120*60 // max seconds for the entire suite to run
34
35 #define TEST_START_FORMAT "! %s:%-5d %-40s "
36
37 static int memerrs()
38 {
39 return (int)VALGRIND_COUNT_ERRORS;
40 }
41
42 static int memleaks()
43 {
44 int leaked = 0, dubious = 0, reachable = 0, suppressed = 0;
45 VALGRIND_DO_LEAK_CHECK;
46 VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed);
47 printf("memleaks: sure:%d dubious:%d reachable:%d suppress:%d\n",
48 leaked, dubious, reachable, suppressed);
49 fflush(stdout);
50
51 // dubious+reachable are normally non-zero because of globals...
52 // return leaked+dubious+reachable;
53 return leaked;
54 }
55
56 // Return 1 if no children are running or zombies, 0 if there are any running
57 // or zombie children.
58 // Will wait for any already-terminated children first.
59 // Passes if no rogue children were running, fails otherwise.
60 // If your test gets a failure in here, either you're not killing all your
61 // children, or you're not calling waitpid(2) on all of them.
62 static bool no_running_children()
63 {
64 #ifndef _WIN32
65 pid_t wait_result;
66
67 // Acknowledge and complain about any zombie children
68 do
69 {
70 int status = 0;
71 wait_result = waitpid(-1, &status, WNOHANG);
72
73 if (wait_result > 0)
74 {
75 char buf[256];
76 snprintf(buf, sizeof(buf) - 1, "%d", wait_result);
77 buf[sizeof(buf)-1] = '\0';
78 WVFAILEQ("Unclaimed dead child process", buf);
79 }
80 } while (wait_result > 0);
81
82 // There should not be any running children, so waitpid should return -1
83 WVPASSEQ(errno, ECHILD);
84 WVPASSEQ(wait_result, -1);
85 return (wait_result == -1 && errno == ECHILD);
86 #endif
87 return true;
88 }
89
90
91 WvTest *WvTest::first, *WvTest::last;
92 int WvTest::fails, WvTest::runs;
93 time_t WvTest::start_time;
94 bool WvTest::run_twice = false;
95
96 void WvTest::alarm_handler(int)
97 {
98 printf("\n! WvTest Current test took longer than %d seconds! FAILED\n",
99 MAX_TEST_TIME);
100 fflush(stdout);
101 abort();
102 }
103
104
105 static const char *pathstrip(const char *filename)
106 {
107 const char *cptr;
108 cptr = strrchr(filename, '/');
109 if (cptr) filename = cptr + 1;
110 cptr = strrchr(filename, '\\');
111 if (cptr) filename = cptr + 1;
112 return filename;
113 }
114
115
116 WvTest::WvTest(const char *_descr, const char *_idstr, MainFunc *_main,
117 int _slowness) :
118 descr(_descr),
119 idstr(pathstrip(_idstr)),
120 main(_main),
121 slowness(_slowness),
122 next(NULL)
123 {
124 if (first)
125 last->next = this;
126 else
127 first = this;
128 last = this;
129 }
130
131
132 static bool prefix_match(const char *s, const char * const *prefixes)
133 {
134 for (const char * const *prefix = prefixes; prefix && *prefix; prefix++)
135 {
136 if (!strncasecmp(s, *prefix, strlen(*prefix)))
137 return true;
138 }
139 return false;
140 }
141
142
143 int WvTest::run_all(const char * const *prefixes)
144 {
145 int old_valgrind_errs = 0, new_valgrind_errs;
146 int old_valgrind_leaks = 0, new_valgrind_leaks;
147
148 #ifdef _WIN32
149 /* I should be doing something to do with SetTimer here,
150 * not sure exactly what just yet */
151 #else
152 char *disable(getenv("WVTEST_DISABLE_TIMEOUT"));
153 if (disable != NULL && disable[0] != '\0' && disable[0] != '0')
154 signal(SIGALRM, SIG_IGN);
155 else
156 signal(SIGALRM, alarm_handler);
157 alarm(MAX_TEST_TIME);
158 #endif
159 start_time = time(NULL);
160
161 // make sure we can always start out in the same directory, so tests have
162 // access to their files. If a test uses chdir(), we want to be able to
163 // reverse it.
164 char wd[1024];
165 if (!getcwd(wd, sizeof(wd)))
166 strcpy(wd, ".");
167
168 const char *slowstr1 = getenv("WVTEST_MIN_SLOWNESS");
169 const char *slowstr2 = getenv("WVTEST_MAX_SLOWNESS");
170 int min_slowness = 0, max_slowness = 65535;
171 if (slowstr1) min_slowness = atoi(slowstr1);
172 if (slowstr2) max_slowness = atoi(slowstr2);
173
174 #ifdef _WIN32
175 run_twice = false;
176 #else
177 char *parallel_str = getenv("WVTEST_PARALLEL");
178 if (parallel_str)
179 run_twice = atoi(parallel_str) > 0;
180 #endif
181
182 // there are lots of fflush() calls in here because stupid win32 doesn't
183 // flush very often by itself.
184 fails = runs = 0;
185 for (WvTest *cur = first; cur; cur = cur->next)
186 {
187 if (cur->slowness <= max_slowness
188 && cur->slowness >= min_slowness
189 && (!prefixes
190 || prefix_match(cur->idstr, prefixes)
191 || prefix_match(cur->descr, prefixes)))
192 {
193 #ifndef _WIN32
194 // set SIGPIPE back to default, helps catch tests which don't set
195 // this signal to SIG_IGN (which is almost always what you want)
196 // on startup
197 signal(SIGPIPE, SIG_DFL);
198
199 pid_t child = 0;
200 if (run_twice)
201 {
202 // I see everything twice!
203 printf("Running test in parallel.\n");
204 child = fork();
205 }
206 #endif
207
208 printf("\nTesting \"%s\" in %s:\n", cur->descr, cur->idstr);
209 fflush(stdout);
210
211 cur->main();
212 chdir(wd);
213
214 new_valgrind_errs = memerrs();
215 WVPASS(new_valgrind_errs == old_valgrind_errs);
216 old_valgrind_errs = new_valgrind_errs;
217
218 new_valgrind_leaks = memleaks();
219 WVPASS(new_valgrind_leaks == old_valgrind_leaks);
220 old_valgrind_leaks = new_valgrind_leaks;
221
222 fflush(stderr);
223 printf("\n");
224 fflush(stdout);
225
226 #ifndef _WIN32
227 if (run_twice)
228 {
229 if (!child)
230 {
231 // I see everything once!
232 printf("Child exiting.\n");
233 _exit(0);
234 }
235 else
236 {
237 printf("Waiting for child to exit.\n");
238 int result;