]> git.ipfire.org Git - thirdparty/kmod.git/blame - testsuite/testsuite.c
testsuite: improve coverage of shared/util.h
[thirdparty/kmod.git] / testsuite / testsuite.c
CommitLineData
e701e381 1/*
e6b0e49b 2 * Copyright (C) 2012-2013 ProFUSION embedded systems
e701e381 3 *
e1b1ab24
LDM
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
e701e381
LDM
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
e1b1ab24
LDM
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
e701e381 13 *
e1b1ab24 14 * You should have received a copy of the GNU Lesser General Public
dea2dfee 15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
e701e381
LDM
16 */
17
c2e4286b 18#include <dirent.h>
80f9e023
LDM
19#include <errno.h>
20#include <fcntl.h>
21#include <getopt.h>
22#include <limits.h>
80f9e023 23#include <stdarg.h>
c2e4286b 24#include <stdio.h>
80f9e023
LDM
25#include <stdlib.h>
26#include <string.h>
b8e344a6 27#include <time.h>
80f9e023 28#include <unistd.h>
3dbb8dea 29#include <sys/epoll.h>
80f9e023 30#include <sys/prctl.h>
0de690c9 31#include <sys/stat.h>
80f9e023
LDM
32#include <sys/wait.h>
33
96573a02
LDM
34#include <shared/util.h>
35
80f9e023
LDM
36#include "testsuite.h"
37
51b1d1ab
LDM
38static const char *ANSI_HIGHLIGHT_GREEN_ON = "\x1B[1;32m";
39static const char *ANSI_HIGHLIGHT_RED_ON = "\x1B[1;31m";
40static const char *ANSI_HIGHLIGHT_OFF = "\x1B[0m";
41
80f9e023 42static const char *progname;
ed2df4e9 43static int oneshot = 0;
80f9e023
LDM
44static const char options_short[] = "lhn";
45static const struct option options[] = {
46 { "list", no_argument, 0, 'l' },
47 { "help", no_argument, 0, 'h' },
48 { NULL, 0, 0, 0 }
49};
50
d2c2b8b5
LDM
51#define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/"
52
395478cb
LDM
53struct _env_config {
54 const char *key;
55 const char *ldpreload;
56} env_config[_TC_LAST] = {
d2c2b8b5
LDM
57 [TC_UNAME_R] = { S_TC_UNAME_R, OVERRIDE_LIBDIR "uname.so" },
58 [TC_ROOTFS] = { S_TC_ROOTFS, OVERRIDE_LIBDIR "path.so" },
59 [TC_INIT_MODULE_RETCODES] = { S_TC_INIT_MODULE_RETCODES, OVERRIDE_LIBDIR "init_module.so" },
f6ef5d6b 60 [TC_DELETE_MODULE_RETCODES] = { S_TC_DELETE_MODULE_RETCODES, OVERRIDE_LIBDIR "delete_module.so" },
395478cb
LDM
61};
62
b8e344a6
LDM
63#define USEC_PER_SEC 1000000ULL
64#define USEC_PER_MSEC 1000ULL
65#define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC
66static unsigned long long now_usec(void)
67{
68 struct timespec ts;
69
70 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
71 return 0;
72
73 return ts_usec(&ts);
74}
75
80f9e023
LDM
76static void help(void)
77{
78 const struct option *itr;
79 const char *itr_short;
80
81 printf("Usage:\n"
82 "\t%s [options] <test>\n"
83 "Options:\n", basename(progname));
84
85 for (itr = options, itr_short = options_short;
86 itr->name != NULL; itr++, itr_short++)
87 printf("\t-%c, --%s\n", *itr_short, itr->name);
88}
89
c5798fea 90static void test_list(const struct test *start, const struct test *stop)
80f9e023 91{
c5798fea 92 const struct test *t;
80f9e023
LDM
93
94 printf("Available tests:\n");
c5798fea
LDM
95 for (t = start; t < stop; t++)
96 printf("\t%s, %s\n", t->name, t->description);
80f9e023
LDM
97}
98
c5798fea
LDM
99int test_init(const struct test *start, const struct test *stop,
100 int argc, char *const argv[])
80f9e023
LDM
101{
102 progname = argv[0];
103
104 for (;;) {
105 int c, idx = 0;
106 c = getopt_long(argc, argv, options_short, options, &idx);
107 if (c == -1)
108 break;
109 switch (c) {
110 case 'l':
c5798fea 111 test_list(start, stop);
80f9e023
LDM
112 return 0;
113 case 'h':
114 help();
115 return 0;
116 case 'n':
117 oneshot = 1;
118 break;
119 case '?':
120 return -1;
121 default:
122 ERR("unexpected getopt_long() value %c\n", c);
123 return -1;
124 }
125 }
126
51b1d1ab
LDM
127 if (isatty(STDOUT_FILENO) == 0) {
128 ANSI_HIGHLIGHT_OFF = "";
129 ANSI_HIGHLIGHT_RED_ON = "";
130 ANSI_HIGHLIGHT_GREEN_ON = "";
131 }
132
80f9e023
LDM
133 return optind;
134}
135
c5798fea
LDM
136const struct test *test_find(const struct test *start,
137 const struct test *stop, const char *name)
80f9e023 138{
c5798fea 139 const struct test *t;
80f9e023 140
c5798fea 141 for (t = start; t < stop; t++) {
5c42c5fc 142 if (streq(t->name, name))
c5798fea 143 return t;
80f9e023
LDM
144 }
145
146 return NULL;
147}
148
ed2df4e9 149static int test_spawn_test(const struct test *t)
80f9e023
LDM
150{
151 const char *const args[] = { progname, "-n", t->name, NULL };
152
153 execv(progname, (char *const *) args);
154
155 ERR("failed to spawn %s for %s: %m\n", progname, t->name);
156 return EXIT_FAILURE;
157}
158
ed2df4e9
LDM
159static int test_run_spawned(const struct test *t)
160{
161 int err = t->func(t);
162 exit(err);
163
164 return EXIT_FAILURE;
165}
166
9e3b9d2e 167int test_spawn_prog(const char *prog, const char *const args[])
80f9e023
LDM
168{
169 execv(prog, (char *const *) args);
170
9e3b9d2e
LDM
171 ERR("failed to spawn %s\n", prog);
172 ERR("did you forget to build tools?\n");
80f9e023
LDM
173 return EXIT_FAILURE;
174}
175
395478cb 176static void test_export_environ(const struct test *t)
80f9e023 177{
395478cb
LDM
178 char *preload = NULL;
179 size_t preloadlen = 0;
180 size_t i;
34db3f2d 181 const struct keyval *env;
395478cb
LDM
182
183 unsetenv("LD_PRELOAD");
184
185 for (i = 0; i < _TC_LAST; i++) {
186 const char *ldpreload;
187 size_t ldpreloadlen;
188 char *tmp;
189
190 if (t->config[i] == NULL)
191 continue;
192
193 setenv(env_config[i].key, t->config[i], 1);
194
195 ldpreload = env_config[i].ldpreload;
196 ldpreloadlen = strlen(ldpreload);
197 tmp = realloc(preload, preloadlen + 2 + ldpreloadlen);
198 if (tmp == NULL) {
199 ERR("oom: test_export_environ()\n");
200 return;
201 }
202 preload = tmp;
203
204 if (preloadlen > 0)
205 preload[preloadlen++] = ' ';
206 memcpy(preload + preloadlen, ldpreload, ldpreloadlen);
207 preloadlen += ldpreloadlen;
208 preload[preloadlen] = '\0';
209 }
210
211 if (preload != NULL)
212 setenv("LD_PRELOAD", preload, 1);
213
214 free(preload);
34db3f2d
LDM
215
216 for (env = t->env_vars; env && env->key; env++)
217 setenv(env->key, env->val, 1);
80f9e023
LDM
218}
219
3dbb8dea 220static inline int test_run_child(const struct test *t, int fdout[2],
ed8e93fd 221 int fderr[2], int fdmonitor[2])
80f9e023 222{
45481ee2
LDM
223 /* kill child if parent dies */
224 prctl(PR_SET_PDEATHSIG, SIGTERM);
225
226 test_export_environ(t);
227
3dbb8dea 228 /* Close read-fds and redirect std{out,err} to the write-fds */
bd4e7340 229 if (t->output.out != NULL) {
3dbb8dea
LDM
230 close(fdout[0]);
231 if (dup2(fdout[1], STDOUT_FILENO) < 0) {
050db08c 232 ERR("could not redirect stdout to pipe: %m\n");
3dbb8dea
LDM
233 exit(EXIT_FAILURE);
234 }
235 }
236
bd4e7340 237 if (t->output.err != NULL) {
3dbb8dea
LDM
238 close(fderr[0]);
239 if (dup2(fderr[1], STDERR_FILENO) < 0) {
bd4e7340 240 ERR("could not redirect stderr to pipe: %m\n");
3dbb8dea
LDM
241 exit(EXIT_FAILURE);
242 }
243 }
244
ed8e93fd
LDM
245 close(fdmonitor[0]);
246
0de690c9
LDM
247 if (t->config[TC_ROOTFS] != NULL) {
248 const char *stamp = TESTSUITE_ROOTFS "../stamp-rootfs";
249 const char *rootfs = t->config[TC_ROOTFS];
250 struct stat rootfsst, stampst;
251
252 if (stat(stamp, &stampst) != 0) {
253 ERR("could not stat %s\n - %m", stamp);
254 exit(EXIT_FAILURE);
255 }
256
257 if (stat(rootfs, &rootfsst) != 0) {
258 ERR("could not stat %s\n - %m", rootfs);
259 exit(EXIT_FAILURE);
260 }
261
262 if (stat_mstamp(&rootfsst) > stat_mstamp(&stampst)) {
263 ERR("rootfs %s is dirty, please run 'make rootfs' before runnning this test\n",
264 rootfs);
265 exit(EXIT_FAILURE);
266 }
267 }
268
45481ee2
LDM
269 if (t->need_spawn)
270 return test_spawn_test(t);
271 else
272 return test_run_spawned(t);
273}
274
3dbb8dea 275static inline bool test_run_parent_check_outputs(const struct test *t,
b8e344a6 276 int fdout, int fderr, int fdmonitor, pid_t child)
3dbb8dea 277{
ed8e93fd 278 struct epoll_event ep_outpipe, ep_errpipe, ep_monitor;
3dbb8dea 279 int err, fd_ep, fd_matchout = -1, fd_matcherr = -1;
b8e344a6 280 unsigned long long end_usec, start_usec;
3dbb8dea 281
3dbb8dea
LDM
282 fd_ep = epoll_create1(EPOLL_CLOEXEC);
283 if (fd_ep < 0) {
284 ERR("could not create epoll fd: %m\n");
285 return false;
286 }
287
bd4e7340
JS
288 if (t->output.out != NULL) {
289 fd_matchout = open(t->output.out, O_RDONLY);
3dbb8dea
LDM
290 if (fd_matchout < 0) {
291 err = -errno;
292 ERR("could not open %s for read: %m\n",
bd4e7340 293 t->output.out);
3dbb8dea
LDM
294 goto out;
295 }
296 memset(&ep_outpipe, 0, sizeof(struct epoll_event));
297 ep_outpipe.events = EPOLLIN;
298 ep_outpipe.data.ptr = &fdout;
299 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdout, &ep_outpipe) < 0) {
300 err = -errno;
301 ERR("could not add fd to epoll: %m\n");
302 goto out;
303 }
304 } else
305 fdout = -1;
306
bd4e7340
JS
307 if (t->output.err != NULL) {
308 fd_matcherr = open(t->output.err, O_RDONLY);
3dbb8dea
LDM
309 if (fd_matcherr < 0) {
310 err = -errno;
311 ERR("could not open %s for read: %m\n",
bd4e7340 312 t->output.err);
3dbb8dea
LDM
313 goto out;
314
315 }
316 memset(&ep_errpipe, 0, sizeof(struct epoll_event));
317 ep_errpipe.events = EPOLLIN;
318 ep_errpipe.data.ptr = &fderr;
319 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fderr, &ep_errpipe) < 0) {
320 err = -errno;
321 ERR("could not add fd to epoll: %m\n");
322 goto out;
323 }
324 } else
325 fderr = -1;
326
ed8e93fd
LDM
327 memset(&ep_monitor, 0, sizeof(struct epoll_event));
328 ep_monitor.events = EPOLLHUP;
329 ep_monitor.data.ptr = &fdmonitor;
330 if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdmonitor, &ep_monitor) < 0) {
331 err = -errno;
332 ERR("could not add monitor fd to epoll: %m\n");
333 goto out;
334 }
335
b8e344a6
LDM
336 start_usec = now_usec();
337 end_usec = start_usec + TEST_TIMEOUT_USEC;
338
ed8e93fd 339 for (err = 0; fdmonitor >= 0 || fdout >= 0 || fderr >= 0;) {
b8e344a6 340 int fdcount, i, timeout;
3dbb8dea 341 struct epoll_event ev[4];
b8e344a6 342 unsigned long long curr_usec = now_usec();
3dbb8dea 343
b8e344a6
LDM
344 if (curr_usec > end_usec)
345 break;
346
347 timeout = (end_usec - curr_usec) / USEC_PER_MSEC;
348 fdcount = epoll_wait(fd_ep, ev, 4, timeout);
3dbb8dea
LDM
349 if (fdcount < 0) {
350 if (errno == EINTR)
351 continue;
352 err = -errno;
353 ERR("could not poll: %m\n");
354 goto out;
355 }
356
357 for (i = 0; i < fdcount; i++) {
358 int *fd = ev[i].data.ptr;
359
360 if (ev[i].events & EPOLLIN) {
361 ssize_t r, done = 0;
362 char buf[4096];
363 char bufmatch[4096];
364 int fd_match;
365
366 /*
367 * compare the output from child with the one
368 * saved as correct
369 */
370
371 r = read(*fd, buf, sizeof(buf) - 1);
372 if (r <= 0)
373 continue;
374
375 if (*fd == fdout)
376 fd_match = fd_matchout;
ed8e93fd 377 else if (*fd == fderr)
3dbb8dea 378 fd_match = fd_matcherr;
ed8e93fd
LDM
379 else {
380 ERR("Unexpected activity on monitor pipe\n");
381 err = -EINVAL;
382 goto out;
383 }
3dbb8dea
LDM
384
385 for (;;) {
386 int rmatch = read(fd_match,
387 bufmatch + done, r - done);
388 if (rmatch == 0)
389 break;
390
391 if (rmatch < 0) {
392 if (errno == EINTR)
393 continue;
394 err = -errno;
395 ERR("could not read match fd %d\n",
396 fd_match);
397 goto out;
398 }
399
400 done += rmatch;
401 }
402
403 buf[r] = '\0';
404 bufmatch[r] = '\0';
5c42c5fc 405 if (!streq(buf, bufmatch)) {
3dbb8dea
LDM
406 ERR("Outputs do not match on %s:\n",
407 fd_match == fd_matchout ? "stdout" : "stderr");
408 ERR("correct:\n%s\n", bufmatch);
409 ERR("wrong:\n%s\n", buf);
410 err = -1;
411 goto out;
412 }
413 } else if (ev[i].events & EPOLLHUP) {
414 if (epoll_ctl(fd_ep, EPOLL_CTL_DEL,
415 *fd, NULL) < 0) {
416 ERR("could not remove fd %d from epoll: %m\n",
417 *fd);
418 }
419 *fd = -1;
420 }
421 }
b8e344a6 422 }
3dbb8dea 423
b8e344a6
LDM
424 if (err == 0 && fdmonitor >= 0) {
425 err = -EINVAL;
426 ERR("Test '%s' timed out, killing %d\n", t->name, child);
427 kill(child, SIGKILL);
3dbb8dea 428 }
b8e344a6 429
3dbb8dea
LDM
430out:
431 if (fd_matchout >= 0)
432 close(fd_matchout);
433 if (fd_matcherr >= 0)
434 close(fd_matcherr);
435 if (fd_ep >= 0)
436 close(fd_ep);
437 return err == 0;
438}
439
3e451bfe
LDM
440static inline int safe_read(int fd, void *buf, size_t count)
441{
442 int r;
443
444 while (1) {
445 r = read(fd, buf, count);
2ad09ccf 446 if (r == -1 && errno == EINTR)
3e451bfe
LDM
447 continue;
448 break;
449 }
450
451 return r;
452}
453
454static bool check_generated_files(const struct test *t)
455{
456 const struct keyval *k;
457
458 /* This is not meant to be a diff replacement, just stupidly check if
459 * the files match. Bear in mind they can be binary files */
460 for (k = t->output.files; k && k->key; k++) {
461 struct stat sta, stb;
462 int fda = -1, fdb = -1;
463 char bufa[4096];
464 char bufb[4096];
465
466 fda = open(k->key, O_RDONLY);
467 if (fda < 0) {
468 ERR("could not open %s\n - %m\n", k->key);
469 goto fail;
470 }
471
472 fdb = open(k->val, O_RDONLY);
473 if (fdb < 0) {
474 ERR("could not open %s\n - %m\n", k->val);
475 goto fail;
476 }
477
478 if (fstat(fda, &sta) != 0) {
479 ERR("could not fstat %d %s\n - %m\n", fda, k->key);
480 goto fail;
481 }
482
483 if (fstat(fdb, &stb) != 0) {
484 ERR("could not fstat %d %s\n - %m\n", fdb, k->key);
485 goto fail;
486 }
487
488 if (sta.st_size != stb.st_size) {
489 ERR("sizes do not match %s %s\n", k->key, k->val);
490 goto fail;
491 }
492
493 for (;;) {
494 int r, done;
495
496 r = safe_read(fda, bufa, sizeof(bufa));
497 if (r < 0)
498 goto fail;
499
500 if (r == 0)
501 /* size is already checked, go to next file */
502 goto next;
503
504 for (done = 0; done < r;) {
505 int r2 = safe_read(fdb, bufb + done, r - done);
506
507 if (r2 <= 0)
508 goto fail;
509
510 done += r2;
511 }
512
513 if (memcmp(bufa, bufb, r) != 0)
514 goto fail;
515 }
516
517next:
518 close(fda);
519 close(fdb);
520 continue;
521
522fail:
523 if (fda >= 0)
524 close(fda);
525 if (fdb >= 0)
526 close(fdb);
527
528 return false;
529 }
530
531 return true;
532}
533
88ac4084
MM
534static int cmp_modnames(const void *m1, const void *m2)
535{
536 const char *s1 = *(char *const *)m1;
537 const char *s2 = *(char *const *)m2;
538 int i;
539
540 for (i = 0; s1[i] || s2[i]; i++) {
541 char c1 = s1[i], c2 = s2[i];
542 if (c1 == '-')
543 c1 = '_';
544 if (c2 == '-')
545 c2 = '_';
546 if (c1 != c2)
547 return c1 - c2;
548 }
549 return 0;
550}
551
552/*
553 * Store the expected module names in buf and return a list of pointers to
554 * them.
555 */
556static const char **read_expected_modules(const struct test *t,
557 char **buf, int *count)
558{
559 const char **res;
560 int len;
561 int i;
562 char *p;
563
564 if (t->modules_loaded[0] == '\0') {
565 *count = 0;
566 *buf = NULL;
567 return NULL;
568 }
569 *buf = strdup(t->modules_loaded);
570 if (!*buf) {
571 *count = -1;
572 return NULL;
573 }
574 len = 1;
575 for (p = *buf; *p; p++)
576 if (*p == ',')
577 len++;
578 res = malloc(sizeof(char *) * len);
579 if (!res) {
580 perror("malloc");
581 *count = -1;
582 free(*buf);
583 *buf = NULL;
584 return NULL;
585 }
586 i = 0;
587 res[i++] = *buf;
588 for (p = *buf; i < len; p++)
589 if (*p == ',') {
590 *p = '\0';
591 res[i++] = p + 1;
592 }
593 *count = len;
594 return res;
595}
596
597static char **read_loaded_modules(const struct test *t, char **buf, int *count)
598{
599 char dirname[PATH_MAX];
600 DIR *dir;
601 struct dirent *dirent;
602 int i;
603 int len = 0, bufsz;
604 char **res = NULL;
605 char *p;
606 const char *rootfs = t->config[TC_ROOTFS] ? t->config[TC_ROOTFS] : "";
607
608 /* Store the entries in /sys/module to res */
609 if (snprintf(dirname, sizeof(dirname), "%s/sys/module", rootfs)
610 >= (int)sizeof(dirname)) {
611 ERR("rootfs path too long: %s\n", rootfs);
612 *buf = NULL;
613 len = -1;
614 goto out;
615 }
616 dir = opendir(dirname);
617 /* not an error, simply return empty list */
618 if (!dir) {
619 *buf = NULL;
620 goto out;
621 }
622 bufsz = 0;
623 while ((dirent = readdir(dir))) {
624 if (dirent->d_name[0] == '.')
625 continue;
626 len++;
627 bufsz += strlen(dirent->d_name) + 1;
628 }
629 res = malloc(sizeof(char *) * len);
630 if (!res) {
631 perror("malloc");
632 len = -1;
633 goto out_dir;
634 }
635 *buf = malloc(bufsz);
636 if (!*buf) {
637 perror("malloc");
638 free(res);
639 res = NULL;
640 len = -1;
641 goto out_dir;
642 }
643 rewinddir(dir);
644 i = 0;
645 p = *buf;
646 while ((dirent = readdir(dir))) {
647 int size;
648
649 if (dirent->d_name[0] == '.')
650 continue;
651 size = strlen(dirent->d_name) + 1;
652 memcpy(p, dirent->d_name, size);
653 res[i++] = p;
654 p += size;
655 }
656out_dir:
657 closedir(dir);
658out:
659 *count = len;
660 return res;
661}
662
663static int check_loaded_modules(const struct test *t)
664{
665 int l1, l2, i1, i2;
666 const char **a1;
667 char **a2;
668 char *buf1, *buf2;
669 int err = false;
670
671 a1 = read_expected_modules(t, &buf1, &l1);
672 if (l1 < 0)
673 return err;
674 a2 = read_loaded_modules(t, &buf2, &l2);
675 if (l2 < 0)
676 goto out_a1;
677 qsort(a1, l1, sizeof(char *), cmp_modnames);
678 qsort(a2, l2, sizeof(char *), cmp_modnames);
679 i1 = i2 = 0;
680 err = true;
681 while (i1 < l1 || i2 < l2) {
682 int cmp;
683
684 if (i1 >= l1)
685 cmp = 1;
686 else if (i2 >= l2)
687 cmp = -1;
688 else
689 cmp = cmp_modnames(&a1[i1], &a2[i2]);
690 if (cmp == 0) {
691 i1++;
692 i2++;
693 } else if (cmp < 0) {
694 err = false;
695 ERR("module %s not loaded\n", a1[i1]);
696 i1++;
697 } else {
698 err = false;
699 ERR("module %s is loaded but should not be \n", a2[i2]);
700 i2++;
701 }
702 }
703 free(a2);
704 free(buf2);
705out_a1:
706 free(a1);
707 free(buf1);
708 return err;
709}
710
3dbb8dea 711static inline int test_run_parent(const struct test *t, int fdout[2],
b8e344a6 712 int fderr[2], int fdmonitor[2], pid_t child)
45481ee2
LDM
713{
714 pid_t pid;
80f9e023 715 int err;
88ac4084 716 bool matchout, match_modules;
3dbb8dea
LDM
717
718 /* Close write-fds */
bd4e7340 719 if (t->output.out != NULL)
3dbb8dea 720 close(fdout[1]);
bd4e7340 721 if (t->output.err != NULL)
3dbb8dea 722 close(fderr[1]);
ed8e93fd 723 close(fdmonitor[1]);
3dbb8dea 724
ed8e93fd 725 matchout = test_run_parent_check_outputs(t, fdout[0], fderr[0],
b8e344a6 726 fdmonitor[0], child);
3dbb8dea
LDM
727
728 /*
729 * break pipe on the other end: either child already closed or we want
730 * to stop it
731 */
bd4e7340 732 if (t->output.out != NULL)
3dbb8dea 733 close(fdout[0]);
bd4e7340 734 if (t->output.err != NULL)
3dbb8dea 735 close(fderr[0]);
ed8e93fd 736 close(fdmonitor[0]);
45481ee2
LDM
737
738 do {
739 pid = wait(&err);
740 if (pid == -1) {
741 ERR("error waitpid(): %m\n");
f988e25c
LDM
742 err = EXIT_FAILURE;
743 goto exit;
45481ee2
LDM
744 }
745 } while (!WIFEXITED(err) && !WIFSIGNALED(err));
746
3dbb8dea
LDM
747 if (WIFEXITED(err)) {
748 if (WEXITSTATUS(err) != 0)
749 ERR("'%s' [%u] exited with return code %d\n",
750 t->name, pid, WEXITSTATUS(err));
751 else
752 LOG("'%s' [%u] exited with return code %d\n",
753 t->name, pid, WEXITSTATUS(err));
754 } else if (WIFSIGNALED(err)) {
755 ERR("'%s' [%u] terminated by signal %d (%s)\n", t->name, pid,
756 WTERMSIG(err), strsignal(WTERMSIG(err)));
f988e25c
LDM
757 err = t->expected_fail ? EXIT_SUCCESS : EXIT_FAILURE;
758 goto exit;
3dbb8dea
LDM
759 }
760
3e451bfe
LDM
761 if (matchout)
762 matchout = check_generated_files(t);
88ac4084
MM
763 if (t->modules_loaded)
764 match_modules = check_loaded_modules(t);
765 else
766 match_modules = true;
3e451bfe 767
fa0046ba
DR
768 if (t->expected_fail == false) {
769 if (err == 0) {
88ac4084 770 if (matchout && match_modules)
fa0046ba
DR
771 LOG("%sPASSED%s: %s\n",
772 ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
773 t->name);
774 else {
88ac4084 775 ERR("%sFAILED%s: exit ok but %s do not match: %s\n",
fa0046ba 776 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
88ac4084 777 matchout ? "loaded modules" : "outputs",
fa0046ba
DR
778 t->name);
779 err = EXIT_FAILURE;
780 }
f429113a 781 } else {
fa0046ba
DR
782 ERR("%sFAILED%s: %s\n",
783 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
784 t->name);
f429113a 785 }
fa0046ba
DR
786 } else {
787 if (err == 0) {
788 if (matchout) {
d7293a16 789 ERR("%sUNEXPECTED PASS%s: exit with 0: %s\n",
658e0471 790 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
fa0046ba
DR
791 t->name);
792 err = EXIT_FAILURE;
d7293a16
LDM
793 } else {
794 ERR("%sUNEXPECTED PASS%s: exit with 0 and outputs do not match: %s\n",
795 ANSI_HIGHLIGHT_RED_ON, ANSI_HIGHLIGHT_OFF,
fa0046ba 796 t->name);
d7293a16
LDM
797 err = EXIT_FAILURE;
798 }
fa0046ba 799 } else {
d7293a16
LDM
800 if (matchout) {
801 LOG("%sEXPECTED FAIL%s: %s\n",
fa0046ba
DR
802 ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
803 t->name);
d7293a16
LDM
804 err = EXIT_SUCCESS;
805 } else {
806 LOG("%sEXPECTED FAIL%s: exit with %d but outputs do not match: %s\n",
807 ANSI_HIGHLIGHT_GREEN_ON, ANSI_HIGHLIGHT_OFF,
808 WEXITSTATUS(err), t->name);
809 err = EXIT_FAILURE;
810 }
3dbb8dea 811 }
fa0046ba 812 }
45481ee2 813
f988e25c
LDM
814exit:
815 LOG("------\n");
45481ee2
LDM
816 return err;
817}
818
f31d49c8
DR
819static int prepend_path(const char *extra)
820{
821 char *oldpath, *newpath;
822 int r;
823
824 if (extra == NULL)
825 return 0;
826
827 oldpath = getenv("PATH");
828 if (oldpath == NULL)
829 return setenv("PATH", extra, 1);
830
831 if (asprintf(&newpath, "%s:%s", extra, oldpath) < 0) {
050db08c 832 ERR("failed to allocate memory to new PATH\n");
f31d49c8
DR
833 return -1;
834 }
835
836 r = setenv("PATH", newpath, 1);
837 free(newpath);
838
839 return r;
840}
841
45481ee2
LDM
842int test_run(const struct test *t)
843{
80f9e023 844 pid_t pid;
3dbb8dea
LDM
845 int fdout[2];
846 int fderr[2];
ed8e93fd 847 int fdmonitor[2];
80f9e023 848
ed2df4e9
LDM
849 if (t->need_spawn && oneshot)
850 test_run_spawned(t);
851
bd4e7340 852 if (t->output.out != NULL) {
3dbb8dea
LDM
853 if (pipe(fdout) != 0) {
854 ERR("could not create out pipe for %s\n", t->name);
855 return EXIT_FAILURE;
856 }
857 }
858
bd4e7340 859 if (t->output.err != NULL) {
3dbb8dea
LDM
860 if (pipe(fderr) != 0) {
861 ERR("could not create err pipe for %s\n", t->name);
862 return EXIT_FAILURE;
863 }
864 }
865
ed8e93fd
LDM
866 if (pipe(fdmonitor) != 0) {
867 ERR("could not create monitor pipe for %s\n", t->name);
868 return EXIT_FAILURE;
869 }
870
f31d49c8
DR
871 if (prepend_path(t->path) < 0) {
872 ERR("failed to prepend '%s' to PATH\n", t->path);
873 return EXIT_FAILURE;
874 }
875
80f9e023
LDM
876 LOG("running %s, in forked context\n", t->name);
877
878 pid = fork();
879 if (pid < 0) {
880 ERR("could not fork(): %m\n");
881 LOG("FAILED: %s\n", t->name);
882 return EXIT_FAILURE;
883 }
884
45481ee2 885 if (pid > 0)
b8e344a6 886 return test_run_parent(t, fdout, fderr, fdmonitor, pid);
80f9e023 887
ed8e93fd 888 return test_run_child(t, fdout, fderr, fdmonitor);
80f9e023 889}