]> rtime.felk.cvut.cz Git - hubacji1/rrts.git/blob - wvtest/cpp/wvtest.cc
5a7527b0cb8c5b8fcbf8115ab30009536c5ad665
[hubacji1/rrts.git] / wvtest / cpp / wvtest.cc
1 /*
2  * WvTest:
3  *   Copyright (C)1997-2012 Net Integration Technologies and contributors.
4  *       Licensed under the GNU Library General Public License, version 2.
5  *       See the included file named LICENSE for license information.
6  *       You can get wvtest from: http://github.com/apenwarr/wvtest
7  */
8 #include "wvtest.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, WvTest::xpasses, WvTest::xfails, WvTest::skips;
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 = xpasses = xfails = skips = 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             if (chdir(wd)) {
214                 perror("Unable to change back to original directory");
215             }
216
217             new_valgrind_errs = memerrs();
218             WVPASS(new_valgrind_errs == old_valgrind_errs);
219             old_valgrind_errs = new_valgrind_errs;
220
221             new_valgrind_leaks = memleaks();
222             WVPASS(new_valgrind_leaks == old_valgrind_leaks);
223             old_valgrind_leaks = new_valgrind_leaks;
224
225             fflush(stderr);
226             printf("\n");
227             fflush(stdout);
228
229 #ifndef _WIN32
230             if (run_twice)
231             {
232                 if (!child)
233                 {
234                     // I see everything once!
235                     printf("Child exiting.\n");
236                     _exit(0);
237                 }
238                 else
239                 {
240                     printf("Waiting for child to exit.\n");
241                     int result;
242                     while ((result = waitpid(child, NULL, 0)) == -1 &&
243                             errno == EINTR)
244                         printf("Waitpid interrupted, retrying.\n");
245                 }
246             }
247 #endif
248
249             WVPASS(no_running_children());
250         }
251     }
252
253     WVPASS(runs > 0);
254
255     if (prefixes && *prefixes && **prefixes)
256         printf("WvTest: WARNING: only ran tests starting with "
257                "specifed prefix(es).\n");
258     else
259         printf("WvTest: ran all tests.\n");
260     printf("WvTest: %d test%s, %d failure%s.\n",
261            runs, runs==1 ? "" : "s",
262            fails, fails==1 ? "": "s");
263     printf("WvTest: %d test%s skipped, %d known breakage%s, %d fixed breakage%s.\n",
264            skips, skips==1 ? "" : "s",
265            xfails, xfails==1 ? "" : "s",
266            xpasses, xpasses==1 ? "" : "s");
267     fflush(stdout);
268
269     return fails != 0;
270 }
271
272
273 // If we aren't running in parallel, we want to output the name of the test
274 // before we run it, so we know what happened if it crashes.  If we are
275 // running in parallel, outputting this information in multiple printf()s
276 // can confuse parsers, so we want to output everything in one printf().
277 //
278 // This function gets called by both start() and check().  If we're not
279 // running in parallel, just print the data.  If we're running in parallel,
280 // and we're starting a test, save a copy of the file/line/description until
281 // the test is done and we can output it all at once.
282 //
283 // Yes, this is probably the worst API of all time.
284 void WvTest::print_result(bool start, const char *_file, int _line,
285         const char *_condstr, const char *result)
286 {
287     static char *file;
288     static char *condstr;
289     static int line;
290
291     if (start)
292     {
293         if (file)
294             free(file);
295         if (condstr)
296             free(condstr);
297         file = strdup(pathstrip(_file));
298         condstr = strdup(_condstr);
299         line = _line;
300
301         for (char *cptr = condstr; *cptr; cptr++)
302         {
303             if (!isprint((unsigned char)*cptr))
304                 *cptr = '!';
305         }
306     }
307
308     if (run_twice)
309     {
310         if (!start)
311             printf(TEST_START_FORMAT "%s\n", file, line, condstr, result);
312     }
313     else
314     {
315         if (start)
316             printf(TEST_START_FORMAT, file, line, condstr);
317         else
318             printf("%s\n", result);
319     }
320     fflush(stdout);
321
322     if (!start)
323     {
324         if (file)
325             free(file);
326         if (condstr)
327             free(condstr);
328         file = condstr = NULL;
329     }
330 }
331
332
333 void WvTest::start(const char *file, int line, const char *condstr)
334 {
335     // Either print the file, line, and condstr, or save them for later.
336     print_result(true, file, line, condstr, NULL);
337 }
338
339
340 void WvTest::check_prologue()
341 {
342 #ifndef _WIN32
343     alarm(MAX_TEST_TIME); // restart per-test timeout
344 #endif
345     if (!start_time) start_time = time(NULL);
346
347     if (time(NULL) - start_time > MAX_TOTAL_TIME)
348     {
349         printf("\n! WvTest   Total run time exceeded %d seconds!  FAILED\n",
350                MAX_TOTAL_TIME);
351         fflush(stdout);
352         abort();
353     }
354
355     runs++;
356
357 }
358
359
360 void WvTest::check(bool cond)
361 {
362     check_prologue();
363     print_result(false, NULL, 0, NULL, cond ? "ok" : "FAILED");
364
365     if (!cond)
366     {
367         fails++;
368
369         if (getenv("WVTEST_DIE_FAST"))
370             abort();
371     }
372 }
373
374
375 void WvTest::check_xfail(bool cond)
376 {
377     check_prologue();
378     print_result(false, NULL, 0, NULL, cond ? "xpass ok" : "xfail ok");
379
380     if (cond)
381         xpasses++;
382     else
383         xfails++;
384 }
385
386
387 void WvTest::skip(const char *file, int line, const char *condstr)
388 {
389     start(file, line, condstr);
390     print_result(false, NULL, 0, NULL, "skip ok");
391     skips++;
392 }
393
394
395 bool WvTest::start_check_eq(const char *file, int line,
396                             const char *a, const char *b, bool expect_pass)
397 {
398     if (!a) a = "";
399     if (!b) b = "";
400
401     size_t len = strlen(a) + strlen(b) + 8 + 1;
402     char *str = new char[len];
403     sprintf(str, "[%s] %s [%s]", a, expect_pass ? "==" : "!=", b);
404
405     start(file, line, str);
406     delete[] str;
407
408     bool cond = !strcmp(a, b);
409     if (!expect_pass)
410         cond = !cond;
411
412     check(cond);
413     return cond;
414 }
415
416
417 bool WvTest::start_check_eq(const char *file, int line,
418                             const std::string &a, const std::string &b,
419                             bool expect_pass)
420 {
421     return start_check_eq(file, line, a.c_str(), b.c_str(), expect_pass);
422 }
423
424
425 bool WvTest::start_check_eq(const char *file, int line,
426                             int a, int b, bool expect_pass)
427 {
428     size_t len = 128 + 128 + 8 + 1;
429     char *str = new char[len];
430     sprintf(str, "%d %s %d", a, expect_pass ? "==" : "!=", b);
431
432     start(file, line, str);
433     delete[] str;
434
435     bool cond = (a == b);
436     if (!expect_pass)
437         cond = !cond;
438
439     check(cond);
440     return cond;
441 }
442
443
444 bool WvTest::start_check_eq(const char *file, int line,
445                             double a, double b, double c, bool expect_pass)
446 {
447     size_t len = 128 + 128 + 128 + 8 + 1;
448     char *str = new char[len];
449     sprintf(str, "%f %s %f eps %f", a, expect_pass ? "==" : "!=", b, c);
450
451     start(file, line, str);
452     delete[] str;
453
454     bool cond = ( WVABS(a - b) <= WVABS(c) );
455     if (!expect_pass)
456         cond = !cond;
457
458     check(cond);
459     return cond;
460 }
461
462
463 bool WvTest::start_check_lt(const char *file, int line,
464                             const char *a, const char *b)
465 {
466     if (!a) a = "";
467     if (!b) b = "";
468
469     size_t len = strlen(a) + strlen(b) + 8 + 1;
470     char *str = new char[len];
471     sprintf(str, "[%s] < [%s]", a, b);
472
473     start(file, line, str);
474     delete[] str;
475
476     bool cond = strcmp(a, b) < 0;
477     check(cond);
478     return cond;
479 }
480
481
482 bool WvTest::start_check_lt(const char *file, int line, int a, int b)
483 {
484     size_t len = 128 + 128 + 8 + 1;
485     char *str = new char[len];
486     sprintf(str, "%d < %d", a, b);
487
488     start(file, line, str);
489     delete[] str;
490
491     bool cond = a < b;
492     check(cond);
493     return cond;
494 }