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>
31 #include "testsuite.h"
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";
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' },
46 #define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/"
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" },
58 static void help(void)
60 const struct option
*itr
;
61 const char *itr_short
;
64 "\t%s [options] <test>\n"
65 "Options:\n", basename(progname
));
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
);
72 static void test_list(const struct test
*tests
[])
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
);
81 int test_init(int argc
, char *const argv
[], const struct test
*tests
[])
87 c
= getopt_long(argc
, argv
, options_short
, options
, &idx
);
103 ERR("unexpected getopt_long() value %c\n", c
);
108 if (isatty(STDOUT_FILENO
) == 0) {
109 ANSI_HIGHLIGHT_OFF
= "";
110 ANSI_HIGHLIGHT_RED_ON
= "";
111 ANSI_HIGHLIGHT_GREEN_ON
= "";
117 const struct test
*test_find(const struct test
*tests
[], const char *name
)
121 for (i
= 0; tests
[i
] != NULL
; i
++) {
122 if (strcmp(tests
[i
]->name
, name
) == 0)
129 static int test_spawn_test(const struct test
*t
)
131 const char *const args
[] = { progname
, "-n", t
->name
, NULL
};
133 execv(progname
, (char *const *) args
);
135 ERR("failed to spawn %s for %s: %m\n", progname
, t
->name
);
139 static int test_run_spawned(const struct test
*t
)
141 int err
= t
->func(t
);
147 int test_spawn_prog(const char *prog
, const char *const args
[])
149 execv(prog
, (char *const *) args
);
151 ERR("failed to spawn %s\n", prog
);
152 ERR("did you forget to build tools?\n");
156 static void test_export_environ(const struct test
*t
)
158 char *preload
= NULL
;
159 size_t preloadlen
= 0;
162 unsetenv("LD_PRELOAD");
164 for (i
= 0; i
< _TC_LAST
; i
++) {
165 const char *ldpreload
;
169 if (t
->config
[i
] == NULL
)
172 setenv(env_config
[i
].key
, t
->config
[i
], 1);
174 ldpreload
= env_config
[i
].ldpreload
;
175 ldpreloadlen
= strlen(ldpreload
);
176 tmp
= realloc(preload
, preloadlen
+ 2 + ldpreloadlen
);
178 ERR("oom: test_export_environ()\n");
184 preload
[preloadlen
++] = ' ';
185 memcpy(preload
+ preloadlen
, ldpreload
, ldpreloadlen
);
186 preloadlen
+= ldpreloadlen
;
187 preload
[preloadlen
] = '\0';
191 setenv("LD_PRELOAD", preload
, 1);
196 static inline int test_run_child(const struct test
*t
, int fdout
[2],
199 /* kill child if parent dies */
200 prctl(PR_SET_PDEATHSIG
, SIGTERM
);
202 test_export_environ(t
);
204 /* Close read-fds and redirect std{out,err} to the write-fds */
205 if (t
->output
.stdout
!= NULL
) {
207 if (dup2(fdout
[1], STDOUT_FILENO
) < 0) {
208 ERR("could not redirect stdout to pipe: %m");
213 if (t
->output
.stderr
!= NULL
) {
215 if (dup2(fderr
[1], STDERR_FILENO
) < 0) {
216 ERR("could not redirect stdout to pipe: %m");
222 return test_spawn_test(t
);
224 return test_run_spawned(t
);
227 static inline bool test_run_parent_check_outputs(const struct test
*t
,
228 int fdout
, int fderr
)
230 struct epoll_event ep_outpipe
, ep_errpipe
;
231 int err
, fd_ep
, fd_matchout
= -1, fd_matcherr
= -1;
233 if (t
->output
.stdout
== NULL
&& t
->output
.stderr
== NULL
)
236 fd_ep
= epoll_create1(EPOLL_CLOEXEC
);
238 ERR("could not create epoll fd: %m\n");
242 if (t
->output
.stdout
!= NULL
) {
243 fd_matchout
= open(t
->output
.stdout
, O_RDONLY
);
244 if (fd_matchout
< 0) {
246 ERR("could not open %s for read: %m\n",
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) {
255 ERR("could not add fd to epoll: %m\n");
261 if (t
->output
.stderr
!= NULL
) {
262 fd_matcherr
= open(t
->output
.stderr
, O_RDONLY
);
263 if (fd_matcherr
< 0) {
265 ERR("could not open %s for read: %m\n",
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) {
275 ERR("could not add fd to epoll: %m\n");
281 for (err
= 0; fdout
>= 0 || fderr
>= 0;) {
283 struct epoll_event ev
[4];
285 fdcount
= epoll_wait(fd_ep
, ev
, 4, -1);
290 ERR("could not poll: %m\n");
294 for (i
= 0; i
< fdcount
; i
++) {
295 int *fd
= ev
[i
].data
.ptr
;
297 if (ev
[i
].events
& EPOLLIN
) {
304 * compare the output from child with the one
308 r
= read(*fd
, buf
, sizeof(buf
) - 1);
313 fd_match
= fd_matchout
;
315 fd_match
= fd_matcherr
;
318 int rmatch
= read(fd_match
,
319 bufmatch
+ done
, r
- done
);
327 ERR("could not read match fd %d\n",
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
);
345 } else if (ev
[i
].events
& EPOLLHUP
) {
346 if (epoll_ctl(fd_ep
, EPOLL_CTL_DEL
,
348 ERR("could not remove fd %d from epoll: %m\n",
357 if (fd_matchout
>= 0)
359 if (fd_matcherr
>= 0)
366 static inline int test_run_parent(const struct test
*t
, int fdout
[2],
373 /* Close write-fds */
374 if (t
->output
.stdout
!= NULL
)
376 if (t
->output
.stderr
!= NULL
)
379 matchout
= test_run_parent_check_outputs(t
, fdout
[0], fderr
[0]);
382 * break pipe on the other end: either child already closed or we want
385 if (t
->output
.stdout
!= NULL
)
387 if (t
->output
.stderr
!= NULL
)
393 ERR("error waitpid(): %m\n");
396 } while (!WIFEXITED(err
) && !WIFSIGNALED(err
));
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
));
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
)));
412 LOG("%sPASSED%s: %s\n",
413 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
416 ERR("%sFAILED%s: exit ok but outputs do not match: %s\n",
417 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
422 ERR("%sFAILED%s: %s\n",
423 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
429 int test_run(const struct test
*t
)
435 if (t
->need_spawn
&& oneshot
)
438 if (t
->output
.stdout
!= NULL
) {
439 if (pipe(fdout
) != 0) {
440 ERR("could not create out pipe for %s\n", t
->name
);
445 if (t
->output
.stderr
!= NULL
) {
446 if (pipe(fderr
) != 0) {
447 ERR("could not create err pipe for %s\n", t
->name
);
452 LOG("running %s, in forked context\n", t
->name
);
456 ERR("could not fork(): %m\n");
457 LOG("FAILED: %s\n", t
->name
);
462 return test_run_parent(t
, fdout
, fderr
);
464 return test_run_child(t
, fdout
, fderr
);