diff --git a/CMakeLists.txt b/CMakeLists.txt index c02705596b..7233477fb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ endif() project(Catch2 - VERSION 3.0.1 # CML version placeholder, don't delete + VERSION 3.1.0 # CML version placeholder, don't delete LANGUAGES CXX # HOMEPAGE_URL is not supported until CMake version 3.12, which # we do not target yet. diff --git a/docs/cmake-integration.md b/docs/cmake-integration.md index 9637be9b02..dc3efc9d9f 100644 --- a/docs/cmake-integration.md +++ b/docs/cmake-integration.md @@ -262,7 +262,7 @@ ParseAndAddCatchTests(bar) ### `CatchShardTests.cmake` -> `CatchShardTests.cmake` was introduced in Catch2 X.Y.Z. +> `CatchShardTests.cmake` was introduced in Catch2 3.1.0. `CatchShardTests.cmake` provides a function `catch_add_sharded_tests(TEST_BINARY)` that splits tests from `TEST_BINARY` diff --git a/docs/configuration.md b/docs/configuration.md index d857e79240..9fd7f58b6d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -103,7 +103,7 @@ Catch2 will register a `JUnit` reporter writing to a path pointed by `XML_OUTPUT > `CATCH_CONFIG_BAZEL_SUPPORT` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 3.0.1. -> `CATCH_CONFIG_BAZEL_SUPPORT` was [deprecated](https://github.com/catchorg/Catch2/pull/2459) in Catch2 X.Y.Z. +> `CATCH_CONFIG_BAZEL_SUPPORT` was [deprecated](https://github.com/catchorg/Catch2/pull/2459) in Catch2 3.1.0. ## C++11 toggles diff --git a/docs/release-notes.md b/docs/release-notes.md index 3a5de2762e..da2c598ceb 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,7 @@ # Release notes **Contents**
+[3.1.0](#310)
[3.0.1](#301)
[2.13.7](#2137)
[2.13.6](#2136)
@@ -49,6 +50,45 @@ [Even Older versions](#even-older-versions)
+## 3.1.0 + +### Improvements +* Improved suppression of `-Wparentheses` for older GCCs + * Turns out that even GCC 9 does not properly handle `_Pragma`s in the C++ frontend. +* Added type constraints onto `random` generator (#2433) + * These constraints copy what the standard says for the underlying `std::uniform_int_distribution` +* Suppressed -Wunused-variable from nvcc (#2306, #2427) +* Suppressed -Wunused-variable from MinGW (#2132) +* Added All/Any/NoneTrue range matchers (#2319) + * These check that all/any/none of boolean values in a range are true. +* The JUnit reporter now normalizes classnames from C++ namespaces to Java-like namespaces (#2468) + * This provides better support for other JUnit based tools. +* The Bazel support now understands `BAZEL_TEST` environment variable (#2459) + * The `CATCH_CONFIG_BAZEL_SUPPORT` configuration option is also still supported. +* Returned support for compiling Catch2 with GCC 5 (#2448) + * This required removing inherited constructors from Catch2's internals. + * I recommend updating to a newer GCC anyway. +* `catch_discover_tests` now has a new options for setting library load path(s) when running the Catch2 binary (#2467) + + +### Fixes +* Fixed crash when listing listeners without any registered listeners (#2442) +* Fixed nvcc compilation error in constructor benchmarking helper (#2477) +* Catch2's CMakeList supports pre-3.12 CMake again (#2428) + * The gain from requiring CMake 3.12 was very minor, but y'all should really update to newer CMake + + +### Miscellaneous +* Fixed SelfTest build on MinGW (#2447) +* The in-repo conan recipe exports the CMake helper (#2460) +* Added experimental CMake script to showcase using test case sharding together with CTest + * Compared to `catch_discover_tests`, it supports very limited number of options and customization +* Added documentation page on best practices when running Catch2 tests +* Catch2 can be built as a dynamic library (#2397, #2398) + * Note that Catch2 does not have visibility annotations, and you are responsible for ensuring correct visibility built into the resulting library. + + + ## 3.0.1 **Catch2 now uses statically compiled library as its distribution model. diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index bd8c8119ba..ecda2301b3 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.0.1 -// Generated: 2022-05-17 22:08:47.054486 +// Catch v3.1.0 +// Generated: 2022-07-17 20:14:05.885021 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -15,6 +15,51 @@ #include "catch_amalgamated.hpp" + + +namespace Catch { + namespace Benchmark { + namespace Detail { + ChronometerConcept::~ChronometerConcept() = default; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + + + +namespace Catch { + namespace Benchmark { + namespace Detail { + BenchmarkFunction::callable::~callable() = default; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + struct optimized_away_error : std::exception { + const char* what() const noexcept override; + }; + + const char* optimized_away_error::what() const noexcept { + return "could not measure benchmark, maybe it was optimized away"; + } + + void throw_optimized_away_error() { + Catch::throw_exception(optimized_away_error{}); + } + + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + // Adapted from donated nonius code. @@ -265,72 +310,6 @@ namespace Catch { } // namespace Catch -/** \file - * This is a special TU that combines what would otherwise be a very - * small benchmarking-related TUs into one bigger TU. - * - * The reason for this is compilation performance improvements by - * avoiding reparsing headers for many small TUs, instead having this - * one TU include bit more, but having it all parsed only once. - * - * To avoid heavy-tail problem with compilation times, each "subpart" - * of Catch2 has its own combined TU like this. - */ - -//////////////////////////////////////////// -// vvv formerly catch_chronometer.cpp vvv // -//////////////////////////////////////////// - - -namespace Catch { - namespace Benchmark { - namespace Detail { - ChronometerConcept::~ChronometerConcept() = default; - } // namespace Detail - } // namespace Benchmark -} // namespace Catch - - -/////////////////////////////////////////////////// -// vvv formerly catch_benchmark_function.cpp vvv // -/////////////////////////////////////////////////// - - -namespace Catch { - namespace Benchmark { - namespace Detail { - BenchmarkFunction::callable::~callable() = default; - } // namespace Detail - } // namespace Benchmark -} // namespace Catch - - -///////////////////////////////////////////////// -// vvv formerly catch_run_for_at_least.cpp vvv // -///////////////////////////////////////////////// - -#include - -namespace Catch { - namespace Benchmark { - namespace Detail { - struct optimized_away_error : std::exception { - const char* what() const noexcept override; - }; - - const char* optimized_away_error::what() const noexcept { - return "could not measure benchmark, maybe it was optimized away"; - } - - void throw_optimized_away_error() { - Catch::throw_exception(optimized_away_error{}); - } - - } // namespace Detail - } // namespace Benchmark -} // namespace Catch - - #include #include @@ -507,6 +486,28 @@ namespace Catch { +namespace { + bool provideBazelReporterOutput() { +#ifdef CATCH_CONFIG_BAZEL_SUPPORT + return true; +#else + +# if defined( _MSC_VER ) + // On Windows getenv throws a warning as there is no input validation, + // since the switch is hardcoded, this should not be an issue. +# pragma warning( push ) +# pragma warning( disable : 4996 ) +# endif + + return std::getenv( "BAZEL_TEST" ) != nullptr; + +# if defined( _MSC_VER ) +# pragma warning( pop ) +# endif +#endif + } +} + namespace Catch { bool operator==( ProcessedReporterSpec const& lhs, @@ -553,27 +554,27 @@ namespace Catch { } ); } -#if defined( CATCH_CONFIG_BAZEL_SUPPORT ) - // Register a JUnit reporter for Bazel. Bazel sets an environment - // variable with the path to XML output. If this file is written to - // during test, Bazel will not generate a default XML output. - // This allows the XML output file to contain higher level of detail - // than what is possible otherwise. + if(provideBazelReporterOutput()){ + // Register a JUnit reporter for Bazel. Bazel sets an environment + // variable with the path to XML output. If this file is written to + // during test, Bazel will not generate a default XML output. + // This allows the XML output file to contain higher level of detail + // than what is possible otherwise. # if defined( _MSC_VER ) - // On Windows getenv throws a warning as there is no input validation, - // since the key is hardcoded, this should not be an issue. -# pragma warning( push ) -# pragma warning( disable : 4996 ) + // On Windows getenv throws a warning as there is no input validation, + // since the key is hardcoded, this should not be an issue. +# pragma warning( push ) +# pragma warning( disable : 4996 ) # endif - const auto bazelOutputFilePtr = std::getenv( "XML_OUTPUT_FILE" ); + const auto bazelOutputFilePtr = std::getenv( "XML_OUTPUT_FILE" ); # if defined( _MSC_VER ) # pragma warning( pop ) # endif - if ( bazelOutputFilePtr != nullptr ) { - m_data.reporterSpecifications.push_back( - { "junit", std::string( bazelOutputFilePtr ), {}, {} } ); - } -#endif + if ( bazelOutputFilePtr != nullptr ) { + m_data.reporterSpecifications.push_back( + { "junit", std::string( bazelOutputFilePtr ), {}, {} } ); + } + } // We now fixup the reporter specs to handle default output spec, @@ -1180,6 +1181,22 @@ namespace Catch { + +namespace Catch { + + RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + +} + + + #include #include #include @@ -1873,7 +1890,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 0, 1, "", 0 ); + static Version version( 3, 1, 0, "", 0 ); return version; } @@ -1882,27 +1899,6 @@ namespace Catch { - -std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } - - -/** \file - * This is a special TU that combines what would otherwise be a very - * small generator-related TUs into one bigger TU. - * - * The reason for this is compilation performance improvements by - * avoiding reparsing headers for many small TUs, instead having this - * one TU include bit more, but having it all parsed only once. - * - * To avoid heavy-tail problem with compilation times, each "subpart" - * of Catch2 has its own combined TU like this. - */ - -//////////////////////////////////////////////////// -// vvv formerly catch_generator_exception.cpp vvv // -//////////////////////////////////////////////////// - - namespace Catch { const char* GeneratorException::what() const noexcept { @@ -1912,9 +1908,6 @@ namespace Catch { } // end namespace Catch -/////////////////////////////////////////// -// vvv formerly catch_generators.cpp vvv // -/////////////////////////////////////////// namespace Catch { @@ -1941,81 +1934,31 @@ namespace Detail { } // namespace Catch -/** \file - * This is a special TU that combines what would otherwise be a very - * small interfaces-related TUs into one bigger TU. - * - * The reason for this is compilation performance improvements by - * avoiding reparsing headers for many small TUs, instead having this - * one TU include bit more, but having it all parsed only once. - * - * To avoid heavy-tail problem with compilation times, each "subpart" - * of Catch2 has its own combined TU like this. - */ - -/////////////////////////////////////////////////// -// vvv formerly catch_interfaces_capture.cpp vvv // -/////////////////////////////////////////////////// - - -namespace Catch { - IResultCapture::~IResultCapture() = default; -} - - -////////////////////////////////////////////////// -// vvv formerly catch_interfaces_config.cpp vvv // -////////////////////////////////////////////////// - - -namespace Catch { - IConfig::~IConfig() = default; -} - -///////////////////////////////////////////////////// -// vvv formerly catch_interfaces_exception.cpp vvv // -///////////////////////////////////////////////////// -namespace Catch { - IExceptionTranslator::~IExceptionTranslator() = default; - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; -} +std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } -//////////////////////////////////////////////////////// -// vvv formerly catch_interfaces_registry_hub.cpp vvv // -//////////////////////////////////////////////////////// namespace Catch { - IRegistryHub::~IRegistryHub() = default; - IMutableRegistryHub::~IMutableRegistryHub() = default; + IResultCapture::~IResultCapture() = default; } -//////////////////////////////////////////////////// -// vvv formerly catch_interfaces_testcase.cpp vvv // -//////////////////////////////////////////////////// namespace Catch { - ITestInvoker::~ITestInvoker() = default; - ITestCaseRegistry::~ITestCaseRegistry() = default; + IConfig::~IConfig() = default; } -namespace Catch { - IReporterRegistry::~IReporterRegistry() = default; -} - - namespace Catch { - IReporterFactory::~IReporterFactory() = default; - EventListenerFactory::~EventListenerFactory() = default; + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; } @@ -2046,6 +1989,14 @@ namespace Catch { + +namespace Catch { + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} + + + #include #include #include @@ -2134,6 +2085,29 @@ namespace Catch { + +namespace Catch { + IReporterFactory::~IReporterFactory() = default; + EventListenerFactory::~EventListenerFactory() = default; +} + + + + +namespace Catch { + IReporterRegistry::~IReporterRegistry() = default; +} + + + + +namespace Catch { + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} + + + namespace Catch { AssertionHandler::AssertionHandler @@ -2672,248 +2646,33 @@ namespace Catch { } // namespace Catch -/** \file - * This is a special TU that combines what would otherwise be a very - * small top-level TUs into one bigger TU. - * - * The reason for this is compilation performance improvements by - * avoiding reparsing headers for many small TUs, instead having this - * one TU include bit more, but having it all parsed only once. - * - * To avoid heavy-tail problem with compilation times, each "subpart" - * of Catch2 has its own combined TU like this. - */ - -//////////////////////////////////////////////////////// -// vvv formerly catch_tag_alias_autoregistrar.cpp vvv // -//////////////////////////////////////////////////////// +#include +#include namespace Catch { - RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { - CATCH_TRY { - getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); - } CATCH_CATCH_ALL { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } - } - -} - + Clara::Parser makeCommandLineParser( ConfigData& config ) { -////////////////////////////////////////// -// vvv formerly catch_polyfills.cpp vvv // -////////////////////////////////////////// + using namespace Clara; -#include + auto const setWarning = [&]( std::string const& warning ) { + if ( warning == "NoAssertions" ) { + config.warnings = static_cast(config.warnings | WarnAbout::NoAssertions); + return ParserResult::ok( ParseResultType::Matched ); + } else if ( warning == "UnmatchedTestSpec" ) { + config.warnings = static_cast(config.warnings | WarnAbout::UnmatchedTestSpec); + return ParserResult::ok( ParseResultType::Matched ); + } -namespace Catch { - -#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) - bool isnan(float f) { - return std::isnan(f); - } - bool isnan(double d) { - return std::isnan(d); - } -#else - // For now we only use this for embarcadero - bool isnan(float f) { - return std::_isnan(f); - } - bool isnan(double d) { - return std::_isnan(d); - } -#endif - -} // end namespace Catch - - -//////////////////////////////////////////////////// -// vvv formerly catch_uncaught_exceptions.cpp vvv // -//////////////////////////////////////////////////// - - -#include - -namespace Catch { - bool uncaught_exceptions() { -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - return false; -#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) - return std::uncaught_exceptions() > 0; -#else - return std::uncaught_exception(); -#endif - } -} // end namespace Catch - - -//////////////////////////////////////////// -// vvv formerly catch_errno_guard.cpp vvv // -//////////////////////////////////////////// - -#include - -namespace Catch { - ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} - ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } -} - - -/////////////////////////////////////////// -// vvv formerly catch_decomposer.cpp vvv // -/////////////////////////////////////////// - -namespace Catch { - - ITransientExpression::~ITransientExpression() = default; - - void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { - if( lhs.size() + rhs.size() < 40 && - lhs.find('\n') == std::string::npos && - rhs.find('\n') == std::string::npos ) - os << lhs << ' ' << op << ' ' << rhs; - else - os << lhs << '\n' << op << '\n' << rhs; - } -} - - -/////////////////////////////////////////////////////////// -// vvv formerly catch_startup_exception_registry.cpp vvv // -/////////////////////////////////////////////////////////// - -namespace Catch { -#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { - CATCH_TRY { - m_exceptions.push_back(exception); - } CATCH_CATCH_ALL { - // If we run out of memory during start-up there's really not a lot more we can do about it - std::terminate(); - } - } - - std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { - return m_exceptions; - } -#endif - -} // end namespace Catch - - -////////////////////////////////////////////// -// vvv formerly catch_leak_detector.cpp vvv // -////////////////////////////////////////////// - - -#ifdef CATCH_CONFIG_WINDOWS_CRTDBG -#include - -namespace Catch { - - LeakDetector::LeakDetector() { - int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - flag |= _CRTDBG_LEAK_CHECK_DF; - flag |= _CRTDBG_ALLOC_MEM_DF; - _CrtSetDbgFlag(flag); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - // Change this to leaking allocation's number to break there - _CrtSetBreakAlloc(-1); - } -} - -#else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv - - Catch::LeakDetector::LeakDetector() {} - -#endif // CATCH_CONFIG_WINDOWS_CRTDBG - -Catch::LeakDetector::~LeakDetector() { - Catch::cleanUp(); -} - - -///////////////////////////////////////////// -// vvv formerly catch_message_info.cpp vvv // -///////////////////////////////////////////// - - -namespace Catch { - - MessageInfo::MessageInfo( StringRef _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - type( _type ), - sequence( ++globalCount ) - {} - - // This may need protecting if threading support is added - unsigned int MessageInfo::globalCount = 0; - -} // end namespace Catch - - - - -////////////////////////////////////////// -// vvv formerly catch_lazy_expr.cpp vvv // -////////////////////////////////////////// - -namespace Catch { - - auto operator << (std::ostream& os, LazyExpression const& lazyExpr) -> std::ostream& { - if (lazyExpr.m_isNegated) - os << '!'; - - if (lazyExpr) { - if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) - os << '(' << *lazyExpr.m_transientExpression << ')'; - else - os << *lazyExpr.m_transientExpression; - } else { - os << "{** error - unchecked empty expression requested **}"; - } - return os; - } - -} // namespace Catch - - - - -#include -#include - -namespace Catch { - - Clara::Parser makeCommandLineParser( ConfigData& config ) { - - using namespace Clara; - - auto const setWarning = [&]( std::string const& warning ) { - if ( warning == "NoAssertions" ) { - config.warnings = static_cast(config.warnings | WarnAbout::NoAssertions); - return ParserResult::ok( ParseResultType::Matched ); - } else if ( warning == "UnmatchedTestSpec" ) { - config.warnings = static_cast(config.warnings | WarnAbout::UnmatchedTestSpec); - return ParserResult::ok( ParseResultType::Matched ); - } - - return ParserResult ::runtimeError( - "Unrecognised warning option: '" + warning + '\'' ); - }; - auto const loadTestNamesFromFile = [&]( std::string const& filename ) { - std::ifstream f( filename.c_str() ); - if( !f.is_open() ) - return ParserResult::runtimeError( "Unable to load input file: '" + filename + '\'' ); + return ParserResult ::runtimeError( + "Unrecognised warning option: '" + warning + '\'' ); + }; + auto const loadTestNamesFromFile = [&]( std::string const& filename ) { + std::ifstream f( filename.c_str() ); + if( !f.is_open() ) + return ParserResult::runtimeError( "Unable to load input file: '" + filename + '\'' ); std::string line; while( std::getline( f, line ) ) { @@ -3425,25 +3184,22 @@ namespace Catch { Detail::unique_ptr makeColourImpl( ColourMode implSelection, IStream* stream ) { - if ( implSelection == ColourMode::None ) { - return Detail::make_unique( stream ); - } - if ( implSelection == ColourMode::ANSI ) { - return Detail::make_unique( stream ); - } #if defined( CATCH_CONFIG_COLOUR_WIN32 ) if ( implSelection == ColourMode::Win32 ) { return Detail::make_unique( stream ); } #endif + if ( implSelection == ColourMode::ANSI ) { + return Detail::make_unique( stream ); + } + if ( implSelection == ColourMode::None ) { + return Detail::make_unique( stream ); + } - // todo: check win32 eligibility under ifdef, otherwise ansi if ( implSelection == ColourMode::PlatformDefault) { -#if defined (CATCH_CONFIG_COLOUR_WIN32) +#if defined( CATCH_CONFIG_COLOUR_WIN32 ) if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { return Detail::make_unique( stream ); - } else { - return Detail::make_unique( stream ); } #endif if ( ANSIColourImpl::useImplementationForStream( *stream ) ) { @@ -3679,6 +3435,23 @@ namespace Catch { + +namespace Catch { + + ITransientExpression::~ITransientExpression() = default; + + void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { + if( lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ) + os << lhs << ' ' << op << ' ' << rhs; + else + os << lhs << '\n' << op << '\n' << rhs; + } +} + + + #include @@ -3779,6 +3552,16 @@ namespace Catch { + +#include + +namespace Catch { + ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } +} + + + namespace Catch { ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { @@ -4256,36 +4039,88 @@ namespace Detail { - namespace Catch { - namespace { - void listTests(IEventListener& reporter, IConfig const& config) { - auto const& testSpec = config.testSpec(); - auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - reporter.listTests(matchedTestCases); + auto operator << (std::ostream& os, LazyExpression const& lazyExpr) -> std::ostream& { + if (lazyExpr.m_isNegated) + os << '!'; + + if (lazyExpr) { + if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) + os << '(' << *lazyExpr.m_transientExpression << ')'; + else + os << *lazyExpr.m_transientExpression; + } else { + os << "{** error - unchecked empty expression requested **}"; } + return os; + } - void listTags(IEventListener& reporter, IConfig const& config) { - auto const& testSpec = config.testSpec(); - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); +} // namespace Catch - std::map tagCounts; - for (auto const& testCase : matchedTestCases) { - for (auto const& tagName : testCase.getTestCaseInfo().tags) { - auto it = tagCounts.find(tagName.original); - if (it == tagCounts.end()) - it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; - it->second.add(tagName.original); - } - } - std::vector infos; infos.reserve(tagCounts.size()); - for (auto& tagc : tagCounts) { - infos.push_back(CATCH_MOVE(tagc.second)); - } - reporter.listTags(infos); + +#ifdef CATCH_CONFIG_WINDOWS_CRTDBG +#include + +namespace Catch { + + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} + +#else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv + + Catch::LeakDetector::LeakDetector() {} + +#endif // CATCH_CONFIG_WINDOWS_CRTDBG + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} + + + + + +namespace Catch { + namespace { + + void listTests(IEventListener& reporter, IConfig const& config) { + auto const& testSpec = config.testSpec(); + auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + reporter.listTests(matchedTestCases); + } + + void listTags(IEventListener& reporter, IConfig const& config) { + auto const& testSpec = config.testSpec(); + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + + std::map tagCounts; + for (auto const& testCase : matchedTestCases) { + for (auto const& tagName : testCase.getTestCaseInfo().tags) { + auto it = tagCounts.find(tagName.original); + if (it == tagCounts.end()) + it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; + it->second.add(tagName.original); + } + } + + std::vector infos; infos.reserve(tagCounts.size()); + for (auto& tagc : tagCounts) { + infos.push_back(CATCH_MOVE(tagc.second)); + } + + reporter.listTags(infos); } void listReporters(IEventListener& reporter) { @@ -4390,6 +4225,25 @@ int main (int argc, char * argv[]) { + +namespace Catch { + + MessageInfo::MessageInfo( StringRef _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + +} // end namespace Catch + + + #include #include #include @@ -4528,6 +4382,32 @@ namespace Catch { + +#include + +namespace Catch { + +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch + + + namespace Catch { namespace { @@ -4784,7 +4664,7 @@ namespace Catch { return {}; } - auto ret = kvPairs.emplace( kv.key, kv.value ); + auto ret = kvPairs.emplace( std::string(kv.key), std::string(kv.value) ); if ( !ret.second ) { // Duplicated key. We might want to handle this differently, // e.g. by overwriting the existing value? @@ -5387,6 +5267,11 @@ namespace Catch { // before running the tests themselves, or the binary can crash // without failed test being reported. FatalConditionHandlerGuard _(&m_fatalConditionhandler); + // We keep having issue where some compilers warn about an unused + // variable, even though the type has non-trivial constructor and + // destructor. This is annoying and ugly, but it makes them stfu. + (void)_; + m_activeTestCase->invoke(); } @@ -5618,6 +5503,27 @@ namespace Catch { +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } + + std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } +#endif + +} // end namespace Catch + + + + #include @@ -6792,6 +6698,23 @@ namespace Catch { + +#include + +namespace Catch { + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; +#else + return std::uncaught_exception(); +#endif + } +} // end namespace Catch + + + namespace Catch { WildcardPattern::WildcardPattern( std::string const& pattern, @@ -7171,6 +7094,72 @@ namespace { + + +namespace Catch { +namespace Matchers { + + std::string MatcherUntypedBase::toString() const { + if (m_cachedToString.empty()) { + m_cachedToString = describe(); + } + return m_cachedToString; + } + + MatcherUntypedBase::~MatcherUntypedBase() = default; + +} // namespace Matchers +} // namespace Catch + + + + +namespace Catch { +namespace Matchers { + + std::string IsEmptyMatcher::describe() const { + return "is empty"; + } + + std::string HasSizeMatcher::describe() const { + ReusableStringStream sstr; + sstr << "has size == " << m_target_size; + return sstr.str(); + } + + IsEmptyMatcher IsEmpty() { + return {}; + } + + HasSizeMatcher SizeIs(std::size_t sz) { + return HasSizeMatcher{ sz }; + } + +} // end namespace Matchers +} // end namespace Catch + + + +namespace Catch { +namespace Matchers { + +bool ExceptionMessageMatcher::match(std::exception const& ex) const { + return ex.what() == m_message; +} + +std::string ExceptionMessageMatcher::describe() const { + return "exception message matches \"" + m_message + '"'; +} + +ExceptionMessageMatcher Message(std::string const& message) { + return ExceptionMessageMatcher(message); +} + +} // namespace Matchers +} // namespace Catch + + + #include #include #include @@ -7389,6 +7378,35 @@ WithinRelMatcher WithinRel(float target) { + +std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } +} + + + +namespace Catch { + namespace Matchers { + std::string AllTrueMatcher::describe() const { return "contains only true"; } + + AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } + + std::string NoneTrueMatcher::describe() const { return "contains no true"; } + + NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } + + std::string AnyTrueMatcher::describe() const { return "contains at least one true"; } + + AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } + } // namespace Matchers +} // namespace Catch + + + #include namespace Catch { @@ -7528,21 +7546,7 @@ namespace Matchers { } // namespace Catch -/** \file - * This is a special TU that combines what would otherwise be a very - * small matcher-related TUs into one bigger TU. - * - * The reason for this is compilation performance improvements by - * avoiding reparsing headers for many small TUs, instead having this - * one TU include bit more, but having it all parsed only once. - * - * To avoid heavy-tail problem with compilation times, each "subpart" - * of Catch2 has its own combined TU like this. - */ -////////////////////////////////////////////// -// vvv formerly catch_matchers_impl.cpp vvv // -////////////////////////////////////////////// namespace Catch { @@ -7558,385 +7562,32 @@ namespace Catch { } // namespace Catch -////////////////////////////////////////////////////////////// -// vvv formerly catch_matchers_container_properties.cpp vvv // -////////////////////////////////////////////////////////////// + +#include namespace Catch { -namespace Matchers { - std::string IsEmptyMatcher::describe() const { - return "is empty"; + AutomakeReporter::~AutomakeReporter() {} + + 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()) { + m_stream << "PASS"; + } else if (_testCaseStats.totals.assertions.allOk()) { + m_stream << "XFAIL"; + } else { + m_stream << "FAIL"; + } + m_stream << ' ' << _testCaseStats.testInfo->name << '\n'; + StreamingReporterBase::testCaseEnded(_testCaseStats); } - std::string HasSizeMatcher::describe() const { - ReusableStringStream sstr; - sstr << "has size == " << m_target_size; - return sstr.str(); - } - - IsEmptyMatcher IsEmpty() { - return {}; - } - - HasSizeMatcher SizeIs(std::size_t sz) { - return HasSizeMatcher{ sz }; - } - -} // end namespace Matchers -} // end namespace Catch - - - -///////////////////////////////////////// -// vvv formerly catch_matchers.cpp vvv // -///////////////////////////////////////// - - -namespace Catch { -namespace Matchers { - - std::string MatcherUntypedBase::toString() const { - if (m_cachedToString.empty()) { - m_cachedToString = describe(); - } - return m_cachedToString; - } - - MatcherUntypedBase::~MatcherUntypedBase() = default; - -} // namespace Matchers -} // namespace Catch - - - -/////////////////////////////////////////////////// -// vvv formerly catch_matchers_predicate.cpp vvv // -/////////////////////////////////////////////////// - -std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) { - if (desc.empty()) { - return "matches undescribed predicate"; - } else { - return "matches predicate: \"" + desc + '"'; - } -} - - - - - -/////////////////////////////////////////////////// -// vvv formerly catch_matchers_exception.cpp vvv // -/////////////////////////////////////////////////// - -namespace Catch { -namespace Matchers { - -bool ExceptionMessageMatcher::match(std::exception const& ex) const { - return ex.what() == m_message; -} - -std::string ExceptionMessageMatcher::describe() const { - return "exception message matches \"" + m_message + '"'; -} - -ExceptionMessageMatcher Message(std::string const& message) { - return ExceptionMessageMatcher(message); -} - -} // namespace Matchers -} // namespace Catch - - - -#include - -namespace Catch { - - AutomakeReporter::~AutomakeReporter() {} - - 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()) { - m_stream << "PASS"; - } else if (_testCaseStats.totals.assertions.allOk()) { - m_stream << "XFAIL"; - } else { - m_stream << "FAIL"; - } - m_stream << ' ' << _testCaseStats.testInfo->name << '\n'; - StreamingReporterBase::testCaseEnded(_testCaseStats); - } - - void AutomakeReporter::skipTest(TestCaseInfo const& testInfo) { - m_stream << ":test-result: SKIP " << testInfo.name << '\n'; - } - -} // end namespace Catch - - -/** \file - * This is a special TU that combines what would otherwise be a very - * small reporter-related TUs into one bigger TU. - * - * The reason for this is compilation performance improvements by - * avoiding reparsing headers for many small TUs, instead having this - * one TU include bit more, but having it all parsed only once. - * - * To avoid heavy-tail problem with compilation times, each "subpart" - * of Catch2 has its own combined TU like this. - */ - - -#include -#include -#include -#include -#include - -namespace Catch { - - namespace { - void listTestNamesOnly(std::ostream& out, - std::vector const& tests) { - for (auto const& test : tests) { - auto const& testCaseInfo = test.getTestCaseInfo(); - - if (startsWith(testCaseInfo.name, '#')) { - out << '"' << testCaseInfo.name << '"'; - } else { - out << testCaseInfo.name; - } - - out << '\n'; - } - out << std::flush; - } - } // end unnamed namespace - - - // Because formatting using c++ streams is stateful, drop down to C is - // required Alternatively we could use stringstream, but its performance - // is... not good. - std::string getFormattedDuration( double duration ) { - // Max exponent + 1 is required to represent the whole part - // + 1 for decimal point - // + 3 for the 3 decimal places - // + 1 for null terminator - const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; - char buffer[maxDoubleSize]; - - // Save previous errno, to prevent sprintf from overwriting it - ErrnoGuard guard; -#ifdef _MSC_VER - size_t printedLength = static_cast( - sprintf_s( buffer, "%.3f", duration ) ); -#else - size_t printedLength = static_cast( - std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) ); -#endif - return std::string( buffer, printedLength ); - } - - bool shouldShowDuration( IConfig const& config, double duration ) { - if ( config.showDurations() == ShowDurations::Always ) { - return true; - } - if ( config.showDurations() == ShowDurations::Never ) { - return false; - } - const double min = config.minDuration(); - return min >= 0 && duration >= min; - } - - std::string serializeFilters( std::vector const& filters ) { - // We add a ' ' separator between each filter - size_t serialized_size = filters.size() - 1; - for (auto const& filter : filters) { - serialized_size += filter.size(); - } - - std::string serialized; - serialized.reserve(serialized_size); - bool first = true; - - for (auto const& filter : filters) { - if (!first) { - serialized.push_back(' '); - } - first = false; - serialized.append(filter); - } - - return serialized; - } - - std::ostream& operator<<( std::ostream& out, lineOfChars value ) { - for ( size_t idx = 0; idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++idx ) { - out.put( value.c ); - } - return out; - } - - void - defaultListReporters( std::ostream& out, - std::vector const& descriptions, - Verbosity verbosity ) { - out << "Available reporters:\n"; - const auto maxNameLen = - std::max_element( descriptions.begin(), - descriptions.end(), - []( ReporterDescription const& lhs, - ReporterDescription const& rhs ) { - return lhs.name.size() < rhs.name.size(); - } ) - ->name.size(); - - for ( auto const& desc : descriptions ) { - if ( verbosity == Verbosity::Quiet ) { - out << TextFlow::Column( desc.name ) - .indent( 2 ) - .width( 5 + maxNameLen ) - << '\n'; - } else { - out << TextFlow::Column( desc.name + ':' ) - .indent( 2 ) - .width( 5 + maxNameLen ) + - TextFlow::Column( desc.description ) - .initialIndent( 0 ) - .indent( 2 ) - .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) - << '\n'; - } - } - out << '\n' << std::flush; - } - - void defaultListListeners( std::ostream& out, - std::vector const& descriptions ) { - const auto maxNameLen = - std::max_element( descriptions.begin(), - descriptions.end(), - []( ListenerDescription const& lhs, - ListenerDescription const& rhs ) { - return lhs.name.size() < rhs.name.size(); - } ) - ->name.size(); - - out << "Registered listeners:\n"; - for ( auto const& desc : descriptions ) { - out << TextFlow::Column( static_cast( desc.name ) + - ':' ) - .indent( 2 ) - .width( maxNameLen + 5 ) + - TextFlow::Column( desc.description ) - .initialIndent( 0 ) - .indent( 2 ) - .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) - << '\n'; - } - - out << '\n' << std::flush; - } - - void defaultListTags( std::ostream& out, - std::vector const& tags, - bool isFiltered ) { - if ( isFiltered ) { - out << "Tags for matching test cases:\n"; - } else { - out << "All available tags:\n"; - } - - for ( auto const& tagCount : tags ) { - ReusableStringStream rss; - rss << " " << std::setw( 2 ) << tagCount.count << " "; - auto str = rss.str(); - auto wrapper = TextFlow::Column( tagCount.all() ) - .initialIndent( 0 ) - .indent( str.size() ) - .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 ); - out << str << wrapper << '\n'; - } - out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush; - } - - void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector const& tests, bool isFiltered, Verbosity verbosity) { - // We special case this to provide the equivalent of old - // `--list-test-names-only`, which could then be used by the - // `--input-file` option. - if (verbosity == Verbosity::Quiet) { - listTestNamesOnly(out, tests); - return; - } - - if (isFiltered) { - out << "Matching test cases:\n"; - } else { - out << "All available test cases:\n"; - } - - for (auto const& test : tests) { - auto const& testCaseInfo = test.getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - auto colourGuard = streamColour->guardColour( colour ).engage( out ); - - out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n'; - if (verbosity >= Verbosity::High) { - out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n'; - } - if (!testCaseInfo.tags.empty() && - verbosity > Verbosity::Quiet) { - out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n'; - } - } - - if (isFiltered) { - out << pluralise(tests.size(), "matching test case"_sr); - } else { - out << pluralise(tests.size(), "test case"_sr); - } - out << "\n\n" << std::flush; + void AutomakeReporter::skipTest(TestCaseInfo const& testInfo) { + m_stream << ":test-result: SKIP " << testInfo.name << '\n'; } -} // namespace Catch - - - -namespace Catch { - - void EventListenerBase::fatalErrorEncountered( StringRef ) {} - - void EventListenerBase::benchmarkPreparing( StringRef ) {} - void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {} - void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {} - void EventListenerBase::benchmarkFailed( StringRef ) {} - - void EventListenerBase::assertionStarting( AssertionInfo const& ) {} - - void EventListenerBase::assertionEnded( AssertionStats const& ) {} - void EventListenerBase::listReporters( - std::vector const& ) {} - void EventListenerBase::listListeners( - std::vector const& ) {} - void EventListenerBase::listTests( std::vector const& ) {} - void EventListenerBase::listTags( std::vector const& ) {} - void EventListenerBase::noMatchingTestCases( StringRef ) {} - void EventListenerBase::reportInvalidTestSpec( StringRef ) {} - void EventListenerBase::testRunStarting( TestRunInfo const& ) {} - void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {} - void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {} - void EventListenerBase::sectionStarting( SectionInfo const& ) {} - void EventListenerBase::sectionEnded( SectionStats const& ) {} - void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {} - void EventListenerBase::testCaseEnded( TestCaseStats const& ) {} - void EventListenerBase::testRunEnded( TestRunStats const& ) {} - void EventListenerBase::skipTest( TestCaseInfo const& ) {} -} // namespace Catch +} // end namespace Catch @@ -8989,129 +8640,386 @@ namespace Catch { } // namespace - namespace Detail { - AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( - AssertionStats const& assertion ): - m_assertion( assertion ) {} + namespace Detail { + AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( + AssertionStats const& assertion ): + m_assertion( assertion ) {} + + AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( + BenchmarkStats<> const& benchmark ): + m_benchmark( benchmark ) {} + + bool AssertionOrBenchmarkResult::isAssertion() const { + return m_assertion.some(); + } + bool AssertionOrBenchmarkResult::isBenchmark() const { + return m_benchmark.some(); + } + + AssertionStats const& AssertionOrBenchmarkResult::asAssertion() const { + assert(m_assertion.some()); + + return *m_assertion; + } + BenchmarkStats<> const& AssertionOrBenchmarkResult::asBenchmark() const { + assert(m_benchmark.some()); + + return *m_benchmark; + } + + } + + CumulativeReporterBase::~CumulativeReporterBase() = default; + + void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { + m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats); + } + + void + CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + SectionNode* node; + if ( m_sectionStack.empty() ) { + if ( !m_rootSection ) { + m_rootSection = + Detail::make_unique( incompleteStats ); + } + node = m_rootSection.get(); + } else { + SectionNode& parentNode = *m_sectionStack.back(); + auto it = std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if ( it == parentNode.childSections.end() ) { + auto newNode = + Detail::make_unique( incompleteStats ); + node = newNode.get(); + parentNode.childSections.push_back( CATCH_MOVE( newNode ) ); + } else { + node = it->get(); + } + } + + m_deepestSection = node; + m_sectionStack.push_back( node ); + } + + void CumulativeReporterBase::assertionEnded( + AssertionStats const& assertionStats ) { + assert( !m_sectionStack.empty() ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + if ( m_shouldStoreFailedAssertions && + !assertionStats.assertionResult.isOk() ) { + static_cast( + assertionStats.assertionResult.getExpandedExpression() ); + } + if ( m_shouldStoreSuccesfulAssertions && + assertionStats.assertionResult.isOk() ) { + static_cast( + assertionStats.assertionResult.getExpandedExpression() ); + } + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats ); + } + + void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + + void CumulativeReporterBase::testCaseEnded( + TestCaseStats const& testCaseStats ) { + auto node = Detail::make_unique( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( CATCH_MOVE(m_rootSection) ); + m_testCases.push_back( CATCH_MOVE(node) ); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + + + void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) { + assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run"); + m_testRun = Detail::make_unique( testRunStats ); + m_testRun->children.swap( m_testCases ); + testRunEndedCumulative(); + } + + bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const { + return std::any_of( + assertionsAndBenchmarks.begin(), + assertionsAndBenchmarks.end(), + []( Detail::AssertionOrBenchmarkResult const& res ) { + return res.isAssertion(); + } ); + } + +} // end namespace Catch + + + + +namespace Catch { + + void EventListenerBase::fatalErrorEncountered( StringRef ) {} + + void EventListenerBase::benchmarkPreparing( StringRef ) {} + void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {} + void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {} + void EventListenerBase::benchmarkFailed( StringRef ) {} + + void EventListenerBase::assertionStarting( AssertionInfo const& ) {} + + void EventListenerBase::assertionEnded( AssertionStats const& ) {} + void EventListenerBase::listReporters( + std::vector const& ) {} + void EventListenerBase::listListeners( + std::vector const& ) {} + void EventListenerBase::listTests( std::vector const& ) {} + void EventListenerBase::listTags( std::vector const& ) {} + void EventListenerBase::noMatchingTestCases( StringRef ) {} + void EventListenerBase::reportInvalidTestSpec( StringRef ) {} + void EventListenerBase::testRunStarting( TestRunInfo const& ) {} + void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {} + void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {} + void EventListenerBase::sectionStarting( SectionInfo const& ) {} + void EventListenerBase::sectionEnded( SectionStats const& ) {} + void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {} + void EventListenerBase::testCaseEnded( TestCaseStats const& ) {} + void EventListenerBase::testRunEnded( TestRunStats const& ) {} + void EventListenerBase::skipTest( TestCaseInfo const& ) {} +} // namespace Catch + + + + +#include +#include +#include +#include +#include + +namespace Catch { + + namespace { + void listTestNamesOnly(std::ostream& out, + std::vector const& tests) { + for (auto const& test : tests) { + auto const& testCaseInfo = test.getTestCaseInfo(); + + if (startsWith(testCaseInfo.name, '#')) { + out << '"' << testCaseInfo.name << '"'; + } else { + out << testCaseInfo.name; + } + + out << '\n'; + } + out << std::flush; + } + } // end unnamed namespace + + + // Because formatting using c++ streams is stateful, drop down to C is + // required Alternatively we could use stringstream, but its performance + // is... not good. + std::string getFormattedDuration( double duration ) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; - AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( - BenchmarkStats<> const& benchmark ): - m_benchmark( benchmark ) {} + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; +#ifdef _MSC_VER + size_t printedLength = static_cast( + sprintf_s( buffer, "%.3f", duration ) ); +#else + size_t printedLength = static_cast( + std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) ); +#endif + return std::string( buffer, printedLength ); + } - bool AssertionOrBenchmarkResult::isAssertion() const { - return m_assertion.some(); + bool shouldShowDuration( IConfig const& config, double duration ) { + if ( config.showDurations() == ShowDurations::Always ) { + return true; } - bool AssertionOrBenchmarkResult::isBenchmark() const { - return m_benchmark.some(); + if ( config.showDurations() == ShowDurations::Never ) { + return false; } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } - AssertionStats const& AssertionOrBenchmarkResult::asAssertion() const { - assert(m_assertion.some()); - - return *m_assertion; + std::string serializeFilters( std::vector const& filters ) { + // We add a ' ' separator between each filter + size_t serialized_size = filters.size() - 1; + for (auto const& filter : filters) { + serialized_size += filter.size(); } - BenchmarkStats<> const& AssertionOrBenchmarkResult::asBenchmark() const { - assert(m_benchmark.some()); - return *m_benchmark; + std::string serialized; + serialized.reserve(serialized_size); + bool first = true; + + for (auto const& filter : filters) { + if (!first) { + serialized.push_back(' '); + } + first = false; + serialized.append(filter); } + return serialized; } - CumulativeReporterBase::~CumulativeReporterBase() = default; - - void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { - m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats); + std::ostream& operator<<( std::ostream& out, lineOfChars value ) { + for ( size_t idx = 0; idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++idx ) { + out.put( value.c ); + } + return out; } void - CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - SectionNode* node; - if ( m_sectionStack.empty() ) { - if ( !m_rootSection ) { - m_rootSection = - Detail::make_unique( incompleteStats ); - } - node = m_rootSection.get(); - } else { - SectionNode& parentNode = *m_sectionStack.back(); - auto it = std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if ( it == parentNode.childSections.end() ) { - auto newNode = - Detail::make_unique( incompleteStats ); - node = newNode.get(); - parentNode.childSections.push_back( CATCH_MOVE( newNode ) ); + defaultListReporters( std::ostream& out, + std::vector const& descriptions, + Verbosity verbosity ) { + out << "Available reporters:\n"; + const auto maxNameLen = + std::max_element( descriptions.begin(), + descriptions.end(), + []( ReporterDescription const& lhs, + ReporterDescription const& rhs ) { + return lhs.name.size() < rhs.name.size(); + } ) + ->name.size(); + + for ( auto const& desc : descriptions ) { + if ( verbosity == Verbosity::Quiet ) { + out << TextFlow::Column( desc.name ) + .indent( 2 ) + .width( 5 + maxNameLen ) + << '\n'; } else { - node = it->get(); + out << TextFlow::Column( desc.name + ':' ) + .indent( 2 ) + .width( 5 + maxNameLen ) + + TextFlow::Column( desc.description ) + .initialIndent( 0 ) + .indent( 2 ) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) + << '\n'; } } - - m_deepestSection = node; - m_sectionStack.push_back( node ); + out << '\n' << std::flush; } - void CumulativeReporterBase::assertionEnded( - AssertionStats const& assertionStats ) { - assert( !m_sectionStack.empty() ); - // AssertionResult holds a pointer to a temporary DecomposedExpression, - // which getExpandedExpression() calls to build the expression string. - // Our section stack copy of the assertionResult will likely outlive the - // temporary, so it must be expanded or discarded now to avoid calling - // a destroyed object later. - if ( m_shouldStoreFailedAssertions && - !assertionStats.assertionResult.isOk() ) { - static_cast( - assertionStats.assertionResult.getExpandedExpression() ); + void defaultListListeners( std::ostream& out, + std::vector const& descriptions ) { + out << "Registered listeners:\n"; + + if(descriptions.empty()) { + return; } - if ( m_shouldStoreSuccesfulAssertions && - assertionStats.assertionResult.isOk() ) { - static_cast( - assertionStats.assertionResult.getExpandedExpression() ); + + const auto maxNameLen = + std::max_element( descriptions.begin(), + descriptions.end(), + []( ListenerDescription const& lhs, + ListenerDescription const& rhs ) { + return lhs.name.size() < rhs.name.size(); + } ) + ->name.size(); + + for ( auto const& desc : descriptions ) { + out << TextFlow::Column( static_cast( desc.name ) + + ':' ) + .indent( 2 ) + .width( maxNameLen + 5 ) + + TextFlow::Column( desc.description ) + .initialIndent( 0 ) + .indent( 2 ) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) + << '\n'; } - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats ); - } - void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); + out << '\n' << std::flush; } - void CumulativeReporterBase::testCaseEnded( - TestCaseStats const& testCaseStats ) { - auto node = Detail::make_unique( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( CATCH_MOVE(m_rootSection) ); - m_testCases.push_back( CATCH_MOVE(node) ); + void defaultListTags( std::ostream& out, + std::vector const& tags, + bool isFiltered ) { + if ( isFiltered ) { + out << "Tags for matching test cases:\n"; + } else { + out << "All available tags:\n"; + } - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; + for ( auto const& tagCount : tags ) { + ReusableStringStream rss; + rss << " " << std::setw( 2 ) << tagCount.count << " "; + auto str = rss.str(); + auto wrapper = TextFlow::Column( tagCount.all() ) + .initialIndent( 0 ) + .indent( str.size() ) + .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 ); + out << str << wrapper << '\n'; + } + out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush; } + void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector const& tests, bool isFiltered, Verbosity verbosity) { + // We special case this to provide the equivalent of old + // `--list-test-names-only`, which could then be used by the + // `--input-file` option. + if (verbosity == Verbosity::Quiet) { + listTestNamesOnly(out, tests); + return; + } - void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) { - assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run"); - m_testRun = Detail::make_unique( testRunStats ); - m_testRun->children.swap( m_testCases ); - testRunEndedCumulative(); - } + if (isFiltered) { + out << "Matching test cases:\n"; + } else { + out << "All available test cases:\n"; + } - bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const { - return std::any_of( - assertionsAndBenchmarks.begin(), - assertionsAndBenchmarks.end(), - []( Detail::AssertionOrBenchmarkResult const& res ) { - return res.isAssertion(); - } ); + for (auto const& test : tests) { + auto const& testCaseInfo = test.getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + auto colourGuard = streamColour->guardColour( colour ).engage( out ); + + out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n'; + if (verbosity >= Verbosity::High) { + out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n'; + } + if (!testCaseInfo.tags.empty() && + verbosity > Verbosity::Quiet) { + out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n'; + } + } + + if (isFiltered) { + out << pluralise(tests.size(), "matching test case"_sr); + } else { + out << pluralise(tests.size(), "test case"_sr); + } + out << "\n\n" << std::flush; } -} // end namespace Catch +} // namespace Catch @@ -9168,6 +9076,15 @@ namespace Catch { return rss.str(); } + static void normalizeNamespaceMarkers(std::string& str) { + std::size_t pos = str.find( "::" ); + while ( pos != str.npos ) { + str.replace( pos, 2, "." ); + pos += 1; + pos = str.find( "::", pos ); + } + } + } // anonymous namespace JunitReporter::JunitReporter( ReporterConfig&& _config ) @@ -9271,6 +9188,8 @@ namespace Catch { if ( !m_config->name().empty() ) className = static_cast(m_config->name()) + '.' + className; + normalizeNamespaceMarkers(className); + writeSection( className, "", rootSection, stats.testInfo->okToFail() ); } diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 5c1caa361e..757e10c5ac 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.0.1 -// Generated: 2022-05-17 22:08:46.674860 +// Catch v3.1.0 +// Generated: 2022-07-17 20:14:04.055157 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -360,11 +360,23 @@ namespace Catch { #endif +#if defined(__CUDACC__) && !defined(__clang__) +# 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" ) +#endif + +// clang-cl defines _MSC_VER as well as __clang__, which could cause the +// start/stop internal suppression macros to be double defined. #if defined(__clang__) && !defined(_MSC_VER) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) +#endif // __clang__ && !_MSC_VER + +#if defined(__clang__) + // As of this writing, IBM XL's implementation of __builtin_constant_p has a bug // which results in calls to destructors being emitted for each temporary, // without a matching initialization. In practice, this can result in something @@ -2320,7 +2332,7 @@ namespace Catch { double z1 = normal_quantile((1. - confidence_level) / 2.); auto cumn = [n]( double x ) -> long { - return std::lround( normal_cdf( x ) * n ); + return std::lround( normal_cdf( x ) * static_cast(n) ); }; auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; double b1 = bias + z1; @@ -2730,7 +2742,7 @@ namespace Catch { } T const& stored_object() const { - return *static_cast(static_cast(data)); + return *static_cast(static_cast(data)); } @@ -5568,9 +5580,9 @@ namespace Catch { #endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED -// We need this suppression to leak, because it took until GCC 9 +// We need this suppression to leak, because it took until GCC 10 // for the front end to handle local suppression via _Pragma properly -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 9 +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 #pragma GCC diagnostic ignored "-Wparentheses" #endif @@ -5879,6 +5891,7 @@ struct AutoReg : Detail::NonCopyable { 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 */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ static void TestName() @@ -5889,6 +5902,7 @@ struct AutoReg : Detail::NonCopyable { #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 @@ -5896,6 +5910,7 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ namespace{ \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ void test(); \ @@ -5912,6 +5927,7 @@ struct AutoReg : Detail::NonCopyable { do { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ } while(false) @@ -7046,8 +7062,8 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 0 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 1 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7752,12 +7768,17 @@ class RandomIntegerGenerator final : public IGenerator { } }; -// TODO: Ideally this would be also constrained against the various char types, -// but I don't expect users to run into that in practice. template -std::enable_if_t::value && !std::is_same::value, -GeneratorWrapper> +std::enable_if_t::value, GeneratorWrapper> random(T a, T b) { + static_assert( + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value, + "The requested type is not supported by the underlying random distributions from std" ); return GeneratorWrapper( Catch::Detail::make_unique>(a, b, Detail::getSeed()) ); @@ -8097,6 +8118,9 @@ namespace Catch { #ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED #define CATCH_CONSOLE_WIDTH_HPP_INCLUDED +// This include must be kept so that user's configured value for CONSOLE_WIDTH +// is used before we attempt to provide a default value + #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 #endif @@ -10455,7 +10479,6 @@ namespace Catch { class IsEmptyMatcher final : public MatcherGenericBase { public: - // todo: Use polyfills template bool match(RangeLike&& rng) const { #if defined(CATCH_CONFIG_POLYFILL_NONMEMBER_CONTAINER_ACCESS) @@ -10856,7 +10879,55 @@ namespace Catch { } }; - // Creates a matcher that checks whether a range contains element matching a matcher + // Matcher for checking that all elements in range are true. + class AllTrueMatcher final : public MatcherGenericBase { + public: + std::string describe() const override; + + template + bool match(RangeLike&& rng) const { + for (auto&& elem : rng) { + if (!elem) { + return false; + } + } + return true; + } + }; + + // Matcher for checking that no element in range is true. + class NoneTrueMatcher final : public MatcherGenericBase { + public: + std::string describe() const override; + + template + bool match(RangeLike&& rng) const { + for (auto&& elem : rng) { + if (elem) { + return false; + } + } + return true; + } + }; + + // Matcher for checking that any element in range is true. + class AnyTrueMatcher final : public MatcherGenericBase { + public: + std::string describe() const override; + + template + bool match(RangeLike&& rng) const { + for (auto&& elem : rng) { + if (elem) { + return true; + } + } + return false; + } + }; + + // Creates a matcher that checks whether all elements in a range match a matcher template AllMatchMatcher AllMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; @@ -10873,6 +10944,15 @@ namespace Catch { AnyMatchMatcher AnyMatch(Matcher&& matcher) { return { CATCH_FORWARD(matcher) }; } + + // Creates a matcher that checks whether all elements in a range are true + AllTrueMatcher AllTrue(); + + // Creates a matcher that checks whether no element in a range is true + NoneTrueMatcher NoneTrue(); + + // Creates a matcher that checks whether any element in a range is true + AnyTrueMatcher AnyTrue(); } } @@ -11252,7 +11332,11 @@ namespace Catch { class StreamingReporterBase : public ReporterBase { public: - using ReporterBase::ReporterBase; + // GCC5 compat: we cannot use inherited constructor, because it + // doesn't implement backport of P0136 + StreamingReporterBase(ReporterConfig&& _config): + ReporterBase(CATCH_MOVE(_config)) + {} ~StreamingReporterBase() override; void benchmarkPreparing( StringRef ) override {} @@ -11309,7 +11393,11 @@ namespace Catch { class AutomakeReporter final : public StreamingReporterBase { public: - using StreamingReporterBase::StreamingReporterBase; + // GCC5 compat: we cannot use inherited constructor, because it + // doesn't implement backport of P0136 + AutomakeReporter(ReporterConfig&& _config): + StreamingReporterBase(CATCH_MOVE(_config)) + {} ~AutomakeReporter() override; static std::string getDescription() { @@ -11505,7 +11593,11 @@ namespace Catch { using TestCaseNode = Node; using TestRunNode = Node; - using ReporterBase::ReporterBase; + // GCC5 compat: we cannot use inherited constructor, because it + // doesn't implement backport of P0136 + CumulativeReporterBase(ReporterConfig&& _config): + ReporterBase(CATCH_MOVE(_config)) + {} ~CumulativeReporterBase() override; void benchmarkPreparing( StringRef ) override {} diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp index ed5392970d..a7075f3006 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, 0, 1, "", 0 ); + static Version version( 3, 1, 0, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 29e185673b..c7212e3a6a 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 0 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 1 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED