2 * Copyright (C) 2012-2013 ProFUSION embedded systems
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.
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 GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
29 #include <sys/epoll.h>
30 #include <sys/prctl.h>
34 #include <shared/util.h>
36 #include "testsuite.h"
38 static const char *ANSI_HIGHLIGHT_GREEN_ON
= "\x1B[1;32m";
39 static const char *ANSI_HIGHLIGHT_RED_ON
= "\x1B[1;31m";
40 static const char *ANSI_HIGHLIGHT_OFF
= "\x1B[0m";
42 static const char *progname
;
43 static int oneshot
= 0;
44 static const char options_short
[] = "lhn";
45 static const struct option options
[] = {
46 { "list", no_argument
, 0, 'l' },
47 { "help", no_argument
, 0, 'h' },
51 #define OVERRIDE_LIBDIR ABS_TOP_BUILDDIR "/testsuite/.libs/"
55 const char *ldpreload
;
56 } env_config
[_TC_LAST
] = {
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" },
60 [TC_DELETE_MODULE_RETCODES
] = { S_TC_DELETE_MODULE_RETCODES
, OVERRIDE_LIBDIR
"delete_module.so" },
63 #define USEC_PER_SEC 1000000ULL
64 #define USEC_PER_MSEC 1000ULL
65 #define TEST_TIMEOUT_USEC 2 * USEC_PER_SEC
66 static unsigned long long now_usec(void)
70 if (clock_gettime(CLOCK_MONOTONIC
, &ts
) != 0)
76 static void help(void)
78 const struct option
*itr
;
79 const char *itr_short
;
82 "\t%s [options] <test>\n"
83 "Options:\n", basename(progname
));
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
);
90 static void test_list(const struct test
*start
, const struct test
*stop
)
94 printf("Available tests:\n");
95 for (t
= start
; t
< stop
; t
++)
96 printf("\t%s, %s\n", t
->name
, t
->description
);
99 int test_init(const struct test
*start
, const struct test
*stop
,
100 int argc
, char *const argv
[])
106 c
= getopt_long(argc
, argv
, options_short
, options
, &idx
);
111 test_list(start
, stop
);
122 ERR("unexpected getopt_long() value %c\n", c
);
127 if (isatty(STDOUT_FILENO
) == 0) {
128 ANSI_HIGHLIGHT_OFF
= "";
129 ANSI_HIGHLIGHT_RED_ON
= "";
130 ANSI_HIGHLIGHT_GREEN_ON
= "";
136 const struct test
*test_find(const struct test
*start
,
137 const struct test
*stop
, const char *name
)
139 const struct test
*t
;
141 for (t
= start
; t
< stop
; t
++) {
142 if (streq(t
->name
, name
))
149 static int test_spawn_test(const struct test
*t
)
151 const char *const args
[] = { progname
, "-n", t
->name
, NULL
};
153 execv(progname
, (char *const *) args
);
155 ERR("failed to spawn %s for %s: %m\n", progname
, t
->name
);
159 static int test_run_spawned(const struct test
*t
)
161 int err
= t
->func(t
);
167 int test_spawn_prog(const char *prog
, const char *const args
[])
169 execv(prog
, (char *const *) args
);
171 ERR("failed to spawn %s\n", prog
);
172 ERR("did you forget to build tools?\n");
176 static void test_export_environ(const struct test
*t
)
178 char *preload
= NULL
;
179 size_t preloadlen
= 0;
181 const struct keyval
*env
;
183 unsetenv("LD_PRELOAD");
185 for (i
= 0; i
< _TC_LAST
; i
++) {
186 const char *ldpreload
;
190 if (t
->config
[i
] == NULL
)
193 setenv(env_config
[i
].key
, t
->config
[i
], 1);
195 ldpreload
= env_config
[i
].ldpreload
;
196 ldpreloadlen
= strlen(ldpreload
);
197 tmp
= realloc(preload
, preloadlen
+ 2 + ldpreloadlen
);
199 ERR("oom: test_export_environ()\n");
205 preload
[preloadlen
++] = ' ';
206 memcpy(preload
+ preloadlen
, ldpreload
, ldpreloadlen
);
207 preloadlen
+= ldpreloadlen
;
208 preload
[preloadlen
] = '\0';
212 setenv("LD_PRELOAD", preload
, 1);
216 for (env
= t
->env_vars
; env
&& env
->key
; env
++)
217 setenv(env
->key
, env
->val
, 1);
220 static inline int test_run_child(const struct test
*t
, int fdout
[2],
221 int fderr
[2], int fdmonitor
[2])
223 /* kill child if parent dies */
224 prctl(PR_SET_PDEATHSIG
, SIGTERM
);
226 test_export_environ(t
);
228 /* Close read-fds and redirect std{out,err} to the write-fds */
229 if (t
->output
.out
!= NULL
) {
231 if (dup2(fdout
[1], STDOUT_FILENO
) < 0) {
232 ERR("could not redirect stdout to pipe: %m\n");
237 if (t
->output
.err
!= NULL
) {
239 if (dup2(fderr
[1], STDERR_FILENO
) < 0) {
240 ERR("could not redirect stderr to pipe: %m\n");
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
;
252 if (stat(stamp
, &stampst
) != 0) {
253 ERR("could not stat %s\n - %m", stamp
);
257 if (stat(rootfs
, &rootfsst
) != 0) {
258 ERR("could not stat %s\n - %m", rootfs
);
262 if (stat_mstamp(&rootfsst
) > stat_mstamp(&stampst
)) {
263 ERR("rootfs %s is dirty, please run 'make rootfs' before runnning this test\n",
270 return test_spawn_test(t
);
272 return test_run_spawned(t
);
275 static int check_activity(int fd
, bool activity
, const char *path
,
280 /* not monitoring or monitoring and it has activity */
281 if (fd
< 0 || activity
)
284 /* monitoring, there was no activity and size matches */
285 if (stat(path
, &st
) == 0 && st
.st_size
== 0)
288 ERR("Expecting output on %s, but test didn't produce any\n", stream
);
293 static inline bool test_run_parent_check_outputs(const struct test
*t
,
294 int fdout
, int fderr
, int fdmonitor
, pid_t child
)
296 struct epoll_event ep_outpipe
, ep_errpipe
, ep_monitor
;
297 int err
, fd_ep
, fd_matchout
= -1, fd_matcherr
= -1;
298 bool fd_activityout
= false, fd_activityerr
= false;
299 unsigned long long end_usec
, start_usec
;
301 fd_ep
= epoll_create1(EPOLL_CLOEXEC
);
303 ERR("could not create epoll fd: %m\n");
307 if (t
->output
.out
!= NULL
) {
308 fd_matchout
= open(t
->output
.out
, O_RDONLY
);
309 if (fd_matchout
< 0) {
311 ERR("could not open %s for read: %m\n",
315 memset(&ep_outpipe
, 0, sizeof(struct epoll_event
));
316 ep_outpipe
.events
= EPOLLIN
;
317 ep_outpipe
.data
.ptr
= &fdout
;
318 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fdout
, &ep_outpipe
) < 0) {
320 ERR("could not add fd to epoll: %m\n");
326 if (t
->output
.err
!= NULL
) {
327 fd_matcherr
= open(t
->output
.err
, O_RDONLY
);
328 if (fd_matcherr
< 0) {
330 ERR("could not open %s for read: %m\n",
335 memset(&ep_errpipe
, 0, sizeof(struct epoll_event
));
336 ep_errpipe
.events
= EPOLLIN
;
337 ep_errpipe
.data
.ptr
= &fderr
;
338 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fderr
, &ep_errpipe
) < 0) {
340 ERR("could not add fd to epoll: %m\n");
346 memset(&ep_monitor
, 0, sizeof(struct epoll_event
));
347 ep_monitor
.events
= EPOLLHUP
;
348 ep_monitor
.data
.ptr
= &fdmonitor
;
349 if (epoll_ctl(fd_ep
, EPOLL_CTL_ADD
, fdmonitor
, &ep_monitor
) < 0) {
351 ERR("could not add monitor fd to epoll: %m\n");
355 start_usec
= now_usec();
356 end_usec
= start_usec
+ TEST_TIMEOUT_USEC
;
358 for (err
= 0; fdmonitor
>= 0 || fdout
>= 0 || fderr
>= 0;) {
359 int fdcount
, i
, timeout
;
360 struct epoll_event ev
[4];
361 unsigned long long curr_usec
= now_usec();
363 if (curr_usec
> end_usec
)
366 timeout
= (end_usec
- curr_usec
) / USEC_PER_MSEC
;
367 fdcount
= epoll_wait(fd_ep
, ev
, 4, timeout
);
372 ERR("could not poll: %m\n");
376 for (i
= 0; i
< fdcount
; i
++) {
377 int *fd
= ev
[i
].data
.ptr
;
379 if (ev
[i
].events
& EPOLLIN
) {
386 * compare the output from child with the one
390 r
= read(*fd
, buf
, sizeof(buf
) - 1);
395 fd_match
= fd_matchout
;
396 fd_activityout
= true;
397 } else if (*fd
== fderr
) {
398 fd_match
= fd_matcherr
;
399 fd_activityerr
= true;
401 ERR("Unexpected activity on monitor pipe\n");
407 int rmatch
= read(fd_match
,
408 bufmatch
+ done
, r
- done
);
416 ERR("could not read match fd %d\n",
427 if (t
->print_outputs
)
429 fd_match
== fd_matchout
? "STDOUT:" : "STDERR:",
432 if (!streq(buf
, bufmatch
)) {
433 ERR("Outputs do not match on %s:\n",
434 fd_match
== fd_matchout
? "STDOUT" : "STDERR");
435 ERR("correct:\n%s\n", bufmatch
);
436 ERR("wrong:\n%s\n", buf
);
440 } else if (ev
[i
].events
& EPOLLHUP
) {
441 if (epoll_ctl(fd_ep
, EPOLL_CTL_DEL
,
443 ERR("could not remove fd %d from epoll: %m\n",
451 err
= check_activity(fd_matchout
, fd_activityout
, t
->output
.out
, "stdout");
452 err
|= check_activity(fd_matcherr
, fd_activityerr
, t
->output
.err
, "stderr");
454 if (err
== 0 && fdmonitor
>= 0) {
456 ERR("Test '%s' timed out, killing %d\n", t
->name
, child
);
457 kill(child
, SIGKILL
);
461 if (fd_matchout
>= 0)
463 if (fd_matcherr
>= 0)
470 static inline int safe_read(int fd
, void *buf
, size_t count
)
475 r
= read(fd
, buf
, count
);
476 if (r
== -1 && errno
== EINTR
)
484 static bool check_generated_files(const struct test
*t
)
486 const struct keyval
*k
;
488 /* This is not meant to be a diff replacement, just stupidly check if
489 * the files match. Bear in mind they can be binary files */
490 for (k
= t
->output
.files
; k
&& k
->key
; k
++) {
491 struct stat sta
, stb
;
492 int fda
= -1, fdb
= -1;
496 fda
= open(k
->key
, O_RDONLY
);
498 ERR("could not open %s\n - %m\n", k
->key
);
502 fdb
= open(k
->val
, O_RDONLY
);
504 ERR("could not open %s\n - %m\n", k
->val
);
508 if (fstat(fda
, &sta
) != 0) {
509 ERR("could not fstat %d %s\n - %m\n", fda
, k
->key
);
513 if (fstat(fdb
, &stb
) != 0) {
514 ERR("could not fstat %d %s\n - %m\n", fdb
, k
->key
);
518 if (sta
.st_size
!= stb
.st_size
) {
519 ERR("sizes do not match %s %s\n", k
->key
, k
->val
);
526 r
= safe_read(fda
, bufa
, sizeof(bufa
));
531 /* size is already checked, go to next file */
534 for (done
= 0; done
< r
;) {
535 int r2
= safe_read(fdb
, bufb
+ done
, r
- done
);
543 if (memcmp(bufa
, bufb
, r
) != 0)
564 static int cmp_modnames(const void *m1
, const void *m2
)
566 const char *s1
= *(char *const *)m1
;
567 const char *s2
= *(char *const *)m2
;
570 for (i
= 0; s1
[i
] || s2
[i
]; i
++) {
571 char c1
= s1
[i
], c2
= s2
[i
];
583 * Store the expected module names in buf and return a list of pointers to
586 static const char **read_expected_modules(const struct test
*t
,
587 char **buf
, int *count
)
594 if (t
->modules_loaded
[0] == '\0') {
599 *buf
= strdup(t
->modules_loaded
);
605 for (p
= *buf
; *p
; p
++)
608 res
= malloc(sizeof(char *) * len
);
618 for (p
= *buf
; i
< len
; p
++)
627 static char **read_loaded_modules(const struct test
*t
, char **buf
, int *count
)
629 char dirname
[PATH_MAX
];
631 struct dirent
*dirent
;
636 const char *rootfs
= t
->config
[TC_ROOTFS
] ? t
->config
[TC_ROOTFS
] : "";
638 /* Store the entries in /sys/module to res */
639 if (snprintf(dirname
, sizeof(dirname
), "%s/sys/module", rootfs
)
640 >= (int)sizeof(dirname
)) {
641 ERR("rootfs path too long: %s\n", rootfs
);
646 dir
= opendir(dirname
);
647 /* not an error, simply return empty list */
653 while ((dirent
= readdir(dir
))) {
654 if (dirent
->d_name
[0] == '.')
657 bufsz
+= strlen(dirent
->d_name
) + 1;
659 res
= malloc(sizeof(char *) * len
);
665 *buf
= malloc(bufsz
);
676 while ((dirent
= readdir(dir
))) {
679 if (dirent
->d_name
[0] == '.')
681 size
= strlen(dirent
->d_name
) + 1;
682 memcpy(p
, dirent
->d_name
, size
);
693 static int check_loaded_modules(const struct test
*t
)
701 a1
= read_expected_modules(t
, &buf1
, &l1
);
704 a2
= read_loaded_modules(t
, &buf2
, &l2
);
707 qsort(a1
, l1
, sizeof(char *), cmp_modnames
);
708 qsort(a2
, l2
, sizeof(char *), cmp_modnames
);
711 while (i1
< l1
|| i2
< l2
) {
719 cmp
= cmp_modnames(&a1
[i1
], &a2
[i2
]);
723 } else if (cmp
< 0) {
725 ERR("module %s not loaded\n", a1
[i1
]);
729 ERR("module %s is loaded but should not be \n", a2
[i2
]);
741 static inline int test_run_parent(const struct test
*t
, int fdout
[2],
742 int fderr
[2], int fdmonitor
[2], pid_t child
)
746 bool matchout
, match_modules
;
748 /* Close write-fds */
749 if (t
->output
.out
!= NULL
)
751 if (t
->output
.err
!= NULL
)
755 matchout
= test_run_parent_check_outputs(t
, fdout
[0], fderr
[0],
756 fdmonitor
[0], child
);
759 * break pipe on the other end: either child already closed or we want
762 if (t
->output
.out
!= NULL
)
764 if (t
->output
.err
!= NULL
)
771 ERR("error waitpid(): %m\n");
775 } while (!WIFEXITED(err
) && !WIFSIGNALED(err
));
777 if (WIFEXITED(err
)) {
778 if (WEXITSTATUS(err
) != 0)
779 ERR("'%s' [%u] exited with return code %d\n",
780 t
->name
, pid
, WEXITSTATUS(err
));
782 LOG("'%s' [%u] exited with return code %d\n",
783 t
->name
, pid
, WEXITSTATUS(err
));
784 } else if (WIFSIGNALED(err
)) {
785 ERR("'%s' [%u] terminated by signal %d (%s)\n", t
->name
, pid
,
786 WTERMSIG(err
), strsignal(WTERMSIG(err
)));
787 err
= t
->expected_fail
? EXIT_SUCCESS
: EXIT_FAILURE
;
792 matchout
= check_generated_files(t
);
793 if (t
->modules_loaded
)
794 match_modules
= check_loaded_modules(t
);
796 match_modules
= true;
798 if (t
->expected_fail
== false) {
800 if (matchout
&& match_modules
)
801 LOG("%sPASSED%s: %s\n",
802 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
805 ERR("%sFAILED%s: exit ok but %s do not match: %s\n",
806 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
807 matchout
? "loaded modules" : "outputs",
812 ERR("%sFAILED%s: %s\n",
813 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
819 ERR("%sUNEXPECTED PASS%s: exit with 0: %s\n",
820 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
824 ERR("%sUNEXPECTED PASS%s: exit with 0 and outputs do not match: %s\n",
825 ANSI_HIGHLIGHT_RED_ON
, ANSI_HIGHLIGHT_OFF
,
831 LOG("%sEXPECTED FAIL%s: %s\n",
832 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
836 LOG("%sEXPECTED FAIL%s: exit with %d but outputs do not match: %s\n",
837 ANSI_HIGHLIGHT_GREEN_ON
, ANSI_HIGHLIGHT_OFF
,
838 WEXITSTATUS(err
), t
->name
);
849 static int prepend_path(const char *extra
)
851 char *oldpath
, *newpath
;
857 oldpath
= getenv("PATH");
859 return setenv("PATH", extra
, 1);
861 if (asprintf(&newpath
, "%s:%s", extra
, oldpath
) < 0) {
862 ERR("failed to allocate memory to new PATH\n");
866 r
= setenv("PATH", newpath
, 1);
872 int test_run(const struct test
*t
)
879 if (t
->need_spawn
&& oneshot
)
882 if (t
->output
.out
!= NULL
) {
883 if (pipe(fdout
) != 0) {
884 ERR("could not create out pipe for %s\n", t
->name
);
889 if (t
->output
.err
!= NULL
) {
890 if (pipe(fderr
) != 0) {
891 ERR("could not create err pipe for %s\n", t
->name
);
896 if (pipe(fdmonitor
) != 0) {
897 ERR("could not create monitor pipe for %s\n", t
->name
);
901 if (prepend_path(t
->path
) < 0) {
902 ERR("failed to prepend '%s' to PATH\n", t
->path
);
906 LOG("running %s, in forked context\n", t
->name
);
910 ERR("could not fork(): %m\n");
911 LOG("FAILED: %s\n", t
->name
);
916 return test_run_parent(t
, fdout
, fderr
, fdmonitor
, pid
);
918 return test_run_child(t
, fdout
, fderr
, fdmonitor
);