]> rtime.felk.cvut.cz Git - frescor/frsh-forb.git/blob - src/wvtest/c/wvtest.c
wvtest: Add WVERRNO macro
[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 #define _GNU_SOURCE
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 <stdlib.h>
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 fails, runs;
39 static time_t start_time;
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 #endif
197
198             printf("\nTesting \"%s\" in %s:\n", cur->descr, cur->idstr);
199             fflush(stdout);
200
201             cur->main();
202             chdir(wd);
203
204             new_valgrind_errs = memerrs();
205             WVPASS(new_valgrind_errs == old_valgrind_errs);
206             old_valgrind_errs = new_valgrind_errs;
207
208             new_valgrind_leaks = memleaks();
209             WVPASS(new_valgrind_leaks == old_valgrind_leaks);
210             old_valgrind_leaks = new_valgrind_leaks;
211
212             fflush(stderr);
213             printf("\n");
214             fflush(stdout);
215
216             WVPASS(no_running_children());
217         }
218     }
219
220     WVPASS(runs > 0);
221
222     if (prefixes && *prefixes && **prefixes)
223         printf("WvTest: WARNING: only ran tests starting with "
224                "specifed prefix(es).\n");
225     else
226         printf("WvTest: ran all tests.\n");
227     printf("WvTest: %d test%s, %d failure%s.\n",
228            runs, runs==1 ? "" : "s",
229            fails, fails==1 ? "": "s");
230     fflush(stdout);
231
232     return fails != 0;
233 }
234
235
236 // If we aren't running in parallel, we want to output the name of the test
237 // before we run it, so we know what happened if it crashes.  If we are
238 // running in parallel, outputting this information in multiple printf()s
239 // can confuse parsers, so we want to output everything in one printf().
240 //
241 // This function gets called by both start() and check().  If we're not
242 // running in parallel, just print the data.  If we're running in parallel,
243 // and we're starting a test, save a copy of the file/line/description until
244 // the test is done and we can output it all at once.
245 //
246 // Yes, this is probably the worst API of all time.
247 static void print_result_str(bool start, const char *_file, int _line,
248                              const char *_condstr, const char *result)
249 {
250     static char *file;
251     static char *condstr;
252     static int line;
253     char *cptr;
254
255     if (start)
256     {
257         if (file)
258             free(file);
259         if (condstr)
260             free(condstr);
261         file = strdup(pathstrip(_file));
262         condstr = strdup(_condstr);
263         line = _line;
264
265         for (cptr = condstr; *cptr; cptr++)
266         {
267             if (!isprint((unsigned char)*cptr))
268                 *cptr = '!';
269         }
270     }
271
272     if (!start)
273             printf(TEST_START_FORMAT "%s\n", file, line, condstr, result);
274     fflush(stdout);
275
276     if (!start)
277     {
278         if (file)
279             free(file);
280         if (condstr)
281             free(condstr);
282         file = condstr = NULL;
283     }
284 }
285
286 static inline void
287 print_result(bool start, const char *file, int line,
288              const char *condstr, bool result)
289 {
290         print_result_str(start, file, line, condstr, result ? "ok" : "FAILED");
291 }
292
293 void wvtest_start(const char *file, int line, const char *condstr)
294 {
295     // Either print the file, line, and condstr, or save them for later.
296     print_result(true, file, line, condstr, false);
297 }
298
299
300 void wvtest_check(bool cond, const char *reason)
301 {
302 #ifndef _WIN32
303     alarm(MAX_TEST_TIME); // restart per-test timeout
304 #endif
305     if (!start_time) start_time = time(NULL);
306
307     if (time(NULL) - start_time > MAX_TOTAL_TIME)
308     {
309         printf("\n! WvTest   Total run time exceeded %d seconds!  FAILED\n",
310                MAX_TOTAL_TIME);
311         fflush(stdout);
312         abort();
313     }
314
315     runs++;
316
317     print_result_str(false, NULL, 0, NULL, cond ? "ok" : (reason ? reason : "FAILED"));
318
319     if (!cond)
320     {
321         fails++;
322
323         if (getenv("WVTEST_DIE_FAST"))
324             abort();
325     }
326 }
327
328
329 bool wvtest_start_check_eq(const char *file, int line,
330                            int a, int b, bool expect_pass,
331                            const char *a_str, const char *b_str)
332 {
333     char *str;
334     char sa[20], sb[20];
335     char *a_op = " == ", *b_op = " == ";
336     sprintf(sa, "%d", a);
337     sprintf(sb, "%d", b);
338     if (strcmp(sa, a_str) == 0)
339             a_str = a_op = "";
340     if (strcmp(sb, b_str) == 0)
341             b_str = b_op = "";
342     asprintf(&str, "%s%s%d %s %s%s%d", a_str, a_op, a, expect_pass ? "==" : "!=",
343                                        b_str, b_op, b);
344
345     wvtest_start(file, line, str);
346     free(str);
347
348     bool cond = (a == b);
349     if (!expect_pass)
350         cond = !cond;
351
352     wvtest_check(cond, NULL);
353     return cond;
354 }
355
356 bool wvtest_start_check_eq_ptr(const char *file, int line,
357                                void *a, void *b, bool expect_pass)
358 {
359     size_t len = 11 + 11 + 8 + 1;
360     char *str = malloc(len);
361     sprintf(str, "%p %s %p", a, expect_pass ? "==" : "!=", b);
362
363     wvtest_start(file, line, str);
364     free(str);
365
366     bool cond = (a == b);
367     if (!expect_pass)
368         cond = !cond;
369
370     wvtest_check(cond, NULL);
371     return cond;
372 }
373
374
375 bool wvtest_start_check_lt(const char *file, int line,
376                            int a, int b)
377 {
378     size_t len = 11 + 11 + 8 + 1;
379     char *str = malloc(len);
380     sprintf(str, "%d < %d", a, b);
381
382     wvtest_start(file, line, str);
383     free(str);
384
385     bool cond = (a < b);
386     wvtest_check(cond, NULL);
387     return cond;
388 }
389 bool wvtest_start_check_eq_str(const char *file, int line,
390                                const char *a, const char *b, bool expect_pass)
391 {
392     if (!a) a = "";
393     if (!b) b = "";
394
395     size_t len = strlen(a) + strlen(b) + 8 + 1;
396     char *str = malloc(len);
397     sprintf(str, "[%s] %s [%s]", a, expect_pass ? "==" : "!=", b);
398
399     wvtest_start(file, line, str);
400
401     bool cond = !strcmp(a, b);
402     if (!expect_pass)
403         cond = !cond;
404
405     wvtest_check(cond, NULL);
406     return cond;
407 }
408
409
410 bool wvtest_start_check_lt_str(const char *file, int line,
411                                const char *a, const char *b)
412 {
413     if (!a) a = "";
414     if (!b) b = "";
415
416     size_t len = strlen(a) + strlen(b) + 8 + 1;
417     char *str = malloc(len);
418     sprintf(str, "[%s] < [%s]", a, b);
419
420     wvtest_start(file, line, str);
421     free(str);
422
423     bool cond = strcmp(a, b) < 0;
424     wvtest_check(cond, NULL);
425     return cond;
426 }
427
428 int wvtest_start_check_errno(const char *file, int line,
429                              const char *condstr, int retval)
430 {
431         wvtest_start(file, line, condstr);
432         if (retval == -1)
433                 perror(condstr);
434
435         wvtest_check(retval != -1, NULL);
436
437         return retval;
438 }