Integrated new protocol into the core
This commit is contained in:
parent
131a6a646b
commit
f4e444a8d3
20 changed files with 539 additions and 230 deletions
|
@ -95,32 +95,18 @@ CR_END_C_API
|
|||
"" CR_TRANSLATE_DEF_MSG__(CR_VA_HEAD(CR_VA_TAIL(__VA_ARGS__))) \
|
||||
))
|
||||
|
||||
# define CR_INIT_STATS_(BufSize, MsgVar, ...) CR_EXPAND( \
|
||||
# define CR_INIT_STATS_(MsgVar, ...) CR_EXPAND( \
|
||||
do { \
|
||||
char *def_msg = CR_EXPAND(CR_TRANSLATE_DEF_MSG_(__VA_ARGS__)); \
|
||||
char *formatted_msg = NULL; \
|
||||
int msglen = cr_asprintf(&formatted_msg, \
|
||||
"" CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))); \
|
||||
cr_asprintf(&formatted_msg, "" CR_VA_TAIL(CR_VA_TAIL(__VA_ARGS__))); \
|
||||
if (formatted_msg && *formatted_msg) { \
|
||||
MsgVar = formatted_msg; \
|
||||
CR_STDN free(def_msg); \
|
||||
} else { \
|
||||
MsgVar = def_msg; \
|
||||
msglen = strlen(def_msg); \
|
||||
CR_STDN free(formatted_msg); \
|
||||
} \
|
||||
\
|
||||
BufSize = sizeof(struct criterion_assert_stats) \
|
||||
+ sizeof (size_t) + msglen + 1; \
|
||||
\
|
||||
char *buf = (char*) CR_STDN malloc(BufSize); \
|
||||
stat = (struct criterion_assert_stats*) buf; \
|
||||
CR_STDN memset(buf, 0, sizeof (struct criterion_assert_stats)); \
|
||||
buf += sizeof (struct criterion_assert_stats); \
|
||||
*((size_t*) buf) = msglen + 1; \
|
||||
buf += sizeof (size_t); \
|
||||
CR_STDN strcpy(buf, MsgVar); \
|
||||
CR_STDN free(MsgVar); \
|
||||
} while (0))
|
||||
|
||||
# define CR_FAIL_ABORT_ criterion_abort_test
|
||||
|
@ -137,16 +123,16 @@ CR_END_C_API
|
|||
bool passed = !!(Condition); \
|
||||
\
|
||||
char *msg = NULL; \
|
||||
size_t bufsize; \
|
||||
\
|
||||
struct criterion_assert_stats *stat; \
|
||||
CR_EXPAND(CR_INIT_STATS_(bufsize, msg, CR_VA_TAIL(__VA_ARGS__))); \
|
||||
stat->passed = passed; \
|
||||
stat->file = __FILE__; \
|
||||
stat->line = __LINE__; \
|
||||
CR_EXPAND(CR_INIT_STATS_(msg, CR_VA_TAIL(__VA_ARGS__))); \
|
||||
struct criterion_assert_stats stat; \
|
||||
stat.passed = passed; \
|
||||
stat.file = __FILE__; \
|
||||
stat.line = __LINE__; \
|
||||
stat.message = msg; \
|
||||
criterion_send_assert(&stat); \
|
||||
\
|
||||
criterion_send_event(ASSERT, stat, bufsize); \
|
||||
CR_STDN free(stat); \
|
||||
CR_STDN free(msg); \
|
||||
\
|
||||
if (!passed) \
|
||||
Fail(); \
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <csptr/smalloc.h>
|
||||
#include "core/worker.h"
|
||||
#include "core/runner.h"
|
||||
#include "protocol/protocol.h"
|
||||
#include "protocol/messages.h"
|
||||
#include "io/event.h"
|
||||
#include "process.h"
|
||||
#include "internal.h"
|
||||
|
@ -118,28 +120,24 @@ static struct full_context local_ctx;
|
|||
# endif
|
||||
|
||||
static void handle_sigchld_pump(void) {
|
||||
int fd = g_worker_pipe->fds[1];
|
||||
pid_t pid;
|
||||
int status;
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
int kind = WORKER_TERMINATED;
|
||||
struct worker_status ws = {
|
||||
(s_proc_handle) { pid }, get_status(status)
|
||||
};
|
||||
int result = WIFEXITED(status)
|
||||
? criterion_protocol_death_result_type_NORMAL
|
||||
: criterion_protocol_death_result_type_CRASH;
|
||||
int code = WIFEXITED(status)
|
||||
? WEXITSTATUS(status)
|
||||
: WTERMSIG(status);
|
||||
|
||||
unsigned long long pid_ull = (unsigned long long) pid;
|
||||
criterion_protocol_msg msg = criterion_message(death,
|
||||
.result = result,
|
||||
.has_status = true,
|
||||
.status = code,
|
||||
);
|
||||
|
||||
char buf[sizeof (int) + sizeof (pid_ull) + sizeof (struct worker_status)];
|
||||
memcpy(buf, &kind, sizeof (kind));
|
||||
memcpy(buf + sizeof (kind), &pid_ull, sizeof (pid_ull));
|
||||
memcpy(buf + sizeof (kind) + sizeof (pid_ull), &ws, sizeof (ws));
|
||||
|
||||
if (write(fd, &buf, sizeof (buf)) < (ssize_t) sizeof (buf)) {
|
||||
criterion_perror("Could not write the WORKER_TERMINATED event "
|
||||
"down the event pipe: %s.\n",
|
||||
strerror(errno));
|
||||
abort();
|
||||
}
|
||||
msg.id.pid = pid;
|
||||
cr_send_to_runner(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,10 @@
|
|||
*/
|
||||
#include <string.h>
|
||||
#include "abort.h"
|
||||
#include "protocol/protocol.h"
|
||||
#include "protocol/messages.h"
|
||||
#include "criterion/internal/asprintf-compat.h"
|
||||
#include "criterion/criterion.h"
|
||||
#include "io/event.h"
|
||||
|
||||
jmp_buf g_pre_test;
|
||||
|
@ -42,12 +45,13 @@ void criterion_test_die(const char *msg, ...) {
|
|||
if (res < 0)
|
||||
abort();
|
||||
|
||||
size_t *buf = malloc(sizeof (size_t) + res + 1);
|
||||
*buf = res + 1;
|
||||
memcpy(buf + 1, formatted_msg, res + 1);
|
||||
criterion_protocol_msg abort_msg = criterion_message(phase,
|
||||
.phase = criterion_protocol_phase_kind_ABORT,
|
||||
.name = (char *) criterion_current_test->name,
|
||||
.message = formatted_msg,
|
||||
);
|
||||
cr_send_to_runner(&abort_msg);
|
||||
|
||||
criterion_send_event(TEST_ABORT, buf, sizeof(size_t) + res + 1);
|
||||
free(buf);
|
||||
free(formatted_msg);
|
||||
|
||||
exit(0);
|
||||
|
|
|
@ -23,7 +23,10 @@
|
|||
*/
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <csptr/smalloc.h>
|
||||
#include "compat/strtok.h"
|
||||
#include "protocol/protocol.h"
|
||||
#include "protocol/messages.h"
|
||||
#include "criterion/logging.h"
|
||||
#include "criterion/options.h"
|
||||
#include "io/event.h"
|
||||
|
@ -31,46 +34,43 @@
|
|||
#include "stats.h"
|
||||
#include "client.h"
|
||||
|
||||
void nothing();
|
||||
|
||||
enum protocol_version {
|
||||
PROTOCOL_V1 = 1,
|
||||
};
|
||||
static void nothing(void) {};
|
||||
|
||||
KHASH_MAP_INIT_INT(ht_client, struct client_ctx)
|
||||
|
||||
static enum client_state msg_to_state[] = {
|
||||
[criterion_protocol_submessage_birth_tag] = CS_BIRTH,
|
||||
[criterion_protocol_submessage_pre_init_tag] = CS_PRE_INIT,
|
||||
[criterion_protocol_submessage_pre_test_tag] = CS_PRE_TEST,
|
||||
[criterion_protocol_submessage_post_test_tag] = CS_POST_TEST,
|
||||
[criterion_protocol_submessage_post_fini_tag] = CS_POST_FINI,
|
||||
[criterion_protocol_submessage_death_tag] = CS_DEATH,
|
||||
static enum client_state phase_to_state[] = {
|
||||
[criterion_protocol_phase_kind_SETUP] = CS_SETUP,
|
||||
[criterion_protocol_phase_kind_MAIN] = CS_MAIN,
|
||||
[criterion_protocol_phase_kind_TEARDOWN] = CS_TEARDOWN,
|
||||
[criterion_protocol_phase_kind_END] = CS_END,
|
||||
[criterion_protocol_phase_kind_ABORT] = CS_ABORT,
|
||||
[criterion_protocol_phase_kind_TIMEOUT] = CS_TIMEOUT,
|
||||
};
|
||||
|
||||
static const char *state_to_string[] = {
|
||||
[CS_BIRTH] = "BIRTH",
|
||||
[CS_PRE_INIT] = "PRE_INIT",
|
||||
[CS_PRE_TEST] = "PRE_TEST",
|
||||
[CS_POST_TEST] = "POST_TEST",
|
||||
[CS_POST_FINI] = "POST_FINI",
|
||||
[CS_DEATH] = "DEATH",
|
||||
[CS_SETUP] = "setup",
|
||||
[CS_MAIN] = "main",
|
||||
[CS_TEARDOWN] = "teardown",
|
||||
[CS_END] = "end",
|
||||
[CS_ABORT] = "abort",
|
||||
[CS_TIMEOUT] = "timeout",
|
||||
};
|
||||
|
||||
typedef void message_handler(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
typedef bool message_handler(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
typedef bool phase_handler(struct server_ctx *, struct client_ctx *, const criterion_protocol_phase *);
|
||||
|
||||
void handle_birth(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
void handle_pre_init(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
void handle_pre_test(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
void handle_post_test(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
void handle_death(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
bool handle_birth(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
bool handle_phase(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
bool handle_death(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
bool handle_assert(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
bool handle_message(struct server_ctx *, struct client_ctx *, const criterion_protocol_msg *);
|
||||
|
||||
static message_handler *message_handlers[] = {
|
||||
[criterion_protocol_submessage_birth_tag] = handle_birth,
|
||||
[criterion_protocol_submessage_pre_init_tag] = handle_pre_init,
|
||||
[criterion_protocol_submessage_pre_test_tag] = handle_pre_test,
|
||||
[criterion_protocol_submessage_post_test_tag] = handle_post_test,
|
||||
[criterion_protocol_submessage_death_tag] = handle_death,
|
||||
[criterion_protocol_submessage_birth_tag] = handle_birth,
|
||||
[criterion_protocol_submessage_phase_tag] = handle_phase,
|
||||
[criterion_protocol_submessage_death_tag] = handle_death,
|
||||
[criterion_protocol_submessage_assert_tag] = handle_assert,
|
||||
[criterion_protocol_submessage_message_tag] = handle_message,
|
||||
};
|
||||
|
||||
static void get_message_id(char *out, size_t n, const criterion_protocol_msg *msg) {
|
||||
|
@ -85,7 +85,19 @@ void init_server_context(struct server_ctx *sctx) {
|
|||
sctx->subprocesses = kh_init(ht_client);
|
||||
}
|
||||
|
||||
void destroy_client_context(struct client_ctx *ctx) {
|
||||
sfree(ctx->worker);
|
||||
}
|
||||
|
||||
void destroy_server_context(struct server_ctx *sctx) {
|
||||
khint_t k;
|
||||
(void) k;
|
||||
struct client_ctx v;
|
||||
(void) v;
|
||||
|
||||
kh_foreach(sctx->subprocesses, k, v, {
|
||||
destroy_client_context(&v);
|
||||
});
|
||||
kh_destroy(ht_client, sctx->subprocesses);
|
||||
}
|
||||
|
||||
|
@ -101,45 +113,31 @@ struct client_ctx *add_client_from_worker(struct server_ctx *sctx, struct client
|
|||
|
||||
void remove_client_by_pid(struct server_ctx *sctx, int pid) {
|
||||
khint_t k = kh_get(ht_client, sctx->subprocesses, pid);
|
||||
if (k != kh_end(sctx->subprocesses))
|
||||
kh_del(ht_client, sctx->subprocesses, pid);
|
||||
if (k != kh_end(sctx->subprocesses)) {
|
||||
destroy_client_context(&kh_value(sctx->subprocesses, k));
|
||||
kh_del(ht_client, sctx->subprocesses, k);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_client_message_impl(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
if (msg->data.which_value >= CS_MAX_CLIENT_STATES
|
||||
|| msg_to_state[msg->data.which_value] == 0) {
|
||||
char id[32];
|
||||
get_message_id(id, sizeof (id), msg);
|
||||
|
||||
criterion_perror("%s: Received message with malformed data.value tag '%d'.",
|
||||
id,
|
||||
msg->data.which_value);
|
||||
return;
|
||||
}
|
||||
|
||||
enum client_state new_state = msg_to_state[msg->data.which_value];
|
||||
if (new_state < CS_DEATH && new_state != ctx->state + 1) {
|
||||
char id[32];
|
||||
get_message_id(id, sizeof (id), msg);
|
||||
|
||||
criterion_perror("%s: Expected message to change to state '%s', got '%s' instead.",
|
||||
id,
|
||||
state_to_string[ctx->state + 1],
|
||||
state_to_string[new_state]);
|
||||
return;
|
||||
}
|
||||
|
||||
message_handler *handler = message_handlers[msg->data.which_value];
|
||||
bool ack;
|
||||
if (handler)
|
||||
handler(sctx, ctx, msg);
|
||||
ack = handler(sctx, ctx, msg);
|
||||
|
||||
if (new_state <= CS_DEATH)
|
||||
ctx->state = new_state;
|
||||
if (!ack)
|
||||
send_ack(sctx->socket, true, NULL);
|
||||
}
|
||||
|
||||
# define handler_error(Ctx, IdFmt, Id, Fmt, ...) \
|
||||
do { \
|
||||
criterion_perror(IdFmt Fmt "\n", Id, __VA_ARGS__); \
|
||||
send_ack((Ctx)->socket, false, Fmt, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
struct client_ctx *process_client_message(struct server_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
if (msg->version != PROTOCOL_V1) {
|
||||
criterion_perror("Received message using invalid protocol version number '%d'.", msg->version);
|
||||
handler_error(ctx, "%s", "", "Received message using invalid protocol version number '%d'.", msg->version);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -150,16 +148,12 @@ struct client_ctx *process_client_message(struct server_ctx *ctx, const criterio
|
|||
process_client_message_impl(ctx, &kh_value(ctx->subprocesses, k), msg);
|
||||
return &kh_value(ctx->subprocesses, k);
|
||||
} else {
|
||||
char id[32];
|
||||
get_message_id(id, sizeof (id), msg);
|
||||
|
||||
criterion_perror("%s: Received message identified by a PID "
|
||||
"that is not a child process.",
|
||||
id);
|
||||
handler_error(ctx, "%s", "", "Received message identified by a PID '%ld'"
|
||||
"that is not a child process.", msg->id.pid);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
criterion_perror("Received message with malformed id tag '%d'.",
|
||||
handler_error(ctx, "%s", "", "Received message with malformed id tag '%d'.\n",
|
||||
criterion_protocol_msg_pid_tag);
|
||||
} break;
|
||||
}
|
||||
|
@ -183,48 +177,183 @@ struct client_ctx *process_client_message(struct server_ctx *ctx, const criterio
|
|||
}); \
|
||||
} while (0)
|
||||
|
||||
void handle_birth(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
bool handle_birth(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
(void) sctx;
|
||||
(void) msg;
|
||||
|
||||
ctx->alive = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_pre_init(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_phase *msg) {
|
||||
(void) sctx;
|
||||
(void) msg;
|
||||
|
||||
if (ctx->state == 0) { // only pre_init if there are no nested states
|
||||
push_event_noreport(PRE_INIT);
|
||||
report(PRE_INIT, ctx->test);
|
||||
log(pre_init, ctx->test);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_pre_test(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_phase *msg) {
|
||||
(void) sctx;
|
||||
(void) msg;
|
||||
|
||||
if (ctx->state < CS_MAX_CLIENT_STATES) {
|
||||
push_event_noreport(PRE_TEST);
|
||||
report(PRE_TEST, ctx->test);
|
||||
log(pre_test, ctx->test);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_post_test(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_phase *msg) {
|
||||
(void) sctx;
|
||||
(void) msg;
|
||||
|
||||
if (ctx->state < CS_MAX_CLIENT_STATES) {
|
||||
double elapsed_time = 0; // TODO: restore elapsed time handling
|
||||
push_event_noreport(POST_TEST, .data = &elapsed_time);
|
||||
report(POST_TEST, ctx->tstats);
|
||||
log(post_test, ctx->tstats);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_post_fini(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_phase *msg) {
|
||||
(void) sctx;
|
||||
(void) ctx;
|
||||
(void) msg;
|
||||
if (ctx->state < CS_MAX_CLIENT_STATES) {
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, ctx->tstats);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void handle_pre_init(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
bool handle_abort(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_phase *msg) {
|
||||
(void) sctx;
|
||||
(void) ctx;
|
||||
(void) msg;
|
||||
|
||||
enum client_state curstate = ctx->state & (CS_MAX_CLIENT_STATES - 1);
|
||||
|
||||
if (ctx->state < CS_MAX_CLIENT_STATES) {
|
||||
if (curstate < CS_TEARDOWN) {
|
||||
double elapsed_time = 0;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
log(post_test, ctx->tstats);
|
||||
}
|
||||
if (curstate < CS_END) {
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, ctx->tstats);
|
||||
}
|
||||
} else {
|
||||
struct criterion_theory_stats ths = {
|
||||
.formatted_args = strdup(msg->message),
|
||||
.stats = ctx->tstats,
|
||||
};
|
||||
report(THEORY_FAIL, &ths);
|
||||
log(theory_fail, &ths);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_timeout(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_phase *msg) {
|
||||
(void) sctx;
|
||||
(void) msg;
|
||||
|
||||
push_event_noreport(PRE_INIT);
|
||||
report(PRE_INIT, ctx->test);
|
||||
log(pre_init, ctx->test);
|
||||
if (ctx->state < CS_MAX_CLIENT_STATES) {
|
||||
ctx->tstats->timed_out = true;
|
||||
double elapsed_time = ctx->test->data->timeout;
|
||||
if (elapsed_time == 0 && ctx->suite->data)
|
||||
elapsed_time = ctx->suite->data->timeout;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
push_event(POST_FINI);
|
||||
log(test_timeout, ctx->tstats);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void handle_pre_test(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
(void) sctx;
|
||||
(void) msg;
|
||||
# define MAX_TEST_DEPTH 15
|
||||
|
||||
report(PRE_TEST, ctx->test);
|
||||
log(pre_test, ctx->test);
|
||||
bool handle_phase(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
const criterion_protocol_phase *phase_msg = &msg->data.value.phase;
|
||||
|
||||
enum client_state new_state = phase_to_state[phase_msg->phase];
|
||||
enum client_state curstate = ctx->state & (CS_MAX_CLIENT_STATES - 1);
|
||||
|
||||
if (new_state == CS_SETUP) {
|
||||
if (ctx->state != 0 && ctx->state != CS_MAIN) {
|
||||
char id[32];
|
||||
get_message_id(id, sizeof (id), msg);
|
||||
|
||||
handler_error(sctx, "%s: ", id, "Cannot spawn a subtest outside of the '%s' test phase.", state_to_string[CS_MAIN]);
|
||||
return true;
|
||||
}
|
||||
if (ctx->state & (0xff << MAX_TEST_DEPTH * 2)) {
|
||||
char id[32];
|
||||
get_message_id(id, sizeof (id), msg);
|
||||
|
||||
handler_error(sctx, "%s: ", id, "Cannot nest more than %d tests at a time.", MAX_TEST_DEPTH);
|
||||
return true;
|
||||
}
|
||||
} else if (curstate == CS_END) {
|
||||
char id[32];
|
||||
get_message_id(id, sizeof (id), msg);
|
||||
|
||||
handler_error(sctx, "%s: ", id, "The test has already ended, invalid state '%s'.", state_to_string[new_state]);
|
||||
return true;
|
||||
} else if (curstate < CS_END && new_state <= CS_END && new_state != curstate + 1) {
|
||||
char id[32];
|
||||
get_message_id(id, sizeof (id), msg);
|
||||
|
||||
handler_error(sctx, "%s: ", id, "Expected message to change to state '%s', got '%s' instead.",
|
||||
state_to_string[ctx->state + 1],
|
||||
state_to_string[new_state]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static phase_handler *handlers[] = {
|
||||
[CS_SETUP] = handle_pre_init,
|
||||
[CS_MAIN] = handle_pre_test,
|
||||
[CS_TEARDOWN] = handle_post_test,
|
||||
[CS_END] = handle_post_fini,
|
||||
[CS_ABORT] = handle_abort,
|
||||
[CS_TIMEOUT] = handle_timeout,
|
||||
};
|
||||
|
||||
bool ack = handlers[new_state](sctx, ctx, phase_msg);
|
||||
|
||||
if (new_state >= CS_END) {
|
||||
if ((ctx->state >> 2) != 0)
|
||||
ctx->state >>= 2; // pop the current state
|
||||
else
|
||||
ctx->state = CS_END;
|
||||
} else if (new_state == CS_SETUP) {
|
||||
ctx->state <<= 2; // shift the state to make space for a new state
|
||||
} else {
|
||||
++ctx->state;
|
||||
}
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
void handle_post_test(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
bool handle_death(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
(void) sctx;
|
||||
(void) msg;
|
||||
|
||||
double elapsed_time = 0; // TODO: restore elapsed time handling
|
||||
push_event_noreport(POST_TEST, .data = &elapsed_time);
|
||||
report(POST_TEST, ctx->tstats);
|
||||
log(post_test, ctx->tstats);
|
||||
}
|
||||
ctx->alive = false;
|
||||
|
||||
void handle_death(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
(void) sctx;
|
||||
const criterion_protocol_death *death = &msg->data.value.death;
|
||||
enum client_state curstate = ctx->state & (CS_MAX_CLIENT_STATES - 1);
|
||||
switch (death->result) {
|
||||
case criterion_protocol_death_result_type_CRASH: {
|
||||
if (ctx->state >= CS_POST_TEST || ctx->state < CS_PRE_TEST) {
|
||||
if (curstate != CS_MAIN) {
|
||||
log(other_crash, ctx->tstats);
|
||||
|
||||
if (ctx->state < CS_PRE_TEST) {
|
||||
if (ctx->state < CS_MAIN) {
|
||||
stat_push_event(ctx->gstats,
|
||||
ctx->sstats,
|
||||
ctx->tstats,
|
||||
|
@ -244,39 +373,19 @@ void handle_death(struct server_ctx *sctx, struct client_ctx *ctx, const criteri
|
|||
}
|
||||
}
|
||||
} break;
|
||||
case criterion_protocol_death_result_type_TIMEOUT: {
|
||||
ctx->tstats->timed_out = true;
|
||||
double elapsed_time = ctx->test->data->timeout;
|
||||
if (elapsed_time == 0 && ctx->suite->data)
|
||||
elapsed_time = ctx->suite->data->timeout;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
push_event(POST_FINI);
|
||||
log(test_timeout, ctx->tstats);
|
||||
} break;
|
||||
case criterion_protocol_death_result_type_ABORT: {
|
||||
if (ctx->state < CS_POST_TEST) {
|
||||
double elapsed_time = 0;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
log(post_test, ctx->tstats);
|
||||
}
|
||||
if (ctx->state < CS_POST_FINI) {
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, ctx->tstats);
|
||||
}
|
||||
} break;
|
||||
case criterion_protocol_death_result_type_NORMAL: {
|
||||
if ((ctx->state >= CS_POST_TEST && ctx->state < CS_POST_FINI) || ctx->state < CS_PRE_TEST) {
|
||||
if (curstate == CS_TEARDOWN || ctx->state == CS_SETUP) {
|
||||
log(abnormal_exit, ctx->tstats);
|
||||
if (ctx->state < CS_PRE_TEST) {
|
||||
if (ctx->state == CS_SETUP) {
|
||||
stat_push_event(ctx->gstats,
|
||||
ctx->sstats,
|
||||
ctx->tstats,
|
||||
&(struct event) { .kind = TEST_CRASH });
|
||||
}
|
||||
return;
|
||||
break;
|
||||
}
|
||||
ctx->tstats->exit_code = death->status;
|
||||
if (ctx->state < CS_POST_TEST) {
|
||||
if (ctx->state == CS_MAIN) {
|
||||
if (ctx->test->data->exit_code == 0) {
|
||||
push_event(TEST_CRASH);
|
||||
log(abnormal_exit, ctx->tstats);
|
||||
|
@ -291,4 +400,30 @@ void handle_death(struct server_ctx *sctx, struct client_ctx *ctx, const criteri
|
|||
} break;
|
||||
default: break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_assert(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
(void) sctx;
|
||||
(void) ctx;
|
||||
(void) msg;
|
||||
const criterion_protocol_assert *asrt = &msg->data.value.assert;
|
||||
struct criterion_assert_stats asrt_stats = {
|
||||
.message = asrt->message,
|
||||
.passed = asrt->passed,
|
||||
.line = asrt->has_line ? asrt->line : 0,
|
||||
.file = asrt->file ? asrt->file : "unknown",
|
||||
};
|
||||
|
||||
push_event_noreport(ASSERT, .data = &asrt_stats);
|
||||
report(ASSERT, &asrt_stats);
|
||||
log(assert, &asrt_stats);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool handle_message(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
|
||||
(void) sctx;
|
||||
(void) ctx;
|
||||
(void) msg;
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -28,19 +28,20 @@
|
|||
|
||||
// order matters here
|
||||
enum client_state {
|
||||
CS_BIRTH = 1,
|
||||
CS_PRE_INIT,
|
||||
CS_PRE_TEST,
|
||||
CS_POST_TEST,
|
||||
CS_POST_FINI,
|
||||
CS_DEATH,
|
||||
CS_SETUP,
|
||||
CS_MAIN,
|
||||
CS_TEARDOWN,
|
||||
CS_END,
|
||||
|
||||
CS_PRE_SUBTEST,
|
||||
CS_POST_SUBTEST,
|
||||
|
||||
CS_MAX_CLIENT_STATES // always leave at the end
|
||||
// The states belows are non-states that should not be
|
||||
// added in the state count
|
||||
CS_ABORT,
|
||||
CS_TIMEOUT,
|
||||
};
|
||||
|
||||
// always make it a power of 2
|
||||
# define CS_MAX_CLIENT_STATES 4
|
||||
|
||||
enum client_kind {
|
||||
WORKER,
|
||||
EXTERN,
|
||||
|
@ -51,6 +52,7 @@ struct client_ctx {
|
|||
struct worker *worker;
|
||||
|
||||
enum client_state state;
|
||||
bool alive;
|
||||
struct criterion_global_stats *gstats;
|
||||
struct criterion_suite_stats *sstats;
|
||||
struct criterion_test_stats *tstats;
|
||||
|
@ -61,6 +63,7 @@ struct client_ctx {
|
|||
typedef struct kh_ht_client_s khash_t(ht_client);
|
||||
|
||||
struct server_ctx {
|
||||
int socket;
|
||||
khash_t(ht_client) *subprocesses;
|
||||
};
|
||||
|
||||
|
|
|
@ -267,6 +267,8 @@ static struct client_ctx *spawn_next_client(struct server_ctx *sctx, ccrContext
|
|||
return add_client_from_worker(sctx, &new_ctx, w);
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void run_tests_async(struct criterion_test_set *set,
|
||||
struct criterion_global_stats *stats,
|
||||
int socket) {
|
||||
|
@ -275,10 +277,13 @@ static void run_tests_async(struct criterion_test_set *set,
|
|||
|
||||
size_t nb_workers = DEF(criterion_options.jobs, get_processor_count());
|
||||
size_t active_workers = 0;
|
||||
int has_msg = 0;
|
||||
|
||||
struct server_ctx sctx;
|
||||
init_server_context(&sctx);
|
||||
|
||||
sctx.socket = socket;
|
||||
|
||||
// initialization of coroutine
|
||||
run_next_test(set, stats, &ctx);
|
||||
|
||||
|
@ -296,11 +301,10 @@ static void run_tests_async(struct criterion_test_set *set,
|
|||
goto cleanup;
|
||||
|
||||
criterion_protocol_msg msg = criterion_protocol_msg_init_zero;
|
||||
while (read_message(socket, &msg) == 1) {
|
||||
while ((has_msg = read_message(socket, &msg)) == 1) {
|
||||
struct client_ctx *cctx = process_client_message(&sctx, &msg);
|
||||
if (cctx->state == CS_DEATH && cctx->kind == WORKER) {
|
||||
if (!cctx->alive && cctx->kind == WORKER) {
|
||||
remove_client_by_pid(&sctx, get_process_id_of(cctx->worker->proc));
|
||||
sfree(cctx->worker);
|
||||
|
||||
cctx = spawn_next_client(&sctx, &ctx);
|
||||
if (!is_runner())
|
||||
|
@ -312,11 +316,14 @@ static void run_tests_async(struct criterion_test_set *set,
|
|||
|
||||
if (!active_workers)
|
||||
break;
|
||||
|
||||
pb_release(criterion_protocol_msg_fields, &msg);
|
||||
}
|
||||
|
||||
destroy_server_context(&sctx);
|
||||
|
||||
cleanup:
|
||||
if (has_msg)
|
||||
pb_release(criterion_protocol_msg_fields, &msg);
|
||||
destroy_server_context(&sctx);
|
||||
ccrAbort(ctx);
|
||||
}
|
||||
|
||||
|
@ -342,6 +349,13 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) {
|
|||
abort();
|
||||
}
|
||||
|
||||
g_client_socket = connect_client();
|
||||
if (g_client_socket < 0) {
|
||||
criterion_perror("Could not initialize the message client: %s.\n",
|
||||
strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
init_proc_compat();
|
||||
|
||||
struct criterion_global_stats *stats = stats_init();
|
||||
|
|
|
@ -63,7 +63,7 @@ static struct worker *run_test(struct criterion_global_stats *stats,
|
|||
.suite_stats = sref(suite_stats),
|
||||
.param = param,
|
||||
};
|
||||
return spawn_test_worker(&ctx, run_test_child, g_worker_pipe);
|
||||
return spawn_test_worker(&ctx, run_test_child);
|
||||
}
|
||||
|
||||
static INLINE bool is_disabled(struct criterion_test *t,
|
||||
|
@ -109,9 +109,11 @@ struct worker *run_next_test(struct criterion_test_set *p_set,
|
|||
|
||||
ccrBegin(ctx);
|
||||
|
||||
ctx->set = p_set;
|
||||
ctx->stats = p_stats;
|
||||
ccrReturn(NULL);
|
||||
do {
|
||||
ctx->set = p_set;
|
||||
ctx->stats = p_stats;
|
||||
ccrReturn(NULL);
|
||||
} while (ctx->set == NULL && ctx->stats == NULL);
|
||||
|
||||
for (ctx->ns = ctx->set->suites->first; ctx->ns; ctx->ns = ctx->ns->next) {
|
||||
ctx->suite_set = (void*) (ctx->ns + 1);
|
||||
|
|
|
@ -108,6 +108,7 @@ static void destroy_test_stats(void *ptr, CR_UNUSED void *meta) {
|
|||
static void destroy_assert_stats(void *ptr, CR_UNUSED void *meta) {
|
||||
s_assert_stats *stats = ptr;
|
||||
free((void *) stats->message);
|
||||
free((void *) stats->file);
|
||||
}
|
||||
|
||||
s_test_stats *test_stats_init(struct criterion_test *t) {
|
||||
|
@ -192,6 +193,7 @@ static void push_assert(s_glob_stats *stats,
|
|||
.dtor = destroy_assert_stats);
|
||||
memcpy(dup, data, sizeof (s_assert_stats));
|
||||
dup->message = strdup(data->message);
|
||||
dup->file = strdup(data->file);
|
||||
|
||||
dup->next = test->asserts;
|
||||
test->asserts = dup;
|
||||
|
|
|
@ -27,18 +27,28 @@
|
|||
#include "core/worker.h"
|
||||
#include "core/report.h"
|
||||
#include "compat/time.h"
|
||||
#include "protocol/protocol.h"
|
||||
#include "protocol/messages.h"
|
||||
#include "io/event.h"
|
||||
|
||||
extern const struct criterion_test *criterion_current_test;
|
||||
extern const struct criterion_suite *criterion_current_suite;
|
||||
|
||||
static void send_event(int phase) {
|
||||
criterion_protocol_msg msg = criterion_message(phase,
|
||||
.phase = phase,
|
||||
.name = (char *) criterion_current_test->name,
|
||||
);
|
||||
cr_send_to_runner(&msg);
|
||||
}
|
||||
|
||||
static INLINE void nothing(void) {}
|
||||
|
||||
void criterion_internal_test_setup(void) {
|
||||
const struct criterion_suite *suite = criterion_current_suite;
|
||||
const struct criterion_test *test = criterion_current_test;
|
||||
|
||||
criterion_send_event(PRE_INIT, NULL, 0);
|
||||
send_event(criterion_protocol_phase_kind_SETUP);
|
||||
if (suite->data)
|
||||
(suite->data->init ? suite->data->init : nothing)();
|
||||
(test->data->init ? test->data->init : nothing)();
|
||||
|
@ -47,7 +57,7 @@ void criterion_internal_test_setup(void) {
|
|||
void criterion_internal_test_main(void (*fn)(void)) {
|
||||
const struct criterion_test *test = criterion_current_test;
|
||||
|
||||
criterion_send_event(PRE_TEST, NULL, 0);
|
||||
send_event(criterion_protocol_phase_kind_MAIN);
|
||||
|
||||
struct timespec_compat ts;
|
||||
if (!setjmp(g_pre_test)) {
|
||||
|
@ -64,7 +74,7 @@ void criterion_internal_test_main(void (*fn)(void)) {
|
|||
if (!timer_end(&elapsed_time, &ts))
|
||||
elapsed_time = -1;
|
||||
|
||||
criterion_send_event(POST_TEST, &elapsed_time, sizeof(double));
|
||||
send_event(criterion_protocol_phase_kind_TEARDOWN);
|
||||
}
|
||||
|
||||
void criterion_internal_test_teardown(void) {
|
||||
|
@ -74,6 +84,7 @@ void criterion_internal_test_teardown(void) {
|
|||
(test->data->fini ? test->data->fini : nothing)();
|
||||
if (suite->data)
|
||||
(suite->data->fini ? suite->data->fini : nothing)();
|
||||
criterion_send_event(POST_FINI, NULL, 0);
|
||||
|
||||
send_event(criterion_protocol_phase_kind_END);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,10 @@
|
|||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include "criterion/theories.h"
|
||||
#include "protocol/protocol.h"
|
||||
#include "protocol/messages.h"
|
||||
#include "io/asprintf.h"
|
||||
#include "io/event.h"
|
||||
#include "abort.h"
|
||||
|
||||
struct criterion_theory_context {
|
||||
|
@ -172,12 +176,14 @@ static void format_arg(char (*arg)[1024], struct criterion_datapoints *dp, void
|
|||
}
|
||||
}
|
||||
|
||||
static void concat_arg(char (*msg)[4096], struct criterion_datapoints *dps, size_t *indices, size_t i) {
|
||||
# define BUFSIZE 4096
|
||||
|
||||
static void concat_arg(char (*msg)[BUFSIZE], struct criterion_datapoints *dps, size_t *indices, size_t i) {
|
||||
void *data = ((char*) dps[i].arr) + dps[i].size * indices[i];
|
||||
|
||||
char arg[1024];
|
||||
format_arg(&arg, dps + i, data);
|
||||
strncat(*msg, arg, sizeof (*msg) - 1);
|
||||
strncat(*msg, arg, BUFSIZE - 1);
|
||||
}
|
||||
|
||||
int try_call_theory(struct criterion_theory_context *ctx, void (*fnptr)(void)) {
|
||||
|
@ -194,8 +200,25 @@ void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*
|
|||
size_t *indices = malloc(sizeof (size_t) * datapoints);
|
||||
memset(indices, 0, datapoints * sizeof (size_t));
|
||||
|
||||
int round = 1;
|
||||
volatile bool has_next = true;
|
||||
while (has_next) {
|
||||
char *name = NULL;
|
||||
cr_asprintf(&name, "%s::%d", criterion_current_test->name, round);
|
||||
|
||||
criterion_protocol_msg setup_msg = criterion_message(phase,
|
||||
.phase = criterion_protocol_phase_kind_SETUP,
|
||||
.name = name,
|
||||
);
|
||||
cr_send_to_runner(&setup_msg);
|
||||
|
||||
criterion_protocol_msg main_msg = criterion_message(phase,
|
||||
.phase = criterion_protocol_phase_kind_MAIN,
|
||||
.name = name,
|
||||
);
|
||||
cr_send_to_runner(&main_msg);
|
||||
|
||||
int theory_aborted = 0;
|
||||
if (!setjmp(theory_jmp)) {
|
||||
cr_theory_reset(ctx);
|
||||
for (size_t i = 0; i < datapoints; ++i) {
|
||||
|
@ -210,9 +233,11 @@ void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*
|
|||
}
|
||||
|
||||
if (!try_call_theory(ctx, fnptr)) {
|
||||
theory_aborted = 1;
|
||||
|
||||
struct {
|
||||
size_t len;
|
||||
char msg[4096];
|
||||
char msg[BUFSIZE];
|
||||
} result = { .len = 0 };
|
||||
|
||||
for (size_t i = 0; i < datapoints - 1; ++i) {
|
||||
|
@ -221,11 +246,32 @@ void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*
|
|||
}
|
||||
concat_arg(&result.msg, dps, indices, datapoints - 1);
|
||||
result.len = strlen(result.msg) + 1;
|
||||
result.msg[result.len] = '\0';
|
||||
|
||||
criterion_send_event(THEORY_FAIL, &result, result.len + sizeof(size_t));
|
||||
criterion_protocol_msg msg = criterion_message(phase,
|
||||
.phase = criterion_protocol_phase_kind_ABORT,
|
||||
.name = name,
|
||||
.message = result.msg
|
||||
);
|
||||
cr_send_to_runner(&msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!theory_aborted) {
|
||||
criterion_protocol_msg teardown_msg = criterion_message(phase,
|
||||
.phase = criterion_protocol_phase_kind_TEARDOWN,
|
||||
.name = name,
|
||||
);
|
||||
cr_send_to_runner(&teardown_msg);
|
||||
|
||||
criterion_protocol_msg end_msg = criterion_message(phase,
|
||||
.phase = criterion_protocol_phase_kind_END,
|
||||
.name = name,
|
||||
);
|
||||
cr_send_to_runner(&end_msg);
|
||||
}
|
||||
free(name);
|
||||
|
||||
for (size_t i = 0; i < datapoints; ++i) {
|
||||
if (indices[i] == dps[i].len - 1) {
|
||||
indices[i] = 0;
|
||||
|
@ -235,6 +281,7 @@ void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*
|
|||
break;
|
||||
}
|
||||
}
|
||||
++round;
|
||||
}
|
||||
|
||||
free(indices);
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "criterion/types.h"
|
||||
#include "criterion/options.h"
|
||||
#include "criterion/redirect.h"
|
||||
#include "protocol/protocol.h"
|
||||
#include "protocol/messages.h"
|
||||
#include "io/event.h"
|
||||
#include "compat/posix.h"
|
||||
#include "worker.h"
|
||||
|
@ -51,7 +53,6 @@ bool is_runner(void) {
|
|||
|
||||
static void close_process(void *ptr, CR_UNUSED void *meta) {
|
||||
struct worker *proc = ptr;
|
||||
sfree(proc->in);
|
||||
sfree(proc->ctx.suite_stats);
|
||||
sfree(proc->ctx.test_stats);
|
||||
sfree(proc->ctx.stats);
|
||||
|
@ -61,9 +62,18 @@ static void close_process(void *ptr, CR_UNUSED void *meta) {
|
|||
void run_worker(struct worker_context *ctx) {
|
||||
cr_redirect_stdin();
|
||||
g_client_socket = connect_client();
|
||||
if (g_client_socket < 0) {
|
||||
criterion_perror("Could not initialize the message client: %s.\n",
|
||||
strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
// Notify the runner that the test was born
|
||||
criterion_protocol_msg msg = criterion_message(birth);
|
||||
cr_send_to_runner(&msg);
|
||||
|
||||
ctx->func(ctx->test, ctx->suite);
|
||||
nn_close(g_client_socket);
|
||||
close_socket(g_client_socket);
|
||||
|
||||
fflush(NULL); // flush all opened streams
|
||||
if (criterion_options.no_early_exit)
|
||||
|
@ -72,13 +82,11 @@ void run_worker(struct worker_context *ctx) {
|
|||
}
|
||||
|
||||
struct worker *spawn_test_worker(struct execution_context *ctx,
|
||||
cr_worker_func func,
|
||||
s_pipe_handle *pipe) {
|
||||
cr_worker_func func) {
|
||||
g_worker_context = (struct worker_context) {
|
||||
.test = ctx->test,
|
||||
.suite = ctx->suite,
|
||||
.func = func,
|
||||
.pipe = pipe,
|
||||
.param = ctx->param,
|
||||
};
|
||||
|
||||
|
@ -103,24 +111,7 @@ struct worker *spawn_test_worker(struct execution_context *ctx,
|
|||
|
||||
*ptr = (struct worker) {
|
||||
.proc = proc,
|
||||
.in = pipe_in_handle(pipe, PIPE_DUP),
|
||||
.ctx = *ctx,
|
||||
};
|
||||
return ptr;
|
||||
}
|
||||
|
||||
struct process_status get_status(int status) {
|
||||
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 };
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
# include <stdbool.h>
|
||||
# include "criterion/types.h"
|
||||
# include "compat/process.h"
|
||||
# include "compat/pipe.h"
|
||||
|
||||
struct test_single_param {
|
||||
size_t size;
|
||||
|
@ -50,7 +49,6 @@ struct execution_context {
|
|||
struct worker {
|
||||
int active;
|
||||
s_proc_handle *proc;
|
||||
s_pipe_file_handle *in;
|
||||
struct execution_context ctx;
|
||||
};
|
||||
|
||||
|
@ -75,17 +73,11 @@ struct worker_set {
|
|||
size_t max_workers;
|
||||
};
|
||||
|
||||
extern s_pipe_handle *g_worker_pipe;
|
||||
|
||||
void run_worker(struct worker_context *ctx);
|
||||
void set_runner_process(void);
|
||||
void unset_runner_process(void);
|
||||
bool is_runner(void);
|
||||
struct process_status wait_proc(struct worker *proc);
|
||||
struct process_status get_status(int status);
|
||||
struct worker *spawn_test_worker(struct execution_context *ctx,
|
||||
cr_worker_func func,
|
||||
s_pipe_handle *pipe);
|
||||
struct event *worker_read_event(struct worker_set *workers, s_pipe_file_handle *pipe);
|
||||
struct worker *spawn_test_worker(struct execution_context *ctx, cr_worker_func func);
|
||||
|
||||
#endif /* !PROCESS_H_ */
|
||||
|
|
30
src/io/asprintf.h
Normal file
30
src/io/asprintf.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef ASPRINTF_H_
|
||||
# define ASPRINTF_H_
|
||||
|
||||
int cr_vasprintf(char **strp, const char *fmt, va_list ap);
|
||||
int cr_asprintf(char **strp, const char *fmt, ...);
|
||||
|
||||
#endif /* !ASPRINTF_H_ */
|
|
@ -25,15 +25,18 @@
|
|||
#include "protocol/protocol.h"
|
||||
#include "protocol/messages.h"
|
||||
#include "event.h"
|
||||
#include "assert.h"
|
||||
|
||||
int g_client_socket = -1;
|
||||
|
||||
void criterion_send_assert(struct criterion_assert_stats *stats) {
|
||||
assert(stats->message);
|
||||
criterion_protocol_msg msg = criterion_message(assert,
|
||||
.message = stats->message,
|
||||
.message = (char *) stats->message,
|
||||
.passed = stats->passed,
|
||||
.file = stats->file,
|
||||
.file = (char *) stats->file,
|
||||
.has_line = true,
|
||||
.line = stats->line,
|
||||
);
|
||||
write_message(g_client_socket, &msg);
|
||||
cr_send_to_runner(&msg);
|
||||
}
|
||||
|
|
|
@ -40,9 +40,6 @@ struct event {
|
|||
size_t worker_index;
|
||||
};
|
||||
|
||||
enum other_event_kinds {
|
||||
WORKER_TERMINATED = 1 << 30,
|
||||
TEST_ABORT,
|
||||
};
|
||||
void criterion_send_assert(struct criterion_assert_stats *stats);
|
||||
|
||||
#endif /* !EVENT_H_ */
|
||||
|
|
|
@ -23,20 +23,20 @@
|
|||
*/
|
||||
#include <errno.h>
|
||||
#include <nanomsg/nn.h>
|
||||
#include <nanomsg/pubsub.h>
|
||||
#include <nanomsg/reqrep.h>
|
||||
|
||||
#define URL "ipc://criterion.sock"
|
||||
|
||||
#define errno_ignore(Stmt) do { int err = errno; Stmt; errno = err; } while (0)
|
||||
|
||||
int bind_server(void) {
|
||||
int sock = nn_socket(AF_SP, NN_SUB);
|
||||
int fstrat = NN_FORK_RESET;
|
||||
nn_setopt(NN_FORK_STRATEGY, &fstrat, sizeof (fstrat));
|
||||
|
||||
int sock = nn_socket(AF_SP, NN_REP);
|
||||
if (sock < 0)
|
||||
return -1;
|
||||
|
||||
if (nn_setsockopt(sock, NN_SUB, NN_SUB_SUBSCRIBE, "", 0) < 0)
|
||||
goto error;
|
||||
|
||||
if (nn_bind(sock, URL) < 0)
|
||||
goto error;
|
||||
|
||||
|
@ -48,7 +48,7 @@ error: {}
|
|||
}
|
||||
|
||||
int connect_client(void) {
|
||||
int sock = nn_socket(AF_SP, NN_PUB);
|
||||
int sock = nn_socket(AF_SP, NN_REQ);
|
||||
if (sock < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -61,3 +61,7 @@ error: {}
|
|||
errno_ignore(nn_close(sock));
|
||||
return -1;
|
||||
}
|
||||
|
||||
void close_socket(int sock) {
|
||||
nn_close(sock);
|
||||
}
|
||||
|
|
|
@ -26,5 +26,6 @@
|
|||
|
||||
int connect_client(void);
|
||||
int bind_server(void);
|
||||
void close_socket(int sock);
|
||||
|
||||
#endif /* !CONNECT_H_ */
|
||||
|
|
|
@ -24,10 +24,13 @@
|
|||
#include <nanomsg/nn.h>
|
||||
#include <stdlib.h>
|
||||
#include "protocol/protocol.h"
|
||||
#include "criterion/logging.h"
|
||||
#include "io/event.h"
|
||||
#include "io/asprintf.h"
|
||||
|
||||
int read_message(int sock, criterion_protocol_msg *message) {
|
||||
int res;
|
||||
unsigned char *buf;
|
||||
unsigned char *buf = NULL;
|
||||
int read = res = nn_recv(sock, &buf, NN_MSG, 0);
|
||||
|
||||
if (read <= 0)
|
||||
|
@ -41,7 +44,8 @@ int read_message(int sock, criterion_protocol_msg *message) {
|
|||
|
||||
res = 1;
|
||||
cleanup:
|
||||
nn_freemsg(buf);
|
||||
if (buf)
|
||||
nn_freemsg(buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -66,3 +70,79 @@ cleanup:
|
|||
free(buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
const char *message_names[] = {
|
||||
[criterion_protocol_submessage_birth_tag] = "birth",
|
||||
[criterion_protocol_submessage_phase_tag] = "phase",
|
||||
[criterion_protocol_submessage_death_tag] = "death",
|
||||
[criterion_protocol_submessage_message_tag] = "message",
|
||||
[criterion_protocol_submessage_assert_tag] = "assert",
|
||||
};
|
||||
|
||||
void cr_send_to_runner(const criterion_protocol_msg *message) {
|
||||
if (write_message(g_client_socket, message) != 1) {
|
||||
criterion_perror("Could not write the \"%s\" message down the event pipe: %s.\n",
|
||||
message_names[message->data.which_value],
|
||||
strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
unsigned char *buf = NULL;
|
||||
int read = nn_recv(g_client_socket, &buf, NN_MSG, 0);
|
||||
|
||||
if (read <= 0) {
|
||||
criterion_perror("Could not read ack: %s.\n", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
criterion_protocol_ack ack;
|
||||
pb_istream_t stream = pb_istream_from_buffer(buf, read);
|
||||
if (!pb_decode(&stream, criterion_protocol_ack_fields, &ack)) {
|
||||
criterion_perror("Could not decode ack: %s.\n", PB_GET_ERROR(&stream));
|
||||
abort();
|
||||
}
|
||||
|
||||
if (ack.status_code != criterion_protocol_ack_status_OK) {
|
||||
criterion_perror("Runner returned an error: %s.\n", ack.message ? ack.message : "Unknown error");
|
||||
abort();
|
||||
}
|
||||
|
||||
if (buf)
|
||||
nn_freemsg(buf);
|
||||
}
|
||||
|
||||
void send_ack(int sock, bool ok, const char *msg, ...) {
|
||||
criterion_protocol_ack ack;
|
||||
ack.status_code = ok ? criterion_protocol_ack_status_OK : criterion_protocol_ack_status_ERROR;
|
||||
ack.message = NULL;
|
||||
|
||||
if (!ok) {
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
if (cr_vasprintf(&ack.message, msg, ap) < 0)
|
||||
ack.message = NULL;
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
size_t size;
|
||||
unsigned char *buf = NULL;
|
||||
if (!pb_get_encoded_size(&size, criterion_protocol_ack_fields, &ack)) {
|
||||
criterion_perror("Could not calculate the size of an ack.\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
buf = malloc(size);
|
||||
pb_ostream_t stream = pb_ostream_from_buffer(buf, size);
|
||||
if (!pb_encode(&stream, criterion_protocol_ack_fields, &ack)) {
|
||||
criterion_perror("Could not encode ack: %s.\n", PB_GET_ERROR(&stream));
|
||||
abort();
|
||||
}
|
||||
|
||||
int written = nn_send(sock, buf, size, 0);
|
||||
if (written <= 0 || written != (int) size) {
|
||||
criterion_perror("Could not send ack: %s.\n", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,11 @@
|
|||
#ifndef MESSAGES_H_
|
||||
# define MESSAGES_H_
|
||||
|
||||
# include "criterion.pb.h"
|
||||
|
||||
int write_message(int sock, const criterion_protocol_msg *message);
|
||||
int read_message(int sock, criterion_protocol_msg *message);
|
||||
void cr_send_to_runner(const criterion_protocol_msg *message);
|
||||
void send_ack(int sock, bool ok, const char *msg, ...);
|
||||
|
||||
#endif /* !MESSAGES_H_ */
|
||||
|
|
|
@ -24,11 +24,16 @@
|
|||
#ifndef PROTOCOL_H_
|
||||
# define PROTOCOL_H_
|
||||
|
||||
# include <pb.h>
|
||||
# include <pb_encode.h>
|
||||
# include <pb_decode.h>
|
||||
# include "criterion.pb.h"
|
||||
# include "criterion/internal/preprocess.h"
|
||||
|
||||
enum protocol_version {
|
||||
PROTOCOL_V1 = 1,
|
||||
};
|
||||
|
||||
bool pb_write_string(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
|
||||
bool pb_read_string(pb_istream_t *stream, const pb_field_t *field, void **arg);
|
||||
|
||||
|
@ -45,7 +50,7 @@ pb_istream_t pb_istream_from_fd(int fd);
|
|||
.data = { \
|
||||
.which_value = criterion_protocol_submessage_ ## Kind ## _tag, \
|
||||
.value = { \
|
||||
.Kind = { CR_EXPAND(__VA_ARGS__) }, \
|
||||
.Kind = { __VA_ARGS__ }, \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue