aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt43
-rw-r--r--src/catch2/benchmark/catch_benchmark.hpp32
-rw-r--r--src/catch2/benchmark/catch_benchmark_all.hpp2
-rw-r--r--src/catch2/benchmark/catch_chronometer.hpp6
-rw-r--r--src/catch2/benchmark/catch_clock.hpp16
-rw-r--r--src/catch2/benchmark/catch_environment.hpp14
-rw-r--r--src/catch2/benchmark/catch_estimate.hpp13
-rw-r--r--src/catch2/benchmark/catch_execution_plan.hpp40
-rw-r--r--src/catch2/benchmark/catch_optimizer.hpp13
-rw-r--r--src/catch2/benchmark/catch_sample_analysis.hpp26
-rw-r--r--src/catch2/benchmark/detail/catch_analyse.cpp85
-rw-r--r--src/catch2/benchmark/detail/catch_analyse.hpp59
-rw-r--r--src/catch2/benchmark/detail/catch_benchmark_function.hpp1
-rw-r--r--src/catch2/benchmark/detail/catch_benchmark_stats.hpp48
-rw-r--r--src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp23
-rw-r--r--src/catch2/benchmark/detail/catch_complete_invoke.hpp5
-rw-r--r--src/catch2/benchmark/detail/catch_estimate_clock.hpp64
-rw-r--r--src/catch2/benchmark/detail/catch_measure.hpp3
-rw-r--r--src/catch2/benchmark/detail/catch_run_for_at_least.cpp3
-rw-r--r--src/catch2/benchmark/detail/catch_run_for_at_least.hpp8
-rw-r--r--src/catch2/benchmark/detail/catch_stats.cpp462
-rw-r--r--src/catch2/benchmark/detail/catch_stats.hpp126
-rw-r--r--src/catch2/benchmark/detail/catch_timing.hpp8
-rw-r--r--src/catch2/catch_all.hpp10
-rw-r--r--src/catch2/catch_approx.cpp4
-rw-r--r--src/catch2/catch_assertion_result.cpp22
-rw-r--r--src/catch2/catch_assertion_result.hpp2
-rw-r--r--src/catch2/catch_config.cpp2
-rw-r--r--src/catch2/catch_message.cpp10
-rw-r--r--src/catch2/catch_message.hpp26
-rw-r--r--src/catch2/catch_registry_hub.cpp5
-rw-r--r--src/catch2/catch_session.cpp11
-rw-r--r--src/catch2/catch_test_case_info.cpp24
-rw-r--r--src/catch2/catch_test_case_info.hpp1
-rw-r--r--src/catch2/catch_test_macros.hpp4
-rw-r--r--src/catch2/catch_test_spec.cpp20
-rw-r--r--src/catch2/catch_tostring.hpp7
-rw-r--r--src/catch2/catch_totals.cpp8
-rw-r--r--src/catch2/catch_totals.hpp1
-rw-r--r--src/catch2/catch_translate_exception.cpp20
-rw-r--r--src/catch2/catch_translate_exception.hpp10
-rw-r--r--src/catch2/catch_user_config.hpp.in12
-rw-r--r--src/catch2/catch_version.cpp2
-rw-r--r--src/catch2/catch_version_macros.hpp4
-rw-r--r--src/catch2/generators/catch_generators.cpp9
-rw-r--r--src/catch2/generators/catch_generators.hpp35
-rw-r--r--src/catch2/generators/catch_generators_random.cpp32
-rw-r--r--src/catch2/generators/catch_generators_random.hpp34
-rw-r--r--src/catch2/generators/catch_generators_range.hpp9
-rw-r--r--src/catch2/interfaces/catch_interfaces_all.hpp2
-rw-r--r--src/catch2/interfaces/catch_interfaces_capture.hpp34
-rw-r--r--src/catch2/interfaces/catch_interfaces_exception.hpp1
-rw-r--r--src/catch2/interfaces/catch_interfaces_registry_hub.hpp4
-rw-r--r--src/catch2/interfaces/catch_interfaces_reporter.cpp19
-rw-r--r--src/catch2/interfaces/catch_interfaces_reporter.hpp64
-rw-r--r--src/catch2/interfaces/catch_interfaces_reporter_registry.cpp13
-rw-r--r--src/catch2/interfaces/catch_interfaces_reporter_registry.hpp42
-rw-r--r--src/catch2/interfaces/catch_interfaces_test_invoker.hpp21
-rw-r--r--src/catch2/interfaces/catch_interfaces_testcase.cpp1
-rw-r--r--src/catch2/interfaces/catch_interfaces_testcase.hpp13
-rw-r--r--src/catch2/internal/catch_assertion_handler.cpp13
-rw-r--r--src/catch2/internal/catch_assertion_handler.hpp5
-rw-r--r--src/catch2/internal/catch_commandline.cpp4
-rw-r--r--src/catch2/internal/catch_compiler_capabilities.hpp56
-rw-r--r--src/catch2/internal/catch_config_counter.hpp2
-rw-r--r--src/catch2/internal/catch_config_prefix_messages.hpp29
-rw-r--r--src/catch2/internal/catch_config_static_analysis_support.hpp34
-rw-r--r--src/catch2/internal/catch_config_uncaught_exceptions.hpp2
-rw-r--r--src/catch2/internal/catch_config_wchar.hpp2
-rw-r--r--src/catch2/internal/catch_console_colour.cpp6
-rw-r--r--src/catch2/internal/catch_console_colour.hpp1
-rw-r--r--src/catch2/internal/catch_context.cpp52
-rw-r--r--src/catch2/internal/catch_context.hpp45
-rw-r--r--src/catch2/internal/catch_debugger.hpp5
-rw-r--r--src/catch2/internal/catch_enum_values_registry.cpp2
-rw-r--r--src/catch2/internal/catch_exception_translator_registry.cpp37
-rw-r--r--src/catch2/internal/catch_exception_translator_registry.hpp1
-rw-r--r--src/catch2/internal/catch_floating_point_helpers.cpp11
-rw-r--r--src/catch2/internal/catch_floating_point_helpers.hpp5
-rw-r--r--src/catch2/internal/catch_is_permutation.hpp138
-rw-r--r--src/catch2/internal/catch_istream.cpp8
-rw-r--r--src/catch2/internal/catch_jsonwriter.cpp148
-rw-r--r--src/catch2/internal/catch_jsonwriter.hpp120
-rw-r--r--src/catch2/internal/catch_leak_detector.cpp2
-rw-r--r--src/catch2/internal/catch_list.cpp6
-rw-r--r--src/catch2/internal/catch_message_info.hpp2
-rw-r--r--src/catch2/internal/catch_optional.hpp55
-rw-r--r--src/catch2/internal/catch_parse_numbers.cpp15
-rw-r--r--src/catch2/internal/catch_polyfills.cpp8
-rw-r--r--src/catch2/internal/catch_polyfills.hpp5
-rw-r--r--src/catch2/internal/catch_preprocessor_internal_stringify.hpp19
-rw-r--r--src/catch2/internal/catch_random_floating_point_helpers.hpp94
-rw-r--r--src/catch2/internal/catch_random_integer_helpers.hpp202
-rw-r--r--src/catch2/internal/catch_random_seed_generation.cpp9
-rw-r--r--src/catch2/internal/catch_reporter_registry.cpp86
-rw-r--r--src/catch2/internal/catch_reporter_registry.hpp45
-rw-r--r--src/catch2/internal/catch_reporter_spec_parser.cpp6
-rw-r--r--src/catch2/internal/catch_result_type.hpp2
-rw-r--r--src/catch2/internal/catch_run_context.cpp415
-rw-r--r--src/catch2/internal/catch_run_context.hpp33
-rw-r--r--src/catch2/internal/catch_section.cpp30
-rw-r--r--src/catch2/internal/catch_section.hpp72
-rw-r--r--src/catch2/internal/catch_stream_end_stop.hpp4
-rw-r--r--src/catch2/internal/catch_string_manip.cpp7
-rw-r--r--src/catch2/internal/catch_string_manip.hpp1
-rw-r--r--src/catch2/internal/catch_stringref.cpp4
-rw-r--r--src/catch2/internal/catch_stringref.hpp7
-rw-r--r--src/catch2/internal/catch_tag_alias_registry.cpp2
-rw-r--r--src/catch2/internal/catch_template_test_registry.hpp4
-rw-r--r--src/catch2/internal/catch_test_case_registry_impl.cpp63
-rw-r--r--src/catch2/internal/catch_test_case_registry_impl.hpp21
-rw-r--r--src/catch2/internal/catch_test_case_tracker.cpp75
-rw-r--r--src/catch2/internal/catch_test_case_tracker.hpp79
-rw-r--r--src/catch2/internal/catch_test_failure_exception.cpp8
-rw-r--r--src/catch2/internal/catch_test_failure_exception.hpp9
-rw-r--r--src/catch2/internal/catch_test_macro_impl.hpp12
-rw-r--r--src/catch2/internal/catch_test_registry.cpp16
-rw-r--r--src/catch2/internal/catch_test_registry.hpp71
-rw-r--r--src/catch2/internal/catch_test_run_info.hpp22
-rw-r--r--src/catch2/internal/catch_test_spec_parser.cpp2
-rw-r--r--src/catch2/internal/catch_textflow.hpp2
-rw-r--r--src/catch2/internal/catch_uniform_floating_point_distribution.hpp131
-rw-r--r--src/catch2/internal/catch_uniform_integer_distribution.hpp126
-rw-r--r--src/catch2/internal/catch_xmlwriter.cpp1
-rw-r--r--src/catch2/matchers/catch_matchers_all.hpp1
-rw-r--r--src/catch2/matchers/catch_matchers_contains.hpp14
-rw-r--r--src/catch2/matchers/catch_matchers_exception.hpp26
-rw-r--r--src/catch2/matchers/catch_matchers_floating_point.cpp29
-rw-r--r--src/catch2/matchers/catch_matchers_floating_point.hpp30
-rw-r--r--src/catch2/matchers/catch_matchers_range_equals.hpp144
-rw-r--r--src/catch2/matchers/catch_matchers_vector.hpp9
-rw-r--r--src/catch2/matchers/internal/catch_matchers_impl.hpp7
-rw-r--r--src/catch2/meson.build20
-rw-r--r--src/catch2/reporters/catch_reporter_automake.cpp6
-rw-r--r--src/catch2/reporters/catch_reporter_automake.hpp1
-rw-r--r--src/catch2/reporters/catch_reporter_compact.cpp13
-rw-r--r--src/catch2/reporters/catch_reporter_console.cpp106
-rw-r--r--src/catch2/reporters/catch_reporter_cumulative_base.cpp3
-rw-r--r--src/catch2/reporters/catch_reporter_cumulative_base.hpp3
-rw-r--r--src/catch2/reporters/catch_reporter_helpers.cpp9
-rw-r--r--src/catch2/reporters/catch_reporter_json.cpp372
-rw-r--r--src/catch2/reporters/catch_reporter_json.hpp95
-rw-r--r--src/catch2/reporters/catch_reporter_junit.cpp18
-rw-r--r--src/catch2/reporters/catch_reporter_multi.cpp1
-rw-r--r--src/catch2/reporters/catch_reporter_registrars.cpp6
-rw-r--r--src/catch2/reporters/catch_reporter_registrars.hpp9
-rw-r--r--src/catch2/reporters/catch_reporter_sonarqube.cpp21
-rw-r--r--src/catch2/reporters/catch_reporter_sonarqube.hpp2
-rw-r--r--src/catch2/reporters/catch_reporter_streaming_base.hpp1
-rw-r--r--src/catch2/reporters/catch_reporter_tap.cpp11
-rw-r--r--src/catch2/reporters/catch_reporter_teamcity.cpp24
-rw-r--r--src/catch2/reporters/catch_reporter_xml.cpp69
-rw-r--r--src/catch2/reporters/catch_reporters_all.hpp1
153 files changed, 3780 insertions, 1408 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b54c6274..d01aab2d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -21,6 +21,8 @@ set(BENCHMARK_HEADERS
${SOURCES_DIR}/benchmark/catch_sample_analysis.hpp
${SOURCES_DIR}/benchmark/detail/catch_analyse.hpp
${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.hpp
+ ${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats.hpp
+ ${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats_fwd.hpp
${SOURCES_DIR}/benchmark/detail/catch_complete_invoke.hpp
${SOURCES_DIR}/benchmark/detail/catch_estimate_clock.hpp
${SOURCES_DIR}/benchmark/detail/catch_measure.hpp
@@ -31,6 +33,7 @@ set(BENCHMARK_HEADERS
)
set(BENCHMARK_SOURCES
${SOURCES_DIR}/benchmark/catch_chronometer.cpp
+ ${SOURCES_DIR}/benchmark/detail/catch_analyse.cpp
${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.cpp
${SOURCES_DIR}/benchmark/detail/catch_run_for_at_least.cpp
${SOURCES_DIR}/benchmark/detail/catch_stats.cpp
@@ -39,7 +42,7 @@ set(BENCHMARK_FILES ${BENCHMARK_HEADERS} ${BENCHMARK_SOURCES})
set(IMPL_HEADERS
- "${CMAKE_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp"
+ "${PROJECT_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp"
${SOURCES_DIR}/catch_user_config.hpp.in
${SOURCES_DIR}/catch_all.hpp
${SOURCES_DIR}/catch_approx.hpp
@@ -71,6 +74,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_compiler_capabilities.hpp
${SOURCES_DIR}/internal/catch_config_android_logwrite.hpp
${SOURCES_DIR}/internal/catch_config_counter.hpp
+ ${SOURCES_DIR}/internal/catch_config_static_analysis_support.hpp
${SOURCES_DIR}/internal/catch_config_uncaught_exceptions.hpp
${SOURCES_DIR}/internal/catch_config_wchar.hpp
${SOURCES_DIR}/internal/catch_console_colour.hpp
@@ -88,6 +92,8 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_floating_point_helpers.hpp
${SOURCES_DIR}/internal/catch_getenv.hpp
${SOURCES_DIR}/internal/catch_istream.hpp
+ ${SOURCES_DIR}/internal/catch_is_permutation.hpp
+ ${SOURCES_DIR}/internal/catch_jsonwriter.hpp
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_list.hpp
@@ -103,6 +109,8 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_polyfills.hpp
${SOURCES_DIR}/internal/catch_preprocessor.hpp
${SOURCES_DIR}/internal/catch_preprocessor_remove_parens.hpp
+ ${SOURCES_DIR}/internal/catch_random_floating_point_helpers.hpp
+ ${SOURCES_DIR}/internal/catch_random_integer_helpers.hpp
${SOURCES_DIR}/internal/catch_random_number_generator.hpp
${SOURCES_DIR}/internal/catch_random_seed_generation.hpp
${SOURCES_DIR}/internal/catch_reporter_registry.hpp
@@ -127,10 +135,13 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_test_failure_exception.hpp
${SOURCES_DIR}/internal/catch_test_macro_impl.hpp
${SOURCES_DIR}/internal/catch_test_registry.hpp
+ ${SOURCES_DIR}/internal/catch_test_run_info.hpp
${SOURCES_DIR}/internal/catch_test_spec_parser.hpp
${SOURCES_DIR}/internal/catch_textflow.hpp
${SOURCES_DIR}/internal/catch_to_string.hpp
${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp
+ ${SOURCES_DIR}/internal/catch_uniform_floating_point_distribution.hpp
+ ${SOURCES_DIR}/internal/catch_uniform_integer_distribution.hpp
${SOURCES_DIR}/internal/catch_unique_name.hpp
${SOURCES_DIR}/internal/catch_unique_ptr.hpp
${SOURCES_DIR}/internal/catch_void_type.hpp
@@ -152,6 +163,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/catch_timer.cpp
${SOURCES_DIR}/catch_tostring.cpp
${SOURCES_DIR}/catch_totals.cpp
+ ${SOURCES_DIR}/catch_translate_exception.cpp
${SOURCES_DIR}/catch_version.cpp
${SOURCES_DIR}/internal/catch_assertion_handler.cpp
${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.cpp
@@ -170,6 +182,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp
${SOURCES_DIR}/internal/catch_getenv.cpp
${SOURCES_DIR}/internal/catch_istream.cpp
+ ${SOURCES_DIR}/internal/catch_jsonwriter.cpp
${SOURCES_DIR}/internal/catch_lazy_expr.cpp
${SOURCES_DIR}/internal/catch_leak_detector.cpp
${SOURCES_DIR}/internal/catch_list.cpp
@@ -215,8 +228,8 @@ set(INTERFACE_HEADERS
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.hpp
- ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_tag_alias_registry.hpp
+ ${SOURCES_DIR}/interfaces/catch_interfaces_test_invoker.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_testcase.hpp
)
set(INTERFACE_SOURCES
@@ -227,7 +240,6 @@ set(INTERFACE_SOURCES
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.cpp
- ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_testcase.cpp
)
set(INTERFACE_FILES ${INTERFACE_HEADERS} ${INTERFACE_SOURCES})
@@ -252,6 +264,7 @@ set(MATCHER_HEADERS
${SOURCES_DIR}/matchers/catch_matchers_all.hpp
${SOURCES_DIR}/matchers/catch_matchers_container_properties.hpp
${SOURCES_DIR}/matchers/catch_matchers_contains.hpp
+ ${SOURCES_DIR}/matchers/catch_matchers_range_equals.hpp
${SOURCES_DIR}/matchers/catch_matchers_exception.hpp
${SOURCES_DIR}/matchers/catch_matchers_floating_point.hpp
${SOURCES_DIR}/matchers/catch_matchers_predicate.hpp
@@ -282,6 +295,7 @@ set(REPORTER_HEADERS
${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.hpp
${SOURCES_DIR}/reporters/catch_reporter_event_listener.hpp
${SOURCES_DIR}/reporters/catch_reporter_helpers.hpp
+ ${SOURCES_DIR}/reporters/catch_reporter_json.hpp
${SOURCES_DIR}/reporters/catch_reporter_junit.hpp
${SOURCES_DIR}/reporters/catch_reporter_multi.hpp
${SOURCES_DIR}/reporters/catch_reporter_registrars.hpp
@@ -300,6 +314,7 @@ set(REPORTER_SOURCES
${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.cpp
${SOURCES_DIR}/reporters/catch_reporter_event_listener.cpp
${SOURCES_DIR}/reporters/catch_reporter_helpers.cpp
+ ${SOURCES_DIR}/reporters/catch_reporter_json.cpp
${SOURCES_DIR}/reporters/catch_reporter_junit.cpp
${SOURCES_DIR}/reporters/catch_reporter_multi.cpp
${SOURCES_DIR}/reporters/catch_reporter_registrars.cpp
@@ -321,7 +336,7 @@ set(ALL_FILES
)
set(FILTERED_FILES ${ALL_FILES})
-list(REMOVE_ITEM FILTERED_FILES "${CMAKE_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp")
+list(REMOVE_ITEM FILTERED_FILES "${PROJECT_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp")
source_group(
TREE ${SOURCES_DIR}
PREFIX sources
@@ -329,7 +344,7 @@ source_group(
)
source_group("generated headers"
FILES
- "${CMAKE_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp"
+ "${PROJECT_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp"
)
add_library(Catch2 ${ALL_FILES})
@@ -372,13 +387,13 @@ target_compile_features(Catch2
configure_file(
"${SOURCES_DIR}/catch_user_config.hpp.in"
- "${CMAKE_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp"
+ "${PROJECT_BINARY_DIR}/generated-includes/catch2/catch_user_config.hpp"
)
target_include_directories(Catch2
PUBLIC
$<BUILD_INTERFACE:${SOURCES_DIR}/..>
- $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/generated-includes>
+ $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/generated-includes>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
@@ -426,7 +441,7 @@ if (NOT_SUBPROJECT)
install(
DIRECTORY
"${SOURCES_DIR}"
- "${CMAKE_BINARY_DIR}/generated-includes/catch2" # Also install the generated header
+ "${PROJECT_BINARY_DIR}/generated-includes/catch2" # Also install the generated header
DESTINATION
"${CMAKE_INSTALL_INCLUDEDIR}"
FILES_MATCHING
@@ -447,7 +462,7 @@ if (CATCH_BUILD_EXAMPLES OR CATCH_BUILD_EXTRA_TESTS)
target_include_directories(Catch2_buildall_interface
INTERFACE
$<BUILD_INTERFACE:${SOURCES_DIR}/..>
- $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/generated-includes>
+ $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/generated-includes>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
target_compile_definitions(Catch2_buildall_interface
@@ -487,12 +502,10 @@ set(CATCH_WARNING_TARGETS ${CATCH_WARNING_TARGETS} PARENT_SCOPE)
# so we want to check & warn users if they do this. However, we won't abort
# the configuration step so that we don't have to also provide an override.
if (BUILD_SHARED_LIBS)
- if (MSVC)
- set_target_properties(Catch2 Catch2WithMain
- PROPERTIES
- WINDOWS_EXPORT_ALL_SYMBOLS ON
- )
- endif()
+ set_target_properties(Catch2 Catch2WithMain
+ PROPERTIES
+ WINDOWS_EXPORT_ALL_SYMBOLS ON
+ )
get_target_property(_VisPreset Catch2 CXX_VISIBILITY_PRESET)
if (NOT MSVC AND _VisPreset STREQUAL "hidden")
diff --git a/src/catch2/benchmark/catch_benchmark.hpp b/src/catch2/benchmark/catch_benchmark.hpp
index a2d5c3ea..3db40bb0 100644
--- a/src/catch2/benchmark/catch_benchmark.hpp
+++ b/src/catch2/benchmark/catch_benchmark.hpp
@@ -10,26 +10,28 @@
#ifndef CATCH_BENCHMARK_HPP_INCLUDED
#define CATCH_BENCHMARK_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/catch_user_config.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_context.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
-#include <catch2/internal/catch_unique_name.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
-#include <catch2/benchmark/catch_chronometer.hpp>
+#include <catch2/internal/catch_test_failure_exception.hpp>
+#include <catch2/internal/catch_unique_name.hpp>
+#include <catch2/interfaces/catch_interfaces_capture.hpp>
+#include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
+#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_environment.hpp>
#include <catch2/benchmark/catch_execution_plan.hpp>
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
-#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/benchmark/detail/catch_analyse.hpp>
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
#include <algorithm>
-#include <functional>
+#include <chrono>
+#include <exception>
#include <string>
-#include <vector>
#include <cmath>
namespace Catch {
@@ -43,16 +45,18 @@ namespace Catch {
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
template <typename Clock>
- ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+ ExecutionPlan prepare(const IConfig &cfg, Environment env) const {
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
- auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
+ auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
- return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
+ return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
}
template <typename Clock = default_clock>
void run() {
+ static_assert( Clock::is_steady,
+ "Benchmarking clock should be steady" );
auto const* cfg = getCurrentContext().getConfig();
auto env = Detail::measure_environment<Clock>();
@@ -64,7 +68,7 @@ namespace Catch {
});
BenchmarkInfo info {
- name,
+ CATCH_MOVE(name),
plan.estimated_duration.count(),
plan.iterations_per_sample,
cfg->benchmarkSamples(),
@@ -79,10 +83,10 @@ namespace Catch {
return plan.template run<Clock>(*cfg, env);
});
- auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
- BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
+ auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
+ BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats);
- } CATCH_CATCH_ANON (TestFailureException) {
+ } CATCH_CATCH_ANON (TestFailureException const&) {
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
} CATCH_CATCH_ALL{
getResultCapture().benchmarkFailed(translateActiveException());
diff --git a/src/catch2/benchmark/catch_benchmark_all.hpp b/src/catch2/benchmark/catch_benchmark_all.hpp
index eb81f238..56fc7c74 100644
--- a/src/catch2/benchmark/catch_benchmark_all.hpp
+++ b/src/catch2/benchmark/catch_benchmark_all.hpp
@@ -33,6 +33,8 @@
#include <catch2/benchmark/catch_sample_analysis.hpp>
#include <catch2/benchmark/detail/catch_analyse.hpp>
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
+#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
+#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
#include <catch2/benchmark/detail/catch_measure.hpp>
diff --git a/src/catch2/benchmark/catch_chronometer.hpp b/src/catch2/benchmark/catch_chronometer.hpp
index bce2406b..95498e6b 100644
--- a/src/catch2/benchmark/catch_chronometer.hpp
+++ b/src/catch2/benchmark/catch_chronometer.hpp
@@ -12,7 +12,6 @@
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_optimizer.hpp>
-#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
@@ -33,7 +32,10 @@ namespace Catch {
void start() override { started = Clock::now(); }
void finish() override { finished = Clock::now(); }
- ClockDuration<Clock> elapsed() const { return finished - started; }
+ IDuration elapsed() const {
+ return std::chrono::duration_cast<std::chrono::nanoseconds>(
+ finished - started );
+ }
TimePoint<Clock> started;
TimePoint<Clock> finished;
diff --git a/src/catch2/benchmark/catch_clock.hpp b/src/catch2/benchmark/catch_clock.hpp
index cee46097..4068c4d2 100644
--- a/src/catch2/benchmark/catch_clock.hpp
+++ b/src/catch2/benchmark/catch_clock.hpp
@@ -11,28 +11,16 @@
#define CATCH_CLOCK_HPP_INCLUDED
#include <chrono>
-#include <ratio>
namespace Catch {
namespace Benchmark {
- template <typename Clock>
- using ClockDuration = typename Clock::duration;
- template <typename Clock>
- using FloatDuration = std::chrono::duration<double, typename Clock::period>;
+ using IDuration = std::chrono::nanoseconds;
+ using FDuration = std::chrono::duration<double, std::nano>;
template <typename Clock>
using TimePoint = typename Clock::time_point;
using default_clock = std::chrono::steady_clock;
-
- template <typename Clock>
- struct now {
- TimePoint<Clock> operator()() const {
- return Clock::now();
- }
- };
-
- using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/benchmark/catch_environment.hpp b/src/catch2/benchmark/catch_environment.hpp
index de4d77df..da3f2fa9 100644
--- a/src/catch2/benchmark/catch_environment.hpp
+++ b/src/catch2/benchmark/catch_environment.hpp
@@ -15,21 +15,13 @@
namespace Catch {
namespace Benchmark {
- template <typename Duration>
struct EnvironmentEstimate {
- Duration mean;
+ FDuration mean;
OutlierClassification outliers;
-
- template <typename Duration2>
- operator EnvironmentEstimate<Duration2>() const {
- return { mean, outliers };
- }
};
- template <typename Clock>
struct Environment {
- using clock_type = Clock;
- EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
- EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
+ EnvironmentEstimate clock_resolution;
+ EnvironmentEstimate clock_cost;
};
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/benchmark/catch_estimate.hpp b/src/catch2/benchmark/catch_estimate.hpp
index be594a18..64383a2e 100644
--- a/src/catch2/benchmark/catch_estimate.hpp
+++ b/src/catch2/benchmark/catch_estimate.hpp
@@ -12,17 +12,12 @@
namespace Catch {
namespace Benchmark {
- template <typename Duration>
+ template <typename Type>
struct Estimate {
- Duration point;
- Duration lower_bound;
- Duration upper_bound;
+ Type point;
+ Type lower_bound;
+ Type upper_bound;
double confidence_interval;
-
- template <typename Duration2>
- operator Estimate<Duration2>() const {
- return { point, lower_bound, upper_bound, confidence_interval };
- }
};
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/benchmark/catch_execution_plan.hpp b/src/catch2/benchmark/catch_execution_plan.hpp
index 039de7ee..17ca589f 100644
--- a/src/catch2/benchmark/catch_execution_plan.hpp
+++ b/src/catch2/benchmark/catch_execution_plan.hpp
@@ -17,38 +17,38 @@
#include <catch2/benchmark/detail/catch_repeat.hpp>
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
-#include <algorithm>
-#include <iterator>
+#include <vector>
namespace Catch {
namespace Benchmark {
- template <typename Duration>
struct ExecutionPlan {
int iterations_per_sample;
- Duration estimated_duration;
+ FDuration estimated_duration;
Detail::BenchmarkFunction benchmark;
- Duration warmup_time;
+ FDuration warmup_time;
int warmup_iterations;
- template <typename Duration2>
- operator ExecutionPlan<Duration2>() const {
- return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
- }
-
template <typename Clock>
- std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
+ std::vector<FDuration> run(const IConfig &cfg, Environment env) const {
// warmup a bit
- Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
-
- std::vector<FloatDuration<Clock>> times;
- times.reserve(cfg.benchmarkSamples());
- std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
+ Detail::run_for_at_least<Clock>(
+ std::chrono::duration_cast<IDuration>( warmup_time ),
+ warmup_iterations,
+ Detail::repeat( []() { return Clock::now(); } )
+ );
+
+ std::vector<FDuration> times;
+ const auto num_samples = cfg.benchmarkSamples();
+ times.reserve( num_samples );
+ for ( size_t i = 0; i < num_samples; ++i ) {
Detail::ChronometerModel<Clock> model;
- this->benchmark(Chronometer(model, iterations_per_sample));
+ this->benchmark( Chronometer( model, iterations_per_sample ) );
auto sample_time = model.elapsed() - env.clock_cost.mean;
- if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
- return sample_time / iterations_per_sample;
- });
+ if ( sample_time < FDuration::zero() ) {
+ sample_time = FDuration::zero();
+ }
+ times.push_back(sample_time / iterations_per_sample);
+ }
return times;
}
};
diff --git a/src/catch2/benchmark/catch_optimizer.hpp b/src/catch2/benchmark/catch_optimizer.hpp
index 0dbfc145..61e6571f 100644
--- a/src/catch2/benchmark/catch_optimizer.hpp
+++ b/src/catch2/benchmark/catch_optimizer.hpp
@@ -10,7 +10,7 @@
#ifndef CATCH_OPTIMIZER_HPP_INCLUDED
#define CATCH_OPTIMIZER_HPP_INCLUDED
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
# include <atomic> // atomic_thread_fence
#endif
@@ -32,16 +32,23 @@ namespace Catch {
namespace Detail {
inline void optimizer_barrier() { keep_memory(); }
} // namespace Detail
-#elif defined(_MSC_VER)
+#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
+#if defined(_MSVC_VER)
#pragma optimize("", off)
+#elif defined(__IAR_SYSTEMS_ICC__)
+// For IAR the pragma only affects the following function
+#pragma optimize=disable
+#endif
template <typename T>
inline void keep_memory(T* p) {
// thanks @milleniumbug
*reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
}
// TODO equivalent keep_memory()
+#if defined(_MSVC_VER)
#pragma optimize("", on)
+#endif
namespace Detail {
inline void optimizer_barrier() {
@@ -63,7 +70,7 @@ namespace Catch {
template <typename Fn, typename... Args>
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
- CATCH_FORWARD(fn) (CATCH_FORWARD(args)...);
+ CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...);
}
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/benchmark/catch_sample_analysis.hpp b/src/catch2/benchmark/catch_sample_analysis.hpp
index d849d246..aeb87d05 100644
--- a/src/catch2/benchmark/catch_sample_analysis.hpp
+++ b/src/catch2/benchmark/catch_sample_analysis.hpp
@@ -10,38 +10,20 @@
#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
-#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp>
-#include <catch2/internal/catch_move_and_forward.hpp>
+#include <catch2/benchmark/catch_clock.hpp>
-#include <algorithm>
#include <vector>
-#include <iterator>
namespace Catch {
namespace Benchmark {
- template <typename Duration>
struct SampleAnalysis {
- std::vector<Duration> samples;
- Estimate<Duration> mean;
- Estimate<Duration> standard_deviation;
+ std::vector<FDuration> samples;
+ Estimate<FDuration> mean;
+ Estimate<FDuration> standard_deviation;
OutlierClassification outliers;
double outlier_variance;
-
- template <typename Duration2>
- operator SampleAnalysis<Duration2>() const {
- std::vector<Duration2> samples2;
- samples2.reserve(samples.size());
- std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
- return {
- CATCH_MOVE(samples2),
- mean,
- standard_deviation,
- outliers,
- outlier_variance,
- };
- }
};
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/benchmark/detail/catch_analyse.cpp b/src/catch2/benchmark/detail/catch_analyse.cpp
new file mode 100644
index 00000000..7d27daf1
--- /dev/null
+++ b/src/catch2/benchmark/detail/catch_analyse.cpp
@@ -0,0 +1,85 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+// Adapted from donated nonius code.
+
+#include <catch2/benchmark/detail/catch_analyse.hpp>
+#include <catch2/benchmark/catch_clock.hpp>
+#include <catch2/benchmark/catch_sample_analysis.hpp>
+#include <catch2/benchmark/detail/catch_stats.hpp>
+#include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
+
+#include <vector>
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
+ SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) {
+ if (!cfg.benchmarkNoAnalysis()) {
+ std::vector<double> samples;
+ samples.reserve(static_cast<size_t>(last - first));
+ for (auto current = first; current != last; ++current) {
+ samples.push_back( current->count() );
+ }
+
+ auto analysis = Catch::Benchmark::Detail::analyse_samples(
+ cfg.benchmarkConfidenceInterval(),
+ cfg.benchmarkResamples(),
+ samples.data(),
+ samples.data() + samples.size() );
+ auto outliers = Catch::Benchmark::Detail::classify_outliers(
+ samples.data(), samples.data() + samples.size() );
+
+ auto wrap_estimate = [](Estimate<double> e) {
+ return Estimate<FDuration> {
+ FDuration(e.point),
+ FDuration(e.lower_bound),
+ FDuration(e.upper_bound),
+ e.confidence_interval,
+ };
+ };
+ std::vector<FDuration> samples2;
+ samples2.reserve(samples.size());
+ for (auto s : samples) {
+ samples2.push_back( FDuration( s ) );
+ }
+
+ return {
+ CATCH_MOVE(samples2),
+ wrap_estimate(analysis.mean),
+ wrap_estimate(analysis.standard_deviation),
+ outliers,
+ analysis.outlier_variance,
+ };
+ } else {
+ std::vector<FDuration> samples;
+ samples.reserve(static_cast<size_t>(last - first));
+
+ FDuration mean = FDuration(0);
+ int i = 0;
+ for (auto it = first; it < last; ++it, ++i) {
+ samples.push_back(FDuration(*it));
+ mean += FDuration(*it);
+ }
+ mean /= i;
+
+ return SampleAnalysis{
+ CATCH_MOVE(samples),
+ Estimate<FDuration>{ mean, mean, mean, 0.0 },
+ Estimate<FDuration>{ FDuration( 0 ),
+ FDuration( 0 ),
+ FDuration( 0 ),
+ 0.0 },
+ OutlierClassification{},
+ 0.0
+ };
+ }
+ }
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
diff --git a/src/catch2/benchmark/detail/catch_analyse.hpp b/src/catch2/benchmark/detail/catch_analyse.hpp
index 77b0a9d3..5e3f7b0f 100644
--- a/src/catch2/benchmark/detail/catch_analyse.hpp
+++ b/src/catch2/benchmark/detail/catch_analyse.hpp
@@ -11,68 +11,15 @@
#define CATCH_ANALYSE_HPP_INCLUDED
#include <catch2/benchmark/catch_clock.hpp>
-#include <catch2/benchmark/catch_environment.hpp>
#include <catch2/benchmark/catch_sample_analysis.hpp>
-#include <catch2/benchmark/detail/catch_stats.hpp>
-#include <catch2/interfaces/catch_interfaces_config.hpp>
-#include <catch2/internal/catch_move_and_forward.hpp>
-#include <algorithm>
-#include <iterator>
-#include <vector>
namespace Catch {
+ class IConfig;
+
namespace Benchmark {
namespace Detail {
- template <typename Duration, typename Iterator>
- SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
- if (!cfg.benchmarkNoAnalysis()) {
- std::vector<double> samples;
- samples.reserve(static_cast<size_t>(last - first));
- std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
-
- auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
- auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
-
- auto wrap_estimate = [](Estimate<double> e) {
- return Estimate<Duration> {
- Duration(e.point),
- Duration(e.lower_bound),
- Duration(e.upper_bound),
- e.confidence_interval,
- };
- };
- std::vector<Duration> samples2;
- samples2.reserve(samples.size());
- std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
- return {
- CATCH_MOVE(samples2),
- wrap_estimate(analysis.mean),
- wrap_estimate(analysis.standard_deviation),
- outliers,
- analysis.outlier_variance,
- };
- } else {
- std::vector<Duration> samples;
- samples.reserve(static_cast<size_t>(last - first));
-
- Duration mean = Duration(0);
- int i = 0;
- for (auto it = first; it < last; ++it, ++i) {
- samples.push_back(Duration(*it));
- mean += Duration(*it);
- }
- mean /= i;
-
- return {
- CATCH_MOVE(samples),
- Estimate<Duration>{mean, mean, mean, 0.0},
- Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
- OutlierClassification{},
- 0.0
- };
- }
- }
+ SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last);
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/benchmark/detail/catch_benchmark_function.hpp b/src/catch2/benchmark/detail/catch_benchmark_function.hpp
index 15298258..144e4b6e 100644
--- a/src/catch2/benchmark/detail/catch_benchmark_function.hpp
+++ b/src/catch2/benchmark/detail/catch_benchmark_function.hpp
@@ -11,7 +11,6 @@
#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
#include <catch2/benchmark/catch_chronometer.hpp>
-#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
diff --git a/src/catch2/benchmark/detail/catch_benchmark_stats.hpp b/src/catch2/benchmark/detail/catch_benchmark_stats.hpp
new file mode 100644
index 00000000..3633bc9f
--- /dev/null
+++ b/src/catch2/benchmark/detail/catch_benchmark_stats.hpp
@@ -0,0 +1,48 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED
+#define CATCH_BENCHMARK_STATS_HPP_INCLUDED
+
+#include <catch2/benchmark/catch_estimate.hpp>
+#include <catch2/benchmark/catch_outlier_classification.hpp>
+// The fwd decl & default specialization needs to be seen by VS2017 before
+// BenchmarkStats itself, or VS2017 will report compilation error.
+#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+ struct BenchmarkInfo {
+ std::string name;
+ double estimatedDuration;
+ int iterations;
+ unsigned int samples;
+ unsigned int resamples;
+ double clockResolution;
+ double clockCost;
+ };
+
+ // We need to keep template parameter for backwards compatibility,
+ // but we also do not want to use the template paraneter.
+ template <class Dummy>
+ struct BenchmarkStats {
+ BenchmarkInfo info;
+
+ std::vector<Benchmark::FDuration> samples;
+ Benchmark::Estimate<Benchmark::FDuration> mean;
+ Benchmark::Estimate<Benchmark::FDuration> standardDeviation;
+ Benchmark::OutlierClassification outliers;
+ double outlierVariance;
+ };
+
+
+} // end namespace Catch
+
+#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED
diff --git a/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp b/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp
new file mode 100644
index 00000000..2ccc25d5
--- /dev/null
+++ b/src/catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp
@@ -0,0 +1,23 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
+#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
+
+#include <catch2/benchmark/catch_clock.hpp>
+
+namespace Catch {
+
+ // We cannot forward declare the type with default template argument
+ // multiple times, so it is split out into a separate header so that
+ // we can prevent multiple declarations in dependees
+ template <typename Duration = Benchmark::FDuration>
+ struct BenchmarkStats;
+
+} // end namespace Catch
+
+#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
diff --git a/src/catch2/benchmark/detail/catch_complete_invoke.hpp b/src/catch2/benchmark/detail/catch_complete_invoke.hpp
index 49db413e..4dff4b7e 100644
--- a/src/catch2/benchmark/detail/catch_complete_invoke.hpp
+++ b/src/catch2/benchmark/detail/catch_complete_invoke.hpp
@@ -10,14 +10,9 @@
#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED
#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED
-#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_meta.hpp>
-#include <catch2/interfaces/catch_interfaces_capture.hpp>
-#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
-#include <type_traits>
-
namespace Catch {
namespace Benchmark {
namespace Detail {
diff --git a/src/catch2/benchmark/detail/catch_estimate_clock.hpp b/src/catch2/benchmark/detail/catch_estimate_clock.hpp
index 907773f2..918484a9 100644
--- a/src/catch2/benchmark/detail/catch_estimate_clock.hpp
+++ b/src/catch2/benchmark/detail/catch_estimate_clock.hpp
@@ -19,7 +19,6 @@
#include <catch2/internal/catch_unique_ptr.hpp>
#include <algorithm>
-#include <iterator>
#include <vector>
#include <cmath>
@@ -30,46 +29,49 @@ namespace Catch {
std::vector<double> resolution(int k) {
std::vector<TimePoint<Clock>> times;
times.reserve(static_cast<size_t>(k + 1));
- std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
+ for ( int i = 0; i < k + 1; ++i ) {
+ times.push_back( Clock::now() );
+ }
std::vector<double> deltas;
deltas.reserve(static_cast<size_t>(k));
- std::transform(std::next(times.begin()), times.end(), times.begin(),
- std::back_inserter(deltas),
- [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
+ for ( size_t idx = 1; idx < times.size(); ++idx ) {
+ deltas.push_back( static_cast<double>(
+ ( times[idx] - times[idx - 1] ).count() ) );
+ }
return deltas;
}
- const auto warmup_iterations = 10000;
- const auto warmup_time = std::chrono::milliseconds(100);
- const auto minimum_ticks = 1000;
- const auto warmup_seed = 10000;
- const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
- const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
- const auto clock_cost_estimation_tick_limit = 100000;
- const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
- const auto clock_cost_estimation_iterations = 10000;
+ constexpr auto warmup_iterations = 10000;
+ constexpr auto warmup_time = std::chrono::milliseconds(100);
+ constexpr auto minimum_ticks = 1000;
+ constexpr auto warmup_seed = 10000;
+ constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
+ constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
+ constexpr auto clock_cost_estimation_tick_limit = 100000;
+ constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10);
+ constexpr auto clock_cost_estimation_iterations = 10000;
template <typename Clock>
int warmup() {
- return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
+ return run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(warmup_time), warmup_seed, &resolution<Clock>)
.iterations;
}
template <typename Clock>
- EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
- auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
+ EnvironmentEstimate estimate_clock_resolution(int iterations) {
+ auto r = run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
.result;
return {
- FloatDuration<Clock>(mean(r.begin(), r.end())),
- classify_outliers(r.begin(), r.end()),
+ FDuration(mean(r.data(), r.data() + r.size())),
+ classify_outliers(r.data(), r.data() + r.size()),
};
}
template <typename Clock>
- EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
+ EnvironmentEstimate estimate_clock_cost(FDuration resolution) {
auto time_limit = (std::min)(
resolution * clock_cost_estimation_tick_limit,
- FloatDuration<Clock>(clock_cost_estimation_time_limit));
+ FDuration(clock_cost_estimation_time_limit));
auto time_clock = [](int k) {
return Detail::measure<Clock>([k] {
for (int i = 0; i < k; ++i) {
@@ -80,26 +82,28 @@ namespace Catch {
};
time_clock(1);
int iters = clock_cost_estimation_iterations;
- auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
+ auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(clock_cost_estimation_time), iters, time_clock);
std::vector<double> times;
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
times.reserve(static_cast<size_t>(nsamples));
- std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
- return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
- });
+ for ( int s = 0; s < nsamples; ++s ) {
+ times.push_back( static_cast<double>(
+ ( time_clock( r.iterations ) / r.iterations )
+ .count() ) );
+ }
return {
- FloatDuration<Clock>(mean(times.begin(), times.end())),
- classify_outliers(times.begin(), times.end()),
+ FDuration(mean(times.data(), times.data() + times.size())),
+ classify_outliers(times.data(), times.data() + times.size()),
};
}
template <typename Clock>
- Environment<FloatDuration<Clock>> measure_environment() {
+ Environment measure_environment() {
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif
- static Catch::Detail::unique_ptr<Environment<FloatDuration<Clock>>> env;
+ static Catch::Detail::unique_ptr<Environment> env;
#if defined(__clang__)
# pragma clang diagnostic pop
#endif
@@ -111,7 +115,7 @@ namespace Catch {
auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
- env = Catch::Detail::make_unique<Environment<FloatDuration<Clock>>>( Environment<FloatDuration<Clock>>{resolution, cost} );
+ env = Catch::Detail::make_unique<Environment>( Environment{resolution, cost} );
return *env;
}
} // namespace Detail
diff --git a/src/catch2/benchmark/detail/catch_measure.hpp b/src/catch2/benchmark/detail/catch_measure.hpp
index 388814c1..37494a68 100644
--- a/src/catch2/benchmark/detail/catch_measure.hpp
+++ b/src/catch2/benchmark/detail/catch_measure.hpp
@@ -10,7 +10,6 @@
#ifndef CATCH_MEASURE_HPP_INCLUDED
#define CATCH_MEASURE_HPP_INCLUDED
-#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/benchmark/detail/catch_timing.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
@@ -19,7 +18,7 @@ namespace Catch {
namespace Benchmark {
namespace Detail {
template <typename Clock, typename Fun, typename... Args>
- TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
+ TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) {
auto start = Clock::now();
auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
auto end = Clock::now();
diff --git a/src/catch2/benchmark/detail/catch_run_for_at_least.cpp b/src/catch2/benchmark/detail/catch_run_for_at_least.cpp
index 35778b27..3ebdcc05 100644
--- a/src/catch2/benchmark/detail/catch_run_for_at_least.cpp
+++ b/src/catch2/benchmark/detail/catch_run_for_at_least.cpp
@@ -7,9 +7,10 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
-#include <exception>
#include <catch2/internal/catch_enforce.hpp>
+#include <exception>
+
namespace Catch {
namespace Benchmark {
namespace Detail {
diff --git a/src/catch2/benchmark/detail/catch_run_for_at_least.hpp b/src/catch2/benchmark/detail/catch_run_for_at_least.hpp
index 976a4b24..4dfa8bbb 100644
--- a/src/catch2/benchmark/detail/catch_run_for_at_least.hpp
+++ b/src/catch2/benchmark/detail/catch_run_for_at_least.hpp
@@ -24,11 +24,11 @@ namespace Catch {
namespace Benchmark {
namespace Detail {
template <typename Clock, typename Fun>
- TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
+ TimingOf<Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
return Detail::measure<Clock>(fun, iters);
}
template <typename Clock, typename Fun>
- TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
+ TimingOf<Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
Detail::ChronometerModel<Clock> meter;
auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
@@ -43,8 +43,8 @@ namespace Catch {
void throw_optimized_away_error();
template <typename Clock, typename Fun>
- TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>>
- run_for_at_least(ClockDuration<Clock> how_long,
+ TimingOf<Fun, run_for_at_least_argument_t<Clock, Fun>>
+ run_for_at_least(IDuration how_long,
const int initial_iterations,
Fun&& fun) {
auto iters = initial_iterations;
diff --git a/src/catch2/benchmark/detail/catch_stats.cpp b/src/catch2/benchmark/detail/catch_stats.cpp
index 514ed1f7..2dd2d864 100644
--- a/src/catch2/benchmark/detail/catch_stats.cpp
+++ b/src/catch2/benchmark/detail/catch_stats.cpp
@@ -10,10 +10,14 @@
#include <catch2/benchmark/detail/catch_stats.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
+#include <catch2/internal/catch_floating_point_helpers.hpp>
+#include <catch2/internal/catch_random_number_generator.hpp>
+#include <algorithm>
#include <cassert>
+#include <cmath>
#include <cstddef>
-#include <iterator>
+#include <numeric>
#include <random>
@@ -21,139 +25,204 @@
#include <future>
#endif
-namespace {
-
-using Catch::Benchmark::Detail::sample;
-
- template <typename URng, typename Estimator>
- sample resample(URng& rng, unsigned int resamples, std::vector<double>::iterator first, std::vector<double>::iterator last, Estimator& estimator) {
- auto n = static_cast<size_t>(last - first);
- std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
-
- sample out;
- out.reserve(resamples);
- std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
- std::vector<double> resampled;
- resampled.reserve(n);
- std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast<std::ptrdiff_t>(dist(rng))]; });
- return estimator(resampled.begin(), resampled.end());
- });
- std::sort(out.begin(), out.end());
- return out;
- }
-
-
- double erf_inv(double x) {
- // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
- double w, p;
-
- w = -log((1.0 - x) * (1.0 + x));
-
- if (w < 6.250000) {
- w = w - 3.125000;
- p = -3.6444120640178196996e-21;
- p = -1.685059138182016589e-19 + p * w;
- p = 1.2858480715256400167e-18 + p * w;
- p = 1.115787767802518096e-17 + p * w;
- p = -1.333171662854620906e-16 + p * w;
- p = 2.0972767875968561637e-17 + p * w;
- p = 6.6376381343583238325e-15 + p * w;
- p = -4.0545662729752068639e-14 + p * w;
- p = -8.1519341976054721522e-14 + p * w;
- p = 2.6335093153082322977e-12 + p * w;
- p = -1.2975133253453532498e-11 + p * w;
- p = -5.4154120542946279317e-11 + p * w;
- p = 1.051212273321532285e-09 + p * w;
- p = -4.1126339803469836976e-09 + p * w;
- p = -2.9070369957882005086e-08 + p * w;
- p = 4.2347877827932403518e-07 + p * w;
- p = -1.3654692000834678645e-06 + p * w;
- p = -1.3882523362786468719e-05 + p * w;
- p = 0.0001867342080340571352 + p * w;
- p = -0.00074070253416626697512 + p * w;
- p = -0.0060336708714301490533 + p * w;
- p = 0.24015818242558961693 + p * w;
- p = 1.6536545626831027356 + p * w;
- } else if (w < 16.000000) {
- w = sqrt(w) - 3.250000;
- p = 2.2137376921775787049e-09;
- p = 9.0756561938885390979e-08 + p * w;
- p = -2.7517406297064545428e-07 + p * w;
- p = 1.8239629214389227755e-08 + p * w;
- p = 1.5027403968909827627e-06 + p * w;
- p = -4.013867526981545969e-06 + p * w;
- p = 2.9234449089955446044e-06 + p * w;
- p = 1.2475304481671778723e-05 + p * w;
- p = -4.7318229009055733981e-05 + p * w;
- p = 6.8284851459573175448e-05 + p * w;
- p = 2.4031110387097893999e-05 + p * w;
- p = -0.0003550375203628474796 + p * w;
- p = 0.00095328937973738049703 + p * w;
- p = -0.0016882755560235047313 + p * w;
- p = 0.0024914420961078508066 + p * w;
- p = -0.0037512085075692412107 + p * w;
- p = 0.005370914553590063617 + p * w;
- p = 1.0052589676941592334 + p * w;
- p = 3.0838856104922207635 + p * w;
- } else {
- w = sqrt(w) - 5.000000;
- p = -2.7109920616438573243e-11;
- p = -2.5556418169965252055e-10 + p * w;
- p = 1.5076572693500548083e-09 + p * w;
- p = -3.7894654401267369937e-09 + p * w;
- p = 7.6157012080783393804e-09 + p * w;
- p = -1.4960026627149240478e-08 + p * w;
- p = 2.9147953450901080826e-08 + p * w;
- p = -6.7711997758452339498e-08 + p * w;
- p = 2.2900482228026654717e-07 + p * w;
- p = -9.9298272942317002539e-07 + p * w;
- p = 4.5260625972231537039e-06 + p * w;
- p = -1.9681778105531670567e-05 + p * w;
- p = 7.5995277030017761139e-05 + p * w;
- p = -0.00021503011930044477347 + p * w;
- p = -0.00013871931833623122026 + p * w;
- p = 1.0103004648645343977 + p * w;
- p = 4.8499064014085844221 + p * w;
- }
- return p * x;
- }
-
- double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
- auto m = Catch::Benchmark::Detail::mean(first, last);
- double variance = std::accumulate( first,
- last,
- 0.,
- [m]( double a, double b ) {
- double diff = b - m;
- return a + diff * diff;
- } ) /
- ( last - first );
- return std::sqrt( variance );
- }
-
-}
-
namespace Catch {
namespace Benchmark {
namespace Detail {
+ namespace {
+
+ template <typename URng, typename Estimator>
+ static sample
+ resample( URng& rng,
+ unsigned int resamples,
+ double const* first,
+ double const* last,
+ Estimator& estimator ) {
+ auto n = static_cast<size_t>( last - first );
+ std::uniform_int_distribution<decltype( n )> dist( 0,
+ n - 1 );
+
+ sample out;
+ out.reserve( resamples );
+ // We allocate the vector outside the loop to avoid realloc
+ // per resample
+ std::vector<double> resampled;
+ resampled.reserve( n );
+ for ( size_t i = 0; i < resamples; ++i ) {
+ resampled.clear();
+ for ( size_t s = 0; s < n; ++s ) {
+ resampled.push_back(
+ first[static_cast<std::ptrdiff_t>(
+ dist( rng ) )] );
+ }
+ const auto estimate =
+ estimator( resampled.data(), resampled.data() + resampled.size() );
+ out.push_back( estimate );
+ }
+ std::sort( out.begin(), out.end() );
+ return out;
+ }
-#if defined( __GNUC__ ) || defined( __clang__ )
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wfloat-equal"
-#endif
- bool directCompare( double lhs, double rhs ) { return lhs == rhs; }
-#if defined( __GNUC__ ) || defined( __clang__ )
-# pragma GCC diagnostic pop
-#endif
+ static double outlier_variance( Estimate<double> mean,
+ Estimate<double> stddev,
+ int n ) {
+ double sb = stddev.point;
+ double mn = mean.point / n;
+ double mg_min = mn / 2.;
+ double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) );
+ double sg2 = sg * sg;
+ double sb2 = sb * sb;
+
+ auto c_max = [n, mn, sb2, sg2]( double x ) -> double {
+ double k = mn - x;
+ double d = k * k;
+ double nd = n * d;
+ double k0 = -n * nd;
+ double k1 = sb2 - n * sg2 + nd;
+ double det = k1 * k1 - 4 * sg2 * k0;
+ return static_cast<int>( -2. * k0 /
+ ( k1 + std::sqrt( det ) ) );
+ };
+
+ auto var_out = [n, sb2, sg2]( double c ) {
+ double nc = n - c;
+ return ( nc / n ) * ( sb2 - nc * sg2 );
+ };
+
+ return (std::min)( var_out( 1 ),
+ var_out(
+ (std::min)( c_max( 0. ),
+ c_max( mg_min ) ) ) ) /
+ sb2;
+ }
+
+ static double erf_inv( double x ) {
+ // Code accompanying the article "Approximating the erfinv
+ // function" in GPU Computing Gems, Volume 2
+ double w, p;
+
+ w = -log( ( 1.0 - x ) * ( 1.0 + x ) );
+
+ if ( w < 6.250000 ) {
+ w = w - 3.125000;
+ p = -3.6444120640178196996e-21;
+ p = -1.685059138182016589e-19 + p * w;
+ p = 1.2858480715256400167e-18 + p * w;
+ p = 1.115787767802518096e-17 + p * w;
+ p = -1.333171662854620906e-16 + p * w;
+ p = 2.0972767875968561637e-17 + p * w;
+ p = 6.6376381343583238325e-15 + p * w;
+ p = -4.0545662729752068639e-14 + p * w;
+ p = -8.1519341976054721522e-14 + p * w;
+ p = 2.6335093153082322977e-12 + p * w;
+ p = -1.2975133253453532498e-11 + p * w;
+ p = -5.4154120542946279317e-11 + p * w;
+ p = 1.051212273321532285e-09 + p * w;
+ p = -4.1126339803469836976e-09 + p * w;
+ p = -2.9070369957882005086e-08 + p * w;
+ p = 4.2347877827932403518e-07 + p * w;
+ p = -1.3654692000834678645e-06 + p * w;
+ p = -1.3882523362786468719e-05 + p * w;
+ p = 0.0001867342080340571352 + p * w;
+ p = -0.00074070253416626697512 + p * w;
+ p = -0.0060336708714301490533 + p * w;
+ p = 0.24015818242558961693 + p * w;
+ p = 1.6536545626831027356 + p * w;
+ } else if ( w < 16.000000 ) {
+ w = sqrt( w ) - 3.250000;
+ p = 2.2137376921775787049e-09;
+ p = 9.0756561938885390979e-08 + p * w;
+ p = -2.7517406297064545428e-07 + p * w;
+ p = 1.8239629214389227755e-08 + p * w;
+ p = 1.5027403968909827627e-06 + p * w;
+ p = -4.013867526981545969e-06 + p * w;
+ p = 2.9234449089955446044e-06 + p * w;
+ p = 1.2475304481671778723e-05 + p * w;
+ p = -4.7318229009055733981e-05 + p * w;
+ p = 6.8284851459573175448e-05 + p * w;
+ p = 2.4031110387097893999e-05 + p * w;
+ p = -0.0003550375203628474796 + p * w;
+ p = 0.00095328937973738049703 + p * w;
+ p = -0.0016882755560235047313 + p * w;
+ p = 0.0024914420961078508066 + p * w;
+ p = -0.0037512085075692412107 + p * w;
+ p = 0.005370914553590063617 + p * w;
+ p = 1.0052589676941592334 + p * w;
+ p = 3.0838856104922207635 + p * w;
+ } else {
+ w = sqrt( w ) - 5.000000;
+ p = -2.7109920616438573243e-11;
+ p = -2.5556418169965252055e-10 + p * w;
+ p = 1.5076572693500548083e-09 + p * w;
+ p = -3.7894654401267369937e-09 + p * w;
+ p = 7.6157012080783393804e-09 + p * w;
+ p = -1.4960026627149240478e-08 + p * w;
+ p = 2.9147953450901080826e-08 + p * w;
+ p = -6.7711997758452339498e-08 + p * w;
+ p = 2.2900482228026654717e-07 + p * w;
+ p = -9.9298272942317002539e-07 + p * w;
+ p = 4.5260625972231537039e-06 + p * w;
+ p = -1.9681778105531670567e-05 + p * w;
+ p = 7.5995277030017761139e-05 + p * w;
+ p = -0.00021503011930044477347 + p * w;
+ p = -0.00013871931833623122026 + p * w;
+ p = 1.0103004648645343977 + p * w;
+ p = 4.8499064014085844221 + p * w;
+ }
+ return p * x;
+ }
+
+ static double
+ standard_deviation( double const* first, double const* last ) {
+ auto m = Catch::Benchmark::Detail::mean( first, last );
+ double variance =
+ std::accumulate( first,
+ last,
+ 0.,
+ [m]( double a, double b ) {
+ double diff = b - m;
+ return a + diff * diff;
+ } ) /
+ ( last - first );
+ return std::sqrt( variance );
+ }
+
+ static sample jackknife( double ( *estimator )( double const*,
+ double const* ),
+ double* first,
+ double* last ) {
+ const auto second = first + 1;
+ sample results;
+ results.reserve( static_cast<size_t>( last - first ) );
+
+ for ( auto it = first; it != last; ++it ) {
+ std::iter_swap( it, first );
+ results.push_back( estimator( second, last ) );
+ }
+
+ return results;
+ }
+
+
+ } // namespace
+ } // namespace Detail
+ } // namespace Benchmark
+} // namespace Catch
+
+namespace Catch {
+ namespace Benchmark {
+ namespace Detail {
- double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
+ double weighted_average_quantile( int k,
+ int q,
+ double* first,
+ double* last ) {
auto count = last - first;
double idx = (count - 1) * k / static_cast<double>(q);
int j = static_cast<int>(idx);
double g = idx - j;
std::nth_element(first, first + j, last);
auto xj = first[j];
- if ( directCompare( g, 0 ) ) {
+ if ( Catch::Detail::directCompare( g, 0 ) ) {
return xj;
}
@@ -161,6 +230,48 @@ namespace Catch {
return xj + g * (xj1 - xj);
}
+ OutlierClassification
+ classify_outliers( double const* first, double const* last ) {
+ std::vector<double> copy( first, last );
+
+ auto q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() );
+ auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() );
+ auto iqr = q3 - q1;
+ auto los = q1 - ( iqr * 3. );
+ auto lom = q1 - ( iqr * 1.5 );
+ auto him = q3 + ( iqr * 1.5 );
+ auto his = q3 + ( iqr * 3. );
+
+ OutlierClassification o;
+ for ( ; first != last; ++first ) {
+ const double t = *first;
+ if ( t < los ) {
+ ++o.low_severe;
+ } else if ( t < lom ) {
+ ++o.low_mild;
+ } else if ( t > his ) {
+ ++o.high_severe;
+ } else if ( t > him ) {
+ ++o.high_mild;
+ }
+ ++o.samples_seen;
+ }
+ return o;
+ }
+
+ double mean( double const* first, double const* last ) {
+ auto count = last - first;
+ double sum = 0.;
+ while (first != last) {
+ sum += *first;
+ ++first;
+ }
+ return sum / static_cast<double>(count);
+ }
+
+ double normal_cdf( double x ) {
+ return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0;
+ }
double erfc_inv(double x) {
return erf_inv(1.0 - x);
@@ -182,50 +293,77 @@ namespace Catch {
return result;
}
+ Estimate<double>
+ bootstrap( double confidence_level,
+ double* first,
+ double* last,
+ sample const& resample,
+ double ( *estimator )( double const*, double const* ) ) {
+ auto n_samples = last - first;
+
+ double point = estimator( first, last );
+ // Degenerate case with a single sample
+ if ( n_samples == 1 )
+ return { point, point, point, confidence_level };
+
+ sample jack = jackknife( estimator, first, last );
+ double jack_mean =
+ mean( jack.data(), jack.data() + jack.size() );
+ double sum_squares = 0, sum_cubes = 0;
+ for ( double x : jack ) {
+ auto difference = jack_mean - x;
+ auto square = difference * difference;
+ auto cube = square * difference;
+ sum_squares += square;
+ sum_cubes += cube;
+ }
- double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
- double sb = stddev.point;
- double mn = mean.point / n;
- double mg_min = mn / 2.;
- double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
- double sg2 = sg * sg;
- double sb2 = sb * sb;
-
- auto c_max = [n, mn, sb2, sg2](double x) -> double {
- double k = mn - x;
- double d = k * k;
- double nd = n * d;
- double k0 = -n * nd;
- double k1 = sb2 - n * sg2 + nd;
- double det = k1 * k1 - 4 * sg2 * k0;
- return static_cast<int>(-2. * k0 / (k1 + std::sqrt(det)));
- };
+ double accel = sum_cubes / ( 6 * std::pow( sum_squares, 1.5 ) );
+ long n = static_cast<long>( resample.size() );
+ double prob_n =
+ std::count_if( resample.begin(),
+ resample.end(),
+ [point]( double x ) { return x < point; } ) /
+ static_cast<double>( n );
+ // degenerate case with uniform samples
+ if ( Catch::Detail::directCompare( prob_n, 0. ) ) {
+ return { point, point, point, confidence_level };
+ }
- auto var_out = [n, sb2, sg2](double c) {
- double nc = n - c;
- return (nc / n) * (sb2 - nc * sg2);
- };
+ double bias = normal_quantile( prob_n );
+ double z1 = normal_quantile( ( 1. - confidence_level ) / 2. );
- return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
+ auto cumn = [n]( double x ) -> long {
+ return std::lround( normal_cdf( x ) *
+ static_cast<double>( n ) );
+ };
+ auto a = [bias, accel]( double b ) {
+ return bias + b / ( 1. - accel * b );
+ };
+ double b1 = bias + z1;
+ double b2 = bias - z1;
+ double a1 = a( b1 );
+ double a2 = a( b2 );
+ auto lo = static_cast<size_t>( (std::max)( cumn( a1 ), 0l ) );
+ auto hi =
+ static_cast<size_t>( (std::min)( cumn( a2 ), n - 1 ) );
+
+ return { point, resample[lo], resample[hi], confidence_level };
}
-
- bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
- static std::random_device entropy;
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
- auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
-
- auto mean = &Detail::mean<std::vector<double>::iterator>;
+ bootstrap_analysis analyse_samples(double confidence_level,
+ unsigned int n_resamples,
+ double* first,
+ double* last) {
+ auto mean = &Detail::mean;
auto stddev = &standard_deviation;
#if defined(CATCH_CONFIG_USE_ASYNC)
- auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
- auto seed = entropy();
+ auto Estimate = [=](double(*f)(double const*, double const*)) {
+ std::random_device rd;
+ auto seed = rd();
return std::async(std::launch::async, [=] {
- std::mt19937 rng(seed);
+ SimplePcg32 rng( seed );
auto resampled = resample(rng, n_resamples, first, last, f);
return bootstrap(confidence_level, first, last, resampled, f);
});
@@ -237,9 +375,10 @@ namespace Catch {
auto mean_estimate = mean_future.get();
auto stddev_estimate = stddev_future.get();
#else
- auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
- auto seed = entropy();
- std::mt19937 rng(seed);
+ auto Estimate = [=](double(*f)(double const* , double const*)) {
+ std::random_device rd;
+ auto seed = rd();
+ SimplePcg32 rng( seed );
auto resampled = resample(rng, n_resamples, first, last, f);
return bootstrap(confidence_level, first, last, resampled, f);
};
@@ -248,6 +387,7 @@ namespace Catch {
auto stddev_estimate = Estimate(stddev);
#endif // CATCH_USE_ASYNC
+ auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
return { mean_estimate, stddev_estimate, outlier_variance };
diff --git a/src/catch2/benchmark/detail/catch_stats.hpp b/src/catch2/benchmark/detail/catch_stats.hpp
index 4c54ec52..3bea612f 100644
--- a/src/catch2/benchmark/detail/catch_stats.hpp
+++ b/src/catch2/benchmark/detail/catch_stats.hpp
@@ -13,122 +13,35 @@
#include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp>
-#include <algorithm>
#include <vector>
-#include <numeric>
-#include <tuple>
-#include <cmath>
namespace Catch {
namespace Benchmark {
namespace Detail {
using sample = std::vector<double>;
- // Used when we know we want == comparison of two doubles
- // to centralize warning suppression
- bool directCompare( double lhs, double rhs );
-
- double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
-
- template <typename Iterator>
- OutlierClassification classify_outliers(Iterator first, Iterator last) {
- std::vector<double> copy(first, last);
-
- auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
- auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
- auto iqr = q3 - q1;
- auto los = q1 - (iqr * 3.);
- auto lom = q1 - (iqr * 1.5);
- auto him = q3 + (iqr * 1.5);
- auto his = q3 + (iqr * 3.);
-
- OutlierClassification o;
- for (; first != last; ++first) {
- auto&& t = *first;
- if (t < los) ++o.low_severe;
- else if (t < lom) ++o.low_mild;
- else if (t > his) ++o.high_severe;
- else if (t > him) ++o.high_mild;
- ++o.samples_seen;
- }
- return o;
- }
-
- template <typename Iterator>
- double mean(Iterator first, Iterator last) {
- auto count = last - first;
- double sum = std::accumulate(first, last, 0.);
- return sum / static_cast<double>(count);
- }
-
- template <typename Estimator, typename Iterator>
- sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
- auto n = static_cast<size_t>(last - first);
- auto second = first;
- ++second;
- sample results;
- results.reserve(n);
-
- for (auto it = first; it != last; ++it) {
- std::iter_swap(it, first);
- results.push_back(estimator(second, last));
- }
-
- return results;
- }
-
- inline double normal_cdf(double x) {
- return std::erfc(-x / std::sqrt(2.0)) / 2.0;
- }
+ double weighted_average_quantile( int k,
+ int q,
+ double* first,
+ double* last );
+
+ OutlierClassification
+ classify_outliers( double const* first, double const* last );
+
+ double mean( double const* first, double const* last );
+
+ double normal_cdf( double x );
double erfc_inv(double x);
double normal_quantile(double p);
- template <typename Iterator, typename Estimator>
- Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
- auto n_samples = last - first;
-
- double point = estimator(first, last);
- // Degenerate case with a single sample
- if (n_samples == 1) return { point, point, point, confidence_level };
-
- sample jack = jackknife(estimator, first, last);
- double jack_mean = mean(jack.begin(), jack.end());
- double sum_squares, sum_cubes;
- std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
- auto d = jack_mean - x;
- auto d2 = d * d;
- auto d3 = d2 * d;
- return { sqcb.first + d2, sqcb.second + d3 };
- });
-
- double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
- long n = static_cast<long>(resample.size());
- double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast<double>(n);
- // degenerate case with uniform samples
- if ( directCompare( prob_n, 0. ) ) {
- return { point, point, point, confidence_level };
- }
-
- double bias = normal_quantile(prob_n);
- double z1 = normal_quantile((1. - confidence_level) / 2.);
-
- auto cumn = [n]( double x ) -> long {
- return std::lround( normal_cdf( x ) * static_cast<double>(n) );
- };
- auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
- double b1 = bias + z1;
- double b2 = bias - z1;
- double a1 = a(b1);
- double a2 = a(b2);
- auto lo = static_cast<size_t>((std::max)(cumn(a1), 0l));
- auto hi = static_cast<size_t>((std::min)(cumn(a2), n - 1));
-
- return { point, resample[lo], resample[hi], confidence_level };
- }
-
- double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
+ Estimate<double>
+ bootstrap( double confidence_level,
+ double* first,
+ double* last,
+ sample const& resample,
+ double ( *estimator )( double const*, double const* ) );
struct bootstrap_analysis {
Estimate<double> mean;
@@ -136,7 +49,10 @@ namespace Catch {
double outlier_variance;
};
- bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
+ bootstrap_analysis analyse_samples(double confidence_level,
+ unsigned int n_resamples,
+ double* first,
+ double* last);
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/benchmark/detail/catch_timing.hpp b/src/catch2/benchmark/detail/catch_timing.hpp
index f5c25571..da567190 100644
--- a/src/catch2/benchmark/detail/catch_timing.hpp
+++ b/src/catch2/benchmark/detail/catch_timing.hpp
@@ -17,14 +17,14 @@
namespace Catch {
namespace Benchmark {
- template <typename Duration, typename Result>
+ template <typename Result>
struct Timing {
- Duration elapsed;
+ IDuration elapsed;
Result result;
int iterations;
};
- template <typename Clock, typename Func, typename... Args>
- using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
+ template <typename Func, typename... Args>
+ using TimingOf = Timing<Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
} // namespace Benchmark
} // namespace Catch
diff --git a/src/catch2/catch_all.hpp b/src/catch2/catch_all.hpp
index a0bf7605..f2cc8536 100644
--- a/src/catch2/catch_all.hpp
+++ b/src/catch2/catch_all.hpp
@@ -54,6 +54,8 @@
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_config_android_logwrite.hpp>
#include <catch2/internal/catch_config_counter.hpp>
+#include <catch2/internal/catch_config_prefix_messages.hpp>
+#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_config_uncaught_exceptions.hpp>
#include <catch2/internal/catch_config_wchar.hpp>
#include <catch2/internal/catch_console_colour.hpp>
@@ -70,7 +72,9 @@
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp>
#include <catch2/internal/catch_getenv.hpp>
+#include <catch2/internal/catch_is_permutation.hpp>
#include <catch2/internal/catch_istream.hpp>
+#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_list.hpp>
@@ -85,7 +89,10 @@
#include <catch2/internal/catch_platform.hpp>
#include <catch2/internal/catch_polyfills.hpp>
#include <catch2/internal/catch_preprocessor.hpp>
+#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
#include <catch2/internal/catch_preprocessor_remove_parens.hpp>
+#include <catch2/internal/catch_random_floating_point_helpers.hpp>
+#include <catch2/internal/catch_random_integer_helpers.hpp>
#include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/internal/catch_random_seed_generation.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
@@ -110,10 +117,13 @@
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_test_macro_impl.hpp>
#include <catch2/internal/catch_test_registry.hpp>
+#include <catch2/internal/catch_test_run_info.hpp>
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_to_string.hpp>
#include <catch2/internal/catch_uncaught_exceptions.hpp>
+#include <catch2/internal/catch_uniform_floating_point_distribution.hpp>
+#include <catch2/internal/catch_uniform_integer_distribution.hpp>
#include <catch2/internal/catch_unique_name.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_void_type.hpp>
diff --git a/src/catch2/catch_approx.cpp b/src/catch2/catch_approx.cpp
index 407586d1..9ad4ce3e 100644
--- a/src/catch2/catch_approx.cpp
+++ b/src/catch2/catch_approx.cpp
@@ -70,10 +70,10 @@ namespace Catch {
}
namespace literals {
- Approx operator "" _a(long double val) {
+ Approx operator ""_a(long double val) {
return Approx(val);
}
- Approx operator "" _a(unsigned long long val) {
+ Approx operator ""_a(unsigned long long val) {
return Approx(val);
}
} // end namespace literals
diff --git a/src/catch2/catch_assertion_result.cpp b/src/catch2/catch_assertion_result.cpp
index 5b5d3290..61d4fd06 100644
--- a/src/catch2/catch_assertion_result.cpp
+++ b/src/catch2/catch_assertion_result.cpp
@@ -7,6 +7,7 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_assertion_result.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
namespace Catch {
@@ -26,9 +27,9 @@ namespace Catch {
return reconstructedExpression;
}
- AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+ AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data )
: m_info( info ),
- m_resultData( data )
+ m_resultData( CATCH_MOVE(data) )
{}
// Result was a success
@@ -67,16 +68,15 @@ namespace Catch {
}
std::string AssertionResult::getExpressionInMacro() const {
- std::string expr;
- if( m_info.macroName.empty() )
- expr = static_cast<std::string>(m_info.capturedExpression);
- else {
- expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
- expr += m_info.macroName;
- expr += "( ";
- expr += m_info.capturedExpression;
- expr += " )";
+ if ( m_info.macroName.empty() ) {
+ return static_cast<std::string>( m_info.capturedExpression );
}
+ std::string expr;
+ expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
+ expr += m_info.macroName;
+ expr += "( ";
+ expr += m_info.capturedExpression;
+ expr += " )";
return expr;
}
diff --git a/src/catch2/catch_assertion_result.hpp b/src/catch2/catch_assertion_result.hpp
index 6829e2f7..48882876 100644
--- a/src/catch2/catch_assertion_result.hpp
+++ b/src/catch2/catch_assertion_result.hpp
@@ -35,7 +35,7 @@ namespace Catch {
class AssertionResult {
public:
AssertionResult() = delete;
- AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+ AssertionResult( AssertionInfo const& info, AssertionResultData&& data );
bool isOk() const;
bool succeeded() const;
diff --git a/src/catch2/catch_config.cpp b/src/catch2/catch_config.cpp
index eb4f5ad3..34f50f17 100644
--- a/src/catch2/catch_config.cpp
+++ b/src/catch2/catch_config.cpp
@@ -105,7 +105,7 @@ namespace Catch {
elem = trim(elem);
}
- // Insert the default reporter if user hasn't asked for a specfic one
+ // Insert the default reporter if user hasn't asked for a specific one
if ( m_data.reporterSpecifications.empty() ) {
m_data.reporterSpecifications.push_back( {
#if defined( CATCH_CONFIG_DEFAULT_REPORTER )
diff --git a/src/catch2/catch_message.cpp b/src/catch2/catch_message.cpp
index 4ab6ecf5..384f180e 100644
--- a/src/catch2/catch_message.cpp
+++ b/src/catch2/catch_message.cpp
@@ -19,8 +19,8 @@ namespace Catch {
////////////////////////////////////////////////////////////////////////////
- ScopedMessage::ScopedMessage( MessageBuilder const& builder ):
- m_info( builder.m_info ) {
+ ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
+ m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
}
@@ -37,7 +37,11 @@ namespace Catch {
}
- Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
+ Capturer::Capturer( StringRef macroName,
+ SourceLineInfo const& lineInfo,
+ ResultWas::OfType resultType,
+ StringRef names ):
+ m_resultCapture( getResultCapture() ) {
auto trimmed = [&] (size_t start, size_t end) {
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;
diff --git a/src/catch2/catch_message.hpp b/src/catch2/catch_message.hpp
index 4f71f227..05325ee8 100644
--- a/src/catch2/catch_message.hpp
+++ b/src/catch2/catch_message.hpp
@@ -8,12 +8,13 @@
#ifndef CATCH_MESSAGE_HPP_INCLUDED
#define CATCH_MESSAGE_HPP_INCLUDED
+#include <catch2/internal/catch_config_prefix_messages.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stream_end_stop.hpp>
#include <catch2/internal/catch_message_info.hpp>
-#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/catch_tostring.hpp>
+#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <string>
#include <vector>
@@ -21,6 +22,7 @@
namespace Catch {
struct SourceLineInfo;
+ class IResultCapture;
struct MessageStream {
@@ -39,11 +41,10 @@ namespace Catch {
ResultWas::OfType type ):
m_info(macroName, lineInfo, type) {}
-
template<typename T>
- MessageBuilder& operator << ( T const& value ) {
+ MessageBuilder&& operator << ( T const& value ) && {
m_stream << value;
- return *this;
+ return CATCH_MOVE(*this);
}
MessageInfo m_info;
@@ -51,7 +52,7 @@ namespace Catch {
class ScopedMessage {
public:
- explicit ScopedMessage( MessageBuilder const& builder );
+ explicit ScopedMessage( MessageBuilder&& builder );
ScopedMessage( ScopedMessage& duplicate ) = delete;
ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage();
@@ -62,7 +63,7 @@ namespace Catch {
class Capturer {
std::vector<MessageInfo> m_messages;
- IResultCapture& m_resultCapture = getResultCapture();
+ IResultCapture& m_resultCapture;
size_t m_captured = 0;
public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
@@ -98,7 +99,10 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
- Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \
+ Catch::Capturer varName( macroName##_catch_sr, \
+ CATCH_INTERNAL_LINEINFO, \
+ Catch::ResultWas::Info, \
+ #__VA_ARGS__##_catch_sr ); \
varName.captureValues( 0, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
@@ -110,28 +114,28 @@ namespace Catch {
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
-#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ )
-#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
#define CATCH_INFO( msg ) (void)(0)
#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
#define CATCH_WARN( msg ) (void)(0)
#define CATCH_CAPTURE( ... ) (void)(0)
-#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE)
+#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ )
-#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE)
+#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
#define INFO( msg ) (void)(0)
#define UNSCOPED_INFO( msg ) (void)(0)
diff --git a/src/catch2/catch_registry_hub.cpp b/src/catch2/catch_registry_hub.cpp
index 243dd2b0..8716db3a 100644
--- a/src/catch2/catch_registry_hub.cpp
+++ b/src/catch2/catch_registry_hub.cpp
@@ -20,6 +20,9 @@
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
+#include <catch2/internal/catch_reporter_registry.hpp>
+
+#include <exception>
namespace Catch {
@@ -31,7 +34,7 @@ namespace Catch {
public: // IRegistryHub
RegistryHub() = default;
- IReporterRegistry const& getReporterRegistry() const override {
+ ReporterRegistry const& getReporterRegistry() const override {
return m_reporterRegistry;
}
ITestCaseRegistry const& getTestCaseRegistry() const override {
diff --git a/src/catch2/catch_session.cpp b/src/catch2/catch_session.cpp
index 128f21d9..f1ed5f9c 100644
--- a/src/catch2/catch_session.cpp
+++ b/src/catch2/catch_session.cpp
@@ -13,13 +13,13 @@
#include <catch2/internal/catch_run_context.hpp>
#include <catch2/catch_test_spec.hpp>
#include <catch2/catch_version.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/internal/catch_startup_exception_registry.hpp>
#include <catch2/internal/catch_sharding.hpp>
+#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_windows_h_proxy.hpp>
#include <catch2/reporters/catch_reporter_multi.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
+#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
@@ -27,6 +27,7 @@
#include <algorithm>
#include <cassert>
+#include <exception>
#include <iomanip>
#include <set>
@@ -341,6 +342,12 @@ namespace Catch {
return 2;
}
+ if ( totals.testCases.total() > 0 &&
+ totals.testCases.total() == totals.testCases.skipped
+ && !m_config->zeroTestsCountAsSuccess() ) {
+ return 4;
+ }
+
// Note that on unices only the lower 8 bits are usually used, clamping
// the return value to 255 prevents false negative when some multiple
// of 256 tests has failed
diff --git a/src/catch2/catch_test_case_info.cpp b/src/catch2/catch_test_case_info.cpp
index a6adce0a..e9dad577 100644
--- a/src/catch2/catch_test_case_info.cpp
+++ b/src/catch2/catch_test_case_info.cpp
@@ -139,12 +139,20 @@ namespace Catch {
for (size_t idx = 0; idx < originalTags.size(); ++idx) {
auto c = originalTags[idx];
if (c == '[') {
- assert(!inTag);
+ CATCH_ENFORCE(
+ !inTag,
+ "Found '[' inside a tag while registering test case '"
+ << _nameAndTags.name << "' at " << _lineInfo );
+
inTag = true;
tagStart = idx;
}
if (c == ']') {
- assert(inTag);
+ CATCH_ENFORCE(
+ inTag,
+ "Found unmatched ']' while registering test case '"
+ << _nameAndTags.name << "' at " << _lineInfo );
+
inTag = false;
tagEnd = idx;
assert(tagStart < tagEnd);
@@ -153,7 +161,11 @@ namespace Catch {
// it over to backing storage and actually reference the
// backing storage in the saved tags
StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
- CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed");
+ CATCH_ENFORCE( !tagStr.empty(),
+ "Found an empty tag while registering test case '"
+ << _nameAndTags.name << "' at "
+ << _lineInfo );
+
enforceNotReservedTag(tagStr, lineInfo);
properties |= parseSpecialTag(tagStr);
// When copying a tag to the backing storage, we need to
@@ -167,8 +179,12 @@ namespace Catch {
// the tags.
internalAppendTag(tagStr);
}
- (void)inTag; // Silence "set-but-unused" warning in release mode.
}
+ CATCH_ENFORCE( !inTag,
+ "Found an unclosed tag while registering test case '"
+ << _nameAndTags.name << "' at " << _lineInfo );
+
+
// Add [.] if relevant
if (isHidden()) {
internalAppendTag("."_sr);
diff --git a/src/catch2/catch_test_case_info.hpp b/src/catch2/catch_test_case_info.hpp
index ab5ae8b9..5ff3e3e7 100644
--- a/src/catch2/catch_test_case_info.hpp
+++ b/src/catch2/catch_test_case_info.hpp
@@ -15,6 +15,7 @@
#include <catch2/internal/catch_unique_ptr.hpp>
+#include <cstdint>
#include <string>
#include <vector>
diff --git a/src/catch2/catch_test_macros.hpp b/src/catch2/catch_test_macros.hpp
index cce2852f..1088afbe 100644
--- a/src/catch2/catch_test_macros.hpp
+++ b/src/catch2/catch_test_macros.hpp
@@ -49,6 +49,7 @@
#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
@@ -102,6 +103,7 @@
#define CATCH_FAIL( ... ) (void)(0)
#define CATCH_FAIL_CHECK( ... ) (void)(0)
#define CATCH_SUCCEED( ... ) (void)(0)
+ #define CATCH_SKIP( ... ) (void)(0)
#define CATCH_STATIC_REQUIRE( ... ) (void)(0)
#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
@@ -146,6 +148,7 @@
#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ )
#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
@@ -198,6 +201,7 @@
#define FAIL( ... ) (void)(0)
#define FAIL_CHECK( ... ) (void)(0)
#define SUCCEED( ... ) (void)(0)
+ #define SKIP( ... ) (void)(0)
#define STATIC_REQUIRE( ... ) (void)(0)
#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
diff --git a/src/catch2/catch_test_spec.cpp b/src/catch2/catch_test_spec.cpp
index f27ce99c..f32f9864 100644
--- a/src/catch2/catch_test_spec.cpp
+++ b/src/catch2/catch_test_spec.cpp
@@ -6,6 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_test_spec.hpp>
+#include <catch2/interfaces/catch_interfaces_testcase.hpp>
+#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/catch_test_case_info.hpp>
@@ -106,16 +108,18 @@ namespace Catch {
return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
}
- TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const
- {
- Matches matches( m_filters.size() );
- std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
+ TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const {
+ Matches matches;
+ matches.reserve( m_filters.size() );
+ for ( auto const& filter : m_filters ) {
std::vector<TestCaseHandle const*> currentMatches;
- for( auto const& test : testCases )
- if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) )
+ for ( auto const& test : testCases )
+ if ( isThrowSafe( test, config ) &&
+ filter.matches( test.getTestCaseInfo() ) )
currentMatches.emplace_back( &test );
- return FilterMatch{ extractFilterName(filter), currentMatches };
- } );
+ matches.push_back(
+ FilterMatch{ extractFilterName( filter ), currentMatches } );
+ }
return matches;
}
diff --git a/src/catch2/catch_tostring.hpp b/src/catch2/catch_tostring.hpp
index 904caa7e..f3fb0beb 100644
--- a/src/catch2/catch_tostring.hpp
+++ b/src/catch2/catch_tostring.hpp
@@ -116,7 +116,6 @@ namespace Catch {
} // namespace Detail
- // If we decide for C++14, change these to enable_if_ts
template <typename T, typename = void>
struct StringMaker {
template <typename Fake = T>
@@ -399,6 +398,12 @@ namespace Catch {
}
}
};
+ template <>
+ struct StringMaker<std::nullopt_t> {
+ static std::string convert(const std::nullopt_t&) {
+ return "{ }";
+ }
+ };
}
#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
diff --git a/src/catch2/catch_totals.cpp b/src/catch2/catch_totals.cpp
index a3e2b384..bd1954fb 100644
--- a/src/catch2/catch_totals.cpp
+++ b/src/catch2/catch_totals.cpp
@@ -14,6 +14,7 @@ namespace Catch {
diff.passed = passed - other.passed;
diff.failed = failed - other.failed;
diff.failedButOk = failedButOk - other.failedButOk;
+ diff.skipped = skipped - other.skipped;
return diff;
}
@@ -21,14 +22,15 @@ namespace Catch {
passed += other.passed;
failed += other.failed;
failedButOk += other.failedButOk;
+ skipped += other.skipped;
return *this;
}
std::uint64_t Counts::total() const {
- return passed + failed + failedButOk;
+ return passed + failed + failedButOk + skipped;
}
bool Counts::allPassed() const {
- return failed == 0 && failedButOk == 0;
+ return failed == 0 && failedButOk == 0 && skipped == 0;
}
bool Counts::allOk() const {
return failed == 0;
@@ -53,6 +55,8 @@ namespace Catch {
++diff.testCases.failed;
else if( diff.assertions.failedButOk > 0 )
++diff.testCases.failedButOk;
+ else if ( diff.assertions.skipped > 0 )
+ ++ diff.testCases.skipped;
else
++diff.testCases.passed;
return diff;
diff --git a/src/catch2/catch_totals.hpp b/src/catch2/catch_totals.hpp
index 8dd360c6..386392c9 100644
--- a/src/catch2/catch_totals.hpp
+++ b/src/catch2/catch_totals.hpp
@@ -23,6 +23,7 @@ namespace Catch {
std::uint64_t passed = 0;
std::uint64_t failed = 0;
std::uint64_t failedButOk = 0;
+ std::uint64_t skipped = 0;
};
struct Totals {
diff --git a/src/catch2/catch_translate_exception.cpp b/src/catch2/catch_translate_exception.cpp
new file mode 100644
index 00000000..c4b28944
--- /dev/null
+++ b/src/catch2/catch_translate_exception.cpp
@@ -0,0 +1,20 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/catch_translate_exception.hpp>
+#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
+
+namespace Catch {
+ namespace Detail {
+ void registerTranslatorImpl(
+ Detail::unique_ptr<IExceptionTranslator>&& translator ) {
+ getMutableRegistryHub().registerTranslator(
+ CATCH_MOVE( translator ) );
+ }
+ } // namespace Detail
+} // namespace Catch
diff --git a/src/catch2/catch_translate_exception.hpp b/src/catch2/catch_translate_exception.hpp
index 2dbeb17e..5a4dc5e3 100644
--- a/src/catch2/catch_translate_exception.hpp
+++ b/src/catch2/catch_translate_exception.hpp
@@ -15,6 +15,10 @@
#include <exception>
namespace Catch {
+ namespace Detail {
+ void registerTranslatorImpl(
+ Detail::unique_ptr<IExceptionTranslator>&& translator );
+ }
class ExceptionTranslatorRegistrar {
template<typename T>
@@ -48,9 +52,9 @@ namespace Catch {
public:
template<typename T>
ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
- getMutableRegistryHub().registerTranslator(
- Detail::make_unique<ExceptionTranslator<T>>(translateFunction)
- );
+ Detail::registerTranslatorImpl(
+ Detail::make_unique<ExceptionTranslator<T>>(
+ translateFunction ) );
}
};
diff --git a/src/catch2/catch_user_config.hpp.in b/src/catch2/catch_user_config.hpp.in
index 3f6b10e8..10d61937 100644
--- a/src/catch2/catch_user_config.hpp.in
+++ b/src/catch2/catch_user_config.hpp.in
@@ -169,9 +169,18 @@
#endif
+#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
+#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
+
+#if defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \
+ defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT )
+# error Cannot force STATIC_ANALYSIS_SUPPORT to both ON and OFF
+#endif
+
+
// ------
// Simple toggle defines
-// their value is never used and they cannot be overriden
+// their value is never used and they cannot be overridden
// ------
@@ -189,6 +198,7 @@
#cmakedefine CATCH_CONFIG_FAST_COMPILE
#cmakedefine CATCH_CONFIG_NOSTDOUT
#cmakedefine CATCH_CONFIG_PREFIX_ALL
+#cmakedefine CATCH_CONFIG_PREFIX_MESSAGES
#cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG
#cmakedefine CATCH_CONFIG_SHARED_LIBRARY
diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp
index b6b818a0..dbf8b4b2 100644
--- a/src/catch2/catch_version.cpp
+++ b/src/catch2/catch_version.cpp
@@ -36,7 +36,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 3, 2, 1, "", 0 );
+ static Version version( 3, 5, 0, "", 0 );
return version;
}
diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp
index f61d999d..fbe1188c 100644
--- a/src/catch2/catch_version_macros.hpp
+++ b/src/catch2/catch_version_macros.hpp
@@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
-#define CATCH_VERSION_MINOR 2
-#define CATCH_VERSION_PATCH 1
+#define CATCH_VERSION_MINOR 5
+#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
diff --git a/src/catch2/generators/catch_generators.cpp b/src/catch2/generators/catch_generators.cpp
index 7cc5aa8a..3514e9f6 100644
--- a/src/catch2/generators/catch_generators.cpp
+++ b/src/catch2/generators/catch_generators.cpp
@@ -27,9 +27,16 @@ namespace Detail {
GeneratorUntypedBase::~GeneratorUntypedBase() = default;
- auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+ IGeneratorTracker* acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) {
return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
}
+ IGeneratorTracker* createGeneratorTracker( StringRef generatorName,
+ SourceLineInfo lineInfo,
+ GeneratorBasePtr&& generator ) {
+ return getResultCapture().createGeneratorTracker(
+ generatorName, lineInfo, CATCH_MOVE( generator ) );
+ }
+
} // namespace Generators
} // namespace Catch
diff --git a/src/catch2/generators/catch_generators.hpp b/src/catch2/generators/catch_generators.hpp
index 8fc7e58c..117f1901 100644
--- a/src/catch2/generators/catch_generators.hpp
+++ b/src/catch2/generators/catch_generators.hpp
@@ -14,7 +14,6 @@
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_unique_name.hpp>
-#include <catch2/internal/catch_preprocessor.hpp>
#include <vector>
#include <tuple>
@@ -204,37 +203,47 @@ namespace Detail {
return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... );
}
- auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+ IGeneratorTracker* acquireGeneratorTracker( StringRef generatorName,
+ SourceLineInfo const& lineInfo );
+ IGeneratorTracker* createGeneratorTracker( StringRef generatorName,
+ SourceLineInfo lineInfo,
+ GeneratorBasePtr&& generator );
template<typename L>
- // Note: The type after -> is weird, because VS2015 cannot parse
- // the expression used in the typedef inside, when it is in
- // return type. Yeah.
- auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
+ auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> typename decltype(generatorExpression())::type {
using UnderlyingType = typename decltype(generatorExpression())::type;
- IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
- if (!tracker.hasGenerator()) {
- tracker.setGenerator(Catch::Detail::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+ IGeneratorTracker* tracker = acquireGeneratorTracker( generatorName, lineInfo );
+ // Creation of tracker is delayed after generator creation, so
+ // that constructing generator can fail without breaking everything.
+ if (!tracker) {
+ tracker = createGeneratorTracker(
+ generatorName,
+ lineInfo,
+ Catch::Detail::make_unique<Generators<UnderlyingType>>(
+ generatorExpression() ) );
}
- auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
+ auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker->getGenerator() );
return generator.get();
}
} // namespace Generators
} // namespace Catch
+#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr
+#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__)
+
#define GENERATE( ... ) \
- Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \
[ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_COPY( ... ) \
- Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \
[=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_REF( ... ) \
- Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
+ Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \
[&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
diff --git a/src/catch2/generators/catch_generators_random.cpp b/src/catch2/generators/catch_generators_random.cpp
index 2e3390fd..00a8e634 100644
--- a/src/catch2/generators/catch_generators_random.cpp
+++ b/src/catch2/generators/catch_generators_random.cpp
@@ -7,7 +7,35 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/generators/catch_generators_random.hpp>
-
#include <catch2/internal/catch_context.hpp>
-std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); }
+#include <random>
+
+namespace Catch {
+ namespace Generators {
+ namespace Detail {
+ std::uint32_t getSeed() { return sharedRng()(); }
+ } // namespace Detail
+
+ struct RandomFloatingGenerator<long double>::PImpl {
+ PImpl( long double a, long double b, uint32_t seed ):
+ rng( seed ), dist( a, b ) {}
+
+ Catch::SimplePcg32 rng;
+ std::uniform_real_distribution<long double> dist;
+ };
+
+ RandomFloatingGenerator<long double>::RandomFloatingGenerator(
+ long double a, long double b, std::uint32_t seed) :
+ m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) {
+ static_cast<void>( next() );
+ }
+
+ RandomFloatingGenerator<long double>::~RandomFloatingGenerator() =
+ default;
+ bool RandomFloatingGenerator<long double>::next() {
+ m_current_number = m_pimpl->dist( m_pimpl->rng );
+ return true;
+ }
+ } // namespace Generators
+} // namespace Catch
diff --git a/src/catch2/generators/catch_generators_random.hpp b/src/catch2/generators/catch_generators_random.hpp
index bcd4888d..d5f307d4 100644
--- a/src/catch2/generators/catch_generators_random.hpp
+++ b/src/catch2/generators/catch_generators_random.hpp
@@ -11,8 +11,9 @@
#include <catch2/internal/catch_context.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_random_number_generator.hpp>
-
-#include <random>
+#include <catch2/internal/catch_uniform_integer_distribution.hpp>
+#include <catch2/internal/catch_uniform_floating_point_distribution.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
namespace Catch {
namespace Generators {
@@ -26,7 +27,7 @@ namespace Detail {
template <typename Float>
class RandomFloatingGenerator final : public IGenerator<Float> {
Catch::SimplePcg32 m_rng;
- std::uniform_real_distribution<Float> m_dist;
+ Catch::uniform_floating_point_distribution<Float> m_dist;
Float m_current_number;
public:
RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ):
@@ -44,10 +45,27 @@ public:
}
};
+template <>
+class RandomFloatingGenerator<long double> final : public IGenerator<long double> {
+ // We still rely on <random> for this specialization, but we don't
+ // want to drag it into the header.
+ struct PImpl;
+ Catch::Detail::unique_ptr<PImpl> m_pimpl;
+ long double m_current_number;
+
+public:
+ RandomFloatingGenerator( long double a, long double b, std::uint32_t seed );
+
+ long double const& get() const override { return m_current_number; }
+ bool next() override;
+
+ ~RandomFloatingGenerator() override; // = default
+};
+
template <typename Integer>
class RandomIntegerGenerator final : public IGenerator<Integer> {
Catch::SimplePcg32 m_rng;
- std::uniform_int_distribution<Integer> m_dist;
+ Catch::uniform_integer_distribution<Integer> m_dist;
Integer m_current_number;
public:
RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
@@ -68,14 +86,6 @@ public:
template <typename T>
std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>>
random(T a, T b) {
- static_assert(
- !std::is_same<T, char>::value &&
- !std::is_same<T, int8_t>::value &&
- !std::is_same<T, uint8_t>::value &&
- !std::is_same<T, signed char>::value &&
- !std::is_same<T, unsigned char>::value &&
- !std::is_same<T, bool>::value,
- "The requested type is not supported by the underlying random distributions from std" );
return GeneratorWrapper<T>(
Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
);
diff --git a/src/catch2/generators/catch_generators_range.hpp b/src/catch2/generators/catch_generators_range.hpp
index 495acb95..b67c1590 100644
--- a/src/catch2/generators/catch_generators_range.hpp
+++ b/src/catch2/generators/catch_generators_range.hpp
@@ -96,10 +96,11 @@ GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(from, to));
}
-template <typename Container,
- typename ResultType = typename Container::value_type>
-GeneratorWrapper<ResultType> from_range(Container const& cnt) {
- return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
+template <typename Container>
+auto from_range(Container const& cnt) {
+ using std::begin;
+ using std::end;
+ return from_range( begin( cnt ), end( cnt ) );
}
diff --git a/src/catch2/interfaces/catch_interfaces_all.hpp b/src/catch2/interfaces/catch_interfaces_all.hpp
index 87b746d8..a99fdcdc 100644
--- a/src/catch2/interfaces/catch_interfaces_all.hpp
+++ b/src/catch2/interfaces/catch_interfaces_all.hpp
@@ -30,8 +30,8 @@
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
+#include <catch2/interfaces/catch_interfaces_test_invoker.hpp>
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED
diff --git a/src/catch2/interfaces/catch_interfaces_capture.hpp b/src/catch2/interfaces/catch_interfaces_capture.hpp
index ac25a8c0..a1876a4c 100644
--- a/src/catch2/interfaces/catch_interfaces_capture.hpp
+++ b/src/catch2/interfaces/catch_interfaces_capture.hpp
@@ -13,6 +13,8 @@
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_result_type.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
+#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
namespace Catch {
@@ -30,19 +32,31 @@ namespace Catch {
class IGeneratorTracker;
struct BenchmarkInfo;
- template <typename Duration = std::chrono::duration<double, std::nano>>
- struct BenchmarkStats;
+
+ namespace Generators {
+ class GeneratorUntypedBase;
+ using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
+ }
+
class IResultCapture {
public:
virtual ~IResultCapture();
- virtual bool sectionStarted( SectionInfo const& sectionInfo,
- Counts& assertions ) = 0;
- virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
- virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
-
- virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+ virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0;
+ virtual bool sectionStarted( StringRef sectionName,
+ SourceLineInfo const& sectionLineInfo,
+ Counts& assertions ) = 0;
+ virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0;
+ virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0;
+
+ virtual IGeneratorTracker*
+ acquireGeneratorTracker( StringRef generatorName,
+ SourceLineInfo const& lineInfo ) = 0;
+ virtual IGeneratorTracker*
+ createGeneratorTracker( StringRef generatorName,
+ SourceLineInfo lineInfo,
+ Generators::GeneratorBasePtr&& generator ) = 0;
virtual void benchmarkPreparing( StringRef name ) = 0;
virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
@@ -52,7 +66,7 @@ namespace Catch {
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
- virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
+ virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
virtual void handleFatalErrorCondition( StringRef message ) = 0;
@@ -70,7 +84,7 @@ namespace Catch {
AssertionReaction& reaction ) = 0;
virtual void handleUnexpectedInflightException
( AssertionInfo const& info,
- std::string const& message,
+ std::string&& message,
AssertionReaction& reaction ) = 0;
virtual void handleIncomplete
( AssertionInfo const& info ) = 0;
diff --git a/src/catch2/interfaces/catch_interfaces_exception.hpp b/src/catch2/interfaces/catch_interfaces_exception.hpp
index 9177666a..fcc2a8f9 100644
--- a/src/catch2/interfaces/catch_interfaces_exception.hpp
+++ b/src/catch2/interfaces/catch_interfaces_exception.hpp
@@ -8,7 +8,6 @@
#ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
#define CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <string>
diff --git a/src/catch2/interfaces/catch_interfaces_registry_hub.hpp b/src/catch2/interfaces/catch_interfaces_registry_hub.hpp
index 8813b538..113f223e 100644
--- a/src/catch2/interfaces/catch_interfaces_registry_hub.hpp
+++ b/src/catch2/interfaces/catch_interfaces_registry_hub.hpp
@@ -19,7 +19,7 @@ namespace Catch {
class ITestCaseRegistry;
class IExceptionTranslatorRegistry;
class IExceptionTranslator;
- class IReporterRegistry;
+ class ReporterRegistry;
class IReporterFactory;
class ITagAliasRegistry;
class ITestInvoker;
@@ -35,7 +35,7 @@ namespace Catch {
public:
virtual ~IRegistryHub(); // = default
- virtual IReporterRegistry const& getReporterRegistry() const = 0;
+ virtual ReporterRegistry const& getReporterRegistry() const = 0;
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
diff --git a/src/catch2/interfaces/catch_interfaces_reporter.cpp b/src/catch2/interfaces/catch_interfaces_reporter.cpp
index 0bcf66df..3274bcf5 100644
--- a/src/catch2/interfaces/catch_interfaces_reporter.cpp
+++ b/src/catch2/interfaces/catch_interfaces_reporter.cpp
@@ -54,24 +54,21 @@ namespace Catch {
infoMessages( _infoMessages ),
totals( _totals )
{
- assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
-
if( assertionResult.hasMessage() ) {
// Copy message into messages list.
// !TBD This should have been done earlier, somewhere
MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
- builder << assertionResult.getMessage();
- builder.m_info.message = builder.m_stream.str();
+ builder.m_info.message = static_cast<std::string>(assertionResult.getMessage());
- infoMessages.push_back( builder.m_info );
+ infoMessages.push_back( CATCH_MOVE(builder.m_info) );
}
}
- SectionStats::SectionStats( SectionInfo const& _sectionInfo,
+ SectionStats::SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions,
double _durationInSeconds,
bool _missingAssertions )
- : sectionInfo( _sectionInfo ),
+ : sectionInfo( CATCH_MOVE(_sectionInfo) ),
assertions( _assertions ),
durationInSeconds( _durationInSeconds ),
missingAssertions( _missingAssertions )
@@ -80,13 +77,13 @@ namespace Catch {
TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals,
- std::string const& _stdOut,
- std::string const& _stdErr,
+ std::string&& _stdOut,
+ std::string&& _stdErr,
bool _aborting )
: testInfo( &_testInfo ),
totals( _totals ),
- stdOut( _stdOut ),
- stdErr( _stdErr ),
+ stdOut( CATCH_MOVE(_stdOut) ),
+ stdErr( CATCH_MOVE(_stdErr) ),
aborting( _aborting )
{}
diff --git a/src/catch2/interfaces/catch_interfaces_reporter.hpp b/src/catch2/interfaces/catch_interfaces_reporter.hpp
index 5f286363..b40fce31 100644
--- a/src/catch2/interfaces/catch_interfaces_reporter.hpp
+++ b/src/catch2/interfaces/catch_interfaces_reporter.hpp
@@ -13,11 +13,10 @@
#include <catch2/catch_assertion_result.hpp>
#include <catch2/internal/catch_message_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
+#include <catch2/internal/catch_test_run_info.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
-#include <catch2/benchmark/catch_estimate.hpp>
-#include <catch2/benchmark/catch_outlier_classification.hpp>
-
+#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
#include <map>
#include <string>
@@ -57,11 +56,6 @@ namespace Catch {
std::map<std::string, std::string> m_customOptions;
};
- struct TestRunInfo {
- constexpr TestRunInfo(StringRef _name) : name(_name) {}
- StringRef name;
- };
-
struct AssertionStats {
AssertionStats( AssertionResult const& _assertionResult,
std::vector<MessageInfo> const& _infoMessages,
@@ -78,7 +72,7 @@ namespace Catch {
};
struct SectionStats {
- SectionStats( SectionInfo const& _sectionInfo,
+ SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions,
double _durationInSeconds,
bool _missingAssertions );
@@ -92,8 +86,8 @@ namespace Catch {
struct TestCaseStats {
TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals,
- std::string const& _stdOut,
- std::string const& _stdErr,
+ std::string&& _stdOut,
+ std::string&& _stdErr,
bool _aborting );
TestCaseInfo const * testInfo;
@@ -113,45 +107,6 @@ namespace Catch {
bool aborting;
};
-
- struct BenchmarkInfo {
- std::string name;
- double estimatedDuration;
- int iterations;
- unsigned int samples;
- unsigned int resamples;
- double clockResolution;
- double clockCost;
- };
-
- template <class Duration>
- struct BenchmarkStats {
- BenchmarkInfo info;
-
- std::vector<Duration> samples;
- Benchmark::Estimate<Duration> mean;
- Benchmark::Estimate<Duration> standardDeviation;
- Benchmark::OutlierClassification outliers;
- double outlierVariance;
-
- template <typename Duration2>
- operator BenchmarkStats<Duration2>() const {
- std::vector<Duration2> samples2;
- samples2.reserve(samples.size());
- for (auto const& sample : samples) {
- samples2.push_back(Duration2(sample));
- }
- return {
- info,
- CATCH_MOVE(samples2),
- mean,
- standardDeviation,
- outliers,
- outlierVariance,
- };
- }
- };
-
//! By setting up its preferences, a reporter can modify Catch2's behaviour
//! in some regards, e.g. it can request Catch2 to capture writes to
//! stdout/stderr during test execution, and pass them to the reporter.
@@ -242,10 +197,15 @@ namespace Catch {
*/
virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
- //! Called with test cases that are skipped due to the test run aborting
+ /**
+ * Called with test cases that are skipped due to the test run aborting.
+ * NOT called for test cases that are explicitly skipped using the `SKIP` macro.
+ *
+ * Deprecated - will be removed in the next major release.
+ */
virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
- //! Called if a fatal error (signal/structured exception) occured
+ //! Called if a fatal error (signal/structured exception) occurred
virtual void fatalErrorEncountered( StringRef error ) = 0;
//! Writes out information about provided reporters using reporter-specific format
diff --git a/src/catch2/interfaces/catch_interfaces_reporter_registry.cpp b/src/catch2/interfaces/catch_interfaces_reporter_registry.cpp
deleted file mode 100644
index f620cbc8..00000000
--- a/src/catch2/interfaces/catch_interfaces_reporter_registry.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-
-// Copyright Catch2 Authors
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at
-// https://www.boost.org/LICENSE_1_0.txt)
-
-// SPDX-License-Identifier: BSL-1.0
-
-#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
-
-namespace Catch {
- IReporterRegistry::~IReporterRegistry() = default;
-}
diff --git a/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp b/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp
deleted file mode 100644
index 277d1761..00000000
--- a/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-
-// Copyright Catch2 Authors
-// Distributed under the Boost Software License, Version 1.0.
-// (See accompanying file LICENSE.txt or copy at
-// https://www.boost.org/LICENSE_1_0.txt)
-
-// SPDX-License-Identifier: BSL-1.0
-#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
-#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
-
-#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
-#include <catch2/internal/catch_unique_ptr.hpp>
-
-#include <string>
-#include <vector>
-#include <map>
-
-namespace Catch {
-
- class IConfig;
-
- class IEventListener;
- using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
- class IReporterFactory;
- using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
- struct ReporterConfig;
- class EventListenerFactory;
-
- class IReporterRegistry {
- public:
- using FactoryMap = std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>;
- using Listeners = std::vector<Detail::unique_ptr<EventListenerFactory>>;
-
- virtual ~IReporterRegistry(); // = default
- virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0;
- virtual FactoryMap const& getFactories() const = 0;
- virtual Listeners const& getListeners() const = 0;
- };
-
-} // end namespace Catch
-
-#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
diff --git a/src/catch2/interfaces/catch_interfaces_test_invoker.hpp b/src/catch2/interfaces/catch_interfaces_test_invoker.hpp
new file mode 100644
index 00000000..3caeff9a
--- /dev/null
+++ b/src/catch2/interfaces/catch_interfaces_test_invoker.hpp
@@ -0,0 +1,21 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
+#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
+
+namespace Catch {
+
+ class ITestInvoker {
+ public:
+ virtual void invoke() const = 0;
+ virtual ~ITestInvoker(); // = default
+ };
+
+} // namespace Catch
+
+#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
diff --git a/src/catch2/interfaces/catch_interfaces_testcase.cpp b/src/catch2/interfaces/catch_interfaces_testcase.cpp
index 5e632ba8..a543116c 100644
--- a/src/catch2/interfaces/catch_interfaces_testcase.cpp
+++ b/src/catch2/interfaces/catch_interfaces_testcase.cpp
@@ -9,6 +9,5 @@
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
namespace Catch {
- ITestInvoker::~ITestInvoker() = default;
ITestCaseRegistry::~ITestCaseRegistry() = default;
}
diff --git a/src/catch2/interfaces/catch_interfaces_testcase.hpp b/src/catch2/interfaces/catch_interfaces_testcase.hpp
index 78ee2021..daee8482 100644
--- a/src/catch2/interfaces/catch_interfaces_testcase.hpp
+++ b/src/catch2/interfaces/catch_interfaces_testcase.hpp
@@ -12,15 +12,7 @@
namespace Catch {
- class TestSpec;
struct TestCaseInfo;
-
- class ITestInvoker {
- public:
- virtual void invoke () const = 0;
- virtual ~ITestInvoker(); // = default
- };
-
class TestCaseHandle;
class IConfig;
@@ -33,11 +25,6 @@ namespace Catch {
virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
};
- bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
- bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
- std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
- std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
-
}
#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_assertion_handler.cpp b/src/catch2/internal/catch_assertion_handler.cpp
index f051314c..e5232f70 100644
--- a/src/catch2/internal/catch_assertion_handler.cpp
+++ b/src/catch2/internal/catch_assertion_handler.cpp
@@ -12,7 +12,6 @@
#include <catch2/internal/catch_debugger.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
-#include <catch2/internal/catch_run_context.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
namespace Catch {
@@ -24,7 +23,9 @@ namespace Catch {
ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
m_resultCapture( getResultCapture() )
- {}
+ {
+ m_resultCapture.notifyAssertionStarted( m_assertionInfo );
+ }
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
@@ -38,7 +39,7 @@ namespace Catch {
}
void AssertionHandler::complete() {
- setCompleted();
+ m_completed = true;
if( m_reaction.shouldDebugBreak ) {
// If you find your debugger stopping you here then go one level up on the
@@ -50,9 +51,9 @@ namespace Catch {
if (m_reaction.shouldThrow) {
throw_test_failure_exception();
}
- }
- void AssertionHandler::setCompleted() {
- m_completed = true;
+ if ( m_reaction.shouldSkip ) {
+ throw_test_skip_exception();
+ }
}
void AssertionHandler::handleUnexpectedInflightException() {
diff --git a/src/catch2/internal/catch_assertion_handler.hpp b/src/catch2/internal/catch_assertion_handler.hpp
index 36b55243..01dd7801 100644
--- a/src/catch2/internal/catch_assertion_handler.hpp
+++ b/src/catch2/internal/catch_assertion_handler.hpp
@@ -11,17 +11,15 @@
#include <catch2/catch_assertion_info.hpp>
#include <catch2/internal/catch_decomposer.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
-#include <catch2/internal/catch_lazy_expr.hpp>
#include <string>
namespace Catch {
- class IResultCapture;
-
struct AssertionReaction {
bool shouldDebugBreak = false;
bool shouldThrow = false;
+ bool shouldSkip = false;
};
class AssertionHandler {
@@ -58,7 +56,6 @@ namespace Catch {
void handleUnexpectedInflightException();
void complete();
- void setCompleted();
// query
auto allowThrows() const -> bool;
diff --git a/src/catch2/internal/catch_commandline.cpp b/src/catch2/internal/catch_commandline.cpp
index 81aa073c..4ac1847b 100644
--- a/src/catch2/internal/catch_commandline.cpp
+++ b/src/catch2/internal/catch_commandline.cpp
@@ -10,7 +10,7 @@
#include <catch2/catch_config.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
+#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_console_colour.hpp>
#include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_reporter_spec_parser.hpp>
@@ -144,7 +144,7 @@ namespace Catch {
auto const& reporterSpec = *parsed;
- IReporterRegistry::FactoryMap const& factories =
+ auto const& factories =
getRegistryHub().getReporterRegistry().getFactories();
auto result = factories.find( reporterSpec.name() );
diff --git a/src/catch2/internal/catch_compiler_capabilities.hpp b/src/catch2/internal/catch_compiler_capabilities.hpp
index d457a4b4..dacae01b 100644
--- a/src/catch2/internal/catch_compiler_capabilities.hpp
+++ b/src/catch2/internal/catch_compiler_capabilities.hpp
@@ -41,7 +41,7 @@
// Only GCC compiler should be used in this block, so other compilers trying to
// mask themselves as GCC should be ignored.
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
@@ -50,20 +50,37 @@
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
_Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
+ _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" )
+
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
_Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" )
# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
_Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" )
+# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
+ _Pragma( "GCC diagnostic ignored \"-Wshadow\"" )
+
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
#endif
+#if defined(__NVCOMPILER)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" )
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" )
+#endif
+
#if defined(__CUDACC__) && !defined(__clang__)
+# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
+// New pragmas introduced in CUDA 11.5+
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic pop" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" )
+# else
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress 177" )
+# endif
#endif
// clang-cl defines _MSC_VER as well as __clang__, which could cause the
@@ -117,6 +134,9 @@
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wcomma\"" )
+# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wshadow\"" )
+
#endif // __clang__
@@ -136,7 +156,9 @@
////////////////////////////////////////////////////////////////////////////////
// Assume that some platforms do not support getenv.
-#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION)
+#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \
+ defined( CATCH_PLATFORM_PLAYSTATION ) || \
+ defined( _GAMING_XBOX )
# define CATCH_INTERNAL_CONFIG_NO_GETENV
#else
# define CATCH_INTERNAL_CONFIG_GETENV
@@ -181,8 +203,14 @@
// Visual C++
#if defined(_MSC_VER)
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
+// We want to defer to nvcc-specific warning suppression if we are compiled
+// with nvcc masquerading for MSVC.
+# if !defined( __CUDACC__ )
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ __pragma( warning( push ) )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ __pragma( warning( pop ) )
+# endif
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
@@ -348,6 +376,9 @@
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT
+#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
#endif
@@ -357,6 +388,16 @@
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
#endif
+#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS )
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS )
+# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS
+#endif
+#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS )
+# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS
+#endif
+
// The goal of this macro is to avoid evaluation of the arguments, but
// still have the compiler warn on problems inside...
@@ -370,13 +411,6 @@
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
-#endif
-
-#if !defined(CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS
-#endif
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
#define CATCH_TRY if ((true))
diff --git a/src/catch2/internal/catch_config_counter.hpp b/src/catch2/internal/catch_config_counter.hpp
index 23b22324..a482ce34 100644
--- a/src/catch2/internal/catch_config_counter.hpp
+++ b/src/catch2/internal/catch_config_counter.hpp
@@ -18,6 +18,8 @@
#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED
#define CATCH_CONFIG_COUNTER_HPP_INCLUDED
+#include <catch2/catch_user_config.hpp>
+
#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
#define CATCH_INTERNAL_CONFIG_COUNTER
#endif
diff --git a/src/catch2/internal/catch_config_prefix_messages.hpp b/src/catch2/internal/catch_config_prefix_messages.hpp
new file mode 100644
index 00000000..be1e9a96
--- /dev/null
+++ b/src/catch2/internal/catch_config_prefix_messages.hpp
@@ -0,0 +1,29 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+/** \file
+ * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option
+ *
+ * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros
+ * by prepending CATCH_. This may not be desirable if the only clashes are with
+ * logger macros such as INFO and WARN. In this cases
+ * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset
+ * of relevant macros.
+ *
+ */
+
+#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
+#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
+
+#include <catch2/catch_user_config.hpp>
+
+#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES)
+ #define CATCH_CONFIG_PREFIX_MESSAGES
+#endif
+
+#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_config_static_analysis_support.hpp b/src/catch2/internal/catch_config_static_analysis_support.hpp
new file mode 100644
index 00000000..81bdf39f
--- /dev/null
+++ b/src/catch2/internal/catch_config_static_analysis_support.hpp
@@ -0,0 +1,34 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+/** \file
+ * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option
+ *
+ * Some of Catch2's macros can be defined differently to work better with
+ * static analysis tools, like clang-tidy or coverity.
+ * Currently the main use case is to show that `SECTION`s are executed
+ * exclusively, and not all in one run of a `TEST_CASE`.
+ */
+
+#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
+#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
+
+#include <catch2/catch_user_config.hpp>
+
+#if defined(__clang_analyzer__) || defined(__COVERITY__)
+ #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT
+#endif
+
+#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \
+ !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \
+ !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT )
+# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
+#endif
+
+
+#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_config_uncaught_exceptions.hpp b/src/catch2/internal/catch_config_uncaught_exceptions.hpp
index 5c4cb930..20b1dfca 100644
--- a/src/catch2/internal/catch_config_uncaught_exceptions.hpp
+++ b/src/catch2/internal/catch_config_uncaught_exceptions.hpp
@@ -17,6 +17,8 @@
#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
+#include <catch2/catch_user_config.hpp>
+
#if defined(_MSC_VER)
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
diff --git a/src/catch2/internal/catch_config_wchar.hpp b/src/catch2/internal/catch_config_wchar.hpp
index 8c758ec4..90d85d05 100644
--- a/src/catch2/internal/catch_config_wchar.hpp
+++ b/src/catch2/internal/catch_config_wchar.hpp
@@ -17,6 +17,8 @@
#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED
#define CATCH_CONFIG_WCHAR_HPP_INCLUDED
+#include <catch2/catch_user_config.hpp>
+
// We assume that WCHAR should be enabled by default, and only disabled
// for a shortlist (so far only DJGPP) of compilers.
diff --git a/src/catch2/internal/catch_console_colour.cpp b/src/catch2/internal/catch_console_colour.cpp
index 099a6c59..e1238816 100644
--- a/src/catch2/internal/catch_console_colour.cpp
+++ b/src/catch2/internal/catch_console_colour.cpp
@@ -85,7 +85,7 @@ namespace Catch {
namespace {
//! A do-nothing implementation of colour, used as fallback for unknown
//! platforms, and when the user asks to deactivate all colours.
- class NoColourImpl : public ColourImpl {
+ class NoColourImpl final : public ColourImpl {
public:
NoColourImpl( IStream* stream ): ColourImpl( stream ) {}
@@ -103,7 +103,7 @@ namespace Catch {
namespace Catch {
namespace {
- class Win32ColourImpl : public ColourImpl {
+ class Win32ColourImpl final : public ColourImpl {
public:
Win32ColourImpl(IStream* stream):
ColourImpl(stream) {
@@ -169,7 +169,7 @@ namespace {
namespace Catch {
namespace {
- class ANSIColourImpl : public ColourImpl {
+ class ANSIColourImpl final : public ColourImpl {
public:
ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {}
diff --git a/src/catch2/internal/catch_console_colour.hpp b/src/catch2/internal/catch_console_colour.hpp
index 9aa6a163..d9144315 100644
--- a/src/catch2/internal/catch_console_colour.hpp
+++ b/src/catch2/internal/catch_console_colour.hpp
@@ -47,6 +47,7 @@ namespace Catch {
Error = BrightRed,
Success = Green,
+ Skip = LightGrey,
OriginalExpression = Cyan,
ReconstructedExpression = BrightYellow,
diff --git a/src/catch2/internal/catch_context.cpp b/src/catch2/internal/catch_context.cpp
index 17f28509..3b1cc277 100644
--- a/src/catch2/internal/catch_context.cpp
+++ b/src/catch2/internal/catch_context.cpp
@@ -11,49 +11,27 @@
namespace Catch {
- class Context : public IMutableContext, private Detail::NonCopyable {
+ Context* Context::currentContext = nullptr;
- public: // IContext
- IResultCapture* getResultCapture() override {
- return m_resultCapture;
- }
-
- IConfig const* getConfig() const override {
- return m_config;
- }
-
- ~Context() override;
-
- public: // IMutableContext
- void setResultCapture( IResultCapture* resultCapture ) override {
- m_resultCapture = resultCapture;
- }
- void setConfig( IConfig const* config ) override {
- m_config = config;
- }
-
- friend IMutableContext& getCurrentMutableContext();
-
- private:
- IConfig const* m_config = nullptr;
- IResultCapture* m_resultCapture = nullptr;
- };
-
- IMutableContext *IMutableContext::currentContext = nullptr;
-
- void IMutableContext::createContext()
- {
+ void cleanUpContext() {
+ delete Context::currentContext;
+ Context::currentContext = nullptr;
+ }
+ void Context::createContext() {
currentContext = new Context();
}
- void cleanUpContext() {
- delete IMutableContext::currentContext;
- IMutableContext::currentContext = nullptr;
+ Context& getCurrentMutableContext() {
+ if ( !Context::currentContext ) { Context::createContext(); }
+ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+ return *Context::currentContext;
+ }
+
+ void Context::setResultCapture( IResultCapture* resultCapture ) {
+ m_resultCapture = resultCapture;
}
- IContext::~IContext() = default;
- IMutableContext::~IMutableContext() = default;
- Context::~Context() = default;
+ void Context::setConfig( IConfig const* config ) { m_config = config; }
SimplePcg32& sharedRng() {
static SimplePcg32 s_rng;
diff --git a/src/catch2/internal/catch_context.hpp b/src/catch2/internal/catch_context.hpp
index a9d1b394..6ccb3b31 100644
--- a/src/catch2/internal/catch_context.hpp
+++ b/src/catch2/internal/catch_context.hpp
@@ -15,38 +15,31 @@ namespace Catch {
class IResultCapture;
class IConfig;
- class IContext {
- public:
- virtual ~IContext(); // = default
+ class Context {
+ IConfig const* m_config = nullptr;
+ IResultCapture* m_resultCapture = nullptr;
- virtual IResultCapture* getResultCapture() = 0;
- virtual IConfig const* getConfig() const = 0;
- };
+ CATCH_EXPORT static Context* currentContext;
+ friend Context& getCurrentMutableContext();
+ friend Context const& getCurrentContext();
+ static void createContext();
+ friend void cleanUpContext();
- class IMutableContext : public IContext {
public:
- ~IMutableContext() override; // = default
- virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
- virtual void setConfig( IConfig const* config ) = 0;
-
- private:
- CATCH_EXPORT static IMutableContext* currentContext;
- friend IMutableContext& getCurrentMutableContext();
- friend void cleanUpContext();
- static void createContext();
+ IResultCapture* getResultCapture() const { return m_resultCapture; }
+ IConfig const* getConfig() const { return m_config; }
+ void setResultCapture( IResultCapture* resultCapture );
+ void setConfig( IConfig const* config );
};
- inline IMutableContext& getCurrentMutableContext()
- {
- if( !IMutableContext::currentContext )
- IMutableContext::createContext();
- // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
- return *IMutableContext::currentContext;
- }
+ Context& getCurrentMutableContext();
- inline IContext& getCurrentContext()
- {
- return getCurrentMutableContext();
+ inline Context const& getCurrentContext() {
+ // We duplicate the logic from `getCurrentMutableContext` here,
+ // to avoid paying the call overhead in debug mode.
+ if ( !Context::currentContext ) { Context::createContext(); }
+ // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+ return *Context::currentContext;
}
void cleanUpContext();
diff --git a/src/catch2/internal/catch_debugger.hpp b/src/catch2/internal/catch_debugger.hpp
index 30063d1f..25c5a260 100644
--- a/src/catch2/internal/catch_debugger.hpp
+++ b/src/catch2/internal/catch_debugger.hpp
@@ -19,7 +19,10 @@ namespace Catch {
#if defined(__i386__) || defined(__x86_64__)
#define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
#elif defined(__aarch64__)
- #define CATCH_TRAP() __asm__(".inst 0xd43e0000")
+ #define CATCH_TRAP() __asm__(".inst 0xd43e0000")
+ #elif defined(__POWERPC__)
+ #define CATCH_TRAP() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+ : : : "memory","r0","r3","r4" ) /* NOLINT */
#endif
#elif defined(CATCH_PLATFORM_IPHONE)
diff --git a/src/catch2/internal/catch_enum_values_registry.cpp b/src/catch2/internal/catch_enum_values_registry.cpp
index 7e8bf5e5..a94b6088 100644
--- a/src/catch2/internal/catch_enum_values_registry.cpp
+++ b/src/catch2/internal/catch_enum_values_registry.cpp
@@ -39,7 +39,7 @@ namespace Catch {
return parsed;
}
- EnumInfo::~EnumInfo() {}
+ EnumInfo::~EnumInfo() = default;
StringRef EnumInfo::lookup( int value ) const {
for( auto const& valueToName : m_values ) {
diff --git a/src/catch2/internal/catch_exception_translator_registry.cpp b/src/catch2/internal/catch_exception_translator_registry.cpp
index 2a240a9b..1eb61147 100644
--- a/src/catch2/internal/catch_exception_translator_registry.cpp
+++ b/src/catch2/internal/catch_exception_translator_registry.cpp
@@ -11,10 +11,27 @@
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
+#include <exception>
+
namespace Catch {
- ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ namespace {
+ static std::string tryTranslators(
+ std::vector<
+ Detail::unique_ptr<IExceptionTranslator const>> const& translators ) {
+ if ( translators.empty() ) {
+ std::rethrow_exception( std::current_exception() );
+ } else {
+ return translators[0]->translate( translators.begin() + 1,
+ translators.end() );
+ }
+ }
+
}
+#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+
+ ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default;
void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) {
m_translators.push_back( CATCH_MOVE( translator ) );
@@ -37,13 +54,16 @@ namespace Catch {
// First we try user-registered translators. If none of them can
// handle the exception, it will be rethrown handled by our defaults.
try {
- return tryTranslators();
+ return tryTranslators(m_translators);
}
// To avoid having to handle TFE explicitly everywhere, we just
// rethrow it so that it goes back up the caller.
catch( TestFailureException& ) {
std::rethrow_exception(std::current_exception());
}
+ catch( TestSkipException& ) {
+ std::rethrow_exception(std::current_exception());
+ }
catch( std::exception const& ex ) {
return ex.what();
}
@@ -58,23 +78,10 @@ namespace Catch {
}
}
- std::string ExceptionTranslatorRegistry::tryTranslators() const {
- if (m_translators.empty()) {
- std::rethrow_exception(std::current_exception());
- } else {
- return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
- }
- }
-
#else // ^^ Exceptions are enabled // Exceptions are disabled vv
std::string ExceptionTranslatorRegistry::translateActiveException() const {
CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
}
-
- std::string ExceptionTranslatorRegistry::tryTranslators() const {
- CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
- }
#endif
-
}
diff --git a/src/catch2/internal/catch_exception_translator_registry.hpp b/src/catch2/internal/catch_exception_translator_registry.hpp
index 2aafa684..3123e93d 100644
--- a/src/catch2/internal/catch_exception_translator_registry.hpp
+++ b/src/catch2/internal/catch_exception_translator_registry.hpp
@@ -21,7 +21,6 @@ namespace Catch {
~ExceptionTranslatorRegistry() override;
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
std::string translateActiveException() const override;
- std::string tryTranslators() const;
private:
ExceptionTranslators m_translators;
diff --git a/src/catch2/internal/catch_floating_point_helpers.cpp b/src/catch2/internal/catch_floating_point_helpers.cpp
index e30ee434..9631ed6d 100644
--- a/src/catch2/internal/catch_floating_point_helpers.cpp
+++ b/src/catch2/internal/catch_floating_point_helpers.cpp
@@ -27,6 +27,17 @@ namespace Catch {
return i;
}
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ bool directCompare( float lhs, float rhs ) { return lhs == rhs; }
+ bool directCompare( double lhs, double rhs ) { return lhs == rhs; }
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic pop
+#endif
+
+
} // end namespace Detail
} // end namespace Catch
diff --git a/src/catch2/internal/catch_floating_point_helpers.hpp b/src/catch2/internal/catch_floating_point_helpers.hpp
index ca883c61..b2143726 100644
--- a/src/catch2/internal/catch_floating_point_helpers.hpp
+++ b/src/catch2/internal/catch_floating_point_helpers.hpp
@@ -22,6 +22,11 @@ namespace Catch {
uint32_t convertToBits(float f);
uint64_t convertToBits(double d);
+ // Used when we know we want == comparison of two doubles
+ // to centralize warning suppression
+ bool directCompare( float lhs, float rhs );
+ bool directCompare( double lhs, double rhs );
+
} // end namespace Detail
diff --git a/src/catch2/internal/catch_is_permutation.hpp b/src/catch2/internal/catch_is_permutation.hpp
new file mode 100644
index 00000000..708053d3
--- /dev/null
+++ b/src/catch2/internal/catch_is_permutation.hpp
@@ -0,0 +1,138 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_IS_PERMUTATION_HPP_INCLUDED
+#define CATCH_IS_PERMUTATION_HPP_INCLUDED
+
+#include <algorithm>
+#include <iterator>
+
+namespace Catch {
+ namespace Detail {
+
+ template <typename ForwardIter,
+ typename Sentinel,
+ typename T,
+ typename Comparator>
+ ForwardIter find_sentinel( ForwardIter start,
+ Sentinel sentinel,
+ T const& value,
+ Comparator cmp ) {
+ while ( start != sentinel ) {
+ if ( cmp( *start, value ) ) { break; }
+ ++start;
+ }
+ return start;
+ }
+
+ template <typename ForwardIter,
+ typename Sentinel,
+ typename T,
+ typename Comparator>
+ std::ptrdiff_t count_sentinel( ForwardIter start,
+ Sentinel sentinel,
+ T const& value,
+ Comparator cmp ) {
+ std::ptrdiff_t count = 0;
+ while ( start != sentinel ) {
+ if ( cmp( *start, value ) ) { ++count; }
+ ++start;
+ }
+ return count;
+ }
+
+ template <typename ForwardIter, typename Sentinel>
+ std::enable_if_t<!std::is_same<ForwardIter, Sentinel>::value,
+ std::ptrdiff_t>
+ sentinel_distance( ForwardIter iter, const Sentinel sentinel ) {
+ std::ptrdiff_t dist = 0;
+ while ( iter != sentinel ) {
+ ++iter;
+ ++dist;
+ }
+ return dist;
+ }
+
+ template <typename ForwardIter>
+ std::ptrdiff_t sentinel_distance( ForwardIter first,
+ ForwardIter last ) {
+ return std::distance( first, last );
+ }
+
+ template <typename ForwardIter1,
+ typename Sentinel1,
+ typename ForwardIter2,
+ typename Sentinel2,
+ typename Comparator>
+ bool check_element_counts( ForwardIter1 first_1,
+ const Sentinel1 end_1,
+ ForwardIter2 first_2,
+ const Sentinel2 end_2,
+ Comparator cmp ) {
+ auto cursor = first_1;
+ while ( cursor != end_1 ) {
+ if ( find_sentinel( first_1, cursor, *cursor, cmp ) ==
+ cursor ) {
+ // we haven't checked this element yet
+ const auto count_in_range_2 =
+ count_sentinel( first_2, end_2, *cursor, cmp );
+ // Not a single instance in 2nd range, so it cannot be a
+ // permutation of 1st range
+ if ( count_in_range_2 == 0 ) { return false; }
+
+ const auto count_in_range_1 =
+ count_sentinel( cursor, end_1, *cursor, cmp );
+ if ( count_in_range_1 != count_in_range_2 ) {
+ return false;
+ }
+ }
+
+ ++cursor;
+ }
+
+ return true;
+ }
+
+ template <typename ForwardIter1,
+ typename Sentinel1,
+ typename ForwardIter2,
+ typename Sentinel2,
+ typename Comparator>
+ bool is_permutation( ForwardIter1 first_1,
+ const Sentinel1 end_1,
+ ForwardIter2 first_2,
+ const Sentinel2 end_2,
+ Comparator cmp ) {
+ // TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types
+ // TODO: Comparator has to be "both sides", e.g. a == b => b == a
+ // This skips shared prefix of the two ranges
+ while (first_1 != end_1 && first_2 != end_2 && cmp(*first_1, *first_2)) {
+ ++first_1;
+ ++first_2;
+ }
+
+ // We need to handle case where at least one of the ranges has no more elements
+ if (first_1 == end_1 || first_2 == end_2) {
+ return first_1 == end_1 && first_2 == end_2;
+ }
+
+ // pair counting is n**2, so we pay linear walk to compare the sizes first
+ auto dist_1 = sentinel_distance( first_1, end_1 );
+ auto dist_2 = sentinel_distance( first_2, end_2 );
+
+ if (dist_1 != dist_2) { return false; }
+
+ // Since we do not try to handle stronger iterators pair (e.g.
+ // bidir) optimally, the only thing left to do is to check counts in
+ // the remaining ranges.
+ return check_element_counts( first_1, end_1, first_2, end_2, cmp );
+ }
+
+ } // namespace Detail
+} // namespace Catch
+
+#endif // CATCH_IS_PERMUTATION_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_istream.cpp b/src/catch2/internal/catch_istream.cpp
index 489396ec..bf0f66e4 100644
--- a/src/catch2/internal/catch_istream.cpp
+++ b/src/catch2/internal/catch_istream.cpp
@@ -24,7 +24,7 @@ namespace Catch {
namespace Detail {
namespace {
template<typename WriterF, std::size_t bufferSize=256>
- class StreamBufImpl : public std::streambuf {
+ class StreamBufImpl final : public std::streambuf {
char data[bufferSize];
WriterF m_writer;
@@ -72,7 +72,7 @@ namespace Detail {
///////////////////////////////////////////////////////////////////////////
- class FileStream : public IStream {
+ class FileStream final : public IStream {
std::ofstream m_ofs;
public:
FileStream( std::string const& filename ) {
@@ -89,7 +89,7 @@ namespace Detail {
///////////////////////////////////////////////////////////////////////////
- class CoutStream : public IStream {
+ class CoutStream final : public IStream {
std::ostream m_os;
public:
// Store the streambuf from cout up-front because
@@ -118,7 +118,7 @@ namespace Detail {
///////////////////////////////////////////////////////////////////////////
- class DebugOutStream : public IStream {
+ class DebugOutStream final : public IStream {
Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
std::ostream m_os;
public:
diff --git a/src/catch2/internal/catch_jsonwriter.cpp b/src/catch2/internal/catch_jsonwriter.cpp
new file mode 100644
index 00000000..ff65a9d3
--- /dev/null
+++ b/src/catch2/internal/catch_jsonwriter.cpp
@@ -0,0 +1,148 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#include <catch2/internal/catch_enforce.hpp>
+#include <catch2/internal/catch_jsonwriter.hpp>
+
+namespace Catch {
+ void JsonUtils::indent( std::ostream& os, std::uint64_t level ) {
+ for ( std::uint64_t i = 0; i < level; ++i ) {
+ os << " ";
+ }
+ }
+ void JsonUtils::appendCommaNewline( std::ostream& os,
+ bool& should_comma,
+ std::uint64_t level ) {
+ if ( should_comma ) { os << ','; }
+ should_comma = true;
+ os << '\n';
+ indent( os, level );
+ }
+
+ JsonObjectWriter::JsonObjectWriter( std::ostream& os ):
+ JsonObjectWriter{ os, 0 } {}
+
+ JsonObjectWriter::JsonObjectWriter( std::ostream& os,
+ std::uint64_t indent_level ):
+ m_os{ os }, m_indent_level{ indent_level } {
+ m_os << '{';
+ }
+ JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ):
+ m_os{ source.m_os },
+ m_indent_level{ source.m_indent_level },
+ m_should_comma{ source.m_should_comma },
+ m_active{ source.m_active } {
+ source.m_active = false;
+ }
+
+ JsonObjectWriter::~JsonObjectWriter() {
+ if ( !m_active ) { return; }
+
+ m_os << '\n';
+ JsonUtils::indent( m_os, m_indent_level );
+ m_os << '}';
+ }
+
+ JsonValueWriter JsonObjectWriter::write( StringRef key ) {
+ JsonUtils::appendCommaNewline(
+ m_os, m_should_comma, m_indent_level + 1 );
+
+ m_os << '"' << key << "\": ";
+ return JsonValueWriter{ m_os, m_indent_level + 1 };
+ }
+
+ JsonArrayWriter::JsonArrayWriter( std::ostream& os ):
+ JsonArrayWriter{ os, 0 } {}
+ JsonArrayWriter::JsonArrayWriter( std::ostream& os,
+ std::uint64_t indent_level ):
+ m_os{ os }, m_indent_level{ indent_level } {
+ m_os << '[';
+ }
+ JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ):
+ m_os{ source.m_os },
+ m_indent_level{ source.m_indent_level },
+ m_should_comma{ source.m_should_comma },
+ m_active{ source.m_active } {
+ source.m_active = false;
+ }
+ JsonArrayWriter::~JsonArrayWriter() {
+ if ( !m_active ) { return; }
+
+ m_os << '\n';
+ JsonUtils::indent( m_os, m_indent_level );
+ m_os << ']';
+ }
+
+ JsonObjectWriter JsonArrayWriter::writeObject() {
+ JsonUtils::appendCommaNewline(
+ m_os, m_should_comma, m_indent_level + 1 );
+ return JsonObjectWriter{ m_os, m_indent_level + 1 };
+ }
+
+ JsonArrayWriter JsonArrayWriter::writeArray() {
+ JsonUtils::appendCommaNewline(
+ m_os, m_should_comma, m_indent_level + 1 );
+ return JsonArrayWriter{ m_os, m_indent_level + 1 };
+ }
+
+ JsonArrayWriter& JsonArrayWriter::write( bool value ) {
+ return writeImpl( value );
+ }
+
+ JsonValueWriter::JsonValueWriter( std::ostream& os ):
+ JsonValueWriter{ os, 0 } {}
+
+ JsonValueWriter::JsonValueWriter( std::ostream& os,
+ std::uint64_t indent_level ):
+ m_os{ os }, m_indent_level{ indent_level } {}
+
+ JsonObjectWriter JsonValueWriter::writeObject() && {
+ return JsonObjectWriter{ m_os, m_indent_level };
+ }
+
+ JsonArrayWriter JsonValueWriter::writeArray() && {
+ return JsonArrayWriter{ m_os, m_indent_level };
+ }
+
+ void JsonValueWriter::write( Catch::StringRef value ) && {
+ writeImpl( value, true );
+ }
+
+ void JsonValueWriter::write( bool value ) && {
+ writeImpl( value ? "true"_sr : "false"_sr, false );
+ }
+
+ void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
+ if ( quote ) { m_os << '"'; }
+ for (char c : value) {
+ // Escape list taken from https://www.json.org/json-en.html,
+ // string definition.
+ // Note that while forward slash _can_ be escaped, it does
+ // not have to be, if JSON is not further embedded somewhere
+ // where forward slash is meaningful.
+ if ( c == '"' ) {
+ m_os << "\\\"";
+ } else if ( c == '\\' ) {
+ m_os << "\\\\";
+ } else if ( c == '\b' ) {
+ m_os << "\\b";
+ } else if ( c == '\f' ) {
+ m_os << "\\f";
+ } else if ( c == '\n' ) {
+ m_os << "\\n";
+ } else if ( c == '\r' ) {
+ m_os << "\\r";
+ } else if ( c == '\t' ) {
+ m_os << "\\t";
+ } else {
+ m_os << c;
+ }
+ }
+ if ( quote ) { m_os << '"'; }
+ }
+
+} // namespace Catch
diff --git a/src/catch2/internal/catch_jsonwriter.hpp b/src/catch2/internal/catch_jsonwriter.hpp
new file mode 100644
index 00000000..59c044e4
--- /dev/null
+++ b/src/catch2/internal/catch_jsonwriter.hpp
@@ -0,0 +1,120 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_JSONWRITER_HPP_INCLUDED
+#define CATCH_JSONWRITER_HPP_INCLUDED
+
+#include <catch2/internal/catch_reusable_string_stream.hpp>
+#include <catch2/internal/catch_stringref.hpp>
+
+#include <cstdint>
+#include <sstream>
+
+namespace Catch {
+ class JsonObjectWriter;
+ class JsonArrayWriter;
+
+ struct JsonUtils {
+ static void indent( std::ostream& os, std::uint64_t level );
+ static void appendCommaNewline( std::ostream& os,
+ bool& should_comma,
+ std::uint64_t level );
+ };
+
+ class JsonValueWriter {
+ public:
+ JsonValueWriter( std::ostream& os );
+ JsonValueWriter( std::ostream& os, std::uint64_t indent_level );
+
+ JsonObjectWriter writeObject() &&;
+ JsonArrayWriter writeArray() &&;
+
+ template <typename T>
+ void write( T const& value ) && {
+ writeImpl( value, !std::is_arithmetic<T>::value );
+ }
+ void write( StringRef value ) &&;
+ void write( bool value ) &&;
+
+ private:
+ void writeImpl( StringRef value, bool quote );
+
+ // Without this SFINAE, this overload is a better match
+ // for `std::string`, `char const*`, `char const[N]` args.
+ // While it would still work, it would cause code bloat
+ // and multiple iteration over the strings
+ template <typename T,
+ typename = typename std::enable_if_t<
+ !std::is_convertible<T, StringRef>::value>>
+ void writeImpl( T const& value, bool quote_value ) {
+ m_sstream << value;
+ writeImpl( m_sstream.str(), quote_value );
+ }
+
+ std::ostream& m_os;
+ std::stringstream m_sstream;
+ std::uint64_t m_indent_level;
+ };
+
+ class JsonObjectWriter {
+ public:
+ JsonObjectWriter( std::ostream& os );
+ JsonObjectWriter( std::ostream& os, std::uint64_t indent_level );
+
+ JsonObjectWriter( JsonObjectWriter&& source );
+ JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
+
+ ~JsonObjectWriter();
+
+ JsonValueWriter write( StringRef key );
+
+ private:
+ std::ostream& m_os;
+ std::uint64_t m_indent_level;
+ bool m_should_comma = false;
+ bool m_active = true;
+ };
+
+ class JsonArrayWriter {
+ public:
+ JsonArrayWriter( std::ostream& os );
+ JsonArrayWriter( std::ostream& os, std::uint64_t indent_level );
+
+ JsonArrayWriter( JsonArrayWriter&& source );
+ JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
+
+ ~JsonArrayWriter();
+
+ JsonObjectWriter writeObject();
+ JsonArrayWriter writeArray();
+
+ template <typename T>
+ JsonArrayWriter& write( T const& value ) {
+ return writeImpl( value );
+ }
+
+ JsonArrayWriter& write( bool value );
+
+ private:
+ template <typename T>
+ JsonArrayWriter& writeImpl( T const& value ) {
+ JsonUtils::appendCommaNewline(
+ m_os, m_should_comma, m_indent_level + 1 );
+ JsonValueWriter{ m_os }.write( value );
+
+ return *this;
+ }
+
+ std::ostream& m_os;
+ std::uint64_t m_indent_level;
+ bool m_should_comma = false;
+ bool m_active = true;
+ };
+
+} // namespace Catch
+
+#endif // CATCH_JSONWRITER_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_leak_detector.cpp b/src/catch2/internal/catch_leak_detector.cpp
index 7389eaf7..691bc772 100644
--- a/src/catch2/internal/catch_leak_detector.cpp
+++ b/src/catch2/internal/catch_leak_detector.cpp
@@ -29,7 +29,7 @@ namespace Catch {
#else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv
- Catch::LeakDetector::LeakDetector() {}
+ Catch::LeakDetector::LeakDetector() = default;
#endif // CATCH_CONFIG_WINDOWS_CRTDBG
diff --git a/src/catch2/internal/catch_list.cpp b/src/catch2/internal/catch_list.cpp
index 263781d6..97e4c593 100644
--- a/src/catch2/internal/catch_list.cpp
+++ b/src/catch2/internal/catch_list.cpp
@@ -9,9 +9,9 @@
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
-#include <catch2/interfaces/catch_interfaces_testcase.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
+#include <catch2/internal/catch_test_case_registry_impl.hpp>
+#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
@@ -54,7 +54,7 @@ namespace Catch {
void listReporters(IEventListener& reporter) {
std::vector<ReporterDescription> descriptions;
- IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+ auto const& factories = getRegistryHub().getReporterRegistry().getFactories();
descriptions.reserve(factories.size());
for (auto const& fac : factories) {
descriptions.push_back({ fac.first, fac.second->getDescription() });
diff --git a/src/catch2/internal/catch_message_info.hpp b/src/catch2/internal/catch_message_info.hpp
index d2658429..1ef43fda 100644
--- a/src/catch2/internal/catch_message_info.hpp
+++ b/src/catch2/internal/catch_message_info.hpp
@@ -10,7 +10,7 @@
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
-#include <catch2/interfaces/catch_interfaces_capture.hpp>
+#include <catch2/internal/catch_stringref.hpp>
#include <string>
diff --git a/src/catch2/internal/catch_optional.hpp b/src/catch2/internal/catch_optional.hpp
index ac3714ee..d1e953ad 100644
--- a/src/catch2/internal/catch_optional.hpp
+++ b/src/catch2/internal/catch_optional.hpp
@@ -8,6 +8,8 @@
#ifndef CATCH_OPTIONAL_HPP_INCLUDED
#define CATCH_OPTIONAL_HPP_INCLUDED
+#include <catch2/internal/catch_move_and_forward.hpp>
+
#include <cassert>
namespace Catch {
@@ -16,35 +18,50 @@ namespace Catch {
template<typename T>
class Optional {
public:
- Optional() : nullableValue( nullptr ) {}
- Optional( T const& _value )
- : nullableValue( new( storage ) T( _value ) )
- {}
- Optional( Optional const& _other )
- : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
- {}
-
- ~Optional() {
+ Optional(): nullableValue( nullptr ) {}
+ ~Optional() { reset(); }
+
+ Optional( T const& _value ):
+ nullableValue( new ( storage ) T( _value ) ) {}
+ Optional( T&& _value ):
+ nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {}
+
+ Optional& operator=( T const& _value ) {
reset();
+ nullableValue = new ( storage ) T( _value );
+ return *this;
}
+ Optional& operator=( T&& _value ) {
+ reset();
+ nullableValue = new ( storage ) T( CATCH_MOVE( _value ) );
+ return *this;
+ }
+
+ Optional( Optional const& _other ):
+ nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {}
+ Optional( Optional&& _other ):
+ nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) )
+ : nullptr ) {}
- Optional& operator= ( Optional const& _other ) {
- if( &_other != this ) {
+ Optional& operator=( Optional const& _other ) {
+ if ( &_other != this ) {
reset();
- if( _other )
- nullableValue = new( storage ) T( *_other );
+ if ( _other ) { nullableValue = new ( storage ) T( *_other ); }
}
return *this;
}
- Optional& operator = ( T const& _value ) {
- reset();
- nullableValue = new( storage ) T( _value );
+ Optional& operator=( Optional&& _other ) {
+ if ( &_other != this ) {
+ reset();
+ if ( _other ) {
+ nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) );
+ }
+ }
return *this;
}
void reset() {
- if( nullableValue )
- nullableValue->~T();
+ if ( nullableValue ) { nullableValue->~T(); }
nullableValue = nullptr;
}
@@ -91,7 +108,7 @@ namespace Catch {
}
private:
- T *nullableValue;
+ T* nullableValue;
alignas(alignof(T)) char storage[sizeof(T)];
};
diff --git a/src/catch2/internal/catch_parse_numbers.cpp b/src/catch2/internal/catch_parse_numbers.cpp
index 390b8c87..d949ac19 100644
--- a/src/catch2/internal/catch_parse_numbers.cpp
+++ b/src/catch2/internal/catch_parse_numbers.cpp
@@ -6,12 +6,12 @@
// SPDX-License-Identifier: BSL-1.0
-#include <catch2/internal/catch_parse_numbers.hpp>
-
#include <catch2/internal/catch_compiler_capabilities.hpp>
+#include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <limits>
+#include <stdexcept>
namespace Catch {
@@ -39,11 +39,14 @@ namespace Catch {
return {};
}
return static_cast<unsigned int>(ret);
- } CATCH_CATCH_ANON( std::exception const& ) {
- // There was a larger issue with the input, e.g. the parsed
- // number would be too large to fit within ull.
- return {};
}
+ CATCH_CATCH_ANON( std::invalid_argument const& ) {
+ // no conversion could be performed
+ }
+ CATCH_CATCH_ANON( std::out_of_range const& ) {
+ // the input does not fit into an unsigned long long
+ }
+ return {};
}
} // namespace Catch
diff --git a/src/catch2/internal/catch_polyfills.cpp b/src/catch2/internal/catch_polyfills.cpp
index 96efad5d..776c2243 100644
--- a/src/catch2/internal/catch_polyfills.cpp
+++ b/src/catch2/internal/catch_polyfills.cpp
@@ -31,4 +31,12 @@ namespace Catch {
}
#endif
+#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER )
+ float nextafter( float x, float y ) { return std::nextafter( x, y ); }
+ double nextafter( double x, double y ) { return std::nextafter( x, y ); }
+#else
+ float nextafter( float x, float y ) { return ::nextafterf( x, y ); }
+ double nextafter( double x, double y ) { return ::nextafter( x, y ); }
+#endif
+
} // end namespace Catch
diff --git a/src/catch2/internal/catch_polyfills.hpp b/src/catch2/internal/catch_polyfills.hpp
index 23a9332b..4503f8f2 100644
--- a/src/catch2/internal/catch_polyfills.hpp
+++ b/src/catch2/internal/catch_polyfills.hpp
@@ -9,8 +9,13 @@
#define CATCH_POLYFILLS_HPP_INCLUDED
namespace Catch {
+
bool isnan(float f);
bool isnan(double d);
+
+ float nextafter(float x, float y);
+ double nextafter(double x, double y);
+
}
#endif // CATCH_POLYFILLS_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_preprocessor_internal_stringify.hpp b/src/catch2/internal/catch_preprocessor_internal_stringify.hpp
new file mode 100644
index 00000000..2fd64e1c
--- /dev/null
+++ b/src/catch2/internal/catch_preprocessor_internal_stringify.hpp
@@ -0,0 +1,19 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
+#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
+
+#include <catch2/catch_user_config.hpp>
+
+#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
+ #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr
+#else
+ #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr
+#endif
+
+#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_random_floating_point_helpers.hpp b/src/catch2/internal/catch_random_floating_point_helpers.hpp
new file mode 100644
index 00000000..c59c0539
--- /dev/null
+++ b/src/catch2/internal/catch_random_floating_point_helpers.hpp
@@ -0,0 +1,94 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
+#define CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
+
+#include <catch2/internal/catch_polyfills.hpp>
+
+#include <cassert>
+#include <cmath>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+namespace Catch {
+
+ namespace Detail {
+ /**
+ * Returns the largest magnitude of 1-ULP distance inside the [a, b] range.
+ *
+ * Assumes `a < b`.
+ */
+ template <typename FloatType>
+ FloatType gamma(FloatType a, FloatType b) {
+ static_assert( std::is_floating_point<FloatType>::value,
+ "gamma returns the largest ULP magnitude within "
+ "floating point range [a, b]. This only makes sense "
+ "for floating point types" );
+ assert( a <= b );
+
+ const auto gamma_up = Catch::nextafter( a, std::numeric_limits<FloatType>::infinity() ) - a;
+ const auto gamma_down = b - Catch::nextafter( b, -std::numeric_limits<FloatType>::infinity() );
+
+ return gamma_up < gamma_down ? gamma_down : gamma_up;
+ }
+
+ template <typename FloatingPoint>
+ struct DistanceTypePicker;
+ template <>
+ struct DistanceTypePicker<float> {
+ using type = std::uint32_t;
+ };
+ template <>
+ struct DistanceTypePicker<double> {
+ using type = std::uint64_t;
+ };
+
+ template <typename T>
+ using DistanceType = typename DistanceTypePicker<T>::type;
+
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ /**
+ * Computes the number of equi-distant floats in [a, b]
+ *
+ * Since not every range can be split into equidistant floats
+ * exactly, we actually compute ceil(b/distance - a/distance),
+ * because in those cases we want to overcount.
+ *
+ * Uses modified Dekker's FastTwoSum algorithm to handle rounding.
+ */
+ template <typename FloatType>
+ DistanceType<FloatType>
+ count_equidistant_floats( FloatType a, FloatType b, FloatType distance ) {
+ assert( a <= b );
+ // We get distance as gamma for our uniform float distribution,
+ // so this will round perfectly.
+ const auto ag = a / distance;
+ const auto bg = b / distance;
+
+ const auto s = bg - ag;
+ const auto err = ( std::fabs( a ) <= std::fabs( b ) )
+ ? -ag - ( s - bg )
+ : bg - ( s + ag );
+ const auto ceil_s = static_cast<DistanceType<FloatType>>( std::ceil( s ) );
+
+ return ( ceil_s != s ) ? ceil_s : ceil_s + ( err > 0 );
+ }
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic pop
+#endif
+
+ }
+
+} // end namespace Catch
+
+#endif // CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_random_integer_helpers.hpp b/src/catch2/internal/catch_random_integer_helpers.hpp
new file mode 100644
index 00000000..1c450f05
--- /dev/null
+++ b/src/catch2/internal/catch_random_integer_helpers.hpp
@@ -0,0 +1,202 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
+#define CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
+
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include <type_traits>
+
+namespace Catch {
+ namespace Detail {
+
+ template <std::size_t>
+ struct SizedUnsignedType;
+#define SizedUnsignedTypeHelper( TYPE ) \
+ template <> \
+ struct SizedUnsignedType<sizeof( TYPE )> { \
+ using type = TYPE; \
+ }
+
+ SizedUnsignedTypeHelper( std::uint8_t );
+ SizedUnsignedTypeHelper( std::uint16_t );
+ SizedUnsignedTypeHelper( std::uint32_t );
+ SizedUnsignedTypeHelper( std::uint64_t );
+#undef SizedUnsignedTypeHelper
+
+ template <std::size_t sz>
+ using SizedUnsignedType_t = typename SizedUnsignedType<sz>::type;
+
+ template <typename T>
+ using DoubleWidthUnsignedType_t = SizedUnsignedType_t<2 * sizeof( T )>;
+
+ template <typename T>
+ struct ExtendedMultResult {
+ T upper;
+ T lower;
+ friend bool operator==( ExtendedMultResult const& lhs,
+ ExtendedMultResult const& rhs ) {
+ return lhs.upper == rhs.upper && lhs.lower == rhs.lower;
+ }
+ };
+
+ // Returns 128 bit result of multiplying lhs and rhs
+ constexpr ExtendedMultResult<std::uint64_t>
+ extendedMult( std::uint64_t lhs, std::uint64_t rhs ) {
+ // We use the simple long multiplication approach for
+ // correctness, we can use platform specific builtins
+ // for performance later.
+
+ // Split the lhs and rhs into two 32bit "digits", so that we can
+ // do 64 bit arithmetic to handle carry bits.
+ // 32b 32b 32b 32b
+ // lhs L1 L2
+ // * rhs R1 R2
+ // ------------------------
+ // | R2 * L2 |
+ // | R2 * L1 |
+ // | R1 * L2 |
+ // | R1 * L1 |
+ // -------------------------
+ // | a | b | c | d |
+
+#define CarryBits( x ) ( x >> 32 )
+#define Digits( x ) ( x & 0xFF'FF'FF'FF )
+
+ auto r2l2 = Digits( rhs ) * Digits( lhs );
+ auto r2l1 = Digits( rhs ) * CarryBits( lhs );
+ auto r1l2 = CarryBits( rhs ) * Digits( lhs );
+ auto r1l1 = CarryBits( rhs ) * CarryBits( lhs );
+
+ // Sum to columns first
+ auto d = Digits( r2l2 );
+ auto c = CarryBits( r2l2 ) + Digits( r2l1 ) + Digits( r1l2 );
+ auto b = CarryBits( r2l1 ) + CarryBits( r1l2 ) + Digits( r1l1 );
+ auto a = CarryBits( r1l1 );
+
+ // Propagate carries between columns
+ c += CarryBits( d );
+ b += CarryBits( c );
+ a += CarryBits( b );
+
+ // Remove the used carries
+ c = Digits( c );
+ b = Digits( b );
+ a = Digits( a );
+
+#undef CarryBits
+#undef Digits
+
+ return {
+ a << 32 | b, // upper 64 bits
+ c << 32 | d // lower 64 bits
+ };
+ }
+
+ template <typename UInt>
+ constexpr ExtendedMultResult<UInt> extendedMult( UInt lhs, UInt rhs ) {
+ static_assert( std::is_unsigned<UInt>::value,
+ "extendedMult can only handle unsigned integers" );
+ static_assert( sizeof( UInt ) < sizeof( std::uint64_t ),
+ "Generic extendedMult can only handle types smaller "
+ "than uint64_t" );
+ using WideType = DoubleWidthUnsignedType_t<UInt>;
+
+ auto result = WideType( lhs ) * WideType( rhs );
+ return {
+ static_cast<UInt>( result >> ( CHAR_BIT * sizeof( UInt ) ) ),
+ static_cast<UInt>( result & UInt( -1 ) ) };
+ }
+
+
+ template <typename TargetType,
+ typename Generator>
+ std::enable_if_t<sizeof(typename Generator::result_type) >= sizeof(TargetType),
+ TargetType> fillBitsFrom(Generator& gen) {
+ using gresult_type = typename Generator::result_type;
+ static_assert( std::is_unsigned<TargetType>::value, "Only unsigned integers are supported" );
+ static_assert( Generator::min() == 0 &&
+ Generator::max() == static_cast<gresult_type>( -1 ),
+ "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" );
+
+ // We want to return the top bits from a generator, as they are
+ // usually considered higher quality.
+ constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT;
+ constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT;
+
+ return static_cast<TargetType>( gen() >>
+ ( generated_bits - return_bits) );
+ }
+
+ template <typename TargetType,
+ typename Generator>
+ std::enable_if_t<sizeof(typename Generator::result_type) < sizeof(TargetType),
+ TargetType> fillBitsFrom(Generator& gen) {
+ using gresult_type = typename Generator::result_type;
+ static_assert( std::is_unsigned<TargetType>::value,
+ "Only unsigned integers are supported" );
+ static_assert( Generator::min() == 0 &&
+ Generator::max() == static_cast<gresult_type>( -1 ),
+ "Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" );
+
+ constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT;
+ constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT;
+ std::size_t filled_bits = 0;
+ TargetType ret = 0;
+ do {
+ ret <<= generated_bits;
+ ret |= gen();
+ filled_bits += generated_bits;
+ } while ( filled_bits < return_bits );
+
+ return ret;
+ }
+
+ /*
+ * Transposes numbers into unsigned type while keeping their ordering
+ *
+ * This means that signed types are changed so that the ordering is
+ * [INT_MIN, ..., -1, 0, ..., INT_MAX], rather than order we would
+ * get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1])
+ */
+ template <typename OriginalType, typename UnsignedType>
+ std::enable_if_t<std::is_signed<OriginalType>::value, UnsignedType>
+ transposeToNaturalOrder( UnsignedType in ) {
+ static_assert(
+ sizeof( OriginalType ) == sizeof( UnsignedType ),
+ "reordering requires the same sized types on both sides" );
+ static_assert( std::is_unsigned<UnsignedType>::value,
+ "Input type must be unsigned" );
+ // Assuming 2s complement (standardized in current C++), the
+ // positive and negative numbers are already internally ordered,
+ // and their difference is in the top bit. Swapping it orders
+ // them the desired way.
+ constexpr auto highest_bit =
+ UnsignedType( 1 ) << ( sizeof( UnsignedType ) * CHAR_BIT - 1 );
+ return static_cast<UnsignedType>( in ^ highest_bit );
+ }
+
+
+
+ template <typename OriginalType,
+ typename UnsignedType>
+ std::enable_if_t<std::is_unsigned<OriginalType>::value, UnsignedType>
+ transposeToNaturalOrder(UnsignedType in) {
+ static_assert(
+ sizeof( OriginalType ) == sizeof( UnsignedType ),
+ "reordering requires the same sized types on both sides" );
+ static_assert( std::is_unsigned<UnsignedType>::value, "Input type must be unsigned" );
+ // No reordering is needed for unsigned -> unsigned
+ return in;
+ }
+ } // namespace Detail
+} // namespace Catch
+
+#endif // CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_random_seed_generation.cpp b/src/catch2/internal/catch_random_seed_generation.cpp
index 40c468cb..fdc3fa19 100644
--- a/src/catch2/internal/catch_random_seed_generation.cpp
+++ b/src/catch2/internal/catch_random_seed_generation.cpp
@@ -9,6 +9,7 @@
#include <catch2/internal/catch_random_seed_generation.hpp>
#include <catch2/internal/catch_enforce.hpp>
+#include <catch2/internal/catch_random_integer_helpers.hpp>
#include <ctime>
#include <random>
@@ -21,10 +22,10 @@ namespace Catch {
return static_cast<std::uint32_t>( std::time( nullptr ) );
case GenerateFrom::Default:
- case GenerateFrom::RandomDevice:
- // In theory, a platform could have random_device that returns just
- // 16 bits. That is still some randomness, so we don't care too much
- return static_cast<std::uint32_t>( std::random_device{}() );
+ case GenerateFrom::RandomDevice: {
+ std::random_device rd;
+ return Detail::fillBitsFrom<std::uint32_t>( rd );
+ }
default:
CATCH_ERROR("Unknown generation method");
diff --git a/src/catch2/internal/catch_reporter_registry.cpp b/src/catch2/internal/catch_reporter_registry.cpp
index 4c0c44f4..cea8c4dc 100644
--- a/src/catch2/internal/catch_reporter_registry.cpp
+++ b/src/catch2/internal/catch_reporter_registry.cpp
@@ -5,61 +5,87 @@
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
-#include <catch2/internal/catch_reporter_registry.hpp>
-#include <catch2/reporters/catch_reporter_registrars.hpp>
+#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
+#include <catch2/internal/catch_enforce.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
+#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/reporters/catch_reporter_automake.hpp>
#include <catch2/reporters/catch_reporter_compact.hpp>
#include <catch2/reporters/catch_reporter_console.hpp>
+#include <catch2/reporters/catch_reporter_json.hpp>
#include <catch2/reporters/catch_reporter_junit.hpp>
+#include <catch2/reporters/catch_reporter_registrars.hpp>
#include <catch2/reporters/catch_reporter_sonarqube.hpp>
#include <catch2/reporters/catch_reporter_tap.hpp>
#include <catch2/reporters/catch_reporter_teamcity.hpp>
#include <catch2/reporters/catch_reporter_xml.hpp>
-#include <catch2/internal/catch_move_and_forward.hpp>
-#include <catch2/internal/catch_enforce.hpp>
namespace Catch {
+ struct ReporterRegistry::ReporterRegistryImpl {
+ std::vector<Detail::unique_ptr<EventListenerFactory>> listeners;
+ std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>
+ factories;
+ };
- ReporterRegistry::ReporterRegistry() {
+ ReporterRegistry::ReporterRegistry():
+ m_impl( Detail::make_unique<ReporterRegistryImpl>() ) {
// Because it is impossible to move out of initializer list,
// we have to add the elements manually
- m_factories["Automake"] = Detail::make_unique<ReporterFactory<AutomakeReporter>>();
- m_factories["compact"] = Detail::make_unique<ReporterFactory<CompactReporter>>();
- m_factories["console"] = Detail::make_unique<ReporterFactory<ConsoleReporter>>();
- m_factories["JUnit"] = Detail::make_unique<ReporterFactory<JunitReporter>>();
- m_factories["SonarQube"] = Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
- m_factories["TAP"] = Detail::make_unique<ReporterFactory<TAPReporter>>();
- m_factories["TeamCity"] = Detail::make_unique<ReporterFactory<TeamCityReporter>>();
- m_factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>();
+ m_impl->factories["Automake"] =
+ Detail::make_unique<ReporterFactory<AutomakeReporter>>();
+ m_impl->factories["compact"] =
+ Detail::make_unique<ReporterFactory<CompactReporter>>();
+ m_impl->factories["console"] =
+ Detail::make_unique<ReporterFactory<ConsoleReporter>>();
+ m_impl->factories["JUnit"] =
+ Detail::make_unique<ReporterFactory<JunitReporter>>();
+ m_impl->factories["SonarQube"] =
+ Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
+ m_impl->factories["TAP"] =
+ Detail::make_unique<ReporterFactory<TAPReporter>>();
+ m_impl->factories["TeamCity"] =
+ Detail::make_unique<ReporterFactory<TeamCityReporter>>();
+ m_impl->factories["XML"] =
+ Detail::make_unique<ReporterFactory<XmlReporter>>();
+ m_impl->factories["JSON"] =
+ Detail::make_unique<ReporterFactory<JsonReporter>>();
}
ReporterRegistry::~ReporterRegistry() = default;
-
- IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const {
- auto it = m_factories.find( name );
- if( it == m_factories.end() )
- return nullptr;
- return it->second->create( CATCH_MOVE(config) );
+ IEventListenerPtr
+ ReporterRegistry::create( std::string const& name,
+ ReporterConfig&& config ) const {
+ auto it = m_impl->factories.find( name );
+ if ( it == m_impl->factories.end() ) return nullptr;
+ return it->second->create( CATCH_MOVE( config ) );
}
- void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) {
+ void ReporterRegistry::registerReporter( std::string const& name,
+ IReporterFactoryPtr factory ) {
CATCH_ENFORCE( name.find( "::" ) == name.npos,
- "'::' is not allowed in reporter name: '" + name + '\'' );
- auto ret = m_factories.emplace(name, CATCH_MOVE(factory));
- CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" );
+ "'::' is not allowed in reporter name: '" + name +
+ '\'' );
+ auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) );
+ CATCH_ENFORCE( ret.second,
+ "reporter using '" + name +
+ "' as name was already registered" );
}
void ReporterRegistry::registerListener(
Detail::unique_ptr<EventListenerFactory> factory ) {
- m_listeners.push_back( CATCH_MOVE(factory) );
+ m_impl->listeners.push_back( CATCH_MOVE( factory ) );
}
- IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
- return m_factories;
- }
- IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
- return m_listeners;
+ std::map<std::string,
+ IReporterFactoryPtr,
+ Detail::CaseInsensitiveLess> const&
+ ReporterRegistry::getFactories() const {
+ return m_impl->factories;
}
-}
+ std::vector<Detail::unique_ptr<EventListenerFactory>> const&
+ ReporterRegistry::getListeners() const {
+ return m_impl->listeners;
+ }
+} // namespace Catch
diff --git a/src/catch2/internal/catch_reporter_registry.hpp b/src/catch2/internal/catch_reporter_registry.hpp
index 5577b9ef..92a88927 100644
--- a/src/catch2/internal/catch_reporter_registry.hpp
+++ b/src/catch2/internal/catch_reporter_registry.hpp
@@ -8,31 +8,48 @@
#ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED
#define CATCH_REPORTER_REGISTRY_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
+#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
#include <map>
+#include <string>
+#include <vector>
namespace Catch {
- class ReporterRegistry : public IReporterRegistry {
- public:
+ class IEventListener;
+ using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
+ class IReporterFactory;
+ using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
+ struct ReporterConfig;
+ class EventListenerFactory;
+
+ class ReporterRegistry {
+ struct ReporterRegistryImpl;
+ Detail::unique_ptr<ReporterRegistryImpl> m_impl;
+ public:
ReporterRegistry();
- ~ReporterRegistry() override; // = default, out of line to allow fwd decl
+ ~ReporterRegistry(); // = default;
- IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override;
+ IEventListenerPtr create( std::string const& name,
+ ReporterConfig&& config ) const;
- void registerReporter( std::string const& name, IReporterFactoryPtr factory );
- void registerListener( Detail::unique_ptr<EventListenerFactory> factory );
+ void registerReporter( std::string const& name,
+ IReporterFactoryPtr factory );
- FactoryMap const& getFactories() const override;
- Listeners const& getListeners() const override;
+ void
+ registerListener( Detail::unique_ptr<EventListenerFactory> factory );
- private:
- FactoryMap m_factories;
- Listeners m_listeners;
+ std::map<std::string,
+ IReporterFactoryPtr,
+ Detail::CaseInsensitiveLess> const&
+ getFactories() const;
+
+ std::vector<Detail::unique_ptr<EventListenerFactory>> const&
+ getListeners() const;
};
-}
+
+} // end namespace Catch
#endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_reporter_spec_parser.cpp b/src/catch2/internal/catch_reporter_spec_parser.cpp
index f6591d9a..8b88b170 100644
--- a/src/catch2/internal/catch_reporter_spec_parser.cpp
+++ b/src/catch2/internal/catch_reporter_spec_parser.cpp
@@ -21,9 +21,9 @@ namespace Catch {
};
kvPair splitKVPair(StringRef kvString) {
- auto splitPos = static_cast<size_t>( std::distance(
- kvString.begin(),
- std::find( kvString.begin(), kvString.end(), '=' ) ) );
+ auto splitPos = static_cast<size_t>(
+ std::find( kvString.begin(), kvString.end(), '=' ) -
+ kvString.begin() );
return { kvString.substr( 0, splitPos ),
kvString.substr( splitPos + 1, kvString.size() ) };
diff --git a/src/catch2/internal/catch_result_type.hpp b/src/catch2/internal/catch_result_type.hpp
index faf0683d..e66afaff 100644
--- a/src/catch2/internal/catch_result_type.hpp
+++ b/src/catch2/internal/catch_result_type.hpp
@@ -16,6 +16,8 @@ namespace Catch {
Ok = 0,
Info = 1,
Warning = 2,
+ // TODO: Should explicit skip be considered "not OK" (cf. isOk)? I.e., should it have the failure bit?
+ ExplicitSkip = 4,
FailureBit = 0x10,
diff --git a/src/catch2/internal/catch_run_context.cpp b/src/catch2/internal/catch_run_context.cpp
index d2e8fb8c..e568100d 100644
--- a/src/catch2/internal/catch_run_context.cpp
+++ b/src/catch2/internal/catch_run_context.cpp
@@ -8,8 +8,9 @@
#include <catch2/internal/catch_run_context.hpp>
#include <catch2/catch_user_config.hpp>
-#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
+#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_enforce.hpp>
@@ -19,6 +20,7 @@
#include <catch2/internal/catch_output_redirect.hpp>
#include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
+#include <catch2/internal/catch_result_type.hpp>
#include <cassert>
#include <algorithm>
@@ -26,149 +28,152 @@
namespace Catch {
namespace Generators {
- struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
- GeneratorBasePtr m_generator;
-
- GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
- : TrackerBase( nameAndLocation, ctx, parent )
- {}
- ~GeneratorTracker() override;
-
- static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
- GeneratorTracker* tracker;
-
- ITracker& currentTracker = ctx.currentTracker();
- // Under specific circumstances, the generator we want
- // to acquire is also the current tracker. If this is
- // the case, we have to avoid looking through current
- // tracker's children, and instead return the current
- // tracker.
- // A case where this check is important is e.g.
- // for (int i = 0; i < 5; ++i) {
- // int n = GENERATE(1, 2);
- // }
- //
- // without it, the code above creates 5 nested generators.
- if ( currentTracker.nameAndLocation() == nameAndLocation ) {
- auto thisTracker =
- currentTracker.parent()->findChild( nameAndLocation );
- assert( thisTracker );
- assert( thisTracker->isGeneratorTracker() );
- tracker = static_cast<GeneratorTracker*>( thisTracker );
- } else if ( ITracker* childTracker =
- currentTracker.findChild( nameAndLocation ) ) {
- assert( childTracker );
- assert( childTracker->isGeneratorTracker() );
- tracker = static_cast<GeneratorTracker*>( childTracker );
- } else {
- auto newTracker =
- Catch::Detail::make_unique<GeneratorTracker>(
- nameAndLocation, ctx, &currentTracker );
- tracker = newTracker.get();
- currentTracker.addChild( CATCH_MOVE(newTracker) );
- }
-
- if( !tracker->isComplete() ) {
- tracker->open();
- }
+ namespace {
+ struct GeneratorTracker final : TestCaseTracking::TrackerBase,
+ IGeneratorTracker {
+ GeneratorBasePtr m_generator;
+
+ GeneratorTracker(
+ TestCaseTracking::NameAndLocation&& nameAndLocation,
+ TrackerContext& ctx,
+ ITracker* parent ):
+ TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {}
+ ~GeneratorTracker() override = default;
+
+ static GeneratorTracker*
+ acquire( TrackerContext& ctx,
+ TestCaseTracking::NameAndLocationRef const&
+ nameAndLocation ) {
+ GeneratorTracker* tracker;
+
+ ITracker& currentTracker = ctx.currentTracker();
+ // Under specific circumstances, the generator we want
+ // to acquire is also the current tracker. If this is
+ // the case, we have to avoid looking through current
+ // tracker's children, and instead return the current
+ // tracker.
+ // A case where this check is important is e.g.
+ // for (int i = 0; i < 5; ++i) {
+ // int n = GENERATE(1, 2);
+ // }
+ //
+ // without it, the code above creates 5 nested generators.
+ if ( currentTracker.nameAndLocation() == nameAndLocation ) {
+ auto thisTracker = currentTracker.parent()->findChild(
+ nameAndLocation );
+ assert( thisTracker );
+ assert( thisTracker->isGeneratorTracker() );
+ tracker = static_cast<GeneratorTracker*>( thisTracker );
+ } else if ( ITracker* childTracker =
+ currentTracker.findChild(
+ nameAndLocation ) ) {
+ assert( childTracker );
+ assert( childTracker->isGeneratorTracker() );
+ tracker =
+ static_cast<GeneratorTracker*>( childTracker );
+ } else {
+ return nullptr;
+ }
- return *tracker;
- }
+ if ( !tracker->isComplete() ) { tracker->open(); }
- // TrackerBase interface
- bool isGeneratorTracker() const override { return true; }
- auto hasGenerator() const -> bool override {
- return !!m_generator;
- }
- void close() override {
- TrackerBase::close();
- // If a generator has a child (it is followed by a section)
- // and none of its children have started, then we must wait
- // until later to start consuming its values.
- // This catches cases where `GENERATE` is placed between two
- // `SECTION`s.
- // **The check for m_children.empty cannot be removed**.
- // doing so would break `GENERATE` _not_ followed by `SECTION`s.
- const bool should_wait_for_child = [&]() {
- // No children -> nobody to wait for
- if ( m_children.empty() ) {
- return false;
- }
- // If at least one child started executing, don't wait
- if ( std::find_if(
- m_children.begin(),
- m_children.end(),
- []( TestCaseTracking::ITrackerPtr const& tracker ) {
- return tracker->hasStarted();
- } ) != m_children.end() ) {
- return false;
- }
+ return tracker;
+ }
- // No children have started. We need to check if they _can_
- // start, and thus we should wait for them, or they cannot
- // start (due to filters), and we shouldn't wait for them
- ITracker* parent = m_parent;
- // This is safe: there is always at least one section
- // tracker in a test case tracking tree
- while ( !parent->isSectionTracker() ) {
- parent = parent->parent();
- }
- assert( parent &&
- "Missing root (test case) level section" );
-
- auto const& parentSection =
- static_cast<SectionTracker const&>( *parent );
- auto const& filters = parentSection.getFilters();
- // No filters -> no restrictions on running sections
- if ( filters.empty() ) {
- return true;
- }
+ // TrackerBase interface
+ bool isGeneratorTracker() const override { return true; }
+ auto hasGenerator() const -> bool override {
+ return !!m_generator;
+ }
+ void close() override {
+ TrackerBase::close();
+ // If a generator has a child (it is followed by a section)
+ // and none of its children have started, then we must wait
+ // until later to start consuming its values.
+ // This catches cases where `GENERATE` is placed between two
+ // `SECTION`s.
+ // **The check for m_children.empty cannot be removed**.
+ // doing so would break `GENERATE` _not_ followed by
+ // `SECTION`s.
+ const bool should_wait_for_child = [&]() {
+ // No children -> nobody to wait for
+ if ( m_children.empty() ) { return false; }
+ // If at least one child started executing, don't wait
+ if ( std::find_if(
+ m_children.begin(),
+ m_children.end(),
+ []( TestCaseTracking::ITrackerPtr const&
+ tracker ) {
+ return tracker->hasStarted();
+ } ) != m_children.end() ) {
+ return false;
+ }
- for ( auto const& child : m_children ) {
- if ( child->isSectionTracker() &&
- std::find(
- filters.begin(),
- filters.end(),
- static_cast<SectionTracker const&>( *child )
- .trimmedName() ) != filters.end() ) {
- return true;
+ // No children have started. We need to check if they
+ // _can_ start, and thus we should wait for them, or
+ // they cannot start (due to filters), and we shouldn't
+ // wait for them
+ ITracker* parent = m_parent;
+ // This is safe: there is always at least one section
+ // tracker in a test case tracking tree
+ while ( !parent->isSectionTracker() ) {
+ parent = parent->parent();
}
+ assert( parent &&
+ "Missing root (test case) level section" );
+
+ auto const& parentSection =
+ static_cast<SectionTracker const&>( *parent );
+ auto const& filters = parentSection.getFilters();
+ // No filters -> no restrictions on running sections
+ if ( filters.empty() ) { return true; }
+
+ for ( auto const& child : m_children ) {
+ if ( child->isSectionTracker() &&
+ std::find( filters.begin(),
+ filters.end(),
+ static_cast<SectionTracker const&>(
+ *child )
+ .trimmedName() ) !=
+ filters.end() ) {
+ return true;
+ }
+ }
+ return false;
+ }();
+
+ // This check is a bit tricky, because m_generator->next()
+ // has a side-effect, where it consumes generator's current
+ // value, but we do not want to invoke the side-effect if
+ // this generator is still waiting for any child to start.
+ assert( m_generator && "Tracker without generator" );
+ if ( should_wait_for_child ||
+ ( m_runState == CompletedSuccessfully &&
+ m_generator->countedNext() ) ) {
+ m_children.clear();
+ m_runState = Executing;
}
- return false;
- }();
-
- // This check is a bit tricky, because m_generator->next()
- // has a side-effect, where it consumes generator's current
- // value, but we do not want to invoke the side-effect if
- // this generator is still waiting for any child to start.
- if ( should_wait_for_child ||
- ( m_runState == CompletedSuccessfully &&
- m_generator->countedNext() ) ) {
- m_children.clear();
- m_runState = Executing;
}
- }
- // IGeneratorTracker interface
- auto getGenerator() const -> GeneratorBasePtr const& override {
- return m_generator;
- }
- void setGenerator( GeneratorBasePtr&& generator ) override {
- m_generator = CATCH_MOVE( generator );
- }
- };
- GeneratorTracker::~GeneratorTracker() = default;
+ // IGeneratorTracker interface
+ auto getGenerator() const -> GeneratorBasePtr const& override {
+ return m_generator;
+ }
+ void setGenerator( GeneratorBasePtr&& generator ) override {
+ m_generator = CATCH_MOVE( generator );
+ }
+ };
+ } // namespace
}
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
: m_runInfo(_config->name()),
- m_context(getCurrentMutableContext()),
m_config(_config),
m_reporter(CATCH_MOVE(reporter)),
m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
{
- m_context.setResultCapture(this);
+ getCurrentMutableContext().setResultCapture( this );
m_reporter->testRunStarting(m_runInfo);
}
@@ -179,13 +184,8 @@ namespace Catch {
Totals RunContext::runTest(TestCaseHandle const& testCase) {
const Totals prevTotals = m_totals;
- std::string redirectedCout;
- std::string redirectedCerr;
-
auto const& testInfo = testCase.getTestCaseInfo();
-
m_reporter->testCaseStarting(testInfo);
-
m_activeTestCase = &testCase;
@@ -227,9 +227,11 @@ namespace Catch {
seedRng( *m_config );
uint64_t testRuns = 0;
+ std::string redirectedCout;
+ std::string redirectedCerr;
do {
m_trackerContext.startCycle();
- m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
+ m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo));
m_reporter->testCasePartialStarting(testInfo, testRuns);
@@ -240,7 +242,7 @@ namespace Catch {
redirectedCerr += oneRunCerr;
const auto singleRunTotals = m_totals.delta(beforeRunTotals);
- auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting());
+ auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
++testRuns;
@@ -255,8 +257,8 @@ namespace Catch {
m_totals.testCases += deltaTotals.testCases;
m_reporter->testCaseEnded(TestCaseStats(testInfo,
deltaTotals,
- redirectedCout,
- redirectedCerr,
+ CATCH_MOVE(redirectedCout),
+ CATCH_MOVE(redirectedCerr),
aborting()));
m_activeTestCase = nullptr;
@@ -266,10 +268,13 @@ namespace Catch {
}
- void RunContext::assertionEnded(AssertionResult const & result) {
+ void RunContext::assertionEnded(AssertionResult&& result) {
if (result.getResultType() == ResultWas::Ok) {
m_totals.assertions.passed++;
m_lastAssertionPassed = true;
+ } else if (result.getResultType() == ResultWas::ExplicitSkip) {
+ m_totals.assertions.skipped++;
+ m_lastAssertionPassed = true;
} else if (!result.succeeded()) {
m_lastAssertionPassed = false;
if (result.isOk()) {
@@ -285,24 +290,37 @@ namespace Catch {
m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
- if (result.getResultType() != ResultWas::Warning)
+ if ( result.getResultType() != ResultWas::Warning ) {
m_messageScopes.clear();
+ }
- // Reset working state
- resetAssertionInfo();
- m_lastResult = result;
+ // Reset working state. assertion info will be reset after
+ // populateReaction is run if it is needed
+ m_lastResult = CATCH_MOVE( result );
}
void RunContext::resetAssertionInfo() {
m_lastAssertionInfo.macroName = StringRef();
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
+ m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal;
+ }
+
+ void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
+ m_reporter->assertionStarting( info );
}
- bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
- ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
+ bool RunContext::sectionStarted( StringRef sectionName,
+ SourceLineInfo const& sectionLineInfo,
+ Counts& assertions ) {
+ ITracker& sectionTracker =
+ SectionTracker::acquire( m_trackerContext,
+ TestCaseTracking::NameAndLocationRef(
+ sectionName, sectionLineInfo ) );
+
if (!sectionTracker.isOpen())
return false;
m_activeSections.push_back(&sectionTracker);
+ SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) );
m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
m_reporter->sectionStarting(sectionInfo);
@@ -311,14 +329,39 @@ namespace Catch {
return true;
}
- auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+ IGeneratorTracker*
+ RunContext::acquireGeneratorTracker( StringRef generatorName,
+ SourceLineInfo const& lineInfo ) {
using namespace Generators;
- GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
- TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
+ GeneratorTracker* tracker = GeneratorTracker::acquire(
+ m_trackerContext,
+ TestCaseTracking::NameAndLocationRef(
+ generatorName, lineInfo ) );
m_lastAssertionInfo.lineInfo = lineInfo;
return tracker;
}
+ IGeneratorTracker* RunContext::createGeneratorTracker(
+ StringRef generatorName,
+ SourceLineInfo lineInfo,
+ Generators::GeneratorBasePtr&& generator ) {
+
+ auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo );
+ auto& currentTracker = m_trackerContext.currentTracker();
+ assert(
+ currentTracker.nameAndLocation() != nameAndLoc &&
+ "Trying to create tracker for a genreator that already has one" );
+
+ auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>(
+ CATCH_MOVE(nameAndLoc), m_trackerContext, &currentTracker );
+ auto ret = newTracker.get();
+ currentTracker.addChild( CATCH_MOVE( newTracker ) );
+
+ ret->setGenerator( CATCH_MOVE( generator ) );
+ ret->open();
+ return ret;
+ }
+
bool RunContext::testForMissingAssertions(Counts& assertions) {
if (assertions.total() != 0)
return false;
@@ -331,7 +374,7 @@ namespace Catch {
return true;
}
- void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
+ void RunContext::sectionEnded(SectionEndInfo&& endInfo) {
Counts assertions = m_totals.assertions - endInfo.prevAssertions;
bool missingAssertions = testForMissingAssertions(assertions);
@@ -340,19 +383,20 @@ namespace Catch {
m_activeSections.pop_back();
}
- m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
+ m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions));
m_messages.clear();
m_messageScopes.clear();
}
- void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
- if (m_unfinishedSections.empty())
+ void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) {
+ if ( m_unfinishedSections.empty() ) {
m_activeSections.back()->fail();
- else
+ } else {
m_activeSections.back()->close();
+ }
m_activeSections.pop_back();
- m_unfinishedSections.push_back(endInfo);
+ m_unfinishedSections.push_back(CATCH_MOVE(endInfo));
}
void RunContext::benchmarkPreparing( StringRef name ) {
@@ -376,8 +420,8 @@ namespace Catch {
m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
}
- void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
- m_messageScopes.emplace_back( builder );
+ void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
+ m_messageScopes.emplace_back( CATCH_MOVE(builder) );
}
std::string RunContext::getCurrentTestName() const {
@@ -402,9 +446,10 @@ namespace Catch {
// Instead, fake a result data.
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
tempResult.message = static_cast<std::string>(message);
- AssertionResult result(m_lastAssertionInfo, tempResult);
+ AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
- assertionEnded(result);
+ assertionEnded(CATCH_MOVE(result) );
+ resetAssertionInfo();
handleUnfinishedSections();
@@ -414,7 +459,7 @@ namespace Catch {
Counts assertions;
assertions.failed = 1;
- SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
+ SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false);
m_reporter->sectionEnded(testCaseSectionStats);
auto const& testInfo = m_activeTestCase->getTestCaseInfo();
@@ -475,6 +520,8 @@ namespace Catch {
duration = timer.getElapsedSeconds();
} CATCH_CATCH_ANON (TestFailureException&) {
// This just means the test was aborted due to failure
+ } CATCH_CATCH_ANON (TestSkipException&) {
+ // This just means the test was explicitly skipped
} CATCH_CATCH_ALL {
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// are reported without translation at the point of origin.
@@ -491,7 +538,7 @@ namespace Catch {
m_messages.clear();
m_messageScopes.clear();
- SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
+ SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats);
}
@@ -515,7 +562,7 @@ namespace Catch {
itEnd = m_unfinishedSections.rend();
it != itEnd;
++it)
- sectionEnded(*it);
+ sectionEnded(CATCH_MOVE(*it));
m_unfinishedSections.clear();
}
@@ -524,8 +571,6 @@ namespace Catch {
ITransientExpression const& expr,
AssertionReaction& reaction
) {
- m_reporter->assertionStarting( info );
-
bool negated = isFalseTest( info.resultDisposition );
bool result = expr.getResult() != negated;
@@ -541,6 +586,7 @@ namespace Catch {
reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
populateReaction( reaction );
}
+ resetAssertionInfo();
}
void RunContext::reportExpr(
AssertionInfo const &info,
@@ -551,10 +597,10 @@ namespace Catch {
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( negated ) );
- AssertionResult assertionResult{ info, data };
+ AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
- assertionEnded( assertionResult );
+ assertionEnded( CATCH_MOVE(assertionResult) );
}
void RunContext::handleMessage(
@@ -563,16 +609,23 @@ namespace Catch {
StringRef message,
AssertionReaction& reaction
) {
- m_reporter->assertionStarting( info );
-
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) );
data.message = static_cast<std::string>(message);
- AssertionResult assertionResult{ m_lastAssertionInfo, data };
- assertionEnded( assertionResult );
- if( !assertionResult.isOk() )
+ AssertionResult assertionResult{ m_lastAssertionInfo,
+ CATCH_MOVE( data ) };
+
+ const auto isOk = assertionResult.isOk();
+ assertionEnded( CATCH_MOVE(assertionResult) );
+ if ( !isOk ) {
populateReaction( reaction );
+ } else if ( resultType == ResultWas::ExplicitSkip ) {
+ // TODO: Need to handle this explicitly, as ExplicitSkip is
+ // considered "OK"
+ reaction.shouldSkip = true;
+ }
+ resetAssertionInfo();
}
void RunContext::handleUnexpectedExceptionNotThrown(
AssertionInfo const& info,
@@ -583,16 +636,17 @@ namespace Catch {
void RunContext::handleUnexpectedInflightException(
AssertionInfo const& info,
- std::string const& message,
+ std::string&& message,
AssertionReaction& reaction
) {
m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
- data.message = message;
- AssertionResult assertionResult{ info, data };
- assertionEnded( assertionResult );
+ data.message = CATCH_MOVE(message);
+ AssertionResult assertionResult{ info, CATCH_MOVE(data) };
+ assertionEnded( CATCH_MOVE(assertionResult) );
populateReaction( reaction );
+ resetAssertionInfo();
}
void RunContext::populateReaction( AssertionReaction& reaction ) {
@@ -603,12 +657,14 @@ namespace Catch {
void RunContext::handleIncomplete(
AssertionInfo const& info
) {
+ using namespace std::string_literals;
m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
- data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
- AssertionResult assertionResult{ info, data };
- assertionEnded( assertionResult );
+ data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
+ AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
+ assertionEnded( CATCH_MOVE(assertionResult) );
+ resetAssertionInfo();
}
void RunContext::handleNonExpr(
AssertionInfo const &info,
@@ -618,11 +674,12 @@ namespace Catch {
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) );
- AssertionResult assertionResult{ info, data };
- assertionEnded( assertionResult );
+ AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
- if( !assertionResult.isOk() )
- populateReaction( reaction );
+ const auto isOk = assertionResult.isOk();
+ assertionEnded( CATCH_MOVE(assertionResult) );
+ if ( !isOk ) { populateReaction( reaction ); }
+ resetAssertionInfo();
}
diff --git a/src/catch2/internal/catch_run_context.hpp b/src/catch2/internal/catch_run_context.hpp
index ab20db57..c749304d 100644
--- a/src/catch2/internal/catch_run_context.hpp
+++ b/src/catch2/internal/catch_run_context.hpp
@@ -8,8 +8,9 @@
#ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED
#define CATCH_RUN_CONTEXT_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
+#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_test_registry.hpp>
+#include <catch2/internal/catch_test_run_info.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/catch_message.hpp>
@@ -24,13 +25,14 @@
namespace Catch {
- class IMutableContext;
class IGeneratorTracker;
class IConfig;
+ class IEventListener;
+ using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
///////////////////////////////////////////////////////////////////////////
- class RunContext : public IResultCapture {
+ class RunContext final : public IResultCapture {
public:
RunContext( RunContext const& ) = delete;
@@ -59,7 +61,7 @@ namespace Catch {
AssertionReaction& reaction ) override;
void handleUnexpectedInflightException
( AssertionInfo const& info,
- std::string const& message,
+ std::string&& message,
AssertionReaction& reaction ) override;
void handleIncomplete
( AssertionInfo const& info ) override;
@@ -68,12 +70,22 @@ namespace Catch {
ResultWas::OfType resultType,
AssertionReaction &reaction ) override;
- bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
+ void notifyAssertionStarted( AssertionInfo const& info ) override;
+ bool sectionStarted( StringRef sectionName,
+ SourceLineInfo const& sectionLineInfo,
+ Counts& assertions ) override;
- void sectionEnded( SectionEndInfo const& endInfo ) override;
- void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
+ void sectionEnded( SectionEndInfo&& endInfo ) override;
+ void sectionEndedEarly( SectionEndInfo&& endInfo ) override;
+
+ IGeneratorTracker*
+ acquireGeneratorTracker( StringRef generatorName,
+ SourceLineInfo const& lineInfo ) override;
+ IGeneratorTracker* createGeneratorTracker(
+ StringRef generatorName,
+ SourceLineInfo lineInfo,
+ Generators::GeneratorBasePtr&& generator ) override;
- auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
void benchmarkPreparing( StringRef name ) override;
void benchmarkStarting( BenchmarkInfo const& info ) override;
@@ -83,7 +95,7 @@ namespace Catch {
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
- void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
+ void emplaceUnscopedMessage( MessageBuilder&& builder ) override;
std::string getCurrentTestName() const override;
@@ -109,7 +121,7 @@ namespace Catch {
void resetAssertionInfo();
bool testForMissingAssertions( Counts& assertions );
- void assertionEnded( AssertionResult const& result );
+ void assertionEnded( AssertionResult&& result );
void reportExpr
( AssertionInfo const &info,
ResultWas::OfType resultType,
@@ -123,7 +135,6 @@ namespace Catch {
void handleUnfinishedSections();
TestRunInfo m_runInfo;
- IMutableContext& m_context;
TestCaseHandle const* m_activeTestCase = nullptr;
ITracker* m_testCaseTracker = nullptr;
Optional<AssertionResult> m_lastResult;
diff --git a/src/catch2/internal/catch_section.cpp b/src/catch2/internal/catch_section.cpp
index ca2e5d8c..061732b1 100644
--- a/src/catch2/internal/catch_section.cpp
+++ b/src/catch2/internal/catch_section.cpp
@@ -15,7 +15,7 @@ namespace Catch {
Section::Section( SectionInfo&& info ):
m_info( CATCH_MOVE( info ) ),
m_sectionIncluded(
- getResultCapture().sectionStarted( m_info, m_assertions ) ) {
+ getResultCapture().sectionStarted( m_info.name, m_info.lineInfo, m_assertions ) ) {
// Non-"included" sections will not use the timing information
// anyway, so don't bother with the potential syscall.
if (m_sectionIncluded) {
@@ -23,13 +23,31 @@ namespace Catch {
}
}
+ Section::Section( SourceLineInfo const& _lineInfo,
+ StringRef _name,
+ const char* const ):
+ m_info( { "invalid", static_cast<std::size_t>( -1 ) }, std::string{} ),
+ m_sectionIncluded(
+ getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) {
+ // We delay initialization the SectionInfo member until we know
+ // this section needs it, so we avoid allocating std::string for name.
+ // We also delay timer start to avoid the potential syscall unless we
+ // will actually use the result.
+ if ( m_sectionIncluded ) {
+ m_info.name = static_cast<std::string>( _name );
+ m_info.lineInfo = _lineInfo;
+ m_timer.start();
+ }
+ }
+
Section::~Section() {
if( m_sectionIncluded ) {
- SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() };
- if( uncaught_exceptions() )
- getResultCapture().sectionEndedEarly( endInfo );
- else
- getResultCapture().sectionEnded( endInfo );
+ SectionEndInfo endInfo{ CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds() };
+ if ( uncaught_exceptions() ) {
+ getResultCapture().sectionEndedEarly( CATCH_MOVE(endInfo) );
+ } else {
+ getResultCapture().sectionEnded( CATCH_MOVE( endInfo ) );
+ }
}
}
diff --git a/src/catch2/internal/catch_section.hpp b/src/catch2/internal/catch_section.hpp
index abd12192..8c894eeb 100644
--- a/src/catch2/internal/catch_section.hpp
+++ b/src/catch2/internal/catch_section.hpp
@@ -9,6 +9,7 @@
#define CATCH_SECTION_HPP_INCLUDED
#include <catch2/internal/catch_compiler_capabilities.hpp>
+#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/catch_section_info.hpp>
#include <catch2/catch_timer.hpp>
@@ -20,6 +21,9 @@ namespace Catch {
class Section : Detail::NonCopyable {
public:
Section( SectionInfo&& info );
+ Section( SourceLineInfo const& _lineInfo,
+ StringRef _name,
+ const char* const = nullptr );
~Section();
// This indicates whether the section should be executed or not
@@ -35,16 +39,62 @@ namespace Catch {
} // end namespace Catch
-#define INTERNAL_CATCH_SECTION( ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
- if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
-#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
- if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
+# define INTERNAL_CATCH_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
+ catch_internal_Section ) = \
+ Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
+ catch_internal_Section ) = \
+ Catch::SectionInfo( \
+ CATCH_INTERNAL_LINEINFO, \
+ ( Catch::ReusableStringStream() << __VA_ARGS__ ) \
+ .str() ) ) \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#else
+
+// These section definitions imply that at most one section at one level
+// will be intered (because only one section's __LINE__ can be equal to
+// the dummy `catchInternalSectionHint` variable from `TEST_CASE`).
+
+namespace Catch {
+ namespace Detail {
+ // Intentionally without linkage, as it should only be used as a dummy
+ // symbol for static analysis.
+ int GetNewSectionHint();
+ } // namespace Detail
+} // namespace Catch
+
+
+# define INTERNAL_CATCH_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
+ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \
+ catchInternalSectionHint, \
+ catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
+ catchInternalPreviousSectionHint == __LINE__ ) \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
+ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \
+ catchInternalSectionHint, \
+ catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
+ catchInternalPreviousSectionHint == __LINE__ ) \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+#endif
+
#endif // CATCH_SECTION_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_stream_end_stop.hpp b/src/catch2/internal/catch_stream_end_stop.hpp
index 61379f20..66d678cf 100644
--- a/src/catch2/internal/catch_stream_end_stop.hpp
+++ b/src/catch2/internal/catch_stream_end_stop.hpp
@@ -17,10 +17,10 @@ namespace Catch {
// as well as
// << stuff +StreamEndStop
struct StreamEndStop {
- StringRef operator+() const { return StringRef(); }
+ constexpr StringRef operator+() const { return StringRef(); }
template <typename T>
- friend T const& operator+( T const& value, StreamEndStop ) {
+ constexpr friend T const& operator+( T const& value, StreamEndStop ) {
return value;
}
};
diff --git a/src/catch2/internal/catch_string_manip.cpp b/src/catch2/internal/catch_string_manip.cpp
index cb96dd4f..0c889ca1 100644
--- a/src/catch2/internal/catch_string_manip.cpp
+++ b/src/catch2/internal/catch_string_manip.cpp
@@ -8,7 +8,6 @@
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_stringref.hpp>
-#include <algorithm>
#include <ostream>
#include <cstring>
#include <cctype>
@@ -32,9 +31,9 @@ namespace Catch {
return s.find( infix ) != std::string::npos;
}
void toLowerInPlace( std::string& s ) {
- std::transform( s.begin(), s.end(), s.begin(), []( char c ) {
- return toLower( c );
- } );
+ for ( char& c : s ) {
+ c = toLower( c );
+ }
}
std::string toLower( std::string const& s ) {
std::string lc = s;
diff --git a/src/catch2/internal/catch_string_manip.hpp b/src/catch2/internal/catch_string_manip.hpp
index 8630b56a..dc0c552c 100644
--- a/src/catch2/internal/catch_string_manip.hpp
+++ b/src/catch2/internal/catch_string_manip.hpp
@@ -10,6 +10,7 @@
#include <catch2/internal/catch_stringref.hpp>
+#include <cstdint>
#include <string>
#include <iosfwd>
#include <vector>
diff --git a/src/catch2/internal/catch_stringref.cpp b/src/catch2/internal/catch_stringref.cpp
index 668ff297..232498eb 100644
--- a/src/catch2/internal/catch_stringref.cpp
+++ b/src/catch2/internal/catch_stringref.cpp
@@ -17,10 +17,6 @@ namespace Catch {
: StringRef( rawChars, std::strlen(rawChars) )
{}
- auto StringRef::operator == ( StringRef other ) const noexcept -> bool {
- return m_size == other.m_size
- && (std::memcmp( m_start, other.m_start, m_size ) == 0);
- }
bool StringRef::operator<(StringRef rhs) const noexcept {
if (m_size < rhs.m_size) {
diff --git a/src/catch2/internal/catch_stringref.hpp b/src/catch2/internal/catch_stringref.hpp
index bd8971bc..99bb9a98 100644
--- a/src/catch2/internal/catch_stringref.hpp
+++ b/src/catch2/internal/catch_stringref.hpp
@@ -13,6 +13,8 @@
#include <iosfwd>
#include <cassert>
+#include <cstring>
+
namespace Catch {
/// A non-owning string class (similar to the forthcoming std::string_view)
@@ -49,7 +51,10 @@ namespace Catch {
}
public: // operators
- auto operator == ( StringRef other ) const noexcept -> bool;
+ auto operator == ( StringRef other ) const noexcept -> bool {
+ return m_size == other.m_size
+ && (std::memcmp( m_start, other.m_start, m_size ) == 0);
+ }
auto operator != (StringRef other) const noexcept -> bool {
return !(*this == other);
}
diff --git a/src/catch2/internal/catch_tag_alias_registry.cpp b/src/catch2/internal/catch_tag_alias_registry.cpp
index b7c6b9ec..4fd0d557 100644
--- a/src/catch2/internal/catch_tag_alias_registry.cpp
+++ b/src/catch2/internal/catch_tag_alias_registry.cpp
@@ -13,7 +13,7 @@
namespace Catch {
- TagAliasRegistry::~TagAliasRegistry() {}
+ TagAliasRegistry::~TagAliasRegistry() = default;
TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
auto it = m_registry.find( alias );
diff --git a/src/catch2/internal/catch_template_test_registry.hpp b/src/catch2/internal/catch_template_test_registry.hpp
index 88599f32..0ea354bc 100644
--- a/src/catch2/internal/catch_template_test_registry.hpp
+++ b/src/catch2/internal/catch_template_test_registry.hpp
@@ -181,7 +181,7 @@
void reg_tests() { \
size_t index = 0; \
using expander = size_t[]; \
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
} \
};\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
@@ -316,7 +316,7 @@
void reg_tests(){\
size_t index = 0;\
using expander = size_t[];\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
}\
};\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
diff --git a/src/catch2/internal/catch_test_case_registry_impl.cpp b/src/catch2/internal/catch_test_case_registry_impl.cpp
index 4b3d2e47..f1702979 100644
--- a/src/catch2/internal/catch_test_case_registry_impl.cpp
+++ b/src/catch2/internal/catch_test_case_registry_impl.cpp
@@ -9,6 +9,7 @@
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_enforce.hpp>
+#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/internal/catch_run_context.hpp>
@@ -23,6 +24,38 @@
namespace Catch {
+ namespace {
+ static void enforceNoDuplicateTestCases(
+ std::vector<TestCaseHandle> const& tests ) {
+ auto testInfoCmp = []( TestCaseInfo const* lhs,
+ TestCaseInfo const* rhs ) {
+ return *lhs < *rhs;
+ };
+ std::set<TestCaseInfo const*, decltype( testInfoCmp )&> seenTests(
+ testInfoCmp );
+ for ( auto const& test : tests ) {
+ const auto infoPtr = &test.getTestCaseInfo();
+ const auto prev = seenTests.insert( infoPtr );
+ CATCH_ENFORCE( prev.second,
+ "error: test case \""
+ << infoPtr->name << "\", with tags \""
+ << infoPtr->tagsAsString()
+ << "\" already defined.\n"
+ << "\tFirst seen at "
+ << ( *prev.first )->lineInfo << "\n"
+ << "\tRedefined at " << infoPtr->lineInfo );
+ }
+ }
+
+ static bool matchTest( TestCaseHandle const& testCase,
+ TestSpec const& testSpec,
+ IConfig const& config ) {
+ return testSpec.matches( testCase.getTestCaseInfo() ) &&
+ isThrowSafe( testCase, config );
+ }
+
+ } // end unnamed namespace
+
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) {
switch (config.runOrder()) {
case TestRunOrder::Declared:
@@ -79,29 +112,6 @@ namespace Catch {
return !testCase.getTestCaseInfo().throws() || config.allowThrows();
}
- bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) {
- return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config );
- }
-
- void
- enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& tests ) {
- auto testInfoCmp = []( TestCaseInfo const* lhs,
- TestCaseInfo const* rhs ) {
- return *lhs < *rhs;
- };
- std::set<TestCaseInfo const*, decltype(testInfoCmp) &> seenTests(testInfoCmp);
- for ( auto const& test : tests ) {
- const auto infoPtr = &test.getTestCaseInfo();
- const auto prev = seenTests.insert( infoPtr );
- CATCH_ENFORCE(
- prev.second,
- "error: test case \"" << infoPtr->name << "\", with tags \""
- << infoPtr->tagsAsString() << "\" already defined.\n"
- << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n"
- << "\tRedefined at " << infoPtr->lineInfo );
- }
- }
-
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
std::vector<TestCaseHandle> filtered;
filtered.reserve( testCases.size() );
@@ -142,11 +152,4 @@ namespace Catch {
return m_sortedFunctions;
}
-
-
- ///////////////////////////////////////////////////////////////////////////
- void TestInvokerAsFunction::invoke() const {
- m_testAsFunction();
- }
-
} // end namespace Catch
diff --git a/src/catch2/internal/catch_test_case_registry_impl.hpp b/src/catch2/internal/catch_test_case_registry_impl.hpp
index 228dbb79..a4a27ed1 100644
--- a/src/catch2/internal/catch_test_case_registry_impl.hpp
+++ b/src/catch2/internal/catch_test_case_registry_impl.hpp
@@ -8,23 +8,22 @@
#ifndef CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
#define CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
-#include <catch2/internal/catch_test_registry.hpp>
+#include <catch2/interfaces/catch_interfaces_testcase.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
#include <vector>
namespace Catch {
- class TestCaseHandle;
class IConfig;
+ class ITestInvoker;
+ class TestCaseHandle;
class TestSpec;
std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases );
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
- bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
-
- void enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& functions );
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
@@ -53,18 +52,6 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////
- class TestInvokerAsFunction final : public ITestInvoker {
- using TestType = void(*)();
- TestType m_testAsFunction;
- public:
- TestInvokerAsFunction(TestType testAsFunction) noexcept:
- m_testAsFunction(testAsFunction) {}
-
- void invoke() const override;
- };
-
- ///////////////////////////////////////////////////////////////////////////
-
} // end namespace Catch
diff --git a/src/catch2/internal/catch_test_case_tracker.cpp b/src/catch2/internal/catch_test_case_tracker.cpp
index e230fac1..1470b91c 100644
--- a/src/catch2/internal/catch_test_case_tracker.cpp
+++ b/src/catch2/internal/catch_test_case_tracker.cpp
@@ -22,8 +22,8 @@
namespace Catch {
namespace TestCaseTracking {
- NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
- : name( _name ),
+ NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location )
+ : name( CATCH_MOVE(_name) ),
location( _location )
{}
@@ -38,14 +38,17 @@ namespace TestCaseTracking {
m_children.push_back( CATCH_MOVE(child) );
}
- ITracker* ITracker::findChild( NameAndLocation const& nameAndLocation ) {
+ ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) {
auto it = std::find_if(
m_children.begin(),
m_children.end(),
[&nameAndLocation]( ITrackerPtr const& tracker ) {
- return tracker->nameAndLocation().location ==
- nameAndLocation.location &&
- tracker->nameAndLocation().name == nameAndLocation.name;
+ auto const& tnameAndLoc = tracker->nameAndLocation();
+ if ( tnameAndLoc.location.line !=
+ nameAndLocation.location.line ) {
+ return false;
+ }
+ return tnameAndLoc == nameAndLocation;
} );
return ( it != m_children.end() ) ? it->get() : nullptr;
}
@@ -53,10 +56,6 @@ namespace TestCaseTracking {
bool ITracker::isSectionTracker() const { return false; }
bool ITracker::isGeneratorTracker() const { return false; }
- bool ITracker::isSuccessfullyCompleted() const {
- return m_runState == CompletedSuccessfully;
- }
-
bool ITracker::isOpen() const {
return m_runState != NotStarted && !isComplete();
}
@@ -83,16 +82,6 @@ namespace TestCaseTracking {
return *m_rootTracker;
}
- void TrackerContext::endRun() {
- m_rootTracker.reset();
- m_currentTracker = nullptr;
- m_runState = NotStarted;
- }
-
- void TrackerContext::startCycle() {
- m_currentTracker = m_rootTracker.get();
- m_runState = Executing;
- }
void TrackerContext::completeCycle() {
m_runState = CompletedCycle;
}
@@ -100,16 +89,13 @@ namespace TestCaseTracking {
bool TrackerContext::completedCycle() const {
return m_runState == CompletedCycle;
}
- ITracker& TrackerContext::currentTracker() {
- return *m_currentTracker;
- }
void TrackerContext::setCurrentTracker( ITracker* tracker ) {
m_currentTracker = tracker;
}
- TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
- ITracker(nameAndLocation, parent),
+ TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
+ ITracker(CATCH_MOVE(nameAndLocation), parent),
m_ctx( ctx )
{}
@@ -169,13 +155,14 @@ namespace TestCaseTracking {
m_ctx.setCurrentTracker( this );
}
- SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
- : TrackerBase( nameAndLocation, ctx, parent ),
- m_trimmed_name(trim(nameAndLocation.name))
+ SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+ : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
+ m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name)))
{
if( parent ) {
- while( !parent->isSectionTracker() )
+ while ( !parent->isSectionTracker() ) {
parent = parent->parent();
+ }
SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
addNextFilters( parentSection.m_filters );
@@ -195,24 +182,30 @@ namespace TestCaseTracking {
bool SectionTracker::isSectionTracker() const { return true; }
- SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
- SectionTracker* section;
+ SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) {
+ SectionTracker* tracker;
ITracker& currentTracker = ctx.currentTracker();
if ( ITracker* childTracker =
currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isSectionTracker() );
- section = static_cast<SectionTracker*>( childTracker );
+ tracker = static_cast<SectionTracker*>( childTracker );
} else {
- auto newSection = Catch::Detail::make_unique<SectionTracker>(
- nameAndLocation, ctx, &currentTracker );
- section = newSection.get();
- currentTracker.addChild( CATCH_MOVE( newSection ) );
+ auto newTracker = Catch::Detail::make_unique<SectionTracker>(
+ NameAndLocation{ static_cast<std::string>(nameAndLocation.name),
+ nameAndLocation.location },
+ ctx,
+ &currentTracker );
+ tracker = newTracker.get();
+ currentTracker.addChild( CATCH_MOVE( newTracker ) );
+ }
+
+ if ( !ctx.completedCycle() ) {
+ tracker->tryOpen();
}
- if( !ctx.completedCycle() )
- section->tryOpen();
- return *section;
+
+ return *tracker;
}
void SectionTracker::tryOpen() {
@@ -233,10 +226,6 @@ namespace TestCaseTracking {
m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
}
- std::vector<StringRef> const& SectionTracker::getFilters() const {
- return m_filters;
- }
-
StringRef SectionTracker::trimmedName() const {
return m_trimmed_name;
}
diff --git a/src/catch2/internal/catch_test_case_tracker.hpp b/src/catch2/internal/catch_test_case_tracker.hpp
index 005b1d8e..50278c91 100644
--- a/src/catch2/internal/catch_test_case_tracker.hpp
+++ b/src/catch2/internal/catch_test_case_tracker.hpp
@@ -22,10 +22,49 @@ namespace TestCaseTracking {
std::string name;
SourceLineInfo location;
- NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
+ NameAndLocation( std::string&& _name, SourceLineInfo const& _location );
friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
- return lhs.name == rhs.name
- && lhs.location == rhs.location;
+ // This is a very cheap check that should have a very high hit rate.
+ // If we get to SourceLineInfo::operator==, we will redo it, but the
+ // cost of repeating is trivial at that point (we will be paying
+ // multiple strcmp/memcmps at that point).
+ if ( lhs.location.line != rhs.location.line ) { return false; }
+ return lhs.name == rhs.name && lhs.location == rhs.location;
+ }
+ friend bool operator!=(NameAndLocation const& lhs,
+ NameAndLocation const& rhs) {
+ return !( lhs == rhs );
+ }
+ };
+
+ /**
+ * This is a variant of `NameAndLocation` that does not own the name string
+ *
+ * This avoids extra allocations when trying to locate a tracker by its
+ * name and location, as long as we make sure that trackers only keep
+ * around the owning variant.
+ */
+ struct NameAndLocationRef {
+ StringRef name;
+ SourceLineInfo location;
+
+ constexpr NameAndLocationRef( StringRef name_,
+ SourceLineInfo location_ ):
+ name( name_ ), location( location_ ) {}
+
+ friend bool operator==( NameAndLocation const& lhs,
+ NameAndLocationRef const& rhs ) {
+ // This is a very cheap check that should have a very high hit rate.
+ // If we get to SourceLineInfo::operator==, we will redo it, but the
+ // cost of repeating is trivial at that point (we will be paying
+ // multiple strcmp/memcmps at that point).
+ if ( lhs.location.line != rhs.location.line ) { return false; }
+ return StringRef( lhs.name ) == rhs.name &&
+ lhs.location == rhs.location;
+ }
+ friend bool operator==( NameAndLocationRef const& lhs,
+ NameAndLocation const& rhs ) {
+ return rhs == lhs;
}
};
@@ -53,8 +92,8 @@ namespace TestCaseTracking {
CycleState m_runState = NotStarted;
public:
- ITracker( NameAndLocation const& nameAndLoc, ITracker* parent ):
- m_nameAndLocation( nameAndLoc ),
+ ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ):
+ m_nameAndLocation( CATCH_MOVE(nameAndLoc) ),
m_parent( parent )
{}
@@ -74,8 +113,10 @@ namespace TestCaseTracking {
//! Returns true if tracker run to completion (successfully or not)
virtual bool isComplete() const = 0;
- //! Returns true if tracker run to completion succesfully
- bool isSuccessfullyCompleted() const;
+ //! Returns true if tracker run to completion successfully
+ bool isSuccessfullyCompleted() const {
+ return m_runState == CompletedSuccessfully;
+ }
//! Returns true if tracker has started but hasn't been completed
bool isOpen() const;
//! Returns true iff tracker has started
@@ -93,7 +134,7 @@ namespace TestCaseTracking {
*
* Returns nullptr if not found.
*/
- ITracker* findChild( NameAndLocation const& nameAndLocation );
+ ITracker* findChild( NameAndLocationRef const& nameAndLocation );
//! Have any children been added?
bool hasChildren() const {
return !m_children.empty();
@@ -134,13 +175,15 @@ namespace TestCaseTracking {
public:
ITracker& startRun();
- void endRun();
- void startCycle();
+ void startCycle() {
+ m_currentTracker = m_rootTracker.get();
+ m_runState = Executing;
+ }
void completeCycle();
bool completedCycle() const;
- ITracker& currentTracker();
+ ITracker& currentTracker() { return *m_currentTracker; }
void setCurrentTracker( ITracker* tracker );
};
@@ -150,7 +193,7 @@ namespace TestCaseTracking {
TrackerContext& m_ctx;
public:
- TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+ TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isComplete() const override;
@@ -166,22 +209,26 @@ namespace TestCaseTracking {
class SectionTracker : public TrackerBase {
std::vector<StringRef> m_filters;
- std::string m_trimmed_name;
+ // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`.
+ // Currently it allocates owns the name, so this is safe. If it is later refactored
+ // to not own the name, the name still has to outlive the `ITracker` parent, so
+ // this should still be safe.
+ StringRef m_trimmed_name;
public:
- SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+ SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent );
bool isSectionTracker() const override;
bool isComplete() const override;
- static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
+ static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation );
void tryOpen();
void addInitialFilters( std::vector<std::string> const& filters );
void addNextFilters( std::vector<StringRef> const& filters );
//! Returns filters active in this tracker
- std::vector<StringRef> const& getFilters() const;
+ std::vector<StringRef> const& getFilters() const { return m_filters; }
//! Returns whitespace-trimmed name of the tracked section
StringRef trimmedName() const;
};
diff --git a/src/catch2/internal/catch_test_failure_exception.cpp b/src/catch2/internal/catch_test_failure_exception.cpp
index c1edff3c..8ea31313 100644
--- a/src/catch2/internal/catch_test_failure_exception.cpp
+++ b/src/catch2/internal/catch_test_failure_exception.cpp
@@ -20,4 +20,12 @@ namespace Catch {
#endif
}
+ void throw_test_skip_exception() {
+#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
+ throw Catch::TestSkipException();
+#else
+ CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
+#endif
+ }
+
} // namespace Catch
diff --git a/src/catch2/internal/catch_test_failure_exception.hpp b/src/catch2/internal/catch_test_failure_exception.hpp
index 810a81c9..1ef88364 100644
--- a/src/catch2/internal/catch_test_failure_exception.hpp
+++ b/src/catch2/internal/catch_test_failure_exception.hpp
@@ -12,6 +12,8 @@ namespace Catch {
//! Used to signal that an assertion macro failed
struct TestFailureException{};
+ //! Used to signal that the remainder of a test should be skipped
+ struct TestSkipException {};
/**
* Outlines throwing of `TestFailureException` into a single TU
@@ -20,6 +22,13 @@ namespace Catch {
*/
[[noreturn]] void throw_test_failure_exception();
+ /**
+ * Outlines throwing of `TestSkipException` into a single TU
+ *
+ * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers.
+ */
+ [[noreturn]] void throw_test_skip_exception();
+
} // namespace Catch
#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_test_macro_impl.hpp b/src/catch2/internal/catch_test_macro_impl.hpp
index 1b4ea8e7..59c851e8 100644
--- a/src/catch2/internal/catch_test_macro_impl.hpp
+++ b/src/catch2/internal/catch_test_macro_impl.hpp
@@ -10,6 +10,7 @@
#include <catch2/catch_user_config.hpp>
#include <catch2/internal/catch_assertion_handler.hpp>
+#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
@@ -22,12 +23,6 @@
#if !defined(CATCH_CONFIG_DISABLE)
-#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
- #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__
-#else
- #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
-#endif
-
#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
///////////////////////////////////////////////////////////////////////////////
@@ -39,7 +34,7 @@
#else // CATCH_CONFIG_FAST_COMPILE
#define INTERNAL_CATCH_TRY try
-#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
+#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); }
#endif
@@ -95,6 +90,7 @@
if( catchAssertionHandler.allowThrows() ) \
try { \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
static_cast<void>(__VA_ARGS__); \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
@@ -115,6 +111,7 @@
if( catchAssertionHandler.allowThrows() ) \
try { \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
static_cast<void>(expr); \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
@@ -141,6 +138,7 @@
if( catchAssertionHandler.allowThrows() ) \
try { \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
static_cast<void>(__VA_ARGS__); \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
diff --git a/src/catch2/internal/catch_test_registry.cpp b/src/catch2/internal/catch_test_registry.cpp
index 9769ed03..e9c999fe 100644
--- a/src/catch2/internal/catch_test_registry.cpp
+++ b/src/catch2/internal/catch_test_registry.cpp
@@ -8,7 +8,6 @@
#include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/catch_test_case_info.hpp>
-#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
@@ -17,9 +16,10 @@
#include <iterator>
namespace Catch {
+ ITestInvoker::~ITestInvoker() = default;
namespace {
- StringRef extractClassName( StringRef classOrMethodName ) {
+ static StringRef extractClassName( StringRef classOrMethodName ) {
if ( !startsWith( classOrMethodName, '&' ) ) {
return classOrMethodName;
}
@@ -46,6 +46,18 @@ namespace Catch {
static_cast<std::size_t>( startIdx ),
static_cast<std::size_t>( classNameSize ) );
}
+
+ class TestInvokerAsFunction final : public ITestInvoker {
+ using TestType = void ( * )();
+ TestType m_testAsFunction;
+
+ public:
+ TestInvokerAsFunction( TestType testAsFunction ) noexcept:
+ m_testAsFunction( testAsFunction ) {}
+
+ void invoke() const override { m_testAsFunction(); }
+ };
+
} // namespace
Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) {
diff --git a/src/catch2/internal/catch_test_registry.hpp b/src/catch2/internal/catch_test_registry.hpp
index 5329a1e9..7766fe11 100644
--- a/src/catch2/internal/catch_test_registry.hpp
+++ b/src/catch2/internal/catch_test_registry.hpp
@@ -8,9 +8,10 @@
#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED
#define CATCH_TEST_REGISTRY_HPP_INCLUDED
+#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
-#include <catch2/interfaces/catch_interfaces_testcase.hpp>
+#include <catch2/interfaces/catch_interfaces_test_invoker.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_unique_name.hpp>
@@ -72,25 +73,55 @@ struct AutoReg : Detail::NonCopyable {
void TestName::test()
#endif
+
+#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
+
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
static void TestName(); \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+ namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
static void TestName()
#define INTERNAL_CATCH_TESTCASE( ... ) \
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ )
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
+
+
+// Dummy registrator for the dumy test case macros
+namespace Catch {
+ namespace Detail {
+ struct DummyUse {
+ DummyUse( void ( * )( int ) );
+ };
+ } // namespace Detail
+} // namespace Catch
+
+// Note that both the presence of the argument and its exact name are
+// necessary for the section support.
+
+// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE`
+// tests can compile. The redefined `TEST_CASE` shadows this with param.
+static int catchInternalSectionHint = 0;
+
+# define INTERNAL_CATCH_TESTCASE2( fname ) \
+ static void fname( int ); \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+ static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \
+ dummyUser )( &(fname) ); \
+ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
+ static void fname( [[maybe_unused]] int catchInternalSectionHint ) \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+# define INTERNAL_CATCH_TESTCASE( ... ) \
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ) )
+
+
+#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
@@ -101,13 +132,33 @@ struct AutoReg : Detail::NonCopyable {
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
void test(); \
}; \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
+ Catch::makeTestInvoker( &TestName::test ), \
+ CATCH_INTERNAL_LINEINFO, \
+ #ClassName##_catch_sr, \
+ Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
} \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
void TestName::test()
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
+
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
+ namespace { \
+ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
+ Catch::makeTestInvoker( &QualifiedMethod ), \
+ CATCH_INTERNAL_LINEINFO, \
+ "&" #QualifiedMethod##_catch_sr, \
+ Catch::NameAndTags{ __VA_ARGS__ } ); \
+ } /* NOLINT */ \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+
+
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
do { \
diff --git a/src/catch2/internal/catch_test_run_info.hpp b/src/catch2/internal/catch_test_run_info.hpp
new file mode 100644
index 00000000..90357b0a
--- /dev/null
+++ b/src/catch2/internal/catch_test_run_info.hpp
@@ -0,0 +1,22 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED
+#define CATCH_TEST_RUN_INFO_HPP_INCLUDED
+
+#include <catch2/internal/catch_stringref.hpp>
+
+namespace Catch {
+
+ struct TestRunInfo {
+ constexpr TestRunInfo(StringRef _name) : name(_name) {}
+ StringRef name;
+ };
+
+} // end namespace Catch
+
+#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_test_spec_parser.cpp b/src/catch2/internal/catch_test_spec_parser.cpp
index bae25475..d6e4cb58 100644
--- a/src/catch2/internal/catch_test_spec_parser.cpp
+++ b/src/catch2/internal/catch_test_spec_parser.cpp
@@ -221,10 +221,8 @@ namespace Catch {
token.erase(token.begin());
if (m_exclusion) {
m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
- m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
} else {
m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring));
- m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
}
}
if (m_exclusion) {
diff --git a/src/catch2/internal/catch_textflow.hpp b/src/catch2/internal/catch_textflow.hpp
index ceac675d..0776ab92 100644
--- a/src/catch2/internal/catch_textflow.hpp
+++ b/src/catch2/internal/catch_textflow.hpp
@@ -59,7 +59,7 @@ namespace Catch {
// Calculates the length of the current line
void calcLength();
- // Returns current indention width
+ // Returns current indentation width
size_t indentSize() const;
// Creates an indented and (optionally) suffixed string from
diff --git a/src/catch2/internal/catch_uniform_floating_point_distribution.hpp b/src/catch2/internal/catch_uniform_floating_point_distribution.hpp
new file mode 100644
index 00000000..23d03b43
--- /dev/null
+++ b/src/catch2/internal/catch_uniform_floating_point_distribution.hpp
@@ -0,0 +1,131 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
+#define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
+
+#include <catch2/internal/catch_random_floating_point_helpers.hpp>
+#include <catch2/internal/catch_uniform_integer_distribution.hpp>
+
+#include <cmath>
+#include <type_traits>
+
+namespace Catch {
+
+ namespace Detail {
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ // The issue with overflow only happens with maximal ULP and HUGE
+ // distance, e.g. when generating numbers in [-inf, inf] for given
+ // type. So we only check for the largest possible ULP in the
+ // type, and return something that does not overflow to inf in 1 mult.
+ constexpr std::uint64_t calculate_max_steps_in_one_go(double gamma) {
+ if ( gamma == 1.99584030953472e+292 ) { return 9007199254740991; }
+ return static_cast<std::uint64_t>( -1 );
+ }
+ constexpr std::uint32_t calculate_max_steps_in_one_go(float gamma) {
+ if ( gamma == 2.028241e+31f ) { return 16777215; }
+ return static_cast<std::uint32_t>( -1 );
+ }
+#if defined( __GNUC__ ) || defined( __clang__ )
+# pragma GCC diagnostic pop
+#endif
+ }
+
+/**
+ * Implementation of uniform distribution on floating point numbers.
+ *
+ * Note that we support only `float` and `double` types, because these
+ * usually mean the same thing across different platform. `long double`
+ * varies wildly by platform and thus we cannot provide reproducible
+ * implementation. Also note that we don't implement all parts of
+ * distribution per standard: this distribution is not serializable, nor
+ * can the range be arbitrarily reset.
+ *
+ * The implementation also uses different approach than the one taken by
+ * `std::uniform_real_distribution`, where instead of generating a number
+ * between [0, 1) and then multiplying the range bounds with it, we first
+ * split the [a, b] range into a set of equidistributed floating point
+ * numbers, and then use uniform int distribution to pick which one to
+ * return.
+ *
+ * This has the advantage of guaranteeing uniformity (the multiplication
+ * method loses uniformity due to rounding when multiplying floats), except
+ * for small non-uniformity at one side of the interval, where we have
+ * to deal with the fact that not every interval is splittable into
+ * equidistributed floats.
+ *
+ * Based on "Drawing random floating-point numbers from an interval" by
+ * Frederic Goualard.
+ */
+template <typename FloatType>
+class uniform_floating_point_distribution {
+ static_assert(std::is_floating_point<FloatType>::value, "...");
+ static_assert(!std::is_same<FloatType, long double>::value,
+ "We do not support long double due to inconsistent behaviour between platforms");
+
+ using WidthType = Detail::DistanceType<FloatType>;
+
+ FloatType m_a, m_b;
+ FloatType m_ulp_magnitude;
+ WidthType m_floats_in_range;
+ uniform_integer_distribution<WidthType> m_int_dist;
+
+ // In specific cases, we can overflow into `inf` when computing the
+ // `steps * g` offset. To avoid this, we don't offset by more than this
+ // in one multiply + addition.
+ WidthType m_max_steps_in_one_go;
+ // We don't want to do the magnitude check every call to `operator()`
+ bool m_a_has_leq_magnitude;
+
+public:
+ using result_type = FloatType;
+
+ uniform_floating_point_distribution( FloatType a, FloatType b ):
+ m_a( a ),
+ m_b( b ),
+ m_ulp_magnitude( Detail::gamma( m_a, m_b ) ),
+ m_floats_in_range( Detail::count_equidistant_floats( m_a, m_b, m_ulp_magnitude ) ),
+ m_int_dist(0, m_floats_in_range),
+ m_max_steps_in_one_go( Detail::calculate_max_steps_in_one_go(m_ulp_magnitude)),
+ m_a_has_leq_magnitude(std::fabs(m_a) <= std::fabs(m_b))
+ {
+ assert( a <= b );
+ }
+
+ template <typename Generator>
+ result_type operator()( Generator& g ) {
+ WidthType steps = m_int_dist( g );
+ if ( m_a_has_leq_magnitude ) {
+ if ( steps == m_floats_in_range ) { return m_a; }
+ auto b = m_b;
+ while (steps > m_max_steps_in_one_go) {
+ b -= m_max_steps_in_one_go * m_ulp_magnitude;
+ steps -= m_max_steps_in_one_go;
+ }
+ return b - steps * m_ulp_magnitude;
+ } else {
+ if ( steps == m_floats_in_range ) { return m_b; }
+ auto a = m_a;
+ while (steps > m_max_steps_in_one_go) {
+ a += m_max_steps_in_one_go * m_ulp_magnitude;
+ steps -= m_max_steps_in_one_go;
+ }
+ return a + steps * m_ulp_magnitude;
+ }
+ }
+
+ result_type a() const { return m_a; }
+ result_type b() const { return m_b; }
+};
+
+} // end namespace Catch
+
+#endif // CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_uniform_integer_distribution.hpp b/src/catch2/internal/catch_uniform_integer_distribution.hpp
new file mode 100644
index 00000000..3b623579
--- /dev/null
+++ b/src/catch2/internal/catch_uniform_integer_distribution.hpp
@@ -0,0 +1,126 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
+#define CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
+
+#include <catch2/internal/catch_random_integer_helpers.hpp>
+
+namespace Catch {
+
+ namespace Detail {
+ // Indirection to enable make_unsigned<bool> behaviour.
+ template <typename T>
+ struct make_unsigned {
+ using type = std::make_unsigned_t<T>;
+ };
+
+ template <>
+ struct make_unsigned<bool> {
+ using type = uint8_t;
+ };
+
+ template <typename T>
+ using make_unsigned_t = typename make_unsigned<T>::type;
+ }
+
+/**
+ * Implementation of uniform distribution on integers.
+ *
+ * Unlike `std::uniform_int_distribution`, this implementation supports
+ * various 1 byte integral types, including bool (but you should not
+ * actually use it for bools).
+ *
+ * The underlying algorithm is based on the one described in "Fast Random
+ * Integer Generation in an Interval" by Daniel Lemire, but has been
+ * optimized under the assumption of reuse of the same distribution object.
+ */
+template <typename IntegerType>
+class uniform_integer_distribution {
+ static_assert(std::is_integral<IntegerType>::value, "...");
+
+ using UnsignedIntegerType = Detail::make_unsigned_t<IntegerType>;
+
+ // We store the left range bound converted to internal representation,
+ // because it will be used in computation in the () operator.
+ UnsignedIntegerType m_a;
+ // After initialization, right bound is only used for the b() getter,
+ // so we keep it in the original type.
+ IntegerType m_b;
+
+ // How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type.
+ UnsignedIntegerType m_ab_distance;
+
+ // We hoisted this out of the main generation function. Technically,
+ // this means that using this distribution will be slower than Lemire's
+ // algorithm if this distribution instance will be used only few times,
+ // but it will be faster if it is used many times. Since Catch2 uses
+ // distributions only to implement random generators, we assume that each
+ // distribution will be reused many times and this is an optimization.
+ UnsignedIntegerType m_rejection_threshold = 0;
+
+ // Assumes m_b and m_a are already filled
+ UnsignedIntegerType computeDistance() const {
+ // This overflows and returns 0 if ua == 0 and ub == TYPE_MAX.
+ // We handle that later when generating the number.
+ return transposeTo(m_b) - m_a + 1;
+ }
+
+ static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) {
+ // distance == 0 means that we will return all possible values from
+ // the type's range, and that we shouldn't reject anything.
+ if ( ab_distance == 0 ) { return 0; }
+ return ( ~ab_distance + 1 ) % ab_distance;
+ }
+
+ static UnsignedIntegerType transposeTo(IntegerType in) {
+ return Detail::transposeToNaturalOrder<IntegerType>(
+ static_cast<UnsignedIntegerType>( in ) );
+ }
+ static IntegerType transposeBack(UnsignedIntegerType in) {
+ return static_cast<IntegerType>(
+ Detail::transposeToNaturalOrder<IntegerType>(in) );
+ }
+
+public:
+ using result_type = IntegerType;
+
+ uniform_integer_distribution( IntegerType a, IntegerType b ):
+ m_a( transposeTo(a) ),
+ m_b( b ),
+ m_ab_distance( computeDistance() ),
+ m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) {
+ assert( a <= b );
+ }
+
+ template <typename Generator>
+ result_type operator()( Generator& g ) {
+ // All possible values of result_type are valid.
+ if ( m_ab_distance == 0 ) {
+ return transposeBack( Detail::fillBitsFrom<UnsignedIntegerType>( g ) );
+ }
+
+ auto random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g );
+ auto emul = Detail::extendedMult( random_number, m_ab_distance );
+ // Unlike Lemire's algorithm we skip the ab_distance check, since
+ // we precomputed the rejection threshold, which is always tighter.
+ while (emul.lower < m_rejection_threshold) {
+ random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g );
+ emul = Detail::extendedMult( random_number, m_ab_distance );
+ }
+
+ return transposeBack(m_a + emul.upper);
+ }
+
+ result_type a() const { return transposeBack(m_a); }
+ result_type b() const { return m_b; }
+};
+
+} // end namespace Catch
+
+#endif // CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
diff --git a/src/catch2/internal/catch_xmlwriter.cpp b/src/catch2/internal/catch_xmlwriter.cpp
index d3bc6303..6c1d45df 100644
--- a/src/catch2/internal/catch_xmlwriter.cpp
+++ b/src/catch2/internal/catch_xmlwriter.cpp
@@ -11,6 +11,7 @@
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_xmlwriter.hpp>
+#include <cstdint>
#include <iomanip>
#include <type_traits>
diff --git a/src/catch2/matchers/catch_matchers_all.hpp b/src/catch2/matchers/catch_matchers_all.hpp
index eadf7082..83fe5386 100644
--- a/src/catch2/matchers/catch_matchers_all.hpp
+++ b/src/catch2/matchers/catch_matchers_all.hpp
@@ -27,6 +27,7 @@
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <catch2/matchers/catch_matchers_predicate.hpp>
#include <catch2/matchers/catch_matchers_quantifiers.hpp>
+#include <catch2/matchers/catch_matchers_range_equals.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <catch2/matchers/catch_matchers_templated.hpp>
#include <catch2/matchers/catch_matchers_vector.hpp>
diff --git a/src/catch2/matchers/catch_matchers_contains.hpp b/src/catch2/matchers/catch_matchers_contains.hpp
index e298cab7..877d6924 100644
--- a/src/catch2/matchers/catch_matchers_contains.hpp
+++ b/src/catch2/matchers/catch_matchers_contains.hpp
@@ -33,13 +33,11 @@ namespace Catch {
}
template <typename RangeLike>
- bool match(RangeLike&& rng) const {
- using std::begin; using std::end;
-
- return end(rng) != std::find_if(begin(rng), end(rng),
- [&](auto const& elem) {
- return m_eq(elem, m_desired);
- });
+ bool match( RangeLike&& rng ) const {
+ for ( auto&& elem : rng ) {
+ if ( m_eq( elem, m_desired ) ) { return true; }
+ }
+ return false;
}
};
@@ -91,7 +89,7 @@ namespace Catch {
/**
* Creates a matcher that checks whether a range contains a specific element.
*
- * Uses `eq` to do the comparisons
+ * Uses `eq` to do the comparisons, the element is provided on the rhs
*/
template <typename T, typename Equality>
ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
diff --git a/src/catch2/matchers/catch_matchers_exception.hpp b/src/catch2/matchers/catch_matchers_exception.hpp
index 27e1b932..e7c3a636 100644
--- a/src/catch2/matchers/catch_matchers_exception.hpp
+++ b/src/catch2/matchers/catch_matchers_exception.hpp
@@ -29,6 +29,32 @@ public:
//! Creates a matcher that checks whether a std derived exception has the provided message
ExceptionMessageMatcher Message(std::string const& message);
+template <typename StringMatcherType>
+class ExceptionMessageMatchesMatcher final
+ : public MatcherBase<std::exception> {
+ StringMatcherType m_matcher;
+
+public:
+ ExceptionMessageMatchesMatcher( StringMatcherType matcher ):
+ m_matcher( CATCH_MOVE( matcher ) ) {}
+
+ bool match( std::exception const& ex ) const override {
+ return m_matcher.match( ex.what() );
+ }
+
+ std::string describe() const override {
+ return " matches \"" + m_matcher.describe() + '"';
+ }
+};
+
+//! Creates a matcher that checks whether a message from an std derived
+//! exception matches a provided matcher
+template <typename StringMatcherType>
+ExceptionMessageMatchesMatcher<StringMatcherType>
+MessageMatches( StringMatcherType&& matcher ) {
+ return { CATCH_FORWARD( matcher ) };
+}
+
} // namespace Matchers
} // namespace Catch
diff --git a/src/catch2/matchers/catch_matchers_floating_point.cpp b/src/catch2/matchers/catch_matchers_floating_point.cpp
index 719fb51f..206332ef 100644
--- a/src/catch2/matchers/catch_matchers_floating_point.cpp
+++ b/src/catch2/matchers/catch_matchers_floating_point.cpp
@@ -38,26 +38,11 @@ namespace {
return ulpDist <= maxUlpDiff;
}
-#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
-
- float nextafter(float x, float y) {
- return ::nextafterf(x, y);
- }
-
- double nextafter(double x, double y) {
- return ::nextafter(x, y);
- }
-
-#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
template <typename FP>
FP step(FP start, FP direction, uint64_t steps) {
for (uint64_t i = 0; i < steps; ++i) {
-#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
start = Catch::nextafter(start, direction);
-#else
- start = std::nextafter(start, direction);
-#endif
}
return start;
}
@@ -225,5 +210,17 @@ WithinRelMatcher WithinRel(float target) {
}
-} // namespace Matchers
+
+bool IsNaNMatcher::match( double const& matchee ) const {
+ return std::isnan( matchee );
+}
+
+std::string IsNaNMatcher::describe() const {
+ using namespace std::string_literals;
+ return "is NaN"s;
+}
+
+IsNaNMatcher IsNaN() { return IsNaNMatcher(); }
+
+ } // namespace Matchers
} // namespace Catch
diff --git a/src/catch2/matchers/catch_matchers_floating_point.hpp b/src/catch2/matchers/catch_matchers_floating_point.hpp
index abdaf159..76816633 100644
--- a/src/catch2/matchers/catch_matchers_floating_point.hpp
+++ b/src/catch2/matchers/catch_matchers_floating_point.hpp
@@ -27,6 +27,11 @@ namespace Matchers {
double m_margin;
};
+ //! Creates a matcher that accepts numbers within certain range of target
+ WithinAbsMatcher WithinAbs( double target, double margin );
+
+
+
class WithinUlpsMatcher final : public MatcherBase<double> {
public:
WithinUlpsMatcher( double target,
@@ -40,6 +45,13 @@ namespace Matchers {
Detail::FloatingPointKind m_type;
};
+ //! Creates a matcher that accepts doubles within certain ULP range of target
+ WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
+ //! Creates a matcher that accepts floats within certain ULP range of target
+ WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
+
+
+
// Given IEEE-754 format for floats and doubles, we can assume
// that float -> double promotion is lossless. Given this, we can
// assume that if we do the standard relative comparison of
@@ -56,13 +68,6 @@ namespace Matchers {
double m_epsilon;
};
- //! Creates a matcher that accepts doubles within certain ULP range of target
- WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
- //! Creates a matcher that accepts floats within certain ULP range of target
- WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
- //! Creates a matcher that accepts numbers within certain range of target
- WithinAbsMatcher WithinAbs(double target, double margin);
-
//! Creates a matcher that accepts doubles within certain relative range of target
WithinRelMatcher WithinRel(double target, double eps);
//! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target
@@ -72,6 +77,17 @@ namespace Matchers {
//! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target
WithinRelMatcher WithinRel(float target);
+
+
+ class IsNaNMatcher final : public MatcherBase<double> {
+ public:
+ IsNaNMatcher() = default;
+ bool match( double const& matchee ) const override;
+ std::string describe() const override;
+ };
+
+ IsNaNMatcher IsNaN();
+
} // namespace Matchers
} // namespace Catch
diff --git a/src/catch2/matchers/catch_matchers_range_equals.hpp b/src/catch2/matchers/catch_matchers_range_equals.hpp
new file mode 100644
index 00000000..95b781a4
--- /dev/null
+++ b/src/catch2/matchers/catch_matchers_range_equals.hpp
@@ -0,0 +1,144 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
+#define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
+
+#include <catch2/internal/catch_is_permutation.hpp>
+#include <catch2/matchers/catch_matchers_templated.hpp>
+
+#include <algorithm>
+#include <utility>
+
+namespace Catch {
+ namespace Matchers {
+
+ /**
+ * Matcher for checking that an element contains the same
+ * elements in the same order
+ */
+ template <typename TargetRangeLike, typename Equality>
+ class RangeEqualsMatcher final : public MatcherGenericBase {
+ TargetRangeLike m_desired;
+ Equality m_predicate;
+
+ public:
+ template <typename TargetRangeLike2, typename Equality2>
+ RangeEqualsMatcher( TargetRangeLike2&& range,
+ Equality2&& predicate ):
+ m_desired( CATCH_FORWARD( range ) ),
+ m_predicate( CATCH_FORWARD( predicate ) ) {}
+
+ template <typename RangeLike>
+ bool match( RangeLike&& rng ) const {
+ auto rng_start = begin( rng );
+ const auto rng_end = end( rng );
+ auto target_start = begin( m_desired );
+ const auto target_end = end( m_desired );
+
+ while (rng_start != rng_end && target_start != target_end) {
+ if (!m_predicate(*rng_start, *target_start)) {
+ return false;
+ }
+ ++rng_start;
+ ++target_start;
+ }
+ return rng_start == rng_end && target_start == target_end;
+ }
+
+ std::string describe() const override {
+ return "elements are " + Catch::Detail::stringify( m_desired );
+ }
+ };
+
+ /**
+ * Matcher for checking that an element contains the same
+ * elements (but not necessarily in the same order)
+ */
+ template <typename TargetRangeLike, typename Equality>
+ class UnorderedRangeEqualsMatcher final : public MatcherGenericBase {
+ TargetRangeLike m_desired;
+ Equality m_predicate;
+
+ public:
+ template <typename TargetRangeLike2, typename Equality2>
+ UnorderedRangeEqualsMatcher( TargetRangeLike2&& range,
+ Equality2&& predicate ):
+ m_desired( CATCH_FORWARD( range ) ),
+ m_predicate( CATCH_FORWARD( predicate ) ) {}
+
+ template <typename RangeLike>
+ bool match( RangeLike&& rng ) const {
+ using std::begin;
+ using std::end;
+ return Catch::Detail::is_permutation( begin( m_desired ),
+ end( m_desired ),
+ begin( rng ),
+ end( rng ),
+ m_predicate );
+ }
+
+ std::string describe() const override {
+ return "unordered elements are " +
+ ::Catch::Detail::stringify( m_desired );
+ }
+ };
+
+ /**
+ * Creates a matcher that checks if all elements in a range are equal
+ * to all elements in another range.
+ *
+ * Uses `std::equal_to` to do the comparison
+ */
+ template <typename RangeLike>
+ std::enable_if_t<!Detail::is_matcher<RangeLike>::value,
+ RangeEqualsMatcher<RangeLike, std::equal_to<>>>
+ RangeEquals( RangeLike&& range ) {
+ return { CATCH_FORWARD( range ), std::equal_to<>{} };
+ }
+
+ /**
+ * Creates a matcher that checks if all elements in a range are equal
+ * to all elements in another range.
+ *
+ * Uses to provided predicate `predicate` to do the comparisons
+ */
+ template <typename RangeLike, typename Equality>
+ RangeEqualsMatcher<RangeLike, Equality>
+ RangeEquals( RangeLike&& range, Equality&& predicate ) {
+ return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
+ }
+
+ /**
+ * Creates a matcher that checks if all elements in a range are equal
+ * to all elements in another range, in some permutation
+ *
+ * Uses `std::equal_to` to do the comparison
+ */
+ template <typename RangeLike>
+ std::enable_if_t<
+ !Detail::is_matcher<RangeLike>::value,
+ UnorderedRangeEqualsMatcher<RangeLike, std::equal_to<>>>
+ UnorderedRangeEquals( RangeLike&& range ) {
+ return { CATCH_FORWARD( range ), std::equal_to<>{} };
+ }
+
+ /**
+ * Creates a matcher that checks if all elements in a range are equal
+ * to all elements in another range, in some permutation.
+ *
+ * Uses to provided predicate `predicate` to do the comparisons
+ */
+ template <typename RangeLike, typename Equality>
+ UnorderedRangeEqualsMatcher<RangeLike, Equality>
+ UnorderedRangeEquals( RangeLike&& range, Equality&& predicate ) {
+ return { CATCH_FORWARD( range ), CATCH_FORWARD( predicate ) };
+ }
+ } // namespace Matchers
+} // namespace Catch
+
+#endif // CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED
diff --git a/src/catch2/matchers/catch_matchers_vector.hpp b/src/catch2/matchers/catch_matchers_vector.hpp
index 9a4b024f..fffbfdf6 100644
--- a/src/catch2/matchers/catch_matchers_vector.hpp
+++ b/src/catch2/matchers/catch_matchers_vector.hpp
@@ -85,11 +85,10 @@ namespace Matchers {
// - a more general approach would be via a compare template that defaults
// to using !=. but could be specialised for, e.g. std::vector<T> etc
// - then just call that directly
- if (m_comparator.size() != v.size())
- return false;
- for (std::size_t i = 0; i < v.size(); ++i)
- if (m_comparator[i] != v[i])
- return false;
+ if ( m_comparator.size() != v.size() ) { return false; }
+ for ( std::size_t i = 0; i < v.size(); ++i ) {
+ if ( !( m_comparator[i] == v[i] ) ) { return false; }
+ }
return true;
}
std::string describe() const override {
diff --git a/src/catch2/matchers/internal/catch_matchers_impl.hpp b/src/catch2/matchers/internal/catch_matchers_impl.hpp
index 12455bfe..2ee9f0c0 100644
--- a/src/catch2/matchers/internal/catch_matchers_impl.hpp
+++ b/src/catch2/matchers/internal/catch_matchers_impl.hpp
@@ -8,9 +8,14 @@
#ifndef CATCH_MATCHERS_IMPL_HPP_INCLUDED
#define CATCH_MATCHERS_IMPL_HPP_INCLUDED
-#include <catch2/internal/catch_test_macro_impl.hpp>
+#include <catch2/internal/catch_assertion_handler.hpp>
+#include <catch2/internal/catch_source_line_info.hpp>
+#include <catch2/internal/catch_decomposer.hpp>
+#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
+#include <string>
+
namespace Catch {
template<typename ArgT, typename MatcherT>
diff --git a/src/catch2/meson.build b/src/catch2/meson.build
index b32f72d2..4b9f3e8e 100644
--- a/src/catch2/meson.build
+++ b/src/catch2/meson.build
@@ -32,6 +32,8 @@ benchmark_headers = [
'benchmark/catch_sample_analysis.hpp',
'benchmark/detail/catch_analyse.hpp',
'benchmark/detail/catch_benchmark_function.hpp',
+ 'benchmark/detail/catch_benchmark_stats.hpp',
+ 'benchmark/detail/catch_benchmark_stats_fwd.hpp',
'benchmark/detail/catch_complete_invoke.hpp',
'benchmark/detail/catch_estimate_clock.hpp',
'benchmark/detail/catch_measure.hpp',
@@ -43,6 +45,7 @@ benchmark_headers = [
benchmark_sources = files(
'benchmark/catch_chronometer.cpp',
+ 'benchmark/detail/catch_analyse.cpp',
'benchmark/detail/catch_benchmark_function.cpp',
'benchmark/detail/catch_run_for_at_least.cpp',
'benchmark/detail/catch_stats.cpp',
@@ -64,8 +67,8 @@ internal_headers = [
'interfaces/catch_interfaces_registry_hub.hpp',
'interfaces/catch_interfaces_reporter.hpp',
'interfaces/catch_interfaces_reporter_factory.hpp',
- 'interfaces/catch_interfaces_reporter_registry.hpp',
'interfaces/catch_interfaces_tag_alias_registry.hpp',
+ 'interfaces/catch_interfaces_test_invoker.hpp',
'interfaces/catch_interfaces_testcase.hpp',
'internal/catch_assertion_handler.hpp',
'internal/catch_case_insensitive_comparisons.hpp',
@@ -76,6 +79,7 @@ internal_headers = [
'internal/catch_compiler_capabilities.hpp',
'internal/catch_config_android_logwrite.hpp',
'internal/catch_config_counter.hpp',
+ 'internal/catch_config_static_analysis_support.hpp',
'internal/catch_config_uncaught_exceptions.hpp',
'internal/catch_config_wchar.hpp',
'internal/catch_console_colour.hpp',
@@ -93,6 +97,8 @@ internal_headers = [
'internal/catch_floating_point_helpers.hpp',
'internal/catch_getenv.hpp',
'internal/catch_istream.hpp',
+ 'internal/catch_is_permutation.hpp',
+ 'internal/catch_jsonwriter.hpp',
'internal/catch_lazy_expr.hpp',
'internal/catch_leak_detector.hpp',
'internal/catch_list.hpp',
@@ -107,7 +113,10 @@ internal_headers = [
'internal/catch_platform.hpp',
'internal/catch_polyfills.hpp',
'internal/catch_preprocessor.hpp',
+ 'internal/catch_preprocessor_internal_stringify.hpp',
'internal/catch_preprocessor_remove_parens.hpp',
+ 'internal/catch_random_floating_point_helpers.hpp',
+ 'internal/catch_random_integer_helpers.hpp',
'internal/catch_random_number_generator.hpp',
'internal/catch_random_seed_generation.hpp',
'internal/catch_reporter_registry.hpp',
@@ -132,10 +141,13 @@ internal_headers = [
'internal/catch_test_failure_exception.hpp',
'internal/catch_test_macro_impl.hpp',
'internal/catch_test_registry.hpp',
+ 'internal/catch_test_run_info.hpp',
'internal/catch_test_spec_parser.hpp',
'internal/catch_textflow.hpp',
'internal/catch_to_string.hpp',
'internal/catch_uncaught_exceptions.hpp',
+ 'internal/catch_uniform_floating_point_distribution.hpp',
+ 'internal/catch_uniform_integer_distribution.hpp',
'internal/catch_unique_name.hpp',
'internal/catch_unique_ptr.hpp',
'internal/catch_void_type.hpp',
@@ -150,6 +162,7 @@ internal_headers = [
'matchers/catch_matchers_floating_point.hpp',
'matchers/catch_matchers_predicate.hpp',
'matchers/catch_matchers_quantifiers.hpp',
+ 'matchers/catch_matchers_range_equals.hpp',
'matchers/catch_matchers_string.hpp',
'matchers/catch_matchers_templated.hpp',
'matchers/catch_matchers_vector.hpp',
@@ -188,7 +201,6 @@ internal_sources = files(
'interfaces/catch_interfaces_registry_hub.cpp',
'interfaces/catch_interfaces_reporter.cpp',
'interfaces/catch_interfaces_reporter_factory.cpp',
- 'interfaces/catch_interfaces_reporter_registry.cpp',
'interfaces/catch_interfaces_testcase.cpp',
'internal/catch_assertion_handler.cpp',
'internal/catch_case_insensitive_comparisons.cpp',
@@ -207,6 +219,7 @@ internal_sources = files(
'internal/catch_floating_point_helpers.cpp',
'internal/catch_getenv.cpp',
'internal/catch_istream.cpp',
+ 'internal/catch_jsonwriter.cpp',
'internal/catch_lazy_expr.cpp',
'internal/catch_leak_detector.cpp',
'internal/catch_list.cpp',
@@ -261,6 +274,7 @@ internal_sources = files(
'catch_timer.cpp',
'catch_tostring.cpp',
'catch_totals.cpp',
+ 'catch_translate_exception.cpp',
'catch_version.cpp',
)
@@ -272,6 +286,7 @@ reporter_headers = [
'reporters/catch_reporter_cumulative_base.hpp',
'reporters/catch_reporter_event_listener.hpp',
'reporters/catch_reporter_helpers.hpp',
+ 'reporters/catch_reporter_json.hpp',
'reporters/catch_reporter_junit.hpp',
'reporters/catch_reporter_multi.hpp',
'reporters/catch_reporter_registrars.hpp',
@@ -291,6 +306,7 @@ reporter_sources = files(
'reporters/catch_reporter_cumulative_base.cpp',
'reporters/catch_reporter_event_listener.cpp',
'reporters/catch_reporter_helpers.cpp',
+ 'reporters/catch_reporter_json.cpp',
'reporters/catch_reporter_junit.cpp',
'reporters/catch_reporter_multi.cpp',
'reporters/catch_reporter_registrars.cpp',
diff --git a/src/catch2/reporters/catch_reporter_automake.cpp b/src/catch2/reporters/catch_reporter_automake.cpp
index 0660d092..5e506a6b 100644
--- a/src/catch2/reporters/catch_reporter_automake.cpp
+++ b/src/catch2/reporters/catch_reporter_automake.cpp
@@ -12,12 +12,14 @@
namespace Catch {
- AutomakeReporter::~AutomakeReporter() {}
+ AutomakeReporter::~AutomakeReporter() = default;
void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
// Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR.
m_stream << ":test-result: ";
- if (_testCaseStats.totals.assertions.allPassed()) {
+ if ( _testCaseStats.totals.testCases.skipped > 0 ) {
+ m_stream << "SKIP";
+ } else if (_testCaseStats.totals.assertions.allPassed()) {
m_stream << "PASS";
} else if (_testCaseStats.totals.assertions.allOk()) {
m_stream << "XFAIL";
diff --git a/src/catch2/reporters/catch_reporter_automake.hpp b/src/catch2/reporters/catch_reporter_automake.hpp
index 3475a1fd..a639428c 100644
--- a/src/catch2/reporters/catch_reporter_automake.hpp
+++ b/src/catch2/reporters/catch_reporter_automake.hpp
@@ -8,7 +8,6 @@
#ifndef CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
#define CATCH_REPORTER_AUTOMAKE_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/reporters/catch_reporter_streaming_base.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
diff --git a/src/catch2/reporters/catch_reporter_compact.cpp b/src/catch2/reporters/catch_reporter_compact.cpp
index d8088457..0f855944 100644
--- a/src/catch2/reporters/catch_reporter_compact.cpp
+++ b/src/catch2/reporters/catch_reporter_compact.cpp
@@ -105,6 +105,11 @@ public:
printIssue("explicitly");
printRemainingMessages(Colour::None);
break;
+ case ResultWas::ExplicitSkip:
+ printResultType(Colour::Skip, "skipped"_sr);
+ printMessage();
+ printRemainingMessages();
+ break;
// These cases are here to prevent compiler warnings
case ResultWas::Unknown:
case ResultWas::FailureBit:
@@ -166,7 +171,7 @@ private:
return;
const auto itEnd = messages.cend();
- const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+ const auto N = static_cast<std::size_t>(itEnd - itMessage);
stream << colourImpl->guardColour( colour ) << " with "
<< pluralise( N, "message"_sr ) << ':';
@@ -187,7 +192,7 @@ private:
private:
std::ostream& stream;
AssertionResult const& result;
- std::vector<MessageInfo> messages;
+ std::vector<MessageInfo> const& messages;
std::vector<MessageInfo>::const_iterator itMessage;
bool printInfoMessages;
ColourImpl* colourImpl;
@@ -220,7 +225,7 @@ private:
// Drop out if result was successful and we're not printing those
if( !m_config->includeSuccessfulResults() && result.isOk() ) {
- if( result.getResultType() != ResultWas::Warning )
+ if( result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip )
return;
printInfoMessages = false;
}
@@ -244,6 +249,6 @@ private:
StreamingReporterBase::testRunEnded( _testRunStats );
}
- CompactReporter::~CompactReporter() {}
+ CompactReporter::~CompactReporter() = default;
} // end namespace Catch
diff --git a/src/catch2/reporters/catch_reporter_console.cpp b/src/catch2/reporters/catch_reporter_console.cpp
index 0edb121e..bbde5ec9 100644
--- a/src/catch2/reporters/catch_reporter_console.cpp
+++ b/src/catch2/reporters/catch_reporter_console.cpp
@@ -51,7 +51,6 @@ public:
stats(_stats),
result(_stats.assertionResult),
colour(Colour::None),
- message(result.getMessage()),
messages(_stats.infoMessages),
colourImpl(colourImpl_),
printInfoMessages(_printInfoMessages) {
@@ -60,10 +59,10 @@ public:
colour = Colour::Success;
passOrFail = "PASSED"_sr;
//if( result.hasMessage() )
- if (_stats.infoMessages.size() == 1)
- messageLabel = "with message";
- if (_stats.infoMessages.size() > 1)
- messageLabel = "with messages";
+ if (messages.size() == 1)
+ messageLabel = "with message"_sr;
+ if (messages.size() > 1)
+ messageLabel = "with messages"_sr;
break;
case ResultWas::ExpressionFailed:
if (result.isOk()) {
@@ -73,43 +72,57 @@ public:
colour = Colour::Error;
passOrFail = "FAILED"_sr;
}
- if (_stats.infoMessages.size() == 1)
- messageLabel = "with message";
- if (_stats.infoMessages.size() > 1)
- messageLabel = "with messages";
+ if (messages.size() == 1)
+ messageLabel = "with message"_sr;
+ if (messages.size() > 1)
+ messageLabel = "with messages"_sr;
break;
case ResultWas::ThrewException:
colour = Colour::Error;
passOrFail = "FAILED"_sr;
- messageLabel = "due to unexpected exception with ";
- if (_stats.infoMessages.size() == 1)
- messageLabel += "message";
- if (_stats.infoMessages.size() > 1)
- messageLabel += "messages";
+ // todo switch
+ switch (messages.size()) { case 0:
+ messageLabel = "due to unexpected exception with "_sr;
+ break;
+ case 1:
+ messageLabel = "due to unexpected exception with message"_sr;
+ break;
+ default:
+ messageLabel = "due to unexpected exception with messages"_sr;
+ break;
+ }
break;
case ResultWas::FatalErrorCondition:
colour = Colour::Error;
passOrFail = "FAILED"_sr;
- messageLabel = "due to a fatal error condition";
+ messageLabel = "due to a fatal error condition"_sr;
break;
case ResultWas::DidntThrowException:
colour = Colour::Error;
passOrFail = "FAILED"_sr;
- messageLabel = "because no exception was thrown where one was expected";
+ messageLabel = "because no exception was thrown where one was expected"_sr;
break;
case ResultWas::Info:
- messageLabel = "info";
+ messageLabel = "info"_sr;
break;
case ResultWas::Warning:
- messageLabel = "warning";
+ messageLabel = "warning"_sr;
break;
case ResultWas::ExplicitFailure:
passOrFail = "FAILED"_sr;
colour = Colour::Error;
- if (_stats.infoMessages.size() == 1)
- messageLabel = "explicitly with message";
- if (_stats.infoMessages.size() > 1)
- messageLabel = "explicitly with messages";
+ if (messages.size() == 1)
+ messageLabel = "explicitly with message"_sr;
+ if (messages.size() > 1)
+ messageLabel = "explicitly with messages"_sr;
+ break;
+ case ResultWas::ExplicitSkip:
+ colour = Colour::Skip;
+ passOrFail = "SKIPPED"_sr;
+ if (messages.size() == 1)
+ messageLabel = "explicitly with message"_sr;
+ if (messages.size() > 1)
+ messageLabel = "explicitly with messages"_sr;
break;
// These cases are here to prevent compiler warnings
case ResultWas::Unknown:
@@ -173,9 +186,8 @@ private:
AssertionResult const& result;
Colour::Code colour;
StringRef passOrFail;
- std::string messageLabel;
- std::string message;
- std::vector<MessageInfo> messages;
+ StringRef messageLabel;
+ std::vector<MessageInfo> const& messages;
ColourImpl* colourImpl;
bool printInfoMessages;
};
@@ -185,13 +197,16 @@ std::size_t makeRatio( std::uint64_t number, std::uint64_t total ) {
return (ratio == 0 && number > 0) ? 1 : static_cast<std::size_t>(ratio);
}
-std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
- if (i > j && i > k)
+std::size_t&
+findMax( std::size_t& i, std::size_t& j, std::size_t& k, std::size_t& l ) {
+ if (i > j && i > k && i > l)
return i;
- else if (j > k)
+ else if (j > k && j > l)
return j;
- else
+ else if (k > l)
return k;
+ else
+ return l;
}
enum class Justification { Left, Right };
@@ -203,6 +218,7 @@ struct ColumnInfo {
};
struct ColumnBreak {};
struct RowBreak {};
+struct OutputFlush {};
class Duration {
enum class Unit {
@@ -319,12 +335,12 @@ public:
}
template<typename T>
- friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
+ friend TablePrinter& operator<< (TablePrinter& tp, T const& value) {
tp.m_oss << value;
return tp;
}
- friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
+ friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) {
auto colStr = tp.m_oss.str();
const auto strSize = colStr.size();
tp.m_oss.str("");
@@ -346,13 +362,18 @@ public:
return tp;
}
- friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
+ friend TablePrinter& operator<< (TablePrinter& tp, RowBreak) {
if (tp.m_currentColumn > 0) {
tp.m_os << '\n';
tp.m_currentColumn = -1;
}
return tp;
}
+
+ friend TablePrinter& operator<<(TablePrinter& tp, OutputFlush) {
+ tp.m_os << std::flush;
+ return tp;
+ }
};
ConsoleReporter::ConsoleReporter(ReporterConfig&& config):
@@ -374,7 +395,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config):
{ "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left },
{ "samples mean std dev", 14, Justification::Right },
{ "iterations low mean low std dev", 14, Justification::Right },
- { "estimated high mean high std dev", 14, Justification::Right }
+ { "est run time high mean high std dev", 14, Justification::Right }
};
}
}())) {}
@@ -400,7 +421,8 @@ void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
// Drop out if result was successful but we're not printing them.
- if (!includeResults && result.getResultType() != ResultWas::Warning)
+ // TODO: Make configurable whether skips should be printed
+ if (!includeResults && result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip)
return;
lazyPrint();
@@ -457,8 +479,11 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) {
void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
(*m_tablePrinter) << info.samples << ColumnBreak()
<< info.iterations << ColumnBreak();
- if (!m_config->benchmarkNoAnalysis())
- (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
+ if ( !m_config->benchmarkNoAnalysis() ) {
+ ( *m_tablePrinter )
+ << Duration( info.estimatedDuration ) << ColumnBreak();
+ }
+ ( *m_tablePrinter ) << OutputFlush{};
}
void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
if (m_config->benchmarkNoAnalysis())
@@ -603,10 +628,11 @@ void ConsoleReporter::printTotalsDivider(Totals const& totals) {
std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
- while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
- findMax(failedRatio, failedButOkRatio, passedRatio)++;
+ std::size_t skippedRatio = makeRatio(totals.testCases.skipped, totals.testCases.total());
+ while (failedRatio + failedButOkRatio + passedRatio + skippedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
+ findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)++;
while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
- findMax(failedRatio, failedButOkRatio, passedRatio)--;
+ findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)--;
m_stream << m_colour->guardColour( Colour::Error )
<< std::string( failedRatio, '=' )
@@ -619,6 +645,8 @@ void ConsoleReporter::printTotalsDivider(Totals const& totals) {
m_stream << m_colour->guardColour( Colour::Success )
<< std::string( passedRatio, '=' );
}
+ m_stream << m_colour->guardColour( Colour::Skip )
+ << std::string( skippedRatio, '=' );
} else {
m_stream << m_colour->guardColour( Colour::Warning )
<< std::string( CATCH_CONFIG_CONSOLE_WIDTH - 1, '=' );
diff --git a/src/catch2/reporters/catch_reporter_cumulative_base.cpp b/src/catch2/reporters/catch_reporter_cumulative_base.cpp
index effd98b7..5e106326 100644
--- a/src/catch2/reporters/catch_reporter_cumulative_base.cpp
+++ b/src/catch2/reporters/catch_reporter_cumulative_base.cpp
@@ -70,7 +70,8 @@ namespace Catch {
void
CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) {
- SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+ // We need a copy, because SectionStats expect to take ownership
+ SectionStats incompleteStats( SectionInfo(sectionInfo), Counts(), 0, false );
SectionNode* node;
if ( m_sectionStack.empty() ) {
if ( !m_rootSection ) {
diff --git a/src/catch2/reporters/catch_reporter_cumulative_base.hpp b/src/catch2/reporters/catch_reporter_cumulative_base.hpp
index cdff9991..267b39fd 100644
--- a/src/catch2/reporters/catch_reporter_cumulative_base.hpp
+++ b/src/catch2/reporters/catch_reporter_cumulative_base.hpp
@@ -8,7 +8,6 @@
#ifndef CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
#define CATCH_REPORTER_CUMULATIVE_BASE_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/reporters/catch_reporter_common_base.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
@@ -125,7 +124,7 @@ namespace Catch {
void skipTest(TestCaseInfo const&) override {}
protected:
- //! Should the cumulative base store the assertion expansion for succesful assertions?
+ //! Should the cumulative base store the assertion expansion for successful assertions?
bool m_shouldStoreSuccesfulAssertions = true;
//! Should the cumulative base store the assertion expansion for failed assertions?
bool m_shouldStoreFailedAssertions = true;
diff --git a/src/catch2/reporters/catch_reporter_helpers.cpp b/src/catch2/reporters/catch_reporter_helpers.cpp
index f6aa6fc5..ffb32ffb 100644
--- a/src/catch2/reporters/catch_reporter_helpers.cpp
+++ b/src/catch2/reporters/catch_reporter_helpers.cpp
@@ -316,15 +316,22 @@ namespace Catch {
}
std::vector<SummaryColumn> columns;
+ // Don't include "skipped assertions" in total count
+ const auto totalAssertionCount =
+ totals.assertions.total() - totals.assertions.skipped;
columns.push_back( SummaryColumn( "", Colour::None )
.addRow( totals.testCases.total() )
- .addRow( totals.assertions.total() ) );
+ .addRow( totalAssertionCount ) );
columns.push_back( SummaryColumn( "passed", Colour::Success )
.addRow( totals.testCases.passed )
.addRow( totals.assertions.passed ) );
columns.push_back( SummaryColumn( "failed", Colour::ResultError )
.addRow( totals.testCases.failed )
.addRow( totals.assertions.failed ) );
+ columns.push_back( SummaryColumn( "skipped", Colour::Skip )
+ .addRow( totals.testCases.skipped )
+ // Don't print "skipped assertions"
+ .addRow( 0 ) );
columns.push_back(
SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
.addRow( totals.testCases.failedButOk )
diff --git a/src/catch2/reporters/catch_reporter_json.cpp b/src/catch2/reporters/catch_reporter_json.cpp
new file mode 100644
index 00000000..1f0db8b0
--- /dev/null
+++ b/src/catch2/reporters/catch_reporter_json.cpp
@@ -0,0 +1,372 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+//
+#include <catch2/catch_test_case_info.hpp>
+#include <catch2/catch_test_spec.hpp>
+#include <catch2/catch_version.hpp>
+#include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/internal/catch_list.hpp>
+#include <catch2/internal/catch_string_manip.hpp>
+#include <catch2/reporters/catch_reporter_json.hpp>
+
+namespace Catch {
+ namespace {
+ void writeSourceInfo( JsonObjectWriter& writer,
+ SourceLineInfo const& sourceInfo ) {
+ auto source_location_writer =
+ writer.write( "source-location"_sr ).writeObject();
+ source_location_writer.write( "filename"_sr )
+ .write( sourceInfo.file );
+ source_location_writer.write( "line"_sr ).write( sourceInfo.line );
+ }
+
+ void writeTags( JsonArrayWriter writer, std::vector<Tag> const& tags ) {
+ for ( auto const& tag : tags ) {
+ writer.write( tag.original );
+ }
+ }
+
+ void writeProperties( JsonArrayWriter writer,
+ TestCaseInfo const& info ) {
+ if ( info.isHidden() ) { writer.write( "is-hidden"_sr ); }
+ if ( info.okToFail() ) { writer.write( "ok-to-fail"_sr ); }
+ if ( info.expectedToFail() ) {
+ writer.write( "expected-to-fail"_sr );
+ }
+ if ( info.throws() ) { writer.write( "throws"_sr ); }
+ }
+
+ } // namespace
+
+ JsonReporter::JsonReporter( ReporterConfig&& config ):
+ StreamingReporterBase{ CATCH_MOVE( config ) } {
+
+ m_preferences.shouldRedirectStdOut = true;
+ // TBD: Do we want to report all assertions? XML reporter does
+ // not, but for machine-parseable reporters I think the answer
+ // should be yes.
+ m_preferences.shouldReportAllAssertions = true;
+
+ m_objectWriters.emplace( m_stream );
+ m_writers.emplace( Writer::Object );
+ auto& writer = m_objectWriters.top();
+
+ writer.write( "version"_sr ).write( 1 );
+
+ {
+ auto metadata_writer = writer.write( "metadata"_sr ).writeObject();
+ metadata_writer.write( "name"_sr ).write( m_config->name() );
+ metadata_writer.write( "rng-seed"_sr ).write( m_config->rngSeed() );
+ metadata_writer.write( "catch2-version"_sr )
+ .write( libraryVersion() );
+ if ( m_config->testSpec().hasFilters() ) {
+ metadata_writer.write( "filters"_sr )
+ .write( m_config->testSpec() );
+ }
+ }
+ }
+
+ JsonReporter::~JsonReporter() {
+ endListing();
+ // TODO: Ensure this closes the top level object, add asserts
+ assert( m_writers.size() == 1 && "Only the top level object should be open" );
+ assert( m_writers.top() == Writer::Object );
+ endObject();
+ m_stream << '\n' << std::flush;
+ assert( m_writers.empty() );
+ }
+
+ JsonArrayWriter& JsonReporter::startArray() {
+ m_arrayWriters.emplace( m_arrayWriters.top().writeArray() );
+ m_writers.emplace( Writer::Array );
+ return m_arrayWriters.top();
+ }
+ JsonArrayWriter& JsonReporter::startArray( StringRef key ) {
+ m_arrayWriters.emplace(
+ m_objectWriters.top().write( key ).writeArray() );
+ m_writers.emplace( Writer::Array );
+ return m_arrayWriters.top();
+ }
+
+ JsonObjectWriter& JsonReporter::startObject() {
+ m_objectWriters.emplace( m_arrayWriters.top().writeObject() );
+ m_writers.emplace( Writer::Object );
+ return m_objectWriters.top();
+ }
+ JsonObjectWriter& JsonReporter::startObject( StringRef key ) {
+ m_objectWriters.emplace(
+ m_objectWriters.top().write( key ).writeObject() );
+ m_writers.emplace( Writer::Object );
+ return m_objectWriters.top();
+ }
+
+ void JsonReporter::endObject() {
+ assert( isInside( Writer::Object ) );
+ m_objectWriters.pop();
+ m_writers.pop();
+ }
+ void JsonReporter::endArray() {
+ assert( isInside( Writer::Array ) );
+ m_arrayWriters.pop();
+ m_writers.pop();
+ }
+
+ bool JsonReporter::isInside( Writer writer ) {
+ return !m_writers.empty() && m_writers.top() == writer;
+ }
+
+ void JsonReporter::startListing() {
+ if ( !m_startedListing ) { startObject( "listings"_sr ); }
+ m_startedListing = true;
+ }
+ void JsonReporter::endListing() {
+ if ( m_startedListing ) { endObject(); }
+ m_startedListing = false;
+ }
+
+ std::string JsonReporter::getDescription() {
+ return "Outputs listings as JSON. Test listing is Work-in-Progress!";
+ }
+
+ void JsonReporter::testRunStarting( TestRunInfo const& testInfo ) {
+ StreamingReporterBase::testRunStarting( testInfo );
+ endListing();
+
+ assert( isInside( Writer::Object ) );
+ startObject( "test-run"_sr );
+ startArray( "test-cases"_sr );
+ }
+
+ static void writeCounts( JsonObjectWriter&& writer, Counts const& counts ) {
+ writer.write( "passed"_sr ).write( counts.passed );
+ writer.write( "failed"_sr ).write( counts.failed );
+ writer.write( "fail-but-ok"_sr ).write( counts.failedButOk );
+ writer.write( "skipped"_sr ).write( counts.skipped );
+ }
+
+ void JsonReporter::testRunEnded(TestRunStats const& runStats) {
+ assert( isInside( Writer::Array ) );
+ // End "test-cases"
+ endArray();
+
+ {
+ auto totals =
+ m_objectWriters.top().write( "totals"_sr ).writeObject();
+ writeCounts( totals.write( "assertions"_sr ).writeObject(),
+ runStats.totals.assertions );
+ writeCounts( totals.write( "test-cases"_sr ).writeObject(),
+ runStats.totals.testCases );
+ }
+
+ // End the "test-run" object
+ endObject();
+ }
+
+ void JsonReporter::testCaseStarting( TestCaseInfo const& tcInfo ) {
+ StreamingReporterBase::testCaseStarting( tcInfo );
+
+ assert( isInside( Writer::Array ) &&
+ "We should be in the 'test-cases' array" );
+ startObject();
+ // "test-info" prelude
+ {
+ auto testInfo =
+ m_objectWriters.top().write( "test-info"_sr ).writeObject();
+ // TODO: handle testName vs className!!
+ testInfo.write( "name"_sr ).write( tcInfo.name );
+ writeSourceInfo(testInfo, tcInfo.lineInfo);
+ writeTags( testInfo.write( "tags"_sr ).writeArray(), tcInfo.tags );
+ writeProperties( testInfo.write( "properties"_sr ).writeArray(),
+ tcInfo );
+ }
+
+
+ // Start the array for individual test runs (testCasePartial pairs)
+ startArray( "runs"_sr );
+ }
+
+ void JsonReporter::testCaseEnded( TestCaseStats const& tcStats ) {
+ StreamingReporterBase::testCaseEnded( tcStats );
+
+ // We need to close the 'runs' array before finishing the test case
+ assert( isInside( Writer::Array ) );
+ endArray();
+
+ {
+ auto totals =
+ m_objectWriters.top().write( "totals"_sr ).writeObject();
+ writeCounts( totals.write( "assertions"_sr ).writeObject(),
+ tcStats.totals.assertions );
+ // We do not write the test case totals, because there will always be just one test case here.
+ // TODO: overall "result" -> success, skip, fail here? Or in partial result?
+ }
+ // We do not write out stderr/stdout, because we instead wrote those out in partial runs
+
+ // TODO: aborting?
+
+ // And we also close this test case's object
+ assert( isInside( Writer::Object ) );
+ endObject();
+ }
+
+ void JsonReporter::testCasePartialStarting( TestCaseInfo const& /*tcInfo*/,
+ uint64_t index ) {
+ startObject();
+ m_objectWriters.top().write( "run-idx"_sr ).write( index );
+ startArray( "path"_sr );
+ // TODO: we want to delay most of the printing to the 'root' section
+ // TODO: childSection key name?
+ }
+
+ void JsonReporter::testCasePartialEnded( TestCaseStats const& tcStats,
+ uint64_t /*index*/ ) {
+ // Fixme: the top level section handles this.
+ //// path object
+ endArray();
+ if ( !tcStats.stdOut.empty() ) {
+ m_objectWriters.top()
+ .write( "captured-stdout"_sr )
+ .write( tcStats.stdOut );
+ }
+ if ( !tcStats.stdErr.empty() ) {
+ m_objectWriters.top()
+ .write( "captured-stderr"_sr )
+ .write( tcStats.stdErr );
+ }
+ {
+ auto totals =
+ m_objectWriters.top().write( "totals"_sr ).writeObject();
+ writeCounts( totals.write( "assertions"_sr ).writeObject(),
+ tcStats.totals.assertions );
+ // We do not write the test case totals, because there will
+ // always be just one test case here.
+ // TODO: overall "result" -> success, skip, fail here? Or in
+ // partial result?
+ }
+ // TODO: aborting?
+ // run object
+ endObject();
+ }
+
+ void JsonReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+ assert( isInside( Writer::Array ) &&
+ "Section should always start inside an object" );
+ // We want to nest top level sections, even though it shares name
+ // and source loc with the TEST_CASE
+ auto& sectionObject = startObject();
+ sectionObject.write( "kind"_sr ).write( "section"_sr );
+ sectionObject.write( "name"_sr ).write( sectionInfo.name );
+ writeSourceInfo( m_objectWriters.top(), sectionInfo.lineInfo );
+
+
+ // TBD: Do we want to create this event lazily? It would become
+ // rather complex, but we could do it, and it would look
+ // better for empty sections. OTOH, empty sections should
+ // be rare.
+ startArray( "path"_sr );
+ }
+ void JsonReporter::sectionEnded( SectionStats const& /*sectionStats */) {
+ // End the subpath array
+ endArray();
+ // TODO: metadata
+ // TODO: what info do we have here?
+
+ // End the section object
+ endObject();
+ }
+
+ void JsonReporter::assertionStarting( AssertionInfo const& /*assertionInfo*/ ) {}
+ void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) {
+ // TODO: There is lot of different things to handle here, but
+ // we can fill it in later, after we show that the basic
+ // outline and streaming reporter impl works well enough.
+ //if ( !m_config->includeSuccessfulResults()
+ // && assertionStats.assertionResult.isOk() ) {
+ // return;
+ //}
+ assert( isInside( Writer::Array ) );
+ auto assertionObject = m_arrayWriters.top().writeObject();
+
+ assertionObject.write( "kind"_sr ).write( "assertion"_sr );
+ writeSourceInfo( assertionObject,
+ assertionStats.assertionResult.getSourceInfo() );
+ assertionObject.write( "status"_sr )
+ .write( assertionStats.assertionResult.isOk() );
+ // TODO: handling of result.
+ // TODO: messages
+ // TODO: totals?
+ }
+
+
+ void JsonReporter::benchmarkPreparing( StringRef name ) { (void)name; }
+ void JsonReporter::benchmarkStarting( BenchmarkInfo const& ) {}
+ void JsonReporter::benchmarkEnded( BenchmarkStats<> const& ) {}
+ void JsonReporter::benchmarkFailed( StringRef error ) { (void)error; }
+
+ void JsonReporter::listReporters(
+ std::vector<ReporterDescription> const& descriptions ) {
+ startListing();
+
+ auto writer =
+ m_objectWriters.top().write( "reporters"_sr ).writeArray();
+ for ( auto const& desc : descriptions ) {
+ auto desc_writer = writer.writeObject();
+ desc_writer.write( "name"_sr ).write( desc.name );
+ desc_writer.write( "description"_sr ).write( desc.description );
+ }
+ }
+ void JsonReporter::listListeners(
+ std::vector<ListenerDescription> const& descriptions ) {
+ startListing();
+
+ auto writer =
+ m_objectWriters.top().write( "listeners"_sr ).writeArray();
+
+ for ( auto const& desc : descriptions ) {
+ auto desc_writer = writer.writeObject();
+ desc_writer.write( "name"_sr ).write( desc.name );
+ desc_writer.write( "description"_sr ).write( desc.description );
+ }
+ }
+ void JsonReporter::listTests( std::vector<TestCaseHandle> const& tests ) {
+ startListing();
+
+ auto writer = m_objectWriters.top().write( "tests"_sr ).writeArray();
+
+ for ( auto const& test : tests ) {
+ auto desc_writer = writer.writeObject();
+ auto const& info = test.getTestCaseInfo();
+
+ desc_writer.write( "name"_sr ).write( info.name );
+ desc_writer.write( "class-name"_sr ).write( info.className );
+ {
+ auto tag_writer = desc_writer.write( "tags"_sr ).writeArray();
+ for ( auto const& tag : info.tags ) {
+ tag_writer.write( tag.original );
+ }
+ }
+ writeSourceInfo( desc_writer, info.lineInfo );
+ }
+ }
+ void JsonReporter::listTags( std::vector<TagInfo> const& tags ) {
+ startListing();
+
+ auto writer = m_objectWriters.top().write( "tags"_sr ).writeArray();
+ for ( auto const& tag : tags ) {
+ auto tag_writer = writer.writeObject();
+ {
+ auto aliases_writer =
+ tag_writer.write( "aliases"_sr ).writeArray();
+ for ( auto alias : tag.spellings ) {
+ aliases_writer.write( alias );
+ }
+ }
+ tag_writer.write( "count"_sr ).write( tag.count );
+ }
+ }
+} // namespace Catch
diff --git a/src/catch2/reporters/catch_reporter_json.hpp b/src/catch2/reporters/catch_reporter_json.hpp
new file mode 100644
index 00000000..c938ca39
--- /dev/null
+++ b/src/catch2/reporters/catch_reporter_json.hpp
@@ -0,0 +1,95 @@
+
+// Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.txt or copy at
+// https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#ifndef CATCH_REPORTER_JSON_HPP_INCLUDED
+#define CATCH_REPORTER_JSON_HPP_INCLUDED
+
+#include <catch2/catch_timer.hpp>
+#include <catch2/internal/catch_jsonwriter.hpp>
+#include <catch2/reporters/catch_reporter_streaming_base.hpp>
+
+#include <stack>
+
+namespace Catch {
+ class JsonReporter : public StreamingReporterBase {
+ public:
+ JsonReporter( ReporterConfig&& config );
+
+ ~JsonReporter() override;
+
+ static std::string getDescription();
+
+ public: // StreamingReporterBase
+ void testRunStarting( TestRunInfo const& runInfo ) override;
+ void testRunEnded( TestRunStats const& runStats ) override;
+
+ void testCaseStarting( TestCaseInfo const& tcInfo ) override;
+ void testCaseEnded( TestCaseStats const& tcStats ) override;
+
+ void testCasePartialStarting( TestCaseInfo const& tcInfo,
+ uint64_t index ) override;
+ void testCasePartialEnded( TestCaseStats const& tcStats,
+ uint64_t index ) override;
+
+ void sectionStarting( SectionInfo const& sectionInfo ) override;
+ void sectionEnded( SectionStats const& sectionStats ) override;
+
+ void assertionStarting( AssertionInfo const& assertionInfo ) override;
+ void assertionEnded( AssertionStats const& assertionStats ) override;
+
+ //void testRunEndedCumulative() override;
+
+ void benchmarkPreparing( StringRef name ) override;
+ void benchmarkStarting( BenchmarkInfo const& ) override;
+ void benchmarkEnded( BenchmarkStats<> const& ) override;
+ void benchmarkFailed( StringRef error ) override;
+
+ void listReporters(
+ std::vector<ReporterDescription> const& descriptions ) override;
+ void listListeners(
+ std::vector<ListenerDescription> const& descriptions ) override;
+ void listTests( std::vector<TestCaseHandle> const& tests ) override;
+ void listTags( std::vector<TagInfo> const& tags ) override;
+
+ private:
+ Timer m_testCaseTimer;
+ enum class Writer {
+ Object,
+ Array
+ };
+
+ JsonArrayWriter& startArray();
+ JsonArrayWriter& startArray( StringRef key );
+
+ JsonObjectWriter& startObject();
+ JsonObjectWriter& startObject( StringRef key );
+
+ void endObject();
+ void endArray();
+
+ bool isInside( Writer writer );
+
+ void startListing();
+ void endListing();
+
+ // Invariant:
+ // When m_writers is not empty and its top element is
+ // - Writer::Object, then m_objectWriters is not be empty
+ // - Writer::Array, then m_arrayWriters shall not be empty
+ std::stack<JsonObjectWriter> m_objectWriters{};
+ std::stack<JsonArrayWriter> m_arrayWriters{};
+ std::stack<Writer> m_writers{};
+
+ bool m_startedListing = false;
+
+ // std::size_t m_sectionDepth = 0;
+ // std::size_t m_sectionStarted = 0;
+ };
+} // namespace Catch
+
+#endif // CATCH_REPORTER_JSON_HPP_INCLUDED
diff --git a/src/catch2/reporters/catch_reporter_junit.cpp b/src/catch2/reporters/catch_reporter_junit.cpp
index 837d0489..fc5cae34 100644
--- a/src/catch2/reporters/catch_reporter_junit.cpp
+++ b/src/catch2/reporters/catch_reporter_junit.cpp
@@ -33,6 +33,8 @@ namespace Catch {
gmtime_s(&timeInfo, &rawtime);
#elif defined (CATCH_PLATFORM_PLAYSTATION)
gmtime_s(&rawtime, &timeInfo);
+#elif defined (__IAR_SYSTEMS_ICC__)
+ timeInfo = *std::gmtime(&rawtime);
#else
gmtime_r(&rawtime, &timeInfo);
#endif
@@ -132,6 +134,7 @@ namespace Catch {
xml.writeAttribute( "name"_sr, stats.runInfo.name );
xml.writeAttribute( "errors"_sr, unexpectedExceptions );
xml.writeAttribute( "failures"_sr, stats.totals.assertions.failed-unexpectedExceptions );
+ xml.writeAttribute( "skipped"_sr, stats.totals.assertions.skipped );
xml.writeAttribute( "tests"_sr, stats.totals.assertions.total() );
xml.writeAttribute( "hostname"_sr, "tbd"_sr ); // !TBD
if( m_config->showDurations() == ShowDurations::Never )
@@ -244,7 +247,8 @@ namespace Catch {
void JunitReporter::writeAssertion( AssertionStats const& stats ) {
AssertionResult const& result = stats.assertionResult;
- if( !result.isOk() ) {
+ if ( !result.isOk() ||
+ result.getResultType() == ResultWas::ExplicitSkip ) {
std::string elementName;
switch( result.getResultType() ) {
case ResultWas::ThrewException:
@@ -256,7 +260,9 @@ namespace Catch {
case ResultWas::DidntThrowException:
elementName = "failure";
break;
-
+ case ResultWas::ExplicitSkip:
+ elementName = "skipped";
+ break;
// We should never see these here:
case ResultWas::Info:
case ResultWas::Warning:
@@ -274,7 +280,9 @@ namespace Catch {
xml.writeAttribute( "type"_sr, result.getTestMacroName() );
ReusableStringStream rss;
- if (stats.totals.assertions.total() > 0) {
+ if ( result.getResultType() == ResultWas::ExplicitSkip ) {
+ rss << "SKIPPED\n";
+ } else {
rss << "FAILED" << ":\n";
if (result.hasExpression()) {
rss << " ";
@@ -285,11 +293,9 @@ namespace Catch {
rss << "with expansion:\n";
rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n';
}
- } else {
- rss << '\n';
}
- if( !result.getMessage().empty() )
+ if( result.hasMessage() )
rss << result.getMessage() << '\n';
for( auto const& msg : stats.infoMessages )
if( msg.type == ResultWas::Info )
diff --git a/src/catch2/reporters/catch_reporter_multi.cpp b/src/catch2/reporters/catch_reporter_multi.cpp
index ebf28b64..531902be 100644
--- a/src/catch2/reporters/catch_reporter_multi.cpp
+++ b/src/catch2/reporters/catch_reporter_multi.cpp
@@ -114,7 +114,6 @@ namespace Catch {
}
}
- // The return value indicates if the messages buffer should be cleared:
void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) {
const bool reportByDefault =
assertionStats.assertionResult.getResultType() != ResultWas::Ok ||
diff --git a/src/catch2/reporters/catch_reporter_registrars.cpp b/src/catch2/reporters/catch_reporter_registrars.cpp
index a9787ce5..2a3ac957 100644
--- a/src/catch2/reporters/catch_reporter_registrars.cpp
+++ b/src/catch2/reporters/catch_reporter_registrars.cpp
@@ -8,6 +8,7 @@
#include <catch2/reporters/catch_reporter_registrars.hpp>
+#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
namespace Catch {
@@ -26,5 +27,10 @@ namespace Catch {
}
}
+ void registerListenerImpl( Detail::unique_ptr<EventListenerFactory> listenerFactory ) {
+ getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) );
+ }
+
+
} // namespace Detail
} // namespace Catch
diff --git a/src/catch2/reporters/catch_reporter_registrars.hpp b/src/catch2/reporters/catch_reporter_registrars.hpp
index fe8cf1ec..a93963f0 100644
--- a/src/catch2/reporters/catch_reporter_registrars.hpp
+++ b/src/catch2/reporters/catch_reporter_registrars.hpp
@@ -8,8 +8,6 @@
#ifndef CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
#define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_unique_name.hpp>
@@ -36,7 +34,8 @@ namespace Catch {
//! independent on the reporter's concrete type
void registerReporterImpl( std::string const& name,
IReporterFactoryPtr reporterPtr );
-
+ //! Actually registers the factory, independent on listener's concrete type
+ void registerListenerImpl( Detail::unique_ptr<EventListenerFactory> listenerFactory );
} // namespace Detail
class IEventListener;
@@ -97,7 +96,7 @@ namespace Catch {
public:
ListenerRegistrar(StringRef listenerName) {
- getMutableRegistryHub().registerListener( Detail::make_unique<TypedListenerFactory>(listenerName) );
+ registerListenerImpl( Detail::make_unique<TypedListenerFactory>(listenerName) );
}
};
}
@@ -118,7 +117,7 @@ namespace Catch {
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace { \
Catch::ListenerRegistrar<listenerType> INTERNAL_CATCH_UNIQUE_NAME( \
- catch_internal_RegistrarFor )( #listenerType ); \
+ catch_internal_RegistrarFor )( #listenerType##_catch_sr ); \
} \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
diff --git a/src/catch2/reporters/catch_reporter_sonarqube.cpp b/src/catch2/reporters/catch_reporter_sonarqube.cpp
index 365979f4..9c391b1f 100644
--- a/src/catch2/reporters/catch_reporter_sonarqube.cpp
+++ b/src/catch2/reporters/catch_reporter_sonarqube.cpp
@@ -40,7 +40,7 @@ namespace Catch {
}
void SonarQubeReporter::writeRun( TestRunNode const& runNode ) {
- std::map<std::string, std::vector<TestCaseNode const*>> testsPerFile;
+ std::map<StringRef, std::vector<TestCaseNode const*>> testsPerFile;
for ( auto const& child : runNode.children ) {
testsPerFile[child->value.testInfo->lineInfo.file].push_back(
@@ -52,7 +52,7 @@ namespace Catch {
}
}
- void SonarQubeReporter::writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes) {
+ void SonarQubeReporter::writeTestFile(StringRef filename, std::vector<TestCaseNode const*> const& testCaseNodes) {
XmlWriter::ScopedElement e = xml.scopedElement("file");
xml.writeAttribute("path"_sr, filename);
@@ -97,7 +97,8 @@ namespace Catch {
void SonarQubeReporter::writeAssertion(AssertionStats const& stats, bool okToFail) {
AssertionResult const& result = stats.assertionResult;
- if (!result.isOk()) {
+ if ( !result.isOk() ||
+ result.getResultType() == ResultWas::ExplicitSkip ) {
std::string elementName;
if (okToFail) {
elementName = "skipped";
@@ -108,15 +109,13 @@ namespace Catch {
elementName = "error";
break;
case ResultWas::ExplicitFailure:
- elementName = "failure";
- break;
case ResultWas::ExpressionFailed:
- elementName = "failure";
- break;
case ResultWas::DidntThrowException:
elementName = "failure";
break;
-
+ case ResultWas::ExplicitSkip:
+ elementName = "skipped";
+ break;
// We should never see these here:
case ResultWas::Info:
case ResultWas::Warning:
@@ -136,7 +135,9 @@ namespace Catch {
xml.writeAttribute("message"_sr, messageRss.str());
ReusableStringStream textRss;
- if (stats.totals.assertions.total() > 0) {
+ if ( result.getResultType() == ResultWas::ExplicitSkip ) {
+ textRss << "SKIPPED\n";
+ } else {
textRss << "FAILED:\n";
if (result.hasExpression()) {
textRss << '\t' << result.getExpressionInMacro() << '\n';
@@ -146,7 +147,7 @@ namespace Catch {
}
}
- if (!result.getMessage().empty())
+ if (result.hasMessage())
textRss << result.getMessage() << '\n';
for (auto const& msg : stats.infoMessages)
diff --git a/src/catch2/reporters/catch_reporter_sonarqube.hpp b/src/catch2/reporters/catch_reporter_sonarqube.hpp
index bc8f0332..cad6deec 100644
--- a/src/catch2/reporters/catch_reporter_sonarqube.hpp
+++ b/src/catch2/reporters/catch_reporter_sonarqube.hpp
@@ -41,7 +41,7 @@ namespace Catch {
void writeRun( TestRunNode const& groupNode );
- void writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes);
+ void writeTestFile(StringRef filename, std::vector<TestCaseNode const*> const& testCaseNodes);
void writeTestCase(TestCaseNode const& testCaseNode);
diff --git a/src/catch2/reporters/catch_reporter_streaming_base.hpp b/src/catch2/reporters/catch_reporter_streaming_base.hpp
index 13672a28..5448000c 100644
--- a/src/catch2/reporters/catch_reporter_streaming_base.hpp
+++ b/src/catch2/reporters/catch_reporter_streaming_base.hpp
@@ -8,7 +8,6 @@
#ifndef CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
#define CATCH_REPORTER_STREAMING_BASE_HPP_INCLUDED
-#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/reporters/catch_reporter_common_base.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
diff --git a/src/catch2/reporters/catch_reporter_tap.cpp b/src/catch2/reporters/catch_reporter_tap.cpp
index 59f8fb8b..67d406fb 100644
--- a/src/catch2/reporters/catch_reporter_tap.cpp
+++ b/src/catch2/reporters/catch_reporter_tap.cpp
@@ -14,7 +14,6 @@
#include <catch2/reporters/catch_reporter_helpers.hpp>
#include <algorithm>
-#include <iterator>
#include <ostream>
namespace Catch {
@@ -100,6 +99,12 @@ namespace Catch {
printIssue("explicitly"_sr);
printRemainingMessages(Colour::None);
break;
+ case ResultWas::ExplicitSkip:
+ printResultType(tapPassedString);
+ printIssue(" # SKIP"_sr);
+ printMessage();
+ printRemainingMessages();
+ break;
// These cases are here to prevent compiler warnings
case ResultWas::Unknown:
case ResultWas::FailureBit:
@@ -159,7 +164,7 @@ namespace Catch {
// using messages.end() directly (or auto) yields compilation error:
std::vector<MessageInfo>::const_iterator itEnd = messages.end();
- const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+ const std::size_t N = static_cast<std::size_t>(itEnd - itMessage);
stream << colourImpl->guardColour( colour ) << " with "
<< pluralise( N, "message"_sr ) << ':';
@@ -178,7 +183,7 @@ namespace Catch {
private:
std::ostream& stream;
AssertionResult const& result;
- std::vector<MessageInfo> messages;
+ std::vector<MessageInfo> const& messages;
std::vector<MessageInfo>::const_iterator itMessage;
bool printInfoMessages;
std::size_t counter;
diff --git a/src/catch2/reporters/catch_reporter_teamcity.cpp b/src/catch2/reporters/catch_reporter_teamcity.cpp
index 1d002c27..38aa55a6 100644
--- a/src/catch2/reporters/catch_reporter_teamcity.cpp
+++ b/src/catch2/reporters/catch_reporter_teamcity.cpp
@@ -45,7 +45,7 @@ namespace Catch {
} // end anonymous namespace
- TeamCityReporter::~TeamCityReporter() {}
+ TeamCityReporter::~TeamCityReporter() = default;
void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) {
m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name )
@@ -59,7 +59,8 @@ namespace Catch {
void TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) {
AssertionResult const& result = assertionStats.assertionResult;
- if (!result.isOk()) {
+ if ( !result.isOk() ||
+ result.getResultType() == ResultWas::ExplicitSkip ) {
ReusableStringStream msg;
if (!m_headerPrintedForThisSection)
@@ -84,6 +85,9 @@ namespace Catch {
case ResultWas::ExplicitFailure:
msg << "explicit failure";
break;
+ case ResultWas::ExplicitSkip:
+ msg << "explicit skip";
+ break;
// We shouldn't get here because of the isOk() test
case ResultWas::Ok:
@@ -111,18 +115,16 @@ namespace Catch {
" " << result.getExpandedExpression() << '\n';
}
- if (currentTestCaseInfo->okToFail()) {
+ if ( result.getResultType() == ResultWas::ExplicitSkip ) {
+ m_stream << "##teamcity[testIgnored";
+ } else if ( currentTestCaseInfo->okToFail() ) {
msg << "- failure ignore as test marked as 'ok to fail'\n";
- m_stream << "##teamcity[testIgnored"
- << " name='" << escape(currentTestCaseInfo->name) << '\''
- << " message='" << escape(msg.str()) << '\''
- << "]\n";
+ m_stream << "##teamcity[testIgnored";
} else {
- m_stream << "##teamcity[testFailed"
- << " name='" << escape(currentTestCaseInfo->name) << '\''
- << " message='" << escape(msg.str()) << '\''
- << "]\n";
+ m_stream << "##teamcity[testFailed";
}
+ m_stream << " name='" << escape( currentTestCaseInfo->name ) << '\''
+ << " message='" << escape( msg.str() ) << '\'' << "]\n";
}
m_stream.flush();
}
diff --git a/src/catch2/reporters/catch_reporter_xml.cpp b/src/catch2/reporters/catch_reporter_xml.cpp
index 57fa1cab..35a3028e 100644
--- a/src/catch2/reporters/catch_reporter_xml.cpp
+++ b/src/catch2/reporters/catch_reporter_xml.cpp
@@ -56,7 +56,7 @@ namespace Catch {
m_xml.startElement("Catch2TestRun")
.writeAttribute("name"_sr, m_config->name())
.writeAttribute("rng-seed"_sr, m_config->rngSeed())
- .writeAttribute("xml-format-version"_sr, 2)
+ .writeAttribute("xml-format-version"_sr, 3)
.writeAttribute("catch2-version"_sr, libraryVersion());
if ( m_config->testSpec().hasFilters() ) {
m_xml.writeAttribute( "filters"_sr, m_config->testSpec() );
@@ -66,7 +66,7 @@ namespace Catch {
void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
StreamingReporterBase::testCaseStarting(testInfo);
m_xml.startElement( "TestCase" )
- .writeAttribute( "name"_sr, trim( testInfo.name ) )
+ .writeAttribute( "name"_sr, trim( StringRef(testInfo.name) ) )
.writeAttribute( "tags"_sr, testInfo.tagsAsString() );
writeSourceInfo( testInfo.lineInfo );
@@ -80,7 +80,7 @@ namespace Catch {
StreamingReporterBase::sectionStarting( sectionInfo );
if( m_sectionDepth++ > 0 ) {
m_xml.startElement( "Section" )
- .writeAttribute( "name"_sr, trim( sectionInfo.name ) );
+ .writeAttribute( "name"_sr, trim( StringRef(sectionInfo.name) ) );
writeSourceInfo( sectionInfo.lineInfo );
m_xml.ensureTagClosed();
}
@@ -98,19 +98,22 @@ namespace Catch {
// Print any info messages in <Info> tags.
for( auto const& msg : assertionStats.infoMessages ) {
if( msg.type == ResultWas::Info && includeResults ) {
- m_xml.scopedElement( "Info" )
- .writeText( msg.message );
+ auto t = m_xml.scopedElement( "Info" );
+ writeSourceInfo( msg.lineInfo );
+ t.writeText( msg.message );
} else if ( msg.type == ResultWas::Warning ) {
- m_xml.scopedElement( "Warning" )
- .writeText( msg.message );
+ auto t = m_xml.scopedElement( "Warning" );
+ writeSourceInfo( msg.lineInfo );
+ t.writeText( msg.message );
}
}
}
// Drop out if result was successful but we're not printing them.
- if( !includeResults && result.getResultType() != ResultWas::Warning )
+ if ( !includeResults && result.getResultType() != ResultWas::Warning &&
+ result.getResultType() != ResultWas::ExplicitSkip ) {
return;
-
+ }
// Print the expression if there is one.
if( result.hasExpression() ) {
@@ -153,6 +156,12 @@ namespace Catch {
m_xml.writeText( result.getMessage() );
m_xml.endElement();
break;
+ case ResultWas::ExplicitSkip:
+ m_xml.startElement( "Skip" );
+ writeSourceInfo( result.getSourceInfo() );
+ m_xml.writeText( result.getMessage() );
+ m_xml.endElement();
+ break;
default:
break;
}
@@ -163,15 +172,18 @@ namespace Catch {
void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
StreamingReporterBase::sectionEnded( sectionStats );
- if( --m_sectionDepth > 0 ) {
- XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
- e.writeAttribute( "successes"_sr, sectionStats.assertions.passed );
- e.writeAttribute( "failures"_sr, sectionStats.assertions.failed );
- e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk );
-
- if ( m_config->showDurations() == ShowDurations::Always )
- e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds );
-
+ if ( --m_sectionDepth > 0 ) {
+ {
+ XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+ e.writeAttribute( "successes"_sr, sectionStats.assertions.passed );
+ e.writeAttribute( "failures"_sr, sectionStats.assertions.failed );
+ e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk );
+ e.writeAttribute( "skipped"_sr, sectionStats.assertions.skipped > 0 );
+
+ if ( m_config->showDurations() == ShowDurations::Always )
+ e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds );
+ }
+ // Ends assertion tag
m_xml.endElement();
}
}
@@ -180,14 +192,14 @@ namespace Catch {
StreamingReporterBase::testCaseEnded( testCaseStats );
XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() );
+ e.writeAttribute( "skips"_sr, testCaseStats.totals.assertions.skipped );
if ( m_config->showDurations() == ShowDurations::Always )
e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() );
-
if( !testCaseStats.stdOut.empty() )
- m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
+ m_xml.scopedElement( "StdOut" ).writeText( trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline );
if( !testCaseStats.stdErr.empty() )
- m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
+ m_xml.scopedElement( "StdErr" ).writeText( trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline );
m_xml.endElement();
}
@@ -197,11 +209,13 @@ namespace Catch {
m_xml.scopedElement( "OverallResults" )
.writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed )
.writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed )
- .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk );
+ .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk )
+ .writeAttribute( "skips"_sr, testRunStats.totals.assertions.skipped );
m_xml.scopedElement( "OverallResultsCases")
.writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed )
.writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed )
- .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk );
+ .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk )
+ .writeAttribute( "skips"_sr, testRunStats.totals.testCases.skipped );
m_xml.endElement();
}
@@ -220,26 +234,23 @@ namespace Catch {
}
void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
- m_xml.startElement("mean")
+ m_xml.scopedElement("mean")
.writeAttribute("value"_sr, benchmarkStats.mean.point.count())
.writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count())
.writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count())
.writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval);
- m_xml.endElement();
- m_xml.startElement("standardDeviation")
+ m_xml.scopedElement("standardDeviation")
.writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count())
.writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count())
.writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count())
.writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval);
- m_xml.endElement();
- m_xml.startElement("outliers")
+ m_xml.scopedElement("outliers")
.writeAttribute("variance"_sr, benchmarkStats.outlierVariance)
.writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild)
.writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe)
.writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild)
.writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe);
m_xml.endElement();
- m_xml.endElement();
}
void XmlReporter::benchmarkFailed(StringRef error) {
diff --git a/src/catch2/reporters/catch_reporters_all.hpp b/src/catch2/reporters/catch_reporters_all.hpp
index 16f7bd70..5c713fe1 100644
--- a/src/catch2/reporters/catch_reporters_all.hpp
+++ b/src/catch2/reporters/catch_reporters_all.hpp
@@ -28,6 +28,7 @@
#include <catch2/reporters/catch_reporter_cumulative_base.hpp>
#include <catch2/reporters/catch_reporter_event_listener.hpp>
#include <catch2/reporters/catch_reporter_helpers.hpp>
+#include <catch2/reporters/catch_reporter_json.hpp>
#include <catch2/reporters/catch_reporter_junit.hpp>
#include <catch2/reporters/catch_reporter_multi.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>