]> git.ipfire.org Git - thirdparty/kmod.git/blob - testsuite/testsuite.c
testsuite: add GPL license
[thirdparty/kmod.git] / testsuite / testsuite.c
1 /*
2 * Copyright (C) 2012 ProFUSION embedded systems
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/epoll.h>
28 #include <sys/prctl.h>
29 #include <sys/wait.h>
30
31 #include "testsuite.h"
32
33 static const char *ANSI_HIGHLIGHT_GREEN_ON = "\x1B[1;32m";
34 static const char *ANSI_HIGHLIGHT_RED_ON = "\x1B[1;31m";
35 static const char *ANSI_HIGHLIGHT_OFF = "\x1B[0m";
36
37 static const char *progname;
38 static int oneshot = 0;
39 static const char options_short[] = "lhn";
40 static const struct option options[] = {
41 { "list", no_argument, 0, 'l' },
42 { "help", no_argument, 0, 'h' },
43 { NULL, 0, 0, 0 }
44 };
45
46 #define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/"
47
48 struct _env_config {
49 const char *key;
50 const char *ldpreload;
51 } env_config[_TC_LAST] = {
52 [TC_UNAME_R] = { S_TC_UNAME_R, OVERRIDE_LIBDIR "uname.so" },
53 [TC_ROOTFS] = { S_TC_ROOTFS, OVERRIDE_LIBDIR "path.so" },
54 [TC_INIT_MODULE_RETCODES] = { S_TC_INIT_MODULE_RETCODES, OVERRIDE_LIBDIR "init_module.so" },
55 [TC_DELETE_MODULE_RETCODES] = { S_TC_DELETE_MODULE_RETCODES, OVERRIDE_LIBDIR "delete_module.so" },
56 };
57
58 static void help(void)
59 {
60 const struct option *itr;
61 const char *itr_short;
62
63 printf("Usage:\n"
64 "\t%s [options] <test>\n"
65 "Options:\n", basename(progname));
66
67 for (itr = options, itr_short = options_short;
68 itr->name != NULL; itr++, itr_short++)
69 printf("\t-%c, --%s\n", *itr_short, itr->name);
70 }
71
72 static void test_list(const struct test *tests[])
73 {
74 size_t i;
75
76 printf("Available tests:\n");
77 for (i = 0; tests[i] != NULL; i++)
78 printf("\t%s, %s\n", tests[i]->name, tests[i]->description);
79 }
80
81 int test_init(int argc, char *const argv[], const struct test *tests[])
82 {
83 progname = argv[0];
84
85 for (;;) {
86 int c, idx = 0;
87 c = getopt_long(argc, argv, options_short, options, &idx);
88 if (c == -1)
89 break;
90 switch (c) {
91 case 'l':
92 test_list(tests);
93 return 0;
94 case 'h':
95 help();
96 return 0;
97 case 'n':
98 oneshot = 1;
99 break;
100 case '?':
101 return -1;
102 default:
103 ERR("unexpected getopt_long() value %c\n", c);
104 return -1;
105 }
106 }
107
108 if (isatty(STDOUT_FILENO) == 0) {
109 ANSI_HIGHLIGHT_OFF = "";
110 ANSI_HIGHLIGHT_RED_ON = "";
111 ANSI_HIGHLIGHT_GREEN_ON = "";
112 }
113
114 return optind;
115 }
116
117 const struct test *test_find(const struct test *tests[], const char *name)
118 {
119 size_t i;
120
121 for (i = 0; tests[i] != NULL; i++) {
122 if (strcmp(tests[i]->name, name) == 0)
123 return tests[i];
124 }
125
126 return NULL;
127 }
128
129 static int test_spawn_test(const struct test *t)
130 {
131 const char *const args[] = { progname, "-n", t->name, NULL };
132
133 execv(progname, (char *const *) args);
134
135 ERR("failed to spawn %s for %s: %m\n", progname, t->name);
136 return EXIT_FAILURE;
137 }
138
139 static int test_run_spawned(const struct test *t)
140 {
141 int err = t->func(t);
142 exit(err);
143
144 return EXIT_FAILURE;
145 }
146
147 int test_spawn_prog(const char *prog, const char *const args[])
148 {
149 execv(prog, (char *const *) args);
150
151 ERR("failed to spawn %s\n", prog);
152 ERR("did you forget to build tools?\n");
153 return EXIT_FAILURE;
154 }
155
156 static void test_export_environ(const struct test *t)
157 {
158 char *preload = NULL;
159 size_t preloadlen = 0;
160 size_t i;
161
162 unsetenv("LD_PRELOAD");
163
164 for (i = 0; i < _TC_LAST; i++) {
165 const char *ldpreload;
166 size_t ldpreloadlen;
167 char *tmp;
168
169 if (t->config[i] == NULL)
170 continue;
171
172 setenv(env_config[i].key, t->config[i], 1);
173
174 ldpreload = env_config[i].ldpreload;
175 ldpreloadlen = strlen(ldpreload);
176 tmp = realloc(preload, preloadlen + 2 + ldpreloadlen);
177 if (tmp == NULL) {
178 ERR("oom: test_export_environ()\n");
179 return;
180 }
181 preload = tmp;
182
183 if (preloadlen > 0)
184 preload[preloadlen++] = ' ';
185 memcpy(preload + preloadlen, ldpreload, ldpreloadlen);
186 preloadlen += ldpreloadlen;
187 preload[preloadlen] = '\0';
188 }
189
190 if (preload != NULL)
191 setenv("LD_PRELOAD", preload, 1);
192
193 free(preload);
194 }
195
196 static inline int test_run_child(const struct test *t, int fdout[2],
197 int fderr[2])
198 {
199 /* kill child if parent dies */
200 prctl(PR_SET_PDEATHSIG, SIGTERM);
201
202 test_export_environ(t);
203
204 /* Close read-fds and redirect std{out,err} to the write-fds */
205 if (t->output.stdout != NULL) {
206 close(fdout[0]);
207 if (dup2(fdout[1], STDOUT_FILENO) < 0) {
208 ERR("could not redirect stdout to pipe: %m");
209 exit(EXIT_FAILURE);
210 }
211 }
212
213 if (t->output.stderr != NULL) {
214 close(fderr[0]);
215 if (dup2(fderr[1], STDERR_FILENO) < 0) {
216 ERR("could not redirect stdout to pipe: %m");
217 exit(EXIT_FAILURE);
218 }
219 }
220
221 if (t->need_spawn)
222 return test_spawn_test(t);
223 else
224 return test_run_spawned(t);
225 }
226
227 static inline bool test_run_parent_check_outputs(const struct test *t,
228 int fdout, int fderr)
229 {
230 struct epoll_event ep_outpipe, ep_errpipe;
231 int err, fd_ep, fd_matchout = -1, fd_matcherr = -1;
232
233 if (t->output.stdout == NULL && t->output.stderr == NULL)
234 return true;
235
236 fd_ep = epoll_create1(EPOLL_CLOEXEC);
237 if (fd_ep < 0) {
238 ERR("could not create epoll fd: %m\n");
239 return false;
240 }
241
242 if (t->output.stdout != NULL) {
243 fd_matchout = open(t->output.stdout, O_RDONLY);
244 if (fd_matchout < 0) {
245 err = -errno;
246 ERR("could not open %s for read: %m\n",
247 t->output.stdout);
248 goto out;
249 }
250 memset(&ep_outpipe, 0, sizeof(struct epoll_event));
251 ep_outpipe.events = EPOLLIN;
252 ep_outpipe.data.ptr = &fdout;
253 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdout, &ep_outpipe) < 0) {
254 err = -errno;
255 ERR("could not add fd to epoll: %m\n");
256 goto out;
257 }
258 } else
259 fdout = -1;
260
261 if (t->output.stderr != NULL) {
262 fd_matcherr = open(t->output.stderr, O_RDONLY);
263 if (fd_matcherr < 0) {
264 err = -errno;
265 ERR("could not open %s for read: %m\n",
266 t->output.stderr);
267 goto out;
268
269 }
270 memset(&ep_errpipe, 0, sizeof(struct epoll_event));
271 ep_errpipe.events = EPOLLIN;
272 ep_errpipe.data.ptr = &fderr;
273 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fderr, &ep_errpipe) < 0) {
274 err = -errno;
275 ERR("could not add fd to epoll: %m\n");
276 goto out;
277 }
278 } else
279 fderr = -1;
280
281 for (err = 0; fdout >= 0 || fderr >= 0;) {
282 int fdcount, i;
283 struct epoll_event ev[4];
284
285 fdcount = epoll_wait(fd_ep, ev, 4, -1);
286 if (fdcount < 0) {
287 if (errno == EINTR)
288 continue;
289 err = -errno;
290 ERR("could not poll: %m\n");
291 goto out;
292 }
293
294 for (i = 0; i < fdcount; i++) {
295 int *fd = ev[i].data.ptr;
296
297 if (ev[i].events & EPOLLIN) {
298 ssize_t r, done = 0;
299 char buf[4096];
300 char bufmatch[4096];
301 int fd_match;
302
303 /*
304 * compare the output from child with the one
305 * saved as correct
306 */
307
308 r = read(*fd, buf, sizeof(buf) - 1);
309 if (r <= 0)
310 continue;
311
312 if (*fd == fdout)
313 fd_match = fd_matchout;
314 else
315 fd_match = fd_matcherr;
316
317 for (;;) {
318 int rmatch = read(fd_match,
319 bufmatch + done, r - done);
320 if (rmatch == 0)
321 break;
322
323 if (rmatch < 0) {
324 if (errno == EINTR)
325 continue;
326 err = -errno;
327 ERR("could not read match fd %d\n",
328 fd_match);
329 goto out;
330 }
331
332 done += rmatch;
333 }
334
335 buf[r] = '\0';
336 bufmatch[r] = '\0';
337 if (strcmp(buf, bufmatch) != 0) {
338 ERR("Outputs do not match on %s:\n",
339 fd_match == fd_matchout ? "stdout" : "stderr");
340 ERR("correct:\n%s\n", bufmatch);
341 ERR("wrong:\n%s\n", buf);
342 err = -1;
343 goto out;
344 }
345 } else if (ev[i].events & EPOLLHUP) {
346 if (epoll_ctl(fd_ep, EPOLL_CTL_DEL,
347 *fd, NULL) < 0) {
348 ERR("could not remove fd %d from epoll: %m\n",
349 *fd);
350 }
351 *fd = -1;
352 }
353 }
354
355 }
356 out:
357 if (fd_matchout >= 0)
358 close(fd_matchout);
359 if (fd_matcherr >= 0)
360 close(fd_matcherr);
361 if (fd_ep >= 0)
362 close(fd_ep);
363 return err == 0;
364 }
365
366 static inline int test_run_parent(const struct test *t, int fdout[2],
367 int fderr[2])
368 {
369 pid_t pid;
370 int err;
371 bool matchout;
372
373 /* Close write-fds */
374 if (t->output.stdout != NULL)
375 close(fdout[1]);
376 if (t->output.stderr != NULL)
377 close(fderr[1]);
378
379 matchout = test_run_parent_check_outputs(t, fdout[0], fderr[0]);
380
381 /*
382 * break pipe on the other end: either child already closed or we want
383 * to stop it
384 */
385 if (t->output.stdout != NULL)
386 close(fdout[0]);
387 if (t->output.stderr != NULL)
388 close(fderr[0]);
389
390 do {
391 pid = wait(&err);
392 if (pid == -1) {
393 ERR("error waitpid(): %m\n");
394 return EXIT_FAILURE;
395 }
396 } while (!WIFEXITED(err) && !WIFSIGNALED(err));
397
398 if (WIFEXITED(err)) {
399 if (WEXITSTATUS(err) != 0)
400 ERR("'%s' [%u] exited with return code %d\n",
401 t->name, pid, WEXITSTATUS(err));
402 else
403 LOG("'%s' [%u] exited with return code %d\n",
404 t->name, pid, WEXITSTATUS(err));
405 } else if (WIFSIGNALED(err)) {
406 ERR("'%s' [%u] terminated by signal %d (%s)\n", t->name, pid,
407 WTERMSIG(err), strsignal(WTERMSIG(err)));
408 }
409
410 if (err == 0) {
411 if (matchout)
412 LOG("%sPASSED%s: %s\n",
413 ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
414 t->name);
415 else {
416 ERR("%sFAILED%s: exit ok but outputs do not match: %s\n",
417 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
418 t->name);
419 err = EXIT_FAILURE;
420 }
421 } else
422 ERR("%sFAILED%s: %s\n",
423 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
424 t->name);
425
426 return err;
427 }
428
429 int test_run(const struct test *t)
430 {
431 pid_t pid;
432 int fdout[2];
433 int fderr[2];
434
435 if (t->need_spawn && oneshot)
436 test_run_spawned(t);
437
438 if (t->output.stdout != NULL) {
439 if (pipe(fdout) != 0) {
440 ERR("could not create out pipe for %s\n", t->name);
441 return EXIT_FAILURE;
442 }
443 }
444
445 if (t->output.stderr != NULL) {
446 if (pipe(fderr) != 0) {
447 ERR("could not create err pipe for %s\n", t->name);
448 return EXIT_FAILURE;
449 }
450 }
451
452 LOG("running %s, in forked context\n", t->name);
453
454 pid = fork();
455 if (pid < 0) {
456 ERR("could not fork(): %m\n");
457 LOG("FAILED: %s\n", t->name);
458 return EXIT_FAILURE;
459 }
460
461 if (pid > 0)
462 return test_run_parent(t, fdout, fderr);
463
464 return test_run_child(t, fdout, fderr);
465 }