From 6e79e682b726f524310d55dec8ddac4e9c52fb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Thu, 13 Jul 2023 13:37:30 +0200 Subject: [PATCH] v3.4.0 --- CMakeLists.txt | 2 +- docs/configuration.md | 2 +- docs/release-notes.md | 28 + extras/catch_amalgamated.cpp | 1036 ++++++----- extras/catch_amalgamated.hpp | 2535 ++++++++++++++------------- meson.build | 2 +- src/catch2/catch_version.cpp | 2 +- src/catch2/catch_version_macros.hpp | 4 +- 8 files changed, 1975 insertions(+), 1636 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ef06de7..42f65237 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.3.2 # CML version placeholder, don't delete + VERSION 3.4.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/configuration.md b/docs/configuration.md index d6e159e5..49b8ed9d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -267,7 +267,7 @@ must compile and must break into debugger. ## Static analysis support -> Introduced in Catch2 X.Y.Z. +> Introduced in Catch2 3.4.0. Some parts of Catch2, e.g. `SECTION`s, can be hard for static analysis tools to reason about. Catch2 can change its internals to help static diff --git a/docs/release-notes.md b/docs/release-notes.md index 8b413b15..a5911862 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -57,6 +57,34 @@ +## 3.4.0 + +### Improvements +* `VectorEquals` supports elements that provide only `==` and not `!=` (#2648) +* Catch2 supports compiling with IAR compiler (#2651) +* Various small internal performance improvements +* Various small internal compilation time improvements +* XMLReporter now reports location info for INFO and WARN (#1251) + * This bumps up the xml format version to 3 +* Documented that `SKIP` in generator constructor can be used to handle empty generator (#1593) +* Added experimental static analysis support to `TEST_CASE` and `SECTION` macros (#2681) + * The two macros are redefined in a way that helps the SA tools reason about the possible paths through a test case with sections. + * The support is controlled by the `CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT` option and autodetects clang-tidy and Coverity. +* `*_THROWS`, `*_THROWS_AS`, etc now suppress warning coming from `__attribute__((warn_unused_result))` on GCC (#2691) + * Unlike plain `[[nodiscard]]`, this warning is not silenced by void cast. WTF GCC? + +### Fixes +* Fixed `assertionStarting` events being sent after the expr is evaluated (#2678) +* Errors in `TEST_CASE` tags are now reported nicely (#2650) + +### Miscellaneous +* Bunch of improvements to `catch_discover_tests` + * Added DISCOVERY_MODE option, so the discovery can happen either post build or pre-run. + * Fixed handling of semicolons and backslashes in test names (#2674, #2676) +* meson build can disable building tests (#2693) +* meson build properly sets meson version 0.54.1 as the minimal supported version (#2688) + + ## 3.3.2 ### Improvements diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index a81b1b6a..eba3f00a 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.2 -// Generated: 2023-02-26 10:28:48.270752 +// Catch v3.4.0 +// Generated: 2023-07-13 13:23:09.554273 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -60,6 +60,7 @@ namespace Catch { + #include namespace Catch { @@ -88,7 +89,7 @@ namespace Catch { #include #include -#include +#include #include @@ -96,117 +97,172 @@ namespace Catch { #include #endif -namespace { +namespace Catch { + namespace Benchmark { + namespace Detail { + namespace { -using Catch::Benchmark::Detail::sample; + template + static sample + resample( URng& rng, + unsigned int resamples, + std::vector::const_iterator first, + std::vector::const_iterator last, + Estimator& estimator ) { + auto n = static_cast( last - first ); + std::uniform_int_distribution dist( 0, + n - 1 ); - template - sample resample(URng& rng, unsigned int resamples, std::vector::iterator first, std::vector::iterator last, Estimator& estimator) { - auto n = static_cast(last - first); - std::uniform_int_distribution dist(0, n - 1); + sample out; + out.reserve( resamples ); + // We allocate the vector outside the loop to avoid realloc + // per resample + std::vector resampled; + resampled.reserve( n ); + for ( size_t i = 0; i < resamples; ++i ) { + resampled.clear(); + for ( size_t s = 0; s < n; ++s ) { + resampled.push_back( + first[static_cast( + dist( rng ) )] ); + } + const auto estimate = + estimator( resampled.begin(), resampled.end() ); + out.push_back( estimate ); + } + std::sort( out.begin(), out.end() ); + return out; + } - sample out; - out.reserve(resamples); - std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { - std::vector resampled; - resampled.reserve(n); - std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast(dist(rng))]; }); - return estimator(resampled.begin(), resampled.end()); - }); - std::sort(out.begin(), out.end()); - return out; - } + static double outlier_variance( Estimate mean, + Estimate stddev, + int n ) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) ); + double sg2 = sg * sg; + double sb2 = sb * sb; + auto c_max = [n, mn, sb2, sg2]( double x ) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return static_cast( -2. * k0 / + ( k1 + std::sqrt( det ) ) ); + }; - double erf_inv(double x) { - // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 - double w, p; + auto var_out = [n, sb2, sg2]( double c ) { + double nc = n - c; + return ( nc / n ) * ( sb2 - nc * sg2 ); + }; - w = -log((1.0 - x) * (1.0 + x)); + return (std::min)( var_out( 1 ), + var_out( + (std::min)( c_max( 0. ), + c_max( mg_min ) ) ) ) / + sb2; + } - if (w < 6.250000) { - w = w - 3.125000; - p = -3.6444120640178196996e-21; - p = -1.685059138182016589e-19 + p * w; - p = 1.2858480715256400167e-18 + p * w; - p = 1.115787767802518096e-17 + p * w; - p = -1.333171662854620906e-16 + p * w; - p = 2.0972767875968561637e-17 + p * w; - p = 6.6376381343583238325e-15 + p * w; - p = -4.0545662729752068639e-14 + p * w; - p = -8.1519341976054721522e-14 + p * w; - p = 2.6335093153082322977e-12 + p * w; - p = -1.2975133253453532498e-11 + p * w; - p = -5.4154120542946279317e-11 + p * w; - p = 1.051212273321532285e-09 + p * w; - p = -4.1126339803469836976e-09 + p * w; - p = -2.9070369957882005086e-08 + p * w; - p = 4.2347877827932403518e-07 + p * w; - p = -1.3654692000834678645e-06 + p * w; - p = -1.3882523362786468719e-05 + p * w; - p = 0.0001867342080340571352 + p * w; - p = -0.00074070253416626697512 + p * w; - p = -0.0060336708714301490533 + p * w; - p = 0.24015818242558961693 + p * w; - p = 1.6536545626831027356 + p * w; - } else if (w < 16.000000) { - w = sqrt(w) - 3.250000; - p = 2.2137376921775787049e-09; - p = 9.0756561938885390979e-08 + p * w; - p = -2.7517406297064545428e-07 + p * w; - p = 1.8239629214389227755e-08 + p * w; - p = 1.5027403968909827627e-06 + p * w; - p = -4.013867526981545969e-06 + p * w; - p = 2.9234449089955446044e-06 + p * w; - p = 1.2475304481671778723e-05 + p * w; - p = -4.7318229009055733981e-05 + p * w; - p = 6.8284851459573175448e-05 + p * w; - p = 2.4031110387097893999e-05 + p * w; - p = -0.0003550375203628474796 + p * w; - p = 0.00095328937973738049703 + p * w; - p = -0.0016882755560235047313 + p * w; - p = 0.0024914420961078508066 + p * w; - p = -0.0037512085075692412107 + p * w; - p = 0.005370914553590063617 + p * w; - p = 1.0052589676941592334 + p * w; - p = 3.0838856104922207635 + p * w; - } else { - w = sqrt(w) - 5.000000; - p = -2.7109920616438573243e-11; - p = -2.5556418169965252055e-10 + p * w; - p = 1.5076572693500548083e-09 + p * w; - p = -3.7894654401267369937e-09 + p * w; - p = 7.6157012080783393804e-09 + p * w; - p = -1.4960026627149240478e-08 + p * w; - p = 2.9147953450901080826e-08 + p * w; - p = -6.7711997758452339498e-08 + p * w; - p = 2.2900482228026654717e-07 + p * w; - p = -9.9298272942317002539e-07 + p * w; - p = 4.5260625972231537039e-06 + p * w; - p = -1.9681778105531670567e-05 + p * w; - p = 7.5995277030017761139e-05 + p * w; - p = -0.00021503011930044477347 + p * w; - p = -0.00013871931833623122026 + p * w; - p = 1.0103004648645343977 + p * w; - p = 4.8499064014085844221 + p * w; - } - return p * x; - } + static double erf_inv( double x ) { + // Code accompanying the article "Approximating the erfinv + // function" in GPU Computing Gems, Volume 2 + double w, p; - double standard_deviation(std::vector::iterator first, std::vector::iterator last) { - auto m = Catch::Benchmark::Detail::mean(first, last); - double variance = std::accumulate( first, - last, - 0., - [m]( double a, double b ) { - double diff = b - m; - return a + diff * diff; - } ) / - ( last - first ); - return std::sqrt( variance ); - } + w = -log( ( 1.0 - x ) * ( 1.0 + x ) ); -} + if ( w < 6.250000 ) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if ( w < 16.000000 ) { + w = sqrt( w ) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt( w ) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + static double + standard_deviation( std::vector::const_iterator first, + std::vector::const_iterator last ) { + auto m = Catch::Benchmark::Detail::mean( first, last ); + double variance = + std::accumulate( first, + last, + 0., + [m]( double a, double b ) { + double diff = b - m; + return a + diff * diff; + } ) / + ( last - first ); + return std::sqrt( variance ); + } + + } // namespace + } // namespace Detail + } // namespace Benchmark +} // namespace Catch namespace Catch { namespace Benchmark { @@ -236,6 +292,47 @@ namespace Catch { return xj + g * (xj1 - xj); } + OutlierClassification + classify_outliers( std::vector::const_iterator first, + std::vector::const_iterator last ) { + std::vector copy( first, last ); + + auto q1 = weighted_average_quantile( 1, 4, copy.begin(), copy.end() ); + auto q3 = weighted_average_quantile( 3, 4, copy.begin(), copy.end() ); + auto iqr = q3 - q1; + auto los = q1 - ( iqr * 3. ); + auto lom = q1 - ( iqr * 1.5 ); + auto him = q3 + ( iqr * 1.5 ); + auto his = q3 + ( iqr * 3. ); + + OutlierClassification o; + for ( ; first != last; ++first ) { + const double t = *first; + if ( t < los ) { + ++o.low_severe; + } else if ( t < lom ) { + ++o.low_mild; + } else if ( t > his ) { + ++o.high_severe; + } else if ( t > him ) { + ++o.high_mild; + } + ++o.samples_seen; + } + return o; + } + + double mean( std::vector::const_iterator first, + std::vector::const_iterator last ) { + auto count = last - first; + double sum = 0.; + while (first != last) { + sum += *first; + ++first; + } + return sum / static_cast(count); + } + double erfc_inv(double x) { return erf_inv(1.0 - x); @@ -257,35 +354,10 @@ namespace Catch { return result; } - - double outlier_variance(Estimate mean, Estimate stddev, int n) { - double sb = stddev.point; - double mn = mean.point / n; - double mg_min = mn / 2.; - double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); - double sg2 = sg * sg; - double sb2 = sb * sb; - - auto c_max = [n, mn, sb2, sg2](double x) -> double { - double k = mn - x; - double d = k * k; - double nd = n * d; - double k0 = -n * nd; - double k1 = sb2 - n * sg2 + nd; - double det = k1 * k1 - 4 * sg2 * k0; - return static_cast(-2. * k0 / (k1 + std::sqrt(det))); - }; - - auto var_out = [n, sb2, sg2](double c) { - double nc = n - c; - return (nc / n) * (sb2 - nc * sg2); - }; - - return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; - } - - - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last) { + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + std::vector::iterator first, + std::vector::iterator last) { CATCH_INTERNAL_START_WARNINGS_SUPPRESSION CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS static std::random_device entropy; @@ -293,11 +365,12 @@ namespace Catch { auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ - auto mean = &Detail::mean::iterator>; + auto mean = &Detail::mean; auto stddev = &standard_deviation; #if defined(CATCH_CONFIG_USE_ASYNC) - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { + auto Estimate = [=](double(*f)(std::vector::const_iterator, + std::vector::const_iterator)) { auto seed = entropy(); return std::async(std::launch::async, [=] { std::mt19937 rng(seed); @@ -312,7 +385,8 @@ namespace Catch { auto mean_estimate = mean_future.get(); auto stddev_estimate = stddev_future.get(); #else - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { + auto Estimate = [=](double(*f)(std::vector::const_iterator, + std::vector::const_iterator)) { auto seed = entropy(); std::mt19937 rng(seed); auto resampled = resample(rng, n_resamples, first, last, f); @@ -596,7 +670,7 @@ namespace Catch { elem = trim(elem); } - // Insert the default reporter if user hasn't asked for a specfic one + // Insert the default reporter if user hasn't asked for a specific one if ( m_data.reporterSpecifications.empty() ) { m_data.reporterSpecifications.push_back( { #if defined( CATCH_CONFIG_DEFAULT_REPORTER ) @@ -775,7 +849,11 @@ namespace Catch { } - Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + Capturer::Capturer( StringRef macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType resultType, + StringRef names ): + m_resultCapture( getResultCapture() ) { auto trimmed = [&] (size_t start, size_t end) { while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; @@ -852,6 +930,8 @@ namespace Catch { +#include + namespace Catch { namespace { @@ -862,7 +942,7 @@ namespace Catch { public: // IRegistryHub RegistryHub() = default; - IReporterRegistry const& getReporterRegistry() const override { + ReporterRegistry const& getReporterRegistry() const override { return m_reporterRegistry; } ITestCaseRegistry const& getTestCaseRegistry() const override { @@ -938,6 +1018,7 @@ namespace Catch { #include #include +#include #include #include @@ -1420,12 +1501,20 @@ namespace Catch { for (size_t idx = 0; idx < originalTags.size(); ++idx) { auto c = originalTags[idx]; if (c == '[') { - assert(!inTag); + CATCH_ENFORCE( + !inTag, + "Found '[' inside a tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = true; tagStart = idx; } if (c == ']') { - assert(inTag); + CATCH_ENFORCE( + inTag, + "Found unmatched ']' while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = false; tagEnd = idx; assert(tagStart < tagEnd); @@ -1434,7 +1523,11 @@ namespace Catch { // it over to backing storage and actually reference the // backing storage in the saved tags StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1); - CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed"); + CATCH_ENFORCE( !tagStr.empty(), + "Found an empty tag while registering test case '" + << _nameAndTags.name << "' at " + << _lineInfo ); + enforceNotReservedTag(tagStr, lineInfo); properties |= parseSpecialTag(tagStr); // When copying a tag to the backing storage, we need to @@ -1448,8 +1541,12 @@ namespace Catch { // the tags. internalAppendTag(tagStr); } - (void)inTag; // Silence "set-but-unused" warning in release mode. } + CATCH_ENFORCE( !inTag, + "Found an unclosed tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + + // Add [.] if relevant if (isHidden()) { internalAppendTag("."_sr); @@ -1625,16 +1722,18 @@ namespace Catch { return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); } - TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const - { - Matches matches( m_filters.size() ); - std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ + TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const { + Matches matches; + matches.reserve( m_filters.size() ); + for ( auto const& filter : m_filters ) { std::vector currentMatches; - for( auto const& test : testCases ) - if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) ) + for ( auto const& test : testCases ) + if ( isThrowSafe( test, config ) && + filter.matches( test.getTestCaseInfo() ) ) currentMatches.emplace_back( &test ); - return FilterMatch{ extractFilterName(filter), currentMatches }; - } ); + matches.push_back( + FilterMatch{ extractFilterName( filter ), currentMatches } ); + } return matches; } @@ -1991,6 +2090,19 @@ namespace Catch { } + + +namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ) { + getMutableRegistryHub().registerTranslator( + CATCH_MOVE( translator ) ); + } + } // namespace Detail +} // namespace Catch + + #include namespace Catch { @@ -2021,7 +2133,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 3, 2, "", 0 ); + static Version version( 3, 4, 0, "", 0 ); return version; } @@ -2172,8 +2284,6 @@ namespace Catch { infoMessages( _infoMessages ), totals( _totals ) { - assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; - if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere @@ -2232,14 +2342,6 @@ namespace Catch { namespace Catch { - IReporterRegistry::~IReporterRegistry() = default; -} - - - - -namespace Catch { - ITestInvoker::~ITestInvoker() = default; ITestCaseRegistry::~ITestCaseRegistry() = default; } @@ -2254,7 +2356,9 @@ namespace Catch { ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, m_resultCapture( getResultCapture() ) - {} + { + m_resultCapture.notifyAssertionStarted( m_assertionInfo ); + } void AssertionHandler::handleExpr( ITransientExpression const& expr ) { m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); @@ -2268,7 +2372,7 @@ namespace Catch { } void AssertionHandler::complete() { - setCompleted(); + m_completed = true; if( m_reaction.shouldDebugBreak ) { // If you find your debugger stopping you here then go one level up on the @@ -2281,16 +2385,9 @@ namespace Catch { throw_test_failure_exception(); } if ( m_reaction.shouldSkip ) { -#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); -#else - CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); -#endif + throw_test_skip_exception(); } } - void AssertionHandler::setCompleted() { - m_completed = true; - } void AssertionHandler::handleUnexpectedInflightException() { m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); @@ -2917,7 +3014,7 @@ namespace Catch { auto const& reporterSpec = *parsed; - IReporterRegistry::FactoryMap const& factories = + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); auto result = factories.find( reporterSpec.name() ); @@ -3155,7 +3252,7 @@ namespace Catch { namespace { //! A do-nothing implementation of colour, used as fallback for unknown //! platforms, and when the user asks to deactivate all colours. - class NoColourImpl : public ColourImpl { + class NoColourImpl final : public ColourImpl { public: NoColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3173,7 +3270,7 @@ namespace Catch { namespace Catch { namespace { - class Win32ColourImpl : public ColourImpl { + class Win32ColourImpl final : public ColourImpl { public: Win32ColourImpl(IStream* stream): ColourImpl(stream) { @@ -3239,7 +3336,7 @@ namespace { namespace Catch { namespace { - class ANSIColourImpl : public ColourImpl { + class ANSIColourImpl final : public ColourImpl { public: ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3355,49 +3452,27 @@ namespace Catch { namespace Catch { - class Context : public IMutableContext, private Detail::NonCopyable { + Context* Context::currentContext = nullptr; - public: // IContext - IResultCapture* getResultCapture() override { - return m_resultCapture; - } - - IConfig const* getConfig() const override { - return m_config; - } - - ~Context() override; - - public: // IMutableContext - void setResultCapture( IResultCapture* resultCapture ) override { - m_resultCapture = resultCapture; - } - void setConfig( IConfig const* config ) override { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IConfig const* m_config = nullptr; - IResultCapture* m_resultCapture = nullptr; - }; - - IMutableContext *IMutableContext::currentContext = nullptr; - - void IMutableContext::createContext() - { + void cleanUpContext() { + delete Context::currentContext; + Context::currentContext = nullptr; + } + void Context::createContext() { currentContext = new Context(); } - void cleanUpContext() { - delete IMutableContext::currentContext; - IMutableContext::currentContext = nullptr; + Context& getCurrentMutableContext() { + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } - IContext::~IContext() = default; - IMutableContext::~IMutableContext() = default; - Context::~Context() = default; + void Context::setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + + void Context::setConfig( IConfig const* config ) { m_config = config; } SimplePcg32& sharedRng() { static SimplePcg32 s_rng; @@ -3680,8 +3755,24 @@ namespace Catch { +#include + namespace Catch { + namespace { + static std::string tryTranslators( + std::vector< + Detail::unique_ptr> const& translators ) { + if ( translators.empty() ) { + std::rethrow_exception( std::current_exception() ); + } else { + return translators[0]->translate( translators.begin() + 1, + translators.end() ); + } + } + + } + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { } @@ -3706,7 +3797,7 @@ namespace Catch { // First we try user-registered translators. If none of them can // handle the exception, it will be rethrown handled by our defaults. try { - return tryTranslators(); + return tryTranslators(m_translators); } // To avoid having to handle TFE explicitly everywhere, we just // rethrow it so that it goes back up the caller. @@ -3730,25 +3821,12 @@ namespace Catch { } } - std::string ExceptionTranslatorRegistry::tryTranslators() const { - if (m_translators.empty()) { - std::rethrow_exception(std::current_exception()); - } else { - return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); - } - } - #else // ^^ Exceptions are enabled // Exceptions are disabled vv std::string ExceptionTranslatorRegistry::translateActiveException() const { CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } - - std::string ExceptionTranslatorRegistry::tryTranslators() const { - CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); - } #endif - } @@ -4053,7 +4131,7 @@ namespace Catch { namespace Detail { namespace { template - class StreamBufImpl : public std::streambuf { + class StreamBufImpl final : public std::streambuf { char data[bufferSize]; WriterF m_writer; @@ -4101,7 +4179,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class FileStream : public IStream { + class FileStream final : public IStream { std::ofstream m_ofs; public: FileStream( std::string const& filename ) { @@ -4118,7 +4196,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class CoutStream : public IStream { + class CoutStream final : public IStream { std::ostream m_os; public: // Store the streambuf from cout up-front because @@ -4147,7 +4225,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class DebugOutStream : public IStream { + class DebugOutStream final : public IStream { Detail::unique_ptr> m_streamBuf; std::ostream m_os; public: @@ -4277,7 +4355,7 @@ namespace Catch { void listReporters(IEventListener& reporter) { std::vector descriptions; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); descriptions.reserve(factories.size()); for (auto const& fac : factories) { descriptions.push_back({ fac.first, fac.second->getDescription() }); @@ -4696,49 +4774,71 @@ namespace Catch { namespace Catch { + struct ReporterRegistry::ReporterRegistryImpl { + std::vector> listeners; + std::map + factories; + }; - ReporterRegistry::ReporterRegistry() { + ReporterRegistry::ReporterRegistry(): + m_impl( Detail::make_unique() ) { // Because it is impossible to move out of initializer list, // we have to add the elements manually - m_factories["Automake"] = Detail::make_unique>(); - m_factories["compact"] = Detail::make_unique>(); - m_factories["console"] = Detail::make_unique>(); - m_factories["JUnit"] = Detail::make_unique>(); - m_factories["SonarQube"] = Detail::make_unique>(); - m_factories["TAP"] = Detail::make_unique>(); - m_factories["TeamCity"] = Detail::make_unique>(); - m_factories["XML"] = Detail::make_unique>(); + m_impl->factories["Automake"] = + Detail::make_unique>(); + m_impl->factories["compact"] = + Detail::make_unique>(); + m_impl->factories["console"] = + Detail::make_unique>(); + m_impl->factories["JUnit"] = + Detail::make_unique>(); + m_impl->factories["SonarQube"] = + Detail::make_unique>(); + m_impl->factories["TAP"] = + Detail::make_unique>(); + m_impl->factories["TeamCity"] = + Detail::make_unique>(); + m_impl->factories["XML"] = + Detail::make_unique>(); } ReporterRegistry::~ReporterRegistry() = default; - - IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const { - auto it = m_factories.find( name ); - if( it == m_factories.end() ) - return nullptr; - return it->second->create( CATCH_MOVE(config) ); + IEventListenerPtr + ReporterRegistry::create( std::string const& name, + ReporterConfig&& config ) const { + auto it = m_impl->factories.find( name ); + if ( it == m_impl->factories.end() ) return nullptr; + return it->second->create( CATCH_MOVE( config ) ); } - void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { + void ReporterRegistry::registerReporter( std::string const& name, + IReporterFactoryPtr factory ) { CATCH_ENFORCE( name.find( "::" ) == name.npos, - "'::' is not allowed in reporter name: '" + name + '\'' ); - auto ret = m_factories.emplace(name, CATCH_MOVE(factory)); - CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" ); + "'::' is not allowed in reporter name: '" + name + + '\'' ); + auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) ); + CATCH_ENFORCE( ret.second, + "reporter using '" + name + + "' as name was already registered" ); } void ReporterRegistry::registerListener( Detail::unique_ptr factory ) { - m_listeners.push_back( CATCH_MOVE(factory) ); + m_impl->listeners.push_back( CATCH_MOVE( factory ) ); } - IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { - return m_factories; - } - IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { - return m_listeners; + std::map const& + ReporterRegistry::getFactories() const { + return m_impl->factories; } -} + std::vector> const& + ReporterRegistry::getListeners() const { + return m_impl->listeners; + } +} // namespace Catch @@ -4754,9 +4854,9 @@ namespace Catch { }; kvPair splitKVPair(StringRef kvString) { - auto splitPos = static_cast( std::distance( - kvString.begin(), - std::find( kvString.begin(), kvString.end(), '=' ) ) ); + auto splitPos = static_cast( + std::find( kvString.begin(), kvString.end(), '=' ) - + kvString.begin() ); return { kvString.substr( 0, splitPos ), kvString.substr( splitPos + 1, kvString.size() ) }; @@ -4988,146 +5088,152 @@ namespace Catch { namespace Catch { namespace Generators { - struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { - GeneratorBasePtr m_generator; + namespace { + struct GeneratorTracker final : TestCaseTracking::TrackerBase, + IGeneratorTracker { + GeneratorBasePtr m_generator; - GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ) - {} - ~GeneratorTracker() override; + GeneratorTracker( + TestCaseTracking::NameAndLocation&& nameAndLocation, + TrackerContext& ctx, + ITracker* parent ): + TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} + ~GeneratorTracker() override = default; - static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef const& nameAndLocation ) { - GeneratorTracker* tracker; + static GeneratorTracker* + acquire( TrackerContext& ctx, + TestCaseTracking::NameAndLocationRef const& + nameAndLocation ) { + GeneratorTracker* tracker; - ITracker& currentTracker = ctx.currentTracker(); - // Under specific circumstances, the generator we want - // to acquire is also the current tracker. If this is - // the case, we have to avoid looking through current - // tracker's children, and instead return the current - // tracker. - // A case where this check is important is e.g. - // for (int i = 0; i < 5; ++i) { - // int n = GENERATE(1, 2); - // } - // - // without it, the code above creates 5 nested generators. - if ( currentTracker.nameAndLocation() == nameAndLocation ) { - auto thisTracker = - currentTracker.parent()->findChild( nameAndLocation ); - assert( thisTracker ); - assert( thisTracker->isGeneratorTracker() ); - tracker = static_cast( thisTracker ); - } else if ( ITracker* childTracker = - currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isGeneratorTracker() ); - tracker = static_cast( childTracker ); - } else { - return nullptr; + ITracker& currentTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if ( currentTracker.nameAndLocation() == nameAndLocation ) { + auto thisTracker = currentTracker.parent()->findChild( + nameAndLocation ); + assert( thisTracker ); + assert( thisTracker->isGeneratorTracker() ); + tracker = static_cast( thisTracker ); + } else if ( ITracker* childTracker = + currentTracker.findChild( + nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = + static_cast( childTracker ); + } else { + return nullptr; + } + + if ( !tracker->isComplete() ) { tracker->open(); } + + return tracker; } - if( !tracker->isComplete() ) { - tracker->open(); + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; } - - return tracker; - } - - // TrackerBase interface - bool isGeneratorTracker() const override { return true; } - auto hasGenerator() const -> bool override { - return !!m_generator; - } - void close() override { - TrackerBase::close(); - // If a generator has a child (it is followed by a section) - // and none of its children have started, then we must wait - // until later to start consuming its values. - // This catches cases where `GENERATE` is placed between two - // `SECTION`s. - // **The check for m_children.empty cannot be removed**. - // doing so would break `GENERATE` _not_ followed by `SECTION`s. - const bool should_wait_for_child = [&]() { - // No children -> nobody to wait for - if ( m_children.empty() ) { - return false; - } - // If at least one child started executing, don't wait - if ( std::find_if( - m_children.begin(), - m_children.end(), - []( TestCaseTracking::ITrackerPtr const& tracker ) { - return tracker->hasStarted(); - } ) != m_children.end() ) { - return false; - } - - // No children have started. We need to check if they _can_ - // start, and thus we should wait for them, or they cannot - // start (due to filters), and we shouldn't wait for them - ITracker* parent = m_parent; - // This is safe: there is always at least one section - // tracker in a test case tracking tree - while ( !parent->isSectionTracker() ) { - parent = parent->parent(); - } - assert( parent && - "Missing root (test case) level section" ); - - auto const& parentSection = - static_cast( *parent ); - auto const& filters = parentSection.getFilters(); - // No filters -> no restrictions on running sections - if ( filters.empty() ) { - return true; - } - - for ( auto const& child : m_children ) { - if ( child->isSectionTracker() && - std::find( - filters.begin(), - filters.end(), - static_cast( *child ) - .trimmedName() ) != filters.end() ) { - return true; + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by + // `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { return false; } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr const& + tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; } + + // No children have started. We need to check if they + // _can_ start, and thus we should wait for them, or + // they cannot start (due to filters), and we shouldn't + // wait for them + ITracker* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = parent->parent(); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { return true; } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( filters.begin(), + filters.end(), + static_cast( + *child ) + .trimmedName() ) != + filters.end() ) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + assert( m_generator && "Tracker without generator" ); + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->countedNext() ) ) { + m_children.clear(); + m_runState = Executing; } - return false; - }(); - - // This check is a bit tricky, because m_generator->next() - // has a side-effect, where it consumes generator's current - // value, but we do not want to invoke the side-effect if - // this generator is still waiting for any child to start. - assert( m_generator && "Tracker without generator" ); - if ( should_wait_for_child || - ( m_runState == CompletedSuccessfully && - m_generator->countedNext() ) ) { - m_children.clear(); - m_runState = Executing; } - } - // IGeneratorTracker interface - auto getGenerator() const -> GeneratorBasePtr const& override { - return m_generator; - } - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = CATCH_MOVE( generator ); - } - }; - GeneratorTracker::~GeneratorTracker() = default; + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = CATCH_MOVE( generator ); + } + }; + } // namespace } RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) : m_runInfo(_config->name()), - m_context(getCurrentMutableContext()), m_config(_config), m_reporter(CATCH_MOVE(reporter)), m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) { - m_context.setResultCapture(this); + getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); } @@ -5222,7 +5328,7 @@ namespace Catch { } - void RunContext::assertionEnded(AssertionResult const & result) { + void RunContext::assertionEnded(AssertionResult&& result) { if (result.getResultType() == ResultWas::Ok) { m_totals.assertions.passed++; m_lastAssertionPassed = true; @@ -5244,19 +5350,26 @@ namespace Catch { m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); - if (result.getResultType() != ResultWas::Warning) + if ( result.getResultType() != ResultWas::Warning ) { m_messageScopes.clear(); + } // Reset working state resetAssertionInfo(); - m_lastResult = result; + m_lastResult = CATCH_MOVE( result ); } void RunContext::resetAssertionInfo() { m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; } - bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) { + void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { + m_reporter->assertionStarting( info ); + } + + bool RunContext::sectionStarted( StringRef sectionName, + SourceLineInfo const& sectionLineInfo, + Counts& assertions ) { ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocationRef( @@ -5394,7 +5507,7 @@ namespace Catch { tempResult.message = static_cast(message); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); - assertionEnded(result); + assertionEnded(CATCH_MOVE(result) ); handleUnfinishedSections(); @@ -5516,8 +5629,6 @@ namespace Catch { ITransientExpression const& expr, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - bool negated = isFalseTest( info.resultDisposition ); bool result = expr.getResult() != negated; @@ -5546,7 +5657,7 @@ namespace Catch { AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); } void RunContext::handleMessage( @@ -5555,16 +5666,16 @@ namespace Catch { StringRef message, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - m_lastAssertionInfo = info; AssertionResultData data( resultType, LazyExpression( false ) ); data.message = static_cast(message); AssertionResult assertionResult{ m_lastAssertionInfo, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if ( !assertionResult.isOk() ) { + + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } else if ( resultType == ResultWas::ExplicitSkip ) { // TODO: Need to handle this explicitly, as ExplicitSkip is @@ -5581,15 +5692,15 @@ namespace Catch { void RunContext::handleUnexpectedInflightException( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) { m_lastAssertionInfo = info; AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = message; + data.message = CATCH_MOVE(message); AssertionResult assertionResult{ info, CATCH_MOVE(data) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); populateReaction( reaction ); } @@ -5607,7 +5718,7 @@ namespace Catch { AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); } void RunContext::handleNonExpr( AssertionInfo const &info, @@ -5618,10 +5729,10 @@ namespace Catch { AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if( !assertionResult.isOk() ) - populateReaction( reaction ); + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } } @@ -5790,7 +5901,6 @@ namespace Catch { -#include #include #include #include @@ -5814,9 +5924,9 @@ namespace Catch { return s.find( infix ) != std::string::npos; } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), []( char c ) { - return toLower( c ); - } ); + for ( char& c : s ) { + c = toLower( c ); + } } std::string toLower( std::string const& s ) { std::string lc = s; @@ -6030,6 +6140,38 @@ namespace Catch { namespace Catch { + namespace { + static void enforceNoDuplicateTestCases( + std::vector const& tests ) { + auto testInfoCmp = []( TestCaseInfo const* lhs, + TestCaseInfo const* rhs ) { + return *lhs < *rhs; + }; + std::set seenTests( + testInfoCmp ); + for ( auto const& test : tests ) { + const auto infoPtr = &test.getTestCaseInfo(); + const auto prev = seenTests.insert( infoPtr ); + CATCH_ENFORCE( prev.second, + "error: test case \"" + << infoPtr->name << "\", with tags \"" + << infoPtr->tagsAsString() + << "\" already defined.\n" + << "\tFirst seen at " + << ( *prev.first )->lineInfo << "\n" + << "\tRedefined at " << infoPtr->lineInfo ); + } + } + + static bool matchTest( TestCaseHandle const& testCase, + TestSpec const& testSpec, + IConfig const& config ) { + return testSpec.matches( testCase.getTestCaseInfo() ) && + isThrowSafe( testCase, config ); + } + + } // end unnamed namespace + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { switch (config.runOrder()) { case TestRunOrder::Declared: @@ -6086,29 +6228,6 @@ namespace Catch { return !testCase.getTestCaseInfo().throws() || config.allowThrows(); } - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config ); - } - - void - enforceNoDuplicateTestCases( std::vector const& tests ) { - auto testInfoCmp = []( TestCaseInfo const* lhs, - TestCaseInfo const* rhs ) { - return *lhs < *rhs; - }; - std::set seenTests(testInfoCmp); - for ( auto const& test : tests ) { - const auto infoPtr = &test.getTestCaseInfo(); - const auto prev = seenTests.insert( infoPtr ); - CATCH_ENFORCE( - prev.second, - "error: test case \"" << infoPtr->name << "\", with tags \"" - << infoPtr->tagsAsString() << "\" already defined.\n" - << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n" - << "\tRedefined at " << infoPtr->lineInfo ); - } - } - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); @@ -6149,13 +6268,6 @@ namespace Catch { return m_sortedFunctions; } - - - /////////////////////////////////////////////////////////////////////////// - void TestInvokerAsFunction::invoke() const { - m_testAsFunction(); - } - } // end namespace Catch @@ -6401,6 +6513,14 @@ namespace Catch { #endif } + void throw_test_skip_exception() { +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + throw Catch::TestSkipException(); +#else + CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); +#endif + } + } // namespace Catch @@ -6409,9 +6529,10 @@ namespace Catch { #include namespace Catch { + ITestInvoker::~ITestInvoker() = default; namespace { - StringRef extractClassName( StringRef classOrMethodName ) { + static StringRef extractClassName( StringRef classOrMethodName ) { if ( !startsWith( classOrMethodName, '&' ) ) { return classOrMethodName; } @@ -6438,6 +6559,18 @@ namespace Catch { static_cast( startIdx ), static_cast( classNameSize ) ); } + + class TestInvokerAsFunction final : public ITestInvoker { + using TestType = void ( * )(); + TestType m_testAsFunction; + + public: + TestInvokerAsFunction( TestType testAsFunction ) noexcept: + m_testAsFunction( testAsFunction ) {} + + void invoke() const override { m_testAsFunction(); } + }; + } // namespace Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ) { @@ -8046,7 +8179,7 @@ private: return; const auto itEnd = messages.cend(); - const auto N = static_cast(std::distance(itMessage, itEnd)); + const auto N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -9299,6 +9432,8 @@ namespace Catch { gmtime_s(&timeInfo, &rawtime); #elif defined (CATCH_PLATFORM_PLAYSTATION) gmtime_s(&rawtime, &timeInfo); +#elif defined (__IAR_SYSTEMS_ICC__) + timeInfo = *std::gmtime(&rawtime); #else gmtime_r(&rawtime, &timeInfo); #endif @@ -9559,7 +9694,7 @@ namespace Catch { } } - if( !result.getMessage().empty() ) + if( result.hasMessage() ) rss << result.getMessage() << '\n'; for( auto const& msg : stats.infoMessages ) if( msg.type == ResultWas::Info ) @@ -9678,7 +9813,6 @@ namespace Catch { } } - // The return value indicates if the messages buffer should be cleared: void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { const bool reportByDefault = assertionStats.assertionResult.getResultType() != ResultWas::Ok || @@ -9781,6 +9915,11 @@ namespace Catch { } } + void registerListenerImpl( Detail::unique_ptr listenerFactory ) { + getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) ); + } + + } // namespace Detail } // namespace Catch @@ -9920,7 +10059,7 @@ namespace Catch { } } - if (!result.getMessage().empty()) + if (result.hasMessage()) textRss << result.getMessage() << '\n'; for (auto const& msg : stats.infoMessages) @@ -9954,7 +10093,6 @@ namespace Catch { #include -#include #include namespace Catch { @@ -10105,7 +10243,7 @@ namespace Catch { // using messages.end() directly (or auto) yields compilation error: std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + const std::size_t N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -10377,7 +10515,7 @@ namespace Catch { m_xml.startElement("Catch2TestRun") .writeAttribute("name"_sr, m_config->name()) .writeAttribute("rng-seed"_sr, m_config->rngSeed()) - .writeAttribute("xml-format-version"_sr, 2) + .writeAttribute("xml-format-version"_sr, 3) .writeAttribute("catch2-version"_sr, libraryVersion()); if ( m_config->testSpec().hasFilters() ) { m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); @@ -10419,11 +10557,13 @@ namespace Catch { // Print any info messages in tags. for( auto const& msg : assertionStats.infoMessages ) { if( msg.type == ResultWas::Info && includeResults ) { - m_xml.scopedElement( "Info" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Info" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } else if ( msg.type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Warning" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } } } diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 321cec5d..694fe1e4 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.2 -// Generated: 2023-02-26 10:28:46.785908 +// Catch v3.4.0 +// Generated: 2023-07-13 13:23:07.577770 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -59,238 +59,6 @@ -#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED -#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED - - - -#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED -#define CATCH_NONCOPYABLE_HPP_INCLUDED - -namespace Catch { - namespace Detail { - - //! Deriving classes become noncopyable and nonmovable - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable&& ) = delete; - NonCopyable& operator=( NonCopyable const& ) = delete; - NonCopyable& operator=( NonCopyable&& ) = delete; - - protected: - NonCopyable() noexcept = default; - }; - - } // namespace Detail -} // namespace Catch - -#endif // CATCH_NONCOPYABLE_HPP_INCLUDED - - -#ifndef CATCH_STRINGREF_HPP_INCLUDED -#define CATCH_STRINGREF_HPP_INCLUDED - -#include -#include -#include -#include - -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - 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); - } - - constexpr auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - bool operator<(StringRef rhs) const noexcept; - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, start + size()). - // If start > size(), then the substring is empty. - constexpr StringRef substr(size_type start, size_type length) const noexcept { - if (start < m_size) { - const auto shortened_size = m_size - start; - return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); - } else { - return StringRef(); - } - } - - // Returns the current start pointer. May not be null-terminated. - constexpr char const* data() const noexcept { - return m_start; - } - - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - - - friend std::string& operator += (std::string& lhs, StringRef sr); - friend std::ostream& operator << (std::ostream& os, StringRef sr); - friend std::string operator+(StringRef lhs, StringRef rhs); - - /** - * Provides a three-way comparison with rhs - * - * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive - * number if lhs > rhs - */ - int compare( StringRef rhs ) const; - }; - - - constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -#endif // CATCH_STRINGREF_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - enum class Verbosity { - Quiet = 0, - Normal, - High - }; - - struct WarnAbout { enum What { - Nothing = 0x00, - //! A test case or leaf section did not run any assertions - NoAssertions = 0x01, - //! A command line test spec matched no test cases - UnmatchedTestSpec = 0x02, - }; }; - - enum class ShowDurations { - DefaultForReporter, - Always, - Never - }; - enum class TestRunOrder { - Declared, - LexicographicallySorted, - Randomized - }; - enum class ColourMode : std::uint8_t { - //! Let Catch2 pick implementation based on platform detection - PlatformDefault, - //! Use ANSI colour code escapes - ANSI, - //! Use Win32 console colour API - Win32, - //! Don't use any colour - None - }; - struct WaitForKeypress { enum When { - Never, - BeforeStart = 1, - BeforeExit = 2, - BeforeStartAndExit = BeforeStart | BeforeExit - }; }; - - class TestSpec; - class IStream; - - class IConfig : public Detail::NonCopyable { - public: - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual StringRef name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual bool warnAboutUnmatchedTestSpecs() const = 0; - virtual bool zeroTestsCountAsSuccess() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations showDurations() const = 0; - virtual double minDuration() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual bool hasTestFilters() const = 0; - virtual std::vector const& getTestsOrTags() const = 0; - virtual TestRunOrder runOrder() const = 0; - virtual uint32_t rngSeed() const = 0; - virtual unsigned int shardCount() const = 0; - virtual unsigned int shardIndex() const = 0; - virtual ColourMode defaultColourMode() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; - virtual Verbosity verbosity() const = 0; - - virtual bool skipBenchmarks() const = 0; - virtual bool benchmarkNoAnalysis() const = 0; - virtual unsigned int benchmarkSamples() const = 0; - virtual double benchmarkConfidenceInterval() const = 0; - virtual unsigned int benchmarkResamples() const = 0; - virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; - }; -} - -#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED - - #ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED #define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -366,12 +134,18 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ + _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) + # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) + # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif @@ -444,6 +218,9 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) + #endif // __clang__ @@ -681,6 +458,9 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS #endif @@ -690,6 +470,16 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif +#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS +#endif + // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... @@ -703,13 +493,6 @@ namespace Catch { # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS -#endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) @@ -755,38 +538,31 @@ namespace Catch { class IResultCapture; class IConfig; - class IContext { - public: - virtual ~IContext(); // = default + class Context { + IConfig const* m_config = nullptr; + IResultCapture* m_resultCapture = nullptr; - virtual IResultCapture* getResultCapture() = 0; - virtual IConfig const* getConfig() const = 0; - }; - - class IMutableContext : public IContext { - public: - ~IMutableContext() override; // = default - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setConfig( IConfig const* config ) = 0; - - private: - CATCH_EXPORT static IMutableContext* currentContext; - friend IMutableContext& getCurrentMutableContext(); - friend void cleanUpContext(); + CATCH_EXPORT static Context* currentContext; + friend Context& getCurrentMutableContext(); + friend Context const& getCurrentContext(); static void createContext(); + friend void cleanUpContext(); + + public: + IResultCapture* getResultCapture() const { return m_resultCapture; } + IConfig const* getConfig() const { return m_config; } + void setResultCapture( IResultCapture* resultCapture ); + void setConfig( IConfig const* config ); }; - inline IMutableContext& getCurrentMutableContext() - { - if( !IMutableContext::currentContext ) - IMutableContext::createContext(); - // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) - return *IMutableContext::currentContext; - } + Context& getCurrentMutableContext(); - inline IContext& getCurrentContext() - { - return getCurrentMutableContext(); + inline Context const& getCurrentContext() { + // We duplicate the logic from `getCurrentMutableContext` here, + // to avoid paying the call overhead in debug mode. + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } void cleanUpContext(); @@ -798,16 +574,6 @@ namespace Catch { #endif // CATCH_CONTEXT_HPP_INCLUDED -#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED -#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED - - - -#ifndef CATCH_SECTION_INFO_HPP_INCLUDED -#define CATCH_SECTION_INFO_HPP_INCLUDED - - - #ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED @@ -822,110 +588,199 @@ namespace Catch { #endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED -#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED -#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED - -#include -#include +#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED namespace Catch { - struct SourceLineInfo { + //! Used to signal that an assertion macro failed + struct TestFailureException{}; + //! Used to signal that the remainder of a test should be skipped + struct TestSkipException {}; - SourceLineInfo() = delete; - constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: - file( _file ), - line( _line ) - {} + /** + * Outlines throwing of `TestFailureException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_failure_exception(); - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; + /** + * Outlines throwing of `TestSkipException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_skip_exception(); - char const* file; - std::size_t line; +} // namespace Catch - friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); - }; -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED -#ifndef CATCH_TOTALS_HPP_INCLUDED -#define CATCH_TOTALS_HPP_INCLUDED +#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED +#define CATCH_UNIQUE_NAME_HPP_INCLUDED -#include -namespace Catch { - struct Counts { - Counts operator - ( Counts const& other ) const; - Counts& operator += ( Counts const& other ); - std::uint64_t total() const; - bool allPassed() const; - bool allOk() const; +/** \file + * Wrapper for the CONFIG configuration option + * + * When generating internal unique names, there are two options. Either + * we mix in the current line number, or mix in an incrementing number. + * We prefer the latter, using `__COUNTER__`, but users might want to + * use the former. + */ - std::uint64_t passed = 0; - std::uint64_t failed = 0; - std::uint64_t failedButOk = 0; - std::uint64_t skipped = 0; - }; +#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define CATCH_CONFIG_COUNTER_HPP_INCLUDED - struct Totals { - Totals operator - ( Totals const& other ) const; - Totals& operator += ( Totals const& other ); +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif - Totals delta( Totals const& prevTotals ) const; +#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ + !defined( CATCH_CONFIG_NO_COUNTER ) && \ + !defined( CATCH_CONFIG_COUNTER ) +# define CATCH_CONFIG_COUNTER +#endif - Counts assertions; - Counts testCases; - }; -} -#endif // CATCH_TOTALS_HPP_INCLUDED +#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED + + +#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED #include +#include + + + +#ifndef CATCH_STRINGREF_HPP_INCLUDED +#define CATCH_STRINGREF_HPP_INCLUDED + +#include +#include +#include +#include + +#include namespace Catch { - struct SectionInfo { - // The last argument is ignored, so that people can write - // SECTION("ShortName", "Proper description that is long") and - // still use the `-c` flag comfortably. - SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, - const char* const = nullptr ): - name(CATCH_MOVE(_name)), - lineInfo(_lineInfo) - {} + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; - std::string name; - SourceLineInfo lineInfo; + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + 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); + } + + constexpr auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + bool operator<(StringRef rhs) const noexcept; + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, start + size()). + // If start > size(), then the substring is empty. + constexpr StringRef substr(size_type start, size_type length) const noexcept { + if (start < m_size) { + const auto shortened_size = m_size - start; + return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); + } else { + return StringRef(); + } + } + + // Returns the current start pointer. May not be null-terminated. + constexpr char const* data() const noexcept { + return m_start; + } + + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + + + friend std::string& operator += (std::string& lhs, StringRef sr); + friend std::ostream& operator << (std::ostream& os, StringRef sr); + friend std::string operator+(StringRef lhs, StringRef rhs); + + /** + * Provides a three-way comparison with rhs + * + * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive + * number if lhs > rhs + */ + int compare( StringRef rhs ) const; }; - struct SectionEndInfo { - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; -} // end namespace Catch + constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch -#endif // CATCH_SECTION_INFO_HPP_INCLUDED - - -#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED -#define CATCH_ASSERTION_RESULT_HPP_INCLUDED - - - -#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED -#define CATCH_ASSERTION_INFO_HPP_INCLUDED +constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} +#endif // CATCH_STRINGREF_HPP_INCLUDED #ifndef CATCH_RESULT_TYPE_HPP_INCLUDED @@ -979,114 +834,6 @@ namespace Catch { #endif // CATCH_RESULT_TYPE_HPP_INCLUDED -namespace Catch { - - struct AssertionInfo { - // AssertionInfo() = delete; - - StringRef macroName; - SourceLineInfo lineInfo; - StringRef capturedExpression; - ResultDisposition::Flags resultDisposition; - }; - -} // end namespace Catch - -#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED - - -#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED -#define CATCH_LAZY_EXPR_HPP_INCLUDED - -#include - -namespace Catch { - - class ITransientExpression; - - class LazyExpression { - friend class AssertionHandler; - friend struct AssertionStats; - friend class RunContext; - - ITransientExpression const* m_transientExpression = nullptr; - bool m_isNegated; - public: - LazyExpression( bool isNegated ): - m_isNegated(isNegated) - {} - LazyExpression(LazyExpression const& other) = default; - LazyExpression& operator = ( LazyExpression const& ) = delete; - - explicit operator bool() const { - return m_transientExpression != nullptr; - } - - friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; - }; - -} // namespace Catch - -#endif // CATCH_LAZY_EXPR_HPP_INCLUDED - -#include - -namespace Catch { - - struct AssertionResultData - { - AssertionResultData() = delete; - - AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - - std::string message; - mutable std::string reconstructedExpression; - LazyExpression lazyExpression; - ResultWas::OfType resultType; - - std::string reconstructExpression() const; - }; - - class AssertionResult { - public: - AssertionResult() = delete; - AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - StringRef getMessage() const; - SourceLineInfo getSourceInfo() const; - StringRef getTestMacroName() const; - - //protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; - -} // end namespace Catch - -#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED - - -#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED -#define CATCH_MESSAGE_INFO_HPP_INCLUDED - - - -#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED -#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED - -#include -#include - - #ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED #define CATCH_UNIQUE_PTR_HPP_INCLUDED @@ -1199,6 +946,24 @@ namespace Detail { #endif // CATCH_UNIQUE_PTR_HPP_INCLUDED + +#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + +#include + +namespace Catch { + + // We cannot forward declare the type with default template argument + // multiple times, so it is split out into a separate header so that + // we can prevent multiple declarations in dependees + template > + struct BenchmarkStats; + +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + namespace Catch { class AssertionResult; @@ -1215,8 +980,6 @@ namespace Catch { class IGeneratorTracker; struct BenchmarkInfo; - template > - struct BenchmarkStats; namespace Generators { class GeneratorUntypedBase; @@ -1228,6 +991,7 @@ namespace Catch { public: virtual ~IResultCapture(); + virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; virtual bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) = 0; @@ -1268,7 +1032,7 @@ namespace Catch { AssertionReaction& reaction ) = 0; virtual void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleIncomplete ( AssertionInfo const& info ) = 0; @@ -1293,34 +1057,186 @@ namespace Catch { #endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED + +#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED +#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED + + + +#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED +#define CATCH_NONCOPYABLE_HPP_INCLUDED + +namespace Catch { + namespace Detail { + + //! Deriving classes become noncopyable and nonmovable + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable&& ) = delete; + NonCopyable& operator=( NonCopyable const& ) = delete; + NonCopyable& operator=( NonCopyable&& ) = delete; + + protected: + NonCopyable() noexcept = default; + }; + + } // namespace Detail +} // namespace Catch + +#endif // CATCH_NONCOPYABLE_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + //! A test case or leaf section did not run any assertions + NoAssertions = 0x01, + //! A command line test spec matched no test cases + UnmatchedTestSpec = 0x02, + }; }; + + enum class ShowDurations { + DefaultForReporter, + Always, + Never + }; + enum class TestRunOrder { + Declared, + LexicographicallySorted, + Randomized + }; + enum class ColourMode : std::uint8_t { + //! Let Catch2 pick implementation based on platform detection + PlatformDefault, + //! Use ANSI colour code escapes + ANSI, + //! Use Win32 console colour API + Win32, + //! Don't use any colour + None + }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + class IStream; + + class IConfig : public Detail::NonCopyable { + public: + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual StringRef name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutUnmatchedTestSpecs() const = 0; + virtual bool zeroTestsCountAsSuccess() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations showDurations() const = 0; + virtual double minDuration() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector const& getTestsOrTags() const = 0; + virtual TestRunOrder runOrder() const = 0; + virtual uint32_t rngSeed() const = 0; + virtual unsigned int shardCount() const = 0; + virtual unsigned int shardIndex() const = 0; + virtual ColourMode defaultColourMode() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + + virtual bool skipBenchmarks() const = 0; + virtual bool benchmarkNoAnalysis() const = 0; + virtual unsigned int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; + }; +} + +#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED + + +#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED + + #include namespace Catch { - struct MessageInfo { - MessageInfo( StringRef _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ); + class TestCaseHandle; + struct TestCaseInfo; + class ITestCaseRegistry; + class IExceptionTranslatorRegistry; + class IExceptionTranslator; + class ReporterRegistry; + class IReporterFactory; + class ITagAliasRegistry; + class ITestInvoker; + class IMutableEnumValuesRegistry; + struct SourceLineInfo; - StringRef macroName; - std::string message; - SourceLineInfo lineInfo; - ResultWas::OfType type; - unsigned int sequence; + class StartupExceptionRegistry; + class EventListenerFactory; - bool operator == (MessageInfo const& other) const { - return sequence == other.sequence; - } - bool operator < (MessageInfo const& other) const { - return sequence < other.sequence; - } - private: - static unsigned int globalCount; + using IReporterFactoryPtr = Detail::unique_ptr; + + class IRegistryHub { + public: + virtual ~IRegistryHub(); // = default + + virtual ReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; -} // end namespace Catch + class IMutableRegistryHub { + public: + virtual ~IMutableRegistryHub(); // = default + virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; + virtual void registerListener( Detail::unique_ptr factory ) = 0; + virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; + virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; + }; + + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED + + +#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_HPP_INCLUDED -#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED // Adapted from donated nonius code. @@ -1370,103 +1286,14 @@ namespace Catch { } // namespace Catch #endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED +// The fwd decl & default specialization needs to be seen by VS2017 before +// BenchmarkStats itself, or VS2017 will report compilation error. - -#include #include #include -#include namespace Catch { - struct ReporterDescription; - struct ListenerDescription; - struct TagInfo; - struct TestCaseInfo; - class TestCaseHandle; - class IConfig; - class IStream; - enum class ColourMode : std::uint8_t; - - struct ReporterConfig { - ReporterConfig( IConfig const* _fullConfig, - Detail::unique_ptr _stream, - ColourMode colourMode, - std::map customOptions ); - - ReporterConfig( ReporterConfig&& ) = default; - ReporterConfig& operator=( ReporterConfig&& ) = default; - ~ReporterConfig(); // = default - - Detail::unique_ptr takeStream() &&; - IConfig const* fullConfig() const; - ColourMode colourMode() const; - std::map const& customOptions() const; - - private: - Detail::unique_ptr m_stream; - IConfig const* m_fullConfig; - ColourMode m_colourMode; - std::map m_customOptions; - }; - - struct TestRunInfo { - constexpr TestRunInfo(StringRef _name) : name(_name) {} - StringRef name; - }; - - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ); - - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = delete; - AssertionStats& operator = ( AssertionStats && ) = delete; - - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; - }; - - struct SectionStats { - SectionStats( SectionInfo&& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ); - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; - - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string&& _stdOut, - std::string&& _stdErr, - bool _aborting ); - - TestCaseInfo const * testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; - - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ); - - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; - - struct BenchmarkInfo { std::string name; double estimatedDuration; @@ -1505,169 +1332,10 @@ namespace Catch { } }; - //! By setting up its preferences, a reporter can modify Catch2's behaviour - //! in some regards, e.g. it can request Catch2 to capture writes to - //! stdout/stderr during test execution, and pass them to the reporter. - struct ReporterPreferences { - //! Catch2 should redirect writes to stdout and pass them to the - //! reporter - bool shouldRedirectStdOut = false; - //! Catch2 should call `Reporter::assertionEnded` even for passing - //! assertions - bool shouldReportAllAssertions = false; - }; - - /** - * The common base for all reporters and event listeners - * - * Implementing classes must also implement: - * - * //! User-friendly description of the reporter/listener type - * static std::string getDescription() - * - * Generally shouldn't be derived from by users of Catch2 directly, - * instead they should derive from one of the utility bases that - * derive from this class. - */ - class IEventListener { - protected: - //! Derived classes can set up their preferences here - ReporterPreferences m_preferences; - //! The test run's config as filled in from CLI and defaults - IConfig const* m_config; - - public: - IEventListener( IConfig const* config ): m_config( config ) {} - - virtual ~IEventListener(); // = default; - - // Implementing class must also provide the following static methods: - // static std::string getDescription(); - - ReporterPreferences const& getPreferences() const { - return m_preferences; - } - - //! Called when no test cases match provided test spec - virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0; - //! Called for all invalid test specs from the cli - virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0; - - /** - * Called once in a testing run before tests are started - * - * Not called if tests won't be run (e.g. only listing will happen) - */ - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - - //! Called _once_ for each TEST_CASE, no matter how many times it is entered - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) - virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0; - //! Called when a `SECTION` is being entered. Not called for skipped sections - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - - //! Called when user-code is being probed before the actual benchmark runs - virtual void benchmarkPreparing( StringRef benchmarkName ) = 0; - //! Called after probe but before the user-code is being benchmarked - virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0; - //! Called with the benchmark results if benchmark successfully finishes - virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0; - //! Called if running the benchmarks fails for any reason - virtual void benchmarkFailed( StringRef benchmarkName ) = 0; - - //! Called before assertion success/failure is evaluated - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - - //! Called after assertion was fully evaluated - virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; - - //! Called after a `SECTION` has finished running - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) - virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0; - //! Called _once_ for each TEST_CASE, no matter how many times it is entered - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - /** - * Called once after all tests in a testing run are finished - * - * Not called if tests weren't run (e.g. only listings happened) - */ - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - - /** - * Called with test cases that are skipped due to the test run aborting. - * NOT called for test cases that are explicitly skipped using the `SKIP` macro. - * - * Deprecated - will be removed in the next major release. - */ - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - - //! Called if a fatal error (signal/structured exception) occured - virtual void fatalErrorEncountered( StringRef error ) = 0; - - //! Writes out information about provided reporters using reporter-specific format - virtual void listReporters(std::vector const& descriptions) = 0; - //! Writes out the provided listeners descriptions using reporter-specific format - virtual void listListeners(std::vector const& descriptions) = 0; - //! Writes out information about provided tests using reporter-specific format - virtual void listTests(std::vector const& tests) = 0; - //! Writes out information about the provided tags using reporter-specific format - virtual void listTags(std::vector const& tags) = 0; - }; - using IEventListenerPtr = Detail::unique_ptr; } // end namespace Catch -#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED - - -#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED -#define CATCH_UNIQUE_NAME_HPP_INCLUDED - - - - -/** \file - * Wrapper for the CONFIG configuration option - * - * When generating internal unique names, there are two options. Either - * we mix in the current line number, or mix in an incrementing number. - * We prefer the latter, using `__COUNTER__`, but users might want to - * use the former. - */ - -#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED -#define CATCH_CONFIG_COUNTER_HPP_INCLUDED - -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ - !defined( CATCH_CONFIG_NO_COUNTER ) && \ - !defined( CATCH_CONFIG_COUNTER ) -# define CATCH_CONFIG_COUNTER -#endif - - -#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_CHRONOMETER_HPP_INCLUDED -#define CATCH_CHRONOMETER_HPP_INCLUDED - +#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED // Adapted from donated nonius code. @@ -1704,12 +1372,63 @@ namespace Catch { #endif // CATCH_CLOCK_HPP_INCLUDED +// Adapted from donated nonius code. + +#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED +#define CATCH_ENVIRONMENT_HPP_INCLUDED + + +namespace Catch { + namespace Benchmark { + template + struct EnvironmentEstimate { + Duration mean; + OutlierClassification outliers; + + template + operator EnvironmentEstimate() const { + return { mean, outliers }; + } + }; + template + struct Environment { + using clock_type = Clock; + EnvironmentEstimate> clock_resolution; + EnvironmentEstimate> clock_cost; + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ENVIRONMENT_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED +#define CATCH_EXECUTION_PLAN_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_CHRONOMETER_HPP_INCLUDED +#define CATCH_CHRONOMETER_HPP_INCLUDED + + + // Adapted from donated nonius code. #ifndef CATCH_OPTIMIZER_HPP_INCLUDED #define CATCH_OPTIMIZER_HPP_INCLUDED -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) # include // atomic_thread_fence #endif @@ -1730,16 +1449,23 @@ namespace Catch { namespace Detail { inline void optimizer_barrier() { keep_memory(); } } // namespace Detail -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) +#if defined(_MSVC_VER) #pragma optimize("", off) +#elif defined(__IAR_SYSTEMS_ICC__) +// For IAR the pragma only affects the following function +#pragma optimize=disable +#endif template inline void keep_memory(T* p) { // thanks @milleniumbug *reinterpret_cast(p) = *reinterpret_cast(p); } // TODO equivalent keep_memory() +#if defined(_MSVC_VER) #pragma optimize("", on) +#endif namespace Detail { inline void optimizer_barrier() { @@ -1769,36 +1495,6 @@ namespace Catch { #endif // CATCH_OPTIMIZER_HPP_INCLUDED -// Adapted from donated nonius code. - -#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED -#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED - - - -#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED -#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED - -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(); - - //! Used to signal that the remainder of a test should be skipped - struct TestSkipException{}; - -} // namespace Catch - -#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED - - #ifndef CATCH_META_HPP_INCLUDED #define CATCH_META_HPP_INCLUDED @@ -1840,112 +1536,6 @@ namespace mpl_{ #endif // CATCH_META_HPP_INCLUDED - -#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED -#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - - -#include - -namespace Catch { - - class TestCaseHandle; - struct TestCaseInfo; - class ITestCaseRegistry; - class IExceptionTranslatorRegistry; - class IExceptionTranslator; - class IReporterRegistry; - class IReporterFactory; - class ITagAliasRegistry; - class ITestInvoker; - class IMutableEnumValuesRegistry; - struct SourceLineInfo; - - class StartupExceptionRegistry; - class EventListenerFactory; - - using IReporterFactoryPtr = Detail::unique_ptr; - - class IRegistryHub { - public: - virtual ~IRegistryHub(); // = default - - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; - virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; - - - virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; - }; - - class IMutableRegistryHub { - public: - virtual ~IMutableRegistryHub(); // = default - virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; - virtual void registerListener( Detail::unique_ptr factory ) = 0; - virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; - virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; - virtual void registerStartupException() noexcept = 0; - virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; - }; - - IRegistryHub const& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} - -#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - -#include - -namespace Catch { - namespace Benchmark { - namespace Detail { - template - struct CompleteType { using type = T; }; - template <> - struct CompleteType { struct type {}; }; - - template - using CompleteType_t = typename CompleteType::type; - - template - struct CompleteInvoker { - template - static Result invoke(Fun&& fun, Args&&... args) { - return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); - } - }; - template <> - struct CompleteInvoker { - template - static CompleteType_t invoke(Fun&& fun, Args&&... args) { - CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); - return {}; - } - }; - - // invoke and not return void :( - template - CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { - return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); - } - - } // namespace Detail - - template - Detail::CompleteType_t> user_code(Fun&& fun) { - return Detail::complete_invoke(CATCH_FORWARD(fun)); - } - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED - namespace Catch { namespace Benchmark { namespace Detail { @@ -2004,50 +1594,6 @@ namespace Catch { #endif // CATCH_CHRONOMETER_HPP_INCLUDED - -// Adapted from donated nonius code. - -#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED -#define CATCH_ENVIRONMENT_HPP_INCLUDED - - -namespace Catch { - namespace Benchmark { - template - struct EnvironmentEstimate { - Duration mean; - OutlierClassification outliers; - - template - operator EnvironmentEstimate() const { - return { mean, outliers }; - } - }; - template - struct Environment { - using clock_type = Clock; - EnvironmentEstimate> clock_resolution; - EnvironmentEstimate> clock_cost; - }; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_ENVIRONMENT_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED -#define CATCH_EXECUTION_PLAN_HPP_INCLUDED - - - -// Adapted from donated nonius code. - -#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED -#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED - - #include namespace Catch { @@ -2184,6 +1730,57 @@ namespace Catch { +// Adapted from donated nonius code. + +#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED +#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct CompleteType { using type = T; }; + template <> + struct CompleteType { struct type {}; }; + + template + using CompleteType_t = typename CompleteType::type; + + template + struct CompleteInvoker { + template + static Result invoke(Fun&& fun, Args&&... args) { + return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + } + }; + template <> + struct CompleteInvoker { + template + static CompleteType_t invoke(Fun&& fun, Args&&... args) { + CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + return {}; + } + }; + + // invoke and not return void :( + template + CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); + } + + } // namespace Detail + + template + Detail::CompleteType_t> user_code(Fun&& fun) { + return Detail::complete_invoke(CATCH_FORWARD(fun)); + } + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + // Adapted from donated nonius code. #ifndef CATCH_TIMING_HPP_INCLUDED @@ -2270,8 +1867,7 @@ namespace Catch { #endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED -#include -#include +#include namespace Catch { namespace Benchmark { @@ -2294,14 +1890,17 @@ namespace Catch { Detail::run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_iterations, Detail::repeat(now{})); std::vector> times; - times.reserve(cfg.benchmarkSamples()); - std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + const auto num_samples = cfg.benchmarkSamples(); + times.reserve( num_samples ); + for ( size_t i = 0; i < num_samples; ++i ) { Detail::ChronometerModel model; - this->benchmark(Chronometer(model, iterations_per_sample)); + this->benchmark( Chronometer( model, iterations_per_sample ) ); auto sample_time = model.elapsed() - env.clock_cost.mean; - if (sample_time < FloatDuration::zero()) sample_time = FloatDuration::zero(); - return sample_time / iterations_per_sample; - }); + if ( sample_time < FloatDuration::zero() ) { + sample_time = FloatDuration::zero(); + } + times.push_back(sample_time / iterations_per_sample); + } return times; } }; @@ -2326,8 +1925,6 @@ namespace Catch { #include #include -#include -#include #include namespace Catch { @@ -2341,39 +1938,17 @@ namespace Catch { double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last); - template - OutlierClassification classify_outliers(Iterator first, Iterator last) { - std::vector copy(first, last); + OutlierClassification + classify_outliers( std::vector::const_iterator first, + std::vector::const_iterator last ); - auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); - auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); - auto iqr = q3 - q1; - auto los = q1 - (iqr * 3.); - auto lom = q1 - (iqr * 1.5); - auto him = q3 + (iqr * 1.5); - auto his = q3 + (iqr * 3.); + double mean( std::vector::const_iterator first, + std::vector::const_iterator last ); - OutlierClassification o; - for (; first != last; ++first) { - auto&& t = *first; - if (t < los) ++o.low_severe; - else if (t < lom) ++o.low_mild; - else if (t > his) ++o.high_severe; - else if (t > him) ++o.high_mild; - ++o.samples_seen; - } - return o; - } - - template - double mean(Iterator first, Iterator last) { - auto count = last - first; - double sum = std::accumulate(first, last, 0.); - return sum / static_cast(count); - } - - template - sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { + template + sample jackknife(Estimator&& estimator, + std::vector::iterator first, + std::vector::iterator last) { auto n = static_cast(last - first); auto second = first; ++second; @@ -2396,8 +1971,12 @@ namespace Catch { double normal_quantile(double p); - template - Estimate bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { + template + Estimate bootstrap( double confidence_level, + std::vector::iterator first, + std::vector::iterator last, + sample const& resample, + Estimator&& estimator ) { auto n_samples = last - first; double point = estimator(first, last); @@ -2406,13 +1985,13 @@ namespace Catch { sample jack = jackknife(estimator, first, last); double jack_mean = mean(jack.begin(), jack.end()); - double sum_squares, sum_cubes; - std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair sqcb, double x) -> std::pair { - auto d = jack_mean - x; - auto d2 = d * d; - auto d3 = d2 * d; - return { sqcb.first + d2, sqcb.second + d3 }; - }); + double sum_squares = 0, sum_cubes = 0; + for (double x : jack) { + auto difference = jack_mean - x; + auto square = difference * difference; + auto cube = square * difference; + sum_squares += square; sum_cubes += cube; + } double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); long n = static_cast(resample.size()); @@ -2439,15 +2018,16 @@ namespace Catch { return { point, resample[lo], resample[hi], confidence_level }; } - double outlier_variance(Estimate mean, Estimate stddev, int n); - struct bootstrap_analysis { Estimate mean; Estimate standard_deviation; double outlier_variance; }; - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last); + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + std::vector::iterator first, + std::vector::iterator last); } // namespace Detail } // namespace Benchmark } // namespace Catch @@ -2455,7 +2035,6 @@ namespace Catch { #endif // CATCH_STATS_HPP_INCLUDED #include -#include #include #include @@ -2466,26 +2045,29 @@ namespace Catch { std::vector resolution(int k) { std::vector> times; times.reserve(static_cast(k + 1)); - std::generate_n(std::back_inserter(times), k + 1, now{}); + for ( int i = 0; i < k + 1; ++i ) { + times.push_back( Clock::now() ); + } std::vector deltas; deltas.reserve(static_cast(k)); - std::transform(std::next(times.begin()), times.end(), times.begin(), - std::back_inserter(deltas), - [](TimePoint a, TimePoint b) { return static_cast((a - b).count()); }); + for ( size_t idx = 1; idx < times.size(); ++idx ) { + deltas.push_back( static_cast( + ( times[idx] - times[idx - 1] ).count() ) ); + } return deltas; } - const auto warmup_iterations = 10000; - const auto warmup_time = std::chrono::milliseconds(100); - const auto minimum_ticks = 1000; - const auto warmup_seed = 10000; - const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); - const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); - const auto clock_cost_estimation_tick_limit = 100000; - const auto clock_cost_estimation_time = std::chrono::milliseconds(10); - const auto clock_cost_estimation_iterations = 10000; + constexpr auto warmup_iterations = 10000; + constexpr auto warmup_time = std::chrono::milliseconds(100); + constexpr auto minimum_ticks = 1000; + constexpr auto warmup_seed = 10000; + constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + constexpr auto clock_cost_estimation_tick_limit = 100000; + constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); + constexpr auto clock_cost_estimation_iterations = 10000; template int warmup() { @@ -2520,9 +2102,11 @@ namespace Catch { std::vector times; int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); times.reserve(static_cast(nsamples)); - std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { - return static_cast((time_clock(r.iterations) / r.iterations).count()); - }); + for ( int s = 0; s < nsamples; ++s ) { + times.push_back( static_cast( + ( time_clock( r.iterations ) / r.iterations ) + .count() ) ); + } return { FloatDuration(mean(times.begin(), times.end())), classify_outliers(times.begin(), times.end()), @@ -2570,9 +2154,7 @@ namespace Catch { #define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED -#include #include -#include namespace Catch { namespace Benchmark { @@ -2588,7 +2170,9 @@ namespace Catch { operator SampleAnalysis() const { std::vector samples2; samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + for (auto const& d : samples) { + samples2.push_back(Duration2(d)); + } return { CATCH_MOVE(samples2), mean, @@ -2603,8 +2187,6 @@ namespace Catch { #endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED -#include -#include #include namespace Catch { @@ -2615,7 +2197,9 @@ namespace Catch { if (!cfg.benchmarkNoAnalysis()) { std::vector samples; samples.reserve(static_cast(last - first)); - std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); + for (auto current = first; current != last; ++current) { + samples.push_back( current->count() ); + } auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); @@ -2630,7 +2214,10 @@ namespace Catch { }; std::vector samples2; samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); + for (auto s : samples) { + samples2.push_back( Duration( s ) ); + } + return { CATCH_MOVE(samples2), wrap_estimate(analysis.mean), @@ -2666,6 +2253,8 @@ namespace Catch { #endif // CATCH_ANALYSE_HPP_INCLUDED #include +#include +#include #include #include #include @@ -2721,7 +2310,7 @@ namespace Catch { auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); BenchmarkStats> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); - } CATCH_CATCH_ANON (TestFailureException) { + } CATCH_CATCH_ANON (TestFailureException const&) { getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); } CATCH_CATCH_ALL{ getResultCapture().benchmarkFailed(translateActiveException()); @@ -2889,6 +2478,7 @@ namespace Catch { #ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED #define CATCH_CONFIG_WCHAR_HPP_INCLUDED + // We assume that WCHAR should be enabled by default, and only disabled // for a shortlist (so far only DJGPP) of compilers. @@ -3112,7 +2702,6 @@ namespace Catch { } // namespace Detail - // If we decide for C++14, change these to enable_if_ts template struct StringMaker { template @@ -3781,6 +3370,143 @@ struct StringMaker { #endif // CATCH_APPROX_HPP_INCLUDED +#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED +#define CATCH_ASSERTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + +#include +#include + +namespace Catch { + + struct SourceLineInfo { + + SourceLineInfo() = delete; + constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: + file( _file ), + line( _line ) + {} + + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + + friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); + }; +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + +namespace Catch { + + struct AssertionInfo { + // AssertionInfo() = delete; + + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + +} // end namespace Catch + +#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED + + +#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED +#define CATCH_ASSERTION_RESULT_HPP_INCLUDED + + + +#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED +#define CATCH_LAZY_EXPR_HPP_INCLUDED + +#include + +namespace Catch { + + class ITransientExpression; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ): + m_isNegated(isNegated) + {} + LazyExpression(LazyExpression const& other) = default; + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const { + return m_transientExpression != nullptr; + } + + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; + +} // namespace Catch + +#endif // CATCH_LAZY_EXPR_HPP_INCLUDED + +#include + +namespace Catch { + + struct AssertionResultData + { + AssertionResultData() = delete; + + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; + + std::string reconstructExpression() const; + }; + + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + StringRef getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED + + #ifndef CATCH_CONFIG_HPP_INCLUDED #define CATCH_CONFIG_HPP_INCLUDED @@ -3945,6 +3671,7 @@ namespace Catch { #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED + #include namespace Catch { @@ -3953,35 +3680,50 @@ namespace Catch { template class Optional { public: - Optional() : nullableValue( nullptr ) {} - Optional( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Optional( Optional const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) - {} + Optional(): nullableValue( nullptr ) {} + ~Optional() { reset(); } - ~Optional() { + Optional( T const& _value ): + nullableValue( new ( storage ) T( _value ) ) {} + Optional( T&& _value ): + nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} + + Optional& operator=( T const& _value ) { reset(); + nullableValue = new ( storage ) T( _value ); + return *this; + } + Optional& operator=( T&& _value ) { + reset(); + nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); + return *this; } - Optional& operator= ( Optional const& _other ) { - if( &_other != this ) { + Optional( Optional const& _other ): + nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} + Optional( Optional&& _other ): + nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) + : nullptr ) {} + + Optional& operator=( Optional const& _other ) { + if ( &_other != this ) { reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); + if ( _other ) { nullableValue = new ( storage ) T( *_other ); } } return *this; } - Optional& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); + Optional& operator=( Optional&& _other ) { + if ( &_other != this ) { + reset(); + if ( _other ) { + nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); + } + } return *this; } void reset() { - if( nullableValue ) - nullableValue->~T(); + if ( nullableValue ) { nullableValue->~T(); } nullableValue = nullptr; } @@ -4028,7 +3770,7 @@ namespace Catch { } private: - T *nullableValue; + T* nullableValue; alignas(alignof(T)) char storage[sizeof(T)]; }; @@ -4435,10 +4177,10 @@ namespace Catch { // as well as // << stuff +StreamEndStop struct StreamEndStop { - StringRef operator+() const { return StringRef(); } + constexpr StringRef operator+() const { return StringRef(); } template - friend T const& operator+( T const& value, StreamEndStop ) { + constexpr friend T const& operator+( T const& value, StreamEndStop ) { return value; } }; @@ -4447,12 +4189,47 @@ namespace Catch { #endif // CATCH_STREAM_END_STOP_HPP_INCLUDED + +#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED +#define CATCH_MESSAGE_INFO_HPP_INCLUDED + + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == (MessageInfo const& other) const { + return sequence == other.sequence; + } + bool operator < (MessageInfo const& other) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + +} // end namespace Catch + +#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED + #include #include namespace Catch { struct SourceLineInfo; + class IResultCapture; struct MessageStream { @@ -4493,7 +4270,7 @@ namespace Catch { class Capturer { std::vector m_messages; - IResultCapture& m_resultCapture = getResultCapture(); + IResultCapture& m_resultCapture; size_t m_captured = 0; public: Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); @@ -4580,6 +4357,75 @@ namespace Catch { #endif // CATCH_MESSAGE_HPP_INCLUDED +#ifndef CATCH_SECTION_INFO_HPP_INCLUDED +#define CATCH_SECTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_TOTALS_HPP_INCLUDED +#define CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::uint64_t total() const; + bool allPassed() const; + bool allOk() const; + + std::uint64_t passed = 0; + std::uint64_t failed = 0; + std::uint64_t failedButOk = 0; + std::uint64_t skipped = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + Counts assertions; + Counts testCases; + }; +} + +#endif // CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct SectionInfo { + // The last argument is ignored, so that people can write + // SECTION("ShortName", "Proper description that is long") and + // still use the `-c` flag comfortably. + SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, + const char* const = nullptr ): + name(CATCH_MOVE(_name)), + lineInfo(_lineInfo) + {} + + std::string name; + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +#endif // CATCH_SECTION_INFO_HPP_INCLUDED + + #ifndef CATCH_SESSION_HPP_INCLUDED #define CATCH_SESSION_HPP_INCLUDED @@ -5855,8 +5701,6 @@ namespace Catch { namespace Catch { - class IResultCapture; - struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; @@ -5897,7 +5741,6 @@ namespace Catch { void handleUnexpectedInflightException(); void complete(); - void setCompleted(); // query auto allowThrows() const -> bool; @@ -5909,6 +5752,19 @@ namespace Catch { #endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED + +#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr +#endif + +#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + // We need this suppression to leak, because it took until GCC 10 // for the front end to handle local suppression via _Pragma properly #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 @@ -5917,12 +5773,6 @@ namespace Catch { #if !defined(CATCH_CONFIG_DISABLE) -#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) - #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr -#else - #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr -#endif - #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) /////////////////////////////////////////////////////////////////////////////// @@ -5990,6 +5840,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6010,6 +5861,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(expr); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6036,6 +5888,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6059,6 +5912,34 @@ namespace Catch { + +/** \file + * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option + * + * Some of Catch2's macros can be defined differently to work better with + * static analysis tools, like clang-tidy or coverity. + * Currently the main use case is to show that `SECTION`s are executed + * exclusively, and not all in one run of a `TEST_CASE`. + */ + +#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + + +#if defined(__clang_analyzer__) || defined(__COVERITY__) + #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT +#endif + +#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) +# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +#endif + + +#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + + #ifndef CATCH_TIMER_HPP_INCLUDED #define CATCH_TIMER_HPP_INCLUDED @@ -6103,17 +5984,63 @@ namespace Catch { } // end namespace Catch -#define INTERNAL_CATCH_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::SectionInfo( \ + CATCH_INTERNAL_LINEINFO, \ + ( Catch::ReusableStringStream() << __VA_ARGS__ ) \ + .str() ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#else + +// These section definitions imply that at most one section at one level +// will be intered (because only one section's __LINE__ can be equal to +// the dummy `catchInternalSectionHint` variable from `TEST_CASE`). + +namespace Catch { + namespace Detail { + // Intentionally without linkage, as it should only be used as a dummy + // symbol for static analysis. + int GetNewSectionHint(); + } // namespace Detail +} // namespace Catch + + +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#endif -#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif // CATCH_SECTION_HPP_INCLUDED @@ -6123,42 +6050,20 @@ namespace Catch { -#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED - -#include +#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED namespace Catch { - class TestSpec; - struct TestCaseInfo; - class ITestInvoker { public: - virtual void invoke () const = 0; + virtual void invoke() const = 0; virtual ~ITestInvoker(); // = default }; - class TestCaseHandle; - class IConfig; +} // namespace Catch - class ITestCaseRegistry { - public: - virtual ~ITestCaseRegistry(); // = default - // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later - virtual std::vector const& getAllInfos() const = 0; - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED #ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED @@ -6230,6 +6135,9 @@ struct AutoReg : Detail::NonCopyable { void TestName::test() #endif + +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ @@ -6242,19 +6150,40 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - namespace { \ - 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 +#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT + + +// Dummy registrator for the dumy test case macros +namespace Catch { + namespace Detail { + struct DummyUse { + DummyUse( void ( * )( int ) ); + }; + } // namespace Detail +} // namespace Catch + +// Note that both the presence of the argument and its exact name are +// necessary for the section support. + +// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` +// tests can compile. The redefined `TEST_CASE` shadows this with param. +static int catchInternalSectionHint = 0; + +# define INTERNAL_CATCH_TESTCASE2( fname ) \ + static void fname( int ); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ + dummyUser )( &fname ); \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ) ) + + +#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ @@ -6276,6 +6205,22 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &QualifiedMethod ), \ + CATCH_INTERNAL_LINEINFO, \ + "&" #QualifiedMethod##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ do { \ @@ -7318,6 +7263,10 @@ namespace Catch { #include namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ); + } class ExceptionTranslatorRegistrar { template @@ -7351,9 +7300,9 @@ namespace Catch { public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) { - getMutableRegistryHub().registerTranslator( - Detail::make_unique>(translateFunction) - ); + Detail::registerTranslatorImpl( + Detail::make_unique>( + translateFunction ) ); } }; @@ -7425,8 +7374,8 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 3 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_MINOR 4 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -8299,6 +8248,232 @@ GeneratorWrapper from_range(Container const& cnt) { +#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED +#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED + + + +#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED +#define CATCH_TEST_RUN_INFO_HPP_INCLUDED + + +namespace Catch { + + struct TestRunInfo { + constexpr TestRunInfo(StringRef _name) : name(_name) {} + StringRef name; + }; + +} // end namespace Catch + +#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + struct ReporterDescription; + struct ListenerDescription; + struct TagInfo; + struct TestCaseInfo; + class TestCaseHandle; + class IConfig; + class IStream; + enum class ColourMode : std::uint8_t; + + struct ReporterConfig { + ReporterConfig( IConfig const* _fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions ); + + ReporterConfig( ReporterConfig&& ) = default; + ReporterConfig& operator=( ReporterConfig&& ) = default; + ~ReporterConfig(); // = default + + Detail::unique_ptr takeStream() &&; + IConfig const* fullConfig() const; + ColourMode colourMode() const; + std::map const& customOptions() const; + + private: + Detail::unique_ptr m_stream; + IConfig const* m_fullConfig; + ColourMode m_colourMode; + std::map m_customOptions; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo&& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string&& _stdOut, + std::string&& _stdErr, + bool _aborting ); + + TestCaseInfo const * testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + //! By setting up its preferences, a reporter can modify Catch2's behaviour + //! in some regards, e.g. it can request Catch2 to capture writes to + //! stdout/stderr during test execution, and pass them to the reporter. + struct ReporterPreferences { + //! Catch2 should redirect writes to stdout and pass them to the + //! reporter + bool shouldRedirectStdOut = false; + //! Catch2 should call `Reporter::assertionEnded` even for passing + //! assertions + bool shouldReportAllAssertions = false; + }; + + /** + * The common base for all reporters and event listeners + * + * Implementing classes must also implement: + * + * //! User-friendly description of the reporter/listener type + * static std::string getDescription() + * + * Generally shouldn't be derived from by users of Catch2 directly, + * instead they should derive from one of the utility bases that + * derive from this class. + */ + class IEventListener { + protected: + //! Derived classes can set up their preferences here + ReporterPreferences m_preferences; + //! The test run's config as filled in from CLI and defaults + IConfig const* m_config; + + public: + IEventListener( IConfig const* config ): m_config( config ) {} + + virtual ~IEventListener(); // = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + + ReporterPreferences const& getPreferences() const { + return m_preferences; + } + + //! Called when no test cases match provided test spec + virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0; + //! Called for all invalid test specs from the cli + virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0; + + /** + * Called once in a testing run before tests are started + * + * Not called if tests won't be run (e.g. only listing will happen) + */ + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0; + //! Called when a `SECTION` is being entered. Not called for skipped sections + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + //! Called when user-code is being probed before the actual benchmark runs + virtual void benchmarkPreparing( StringRef benchmarkName ) = 0; + //! Called after probe but before the user-code is being benchmarked + virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0; + //! Called with the benchmark results if benchmark successfully finishes + virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0; + //! Called if running the benchmarks fails for any reason + virtual void benchmarkFailed( StringRef benchmarkName ) = 0; + + //! Called before assertion success/failure is evaluated + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + //! Called after assertion was fully evaluated + virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; + + //! Called after a `SECTION` has finished running + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0; + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + /** + * Called once after all tests in a testing run are finished + * + * Not called if tests weren't run (e.g. only listings happened) + */ + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + /** + * Called with test cases that are skipped due to the test run aborting. + * NOT called for test cases that are explicitly skipped using the `SKIP` macro. + * + * Deprecated - will be removed in the next major release. + */ + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + //! Called if a fatal error (signal/structured exception) occurred + virtual void fatalErrorEncountered( StringRef error ) = 0; + + //! Writes out information about provided reporters using reporter-specific format + virtual void listReporters(std::vector const& descriptions) = 0; + //! Writes out the provided listeners descriptions using reporter-specific format + virtual void listListeners(std::vector const& descriptions) = 0; + //! Writes out information about provided tests using reporter-specific format + virtual void listTests(std::vector const& tests) = 0; + //! Writes out information about the provided tags using reporter-specific format + virtual void listTags(std::vector const& tags) = 0; + }; + using IEventListenerPtr = Detail::unique_ptr; + +} // end namespace Catch + +#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED + + #ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED #define CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED @@ -8337,9 +8512,55 @@ namespace Catch { #endif // CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED -#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED +#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#include + +namespace Catch { + + struct TagAlias; + + class ITagAliasRegistry { + public: + virtual ~ITagAliasRegistry(); // = default + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED + + +#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED + +#include + +namespace Catch { + + struct TestCaseInfo; + class TestCaseHandle; + class IConfig; + + class ITestCaseRegistry { + public: + virtual ~ITestCaseRegistry(); // = default + // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later + virtual std::vector const& getAllInfos() const = 0; + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + +} + +#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED + +#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED #ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED @@ -8365,62 +8586,6 @@ namespace Catch { #endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED -#include -#include -#include - -namespace Catch { - - class IConfig; - - class IEventListener; - using IEventListenerPtr = Detail::unique_ptr; - class IReporterFactory; - using IReporterFactoryPtr = Detail::unique_ptr; - struct ReporterConfig; - class EventListenerFactory; - - class IReporterRegistry { - public: - using FactoryMap = std::map; - using Listeners = std::vector>; - - virtual ~IReporterRegistry(); // = default - virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; - -} // end namespace Catch - -#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED - - -#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED - -#include - -namespace Catch { - - struct TagAlias; - - class ITagAliasRegistry { - public: - virtual ~ITagAliasRegistry(); // = default - // Nullptr if not present - virtual TagAlias const* find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - - static ITagAliasRegistry const& get(); - }; - -} // end namespace Catch - -#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED - -#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED - /** \file @@ -8461,6 +8626,7 @@ namespace Catch { #ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED #define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED + #if defined(_MSC_VER) # if _MSC_VER >= 1900 // Visual Studio 2015 or newer # define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS @@ -8751,7 +8917,6 @@ namespace Catch { ~ExceptionTranslatorRegistry() override; void registerTranslator( Detail::unique_ptr&& translator ); std::string translateActiveException() const override; - std::string tryTranslators() const; private: ExceptionTranslators m_translators; @@ -9312,28 +9477,45 @@ namespace Catch { #include +#include +#include namespace Catch { - class ReporterRegistry : public IReporterRegistry { + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; + class IReporterFactory; + using IReporterFactoryPtr = Detail::unique_ptr; + struct ReporterConfig; + class EventListenerFactory; + + class ReporterRegistry { + struct ReporterRegistryImpl; + Detail::unique_ptr m_impl; + public: - ReporterRegistry(); - ~ReporterRegistry() override; // = default, out of line to allow fwd decl + ~ReporterRegistry(); // = default; - IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override; + IEventListenerPtr create( std::string const& name, + ReporterConfig&& config ) const; - void registerReporter( std::string const& name, IReporterFactoryPtr factory ); - void registerListener( Detail::unique_ptr factory ); + void registerReporter( std::string const& name, + IReporterFactoryPtr factory ); - FactoryMap const& getFactories() const override; - Listeners const& getListeners() const override; + void + registerListener( Detail::unique_ptr factory ); - private: - FactoryMap m_factories; - Listeners m_listeners; + std::map const& + getFactories() const; + + std::vector> const& + getListeners() const; }; -} + +} // end namespace Catch #endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED @@ -9448,7 +9630,7 @@ 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 + //! Returns true if tracker run to completion successfully bool isSuccessfullyCompleted() const { return m_runState == CompletedSuccessfully; } @@ -9582,13 +9764,14 @@ using TestCaseTracking::SectionTracker; namespace Catch { - class IMutableContext; class IGeneratorTracker; class IConfig; + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; /////////////////////////////////////////////////////////////////////////// - class RunContext : public IResultCapture { + class RunContext final : public IResultCapture { public: RunContext( RunContext const& ) = delete; @@ -9617,7 +9800,7 @@ namespace Catch { AssertionReaction& reaction ) override; void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) override; void handleIncomplete ( AssertionInfo const& info ) override; @@ -9626,6 +9809,7 @@ namespace Catch { ResultWas::OfType resultType, AssertionReaction &reaction ) override; + void notifyAssertionStarted( AssertionInfo const& info ) override; bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) override; @@ -9676,7 +9860,7 @@ namespace Catch { void resetAssertionInfo(); bool testForMissingAssertions( Counts& assertions ); - void assertionEnded( AssertionResult const& result ); + void assertionEnded( AssertionResult&& result ); void reportExpr ( AssertionInfo const &info, ResultWas::OfType resultType, @@ -9690,7 +9874,6 @@ namespace Catch { void handleUnfinishedSections(); TestRunInfo m_runInfo; - IMutableContext& m_context; TestCaseHandle const* m_activeTestCase = nullptr; ITracker* m_testCaseTracker = nullptr; Optional m_lastResult; @@ -9947,16 +10130,14 @@ namespace Catch { namespace Catch { - class TestCaseHandle; class IConfig; + class ITestInvoker; + class TestCaseHandle; class TestSpec; std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - - void enforceNoDuplicateTestCases( std::vector const& functions ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); @@ -9985,18 +10166,6 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - class TestInvokerAsFunction final : public ITestInvoker { - using TestType = void(*)(); - TestType m_testAsFunction; - public: - TestInvokerAsFunction(TestType testAsFunction) noexcept: - m_testAsFunction(testAsFunction) {} - - void invoke() const override; - }; - - /////////////////////////////////////////////////////////////////////////// - } // end namespace Catch @@ -10132,7 +10301,7 @@ namespace Catch { // Calculates the length of the current line void calcLength(); - // Returns current indention width + // Returns current indentation width size_t indentSize() const; // Creates an indented and (optionally) suffixed string from @@ -10445,6 +10614,8 @@ namespace Catch { #define CATCH_MATCHERS_IMPL_HPP_INCLUDED +#include + namespace Catch { template @@ -11680,7 +11851,7 @@ namespace Catch { /** * Creates a matcher that checks if all elements in a range are equal - * to all elements in another range, in some permuation. + * to all elements in another range, in some permutation. * * Uses to provided predicate `predicate` to do the comparisons */ @@ -11850,11 +12021,10 @@ namespace Matchers { // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly - if (m_comparator.size() != v.size()) - return false; - for (std::size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != v[i]) - return false; + if ( m_comparator.size() != v.size() ) { return false; } + for ( std::size_t i = 0; i < v.size(); ++i ) { + if ( !( m_comparator[i] == v[i] ) ) { return false; } + } return true; } std::string describe() const override { @@ -12358,7 +12528,7 @@ namespace Catch { void skipTest(TestCaseInfo const&) override {} protected: - //! Should the cumulative base store the assertion expansion for succesful assertions? + //! Should the cumulative base store the assertion expansion for successful assertions? bool m_shouldStoreSuccesfulAssertions = true; //! Should the cumulative base store the assertion expansion for failed assertions? bool m_shouldStoreFailedAssertions = true; @@ -12665,7 +12835,8 @@ namespace Catch { //! independent on the reporter's concrete type void registerReporterImpl( std::string const& name, IReporterFactoryPtr reporterPtr ); - + //! Actually registers the factory, independent on listener's concrete type + void registerListenerImpl( Detail::unique_ptr listenerFactory ); } // namespace Detail class IEventListener; @@ -12726,7 +12897,7 @@ namespace Catch { public: ListenerRegistrar(StringRef listenerName) { - getMutableRegistryHub().registerListener( Detail::make_unique(listenerName) ); + registerListenerImpl( Detail::make_unique(listenerName) ); } }; } diff --git a/meson.build b/meson.build index ed5033ac..de028e87 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.3.2', # CML version placeholder, don't delete + version: '3.4.0', # CML version placeholder, don't delete license: 'BSL-1.0', meson_version: '>=0.54.1', ) diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp index 19cab91b..ac86aa3b 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, 2, "", 0 ); + static Version version( 3, 4, 0, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 9ece8505..28b20aad 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 3 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_MINOR 4 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED