]> rtime.felk.cvut.cz Git - wvtest.git/blob - cpp/wvtest.cc
Import basic C++ wvtest from wvstreams project.
[wvtest.git] / cpp / wvtest.cc
1 /*
2  * Worldvisions Weaver Software:
3  *   Copyright (C) 1997-2003 Net Integration Technologies, Inc.
4  *
5  * Part of an automated testing framework.  See wvtest.h.
6  */
7 #include "wvtest.h"
8 #include "wvautoconf.h"
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <ctype.h>
13 #ifdef _WIN32
14 #include <direct.h>
15 #else
16 #include <unistd.h>
17 #include <sys/wait.h>
18 #endif
19 #include <errno.h>
20 #include <signal.h>
21
22 #include <cstdlib>
23
24 #ifdef HAVE_VALGRIND_MEMCHECK_H
25 # include <valgrind/memcheck.h>
26 # include <valgrind/valgrind.h>
27 #else
28 # define VALGRIND_COUNT_ERRORS 0
29 # define VALGRIND_DO_LEAK_CHECK
30 # define VALGRIND_COUNT_LEAKS(a,b,c,d) (a=b=c=d=0)
31 #endif
32
33 #define MAX_TEST_TIME 40     // max seconds for a single test to run
34 #define MAX_TOTAL_TIME 120*60 // max seconds for the entire suite to run
35
36 #define TEST_START_FORMAT "! %s:%-5d %-40s "
37
38 static int memerrs()
39 {
40     return (int)VALGRIND_COUNT_ERRORS;
41 }
42
43 static int memleaks()
44 {
45     int leaked = 0, dubious = 0, reachable = 0, suppressed = 0;
46     VALGRIND_DO_LEAK_CHECK;
47     VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed);
48     printf("memleaks: sure:%d dubious:%d reachable:%d suppress:%d\n",
49            leaked, dubious, reachable, suppressed);
50     fflush(stdout);
51     
52     // dubious+reachable are normally non-zero because of globals...
53     // return leaked+dubious+reachable;
54     return leaked;
55 }
56
57 // Return 1 if no children are running or zombies, 0 if there are any running
58 // or zombie children.
59 // Will wait for any already-terminated children first.
60 // Passes if no rogue children were running, fails otherwise.
61 // If your test gets a failure in here, either you're not killing all your
62 // children, or you're not calling waitpid(2) on all of them.
63 static bool no_running_children()
64 {
65 #ifndef _WIN32
66     pid_t wait_result;
67
68     // Acknowledge and complain about any zombie children
69     do 
70     {
71         int status = 0;
72         wait_result = waitpid(-1, &status, WNOHANG);
73
74         if (wait_result > 0)
75         {
76             char buf[256];
77             snprintf(buf, sizeof(buf) - 1, "%d", wait_result);
78             buf[sizeof(buf)-1] = '\0';
79             WVFAILEQ("Unclaimed dead child process", buf);
80         }
81     } while (wait_result > 0);
82         
83     // There should not be any running children, so waitpid should return -1
84     WVPASSEQ(errno, ECHILD);
85     WVPASSEQ(wait_result, -1);
86     return (wait_result == -1 && errno == ECHILD);
87 #endif
88     return true;
89 }
90
91
92 WvTest *WvTest::first, *WvTest::last;
93 int WvTest::fails, WvTest::runs;
94 time_t WvTest::start_time;
95 bool WvTest::run_twice = false;
96
97 void WvTest::alarm_handler(int)
98 {
99     printf("\n! WvTest  Current test took longer than %d seconds!  FAILED\n",
100            MAX_TEST_TIME);
101     fflush(stdout);
102     abort();
103 }
104
105
106 static const char *pathstrip(const char *filename)
107 {
108     const char *cptr;
109     cptr = strrchr(filename, '/');
110     if (cptr) filename = cptr + 1;
111     cptr = strrchr(filename, '\\');
112     if (cptr) filename = cptr + 1;
113     return filename;
114 }
115
116
117 WvTest::WvTest(const char *_descr, const char *_idstr, MainFunc *_main,
118                int _slowness) :
119     descr(_descr), 
120     idstr(pathstrip(_idstr)), 
121     main(_main), 
122     slowness(_slowness),
123     next(NULL)
124 {
125     if (first)
126         last->next = this;
127     else
128         first = this;
129     last = this;
130 }
131
132
133 static bool prefix_match(const char *s, const char * const *prefixes)
134 {
135     for (const char * const *prefix = prefixes; prefix && *prefix; prefix++)
136     {
137         if (!strncasecmp(s, *prefix, strlen(*prefix)))
138             return true;
139     }
140     return false;
141 }
142
143
144 int WvTest::run_all(const char * const *prefixes)
145 {
146     int old_valgrind_errs = 0, new_valgrind_errs;
147     int old_valgrind_leaks = 0, new_valgrind_leaks;
148     
149 #ifdef _WIN32
150     /* I should be doing something to do with SetTimer here, 
151      * not sure exactly what just yet */
152 #else
153     char *disable(getenv("WVTEST_DISABLE_TIMEOUT"));
154     if (disable != NULL && disable[0] != '\0' && disable[0] != '0')
155         signal(SIGALRM, SIG_IGN);
156     else
157         signal(SIGALRM, alarm_handler);
158     alarm(MAX_TEST_TIME);
159 #endif
160     start_time = time(NULL);
161     
162     // make sure we can always start out in the same directory, so tests have
163     // access to their files.  If a test uses chdir(), we want to be able to
164     // reverse it.
165     char wd[1024];
166     if (!getcwd(wd, sizeof(wd)))
167         strcpy(wd, ".");
168     
169     const char *slowstr1 = getenv("WVTEST_MIN_SLOWNESS");
170     const char *slowstr2 = getenv("WVTEST_MAX_SLOWNESS");
171     int min_slowness = 0, max_slowness = 65535;
172     if (slowstr1) min_slowness = atoi(slowstr1);
173     if (slowstr2) max_slowness = atoi(slowstr2);
174
175 #ifdef _WIN32
176     run_twice = false;
177 #else
178     char *parallel_str = getenv("WVTEST_PARALLEL");
179     if (parallel_str) 
180         run_twice = atoi(parallel_str) > 0;
181 #endif
182
183     // there are lots of fflush() calls in here because stupid win32 doesn't
184     // flush very often by itself.
185     fails = runs = 0;
186     for (WvTest *cur = first; cur; cur = cur->next)
187     {
188         if (cur->slowness <= max_slowness
189             && cur->slowness >= min_slowness
190             && (!prefixes
191                 || prefix_match(cur->idstr, prefixes)
192                 || prefix_match(cur->descr, prefixes)))
193         {
194 #ifndef _WIN32
195             // set SIGPIPE back to default, helps catch tests which don't set
196             // this signal to SIG_IGN (which is almost always what you want)
197             // on startup
198             signal(SIGPIPE, SIG_DFL);
199
200             pid_t child = 0;
201             if (run_twice)
202             {
203                 // I see everything twice!
204                 printf("Running test in parallel.\n");
205                 child = fork();
206             }
207 #endif
208
209             printf("\nTesting \"%s\" in %s:\n", cur->descr, cur->idstr);
210             fflush(stdout);
211             
212             cur->main();
213             chdir(wd);
214             
215             new_valgrind_errs = memerrs();
216             WVPASS(new_valgrind_errs == old_valgrind_errs);
217             old_valgrind_errs = new_valgrind_errs;
218             
219             new_valgrind_leaks = memleaks();
220             WVPASS(new_valgrind_leaks == old_valgrind_leaks);
221             old_valgrind_leaks = new_valgrind_leaks;
222             
223             fflush(stderr);
224             printf("\n");
225             fflush(stdout);
226
227 #ifndef _WIN32
228             if (run_twice)
229             {
230                 if (!child)
231                 {
232                     // I see everything once!
233                     printf("Child exiting.\n");
234                     _exit(0);
235                 }
236                 else
237                 {
238                     printf("Waiting for child to exit.\n");
239                     int result;
240                     while ((result = waitpid(child, NULL, 0)) == -1 && 
241                             errno == EINTR)
242                         printf("Waitpid interrupted, retrying.\n");
243                 }
244             }
245 #endif
246
247             WVPASS(no_running_children());
248         }
249     }
250     
251     WVPASS(runs > 0);
252     
253     if (prefixes && *prefixes && **prefixes)
254         printf("WvTest: WARNING: only ran tests starting with "
255                "specifed prefix(es).\n");
256     else
257         printf("WvTest: ran all tests.\n");
258     printf("WvTest: %d test%s, %d failure%s.\n",
259            runs, runs==1 ? "" : "s",
260            fails, fails==1 ? "": "s");
261     fflush(stdout);
262     
263     return fails != 0;
264 }
265
266
267 // If we aren't running in parallel, we want to output the name of the test
268 // before we run it, so we know what happened if it crashes.  If we are
269 // running in parallel, outputting this information in multiple printf()s
270 // can confuse parsers, so we want to output everything in one printf().
271 //
272 // This function gets called by both start() and check().  If we're not
273 // running in parallel, just print the data.  If we're running in parallel,
274 // and we're starting a test, save a copy of the file/line/description until
275 // the test is done and we can output it all at once.
276 //
277 // Yes, this is probably the worst API of all time.
278 void WvTest::print_result(bool start, const char *_file, int _line, 
279         const char *_condstr, bool result)
280 {
281     static char *file;
282     static char *condstr;
283     static int line;
284     
285     if (start)
286     {
287         if (file) 
288             free(file);
289         if (condstr) 
290             free(condstr);
291         file = strdup(pathstrip(_file));
292         condstr = strdup(_condstr);
293         line = _line;
294
295         for (char *cptr = condstr; *cptr; cptr++)
296         {
297             if (!isprint((unsigned char)*cptr))
298                 *cptr = '!';
299         }
300     }
301             
302     const char *result_str = result ? "ok\n" : "FAILED\n";
303     if (run_twice)
304     {
305         if (!start)
306             printf(TEST_START_FORMAT "%s", file, line, condstr, result_str);
307     }
308     else
309     {
310         if (start)
311             printf(TEST_START_FORMAT, file, line, condstr);
312         else
313             printf("%s", result_str);
314     }
315     fflush(stdout);
316
317     if (!start)
318     {
319         if (file)
320             free(file);
321         if (condstr)
322             free(condstr);
323         file = condstr = NULL;
324     }
325 }
326
327
328 void WvTest::start(const char *file, int line, const char *condstr)
329 {
330     // Either print the file, line, and condstr, or save them for later.
331     print_result(true, file, line, condstr, 0);
332 }
333
334
335 void WvTest::check(bool cond)
336 {
337 #ifndef _WIN32
338     alarm(MAX_TEST_TIME); // restart per-test timeout
339 #endif
340     if (!start_time) start_time = time(NULL);
341     
342     if (time(NULL) - start_time > MAX_TOTAL_TIME)
343     {
344         printf("\n! WvTest   Total run time exceeded %d seconds!  FAILED\n",
345                MAX_TOTAL_TIME);
346         fflush(stdout);
347         abort();
348     }
349     
350     runs++;
351
352     print_result(false, NULL, 0, NULL, cond);
353
354     if (!cond)
355     {
356         fails++;
357         
358         if (getenv("WVTEST_DIE_FAST"))
359             abort();
360     }
361 }
362
363
364 bool WvTest::start_check_eq(const char *file, int line,
365                             const char *a, const char *b, bool expect_pass)
366 {
367     if (!a) a = "";
368     if (!b) b = "";
369     
370     size_t len = strlen(a) + strlen(b) + 8 + 1;
371     char *str = new char[len];
372     sprintf(str, "[%s] %s [%s]", a, expect_pass ? "==" : "!=", b);
373     
374     start(file, line, str);
375     delete[] str;
376     
377     bool cond = !strcmp(a, b);
378     if (!expect_pass)
379         cond = !cond;
380
381     check(cond);
382     return cond;
383 }
384
385
386 bool WvTest::start_check_eq(const char *file, int line, 
387                             int a, int b, bool expect_pass)
388 {
389     size_t len = 128 + 128 + 8 + 1;
390     char *str = new char[len];
391     sprintf(str, "%d %s %d", a, expect_pass ? "==" : "!=", b);
392     
393     start(file, line, str);
394     delete[] str;
395     
396     bool cond = (a == b);
397     if (!expect_pass)
398         cond = !cond;
399
400     check(cond);
401     return cond;
402 }
403
404
405 bool WvTest::start_check_lt(const char *file, int line,
406                             const char *a, const char *b)
407 {
408     if (!a) a = "";
409     if (!b) b = "";
410     
411     size_t len = strlen(a) + strlen(b) + 8 + 1;
412     char *str = new char[len];
413     sprintf(str, "[%s] < [%s]", a, b);
414     
415     start(file, line, str);
416     delete[] str;
417
418     bool cond = strcmp(a, b) < 0;
419     check(cond);
420     return cond;
421 }
422
423
424 bool WvTest::start_check_lt(const char *file, int line, int a, int b)
425 {
426     size_t len = 128 + 128 + 8 + 1;
427     char *str = new char[len];
428     sprintf(str, "%d < %d", a, b);
429     
430     start(file, line, str);
431     delete[] str;
432     
433     bool cond = a < b;
434     check(cond);
435     return cond;
436 }