diff --git a/CMakeLists.txt b/CMakeLists.txt index 38d2b47e61..6d381d8de4 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.3.1 # CML version placeholder, don't delete + VERSION 3.3.2 # 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/matchers.md b/docs/matchers.md index d2a2f3c17d..14c1589821 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -151,7 +151,7 @@ are: > `WithinRel` matcher was introduced in Catch2 2.10.0 -> `IsNaN` matcher was introduced in Catch2 X.Y.Z. +> `IsNaN` matcher was introduced in Catch2 3.3.2. The first three serve to compare two floating pointe numbers. For more details about how they work, read [the docs on comparing floating point diff --git a/docs/release-notes.md b/docs/release-notes.md index 3c46cb5732..1fa37da432 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,7 @@ # Release notes **Contents**
+[3.3.2](#332)
[3.3.1](#331)
[3.3.0](#330)
[3.2.1](#321)
@@ -56,6 +57,29 @@ +## 3.3.2 + +### Improvements +* Further reduced allocations + * The compact, console, TAP and XML reporters perform less allocations in various cases + * Removed 1 allocation per entered `SECTION`/`TEST_CASE`. + * Removed 2 allocations per test case exit, if stdout/stderr is captured +* Improved performance + * Section tracking is 10%-25% faster than in v3.3.0 + * Assertion handling is 5%-10% faster than in v3.3.0 + * Test case registration is 1%-2% faster than in v3.3.0 + * Tiny speedup for registering listeners + * Tiny speedup for `CAPTURE`, `TEST_CASE_METHOD`, `METHOD_AS_TEST_CASE`, and `TEMPLATE_LIST_TEST_*` macros. +* `Contains`, `RangeEquals` and `UnorderedRangeEquals` matchers now support ranges with iterator + sentinel pair +* Added `IsNaN` matcher + * Unlike `REQUIRE(isnan(x))`, `REQUIRE_THAT(x, IsNaN())` shows you the value of `x`. +* Suppressed `declared_but_not_referenced` warning for NVHPC (#2637) + +### Fixes +* Fixed performance regression in section tracking introduced in v3.3.1 + * Extreme cases would cause the tracking to run about 4x slower than in 3.3.0 + + ## 3.3.1 ### Improvements @@ -65,7 +89,6 @@ * The main improvement comes from smarter handling of `SECTION`s, especially sibling `SECTION`s - ## 3.3.0 ### Improvements diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index 0e12bd1438..a81b1b6ae5 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.1 -// Generated: 2023-01-29 22:55:05.183536 +// Catch v3.3.2 +// Generated: 2023-02-26 10:28:48.270752 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -469,16 +469,15 @@ namespace Catch { } std::string AssertionResult::getExpressionInMacro() const { - std::string expr; - if( m_info.macroName.empty() ) - expr = static_cast(m_info.capturedExpression); - else { - expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); - expr += m_info.macroName; - expr += "( "; - expr += m_info.capturedExpression; - expr += " )"; + if ( m_info.macroName.empty() ) { + return static_cast( m_info.capturedExpression ); } + std::string expr; + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; return expr; } @@ -2022,7 +2021,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 3, 1, "", 0 ); + static Version version( 3, 3, 2, "", 0 ); return version; } @@ -2198,13 +2197,13 @@ namespace Catch { TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, + std::string&& _stdOut, + std::string&& _stdErr, bool _aborting ) : testInfo( &_testInfo ), totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), + stdOut( CATCH_MOVE(_stdOut) ), + stdErr( CATCH_MOVE(_stdErr) ), aborting( _aborting ) {} @@ -4997,7 +4996,7 @@ namespace Catch { {} ~GeneratorTracker() override; - static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef nameAndLocation ) { + static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef const& nameAndLocation ) { GeneratorTracker* tracker; ITracker& currentTracker = ctx.currentTracker(); @@ -5139,13 +5138,8 @@ namespace Catch { Totals RunContext::runTest(TestCaseHandle const& testCase) { const Totals prevTotals = m_totals; - std::string redirectedCout; - std::string redirectedCerr; - auto const& testInfo = testCase.getTestCaseInfo(); - m_reporter->testCaseStarting(testInfo); - m_activeTestCase = &testCase; @@ -5187,6 +5181,8 @@ namespace Catch { seedRng( *m_config ); uint64_t testRuns = 0; + std::string redirectedCout; + std::string redirectedCerr; do { m_trackerContext.startCycle(); m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo)); @@ -5200,7 +5196,7 @@ namespace Catch { redirectedCerr += oneRunCerr; const auto singleRunTotals = m_totals.delta(beforeRunTotals); - auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting()); + auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); m_reporter->testCasePartialEnded(statsForOneRun, testRuns); ++testRuns; @@ -5215,8 +5211,8 @@ namespace Catch { m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, - redirectedCout, - redirectedCerr, + CATCH_MOVE(redirectedCout), + CATCH_MOVE(redirectedCerr), aborting())); m_activeTestCase = nullptr; @@ -5605,10 +5601,11 @@ namespace Catch { void RunContext::handleIncomplete( AssertionInfo const& info ) { + using namespace std::string_literals; m_lastAssertionInfo = info; AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionEnded( assertionResult ); } @@ -5663,7 +5660,7 @@ namespace Catch { Section::Section( SourceLineInfo const& _lineInfo, StringRef _name, const char* const ): - m_info( { "invalid", static_cast(-1) }, "" ), + m_info( { "invalid", static_cast( -1 ) }, std::string{} ), m_sectionIncluded( getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) { // We delay initialization the SectionInfo member until we know @@ -5900,10 +5897,6 @@ namespace Catch { : StringRef( rawChars, std::strlen(rawChars) ) {} - auto StringRef::operator == ( StringRef other ) const noexcept -> bool { - return m_size == other.m_size - && (std::memcmp( m_start, other.m_start, m_size ) == 0); - } bool StringRef::operator<(StringRef rhs) const noexcept { if (m_size < rhs.m_size) { @@ -6195,12 +6188,17 @@ namespace TestCaseTracking { m_children.push_back( CATCH_MOVE(child) ); } - ITracker* ITracker::findChild( NameAndLocationRef nameAndLocation ) { + ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) { auto it = std::find_if( m_children.begin(), m_children.end(), [&nameAndLocation]( ITrackerPtr const& tracker ) { - return tracker->nameAndLocation() == nameAndLocation; + auto const& tnameAndLoc = tracker->nameAndLocation(); + if ( tnameAndLoc.location.line != + nameAndLocation.location.line ) { + return false; + } + return tnameAndLoc == nameAndLocation; } ); return ( it != m_children.end() ) ? it->get() : nullptr; } @@ -6208,10 +6206,6 @@ namespace TestCaseTracking { bool ITracker::isSectionTracker() const { return false; } bool ITracker::isGeneratorTracker() const { return false; } - bool ITracker::isSuccessfullyCompleted() const { - return m_runState == CompletedSuccessfully; - } - bool ITracker::isOpen() const { return m_runState != NotStarted && !isComplete(); } @@ -6238,16 +6232,6 @@ namespace TestCaseTracking { return *m_rootTracker; } - void TrackerContext::endRun() { - m_rootTracker.reset(); - m_currentTracker = nullptr; - m_runState = NotStarted; - } - - void TrackerContext::startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } void TrackerContext::completeCycle() { m_runState = CompletedCycle; } @@ -6255,9 +6239,6 @@ namespace TestCaseTracking { bool TrackerContext::completedCycle() const { return m_runState == CompletedCycle; } - ITracker& TrackerContext::currentTracker() { - return *m_currentTracker; - } void TrackerContext::setCurrentTracker( ITracker* tracker ) { m_currentTracker = tracker; } @@ -6326,7 +6307,7 @@ namespace TestCaseTracking { SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), - m_trimmed_name(trim(ITracker::nameAndLocation().name)) + m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) { if( parent ) { while ( !parent->isSectionTracker() ) { @@ -6351,7 +6332,7 @@ namespace TestCaseTracking { bool SectionTracker::isSectionTracker() const { return true; } - SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef nameAndLocation ) { + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) { SectionTracker* tracker; ITracker& currentTracker = ctx.currentTracker(); @@ -6395,10 +6376,6 @@ namespace TestCaseTracking { m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); } - std::vector const& SectionTracker::getFilters() const { - return m_filters; - } - StringRef SectionTracker::trimmedName() const { return m_trimmed_name; } @@ -6697,10 +6674,8 @@ namespace Catch { token.erase(token.begin()); if (m_exclusion) { m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(".", m_substring)); - m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); } else { m_currentFilter.m_required.emplace_back(Detail::make_unique(".", m_substring)); - m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); } } if (m_exclusion) { @@ -7643,7 +7618,19 @@ WithinRelMatcher WithinRel(float target) { } -} // namespace Matchers + +bool IsNaNMatcher::match( double const& matchee ) const { + return std::isnan( matchee ); +} + +std::string IsNaNMatcher::describe() const { + using namespace std::string_literals; + return "is NaN"s; +} + +IsNaNMatcher IsNaN() { return IsNaNMatcher(); } + + } // namespace Matchers } // namespace Catch @@ -8080,7 +8067,7 @@ class AssertionPrinter { private: std::ostream& stream; AssertionResult const& result; - std::vector messages; + std::vector const& messages; std::vector::const_iterator itMessage; bool printInfoMessages; ColourImpl* colourImpl; @@ -8174,7 +8161,6 @@ class ConsoleAssertionPrinter { stats(_stats), result(_stats.assertionResult), colour(Colour::None), - message(result.getMessage()), messages(_stats.infoMessages), colourImpl(colourImpl_), printInfoMessages(_printInfoMessages) { @@ -8183,10 +8169,10 @@ class ConsoleAssertionPrinter { colour = Colour::Success; passOrFail = "PASSED"_sr; //if( result.hasMessage() ) - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; + if (messages.size() == 1) + messageLabel = "with message"_sr; + if (messages.size() > 1) + messageLabel = "with messages"_sr; break; case ResultWas::ExpressionFailed: if (result.isOk()) { @@ -8196,51 +8182,57 @@ class ConsoleAssertionPrinter { colour = Colour::Error; passOrFail = "FAILED"_sr; } - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; + if (messages.size() == 1) + messageLabel = "with message"_sr; + if (messages.size() > 1) + messageLabel = "with messages"_sr; break; case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"_sr; - messageLabel = "due to unexpected exception with "; - if (_stats.infoMessages.size() == 1) - messageLabel += "message"; - if (_stats.infoMessages.size() > 1) - messageLabel += "messages"; + // todo switch + switch (messages.size()) { case 0: + messageLabel = "due to unexpected exception with "_sr; + break; + case 1: + messageLabel = "due to unexpected exception with message"_sr; + break; + default: + messageLabel = "due to unexpected exception with messages"_sr; + break; + } break; case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"_sr; - messageLabel = "due to a fatal error condition"; + messageLabel = "due to a fatal error condition"_sr; break; case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"_sr; - messageLabel = "because no exception was thrown where one was expected"; + messageLabel = "because no exception was thrown where one was expected"_sr; break; case ResultWas::Info: - messageLabel = "info"; + messageLabel = "info"_sr; break; case ResultWas::Warning: - messageLabel = "warning"; + messageLabel = "warning"_sr; break; case ResultWas::ExplicitFailure: passOrFail = "FAILED"_sr; colour = Colour::Error; - if (_stats.infoMessages.size() == 1) - messageLabel = "explicitly with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "explicitly with messages"; + if (messages.size() == 1) + messageLabel = "explicitly with message"_sr; + if (messages.size() > 1) + messageLabel = "explicitly with messages"_sr; break; case ResultWas::ExplicitSkip: colour = Colour::Skip; passOrFail = "SKIPPED"_sr; - if (_stats.infoMessages.size() == 1) - messageLabel = "explicitly with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "explicitly with messages"; + if (messages.size() == 1) + messageLabel = "explicitly with message"_sr; + if (messages.size() > 1) + messageLabel = "explicitly with messages"_sr; break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: @@ -8304,9 +8296,8 @@ class ConsoleAssertionPrinter { AssertionResult const& result; Colour::Code colour; StringRef passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; + StringRef messageLabel; + std::vector const& messages; ColourImpl* colourImpl; bool printInfoMessages; }; @@ -10133,7 +10124,7 @@ namespace Catch { private: std::ostream& stream; AssertionResult const& result; - std::vector messages; + std::vector const& messages; std::vector::const_iterator itMessage; bool printInfoMessages; std::size_t counter; @@ -10396,7 +10387,7 @@ namespace Catch { void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ) - .writeAttribute( "name"_sr, trim( testInfo.name ) ) + .writeAttribute( "name"_sr, trim( StringRef(testInfo.name) ) ) .writeAttribute( "tags"_sr, testInfo.tagsAsString() ); writeSourceInfo( testInfo.lineInfo ); @@ -10410,7 +10401,7 @@ namespace Catch { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) - .writeAttribute( "name"_sr, trim( sectionInfo.name ) ); + .writeAttribute( "name"_sr, trim( StringRef(sectionInfo.name) ) ); writeSourceInfo( sectionInfo.lineInfo ); m_xml.ensureTagClosed(); } @@ -10524,11 +10515,10 @@ namespace Catch { if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() ); - if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); + m_xml.scopedElement( "StdOut" ).writeText( trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline ); if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); + m_xml.scopedElement( "StdErr" ).writeText( trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline ); m_xml.endElement(); } diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 88f2dd912c..321cec5dac 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.1 -// Generated: 2023-01-29 22:55:03.856079 +// Catch v3.3.2 +// Generated: 2023-02-26 10:28:46.785908 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -95,6 +95,8 @@ namespace Catch { #include #include +#include + namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) @@ -131,7 +133,10 @@ namespace Catch { } public: // operators - auto operator == ( StringRef other ) const noexcept -> bool; + auto operator == ( StringRef other ) const noexcept -> bool { + return m_size == other.m_size + && (std::memcmp( m_start, other.m_start, m_size ) == 0); + } auto operator != (StringRef other) const noexcept -> bool { return !(*this == other); } @@ -352,7 +357,7 @@ namespace Catch { // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) @@ -371,6 +376,12 @@ namespace Catch { #endif +#if defined(__NVCOMPILER) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" ) +#endif + #if defined(__CUDACC__) && !defined(__clang__) # ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ // New pragmas introduced in CUDA 11.5+ @@ -1434,8 +1445,8 @@ namespace Catch { struct TestCaseStats { TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, + std::string&& _stdOut, + std::string&& _stdErr, bool _aborting ); TestCaseInfo const * testInfo; @@ -4518,7 +4529,10 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ - Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + Catch::Capturer varName( macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info, \ + #__VA_ARGS__##_catch_sr ); \ varName.captureValues( 0, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// @@ -5904,9 +5918,9 @@ namespace Catch { #if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) - #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr #else - #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr #endif #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) @@ -6233,7 +6247,13 @@ struct AutoReg : Detail::NonCopyable { CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ + namespace { \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &QualifiedMethod ), \ + CATCH_INTERNAL_LINEINFO, \ + "&" #QualifiedMethod##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); \ + } /* NOLINT */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION /////////////////////////////////////////////////////////////////////////////// @@ -6245,7 +6265,11 @@ struct AutoReg : Detail::NonCopyable { struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ void test(); \ }; \ - const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &TestName::test ), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ void TestName::test() @@ -6878,7 +6902,7 @@ struct AutoReg : Detail::NonCopyable { void reg_tests() { \ size_t index = 0; \ using expander = size_t[]; \ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ } \ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ @@ -7013,7 +7037,7 @@ struct AutoReg : Detail::NonCopyable { void reg_tests(){\ size_t index = 0;\ using expander = size_t[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -7402,7 +7426,7 @@ namespace Catch { #define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MINOR 3 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_PATCH 2 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7754,16 +7778,19 @@ namespace Detail { } // namespace Generators } // namespace Catch +#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr +#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__) + #define GENERATE( ... ) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ CATCH_INTERNAL_LINEINFO, \ [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_COPY( ... ) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ CATCH_INTERNAL_LINEINFO, \ [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_REF( ... ) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ CATCH_INTERNAL_LINEINFO, \ [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) @@ -8919,6 +8946,139 @@ namespace Detail { #endif // CATCH_GETENV_HPP_INCLUDED +#ifndef CATCH_IS_PERMUTATION_HPP_INCLUDED +#define CATCH_IS_PERMUTATION_HPP_INCLUDED + +#include +#include + +namespace Catch { + namespace Detail { + + template + ForwardIter find_sentinel( ForwardIter start, + Sentinel sentinel, + T const& value, + Comparator cmp ) { + while ( start != sentinel ) { + if ( cmp( *start, value ) ) { break; } + ++start; + } + return start; + } + + template + std::ptrdiff_t count_sentinel( ForwardIter start, + Sentinel sentinel, + T const& value, + Comparator cmp ) { + std::ptrdiff_t count = 0; + while ( start != sentinel ) { + if ( cmp( *start, value ) ) { ++count; } + ++start; + } + return count; + } + + template + std::enable_if_t::value, + std::ptrdiff_t> + sentinel_distance( ForwardIter iter, const Sentinel sentinel ) { + std::ptrdiff_t dist = 0; + while ( iter != sentinel ) { + ++iter; + ++dist; + } + return dist; + } + + template + std::ptrdiff_t sentinel_distance( ForwardIter first, + ForwardIter last ) { + return std::distance( first, last ); + } + + template + bool check_element_counts( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { + auto cursor = first_1; + while ( cursor != end_1 ) { + if ( find_sentinel( first_1, cursor, *cursor, cmp ) == + cursor ) { + // we haven't checked this element yet + const auto count_in_range_2 = + count_sentinel( first_2, end_2, *cursor, cmp ); + // Not a single instance in 2nd range, so it cannot be a + // permutation of 1st range + if ( count_in_range_2 == 0 ) { return false; } + + const auto count_in_range_1 = + count_sentinel( cursor, end_1, *cursor, cmp ); + if ( count_in_range_1 != count_in_range_2 ) { + return false; + } + } + + ++cursor; + } + + return true; + } + + template + bool is_permutation( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { + // TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types + // TODO: Comparator has to be "both sides", e.g. a == b => b == a + // This skips shared prefix of the two ranges + while (first_1 != end_1 && first_2 != end_2 && cmp(*first_1, *first_2)) { + ++first_1; + ++first_2; + } + + // We need to handle case where at least one of the ranges has no more elements + if (first_1 == end_1 || first_2 == end_2) { + return first_1 == end_1 && first_2 == end_2; + } + + // pair counting is n**2, so we pay linear walk to compare the sizes first + auto dist_1 = sentinel_distance( first_1, end_1 ); + auto dist_2 = sentinel_distance( first_2, end_2 ); + + if (dist_1 != dist_2) { return false; } + + // Since we do not try to handle stronger iterators pair (e.g. + // bidir) optimally, the only thing left to do is to check counts in + // the remaining ranges. + return check_element_counts( first_1, end_1, first_2, end_2, cmp ); + } + + } // namespace Detail +} // namespace Catch + +#endif // CATCH_IS_PERMUTATION_HPP_INCLUDED + + #ifndef CATCH_ISTREAM_HPP_INCLUDED #define CATCH_ISTREAM_HPP_INCLUDED @@ -9199,8 +9359,12 @@ namespace TestCaseTracking { NameAndLocation( std::string&& _name, SourceLineInfo const& _location ); friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { - return lhs.name == rhs.name - && lhs.location == rhs.location; + // This is a very cheap check that should have a very high hit rate. + // If we get to SourceLineInfo::operator==, we will redo it, but the + // cost of repeating is trivial at that point (we will be paying + // multiple strcmp/memcmps at that point). + if ( lhs.location.line != rhs.location.line ) { return false; } + return lhs.name == rhs.name && lhs.location == rhs.location; } friend bool operator!=(NameAndLocation const& lhs, NameAndLocation const& rhs) { @@ -9224,11 +9388,16 @@ namespace TestCaseTracking { name( name_ ), location( location_ ) {} friend bool operator==( NameAndLocation const& lhs, - NameAndLocationRef rhs ) { + NameAndLocationRef const& rhs ) { + // This is a very cheap check that should have a very high hit rate. + // If we get to SourceLineInfo::operator==, we will redo it, but the + // cost of repeating is trivial at that point (we will be paying + // multiple strcmp/memcmps at that point). + if ( lhs.location.line != rhs.location.line ) { return false; } return StringRef( lhs.name ) == rhs.name && lhs.location == rhs.location; } - friend bool operator==( NameAndLocationRef lhs, + friend bool operator==( NameAndLocationRef const& lhs, NameAndLocation const& rhs ) { return rhs == lhs; } @@ -9280,7 +9449,9 @@ namespace TestCaseTracking { //! Returns true if tracker run to completion (successfully or not) virtual bool isComplete() const = 0; //! Returns true if tracker run to completion succesfully - bool isSuccessfullyCompleted() const; + bool isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } //! Returns true if tracker has started but hasn't been completed bool isOpen() const; //! Returns true iff tracker has started @@ -9298,7 +9469,7 @@ namespace TestCaseTracking { * * Returns nullptr if not found. */ - ITracker* findChild( NameAndLocationRef nameAndLocation ); + ITracker* findChild( NameAndLocationRef const& nameAndLocation ); //! Have any children been added? bool hasChildren() const { return !m_children.empty(); @@ -9339,13 +9510,15 @@ namespace TestCaseTracking { public: ITracker& startRun(); - void endRun(); - void startCycle(); + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } void completeCycle(); bool completedCycle() const; - ITracker& currentTracker(); + ITracker& currentTracker() { return *m_currentTracker; } void setCurrentTracker( ITracker* tracker ); }; @@ -9371,7 +9544,11 @@ namespace TestCaseTracking { class SectionTracker : public TrackerBase { std::vector m_filters; - std::string m_trimmed_name; + // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`. + // Currently it allocates owns the name, so this is safe. If it is later refactored + // to not own the name, the name still has to outlive the `ITracker` parent, so + // this should still be safe. + StringRef m_trimmed_name; public: SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ); @@ -9379,14 +9556,14 @@ namespace TestCaseTracking { bool isComplete() const override; - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef nameAndLocation ); + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ); void tryOpen(); void addInitialFilters( std::vector const& filters ); void addNextFilters( std::vector const& filters ); //! Returns filters active in this tracker - std::vector const& getFilters() const; + std::vector const& getFilters() const { return m_filters; } //! Returns whitespace-trimmed name of the tracked section StringRef trimmedName() const; }; @@ -10960,13 +11137,11 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { - using std::begin; using std::end; - - return end(rng) != std::find_if(begin(rng), end(rng), - [&](auto const& elem) { - return m_eq(elem, m_desired); - }); + bool match( RangeLike&& rng ) const { + for ( auto&& elem : rng ) { + if ( m_eq( elem, m_desired ) ) { return true; } + } + return false; } }; @@ -11018,7 +11193,7 @@ namespace Catch { /** * Creates a matcher that checks whether a range contains a specific element. * - * Uses `eq` to do the comparisons + * Uses `eq` to do the comparisons, the element is provided on the rhs */ template ContainsElementMatcher Contains(T&& elem, Equality&& eq) { @@ -11107,6 +11282,11 @@ namespace Matchers { double m_margin; }; + //! Creates a matcher that accepts numbers within certain range of target + WithinAbsMatcher WithinAbs( double target, double margin ); + + + class WithinUlpsMatcher final : public MatcherBase { public: WithinUlpsMatcher( double target, @@ -11120,6 +11300,13 @@ namespace Matchers { Detail::FloatingPointKind m_type; }; + //! Creates a matcher that accepts doubles within certain ULP range of target + WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); + //! Creates a matcher that accepts floats within certain ULP range of target + WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); + + + // Given IEEE-754 format for floats and doubles, we can assume // that float -> double promotion is lossless. Given this, we can // assume that if we do the standard relative comparison of @@ -11136,13 +11323,6 @@ namespace Matchers { double m_epsilon; }; - //! Creates a matcher that accepts doubles within certain ULP range of target - WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); - //! Creates a matcher that accepts floats within certain ULP range of target - WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); - //! Creates a matcher that accepts numbers within certain range of target - WithinAbsMatcher WithinAbs(double target, double margin); - //! Creates a matcher that accepts doubles within certain relative range of target WithinRelMatcher WithinRel(double target, double eps); //! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target @@ -11152,6 +11332,17 @@ namespace Matchers { //! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target WithinRelMatcher WithinRel(float target); + + + class IsNaNMatcher final : public MatcherBase { + public: + IsNaNMatcher() = default; + bool match( double const& matchee ) const override; + std::string describe() const override; + }; + + IsNaNMatcher IsNaN(); + } // namespace Matchers } // namespace Catch @@ -11370,6 +11561,7 @@ namespace Catch { #ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED #define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED + #include #include @@ -11394,13 +11586,19 @@ namespace Catch { template bool match( RangeLike&& rng ) const { - using std::begin; - using std::end; - return std::equal( begin(m_desired), - end(m_desired), - begin(rng), - end(rng), - m_predicate ); + auto rng_start = begin( rng ); + const auto rng_end = end( rng ); + auto target_start = begin( m_desired ); + const auto target_end = end( m_desired ); + + while (rng_start != rng_end && target_start != target_end) { + if (!m_predicate(*rng_start, *target_start)) { + return false; + } + ++rng_start; + ++target_start; + } + return rng_start == rng_end && target_start == target_end; } std::string describe() const override { @@ -11428,11 +11626,11 @@ namespace Catch { bool match( RangeLike&& rng ) const { using std::begin; using std::end; - return std::is_permutation( begin( m_desired ), - end( m_desired ), - begin( rng ), - end( rng ), - m_predicate ); + return Catch::Detail::is_permutation( begin( m_desired ), + end( m_desired ), + begin( rng ), + end( rng ), + m_predicate ); } std::string describe() const override { @@ -12549,7 +12747,7 @@ namespace Catch { CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace { \ Catch::ListenerRegistrar INTERNAL_CATCH_UNIQUE_NAME( \ - catch_internal_RegistrarFor )( #listenerType ); \ + catch_internal_RegistrarFor )( #listenerType##_catch_sr ); \ } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION diff --git a/meson.build b/meson.build index 5555ee4008..1faca35f7a 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.3.1', # CML version placeholder, don't delete + version: '3.3.2', # 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 d797a3b12e..19cab91b3d 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, 3, 1, "", 0 ); + static Version version( 3, 3, 2, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 75075dd5ce..9ece850511 100644 --- a/src/catch2/catch_version_macros.hpp +++ b/src/catch2/catch_version_macros.hpp @@ -10,6 +10,6 @@ #define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MINOR 3 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_PATCH 2 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED