Refactored process management into its own file

This commit is contained in:
Snaipe 2015-02-10 18:20:06 +01:00
parent d4eb936c00
commit df1f23ef4c
4 changed files with 132 additions and 66 deletions

View file

@ -37,5 +37,7 @@ libcriterion_la_SOURCES = \
src/report.h \
src/runner.c \
src/runner.h \
src/process.c \
src/process.h \
src/stats.c \
src/stats.h

71
src/process.c Normal file
View file

@ -0,0 +1,71 @@
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <csptr/smart_ptr.h>
#include "criterion/criterion.h"
#include "process.h"
#include "event.h"
struct process {
pid_t pid;
int in;
};
static pid_t g_runner_pid;
void set_runner_pid(void) {
g_runner_pid = getpid();
}
bool is_runner(void) {
return g_runner_pid == getpid();
}
static void close_process(void *ptr, UNUSED void *meta) {
close(((struct process *) ptr)->in);
}
struct event *worker_read_event(struct process *proc) {
return read_event(proc->in);
}
struct process *spawn_test_worker(struct criterion_test *test, void (*func)(struct criterion_test *)) {
int fds[2];
if (pipe(fds) == -1)
abort();
pid_t pid = fork();
if (pid == -1) {
return NULL;
} else if (!pid) {
close(STDIN_FILENO);
close(fds[0]);
EVENT_PIPE = fds[1];
func(test);
close(fds[1]);
if (!strcmp("1", getenv("CRITERION_NO_EARLY_EXIT") ?: "0"))
return NULL;
else
_exit(0);
}
close(fds[1]);
return unique_ptr(struct process, ({ .pid = pid, .in = fds[0] }), close_process);
}
struct process_status wait_proc(struct process *proc) {
int status;
waitpid(proc->pid, &status, 0);
if (WIFEXITED(status))
return (struct process_status) { .kind = EXIT_STATUS, .status = WEXITSTATUS(status) };
if (WIFSIGNALED(status))
return (struct process_status) { .kind = SIGNAL, .status = WTERMSIG(status) };
return (struct process_status) { .kind = STOPPED };
}

23
src/process.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef PROCESS_H_
# define PROCESS_H_
struct process;
enum status_kind {
EXIT_STATUS,
STOPPED,
SIGNAL,
};
struct process_status {
enum status_kind kind;
int status;
};
void set_runner_pid(void);
bool is_runner(void);
struct process_status wait_proc(struct process *proc);
struct process *spawn_test_worker(struct criterion_test *test, void (*func)(struct criterion_test *));
struct event *worker_read_event(struct process *proc);
#endif /* !PROCESS_H_ */

View file

@ -31,35 +31,26 @@
#include "runner.h"
#include "report.h"
#include "event.h"
#include "process.h"
static struct criterion_test * const g_section_start = &__start_criterion_tests;
static struct criterion_test * const g_section_end = &__stop_criterion_tests;
static pid_t g_runner_pid;
struct test_set {
struct criterion_test **tests;
size_t nb_tests;
};
static int compare_test_by_name(const struct criterion_test *first,
const struct criterion_test *second) {
// likely to happen
if (first->name == second->name)
return 0;
return strcmp(first->name, second->name);
}
static int compare_test(const void *a, const void *b) {
struct criterion_test *first = *(struct criterion_test **) a;
struct criterion_test *second = *(struct criterion_test **) b;
// likely to happen
if (first->category == second->category) {
return compare_test_by_name(first, second);
return strcmp(first->name, second->name);
} else {
return strcmp(first->category, second->category)
?: compare_test_by_name(first, second);
?: strcmp(first->name, second->name);
}
}
@ -91,7 +82,7 @@ static void map_tests(struct test_set *set, struct criterion_global_stats *stats
size_t i = 0;
for (struct criterion_test **t = set->tests; i < set->nb_tests; ++i, ++t) {
fun(stats, *t);
if (g_runner_pid != getpid())
if (!is_runner())
return;
}
}
@ -109,63 +100,41 @@ static void run_test_child(struct criterion_test *test) {
send_event(POST_FINI, NULL, 0);
}
struct pipefds {
int in, out;
} __attribute__ ((packed));
static void setup_child(struct pipefds *fds) {
close(STDIN_FILENO);
close(fds->in);
EVENT_PIPE = fds->out;
}
static void run_test(struct criterion_global_stats *stats, struct criterion_test *test) {
smart struct criterion_test_stats *test_stats = test_stats_init(test);
struct pipefds fds;
if (pipe((int*) &fds) == -1)
abort();
smart struct process *proc = spawn_test_worker(test, run_test_child);
if (proc == NULL && !is_runner())
return;
pid_t pid;
if (!(pid = fork())) {
setup_child(&fds);
run_test_child(test);
if (!strcmp("1", getenv("CRITERION_NO_EARLY_EXIT") ?: "0"))
return;
else
_exit(0);
} else {
close(fds.out);
struct event *ev;
while ((ev = read_event(fds.in)) != NULL) {
stat_push_event(stats, test_stats, ev);
switch (ev->kind) {
case PRE_INIT: report(PRE_INIT, test); break;
case PRE_TEST: report(PRE_TEST, test); break;
case ASSERT: report(ASSERT, ev->data); break;
case POST_TEST: report(POST_TEST, test_stats); break;
case POST_FINI: report(POST_FINI, test_stats); break;
}
sfree(ev);
struct event *ev;
while ((ev = worker_read_event(proc)) != NULL) {
stat_push_event(stats, test_stats, ev);
switch (ev->kind) {
case PRE_INIT: report(PRE_INIT, test); break;
case PRE_TEST: report(PRE_TEST, test); break;
case ASSERT: report(ASSERT, ev->data); break;
case POST_TEST: report(POST_TEST, test_stats); break;
case POST_FINI: report(POST_FINI, test_stats); break;
}
int status;
waitpid(pid, &status, 0);
if (WIFSIGNALED(status)) {
test_stats->signal = WTERMSIG(status);
if (test->data->signal == 0) {
struct event ev = { .kind = TEST_CRASH };
stat_push_event(stats, test_stats, &ev);
report(TEST_CRASH, test_stats);
} else {
struct event ev = { .kind = POST_TEST };
stat_push_event(stats, test_stats, &ev);
report(POST_TEST, test_stats);
sfree(ev);
}
ev.kind = POST_FINI;
stat_push_event(stats, test_stats, &ev);
report(POST_FINI, test_stats);
}
struct process_status status = wait_proc(proc);
if (status.kind == SIGNAL) {
test_stats->signal = status.status;
if (test->data->signal == 0) {
struct event ev = { .kind = TEST_CRASH };
stat_push_event(stats, test_stats, &ev);
report(TEST_CRASH, test_stats);
} else {
struct event ev = { .kind = POST_TEST };
stat_push_event(stats, test_stats, &ev);
report(POST_TEST, test_stats);
ev.kind = POST_FINI;
stat_push_event(stats, test_stats, &ev);
report(POST_FINI, test_stats);
}
}
}
@ -173,14 +142,15 @@ static void run_test(struct criterion_global_stats *stats, struct criterion_test
// TODO: disable & change tests at runtime
static int criterion_run_all_tests_impl(void) {
report(PRE_EVERYTHING, NULL);
g_runner_pid = getpid();
set_runner_pid();
smart struct test_set *set = read_all_tests();
smart struct criterion_global_stats *stats = stats_init();
if (!set)
abort();
map_tests(set, stats, run_test);
if (g_runner_pid != getpid())
if (!is_runner())
return -1;
report(POST_EVERYTHING, stats);