Merge branch 'features/io-rewrite-nanopb' into bleeding

This commit is contained in:
Snaipe 2016-01-18 01:35:55 +01:00
commit e638cf3d43
40 changed files with 1771 additions and 476 deletions

View File

@ -26,31 +26,4 @@ else()
)
endif()
### Then, checkout each submodule to the specified commit
# Note: Execute separate processes here, to make sure each one is run,
# should one crash (because of branch not existing, this, that ... whatever)
foreach(GIT_SUBMODULE ${GIT_SUBMODULES})
if( "${GIT_SUBMODULE_VERSION_${GIT_SUBMODULE}}" STREQUAL "" )
message(STATUS "no specific version given for submodule ${GIT_SUBMODULE}, checking out master")
set(GIT_SUBMODULE_VERSION_${GIT_SUBMODULE} "master")
endif()
if(${GIT_SUBMODULES_CHECKOUT_QUIET})
execute_process(
COMMAND git checkout ${GIT_SUBMODULE_VERSION_${GIT_SUBMODULE}}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/${GIT_SUBMODULES_DIRECTORY}/${GIT_SUBMODULE}
OUTPUT_QUIET
ERROR_QUIET
)
else()
message(STATUS "checking out ${GIT_SUBMODULE}'s commit/tag ${GIT_SUBMODULE_VERSION_${GIT_SUBMODULE}}")
execute_process(
COMMAND git checkout -q ${GIT_SUBMODULE_VERSION_${GIT_SUBMODULE}}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/${GIT_SUBMODULES_DIRECTORY}/${GIT_SUBMODULE}
)
endif()
endforeach(${GIT_SUBMODULE})
endif()

1
.gitignore vendored
View File

@ -18,6 +18,7 @@
!*.po
!*.in
!*.t
!*.proto
!.cmake/Modules/*.cmake
!samples/tests/*.sh
!samples/**/*.expected

9
.gitmodules vendored
View File

@ -10,3 +10,12 @@
[submodule "dependencies/klib"]
path = dependencies/klib
url = https://github.com/attractivechaos/klib.git
[submodule "dependencies/nanopb"]
path = dependencies/nanopb
url = https://github.com/nanopb/nanopb.git
[submodule "dependencies/nanomsg"]
path = dependencies/nanomsg
url = https://github.com/nanomsg/nanomsg.git
[submodule "dependencies/nanomsg-patched"]
path = dependencies/nanomsg-patched
url = https://github.com/Snaipe/nanomsg.git

View File

@ -133,7 +133,7 @@ script:
- |
if [ "${TESTS:-ON}" = "ON" ]; then
TERM=dumb cmake --build . --target criterion_tests -- -j4
ctest -j4
ctest
else
TERM=dumb cmake --build . -- -j4
fi

View File

@ -22,6 +22,52 @@ endif ()
add_subdirectory(dependencies/libcsptr/ EXCLUDE_FROM_ALL)
include(ExternalProject)
include(CMakeParseArguments)
function (add_cmake_subproject _NAME)
set (options)
set (oneValueArgs GIT PATH PREFIX)
set (multiValueArgs OPTS)
cmake_parse_arguments (ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if (NOT "${ARGS_PREFIX}" STREQUAL "")
set (install_prefix ${CMAKE_BINARY_DIR}/external/${ARGS_PREFIX})
else ()
set (install_prefix ${CMAKE_BINARY_DIR}/external)
endif ()
if (NOT "${ARGS_GIT}" STREQUAL "")
string(REPLACE "#" ";" git_opts ${ARGS_GIT})
list(LENGTH ${git_opts} git_opts_len)
list(GET 0 repo)
set (epa_opts GIT_REPOSITORY "${repo}")
if (git_opts_len GREATER 1)
list(GET 1 object)
set (epa_opts ${epa_opts} GIT_TAG "${object}")
endif ()
elseif (NOT "${ARGS_PATH}" STREQUAL "")
set (epa_opts SOURCE_DIR "${CMAKE_SOURCE_DIR}/${ARGS_PATH}")
endif ()
externalproject_add(
${_NAME}
${epa_opts}
BINARY_DIR "${CMAKE_BINARY_DIR}/${_NAME}"
CONFIGURE_COMMAND ${CMAKE_COMMAND} <SOURCE_DIR>
-DCMAKE_INSTALL_PREFIX=${install_prefix}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
${ARGS_OPTS}
BUILD_COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}/${_NAME}"
INSTALL_COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}/${_NAME}" --target install
)
endfunction ()
include_directories(${CMAKE_BINARY_DIR}/external/include)
link_directories(${CMAKE_BINARY_DIR}/external/lib)
if (THEORIES)
add_subdirectory(dependencies/dyncall/ EXCLUDE_FROM_ALL)
include_directories(dependencies/dyncall/dyncall/)
@ -32,10 +78,39 @@ include_directories(SYSTEM
/usr/include/GNUstep
)
if (MSVC)
add_cmake_subproject(nanomsg
PATH dependencies/nanomsg-patched
OPTS "-DNN_TESTS=OFF")
else ()
include(ExternalProject)
externalproject_add(
nanomsg
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/nanomsg-patched"
PREFIX "${CMAKE_CURRENT_BINARY_DIR}/nanomsg-patched"
UPDATE_COMMAND <SOURCE_DIR>/autogen.sh
CONFIGURE_COMMAND <SOURCE_DIR>/configure --prefix=<INSTALL_DIR>
BUILD_COMMAND make -j4
INSTALL_COMMAND make install
)
externalproject_get_property(nanomsg install_dir)
set(NANOMSG_INSTALL_DIR "${install_dir}")
include_directories(${CMAKE_CURRENT_BINARY_DIR}/nanomsg-patched/include/)
link_directories(${CMAKE_CURRENT_BINARY_DIR}/nanomsg-patched/lib/)
endif ()
include_directories(
dependencies/libcsptr/include/
dependencies/valgrind/include/
dependencies/klib/
dependencies/nanopb/
)
if (MSVC)
@ -74,6 +149,7 @@ set(GettextTranslate_ALL 1)
set(GettextTranslate_GMO_BINARY 1)
add_definitions(-DCRITERION_BUILDING_DLL=1)
add_definitions(-DPB_ENABLE_MALLOC=1)
set(CMAKE_C_FLAGS_DEFAULT "${CMAKE_C_FLAGS}")
set(CMAKE_CXX_FLAGS_DEFAULT "${CMAKE_CXX_FLAGS}")
@ -150,6 +226,8 @@ set(SOURCE_FILES
src/core/stats.h
src/core/ordered-set.c
src/core/test.c
src/core/client.c
src/core/client.h
src/compat/internal.h
src/compat/pipe.c
src/compat/pipe.h
@ -187,8 +265,22 @@ set(SOURCE_FILES
src/entry/options.c
src/entry/params.c
src/entry/entry.c
src/protocol/criterion.pb.c
src/protocol/criterion.pb.h
src/protocol/protocol.c
src/protocol/messages.c
src/protocol/messages.h
src/protocol/connect.c
src/protocol/connect.h
src/common.h
src/config.h
dependencies/nanopb/pb_common.c
dependencies/nanopb/pb_common.h
dependencies/nanopb/pb_encode.c
dependencies/nanopb/pb_encode.h
dependencies/nanopb/pb_decode.c
dependencies/nanopb/pb_decode.h
)
if (THEORIES)
@ -250,13 +342,28 @@ configure_file(
include_directories(include src)
add_library(criterion SHARED ${SOURCE_FILES} ${INTERFACE_FILES})
set_target_properties(criterion PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_SOVERSION}
)
add_dependencies(criterion nanomsg)
target_link_libraries(criterion csptr)
if (WIN32)
target_link_libraries(criterion ${CMAKE_BINARY_DIR}/external/lib/nanomsg.lib)
else ()
target_link_libraries(criterion ${CMAKE_CURRENT_BINARY_DIR}/nanomsg-patched/lib/libnanomsg.a)
endif ()
if (NOT WIN32)
target_link_libraries(criterion csptr pthread)
if (NOT APPLE)
target_link_libraries(criterion csptr anl)
endif ()
endif ()
if (THEORIES)
target_link_libraries(criterion dyncall_s)
endif ()

View File

@ -4,7 +4,7 @@ os: Visual Studio 2015
init:
- git config --global core.autocrlf input
- 'SET PATH=C:\MinGW\bin;%PATH%;C:\MinGW\msys\1.0\bin;%APPVEYOR_BUILD_FOLDER%\build;%APPVEYOR_BUILD_FOLDER%\build\Debug'
- 'SET PATH=C:\MinGW\bin;%PATH%;C:\MinGW\msys\1.0\bin;%APPVEYOR_BUILD_FOLDER%\build;%APPVEYOR_BUILD_FOLDER%\build\Debug;%APPVEYOR_BUILD_FOLDER%\build\external\bin;%APPVEYOR_BUILD_FOLDER%\build\external\lib'
environment:
COVERALLS_REPO_TOKEN:
@ -63,7 +63,7 @@ before_deploy:
test_script:
- cmake --build . --target criterion_tests
- ps: |
ctest -j2
ctest
if (-not $lastexitcode -eq 0) {
type Testing/Temporary/LastTest.log
}

1
dependencies/nanomsg vendored Submodule

@ -0,0 +1 @@
Subproject commit fd66ff55a5bad44ea0c3cca8bea345b6f02663bf

1
dependencies/nanomsg-patched vendored Submodule

@ -0,0 +1 @@
Subproject commit e0d49e180b93ad1557f447d43654d28793512aca

1
dependencies/nanopb vendored Submodule

@ -0,0 +1 @@
Subproject commit 56f7c488df99ae655b47b5838055e48b886665a1

View File

@ -30,10 +30,11 @@
# include <stddef.h>
# endif
# include "internal/common.h"
# include "stats.h"
CR_BEGIN_C_API
CR_API void criterion_send_event(int kind, void *data, size_t size);
CR_API void criterion_send_assert(struct criterion_assert_stats *stats);
CR_END_C_API

View File

@ -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(); \

View File

@ -105,6 +105,13 @@ struct criterion_options {
* default: true
*/
bool measure_time;
/**
* Whether criterion should wait for incoming connections in server mode
*
* default: false
*/
bool wait_for_clients;
};
CR_BEGIN_C_API

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: criterion 2.0.0\n"
"Report-Msgid-Bugs-To: franklinmathieu+criterion@gmail.com\n"
"POT-Creation-Date: 2015-11-27 12:24+0100\n"
"POT-Creation-Date: 2016-01-18 01:27+0100\n"
"PO-Revision-Date: 2015-04-03 17:58+0200\n"
"Last-Translator: <franklinmathieu@gmail.com>\n"
"Language-Team: French\n"
@ -185,7 +185,7 @@ msgstr "L'instruction `%1$s` a levé une instance de l'exception `%2$s`."
msgid "The statement `%1$s` did not throw an instance of the `%2$s` exception."
msgstr "L'instruction `%1$s` n'a pas levé d'instance de l'exception `%2$s`."
#: src/core/runner.c:58
#: src/core/runner.c:64
#, c-format
msgid ""
"%1$sWarning! Criterion has detected that it is running under valgrind, but "
@ -196,7 +196,7 @@ msgstr ""
"l'option no_early_exit est explicitement désactivée. Les rapports d'erreur "
"ne seront pas précis!%2$s\n"
#: src/core/runner.c:62
#: src/core/runner.c:68
#, c-format
msgid ""
"%1$sWarning! Criterion has detected that it is running under valgrind, but "

View File

@ -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"
@ -101,7 +103,6 @@ struct full_context {
struct criterion_suite suite;
struct criterion_test_extra_data suite_data;
cr_worker_func func;
struct pipe_handle pipe;
struct test_single_param param;
HANDLE sync;
DWORD extra_size;
@ -118,28 +119,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);
}
}
@ -201,20 +198,24 @@ static void CALLBACK handle_child_terminated(PVOID lpParameter,
struct wait_context *wctx = lpParameter;
int status = get_win_status(wctx->proc_handle);
int kind = WORKER_TERMINATED;
struct worker_status ws = {
(s_proc_handle) { wctx->proc_handle }, get_status(status)
};
unsigned long long pid_ull = (unsigned long long) GetProcessId(wctx->proc_handle);
int64_t pid = (int64_t) GetProcessId(wctx->proc_handle);
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));
int result = WIFEXITED(status)
? criterion_protocol_death_result_type_NORMAL
: criterion_protocol_death_result_type_CRASH;
int code = WIFEXITED(status)
? WEXITSTATUS(status)
: WTERMSIG(status);
DWORD written;
WriteFile(g_worker_pipe->fhs[1], buf, sizeof (buf), &written, NULL);
criterion_protocol_msg msg = criterion_message(death,
.result = result,
.has_status = true,
.status = code,
);
msg.id.pid = pid;
cr_send_to_runner(&msg);
HANDLE whandle = wctx->wait_handle;
free(lpParameter);
@ -279,7 +280,6 @@ int resume_child(void) {
.test = &local_ctx.test,
.suite = &local_ctx.suite,
.func = local_ctx.func,
.pipe = &local_ctx.pipe,
.param = param,
};
@ -357,7 +357,6 @@ s_proc_handle *fork_process() {
.test_data = *g_worker_context.test->data,
.suite = *g_worker_context.suite,
.func = g_worker_context.func,
.pipe = *g_worker_context.pipe,
.sync = sync,
};

View File

@ -43,7 +43,6 @@ struct worker_context {
struct criterion_test *test;
struct criterion_suite *suite;
cr_worker_func func;
struct pipe_handle *pipe;
struct test_single_param *param;
};

View File

@ -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,14 @@ 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,
);
criterion_message_set_id(abort_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);

483
src/core/client.c Normal file
View File

@ -0,0 +1,483 @@
/*
* 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.
*/
#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"
#include "report.h"
#include "stats.h"
#include "client.h"
static void nothing(void) {};
KHASH_MAP_INIT_INT(ht_client, struct client_ctx)
KHASH_MAP_INIT_STR(ht_extern, struct client_ctx)
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_SETUP] = "setup",
[CS_MAIN] = "main",
[CS_TEARDOWN] = "teardown",
[CS_END] = "end",
[CS_ABORT] = "abort",
[CS_TIMEOUT] = "timeout",
};
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 *);
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_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) {
switch (msg->which_id) {
case criterion_protocol_msg_pid_tag:
snprintf(out, n, "[PID %" PRId64 "]", msg->id.pid); return;
case criterion_protocol_msg_uid_tag:
snprintf(out, n, "[external \"%s\"]", msg->id.uid); return;
default: break;
}
}
void init_server_context(struct server_ctx *sctx, struct criterion_global_stats *gstats) {
sctx->subprocesses = kh_init(ht_client);
sctx->clients = kh_init(ht_extern);
sctx->gstats = gstats;
sctx->extern_suite = (struct criterion_suite) {
.name = "external",
.data = &sctx->extern_suite_data,
};
sctx->extern_suite_data = (struct criterion_test_extra_data) {
.disabled = 0,
};
sctx->extern_sstats = suite_stats_init(&sctx->extern_suite);
}
void destroy_client_context(struct client_ctx *ctx) {
if (ctx->kind == WORKER)
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);
kh_destroy(ht_extern, sctx->clients);
}
struct client_ctx *add_client_from_worker(struct server_ctx *sctx, struct client_ctx *ctx, struct worker *w) {
unsigned long long pid = get_process_id_of(w->proc);
int absent;
khint_t k = kh_put(ht_client, sctx->subprocesses, pid, &absent);
ctx->worker = w;
ctx->kind = WORKER;
kh_value(sctx->subprocesses, k) = *ctx;
return &kh_value(sctx->subprocesses, k);
}
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)) {
destroy_client_context(&kh_value(sctx->subprocesses, k));
kh_del(ht_client, sctx->subprocesses, k);
}
}
struct client_ctx *add_external_client(struct server_ctx *sctx, char *id) {
int absent;
khint_t k = kh_put(ht_extern, sctx->clients, id, &absent);
kh_value(sctx->clients, k) = (struct client_ctx) {
.kind = EXTERN,
.extern_test = {
.name = strdup(id),
.category = "external",
},
.gstats = sctx->gstats,
.sstats = sctx->extern_sstats,
};
struct client_ctx *ctx = &kh_value(sctx->clients, k);
ctx->test = &ctx->extern_test;
ctx->suite = &sctx->extern_suite;
ctx->extern_test.data = &ctx->extern_test_data;
ctx->tstats = test_stats_init(&ctx->extern_test);
return ctx;
}
static void process_client_message_impl(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
message_handler *handler = message_handlers[msg->data.which_value];
bool ack = false;
if (handler)
ack = handler(sctx, ctx, msg);
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) {
handler_error(ctx, "%s", "", "Received message using invalid protocol version number '%" PRIi32 "'.", msg->version);
return NULL;
}
struct client_ctx *client = NULL;
switch (msg->which_id) {
case criterion_protocol_msg_pid_tag: {
khiter_t k = kh_get(ht_client, ctx->subprocesses, msg->id.pid);
if (k != kh_end(ctx->subprocesses)) {
client = &kh_value(ctx->subprocesses, k);
} else {
handler_error(ctx, "%s", "", "Received message identified by a PID '%" PRIi64 "' "
"that is not a child process.", msg->id.pid);
}
} break;
case criterion_protocol_msg_uid_tag: {
khiter_t k = kh_get(ht_extern, ctx->clients, msg->id.uid);
bool client_found = k != kh_end(ctx->clients);
if (!client_found && msg->data.which_value == criterion_protocol_submessage_birth_tag) {
client = add_external_client(ctx, msg->id.uid);
} else if (client_found) {
client = &kh_value(ctx->clients, k);
} else {
handler_error(ctx, "%s", "", "Received message identified by the ID '%s'"
"that did not send a birth message previously.", msg->id.uid);
}
} break;
default: {
handler_error(ctx, "%s", "", "Received message with malformed id tag '%d'.\n",
criterion_protocol_msg_pid_tag);
} break;
}
if (client)
process_client_message_impl(ctx, client, msg);
return client;
}
#define push_event(...) \
do { \
push_event_noreport(__VA_ARGS__); \
report(CR_VA_HEAD(__VA_ARGS__), ctx->tstats); \
} while (0)
#define push_event_noreport(...) \
do { \
stat_push_event(ctx->gstats, \
ctx->sstats, \
ctx->tstats, \
&(struct event) { \
.kind = CR_VA_HEAD(__VA_ARGS__), \
CR_VA_TAIL(__VA_ARGS__) \
}); \
} while (0)
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;
}
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;
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;
}
# define MAX_TEST_DEPTH 16
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 & (0x3 << (MAX_TEST_DEPTH - 1) * 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;
}
bool handle_death(struct server_ctx *sctx, struct client_ctx *ctx, const criterion_protocol_msg *msg) {
(void) sctx;
ctx->alive = false;
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 (curstate != CS_MAIN) {
log(other_crash, ctx->tstats);
if (ctx->state < CS_MAIN) {
stat_push_event(ctx->gstats,
ctx->sstats,
ctx->tstats,
&(struct event) { .kind = TEST_CRASH });
}
} else {
ctx->tstats->signal = death->status;
if (ctx->test->data->signal == 0) {
push_event(TEST_CRASH);
log(test_crash, ctx->tstats);
} else {
double elapsed_time = 0;
push_event(POST_TEST, .data = &elapsed_time);
log(post_test, ctx->tstats);
push_event(POST_FINI);
log(post_fini, ctx->tstats);
}
}
} break;
case criterion_protocol_death_result_type_NORMAL: {
if (curstate == CS_TEARDOWN || ctx->state == CS_SETUP) {
log(abnormal_exit, ctx->tstats);
if (ctx->state == CS_SETUP) {
stat_push_event(ctx->gstats,
ctx->sstats,
ctx->tstats,
&(struct event) { .kind = TEST_CRASH });
}
break;
}
ctx->tstats->exit_code = death->status;
if (ctx->state == CS_MAIN) {
if (ctx->test->data->exit_code == 0) {
push_event(TEST_CRASH);
log(abnormal_exit, ctx->tstats);
} else {
double elapsed_time = 0;
push_event(POST_TEST, .data = &elapsed_time);
log(post_test, ctx->tstats);
push_event(POST_FINI);
log(post_fini, ctx->tstats);
}
}
} 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;
}

86
src/core/client.h Normal file
View File

@ -0,0 +1,86 @@
/*
* 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 CLIENT_H_
# define CLIENT_H_
# include <khash.h>
// order matters here
enum client_state {
CS_SETUP,
CS_MAIN,
CS_TEARDOWN,
CS_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,
};
struct client_ctx {
enum client_kind kind;
struct worker *worker;
struct criterion_test_extra_data extern_test_data;
struct criterion_test extern_test;
uint32_t state;
bool alive;
struct criterion_global_stats *gstats;
struct criterion_suite_stats *sstats;
struct criterion_test_stats *tstats;
struct criterion_test *test;
struct criterion_suite *suite;
};
struct kh_ht_client_s;
struct kh_ht_extern_s;
struct server_ctx {
int socket;
struct criterion_suite extern_suite;
struct criterion_test_extra_data extern_suite_data;
struct criterion_global_stats *gstats;
struct criterion_suite_stats *extern_sstats;
struct kh_ht_client_s *subprocesses;
struct kh_ht_extern_s *clients;
};
struct client_ctx *process_client_message(struct server_ctx *ctx, const criterion_protocol_msg *msg);
void init_server_context(struct server_ctx *sctx, struct criterion_global_stats *gstats);
void destroy_server_context(struct server_ctx *sctx);
struct client_ctx *add_client_from_worker(struct server_ctx *sctx, struct client_ctx *ctx, struct worker *w);
void remove_client_by_pid(struct server_ctx *sctx, int pid);
#endif /* !CLIENT_H_ */

View File

