Refactored process management into its own file
This commit is contained in:
parent
d4eb936c00
commit
df1f23ef4c
4 changed files with 132 additions and 66 deletions
|
@ -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
71
src/process.c
Normal 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
23
src/process.h
Normal 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_ */
|
102
src/runner.c
102
src/runner.c
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue