From 4dcde5259cd241791f288da8c6864b2673c71ab7 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 1 Oct 2015 15:05:22 +0200 Subject: [PATCH 1/4] Added C/C++ language isolation wrappers --- CMakeLists.txt | 3 ++ include/criterion/criterion.h | 3 ++ include/criterion/types.h | 6 +++ src/core/runner.c | 35 +++---------- src/core/wrappers/wrap.c | 65 ++++++++++++++++++++++++ src/core/wrappers/wrap.cc | 96 +++++++++++++++++++++++++++++++++++ src/core/wrappers/wrap.h | 36 +++++++++++++ 7 files changed, 216 insertions(+), 28 deletions(-) create mode 100644 src/core/wrappers/wrap.c create mode 100644 src/core/wrappers/wrap.cc create mode 100644 src/core/wrappers/wrap.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 71ae77d..8a9e1bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ add_definitions(-DCRITERION_BUILDING_DLL=1) if (NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -g -std=gnu99") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror -g -std=c++11") endif () if (MSVC) @@ -84,6 +85,8 @@ find_package(PCRE) # List sources and headers set(SOURCE_FILES + src/core/wrappers/wrap.c + src/core/wrappers/wrap.cc src/core/abort.c src/core/abort.h src/core/report.c diff --git a/include/criterion/criterion.h b/include/criterion/criterion.h index db9dfdc..6182434 100644 --- a/include/criterion/criterion.h +++ b/include/criterion/criterion.h @@ -36,9 +36,11 @@ # ifdef __cplusplus # define TEST_PROTOTYPE_(Category, Name) \ extern "C" void IDENTIFIER_(Category, Name, impl)(void) +# define CR_LANG CR_LANG_CPP # else # define TEST_PROTOTYPE_(Category, Name) \ void IDENTIFIER_(Category, Name, impl)(void) +# define CR_LANG CR_LANG_C # endif # define SUITE_IDENTIFIER_(Name, Suffix) \ @@ -49,6 +51,7 @@ TEST_PROTOTYPE_(Category, Name); \ struct criterion_test_extra_data IDENTIFIER_(Category, Name, extra) = \ CR_EXPAND(CRITERION_MAKE_STRUCT(struct criterion_test_extra_data, \ + .lang_ = CR_LANG, \ .kind_ = CR_TEST_NORMAL, \ .param_ = (struct criterion_test_params(*)(void)) NULL, \ .identifier_ = #Category "/" #Name, \ diff --git a/include/criterion/types.h b/include/criterion/types.h index a28d833..3354098 100644 --- a/include/criterion/types.h +++ b/include/criterion/types.h @@ -35,6 +35,11 @@ using std::size_t; # endif # include "common.h" +enum criterion_language { + CR_LANG_C, + CR_LANG_CPP, +}; + enum criterion_test_kind { CR_TEST_NORMAL, CR_TEST_PARAMETERIZED, @@ -84,6 +89,7 @@ struct criterion_test_params { struct criterion_test_extra_data { int sentinel_; + enum criterion_language lang_; enum criterion_test_kind kind_; struct criterion_test_params (*param_)(void); const char *identifier_; diff --git a/src/core/runner.c b/src/core/runner.c index ca72f17..9e2d08e 100644 --- a/src/core/runner.c +++ b/src/core/runner.c @@ -34,6 +34,7 @@ #include "compat/time.h" #include "compat/posix.h" #include "compat/processor.h" +#include "wrappers/wrap.h" #include "string/i18n.h" #include "io/event.h" #include "runner_coroutine.h" @@ -156,6 +157,11 @@ struct criterion_test_set *criterion_init(void) { return set; } +f_wrapper *g_wrappers[] = { + [CR_LANG_C] = c_wrap, + [CR_LANG_CPP] = cpp_wrap, +}; + void run_test_child(struct criterion_test *test, struct criterion_suite *suite) { @@ -168,34 +174,7 @@ void run_test_child(struct criterion_test *test, else if (test->data->timeout != 0) setup_timeout((uint64_t) (test->data->timeout * 1e9)); - send_event(PRE_INIT, NULL, 0); - if (suite->data) - (suite->data->init ? suite->data->init : nothing)(); - (test->data->init ? test->data->init : nothing)(); - send_event(PRE_TEST, NULL, 0); - - struct timespec_compat ts; - if (!setjmp(g_pre_test)) { - timer_start(&ts); - if (test->test) { - if (!test->data->param_) { - test->test(); - } else { - void(*param_test_func)(void *) = (void(*)(void*)) test->test; - param_test_func(g_worker_context.param->ptr); - } - } - } - - double elapsed_time; - if (!timer_end(&elapsed_time, &ts)) - elapsed_time = -1; - - send_event(POST_TEST, &elapsed_time, sizeof (double)); - (test->data->fini ? test->data->fini : nothing)(); - if (suite->data) - (suite->data->fini ? suite->data->fini : nothing)(); - send_event(POST_FINI, NULL, 0); + g_wrappers[test->data->lang_](test, suite); } #define push_event(Kind, ...) \ diff --git a/src/core/wrappers/wrap.c b/src/core/wrappers/wrap.c new file mode 100644 index 0000000..01acf62 --- /dev/null +++ b/src/core/wrappers/wrap.c @@ -0,0 +1,65 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * 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 "core/abort.h" +#include "core/stats.h" +#include "core/worker.h" +#include "core/report.h" +#include "compat/time.h" +#include "io/event.h" +#include "wrap.h" + +static INLINE void nothing(void) {} + +void c_wrap(struct criterion_test *test, struct criterion_suite *suite) { + + send_event(PRE_INIT, NULL, 0); + if (suite->data) + (suite->data->init ? suite->data->init : nothing)(); + (test->data->init ? test->data->init : nothing)(); + send_event(PRE_TEST, NULL, 0); + + struct timespec_compat ts; + if (!setjmp(g_pre_test)) { + timer_start(&ts); + if (test->test) { + if (!test->data->param_) { + test->test(); + } else { + void(*param_test_func)(void *) = (void(*)(void*)) test->test; + param_test_func(g_worker_context.param->ptr); + } + } + } + + double elapsed_time; + if (!timer_end(&elapsed_time, &ts)) + elapsed_time = -1; + + send_event(POST_TEST, &elapsed_time, sizeof (double)); + (test->data->fini ? test->data->fini : nothing)(); + if (suite->data) + (suite->data->fini ? suite->data->fini : nothing)(); + send_event(POST_FINI, NULL, 0); + +} diff --git a/src/core/wrappers/wrap.cc b/src/core/wrappers/wrap.cc new file mode 100644 index 0000000..386e3eb --- /dev/null +++ b/src/core/wrappers/wrap.cc @@ -0,0 +1,96 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * 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 +#include "criterion/assert.h" + +extern "C" { + +#include "core/abort.h" +#include "core/stats.h" +#include "core/worker.h" +#include "core/report.h" +#include "compat/time.h" +#include "io/event.h" +#include "wrap.h" + +static INLINE void nothing(void) {} + +void cpp_wrap(struct criterion_test *test, struct criterion_suite *suite) { + + send_event(PRE_INIT, NULL, 0); + try { + if (suite->data) + (suite->data->init ? suite->data->init : nothing)(); + (test->data->init ? test->data->init : nothing)(); + } catch (const std::exception &e) { + cr_assert_fail("Caught an unexpected exception during the test initialization: %s.", e.what()); + std::exit(1); + } catch (...) { + cr_assert_fail("Caught some unexpected exception during the test initialization."); + std::exit(1); + } + send_event(PRE_TEST, NULL, 0); + + struct timespec_compat ts; + if (!setjmp(g_pre_test)) { + timer_start(&ts); + if (test->test) { + try { + if (!test->data->param_) { + test->test(); + } else { + void(*param_test_func)(void *) = (void(*)(void*)) test->test; + param_test_func(g_worker_context.param->ptr); + } + } catch (const std::exception &e) { + cr_assert_fail("Caught an unexpected exception during the test execution: %s.", e.what()); + std::exit(1); + } catch (...) { + cr_assert_fail("Caught some unexpected exception during the test execution."); + std::exit(1); + } + } + } + + double elapsed_time; + if (!timer_end(&elapsed_time, &ts)) + elapsed_time = -1; + + send_event(POST_TEST, &elapsed_time, sizeof (double)); + try { + (test->data->fini ? test->data->fini : nothing)(); + if (suite->data) + (suite->data->fini ? suite->data->fini : nothing)(); + } catch (const std::exception &e) { + cr_assert_fail("Caught an unexpected exception during the test finalization: %s.", e.what()); + std::exit(1); + } catch (...) { + cr_assert_fail("Caught some unexpected exception during the test finalization."); + std::exit(1); + } + send_event(POST_FINI, NULL, 0); + +} + +} diff --git a/src/core/wrappers/wrap.h b/src/core/wrappers/wrap.h new file mode 100644 index 0000000..b7d518f --- /dev/null +++ b/src/core/wrappers/wrap.h @@ -0,0 +1,36 @@ +/* + * The MIT License (MIT) + * + * Copyright © 2015 Franklin "Snaipe" Mathieu + * + * 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 WRAP_H_ +# define WRAP_H_ + +# include "criterion/types.h" + +typedef void (f_wrapper)(struct criterion_test *, struct criterion_suite *); + +void c_wrap(struct criterion_test *test, struct criterion_suite *suite); +void cpp_wrap(struct criterion_test *test, struct criterion_suite *suite); + +extern f_wrapper *g_wrappers[]; + +#endif /* !WRAP_H_ */ From 55553a06814c02eece5a7e46181fc6e45ca6f8d2 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 1 Oct 2015 15:36:51 +0200 Subject: [PATCH 2/4] Cleaned up headers for wrap.cc --- src/core/wrappers/wrap.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/wrappers/wrap.cc b/src/core/wrappers/wrap.cc index 386e3eb..f2ae058 100644 --- a/src/core/wrappers/wrap.cc +++ b/src/core/wrappers/wrap.cc @@ -23,16 +23,16 @@ */ #include #include "criterion/assert.h" +#include "criterion/event.h" extern "C" { #include "core/abort.h" -#include "core/stats.h" -#include "core/worker.h" #include "core/report.h" +#include "core/worker.h" #include "compat/time.h" -#include "io/event.h" #include "wrap.h" +#include "common.h" static INLINE void nothing(void) {} From d4c306f7cd5ba3a6d544412b89a6e6a38b9711c6 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 1 Oct 2015 15:38:24 +0200 Subject: [PATCH 3/4] Fixed MinGW header bug where off_t/off64_t is not defined in io.h (See http://sourceforge.net/p/mingw/bugs/2024/) --- src/compat/internal.h | 8 ++++++++ src/compat/posix.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/compat/internal.h b/src/compat/internal.h index 43ff62d..551212b 100644 --- a/src/compat/internal.h +++ b/src/compat/internal.h @@ -30,7 +30,15 @@ # undef _WIN32_WINNT # define _WIN32_WINNT 0x0502 # include +# if defined(__MINGW32__) || defined(__MINGW64__) +# define off_t _off_t +# define off64_t _off64_t +# endif # include +# if defined(__MINGW32__) || defined(__MINGW64__) +# undef off_t +# undef off64_t +# endif # include # include # include diff --git a/src/compat/posix.h b/src/compat/posix.h index 043562d..fc0e8d8 100644 --- a/src/compat/posix.h +++ b/src/compat/posix.h @@ -40,7 +40,15 @@ # define _POSIX_SOURCE 1 # define TMP_POSIX # endif +# if defined(__MINGW32__) || defined(__MINGW64__) +# define off_t _off_t +# define off64_t _off64_t +# endif # include +# if defined(__MINGW32__) || defined(__MINGW64__) +# undef off_t +# undef off64_t +# endif # ifdef TMP_POSIX # undef _POSIX_SOURCE # undef TMP_POSIX From 547c77266b2cc935f045042d62efb25c5c0d9aef Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 1 Oct 2015 23:11:56 +0200 Subject: [PATCH 4/4] Added criterion_test_die to abort tests with a message on unhandled exceptions --- include/criterion/logging.h | 1 + po/fr.po | 22 ++++++++++++++++------ samples/tests/CMakeLists.txt | 2 ++ samples/tests/exception.cc | 18 ++++++++++++++++++ src/core/abort.c | 24 ++++++++++++++++++++++++ src/core/abort.h | 2 ++ src/core/report.h | 8 ++++---- src/core/runner.c | 17 +++++++++++++++++ src/core/worker.h | 1 + src/core/wrappers/wrap.cc | 18 ++++++------------ src/io/event.c | 16 ++++++++++++++++ src/io/event.h | 1 + src/log/normal.c | 30 ++++++++++++++++++++++++++++++ 13 files changed, 138 insertions(+), 22 deletions(-) create mode 100644 samples/tests/exception.cc diff --git a/include/criterion/logging.h b/include/criterion/logging.h index d823b9c..2f8e1d9 100644 --- a/include/criterion/logging.h +++ b/include/criterion/logging.h @@ -110,6 +110,7 @@ struct criterion_output_provider { void (*log_theory_fail )(struct criterion_theory_stats *stats); void (*log_test_timeout )(struct criterion_test_stats *stats); void (*log_test_crash )(struct criterion_test_stats *stats); + void (*log_test_abort )(struct criterion_test_stats *stats, const char *msg); void (*log_other_crash )(struct criterion_test_stats *stats); void (*log_abnormal_exit)(struct criterion_test_stats *stats); void (*log_post_test )(struct criterion_test_stats *stats); diff --git a/po/fr.po b/po/fr.po index 92ad06a..65717ee 100644 --- a/po/fr.po +++ b/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-09-27 12:24+0200\n" +"POT-Creation-Date: 2015-10-01 23:09+0200\n" "PO-Revision-Date: 2015-04-03 17:58+0200\n" "Last-Translator: \n" "Language-Team: French\n" @@ -78,6 +78,11 @@ msgstr "%1$s::%2$s: PLANTAGE!\n" #: src/log/normal.c:64 #, fuzzy, c-format +msgid "%1$s::%2$s: %3$s\n" +msgstr "%1$s::%2$s: (%3$3.2fs)\n" + +#: src/log/normal.c:65 +#, fuzzy, c-format msgid "" "%1$sWarning! The test `%2$s::%3$s` crashed during its setup or teardown." "%4$s\n" @@ -85,7 +90,7 @@ msgstr "" "%1$sAttention! Le test `%2$s::%3$s` a planté pendant son initialisation ou " "sa finalisation.%4$s\n" -#: src/log/normal.c:65 +#: src/log/normal.c:66 #, fuzzy, c-format msgid "" "%1$sWarning! The test `%2$s::%3$s` exited during its setup or teardown.%4$s\n" @@ -93,14 +98,14 @@ msgstr "" "%1$sAttention! Le test `%2$s::%3$s` a quitté pendant son initialisation ou " "sa finalisation.%4$s\n" -#: src/log/normal.c:66 +#: src/log/normal.c:67 #, c-format msgid "Running %1$s%2$lu%3$s test from %4$s%5$s%6$s:\n" msgid_plural "Running %1$s%2$lu%3$s tests from %4$s%5$s%6$s:\n" msgstr[0] "Lancement de %1$s%2$lu%3$s test dans %4$s%5$s%6$s:\n" msgstr[1] "Lancement de %1$s%2$lu%3$s tests dans %4$s%5$s%6$s:\n" -#: src/log/normal.c:68 +#: src/log/normal.c:69 #, c-format msgid "" "%1$sSynthesis: Tested: %2$s%3$lu%4$s | Passing: %5$s%6$lu%7$s | Failing: %8$s" @@ -109,6 +114,11 @@ msgstr "" "%1$sSynthèse: Testés: %2$s%3$lu%4$s | Validés: %5$s%6$lu%7$s | Échoués: %8$s" "%9$lu%10$s | Plantages: %11$s%12$lu%13$s %14$s\n" +#: src/log/normal.c:85 +#, fuzzy, c-format +msgid "%s::%s: %s\n" +msgstr "%1$s::%2$s: (%3$3.2fs)\n" + #: src/string/i18n.c:13 msgid "The conditions for this assertion were not met." msgstr "Les conditions de cette assertion n'ont pas été remplies." @@ -175,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:54 +#: src/core/runner.c:56 #, c-format msgid "" "%1$sWarning! Criterion has detected that it is running under valgrind, but " @@ -186,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:58 +#: src/core/runner.c:60 #, c-format msgid "" "%1$sWarning! Criterion has detected that it is running under valgrind, but " diff --git a/samples/tests/CMakeLists.txt b/samples/tests/CMakeLists.txt index 388c5ae..b2d2fca 100644 --- a/samples/tests/CMakeLists.txt +++ b/samples/tests/CMakeLists.txt @@ -10,6 +10,8 @@ set(SAMPLES long-messages.cc other-crashes.cc theories_regression.cc + + exception.cc ) add_samples("${CMAKE_CURRENT_LIST_DIR}" "${SAMPLES}") diff --git a/samples/tests/exception.cc b/samples/tests/exception.cc new file mode 100644 index 0000000..2cbf7f3 --- /dev/null +++ b/samples/tests/exception.cc @@ -0,0 +1,18 @@ +#include +#include + +void raise_std(void) { + throw std::invalid_argument("Some exception message"); +} + +void raise_any(void) { + throw 1; +} + +Test(exception, raise_std) { raise_std(); } +Test(exception, raise_any) { raise_any(); } + +Test(exception, raise_std_init, .init = raise_std) {} +Test(exception, raise_any_init, .init = raise_any) {} +Test(exception, raise_std_fini, .fini = raise_std) {} +Test(exception, raise_any_fini, .fini = raise_any) {} diff --git a/src/core/abort.c b/src/core/abort.c index 1124244..d8d7c65 100644 --- a/src/core/abort.c +++ b/src/core/abort.c @@ -21,10 +21,34 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include #include "abort.h" +#include "criterion/asprintf-compat.h" +#include "io/event.h" jmp_buf g_pre_test; void criterion_abort_test(void) { longjmp(g_pre_test, 1); } + +void criterion_test_die(const char *msg, ...) { + va_list vl; + va_start(vl, msg); + char *formatted_msg = NULL; + int res = cr_vasprintf(&formatted_msg, msg, vl); + va_end(vl); + + if (res < 0) + abort(); + + size_t *buf = malloc(sizeof (size_t) + res + 1); + *buf = res + 1; + memcpy(buf + 1, formatted_msg, res + 1); + + send_event(TEST_ABORT, buf, sizeof (size_t) + res + 1); + free(buf); + free(formatted_msg); + + exit(0); +} diff --git a/src/core/abort.h b/src/core/abort.h index 064fe67..401f09b 100644 --- a/src/core/abort.h +++ b/src/core/abort.h @@ -29,4 +29,6 @@ extern jmp_buf g_pre_test; +void criterion_test_die(const char *msg, ...); + #endif /* !ABORT_H_ */ diff --git a/src/core/report.h b/src/core/report.h index bdcffac..69f1126 100644 --- a/src/core/report.h +++ b/src/core/report.h @@ -45,9 +45,9 @@ DECL_CALL_REPORT_HOOKS(POST_FINI); DECL_CALL_REPORT_HOOKS(POST_SUITE); DECL_CALL_REPORT_HOOKS(POST_ALL); -#define log(Type, Arg) \ - log_(criterion_options.output_provider->log_ ## Type, Arg); -#define log_(Log, Arg) \ - (Log ? Log(Arg) : nothing()); +#define log(Type, ...) \ + log_(criterion_options.output_provider->log_ ## Type, __VA_ARGS__); +#define log_(Log, ...) \ + (Log ? Log(__VA_ARGS__) : nothing()); #endif /* !REPORT_H_ */ diff --git a/src/core/runner.c b/src/core/runner.c index 9e2d08e..3ca3281 100644 --- a/src/core/runner.c +++ b/src/core/runner.c @@ -228,6 +228,18 @@ static void handle_worker_terminated(struct event *ev, 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) { @@ -280,6 +292,11 @@ static void handle_event(struct event *ev) { 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); diff --git a/src/core/worker.h b/src/core/worker.h index 95a6ab7..649e3eb 100644 --- a/src/core/worker.h +++ b/src/core/worker.h @@ -38,6 +38,7 @@ struct execution_context { bool test_started; bool normal_finish; bool cleaned_up; + bool aborted; struct criterion_global_stats *stats; struct criterion_test *test; struct criterion_test_stats *test_stats; diff --git a/src/core/wrappers/wrap.cc b/src/core/wrappers/wrap.cc index f2ae058..f6b140a 100644 --- a/src/core/wrappers/wrap.cc +++ b/src/core/wrappers/wrap.cc @@ -44,11 +44,9 @@ void cpp_wrap(struct criterion_test *test, struct criterion_suite *suite) { (suite->data->init ? suite->data->init : nothing)(); (test->data->init ? test->data->init : nothing)(); } catch (const std::exception &e) { - cr_assert_fail("Caught an unexpected exception during the test initialization: %s.", e.what()); - std::exit(1); + criterion_test_die("Caught an unexpected exception during the test initialization: %s.", e.what()); } catch (...) { - cr_assert_fail("Caught some unexpected exception during the test initialization."); - std::exit(1); + criterion_test_die("Caught some unexpected exception during the test initialization."); } send_event(PRE_TEST, NULL, 0); @@ -64,11 +62,9 @@ void cpp_wrap(struct criterion_test *test, struct criterion_suite *suite) { param_test_func(g_worker_context.param->ptr); } } catch (const std::exception &e) { - cr_assert_fail("Caught an unexpected exception during the test execution: %s.", e.what()); - std::exit(1); + criterion_test_die("Caught an unexpected exception during the test execution: %s.", e.what()); } catch (...) { - cr_assert_fail("Caught some unexpected exception during the test execution."); - std::exit(1); + criterion_test_die("Caught some unexpected exception during the test execution."); } } } @@ -83,11 +79,9 @@ void cpp_wrap(struct criterion_test *test, struct criterion_suite *suite) { if (suite->data) (suite->data->fini ? suite->data->fini : nothing)(); } catch (const std::exception &e) { - cr_assert_fail("Caught an unexpected exception during the test finalization: %s.", e.what()); - std::exit(1); + criterion_test_die("Caught an unexpected exception during the test finalization: %s.", e.what()); } catch (...) { - cr_assert_fail("Caught some unexpected exception during the test finalization."); - std::exit(1); + criterion_test_die("Caught some unexpected exception during the test finalization."); } send_event(POST_FINI, NULL, 0); diff --git a/src/io/event.c b/src/io/event.c index 2747f63..99bdd00 100644 --- a/src/io/event.c +++ b/src/io/event.c @@ -90,6 +90,22 @@ struct event *read_event(s_pipe_file_handle *f) { *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); diff --git a/src/io/event.h b/src/io/event.h index 37818fb..e4e555b 100644 --- a/src/io/event.h +++ b/src/io/event.h @@ -41,6 +41,7 @@ struct event { enum other_event_kinds { WORKER_TERMINATED = 1 << 30, + TEST_ABORT, }; struct event *read_event(s_pipe_file_handle *f); diff --git a/src/log/normal.c b/src/log/normal.c index 3bb0c41..b48486a 100644 --- a/src/log/normal.c +++ b/src/log/normal.c @@ -61,6 +61,7 @@ static msg_t msg_theory_fail = N_(" Theory %1$s::%2$s failed with the following static msg_t msg_test_timeout = N_("%1$s::%2$s: Timed out. (%3$3.2fs)\n"); static msg_t msg_test_crash_line = N_("%1$s%2$s%3$s:%4$s%5$u%6$s: Unexpected signal caught below this line!\n"); static msg_t msg_test_crash = N_("%1$s::%2$s: CRASH!\n"); +static msg_t msg_test_abort = N_("%1$s::%2$s: %3$s\n"); static msg_t msg_test_other_crash = N_("%1$sWarning! The test `%2$s::%3$s` crashed during its setup or teardown.%4$s\n"); static msg_t msg_test_abnormal_exit = N_("%1$sWarning! The test `%2$s::%3$s` exited during its setup or teardown.%4$s\n"); static msg_t msg_pre_suite[] = N_s("Running %1$s%2$lu%3$s test from %4$s%5$s%6$s:\n", @@ -81,6 +82,7 @@ static msg_t msg_theory_fail = " Theory %s::%s failed with the following parame static msg_t msg_test_timeout = "%s::%s: Timed out. (%3.2fs)\n"; static msg_t msg_test_crash_line = "%s%s%s:%s%u%s: Unexpected signal caught below this line!\n"; static msg_t msg_test_crash = "%s::%s: CRASH!\n"; +static msg_t msg_test_abort = N_("%s::%s: %s\n"); static msg_t msg_test_other_crash = "%sWarning! The test `%s::%s` crashed during its setup or teardown.%s\n"; static msg_t msg_test_abnormal_exit = "%sWarning! The test `%s::%s` exited during its setup or teardown.%s\n"; static msg_t msg_pre_suite[] = { "Running %s%lu%s test from %s%s%s:\n", @@ -230,6 +232,33 @@ void normal_log_test_timeout(UNUSED struct criterion_test_stats *stats) { stats->elapsed_time); } +void normal_log_test_abort(UNUSED struct criterion_test_stats *stats, const char *msg) { + + char *dup = strdup(msg); + +#ifdef VANILLA_WIN32 + char *line = strtok(dup, "\n"); +#else + char *saveptr = NULL; + char *line = strtok_r(dup, "\n", &saveptr); +#endif + + criterion_pimportant(CRITERION_PREFIX_DASHES, + _(msg_test_abort), + stats->test->category, + stats->test->name, + line); + +#ifdef VANILLA_WIN32 + while ((line = strtok(NULL, "\n"))) +#else + while ((line = strtok_r(NULL, "\n", &saveptr))) +#endif + criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_desc), line); + + free(dup); +} + struct criterion_output_provider normal_logging = { .log_pre_all = normal_log_pre_all, .log_pre_init = normal_log_pre_init, @@ -238,6 +267,7 @@ struct criterion_output_provider normal_logging = { .log_theory_fail = normal_log_theory_fail, .log_test_timeout = normal_log_test_timeout, .log_test_crash = normal_log_test_crash, + .log_test_abort = normal_log_test_abort, .log_other_crash = normal_log_other_crash, .log_abnormal_exit = normal_log_abnormal_exit, .log_post_test = normal_log_post_test,