diff --git a/CMakeLists.txt b/CMakeLists.txt
index c42a3e2a19..a9b4ca723e 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 12650e3d9f..c07da29f05 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 1392caaa04..d4421f3c01 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 a0586dad37..f83721e37d 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 aeed77f4f5..f2437d19c5 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,32 +508,73 @@ 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;
+#include
+
+namespace Catch {
+
+ namespace {
+ static bool enableBazelEnvSupport() {
+#if defined( CATCH_CONFIG_BAZEL_SUPPORT )
+ return true;
#else
+ return Detail::getEnv( "BAZEL_TEST" ) != nullptr;
+#endif
+ }
-# 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
+ struct bazelShardingOptions {
+ unsigned int shardIndex, shardCount;
+ std::string shardFilePath;
+ };
- return std::getenv( "BAZEL_TEST" ) != nullptr;
+ 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 {};
+ }
-# if defined( _MSC_VER )
-# pragma warning( pop )
-# endif
-#endif
- }
-}
+ 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
-namespace Catch {
bool operator==( ProcessedReporterSpec const& lhs,
ProcessedReporterSpec const& rhs ) {
@@ -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 @@ class AssertionPrinter {
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 @@ class AssertionPrinter {
}
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 51afbd6c00..b2e002fcac 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
-#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
+#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
-#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 dfd3b2f585..230e24bd63 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 20c8f4119c..027f0f4122 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 69b444fd2e..36185fc644 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