Merge branch 'features/io-rewrite-nanopb' into bleeding
This commit is contained in:
commit
e638cf3d43
40 changed files with 1771 additions and 476 deletions
|
@ -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
1
.gitignore
vendored
|
@ -18,6 +18,7 @@
|
|||
!*.po
|
||||
!*.in
|
||||
!*.t
|
||||
!*.proto
|
||||
!.cmake/Modules/*.cmake
|
||||
!samples/tests/*.sh
|
||||
!samples/**/*.expected
|
||||
|
|
9
.gitmodules
vendored
9
.gitmodules
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
107
CMakeLists.txt
107
CMakeLists.txt
|
@ -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 ()
|
||||
|
|
|
@ -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
1
dependencies/nanomsg
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit fd66ff55a5bad44ea0c3cca8bea345b6f02663bf
|
1
dependencies/nanomsg-patched
vendored
Submodule
1
dependencies/nanomsg-patched
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit e0d49e180b93ad1557f447d43654d28793512aca
|
1
dependencies/nanopb
vendored
Submodule
1
dependencies/nanopb
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 56f7c488df99ae655b47b5838055e48b886665a1
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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(); \
|
||||
|
|
|
@ -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
|
||||
|
|
6
po/fr.po
6
po/fr.po
|
@ -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 "
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
483
src/core/client.c
Normal 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
86
src/core/client.h
Normal 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_ */
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
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_ */
|
151
src/io/event.c
151
src/io/event.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
16
src/protocol/Makefile
Normal 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
67
src/protocol/connect.c
Normal 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
31
src/protocol/connect.h
Normal 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_ */
|
95
src/protocol/criterion.pb.c
Normal file
95
src/protocol/criterion.pb.c
Normal 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
178
src/protocol/criterion.pb.h
Normal 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
|
88
src/protocol/criterion.proto
Normal file
88
src/protocol/criterion.proto
Normal 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
153
src/protocol/messages.c
Normal 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
35
src/protocol/messages.h
Normal 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
13
src/protocol/protoc-gen-nanopb
Executable 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
26
src/protocol/protocol.c
Normal 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
62
src/protocol/protocol.h
Normal 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_ */
|
Loading…
Add table
Reference in a new issue