@ -27,11 +27,16 @@
#include <errno.h>
#include <csptr/smalloc.h>
#include <valgrind/valgrind.h>
#include <nanomsg/nn.h>
#include "criterion/internal/test.h"
#include "criterion/options.h"
#include "criterion/internal/ordered-set.h"
#include "criterion/logging.h"
#include "criterion/internal/preprocess.h"
#include "criterion/redirect.h"
#include "protocol/protocol.h"
#include "protocol/connect.h"
#include "protocol/messages.h"
#include "compat/time.h"
#include "compat/posix.h"
#include "compat/processor.h"
@ -47,6 +52,7 @@
#include "abort.h"
#include "config.h"
#include "common.h"
#include "client.h"
#ifdef HAVE_PCRE
#include "string/extmatch.h"
@ -165,6 +171,19 @@ const struct criterion_suite *criterion_current_suite;
void run_test_child(struct criterion_test *test,
struct criterion_suite *suite) {
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, .name = (char *) test->name);
criterion_message_set_id(msg);
cr_send_to_runner(&msg);
#ifndef ENABLE_VALGRIND_ERRORS
VALGRIND_ENABLE_ERROR_REPORTING;
#endif
@ -179,6 +198,17 @@ void run_test_child(struct criterion_test *test,
if (test->test)
test->test();
#ifndef ENABLE_VALGRIND_ERRORS
VALGRIND_DISABLE_ERROR_REPORTING;
#endif
close_socket(g_client_socket);
fflush(NULL); // flush all opened streams
if (criterion_options.no_early_exit)
return;
_Exit(0);
}
#define push_event(...) \
@ -195,135 +225,6 @@ void run_test_child(struct criterion_test *test,
s_pipe_handle *g_worker_pipe;
static void handle_worker_terminated(struct event *ev,
struct execution_context *ctx) {
struct worker_status *ws = ev->data;
struct process_status status = ws->status;
if (status.kind == SIGNAL) {
if (status.status == SIGPROF) {
ctx->test_stats->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->test_stats);
return;
}
if (ctx->normal_finish || !ctx->test_started) {
log(other_crash, ctx->test_stats);
if (!ctx->test_started) {
stat_push_event(ctx->stats,
ctx->suite_stats,
ctx->test_stats,
&(struct event) { .kind = TEST_CRASH });
}
return;
}
ctx->test_stats->signal = status.status;
if (ctx->test->data->signal == 0) {
push_event(TEST_CRASH);
log(test_crash, ctx->test_stats);
} else {
double elapsed_time = 0;
push_event(POST_TEST, .data = &elapsed_time);
log(post_test, ctx->test_stats);
push_event(POST_FINI);
log(post_fini, ctx->test_stats);
}
} else {
if (ctx->aborted) {
if (!ctx->normal_finish) {
double elapsed_time = 0;
push_event(POST_TEST, .data = &elapsed_time);
log(post_test, ctx->test_stats);
}
if (!ctx->cleaned_up) {
push_event(POST_FINI);
log(post_fini, ctx->test_stats);
}
return;
}
if ((ctx->normal_finish && !ctx->cleaned_up) || !ctx->test_started) {
log(abnormal_exit, ctx->test_stats);
if (!ctx->test_started) {
stat_push_event(ctx->stats,
ctx->suite_stats,
ctx->test_stats,
&(struct event) { .kind = TEST_CRASH });
}
return;
}
ctx->test_stats->exit_code = status.status;
if (!ctx->normal_finish) {
if (ctx->test->data->exit_code == 0) {
push_event(TEST_CRASH);
log(abnormal_exit, ctx->test_stats);
} else {
double elapsed_time = 0;
push_event(POST_TEST, .data = &elapsed_time);
log(post_test, ctx->test_stats);
push_event(POST_FINI);
log(post_fini, ctx->test_stats);
}
}
}
if (ctx->test_stats->failed && criterion_options.fail_fast) {
cr_terminate(ctx->stats);
}
}
static void handle_event(struct event *ev) {
struct execution_context *ctx = &ev->worker->ctx;
if (ev->kind < WORKER_TERMINATED)
stat_push_event(ctx->stats, ctx->suite_stats, ctx->test_stats, ev);
switch (ev->kind) {
case PRE_INIT:
report(PRE_INIT, ctx->test);
log(pre_init, ctx->test);
break;
case PRE_TEST:
report(PRE_TEST, ctx->test);
log(pre_test, ctx->test);
ctx->test_started = true;
break;
case THEORY_FAIL: {
struct criterion_theory_stats ths = {
.formatted_args = (char*) ev->data,
.stats = ctx->test_stats,
};
report(THEORY_FAIL, &ths);
log(theory_fail, &ths);
} break;
case ASSERT:
report(ASSERT, ev->data);
log(assert, ev->data);
break;
case TEST_ABORT:
log(test_abort, ctx->test_stats, ev->data);
ctx->test_stats->failed = 1;
ctx->aborted = true;
break;
case POST_TEST:
report(POST_TEST, ctx->test_stats);
log(post_test, ctx->test_stats);
ctx->normal_finish = true;
break;
case POST_FINI:
report(POST_FINI, ctx->test_stats);
log(post_fini, ctx->test_stats);
ctx->cleaned_up = true;
break;
case WORKER_TERMINATED:
handle_worker_terminated(ev, ctx);
break;
}
}
#ifdef HAVE_PCRE
void disable_unmatching(struct criterion_test_set *set) {
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
@ -377,63 +278,91 @@ void criterion_finalize(struct criterion_test_set *set) {
criterion_free_output();
}
static struct client_ctx *spawn_next_client(struct server_ctx *sctx, ccrContext *ctx) {
struct worker *w = ctx ? run_next_test(NULL, NULL, ctx) : NULL;
if (!is_runner() || w == NULL)
return NULL;
struct client_ctx new_ctx = (struct client_ctx) {
.test = w->ctx.test,
.tstats = w->ctx.test_stats,
.suite = w->ctx.suite,
.sstats = w->ctx.suite_stats,
.gstats = w->ctx.stats,
};
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) {
struct criterion_global_stats *stats,
int socket) {
ccrContext ctx = 0;
size_t nb_workers = DEF(criterion_options.jobs, get_processor_count());
struct worker_set workers = {
.max_workers = nb_workers,
.workers = calloc(nb_workers, sizeof (struct worker*)),
};
size_t active_workers = 0;
int has_msg = 0;
s_pipe_file_handle *event_pipe = pipe_in_handle(g_worker_pipe, PIPE_DUP);
struct event *ev = NULL;
struct server_ctx sctx;
init_server_context(&sctx, stats);
sctx.socket = socket;
// initialization of coroutine
run_next_test(set, stats, &ctx);
for (size_t i = 0; i < nb_workers; ++i) {
workers.workers[i] = run_next_test(NULL, NULL, &ctx);
struct client_ctx *cctx = spawn_next_client(&sctx, &ctx);
if (!is_runner())
goto cleanup;
if (!ctx)
if (!cctx)
break;
++active_workers;
}
if (!active_workers)
if (!active_workers && !criterion_options.wait_for_clients)
goto cleanup;
while ((ev = worker_read_event(&workers, event_pipe)) != NULL) {
handle_event(ev);
size_t wi = ev->worker_index;
if (ev->kind == WORKER_TERMINATED) {
sfree(workers.workers[wi]);
workers.workers[wi] = ctx ? run_next_test(NULL, NULL, &ctx) : NULL;
criterion_protocol_msg msg = criterion_protocol_msg_init_zero;
while ((has_msg = read_message(socket, &msg)) == 1) {
struct client_ctx *cctx = process_client_message(&sctx, &msg);
if (!is_runner())
goto cleanup;
// drop invalid messages
if (!cctx)
continue;
if (workers.workers[wi] == NULL)
--active_workers;
if (!cctx->alive) {
if (cctx->tstats->failed && criterion_options.fail_fast) {
cr_terminate(cctx->gstats);
}
if (cctx->kind == WORKER) {
remove_client_by_pid(&sctx, get_process_id_of(cctx->worker->proc));
cctx = spawn_next_client(&sctx, &ctx);
if (!is_runner())
goto cleanup;
if (cctx == NULL)
--active_workers;
}
}
sfree(ev);
if (!active_workers)
if (!active_workers && !criterion_options.wait_for_clients)
break;
free_message(&msg);
}
ev = NULL;
cleanup:
sfree(event_pipe);
sfree(ev);
for (size_t i = 0; i < nb_workers; ++i)
sfree(workers.workers[i]);
free(workers.workers);
if (has_msg)
free_message(&msg);
destroy_server_context(&sctx);
ccrAbort(ctx);
}
@ -452,16 +381,24 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) {
fflush(NULL); // flush everything before forking
g_worker_pipe = stdpipe();
if (g_worker_pipe == NULL) {
criterion_perror("Could not initialize the event pipe: %s.\n",
int sock = bind_server();
if (sock < 0) {
criterion_perror("Could not initialize the message server: %s.\n",
strerror(errno));
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();
run_tests_async(set, stats);
run_tests_async(set, stats, sock);
int result = is_runner() ? stats->tests_failed == 0 : -1;
@ -474,7 +411,7 @@ static int criterion_run_all_tests_impl(struct criterion_test_set *set) {
cleanup:
free_proc_compat();
sfree(g_worker_pipe);
nn_close(sock);
sfree(stats);
return result;
}
@ -500,7 +437,8 @@ int criterion_run_all_tests(struct criterion_test_set *set) {
void run_single_test_by_name(const char *testname) {
struct criterion_test_set *set = criterion_init();
g_event_pipe = pipe_file_open(NULL);
struct criterion_test *test = NULL;
struct criterion_suite *suite = NULL;
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
size_t tests = s->tests ? s->tests->size : 0;
@ -510,10 +448,21 @@ void run_single_test_by_name(const char *testname) {
FOREACH_SET(struct criterion_test *t, s->tests) {
char name[1024];
snprintf(name, sizeof (name), "%s::%s", s->suite.name, t->name);
if (!strncmp(name, testname, 1024))
run_test_child(t, &s->suite);
if (!strncmp(name, testname, 1024)) {
test = t;
suite = &s->suite;
break;
}
}
}
if (test) {
is_extern_worker = true;
criterion_current_test = test;
criterion_current_suite = suite;
run_test_child(test, suite);
}
sfree(set);
}

View File

@ -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);

View File

@ -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;

View File

@ -27,18 +27,29 @@
#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,
);
criterion_message_set_id(msg);
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 +58,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 +75,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 +85,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);
}

View File

@ -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,27 @@ 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));
volatile 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,
);
criterion_message_set_id(setup_msg);
cr_send_to_runner(&setup_msg);
criterion_protocol_msg main_msg = criterion_message(phase,
.phase = criterion_protocol_phase_kind_MAIN,
.name = name,
);
criterion_message_set_id(main_msg);
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 +235,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 +248,35 @@ 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
);
criterion_message_set_id(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,
);
criterion_message_set_id(teardown_msg);
cr_send_to_runner(&teardown_msg);
criterion_protocol_msg end_msg = criterion_message(phase,
.phase = criterion_protocol_phase_kind_END,
.name = name,
);
criterion_message_set_id(end_msg);
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 +286,7 @@ void cr_theory_main(struct criterion_datapoints *dps, size_t datapoints, void (*
break;
}
}
++round;
}
free(indices);

