diff --git a/CMakeLists.txt b/CMakeLists.txt index b654c1e8..5d791b06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ set(SNITCH_MAX_UNIQUE_TAGS 1024 CACHE STRING "Maximum number of unique set(SNITCH_MAX_COMMAND_LINE_ARGS 1024 CACHE STRING "Maximum number of command line arguments to a test application.") set(SNITCH_MAX_REGISTERED_REPORTERS 8 CACHE STRING "Maximum number of registered reporter that can be selected from the command line.") set(SNITCH_MAX_PATH_LENGTH 1024 CACHE STRING "Maximum length of a file path when writing output to file.") +set(SNITCH_MAX_REPORTER_SIZE_BYTES 128 CACHE STRING "Maximum size (in bytes) of a reporter object.") # Feature toggles. set(SNITCH_DEFINE_MAIN ON CACHE BOOL "Define main() in snitch -- disable to provide your own main() function.") @@ -221,6 +222,7 @@ if (SNITCH_DO_TEST) SNITCH_MAX_MESSAGE_LENGTH=129 SNITCH_MAX_TEST_NAME_LENGTH=130 SNITCH_MAX_CAPTURE_LENGTH=131 + SNITCH_MAX_REPORTER_SIZE_BYTES=16 SNITCH_DEFINE_MAIN=0) endfunction() diff --git a/include/snitch/snitch_config.hpp.config b/include/snitch/snitch_config.hpp.config index e8974880..fcce56c9 100644 --- a/include/snitch/snitch_config.hpp.config +++ b/include/snitch/snitch_config.hpp.config @@ -47,6 +47,9 @@ #if !defined(SNITCH_MAX_PATH_LENGTH) # define SNITCH_MAX_PATH_LENGTH ${SNITCH_MAX_PATH_LENGTH} #endif +#if !defined(SNITCH_MAX_REPORTER_SIZE_BYTES) +# define SNITCH_MAX_REPORTER_SIZE_BYTES ${SNITCH_MAX_REPORTER_SIZE_BYTES} +#endif #if !defined(SNITCH_DEFINE_MAIN) #cmakedefine01 SNITCH_DEFINE_MAIN #endif diff --git a/include/snitch/snitch_macros_reporter.hpp b/include/snitch/snitch_macros_reporter.hpp index ff18a119..91934758 100644 --- a/include/snitch/snitch_macros_reporter.hpp +++ b/include/snitch/snitch_macros_reporter.hpp @@ -9,31 +9,9 @@ static const std::string_view SNITCH_MACRO_CONCAT(reporter_id_, __COUNTER__) \ [[maybe_unused]] = snitch::tests.add_reporter(NAME, __VA_ARGS__) -#define SNITCH_REGISTER_REPORTER_IMPL(NAME, TYPE, COUNTER) \ - static std::optional SNITCH_MACRO_CONCAT(reporter_, COUNTER); \ - static void SNITCH_MACRO_CONCAT(reporter_init_, COUNTER)(snitch::registry & r) noexcept { \ - SNITCH_MACRO_CONCAT(reporter_, COUNTER).emplace(r); \ - } \ - template \ - static bool SNITCH_MACRO_CONCAT(reporter_config_, COUNTER)( \ - snitch::registry & r, std::string_view k, std::string_view v) noexcept { \ - return SNITCH_MACRO_CONCAT(reporter_, COUNTER)->configure(r, k, v); \ - } \ - static void SNITCH_MACRO_CONCAT(reporter_report_, COUNTER)( \ - const snitch::registry& r, const snitch::event::data& e) noexcept { \ - SNITCH_MACRO_CONCAT(reporter_, COUNTER)->report(r, e); \ - } \ - static void SNITCH_MACRO_CONCAT(reporter_finish_, COUNTER)(snitch::registry&) noexcept { \ - SNITCH_MACRO_CONCAT(reporter_, COUNTER).reset(); \ - } \ - static const std::string_view SNITCH_MACRO_CONCAT(reporter_id_, COUNTER) [[maybe_unused]] = \ - snitch::tests.add_reporter( \ - NAME, &SNITCH_MACRO_CONCAT(reporter_init_, COUNTER), \ - &SNITCH_MACRO_CONCAT(reporter_config_, COUNTER) < TYPE >, \ - &SNITCH_MACRO_CONCAT(reporter_report_, COUNTER), \ - &SNITCH_MACRO_CONCAT(reporter_finish_, COUNTER)) - -#define SNITCH_REGISTER_REPORTER(NAME, TYPE) SNITCH_REGISTER_REPORTER_IMPL(NAME, TYPE, __COUNTER__) +#define SNITCH_REGISTER_REPORTER(NAME, TYPE) \ + static const std::string_view SNITCH_MACRO_CONCAT(reporter_id_, __COUNTER__) \ + [[maybe_unused]] = snitch::tests.add_reporter(NAME) // clang-format off #if SNITCH_WITH_SHORTHAND_MACROS diff --git a/include/snitch/snitch_registry.hpp b/include/snitch/snitch_registry.hpp index 939f0918..6fe63d29 100644 --- a/include/snitch/snitch_registry.hpp +++ b/include/snitch/snitch_registry.hpp @@ -1,6 +1,7 @@ #ifndef SNITCH_REGISTRY_HPP #define SNITCH_REGISTRY_HPP +#include "snitch/snitch_any.hpp" #include "snitch/snitch_append.hpp" #include "snitch/snitch_cli.hpp" #include "snitch/snitch_config.hpp" @@ -34,6 +35,8 @@ constexpr std::size_t max_tag_length = SNITCH_MAX_TAG_LENGTH; constexpr std::size_t max_unique_tags = SNITCH_MAX_UNIQUE_TAGS; // Maximum number of registered reporters to select from the command line. constexpr std::size_t max_registered_reporters = SNITCH_MAX_REGISTERED_REPORTERS; +// Maximum size of a reporter instance, in bytes. +constexpr std::size_t max_reporter_size_bytes = SNITCH_MAX_REPORTER_SIZE_BYTES; } // namespace snitch namespace snitch::impl { @@ -102,6 +105,13 @@ struct registered_reporter { finish_report_function finish = [](registry&) noexcept {}; }; +template +concept reporter_type = + requires(registry& reg) { T{reg}; } && + requires(T& rep, registry& reg, std::string_view k, std::string_view v) { + { rep.configure(reg, k, v) } -> convertible_to; + } && requires(T& rep, const registry& reg, const event::data& e) { rep.report(reg, e); }; + class registry { // Contains all registered test cases. small_vector test_list; @@ -112,8 +122,27 @@ class registry { // Used when writing output to file. std::optional file_writer; - // the default console reporter - snitch::reporter::console::reporter console_reporter; + // Type-erased storage for the current reporter instance. + inplace_any reporter_storage; + + template + void initialize_reporter(registry&) noexcept { + this->reporter_storage.emplace(*this); + } + + template + void report(const registry&, const event::data& e) noexcept { + this->reporter_storage.get().report(*this, e); + } + + template + bool configure_reporter(registry&, std::string_view k, std::string_view v) noexcept { + return this->reporter_storage.get().configure(*this, k, v); + } + + SNITCH_EXPORT void destroy_reporter(registry&) noexcept; + + SNITCH_EXPORT void report_default(const registry&, const event::data& e) noexcept; public: enum class verbosity { quiet, normal, high, full } verbose = verbosity::normal; @@ -125,9 +154,8 @@ class registry { using report_function = snitch::report_function; using finish_report_function = snitch::finish_report_function; - print_function print_callback = &snitch::impl::stdout_print; - report_function report_callback = { - console_reporter, snitch::constant<&snitch::reporter::console::reporter::report>{}}; + print_function print_callback = &snitch::impl::stdout_print; + report_function report_callback = {*this, constant<®istry::report_default>{}}; finish_report_function finish_callback = [](registry&) noexcept {}; // Internal API; do not use. @@ -178,8 +206,15 @@ class registry { const report_function& report, const std::optional& finish); - // Internal API; do not use. - SNITCH_EXPORT std::string_view add_console_reporter(); + // Requires: number of reporters + 1 <= max_registered_reporters. + template + std::string_view add_reporter(std::string_view name) { + return this->add_reporter( + name, initialize_report_function{*this, constant<®istry::initialize_reporter>{}}, + configure_report_function{*this, constant<®istry::configure_reporter>{}}, + report_function{*this, constant<®istry::report>{}}, + finish_report_function{*this, constant<®istry::destroy_reporter>{}}); + } // Internal API; do not use. // Requires: number of tests + 1 <= max_test_cases, well-formed test ID. diff --git a/include/snitch/snitch_reporter_console.hpp b/include/snitch/snitch_reporter_console.hpp index f419a323..73743854 100644 --- a/include/snitch/snitch_reporter_console.hpp +++ b/include/snitch/snitch_reporter_console.hpp @@ -12,7 +12,7 @@ struct reporter { reporter() = default; - SNITCH_EXPORT void init(registry& r) noexcept; + SNITCH_EXPORT explicit reporter(registry& r) noexcept; SNITCH_EXPORT bool configure(registry&, std::string_view, std::string_view) noexcept; diff --git a/meson_options.txt b/meson_options.txt index 097599d2..18aad8d0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,6 +11,7 @@ option('max_unique_tags' ,type: 'integer' ,value: 1024, description: 'M option('max_command_line_args' ,type: 'integer' ,value: 1024, description: 'Maximum number of command line arguments to a test application.') option('max_registered_reporters' ,type: 'integer' ,value: 8 , description: 'Maximum number of registered reporter that can be selected from the command line.') option('max_path_length' ,type: 'integer' ,value: 1024, description: 'Maximum length of a file path when writing output to file.') +option('max_reporter_size_bytes' ,type: 'integer' ,value: 128, description: 'Maximum size (in bytes) of a reporter object.') # Feature toggles. option('define_main' ,type: 'boolean' ,value: true, description: 'Define main() in snitch -- disable to provide your own main() function.') diff --git a/snitch/meson.build b/snitch/meson.build index b9f958cd..078b40bc 100644 --- a/snitch/meson.build +++ b/snitch/meson.build @@ -31,6 +31,7 @@ conf_data = configuration_data({ 'SNITCH_MAX_COMMAND_LINE_ARGS' : get_option('max_command_line_args'), 'SNITCH_MAX_REGISTERED_REPORTERS' : get_option('max_registered_reporters'), 'SNITCH_MAX_PATH_LENGTH' : get_option('max_path_length'), + 'SNITCH_MAX_REPORTER_SIZE_BYTES' : get_option('max_reporter_size_bytes'), 'SNITCH_DEFINE_MAIN' : get_option('define_main').to_int(), 'SNITCH_WITH_EXCEPTIONS' : get_option('with_exceptions').to_int(), diff --git a/src/snitch_registry.cpp b/src/snitch_registry.cpp index c5a08c20..796b97fc 100644 --- a/src/snitch_registry.cpp +++ b/src/snitch_registry.cpp @@ -336,13 +336,18 @@ std::string_view registry::add_reporter( return name; } -std::string_view registry::add_console_reporter() { - using reporter_type = snitch::reporter::console::reporter; - return add_reporter("console", - initialize_report_function{console_reporter, snitch::constant<&reporter_type::init>{}}, - configure_report_function{console_reporter, snitch::constant<&reporter_type::configure>{}}, - {console_reporter, snitch::constant<&snitch::reporter::console::reporter::report>{}}, - {}); +void registry::destroy_reporter(registry&) noexcept { + reporter_storage.reset(); +} + +void registry::report_default(const registry&, const event::data& e) noexcept { + using default_reporter = reporter::console::reporter; + + if (reporter_storage.type() != type_id()) { + reporter_storage.emplace(*this); + } + + reporter_storage.get().report(*this, e); } const char* @@ -1028,6 +1033,3 @@ small_vector_span registry::reporters() const noexcep constinit registry tests; } // namespace snitch - -static const std::string_view console_reporter_id [[maybe_unused]] = - snitch::tests.add_console_reporter(); diff --git a/src/snitch_reporter_console.cpp b/src/snitch_reporter_console.cpp index ff8ab098..b4ea916e 100644 --- a/src/snitch_reporter_console.cpp +++ b/src/snitch_reporter_console.cpp @@ -86,9 +86,7 @@ void print_message(const registry& r, const assertion_data& data) { } } // namespace -void reporter::init(registry&) noexcept { - counter = 0; -} +reporter::reporter(registry&) noexcept {} bool reporter::configure(registry& r, std::string_view option, std::string_view value) noexcept { if (option == "color") { @@ -205,3 +203,5 @@ void reporter::report(const registry& r, const event::data& event) noexcept { event); } } // namespace snitch::reporter::console + +SNITCH_REGISTER_REPORTER("console", snitch::reporter::console::reporter); diff --git a/tests/approval_tests/reporter_console.cpp b/tests/approval_tests/reporter_console.cpp index adf69338..86ec6ba4 100644 --- a/tests/approval_tests/reporter_console.cpp +++ b/tests/approval_tests/reporter_console.cpp @@ -13,7 +13,7 @@ TEST_CASE("console reporter", "[reporters]") { mock_framework framework; register_tests_for_reporters(framework.registry); - framework.registry.add_console_reporter(); + framework.registry.add_reporter("console"); framework.registry.with_color = false; diff --git a/tests/testing_event.cpp b/tests/testing_event.cpp index 5a8f48ca..9e8523e9 100644 --- a/tests/testing_event.cpp +++ b/tests/testing_event.cpp @@ -229,7 +229,7 @@ std::optional get_location(const owning_event::data& e) } mock_framework::mock_framework() noexcept { - registry.add_console_reporter(); + registry.add_reporter("console"); registry.print_callback = [](std::string_view msg) noexcept { snitch::cli::console_print(msg);