]>
git.ipfire.org Git - thirdparty/kmod.git/blob - testsuite/testsuite.c
10 #include <sys/epoll.h>
11 #include <sys/prctl.h>
14 #include "testsuite.h"
16 static const char *ANSI_HIGHLIGHT_GREEN_ON
= "\x1B[1;32m";
17 static const char *ANSI_HIGHLIGHT_RED_ON
= "\x1B[1;31m";
18 static const char *ANSI_HIGHLIGHT_OFF
= "\x1B[0m";
20 static const char *progname
;
21 static int oneshot
= 0;
22 static const char options_short
[] = "lhn";
23 static const struct option options
[] = {
24 { "list", no_argument
, 0, 'l' },
25 { "help", no_argument
, 0, 'h' },
31 const char *ldpreload
;
32 } env_config
[_TC_LAST
] = {
33 [TC_UNAME_R
] = { S_TC_UNAME_R
, ABS_TOP_BUILDDIR
"/testsuite/uname.so" },
34 [TC_ROOTFS
] = { S_TC_ROOTFS
, ABS_TOP_BUILDDIR
"/testsuite/path.so" },
37 static void help(void)
39 const struct option
*itr
;
40 const char *itr_short
;
43 "\t%s [options] <test>\n"
44 "Options:\n", basename(progname
));
46 for (itr
= options
, itr_short
= options_short
;
47 itr
->name
!= NULL
; itr
++, itr_short
++)
48 printf("\t-%c, --%s\n", *itr_short
, itr
->name
);
51 static void test_list(const struct test
*tests
[])
55 printf("Available tests:\n");
56 for (i
= 0; tests
[i
] != NULL
; i
++)
57 printf("\t%s, %s\n", tests
[i
]->name
, tests
[i
]->description
);
60 int test_init(int argc
, char *const argv
[], const struct test
*tests
[])
66 c
= getopt_long(argc
, argv
, options_short
, options
, &idx
);
82 ERR("unexpected getopt_long() value %c\n", c
);
87 if (isatty(STDOUT_FILENO
) == 0) {
88 ANSI_HIGHLIGHT_OFF
= "";
89 ANSI_HIGHLIGHT_RED_ON
= "";
90 ANSI_HIGHLIGHT_GREEN_ON
= "";
96 const struct test
*test_find(const struct test
*tests
[], const char *name
)
100 for (i
= 0; tests
[i
] != NULL
; i
++) {
101 if (strcmp(tests
[i
]->name
, name
) == 0)
108 static int test_spawn_test(const struct test
*t
)
110 const char *const args
[] = { progname
, "-n", t
->name
, NULL
};
112 execv(progname
, (char *const *) args
);
114 ERR("failed to spawn %s for %s: %m\n", progname
, t
->name
);
118 static int test_run_spawned(const struct test
*t
)
120 int err
= t
->func(t
);
126 int test_spawn_prog(const char *prog
, const char *const args
[])
128 execv(prog
, (char *const *) args
);
130 ERR("failed to spawn %s\n", prog
);
131 ERR("did you forget to build tools?\n");
135 static void test_export_environ(const struct test
*t
)
137 char *preload
= NULL
;
138 size_t preloadlen
= 0;
141 unsetenv("LD_PRELOAD");
143 for (i
= 0; i
< _TC_LAST
; i
++) {
144 const char *ldpreload
;
148 if (t
->config
[i
] == NULL
)
151 setenv(env_config
[i
].key
, t
->config
[i
], 1);
153 ldpreload
= env_config
[i
].ldpreload
;
154 ldpreloadlen
= strlen(ldpreload
);
155 tmp
= realloc(preload
, preloadlen
+ 2 + ldpreloadlen
);
157 ERR("oom: test_export_environ()\n");
163 preload
[preloadlen
++] = ' ';
164 memcpy(preload
+ preloadlen
, ldpreload
, ldpreloadlen
);
165 preloadlen
+= ldpreloadlen
;
166 preload
[preloadlen
] = '\0';
170 setenv("LD_PRELOAD", preload
, 1);
175 static inline int test_run_child(const struct test
*t
, int fdout
[2],
178 /* kill child if parent dies */
179 prctl(PR_SET_PDEATHSIG
, SIGTERM
);
181 test_export_environ(t
);
183 /* Close read-fds and redirect std{out,err} to the write-fds */
184 if (t
->output
.stdout
!= NULL
) {
186 if (dup2(fdout
[1], STDOUT_FILENO
) < 0) {
187 ERR("could not redirect stdout to pipe: %m");
192 if (t
->output
.stderr
!= NULL
) {
194 if (dup2(fderr
[1], STDERR_FILENO
) < 0) {
195 ERR("could not redirect stdout to pipe: %m");
201 return test_spawn_test(t
);
203 return test_run_spawned(t
);
206 static inline bool test_run_parent_check_outputs(const struct test
*t
,
207 int fdout
, int fderr
)
209 struct epoll_event ep_outpipe
, ep_errpipe
;
210 int err
, fd_ep
, fd_matchout
= -1, fd_matcherr
= -1;
212 if (t
->output
.stdout
== NULL
&& t
->output
.stderr
== NULL
)
215 fd_ep
= epoll_create1(EPOLL_CLOEXEC
);
217 ERR("could not create epoll fd: %m\n");
221 if (t
->output
.stdout
!= NULL
) {
222 fd_matchout
= open(t
->output
.stdout
, O_RDONLY
);
223 if (fd_matchout
< 0) {
225 ERR("could not open %s for read: %m\n",
229 memset(&ep_outpipe
, 0, sizeof(struct epoll_event
));
230 ep_outpipe
.events
= EPOLLIN
;
231 ep_outpipe
.data
.ptr
= &fdout
;
232 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fdout
, &ep_outpipe
) < 0) {
234 ERR("could not add fd to epoll: %m\n");
240 if (t
->output
.stderr
!= NULL
) {
241 fd_matcherr
= open(t
->output
.stderr
, O_RDONLY
);
242 if (fd_matcherr
< 0) {
244 ERR("could not open %s for read: %m\n",
249 memset(&ep_errpipe
, 0, sizeof(struct epoll_event
));
250 ep_errpipe
.events
= EPOLLIN
;
251 ep_errpipe
.data
.ptr
= &fderr
;
252 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fderr
, &ep_errpipe
) < 0) {
254 ERR("could not add fd to epoll: %m\n");
260 for (err
= 0; fdout
>= 0 || fderr
>= 0;) {
262 struct epoll_event ev
[4];
264 fdcount
= epoll_wait(fd_ep
, ev
, 4, -1);
269 ERR("could not poll: %m\n");
273 for (i
= 0; i
< fdcount
; i
++) {
274 int *fd
= ev
[i
].data
.ptr
;
276 if (ev
[i
].events
& EPOLLIN
) {
283 * compare the output from child with the one
287 r
= read(*fd
, buf
, sizeof(buf
) - 1);
292 fd_match
= fd_matchout
;
294 fd_match
= fd_matcherr
;
297 int rmatch
= read(fd_match
,
298 bufmatch
+ done
, r
- done
);
306 ERR("could not read match fd %d\n",
316 if (strcmp(buf
, bufmatch
) != 0) {
317 ERR("Outputs do not match on %s:\n",
318 fd_match
== fd_matchout
? "stdout" : "stderr");
319 ERR("correct:\n%s\n", bufmatch
);
320 ERR("wrong:\n%s\n", buf
);
324 } else if (ev
[i
].events
& EPOLLHUP
) {
325 if (epoll_ctl(fd_ep
, EPOLL_CTL_DEL
,
327 ERR("could not remove fd %d from epoll: %m\n",
336 if (fd_matchout
>= 0)
338 if (fd_matcherr
>= 0)
345 static inline int test_run_parent(const struct test
*t
, int fdout
[2],
352 /* Close write-fds */
353 if (t
->output
.stdout
!= NULL
)
355 if (t
->output
.stderr
!= NULL
)
358 matchout
= test_run_parent_check_outputs(t
, fdout
[0], fderr
[0]);
361 * break pipe on the other end: either child already closed or we want
364 if (t
->output
.stdout
!= NULL
)
366 if (t
->output
.stderr
!= NULL
)
372 ERR("error waitpid(): %m\n");
375 } while (!WIFEXITED(err
) && !WIFSIGNALED(err
));
377 if (WIFEXITED(err
)) {
378 if (WEXITSTATUS(err
) != 0)
379 ERR("'%s' [%u] exited with return code %d\n",
380 t
->name
, pid
, WEXITSTATUS(err
));
382 LOG("'%s' [%u] exited with return code %d\n",
383 t
->name
, pid
, WEXITSTATUS(err
));
384 } else if (WIFSIGNALED(err
)) {
385 ERR("'%s' [%u] terminated by signal %d (%s)\n", t
->name
, pid
,
386 WTERMSIG(err
), strsignal(WTERMSIG(err
)));
391 LOG("%sPASSED%s: %s\n",
392 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
395 ERR("%sFAILED%s: exit ok but outputs do not match: %s\n",
396 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
401 ERR("%sFAILED%s: %s\n",
402 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
408 int test_run(const struct test
*t
)
414 if (t
->need_spawn
&& oneshot
)
417 if (t
->output
.stdout
!= NULL
) {
418 if (pipe(fdout
) != 0) {
419 ERR("could not create out pipe for %s\n", t
->name
);
424 if (t
->output
.stderr
!= NULL
) {
425 if (pipe(fderr
) != 0) {
426 ERR("could not create err pipe for %s\n", t
->name
);
431 LOG("running %s, in forked context\n", t
->name
);
435 ERR("could not fork(): %m\n");
436 LOG("FAILED: %s\n", t
->name
);
441 return test_run_parent(t
, fdout
, fderr
);
443 return test_run_child(t
, fdout
, fderr
);