View File

@ -25,13 +25,17 @@
#include <stdbool.h>
#include <errno.h>
#include <csptr/smalloc.h>
#include <nanomsg/nn.h>
#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"
#include "protocol/connect.h"
static s_proc_handle *g_current_proc;
@ -49,55 +53,22 @@ 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);
sfree(proc->proc);
}
struct event *worker_read_event(struct worker_set *workers, s_pipe_file_handle *pipe) {
struct event *ev = read_event(pipe);
if (ev) {
ev->worker_index = -1;
for (size_t i = 0; i < workers->max_workers; ++i) {
if (!workers->workers[i])
continue;
if (get_process_id_of(workers->workers[i]->proc) == ev->pid) {
ev->worker = workers->workers[i];
ev->worker_index = i;
return ev;
}
}
criterion_perror("Could not link back the event PID to the active workers.\n");
criterion_perror("The event pipe might have been corrupted.\n");
abort();
}
return NULL;
}
void run_worker(struct worker_context *ctx) {
cr_redirect_stdin();
g_event_pipe = pipe_out_handle(ctx->pipe, PIPE_CLOSE);
ctx->func(ctx->test, ctx->suite);
sfree(g_event_pipe);
fflush(NULL); // flush all opened streams
if (criterion_options.no_early_exit)
return;
_Exit(0);
}
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,
};
@ -122,24 +93,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 };
}

View File

@ -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_ */

View File

