]> git.ipfire.org Git - thirdparty/kmod.git/blame - testsuite/testsuite.c
testsuite: add timeout for each test
[thirdparty/kmod.git] / testsuite / testsuite.c
CommitLineData
e701e381
LDM
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
80f9e023
LDM
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>
b8e344a6 26#include <time.h>
80f9e023 27#include <unistd.h>
3dbb8dea 28#include <sys/epoll.h>
80f9e023 29#include <sys/prctl.h>
0de690c9 30#include <sys/stat.h>
80f9e023
LDM
31#include <sys/wait.h>
32
0de690c9 33#include "libkmod-util.h"
80f9e023
LDM
34#include "testsuite.h"
35
51b1d1ab
LDM
36static const char *ANSI_HIGHLIGHT_GREEN_ON = "\x1B[1;32m";
37static const char *ANSI_HIGHLIGHT_RED_ON = "\x1B[1;31m";
38static const char *ANSI_HIGHLIGHT_OFF = "\x1B[0m";
39
80f9e023 40static const char *progname;
ed2df4e9 41static int oneshot = 0;
80f9e023
LDM
42static const char options_short[] = "lhn";
43static const struct option options[] = {
44 { "list", no_argument, 0, 'l' },
45 { "help", no_argument, 0, 'h' },
46 { NULL, 0, 0, 0 }
47};
48
d2c2b8b5
LDM
49#define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/"
50
395478cb
LDM
51struct _env_config {
52 const char *key;
53 const char *ldpreload;
54} env_config[_TC_LAST] = {
d2c2b8b5
LDM
55 [TC_UNAME_R] = { S_TC_UNAME_R, OVERRIDE_LIBDIR "uname.so" },
56 [TC_ROOTFS] = { S_TC_ROOTFS, OVERRIDE_LIBDIR "path.so" },
57 [TC_INIT_MODULE_RETCODES] = { S_TC_INIT_MODULE_RETCODES, OVERRIDE_LIBDIR "init_module.so" },
f6ef5d6b 58 [TC_DELETE_MODULE_RETCODES] = { S_TC_DELETE_MODULE_RETCODES, OVERRIDE_LIBDIR "delete_module.so" },
395478cb
LDM
59};
60
b8e344a6
LDM
61#define USEC_PER_SEC 1000000ULL
62#define USEC_PER_MSEC 1000ULL
63#define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC
64static unsigned long long now_usec(void)
65{
66 struct timespec ts;
67
68 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
69 return 0;
70
71 return ts_usec(&ts);
72}
73
80f9e023
LDM
74static void help(void)
75{
76 const struct option *itr;
77 const char *itr_short;
78
79 printf("Usage:\n"
80 "\t%s [options] <test>\n"
81 "Options:\n", basename(progname));
82
83 for (itr = options, itr_short = options_short;
84 itr->name != NULL; itr++, itr_short++)
85 printf("\t-%c, --%s\n", *itr_short, itr->name);
86}
87
88static void test_list(const struct test *tests[])
89{
90 size_t i;
91
92 printf("Available tests:\n");
93 for (i = 0; tests[i] != NULL; i++)
94 printf("\t%s, %s\n", tests[i]->name, tests[i]->description);
95}
96
97int test_init(int argc, char *const argv[], const struct test *tests[])
98{
99 progname = argv[0];
100
101 for (;;) {
102 int c, idx = 0;
103 c = getopt_long(argc, argv, options_short, options, &idx);
104 if (c == -1)
105 break;
106 switch (c) {
107 case 'l':
108 test_list(tests);
109 return 0;
110 case 'h':
111 help();
112 return 0;
113 case 'n':
114 oneshot = 1;
115 break;
116 case '?':
117 return -1;
118 default:
119 ERR("unexpected getopt_long() value %c\n", c);
120 return -1;
121 }
122 }
123
51b1d1ab
LDM
124 if (isatty(STDOUT_FILENO) == 0) {
125 ANSI_HIGHLIGHT_OFF = "";
126 ANSI_HIGHLIGHT_RED_ON = "";
127 ANSI_HIGHLIGHT_GREEN_ON = "";
128 }
129
80f9e023
LDM
130 return optind;
131}
132
133const struct test *test_find(const struct test *tests[], const char *name)
134{
135 size_t i;
136
137 for (i = 0; tests[i] != NULL; i++) {
138 if (strcmp(tests[i]->name, name) == 0)
139 return tests[i];
140 }
141
142 return NULL;
143}
144
ed2df4e9 145static int test_spawn_test(const struct test *t)
80f9e023
LDM
146{
147 const char *const args[] = { progname, "-n", t->name, NULL };
148
149 execv(progname, (char *const *) args);
150
151 ERR("failed to spawn %s for %s: %m\n", progname, t->name);
152 return EXIT_FAILURE;
153}
154
ed2df4e9
LDM
155static int test_run_spawned(const struct test *t)
156{
157 int err = t->func(t);
158 exit(err);
159
160 return EXIT_FAILURE;
161}
162
9e3b9d2e 163int test_spawn_prog(const char *prog, const char *const args[])
80f9e023
LDM
164{
165 execv(prog, (char *const *) args);
166
9e3b9d2e
LDM
167 ERR("failed to spawn %s\n", prog);
168 ERR("did you forget to build tools?\n");
80f9e023
LDM
169 return EXIT_FAILURE;
170}
171
395478cb 172static void test_export_environ(const struct test *t)
80f9e023 173{
395478cb
LDM
174 char *preload = NULL;
175 size_t preloadlen = 0;
176 size_t i;
177
178 unsetenv("LD_PRELOAD");
179
180 for (i = 0; i < _TC_LAST; i++) {
181 const char *ldpreload;
182 size_t ldpreloadlen;
183 char *tmp;
184
185 if (t->config[i] == NULL)
186 continue;
187
188 setenv(env_config[i].key, t->config[i], 1);
189
190 ldpreload = env_config[i].ldpreload;
191 ldpreloadlen = strlen(ldpreload);
192 tmp = realloc(preload, preloadlen + 2 + ldpreloadlen);
193 if (tmp == NULL) {
194 ERR("oom: test_export_environ()\n");
195 return;
196 }
197 preload = tmp;
198
199 if (preloadlen > 0)
200 preload[preloadlen++] = ' ';
201 memcpy(preload + preloadlen, ldpreload, ldpreloadlen);
202 preloadlen += ldpreloadlen;
203 preload[preloadlen] = '\0';
204 }
205
206 if (preload != NULL)
207 setenv("LD_PRELOAD", preload, 1);
208
209 free(preload);
80f9e023
LDM
210}
211
3dbb8dea 212static inline int test_run_child(const struct test *t, int fdout[2],
ed8e93fd 213 int fderr[2], int fdmonitor[2])
80f9e023 214{
45481ee2
LDM
215 /* kill child if parent dies */
216 prctl(PR_SET_PDEATHSIG, SIGTERM);
217
218 test_export_environ(t);
219
3dbb8dea
LDM
220 /* Close read-fds and redirect std{out,err} to the write-fds */
221 if (t->output.stdout != NULL) {
222 close(fdout[0]);
223 if (dup2(fdout[1], STDOUT_FILENO) < 0) {
050db08c 224 ERR("could not redirect stdout to pipe: %m\n");
3dbb8dea
LDM
225 exit(EXIT_FAILURE);
226 }
227 }
228
229 if (t->output.stderr != NULL) {
230 close(fderr[0]);
231 if (dup2(fderr[1], STDERR_FILENO) < 0) {
050db08c 232 ERR("could not redirect stdout to pipe: %m\n");
3dbb8dea
LDM
233 exit(EXIT_FAILURE);
234 }
235 }
236
ed8e93fd
LDM
237 close(fdmonitor[0]);
238
0de690c9
LDM
239 if (t->config[TC_ROOTFS] != NULL) {
240 const char *stamp = TESTSUITE_ROOTFS "../stamp-rootfs";
241 const char *rootfs = t->config[TC_ROOTFS];
242 struct stat rootfsst, stampst;
243
244 if (stat(stamp, &stampst) != 0) {
245 ERR("could not stat %s\n - %m", stamp);
246 exit(EXIT_FAILURE);
247 }
248
249 if (stat(rootfs, &rootfsst) != 0) {
250 ERR("could not stat %s\n - %m", rootfs);
251 exit(EXIT_FAILURE);
252 }
253
254 if (stat_mstamp(&rootfsst) > stat_mstamp(&stampst)) {
255 ERR("rootfs %s is dirty, please run 'make rootfs' before runnning this test\n",
256 rootfs);
257 exit(EXIT_FAILURE);
258 }
259 }
260
45481ee2
LDM
261 if (t->need_spawn)
262 return test_spawn_test(t);
263 else
264 return test_run_spawned(t);
265}
266
3dbb8dea 267static inline bool test_run_parent_check_outputs(const struct test *t,
b8e344a6 268 int fdout, int fderr, int fdmonitor, pid_t child)
3dbb8dea 269{
ed8e93fd 270 struct epoll_event ep_outpipe, ep_errpipe, ep_monitor;
3dbb8dea 271 int err, fd_ep, fd_matchout = -1, fd_matcherr = -1;
b8e344a6 272 unsigned long long end_usec, start_usec;
3dbb8dea 273
3dbb8dea
LDM
274 fd_ep = epoll_create1(EPOLL_CLOEXEC);
275 if (fd_ep < 0) {
276 ERR("could not create epoll fd: %m\n");
277 return false;
278 }
279
280 if (t->output.stdout != NULL) {
281 fd_matchout = open(t->output.stdout, O_RDONLY);
282 if (fd_matchout < 0) {
283 err = -errno;
284 ERR("could not open %s for read: %m\n",
285 t->output.stdout);
286 goto out;
287 }
288 memset(&ep_outpipe, 0, sizeof(struct epoll_event));
289 ep_outpipe.events = EPOLLIN;
290 ep_outpipe.data.ptr = &fdout;
291 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdout, &ep_outpipe) < 0) {
292 err = -errno;
293 ERR("could not add fd to epoll: %m\n");
294 goto out;
295 }
296 } else
297 fdout = -1;
298
299 if (t->output.stderr != NULL) {
300 fd_matcherr = open(t->output.stderr, O_RDONLY);
301 if (fd_matcherr < 0) {
302 err = -errno;
303 ERR("could not open %s for read: %m\n",
304 t->output.stderr);
305 goto out;
306
307 }
308 memset(&ep_errpipe, 0, sizeof(struct epoll_event));
309 ep_errpipe.events = EPOLLIN;
310 ep_errpipe.data.ptr = &fderr;
311 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fderr, &ep_errpipe) < 0) {
312 err = -errno;
313 ERR("could not add fd to epoll: %m\n");
314 goto out;
315 }
316 } else
317 fderr = -1;
318
ed8e93fd
LDM
319 memset(&ep_monitor, 0, sizeof(struct epoll_event));
320 ep_monitor.events = EPOLLHUP;
321 ep_monitor.data.ptr = &fdmonitor;
322 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdmonitor, &ep_monitor) < 0) {
323 err = -errno;
324 ERR("could not add monitor fd to epoll: %m\n");
325 goto out;
326 }
327
b8e344a6
LDM
328 start_usec = now_usec();
329 end_usec = start_usec + TEST_TIMEOUT_USEC;
330
ed8e93fd 331 for (err = 0; fdmonitor >= 0 || fdout >= 0 || fderr >= 0;) {
b8e344a6 332 int fdcount, i, timeout;
3dbb8dea 333 struct epoll_event ev[4];
b8e344a6 334 unsigned long long curr_usec = now_usec();
3dbb8dea 335
b8e344a6
LDM
336 if (curr_usec > end_usec)
337 break;
338
339 timeout = (end_usec - curr_usec) / USEC_PER_MSEC;
340 fdcount = epoll_wait(fd_ep, ev, 4, timeout);
3dbb8dea
LDM
341 if (fdcount < 0) {
342 if (errno == EINTR)
343 continue;
344 err = -errno;
345 ERR("could not poll: %m\n");
346 goto out;
347 }
348
349 for (i = 0; i < fdcount; i++) {
350 int *fd = ev[i].data.ptr;
351
352 if (ev[i].events & EPOLLIN) {
353 ssize_t r, done = 0;
354 char buf[4096];
355 char bufmatch[4096];
356 int fd_match;
357
358 /*
359 * compare the output from child with the one
360 * saved as correct
361 */
362
363 r = read(*fd, buf, sizeof(buf) - 1);
364 if (r <= 0)
365 continue;
366
367 if (*fd == fdout)
368 fd_match = fd_matchout;
ed8e93fd 369 else if (*fd == fderr)
3dbb8dea 370 fd_match = fd_matcherr;
ed8e93fd
LDM
371 else {
372 ERR("Unexpected activity on monitor pipe\n");
373 err = -EINVAL;
374 goto out;
375 }
3dbb8dea
LDM
376
377 for (;;) {
378 int rmatch = read(fd_match,
379 bufmatch + done, r - done);
380 if (rmatch == 0)
381 break;
382
383 if (rmatch < 0) {
384 if (errno == EINTR)
385 continue;
386 err = -errno;
387 ERR("could not read match fd %d\n",
388 fd_match);
389 goto out;
390 }
391
392 done += rmatch;
393 }
394
395 buf[r] = '\0';
396 bufmatch[r] = '\0';
397 if (strcmp(buf, bufmatch) != 0) {
398 ERR("Outputs do not match on %s:\n",
399 fd_match == fd_matchout ? "stdout" : "stderr");
400 ERR("correct:\n%s\n", bufmatch);
401 ERR("wrong:\n%s\n", buf);
402 err = -1;
403 goto out;
404 }
405 } else if (ev[i].events & EPOLLHUP) {
406 if (epoll_ctl(fd_ep, EPOLL_CTL_DEL,
407 *fd, NULL) < 0) {
408 ERR("could not remove fd %d from epoll: %m\n",
409 *fd);
410 }
411 *fd = -1;
412 }
413 }
b8e344a6 414 }
3dbb8dea 415
b8e344a6
LDM
416 if (err == 0 && fdmonitor >= 0) {
417 err = -EINVAL;
418 ERR("Test '%s' timed out, killing %d\n", t->name, child);
419 kill(child, SIGKILL);
3dbb8dea 420 }
b8e344a6 421
3dbb8dea
LDM
422out:
423 if (fd_matchout >= 0)
424 close(fd_matchout);
425 if (fd_matcherr >= 0)
426 close(fd_matcherr);
427 if (fd_ep >= 0)
428 close(fd_ep);
429 return err == 0;
430}
431
432static inline int test_run_parent(const struct test *t, int fdout[2],
b8e344a6 433 int fderr[2], int fdmonitor[2], pid_t child)
45481ee2
LDM
434{
435 pid_t pid;
80f9e023 436 int err;
3dbb8dea
LDM
437 bool matchout;
438
439 /* Close write-fds */
440 if (t->output.stdout != NULL)
441 close(fdout[1]);
442 if (t->output.stderr != NULL)
443 close(fderr[1]);
ed8e93fd 444 close(fdmonitor[1]);
3dbb8dea 445
ed8e93fd 446 matchout = test_run_parent_check_outputs(t, fdout[0], fderr[0],
b8e344a6 447 fdmonitor[0], child);
3dbb8dea
LDM
448
449 /*
450 * break pipe on the other end: either child already closed or we want
451 * to stop it
452 */
453 if (t->output.stdout != NULL)
454 close(fdout[0]);
455 if (t->output.stderr != NULL)
456 close(fderr[0]);
ed8e93fd 457 close(fdmonitor[0]);
45481ee2
LDM
458
459 do {
460 pid = wait(&err);
461 if (pid == -1) {
462 ERR("error waitpid(): %m\n");
463 return EXIT_FAILURE;
464 }
465 } while (!WIFEXITED(err) && !WIFSIGNALED(err));
466
3dbb8dea
LDM
467 if (WIFEXITED(err)) {
468 if (WEXITSTATUS(err) != 0)
469 ERR("'%s' [%u] exited with return code %d\n",
470 t->name, pid, WEXITSTATUS(err));
471 else
472 LOG("'%s' [%u] exited with return code %d\n",
473 t->name, pid, WEXITSTATUS(err));
474 } else if (WIFSIGNALED(err)) {
475 ERR("'%s' [%u] terminated by signal %d (%s)\n", t->name, pid,
476 WTERMSIG(err), strsignal(WTERMSIG(err)));
b8e344a6 477 return EXIT_FAILURE;
3dbb8dea
LDM
478 }
479
fa0046ba
DR
480 if (t->expected_fail == false) {
481 if (err == 0) {
482 if (matchout)
483 LOG("%sPASSED%s: %s\n",
484 ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
485 t->name);
486 else {
487 ERR("%sFAILED%s: exit ok but outputs do not match: %s\n",
488 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
489 t->name);
490 err = EXIT_FAILURE;
491 }
492 } else
493 ERR("%sFAILED%s: %s\n",
494 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
495 t->name);
496 } else {
497 if (err == 0) {
498 if (matchout) {
499 LOG("%sUNEXPECTED PASS%s: %s\n",
658e0471 500 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
fa0046ba
DR
501 t->name);
502 err = EXIT_FAILURE;
503 } else
504 LOG("%sEXPECTED FAIL%s: exit ok but outputs do not match: %s\n",
505 ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
506 t->name);
507 } else {
508 ERR("%sEXPECTED FAIL%s: %s\n",
509 ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
510 t->name);
511 err = EXIT_SUCCESS;
3dbb8dea 512 }
fa0046ba 513 }
45481ee2 514
45481ee2
LDM
515 return err;
516}
517
f31d49c8
DR
518static int prepend_path(const char *extra)
519{
520 char *oldpath, *newpath;
521 int r;
522
523 if (extra == NULL)
524 return 0;
525
526 oldpath = getenv("PATH");
527 if (oldpath == NULL)
528 return setenv("PATH", extra, 1);
529
530 if (asprintf(&newpath, "%s:%s", extra, oldpath) < 0) {
050db08c 531 ERR("failed to allocate memory to new PATH\n");
f31d49c8
DR
532 return -1;
533 }
534
535 r = setenv("PATH", newpath, 1);
536 free(newpath);
537
538 return r;
539}
540
45481ee2
LDM
541int test_run(const struct test *t)
542{
80f9e023 543 pid_t pid;
3dbb8dea
LDM
544 int fdout[2];
545 int fderr[2];
ed8e93fd 546 int fdmonitor[2];
80f9e023 547
ed2df4e9
LDM
548 if (t->need_spawn && oneshot)
549 test_run_spawned(t);
550
3dbb8dea
LDM
551 if (t->output.stdout != NULL) {
552 if (pipe(fdout) != 0) {
553 ERR("could not create out pipe for %s\n", t->name);
554 return EXIT_FAILURE;
555 }
556 }
557
558 if (t->output.stderr != NULL) {
559 if (pipe(fderr) != 0) {
560 ERR("could not create err pipe for %s\n", t->name);
561 return EXIT_FAILURE;
562 }
563 }
564
ed8e93fd
LDM
565 if (pipe(fdmonitor) != 0) {
566 ERR("could not create monitor pipe for %s\n", t->name);
567 return EXIT_FAILURE;
568 }
569
f31d49c8
DR
570 if (prepend_path(t->path) < 0) {
571 ERR("failed to prepend '%s' to PATH\n", t->path);
572 return EXIT_FAILURE;
573 }
574
80f9e023
LDM
575 LOG("running %s, in forked context\n", t->name);
576
577 pid = fork();
578 if (pid < 0) {
579 ERR("could not fork(): %m\n");
580 LOG("FAILED: %s\n", t->name);
581 return EXIT_FAILURE;
582 }
583
45481ee2 584 if (pid > 0)
b8e344a6 585 return test_run_parent(t, fdout, fderr, fdmonitor, pid);
80f9e023 586
ed8e93fd 587 return test_run_child(t, fdout, fderr, fdmonitor);
80f9e023 588}