]> rtime.felk.cvut.cz Git - wvtest.git/blob - cpp/wvtest.cc
Clean up license information and add LICENSE file.
[wvtest.git] / cpp / 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;
239                     while ((result = waitpid(child, NULL, 0)) == -1 && 
240                             errno == EINTR)
241                         printf("Waitpid interrupted, retrying.\n");
242                 }
243             }
244 #endif
245
246             WVPASS(no_running_children());
247         }
248     }
249     
250     WVPASS(runs > 0);
251     
252     if (prefixes && *prefixes && **prefixes)
253         printf("WvTest: WARNING: only ran tests starting with "
254                "specifed prefix(es).\n");
255     else
256         printf("WvTest: ran all tests.\n");
257     printf("WvTest: %d test%s, %d failure%s.\n",
258            runs, runs==1 ? "" : "s",
259            fails, fails==1 ? "": "s");
260     fflush(stdout);
261     
262     return fails != 0;
263 }
264
265
266 // If we aren't running in parallel, we want to output the name of the test
267 // before we run it, so we know what happened if it crashes.  If we are
268 // running in parallel, outputting this information in multiple printf()s
269 // can confuse parsers, so we want to output everything in one printf().
270 //
271 // This function gets called by both start() and check().  If we're not
272 // running in parallel, just print the data.  If we're running in parallel,
273 // and we're starting a test, save a copy of the file/line/description until
274 // the test is done and we can output it all at once.
275 //
276 // Yes, this is probably the worst API of all time.
277 void WvTest::print_result(bool start, const char *_file, int _line, 
278         const char *_condstr, bool result)
279 {
280     static char *file;
281     static char *condstr;
282     static int line;
283     
284     if (start)
285     {
286         if (file) 
287             free(file);
288         if (condstr) 
289             free(condstr);
290         file = strdup(pathstrip(_file));
291         condstr = strdup(_condstr);
292         line = _line;
293
294         for (char *cptr = condstr; *cptr; cptr++)
295         {
296             if (!isprint((unsigned char)*cptr))
297                 *cptr = '!';
298         }
299     }
300             
301     const char *result_str = result ? "ok\n" : "FAILED\n";
302     if (run_twice)
303     {
304         if (!start)
305             printf(TEST_START_FORMAT "%s", file, line, condstr, result_str);
306     }
307     else
308     {
309         if (start)
310             printf(TEST_START_FORMAT, file, line, condstr);
311         else
312             printf("%s", result_str);
313     }
314     fflush(stdout);
315
316     if (!start)
317     {
318         if (file)
319             free(file);
320         if (condstr)
321             free(condstr);
322         file = condstr = NULL;
323     }
324 }
325
326
327 void WvTest::start(const char *file, int line, const char *condstr)
328 {
329     // Either print the file, line, and condstr, or save them for later.
330     print_result(true, file, line, condstr, 0);
331 }
332
333
334 void WvTest::check(bool cond)
335 {
336 #ifndef _WIN32
337     alarm(MAX_TEST_TIME); // restart per-test timeout
338 #endif
339     if (!start_time) start_time = time(NULL);
340     
341     if (time(NULL) - start_time > MAX_TOTAL_TIME)
342     {
343         printf("\n! WvTest   Total run time exceeded %d seconds!  FAILED\n",
344                MAX_TOTAL_TIME);
345         fflush(stdout);
346         abort();
347     }
348     
349     runs++;
350
351     print_result(false, NULL, 0, NULL, cond);
352
353     if (!cond)
354     {
355         fails++;
356         
357         if (getenv("WVTEST_DIE_FAST"))
358             abort();
359     }
360 }
361
362
363 bool WvTest::start_check_eq(const char *file, int line,
364                             const char *a, const char *b, bool expect_pass)
365 {
366     if (!a) a = "";
367     if (!b) b = "";
368     
369     size_t len = strlen(a) + strlen(b) + 8 + 1;
370     char *str = new char[len];
371     sprintf(str, "[%s] %s [%s]", a, expect_pass ? "==" : "!=", b);
372     
373     start(file, line, str);
374     delete[] str;
375     
376     bool cond = !strcmp(a, b);
377     if (!expect_pass)
378         cond = !cond;
379
380     check(cond);
381     return cond;
382 }
383
384
385 bool WvTest::start_check_eq(const char *file, int line, 
386                             int a, int b, bool expect_pass)
387 {
388     size_t len = 128 + 128 + 8 + 1;
389     char *str = new char[len];
390     sprintf(str, "%d %s %d", a, expect_pass ? "==" : "!=", b);
391     
392     start(file, line, str);
393     delete[] str;
394     
395     bool cond = (a == b);
396     if (!expect_pass)
397         cond = !cond;
398
399     check(cond);
400     return cond;
401 }
402
403
404 bool WvTest::start_check_lt(const char *file, int line,
405                             const char *a, const char *b)
406 {
407     if (!a) a = "";
408     if (!b) b = "";
409     
410     size_t len = strlen(a) + strlen(b) + 8 + 1;
411     char *str = new char[len];
412     sprintf(str, "[%s] < [%s]", a, b);
413     
414     start(file, line, str);
415     delete[] str;
416
417     bool cond = strcmp(a, b) < 0;
418     check(cond);
419     return cond;
420 }
421
422
423 bool WvTest::start_check_lt(const char *file, int line, int a, int b)
424 {
425     size_t len = 128 + 128 + 8 + 1;
426     char *str = new char[len];
427     sprintf(str, "%d < %d", a, b);
428     
429     start(file, line, str);
430     delete[] str;
431     
432     bool cond = a < b;
433     check(cond);
434     return cond;
435 }