@ -156,6 +156,7 @@ int criterion_handle_args(int argc, char *argv[], bool handle_unknown_arg) {
{"always-succeed", no_argument, 0, 'y'},
{"no-early-exit", no_argument, 0, 'z'},
{"output", required_argument, 0, 'O'},
{"wait", no_argument, 0, 'w'},
{0, 0, 0, 0 }
};
@ -237,7 +238,7 @@ int criterion_handle_args(int argc, char *argv[], bool handle_unknown_arg) {
free(out);
}
for (int c; (c = getopt_long(argc, argv, "hvlfj:SqO:", opts, NULL)) != -1;) {
for (int c; (c = getopt_long(argc, argv, "hvlfj:SqO:w", opts, NULL)) != -1;) {
switch (c) {
case 'b': criterion_options.logging_threshold = (enum criterion_logging_level) atou(DEF(optarg, "1")); break;
case 'y': criterion_options.always_succeed = true; break;
@ -281,6 +282,7 @@ int criterion_handle_args(int argc, char *argv[], bool handle_unknown_arg) {
quiet = !strcmp(path, "-");
criterion_add_output(arg, path);
} break;
case 'w': criterion_options.wait_for_clients = true; break;
case '?':
default : do_print_usage = handle_unknown_arg; break;
}

30
src/io/asprintf.h Normal file
View 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_ */

View File

@ -22,142 +22,23 @@
* THE SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <csptr/smalloc.h>
#include "criterion/stats.h"
#include "criterion/internal/common.h"
#include "criterion/hooks.h"
#include "criterion/logging.h"
#include "core/worker.h"
#include "criterion/criterion.h"
#include "protocol/protocol.h"
#include "protocol/messages.h"
#include "event.h"
#include "assert.h"
s_pipe_file_handle *g_event_pipe = NULL;
int g_client_socket = -1;
void destroy_event(void *ptr, CR_UNUSED void *meta) {
struct event *ev = ptr;
free(ev->data);
}
void destroy_assert_event(void *ptr, CR_UNUSED void *meta) {
struct event *ev = ptr;
free((void*) ((struct criterion_assert_stats *) ev->data)->message);
free(ev->data);
}
#ifdef __GNUC__
# define unlikely(x) __builtin_expect((x),0)
#else
# define unlikely(x) (x)
#endif
#define ASSERT(Cond) \
do { \
if (unlikely(!(Cond))){ \
criterion_perror("Corrupted event IO in the worker pipe.\n"); \
abort(); \
} \
} while (0)
struct event *read_event(s_pipe_file_handle *f) {
unsigned kind;
ASSERT(pipe_read(&kind, sizeof (unsigned), f) == 1);
unsigned long long pid;
ASSERT(pipe_read(&pid, sizeof (unsigned long long), f) == 1);
switch (kind) {
case ASSERT: {
const size_t assert_size = sizeof (struct criterion_assert_stats);
struct criterion_assert_stats *buf = NULL;
char *msg = NULL;
buf = malloc(assert_size);
ASSERT(pipe_read(buf, assert_size, f) == 1);
size_t len = 0;
ASSERT(pipe_read(&len, sizeof (size_t), f) == 1);
msg = malloc(len);
ASSERT(pipe_read(msg, len, f) == 1);
buf->message = msg;
struct event *ev = smalloc(
.size = sizeof (struct event),
.dtor = destroy_assert_event
);
*ev = (struct event) { .pid = pid, .kind = kind, .data = buf };
return ev;
}
case TEST_ABORT: {
char *msg = NULL;
size_t len = 0;
ASSERT(pipe_read(&len, sizeof (size_t), f) == 1);
msg = malloc(len);
ASSERT(pipe_read(msg, len, f) == 1);
struct event *ev = smalloc(
.size = sizeof (struct event),
.dtor = destroy_event
);
*ev = (struct event) { .pid = pid, .kind = kind, .data = msg };
return ev;
}
case THEORY_FAIL: {
size_t len = 0;
ASSERT(pipe_read(&len, sizeof (size_t), f) == 1);
char *buf = malloc(len);
ASSERT(pipe_read(buf, len, f) == 1);
struct event *ev = smalloc(
.size = sizeof (struct event),
.dtor = destroy_event
);
*ev = (struct event) { .pid = pid, .kind = kind, .data = buf };
return ev;
}
case POST_TEST: {
double *elapsed_time = malloc(sizeof (double));
ASSERT(pipe_read(elapsed_time, sizeof (double), f) == 1);
struct event *ev = smalloc(
.size = sizeof (struct event),
.dtor = destroy_event
);
*ev = (struct event) { .pid = pid, .kind = kind, .data = elapsed_time };
return ev;
}
case WORKER_TERMINATED: {
struct worker_status *status = malloc(sizeof (struct worker_status));
ASSERT(pipe_read(status, sizeof (struct worker_status), f) == 1);
struct event *ev = smalloc(
.size = sizeof (struct event),
.dtor = destroy_event
);
*ev = (struct event) { .pid = pid, .kind = kind, .data = status };
return ev;
}
default: {
struct event *ev = smalloc(sizeof (struct event));
*ev = (struct event) { .pid = pid, .kind = kind, .data = NULL };
return ev;
}
}
}
void criterion_send_event(int kind, void *data, size_t size) {
unsigned long long pid = get_process_id();
unsigned char *buf = malloc(sizeof (int) + sizeof (pid) + size);
memcpy(buf, &kind, sizeof (int));
memcpy(buf + sizeof (int), &pid, sizeof (pid));
memcpy(buf + sizeof (int) + sizeof (pid), data, size);
ASSERT(pipe_write(buf, sizeof (int) + sizeof (pid) + size, g_event_pipe) == 1);
free(buf);
void criterion_send_assert(struct criterion_assert_stats *stats) {
assert(stats->message);
criterion_protocol_msg msg = criterion_message(assert,
.message = (char *) stats->message,
.passed = stats->passed,
.file = (char *) stats->file,
.has_line = true,
.line = stats->line,
);
criterion_message_set_id(msg);
cr_send_to_runner(&msg);
}

View File

@ -27,8 +27,9 @@
# include "criterion/event.h"
# include "core/worker.h"
# include <stdio.h>
# include <pb.h>
extern s_pipe_file_handle *g_event_pipe;
extern int g_client_socket;
struct event {
unsigned long long pid;
@ -39,11 +40,6 @@ struct event {
size_t worker_index;
};
enum other_event_kinds {
WORKER_TERMINATED = 1 << 30,
TEST_ABORT,
};
struct event *read_event(s_pipe_file_handle *f);
void criterion_send_assert(struct criterion_assert_stats *stats);
#endif /* !EVENT_H_ */

16
src/protocol/Makefile Normal file
View File

@ -0,0 +1,16 @@
NANOPB_DIR = ../../dependencies/nanopb
NANOPB_PROTO_DIR = $(NANOPB_DIR)/generator/proto
PROTOC = protoc
PROTOC_OPTS = --plugin=protoc-gen-nanopb=protoc-gen-nanopb \
-I$(NANOPB_PROTO_DIR) -I. --nanopb_out=.
gen: make-nanopb criterion.pb.c
make-nanopb:
$(MAKE) -C $(NANOPB_PROTO_DIR)
criterion.pb.c: criterion.proto
$(PROTOC) $(PROTOC_OPTS) $<
.PHONY: gen make-nanopb

67
src/protocol/connect.c Normal file
View File

@ -0,0 +1,67 @@
/*
* 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.
*/
#include <errno.h>
#include <nanomsg/nn.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 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_bind(sock, URL) < 0)
goto error;
return sock;
error: {}
errno_ignore(nn_close(sock));
return -1;
}
int connect_client(void) {
int sock = nn_socket(AF_SP, NN_REQ);
if (sock < 0)
return -1;
if (nn_connect (sock, URL) < 0)
goto error;
return sock;
error: {}
errno_ignore(nn_close(sock));
return -1;
}
void close_socket(int sock) {
nn_close(sock);
}

31
src/protocol/connect.h Normal file
View File

@ -0,0 +1,31 @@
/*
* 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 CONNECT_H_
# define CONNECT_H_
int connect_client(void);
int bind_server(void);
void close_socket(int sock);
#endif /* !CONNECT_H_ */

View File

@ -0,0 +1,95 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.3.5-dev at Sun Jan 17 02:06:50 2016. */
#include "criterion.pb.h"
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
const int32_t criterion_protocol_msg_version_default = 1;
const pb_field_t criterion_protocol_birth_fields[3] = {
PB_FIELD( 1, STRING , REQUIRED, POINTER , FIRST, criterion_protocol_birth, name, name, 0),
PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, criterion_protocol_birth, timestamp, name, 0),
PB_LAST_FIELD
};
const pb_field_t criterion_protocol_phase_fields[5] = {
PB_FIELD( 1, UENUM , REQUIRED, STATIC , FIRST, criterion_protocol_phase, phase, phase, 0),
PB_FIELD( 2, STRING , OPTIONAL, POINTER , OTHER, criterion_protocol_phase, name, phase, 0),
PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, criterion_protocol_phase, timestamp, name, 0),
PB_FIELD( 4, STRING , OPTIONAL, POINTER , OTHER, criterion_protocol_phase, message, timestamp, 0),
PB_LAST_FIELD
};
const pb_field_t criterion_protocol_death_fields[4] = {
PB_FIELD( 1, UENUM , REQUIRED, STATIC , FIRST, criterion_protocol_death, result, result, 0),
PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, criterion_protocol_death, status, result, 0),
PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, criterion_protocol_death, timestamp, status, 0),
PB_LAST_FIELD
};
const pb_field_t criterion_protocol_assert_fields[5] = {
PB_FIELD( 1, STRING , REQUIRED, POINTER , FIRST, criterion_protocol_assert, message, message, 0),
PB_FIELD( 2, BOOL , REQUIRED, STATIC , OTHER, criterion_protocol_assert, passed, message, 0),
PB_FIELD( 3, STRING , OPTIONAL, POINTER , OTHER, criterion_protocol_assert, file, passed, 0),
PB_FIELD( 4, INT64 , OPTIONAL, STATIC , OTHER, criterion_protocol_assert, line, file, 0),
PB_LAST_FIELD
};
const pb_field_t criterion_protocol_log_fields[3] = {
PB_FIELD( 1, UENUM , REQUIRED, STATIC , FIRST, criterion_protocol_log, severity, severity, 0),
PB_FIELD( 2, STRING , REQUIRED, POINTER , OTHER, criterion_protocol_log, message, severity, 0),
PB_LAST_FIELD
};
const pb_field_t criterion_protocol_ack_fields[3] = {
PB_FIELD( 1, UENUM , REQUIRED, STATIC , FIRST, criterion_protocol_ack, status_code, status_code, 0),
PB_FIELD( 2, STRING , OPTIONAL, POINTER , OTHER, criterion_protocol_ack, message, status_code, 0),
PB_LAST_FIELD
};
const pb_field_t criterion_protocol_submessage_fields[6] = {
PB_ONEOF_FIELD(value, 1, MESSAGE , ONEOF, STATIC , FIRST, criterion_protocol_submessage, birth, birth, &criterion_protocol_birth_fields),
PB_ONEOF_FIELD(value, 2, MESSAGE , ONEOF, STATIC , FIRST, criterion_protocol_submessage, phase, phase, &criterion_protocol_phase_fields),
PB_ONEOF_FIELD(value, 3, MESSAGE , ONEOF, STATIC , FIRST, criterion_protocol_submessage, death, death, &criterion_protocol_death_fields),
PB_ONEOF_FIELD(value, 4, MESSAGE , ONEOF, STATIC , FIRST, criterion_protocol_submessage, message, message, &criterion_protocol_log_fields),
PB_ONEOF_FIELD(value, 5, MESSAGE , ONEOF, STATIC , FIRST, criterion_protocol_submessage, assert, assert, &criterion_protocol_assert_fields),
PB_LAST_FIELD
};
const pb_field_t criterion_protocol_msg_fields[5] = {
PB_FIELD( 1, INT32 , REQUIRED, STATIC , FIRST, criterion_protocol_msg, version, version, &criterion_protocol_msg_version_default),
PB_ONEOF_FIELD(id, 2, INT64 , ONEOF, STATIC , OTHER, criterion_protocol_msg, pid, version, 0),
PB_ONEOF_FIELD(id, 3, STRING , ONEOF, POINTER , OTHER, criterion_protocol_msg, uid, version, 0),
PB_FIELD( 16, MESSAGE , REQUIRED, STATIC , OTHER, criterion_protocol_msg, data, id.uid, &criterion_protocol_submessage_fields),
PB_LAST_FIELD
};
/* Check that field information fits in pb_field_t */
#if !defined(PB_FIELD_32BIT)
/* If you get an error here, it means that you need to define PB_FIELD_32BIT
* compile-time option. You can do that in pb.h or on compiler command line.
*
* The reason you need to do this is that some of your messages contain tag
* numbers or field sizes that are larger than what can fit in 8 or 16 bit
* field descriptors.
*/
PB_STATIC_ASSERT((pb_membersize(criterion_protocol_submessage, value.birth) < 65536 && pb_membersize(criterion_protocol_submessage, value.phase) < 65536 && pb_membersize(criterion_protocol_submessage, value.death) < 65536 && pb_membersize(criterion_protocol_submessage, value.message) < 65536 && pb_membersize(criterion_protocol_submessage, value.assert) < 65536 && pb_membersize(criterion_protocol_submessage, value.birth) < 65536 && pb_membersize(criterion_protocol_submessage, value.phase) < 65536 && pb_membersize(criterion_protocol_submessage, value.death) < 65536 && pb_membersize(criterion_protocol_submessage, value.message) < 65536 && pb_membersize(criterion_protocol_submessage, value.assert) < 65536 && pb_membersize(criterion_protocol_submessage, value.birth) < 65536 && pb_membersize(criterion_protocol_submessage, value.phase) < 65536 && pb_membersize(criterion_protocol_submessage, value.death) < 65536 && pb_membersize(criterion_protocol_submessage, value.message) < 65536 && pb_membersize(criterion_protocol_submessage, value.assert) < 65536 && pb_membersize(criterion_protocol_submessage, value.birth) < 65536 && pb_membersize(criterion_protocol_submessage, value.phase) < 65536 && pb_membersize(criterion_protocol_submessage, value.death) < 65536 && pb_membersize(criterion_protocol_submessage, value.message) < 65536 && pb_membersize(criterion_protocol_submessage, value.assert) < 65536 && pb_membersize(criterion_protocol_msg, data) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_criterion_protocol_birth_criterion_protocol_phase_criterion_protocol_death_criterion_protocol_assert_criterion_protocol_log_criterion_protocol_ack_criterion_protocol_submessage_criterion_protocol_msg)
#endif
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
/* If you get an error here, it means that you need to define PB_FIELD_16BIT
* compile-time option. You can do that in pb.h or on compiler command line.
*
* The reason you need to do this is that some of your messages contain tag
* numbers or field sizes that are larger than what can fit in the default
* 8 bit descriptors.
*/
PB_STATIC_ASSERT((pb_membersize(criterion_protocol_submessage, value.birth) < 256 && pb_membersize(criterion_protocol_submessage, value.phase) < 256 && pb_membersize(criterion_protocol_submessage, value.death) < 256 && pb_membersize(criterion_protocol_submessage, value.message) < 256 && pb_membersize(criterion_protocol_submessage, value.assert) < 256 && pb_membersize(criterion_protocol_submessage, value.birth) < 256 && pb_membersize(criterion_protocol_submessage, value.phase) < 256 && pb_membersize(criterion_protocol_submessage, value.death) < 256 && pb_membersize(criterion_protocol_submessage, value.message) < 256 && pb_membersize(criterion_protocol_submessage, value.assert) < 256 && pb_membersize(criterion_protocol_submessage, value.birth) < 256 && pb_membersize(criterion_protocol_submessage, value.phase) < 256 && pb_membersize(criterion_protocol_submessage, value.death) < 256 && pb_membersize(criterion_protocol_submessage, value.message) < 256 && pb_membersize(criterion_protocol_submessage, value.assert) < 256 && pb_membersize(criterion_protocol_submessage, value.birth) < 256 && pb_membersize(criterion_protocol_submessage, value.phase) < 256 && pb_membersize(criterion_protocol_submessage, value.death) < 256 && pb_membersize(criterion_protocol_submessage, value.message) < 256 && pb_membersize(criterion_protocol_submessage, value.assert) < 256 && pb_membersize(criterion_protocol_msg, data) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_criterion_protocol_birth_criterion_protocol_phase_criterion_protocol_death_criterion_protocol_assert_criterion_protocol_log_criterion_protocol_ack_criterion_protocol_submessage_criterion_protocol_msg)
#endif

178
src/protocol/criterion.pb.h Normal file
View File

@ -0,0 +1,178 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.5-dev at Sun Jan 17 02:06:50 2016. */
#ifndef PB_CRITERION_PB_H_INCLUDED
#define PB_CRITERION_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 30
#error Regenerate this file with the current version of nanopb generator.
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Enum definitions */
typedef enum _criterion_protocol_phase_kind {
criterion_protocol_phase_kind_SETUP = 1,
criterion_protocol_phase_kind_MAIN = 2,
criterion_protocol_phase_kind_TEARDOWN = 3,
criterion_protocol_phase_kind_END = 4,
criterion_protocol_phase_kind_ABORT = 5,
criterion_protocol_phase_kind_TIMEOUT = 6
} criterion_protocol_phase_kind;
typedef enum _criterion_protocol_death_result_type {
criterion_protocol_death_result_type_NORMAL = 1,
criterion_protocol_death_result_type_CRASH = 2
} criterion_protocol_death_result_type;
typedef enum _criterion_protocol_log_level {
criterion_protocol_log_level_INFO = 0,
criterion_protocol_log_level_WARNING = 1,
criterion_protocol_log_level_ERROR = 2
} criterion_protocol_log_level;
typedef enum _criterion_protocol_ack_status {
criterion_protocol_ack_status_OK = 0,
criterion_protocol_ack_status_ERROR = 1
} criterion_protocol_ack_status;
/* Struct definitions */
typedef struct _criterion_protocol_ack {
criterion_protocol_ack_status status_code;
char *message;
} criterion_protocol_ack;
typedef struct _criterion_protocol_assert {
char *message;
bool passed;
char *file;
bool has_line;
int64_t line;
} criterion_protocol_assert;
typedef struct _criterion_protocol_birth {
char *name;
bool has_timestamp;
int64_t timestamp;
} criterion_protocol_birth;
typedef struct _criterion_protocol_death {
criterion_protocol_death_result_type result;
bool has_status;
int64_t status;
bool has_timestamp;
int64_t timestamp;
} criterion_protocol_death;
typedef struct _criterion_protocol_log {
criterion_protocol_log_level severity;
char *message;
} criterion_protocol_log;
typedef struct _criterion_protocol_phase {
criterion_protocol_phase_kind phase;
char *name;
bool has_timestamp;
int64_t timestamp;
char *message;
} criterion_protocol_phase;
typedef struct _criterion_protocol_submessage {
pb_size_t which_value;
union {
criterion_protocol_birth birth;
criterion_protocol_phase phase;
criterion_protocol_death death;
criterion_protocol_log message;
criterion_protocol_assert assert;
} value;
} criterion_protocol_submessage;
typedef struct _criterion_protocol_msg {
int32_t version;
pb_size_t which_id;
union {
int64_t pid;
char *uid;
} id;
criterion_protocol_submessage data;
} criterion_protocol_msg;
/* Default values for struct fields */
extern const int32_t criterion_protocol_msg_version_default;
/* Initializer values for message structs */
#define criterion_protocol_birth_init_default {NULL, false, 0}
#define criterion_protocol_phase_init_default {(criterion_protocol_phase_kind)0, NULL, false, 0, NULL}
#define criterion_protocol_death_init_default {(criterion_protocol_death_result_type)0, false, 0, false, 0}
#define criterion_protocol_assert_init_default {NULL, 0, NULL, false, 0}
#define criterion_protocol_log_init_default {(criterion_protocol_log_level)0, NULL}
#define criterion_protocol_ack_init_default {(criterion_protocol_ack_status)0, NULL}
#define criterion_protocol_submessage_init_default {0, {criterion_protocol_birth_init_default}}
#define criterion_protocol_msg_init_default {1, 0, {0}, criterion_protocol_submessage_init_default}
#define criterion_protocol_birth_init_zero {NULL, false, 0}
#define criterion_protocol_phase_init_zero {(criterion_protocol_phase_kind)0, NULL, false, 0, NULL}
#define criterion_protocol_death_init_zero {(criterion_protocol_death_result_type)0, false, 0, false, 0}
#define criterion_protocol_assert_init_zero {NULL, 0, NULL, false, 0}
#define criterion_protocol_log_init_zero {(criterion_protocol_log_level)0, NULL}
#define criterion_protocol_ack_init_zero {(criterion_protocol_ack_status)0, NULL}
#define criterion_protocol_submessage_init_zero {0, {criterion_protocol_birth_init_zero}}
#define criterion_protocol_msg_init_zero {0, 0, {0}, criterion_protocol_submessage_init_zero}
/* Field tags (for use in manual encoding/decoding) */
#define criterion_protocol_ack_status_code_tag 1
#define criterion_protocol_ack_message_tag 2
#define criterion_protocol_assert_message_tag 1
#define criterion_protocol_assert_passed_tag 2
#define criterion_protocol_assert_file_tag 3
#define criterion_protocol_assert_line_tag 4
#define criterion_protocol_birth_name_tag 1
#define criterion_protocol_birth_timestamp_tag 2
#define criterion_protocol_death_result_tag 1
#define criterion_protocol_death_status_tag 2
#define criterion_protocol_death_timestamp_tag 3
#define criterion_protocol_log_severity_tag 1
#define criterion_protocol_log_message_tag 2
#define criterion_protocol_phase_phase_tag 1
#define criterion_protocol_phase_name_tag 2
#define criterion_protocol_phase_timestamp_tag 3
#define criterion_protocol_phase_message_tag 4
#define criterion_protocol_submessage_birth_tag 1
#define criterion_protocol_submessage_phase_tag 2
#define criterion_protocol_submessage_death_tag 3
#define criterion_protocol_submessage_message_tag 4
#define criterion_protocol_submessage_assert_tag 5
#define criterion_protocol_msg_pid_tag 2
#define criterion_protocol_msg_uid_tag 3
#define criterion_protocol_msg_version_tag 1
#define criterion_protocol_msg_data_tag 16
/* Struct field encoding specification for nanopb */
extern const pb_field_t criterion_protocol_birth_fields[3];
extern const pb_field_t criterion_protocol_phase_fields[5];
extern const pb_field_t criterion_protocol_death_fields[4];
extern const pb_field_t criterion_protocol_assert_fields[5];
extern const pb_field_t criterion_protocol_log_fields[3];
extern const pb_field_t criterion_protocol_ack_fields[3];
extern const pb_field_t criterion_protocol_submessage_fields[6];
extern const pb_field_t criterion_protocol_msg_fields[5];
/* Maximum encoded size of messages (where known) */
#define criterion_protocol_death_size 24
/* Message IDs (where set with "msgid" option) */
#ifdef PB_MSGID
#define CRITERION_MESSAGES \
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@ -0,0 +1,88 @@
package criterion.protocol;
message birth {
required string name = 1;
optional int64 timestamp = 2;
}
message phase {
enum kind {
SETUP = 1;
MAIN = 2;
TEARDOWN = 3;
END = 4;
ABORT = 5;
TIMEOUT = 6;
}
required kind phase = 1;
optional string name = 2;
optional int64 timestamp = 3;
optional string message = 4;
}
message death {
enum result_type {
NORMAL = 1;
CRASH = 2;
}
required result_type result = 1;
optional int64 status = 2;
optional int64 timestamp = 3;
}
message assert {
required string message = 1;
required bool passed = 2;
optional string file = 3;
optional int64 line = 4;
}
message log {
enum level {
INFO = 0;
WARNING = 1;
ERROR = 2;
}
required level severity = 1;
required string message = 2;
}
message ack {
enum status {
OK = 0;
ERROR = 1;
}
required status status_code = 1;
optional string message = 2;
}
message submessage {
oneof value {
criterion.protocol.birth birth = 1;
criterion.protocol.phase phase = 2;
criterion.protocol.death death = 3;
criterion.protocol.log message = 4;
criterion.protocol.assert assert = 5;
}
}
message msg {
required int32 version = 1 [default = 1];
oneof id {
int64 pid = 2;
string uid = 3;
}
required submessage data = 16;
}

153
src/protocol/messages.c Normal file
View File

@ -0,0 +1,153 @@
/*
* 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.
*/
#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 = NULL;
int read = res = nn_recv(sock, &buf, NN_MSG, 0);
if (read <= 0)
goto cleanup;
pb_istream_t stream = pb_istream_from_buffer(buf, read);
if (!pb_decode(&stream, criterion_protocol_msg_fields, message)) {
res = -2;
goto cleanup;
}
res = 1;
cleanup:
if (buf)
nn_freemsg(buf);
return res;
}
int write_message(int sock, const criterion_protocol_msg *message) {
int res = -1;
size_t size;
unsigned char *buf = NULL;
if (!pb_get_encoded_size(&size, criterion_protocol_msg_fields, message))
goto cleanup;
buf = malloc(size);
pb_ostream_t stream = pb_ostream_from_buffer(buf, size);
if (!pb_encode(&stream, criterion_protocol_msg_fields, message))
goto cleanup;
int written = nn_send(sock, buf, size, 0);
if (written <= 0 || written != (int) size)
goto cleanup;
res = 1;
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],
nn_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", nn_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();
}
pb_release(criterion_protocol_ack_fields, &ack);
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", nn_strerror(errno));
abort();
}
free(buf);
}
void free_message(criterion_protocol_msg *msg) {
pb_release(criterion_protocol_msg_fields, msg);
}

