LADI
/
spa
1
Fork 0

pwtest: handle SIGTERM/SIGINT in runner

Catch SIGTERM/SIGINT in test runner, and clean up any spawned processes.

Fixes printing test output on termination by signal (e.g. meson
timeout), and doesn't leave spawned processes running.
This commit is contained in:
Pauli Virtanen 2022-03-05 13:25:28 +02:00 committed by Wim Taymans
parent ad3c90dbb9
commit 76bab25afb
1 changed files with 99 additions and 0 deletions

View File

@ -128,6 +128,8 @@ struct pwtest_context {
struct spa_list suites;
unsigned int timeout;
bool no_fork;
bool terminate;
struct spa_list cleanup_pids;
const char *test_filter;
bool has_iteration_filter;
@ -135,6 +137,11 @@ struct pwtest_context {
char *xdg_dir;
};
struct cleanup_pid {
struct spa_list link;
pid_t pid;
};
struct pwtest_context *pwtest_get_context(struct pwtest_test *t)
{
return ctx;
@ -175,6 +182,56 @@ static void restore_env(struct pwtest_test *t)
}
}
static int add_cleanup_pid(struct pwtest_context *ctx, pid_t pid)
{
struct cleanup_pid *cpid;
if (pid == 0)
return -EINVAL;
cpid = calloc(1, sizeof(struct cleanup_pid));
if (cpid == NULL)
return -errno;
cpid->pid = pid;
spa_list_append(&ctx->cleanup_pids, &cpid->link);
return 0;
}
static void remove_cleanup_pid(struct pwtest_context *ctx, pid_t pid)
{
struct cleanup_pid *cpid, *t;
spa_list_for_each_safe(cpid, t, &ctx->cleanup_pids, link) {
if (cpid->pid == pid) {
spa_list_remove(&cpid->link);
free(cpid);
}
}
}
static void terminate_cleanup_pids(struct pwtest_context *ctx)
{
struct cleanup_pid *cpid;
spa_list_for_each(cpid, &ctx->cleanup_pids, link) {
/* Don't free here, to be signal-safe */
if (cpid->pid != 0) {
kill(cpid->pid, SIGTERM);
cpid->pid = 0;
}
}
}
static void free_cleanup_pids(struct pwtest_context *ctx)
{
struct cleanup_pid *cpid;
spa_list_consume(cpid, &ctx->cleanup_pids, link) {
spa_list_remove(&cpid->link);
free(cpid);
}
}
static void pwtest_backtrace(pid_t p)
{
#ifdef HAVE_GSTACK
@ -425,7 +482,9 @@ pwtest_spawn(const char *file, char *const argv[])
} else if (pid < 0)
pwtest_error_with_msg("Unable to fork: %s", strerror(errno));
add_cleanup_pid(ctx, pid);
r = waitpid(pid, &status, 0);
remove_cleanup_pid(ctx, pid);
if (r <= 0)
pwtest_error_with_msg("waitpid failed: %s", strerror(errno));
@ -596,6 +655,9 @@ static void cleanup(struct pwtest_context *ctx)
{
struct pwtest_suite *c, *tmp;
terminate_cleanup_pids(ctx);
free_cleanup_pids(ctx);
spa_list_for_each_safe(c, tmp, &ctx->suites, link) {
free_suite(c);
}
@ -613,6 +675,7 @@ static void sighandler(int signal)
sigaction(signal, &act, NULL);
pwtest_backtrace(0);
terminate_cleanup_pids(ctx);
raise(signal);
}
@ -693,8 +756,12 @@ static pid_t start_pwdaemon(struct pwtest_test *t, int stderr_fd, int log_fd)
execl(daemon, daemon, (char*)NULL);
return -errno;
} else if (pid < 0) {
return pid;
}
add_cleanup_pid(ctx, -pid);
/* parent */
sleep(1); /* FIXME how to wait for pw to be ready? */
if (waitpid(pid, &status, WNOHANG) > 0) {
@ -817,6 +884,9 @@ static int start_test_forked(struct pwtest_test *t, int read_fds[_FD_LAST], int
close_pipes(read_fds);
/* Reset cleanup pid list */
free_cleanup_pids(ctx);
/* Catch any crashers so we can insert a backtrace */
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
@ -997,12 +1067,14 @@ static void run_test(struct pwtest_context *ctx, struct pwtest_suite *c, struct
errno = -r;
goto error;
}
add_cleanup_pid(ctx, pid);
r = monitor_test_forked(t, pid, read_fds);
if (r < 0) {
errno = -r;
goto error;
}
remove_cleanup_pid(ctx, pid);
}
errno = 0;
@ -1010,6 +1082,12 @@ error:
if (errno)
t->sig_or_errno = -errno;
if (ctx->terminate) {
char *buf = pw_array_add(&t->logs[FD_LOG], 64);
spa_scnprintf(buf, 64, "pwtest: tests terminated by signal\n");
t->result = PWTEST_SYSTEM_ERROR;
}
for (size_t i = 0; i < SPA_N_ELEMENTS(read_fds); i++) {
log_append(&t->logs[i], read_fds[i]);
}
@ -1018,6 +1096,7 @@ error:
int status;
kill(-pw_daemon, SIGTERM);
remove_cleanup_pid(ctx, -pw_daemon);
r = waitpid(pw_daemon, &status, 0);
if (r > 0) {
/* write_fds are closed in the parent process, so we append directly */
@ -1181,6 +1260,11 @@ static int run_tests(struct pwtest_context *ctx)
r = EXIT_FAILURE;
break;
}
if (ctx->terminate) {
r = EXIT_FAILURE;
return r;
}
}
}
}
@ -1257,6 +1341,17 @@ static void usage(FILE *fp, const char *progname)
progname);
}
static void sigterm_handler(int signo)
{
terminate_cleanup_pids(ctx);
ctx->terminate = true;
if (ctx->no_fork) {
signal(SIGTERM, SIG_DFL);
signal(SIGINT, SIG_DFL);
raise(signo);
}
}
int main(int argc, char **argv)
{
int r = EXIT_SUCCESS;
@ -1292,6 +1387,8 @@ int main(int argc, char **argv)
} mode = MODE_TEST;
const char *suite_filter = NULL;
spa_list_init(&test_ctx.cleanup_pids);
ctx = &test_ctx;
while (1) {
@ -1349,6 +1446,8 @@ int main(int argc, char **argv)
break;
case MODE_TEST:
setrlimit(RLIMIT_CORE, &((struct rlimit){0, 0}));
signal(SIGTERM, sigterm_handler);
signal(SIGINT, sigterm_handler);
r = run_tests(ctx);
break;
}