2 * Copyright (C) 2012 ProFUSION embedded systems
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.
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.
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/>.
27 #include <sys/epoll.h>
28 #include <sys/prctl.h>
32 #include "libkmod-util.h"
33 #include "testsuite.h"
35 static const char *ANSI_HIGHLIGHT_GREEN_ON
= "\x1B[1;32m";
36 static const char *ANSI_HIGHLIGHT_RED_ON
= "\x1B[1;31m";
37 static const char *ANSI_HIGHLIGHT_OFF
= "\x1B[0m";
39 static const char *progname
;
40 static int oneshot
= 0;
41 static const char options_short
[] = "lhn";
42 static const struct option options
[] = {
43 { "list", no_argument
, 0, 'l' },
44 { "help", no_argument
, 0, 'h' },
48 #define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/"
52 const char *ldpreload
;
53 } env_config
[_TC_LAST
] = {
54 [TC_UNAME_R
] = { S_TC_UNAME_R
, OVERRIDE_LIBDIR
"uname.so" },
55 [TC_ROOTFS
] = { S_TC_ROOTFS
, OVERRIDE_LIBDIR
"path.so" },
56 [TC_INIT_MODULE_RETCODES
] = { S_TC_INIT_MODULE_RETCODES
, OVERRIDE_LIBDIR
"init_module.so" },
57 [TC_DELETE_MODULE_RETCODES
] = { S_TC_DELETE_MODULE_RETCODES
, OVERRIDE_LIBDIR
"delete_module.so" },
60 static void help(void)
62 const struct option
*itr
;
63 const char *itr_short
;
66 "\t%s [options] <test>\n"
67 "Options:\n", basename(progname
));
69 for (itr
= options
, itr_short
= options_short
;
70 itr
->name
!= NULL
; itr
++, itr_short
++)
71 printf("\t-%c, --%s\n", *itr_short
, itr
->name
);
74 static void test_list(const struct test
*tests
[])
78 printf("Available tests:\n");
79 for (i
= 0; tests
[i
] != NULL
; i
++)
80 printf("\t%s, %s\n", tests
[i
]->name
, tests
[i
]->description
);
83 int test_init(int argc
, char *const argv
[], const struct test
*tests
[])
89 c
= getopt_long(argc
, argv
, options_short
, options
, &idx
);
105 ERR("unexpected getopt_long() value %c\n", c
);
110 if (isatty(STDOUT_FILENO
) == 0) {
111 ANSI_HIGHLIGHT_OFF
= "";
112 ANSI_HIGHLIGHT_RED_ON
= "";
113 ANSI_HIGHLIGHT_GREEN_ON
= "";
119 const struct test
*test_find(const struct test
*tests
[], const char *name
)
123 for (i
= 0; tests
[i
] != NULL
; i
++) {
124 if (strcmp(tests
[i
]->name
, name
) == 0)
131 static int test_spawn_test(const struct test
*t
)
133 const char *const args
[] = { progname
, "-n", t
->name
, NULL
};
135 execv(progname
, (char *const *) args
);
137 ERR("failed to spawn %s for %s: %m\n", progname
, t
->name
);
141 static int test_run_spawned(const struct test
*t
)
143 int err
= t
->func(t
);
149 int test_spawn_prog(const char *prog
, const char *const args
[])
151 execv(prog
, (char *const *) args
);
153 ERR("failed to spawn %s\n", prog
);
154 ERR("did you forget to build tools?\n");
158 static void test_export_environ(const struct test
*t
)
160 char *preload
= NULL
;
161 size_t preloadlen
= 0;
164 unsetenv("LD_PRELOAD");
166 for (i
= 0; i
< _TC_LAST
; i
++) {
167 const char *ldpreload
;
171 if (t
->config
[i
] == NULL
)
174 setenv(env_config
[i
].key
, t
->config
[i
], 1);
176 ldpreload
= env_config
[i
].ldpreload
;
177 ldpreloadlen
= strlen(ldpreload
);
178 tmp
= realloc(preload
, preloadlen
+ 2 + ldpreloadlen
);
180 ERR("oom: test_export_environ()\n");
186 preload
[preloadlen
++] = ' ';
187 memcpy(preload
+ preloadlen
, ldpreload
, ldpreloadlen
);
188 preloadlen
+= ldpreloadlen
;
189 preload
[preloadlen
] = '\0';
193 setenv("LD_PRELOAD", preload
, 1);
198 static inline int test_run_child(const struct test
*t
, int fdout
[2],
199 int fderr
[2], int fdmonitor
[2])
201 /* kill child if parent dies */
202 prctl(PR_SET_PDEATHSIG
, SIGTERM
);
204 test_export_environ(t
);
206 /* Close read-fds and redirect std{out,err} to the write-fds */
207 if (t
->output
.stdout
!= NULL
) {
209 if (dup2(fdout
[1], STDOUT_FILENO
) < 0) {
210 ERR("could not redirect stdout to pipe: %m\n");
215 if (t
->output
.stderr
!= NULL
) {
217 if (dup2(fderr
[1], STDERR_FILENO
) < 0) {
218 ERR("could not redirect stdout to pipe: %m\n");
225 if (t
->config
[TC_ROOTFS
] != NULL
) {
226 const char *stamp
= TESTSUITE_ROOTFS
"../stamp-rootfs";
227 const char *rootfs
= t
->config
[TC_ROOTFS
];
228 struct stat rootfsst
, stampst
;
230 if (stat(stamp
, &stampst
) != 0) {
231 ERR("could not stat %s\n - %m", stamp
);
235 if (stat(rootfs
, &rootfsst
) != 0) {
236 ERR("could not stat %s\n - %m", rootfs
);
240 if (stat_mstamp(&rootfsst
) > stat_mstamp(&stampst
)) {
241 ERR("rootfs %s is dirty, please run 'make rootfs' before runnning this test\n",
248 return test_spawn_test(t
);
250 return test_run_spawned(t
);
253 static inline bool test_run_parent_check_outputs(const struct test
*t
,
254 int fdout
, int fderr
, int fdmonitor
)
256 struct epoll_event ep_outpipe
, ep_errpipe
, ep_monitor
;
257 int err
, fd_ep
, fd_matchout
= -1, fd_matcherr
= -1;
259 fd_ep
= epoll_create1(EPOLL_CLOEXEC
);
261 ERR("could not create epoll fd: %m\n");
265 if (t
->output
.stdout
!= NULL
) {
266 fd_matchout
= open(t
->output
.stdout
, O_RDONLY
);
267 if (fd_matchout
< 0) {
269 ERR("could not open %s for read: %m\n",
273 memset(&ep_outpipe
, 0, sizeof(struct epoll_event
));
274 ep_outpipe
.events
= EPOLLIN
;
275 ep_outpipe
.data
.ptr
= &fdout
;
276 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fdout
, &ep_outpipe
) < 0) {
278 ERR("could not add fd to epoll: %m\n");
284 if (t
->output
.stderr
!= NULL
) {
285 fd_matcherr
= open(t
->output
.stderr
, O_RDONLY
);
286 if (fd_matcherr
< 0) {
288 ERR("could not open %s for read: %m\n",
293 memset(&ep_errpipe
, 0, sizeof(struct epoll_event
));
294 ep_errpipe
.events
= EPOLLIN
;
295 ep_errpipe
.data
.ptr
= &fderr
;
296 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fderr
, &ep_errpipe
) < 0) {
298 ERR("could not add fd to epoll: %m\n");
304 memset(&ep_monitor
, 0, sizeof(struct epoll_event
));
305 ep_monitor
.events
= EPOLLHUP
;
306 ep_monitor
.data
.ptr
= &fdmonitor
;
307 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fdmonitor
, &ep_monitor
) < 0) {
309 ERR("could not add monitor fd to epoll: %m\n");
313 for (err
= 0; fdmonitor
>= 0 || fdout
>= 0 || fderr
>= 0;) {
315 struct epoll_event ev
[4];
317 fdcount
= epoll_wait(fd_ep
, ev
, 4, -1);
322 ERR("could not poll: %m\n");
326 for (i
= 0; i
< fdcount
; i
++) {
327 int *fd
= ev
[i
].data
.ptr
;
329 if (ev
[i
].events
& EPOLLIN
) {
336 * compare the output from child with the one
340 r
= read(*fd
, buf
, sizeof(buf
) - 1);
345 fd_match
= fd_matchout
;
346 else if (*fd
== fderr
)
347 fd_match
= fd_matcherr
;
349 ERR("Unexpected activity on monitor pipe\n");
355 int rmatch
= read(fd_match
,
356 bufmatch
+ done
, r
- done
);
364 ERR("could not read match fd %d\n",
374 if (strcmp(buf
, bufmatch
) != 0) {
375 ERR("Outputs do not match on %s:\n",
376 fd_match
== fd_matchout
? "stdout" : "stderr");
377 ERR("correct:\n%s\n", bufmatch
);
378 ERR("wrong:\n%s\n", buf
);
382 } else if (ev
[i
].events
& EPOLLHUP
) {
383 if (epoll_ctl(fd_ep
, EPOLL_CTL_DEL
,
385 ERR("could not remove fd %d from epoll: %m\n",
394 if (fd_matchout
>= 0)
396 if (fd_matcherr
>= 0)
403 static inline int test_run_parent(const struct test
*t
, int fdout
[2],
404 int fderr
[2], int fdmonitor
[2])
410 /* Close write-fds */
411 if (t
->output
.stdout
!= NULL
)
413 if (t
->output
.stderr
!= NULL
)
417 matchout
= test_run_parent_check_outputs(t
, fdout
[0], fderr
[0],
421 * break pipe on the other end: either child already closed or we want
424 if (t
->output
.stdout
!= NULL
)
426 if (t
->output
.stderr
!= NULL
)
433 ERR("error waitpid(): %m\n");
436 } while (!WIFEXITED(err
) && !WIFSIGNALED(err
));
438 if (WIFEXITED(err
)) {
439 if (WEXITSTATUS(err
) != 0)
440 ERR("'%s' [%u] exited with return code %d\n",
441 t
->name
, pid
, WEXITSTATUS(err
));
443 LOG("'%s' [%u] exited with return code %d\n",
444 t
->name
, pid
, WEXITSTATUS(err
));
445 } else if (WIFSIGNALED(err
)) {
446 ERR("'%s' [%u] terminated by signal %d (%s)\n", t
->name
, pid
,
447 WTERMSIG(err
), strsignal(WTERMSIG(err
)));
450 if (t
->expected_fail
== false) {
453 LOG("%sPASSED%s: %s\n",
454 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
457 ERR("%sFAILED%s: exit ok but outputs do not match: %s\n",
458 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
463 ERR("%sFAILED%s: %s\n",
464 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
469 LOG("%sUNEXPECTED PASS%s: %s\n",
470 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
474 LOG("%sEXPECTED FAIL%s: exit ok but outputs do not match: %s\n",
475 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
478 ERR("%sEXPECTED FAIL%s: %s\n",
479 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
488 static int prepend_path(const char *extra
)
490 char *oldpath
, *newpath
;
496 oldpath
= getenv("PATH");
498 return setenv("PATH", extra
, 1);
500 if (asprintf(&newpath
, "%s:%s", extra
, oldpath
) < 0) {
501 ERR("failed to allocate memory to new PATH\n");
505 r
= setenv("PATH", newpath
, 1);
511 int test_run(const struct test
*t
)
518 if (t
->need_spawn
&& oneshot
)
521 if (t
->output
.stdout
!= NULL
) {
522 if (pipe(fdout
) != 0) {
523 ERR("could not create out pipe for %s\n", t
->name
);
528 if (t
->output
.stderr
!= NULL
) {
529 if (pipe(fderr
) != 0) {
530 ERR("could not create err pipe for %s\n", t
->name
);
535 if (pipe(fdmonitor
) != 0) {
536 ERR("could not create monitor pipe for %s\n", t
->name
);
540 if (prepend_path(t
->path
) < 0) {
541 ERR("failed to prepend '%s' to PATH\n", t
->path
);
545 LOG("running %s, in forked context\n", t
->name
);
549 ERR("could not fork(): %m\n");
550 LOG("FAILED: %s\n", t
->name
);
555 return test_run_parent(t
, fdout
, fderr
, fdmonitor
);
557 return test_run_child(t
, fdout
, fderr
, fdmonitor
);