35
src/protocol/messages.h Normal file
View File

@ -0,0 +1,35 @@
/*
* 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 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, ...);
void free_message(criterion_protocol_msg *msg);
#endif /* !MESSAGES_H_ */

13
src/protocol/protoc-gen-nanopb Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
# This file is used to invoke nanopb_generator.py as a plugin
# to protoc on Linux and other *nix-style systems.
# Use it like this:
# protoc --plugin=nanopb=..../protoc-gen-nanopb --nanopb_out=dir foo.proto
#
# Note that if you use the binary package of nanopb, the protoc
# path is already set up properly and there is no need to give
# --plugin= on the command line.
NANOPB_PATH="../../dependencies/nanopb/generator"
exec python2 "$NANOPB_PATH/nanopb_generator.py" --protoc-plugin

26
src/protocol/protocol.c Normal file
View File

@ -0,0 +1,26 @@
/*
* 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.
*/
#include "protocol.h"
volatile bool is_extern_worker = false;

62
src/protocol/protocol.h Normal file
View File

@ -0,0 +1,62 @@
/*
* 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 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,
};
extern volatile bool is_extern_worker;
# define criterion_message_set_id(Msg) \
do { \
if (is_extern_worker) { \
(Msg).id.uid = (char *) criterion_current_test->name; \
} else { \
(Msg).id.pid = get_process_id(); \
} \
} while (0)
# define criterion_message(Kind, ...) \
(criterion_protocol_msg) { \
.version = PROTOCOL_V1, \
.which_id = is_extern_worker \
? criterion_protocol_msg_uid_tag \
: criterion_protocol_msg_pid_tag, \
.data = { \
.which_value = criterion_protocol_submessage_ ## Kind ## _tag, \
.value = { \
.Kind = { __VA_ARGS__ }, \
} \
} \
}
#endif /* !PROTOCOL_H_ */