From 65cc7fd2ae39a7a543767f661b096d7d521ee4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Wed, 16 Nov 2022 16:06:45 +0100 Subject: [PATCH] v3.2.0 --- CMakeLists.txt | 2 +- docs/ci-and-misc.md | 2 +- docs/configuration.md | 2 +- docs/release-notes.md | 37 ++ extras/catch_amalgamated.cpp | 660 ++++++++++++++++------------ extras/catch_amalgamated.hpp | 391 ++++++++++++---- meson.build | 2 +- src/catch2/catch_version.cpp | 2 +- src/catch2/catch_version_macros.hpp | 4 +- 9 files changed, 747 insertions(+), 355 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c42a3e2a..a9b4ca72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.1.1 # CML version placeholder, don't delete + VERSION 3.2.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/ci-and-misc.md b/docs/ci-and-misc.md index 12650e3d..c07da29f 100644 --- a/docs/ci-and-misc.md +++ b/docs/ci-and-misc.md @@ -69,7 +69,7 @@ test execution. Specifically it understands > Support for `XML_OUTPUT_FILE` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 3.0.1 -> Support for `TESTBRIDGE_TEST_ONLY` and sharding was introduced in Catch2 X.Y.Z +> Support for `TESTBRIDGE_TEST_ONLY` and sharding was introduced in Catch2 3.2.0 This integration is enabled via either a [compile time configuration option](configuration.md#bazel-support), or via `BAZEL_TEST` environment diff --git a/docs/configuration.md b/docs/configuration.md index 1392caaa..d4421f3c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -159,7 +159,7 @@ by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`. > [`CATCH_CONFIG_ANDROID_LOGWRITE`](https://github.com/catchorg/Catch2/issues/1743) and [`CATCH_CONFIG_GLOBAL_NEXTAFTER`](https://github.com/catchorg/Catch2/pull/1739) were introduced in Catch2 2.10.0 -> `CATCH_CONFIG_GETENV` was [introduced](https://github.com/catchorg/Catch2/pull/2562) in Catch2 X.Y.Z +> `CATCH_CONFIG_GETENV` was [introduced](https://github.com/catchorg/Catch2/pull/2562) in Catch2 3.2.0 Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support. diff --git a/docs/release-notes.md b/docs/release-notes.md index a0586dad..f83721e3 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,7 @@ # Release notes **Contents**
+[3.2.0](#320)
[3.1.1](#311)
[3.1.0](#310)
[3.0.1](#301)
@@ -52,6 +53,42 @@ +## 3.2.0 + +### Improvements +* Catch2 now compiles on PlayStation (#2562) +* Added `CATCH_CONFIG_GETENV` compile-time toggle (#2562) + * This toggle guards whether Catch2 calls `std::getenv` when reading env variables +* Added support for more Bazel test environment variables + * `TESTBRIDGE_TEST_ONLY` is now supported (#2490) + * Sharding variables, `TEST_SHARD_INDEX`, `TEST_TOTAL_SHARDS`, `TEST_SHARD_STATUS_FILE`, are now all supported (#2491) +* Bunch of small tweaks and improvements in reporters + * The TAP and SonarQube reporters output the used test filters + * The XML reporter now also reports the version of its output format + * The compact reporter now uses the same summary output as the console reporter (#878, #2554) +* Added support for asserting on types that can only be compared with literal 0 (#2555) + * A canonical example is C++20's `std::*_ordering` types, which cannot be compared with an `int` variable, only `0` + * The support extends to any type with this property, not just the ones in stdlib + * This change imposes 2-3% slowdown on compiling files that are heavy on `REQUIRE` and friends + * **This required significant rewrite of decomposition, there might be bugs** +* Simplified internals of matcher related macros + * This provides about ~2% speed up compiling files that are heavy on `REQUIRE_THAT` and friends + + +### Fixes +* Cleaned out some warnings and static analysis issues + * Suppressed `-Wcomma` warning rarely occuring in templated test cases (#2543) + * Constified implementation details in `INFO` (#2564) + * Made `MatcherGenericBase` copy constructor const (#2566) +* Fixed serialization of test filters so the output roundtrips + * This means that e.g. `./tests/SelfTest "aaa bbb", [approx]` outputs `Filters: "aaa bbb",[approx]` + + +### Miscellaneous +* Catch2's build no longer leaks `-ffile-prefix-map` setting to dependees (#2533) + + + ## 3.1.1 ### Improvements diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index aeed77f4..f2437d19 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.1.1 -// Generated: 2022-10-17 18:47:22.400176 +// Catch v3.2.0 +// Generated: 2022-11-16 19:30:16.114602 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -508,33 +508,74 @@ namespace Catch { -namespace { - bool provideBazelReporterOutput() { -#if defined(CATCH_CONFIG_BAZEL_SUPPORT) - return true; -#elif defined(CATCH_PLATFORM_WINDOWS_UWP) - // UWP does not support environment variables - return false; -#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 - } -} +#include namespace Catch { + namespace { + static bool enableBazelEnvSupport() { +#if defined( CATCH_CONFIG_BAZEL_SUPPORT ) + return true; +#else + return Detail::getEnv( "BAZEL_TEST" ) != nullptr; +#endif + } + + struct bazelShardingOptions { + unsigned int shardIndex, shardCount; + std::string shardFilePath; + }; + + static Optional readBazelShardingOptions() { + const auto bazelShardIndex = Detail::getEnv( "TEST_SHARD_INDEX" ); + const auto bazelShardTotal = Detail::getEnv( "TEST_TOTAL_SHARDS" ); + const auto bazelShardInfoFile = Detail::getEnv( "TEST_SHARD_STATUS_FILE" ); + + + const bool has_all = + bazelShardIndex && bazelShardTotal && bazelShardInfoFile; + if ( !has_all ) { + // We provide nice warning message if the input is + // misconfigured. + auto warn = []( const char* env_var ) { + Catch::cerr() + << "Warning: Bazel shard configuration is missing '" + << env_var << "'. Shard configuration is skipped.\n"; + }; + if ( !bazelShardIndex ) { + warn( "TEST_SHARD_INDEX" ); + } + if ( !bazelShardTotal ) { + warn( "TEST_TOTAL_SHARDS" ); + } + if ( !bazelShardInfoFile ) { + warn( "TEST_SHARD_STATUS_FILE" ); + } + return {}; + } + + auto shardIndex = parseUInt( bazelShardIndex ); + if ( !shardIndex ) { + Catch::cerr() + << "Warning: could not parse 'TEST_SHARD_INDEX' ('" << bazelShardIndex + << "') as unsigned int.\n"; + return {}; + } + auto shardTotal = parseUInt( bazelShardTotal ); + if ( !shardTotal ) { + Catch::cerr() + << "Warning: could not parse 'TEST_TOTAL_SHARD' ('" + << bazelShardTotal << "') as unsigned int.\n"; + return {}; + } + + return bazelShardingOptions{ + *shardIndex, *shardTotal, bazelShardInfoFile }; + + } + } // end namespace + + bool operator==( ProcessedReporterSpec const& lhs, ProcessedReporterSpec const& rhs ) { return lhs.name == rhs.name && @@ -556,17 +597,6 @@ namespace Catch { elem = trim(elem); } - - TestSpecParser parser(ITagAliasRegistry::get()); - if (!m_data.testsOrTags.empty()) { - m_hasTestFilters = true; - for (auto const& testOrTags : m_data.testsOrTags) { - parser.parse(testOrTags); - } - } - m_testSpec = parser.testSpec(); - - // Insert the default reporter if user hasn't asked for a specfic one if ( m_data.reporterSpecifications.empty() ) { m_data.reporterSpecifications.push_back( { @@ -579,29 +609,21 @@ namespace Catch { } ); } -#if !defined(CATCH_PLATFORM_WINDOWS_UWP) - 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 ) -# endif - 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 ), {}, {} } ); + if ( enableBazelEnvSupport() ) { + readBazelEnvVars(); + } + + // Bazel support can modify the test specs, so parsing has to happen + // after reading Bazel env vars. + TestSpecParser parser( ITagAliasRegistry::get() ); + if ( !m_data.testsOrTags.empty() ) { + m_hasTestFilters = true; + for ( auto const& testOrTags : m_data.testsOrTags ) { + parser.parse( testOrTags ); } - } -#endif + } + m_testSpec = parser.testSpec(); + // We now fixup the reporter specs to handle default output spec, // default colour spec, etc @@ -681,6 +703,39 @@ namespace Catch { unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } + void Config::readBazelEnvVars() { + // 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. + const auto bazelOutputFile = Detail::getEnv( "XML_OUTPUT_FILE" ); + + if ( bazelOutputFile ) { + m_data.reporterSpecifications.push_back( + { "junit", std::string( bazelOutputFile ), {}, {} } ); + } + + const auto bazelTestSpec = Detail::getEnv( "TESTBRIDGE_TEST_ONLY" ); + if ( bazelTestSpec ) { + // Presumably the test spec from environment should overwrite + // the one we got from CLI (if we got any) + m_data.testsOrTags.clear(); + m_data.testsOrTags.push_back( bazelTestSpec ); + } + + const auto bazelShardOptions = readBazelShardingOptions(); + if ( bazelShardOptions ) { + std::ofstream f( bazelShardOptions->shardFilePath, + std::ios_base::out | std::ios_base::trunc ); + if ( f.is_open() ) { + f << ""; + m_data.shardIndex = bazelShardOptions->shardIndex; + m_data.shardCount = bazelShardOptions->shardCount; + } + } + } + } // end namespace Catch @@ -1472,6 +1527,7 @@ namespace Catch { #include #include #include +#include namespace Catch { @@ -1495,6 +1551,10 @@ namespace Catch { return m_wildcardPattern.matches( testCase.name ); } + void TestSpec::NamePattern::serializeTo( std::ostream& out ) const { + out << '"' << name() << '"'; + } + TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString ) : Pattern( filterString ) @@ -1507,6 +1567,10 @@ namespace Catch { Tag( m_tag ) ) != end( testCase.tags ); } + void TestSpec::TagPattern::serializeTo( std::ostream& out ) const { + out << name(); + } + bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { bool should_use = !testCase.isHidden(); for (auto const& pattern : m_required) { @@ -1523,18 +1587,31 @@ namespace Catch { return should_use; } - std::string TestSpec::Filter::name() const { - std::string name; - for (auto const& p : m_required) { - name += p->name(); + void TestSpec::Filter::serializeTo( std::ostream& out ) const { + bool first = true; + for ( auto const& pattern : m_required ) { + if ( !first ) { + out << ' '; + } + out << *pattern; + first = false; } - for (auto const& p : m_forbidden) { - name += p->name(); + for ( auto const& pattern : m_forbidden ) { + if ( !first ) { + out << ' '; + } + out << *pattern; + first = false; } - return name; } + std::string TestSpec::extractFilterName( Filter const& filter ) { + Catch::ReusableStringStream sstr; + sstr << filter; + return sstr.str(); + } + bool TestSpec::hasFilters() const { return !m_filters.empty(); } @@ -1551,7 +1628,7 @@ namespace Catch { for( auto const& test : testCases ) if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) ) currentMatches.emplace_back( &test ); - return FilterMatch{ filter.name(), currentMatches }; + return FilterMatch{ extractFilterName(filter), currentMatches }; } ); return matches; } @@ -1560,6 +1637,17 @@ namespace Catch { return m_invalidSpecs; } + void TestSpec::serializeTo( std::ostream& out ) const { + bool first = true; + for ( auto const& filter : m_filters ) { + if ( !first ) { + out << ','; + } + out << filter; + first = false; + } + } + } @@ -1924,7 +2012,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 1, 1, "", 0 ); + static Version version( 3, 2, 0, "", 0 ); return version; } @@ -2175,11 +2263,7 @@ namespace Catch { CATCH_BREAK_INTO_DEBUGGER(); } if (m_reaction.shouldThrow) { -#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - throw Catch::TestFailureException(); -#else - CATCH_ERROR( "Test failure requires aborting test!" ); -#endif + throw_test_failure_exception(); } } void AssertionHandler::setCompleted() { @@ -2207,8 +2291,8 @@ namespace Catch { // This is the overload that takes a string and infers the Equals matcher from it // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp - void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { - handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ) { + handleExceptionMatchExpr( handler, Matchers::Equals( str ) ); } } // namespace Catch @@ -2744,23 +2828,14 @@ namespace Catch { return ParserResult::ok(ParseResultType::Matched); } - CATCH_TRY { - std::size_t parsedTo = 0; - unsigned long parsedSeed = std::stoul(seed, &parsedTo, 0); - if (parsedTo != seed.size()) { - return ParserResult::runtimeError("Could not parse '" + seed + "' as seed"); - } - - // TODO: Ideally we could parse unsigned int directly, - // but the stdlib doesn't provide helper for that - // type. After this is refactored to use fixed size - // type, we should check the parsed value is in range - // of the underlying type. - config.rngSeed = static_cast(parsedSeed); - return ParserResult::ok(ParseResultType::Matched); - } CATCH_CATCH_ANON(std::exception const&) { - return ParserResult::runtimeError("Could not parse '" + seed + "' as seed"); + // TODO: ideally we should be parsing uint32_t directly + // fix this later when we add new parse overload + auto parsedSeed = parseUInt( seed, 0 ); + if ( !parsedSeed ) { + return ParserResult::runtimeError( "Could not parse '" + seed + "' as seed" ); } + config.rngSeed = *parsedSeed; + return ParserResult::ok( ParseResultType::Matched ); }; auto const setDefaultColourMode = [&]( std::string const& colourMode ) { Optional maybeMode = Catch::Detail::stringToColourMode(toLower( colourMode )); @@ -2852,42 +2927,29 @@ namespace Catch { return ParserResult::ok( ParseResultType::Matched ); }; auto const setShardCount = [&]( std::string const& shardCount ) { - CATCH_TRY{ - std::size_t parsedTo = 0; - int64_t parsedCount = std::stoll(shardCount, &parsedTo, 0); - if (parsedTo != shardCount.size()) { - return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count"); - } - if (parsedCount <= 0) { - return ParserResult::runtimeError("Shard count must be a positive number"); - } - - config.shardCount = static_cast(parsedCount); - return ParserResult::ok(ParseResultType::Matched); - } CATCH_CATCH_ANON(std::exception const&) { - return ParserResult::runtimeError("Could not parse '" + shardCount + "' as shard count"); + auto parsedCount = parseUInt( shardCount ); + if ( !parsedCount ) { + return ParserResult::runtimeError( + "Could not parse '" + shardCount + "' as shard count" ); } + if ( *parsedCount == 0 ) { + return ParserResult::runtimeError( + "Shard count must be positive" ); + } + config.shardCount = *parsedCount; + return ParserResult::ok( ParseResultType::Matched ); }; auto const setShardIndex = [&](std::string const& shardIndex) { - CATCH_TRY{ - std::size_t parsedTo = 0; - int64_t parsedIndex = std::stoll(shardIndex, &parsedTo, 0); - if (parsedTo != shardIndex.size()) { - return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index"); - } - if (parsedIndex < 0) { - return ParserResult::runtimeError("Shard index must be a non-negative number"); - } - - config.shardIndex = static_cast(parsedIndex); - return ParserResult::ok(ParseResultType::Matched); - } CATCH_CATCH_ANON(std::exception const&) { - return ParserResult::runtimeError("Could not parse '" + shardIndex + "' as shard index"); + auto parsedIndex = parseUInt( shardIndex ); + if ( !parsedIndex ) { + return ParserResult::runtimeError( + "Could not parse '" + shardIndex + "' as shard index" ); } + config.shardIndex = *parsedIndex; + return ParserResult::ok( ParseResultType::Matched ); }; - auto cli = ExeName( config.processName ) | Help( config.showHelp ) @@ -3925,6 +3987,35 @@ namespace Catch { + +#include + +namespace Catch { + namespace Detail { + +#if !defined (CATCH_CONFIG_GETENV) + char const* getEnv( char const* ) { return nullptr; } +#else + + char const* getEnv( char const* varName ) { +# if defined( _MSC_VER ) +# pragma warning( push ) +# pragma warning( disable : 4996 ) // use getenv_s instead of getenv +# endif + + return std::getenv( varName ); + +# if defined( _MSC_VER ) +# pragma warning( pop ) +# endif + } +#endif +} // namespace Detail +} // namespace Catch + + + + #include #include #include @@ -4418,6 +4509,47 @@ namespace Catch { + +#include + +namespace Catch { + + Optional parseUInt(std::string const& input, int base) { + auto trimmed = trim( input ); + // std::stoull is annoying and accepts numbers starting with '-', + // it just negates them into unsigned int + if ( trimmed.empty() || trimmed[0] == '-' ) { + return {}; + } + + CATCH_TRY { + size_t pos = 0; + const auto ret = std::stoull( trimmed, &pos, base ); + + // We did not consume the whole input, so there is an issue + // This can be bunch of different stuff, like multiple numbers + // in the input, or invalid digits/characters and so on. Either + // way, we do not want to return the partially parsed result. + if ( pos != trimmed.size() ) { + return {}; + } + // Too large + if ( ret > std::numeric_limits::max() ) { + return {}; + } + return static_cast(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 {}; + } + } + +} // namespace Catch + + + + #include namespace Catch { @@ -5885,7 +6017,7 @@ namespace Catch { TestCaseInfo const* rhs ) { return *lhs < *rhs; }; - std::set seenTests(testInfoCmp); + std::set seenTests(testInfoCmp); for ( auto const& test : tests ) { const auto infoPtr = &test.getTestCaseInfo(); const auto prev = seenTests.insert( infoPtr ); @@ -6190,6 +6322,21 @@ namespace TestCaseTracking { + +namespace Catch { + + void throw_test_failure_exception() { +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + throw TestFailureException{}; +#else + CATCH_ERROR( "Test failure requires aborting test!" ); +#endif + } + +} // namespace Catch + + + #include #include @@ -6476,10 +6623,6 @@ namespace Catch { m_mode = None; } - TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); - } - } // namespace Catch @@ -7588,9 +7731,9 @@ namespace Catch { // This is the general overload that takes a any string matcher // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers // the Equals matcher (so the header does not mention matchers) - void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher ) { std::string exceptionMessage = Catch::translateActiveException(); - MatchExpr expr( CATCH_MOVE(exceptionMessage), matcher, matcherString ); + MatchExpr expr( CATCH_MOVE(exceptionMessage), matcher ); handler.handleExpr( expr ); } @@ -7669,22 +7812,6 @@ namespace Catch { #include -namespace { - - constexpr Catch::StringRef bothOrAll( std::uint64_t count ) { - switch (count) { - case 1: - return Catch::StringRef{}; - case 2: - return "both "_catch_sr; - default: - return "all "_catch_sr; - } - } - -} // anon namespace - - namespace Catch { namespace { @@ -7699,42 +7826,6 @@ namespace { static constexpr Catch::StringRef compactPassedString = "passed"_sr; #endif -// Colour, message variants: -// - white: No tests ran. -// - red: Failed [both/all] N test cases, failed [both/all] M assertions. -// - white: Passed [both/all] N test cases (no assertions). -// - red: Failed N tests cases, failed M assertions. -// - green: Passed [both/all] N tests cases with M assertions. -void printTotals(std::ostream& out, const Totals& totals, ColourImpl* colourImpl) { - if (totals.testCases.total() == 0) { - out << "No tests ran."; - } else if (totals.testCases.failed == totals.testCases.total()) { - auto guard = colourImpl->guardColour( Colour::ResultError ).engage( out ); - const StringRef qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll(totals.assertions.failed) : StringRef{}; - out << - "Failed " << bothOrAll(totals.testCases.failed) - << pluralise(totals.testCases.failed, "test case"_sr) << ", " - "failed " << qualify_assertions_failed << - pluralise(totals.assertions.failed, "assertion"_sr) << '.'; - } else if (totals.assertions.total() == 0) { - out << - "Passed " << bothOrAll(totals.testCases.total()) - << pluralise(totals.testCases.total(), "test case"_sr) - << " (no assertions)."; - } else if (totals.assertions.failed) { - out << colourImpl->guardColour( Colour::ResultError ) << - "Failed " << pluralise(totals.testCases.failed, "test case"_sr) << ", " - "failed " << pluralise(totals.assertions.failed, "assertion"_sr) << '.'; - } else { - out << colourImpl->guardColour( Colour::ResultSuccess ) << - "Passed " << bothOrAll(totals.testCases.passed) - << pluralise(totals.testCases.passed, "test case"_sr) << - " with " << pluralise(totals.assertions.passed, "assertion"_sr) << '.'; - } -} - // Implementation of CompactReporter formatting class AssertionPrinter { public: @@ -7910,7 +8001,7 @@ private: if ( m_config->testSpec().hasFilters() ) { m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: " - << serializeFilters( m_config->getTestsOrTags() ) + << m_config->testSpec() << '\n'; } m_stream << "RNG seed: " << getSeed() << '\n'; @@ -7942,7 +8033,7 @@ private: } void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( m_stream, _testRunStats.totals, m_colour.get() ); + printTestRunTotals( m_stream, *m_colour, _testRunStats.totals ); m_stream << "\n\n" << std::flush; StreamingReporterBase::testRunEnded( _testRunStats ); } @@ -8424,7 +8515,7 @@ void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { } void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { printTotalsDivider(_testRunStats.totals); - printTotals(_testRunStats.totals); + printTestRunTotals( m_stream, *m_colour, _testRunStats.totals ); m_stream << '\n' << std::flush; StreamingReporterBase::testRunEnded(_testRunStats); } @@ -8432,7 +8523,7 @@ void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) { StreamingReporterBase::testRunStarting(_testInfo); if ( m_config->testSpec().hasFilters() ) { m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: " - << serializeFilters( m_config->getTestsOrTags() ) << '\n'; + << m_config->testSpec() << '\n'; } m_stream << "Randomness seeded to: " << getSeed() << '\n'; } @@ -8531,82 +8622,6 @@ void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t << '\n'; } -struct SummaryColumn { - - SummaryColumn( std::string _label, Colour::Code _colour ) - : label( CATCH_MOVE( _label ) ), - colour( _colour ) {} - SummaryColumn addRow( std::uint64_t count ) { - ReusableStringStream rss; - rss << count; - std::string row = rss.str(); - for (auto& oldRow : rows) { - while (oldRow.size() < row.size()) - oldRow = ' ' + oldRow; - while (oldRow.size() > row.size()) - row = ' ' + row; - } - rows.push_back(row); - return *this; - } - - std::string label; - Colour::Code colour; - std::vector rows; - -}; - -void ConsoleReporter::printTotals( Totals const& totals ) { - if (totals.testCases.total() == 0) { - m_stream << m_colour->guardColour( Colour::Warning ) - << "No tests ran\n"; - } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { - m_stream << m_colour->guardColour( Colour::ResultSuccess ) - << "All tests passed"; - m_stream << " (" - << pluralise(totals.assertions.passed, "assertion"_sr) << " in " - << pluralise(totals.testCases.passed, "test case"_sr) << ')' - << '\n'; - } else { - - std::vector columns; - columns.push_back(SummaryColumn("", Colour::None) - .addRow(totals.testCases.total()) - .addRow(totals.assertions.total())); - 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("failed as expected", Colour::ResultExpectedFailure) - .addRow(totals.testCases.failedButOk) - .addRow(totals.assertions.failedButOk)); - - printSummaryRow("test cases"_sr, columns, 0); - printSummaryRow("assertions"_sr, columns, 1); - } -} -void ConsoleReporter::printSummaryRow(StringRef label, std::vector const& cols, std::size_t row) { - for (auto col : cols) { - std::string const& value = col.rows[row]; - if (col.label.empty()) { - m_stream << label << ": "; - if ( value != "0" ) { - m_stream << value; - } else { - m_stream << m_colour->guardColour( Colour::Warning ) - << "- none -"; - } - } else if (value != "0") { - m_stream << m_colour->guardColour( Colour::LightGrey ) << " | " - << m_colour->guardColour( col.colour ) << value << ' ' - << col.label; - } - } - m_stream << '\n'; -} - void ConsoleReporter::printTotalsDivider(Totals const& totals) { if (totals.testCases.total() > 0) { std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); @@ -8634,9 +8649,6 @@ void ConsoleReporter::printTotalsDivider(Totals const& totals) { } m_stream << '\n'; } -void ConsoleReporter::printSummaryDivider() { - m_stream << lineOfChars('-') << '\n'; -} } // end namespace Catch @@ -9054,6 +9066,104 @@ namespace Catch { out << "\n\n" << std::flush; } + namespace { + class SummaryColumn { + public: + SummaryColumn( std::string suffix, Colour::Code colour ): + m_suffix( CATCH_MOVE( suffix ) ), m_colour( colour ) {} + + SummaryColumn&& addRow( std::uint64_t count ) && { + std::string row = std::to_string(count); + auto const new_width = std::max( m_width, row.size() ); + if ( new_width > m_width ) { + for ( auto& oldRow : m_rows ) { + oldRow.insert( 0, new_width - m_width, ' ' ); + } + } else { + row.insert( 0, m_width - row.size(), ' ' ); + } + m_width = new_width; + m_rows.push_back( row ); + return std::move( *this ); + } + + std::string const& getSuffix() const { return m_suffix; } + Colour::Code getColour() const { return m_colour; } + std::string const& getRow( std::size_t index ) const { + return m_rows[index]; + } + + private: + std::string m_suffix; + Colour::Code m_colour; + std::size_t m_width = 0; + std::vector m_rows; + }; + + void printSummaryRow( std::ostream& stream, + ColourImpl& colour, + StringRef label, + std::vector const& cols, + std::size_t row ) { + for ( auto const& col : cols ) { + auto const& value = col.getRow( row ); + auto const& suffix = col.getSuffix(); + if ( suffix.empty() ) { + stream << label << ": "; + if ( value != "0" ) { + stream << value; + } else { + stream << colour.guardColour( Colour::Warning ) + << "- none -"; + } + } else if ( value != "0" ) { + stream << colour.guardColour( Colour::LightGrey ) << " | " + << colour.guardColour( col.getColour() ) << value + << ' ' << suffix; + } + } + stream << '\n'; + } + } // namespace + + void printTestRunTotals( std::ostream& stream, + ColourImpl& streamColour, + Totals const& totals ) { + if ( totals.testCases.total() == 0 ) { + stream << streamColour.guardColour( Colour::Warning ) + << "No tests ran\n"; + return; + } + + if ( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { + stream << streamColour.guardColour( Colour::ResultSuccess ) + << "All tests passed"; + stream << " (" + << pluralise( totals.assertions.passed, "assertion"_sr ) + << " in " + << pluralise( totals.testCases.passed, "test case"_sr ) + << ')' << '\n'; + return; + } + + std::vector columns; + columns.push_back( SummaryColumn( "", Colour::None ) + .addRow( totals.testCases.total() ) + .addRow( totals.assertions.total() ) ); + 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( "failed as expected", Colour::ResultExpectedFailure ) + .addRow( totals.testCases.failedButOk ) + .addRow( totals.assertions.failedButOk ) ); + printSummaryRow( stream, streamColour, "test cases"_sr, columns, 0 ); + printSummaryRow( stream, streamColour, "assertions"_sr, columns, 1 ); + } + } // namespace Catch @@ -9074,6 +9184,8 @@ namespace Catch { std::tm timeInfo = {}; #if defined (_MSC_VER) || defined (__MINGW32__) gmtime_s(&timeInfo, &rawtime); +#elif defined (CATCH_PLATFORM_PLAYSTATION) + gmtime_s(&rawtime, &timeInfo); #else gmtime_r(&rawtime, &timeInfo); #endif @@ -9187,10 +9299,10 @@ namespace Catch { xml.scopedElement("property") .writeAttribute("name"_sr, "random-seed"_sr) .writeAttribute("value"_sr, m_config->rngSeed()); - if (m_config->hasTestFilters()) { + if (m_config->testSpec().hasFilters()) { xml.scopedElement("property") .writeAttribute("name"_sr, "filters"_sr) - .writeAttribute("value"_sr, serializeFilters(m_config->getTestsOrTags())); + .writeAttribute("value"_sr, m_config->testSpec()); } } @@ -9563,9 +9675,14 @@ namespace Catch { namespace Catch { namespace { - std::string createRngSeedString(uint32_t seed) { + std::string createMetadataString(IConfig const& config) { ReusableStringStream sstr; - sstr << "rng-seed=" << seed; + if ( config.testSpec().hasFilters() ) { + sstr << "filters='" + << config.testSpec() + << "' "; + } + sstr << "rng-seed=" << config.rngSeed(); return sstr.str(); } } @@ -9573,7 +9690,7 @@ namespace Catch { void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) { CumulativeReporterBase::testRunStarting(testRunInfo); - xml.writeComment( createRngSeedString( m_config->rngSeed() ) ); + xml.writeComment( createMetadataString( *m_config ) ); xml.startElement("testExecutions"); xml.writeAttribute("version"_sr, '1'); } @@ -9893,6 +10010,9 @@ namespace Catch { } // End anonymous namespace void TAPReporter::testRunStarting( TestRunInfo const& ) { + if ( m_config->testSpec().hasFilters() ) { + m_stream << "# filters: " << m_config->testSpec() << '\n'; + } m_stream << "# rng-seed: " << m_config->rngSeed() << '\n'; } @@ -10131,9 +10251,11 @@ 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("catch2-version"_sr, libraryVersion()); - if (m_config->testSpec().hasFilters()) - m_xml.writeAttribute( "filters"_sr, serializeFilters( m_config->getTestsOrTags() ) ); + if ( m_config->testSpec().hasFilters() ) { + m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); + } } void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 51afbd6c..b2e002fc 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.1.1 -// Generated: 2022-10-17 18:47:20.510385 +// Catch v3.2.0 +// Generated: 2022-11-16 19:30:14.116909 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -330,6 +330,10 @@ namespace Catch { # if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP ) # define CATCH_PLATFORM_WINDOWS_UWP # endif + +#elif defined(__ORBIS__) || defined(__PROSPERO__) +# define CATCH_PLATFORM_PLAYSTATION + #endif #endif // CATCH_PLATFORM_HPP_INCLUDED @@ -421,23 +425,32 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) +# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) + #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +// We know some environments not to support full POSIX signals +#if defined( CATCH_PLATFORM_WINDOWS ) || \ + defined( CATCH_PLATFORM_PLAYSTATION ) || \ + defined( __CYGWIN__ ) || \ + defined( __QNX__ ) || \ + defined( __EMSCRIPTEN__ ) || \ + defined( __DJGPP__ ) || \ + defined( __OS400__ ) +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#else +# define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +// Assume that some platforms do not support getenv. +#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION) +# define CATCH_INTERNAL_CONFIG_NO_GETENV +#else +# define CATCH_INTERNAL_CONFIG_GETENV #endif //////////////////////////////////////////////////////////////////////////////// @@ -580,6 +593,10 @@ namespace Catch { # define CATCH_CONFIG_POSIX_SIGNALS #endif +#if defined(CATCH_INTERNAL_CONFIG_GETENV) && !defined(CATCH_INTERNAL_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_GETENV) +# define CATCH_CONFIG_GETENV +#endif + #if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) # define CATCH_CONFIG_CPP11_TO_STRING #endif @@ -668,6 +685,10 @@ namespace Catch { # 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)) #define CATCH_CATCH_ALL if ((false)) @@ -1720,6 +1741,13 @@ namespace Catch { //! Used to signal that an assertion macro failed struct TestFailureException{}; + /** + * Outlines throwing of `TestFailureException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_failure_exception(); + } // namespace Catch #endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED @@ -3766,6 +3794,7 @@ namespace Catch #endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED +#include #include #include @@ -3784,6 +3813,14 @@ namespace Catch { virtual bool matches( TestCaseInfo const& testCase ) const = 0; std::string const& name() const; private: + virtual void serializeTo( std::ostream& out ) const = 0; + // Writes string that would be reparsed into the pattern + friend std::ostream& operator<<(std::ostream& out, + Pattern const& pattern) { + pattern.serializeTo( out ); + return out; + } + std::string const m_name; }; @@ -3792,6 +3829,8 @@ namespace Catch { explicit NamePattern( std::string const& name, std::string const& filterString ); bool matches( TestCaseInfo const& testCase ) const override; private: + void serializeTo( std::ostream& out ) const override; + WildcardPattern m_wildcardPattern; }; @@ -3800,6 +3839,8 @@ namespace Catch { explicit TagPattern( std::string const& tag, std::string const& filterString ); bool matches( TestCaseInfo const& testCase ) const override; private: + void serializeTo( std::ostream& out ) const override; + std::string m_tag; }; @@ -3807,10 +3848,19 @@ namespace Catch { std::vector> m_required; std::vector> m_forbidden; + //! Serializes this filter into a string that would be parsed into + //! an equivalent filter + void serializeTo( std::ostream& out ) const; + friend std::ostream& operator<<(std::ostream& out, Filter const& f) { + f.serializeTo( out ); + return out; + } + bool matches( TestCaseInfo const& testCase ) const; - std::string name() const; }; + static std::string extractFilterName( Filter const& filter ); + public: struct FilterMatch { std::string name; @@ -3827,7 +3877,16 @@ namespace Catch { private: std::vector m_filters; std::vector m_invalidSpecs; + friend class TestSpecParser; + //! Serializes this test spec into a string that would be parsed into + //! equivalent test spec + void serializeTo( std::ostream& out ) const; + friend std::ostream& operator<<(std::ostream& out, + TestSpec const& spec) { + spec.serializeTo( out ); + return out; + } }; } @@ -4288,6 +4347,9 @@ namespace Catch { std::chrono::milliseconds benchmarkWarmupTime() const override; private: + // Reads Bazel env vars and applies them to the config + void readBazelEnvVars(); + ConfigData m_data; std::vector m_processedReporterSpecs; TestSpec m_testSpec; @@ -4427,7 +4489,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( macroName, log ) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ @@ -5308,6 +5370,66 @@ namespace Catch { #define CATCH_DECOMPOSER_HPP_INCLUDED + +#ifndef CATCH_COMPARE_TRAITS_HPP_INCLUDED +#define CATCH_COMPARE_TRAITS_HPP_INCLUDED + + +#include + +namespace Catch { + namespace Detail { + +#if defined( __GNUC__ ) && !defined( __clang__ ) +# pragma GCC diagnostic push + // GCC likes to complain about comparing bool with 0, in the decltype() + // that defines the comparable traits below. +# pragma GCC diagnostic ignored "-Wbool-compare" + // "ordered comparison of pointer with integer zero" same as above, + // but it does not have a separate warning flag to suppress +# pragma GCC diagnostic ignored "-Wextra" + // Did you know that comparing floats with `0` directly + // is super-duper dangerous in unevaluated context? +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \ + template \ + struct is_##id##_comparable : std::false_type {}; \ + template \ + struct is_##id##_comparable< \ + T, \ + U, \ + void_t() op std::declval() )>> \ + : std::true_type {}; \ + template \ + struct is_##id##_0_comparable : std::false_type {}; \ + template \ + struct is_##id##_0_comparable() op 0 )>> \ + : std::true_type {}; + + // We need all 6 pre-spaceship comparison ops: <, <=, >, >=, ==, != + CATCH_DEFINE_COMPARABLE_TRAIT( lt, < ) + CATCH_DEFINE_COMPARABLE_TRAIT( le, <= ) + CATCH_DEFINE_COMPARABLE_TRAIT( gt, > ) + CATCH_DEFINE_COMPARABLE_TRAIT( ge, >= ) + CATCH_DEFINE_COMPARABLE_TRAIT( eq, == ) + CATCH_DEFINE_COMPARABLE_TRAIT( ne, != ) + +#undef CATCH_DEFINE_COMPARABLE_TRAIT + +#if defined( __GNUC__ ) && !defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + + } // namespace Detail +} // namespace Catch + +#endif // CATCH_COMPARE_TRAITS_HPP_INCLUDED + +#include #include #ifdef _MSC_VER @@ -5451,53 +5573,129 @@ namespace Catch { }; - // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) - template - auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } - template - auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } - template - auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } - template - auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } - template - auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } - - template - auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } - template - auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } - template - auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } - template - auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } - template - auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } - - template class ExprLhs { LhsT m_lhs; public: explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} - template>::value, int> = 0> - friend auto operator == ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr { - return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs }; - } - template::value, int> = 0> - friend auto operator == ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr { - return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs }; - } +#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ + template < \ + typename RhsT, \ + std::enable_if_t< \ + Detail::is_##id##_comparable::value && \ + !std::is_arithmetic>::value, \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + ->BinaryExpr { \ + return { \ + static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + } \ + template < \ + typename RhsT, \ + std::enable_if_t::value && \ + std::is_arithmetic::value, \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + ->BinaryExpr { \ + return { \ + static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + } \ + template < \ + typename RhsT, \ + std::enable_if_t::value && \ + Detail::is_eq_0_comparable:: \ + value && /* We allow long because we want \ + `ptr op NULL to be accepted */ \ + ( std::is_same::value || \ + std::is_same::value ), \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + ->BinaryExpr { \ + if ( rhs != 0 ) { \ + throw_test_failure_exception(); \ + } \ + return { \ + static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ + } \ + template < \ + typename RhsT, \ + std::enable_if_t::value && \ + Detail::is_eq_0_comparable:: \ + value && /* We allow long because we want \ + `ptr op NULL` to be accepted */ \ + ( std::is_same::value || \ + std::is_same::value ), \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + ->BinaryExpr { \ + if ( lhs.m_lhs != 0 ) { \ + throw_test_failure_exception(); \ + } \ + return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + } + CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) + CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) + + #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR + + #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ + template < \ + typename RhsT, \ + std::enable_if_t< \ + Detail::is_##id##_comparable::value && \ + !std::is_arithmetic>::value, \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ + ->BinaryExpr { \ + return { \ + static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + } \ + template < \ + typename RhsT, \ + std::enable_if_t::value && \ + std::is_arithmetic::value, \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + ->BinaryExpr { \ + return { \ + static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + } \ + template < \ + typename RhsT, \ + std::enable_if_t::value && \ + Detail::is_##id##_0_comparable::value && \ + std::is_same::value, \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + ->BinaryExpr { \ + if ( rhs != 0 ) { \ + throw_test_failure_exception(); \ + } \ + return { \ + static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ + } \ + template < \ + typename RhsT, \ + std::enable_if_t::value && \ + Detail::is_##id##_0_comparable::value && \ + std::is_same::value, \ + int> = 0> \ + friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ + ->BinaryExpr { \ + if ( lhs.m_lhs != 0 ) { \ + throw_test_failure_exception(); \ + } \ + return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + } + + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < ) + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= ) + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > ) + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= ) + + #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR - template>::value, int> = 0> - friend auto operator != ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr { - return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs }; - } - template::value, int> = 0> - friend auto operator != ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr { - return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs }; - } #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \ template>::value, int> = 0> \ @@ -5509,10 +5707,6 @@ namespace Catch { return { static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \ } - CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<) - CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>) - CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<=) - CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>=) CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) @@ -5614,7 +5808,7 @@ namespace Catch { auto allowThrows() const -> bool; }; - void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); + void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ); } // namespace Catch @@ -5753,7 +5947,7 @@ namespace Catch { catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( ... ) { \ - Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \ + Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher ); \ } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ @@ -6489,6 +6683,7 @@ struct AutoReg : Detail::NonCopyable { CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\ namespace {\ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\ @@ -6535,6 +6730,7 @@ struct AutoReg : Detail::NonCopyable { CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ template static void TestFuncName(); \ namespace {\ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ @@ -6584,6 +6780,7 @@ struct AutoReg : Detail::NonCopyable { CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ template static void TestFunc(); \ namespace {\ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\ @@ -6715,6 +6912,7 @@ struct AutoReg : Detail::NonCopyable { CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ template \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \ void test();\ @@ -7114,8 +7312,8 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 1 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 2 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -8607,6 +8805,21 @@ namespace Catch { #endif // CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED +#ifndef CATCH_GETENV_HPP_INCLUDED +#define CATCH_GETENV_HPP_INCLUDED + +namespace Catch { +namespace Detail { + + //! Wrapper over `std::getenv` that compiles on UWP (and always returns nullptr there) + char const* getEnv(char const* varName); + +} +} + +#endif // CATCH_GETENV_HPP_INCLUDED + + #ifndef CATCH_ISTREAM_HPP_INCLUDED #define CATCH_ISTREAM_HPP_INCLUDED @@ -8815,6 +9028,26 @@ namespace Catch { #endif // CATCH_OUTPUT_REDIRECT_HPP_INCLUDED +#ifndef CATCH_PARSE_NUMBERS_HPP_INCLUDED +#define CATCH_PARSE_NUMBERS_HPP_INCLUDED + + +#include + +namespace Catch { + + /** + * Parses unsigned int from the input, using provided base + * + * Effectively a wrapper around std::stoul but with better error checking + * e.g. "-1" is rejected, instead of being parsed as UINT_MAX. + */ + Optional parseUInt(std::string const& input, int base = 10); +} + +#endif // CATCH_PARSE_NUMBERS_HPP_INCLUDED + + #ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED #define CATCH_REPORTER_REGISTRY_HPP_INCLUDED @@ -9520,7 +9753,6 @@ namespace Catch { } }; - TestSpec parseTestSpec( std::string const& arg ); } // namespace Catch @@ -9903,13 +10135,11 @@ namespace Catch { class MatchExpr : public ITransientExpression { ArgT && m_arg; MatcherT const& m_matcher; - StringRef m_matcherString; public: - MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString ) + MatchExpr( ArgT && arg, MatcherT const& matcher ) : ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose m_arg( CATCH_FORWARD(arg) ), - m_matcher( matcher ), - m_matcherString( matcherString ) + m_matcher( matcher ) {} void streamReconstructedExpression( std::ostream& os ) const override { @@ -9926,11 +10156,11 @@ namespace Catch { using StringMatcher = Matchers::MatcherBase; - void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ); + void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher ); template - auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr { - return MatchExpr( CATCH_FORWARD(arg), matcher, matcherString ); + auto makeMatchExpr( ArgT && arg, MatcherT const& matcher ) -> MatchExpr { + return MatchExpr( CATCH_FORWARD(arg), matcher ); } } // namespace Catch @@ -9941,7 +10171,7 @@ namespace Catch { do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ INTERNAL_CATCH_TRY { \ - catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher ) ); \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ } while( false ) @@ -9957,7 +10187,7 @@ namespace Catch { catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( exceptionType const& ex ) { \ - catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \ + catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher ) ); \ } \ catch( ... ) { \ catchAssertionHandler.handleUnexpectedInflightException(); \ @@ -10217,7 +10447,7 @@ namespace Matchers { MatcherGenericBase() = default; ~MatcherGenericBase() override; // = default; - MatcherGenericBase(MatcherGenericBase&) = default; + MatcherGenericBase(MatcherGenericBase const&) = default; MatcherGenericBase(MatcherGenericBase&&) = default; MatcherGenericBase& operator=(MatcherGenericBase const&) = delete; @@ -11480,7 +11710,6 @@ namespace Catch { namespace Catch { // Fwd decls - struct SummaryColumn; class TablePrinter; class ConsoleReporter final : public StreamingReporterBase { @@ -11524,12 +11753,7 @@ namespace Catch { // subsequent lines void printHeaderString(std::string const& _string, std::size_t indent = 0); - - void printTotals(Totals const& totals); - void printSummaryRow(StringRef label, std::vector const& cols, std::size_t row); - void printTotalsDivider(Totals const& totals); - void printSummaryDivider(); bool m_headerPrinted = false; bool m_testRunInfoPrinted = false; @@ -11809,6 +12033,15 @@ namespace Catch { bool isFiltered, Verbosity verbosity ); + /** + * Prints test run totals to the provided stream in user-friendly format + * + * Used by the console and compact reporters. + */ + void printTestRunTotals( std::ostream& stream, + ColourImpl& streamColour, + Totals const& totals ); + } // end namespace Catch #endif // CATCH_REPORTER_HELPERS_HPP_INCLUDED diff --git a/meson.build b/meson.build index dfd3b2f5..230e24bd 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.1.1', # CML version placeholder, don't delete + version: '3.2.0', # CML version placeholder, don't delete license: 'BSL-1.0', meson_version: '>=0.50.0', ) diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp index 20c8f411..027f0f41 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, 1, 1, "", 0 ); + static Version version( 3, 2, 0, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 69b444fd..36185fc6 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 1 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 2 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED