Imported Upstream version 3.1.0
[debian/amanda] / common-src / testutils.c
1 /*
2  * Copyright (c) 2008 Zmanda, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16  *
17  * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18  * Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19  */
20
21 #include "amanda.h"
22 #include "testutils.h"
23
24 int tu_debugging_enabled = FALSE;
25
26 static void
27 alarm_hdlr(int sig G_GNUC_UNUSED)
28 {
29     g_fprintf(stderr, "-- TEST TIMED OUT --\n");
30     exit(1);
31 }
32
33 /* Call testfn in a forked process, such that any failures will trigger a
34  * test failure, but allow the other tests to proceed.
35  */
36 static int
37 callinfork(TestUtilsTest *test, int ignore_timeouts, gboolean skip_fork)
38 {
39     pid_t pid;
40     amwait_t status;
41     gboolean result;
42
43     if (skip_fork) {
44         /* kill the test after a bit */
45         signal(SIGALRM, alarm_hdlr);
46         if (!ignore_timeouts) alarm(test->timeout);
47
48         result = test->fn();
49     } else {
50         switch (pid = fork()) {
51             case 0:     /* child */
52                 /* kill the test after a bit */
53                 signal(SIGALRM, alarm_hdlr);
54                 if (!ignore_timeouts) alarm(test->timeout);
55
56                 result = test->fn();
57                 exit(result? 0:1);
58
59             case -1:
60                 perror("fork");
61                 exit(1);
62
63             default: /* parent */
64                 waitpid(pid, &status, 0);
65                 result = status == 0;
66                 break;
67         }
68     }
69
70     if (result) {
71         g_fprintf(stderr, " PASS %s\n", test->name);
72     } else {
73         g_fprintf(stderr, " FAIL %s\n", test->name);
74     }
75
76     return result;
77 }
78
79 static void
80 usage(
81     TestUtilsTest *tests)
82 {
83     printf("USAGE: <test-script> [-d] [-h] [testname [testname [..]]]\n"
84         "\n"
85         "\t-h: this message\n"
86         "\t-d: print debugging messages\n"
87         "\t-t: ignore timeouts\n"
88         "\t-n: do not fork\n"
89         "\t-l: loop the same test repeatedly (use with -n for leak checks)\n"
90         "\n"
91         "If no test names are specified, all tests are run.  Available tests:\n"
92         "\n");
93     while (tests->fn) {
94         printf("\t%s\n", tests->name);
95         tests++;
96     }
97 }
98
99 static void
100 ignore_debug_messages(
101             const gchar *log_domain G_GNUC_UNUSED,
102             GLogLevelFlags log_level G_GNUC_UNUSED,
103             const gchar *message G_GNUC_UNUSED,
104             gpointer user_data G_GNUC_UNUSED)
105 {
106 }
107
108 int
109 testutils_run_tests(
110     int argc,
111     char **argv,
112     TestUtilsTest *tests)
113 {
114     TestUtilsTest *t;
115     int run_all = 1;
116     int success;
117     int ignore_timeouts = 0;
118     gboolean skip_fork = FALSE;
119     gboolean only_one = FALSE;
120     gboolean loop_forever = FALSE;
121
122     /* first_parse the command line */
123     while (argc > 1) {
124         if (strcmp(argv[1], "-d") == 0) {
125             tu_debugging_enabled = TRUE;
126         } else if (strcmp(argv[1], "-t") == 0) {
127             ignore_timeouts = TRUE;
128         } else if (strcmp(argv[1], "-n") == 0) {
129             skip_fork = TRUE;
130             only_one = TRUE;
131         } else if (strcmp(argv[1], "-l") == 0) {
132             loop_forever = TRUE;
133             only_one = TRUE;
134         } else if (strcmp(argv[1], "-h") == 0) {
135             usage(tests);
136             return 1;
137         } else {
138             int found = 0;
139
140             for (t = tests; t->fn; t++) {
141                 if (strcmp(argv[1], t->name) == 0) {
142                     found = 1;
143                     t->selected = 1;
144                     break;
145                 }
146             }
147
148             if (!found) {
149                 g_fprintf(stderr, "Test '%s' not found\n", argv[1]);
150                 return 1;
151             }
152
153             run_all = 0;
154         }
155
156         argc--; argv++;
157     }
158
159     if (run_all) {
160         for (t = tests; t->fn; t++)
161             t->selected = 1;
162     }
163
164     /* check only_one */
165     if (only_one) {
166         int num_tests = 0;
167         for (t = tests; t->fn; t++) {
168             if (t->selected)
169                 num_tests++;
170         }
171
172         if (num_tests > 1) {
173             g_fprintf(stderr, "Only run one test with '-n'\n");
174             return 1;
175         }
176     }
177
178     /* Make sure g_critical and g_error will exit */
179     g_log_set_always_fatal(G_LOG_LEVEL_ERROR |  G_LOG_LEVEL_CRITICAL);
180
181     /* and silently drop debug messages unless we're debugging */
182     if (!tu_debugging_enabled) {
183         g_log_set_handler(NULL, G_LOG_LEVEL_DEBUG, ignore_debug_messages, NULL);
184     }
185
186     /* Now actually run the tests */
187     success = 1;
188     for (t = tests; t->fn; t++) {
189         if (t->selected) {
190             do {
191                 success = callinfork(t, ignore_timeouts, skip_fork) && success;
192             } while (loop_forever);
193         }
194     }
195
196     return success? 0:1;
197 }