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