diff --git a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp index cdd93396..3ae59880 100644 --- a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -81,10 +81,10 @@ namespace chaiscript { template Ret call_func_impl(Ret (*)(Params...), - std::index_sequence, - const Callable &f, - [[maybe_unused]] const chaiscript::Function_Params ¶ms, - [[maybe_unused]] const Type_Conversions_State &t_conversions) { + std::index_sequence, + const Callable &f, + [[maybe_unused]] const chaiscript::Function_Params ¶ms, + [[maybe_unused]] const Type_Conversions_State &t_conversions) { return f(boxed_cast(params[I], &t_conversions)...); } @@ -101,7 +101,7 @@ namespace chaiscript { // MSVC has a broken warning for unreachable code in this block #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable: 4702) +#pragma warning(disable : 4702) #endif template Boxed_Value diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index fa46a460..48f57f4c 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -317,7 +317,7 @@ namespace chaiscript { std::vector t_module_paths = {}, std::vector t_use_paths = {}, const std::vector &t_opts = chaiscript::default_options(), - const bool t_no_io=false) + const bool t_no_io = false) : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))) , m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))) , m_parser(std::move(parser)) diff --git a/include/chaiscript/utility/hash.hpp b/include/chaiscript/utility/hash.hpp index 9a5b49f5..06cef3a0 100644 --- a/include/chaiscript/utility/hash.hpp +++ b/include/chaiscript/utility/hash.hpp @@ -9,8 +9,8 @@ #include "../chaiscript_defines.hpp" -#include #include +#include namespace chaiscript { namespace utility { diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index a7f998cc..cb5fdffe 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -126,7 +126,7 @@ namespace chaiscript::json { JSONWrapper(Container *val) : object(val) { } - JSONWrapper(std::nullptr_t) { } + JSONWrapper(std::nullptr_t) {} Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } Container::iterator end() { return object ? object->end() : typename Container::iterator(); } diff --git a/include/chaiscript/utility/unicode.hpp b/include/chaiscript/utility/unicode.hpp index 294278ad..36363a31 100644 --- a/include/chaiscript/utility/unicode.hpp +++ b/include/chaiscript/utility/unicode.hpp @@ -15,7 +15,9 @@ namespace chaiscript { inline constexpr std::uint32_t max_codepoint = 0x10FFFF; - constexpr bool is_surrogate(std::uint32_t cp) noexcept { return cp >= 0xD800 && cp <= 0xDFFF; } + constexpr bool is_surrogate(std::uint32_t cp) noexcept { + return cp >= 0xD800 && cp <= 0xDFFF; + } // Append cp to out as UTF-8. Returns bytes written, or 0 if cp >= 0x200000. // Surrogates are not rejected here; callers that care check is_surrogate() first. diff --git a/unittests/catch_amalgamated.cpp b/unittests/catch_amalgamated.cpp index b0c5c7ca..a9244385 100644 --- a/unittests/catch_amalgamated.cpp +++ b/unittests/catch_amalgamated.cpp @@ -15,20 +15,18 @@ #include "catch_amalgamated.hpp" - #ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED #define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED - #if defined(CATCH_PLATFORM_WINDOWS) // We might end up with the define made globally through the compiler, // and we don't want to trigger warnings for this #if !defined(NOMINMAX) -# define NOMINMAX +#define NOMINMAX #endif #if !defined(WIN32_LEAN_AND_MEAN) -# define WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN #endif #include @@ -37,137 +35,122 @@ #endif // CATCH_WINDOWS_H_PROXY_HPP_INCLUDED - - - namespace Catch { - namespace Benchmark { - namespace Detail { - ChronometerConcept::~ChronometerConcept() = default; - } // namespace Detail - } // namespace Benchmark + namespace Benchmark { + namespace Detail { + ChronometerConcept::~ChronometerConcept() = default; + } // namespace Detail + } // namespace Benchmark } // namespace Catch - // Adapted from donated nonius code. - #include namespace Catch { - namespace Benchmark { - namespace Detail { - SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) { - if (!cfg.benchmarkNoAnalysis()) { - std::vector samples; - samples.reserve(static_cast(last - first)); - for (auto current = first; current != last; ++current) { - samples.push_back( current->count() ); - } + namespace Benchmark { + namespace Detail { + SampleAnalysis analyse(const IConfig &cfg, FDuration *first, FDuration *last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector samples; + samples.reserve(static_cast(last - first)); + for (auto current = first; current != last; ++current) { + samples.push_back(current->count()); + } - auto analysis = Catch::Benchmark::Detail::analyse_samples( - cfg.benchmarkConfidenceInterval(), - cfg.benchmarkResamples(), - samples.data(), - samples.data() + samples.size() ); - auto outliers = Catch::Benchmark::Detail::classify_outliers( - samples.data(), samples.data() + samples.size() ); + auto analysis = Catch::Benchmark::Detail::analyse_samples( + cfg.benchmarkConfidenceInterval(), + cfg.benchmarkResamples(), + samples.data(), + samples.data() + samples.size()); + auto outliers = Catch::Benchmark::Detail::classify_outliers( + samples.data(), samples.data() + samples.size()); - auto wrap_estimate = [](Estimate e) { - return Estimate { - FDuration(e.point), - FDuration(e.lower_bound), - FDuration(e.upper_bound), - e.confidence_interval, - }; - }; - std::vector samples2; - samples2.reserve(samples.size()); - for (auto s : samples) { - samples2.push_back( FDuration( s ) ); - } + auto wrap_estimate = [](Estimate e) { + return Estimate{ + FDuration(e.point), + FDuration(e.lower_bound), + FDuration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector samples2; + samples2.reserve(samples.size()); + for (auto s : samples) { + samples2.push_back(FDuration(s)); + } - return { - CATCH_MOVE(samples2), - wrap_estimate(analysis.mean), - wrap_estimate(analysis.standard_deviation), - outliers, - analysis.outlier_variance, - }; - } else { - std::vector samples; - samples.reserve(static_cast(last - first)); + return { + CATCH_MOVE(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector samples; + samples.reserve(static_cast(last - first)); - FDuration mean = FDuration(0); - int i = 0; - for (auto it = first; it < last; ++it, ++i) { - samples.push_back(*it); - mean += *it; - } - mean /= i; + FDuration mean = FDuration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(*it); + mean += *it; + } + mean /= i; - return SampleAnalysis{ - CATCH_MOVE(samples), - Estimate{ mean, mean, mean, 0.0 }, - Estimate{ FDuration( 0 ), - FDuration( 0 ), - FDuration( 0 ), - 0.0 }, - OutlierClassification{}, - 0.0 - }; - } - } - } // namespace Detail - } // namespace Benchmark + return SampleAnalysis{ + CATCH_MOVE(samples), + Estimate{mean, mean, mean, 0.0}, + Estimate{FDuration(0), + FDuration(0), + FDuration(0), + 0.0}, + OutlierClassification{}, + 0.0}; + } + } + } // namespace Detail + } // namespace Benchmark } // namespace Catch - - - namespace Catch { - namespace Benchmark { - namespace Detail { - struct do_nothing { - void operator()() const {} - }; + namespace Benchmark { + namespace Detail { + struct do_nothing { + void operator()() const {} + }; - BenchmarkFunction::callable::~callable() = default; - BenchmarkFunction::BenchmarkFunction(): - f( new model{ {} } ){} - } // namespace Detail - } // namespace Benchmark + BenchmarkFunction::callable::~callable() = default; + BenchmarkFunction::BenchmarkFunction() + : f(new model{{}}) {} + } // namespace Detail + } // namespace Benchmark } // namespace Catch - - - #include namespace Catch { - namespace Benchmark { - namespace Detail { - struct optimized_away_error : std::exception { - const char* what() const noexcept override; - }; + namespace Benchmark { + namespace Detail { + struct optimized_away_error : std::exception { + const char *what() const noexcept override; + }; - const char* optimized_away_error::what() const noexcept { - return "could not measure benchmark, maybe it was optimized away"; - } + const char *optimized_away_error::what() const noexcept { + return "could not measure benchmark, maybe it was optimized away"; + } - void throw_optimized_away_error() { - Catch::throw_exception(optimized_away_error{}); - } + void throw_optimized_away_error() { + Catch::throw_exception(optimized_away_error{}); + } - } // namespace Detail - } // namespace Benchmark + } // namespace Detail + } // namespace Benchmark } // namespace Catch - // Adapted from donated nonius code. - - #include #include #include @@ -175,1025 +158,1043 @@ namespace Catch { #include #include - #if defined(CATCH_CONFIG_USE_ASYNC) #include #endif namespace Catch { - namespace Benchmark { - namespace Detail { - namespace { + namespace Benchmark { + namespace Detail { + namespace { - template - static sample - resample( URng& rng, - unsigned int resamples, - double const* first, - double const* last, - Estimator& estimator ) { - auto n = static_cast( last - first ); - Catch::uniform_integer_distribution dist( 0, n - 1 ); + template + static sample + resample(URng &rng, + unsigned int resamples, + double const *first, + double const *last, + Estimator &estimator) { + auto n = static_cast(last - first); + Catch::uniform_integer_distribution dist(0, n - 1); - sample out; - out.reserve( resamples ); - 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[dist( rng )] ); - } - const auto estimate = - estimator( resampled.data(), resampled.data() + resampled.size() ); - out.push_back( estimate ); - } - std::sort( out.begin(), out.end() ); - return out; - } + sample out; + out.reserve(resamples); + 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[dist(rng)]); + } + const auto estimate = estimator(resampled.data(), resampled.data() + resampled.size()); + out.push_back(estimate); + } + 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; + 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 ) ) ); - }; + 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 ); - }; + 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; - } + return (std::min)(var_out(1), + var_out( + (std::min)(c_max(0.), + c_max(mg_min)))) + / sb2; + } - static double erf_inv( double x ) { - // Code accompanying the article "Approximating the erfinv - // function" in GPU Computing Gems, Volume 2 - double w, p; + static double erf_inv(double x) { + // Code accompanying the article "Approximating the erfinv + // function" in GPU Computing Gems, Volume 2 + double w, p; - w = -log( ( 1.0 - x ) * ( 1.0 + x ) ); + 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; - } + 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( double const* first, double const* 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; - } ) / - static_cast( last - first ); - return std::sqrt( variance ); - } + static double + standard_deviation(double const *first, double const *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; + }) + / static_cast(last - first); + return std::sqrt(variance); + } - static sample jackknife( double ( *estimator )( double const*, - double const* ), - double* first, - double* last ) { - const auto second = first + 1; - sample results; - results.reserve( static_cast( last - first ) ); + static sample jackknife(double (*estimator)(double const *, + double const *), + double *first, + double *last) { + const auto second = first + 1; + sample results; + results.reserve(static_cast(last - first)); - for ( auto it = first; it != last; ++it ) { - std::iter_swap( it, first ); - results.push_back( estimator( second, last ) ); - } + for (auto it = first; it != last; ++it) { + std::iter_swap(it, first); + results.push_back(estimator(second, last)); + } - return results; - } + return results; + } - - } // namespace - } // namespace Detail - } // namespace Benchmark + } // namespace + } // namespace Detail + } // namespace Benchmark } // namespace Catch namespace Catch { - namespace Benchmark { - namespace Detail { + namespace Benchmark { + namespace Detail { - double weighted_average_quantile( int k, - int q, - double* first, - double* last ) { - auto count = last - first; - double idx = static_cast((count - 1) * k) / static_cast(q); - int j = static_cast(idx); - double g = idx - j; - std::nth_element(first, first + j, last); - auto xj = first[j]; - if ( Catch::Detail::directCompare( g, 0 ) ) { - return xj; - } + double weighted_average_quantile(int k, + int q, + double *first, + double *last) { + auto count = last - first; + double idx = static_cast((count - 1) * k) / static_cast(q); + int j = static_cast(idx); + double g = idx - j; + std::nth_element(first, first + j, last); + auto xj = first[j]; + if (Catch::Detail::directCompare(g, 0)) { + return xj; + } - auto xj1 = *std::min_element(first + (j + 1), last); - return xj + g * (xj1 - xj); - } + auto xj1 = *std::min_element(first + (j + 1), last); + return xj + g * (xj1 - xj); + } - OutlierClassification - classify_outliers( double const* first, double const* last ) { - std::vector copy( first, last ); + OutlierClassification + classify_outliers(double const *first, double const *last) { + std::vector copy(first, last); - auto q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() ); - auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() ); - 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. ); + auto q1 = weighted_average_quantile(1, 4, copy.data(), copy.data() + copy.size()); + auto q3 = weighted_average_quantile(3, 4, copy.data(), copy.data() + copy.size()); + 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; - } + 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( double const* first, double const* last ) { - auto count = last - first; - double sum = 0.; - while (first != last) { - sum += *first; - ++first; - } - return sum / static_cast(count); - } + double mean(double const *first, double const *last) { + auto count = last - first; + double sum = 0.; + while (first != last) { + sum += *first; + ++first; + } + return sum / static_cast(count); + } - double normal_cdf( double x ) { - return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0; - } + double normal_cdf(double x) { + return std::erfc(-x / std::sqrt(2.0)) / 2.0; + } - double erfc_inv(double x) { - return erf_inv(1.0 - x); - } + double erfc_inv(double x) { + return erf_inv(1.0 - x); + } - double normal_quantile(double p) { - static const double ROOT_TWO = std::sqrt(2.0); + double normal_quantile(double p) { + static const double ROOT_TWO = std::sqrt(2.0); - double result = 0.0; - assert(p >= 0 && p <= 1); - if (p < 0 || p > 1) { - return result; - } + double result = 0.0; + assert(p >= 0 && p <= 1); + if (p < 0 || p > 1) { + return result; + } - result = -erfc_inv(2.0 * p); - // result *= normal distribution standard deviation (1.0) * sqrt(2) - result *= /*sd * */ ROOT_TWO; - // result += normal disttribution mean (0) - return result; - } + result = -erfc_inv(2.0 * p); + // result *= normal distribution standard deviation (1.0) * sqrt(2) + result *= /*sd * */ ROOT_TWO; + // result += normal disttribution mean (0) + return result; + } - Estimate - bootstrap( double confidence_level, - double* first, - double* last, - sample const& resample, - double ( *estimator )( double const*, double const* ) ) { - auto n_samples = last - first; + Estimate + bootstrap(double confidence_level, + double *first, + double *last, + sample const &resample, + double (*estimator)(double const *, double const *)) { + auto n_samples = last - first; - double point = estimator( first, last ); - // Degenerate case with a single sample - if ( n_samples == 1 ) - return { point, point, point, confidence_level }; + double point = estimator(first, last); + // Degenerate case with a single sample + if (n_samples == 1) + return {point, point, point, confidence_level}; - sample jack = jackknife( estimator, first, last ); - double jack_mean = - mean( jack.data(), jack.data() + jack.size() ); - 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; - } + sample jack = jackknife(estimator, first, last); + double jack_mean = mean(jack.data(), jack.data() + jack.size()); + 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() ); - double prob_n = static_cast( - std::count_if( resample.begin(), - resample.end(), - [point]( double x ) { return x < point; } )) / - static_cast( n ); - // degenerate case with uniform samples - if ( Catch::Detail::directCompare( prob_n, 0. ) ) { - return { point, point, point, confidence_level }; - } + double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); + long n = static_cast(resample.size()); + double prob_n = static_cast( + std::count_if(resample.begin(), + resample.end(), + [point](double x) { return x < point; })) + / static_cast(n); + // degenerate case with uniform samples + if (Catch::Detail::directCompare(prob_n, 0.)) { + return {point, point, point, confidence_level}; + } - double bias = normal_quantile( prob_n ); - double z1 = normal_quantile( ( 1. - confidence_level ) / 2. ); + double bias = normal_quantile(prob_n); + double z1 = normal_quantile((1. - confidence_level) / 2.); - auto cumn = [n]( double x ) -> long { - return std::lround( normal_cdf( x ) * - static_cast( n ) ); - }; - auto a = [bias, accel]( double b ) { - return bias + b / ( 1. - accel * b ); - }; - double b1 = bias + z1; - double b2 = bias - z1; - double a1 = a( b1 ); - double a2 = a( b2 ); - auto lo = static_cast( (std::max)( cumn( a1 ), 0l ) ); - auto hi = - static_cast( (std::min)( cumn( a2 ), n - 1 ) ); + auto cumn = [n](double x) -> long { + return std::lround(normal_cdf(x) * static_cast(n)); + }; + auto a = [bias, accel](double b) { + return bias + b / (1. - accel * b); + }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a(b1); + double a2 = a(b2); + auto lo = static_cast((std::max)(cumn(a1), 0l)); + auto hi = static_cast((std::min)(cumn(a2), n - 1)); - return { point, resample[lo], resample[hi], confidence_level }; - } + return {point, resample[lo], resample[hi], confidence_level}; + } - bootstrap_analysis analyse_samples(double confidence_level, - unsigned int n_resamples, - double* first, - double* last) { - auto mean = &Detail::mean; - auto stddev = &standard_deviation; + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double *first, + double *last) { + auto mean = &Detail::mean; + auto stddev = &standard_deviation; #if defined(CATCH_CONFIG_USE_ASYNC) - auto Estimate = [=](double(*f)(double const*, double const*)) { - std::random_device rd; - auto seed = rd(); - return std::async(std::launch::async, [=] { - SimplePcg32 rng( seed ); - auto resampled = resample(rng, n_resamples, first, last, f); - return bootstrap(confidence_level, first, last, resampled, f); - }); - }; + auto Estimate = [=](double (*f)(double const *, double const *)) { + std::random_device rd; + auto seed = rd(); + return std::async(std::launch::async, [=] { + SimplePcg32 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }); + }; - auto mean_future = Estimate(mean); - auto stddev_future = Estimate(stddev); + auto mean_future = Estimate(mean); + auto stddev_future = Estimate(stddev); - auto mean_estimate = mean_future.get(); - auto stddev_estimate = stddev_future.get(); + auto mean_estimate = mean_future.get(); + auto stddev_estimate = stddev_future.get(); #else - auto Estimate = [=](double(*f)(double const* , double const*)) { - std::random_device rd; - auto seed = rd(); - SimplePcg32 rng( seed ); - auto resampled = resample(rng, n_resamples, first, last, f); - return bootstrap(confidence_level, first, last, resampled, f); - }; + auto Estimate = [=](double (*f)(double const *, double const *)) { + std::random_device rd; + auto seed = rd(); + SimplePcg32 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }; - auto mean_estimate = Estimate(mean); - auto stddev_estimate = Estimate(stddev); + auto mean_estimate = Estimate(mean); + auto stddev_estimate = Estimate(stddev); #endif // CATCH_USE_ASYNC - auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ - double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); + auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ + double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); - return { mean_estimate, stddev_estimate, outlier_variance }; - } - } // namespace Detail - } // namespace Benchmark + return {mean_estimate, stddev_estimate, outlier_variance}; + } + } // namespace Detail + } // namespace Benchmark } // namespace Catch - - #include #include namespace { -// Performs equivalent check of std::fabs(lhs - rhs) <= margin -// But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool marginComparison(double lhs, double rhs, double margin) { return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} + } -} +} // namespace namespace Catch { - Approx::Approx ( double value ) - : m_epsilon( static_cast(std::numeric_limits::epsilon())*100. ), - m_margin( 0.0 ), - m_scale( 0.0 ), - m_value( value ) - {} + Approx::Approx(double value) + : m_epsilon(static_cast(std::numeric_limits::epsilon()) * 100.), m_margin(0.0), m_scale(0.0), m_value(value) {} - Approx Approx::custom() { - return Approx( 0 ); + Approx Approx::custom() { + return Approx(0); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify(m_value) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) + || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value) ? 0 : m_value))); + } + + void Approx::setMargin(double newMargin) { + CATCH_ENFORCE(newMargin >= 0, + "Invalid Approx::margin: " << newMargin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = newMargin; + } + + void Approx::setEpsilon(double newEpsilon) { + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " << newEpsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; + } + + namespace literals { + Approx operator""_a(long double val) { + return Approx(val); } - - Approx Approx::operator-() const { - auto temp(*this); - temp.m_value = -temp.m_value; - return temp; + Approx operator""_a(unsigned long long val) { + return Approx(val); } + } // end namespace literals - - std::string Approx::toString() const { - ReusableStringStream rss; - rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; - return rss.str(); - } - - bool Approx::equalityComparisonImpl(const double other) const { - // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value - // Thanks to Richard Harris for his help refining the scaled margin value - return marginComparison(m_value, other, m_margin) - || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); - } - - void Approx::setMargin(double newMargin) { - CATCH_ENFORCE(newMargin >= 0, - "Invalid Approx::margin: " << newMargin << '.' - << " Approx::Margin has to be non-negative."); - m_margin = newMargin; - } - - void Approx::setEpsilon(double newEpsilon) { - CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, - "Invalid Approx::epsilon: " << newEpsilon << '.' - << " Approx::epsilon has to be in [0, 1]"); - m_epsilon = newEpsilon; - } - -namespace literals { - Approx operator ""_a(long double val) { - return Approx(val); - } - Approx operator ""_a(unsigned long long val) { - return Approx(val); - } -} // end namespace literals - -std::string StringMaker::convert(Catch::Approx const& value) { + std::string StringMaker::convert(Catch::Approx const &value) { return value.toString(); -} + } } // end namespace Catch - - namespace Catch { - AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const& _lazyExpression): - lazyExpression(_lazyExpression), - resultType(_resultType) {} + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression) + : lazyExpression(_lazyExpression), resultType(_resultType) {} - std::string AssertionResultData::reconstructExpression() const { - - if( reconstructedExpression.empty() ) { - if( lazyExpression ) { - ReusableStringStream rss; - rss << lazyExpression; - reconstructedExpression = rss.str(); - } - } - return reconstructedExpression; + std::string AssertionResultData::reconstructExpression() const { + if (reconstructedExpression.empty()) { + if (lazyExpression) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } } + return reconstructedExpression; + } - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data ) - : m_info( info ), - m_resultData( CATCH_MOVE(data) ) - {} + AssertionResult::AssertionResult(AssertionInfo const &info, AssertionResultData &&data) + : m_info(info), m_resultData(CATCH_MOVE(data)) {} - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); - } + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk(m_resultData.resultType); + } - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); - } + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk(m_resultData.resultType) || shouldSuppressFailure(m_info.resultDisposition); + } - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; - } + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } - bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); - } + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); - } + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } - std::string AssertionResult::getExpression() const { - // Possibly overallocating by 3 characters should be basically free - std::string expr; expr.reserve(m_info.capturedExpression.size() + 3); - if (isFalseTest(m_info.resultDisposition)) { - expr += "!("; - } - expr += m_info.capturedExpression; - if (isFalseTest(m_info.resultDisposition)) { - expr += ')'; - } - return expr; + std::string AssertionResult::getExpression() const { + // Possibly overallocating by 3 characters should be basically free + std::string expr; + expr.reserve(m_info.capturedExpression.size() + 3); + if (isFalseTest(m_info.resultDisposition)) { + expr += "!("; } + expr += m_info.capturedExpression; + if (isFalseTest(m_info.resultDisposition)) { + expr += ')'; + } + return expr; + } - std::string AssertionResult::getExpressionInMacro() const { - if ( m_info.macroName.empty() ) { - return static_cast( m_info.capturedExpression ); - } - std::string expr; - expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); - expr += m_info.macroName; - expr += "( "; - expr += m_info.capturedExpression; - expr += " )"; - return expr; + std::string AssertionResult::getExpressionInMacro() const { + if (m_info.macroName.empty()) { + return static_cast(m_info.capturedExpression); } + std::string expr; + expr.reserve(m_info.macroName.size() + m_info.capturedExpression.size() + 4); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + return expr; + } - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); - } + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } - std::string AssertionResult::getExpandedExpression() const { - std::string expr = m_resultData.reconstructExpression(); - return expr.empty() - ? getExpression() - : expr; - } + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } - StringRef AssertionResult::getMessage() const { - return m_resultData.message; - } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; - } + StringRef AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } - StringRef AssertionResult::getTestMacroName() const { - return m_info.macroName; - } + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } } // end namespace Catch - - #include namespace Catch { - namespace { - static bool enableBazelEnvSupport() { -#if defined( CATCH_CONFIG_BAZEL_SUPPORT ) - return true; + namespace { + static bool enableBazelEnvSupport() { +#if defined(CATCH_CONFIG_BAZEL_SUPPORT) + return true; #else - return Detail::getEnv( "BAZEL_TEST" ) != nullptr; + return Detail::getEnv("BAZEL_TEST") != nullptr; #endif - } + } - struct bazelShardingOptions { - unsigned int shardIndex, shardCount; - std::string shardFilePath; + struct bazelShardingOptions { + unsigned int shardIndex, shardCount; + std::string shardFilePath; + }; + + static Optional readBazelShardingOptions() { + const auto bazelShardIndex = Detail::getEnv("TEST_SHARD_INDEX"); + const auto bazelShardTotal = Detail::getEnv("TEST_TOTAL_SHARDS"); + const auto bazelShardInfoFile = Detail::getEnv("TEST_SHARD_STATUS_FILE"); + + const bool has_all = bazelShardIndex && bazelShardTotal && bazelShardInfoFile; + if (!has_all) { + // We provide nice warning message if the input is + // misconfigured. + auto warn = [](const char *env_var) { + Catch::cerr() + << "Warning: Bazel shard configuration is missing '" + << env_var << "'. Shard configuration is skipped.\n"; }; - - static Optional readBazelShardingOptions() { - const auto bazelShardIndex = Detail::getEnv( "TEST_SHARD_INDEX" ); - const auto bazelShardTotal = Detail::getEnv( "TEST_TOTAL_SHARDS" ); - const auto bazelShardInfoFile = Detail::getEnv( "TEST_SHARD_STATUS_FILE" ); - - - const bool has_all = - bazelShardIndex && bazelShardTotal && bazelShardInfoFile; - if ( !has_all ) { - // We provide nice warning message if the input is - // misconfigured. - auto warn = []( const char* env_var ) { - Catch::cerr() - << "Warning: Bazel shard configuration is missing '" - << env_var << "'. Shard configuration is skipped.\n"; - }; - if ( !bazelShardIndex ) { - warn( "TEST_SHARD_INDEX" ); - } - if ( !bazelShardTotal ) { - warn( "TEST_TOTAL_SHARDS" ); - } - if ( !bazelShardInfoFile ) { - warn( "TEST_SHARD_STATUS_FILE" ); - } - return {}; - } - - auto shardIndex = parseUInt( bazelShardIndex ); - if ( !shardIndex ) { - Catch::cerr() - << "Warning: could not parse 'TEST_SHARD_INDEX' ('" << bazelShardIndex - << "') as unsigned int.\n"; - return {}; - } - auto shardTotal = parseUInt( bazelShardTotal ); - if ( !shardTotal ) { - Catch::cerr() - << "Warning: could not parse 'TEST_TOTAL_SHARD' ('" - << bazelShardTotal << "') as unsigned int.\n"; - return {}; - } - - return bazelShardingOptions{ - *shardIndex, *shardTotal, bazelShardInfoFile }; - + if (!bazelShardIndex) { + warn("TEST_SHARD_INDEX"); } - } // end namespace + if (!bazelShardTotal) { + warn("TEST_TOTAL_SHARDS"); + } + if (!bazelShardInfoFile) { + warn("TEST_SHARD_STATUS_FILE"); + } + return {}; + } + auto shardIndex = parseUInt(bazelShardIndex); + if (!shardIndex) { + Catch::cerr() + << "Warning: could not parse 'TEST_SHARD_INDEX' ('" << bazelShardIndex + << "') as unsigned int.\n"; + return {}; + } + auto shardTotal = parseUInt(bazelShardTotal); + if (!shardTotal) { + Catch::cerr() + << "Warning: could not parse 'TEST_TOTAL_SHARD' ('" + << bazelShardTotal << "') as unsigned int.\n"; + return {}; + } - bool operator==( ProcessedReporterSpec const& lhs, - ProcessedReporterSpec const& rhs ) { - return lhs.name == rhs.name && - lhs.outputFilename == rhs.outputFilename && - lhs.colourMode == rhs.colourMode && - lhs.customOptions == rhs.customOptions; + return bazelShardingOptions{ + *shardIndex, *shardTotal, bazelShardInfoFile}; + } + } // end namespace + + bool operator==(ProcessedReporterSpec const &lhs, + ProcessedReporterSpec const &rhs) { + return lhs.name == rhs.name && lhs.outputFilename == rhs.outputFilename && lhs.colourMode == rhs.colourMode && lhs.customOptions == rhs.customOptions; + } + + bool operator==(PathFilter const &lhs, PathFilter const &rhs) { + return lhs.type == rhs.type && lhs.filter == rhs.filter; + } + + Config::Config(ConfigData const &data) + : m_data(data) { + // We need to trim filter specs to avoid trouble with superfluous + // whitespace (esp. important for bdd macros, as those are manually + // aligned with whitespace). + + for (auto &elem : m_data.testsOrTags) { + elem = trim(elem); } - bool operator==( PathFilter const& lhs, PathFilter const& rhs ) { - return lhs.type == rhs.type && lhs.filter == rhs.filter; - } - - Config::Config( ConfigData const& data ): - m_data( data ) { - // We need to trim filter specs to avoid trouble with superfluous - // whitespace (esp. important for bdd macros, as those are manually - // aligned with whitespace). - - for (auto& elem : m_data.testsOrTags) { - elem = trim(elem); - } - - // Insert the default reporter if user hasn't asked for a specific one - if ( m_data.reporterSpecifications.empty() ) { -#if defined( CATCH_CONFIG_DEFAULT_REPORTER ) - const auto default_spec = CATCH_CONFIG_DEFAULT_REPORTER; + // Insert the default reporter if user hasn't asked for a specific one + if (m_data.reporterSpecifications.empty()) { +#if defined(CATCH_CONFIG_DEFAULT_REPORTER) + const auto default_spec = CATCH_CONFIG_DEFAULT_REPORTER; #else - const auto default_spec = "console"; + const auto default_spec = "console"; #endif - auto parsed = parseReporterSpec(default_spec); - CATCH_ENFORCE( parsed, - "Cannot parse the provided default reporter spec: '" - << default_spec << '\'' ); - m_data.reporterSpecifications.push_back( std::move( *parsed ) ); - } - - // Reading bazel env vars can change some parts of the config data, - // so we have to process the bazel env before acting on the config. - if ( enableBazelEnvSupport() ) { - readBazelEnvVars(); - } - - // Bazel support can modify the test specs, so parsing has to happen - // after reading Bazel env vars. - TestSpecParser parser( ITagAliasRegistry::get() ); - if ( !m_data.testsOrTags.empty() ) { - m_hasTestFilters = true; - for ( auto const& testOrTags : m_data.testsOrTags ) { - parser.parse( testOrTags ); - } - } - m_testSpec = parser.testSpec(); - - - // We now fixup the reporter specs to handle default output spec, - // default colour spec, etc - bool defaultOutputUsed = false; - for ( auto const& reporterSpec : m_data.reporterSpecifications ) { - // We do the default-output check separately, while always - // using the default output below to make the code simpler - // and avoid superfluous copies. - if ( reporterSpec.outputFile().none() ) { - CATCH_ENFORCE( !defaultOutputUsed, - "Internal error: cannot use default output for " - "multiple reporters" ); - defaultOutputUsed = true; - } - - m_processedReporterSpecs.push_back( ProcessedReporterSpec{ - reporterSpec.name(), - reporterSpec.outputFile() ? *reporterSpec.outputFile() - : data.defaultOutputFilename, - reporterSpec.colourMode().valueOr( data.defaultColourMode ), - reporterSpec.customOptions() } ); - } + auto parsed = parseReporterSpec(default_spec); + CATCH_ENFORCE(parsed, + "Cannot parse the provided default reporter spec: '" + << default_spec << '\''); + m_data.reporterSpecifications.push_back(std::move(*parsed)); } - Config::~Config() = default; - - - bool Config::listTests() const { return m_data.listTests; } - bool Config::listTags() const { return m_data.listTags; } - bool Config::listReporters() const { return m_data.listReporters; } - bool Config::listListeners() const { return m_data.listListeners; } - - std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } - std::vector const& Config::getPathFilters() const { return m_data.pathFilters; } - bool Config::useNewFilterBehaviour() const { return m_data.useNewPathFilteringBehaviour; } - - std::vector const& Config::getReporterSpecs() const { - return m_data.reporterSpecifications; + // Reading bazel env vars can change some parts of the config data, + // so we have to process the bazel env before acting on the config. + if (enableBazelEnvSupport()) { + readBazelEnvVars(); } - std::vector const& - Config::getProcessedReporterSpecs() const { - return m_processedReporterSpecs; + // Bazel support can modify the test specs, so parsing has to happen + // after reading Bazel env vars. + TestSpecParser parser(ITagAliasRegistry::get()); + if (!m_data.testsOrTags.empty()) { + m_hasTestFilters = true; + for (auto const &testOrTags : m_data.testsOrTags) { + parser.parse(testOrTags); + } + } + m_testSpec = parser.testSpec(); + + // We now fixup the reporter specs to handle default output spec, + // default colour spec, etc + bool defaultOutputUsed = false; + for (auto const &reporterSpec : m_data.reporterSpecifications) { + // We do the default-output check separately, while always + // using the default output below to make the code simpler + // and avoid superfluous copies. + if (reporterSpec.outputFile().none()) { + CATCH_ENFORCE(!defaultOutputUsed, + "Internal error: cannot use default output for " + "multiple reporters"); + defaultOutputUsed = true; + } + + m_processedReporterSpecs.push_back(ProcessedReporterSpec{ + reporterSpec.name(), + reporterSpec.outputFile() ? *reporterSpec.outputFile() + : data.defaultOutputFilename, + reporterSpec.colourMode().valueOr(data.defaultColourMode), + reporterSpec.customOptions()}); + } + } + + Config::~Config() = default; + + bool Config::listTests() const { + return m_data.listTests; + } + bool Config::listTags() const { + return m_data.listTags; + } + bool Config::listReporters() const { + return m_data.listReporters; + } + bool Config::listListeners() const { + return m_data.listListeners; + } + + std::vector const &Config::getTestsOrTags() const { + return m_data.testsOrTags; + } + std::vector const &Config::getPathFilters() const { + return m_data.pathFilters; + } + bool Config::useNewFilterBehaviour() const { + return m_data.useNewPathFilteringBehaviour; + } + + std::vector const &Config::getReporterSpecs() const { + return m_data.reporterSpecifications; + } + + std::vector const & + Config::getProcessedReporterSpecs() const { + return m_processedReporterSpecs; + } + + TestSpec const &Config::testSpec() const { + return m_testSpec; + } + bool Config::hasTestFilters() const { + return m_hasTestFilters; + } + + bool Config::showHelp() const { + return m_data.showHelp; + } + + std::string const &Config::getExitGuardFilePath() const { + return m_data.prematureExitGuardFilePath; + } + + // IConfig interface + bool Config::allowThrows() const { + return !m_data.noThrow; + } + StringRef Config::name() const { + return m_data.name.empty() ? m_data.processName : m_data.name; + } + bool Config::includeSuccessfulResults() const { + return m_data.showSuccessfulTests; + } + bool Config::warnAboutMissingAssertions() const { + return !!(m_data.warnings & WarnAbout::NoAssertions); + } + bool Config::warnAboutUnmatchedTestSpecs() const { + return !!(m_data.warnings & WarnAbout::UnmatchedTestSpec); + } + bool Config::warnAboutInfiniteGenerators() const { + return !!(m_data.warnings & WarnAbout::InfiniteGenerator); + } + bool Config::zeroTestsCountAsSuccess() const { + return m_data.allowZeroTests; + } + ShowDurations Config::showDurations() const { + return m_data.showDurations; + } + double Config::minDuration() const { + return m_data.minDuration; + } + TestRunOrder Config::runOrder() const { + return m_data.runOrder; + } + uint32_t Config::rngSeed() const { + return m_data.rngSeed; + } + unsigned int Config::shardCount() const { + return m_data.shardCount; + } + unsigned int Config::shardIndex() const { + return m_data.shardIndex; + } + ColourMode Config::defaultColourMode() const { + return m_data.defaultColourMode; + } + bool Config::shouldDebugBreak() const { + return m_data.shouldDebugBreak; + } + int Config::abortAfter() const { + return m_data.abortAfter; + } + bool Config::showInvisibles() const { + return m_data.showInvisibles; + } + Verbosity Config::verbosity() const { + return m_data.verbosity; + } + + bool Config::skipBenchmarks() const { + return m_data.skipBenchmarks; + } + bool Config::benchmarkNoAnalysis() const { + return m_data.benchmarkNoAnalysis; + } + unsigned int Config::benchmarkSamples() const { + return m_data.benchmarkSamples; + } + double Config::benchmarkConfidenceInterval() const { + return m_data.benchmarkConfidenceInterval; + } + unsigned int Config::benchmarkResamples() const { + return m_data.benchmarkResamples; + } + std::chrono::milliseconds Config::benchmarkWarmupTime() const { + return std::chrono::milliseconds(m_data.benchmarkWarmupTime); + } + + void Config::readBazelEnvVars() { + // Register a JUnit reporter for Bazel. Bazel sets an environment + // variable with the path to XML output. If this file is written to + // during test, Bazel will not generate a default XML output. + // This allows the XML output file to contain higher level of detail + // than what is possible otherwise. + const auto bazelOutputFile = Detail::getEnv("XML_OUTPUT_FILE"); + + if (bazelOutputFile) { + m_data.reporterSpecifications.push_back( + {"junit", std::string(bazelOutputFile), {}, {}}); } - TestSpec const& Config::testSpec() const { return m_testSpec; } - bool Config::hasTestFilters() const { return m_hasTestFilters; } - - bool Config::showHelp() const { return m_data.showHelp; } - - std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; } - - // IConfig interface - bool Config::allowThrows() const { return !m_data.noThrow; } - StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - bool Config::warnAboutMissingAssertions() const { - return !!( m_data.warnings & WarnAbout::NoAssertions ); + const auto bazelTestSpec = Detail::getEnv("TESTBRIDGE_TEST_ONLY"); + if (bazelTestSpec) { + // Presumably the test spec from environment should overwrite + // the one we got from CLI (if we got any) + m_data.testsOrTags.clear(); + m_data.testsOrTags.push_back(bazelTestSpec); } - bool Config::warnAboutUnmatchedTestSpecs() const { - return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec ); + + const auto bazelShardOptions = readBazelShardingOptions(); + if (bazelShardOptions) { + std::ofstream f(bazelShardOptions->shardFilePath, + std::ios_base::out | std::ios_base::trunc); + if (f.is_open()) { + f << ""; + m_data.shardIndex = bazelShardOptions->shardIndex; + m_data.shardCount = bazelShardOptions->shardCount; + } } - bool Config::warnAboutInfiniteGenerators() const { - return !!( m_data.warnings & WarnAbout::InfiniteGenerator ); + + const auto bazelExitGuardFile = Detail::getEnv("TEST_PREMATURE_EXIT_FILE"); + if (bazelExitGuardFile) { + m_data.prematureExitGuardFilePath = bazelExitGuardFile; } - bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; } - ShowDurations Config::showDurations() const { return m_data.showDurations; } - double Config::minDuration() const { return m_data.minDuration; } - TestRunOrder Config::runOrder() const { return m_data.runOrder; } - uint32_t Config::rngSeed() const { return m_data.rngSeed; } - unsigned int Config::shardCount() const { return m_data.shardCount; } - unsigned int Config::shardIndex() const { return m_data.shardIndex; } - ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; } - bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } - int Config::abortAfter() const { return m_data.abortAfter; } - bool Config::showInvisibles() const { return m_data.showInvisibles; } - Verbosity Config::verbosity() const { return m_data.verbosity; } - bool Config::skipBenchmarks() const { return m_data.skipBenchmarks; } - bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } - unsigned int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } - double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } - unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } - std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } - - void Config::readBazelEnvVars() { - // Register a JUnit reporter for Bazel. Bazel sets an environment - // variable with the path to XML output. If this file is written to - // during test, Bazel will not generate a default XML output. - // This allows the XML output file to contain higher level of detail - // than what is possible otherwise. - const auto bazelOutputFile = Detail::getEnv( "XML_OUTPUT_FILE" ); - - if ( bazelOutputFile ) { - m_data.reporterSpecifications.push_back( - { "junit", std::string( bazelOutputFile ), {}, {} } ); - } - - const auto bazelTestSpec = Detail::getEnv( "TESTBRIDGE_TEST_ONLY" ); - if ( bazelTestSpec ) { - // Presumably the test spec from environment should overwrite - // the one we got from CLI (if we got any) - m_data.testsOrTags.clear(); - m_data.testsOrTags.push_back( bazelTestSpec ); - } - - const auto bazelShardOptions = readBazelShardingOptions(); - if ( bazelShardOptions ) { - std::ofstream f( bazelShardOptions->shardFilePath, - std::ios_base::out | std::ios_base::trunc ); - if ( f.is_open() ) { - f << ""; - m_data.shardIndex = bazelShardOptions->shardIndex; - m_data.shardCount = bazelShardOptions->shardCount; - } - } - - const auto bazelExitGuardFile = Detail::getEnv( "TEST_PREMATURE_EXIT_FILE" ); - if (bazelExitGuardFile) { - m_data.prematureExitGuardFilePath = bazelExitGuardFile; - } - - const auto bazelRandomSeed = Detail::getEnv( "TEST_RANDOM_SEED" ); - if ( bazelRandomSeed ) { - auto parsedSeed = parseUInt( bazelRandomSeed, 0 ); - if ( !parsedSeed ) { - // Currently we handle issues with parsing other Bazel Env - // options by warning and ignoring the issue. So we do the - // same for random seed option. - Catch::cerr() - << "Warning: could not parse 'TEST_RANDOM_SEED' ('" - << bazelRandomSeed << "') as proper seed.\n"; - } else { - m_data.rngSeed = *parsedSeed; - } - } + const auto bazelRandomSeed = Detail::getEnv("TEST_RANDOM_SEED"); + if (bazelRandomSeed) { + auto parsedSeed = parseUInt(bazelRandomSeed, 0); + if (!parsedSeed) { + // Currently we handle issues with parsing other Bazel Env + // options by warning and ignoring the issue. So we do the + // same for random seed option. + Catch::cerr() + << "Warning: could not parse 'TEST_RANDOM_SEED' ('" + << bazelRandomSeed << "') as proper seed.\n"; + } else { + m_data.rngSeed = *parsedSeed; + } } + } } // end namespace Catch - - - - namespace Catch { - std::uint32_t getSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } -} - - + std::uint32_t getSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} // namespace Catch #include #include namespace Catch { - //////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + ScopedMessage::ScopedMessage(MessageBuilder &&builder) + : m_messageId(builder.m_info.sequence) { + MessageInfo info(CATCH_MOVE(builder.m_info)); + info.message = builder.m_stream.str(); + IResultCapture::pushScopedMessage(CATCH_MOVE(info)); + } - ScopedMessage::ScopedMessage( MessageBuilder&& builder ): - m_messageId( builder.m_info.sequence ) { - MessageInfo info( CATCH_MOVE( builder.m_info ) ); - info.message = builder.m_stream.str(); - IResultCapture::pushScopedMessage( CATCH_MOVE( info ) ); + ScopedMessage::ScopedMessage(ScopedMessage &&old) noexcept + : m_messageId(old.m_messageId) { + old.m_moved = true; + } + + ScopedMessage::~ScopedMessage() { + if (!m_moved) { IResultCapture::popScopedMessage(m_messageId); } + } + + Capturer::Capturer(StringRef macroName, + SourceLineInfo const &lineInfo, + ResultWas::OfType resultType, + StringRef names, + bool isScoped) + : m_isScoped(isScoped) { + auto trimmed = [&](size_t start, size_t end) { + while (names[start] == ',' || isspace(static_cast(names[start]))) { + ++start; + } + while (names[end] == ',' || isspace(static_cast(names[end]))) { + --end; + } + return names.substr(start, end - start + 1); + }; + auto skipq = [&](size_t start, char quote) { + for (auto i = start + 1; i < names.size(); ++i) { + if (names[i] == quote) + return i; + if (names[i] == '\\') + ++i; + } + CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); + }; + + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context + // case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': + // case '>': + openings.pop(); + break; + case '"': + case '\'': + pos = skipq(pos, c); + break; + case ',': + if (start != pos && openings.empty()) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message += trimmed(start, pos); + m_messages.back().message += " := "_sr; + start = pos; + } + break; + default:; // noop + } } - - ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept: - m_messageId( old.m_messageId ) { - old.m_moved = true; + assert(openings.empty() && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message += trimmed(start, names.size() - 1); + m_messages.back().message += " := "_sr; + } + Capturer::~Capturer() { + assert(m_captured == m_messages.size()); + if (m_isScoped) { + for (auto const &message : m_messages) { + IResultCapture::popScopedMessage(message.sequence); + } } + } - ScopedMessage::~ScopedMessage() { - if ( !m_moved ) { IResultCapture::popScopedMessage( m_messageId ); } - } - - - Capturer::Capturer( StringRef macroName, - SourceLineInfo const& lineInfo, - ResultWas::OfType resultType, - StringRef names, - bool isScoped): - m_isScoped(isScoped) { - auto trimmed = [&] (size_t start, size_t end) { - while (names[start] == ',' || isspace(static_cast(names[start]))) { - ++start; - } - while (names[end] == ',' || isspace(static_cast(names[end]))) { - --end; - } - return names.substr(start, end - start + 1); - }; - auto skipq = [&] (size_t start, char quote) { - for (auto i = start + 1; i < names.size() ; ++i) { - if (names[i] == quote) - return i; - if (names[i] == '\\') - ++i; - } - CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); - }; - - size_t start = 0; - std::stack openings; - for (size_t pos = 0; pos < names.size(); ++pos) { - char c = names[pos]; - switch (c) { - case '[': - case '{': - case '(': - // It is basically impossible to disambiguate between - // comparison and start of template args in this context -// case '<': - openings.push(c); - break; - case ']': - case '}': - case ')': -// case '>': - openings.pop(); - break; - case '"': - case '\'': - pos = skipq(pos, c); - break; - case ',': - if (start != pos && openings.empty()) { - m_messages.emplace_back(macroName, lineInfo, resultType); - m_messages.back().message += trimmed(start, pos); - m_messages.back().message += " := "_sr; - start = pos; - } - break; - default:; // noop - } - } - assert(openings.empty() && "Mismatched openings"); - m_messages.emplace_back(macroName, lineInfo, resultType); - m_messages.back().message += trimmed(start, names.size() - 1); - m_messages.back().message += " := "_sr; - } - Capturer::~Capturer() { - assert( m_captured == m_messages.size() ); - if ( m_isScoped ) { - for ( auto const& message : m_messages ) { - IResultCapture::popScopedMessage( message.sequence ); - } - } - } - - void Capturer::captureValue( size_t index, std::string const& value ) { - assert( index < m_messages.size() ); - m_messages[index].message += value; - if ( m_isScoped ) { - IResultCapture::pushScopedMessage( CATCH_MOVE( m_messages[index] ) ); - } else { - IResultCapture::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) ); - } - m_captured++; + void Capturer::captureValue(size_t index, std::string const &value) { + assert(index < m_messages.size()); + m_messages[index].message += value; + if (m_isScoped) { + IResultCapture::pushScopedMessage(CATCH_MOVE(m_messages[index])); + } else { + IResultCapture::addUnscopedMessage(CATCH_MOVE(m_messages[index])); } + m_captured++; + } } // end namespace Catch - - - #include namespace Catch { - namespace { + namespace { - class RegistryHub : public IRegistryHub, - public IMutableRegistryHub, - private Detail::NonCopyable { + class RegistryHub : public IRegistryHub, + public IMutableRegistryHub, + private Detail::NonCopyable { + public: // IRegistryHub + RegistryHub() = default; + ReporterRegistry const &getReporterRegistry() const override { + return m_reporterRegistry; + } + ITestCaseRegistry const &getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + IExceptionTranslatorRegistry const &getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + ITagAliasRegistry const &getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } + StartupExceptionRegistry const &getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } - public: // IRegistryHub - RegistryHub() = default; - ReporterRegistry const& getReporterRegistry() const override { - return m_reporterRegistry; - } - ITestCaseRegistry const& getTestCaseRegistry() const override { - return m_testCaseRegistry; - } - IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { - return m_exceptionTranslatorRegistry; - } - ITagAliasRegistry const& getTagAliasRegistry() const override { - return m_tagAliasRegistry; - } - StartupExceptionRegistry const& getStartupExceptionRegistry() const override { - return m_exceptionRegistry; - } - - public: // IMutableRegistryHub - void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override { - m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) ); - } - void registerListener( Detail::unique_ptr factory ) override { - m_reporterRegistry.registerListener( CATCH_MOVE(factory) ); - } - void registerTest( Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker ) override { - m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) ); - } - void registerTranslator( Detail::unique_ptr&& translator ) override { - m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) ); - } - void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { - m_tagAliasRegistry.add( alias, tag, lineInfo ); - } - void registerStartupException() noexcept override { + public: // IMutableRegistryHub + void registerReporter(std::string const &name, IReporterFactoryPtr factory) override { + m_reporterRegistry.registerReporter(name, CATCH_MOVE(factory)); + } + void registerListener(Detail::unique_ptr factory) override { + m_reporterRegistry.registerListener(CATCH_MOVE(factory)); + } + void registerTest(Detail::unique_ptr &&testInfo, Detail::unique_ptr &&invoker) override { + m_testCaseRegistry.registerTest(CATCH_MOVE(testInfo), CATCH_MOVE(invoker)); + } + void registerTranslator(Detail::unique_ptr &&translator) override { + m_exceptionTranslatorRegistry.registerTranslator(CATCH_MOVE(translator)); + } + void registerTagAlias(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) override { + m_tagAliasRegistry.add(alias, tag, lineInfo); + } + void registerStartupException() noexcept override { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - m_exceptionRegistry.add(std::current_exception()); + m_exceptionRegistry.add(std::current_exception()); #else - CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); #endif - } - IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { - return m_enumValuesRegistry; - } + } + IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() override { + return m_enumValuesRegistry; + } - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - TagAliasRegistry m_tagAliasRegistry; - StartupExceptionRegistry m_exceptionRegistry; - Detail::EnumValuesRegistry m_enumValuesRegistry; - }; - } + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; + }; + } // namespace - using RegistryHubSingleton = Singleton; - - IRegistryHub const& getRegistryHub() { - return RegistryHubSingleton::get(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return RegistryHubSingleton::getMutable(); - } - void cleanUp() { - cleanupSingletons(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); - } + using RegistryHubSingleton = Singleton; + IRegistryHub const &getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub &getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } } // end namespace Catch - - #include #include #include @@ -1203,3043 +1204,2839 @@ namespace Catch { namespace Catch { - namespace { - IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) { - auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config)); - CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\''); + namespace { + IEventListenerPtr createReporter(std::string const &reporterName, ReporterConfig &&config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config)); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\''); - return reporter; + return reporter; + } + + IEventListenerPtr prepareReporters(Config const *config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty() + && config->getProcessedReporterSpecs().size() == 1) { + auto const &spec = config->getProcessedReporterSpecs()[0]; + return createReporter( + spec.name, + ReporterConfig(config, + makeStream(spec.outputFilename), + spec.colourMode, + spec.customOptions)); + } + + auto multi = Detail::make_unique(config); + + auto const &listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const &listener : listeners) { + multi->addListener(listener->create(config)); + } + + for (auto const &reporterSpec : config->getProcessedReporterSpecs()) { + multi->addReporter(createReporter( + reporterSpec.name, + ReporterConfig(config, + makeStream(reporterSpec.outputFilename), + reporterSpec.colourMode, + reporterSpec.customOptions))); + } + + return multi; + } + + class TestGroup { + public: + explicit TestGroup(IEventListenerPtr &&reporter, Config const *config) + : m_reporter(reporter.get()), m_config{config}, m_context{config, CATCH_MOVE(reporter)} { + assert(m_config->testSpec().getInvalidSpecs().empty() && "Invalid test specs should be handled before running tests"); + + auto const &allTestCases = getAllTestCasesSorted(*m_config); + auto const &testSpec = m_config->testSpec(); + if (!testSpec.hasFilters()) { + for (auto const &test : allTestCases) { + if (!test.getTestCaseInfo().isHidden()) { + m_tests.emplace(&test); + } + } + } else { + m_matches = testSpec.matchesByFilter(allTestCases, *m_config); + for (auto const &match : m_matches) { + m_tests.insert(match.tests.begin(), + match.tests.end()); + } } - IEventListenerPtr prepareReporters(Config const* config) { - if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty() - && config->getProcessedReporterSpecs().size() == 1) { - auto const& spec = config->getProcessedReporterSpecs()[0]; - return createReporter( - spec.name, - ReporterConfig( config, - makeStream( spec.outputFilename ), - spec.colourMode, - spec.customOptions ) ); - } + m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex()); + } - auto multi = Detail::make_unique(config); - - auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); - for (auto const& listener : listeners) { - multi->addListener(listener->create(config)); - } - - for ( auto const& reporterSpec : config->getProcessedReporterSpecs() ) { - multi->addReporter( createReporter( - reporterSpec.name, - ReporterConfig( config, - makeStream( reporterSpec.outputFilename ), - reporterSpec.colourMode, - reporterSpec.customOptions ) ) ); - } - - return multi; + Totals execute() { + Totals totals; + for (auto const &testCase : m_tests) { + if (!m_context.aborting()) + totals += m_context.runTest(*testCase); + else + m_reporter->skipTest(testCase->getTestCaseInfo()); } - class TestGroup { - public: - explicit TestGroup(IEventListenerPtr&& reporter, Config const* config): - m_reporter(reporter.get()), - m_config{config}, - m_context{config, CATCH_MOVE(reporter)} { - - assert( m_config->testSpec().getInvalidSpecs().empty() && - "Invalid test specs should be handled before running tests" ); - - auto const& allTestCases = getAllTestCasesSorted(*m_config); - auto const& testSpec = m_config->testSpec(); - if ( !testSpec.hasFilters() ) { - for ( auto const& test : allTestCases ) { - if ( !test.getTestCaseInfo().isHidden() ) { - m_tests.emplace( &test ); - } - } - } else { - m_matches = - testSpec.matchesByFilter( allTestCases, *m_config ); - for ( auto const& match : m_matches ) { - m_tests.insert( match.tests.begin(), - match.tests.end() ); - } - } - - m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex()); - } - - Totals execute() { - Totals totals; - for (auto const& testCase : m_tests) { - if (!m_context.aborting()) - totals += m_context.runTest(*testCase); - else - m_reporter->skipTest(testCase->getTestCaseInfo()); - } - - for (auto const& match : m_matches) { - if (match.tests.empty()) { - m_unmatchedTestSpecs = true; - m_reporter->noMatchingTestCases( match.name ); - } - } - - return totals; - } - - bool hadUnmatchedTestSpecs() const { - return m_unmatchedTestSpecs; - } - - - private: - IEventListener* m_reporter; - Config const* m_config; - RunContext m_context; - std::set m_tests; - TestSpec::Matches m_matches; - bool m_unmatchedTestSpecs = false; - }; - - void applyFilenamesAsTags() { - for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) { - testInfo->addFilenameTag(); - } + for (auto const &match : m_matches) { + if (match.tests.empty()) { + m_unmatchedTestSpecs = true; + m_reporter->noMatchingTestCases(match.name); + } } - // Creates empty file at path. The path must be writable, we do not - // try to create directories in path because that's hard in C++14. - void setUpGuardFile( std::string const& guardFilePath ) { - if ( !guardFilePath.empty() ) { -#if defined( _MSC_VER ) - std::FILE* file = nullptr; - if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) { - char msgBuffer[100]; - const auto err = errno; - std::string errMsg; - if ( !strerror_s( msgBuffer, err ) ) { - errMsg = msgBuffer; - } else { - errMsg = "Could not translate errno to a string"; - } + return totals; + } + + bool hadUnmatchedTestSpecs() const { + return m_unmatchedTestSpecs; + } + + private: + IEventListener *m_reporter; + Config const *m_config; + RunContext m_context; + std::set m_tests; + TestSpec::Matches m_matches; + bool m_unmatchedTestSpecs = false; + }; + + void applyFilenamesAsTags() { + for (auto const &testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) { + testInfo->addFilenameTag(); + } + } + + // Creates empty file at path. The path must be writable, we do not + // try to create directories in path because that's hard in C++14. + void setUpGuardFile(std::string const &guardFilePath) { + if (!guardFilePath.empty()) { +#if defined(_MSC_VER) + std::FILE *file = nullptr; + if (fopen_s(&file, guardFilePath.c_str(), "w")) { + char msgBuffer[100]; + const auto err = errno; + std::string errMsg; + if (!strerror_s(msgBuffer, err)) { + errMsg = msgBuffer; + } else { + errMsg = "Could not translate errno to a string"; + } #else - std::FILE* file = std::fopen( guardFilePath.c_str(), "w" ); - if ( !file ) { - const auto err = errno; - const char* errMsg = std::strerror( err ); + std::FILE *file = std::fopen(guardFilePath.c_str(), "w"); + if (!file) { + const auto err = errno; + const char *errMsg = std::strerror(err); #endif - CATCH_RUNTIME_ERROR( "Could not open the exit guard file '" - << guardFilePath << "' because '" - << errMsg << "' (" << err << ')' ); - } - const int ret = std::fclose( file ); - CATCH_ENFORCE( - ret == 0, - "Error when closing the exit guard file: " << ret ); - } + CATCH_RUNTIME_ERROR("Could not open the exit guard file '" + << guardFilePath << "' because '" + << errMsg << "' (" << err << ')'); } + const int ret = std::fclose(file); + CATCH_ENFORCE( + ret == 0, + "Error when closing the exit guard file: " << ret); + } + } - // Removes file at path. Assuming we created it in setUpGuardFile. - void tearDownGuardFile( std::string const& guardFilePath ) { - if ( !guardFilePath.empty() ) { - const int ret = std::remove( guardFilePath.c_str() ); - CATCH_ENFORCE( - ret == 0, - "Error when removing the exit guard file: " << ret ); - } - } + // Removes file at path. Assuming we created it in setUpGuardFile. + void tearDownGuardFile(std::string const &guardFilePath) { + if (!guardFilePath.empty()) { + const int ret = std::remove(guardFilePath.c_str()); + CATCH_ENFORCE( + ret == 0, + "Error when removing the exit guard file: " << ret); + } + } - } // anon namespace + } // namespace - Session::Session() { - static bool alreadyInstantiated = false; - if( alreadyInstantiated ) { - CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } - CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } - } + Session::Session() { + static bool alreadyInstantiated = false; + if (alreadyInstantiated) { + CATCH_TRY { + CATCH_INTERNAL_ERROR("Only one instance of Catch::Session can ever be used"); + } + CATCH_CATCH_ALL { + getMutableRegistryHub().registerStartupException(); + } + } - // There cannot be exceptions at startup in no-exception mode. + // There cannot be exceptions at startup in no-exception mode. #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); - if ( !exceptions.empty() ) { - config(); - getCurrentMutableContext().setConfig(m_config.get()); + const auto &exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if (!exceptions.empty()) { + config(); + getCurrentMutableContext().setConfig(m_config.get()); - m_startupExceptions = true; - auto errStream = makeStream( "%stderr" ); - auto colourImpl = makeColourImpl( - ColourMode::PlatformDefault, errStream.get() ); - auto guard = colourImpl->guardColour( Colour::Red ); - errStream->stream() << "Errors occurred during startup!" << '\n'; - // iterate over all exceptions and notify user - for ( const auto& ex_ptr : exceptions ) { - try { - std::rethrow_exception(ex_ptr); - } catch ( std::exception const& ex ) { - errStream->stream() << TextFlow::Column( ex.what() ).indent(2) << '\n'; - } - } + m_startupExceptions = true; + auto errStream = makeStream("%stderr"); + auto colourImpl = makeColourImpl( + ColourMode::PlatformDefault, errStream.get()); + auto guard = colourImpl->guardColour(Colour::Red); + errStream->stream() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for (const auto &ex_ptr : exceptions) { + try { + std::rethrow_exception(ex_ptr); + } catch (std::exception const &ex) { + errStream->stream() << TextFlow::Column(ex.what()).indent(2) << '\n'; } + } + } #endif - alreadyInstantiated = true; - m_cli = makeCommandLineParser( m_configData ); - } - Session::~Session() { - Catch::cleanUp(); + alreadyInstantiated = true; + m_cli = makeCommandLineParser(m_configData); + } + Session::~Session() { + Catch::cleanUp(); + } + + void Session::showHelp() const { + Catch::cout() + << "\nCatch2 v" << libraryVersion() << '\n' + << m_cli << '\n' + << "For more detailed usage please see the project docs\n\n" + << std::flush; + } + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch2\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' + << std::flush; + } + + int Session::applyCommandLine(int argc, char const *const *argv) { + if (m_startupExceptions) { return UnspecifiedErrorExitCode; } + + auto result = m_cli.parse(Clara::Args(argc, argv)); + + if (!result) { + config(); + getCurrentMutableContext().setConfig(m_config.get()); + auto errStream = makeStream("%stderr"); + auto colour = makeColourImpl(ColourMode::PlatformDefault, errStream.get()); + + errStream->stream() + << colour->guardColour(Colour::Red) + << "\nError(s) in input:\n" + << TextFlow::Column(result.errorMessage()).indent(2) + << "\n\n"; + errStream->stream() << "Run with -? for usage\n\n" + << std::flush; + return UnspecifiedErrorExitCode; } - void Session::showHelp() const { - Catch::cout() - << "\nCatch2 v" << libraryVersion() << '\n' - << m_cli << '\n' - << "For more detailed usage please see the project docs\n\n" << std::flush; - } - void Session::libIdentify() { - Catch::cout() - << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" - << std::left << std::setw(16) << "category: " << "testframework\n" - << std::left << std::setw(16) << "framework: " << "Catch2\n" - << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush; - } + if (m_configData.showHelp) + showHelp(); + if (m_configData.libIdentify) + libIdentify(); - int Session::applyCommandLine( int argc, char const * const * argv ) { - if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; } - - auto result = m_cli.parse( Clara::Args( argc, argv ) ); - - if( !result ) { - config(); - getCurrentMutableContext().setConfig(m_config.get()); - auto errStream = makeStream( "%stderr" ); - auto colour = makeColourImpl( ColourMode::PlatformDefault, errStream.get() ); - - errStream->stream() - << colour->guardColour( Colour::Red ) - << "\nError(s) in input:\n" - << TextFlow::Column( result.errorMessage() ).indent( 2 ) - << "\n\n"; - errStream->stream() << "Run with -? for usage\n\n" << std::flush; - return UnspecifiedErrorExitCode; - } - - if( m_configData.showHelp ) - showHelp(); - if( m_configData.libIdentify ) - libIdentify(); - - m_config.reset(); - return 0; - } + m_config.reset(); + return 0; + } #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) - int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + int Session::applyCommandLine(int argc, wchar_t const *const *argv) { + char **utf8Argv = new char *[argc]; - char **utf8Argv = new char *[ argc ]; + for (int i = 0; i < argc; ++i) { + int bufSize = WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr); - for ( int i = 0; i < argc; ++i ) { - int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr ); + utf8Argv[i] = new char[bufSize]; - utf8Argv[ i ] = new char[ bufSize ]; - - WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr ); - } - - int returnCode = applyCommandLine( argc, utf8Argv ); - - for ( int i = 0; i < argc; ++i ) - delete [] utf8Argv[ i ]; - - delete [] utf8Argv; - - return returnCode; + WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr); } + + int returnCode = applyCommandLine(argc, utf8Argv); + + for (int i = 0; i < argc; ++i) + delete[] utf8Argv[i]; + + delete[] utf8Argv; + + return returnCode; + } #endif - void Session::useConfigData( ConfigData const& configData ) { - m_configData = configData; - m_config.reset(); + void Session::useConfigData(ConfigData const &configData) { + m_configData = configData; + m_config.reset(); + } + + int Session::run() { + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeStart) != 0) { + Catch::cout() << "...waiting for enter/ return before starting\n" + << std::flush; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeExit) != 0) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' + << std::flush; + static_cast(std::getchar()); + } + return exitCode; + } + + Clara::Parser const &Session::cli() const { + return m_cli; + } + void Session::cli(Clara::Parser const &newParser) { + m_cli = newParser; + } + ConfigData &Session::configData() { + return m_configData; + } + Config &Session::config() { + if (!m_config) + m_config = Detail::make_unique(m_configData); + return *m_config; + } + + int Session::runInternal() { + if (m_startupExceptions) { return UnspecifiedErrorExitCode; } + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; } - int Session::run() { - if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { - Catch::cout() << "...waiting for enter/ return before starting\n" << std::flush; - static_cast(std::getchar()); + if (m_configData.shardIndex >= m_configData.shardCount) { + Catch::cerr() << "The shard count (" << m_configData.shardCount + << ") must be greater than the shard index (" + << m_configData.shardIndex << ")\n" + << std::flush; + return UnspecifiedErrorExitCode; + } + + CATCH_TRY { + config(); // Force config to be constructed + + // We need to retrieve potential Bazel config with the full Config + // constructor, so we have to create the guard file after it is created. + setUpGuardFile(m_config->getExitGuardFilePath()); + + seedRng(*m_config); + + if (m_configData.filenamesAsTags) { + applyFilenamesAsTags(); + } + + // Set up global config instance before we start calling into other functions + getCurrentMutableContext().setConfig(m_config.get()); + + // Create reporter(s) so we can route listings through them + auto reporter = prepareReporters(m_config.get()); + + auto const &invalidSpecs = m_config->testSpec().getInvalidSpecs(); + if (!invalidSpecs.empty()) { + for (auto const &spec : invalidSpecs) { + reporter->reportInvalidTestSpec(spec); } - int exitCode = runInternal(); + return InvalidTestSpecExitCode; + } - if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { - Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush; - static_cast(std::getchar()); - } - return exitCode; + // Handle list request + if (list(*reporter, *m_config)) { + return 0; + } + + TestGroup tests{CATCH_MOVE(reporter), m_config.get()}; + auto const totals = tests.execute(); + + // If we got here, running the tests finished normally-enough. + // They might've failed, but that would've been reported elsewhere. + tearDownGuardFile(m_config->getExitGuardFilePath()); + + if (tests.hadUnmatchedTestSpecs() + && m_config->warnAboutUnmatchedTestSpecs()) { + return UnmatchedTestSpecExitCode; + } + + if (totals.testCases.total() == 0 + && !m_config->zeroTestsCountAsSuccess()) { + return NoTestsRunExitCode; + } + + if (totals.testCases.total() > 0 && totals.testCases.total() == totals.testCases.skipped + && !m_config->zeroTestsCountAsSuccess()) { + return AllTestsSkippedExitCode; + } + + if (totals.assertions.failed) { return TestFailureExitCode; } + return 0; } - - Clara::Parser const& Session::cli() const { - return m_cli; - } - void Session::cli( Clara::Parser const& newParser ) { - m_cli = newParser; - } - ConfigData& Session::configData() { - return m_configData; - } - Config& Session::config() { - if( !m_config ) - m_config = Detail::make_unique( m_configData ); - return *m_config; - } - - int Session::runInternal() { - if ( m_startupExceptions ) { return UnspecifiedErrorExitCode; } - - if (m_configData.showHelp || m_configData.libIdentify) { - return 0; - } - - if ( m_configData.shardIndex >= m_configData.shardCount ) { - Catch::cerr() << "The shard count (" << m_configData.shardCount - << ") must be greater than the shard index (" - << m_configData.shardIndex << ")\n" - << std::flush; - return UnspecifiedErrorExitCode; - } - - CATCH_TRY { - config(); // Force config to be constructed - - // We need to retrieve potential Bazel config with the full Config - // constructor, so we have to create the guard file after it is created. - setUpGuardFile( m_config->getExitGuardFilePath() ); - - seedRng( *m_config ); - - if (m_configData.filenamesAsTags) { - applyFilenamesAsTags(); - } - - // Set up global config instance before we start calling into other functions - getCurrentMutableContext().setConfig(m_config.get()); - - // Create reporter(s) so we can route listings through them - auto reporter = prepareReporters(m_config.get()); - - auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs(); - if ( !invalidSpecs.empty() ) { - for ( auto const& spec : invalidSpecs ) { - reporter->reportInvalidTestSpec( spec ); - } - return InvalidTestSpecExitCode; - } - - - // Handle list request - if (list(*reporter, *m_config)) { - return 0; - } - - TestGroup tests { CATCH_MOVE(reporter), m_config.get() }; - auto const totals = tests.execute(); - - // If we got here, running the tests finished normally-enough. - // They might've failed, but that would've been reported elsewhere. - tearDownGuardFile( m_config->getExitGuardFilePath() ); - - if ( tests.hadUnmatchedTestSpecs() - && m_config->warnAboutUnmatchedTestSpecs() ) { - return UnmatchedTestSpecExitCode; - } - - if ( totals.testCases.total() == 0 - && !m_config->zeroTestsCountAsSuccess() ) { - return NoTestsRunExitCode; - } - - if ( totals.testCases.total() > 0 && - totals.testCases.total() == totals.testCases.skipped - && !m_config->zeroTestsCountAsSuccess() ) { - return AllTestsSkippedExitCode; - } - - if ( totals.assertions.failed ) { return TestFailureExitCode; } - return 0; - - } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << '\n' << std::flush; - return UnspecifiedErrorExitCode; - } -#endif + catch (std::exception &ex) { + Catch::cerr() << ex.what() << '\n' + << std::flush; + return UnspecifiedErrorExitCode; } +#endif + } } // end namespace Catch - - - namespace Catch { - RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { - CATCH_TRY { - getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); - } CATCH_CATCH_ALL { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } + RegistrarForTagAliases::RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); } + CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } -} - - +} // namespace Catch +#include #include #include -#include namespace Catch { - namespace { - using TCP_underlying_type = uint8_t; - static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type), - "The size of the TestCaseProperties is different from the assumed size"); + namespace { + using TCP_underlying_type = uint8_t; + static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type), + "The size of the TestCaseProperties is different from the assumed size"); - constexpr TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { - return static_cast( - static_cast(lhs) | static_cast(rhs) - ); - } - - constexpr TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { - lhs = static_cast( - static_cast(lhs) | static_cast(rhs) - ); - return lhs; - } - - constexpr TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { - return static_cast( - static_cast(lhs) & static_cast(rhs) - ); - } - - constexpr bool applies(TestCaseProperties tcp) { - static_assert(static_cast(TestCaseProperties::None) == 0, - "TestCaseProperties::None must be equal to 0"); - return tcp != TestCaseProperties::None; - } - - TestCaseProperties parseSpecialTag( StringRef tag ) { - if( !tag.empty() && tag[0] == '.' ) - return TestCaseProperties::IsHidden; - else if( tag == "!throws"_sr ) - return TestCaseProperties::Throws; - else if( tag == "!shouldfail"_sr ) - return TestCaseProperties::ShouldFail; - else if( tag == "!mayfail"_sr ) - return TestCaseProperties::MayFail; - else if( tag == "!nonportable"_sr ) - return TestCaseProperties::NonPortable; - else if( tag == "!benchmark"_sr ) - return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden; - else - return TestCaseProperties::None; - } - bool isReservedTag( StringRef tag ) { - return parseSpecialTag( tag ) == TestCaseProperties::None - && tag.size() > 0 - && !std::isalnum( static_cast(tag[0]) ); - } - void enforceNotReservedTag( StringRef tag, SourceLineInfo const& _lineInfo ) { - CATCH_ENFORCE( !isReservedTag(tag), - "Tag name: [" << tag << "] is not allowed.\n" - << "Tag names starting with non alphanumeric characters are reserved\n" - << _lineInfo ); - } - - std::string makeDefaultName() { - static size_t counter = 0; - return "Anonymous test case " + std::to_string(++counter); - } - - constexpr StringRef extractFilenamePart(StringRef filename) { - size_t lastDot = filename.size(); - while (lastDot > 0 && filename[lastDot - 1] != '.') { - --lastDot; - } - // In theory we could have filename without any extension in it - if ( lastDot == 0 ) { return StringRef(); } - - --lastDot; - size_t nameStart = lastDot; - while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') { - --nameStart; - } - - return filename.substr(nameStart, lastDot - nameStart); - } - - // Returns the upper bound on size of extra tags ([#file]+[.]) - constexpr size_t sizeOfExtraTags(StringRef filepath) { - // [.] is 3, [#] is another 3 - const size_t extras = 3 + 3; - return extractFilenamePart(filepath).size() + extras; - } - } // end unnamed namespace - - bool operator<( Tag const& lhs, Tag const& rhs ) { - Detail::CaseInsensitiveLess cmp; - return cmp( lhs.original, rhs.original ); - } - bool operator==( Tag const& lhs, Tag const& rhs ) { - Detail::CaseInsensitiveEqualTo cmp; - return cmp( lhs.original, rhs.original ); + constexpr TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { + return static_cast( + static_cast(lhs) | static_cast(rhs)); } - Detail::unique_ptr - makeTestCaseInfo(StringRef _className, - NameAndTags const& nameAndTags, - SourceLineInfo const& _lineInfo ) { - return Detail::make_unique(_className, nameAndTags, _lineInfo); + constexpr TestCaseProperties &operator|=(TestCaseProperties &lhs, TestCaseProperties rhs) { + lhs = static_cast( + static_cast(lhs) | static_cast(rhs)); + return lhs; } - TestCaseInfo::TestCaseInfo(StringRef _className, - NameAndTags const& _nameAndTags, - SourceLineInfo const& _lineInfo): - name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ), - className( _className ), - lineInfo( _lineInfo ) - { - StringRef originalTags = _nameAndTags.tags; - // We need to reserve enough space to store all of the tags - // (including optional hidden tag and filename tag) - auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file); - backingTags.reserve(requiredSize); + constexpr TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { + return static_cast( + static_cast(lhs) & static_cast(rhs)); + } - // We cannot copy the tags directly, as we need to normalize - // some tags, so that [.foo] is copied as [.][foo]. - size_t tagStart = 0; - size_t tagEnd = 0; - bool inTag = false; - for (size_t idx = 0; idx < originalTags.size(); ++idx) { - auto c = originalTags[idx]; - if (c == '[') { - CATCH_ENFORCE( - !inTag, - "Found '[' inside a tag while registering test case '" - << _nameAndTags.name << "' at " << _lineInfo ); + constexpr bool applies(TestCaseProperties tcp) { + static_assert(static_cast(TestCaseProperties::None) == 0, + "TestCaseProperties::None must be equal to 0"); + return tcp != TestCaseProperties::None; + } - inTag = true; - tagStart = idx; - } - if (c == ']') { - CATCH_ENFORCE( - inTag, - "Found unmatched ']' while registering test case '" - << _nameAndTags.name << "' at " << _lineInfo ); + TestCaseProperties parseSpecialTag(StringRef tag) { + if (!tag.empty() && tag[0] == '.') + return TestCaseProperties::IsHidden; + else if (tag == "!throws"_sr) + return TestCaseProperties::Throws; + else if (tag == "!shouldfail"_sr) + return TestCaseProperties::ShouldFail; + else if (tag == "!mayfail"_sr) + return TestCaseProperties::MayFail; + else if (tag == "!nonportable"_sr) + return TestCaseProperties::NonPortable; + else if (tag == "!benchmark"_sr) + return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden; + else + return TestCaseProperties::None; + } + bool isReservedTag(StringRef tag) { + return parseSpecialTag(tag) == TestCaseProperties::None + && tag.size() > 0 + && !std::isalnum(static_cast(tag[0])); + } + void enforceNotReservedTag(StringRef tag, SourceLineInfo const &_lineInfo) { + CATCH_ENFORCE(!isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo); + } - inTag = false; - tagEnd = idx; - assert(tagStart < tagEnd); + std::string makeDefaultName() { + static size_t counter = 0; + return "Anonymous test case " + std::to_string(++counter); + } - // We need to check the tag for special meanings, copy - // 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(), - "Found an empty tag while registering test case '" - << _nameAndTags.name << "' at " - << _lineInfo ); + constexpr StringRef extractFilenamePart(StringRef filename) { + size_t lastDot = filename.size(); + while (lastDot > 0 && filename[lastDot - 1] != '.') { + --lastDot; + } + // In theory we could have filename without any extension in it + if (lastDot == 0) { return StringRef(); } - enforceNotReservedTag(tagStr, lineInfo); - properties |= parseSpecialTag(tagStr); - // When copying a tag to the backing storage, we need to - // check if it is a merged hide tag, such as [.foo], and - // if it is, we need to handle it as if it was [foo]. - if (tagStr.size() > 1 && tagStr[0] == '.') { - tagStr = tagStr.substr(1, tagStr.size() - 1); - } - // We skip over dealing with the [.] tag, as we will add - // it later unconditionally and then sort and unique all - // the tags. - internalAppendTag(tagStr); - } + --lastDot; + size_t nameStart = lastDot; + while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') { + --nameStart; + } + + return filename.substr(nameStart, lastDot - nameStart); + } + + // Returns the upper bound on size of extra tags ([#file]+[.]) + constexpr size_t sizeOfExtraTags(StringRef filepath) { + // [.] is 3, [#] is another 3 + const size_t extras = 3 + 3; + return extractFilenamePart(filepath).size() + extras; + } + } // end unnamed namespace + + bool operator<(Tag const &lhs, Tag const &rhs) { + Detail::CaseInsensitiveLess cmp; + return cmp(lhs.original, rhs.original); + } + bool operator==(Tag const &lhs, Tag const &rhs) { + Detail::CaseInsensitiveEqualTo cmp; + return cmp(lhs.original, rhs.original); + } + + Detail::unique_ptr + makeTestCaseInfo(StringRef _className, + NameAndTags const &nameAndTags, + SourceLineInfo const &_lineInfo) { + return Detail::make_unique(_className, nameAndTags, _lineInfo); + } + + TestCaseInfo::TestCaseInfo(StringRef _className, + NameAndTags const &_nameAndTags, + SourceLineInfo const &_lineInfo) + : name(_nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name), className(_className), lineInfo(_lineInfo) { + StringRef originalTags = _nameAndTags.tags; + // We need to reserve enough space to store all of the tags + // (including optional hidden tag and filename tag) + auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file); + backingTags.reserve(requiredSize); + + // We cannot copy the tags directly, as we need to normalize + // some tags, so that [.foo] is copied as [.][foo]. + size_t tagStart = 0; + size_t tagEnd = 0; + bool inTag = false; + for (size_t idx = 0; idx < originalTags.size(); ++idx) { + auto c = originalTags[idx]; + if (c == '[') { + CATCH_ENFORCE( + !inTag, + "Found '[' inside a tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo); + + inTag = true; + tagStart = idx; + } + if (c == ']') { + CATCH_ENFORCE( + inTag, + "Found unmatched ']' while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo); + + inTag = false; + tagEnd = idx; + assert(tagStart < tagEnd); + + // We need to check the tag for special meanings, copy + // 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(), + "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 + // check if it is a merged hide tag, such as [.foo], and + // if it is, we need to handle it as if it was [foo]. + if (tagStr.size() > 1 && tagStr[0] == '.') { + tagStr = tagStr.substr(1, tagStr.size() - 1); } - CATCH_ENFORCE( !inTag, - "Found an unclosed tag while registering test case '" - << _nameAndTags.name << "' at " << _lineInfo ); + // We skip over dealing with the [.] tag, as we will add + // it later unconditionally and then sort and unique all + // the tags. + internalAppendTag(tagStr); + } + } + CATCH_ENFORCE(!inTag, + "Found an unclosed tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo); - - // Add [.] if relevant - if (isHidden()) { - internalAppendTag("."_sr); - } - - // Sort and prepare tags - std::sort(begin(tags), end(tags)); - tags.erase(std::unique(begin(tags), end(tags)), - end(tags)); + // Add [.] if relevant + if (isHidden()) { + internalAppendTag("."_sr); } - bool TestCaseInfo::isHidden() const { - return applies( properties & TestCaseProperties::IsHidden ); + // Sort and prepare tags + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), + end(tags)); + } + + bool TestCaseInfo::isHidden() const { + return applies(properties & TestCaseProperties::IsHidden); + } + bool TestCaseInfo::throws() const { + return applies(properties & TestCaseProperties::Throws); + } + bool TestCaseInfo::okToFail() const { + return applies(properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail)); + } + bool TestCaseInfo::expectedToFail() const { + return applies(properties & (TestCaseProperties::ShouldFail)); + } + + void TestCaseInfo::addFilenameTag() { + std::string combined("#"); + combined += extractFilenamePart(lineInfo.file); + internalAppendTag(combined); + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto &tag : tags) { + full_size += tag.original.size(); } - bool TestCaseInfo::throws() const { - return applies( properties & TestCaseProperties::Throws ); - } - bool TestCaseInfo::okToFail() const { - return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) ); - } - bool TestCaseInfo::expectedToFail() const { - return applies( properties & (TestCaseProperties::ShouldFail) ); + ret.reserve(full_size); + for (const auto &tag : tags) { + ret.push_back('['); + ret += tag.original; + ret.push_back(']'); } - void TestCaseInfo::addFilenameTag() { - std::string combined("#"); - combined += extractFilenamePart(lineInfo.file); - internalAppendTag(combined); - } + return ret; + } - std::string TestCaseInfo::tagsAsString() const { - std::string ret; - // '[' and ']' per tag - std::size_t full_size = 2 * tags.size(); - for (const auto& tag : tags) { - full_size += tag.original.size(); - } - ret.reserve(full_size); - for (const auto& tag : tags) { - ret.push_back('['); - ret += tag.original; - ret.push_back(']'); - } + void TestCaseInfo::internalAppendTag(StringRef tagStr) { + backingTags += '['; + const auto backingStart = backingTags.size(); + backingTags += tagStr; + const auto backingEnd = backingTags.size(); + backingTags += ']'; + tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart)); + } - return ret; + bool operator<(TestCaseInfo const &lhs, TestCaseInfo const &rhs) { + // We want to avoid redoing the string comparisons multiple times, + // so we store the result of a three-way comparison before using + // it in the actual comparison logic. + const auto cmpName = lhs.name.compare(rhs.name); + if (cmpName != 0) { + return cmpName < 0; } - - void TestCaseInfo::internalAppendTag(StringRef tagStr) { - backingTags += '['; - const auto backingStart = backingTags.size(); - backingTags += tagStr; - const auto backingEnd = backingTags.size(); - backingTags += ']'; - tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart)); - } - - bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) { - // We want to avoid redoing the string comparisons multiple times, - // so we store the result of a three-way comparison before using - // it in the actual comparison logic. - const auto cmpName = lhs.name.compare( rhs.name ); - if ( cmpName != 0 ) { - return cmpName < 0; - } - const auto cmpClassName = lhs.className.compare( rhs.className ); - if ( cmpClassName != 0 ) { - return cmpClassName < 0; - } - return lhs.tags < rhs.tags; + const auto cmpClassName = lhs.className.compare(rhs.className); + if (cmpClassName != 0) { + return cmpClassName < 0; } + return lhs.tags < rhs.tags; + } } // end namespace Catch - - #include +#include #include #include -#include namespace Catch { - TestSpec::Pattern::Pattern( std::string const& name ) - : m_name( name ) - {} + TestSpec::Pattern::Pattern(std::string const &name) + : m_name(name) {} - TestSpec::Pattern::~Pattern() = default; + TestSpec::Pattern::~Pattern() = default; - std::string const& TestSpec::Pattern::name() const { - return m_name; + std::string const &TestSpec::Pattern::name() const { + return m_name; + } + + TestSpec::NamePattern::NamePattern(std::string const &name, std::string const &filterString) + : Pattern(filterString) + , m_wildcardPattern(toLower(name), CaseSensitive::No) {} + + bool TestSpec::NamePattern::matches(TestCaseInfo const &testCase) const { + return m_wildcardPattern.matches(testCase.name); + } + + void TestSpec::NamePattern::serializeTo(std::ostream &out) const { + out << '"' << name() << '"'; + } + + TestSpec::TagPattern::TagPattern(std::string const &tag, std::string const &filterString) + : Pattern(filterString) + , m_tag(tag) {} + + bool TestSpec::TagPattern::matches(TestCaseInfo const &testCase) const { + return std::find(begin(testCase.tags), + end(testCase.tags), + Tag(m_tag)) + != end(testCase.tags); + } + + void TestSpec::TagPattern::serializeTo(std::ostream &out) const { + out << name(); + } + + bool TestSpec::Filter::matches(TestCaseInfo const &testCase) const { + bool should_use = !testCase.isHidden(); + for (auto const &pattern : m_required) { + should_use = true; + if (!pattern->matches(testCase)) { + return false; + } } - - - TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString ) - : Pattern( filterString ) - , m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} - - bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( testCase.name ); + for (auto const &pattern : m_forbidden) { + if (pattern->matches(testCase)) { + return false; + } } + return should_use; + } - void TestSpec::NamePattern::serializeTo( std::ostream& out ) const { - out << '"' << name() << '"'; + void TestSpec::Filter::serializeTo(std::ostream &out) const { + bool first = true; + for (auto const &pattern : m_required) { + if (!first) { + out << ' '; + } + out << *pattern; + first = false; } - - - TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString ) - : Pattern( filterString ) - , m_tag( tag ) - {} - - bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { - return std::find( begin( testCase.tags ), - end( testCase.tags ), - Tag( m_tag ) ) != end( testCase.tags ); + for (auto const &pattern : m_forbidden) { + if (!first) { + out << ' '; + } + out << *pattern; + first = false; } + } - void TestSpec::TagPattern::serializeTo( std::ostream& out ) const { - out << name(); + std::string TestSpec::extractFilterName(Filter const &filter) { + Catch::ReusableStringStream sstr; + sstr << filter; + return sstr.str(); + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + + bool TestSpec::matches(TestCaseInfo const &testCase) const { + 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; + 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())) + currentMatches.emplace_back(&test); + matches.push_back( + FilterMatch{extractFilterName(filter), currentMatches}); } + return matches; + } - bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { - bool should_use = !testCase.isHidden(); - for (auto const& pattern : m_required) { - should_use = true; - if (!pattern->matches(testCase)) { - return false; - } - } - for (auto const& pattern : m_forbidden) { - if (pattern->matches(testCase)) { - return false; - } - } - return should_use; + const TestSpec::vectorStrings &TestSpec::getInvalidSpecs() const { + return m_invalidSpecs; + } + + void TestSpec::serializeTo(std::ostream &out) const { + bool first = true; + for (auto const &filter : m_filters) { + if (!first) { + out << ','; + } + out << filter; + first = false; } + } - void TestSpec::Filter::serializeTo( std::ostream& out ) const { - bool first = true; - for ( auto const& pattern : m_required ) { - if ( !first ) { - out << ' '; - } - out << *pattern; - first = false; - } - for ( auto const& pattern : m_forbidden ) { - if ( !first ) { - out << ' '; - } - out << *pattern; - first = false; - } - } - - - std::string TestSpec::extractFilterName( Filter const& filter ) { - Catch::ReusableStringStream sstr; - sstr << filter; - return sstr.str(); - } - - bool TestSpec::hasFilters() const { - return !m_filters.empty(); - } - - bool TestSpec::matches( TestCaseInfo const& testCase ) const { - 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; - 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() ) ) - currentMatches.emplace_back( &test ); - matches.push_back( - FilterMatch{ extractFilterName( filter ), currentMatches } ); - } - return matches; - } - - const TestSpec::vectorStrings& TestSpec::getInvalidSpecs() const { - return m_invalidSpecs; - } - - void TestSpec::serializeTo( std::ostream& out ) const { - bool first = true; - for ( auto const& filter : m_filters ) { - if ( !first ) { - out << ','; - } - out << filter; - first = false; - } - } - -} - - +} // namespace Catch #include namespace Catch { - namespace { - static auto getCurrentNanosecondsSinceEpoch() -> uint64_t { - return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); - } - } // end unnamed namespace - - void Timer::start() { - m_nanoseconds = getCurrentNanosecondsSinceEpoch(); - } - auto Timer::getElapsedNanoseconds() const -> uint64_t { - return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; - } - auto Timer::getElapsedMicroseconds() const -> uint64_t { - return getElapsedNanoseconds()/1000; - } - auto Timer::getElapsedMilliseconds() const -> unsigned int { - return static_cast(getElapsedMicroseconds()/1000); - } - auto Timer::getElapsedSeconds() const -> double { - return static_cast(getElapsedMicroseconds())/1000000.0; - } + namespace { + static auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); + } + } // end unnamed namespace + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds() / 1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds() / 1000); + } + auto Timer::getElapsedSeconds() const -> double { + return static_cast(getElapsedMicroseconds()) / 1000000.0; + } } // namespace Catch - - - #include namespace Catch { -namespace Detail { + namespace Detail { namespace { - const int hexThreshold = 255; + const int hexThreshold = 255; - struct Endianness { - enum Arch : uint8_t { - Big, - Little - }; - - static Arch which() { - int one = 1; - // If the lowest byte we read is non-zero, we can assume - // that little endian format is used. - auto value = *reinterpret_cast(&one); - return value ? Little : Big; - } + struct Endianness { + enum Arch : uint8_t { + Big, + Little }; - template - std::string fpToString(T value, int precision) { - if (Catch::isnan(value)) { - return "nan"; - } - - ReusableStringStream rss; - rss << std::setprecision(precision) - << std::fixed - << value; - std::string d = rss.str(); - std::size_t i = d.find_last_not_of('0'); - if (i != std::string::npos && i != d.size() - 1) { - if (d[i] == '.') - i++; - d = d.substr(0, i + 1); - } - return d; + static Arch which() { + int one = 1; + // If the lowest byte we read is non-zero, we can assume + // that little endian format is used. + auto value = *reinterpret_cast(&one); + return value ? Little : Big; } + }; + + template + std::string fpToString(T value, int precision) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision(precision) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of('0'); + if (i != std::string::npos && i != d.size() - 1) { + if (d[i] == '.') + i++; + d = d.substr(0, i + 1); + } + return d; + } } // end unnamed namespace - std::size_t catch_strnlen( const char* str, std::size_t n ) { - auto ret = std::char_traits::find( str, n, '\0' ); - if ( ret != nullptr ) { return static_cast( ret - str ); } - return n; + std::size_t catch_strnlen(const char *str, std::size_t n) { + auto ret = std::char_traits::find(str, n, '\0'); + if (ret != nullptr) { return static_cast(ret - str); } + return n; } std::string formatTimeT(std::time_t time) { #ifdef _MSC_VER - std::tm timeInfo = {}; - const auto err = gmtime_s( &timeInfo, &time ); - if ( err ) { - return "gmtime from provided timepoint has failed. This " - "happens e.g. with pre-1970 dates using Microsoft libc"; - } + std::tm timeInfo = {}; + const auto err = gmtime_s(&timeInfo, &time); + if (err) { + return "gmtime from provided timepoint has failed. This " + "happens e.g. with pre-1970 dates using Microsoft libc"; + } #else - std::tm* timeInfo = std::gmtime( &time ); + std::tm *timeInfo = std::gmtime(&time); #endif - auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" ); - char timeStamp[timeStampSize]; - const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER - std::strftime( timeStamp, timeStampSize, fmt, &timeInfo ); + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else - std::strftime( timeStamp, timeStampSize, fmt, timeInfo ); + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return std::string( timeStamp, timeStampSize - 1 ); + return std::string(timeStamp, timeStampSize - 1); } std::string convertIntoString(StringRef string, bool escapeInvisibles) { - std::string ret; - // This is enough for the "don't escape invisibles" case, and a good - // lower bound on the "escape invisibles" case. - ret.reserve( string.size() + 2 ); - - if ( !escapeInvisibles ) { - ret += '"'; - ret += string; - ret += '"'; - return ret; - } - - size_t last_start = 0; - auto write_to = [&]( size_t idx ) { - if ( last_start < idx ) { - ret += string.substr( last_start, idx - last_start ); - } - last_start = idx + 1; - }; + std::string ret; + // This is enough for the "don't escape invisibles" case, and a good + // lower bound on the "escape invisibles" case. + ret.reserve(string.size() + 2); + if (!escapeInvisibles) { ret += '"'; - for ( size_t i = 0; i < string.size(); ++i ) { - const char c = string[i]; - if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) { - write_to( i ); - if ( c == '\r' ) { ret.append( "\\r" ); } - if ( c == '\n' ) { ret.append( "\\n" ); } - if ( c == '\t' ) { ret.append( "\\t" ); } - if ( c == '\f' ) { ret.append( "\\f" ); } - } - } - write_to( string.size() ); + ret += string; ret += '"'; - return ret; + } + + size_t last_start = 0; + auto write_to = [&](size_t idx) { + if (last_start < idx) { + ret += string.substr(last_start, idx - last_start); + } + last_start = idx + 1; + }; + + ret += '"'; + for (size_t i = 0; i < string.size(); ++i) { + const char c = string[i]; + if (c == '\r' || c == '\n' || c == '\t' || c == '\f') { + write_to(i); + if (c == '\r') { ret.append("\\r"); } + if (c == '\n') { ret.append("\\n"); } + if (c == '\t') { ret.append("\\t"); } + if (c == '\f') { ret.append("\\f"); } + } + } + write_to(string.size()); + ret += '"'; + + return ret; } std::string convertIntoString(StringRef string) { - return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles()); + return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles()); } - std::string rawMemoryToString( const void *object, std::size_t size ) { - // Reverse order for little endian architectures - int i = 0, end = static_cast( size ), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; - } + std::string rawMemoryToString(const void *object, std::size_t size) { + // Reverse order for little endian architectures + int i = 0, end = static_cast(size), inc = 1; + if (Endianness::which() == Endianness::Little) { + i = end - 1; + end = inc = -1; + } - unsigned char const *bytes = static_cast(object); - ReusableStringStream rss; - rss << "0x" << std::setfill('0') << std::hex; - for( ; i != end; i += inc ) - rss << std::setw(2) << static_cast(bytes[i]); - return rss.str(); + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for (; i != end; i += inc) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); } std::string makeExceptionHappenedString() { - return "{ stringification failed with an exception: \"" + - translateActiveException() + "\" }"; - + return "{ stringification failed with an exception: \"" + translateActiveException() + "\" }"; } -} // end Detail namespace + } // namespace Detail + //// ======================================================= //// + // + // Out-of-line defs for full specialization of StringMaker + // + //// ======================================================= //// - -//// ======================================================= //// -// -// Out-of-line defs for full specialization of StringMaker -// -//// ======================================================= //// - -std::string StringMaker::convert(const std::string& str) { - return Detail::convertIntoString( str ); -} + std::string StringMaker::convert(const std::string &str) { + return Detail::convertIntoString(str); + } #ifdef CATCH_CONFIG_CPP17_STRING_VIEW -std::string StringMaker::convert(std::string_view str) { - return Detail::convertIntoString( StringRef( str.data(), str.size() ) ); -} + std::string StringMaker::convert(std::string_view str) { + return Detail::convertIntoString(StringRef(str.data(), str.size())); + } #endif -std::string StringMaker::convert(char const* str) { + std::string StringMaker::convert(char const *str) { if (str) { - return Detail::convertIntoString( str ); + return Detail::convertIntoString(str); } else { - return{ "{null string}" }; + return {"{null string}"}; } -} -std::string StringMaker::convert(char* str) { // NOLINT(readability-non-const-parameter) + } + std::string StringMaker::convert(char *str) { // NOLINT(readability-non-const-parameter) if (str) { - return Detail::convertIntoString( str ); + return Detail::convertIntoString(str); } else { - return{ "{null string}" }; + return {"{null string}"}; } -} + } #ifdef CATCH_CONFIG_WCHAR -std::string StringMaker::convert(const std::wstring& wstr) { + std::string StringMaker::convert(const std::wstring &wstr) { std::string s; s.reserve(wstr.size()); for (auto c : wstr) { - s += (c <= 0xff) ? static_cast(c) : '?'; + s += (c <= 0xff) ? static_cast(c) : '?'; } return ::Catch::Detail::stringify(s); -} + } -# ifdef CATCH_CONFIG_CPP17_STRING_VIEW -std::string StringMaker::convert(std::wstring_view str) { +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + std::string StringMaker::convert(std::wstring_view str) { return StringMaker::convert(std::wstring(str)); -} -# endif + } +#endif -std::string StringMaker::convert(wchar_t const * str) { + std::string StringMaker::convert(wchar_t const *str) { if (str) { - return ::Catch::Detail::stringify(std::wstring{ str }); + return ::Catch::Detail::stringify(std::wstring{str}); } else { - return{ "{null string}" }; + return {"{null string}"}; } -} -std::string StringMaker::convert(wchar_t * str) { + } + std::string StringMaker::convert(wchar_t *str) { if (str) { - return ::Catch::Detail::stringify(std::wstring{ str }); + return ::Catch::Detail::stringify(std::wstring{str}); } else { - return{ "{null string}" }; + return {"{null string}"}; } -} + } #endif #if defined(CATCH_CONFIG_CPP17_BYTE) #include -std::string StringMaker::convert(std::byte value) { + std::string StringMaker::convert(std::byte value) { return ::Catch::Detail::stringify(std::to_integer(value)); -} + } #endif // defined(CATCH_CONFIG_CPP17_BYTE) -std::string StringMaker::convert(int value) { + std::string StringMaker::convert(int value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long value) { + } + std::string StringMaker::convert(long value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long long value) { + } + std::string StringMaker::convert(long long value) { ReusableStringStream rss; rss << value; if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; + rss << " (0x" << std::hex << value << ')'; } return rss.str(); -} + } -std::string StringMaker::convert(unsigned int value) { + std::string StringMaker::convert(unsigned int value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long value) { + } + std::string StringMaker::convert(unsigned long value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long long value) { + } + std::string StringMaker::convert(unsigned long long value) { ReusableStringStream rss; rss << value; if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; + rss << " (0x" << std::hex << value << ')'; } return rss.str(); -} + } -std::string StringMaker::convert(signed char value) { + std::string StringMaker::convert(signed char value) { if (value == '\r') { - return "'\\r'"; + return "'\\r'"; } else if (value == '\f') { - return "'\\f'"; + return "'\\f'"; } else if (value == '\n') { - return "'\\n'"; + return "'\\n'"; } else if (value == '\t') { - return "'\\t'"; + return "'\\t'"; } else if ('\0' <= value && value < ' ') { - return ::Catch::Detail::stringify(static_cast(value)); + return ::Catch::Detail::stringify(static_cast(value)); } else { - char chstr[] = "' '"; - chstr[1] = value; - return chstr; + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } -} -std::string StringMaker::convert(char c) { + } + std::string StringMaker::convert(char c) { return ::Catch::Detail::stringify(static_cast(c)); -} -std::string StringMaker::convert(unsigned char value) { + } + std::string StringMaker::convert(unsigned char value) { return ::Catch::Detail::stringify(static_cast(value)); -} + } -int StringMaker::precision = std::numeric_limits::max_digits10; + int StringMaker::precision = std::numeric_limits::max_digits10; -std::string StringMaker::convert(float value) { + std::string StringMaker::convert(float value) { return Detail::fpToString(value, precision) + 'f'; -} + } -int StringMaker::precision = std::numeric_limits::max_digits10; + int StringMaker::precision = std::numeric_limits::max_digits10; -std::string StringMaker::convert(double value) { + std::string StringMaker::convert(double value) { return Detail::fpToString(value, precision); -} + } } // end namespace Catch - - namespace Catch { - Counts Counts::operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - diff.skipped = skipped - other.skipped; - return diff; - } + Counts Counts::operator-(Counts const &other) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + diff.skipped = skipped - other.skipped; + return diff; + } - Counts& Counts::operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - skipped += other.skipped; - return *this; - } + Counts &Counts::operator+=(Counts const &other) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + skipped += other.skipped; + return *this; + } - std::uint64_t Counts::total() const { - return passed + failed + failedButOk + skipped; - } - bool Counts::allPassed() const { - return failed == 0 && failedButOk == 0 && skipped == 0; - } - bool Counts::allOk() const { - return failed == 0; - } + std::uint64_t Counts::total() const { + return passed + failed + failedButOk + skipped; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0 && skipped == 0; + } + bool Counts::allOk() const { + return failed == 0; + } - Totals Totals::operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } + Totals Totals::operator-(Totals const &other) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } - Totals& Totals::operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } + Totals &Totals::operator+=(Totals const &other) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } - Totals Totals::delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else if ( diff.assertions.skipped > 0 ) - ++ diff.testCases.skipped; - else - ++diff.testCases.passed; - return diff; - } + Totals Totals::delta(Totals const &prevTotals) const { + Totals diff = *this - prevTotals; + if (diff.assertions.failed > 0) + ++diff.testCases.failed; + else if (diff.assertions.failedButOk > 0) + ++diff.testCases.failedButOk; + else if (diff.assertions.skipped > 0) + ++diff.testCases.skipped; + else + ++diff.testCases.passed; + return diff; + } -} - - - - -namespace Catch { - namespace Detail { - void registerTranslatorImpl( - Detail::unique_ptr&& translator ) { - getMutableRegistryHub().registerTranslator( - CATCH_MOVE( translator ) ); - } - } // namespace Detail } // namespace Catch +namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr &&translator) { + getMutableRegistryHub().registerTranslator( + CATCH_MOVE(translator)); + } + } // namespace Detail +} // namespace Catch #include namespace Catch { - Version::Version - ( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const * const _branchName, - unsigned int _buildNumber ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - patchNumber( _patchNumber ), - branchName( _branchName ), - buildNumber( _buildNumber ) - {} + Version::Version(unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const *const _branchName, + unsigned int _buildNumber) + : majorVersion(_majorVersion), minorVersion(_minorVersion), patchNumber(_patchNumber), branchName(_branchName), buildNumber(_buildNumber) {} - std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << '.' - << version.minorVersion << '.' - << version.patchNumber; - // branchName is never null -> 0th char is \0 if it is empty - if (version.branchName[0]) { - os << '-' << version.branchName - << '.' << version.buildNumber; - } - return os; + std::ostream &operator<<(std::ostream &os, Version const &version) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; } + return os; + } - Version const& libraryVersion() { - static Version version( 3, 14, 0, "", 0 ); - return version; - } - -} - - + Version const &libraryVersion() { + static Version version(3, 14, 0, "", 0); + return version; + } +} // namespace Catch namespace Catch { - const char* GeneratorException::what() const noexcept { - return m_msg; - } + const char *GeneratorException::what() const noexcept { + return m_msg; + } } // end namespace Catch - - - namespace Catch { - IGeneratorTracker::~IGeneratorTracker() = default; + IGeneratorTracker::~IGeneratorTracker() = default; -namespace Generators { + namespace Generators { GeneratorUntypedBase::~GeneratorUntypedBase() = default; - IGeneratorTracker* acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) { - return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); + IGeneratorTracker *acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) { + return getResultCapture().acquireGeneratorTracker(generatorName, lineInfo); } - IGeneratorTracker* createGeneratorTracker( StringRef generatorName, - SourceLineInfo lineInfo, - GeneratorBasePtr&& generator ) { - return getResultCapture().createGeneratorTracker( - generatorName, lineInfo, CATCH_MOVE( generator ) ); + IGeneratorTracker *createGeneratorTracker(StringRef generatorName, + SourceLineInfo lineInfo, + GeneratorBasePtr &&generator) { + return getResultCapture().createGeneratorTracker( + generatorName, lineInfo, CATCH_MOVE(generator)); } -} // namespace Generators + } // namespace Generators } // namespace Catch - - - #include namespace Catch { - namespace Generators { - namespace Detail { - std::uint32_t getSeed() { return sharedRng()(); } - } // namespace Detail - - struct RandomFloatingGenerator::PImpl { - PImpl( long double a, long double b, uint32_t seed ): - rng( seed ), dist( a, b ) {} - - Catch::SimplePcg32 rng; - std::uniform_real_distribution dist; - }; - - RandomFloatingGenerator::RandomFloatingGenerator( - long double a, long double b, std::uint32_t seed) : - m_pimpl(Catch::Detail::make_unique(a, b, seed)) { - static_cast( next() ); - } - - RandomFloatingGenerator::~RandomFloatingGenerator() = - default; - bool RandomFloatingGenerator::next() { - m_current_number = m_pimpl->dist( m_pimpl->rng ); - return true; - } - - bool RandomFloatingGenerator::isFinite() const { - return false; - } - - } // namespace Generators -} // namespace Catch - - - - -namespace Catch { - namespace Generators { - namespace Detail { - - [[noreturn]] - void throw_generator_exception( char const* msg ) { - Catch::throw_exception( GeneratorException{ msg } ); - } - - } // namespace Detail - } // namespace Generators -} // namespace Catch - - - - -namespace Catch { + namespace Generators { namespace Detail { - void missingCaptureInstance() { - CATCH_INTERNAL_ERROR( "No result capture instance" ); - } + std::uint32_t getSeed() { + return sharedRng()(); + } } // namespace Detail - IResultCapture::~IResultCapture() = default; + struct RandomFloatingGenerator::PImpl { + PImpl(long double a, long double b, uint32_t seed) + : rng(seed), dist(a, b) {} + + Catch::SimplePcg32 rng; + std::uniform_real_distribution dist; + }; + + RandomFloatingGenerator::RandomFloatingGenerator( + long double a, long double b, std::uint32_t seed) + : m_pimpl(Catch::Detail::make_unique(a, b, seed)) { + static_cast(next()); + } + + RandomFloatingGenerator::~RandomFloatingGenerator() = default; + bool RandomFloatingGenerator::next() { + m_current_number = m_pimpl->dist(m_pimpl->rng); + return true; + } + + bool RandomFloatingGenerator::isFinite() const { + return false; + } + + } // namespace Generators } // namespace Catch +namespace Catch { + namespace Generators { + namespace Detail { + [[noreturn]] + void throw_generator_exception(char const *msg) { + Catch::throw_exception(GeneratorException{msg}); + } + } // namespace Detail + } // namespace Generators +} // namespace Catch namespace Catch { - IConfig::~IConfig() = default; -} - - + namespace Detail { + void missingCaptureInstance() { + CATCH_INTERNAL_ERROR("No result capture instance"); + } + } // namespace Detail + IResultCapture::~IResultCapture() = default; +} // namespace Catch namespace Catch { - IExceptionTranslator::~IExceptionTranslator() = default; - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; + IConfig::~IConfig() = default; } - - +namespace Catch { + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} // namespace Catch #include namespace Catch { - namespace Generators { + namespace Generators { - bool GeneratorUntypedBase::countedNext() { - auto ret = next(); - if ( ret ) { - m_stringReprCache.clear(); - ++m_currentElementIndex; - } - return ret; + bool GeneratorUntypedBase::countedNext() { + auto ret = next(); + if (ret) { + m_stringReprCache.clear(); + ++m_currentElementIndex; + } + return ret; + } + + void GeneratorUntypedBase::skipToNthElementImpl(std::size_t n) { + for (size_t i = m_currentElementIndex; i < n; ++i) { + bool isValid = next(); + if (!isValid) { + Detail::throw_generator_exception( + "Coud not jump to Nth element: not enough elements"); } + } + } - void GeneratorUntypedBase::skipToNthElementImpl( std::size_t n ) { - for ( size_t i = m_currentElementIndex; i < n; ++i ) { - bool isValid = next(); - if ( !isValid ) { - Detail::throw_generator_exception( - "Coud not jump to Nth element: not enough elements" ); - } - } - } + void GeneratorUntypedBase::skipToNthElement(std::size_t n) { + if (n < m_currentElementIndex) { + Detail::throw_generator_exception( + "Tried to jump generator backwards"); + } + if (n == m_currentElementIndex) { return; } - void GeneratorUntypedBase::skipToNthElement( std::size_t n ) { - if ( n < m_currentElementIndex ) { - Detail::throw_generator_exception( - "Tried to jump generator backwards" ); - } - if ( n == m_currentElementIndex ) { return; } + skipToNthElementImpl(n); + // Fixup tracking after moving the generator forward + // * Ensure that the correct element index is set after skipping + // * Invalidate cache + m_currentElementIndex = n; + m_stringReprCache.clear(); + } - skipToNthElementImpl(n); - // Fixup tracking after moving the generator forward - // * Ensure that the correct element index is set after skipping - // * Invalidate cache - m_currentElementIndex = n; - m_stringReprCache.clear(); - } + StringRef GeneratorUntypedBase::currentElementAsString() const { + if (m_stringReprCache.empty()) { + m_stringReprCache = stringifyImpl(); + } + return m_stringReprCache; + } - StringRef GeneratorUntypedBase::currentElementAsString() const { - if ( m_stringReprCache.empty() ) { - m_stringReprCache = stringifyImpl(); - } - return m_stringReprCache; - } + bool GeneratorUntypedBase::isFinite() const { + return true; + } - bool GeneratorUntypedBase::isFinite() const { return true; } - - } // namespace Generators + } // namespace Generators } // namespace Catch - - - namespace Catch { - IRegistryHub::~IRegistryHub() = default; - IMutableRegistryHub::~IMutableRegistryHub() = default; -} - - + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} // namespace Catch #include namespace Catch { - ReporterConfig::ReporterConfig( - IConfig const* _fullConfig, - Detail::unique_ptr _stream, - ColourMode colourMode, - std::map customOptions ): - m_stream( CATCH_MOVE(_stream) ), - m_fullConfig( _fullConfig ), - m_colourMode( colourMode ), - m_customOptions( CATCH_MOVE( customOptions ) ) {} + ReporterConfig::ReporterConfig( + IConfig const *_fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions) + : m_stream(CATCH_MOVE(_stream)), m_fullConfig(_fullConfig), m_colourMode(colourMode), m_customOptions(CATCH_MOVE(customOptions)) {} - Detail::unique_ptr ReporterConfig::takeStream() && { - assert( m_stream ); - return CATCH_MOVE( m_stream ); + Detail::unique_ptr ReporterConfig::takeStream() && { + assert(m_stream); + return CATCH_MOVE(m_stream); + } + IConfig const *ReporterConfig::fullConfig() const { + return m_fullConfig; + } + ColourMode ReporterConfig::colourMode() const { + return m_colourMode; + } + + std::map const & + ReporterConfig::customOptions() const { + return m_customOptions; + } + + ReporterConfig::~ReporterConfig() = default; + + AssertionStats::AssertionStats(AssertionResult const &_assertionResult, + std::vector const &_infoMessages, + Totals const &_totals) + : assertionResult(_assertionResult), infoMessages(_infoMessages), totals(_totals) { + if (assertionResult.hasMessage()) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder(assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType()); + builder.m_info.message = static_cast(assertionResult.getMessage()); + + infoMessages.push_back(CATCH_MOVE(builder.m_info)); } - IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; } - ColourMode ReporterConfig::colourMode() const { return m_colourMode; } + } - std::map const& - ReporterConfig::customOptions() const { - return m_customOptions; - } + SectionStats::SectionStats(SectionInfo &&_sectionInfo, + Counts const &_assertions, + double _durationInSeconds, + bool _missingAssertions) + : sectionInfo(CATCH_MOVE(_sectionInfo)), assertions(_assertions), durationInSeconds(_durationInSeconds), missingAssertions(_missingAssertions) {} - ReporterConfig::~ReporterConfig() = default; + TestCaseStats::TestCaseStats(TestCaseInfo const &_testInfo, + Totals const &_totals, + std::string &&_stdOut, + std::string &&_stdErr, + bool _aborting) + : testInfo(&_testInfo), totals(_totals), stdOut(CATCH_MOVE(_stdOut)), stdErr(CATCH_MOVE(_stdErr)), aborting(_aborting) {} - AssertionStats::AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder.m_info.message = static_cast(assertionResult.getMessage()); + TestRunStats::TestRunStats(TestRunInfo const &_runInfo, + Totals const &_totals, + bool _aborting) + : runInfo(_runInfo), totals(_totals), aborting(_aborting) {} - infoMessages.push_back( CATCH_MOVE(builder.m_info) ); - } - } - - SectionStats::SectionStats( SectionInfo&& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( CATCH_MOVE(_sectionInfo) ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} - - - TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string&& _stdOut, - std::string&& _stdErr, - bool _aborting ) - : testInfo( &_testInfo ), - totals( _totals ), - stdOut( CATCH_MOVE(_stdOut) ), - stdErr( CATCH_MOVE(_stdErr) ), - aborting( _aborting ) - {} - - - TestRunStats::TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} - - IEventListener::~IEventListener() = default; + IEventListener::~IEventListener() = default; } // end namespace Catch - - - namespace Catch { - IReporterFactory::~IReporterFactory() = default; - EventListenerFactory::~EventListenerFactory() = default; -} - - - - -namespace Catch { - ITestCaseRegistry::~ITestCaseRegistry() = default; -} - - - -namespace Catch { - - AssertionHandler::AssertionHandler - ( StringRef macroName, - SourceLineInfo const& lineInfo, - StringRef capturedExpression, - 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 ); - } - void AssertionHandler::handleMessage(ResultWas::OfType resultType, std::string&& message) { - m_resultCapture.handleMessage( m_assertionInfo, resultType, CATCH_MOVE(message), m_reaction ); - } - - auto AssertionHandler::allowThrows() const -> bool { - return getCurrentContext().getConfig()->allowThrows(); - } - - void AssertionHandler::complete() { - m_completed = true; - if( m_reaction.shouldDebugBreak ) { - - // If you find your debugger stopping you here then go one level up on the - // call-stack for the code that caused it (typically a failed assertion) - - // (To go back to the test and change execution, jump over the throw, next) - CATCH_BREAK_INTO_DEBUGGER(); - } - if (m_reaction.shouldThrow) { - throw_test_failure_exception(); - } - if ( m_reaction.shouldSkip ) { - throw_test_skip_exception(); - } - } - - void AssertionHandler::handleUnexpectedInflightException() { - m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); - } - - void AssertionHandler::handleExceptionThrownAsExpected() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); - } - void AssertionHandler::handleExceptionNotThrownAsExpected() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); - } - - void AssertionHandler::handleUnexpectedExceptionNotThrown() { - m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); - } - - void AssertionHandler::handleThrowingCallSkipped() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); - } - - // This is the overload that takes a string and infers the Equals matcher from it - // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp - void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ) { - handleExceptionMatchExpr( handler, Matchers::Equals( str ) ); - } - + IReporterFactory::~IReporterFactory() = default; + EventListenerFactory::~EventListenerFactory() = default; } // namespace Catch +namespace Catch { + ITestCaseRegistry::~ITestCaseRegistry() = default; +} +namespace Catch { + AssertionHandler::AssertionHandler(StringRef macroName, + SourceLineInfo const &lineInfo, + StringRef capturedExpression, + 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); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, std::string &&message) { + m_resultCapture.handleMessage(m_assertionInfo, resultType, CATCH_MOVE(message), m_reaction); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + m_completed = true; + if (m_reaction.shouldDebugBreak) { + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { + throw_test_failure_exception(); + } + if (m_reaction.shouldSkip) { + throw_test_skip_exception(); + } + } + + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException(m_assertionInfo, Catch::translateActiveException(), m_reaction); + } + + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown(m_assertionInfo, m_reaction); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str) { + handleExceptionMatchExpr(handler, Matchers::Equals(str)); + } + +} // namespace Catch #include namespace Catch { - namespace Detail { + namespace Detail { - bool CaseInsensitiveLess::operator()( StringRef lhs, - StringRef rhs ) const { - return std::lexicographical_compare( - lhs.begin(), lhs.end(), - rhs.begin(), rhs.end(), - []( char l, char r ) { return toLower( l ) < toLower( r ); } ); - } + bool CaseInsensitiveLess::operator()(StringRef lhs, + StringRef rhs) const { + return std::lexicographical_compare( + lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + [](char l, char r) { return toLower(l) < toLower(r); }); + } - bool - CaseInsensitiveEqualTo::operator()( StringRef lhs, - StringRef rhs ) const { - return std::equal( - lhs.begin(), lhs.end(), - rhs.begin(), rhs.end(), - []( char l, char r ) { return toLower( l ) == toLower( r ); } ); - } + bool + CaseInsensitiveEqualTo::operator()(StringRef lhs, + StringRef rhs) const { + return std::equal( + lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + [](char l, char r) { return toLower(l) == toLower(r); }); + } - } // namespace Detail + } // namespace Detail } // namespace Catch - - - #include #include namespace { - bool isOptPrefix( char c ) { - return c == '-' + bool isOptPrefix(char c) { + return c == '-' #ifdef CATCH_PLATFORM_WINDOWS - || c == '/' + || c == '/' #endif - ; - } + ; + } - Catch::StringRef normaliseOpt( Catch::StringRef optName ) { - if ( optName[0] == '-' + Catch::StringRef normaliseOpt(Catch::StringRef optName) { + if (optName[0] == '-' #if defined(CATCH_PLATFORM_WINDOWS) - || optName[0] == '/' + || optName[0] == '/' #endif - ) { - return optName.substr( 1, optName.size() ); - } - - return optName; + ) { + return optName.substr(1, optName.size()); } - static size_t find_first_separator(Catch::StringRef sr) { - auto is_separator = []( char c ) { - return c == ' ' || c == ':' || c == '='; - }; - size_t pos = 0; - while (pos < sr.size()) { - if (is_separator(sr[pos])) { return pos; } - ++pos; - } + return optName; + } - return Catch::StringRef::npos; + static size_t find_first_separator(Catch::StringRef sr) { + auto is_separator = [](char c) { + return c == ' ' || c == ':' || c == '='; + }; + size_t pos = 0; + while (pos < sr.size()) { + if (is_separator(sr[pos])) { return pos; } + ++pos; } + return Catch::StringRef::npos; + } + } // namespace namespace Catch { - namespace Clara { - namespace Detail { + namespace Clara { + namespace Detail { - void TokenStream::loadBuffer() { - m_tokenBuffer.clear(); + void TokenStream::loadBuffer() { + m_tokenBuffer.clear(); - // Skip any empty strings - while ( it != itEnd && it->empty() ) { - ++it; - } - - if ( it != itEnd ) { - StringRef next = *it; - if ( isOptPrefix( next[0] ) ) { - auto delimiterPos = find_first_separator(next); - if ( delimiterPos != StringRef::npos ) { - m_tokenBuffer.push_back( - { TokenType::Option, - next.substr( 0, delimiterPos ) } ); - m_tokenBuffer.push_back( - { TokenType::Argument, - next.substr( delimiterPos + 1, next.size() ) } ); - } else { - if ( next.size() > 1 && next[1] != '-' && next.size() > 2 ) { - // Combined short args, e.g. "-ab" for "-a -b" - for ( size_t i = 1; i < next.size(); ++i ) { - m_tokenBuffer.push_back( - { TokenType::Option, - next.substr( i, 1 ) } ); - } - } else { - m_tokenBuffer.push_back( - { TokenType::Option, next } ); - } - } - } else { - m_tokenBuffer.push_back( - { TokenType::Argument, next } ); - } - } - } - - TokenStream::TokenStream( Args const& args ): - TokenStream( args.m_args.begin(), args.m_args.end() ) {} - - TokenStream::TokenStream( Iterator it_, Iterator itEnd_ ): - it( it_ ), itEnd( itEnd_ ) { - loadBuffer(); - } - - TokenStream& TokenStream::operator++() { - if ( m_tokenBuffer.size() >= 2 ) { - m_tokenBuffer.erase( m_tokenBuffer.begin() ); - } else { - if ( it != itEnd ) - ++it; - loadBuffer(); - } - return *this; - } - - ParserResult convertInto( std::string const& source, - std::string& target ) { - target = source; - return ParserResult::ok( ParseResultType::Matched ); - } - - ParserResult convertInto( std::string const& source, - bool& target ) { - std::string srcLC = toLower( source ); - - if ( srcLC == "y" || srcLC == "1" || srcLC == "true" || - srcLC == "yes" || srcLC == "on" ) { - target = true; - } else if ( srcLC == "n" || srcLC == "0" || srcLC == "false" || - srcLC == "no" || srcLC == "off" ) { - target = false; - } else { - return ParserResult::runtimeError( - "Expected a boolean value but did not recognise: '" + - source + '\'' ); - } - return ParserResult::ok( ParseResultType::Matched ); - } - - size_t ParserBase::cardinality() const { return 1; } - - InternalParseResult ParserBase::parse( Args const& args ) const { - return parse( static_cast(args.exeName()), TokenStream( args ) ); - } - - ParseState::ParseState( ParseResultType type, - TokenStream remainingTokens ): - m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {} - - ParserResult BoundFlagRef::setFlag( bool flag ) { - m_ref = flag; - return ParserResult::ok( ParseResultType::Matched ); - } - - ResultBase::~ResultBase() = default; - - bool BoundRef::isContainer() const { return false; } - - bool BoundRef::isFlag() const { return false; } - - bool BoundFlagRefBase::isFlag() const { return true; } - -} // namespace Detail - - Detail::InternalParseResult Arg::parse(std::string const&, - Detail::TokenStream tokens) const { - auto validationResult = validate(); - if (!validationResult) - return Detail::InternalParseResult(validationResult); - - auto token = *tokens; - if (token.type != Detail::TokenType::Argument) - return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::NoMatch, CATCH_MOVE(tokens))); - - assert(!m_ref->isFlag()); - auto valueRef = - static_cast(m_ref.get()); - - auto result = valueRef->setValue(static_cast(token.token)); - if ( !result ) - return Detail::InternalParseResult( result ); - else - return Detail::InternalParseResult::ok( - Detail::ParseState( ParseResultType::Matched, - CATCH_MOVE( ++tokens ) ) ); + // Skip any empty strings + while (it != itEnd && it->empty()) { + ++it; } - Opt::Opt(bool& ref) : - ParserRefImpl(std::make_shared(ref)) {} - - Detail::HelpColumns Opt::getHelpColumns() const { - ReusableStringStream oss; - bool first = true; - for (auto const& opt : m_optNames) { - if (first) - first = false; - else - oss << ", "; - oss << opt; - } - if (!m_hint.empty()) - oss << " <" << m_hint << '>'; - return { oss.str(), m_description }; - } - - bool Opt::isMatch(StringRef optToken) const { - auto normalisedToken = normaliseOpt(optToken); - for (auto const& name : m_optNames) { - if (normaliseOpt(name) == normalisedToken) - return true; - } - return false; - } - - Detail::InternalParseResult Opt::parse(std::string const&, - Detail::TokenStream tokens) const { - auto validationResult = validate(); - if (!validationResult) - return Detail::InternalParseResult(validationResult); - - if (tokens && - tokens->type == Detail::TokenType::Option) { - auto const& token = *tokens; - if (isMatch(token.token)) { - if (m_ref->isFlag()) { - auto flagRef = - static_cast( - m_ref.get()); - auto result = flagRef->setFlag(true); - if (!result) - return Detail::InternalParseResult(result); - if (result.value() == - ParseResultType::ShortCircuitAll) - return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), CATCH_MOVE(tokens))); - } else { - auto valueRef = - static_cast( - m_ref.get()); - ++tokens; - if (!tokens) - return Detail::InternalParseResult::runtimeError( - "Expected argument following " + - token.token); - auto const& argToken = *tokens; - if (argToken.type != Detail::TokenType::Argument) - return Detail::InternalParseResult::runtimeError( - "Expected argument following " + - token.token); - const auto result = valueRef->setValue(static_cast(argToken.token)); - if (!result) - return Detail::InternalParseResult(result); - if (result.value() == - ParseResultType::ShortCircuitAll) - return Detail::InternalParseResult::ok(Detail::ParseState( - result.value(), CATCH_MOVE(tokens))); - } - return Detail::InternalParseResult::ok(Detail::ParseState( - ParseResultType::Matched, CATCH_MOVE(++tokens))); + if (it != itEnd) { + StringRef next = *it; + if (isOptPrefix(next[0])) { + auto delimiterPos = find_first_separator(next); + if (delimiterPos != StringRef::npos) { + m_tokenBuffer.push_back( + {TokenType::Option, + next.substr(0, delimiterPos)}); + m_tokenBuffer.push_back( + {TokenType::Argument, + next.substr(delimiterPos + 1, next.size())}); + } else { + if (next.size() > 1 && next[1] != '-' && next.size() > 2) { + // Combined short args, e.g. "-ab" for "-a -b" + for (size_t i = 1; i < next.size(); ++i) { + m_tokenBuffer.push_back( + {TokenType::Option, + next.substr(i, 1)}); } + } else { + m_tokenBuffer.push_back( + {TokenType::Option, next}); + } } - return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); + } else { + m_tokenBuffer.push_back( + {TokenType::Argument, next}); + } } + } - Detail::Result Opt::validate() const { - if (m_optNames.empty()) - return Detail::Result::logicError("No options supplied to Opt"); - for (auto const& name : m_optNames) { - if (name.empty()) - return Detail::Result::logicError( - "Option name cannot be empty"); + TokenStream::TokenStream(Args const &args) + : TokenStream(args.m_args.begin(), args.m_args.end()) {} + + TokenStream::TokenStream(Iterator it_, Iterator itEnd_) + : it(it_), itEnd(itEnd_) { + loadBuffer(); + } + + TokenStream &TokenStream::operator++() { + if (m_tokenBuffer.size() >= 2) { + m_tokenBuffer.erase(m_tokenBuffer.begin()); + } else { + if (it != itEnd) + ++it; + loadBuffer(); + } + return *this; + } + + ParserResult convertInto(std::string const &source, + std::string &target) { + target = source; + return ParserResult::ok(ParseResultType::Matched); + } + + ParserResult convertInto(std::string const &source, + bool &target) { + std::string srcLC = toLower(source); + + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") { + target = true; + } else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") { + target = false; + } else { + return ParserResult::runtimeError( + "Expected a boolean value but did not recognise: '" + source + '\''); + } + return ParserResult::ok(ParseResultType::Matched); + } + + size_t ParserBase::cardinality() const { + return 1; + } + + InternalParseResult ParserBase::parse(Args const &args) const { + return parse(static_cast(args.exeName()), TokenStream(args)); + } + + ParseState::ParseState(ParseResultType type, + TokenStream remainingTokens) + : m_type(type), m_remainingTokens(CATCH_MOVE(remainingTokens)) {} + + ParserResult BoundFlagRef::setFlag(bool flag) { + m_ref = flag; + return ParserResult::ok(ParseResultType::Matched); + } + + ResultBase::~ResultBase() = default; + + bool BoundRef::isContainer() const { + return false; + } + + bool BoundRef::isFlag() const { + return false; + } + + bool BoundFlagRefBase::isFlag() const { + return true; + } + + } // namespace Detail + + Detail::InternalParseResult Arg::parse(std::string const &, + Detail::TokenStream tokens) const { + auto validationResult = validate(); + if (!validationResult) + return Detail::InternalParseResult(validationResult); + + auto token = *tokens; + if (token.type != Detail::TokenType::Argument) + return Detail::InternalParseResult::ok(Detail::ParseState( + ParseResultType::NoMatch, CATCH_MOVE(tokens))); + + assert(!m_ref->isFlag()); + auto valueRef = static_cast(m_ref.get()); + + auto result = valueRef->setValue(static_cast(token.token)); + if (!result) + return Detail::InternalParseResult(result); + else + return Detail::InternalParseResult::ok( + Detail::ParseState(ParseResultType::Matched, + CATCH_MOVE(++tokens))); + } + + Opt::Opt(bool &ref) + : ParserRefImpl(std::make_shared(ref)) {} + + Detail::HelpColumns Opt::getHelpColumns() const { + ReusableStringStream oss; + bool first = true; + for (auto const &opt : m_optNames) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if (!m_hint.empty()) + oss << " <" << m_hint << '>'; + return {oss.str(), m_description}; + } + + bool Opt::isMatch(StringRef optToken) const { + auto normalisedToken = normaliseOpt(optToken); + for (auto const &name : m_optNames) { + if (normaliseOpt(name) == normalisedToken) + return true; + } + return false; + } + + Detail::InternalParseResult Opt::parse(std::string const &, + Detail::TokenStream tokens) const { + auto validationResult = validate(); + if (!validationResult) + return Detail::InternalParseResult(validationResult); + + if (tokens && tokens->type == Detail::TokenType::Option) { + auto const &token = *tokens; + if (isMatch(token.token)) { + if (m_ref->isFlag()) { + auto flagRef = static_cast( + m_ref.get()); + auto result = flagRef->setFlag(true); + if (!result) + return Detail::InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return Detail::InternalParseResult::ok(Detail::ParseState( + result.value(), CATCH_MOVE(tokens))); + } else { + auto valueRef = static_cast( + m_ref.get()); + ++tokens; + if (!tokens) + return Detail::InternalParseResult::runtimeError( + "Expected argument following " + token.token); + auto const &argToken = *tokens; + if (argToken.type != Detail::TokenType::Argument) + return Detail::InternalParseResult::runtimeError( + "Expected argument following " + token.token); + const auto result = valueRef->setValue(static_cast(argToken.token)); + if (!result) + return Detail::InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return Detail::InternalParseResult::ok(Detail::ParseState( + result.value(), CATCH_MOVE(tokens))); + } + return Detail::InternalParseResult::ok(Detail::ParseState( + ParseResultType::Matched, CATCH_MOVE(++tokens))); + } + } + return Detail::InternalParseResult::ok( + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); + } + + Detail::Result Opt::validate() const { + if (m_optNames.empty()) + return Detail::Result::logicError("No options supplied to Opt"); + for (auto const &name : m_optNames) { + if (name.empty()) + return Detail::Result::logicError( + "Option name cannot be empty"); #ifdef CATCH_PLATFORM_WINDOWS - if (name[0] != '-' && name[0] != '/') - return Detail::Result::logicError( - "Option name must begin with '-' or '/'"); + if (name[0] != '-' && name[0] != '/') + return Detail::Result::logicError( + "Option name must begin with '-' or '/'"); #else - if (name[0] != '-') - return Detail::Result::logicError( - "Option name must begin with '-'"); + if (name[0] != '-') + return Detail::Result::logicError( + "Option name must begin with '-'"); #endif + } + return ParserRefImpl::validate(); + } + + ExeName::ExeName() + : m_name(std::make_shared("")) {} + + ExeName::ExeName(std::string &ref) + : ExeName() { + m_ref = std::make_shared>(ref); + } + + Detail::InternalParseResult + ExeName::parse(std::string const &, + Detail::TokenStream tokens) const { + return Detail::InternalParseResult::ok( + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); + } + + ParserResult ExeName::set(std::string const &newName) { + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) + ? newName + : newName.substr(lastSlash + 1); + + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return ParserResult::ok(ParseResultType::Matched); + } + + Parser &Parser::operator|=(Parser const &other) { + m_options.insert(m_options.end(), + other.m_options.begin(), + other.m_options.end()); + m_args.insert( + m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + std::vector Parser::getHelpColumns() const { + std::vector cols; + cols.reserve(m_options.size()); + for (auto const &o : m_options) { + cols.push_back(o.getHelpColumns()); + } + return cols; + } + + void Parser::writeToStream(std::ostream &os) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" + << " " << m_exeName.name() << ' '; + bool required = true, first = true; + for (auto const &arg : m_args) { + if (first) + first = false; + else + os << ' '; + if (arg.isOptional() && required) { + os << '['; + required = false; + } + os << '<' << arg.hint() << '>'; + if (arg.cardinality() == 0) + os << " ... "; + } + if (!required) + os << ']'; + if (!m_options.empty()) + os << " options"; + os << "\n\nwhere options are:\n"; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for (auto const &cols : rows) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth / 2); + + for (auto &cols : rows) { + auto row = TextFlow::Column(CATCH_MOVE(cols.left)) + .width(optWidth) + .indent(2) + + TextFlow::Spacer(4) + TextFlow::Column(static_cast(cols.descriptions)).width(consoleWidth - 7 - optWidth); + os << row << '\n'; + } + } + + Detail::Result Parser::validate() const { + for (auto const &opt : m_options) { + auto result = opt.validate(); + if (!result) + return result; + } + for (auto const &arg : m_args) { + auto result = arg.validate(); + if (!result) + return result; + } + return Detail::Result::ok(); + } + + Detail::InternalParseResult + Parser::parse(std::string const &exeName, + Detail::TokenStream tokens) const { + struct ParserInfo { + ParserBase const *parser = nullptr; + size_t count = 0; + }; + std::vector parseInfos; + parseInfos.reserve(m_options.size() + m_args.size()); + for (auto const &opt : m_options) { + parseInfos.push_back({&opt, 0}); + } + for (auto const &arg : m_args) { + parseInfos.push_back({&arg, 0}); + } + + m_exeName.set(exeName); + + auto result = Detail::InternalParseResult::ok( + Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); + while (result.value().remainingTokens()) { + bool tokenParsed = false; + + for (auto &parseInfo : parseInfos) { + if (parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality()) { + result = parseInfo.parser->parse( + exeName, CATCH_MOVE(result).value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; } - return ParserRefImpl::validate(); + } } - ExeName::ExeName() : - m_name(std::make_shared("")) {} + if (result.value().type() == ParseResultType::ShortCircuitAll) + return result; + if (!tokenParsed) + return Detail::InternalParseResult::runtimeError( + "Unrecognised token: " + result.value().remainingTokens()->token); + } + // !TBD Check missing required options + return result; + } - ExeName::ExeName(std::string& ref) : ExeName() { - m_ref = std::make_shared>(ref); - } + Args::Args(int argc, char const *const *argv) + : m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} - Detail::InternalParseResult - ExeName::parse(std::string const&, - Detail::TokenStream tokens) const { - return Detail::InternalParseResult::ok( - Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); - } + Args::Args(std::initializer_list args) + : m_exeName(*args.begin()), m_args(args.begin() + 1, args.end()) {} - ParserResult ExeName::set(std::string const& newName) { - auto lastSlash = newName.find_last_of("\\/"); - auto filename = (lastSlash == std::string::npos) - ? newName - : newName.substr(lastSlash + 1); + Help::Help(bool &showHelpFlag) + : Opt([&](bool flag) { + showHelpFlag = flag; + return ParserResult::ok(ParseResultType::ShortCircuitAll); + }) { + static_cast(*this)( + "display usage information")["-?"]["-h"]["--help"] + .optional(); + } - *m_name = filename; - if (m_ref) - return m_ref->setValue(filename); - else - return ParserResult::ok(ParseResultType::Matched); - } - - - - - Parser& Parser::operator|=( Parser const& other ) { - m_options.insert( m_options.end(), - other.m_options.begin(), - other.m_options.end() ); - m_args.insert( - m_args.end(), other.m_args.begin(), other.m_args.end() ); - return *this; - } - - std::vector Parser::getHelpColumns() const { - std::vector cols; - cols.reserve( m_options.size() ); - for ( auto const& o : m_options ) { - cols.push_back(o.getHelpColumns()); - } - return cols; - } - - void Parser::writeToStream( std::ostream& os ) const { - if ( !m_exeName.name().empty() ) { - os << "usage:\n" - << " " << m_exeName.name() << ' '; - bool required = true, first = true; - for ( auto const& arg : m_args ) { - if ( first ) - first = false; - else - os << ' '; - if ( arg.isOptional() && required ) { - os << '['; - required = false; - } - os << '<' << arg.hint() << '>'; - if ( arg.cardinality() == 0 ) - os << " ... "; - } - if ( !required ) - os << ']'; - if ( !m_options.empty() ) - os << " options"; - os << "\n\nwhere options are:\n"; - } - - auto rows = getHelpColumns(); - size_t consoleWidth = CATCH_CONFIG_CONSOLE_WIDTH; - size_t optWidth = 0; - for ( auto const& cols : rows ) - optWidth = ( std::max )( optWidth, cols.left.size() + 2 ); - - optWidth = ( std::min )( optWidth, consoleWidth / 2 ); - - for ( auto& cols : rows ) { - auto row = TextFlow::Column( CATCH_MOVE(cols.left) ) - .width( optWidth ) - .indent( 2 ) + - TextFlow::Spacer( 4 ) + - TextFlow::Column( static_cast(cols.descriptions) ) - .width( consoleWidth - 7 - optWidth ); - os << row << '\n'; - } - } - - Detail::Result Parser::validate() const { - for ( auto const& opt : m_options ) { - auto result = opt.validate(); - if ( !result ) - return result; - } - for ( auto const& arg : m_args ) { - auto result = arg.validate(); - if ( !result ) - return result; - } - return Detail::Result::ok(); - } - - Detail::InternalParseResult - Parser::parse( std::string const& exeName, - Detail::TokenStream tokens ) const { - - struct ParserInfo { - ParserBase const* parser = nullptr; - size_t count = 0; - }; - std::vector parseInfos; - parseInfos.reserve( m_options.size() + m_args.size() ); - for ( auto const& opt : m_options ) { - parseInfos.push_back( { &opt, 0 } ); - } - for ( auto const& arg : m_args ) { - parseInfos.push_back( { &arg, 0 } ); - } - - m_exeName.set( exeName ); - - auto result = Detail::InternalParseResult::ok( - Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) ); - while ( result.value().remainingTokens() ) { - bool tokenParsed = false; - - for ( auto& parseInfo : parseInfos ) { - if ( parseInfo.parser->cardinality() == 0 || - parseInfo.count < parseInfo.parser->cardinality() ) { - result = parseInfo.parser->parse( - exeName, CATCH_MOVE(result).value().remainingTokens() ); - if ( !result ) - return result; - if ( result.value().type() != - ParseResultType::NoMatch ) { - tokenParsed = true; - ++parseInfo.count; - break; - } - } - } - - if ( result.value().type() == ParseResultType::ShortCircuitAll ) - return result; - if ( !tokenParsed ) - return Detail::InternalParseResult::runtimeError( - "Unrecognised token: " + - result.value().remainingTokens()->token ); - } - // !TBD Check missing required options - return result; - } - - Args::Args(int argc, char const* const* argv) : - m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} - - Args::Args(std::initializer_list args) : - m_exeName(*args.begin()), - m_args(args.begin() + 1, args.end()) {} - - - Help::Help( bool& showHelpFlag ): - Opt( [&]( bool flag ) { - showHelpFlag = flag; - return ParserResult::ok( ParseResultType::ShortCircuitAll ); - } ) { - static_cast ( *this )( - "display usage information" )["-?"]["-h"]["--help"] - .optional(); - } - - } // namespace Clara + } // namespace Clara } // namespace Catch - - - #include #include namespace Catch { - Clara::Parser makeCommandLineParser( ConfigData& config ) { + Clara::Parser makeCommandLineParser(ConfigData &config) { + using namespace Clara; - using namespace Clara; + auto const setWarning = [&](std::string const &warning) { + if (warning == "NoAssertions") { + config.warnings = static_cast(config.warnings | WarnAbout::NoAssertions); + return ParserResult::ok(ParseResultType::Matched); + } else if (warning == "UnmatchedTestSpec") { + config.warnings = static_cast(config.warnings | WarnAbout::UnmatchedTestSpec); + return ParserResult::ok(ParseResultType::Matched); + } else if (warning == "InfiniteGenerators") { + config.warnings = static_cast(config.warnings | WarnAbout::InfiniteGenerator); + return ParserResult::ok(ParseResultType::Matched); + } - auto const setWarning = [&]( std::string const& warning ) { - if ( warning == "NoAssertions" ) { - config.warnings = static_cast(config.warnings | WarnAbout::NoAssertions); - return ParserResult::ok( ParseResultType::Matched ); - } else if ( warning == "UnmatchedTestSpec" ) { - config.warnings = static_cast(config.warnings | WarnAbout::UnmatchedTestSpec); - return ParserResult::ok( ParseResultType::Matched ); - } else if ( warning == "InfiniteGenerators" ) { - config.warnings = static_cast(config.warnings | WarnAbout::InfiniteGenerator); - return ParserResult::ok( ParseResultType::Matched ); - } + return ParserResult ::runtimeError( + "Unrecognised warning option: '" + warning + '\''); + }; + auto const loadTestNamesFromFile = [&](std::string const &filename) { + std::ifstream f(filename.c_str()); + if (!f.is_open()) + return ParserResult::runtimeError("Unable to load input file: '" + filename + '\''); - return ParserResult ::runtimeError( - "Unrecognised warning option: '" + warning + '\'' ); - }; - auto const loadTestNamesFromFile = [&]( std::string const& filename ) { - std::ifstream f( filename.c_str() ); - if( !f.is_open() ) - return ParserResult::runtimeError( "Unable to load input file: '" + filename + '\'' ); + std::string line; + while (std::getline(f, line)) { + line = trim(line); + if (!line.empty() && !startsWith(line, '#')) { + if (!startsWith(line, '"')) + line = '"' + CATCH_MOVE(line) + '"'; + config.testsOrTags.push_back(line); + config.testsOrTags.emplace_back(","); + } + } + // Remove comma in the end + if (!config.testsOrTags.empty()) + config.testsOrTags.erase(config.testsOrTags.end() - 1); - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, '#' ) ) { - if( !startsWith( line, '"' ) ) - line = '"' + CATCH_MOVE(line) + '"'; - config.testsOrTags.push_back( line ); - config.testsOrTags.emplace_back( "," ); - } - } - //Remove comma in the end - if(!config.testsOrTags.empty()) - config.testsOrTags.erase( config.testsOrTags.end()-1 ); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setTestOrder = [&](std::string const &order) { + if (startsWith("declared", order)) + config.runOrder = TestRunOrder::Declared; + else if (startsWith("lexical", order)) + config.runOrder = TestRunOrder::LexicographicallySorted; + else if (startsWith("random", order)) + config.runOrder = TestRunOrder::Randomized; + else + return ParserResult::runtimeError("Unrecognised ordering: '" + order + '\''); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setRngSeed = [&](std::string const &seed) { + if (seed == "time") { + config.rngSeed = generateRandomSeed(GenerateFrom::Time); + return ParserResult::ok(ParseResultType::Matched); + } else if (seed == "random-device") { + config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice); + return ParserResult::ok(ParseResultType::Matched); + } - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setTestOrder = [&]( std::string const& order ) { - if( startsWith( "declared", order ) ) - config.runOrder = TestRunOrder::Declared; - else if( startsWith( "lexical", order ) ) - config.runOrder = TestRunOrder::LexicographicallySorted; - else if( startsWith( "random", order ) ) - config.runOrder = TestRunOrder::Randomized; - else - return ParserResult::runtimeError( "Unrecognised ordering: '" + order + '\'' ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setRngSeed = [&]( std::string const& seed ) { - if( seed == "time" ) { - config.rngSeed = generateRandomSeed(GenerateFrom::Time); - return ParserResult::ok(ParseResultType::Matched); - } else if (seed == "random-device") { - config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice); - return ParserResult::ok(ParseResultType::Matched); - } + // TODO: ideally we should be parsing uint32_t directly + // fix this later when we add new parse overload + auto parsedSeed = parseUInt(seed, 0); + if (!parsedSeed) { + return ParserResult::runtimeError("Could not parse '" + seed + "' as seed"); + } + config.rngSeed = *parsedSeed; + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setDefaultColourMode = [&](std::string const &colourMode) { + Optional maybeMode = Catch::Detail::stringToColourMode(toLower(colourMode)); + if (!maybeMode) { + return ParserResult::runtimeError( + "colour mode must be one of: default, ansi, win32, " + "or none. '" + + colourMode + "' is not recognised"); + } + auto mode = *maybeMode; + if (!isColourImplAvailable(mode)) { + return ParserResult::runtimeError( + "colour mode '" + colourMode + "' is not supported in this binary"); + } + config.defaultColourMode = mode; + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setWaitForKeypress = [&](std::string const &keypress) { + auto keypressLc = toLower(keypress); + if (keypressLc == "never") + config.waitForKeypress = WaitForKeypress::Never; + else if (keypressLc == "start") + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if (keypressLc == "exit") + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if (keypressLc == "both") + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError("keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setVerbosity = [&](std::string const &verbosity) { + auto lcVerbosity = toLower(verbosity); + if (lcVerbosity == "quiet") + config.verbosity = Verbosity::Quiet; + else if (lcVerbosity == "normal") + config.verbosity = Verbosity::Normal; + else if (lcVerbosity == "high") + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError("Unrecognised verbosity, '" + verbosity + '\''); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setReporter = [&](std::string const &userReporterSpec) { + if (userReporterSpec.empty()) { + return ParserResult::runtimeError("Received empty reporter spec."); + } - // TODO: ideally we should be parsing uint32_t directly - // fix this later when we add new parse overload - auto parsedSeed = parseUInt( seed, 0 ); - if ( !parsedSeed ) { - return ParserResult::runtimeError( "Could not parse '" + seed + "' as seed" ); - } - config.rngSeed = *parsedSeed; - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setDefaultColourMode = [&]( std::string const& colourMode ) { - Optional maybeMode = Catch::Detail::stringToColourMode(toLower( colourMode )); - if ( !maybeMode ) { - return ParserResult::runtimeError( - "colour mode must be one of: default, ansi, win32, " - "or none. '" + - colourMode + "' is not recognised" ); - } - auto mode = *maybeMode; - if ( !isColourImplAvailable( mode ) ) { - return ParserResult::runtimeError( - "colour mode '" + colourMode + - "' is not supported in this binary" ); - } - config.defaultColourMode = mode; - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setWaitForKeypress = [&]( std::string const& keypress ) { - auto keypressLc = toLower( keypress ); - if (keypressLc == "never") - config.waitForKeypress = WaitForKeypress::Never; - else if( keypressLc == "start" ) - config.waitForKeypress = WaitForKeypress::BeforeStart; - else if( keypressLc == "exit" ) - config.waitForKeypress = WaitForKeypress::BeforeExit; - else if( keypressLc == "both" ) - config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; - else - return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setVerbosity = [&]( std::string const& verbosity ) { - auto lcVerbosity = toLower( verbosity ); - if( lcVerbosity == "quiet" ) - config.verbosity = Verbosity::Quiet; - else if( lcVerbosity == "normal" ) - config.verbosity = Verbosity::Normal; - else if( lcVerbosity == "high" ) - config.verbosity = Verbosity::High; - else - return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + '\'' ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setReporter = [&]( std::string const& userReporterSpec ) { - if ( userReporterSpec.empty() ) { - return ParserResult::runtimeError( "Received empty reporter spec." ); - } + Optional parsed = parseReporterSpec(userReporterSpec); + if (!parsed) { + return ParserResult::runtimeError( + "Could not parse reporter spec '" + userReporterSpec + "'"); + } - Optional parsed = - parseReporterSpec( userReporterSpec ); - if ( !parsed ) { - return ParserResult::runtimeError( - "Could not parse reporter spec '" + userReporterSpec + - "'" ); - } + auto const &reporterSpec = *parsed; - auto const& reporterSpec = *parsed; + auto const &factories = getRegistryHub().getReporterRegistry().getFactories(); + auto result = factories.find(reporterSpec.name()); - auto const& factories = - getRegistryHub().getReporterRegistry().getFactories(); - auto result = factories.find( reporterSpec.name() ); + if (result == factories.end()) { + return ParserResult::runtimeError( + "Unrecognized reporter, '" + reporterSpec.name() + "'. Check available with --list-reporters"); + } - if ( result == factories.end() ) { - return ParserResult::runtimeError( - "Unrecognized reporter, '" + reporterSpec.name() + - "'. Check available with --list-reporters" ); - } + const bool hadOutputFile = reporterSpec.outputFile().some(); + config.reporterSpecifications.push_back(CATCH_MOVE(*parsed)); + // It would be enough to check this only once at the very end, but + // there is not a place where we could call this check, so do it + // every time it could fail. For valid inputs, this is still called + // at most once. + if (!hadOutputFile) { + int n_reporters_without_file = 0; + for (auto const &spec : config.reporterSpecifications) { + if (spec.outputFile().none()) { + n_reporters_without_file++; + } + } + if (n_reporters_without_file > 1) { + return ParserResult::runtimeError("Only one reporter may have unspecified output file."); + } + } + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setShardCount = [&](std::string const &shardCount) { + auto parsedCount = parseUInt(shardCount); + if (!parsedCount) { + return ParserResult::runtimeError( + "Could not parse '" + shardCount + "' as shard count"); + } + if (*parsedCount == 0) { + return ParserResult::runtimeError( + "Shard count must be positive"); + } + config.shardCount = *parsedCount; + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setBenchmarkSamples = [&](std::string const &samples) { + auto parsedSamples = parseUInt(samples); + if (!parsedSamples) { + return ParserResult::runtimeError( + "Could not parse '" + samples + "' as benchmark samples"); + } + if (*parsedSamples == 0) { + return ParserResult::runtimeError( + "Benchmark samples must be greater than 0"); + } + config.benchmarkSamples = *parsedSamples; + return ParserResult::ok(ParseResultType::Matched); + }; - const bool hadOutputFile = reporterSpec.outputFile().some(); - config.reporterSpecifications.push_back( CATCH_MOVE( *parsed ) ); - // It would be enough to check this only once at the very end, but - // there is not a place where we could call this check, so do it - // every time it could fail. For valid inputs, this is still called - // at most once. - if (!hadOutputFile) { - int n_reporters_without_file = 0; - for (auto const& spec : config.reporterSpecifications) { - if (spec.outputFile().none()) { - n_reporters_without_file++; - } - } - if (n_reporters_without_file > 1) { - return ParserResult::runtimeError( "Only one reporter may have unspecified output file." ); - } - } + auto const setShardIndex = [&](std::string const &shardIndex) { + auto parsedIndex = parseUInt(shardIndex); + if (!parsedIndex) { + return ParserResult::runtimeError( + "Could not parse '" + shardIndex + "' as shard index"); + } + config.shardIndex = *parsedIndex; + return ParserResult::ok(ParseResultType::Matched); + }; - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setShardCount = [&]( std::string const& shardCount ) { - auto parsedCount = parseUInt( shardCount ); - if ( !parsedCount ) { - return ParserResult::runtimeError( - "Could not parse '" + shardCount + "' as shard count" ); - } - if ( *parsedCount == 0 ) { - return ParserResult::runtimeError( - "Shard count must be positive" ); - } - config.shardCount = *parsedCount; - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setBenchmarkSamples = [&]( std::string const& samples ) { - auto parsedSamples = parseUInt( samples ); - if ( !parsedSamples ) { - return ParserResult::runtimeError( - "Could not parse '" + samples + "' as benchmark samples" ); - } - if ( *parsedSamples == 0 ) { - return ParserResult::runtimeError( - "Benchmark samples must be greater than 0" ); - } - config.benchmarkSamples = *parsedSamples; - return ParserResult::ok( ParseResultType::Matched ); - }; + auto const setSectionFilter = [&](std::string const §ionFilter) { + config.pathFilters.emplace_back(PathFilter::For::Section, trim(sectionFilter)); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setGeneratorFilter = [&](std::string const &generatorFilter) { + if (generatorFilter != "*") { + // TODO: avoid re-parsing the index? + auto parsedIndex = parseUInt(generatorFilter); + if (!parsedIndex) { + return ParserResult::runtimeError("Could not parse '" + generatorFilter + "' as generator index"); + } + } + config.useNewPathFilteringBehaviour = true; + config.pathFilters.emplace_back(PathFilter::For::Generator, trim(generatorFilter)); + return ParserResult::ok(ParseResultType::Matched); + }; + // Copy-capturing other `setFoo` functions enables calling them later, + // as the config ref remains valid, but the local lambda vars won't. + auto const setPathFilter = [=, &config](std::string const &pathFilter) { + config.useNewPathFilteringBehaviour = true; + if (pathFilter.size() < 3) { + return ParserResult::runtimeError( + "Path filter '" + pathFilter + "' is too short"); + } + if (startsWith(pathFilter, "g:")) { + return setGeneratorFilter(pathFilter.substr(2)); + } + if (startsWith(pathFilter, "c:")) { + return setSectionFilter(pathFilter.substr(2)); + } + return ParserResult::runtimeError("Path filter '" + pathFilter + "' has unknown type prefix"); + }; - auto const setShardIndex = [&](std::string const& shardIndex) { - auto parsedIndex = parseUInt( shardIndex ); - if ( !parsedIndex ) { - return ParserResult::runtimeError( - "Could not parse '" + shardIndex + "' as shard index" ); - } - config.shardIndex = *parsedIndex; - return ParserResult::ok( ParseResultType::Matched ); - }; + auto cli + = ExeName(config.processName) + | Help(config.showHelp) + | Opt(config.showSuccessfulTests) + ["-s"]["--success"]("include successful tests in output") + | Opt(config.shouldDebugBreak) + ["-b"]["--break"]("break into debugger on failure") + | Opt(config.noThrow) + ["-e"]["--nothrow"]("skip exception tests") + | Opt(config.showInvisibles) + ["-i"]["--invisibles"]("show invisibles (tabs, newlines)") + | Opt(config.defaultOutputFilename, "filename") + ["-o"]["--out"]("default output filename") + | Opt(accept_many, setReporter, "name[::key=value]*") + ["-r"]["--reporter"]("reporter to use (defaults to console)") + | Opt(config.name, "name") + ["-n"]["--name"]("suite name") + | Opt([&](bool) { config.abortAfter = 1; }) + ["-a"]["--abort"]("abort at first failure") + | Opt([&](int x) { config.abortAfter = x; }, "no. failures") + ["-x"]["--abortx"]("abort after x failures") + | Opt(accept_many, setWarning, "warning name") + ["-w"]["--warn"]("enable warnings") + | Opt([&](bool flag) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no") + ["-d"]["--durations"]("show test durations") + | Opt(config.minDuration, "seconds") + ["-D"]["--min-duration"]("show test durations for tests taking at least the given number of seconds") + | Opt(loadTestNamesFromFile, "filename") + ["-f"]["--input-file"]("load test names to run from a file") + | Opt(config.filenamesAsTags) + ["-#"]["--filenames-as-tags"]("adds a tag for the filename") + | Opt(accept_many, setSectionFilter, "section name") + ["-c"]["--section"]("specify section to run") + | Opt(accept_many, setGeneratorFilter, "index spec") + ["-g"]["--generator-index"]("specify generator elements to try") + | Opt(accept_many, setPathFilter, "path filter spec") + ["-p"]["--path-filter"]("qualified path filter") + | Opt(setVerbosity, "quiet|normal|high") + ["-v"]["--verbosity"]("set output verbosity") + | Opt(config.listTests) + ["--list-tests"]("list all/matching test cases") + | Opt(config.listTags) + ["--list-tags"]("list all/matching tags") + | Opt(config.listReporters) + ["--list-reporters"]("list all available reporters") + | Opt(config.listListeners) + ["--list-listeners"]("list all listeners") + | Opt(setTestOrder, "decl|lex|rand") + ["--order"]("test case order (defaults to rand)") + | Opt(setRngSeed, "'time'|'random-device'|number") + ["--rng-seed"]("set a specific seed for random numbers") + | Opt(setDefaultColourMode, "ansi|win32|none|default") + ["--colour-mode"]("what color mode should be used as default") + | Opt(config.libIdentify) + ["--libidentify"]("report name and version according to libidentify standard") + | Opt(setWaitForKeypress, "never|start|exit|both") + ["--wait-for-keypress"]("waits for a keypress before exiting") + | Opt(config.skipBenchmarks) + ["--skip-benchmarks"]("disable running benchmarks") + | Opt(setBenchmarkSamples, "samples") + ["--benchmark-samples"]("number of samples to collect (default: 100)") + | Opt(config.benchmarkResamples, "resamples") + ["--benchmark-resamples"]("number of resamples for the bootstrap (default: 100000)") + | Opt(config.benchmarkConfidenceInterval, "confidence interval") + ["--benchmark-confidence-interval"]("confidence interval for the bootstrap (between 0 and 1, default: 0.95)") + | Opt(config.benchmarkNoAnalysis) + ["--benchmark-no-analysis"]("perform only measurements; do not perform any analysis") + | Opt(config.benchmarkWarmupTime, "benchmarkWarmupTime") + ["--benchmark-warmup-time"]("amount of time in milliseconds spent on warming up each test (default: 100)") + | Opt(setShardCount, "shard count") + ["--shard-count"]("split the tests to execute into this many groups") + | Opt(setShardIndex, "shard index") + ["--shard-index"]("index of the group of tests to execute (see --shard-count)") + | Opt(config.allowZeroTests) + ["--allow-running-no-tests"]("Treat 'No tests run' as a success") + | Opt(config.prematureExitGuardFilePath, "path") + ["--premature-exit-guard-file"]("create a file before running tests and delete it during clean exit") + | Arg(config.testsOrTags, "test name|pattern|tags")("which test or tests to use"); - auto const setSectionFilter = [&]( std::string const& sectionFilter ) { - config.pathFilters.emplace_back( PathFilter::For::Section, trim(sectionFilter) ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setGeneratorFilter = [&]( std::string const& generatorFilter ) { - if (generatorFilter != "*") { - // TODO: avoid re-parsing the index? - auto parsedIndex = parseUInt( generatorFilter ); - if ( !parsedIndex ) { - return ParserResult::runtimeError( "Could not parse '" + - generatorFilter + - "' as generator index" ); - } - } - config.useNewPathFilteringBehaviour = true; - config.pathFilters.emplace_back( PathFilter::For::Generator, trim(generatorFilter) ); - return ParserResult::ok( ParseResultType::Matched ); - }; - // Copy-capturing other `setFoo` functions enables calling them later, - // as the config ref remains valid, but the local lambda vars won't. - auto const setPathFilter = [=, &config]( std::string const& pathFilter ) { - config.useNewPathFilteringBehaviour = true; - if ( pathFilter.size() < 3 ) { - return ParserResult::runtimeError( - "Path filter '" + pathFilter + "' is too short" ); - } - if ( startsWith( pathFilter, "g:" ) ) { - return setGeneratorFilter( pathFilter.substr( 2 ) ); - } - if ( startsWith( pathFilter, "c:" ) ) { - return setSectionFilter( pathFilter.substr( 2 ) ); - } - return ParserResult::runtimeError( "Path filter '" + pathFilter + - "' has unknown type prefix" ); - }; - - - auto cli - = ExeName( config.processName ) - | Help( config.showHelp ) - | Opt( config.showSuccessfulTests ) - ["-s"]["--success"] - ( "include successful tests in output" ) - | Opt( config.shouldDebugBreak ) - ["-b"]["--break"] - ( "break into debugger on failure" ) - | Opt( config.noThrow ) - ["-e"]["--nothrow"] - ( "skip exception tests" ) - | Opt( config.showInvisibles ) - ["-i"]["--invisibles"] - ( "show invisibles (tabs, newlines)" ) - | Opt( config.defaultOutputFilename, "filename" ) - ["-o"]["--out"] - ( "default output filename" ) - | Opt( accept_many, setReporter, "name[::key=value]*" ) - ["-r"]["--reporter"] - ( "reporter to use (defaults to console)" ) - | Opt( config.name, "name" ) - ["-n"]["--name"] - ( "suite name" ) - | Opt( [&]( bool ){ config.abortAfter = 1; } ) - ["-a"]["--abort"] - ( "abort at first failure" ) - | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) - ["-x"]["--abortx"] - ( "abort after x failures" ) - | Opt( accept_many, setWarning, "warning name" ) - ["-w"]["--warn"] - ( "enable warnings" ) - | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) - ["-d"]["--durations"] - ( "show test durations" ) - | Opt( config.minDuration, "seconds" ) - ["-D"]["--min-duration"] - ( "show test durations for tests taking at least the given number of seconds" ) - | Opt( loadTestNamesFromFile, "filename" ) - ["-f"]["--input-file"] - ( "load test names to run from a file" ) - | Opt( config.filenamesAsTags ) - ["-#"]["--filenames-as-tags"] - ( "adds a tag for the filename" ) - | Opt( accept_many, setSectionFilter, "section name" ) - ["-c"]["--section"] - ( "specify section to run" ) - | Opt( accept_many, setGeneratorFilter, "index spec" ) - ["-g"]["--generator-index"] - ( "specify generator elements to try" ) - | Opt( accept_many, setPathFilter, "path filter spec" ) - ["-p"]["--path-filter"] - ( "qualified path filter" ) - | Opt( setVerbosity, "quiet|normal|high" ) - ["-v"]["--verbosity"] - ( "set output verbosity" ) - | Opt( config.listTests ) - ["--list-tests"] - ( "list all/matching test cases" ) - | Opt( config.listTags ) - ["--list-tags"] - ( "list all/matching tags" ) - | Opt( config.listReporters ) - ["--list-reporters"] - ( "list all available reporters" ) - | Opt( config.listListeners ) - ["--list-listeners"] - ( "list all listeners" ) - | Opt( setTestOrder, "decl|lex|rand" ) - ["--order"] - ( "test case order (defaults to rand)" ) - | Opt( setRngSeed, "'time'|'random-device'|number" ) - ["--rng-seed"] - ( "set a specific seed for random numbers" ) - | Opt( setDefaultColourMode, "ansi|win32|none|default" ) - ["--colour-mode"] - ( "what color mode should be used as default" ) - | Opt( config.libIdentify ) - ["--libidentify"] - ( "report name and version according to libidentify standard" ) - | Opt( setWaitForKeypress, "never|start|exit|both" ) - ["--wait-for-keypress"] - ( "waits for a keypress before exiting" ) - | Opt( config.skipBenchmarks) - ["--skip-benchmarks"] - ( "disable running benchmarks") - | Opt( setBenchmarkSamples, "samples" ) - ["--benchmark-samples"] - ( "number of samples to collect (default: 100)" ) - | Opt( config.benchmarkResamples, "resamples" ) - ["--benchmark-resamples"] - ( "number of resamples for the bootstrap (default: 100000)" ) - | Opt( config.benchmarkConfidenceInterval, "confidence interval" ) - ["--benchmark-confidence-interval"] - ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" ) - | Opt( config.benchmarkNoAnalysis ) - ["--benchmark-no-analysis"] - ( "perform only measurements; do not perform any analysis" ) - | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" ) - ["--benchmark-warmup-time"] - ( "amount of time in milliseconds spent on warming up each test (default: 100)" ) - | Opt( setShardCount, "shard count" ) - ["--shard-count"] - ( "split the tests to execute into this many groups" ) - | Opt( setShardIndex, "shard index" ) - ["--shard-index"] - ( "index of the group of tests to execute (see --shard-count)" ) - | Opt( config.allowZeroTests ) - ["--allow-running-no-tests"] - ( "Treat 'No tests run' as a success" ) - | Opt( config.prematureExitGuardFilePath, "path" ) - ["--premature-exit-guard-file"] - ( "create a file before running tests and delete it during clean exit" ) - | Arg( config.testsOrTags, "test name|pattern|tags" ) - ( "which test or tests to use" ); - - return cli; - } + return cli; + } } // end namespace Catch - #if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" #endif - - #include #include #include namespace Catch { - ColourImpl::~ColourImpl() = default; + ColourImpl::~ColourImpl() = default; - ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) { - return ColourGuard(colourCode, this ); + ColourImpl::ColourGuard ColourImpl::guardColour(Colour::Code colourCode) { + return ColourGuard(colourCode, this); + } + + void ColourImpl::ColourGuard::engageImpl(std::ostream &stream) { + assert(&stream == &m_colourImpl->m_stream->stream() && "Engaging colour guard for different stream than used by the " + "parent colour implementation"); + static_cast(stream); + + m_engaged = true; + m_colourImpl->use(m_code); + } + + ColourImpl::ColourGuard::ColourGuard(Colour::Code code, + ColourImpl const *colour) + : m_colourImpl(colour), m_code(code) { + } + ColourImpl::ColourGuard::ColourGuard(ColourGuard &&rhs) noexcept + : m_colourImpl(rhs.m_colourImpl), m_code(rhs.m_code), m_engaged(rhs.m_engaged) { + rhs.m_engaged = false; + } + ColourImpl::ColourGuard & + ColourImpl::ColourGuard::operator=(ColourGuard &&rhs) noexcept { + using std::swap; + swap(m_colourImpl, rhs.m_colourImpl); + swap(m_code, rhs.m_code); + swap(m_engaged, rhs.m_engaged); + + return *this; + } + ColourImpl::ColourGuard::~ColourGuard() { + if (m_engaged) { + m_colourImpl->use(Colour::None); } + } - void ColourImpl::ColourGuard::engageImpl( std::ostream& stream ) { - assert( &stream == &m_colourImpl->m_stream->stream() && - "Engaging colour guard for different stream than used by the " - "parent colour implementation" ); - static_cast( stream ); + ColourImpl::ColourGuard & + ColourImpl::ColourGuard::engage(std::ostream &stream) & { + engageImpl(stream); + return *this; + } - m_engaged = true; - m_colourImpl->use( m_code ); - } + ColourImpl::ColourGuard && + ColourImpl::ColourGuard::engage(std::ostream &stream) && { + engageImpl(stream); + return CATCH_MOVE(*this); + } - ColourImpl::ColourGuard::ColourGuard( Colour::Code code, - ColourImpl const* colour ): - m_colourImpl( colour ), m_code( code ) { - } - ColourImpl::ColourGuard::ColourGuard( ColourGuard&& rhs ) noexcept: - m_colourImpl( rhs.m_colourImpl ), - m_code( rhs.m_code ), - m_engaged( rhs.m_engaged ) { - rhs.m_engaged = false; - } - ColourImpl::ColourGuard& - ColourImpl::ColourGuard::operator=( ColourGuard&& rhs ) noexcept { - using std::swap; - swap( m_colourImpl, rhs.m_colourImpl ); - swap( m_code, rhs.m_code ); - swap( m_engaged, rhs.m_engaged ); - - return *this; - } - ColourImpl::ColourGuard::~ColourGuard() { - if ( m_engaged ) { - m_colourImpl->use( Colour::None ); - } - } - - ColourImpl::ColourGuard& - ColourImpl::ColourGuard::engage( std::ostream& stream ) & { - engageImpl( stream ); - return *this; - } - - ColourImpl::ColourGuard&& - ColourImpl::ColourGuard::engage( std::ostream& stream ) && { - engageImpl( stream ); - return CATCH_MOVE(*this); - } - - namespace { - //! A do-nothing implementation of colour, used as fallback for unknown - //! platforms, and when the user asks to deactivate all colours. - class NoColourImpl final : public ColourImpl { - public: - NoColourImpl( IStream* stream ): ColourImpl( stream ) {} - - private: - void use( Colour::Code ) const override {} - }; - } // namespace + namespace { + //! A do-nothing implementation of colour, used as fallback for unknown + //! platforms, and when the user asks to deactivate all colours. + class NoColourImpl final : public ColourImpl { + public: + NoColourImpl(IStream *stream) + : ColourImpl(stream) {} + private: + void use(Colour::Code) const override {} + }; + } // namespace } // namespace Catch - -#if defined ( CATCH_CONFIG_COLOUR_WIN32 ) ///////////////////////////////////////// +#if defined(CATCH_CONFIG_COLOUR_WIN32) ///////////////////////////////////////// namespace Catch { -namespace { + namespace { class Win32ColourImpl final : public ColourImpl { public: - Win32ColourImpl(IStream* stream): - ColourImpl(stream) { - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), - &csbiInfo ); - originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); - originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); - } + Win32ColourImpl(IStream *stream) + : ColourImpl(stream) { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), + &csbiInfo); + originalForegroundAttributes = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); + originalBackgroundAttributes = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } - static bool useImplementationForStream(IStream const& stream) { - // Win32 text colour APIs can only be used on console streams - // We cannot check that the output hasn't been redirected, - // so we just check that the original stream is console stream. - return stream.isConsole(); - } + static bool useImplementationForStream(IStream const &stream) { + // Win32 text colour APIs can only be used on console streams + // We cannot check that the output hasn't been redirected, + // so we just check that the original stream is console stream. + return stream.isConsole(); + } private: - void use( Colour::Code _colourCode ) const override { - switch( _colourCode ) { - case Colour::None: return setTextAttribute( originalForegroundAttributes ); - case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Red: return setTextAttribute( FOREGROUND_RED ); - case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); - case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); - case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); - case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); - case Colour::Grey: return setTextAttribute( 0 ); + void use(Colour::Code _colourCode) const override { + switch (_colourCode) { + case Colour::None: return setTextAttribute(originalForegroundAttributes); + case Colour::White: return setTextAttribute(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); + case Colour::Red: return setTextAttribute(FOREGROUND_RED); + case Colour::Green: return setTextAttribute(FOREGROUND_GREEN); + case Colour::Blue: return setTextAttribute(FOREGROUND_BLUE); + case Colour::Cyan: return setTextAttribute(FOREGROUND_BLUE | FOREGROUND_GREEN); + case Colour::Yellow: return setTextAttribute(FOREGROUND_RED | FOREGROUND_GREEN); + case Colour::Grey: return setTextAttribute(0); - case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); - case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); - case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); - case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::LightGrey: return setTextAttribute(FOREGROUND_INTENSITY); + case Colour::BrightRed: return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_RED); + case Colour::BrightGreen: return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN); + case Colour::BrightWhite: return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); + case Colour::BrightYellow: return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN); - case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); + case Colour::Bright: CATCH_INTERNAL_ERROR("not a colour"); - default: - CATCH_ERROR( "Unknown colour requested" ); - } + default: + CATCH_ERROR("Unknown colour requested"); } + } - void setTextAttribute( WORD _textAttribute ) const { - SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), - _textAttribute | - originalBackgroundAttributes ); - } - WORD originalForegroundAttributes; - WORD originalBackgroundAttributes; + void setTextAttribute(WORD _textAttribute) const { + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), + _textAttribute | originalBackgroundAttributes); + } + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; }; -} // end anon namespace + } // namespace } // end namespace Catch #endif // Windows/ ANSI/ None - -#if defined( CATCH_PLATFORM_LINUX ) \ - || defined( CATCH_PLATFORM_MAC ) \ - || defined( __GLIBC__ ) \ - || (defined( __FreeBSD__ ) \ - /* PlayStation platform does not have `isatty()` */ \ - && !defined(CATCH_PLATFORM_PLAYSTATION)) \ - || defined( CATCH_PLATFORM_QNX ) -# define CATCH_INTERNAL_HAS_ISATTY -# include +#if defined(CATCH_PLATFORM_LINUX) \ + || defined(CATCH_PLATFORM_MAC) \ + || defined(__GLIBC__) \ + || (defined(__FreeBSD__) /* PlayStation platform does not have `isatty()` */ \ + && !defined(CATCH_PLATFORM_PLAYSTATION)) \ + || defined(CATCH_PLATFORM_QNX) +#define CATCH_INTERNAL_HAS_ISATTY +#include #endif namespace Catch { -namespace { + namespace { class ANSIColourImpl final : public ColourImpl { public: - ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} + ANSIColourImpl(IStream *stream) + : ColourImpl(stream) {} - static bool useImplementationForStream(IStream const& stream) { - // This is kinda messy due to trying to support a bunch of - // different platforms at once. - // The basic idea is that if we are asked to do autodetection (as - // opposed to being told to use posixy colours outright), then we - // only want to use the colours if we are writing to console. - // However, console might be redirected, so we make an attempt at - // checking for that on platforms where we know how to do that. - bool useColour = stream.isConsole(); -#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \ - !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) ) - ErrnoGuard _; // for isatty - useColour = useColour && isatty( STDOUT_FILENO ); -# endif -# if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE ) - useColour = useColour && !isDebuggerActive(); -# endif + static bool useImplementationForStream(IStream const &stream) { + // This is kinda messy due to trying to support a bunch of + // different platforms at once. + // The basic idea is that if we are asked to do autodetection (as + // opposed to being told to use posixy colours outright), then we + // only want to use the colours if we are writing to console. + // However, console might be redirected, so we make an attempt at + // checking for that on platforms where we know how to do that. + bool useColour = stream.isConsole(); +#if defined(CATCH_INTERNAL_HAS_ISATTY) && !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) + ErrnoGuard _; // for isatty + useColour = useColour && isatty(STDOUT_FILENO); +#endif +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) + useColour = useColour && !isDebuggerActive(); +#endif - return useColour; - } + return useColour; + } private: - void use( Colour::Code _colourCode ) const override { - auto setColour = [&out = - m_stream->stream()]( char const* escapeCode ) { - // The escape sequence must be flushed to console, otherwise - // if stdin and stderr are intermixed, we'd get accidentally - // coloured output. - out << '\033' << escapeCode << std::flush; - }; - switch( _colourCode ) { - case Colour::None: - case Colour::White: return setColour( "[0m" ); - case Colour::Red: return setColour( "[0;31m" ); - case Colour::Green: return setColour( "[0;32m" ); - case Colour::Blue: return setColour( "[0;34m" ); - case Colour::Cyan: return setColour( "[0;36m" ); - case Colour::Yellow: return setColour( "[0;33m" ); - case Colour::Grey: return setColour( "[1;30m" ); + void use(Colour::Code _colourCode) const override { + auto setColour = [&out = m_stream->stream()](char const *escapeCode) { + // The escape sequence must be flushed to console, otherwise + // if stdin and stderr are intermixed, we'd get accidentally + // coloured output. + out << '\033' << escapeCode << std::flush; + }; + switch (_colourCode) { + case Colour::None: + case Colour::White: return setColour("[0m"); + case Colour::Red: return setColour("[0;31m"); + case Colour::Green: return setColour("[0;32m"); + case Colour::Blue: return setColour("[0;34m"); + case Colour::Cyan: return setColour("[0;36m"); + case Colour::Yellow: return setColour("[0;33m"); + case Colour::Grey: return setColour("[1;30m"); - case Colour::LightGrey: return setColour( "[0;37m" ); - case Colour::BrightRed: return setColour( "[1;31m" ); - case Colour::BrightGreen: return setColour( "[1;32m" ); - case Colour::BrightWhite: return setColour( "[1;37m" ); - case Colour::BrightYellow: return setColour( "[1;33m" ); + case Colour::LightGrey: return setColour("[0;37m"); + case Colour::BrightRed: return setColour("[1;31m"); + case Colour::BrightGreen: return setColour("[1;32m"); + case Colour::BrightWhite: return setColour("[1;37m"); + case Colour::BrightYellow: return setColour("[1;33m"); - case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); - default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); - } + case Colour::Bright: CATCH_INTERNAL_ERROR("not a colour"); + default: CATCH_INTERNAL_ERROR("Unknown colour requested"); } + } }; -} // end anon namespace + } // namespace } // end namespace Catch namespace Catch { - Detail::unique_ptr makeColourImpl( ColourMode colourSelection, - IStream* stream ) { -#if defined( CATCH_CONFIG_COLOUR_WIN32 ) - if ( colourSelection == ColourMode::Win32 ) { - return Detail::make_unique( stream ); - } + Detail::unique_ptr makeColourImpl(ColourMode colourSelection, + IStream *stream) { +#if defined(CATCH_CONFIG_COLOUR_WIN32) + if (colourSelection == ColourMode::Win32) { + return Detail::make_unique(stream); + } #endif - if ( colourSelection == ColourMode::ANSI ) { - return Detail::make_unique( stream ); - } - if ( colourSelection == ColourMode::None ) { - return Detail::make_unique( stream ); - } - - if ( colourSelection == ColourMode::PlatformDefault) { -#if defined( CATCH_CONFIG_COLOUR_WIN32 ) - if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { - return Detail::make_unique( stream ); - } -#endif - if ( ANSIColourImpl::useImplementationForStream( *stream ) ) { - return Detail::make_unique( stream ); - } - return Detail::make_unique( stream ); - } - - CATCH_ERROR( "Could not create colour impl for selection " << static_cast(colourSelection) ); + if (colourSelection == ColourMode::ANSI) { + return Detail::make_unique(stream); + } + if (colourSelection == ColourMode::None) { + return Detail::make_unique(stream); } - bool isColourImplAvailable( ColourMode colourSelection ) { - switch ( colourSelection ) { -#if defined( CATCH_CONFIG_COLOUR_WIN32 ) - case ColourMode::Win32: + if (colourSelection == ColourMode::PlatformDefault) { +#if defined(CATCH_CONFIG_COLOUR_WIN32) + if (Win32ColourImpl::useImplementationForStream(*stream)) { + return Detail::make_unique(stream); + } #endif - case ColourMode::ANSI: - case ColourMode::None: - case ColourMode::PlatformDefault: - return true; - default: - return false; - } + if (ANSIColourImpl::useImplementationForStream(*stream)) { + return Detail::make_unique(stream); + } + return Detail::make_unique(stream); } + CATCH_ERROR("Could not create colour impl for selection " << static_cast(colourSelection)); + } + + bool isColourImplAvailable(ColourMode colourSelection) { + switch (colourSelection) { +#if defined(CATCH_CONFIG_COLOUR_WIN32) + case ColourMode::Win32: +#endif + case ColourMode::ANSI: + case ColourMode::None: + case ColourMode::PlatformDefault: + return true; + default: + return false; + } + } } // end namespace Catch #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif - - - namespace Catch { - Context Context::currentContext; - - Context& getCurrentMutableContext() { - return Context::currentContext; - } - - SimplePcg32& sharedRng() { - static SimplePcg32 s_rng; - return s_rng; - } - -} - + Context Context::currentContext; + Context &getCurrentMutableContext() { + return Context::currentContext; + } + SimplePcg32 &sharedRng() { + static SimplePcg32 s_rng; + return s_rng; + } +} // namespace Catch #include #if defined(CATCH_CONFIG_ANDROID_LOGWRITE) #include - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() ); - } - } +namespace Catch { + void writeToDebugConsole(std::string const &text) { + __android_log_write(ANDROID_LOG_DEBUG, "Catch", text.c_str()); + } +} // namespace Catch #elif defined(CATCH_PLATFORM_WINDOWS) - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); - } - } +namespace Catch { + void writeToDebugConsole(std::string const &text) { + ::OutputDebugStringA(text.c_str()); + } +} // namespace Catch #else - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; - } - } +namespace Catch { + void writeToDebugConsole(std::string const &text) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } +} #endif // Platform - - #if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) -# include -# include -# include -# include -# include +#include +#include +#include +#include +#include #ifdef __apple_build_version__ - // These headers will only compile with AppleClang (XCode) - // For other compilers (Clang, GCC, ... ) we need to exclude them -# include +// These headers will only compile with AppleClang (XCode) +// For other compilers (Clang, GCC, ... ) we need to exclude them +#include #endif - namespace Catch { - #ifdef __apple_build_version__ - // The following function is taken directly from the following technical note: - // https://developer.apple.com/library/archive/qa/qa1361/_index.html +namespace Catch { +#ifdef __apple_build_version__ + // The following function is taken directly from the following technical note: + // https://developer.apple.com/library/archive/qa/qa1361/_index.html - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive(){ - int mib[4]; - struct kinfo_proc info; - std::size_t size; + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { + int mib[4]; + struct kinfo_proc info; + std::size_t size; - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. - info.kp_proc.p_flag = 0; + info.kp_proc.p_flag = 0; - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); - // Call sysctl. + // Call sysctl. - size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { - Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush; - return false; - } + size = sizeof(info); + if (sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" + << std::flush; + return false; + } - // We're being debugged if the P_TRACED flag is set. + // We're being debugged if the P_TRACED flag is set. - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); - } - #else - bool isDebuggerActive() { - // We need to find another way to determine this for non-appleclang compilers on macOS - return false; - } - #endif - } // namespace Catch + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +#else + bool isDebuggerActive() { + // We need to find another way to determine this for non-appleclang compilers on macOS + return false; + } +#endif +} // namespace Catch #elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX) - #include - #include +#include +#include - namespace Catch{ - // The standard POSIX way of detecting a debugger is to attempt to - // ptrace() the process, but this needs to be done from a child and not - // this process itself to still allow attaching to this process later - // if wanted, so is rather heavy. Under Linux we have the PID of the - // "debugger" (which doesn't need to be gdb, of course, it could also - // be strace, for example) in /proc/$PID/status, so just get it from - // there instead. - bool isDebuggerActive(){ - // Libstdc++ has a bug, where std::ifstream sets errno to 0 - // This way our users can properly assert over errno values - ErrnoGuard guard; - std::ifstream in("/proc/self/status"); - for( std::string line; std::getline(in, line); ) { - static const int PREFIX_LEN = 11; - if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { - // We're traced if the PID is not 0 and no other PID starts - // with 0 digit, so it's enough to check for just a single - // character. - return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; - } - } +namespace Catch { + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive() { + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for (std::string line; std::getline(in, line);) { + static const int PREFIX_LEN = 11; + if (line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } - return false; - } - } // namespace Catch + return false; + } +} // namespace Catch #elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } +} #elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } +} #else - namespace Catch { - bool isDebuggerActive() { return false; } - } +namespace Catch { + bool isDebuggerActive() { + return false; + } +} #endif // Platform - - - namespace Catch { - void ITransientExpression::streamReconstructedExpression( - std::ostream& os ) const { - // We can't make this function pure virtual to keep ITransientExpression - // constexpr, so we write error message instead - os << "Some class derived from ITransientExpression without overriding streamReconstructedExpression"; - } - - void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { - if( lhs.size() + rhs.size() < 40 && - lhs.find('\n') == std::string::npos && - rhs.find('\n') == std::string::npos ) - os << lhs << ' ' << op << ' ' << rhs; - else - os << lhs << '\n' << op << '\n' << rhs; - } -} - + void ITransientExpression::streamReconstructedExpression( + std::ostream &os) const { + // We can't make this function pure virtual to keep ITransientExpression + // constexpr, so we write error message instead + os << "Some class derived from ITransientExpression without overriding streamReconstructedExpression"; + } + void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs) { + if (lhs.size() + rhs.size() < 40 && lhs.find('\n') == std::string::npos && rhs.find('\n') == std::string::npos) + os << lhs << ' ' << op << ' ' << rhs; + else + os << lhs << '\n' + << op << '\n' + << rhs; + } +} // namespace Catch #include - namespace Catch { #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) - [[noreturn]] - void throw_exception(std::exception const& e) { - Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" - << "The message was: " << e.what() << '\n'; - std::terminate(); - } + [[noreturn]] + void throw_exception(std::exception const &e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } #endif - [[noreturn]] - void throw_logic_error(std::string const& msg) { - throw_exception(std::logic_error(msg)); - } + [[noreturn]] + void throw_logic_error(std::string const &msg) { + throw_exception(std::logic_error(msg)); + } - [[noreturn]] - void throw_domain_error(std::string const& msg) { - throw_exception(std::domain_error(msg)); - } - - [[noreturn]] - void throw_runtime_error(std::string const& msg) { - throw_exception(std::runtime_error(msg)); - } - - - -} // namespace Catch; + [[noreturn]] + void throw_domain_error(std::string const &msg) { + throw_exception(std::domain_error(msg)); + } + [[noreturn]] + void throw_runtime_error(std::string const &msg) { + throw_exception(std::runtime_error(msg)); + } +} // namespace Catch #include namespace Catch { - IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default; + IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default; - namespace Detail { + namespace Detail { - namespace { - // Extracts the actual name part of an enum instance - // In other words, it returns the Blue part of Bikeshed::Colour::Blue - StringRef extractInstanceName(StringRef enumInstance) { - // Find last occurrence of ":" - size_t name_start = enumInstance.size(); - while (name_start > 0 && enumInstance[name_start - 1] != ':') { - --name_start; - } - return enumInstance.substr(name_start, enumInstance.size() - name_start); - } + namespace { + // Extracts the actual name part of an enum instance + // In other words, it returns the Blue part of Bikeshed::Colour::Blue + StringRef extractInstanceName(StringRef enumInstance) { + // Find last occurrence of ":" + size_t name_start = enumInstance.size(); + while (name_start > 0 && enumInstance[name_start - 1] != ':') { + --name_start; } + return enumInstance.substr(name_start, enumInstance.size() - name_start); + } + } // namespace - std::vector parseEnums( StringRef enums ) { - auto enumValues = splitStringRef( enums, ',' ); - std::vector parsed; - parsed.reserve( enumValues.size() ); - for( auto const& enumValue : enumValues ) { - parsed.push_back(trim(extractInstanceName(enumValue))); - } - return parsed; - } + std::vector parseEnums(StringRef enums) { + auto enumValues = splitStringRef(enums, ','); + std::vector parsed; + parsed.reserve(enumValues.size()); + for (auto const &enumValue : enumValues) { + parsed.push_back(trim(extractInstanceName(enumValue))); + } + return parsed; + } - EnumInfo::~EnumInfo() = default; + EnumInfo::~EnumInfo() = default; - StringRef EnumInfo::lookup( int value ) const { - for( auto const& valueToName : m_values ) { - if( valueToName.first == value ) - return valueToName.second; - } - return "{** unexpected enum value **}"_sr; - } + StringRef EnumInfo::lookup(int value) const { + for (auto const &valueToName : m_values) { + if (valueToName.first == value) + return valueToName.second; + } + return "{** unexpected enum value **}"_sr; + } - Catch::Detail::unique_ptr makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector const& values ) { - auto enumInfo = Catch::Detail::make_unique(); - enumInfo->m_name = enumName; - enumInfo->m_values.reserve( values.size() ); - - const auto valueNames = Catch::Detail::parseEnums( allValueNames ); - assert( valueNames.size() == values.size() ); - std::size_t i = 0; - for( auto value : values ) - enumInfo->m_values.emplace_back(value, valueNames[i++]); - - return enumInfo; - } - - EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector const& values ) { - m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); - return *m_enumInfos.back(); - } - - } // Detail -} // Catch + Catch::Detail::unique_ptr makeEnumInfo(StringRef enumName, StringRef allValueNames, std::vector const &values) { + auto enumInfo = Catch::Detail::make_unique(); + enumInfo->m_name = enumName; + enumInfo->m_values.reserve(values.size()); + const auto valueNames = Catch::Detail::parseEnums(allValueNames); + assert(valueNames.size() == values.size()); + std::size_t i = 0; + for (auto value : values) + enumInfo->m_values.emplace_back(value, valueNames[i++]); + return enumInfo; + } + EnumInfo const &EnumValuesRegistry::registerEnum(StringRef enumName, StringRef allValueNames, std::vector const &values) { + m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); + return *m_enumInfos.back(); + } + } // namespace Detail +} // namespace Catch #include namespace Catch { - ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} - ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } -} - - + ErrnoGuard::ErrnoGuard() + : m_oldErrno(errno) {} + ErrnoGuard::~ErrnoGuard() { + errno = m_oldErrno; + } +} // namespace Catch #include namespace Catch { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - 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() ); - } - } - + 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()); + } } -#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; + } // namespace +#endif //! defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr&& translator ) { - m_translators.push_back( CATCH_MOVE( translator ) ); - } + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; + + void ExceptionTranslatorRegistry::registerTranslator(Detail::unique_ptr &&translator) { + m_translators.push_back(CATCH_MOVE(translator)); + } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - std::string ExceptionTranslatorRegistry::translateActiveException() const { - // Compiling a mixed mode project with MSVC means that CLR - // exceptions will be caught in (...) as well. However, these do - // do not fill-in std::current_exception and thus lead to crash - // when attempting rethrow. - // /EHa switch also causes structured exceptions to be caught - // here, but they fill-in current_exception properly, so - // at worst the output should be a little weird, instead of - // causing a crash. - if ( std::current_exception() == nullptr ) { - return "Non C++ exception. Possibly a CLR exception."; - } - - // 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(m_translators); - } - // To avoid having to handle TFE explicitly everywhere, we just - // rethrow it so that it goes back up the caller. - catch( TestFailureException& ) { - return "{ nested assertion failed }"; - } - catch( TestSkipException& ) { - return "{ nested SKIP() called }"; - } - catch( std::exception const& ex ) { - return ex.what(); - } - catch( std::string const& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return "Unknown exception"; - } + std::string ExceptionTranslatorRegistry::translateActiveException() const { + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these do + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; } + // 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(m_translators); + } + // To avoid having to handle TFE explicitly everywhere, we just + // rethrow it so that it goes back up the caller. + catch (TestFailureException &) { + return "{ nested assertion failed }"; + } catch (TestSkipException &) { + return "{ nested SKIP() called }"; + } catch (std::exception const &ex) { + return ex.what(); + } catch (std::string const &msg) { + return msg; + } catch (const char *msg) { + return msg; + } catch (...) { + return "Unknown exception"; + } + } + #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::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } #endif -} - - +} // namespace Catch /** \file * This file provides platform specific implementations of FatalConditionHandler @@ -4259,1540 +4056,1458 @@ namespace Catch { * below will need to be updated taking these assumptions into account. */ - - #include -#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) +#if !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_POSIX_SIGNALS) namespace Catch { - // If neither SEH nor signal handling is required, the handler impls - // do not have to do anything, and can be empty. - void FatalConditionHandler::engage_platform() {} - void FatalConditionHandler::disengage_platform() noexcept {} - FatalConditionHandler::FatalConditionHandler() = default; - FatalConditionHandler::~FatalConditionHandler() = default; + // If neither SEH nor signal handling is required, the handler impls + // do not have to do anything, and can be empty. + void FatalConditionHandler::engage_platform() {} + void FatalConditionHandler::disengage_platform() noexcept {} + FatalConditionHandler::FatalConditionHandler() = default; + FatalConditionHandler::~FatalConditionHandler() = default; } // end namespace Catch #endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS -#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) +#if defined(CATCH_CONFIG_WINDOWS_SEH) && defined(CATCH_CONFIG_POSIX_SIGNALS) #error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" #endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS -#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) +#if defined(CATCH_CONFIG_WINDOWS_SEH) || defined(CATCH_CONFIG_POSIX_SIGNALS) namespace { - //! Signals fatal error message to the run context - void reportFatal( char const * const message ) { - Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); - } + //! Signals fatal error message to the run context + void reportFatal(char const *const message) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition(message); + } - //! Minimal size Catch2 needs for its own fatal error handling. - //! Picked empirically, so it might not be sufficient on all - //! platforms, and for all configurations. - constexpr std::size_t minStackSizeForErrors = 32 * 1024; + //! Minimal size Catch2 needs for its own fatal error handling. + //! Picked empirically, so it might not be sufficient on all + //! platforms, and for all configurations. + constexpr std::size_t minStackSizeForErrors = 32 * 1024; } // end unnamed namespace #endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS -#if defined( CATCH_CONFIG_WINDOWS_SEH ) +#if defined(CATCH_CONFIG_WINDOWS_SEH) namespace Catch { - struct SignalDefs { DWORD id; const char* name; }; + struct SignalDefs { + DWORD id; + const char *name; + }; - // There is no 1-1 mapping between signals and windows exceptions. - // Windows can easily distinguish between SO and SigSegV, - // but SigInt, SigTerm, etc are handled differently. - static constexpr SignalDefs signalDefs[] = { - { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, - { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, - { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, - { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, - }; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static constexpr SignalDefs signalDefs[] = { + {EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal"}, + {EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow"}, + {EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal"}, + {EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error"}, + }; - // Since we do not support multiple instantiations, we put these - // into global variables and rely on cleaning them up in outlined - // constructors/destructors - static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr; + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr; - - static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { - for (auto const& def : signalDefs) { - if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { - reportFatal(def.name); - } - } - // If a filter was previously registered, invoke it - if (previousTopLevelExceptionFilter) { - return previousTopLevelExceptionFilter(ExceptionInfo); - } - // Otherwise, pass along all exceptions. - // This stops us from eating debugger breaks etc. - return EXCEPTION_CONTINUE_SEARCH; + static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const &def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } } - - // For MSVC, we reserve part of the stack memory for handling - // memory overflow structured exception. - FatalConditionHandler::FatalConditionHandler() { - ULONG guaranteeSize = static_cast(minStackSizeForErrors); - if (!SetThreadStackGuarantee(&guaranteeSize)) { - // We do not want to fully error out, because needing - // the stack reserve should be rare enough anyway. - Catch::cerr() - << "Failed to reserve piece of stack." - << " Stack overflows will not be reported successfully."; - } + // If a filter was previously registered, invoke it + if (previousTopLevelExceptionFilter) { + return previousTopLevelExceptionFilter(ExceptionInfo); } + // Otherwise, pass along all exceptions. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } - // We do not attempt to unset the stack guarantee, because - // Windows does not support lowering the stack size guarantee. - FatalConditionHandler::~FatalConditionHandler() = default; - - - void FatalConditionHandler::engage_platform() { - // Register as a the top level exception filter. - previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter); + // For MSVC, we reserve part of the stack memory for handling + // memory overflow structured exception. + FatalConditionHandler::FatalConditionHandler() { + ULONG guaranteeSize = static_cast(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; } + } - void FatalConditionHandler::disengage_platform() noexcept { - if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) { - Catch::cerr() - << "Unexpected SEH unhandled exception filter on disengage." - << " The filter was restored, but might be rolled back unexpectedly."; - } - previousTopLevelExceptionFilter = nullptr; + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + FatalConditionHandler::~FatalConditionHandler() = default; + + void FatalConditionHandler::engage_platform() { + // Register as a the top level exception filter. + previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter); + } + + void FatalConditionHandler::disengage_platform() noexcept { + if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) { + Catch::cerr() + << "Unexpected SEH unhandled exception filter on disengage." + << " The filter was restored, but might be rolled back unexpectedly."; } + previousTopLevelExceptionFilter = nullptr; + } } // end namespace Catch #endif // CATCH_CONFIG_WINDOWS_SEH -#if defined( CATCH_CONFIG_POSIX_SIGNALS ) +#if defined(CATCH_CONFIG_POSIX_SIGNALS) #include namespace Catch { - struct SignalDefs { - int id; - const char* name; - }; + struct SignalDefs { + int id; + const char *name; + }; - static constexpr SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; + static constexpr SignalDefs signalDefs[] = { + {SIGINT, "SIGINT - Terminal interrupt signal"}, + {SIGILL, "SIGILL - Illegal instruction signal"}, + {SIGFPE, "SIGFPE - Floating point error signal"}, + {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, + {SIGTERM, "SIGTERM - Termination request signal"}, + {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; // Older GCCs trigger -Wmissing-field-initializers for T foo = {} // which is zero initialization, but not explicit. We want to avoid // that. #if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif - static char* altStackMem = nullptr; - static std::size_t altStackSize = 0; - static stack_t oldSigStack{}; - static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + static char *altStackMem = nullptr; + static std::size_t altStackSize = 0; + static stack_t oldSigStack{}; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; - static void restorePreviousSignalHandlers() noexcept { - // We set signal handlers back to the previous ones. Hopefully - // nobody overwrote them in the meantime, and doesn't expect - // their signal handlers to live past ours given that they - // installed them after ours.. - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); - } - // Return the old stack - sigaltstack(&oldSigStack, nullptr); + static void restorePreviousSignalHandlers() noexcept { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + } - static void handleSignal( int sig ) { - char const * name = ""; - for (auto const& def : signalDefs) { - if (sig == def.id) { - name = def.name; - break; - } - } - // We need to restore previous signal handlers and let them do - // their thing, so that the users can have the debugger break - // when a signal is raised, and so on. - restorePreviousSignalHandlers(); - reportFatal( name ); - raise( sig ); + static void handleSignal(int sig) { + char const *name = ""; + for (auto const &def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } } + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal(name); + raise(sig); + } - FatalConditionHandler::FatalConditionHandler() { - assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); - if (altStackSize == 0) { - altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); - } - altStackMem = new char[altStackSize](); + FatalConditionHandler::FatalConditionHandler() { + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); } + altStackMem = new char[altStackSize](); + } - FatalConditionHandler::~FatalConditionHandler() { - delete[] altStackMem; - // We signal that another instance can be constructed by zeroing - // out the pointer. - altStackMem = nullptr; - } - - void FatalConditionHandler::engage_platform() { - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = altStackSize; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = { }; - - sa.sa_handler = handleSignal; - sa.sa_flags = SA_ONSTACK; - for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } + FatalConditionHandler::~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; + } + + void FatalConditionHandler::engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = {}; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } + } #if defined(__GNUC__) -# pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif - - void FatalConditionHandler::disengage_platform() noexcept { - restorePreviousSignalHandlers(); - } + void FatalConditionHandler::disengage_platform() noexcept { + restorePreviousSignalHandlers(); + } } // end namespace Catch #endif // CATCH_CONFIG_POSIX_SIGNALS - - - #include namespace Catch { - namespace Detail { + namespace Detail { - uint32_t convertToBits(float f) { - static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated"); - uint32_t i; - std::memcpy(&i, &f, sizeof(f)); - return i; - } + uint32_t convertToBits(float f) { + static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated"); + uint32_t i; + std::memcpy(&i, &f, sizeof(f)); + return i; + } - uint64_t convertToBits(double d) { - static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated"); - uint64_t i; - std::memcpy(&i, &d, sizeof(d)); - return i; - } + uint64_t convertToBits(double d) { + static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated"); + uint64_t i; + std::memcpy(&i, &d, sizeof(d)); + return i; + } -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wfloat-equal" +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" #endif - bool directCompare( float lhs, float rhs ) { return lhs == rhs; } - bool directCompare( double lhs, double rhs ) { return lhs == rhs; } -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic pop + bool directCompare(float lhs, float rhs) { + return lhs == rhs; + } + bool directCompare(double lhs, double rhs) { + return lhs == rhs; + } +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop #endif - - } // end namespace Detail + } // end namespace Detail } // end namespace Catch - - - - - #include namespace Catch { - namespace Detail { + namespace Detail { -#if !defined (CATCH_CONFIG_GETENV) - char const* getEnv( char const* ) { return nullptr; } +#if !defined(CATCH_CONFIG_GETENV) + char const *getEnv(char const *) { + return nullptr; + } #else - char const* getEnv( char const* varName ) { -# if defined( _MSC_VER ) -# pragma warning( push ) -# pragma warning( disable : 4996 ) // use getenv_s instead of getenv -# endif - - return std::getenv( varName ); - -# if defined( _MSC_VER ) -# pragma warning( pop ) -# endif - } + char const *getEnv(char const *varName) { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) // use getenv_s instead of getenv #endif -} // namespace Detail + + return std::getenv(varName); + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + } +#endif + } // namespace Detail } // namespace Catch - - - #include #include namespace Catch { - Catch::IStream::~IStream() = default; + Catch::IStream::~IStream() = default; -namespace Detail { + namespace Detail { namespace { - template - class StreamBufImpl final : public std::streambuf { - char data[bufferSize]; - WriterF m_writer; + template + class StreamBufImpl final : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } + public: + StreamBufImpl() { + setp(data, data + sizeof(data)); + } - ~StreamBufImpl() noexcept override { - StreamBufImpl::sync(); - } + ~StreamBufImpl() noexcept override { + StreamBufImpl::sync(); + } - private: - int overflow( int c ) override { - sync(); + private: + int overflow(int c) override { + sync(); - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } + if (c != EOF) { + if (pbase() == epptr()) + m_writer(std::string(1, static_cast(c))); + else + sputc(static_cast(c)); + } + return 0; + } - int sync() override { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; + int sync() override { + if (pbase() != pptr()) { + m_writer(std::string(pbase(), static_cast(pptr() - pbase()))); + setp(pbase(), epptr()); + } + return 0; + } + }; - /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// - struct OutputDebugWriter { + struct OutputDebugWriter { + void operator()(std::string const &str) { + if (!str.empty()) { + writeToDebugConsole(str); + } + } + }; - void operator()( std::string const& str ) { - if ( !str.empty() ) { - writeToDebugConsole( str ); - } - } - }; + /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// + class FileStream final : public IStream { + std::ofstream m_ofs; - class FileStream final : public IStream { - std::ofstream m_ofs; - public: - FileStream( std::string const& filename ) { - m_ofs.open( filename.c_str() ); - CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' ); - m_ofs << std::unitbuf; - } - public: // IStream - std::ostream& stream() override { - return m_ofs; - } - }; + public: + FileStream(std::string const &filename) { + m_ofs.open(filename.c_str()); + CATCH_ENFORCE(!m_ofs.fail(), "Unable to open file: '" << filename << '\''); + m_ofs << std::unitbuf; + } - /////////////////////////////////////////////////////////////////////////// + public: // IStream + std::ostream &stream() override { + return m_ofs; + } + }; - class CoutStream final : public IStream { - std::ostream m_os; - public: - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream() : m_os( Catch::cout().rdbuf() ) {} + /////////////////////////////////////////////////////////////////////////// - public: // IStream - std::ostream& stream() override { return m_os; } - bool isConsole() const override { return true; } - }; + class CoutStream final : public IStream { + std::ostream m_os; - class CerrStream : public IStream { - std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() + : m_os(Catch::cout().rdbuf()) {} - public: - // Store the streambuf from cerr up-front because - // cout may get redirected when running tests - CerrStream(): m_os( Catch::cerr().rdbuf() ) {} + public: // IStream + std::ostream &stream() override { return m_os; } + bool isConsole() const override { return true; } + }; - public: // IStream - std::ostream& stream() override { return m_os; } - bool isConsole() const override { return true; } - }; + class CerrStream : public IStream { + std::ostream m_os; - /////////////////////////////////////////////////////////////////////////// + public: + // Store the streambuf from cerr up-front because + // cout may get redirected when running tests + CerrStream() + : m_os(Catch::cerr().rdbuf()) {} - class DebugOutStream final : public IStream { - Detail::unique_ptr> m_streamBuf; - std::ostream m_os; - public: - DebugOutStream() - : m_streamBuf( Detail::make_unique>() ), - m_os( m_streamBuf.get() ) - {} + public: // IStream + std::ostream &stream() override { return m_os; } + bool isConsole() const override { return true; } + }; - public: // IStream - std::ostream& stream() override { return m_os; } - }; + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream final : public IStream { + Detail::unique_ptr> m_streamBuf; + std::ostream m_os; + + public: + DebugOutStream() + : m_streamBuf(Detail::make_unique>()), m_os(m_streamBuf.get()) {} + + public: // IStream + std::ostream &stream() override { return m_os; } + }; } // unnamed namespace -} // namespace Detail + } // namespace Detail - /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// - auto makeStream( std::string const& filename ) -> Detail::unique_ptr { - if ( filename.empty() || filename == "-" ) { - return Detail::make_unique(); - } - if( filename[0] == '%' ) { - if ( filename == "%debug" ) { - return Detail::make_unique(); - } else if ( filename == "%stderr" ) { - return Detail::make_unique(); - } else if ( filename == "%stdout" ) { - return Detail::make_unique(); - } else { - CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' ); - } - } - return Detail::make_unique( filename ); + auto makeStream(std::string const &filename) -> Detail::unique_ptr { + if (filename.empty() || filename == "-") { + return Detail::make_unique(); } - -} - - - -namespace Catch { - - namespace { - static bool needsEscape( char c ) { - return c == '"' || c == '\\' || c == '\b' || c == '\f' || - c == '\n' || c == '\r' || c == '\t'; - } - - static Catch::StringRef makeEscapeStringRef( char c ) { - if ( c == '"' ) { - return "\\\""_sr; - } else if ( c == '\\' ) { - return "\\\\"_sr; - } else if ( c == '\b' ) { - return "\\b"_sr; - } else if ( c == '\f' ) { - return "\\f"_sr; - } else if ( c == '\n' ) { - return "\\n"_sr; - } else if ( c == '\r' ) { - return "\\r"_sr; - } else if ( c == '\t' ) { - return "\\t"_sr; - } - Catch::Detail::Unreachable(); - } - } // namespace - - void JsonUtils::indent( std::ostream& os, std::uint64_t level ) { - for ( std::uint64_t i = 0; i < level; ++i ) { - os << " "; - } - } - void JsonUtils::appendCommaNewline( std::ostream& os, - bool& should_comma, - std::uint64_t level ) { - if ( should_comma ) { os << ','; } - should_comma = true; - os << '\n'; - indent( os, level ); - } - - JsonObjectWriter::JsonObjectWriter( std::ostream& os ): - JsonObjectWriter{ os, 0 } {} - - JsonObjectWriter::JsonObjectWriter( std::ostream& os, - std::uint64_t indent_level ): - m_os{ os }, m_indent_level{ indent_level } { - m_os << '{'; - } - JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ) noexcept: - m_os{ source.m_os }, - m_indent_level{ source.m_indent_level }, - m_should_comma{ source.m_should_comma }, - m_active{ source.m_active } { - source.m_active = false; - } - - JsonObjectWriter::~JsonObjectWriter() { - if ( !m_active ) { return; } - - m_os << '\n'; - JsonUtils::indent( m_os, m_indent_level ); - m_os << '}'; - } - - JsonValueWriter JsonObjectWriter::write( StringRef key ) { - JsonUtils::appendCommaNewline( - m_os, m_should_comma, m_indent_level + 1 ); - - m_os << '"' << key << "\": "; - return JsonValueWriter{ m_os, m_indent_level + 1 }; - } - - JsonArrayWriter::JsonArrayWriter( std::ostream& os ): - JsonArrayWriter{ os, 0 } {} - JsonArrayWriter::JsonArrayWriter( std::ostream& os, - std::uint64_t indent_level ): - m_os{ os }, m_indent_level{ indent_level } { - m_os << '['; - } - JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ) noexcept: - m_os{ source.m_os }, - m_indent_level{ source.m_indent_level }, - m_should_comma{ source.m_should_comma }, - m_active{ source.m_active } { - source.m_active = false; - } - JsonArrayWriter::~JsonArrayWriter() { - if ( !m_active ) { return; } - - m_os << '\n'; - JsonUtils::indent( m_os, m_indent_level ); - m_os << ']'; - } - - JsonObjectWriter JsonArrayWriter::writeObject() { - JsonUtils::appendCommaNewline( - m_os, m_should_comma, m_indent_level + 1 ); - return JsonObjectWriter{ m_os, m_indent_level + 1 }; - } - - JsonArrayWriter JsonArrayWriter::writeArray() { - JsonUtils::appendCommaNewline( - m_os, m_should_comma, m_indent_level + 1 ); - return JsonArrayWriter{ m_os, m_indent_level + 1 }; - } - - JsonArrayWriter& JsonArrayWriter::write( bool value ) { - return writeImpl( value ); - } - - JsonValueWriter::JsonValueWriter( std::ostream& os ): - JsonValueWriter{ os, 0 } {} - - JsonValueWriter::JsonValueWriter( std::ostream& os, - std::uint64_t indent_level ): - m_os{ os }, m_indent_level{ indent_level } {} - - JsonObjectWriter JsonValueWriter::writeObject() && { - return JsonObjectWriter{ m_os, m_indent_level }; - } - - JsonArrayWriter JsonValueWriter::writeArray() && { - return JsonArrayWriter{ m_os, m_indent_level }; - } - - void JsonValueWriter::write( Catch::StringRef value ) && { - writeImpl( value, true ); - } - - void JsonValueWriter::write( bool value ) && { - writeImpl( value ? "true"_sr : "false"_sr, false ); - } - - void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { - if ( quote ) { m_os << '"'; } - size_t current_start = 0; - for ( size_t i = 0; i < value.size(); ++i ) { - if ( needsEscape( value[i] ) ) { - if ( current_start < i ) { - m_os << value.substr( current_start, i - current_start ); - } - m_os << makeEscapeStringRef( value[i] ); - current_start = i + 1; - } - } - if ( current_start < value.size() ) { - m_os << value.substr( current_start, value.size() - current_start ); - } - if ( quote ) { m_os << '"'; } + if (filename[0] == '%') { + if (filename == "%debug") { + return Detail::make_unique(); + } else if (filename == "%stderr") { + return Detail::make_unique(); + } else if (filename == "%stdout") { + return Detail::make_unique(); + } else { + CATCH_ERROR("Unrecognised stream: '" << filename << '\''); + } } + return Detail::make_unique(filename); + } } // namespace Catch - - - namespace Catch { - auto operator << (std::ostream& os, LazyExpression const& lazyExpr) -> std::ostream& { - if (lazyExpr.m_isNegated) - os << '!'; - - if (lazyExpr) { - if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) - os << '(' << *lazyExpr.m_transientExpression << ')'; - else - os << *lazyExpr.m_transientExpression; - } else { - os << "{** error - unchecked empty expression requested **}"; - } - return os; + namespace { + static bool needsEscape(char c) { + return c == '"' || c == '\\' || c == '\b' || c == '\f' || c == '\n' || c == '\r' || c == '\t'; } + static Catch::StringRef makeEscapeStringRef(char c) { + if (c == '"') { + return "\\\""_sr; + } else if (c == '\\') { + return "\\\\"_sr; + } else if (c == '\b') { + return "\\b"_sr; + } else if (c == '\f') { + return "\\f"_sr; + } else if (c == '\n') { + return "\\n"_sr; + } else if (c == '\r') { + return "\\r"_sr; + } else if (c == '\t') { + return "\\t"_sr; + } + Catch::Detail::Unreachable(); + } + } // namespace + + void JsonUtils::indent(std::ostream &os, std::uint64_t level) { + for (std::uint64_t i = 0; i < level; ++i) { + os << " "; + } + } + void JsonUtils::appendCommaNewline(std::ostream &os, + bool &should_comma, + std::uint64_t level) { + if (should_comma) { os << ','; } + should_comma = true; + os << '\n'; + indent(os, level); + } + + JsonObjectWriter::JsonObjectWriter(std::ostream &os) + : JsonObjectWriter{os, 0} {} + + JsonObjectWriter::JsonObjectWriter(std::ostream &os, + std::uint64_t indent_level) + : m_os{os}, m_indent_level{indent_level} { + m_os << '{'; + } + JsonObjectWriter::JsonObjectWriter(JsonObjectWriter &&source) noexcept + : m_os{source.m_os}, m_indent_level{source.m_indent_level}, m_should_comma{source.m_should_comma}, m_active{source.m_active} { + source.m_active = false; + } + + JsonObjectWriter::~JsonObjectWriter() { + if (!m_active) { return; } + + m_os << '\n'; + JsonUtils::indent(m_os, m_indent_level); + m_os << '}'; + } + + JsonValueWriter JsonObjectWriter::write(StringRef key) { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1); + + m_os << '"' << key << "\": "; + return JsonValueWriter{m_os, m_indent_level + 1}; + } + + JsonArrayWriter::JsonArrayWriter(std::ostream &os) + : JsonArrayWriter{os, 0} {} + JsonArrayWriter::JsonArrayWriter(std::ostream &os, + std::uint64_t indent_level) + : m_os{os}, m_indent_level{indent_level} { + m_os << '['; + } + JsonArrayWriter::JsonArrayWriter(JsonArrayWriter &&source) noexcept + : m_os{source.m_os}, m_indent_level{source.m_indent_level}, m_should_comma{source.m_should_comma}, m_active{source.m_active} { + source.m_active = false; + } + JsonArrayWriter::~JsonArrayWriter() { + if (!m_active) { return; } + + m_os << '\n'; + JsonUtils::indent(m_os, m_indent_level); + m_os << ']'; + } + + JsonObjectWriter JsonArrayWriter::writeObject() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1); + return JsonObjectWriter{m_os, m_indent_level + 1}; + } + + JsonArrayWriter JsonArrayWriter::writeArray() { + JsonUtils::appendCommaNewline( + m_os, m_should_comma, m_indent_level + 1); + return JsonArrayWriter{m_os, m_indent_level + 1}; + } + + JsonArrayWriter &JsonArrayWriter::write(bool value) { + return writeImpl(value); + } + + JsonValueWriter::JsonValueWriter(std::ostream &os) + : JsonValueWriter{os, 0} {} + + JsonValueWriter::JsonValueWriter(std::ostream &os, + std::uint64_t indent_level) + : m_os{os}, m_indent_level{indent_level} {} + + JsonObjectWriter JsonValueWriter::writeObject() && { + return JsonObjectWriter{m_os, m_indent_level}; + } + + JsonArrayWriter JsonValueWriter::writeArray() && { + return JsonArrayWriter{m_os, m_indent_level}; + } + + void JsonValueWriter::write(Catch::StringRef value) && { + writeImpl(value, true); + } + + void JsonValueWriter::write(bool value) && { + writeImpl(value ? "true"_sr : "false"_sr, false); + } + + void JsonValueWriter::writeImpl(Catch::StringRef value, bool quote) { + if (quote) { m_os << '"'; } + size_t current_start = 0; + for (size_t i = 0; i < value.size(); ++i) { + if (needsEscape(value[i])) { + if (current_start < i) { + m_os << value.substr(current_start, i - current_start); + } + m_os << makeEscapeStringRef(value[i]); + current_start = i + 1; + } + } + if (current_start < value.size()) { + m_os << value.substr(current_start, value.size() - current_start); + } + if (quote) { m_os << '"'; } + } + } // namespace Catch +namespace Catch { + auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream & { + if (lazyExpr.m_isNegated) + os << '!'; + if (lazyExpr) { + if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) + os << '(' << *lazyExpr.m_transientExpression << ')'; + else + os << *lazyExpr.m_transientExpression; + } else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } + +} // namespace Catch #ifdef CATCH_CONFIG_WINDOWS_CRTDBG #include namespace Catch { - LeakDetector::LeakDetector() { - int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - flag |= _CRTDBG_LEAK_CHECK_DF; - flag |= _CRTDBG_ALLOC_MEM_DF; - _CrtSetDbgFlag(flag); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - // Change this to leaking allocation's number to break there - _CrtSetBreakAlloc(-1); - } -} + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} // namespace Catch #else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv - Catch::LeakDetector::LeakDetector() = default; +Catch::LeakDetector::LeakDetector() = default; #endif // CATCH_CONFIG_WINDOWS_CRTDBG Catch::LeakDetector::~LeakDetector() { - Catch::cleanUp(); + Catch::cleanUp(); } - - - namespace Catch { - namespace { + namespace { - void listTests(IEventListener& reporter, IConfig const& config) { - auto const& testSpec = config.testSpec(); - auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - reporter.listTests(matchedTestCases); - } - - void listTags(IEventListener& reporter, IConfig const& config) { - auto const& testSpec = config.testSpec(); - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - - std::map tagCounts; - for (auto const& testCase : matchedTestCases) { - for (auto const& tagName : testCase.getTestCaseInfo().tags) { - auto it = tagCounts.find(tagName.original); - if (it == tagCounts.end()) - it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; - it->second.add(tagName.original); - } - } - - std::vector infos; infos.reserve(tagCounts.size()); - for (auto& tagc : tagCounts) { - infos.push_back(CATCH_MOVE(tagc.second)); - } - - reporter.listTags(infos); - } - - void listReporters(IEventListener& reporter) { - std::vector descriptions; - - auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); - descriptions.reserve(factories.size()); - for (auto const& fac : factories) { - descriptions.push_back({ fac.first, fac.second->getDescription() }); - } - - reporter.listReporters(descriptions); - } - - void listListeners(IEventListener& reporter) { - std::vector descriptions; - - auto const& factories = - getRegistryHub().getReporterRegistry().getListeners(); - descriptions.reserve( factories.size() ); - for ( auto const& fac : factories ) { - descriptions.push_back( { fac->getName(), fac->getDescription() } ); - } - - reporter.listListeners( descriptions ); - } - - } // end anonymous namespace - - void TagInfo::add( StringRef spelling ) { - ++count; - spellings.insert( spelling ); + void listTests(IEventListener &reporter, IConfig const &config) { + auto const &testSpec = config.testSpec(); + auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + reporter.listTests(matchedTestCases); } - std::string TagInfo::all() const { - // 2 per tag for brackets '[' and ']' - size_t size = spellings.size() * 2; - for (auto const& spelling : spellings) { - size += spelling.size(); - } + void listTags(IEventListener &reporter, IConfig const &config) { + auto const &testSpec = config.testSpec(); + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - std::string out; out.reserve(size); - for (auto const& spelling : spellings) { - out += '['; - out += spelling; - out += ']'; + std::map tagCounts; + for (auto const &testCase : matchedTestCases) { + for (auto const &tagName : testCase.getTestCaseInfo().tags) { + auto it = tagCounts.find(tagName.original); + if (it == tagCounts.end()) + it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; + it->second.add(tagName.original); } - return out; + } + + std::vector infos; + infos.reserve(tagCounts.size()); + for (auto &tagc : tagCounts) { + infos.push_back(CATCH_MOVE(tagc.second)); + } + + reporter.listTags(infos); } - bool list( IEventListener& reporter, Config const& config ) { - bool listed = false; - if (config.listTests()) { - listed = true; - listTests(reporter, config); - } - if (config.listTags()) { - listed = true; - listTags(reporter, config); - } - if (config.listReporters()) { - listed = true; - listReporters(reporter); - } - if ( config.listListeners() ) { - listed = true; - listListeners( reporter ); - } - return listed; + void listReporters(IEventListener &reporter) { + std::vector descriptions; + + auto const &factories = getRegistryHub().getReporterRegistry().getFactories(); + descriptions.reserve(factories.size()); + for (auto const &fac : factories) { + descriptions.push_back({fac.first, fac.second->getDescription()}); + } + + reporter.listReporters(descriptions); } + void listListeners(IEventListener &reporter) { + std::vector descriptions; + + auto const &factories = getRegistryHub().getReporterRegistry().getListeners(); + descriptions.reserve(factories.size()); + for (auto const &fac : factories) { + descriptions.push_back({fac->getName(), fac->getDescription()}); + } + + reporter.listListeners(descriptions); + } + + } // end anonymous namespace + + void TagInfo::add(StringRef spelling) { + ++count; + spellings.insert(spelling); + } + + std::string TagInfo::all() const { + // 2 per tag for brackets '[' and ']' + size_t size = spellings.size() * 2; + for (auto const &spelling : spellings) { + size += spelling.size(); + } + + std::string out; + out.reserve(size); + for (auto const &spelling : spellings) { + out += '['; + out += spelling; + out += ']'; + } + return out; + } + + bool list(IEventListener &reporter, Config const &config) { + bool listed = false; + if (config.listTests()) { + listed = true; + listTests(reporter, config); + } + if (config.listTags()) { + listed = true; + listTags(reporter, config); + } + if (config.listReporters()) { + listed = true; + listReporters(reporter); + } + if (config.listListeners()) { + listed = true; + listListeners(reporter); + } + return listed; + } + } // end namespace Catch - - namespace Catch { - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - static const LeakDetector leakDetector; - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -} + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static const LeakDetector leakDetector; + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +} // namespace Catch // Allow users of amalgamated .cpp file to remove our main and provide their own. #if !defined(CATCH_AMALGAMATED_CUSTOM_MAIN) #if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) // Standard C/C++ Win32 Unicode wmain entry point -extern "C" int __cdecl wmain (int argc, wchar_t * argv[], wchar_t * []) { +extern "C" int __cdecl wmain(int argc, wchar_t *argv[], wchar_t *[]) { #else // Standard C/C++ main entry point -int main (int argc, char * argv[]) { +int main(int argc, char *argv[]) { #endif - // We want to force the linker not to discard the global variable - // and its constructor, as it (optionally) registers leak detector - (void)&Catch::leakDetector; + // We want to force the linker not to discard the global variable + // and its constructor, as it (optionally) registers leak detector + (void)&Catch::leakDetector; - return Catch::Session().run( argc, argv ); + return Catch::Session().run(argc, argv); } #endif // !defined(CATCH_AMALGAMATED_CUSTOM_MAIN - - - namespace Catch { - namespace { - // Messages are owned by their individual threads, so the counter should - // be thread-local as well. Alternative consideration: atomic counter, - // so threads don't share IDs and things are easier to debug. - static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0; - } + namespace { + // Messages are owned by their individual threads, so the counter should + // be thread-local as well. Alternative consideration: atomic counter, + // so threads don't share IDs and things are easier to debug. + static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0; + } // namespace - MessageInfo::MessageInfo( StringRef _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - type( _type ), - sequence( ++messageIDCounter ) - {} + MessageInfo::MessageInfo(StringRef _macroName, + SourceLineInfo const &_lineInfo, + ResultWas::OfType _type) + : macroName(_macroName), lineInfo(_lineInfo), type(_type), sequence(++messageIDCounter) {} } // end namespace Catch - - #include #include #include #include -#if defined( CATCH_CONFIG_NEW_CAPTURE ) -# if defined( _MSC_VER ) -# include //_dup and _dup2 -# define dup _dup -# define dup2 _dup2 -# define fileno _fileno -# else -# include // dup and dup2 -# endif +#if defined(CATCH_CONFIG_NEW_CAPTURE) +#if defined(_MSC_VER) +#include //_dup and _dup2 +#define dup _dup +#define dup2 _dup2 +#define fileno _fileno +#else +#include // dup and dup2 +#endif #endif namespace Catch { - namespace { - //! A no-op implementation, used if no reporter wants output - //! redirection. - class NoopRedirect : public OutputRedirect { - void activateImpl() override {} - void deactivateImpl() override {} - std::string getStdout() override { return {}; } - std::string getStderr() override { return {}; } - void clearBuffers() override {} - }; + namespace { + //! A no-op implementation, used if no reporter wants output + //! redirection. + class NoopRedirect : public OutputRedirect { + void activateImpl() override {} + void deactivateImpl() override {} + std::string getStdout() override { return {}; } + std::string getStderr() override { return {}; } + void clearBuffers() override {} + }; - /** - * Redirects specific stream's rdbuf with another's. - * - * Redirection can be stopped and started on-demand, assumes - * that the underlying stream's rdbuf aren't changed by other - * users. - */ - class RedirectedStreamNew { - std::ostream& m_originalStream; - std::ostream& m_redirectionStream; - std::streambuf* m_prevBuf; + /** + * Redirects specific stream's rdbuf with another's. + * + * Redirection can be stopped and started on-demand, assumes + * that the underlying stream's rdbuf aren't changed by other + * users. + */ + class RedirectedStreamNew { + std::ostream &m_originalStream; + std::ostream &m_redirectionStream; + std::streambuf *m_prevBuf; - public: - RedirectedStreamNew( std::ostream& originalStream, - std::ostream& redirectionStream ): - m_originalStream( originalStream ), - m_redirectionStream( redirectionStream ), - m_prevBuf( m_originalStream.rdbuf() ) {} + public: + RedirectedStreamNew(std::ostream &originalStream, + std::ostream &redirectionStream) + : m_originalStream(originalStream), m_redirectionStream(redirectionStream), m_prevBuf(m_originalStream.rdbuf()) {} - void startRedirect() { - m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); - } - void stopRedirect() { m_originalStream.rdbuf( m_prevBuf ); } - }; + void startRedirect() { + m_originalStream.rdbuf(m_redirectionStream.rdbuf()); + } + void stopRedirect() { m_originalStream.rdbuf(m_prevBuf); } + }; - /** - * Redirects the `std::cout`, `std::cerr`, `std::clog` streams, - * but does not touch the actual `stdout`/`stderr` file descriptors. - */ - class StreamRedirect : public OutputRedirect { - ReusableStringStream m_redirectedOut, m_redirectedErr; - RedirectedStreamNew m_cout, m_cerr, m_clog; + /** + * Redirects the `std::cout`, `std::cerr`, `std::clog` streams, + * but does not touch the actual `stdout`/`stderr` file descriptors. + */ + class StreamRedirect : public OutputRedirect { + ReusableStringStream m_redirectedOut, m_redirectedErr; + RedirectedStreamNew m_cout, m_cerr, m_clog; - public: - StreamRedirect(): - m_cout( Catch::cout(), m_redirectedOut.get() ), - m_cerr( Catch::cerr(), m_redirectedErr.get() ), - m_clog( Catch::clog(), m_redirectedErr.get() ) {} + public: + StreamRedirect() + : m_cout(Catch::cout(), m_redirectedOut.get()), m_cerr(Catch::cerr(), m_redirectedErr.get()), m_clog(Catch::clog(), m_redirectedErr.get()) {} - void activateImpl() override { - m_cout.startRedirect(); - m_cerr.startRedirect(); - m_clog.startRedirect(); - } - void deactivateImpl() override { - m_cout.stopRedirect(); - m_cerr.stopRedirect(); - m_clog.stopRedirect(); - } - std::string getStdout() override { return m_redirectedOut.str(); } - std::string getStderr() override { return m_redirectedErr.str(); } - void clearBuffers() override { - m_redirectedOut.str( "" ); - m_redirectedErr.str( "" ); - } - }; + void activateImpl() override { + m_cout.startRedirect(); + m_cerr.startRedirect(); + m_clog.startRedirect(); + } + void deactivateImpl() override { + m_cout.stopRedirect(); + m_cerr.stopRedirect(); + m_clog.stopRedirect(); + } + std::string getStdout() override { return m_redirectedOut.str(); } + std::string getStderr() override { return m_redirectedErr.str(); } + void clearBuffers() override { + m_redirectedOut.str(""); + m_redirectedErr.str(""); + } + }; -#if defined( CATCH_CONFIG_NEW_CAPTURE ) +#if defined(CATCH_CONFIG_NEW_CAPTURE) - // Windows's implementation of std::tmpfile is terrible (it tries - // to create a file inside system folder, thus requiring elevated - // privileges for the binary), so we have to use tmpnam(_s) and - // create the file ourselves there. - class TempFile { - public: - TempFile( TempFile const& ) = delete; - TempFile& operator=( TempFile const& ) = delete; - TempFile( TempFile&& ) = delete; - TempFile& operator=( TempFile&& ) = delete; + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const &) = delete; + TempFile &operator=(TempFile const &) = delete; + TempFile(TempFile &&) = delete; + TempFile &operator=(TempFile &&) = delete; -# if defined( _MSC_VER ) - TempFile() { - if ( tmpnam_s( m_buffer ) ) { - CATCH_RUNTIME_ERROR( "Could not get a temp filename" ); - } - if ( fopen_s( &m_file, m_buffer, "wb+" ) ) { - char buffer[100]; - if ( strerror_s( buffer, errno ) ) { - CATCH_RUNTIME_ERROR( - "Could not translate errno to a string" ); - } - CATCH_RUNTIME_ERROR( "Could not open the temp file: '" - << m_buffer - << "' because: " << buffer ); - } - } -# else - TempFile() { - m_file = std::tmpfile(); - if ( !m_file ) { - CATCH_RUNTIME_ERROR( "Could not create a temp file." ); - } - } -# endif +#if defined(_MSC_VER) + TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } + if (fopen_s(&m_file, m_buffer, "wb+")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR( + "Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Could not open the temp file: '" + << m_buffer + << "' because: " << buffer); + } + } +#else + TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } +#endif - ~TempFile() { - // TBD: What to do about errors here? - std::fclose( m_file ); - // We manually create the file on Windows only, on Linux - // it will be autodeleted -# if defined( _MSC_VER ) - std::remove( m_buffer ); -# endif - } + ~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); + // We manually create the file on Windows only, on Linux + // it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } - std::FILE* getFile() { return m_file; } - std::string getContents() { - ReusableStringStream sstr; - constexpr long buffer_size = 100; - char buffer[buffer_size + 1] = {}; - long current_pos = ftell( m_file ); - CATCH_ENFORCE( current_pos >= 0, - "ftell failed, errno: " << errno ); - std::rewind( m_file ); - while ( current_pos > 0 ) { - auto read_characters = - std::fread( buffer, - 1, - std::min( buffer_size, current_pos ), - m_file ); - buffer[read_characters] = '\0'; - sstr << buffer; - current_pos -= static_cast( read_characters ); - } - return sstr.str(); - } + std::FILE *getFile() { return m_file; } + std::string getContents() { + ReusableStringStream sstr; + constexpr long buffer_size = 100; + char buffer[buffer_size + 1] = {}; + long current_pos = ftell(m_file); + CATCH_ENFORCE(current_pos >= 0, + "ftell failed, errno: " << errno); + std::rewind(m_file); + while (current_pos > 0) { + auto read_characters = std::fread(buffer, + 1, + std::min(buffer_size, current_pos), + m_file); + buffer[read_characters] = '\0'; + sstr << buffer; + current_pos -= static_cast(read_characters); + } + return sstr.str(); + } - void clear() { std::rewind( m_file ); } + void clear() { std::rewind(m_file); } - private: - std::FILE* m_file = nullptr; - char m_buffer[L_tmpnam] = { 0 }; - }; + private: + std::FILE *m_file = nullptr; + char m_buffer[L_tmpnam] = {0}; + }; - /** - * Redirects the actual `stdout`/`stderr` file descriptors. - * - * Works by replacing the file descriptors numbered 1 and 2 - * with an open temporary file. - */ - class FileRedirect : public OutputRedirect { - TempFile m_outFile, m_errFile; - int m_originalOut = -1; - int m_originalErr = -1; + /** + * Redirects the actual `stdout`/`stderr` file descriptors. + * + * Works by replacing the file descriptors numbered 1 and 2 + * with an open temporary file. + */ + class FileRedirect : public OutputRedirect { + TempFile m_outFile, m_errFile; + int m_originalOut = -1; + int m_originalErr = -1; - // Flushes cout/cerr/clog streams and stdout/stderr FDs - void flushEverything() { - Catch::cout() << std::flush; - fflush( stdout ); - // Since we support overriding these streams, we flush cerr - // even though std::cerr is unbuffered - Catch::cerr() << std::flush; - Catch::clog() << std::flush; - fflush( stderr ); - } + // Flushes cout/cerr/clog streams and stdout/stderr FDs + void flushEverything() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + } - public: - FileRedirect(): - m_originalOut( dup( fileno( stdout ) ) ), - m_originalErr( dup( fileno( stderr ) ) ) { - CATCH_ENFORCE( m_originalOut >= 0, "Could not dup stdout" ); - CATCH_ENFORCE( m_originalErr >= 0, "Could not dup stderr" ); - } + public: + FileRedirect() + : m_originalOut(dup(fileno(stdout))), m_originalErr(dup(fileno(stderr))) { + CATCH_ENFORCE(m_originalOut >= 0, "Could not dup stdout"); + CATCH_ENFORCE(m_originalErr >= 0, "Could not dup stderr"); + } - std::string getStdout() override { return m_outFile.getContents(); } - std::string getStderr() override { return m_errFile.getContents(); } - void clearBuffers() override { - m_outFile.clear(); - m_errFile.clear(); - } + std::string getStdout() override { return m_outFile.getContents(); } + std::string getStderr() override { return m_errFile.getContents(); } + void clearBuffers() override { + m_outFile.clear(); + m_errFile.clear(); + } - void activateImpl() override { - // We flush before starting redirect, to ensure that we do - // not capture the end of message sent before activation. - flushEverything(); + void activateImpl() override { + // We flush before starting redirect, to ensure that we do + // not capture the end of message sent before activation. + flushEverything(); - int ret; - ret = dup2( fileno( m_outFile.getFile() ), fileno( stdout ) ); - CATCH_ENFORCE( ret >= 0, - "dup2 to stdout has failed, errno: " << errno ); - ret = dup2( fileno( m_errFile.getFile() ), fileno( stderr ) ); - CATCH_ENFORCE( ret >= 0, - "dup2 to stderr has failed, errno: " << errno ); - } - void deactivateImpl() override { - // We flush before ending redirect, to ensure that we - // capture all messages sent while the redirect was active. - flushEverything(); + int ret; + ret = dup2(fileno(m_outFile.getFile()), fileno(stdout)); + CATCH_ENFORCE(ret >= 0, + "dup2 to stdout has failed, errno: " << errno); + ret = dup2(fileno(m_errFile.getFile()), fileno(stderr)); + CATCH_ENFORCE(ret >= 0, + "dup2 to stderr has failed, errno: " << errno); + } + void deactivateImpl() override { + // We flush before ending redirect, to ensure that we + // capture all messages sent while the redirect was active. + flushEverything(); - int ret; - ret = dup2( m_originalOut, fileno( stdout ) ); - CATCH_ENFORCE( - ret >= 0, - "dup2 of original stdout has failed, errno: " << errno ); - ret = dup2( m_originalErr, fileno( stderr ) ); - CATCH_ENFORCE( - ret >= 0, - "dup2 of original stderr has failed, errno: " << errno ); - } - }; + int ret; + ret = dup2(m_originalOut, fileno(stdout)); + CATCH_ENFORCE( + ret >= 0, + "dup2 of original stdout has failed, errno: " << errno); + ret = dup2(m_originalErr, fileno(stderr)); + CATCH_ENFORCE( + ret >= 0, + "dup2 of original stderr has failed, errno: " << errno); + } + }; #endif // CATCH_CONFIG_NEW_CAPTURE - } // end namespace + } // end namespace - bool isRedirectAvailable( OutputRedirect::Kind kind ) { - switch ( kind ) { - // These two are always available - case OutputRedirect::None: - case OutputRedirect::Streams: - return true; -#if defined( CATCH_CONFIG_NEW_CAPTURE ) - case OutputRedirect::FileDescriptors: - return true; + bool isRedirectAvailable(OutputRedirect::Kind kind) { + switch (kind) { + // These two are always available + case OutputRedirect::None: + case OutputRedirect::Streams: + return true; +#if defined(CATCH_CONFIG_NEW_CAPTURE) + case OutputRedirect::FileDescriptors: + return true; #endif - default: - return false; - } + default: + return false; } + } - Detail::unique_ptr makeOutputRedirect( bool actual ) { - if ( actual ) { - // TODO: Clean this up later -#if defined( CATCH_CONFIG_NEW_CAPTURE ) - return Detail::make_unique(); + Detail::unique_ptr makeOutputRedirect(bool actual) { + if (actual) { + // TODO: Clean this up later +#if defined(CATCH_CONFIG_NEW_CAPTURE) + return Detail::make_unique(); #else - return Detail::make_unique(); + return Detail::make_unique(); #endif - } else { - return Detail::make_unique(); - } + } else { + return Detail::make_unique(); } + } - RedirectGuard scopedActivate( OutputRedirect& redirectImpl ) { - return RedirectGuard( true, redirectImpl ); + RedirectGuard scopedActivate(OutputRedirect &redirectImpl) { + return RedirectGuard(true, redirectImpl); + } + + RedirectGuard scopedDeactivate(OutputRedirect &redirectImpl) { + return RedirectGuard(false, redirectImpl); + } + + OutputRedirect::~OutputRedirect() = default; + + RedirectGuard::RedirectGuard(bool activate, OutputRedirect &redirectImpl) + : m_redirect(&redirectImpl), m_activate(activate), m_previouslyActive(redirectImpl.isActive()) { + // Skip cases where there is no actual state change. + if (m_activate == m_previouslyActive) { return; } + + if (m_activate) { + m_redirect->activate(); + } else { + m_redirect->deactivate(); } + } - RedirectGuard scopedDeactivate( OutputRedirect& redirectImpl ) { - return RedirectGuard( false, redirectImpl ); + RedirectGuard::~RedirectGuard() noexcept(false) { + if (m_moved) { return; } + // Skip cases where there is no actual state change. + if (m_activate == m_previouslyActive) { return; } + + if (m_activate) { + m_redirect->deactivate(); + } else { + m_redirect->activate(); } + } - OutputRedirect::~OutputRedirect() = default; + RedirectGuard::RedirectGuard(RedirectGuard &&rhs) noexcept + : m_redirect(rhs.m_redirect), m_activate(rhs.m_activate), m_previouslyActive(rhs.m_previouslyActive), m_moved(false) { + rhs.m_moved = true; + } - RedirectGuard::RedirectGuard( bool activate, OutputRedirect& redirectImpl ): - m_redirect( &redirectImpl ), - m_activate( activate ), - m_previouslyActive( redirectImpl.isActive() ) { - - // Skip cases where there is no actual state change. - if ( m_activate == m_previouslyActive ) { return; } - - if ( m_activate ) { - m_redirect->activate(); - } else { - m_redirect->deactivate(); - } - } - - RedirectGuard::~RedirectGuard() noexcept( false ) { - if ( m_moved ) { return; } - // Skip cases where there is no actual state change. - if ( m_activate == m_previouslyActive ) { return; } - - if ( m_activate ) { - m_redirect->deactivate(); - } else { - m_redirect->activate(); - } - } - - RedirectGuard::RedirectGuard( RedirectGuard&& rhs ) noexcept: - m_redirect( rhs.m_redirect ), - m_activate( rhs.m_activate ), - m_previouslyActive( rhs.m_previouslyActive ), - m_moved( false ) { - rhs.m_moved = true; - } - - RedirectGuard& RedirectGuard::operator=( RedirectGuard&& rhs ) noexcept { - m_redirect = rhs.m_redirect; - m_activate = rhs.m_activate; - m_previouslyActive = rhs.m_previouslyActive; - m_moved = false; - rhs.m_moved = true; - return *this; - } + RedirectGuard &RedirectGuard::operator=(RedirectGuard &&rhs) noexcept { + m_redirect = rhs.m_redirect; + m_activate = rhs.m_activate; + m_previouslyActive = rhs.m_previouslyActive; + m_moved = false; + rhs.m_moved = true; + return *this; + } } // namespace Catch -#if defined( CATCH_CONFIG_NEW_CAPTURE ) -# if defined( _MSC_VER ) -# undef dup -# undef dup2 -# undef fileno -# endif +#if defined(CATCH_CONFIG_NEW_CAPTURE) +#if defined(_MSC_VER) +#undef dup +#undef dup2 +#undef fileno +#endif #endif - - - #include #include namespace Catch { - Optional parseUInt(std::string const& input, int base) { - auto trimmed = trim( input ); - // std::stoull is annoying and accepts numbers starting with '-', - // it just negates them into unsigned int - if ( trimmed.empty() || trimmed[0] == '-' ) { - return {}; - } - - CATCH_TRY { - size_t pos = 0; - const auto ret = std::stoull( trimmed, &pos, base ); - - // We did not consume the whole input, so there is an issue - // This can be bunch of different stuff, like multiple numbers - // in the input, or invalid digits/characters and so on. Either - // way, we do not want to return the partially parsed result. - if ( pos != trimmed.size() ) { - return {}; - } - // Too large - if ( ret > std::numeric_limits::max() ) { - return {}; - } - return static_cast(ret); - } - CATCH_CATCH_ANON( std::invalid_argument const& ) { - // no conversion could be performed - } - CATCH_CATCH_ANON( std::out_of_range const& ) { - // the input does not fit into an unsigned long long - } - return {}; + Optional parseUInt(std::string const &input, int base) { + auto trimmed = trim(input); + // std::stoull is annoying and accepts numbers starting with '-', + // it just negates them into unsigned int + if (trimmed.empty() || trimmed[0] == '-') { + return {}; } + CATCH_TRY { + size_t pos = 0; + const auto ret = std::stoull(trimmed, &pos, base); + + // We did not consume the whole input, so there is an issue + // This can be bunch of different stuff, like multiple numbers + // in the input, or invalid digits/characters and so on. Either + // way, we do not want to return the partially parsed result. + if (pos != trimmed.size()) { + return {}; + } + // Too large + if (ret > std::numeric_limits::max()) { + return {}; + } + return static_cast(ret); + } + CATCH_CATCH_ANON(std::invalid_argument const &) { + // no conversion could be performed + } + CATCH_CATCH_ANON(std::out_of_range const &) { + // the input does not fit into an unsigned long long + } + return {}; + } + } // namespace Catch - - - #include namespace Catch { #if !defined(CATCH_CONFIG_POLYFILL_ISNAN) - bool isnan(float f) { - return std::isnan(f); - } - bool isnan(double d) { - return std::isnan(d); - } + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } #else - // For now we only use this for embarcadero - bool isnan(float f) { - return std::_isnan(f); - } - bool isnan(double d) { - return std::_isnan(d); - } + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } #endif -#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER ) - float nextafter( float x, float y ) { return std::nextafter( x, y ); } - double nextafter( double x, double y ) { return std::nextafter( x, y ); } +#if !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + float nextafter(float x, float y) { + return std::nextafter(x, y); + } + double nextafter(double x, double y) { + return std::nextafter(x, y); + } #else - float nextafter( float x, float y ) { return ::nextafterf( x, y ); } - double nextafter( double x, double y ) { return ::nextafter( x, y ); } + float nextafter(float x, float y) { + return ::nextafterf(x, y); + } + double nextafter(double x, double y) { + return ::nextafter(x, y); + } #endif } // end namespace Catch - - -#if defined( __clang__ ) -# define CATCH2_CLANG_NO_SANITIZE_INTEGER \ - __attribute__( ( no_sanitize( "unsigned-integer-overflow" ) ) ) +#if defined(__clang__) +#define CATCH2_CLANG_NO_SANITIZE_INTEGER \ + __attribute__((no_sanitize("unsigned-integer-overflow"))) #else -# define CATCH2_CLANG_NO_SANITIZE_INTEGER +#define CATCH2_CLANG_NO_SANITIZE_INTEGER #endif namespace Catch { -namespace { + namespace { #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4146) // we negate uint32 during the rotate +#pragma warning(disable : 4146) // we negate uint32 during the rotate #endif - // Safe rotr implementation thanks to John Regehr - CATCH2_CLANG_NO_SANITIZE_INTEGER - uint32_t rotate_right(uint32_t val, uint32_t count) { - const uint32_t mask = 31; - count &= mask; - return (val >> count) | (val << (-count & mask)); - } + // Safe rotr implementation thanks to John Regehr + CATCH2_CLANG_NO_SANITIZE_INTEGER + uint32_t rotate_right(uint32_t val, uint32_t count) { + const uint32_t mask = 31; + count &= mask; + return (val >> count) | (val << (-count & mask)); + } #if defined(_MSC_VER) #pragma warning(pop) #endif -} + } // namespace + SimplePcg32::SimplePcg32(result_type seed_) { + seed(seed_); + } - SimplePcg32::SimplePcg32(result_type seed_) { - seed(seed_); + void SimplePcg32::seed(result_type seed_) { + m_state = 0; + (*this)(); + m_state += seed_; + (*this)(); + } + + void SimplePcg32::discard(uint64_t skip) { + // We could implement this to run in O(log n) steps, but this + // should suffice for our use case. + for (uint64_t s = 0; s < skip; ++s) { + static_cast((*this)()); } + } + CATCH2_CLANG_NO_SANITIZE_INTEGER + SimplePcg32::result_type SimplePcg32::operator()() { + // prepare the output value + const uint32_t xorshifted = static_cast(((m_state >> 18u) ^ m_state) >> 27u); + const auto output = rotate_right(xorshifted, static_cast(m_state >> 59u)); - void SimplePcg32::seed(result_type seed_) { - m_state = 0; - (*this)(); - m_state += seed_; - (*this)(); - } - - void SimplePcg32::discard(uint64_t skip) { - // We could implement this to run in O(log n) steps, but this - // should suffice for our use case. - for (uint64_t s = 0; s < skip; ++s) { - static_cast((*this)()); - } - } - - CATCH2_CLANG_NO_SANITIZE_INTEGER - SimplePcg32::result_type SimplePcg32::operator()() { - // prepare the output value - const uint32_t xorshifted = static_cast(((m_state >> 18u) ^ m_state) >> 27u); - const auto output = rotate_right(xorshifted, static_cast(m_state >> 59u)); - - // advance state - m_state = m_state * 6364136223846793005ULL + s_inc; - - return output; - } - - bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { - return lhs.m_state == rhs.m_state; - } - - bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { - return lhs.m_state != rhs.m_state; - } -} - + // advance state + m_state = m_state * 6364136223846793005ULL + s_inc; + return output; + } + bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { + return lhs.m_state == rhs.m_state; + } + bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { + return lhs.m_state != rhs.m_state; + } +} // namespace Catch #include #include namespace Catch { - std::uint32_t generateRandomSeed( GenerateFrom from ) { - switch ( from ) { - case GenerateFrom::Time: - return static_cast( std::time( nullptr ) ); + std::uint32_t generateRandomSeed(GenerateFrom from) { + switch (from) { + case GenerateFrom::Time: + return static_cast(std::time(nullptr)); - case GenerateFrom::Default: - case GenerateFrom::RandomDevice: { - std::random_device rd; - return Detail::fillBitsFrom( rd ); - } + case GenerateFrom::Default: + case GenerateFrom::RandomDevice: { + std::random_device rd; + return Detail::fillBitsFrom(rd); + } - default: - CATCH_ERROR("Unknown generation method"); - } + default: + CATCH_ERROR("Unknown generation method"); } + } } // end namespace Catch - - - namespace Catch { - struct ReporterRegistry::ReporterRegistryImpl { - std::vector> listeners; - std::map - factories; - }; + struct ReporterRegistry::ReporterRegistryImpl { + std::vector> listeners; + std::map + factories; + }; - 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_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>(); - m_impl->factories["JSON"] = - Detail::make_unique>(); - } + 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_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>(); + m_impl->factories["JSON"] = Detail::make_unique>(); + } - ReporterRegistry::~ReporterRegistry() = default; + ReporterRegistry::~ReporterRegistry() = default; - 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 ) ); - } + 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 ) { - CATCH_ENFORCE( name.find( "::" ) == name.npos, - "'::' 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_impl->listeners.push_back( CATCH_MOVE( 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_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_impl->listeners.push_back(CATCH_MOVE(factory)); + } - std::map const& - ReporterRegistry::getFactories() const { - return m_impl->factories; - } + std::map const & + ReporterRegistry::getFactories() const { + return m_impl->factories; + } - std::vector> const& - ReporterRegistry::getListeners() const { - return m_impl->listeners; - } + std::vector> const & + ReporterRegistry::getListeners() const { + return m_impl->listeners; + } } // namespace Catch - - - - #include namespace Catch { - namespace { - struct kvPair { - StringRef key, value; - }; + namespace { + struct kvPair { + StringRef key, value; + }; - kvPair splitKVPair(StringRef kvString) { - auto splitPos = static_cast( - std::find( kvString.begin(), kvString.end(), '=' ) - - kvString.begin() ); + kvPair splitKVPair(StringRef kvString) { + auto splitPos = static_cast( + std::find(kvString.begin(), kvString.end(), '=') - kvString.begin()); - return { kvString.substr( 0, splitPos ), - kvString.substr( splitPos + 1, kvString.size() ) }; + return {kvString.substr(0, splitPos), + kvString.substr(splitPos + 1, kvString.size())}; + } + } // namespace + + namespace Detail { + std::vector splitReporterSpec(StringRef reporterSpec) { + static constexpr auto separator = "::"; + static constexpr size_t separatorSize = 2; + + size_t separatorPos = 0; + auto findNextSeparator = [&reporterSpec](size_t startPos) { + static_assert( + separatorSize == 2, + "The code below currently assumes 2 char separator"); + + auto currentPos = startPos; + do { + while (currentPos < reporterSpec.size() && reporterSpec[currentPos] != separator[0]) { + ++currentPos; + } + if (currentPos + 1 < reporterSpec.size() && reporterSpec[currentPos + 1] == separator[1]) { + return currentPos; + } + ++currentPos; + } while (currentPos < reporterSpec.size()); + + return static_cast(-1); + }; + + std::vector parts; + + while (separatorPos < reporterSpec.size()) { + const auto nextSeparator = findNextSeparator(separatorPos); + parts.push_back(static_cast(reporterSpec.substr( + separatorPos, nextSeparator - separatorPos))); + + if (nextSeparator == static_cast(-1)) { + break; } + separatorPos = nextSeparator + separatorSize; + } + + // Handle a separator at the end. + // This is not a valid spec, but we want to do validation in a + // centralized place + if (separatorPos == reporterSpec.size()) { + parts.emplace_back(); + } + + return parts; } - namespace Detail { - std::vector splitReporterSpec( StringRef reporterSpec ) { - static constexpr auto separator = "::"; - static constexpr size_t separatorSize = 2; + Optional stringToColourMode(StringRef colourMode) { + if (colourMode == "default") { + return ColourMode::PlatformDefault; + } else if (colourMode == "ansi") { + return ColourMode::ANSI; + } else if (colourMode == "win32") { + return ColourMode::Win32; + } else if (colourMode == "none") { + return ColourMode::None; + } else { + return {}; + } + } + } // namespace Detail - size_t separatorPos = 0; - auto findNextSeparator = [&reporterSpec]( size_t startPos ) { - static_assert( - separatorSize == 2, - "The code below currently assumes 2 char separator" ); + bool operator==(ReporterSpec const &lhs, ReporterSpec const &rhs) { + return lhs.m_name == rhs.m_name && lhs.m_outputFileName == rhs.m_outputFileName && lhs.m_colourMode == rhs.m_colourMode && lhs.m_customOptions == rhs.m_customOptions; + } - auto currentPos = startPos; - do { - while ( currentPos < reporterSpec.size() && - reporterSpec[currentPos] != separator[0] ) { - ++currentPos; - } - if ( currentPos + 1 < reporterSpec.size() && - reporterSpec[currentPos + 1] == separator[1] ) { - return currentPos; - } - ++currentPos; - } while ( currentPos < reporterSpec.size() ); + Optional parseReporterSpec(StringRef reporterSpec) { + auto parts = Detail::splitReporterSpec(reporterSpec); - return static_cast( -1 ); - }; + assert(parts.size() > 0 && "Split should never return empty vector"); - std::vector parts; + std::map kvPairs; + Optional outputFileName; + Optional colourMode; - while ( separatorPos < reporterSpec.size() ) { - const auto nextSeparator = findNextSeparator( separatorPos ); - parts.push_back( static_cast( reporterSpec.substr( - separatorPos, nextSeparator - separatorPos ) ) ); + // First part is always reporter name, so we skip it + for (size_t i = 1; i < parts.size(); ++i) { + auto kv = splitKVPair(parts[i]); + auto key = kv.key, value = kv.value; - if ( nextSeparator == static_cast( -1 ) ) { - break; - } - separatorPos = nextSeparator + separatorSize; - } - - // Handle a separator at the end. - // This is not a valid spec, but we want to do validation in a - // centralized place - if ( separatorPos == reporterSpec.size() ) { - parts.emplace_back(); - } - - return parts; + if (key.empty() || value.empty()) { // NOLINT(bugprone-branch-clone) + return {}; + } else if (key[0] == 'X') { + // This is a reporter-specific option, we don't check these + // apart from basic sanity checks + if (key.size() == 1) { + return {}; } - Optional stringToColourMode( StringRef colourMode ) { - if ( colourMode == "default" ) { - return ColourMode::PlatformDefault; - } else if ( colourMode == "ansi" ) { - return ColourMode::ANSI; - } else if ( colourMode == "win32" ) { - return ColourMode::Win32; - } else if ( colourMode == "none" ) { - return ColourMode::None; - } else { - return {}; - } + auto ret = kvPairs.emplace(std::string(kv.key), std::string(kv.value)); + if (!ret.second) { + // Duplicated key. We might want to handle this differently, + // e.g. by overwriting the existing value? + return {}; } - } // namespace Detail - - - bool operator==( ReporterSpec const& lhs, ReporterSpec const& rhs ) { - return lhs.m_name == rhs.m_name && - lhs.m_outputFileName == rhs.m_outputFileName && - lhs.m_colourMode == rhs.m_colourMode && - lhs.m_customOptions == rhs.m_customOptions; + } else if (key == "out") { + // Duplicated key + if (outputFileName) { + return {}; + } + outputFileName = static_cast(value); + } else if (key == "colour-mode") { + // Duplicated key + if (colourMode) { + return {}; + } + colourMode = Detail::stringToColourMode(value); + // Parsing failed + if (!colourMode) { + return {}; + } + } else { + // Unrecognized option + return {}; + } } - Optional parseReporterSpec( StringRef reporterSpec ) { - auto parts = Detail::splitReporterSpec( reporterSpec ); + return ReporterSpec{CATCH_MOVE(parts[0]), + CATCH_MOVE(outputFileName), + CATCH_MOVE(colourMode), + CATCH_MOVE(kvPairs)}; + } - assert( parts.size() > 0 && "Split should never return empty vector" ); - - std::map kvPairs; - Optional outputFileName; - Optional colourMode; - - // First part is always reporter name, so we skip it - for ( size_t i = 1; i < parts.size(); ++i ) { - auto kv = splitKVPair( parts[i] ); - auto key = kv.key, value = kv.value; - - if ( key.empty() || value.empty() ) { // NOLINT(bugprone-branch-clone) - return {}; - } else if ( key[0] == 'X' ) { - // This is a reporter-specific option, we don't check these - // apart from basic sanity checks - if ( key.size() == 1 ) { - return {}; - } - - auto ret = kvPairs.emplace( std::string(kv.key), std::string(kv.value) ); - if ( !ret.second ) { - // Duplicated key. We might want to handle this differently, - // e.g. by overwriting the existing value? - return {}; - } - } else if ( key == "out" ) { - // Duplicated key - if ( outputFileName ) { - return {}; - } - outputFileName = static_cast( value ); - } else if ( key == "colour-mode" ) { - // Duplicated key - if ( colourMode ) { - return {}; - } - colourMode = Detail::stringToColourMode( value ); - // Parsing failed - if ( !colourMode ) { - return {}; - } - } else { - // Unrecognized option - return {}; - } - } - - return ReporterSpec{ CATCH_MOVE( parts[0] ), - CATCH_MOVE( outputFileName ), - CATCH_MOVE( colourMode ), - CATCH_MOVE( kvPairs ) }; - } - -ReporterSpec::ReporterSpec( - std::string name, - Optional outputFileName, - Optional colourMode, - std::map customOptions ): - m_name( CATCH_MOVE( name ) ), - m_outputFileName( CATCH_MOVE( outputFileName ) ), - m_colourMode( CATCH_MOVE( colourMode ) ), - m_customOptions( CATCH_MOVE( customOptions ) ) {} + ReporterSpec::ReporterSpec( + std::string name, + Optional outputFileName, + Optional colourMode, + std::map customOptions) + : m_name(CATCH_MOVE(name)), m_outputFileName(CATCH_MOVE(outputFileName)), m_colourMode(CATCH_MOVE(colourMode)), m_customOptions(CATCH_MOVE(customOptions)) {} } // namespace Catch - - #include #include #include @@ -5800,2535 +5515,2412 @@ ReporterSpec::ReporterSpec( namespace Catch { - // This class encapsulates the idea of a pool of ostringstreams that can be reused. - struct StringStreams { - std::vector> m_streams; - std::vector m_unused; - std::ostringstream m_referenceStream; // Used for copy state/ flags from - Detail::Mutex m_mutex; + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + Detail::Mutex m_mutex; - auto add() -> std::pair { - Detail::LockGuard _( m_mutex ); - if( m_unused.empty() ) { - m_streams.push_back( Detail::make_unique() ); - return { m_streams.size()-1, m_streams.back().get() }; + auto add() -> std::pair { + Detail::LockGuard _(m_mutex); + if (m_unused.empty()) { + m_streams.push_back(Detail::make_unique()); + return {m_streams.size() - 1, m_streams.back().get()}; + } else { + auto index = m_unused.back(); + m_unused.pop_back(); + return {index, m_streams[index].get()}; + } + } + + void release(std::size_t index, std::ostream *originalPtr) { + assert(originalPtr); + originalPtr->copyfmt(m_referenceStream); // Restore initial flags and other state + + Detail::LockGuard _(m_mutex); + assert(originalPtr == m_streams[index].get() && "Mismatch between release index and stream ptr"); + m_unused.push_back(index); + } + }; + + ReusableStringStream::ReusableStringStream() { + std::tie(m_index, m_oss) = Singleton::getMutable().add(); + } + + ReusableStringStream::~ReusableStringStream() { + static_cast(m_oss)->str(""); + m_oss->clear(); + Singleton::getMutable().release(m_index, m_oss); + } + + std::string ReusableStringStream::str() const { + return static_cast(m_oss)->str(); + } + + void ReusableStringStream::str(std::string const &str) { + static_cast(m_oss)->str(str); + } + +} // namespace Catch + +#include +#include + +namespace Catch { + + namespace Generators { + namespace { + struct GeneratorTracker final : TestCaseTracking::TrackerBase, + IGeneratorTracker { + GeneratorBasePtr m_generator; + // Filtered generator has moved to specific index due to + // a filter, it needs special handling of `countedNext()` + bool m_isFiltered = false; + + GeneratorTracker( + TestCaseTracking::NameAndLocation &&nameAndLocation, + TrackerContext &ctx, + ITracker *parent, + GeneratorBasePtr &&generator) + : TrackerBase(CATCH_MOVE(nameAndLocation), ctx, parent), m_generator(CATCH_MOVE(generator)) { + assert(m_generator && "Cannot create tracker without generator"); + + // Handle potential filter and move forward here... + // Old style filters do not affect generators at all + if (m_newStyleFilters && m_allTrackerDepth < m_filterRef->size()) { + auto const &filter = (*m_filterRef)[m_allTrackerDepth]; + // Generator cannot be un-entered the way a section + // can be, so the tracker has to throw for a wrong + // filter to stop the execution flow. + if (filter.type == PathFilter::For::Section) { + // We want the semantics of `SKIP()`, but we inline it + // to avoid issues with conditionally prefixed macros + INTERNAL_CATCH_MSG( + "SKIP", + Catch::ResultWas::ExplicitSkip, + Catch::ResultDisposition::Normal, + ""); + Catch::Detail::Unreachable(); } - else { - auto index = m_unused.back(); - m_unused.pop_back(); - return { index, m_streams[index].get() }; + // '*' is the wildcard for "all elements in generator" + // used for filtering sections below the generator, but + // not the generator itself. + if (filter.filter != "*") { + m_isFiltered = true; + // TBD: We assume that the filter was validated as + // number during parsing. We should pass it + // as number from the CLI parser. + size_t targetIndex = std::stoul(filter.filter); + m_generator->skipToNthElement(targetIndex); } + } } - void release( std::size_t index, std::ostream* originalPtr ) { - assert( originalPtr ); - originalPtr->copyfmt( m_referenceStream ); // Restore initial flags and other state + static GeneratorTracker * + acquire(TrackerContext &ctx, + TestCaseTracking::NameAndLocationRef const & + nameAndLocation) { + GeneratorTracker *tracker; - Detail::LockGuard _( m_mutex ); - assert( originalPtr == m_streams[index].get() && "Mismatch between release index and stream ptr" ); - m_unused.push_back( index ); + ITracker ¤tTracker = 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; } + + // TrackerBase interface + bool isGeneratorTracker() const override { 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 + + // No filters left -> no restrictions on running sections + size_t childDepth = 1 + (m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth); + if (childDepth >= m_filterRef->size()) { + return true; + } + + // If we are using the new style filters, we need to check + // whether the successive filter is for section or a generator. + if (m_newStyleFilters + && (*m_filterRef)[childDepth].type != PathFilter::For::Section) { + return false; + } + // Look for any child section that could match the remaining filters + for (auto const &child : m_children) { + if (child->isSectionTracker() && static_cast(*child).trimmedName() == StringRef((*m_filterRef)[childDepth].filter)) { + 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_isFiltered // filtered generators cannot meaningfully move forward, as they would get past the filter + && m_generator->countedNext())) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const & override { + return m_generator; + } + }; + } // namespace + } // namespace Generators + + namespace Detail { + // Assertions are owned by the thread that is executing them. + // This allows for lock-free progress in common cases where we + // do not need to send the assertion events to the reporter. + // This also implies that messages are owned by their respective + // threads, and should not be shared across different threads. + // + // This implies that various pieces of metadata referring to last + // assertion result/source location/message handling, etc + // should also be thread local. For now we just use naked globals + // below, in the future we will want to allocate piece of memory + // from heap, to avoid consuming too much thread-local storage. + // + // Note that we also don't want non-trivial the thread-local variables + // below be initialized for every thread, only for those that touch + // Catch2. To make this work with both GCC/Clang and MSVC, we have to + // make them thread-local magic statics. (Class-level statics have the + // desired semantics on GCC, but not on MSVC). + + // This is used for the "if" part of CHECKED_IF/CHECKED_ELSE + static CATCH_INTERNAL_THREAD_LOCAL bool g_lastAssertionPassed = false; + + // This is the source location for last encountered macro. It is + // used to provide the users with more precise location of error + // when an unexpected exception/fatal error happens. + static CATCH_INTERNAL_THREAD_LOCAL SourceLineInfo + g_lastKnownLineInfo("DummyLocation", static_cast(-1)); + + // Should we clear message scopes before sending off the messages to + // reporter? Set in `assertionPassedFastPath` to avoid doing the full + // clear there for performance reasons. + static CATCH_INTERNAL_THREAD_LOCAL bool g_clearMessageScopes = false; + + // Holds the data for both scoped and unscoped messages together, + // to avoid issues where their lifetimes start in wrong order, + // and then are destroyed in wrong order. + class MessageHolder { + // The actual message vector passed to the reporters + std::vector messages; + // IDs of messages from UNSCOPED_X macros, which we have to + // remove manually. + std::vector unscoped_ids; + + public: + // We do not need to special-case the unscoped messages when + // we only keep around the raw msg ids. + ~MessageHolder() = default; + + void addUnscopedMessage(MessageInfo &&info) { + repairUnscopedMessageInvariant(); + unscoped_ids.push_back(info.sequence); + messages.push_back(CATCH_MOVE(info)); + } + + void addUnscopedMessage(MessageBuilder &&builder) { + MessageInfo info(CATCH_MOVE(builder.m_info)); + info.message = builder.m_stream.str(); + addUnscopedMessage(CATCH_MOVE(info)); + } + + void addScopedMessage(MessageInfo &&info) { + messages.push_back(CATCH_MOVE(info)); + } + + std::vector const &getMessages() const { + return messages; + } + + void removeMessage(unsigned int messageId) { + // Note: On average, it would probably be better to look for + // the message backwards. However, we do not expect to have + // to deal with more messages than low single digits, so + // the improvement is tiny, and we would have to hand-write + // the loop to avoid terrible codegen of reverse iterators + // in debug mode. + auto iter = std::find_if(messages.begin(), + messages.end(), + [messageId](MessageInfo const &msg) { + return msg.sequence == messageId; + }); + assert(iter != messages.end() && "Trying to remove non-existent message."); + messages.erase(iter); + } + + void removeUnscopedMessages() { + for (const auto messageId : unscoped_ids) { + removeMessage(messageId); + } + unscoped_ids.clear(); + g_clearMessageScopes = false; + } + + void repairUnscopedMessageInvariant() { + if (g_clearMessageScopes) { removeUnscopedMessages(); } + g_clearMessageScopes = false; + } }; - ReusableStringStream::ReusableStringStream() { - std::tie( m_index, m_oss ) = - Singleton::getMutable().add(); + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static MessageHolder &g_messageHolder() { + static CATCH_INTERNAL_THREAD_LOCAL MessageHolder value; + return value; } + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - ReusableStringStream::~ReusableStringStream() { - static_cast( m_oss )->str(""); - m_oss->clear(); - Singleton::getMutable().release( m_index, m_oss ); + } // namespace Detail + + RunContext::RunContext(IConfig const *_config, IEventListenerPtr &&reporter) + : m_runInfo(_config->name()), m_config(_config), m_reporter(CATCH_MOVE(reporter)), m_outputRedirect(makeOutputRedirect(m_reporter->getPreferences().shouldRedirectStdOut)), m_abortAfterXFailedAssertions(m_config->abortAfter()), m_reportAssertionStarting(m_reporter->getPreferences().shouldReportAllAssertionStarts), m_includeSuccessfulResults(m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions), m_shouldDebugBreak(m_config->shouldDebugBreak()) { + getCurrentMutableContext().setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + + // TODO: HACK! + // We need to make sure the underlying cache is initialized + // while we are guaranteed to be running in a single thread, + // because the initialization is not thread-safe. + ReusableStringStream rss; + (void)rss; + } + + RunContext::~RunContext() { + updateTotalsFromAtomics(); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } + + Totals RunContext::runTest(TestCaseHandle const &testCase) { + updateTotalsFromAtomics(); + const Totals prevTotals = m_totals; + + auto const &testInfo = testCase.getTestCaseInfo(); + m_reporter->testCaseStarting(testInfo); + testCase.prepareTestCase(); + m_activeTestCase = &testCase; + + ITracker &rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + rootTracker.setFilters(&m_config->getPathFilters(), + m_config->useNewFilterBehaviour()); + + // We intentionally only seed the internal RNG once per test case, + // before it is first invoked. The reason for that is a complex + // interplay of generator/section implementation details and the + // Random*Generator types. + // + // The issue boils down to us needing to seed the Random*Generators + // with different seed each, so that they return different sequences + // of random numbers. We do this by giving them a number from the + // shared RNG instance as their seed. + // + // However, this runs into an issue if the reseeding happens each + // time the test case is entered (as opposed to first time only), + // because multiple generators could get the same seed, e.g. in + // ```cpp + // TEST_CASE() { + // auto i = GENERATE(take(10, random(0, 100)); + // SECTION("A") { + // auto j = GENERATE(take(10, random(0, 100)); + // } + // SECTION("B") { + // auto k = GENERATE(take(10, random(0, 100)); + // } + // } + // ``` + // `i` and `j` would properly return values from different sequences, + // but `i` and `k` would return the same sequence, because their seed + // would be the same. + // (The reason their seeds would be the same is that the generator + // for k would be initialized when the test case is entered the second + // time, after the shared RNG instance was reset to the same value + // it had when the generator for i was initialized.) + seedRng(*m_config); + + uint64_t testRuns = 0; + std::string redirectedCout; + std::string redirectedCerr; + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo)); + + m_reporter->testCasePartialStarting(testInfo, testRuns); + + updateTotalsFromAtomics(); + const auto beforeRunTotals = m_totals; + runCurrentTest(); + std::string oneRunCout = m_outputRedirect->getStdout(); + std::string oneRunCerr = m_outputRedirect->getStderr(); + m_outputRedirect->clearBuffers(); + redirectedCout += oneRunCout; + redirectedCerr += oneRunCerr; + + updateTotalsFromAtomics(); + const auto singleRunTotals = m_totals.delta(beforeRunTotals); + auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); + m_reporter->testCasePartialEnded(statsForOneRun, testRuns); + + ++testRuns; + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; } - - std::string ReusableStringStream::str() const { - return static_cast( m_oss )->str(); - } - - void ReusableStringStream::str( std::string const& str ) { - static_cast( m_oss )->str( str ); - } - - -} - - - - -#include -#include - -namespace Catch { - - namespace Generators { - namespace { - struct GeneratorTracker final : TestCaseTracking::TrackerBase, - IGeneratorTracker { - GeneratorBasePtr m_generator; - // Filtered generator has moved to specific index due to - // a filter, it needs special handling of `countedNext()` - bool m_isFiltered = false; - - GeneratorTracker( - TestCaseTracking::NameAndLocation&& nameAndLocation, - TrackerContext& ctx, - ITracker* parent, - GeneratorBasePtr&& generator ): - TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ), - m_generator( CATCH_MOVE( generator ) ) { - assert( m_generator && - "Cannot create tracker without generator" ); - - // Handle potential filter and move forward here... - // Old style filters do not affect generators at all - if (m_newStyleFilters && m_allTrackerDepth < m_filterRef->size()) { - auto const& filter = - ( *m_filterRef )[m_allTrackerDepth]; - // Generator cannot be un-entered the way a section - // can be, so the tracker has to throw for a wrong - // filter to stop the execution flow. - if (filter.type == PathFilter::For::Section) { - // We want the semantics of `SKIP()`, but we inline it - // to avoid issues with conditionally prefixed macros - INTERNAL_CATCH_MSG( - "SKIP", - Catch::ResultWas::ExplicitSkip, - Catch::ResultDisposition::Normal, - "" ); - Catch::Detail::Unreachable(); - } - // '*' is the wildcard for "all elements in generator" - // used for filtering sections below the generator, but - // not the generator itself. - if ( filter.filter != "*" ) { - m_isFiltered = true; - // TBD: We assume that the filter was validated as - // number during parsing. We should pass it - // as number from the CLI parser. - size_t targetIndex = std::stoul( filter.filter ); - m_generator->skipToNthElement( targetIndex ); - } - } - } - - 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; - } - - if ( !tracker->isComplete() ) { tracker->open(); } - - return tracker; - } - - // TrackerBase interface - bool isGeneratorTracker() const override { 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 - - // No filters left -> no restrictions on running sections - size_t childDepth = 1 + (m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth); - if ( childDepth >= m_filterRef->size() ) { - return true; - } - - // If we are using the new style filters, we need to check - // whether the successive filter is for section or a generator. - if ( m_newStyleFilters - && (*m_filterRef)[childDepth].type != PathFilter::For::Section ) { - return false; - } - // Look for any child section that could match the remaining filters - for ( auto const& child : m_children ) { - if ( child->isSectionTracker() && - static_cast( *child ) - .trimmedName() == StringRef((*m_filterRef)[childDepth].filter) ) { - 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_isFiltered // filtered generators cannot meaningfully move forward, as they would get past the filter - && m_generator->countedNext() ) ) { - m_children.clear(); - m_runState = Executing; - } - } - - // IGeneratorTracker interface - auto getGenerator() const -> GeneratorBasePtr const& override { - return m_generator; - } - }; - } // namespace - } - - namespace Detail { - // Assertions are owned by the thread that is executing them. - // This allows for lock-free progress in common cases where we - // do not need to send the assertion events to the reporter. - // This also implies that messages are owned by their respective - // threads, and should not be shared across different threads. - // - // This implies that various pieces of metadata referring to last - // assertion result/source location/message handling, etc - // should also be thread local. For now we just use naked globals - // below, in the future we will want to allocate piece of memory - // from heap, to avoid consuming too much thread-local storage. - // - // Note that we also don't want non-trivial the thread-local variables - // below be initialized for every thread, only for those that touch - // Catch2. To make this work with both GCC/Clang and MSVC, we have to - // make them thread-local magic statics. (Class-level statics have the - // desired semantics on GCC, but not on MSVC). - - // This is used for the "if" part of CHECKED_IF/CHECKED_ELSE - static CATCH_INTERNAL_THREAD_LOCAL bool g_lastAssertionPassed = false; - - // This is the source location for last encountered macro. It is - // used to provide the users with more precise location of error - // when an unexpected exception/fatal error happens. - static CATCH_INTERNAL_THREAD_LOCAL SourceLineInfo - g_lastKnownLineInfo( "DummyLocation", static_cast( -1 ) ); - - // Should we clear message scopes before sending off the messages to - // reporter? Set in `assertionPassedFastPath` to avoid doing the full - // clear there for performance reasons. - static CATCH_INTERNAL_THREAD_LOCAL bool g_clearMessageScopes = false; - - - // Holds the data for both scoped and unscoped messages together, - // to avoid issues where their lifetimes start in wrong order, - // and then are destroyed in wrong order. - class MessageHolder { - // The actual message vector passed to the reporters - std::vector messages; - // IDs of messages from UNSCOPED_X macros, which we have to - // remove manually. - std::vector unscoped_ids; - - public: - // We do not need to special-case the unscoped messages when - // we only keep around the raw msg ids. - ~MessageHolder() = default; - - void addUnscopedMessage( MessageInfo&& info ) { - repairUnscopedMessageInvariant(); - unscoped_ids.push_back( info.sequence ); - messages.push_back( CATCH_MOVE( info ) ); - } - - void addUnscopedMessage(MessageBuilder&& builder) { - MessageInfo info( CATCH_MOVE( builder.m_info ) ); - info.message = builder.m_stream.str(); - addUnscopedMessage( CATCH_MOVE( info ) ); - } - - void addScopedMessage(MessageInfo&& info) { - messages.push_back( CATCH_MOVE( info ) ); - } - - std::vector const& getMessages() const { - return messages; - } - - void removeMessage( unsigned int messageId ) { - // Note: On average, it would probably be better to look for - // the message backwards. However, we do not expect to have - // to deal with more messages than low single digits, so - // the improvement is tiny, and we would have to hand-write - // the loop to avoid terrible codegen of reverse iterators - // in debug mode. - auto iter = - std::find_if( messages.begin(), - messages.end(), - [messageId]( MessageInfo const& msg ) { - return msg.sequence == messageId; - } ); - assert( iter != messages.end() && - "Trying to remove non-existent message." ); - messages.erase( iter ); - } - - void removeUnscopedMessages() { - for ( const auto messageId : unscoped_ids ) { - removeMessage( messageId ); - } - unscoped_ids.clear(); - g_clearMessageScopes = false; - } - - void repairUnscopedMessageInvariant() { - if ( g_clearMessageScopes ) { removeUnscopedMessages(); } - g_clearMessageScopes = false; - } - }; - - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - static MessageHolder& g_messageHolder() { - static CATCH_INTERNAL_THREAD_LOCAL MessageHolder value; - return value; - } - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - - } // namespace Detail - - RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) - : m_runInfo(_config->name()), - m_config(_config), - m_reporter(CATCH_MOVE(reporter)), - m_outputRedirect( makeOutputRedirect( m_reporter->getPreferences().shouldRedirectStdOut ) ), - m_abortAfterXFailedAssertions( m_config->abortAfter() ), - m_reportAssertionStarting( m_reporter->getPreferences().shouldReportAllAssertionStarts ), - m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ), - m_shouldDebugBreak( m_config->shouldDebugBreak() ) - { - getCurrentMutableContext().setResultCapture( this ); - m_reporter->testRunStarting(m_runInfo); - - // TODO: HACK! - // We need to make sure the underlying cache is initialized - // while we are guaranteed to be running in a single thread, - // because the initialization is not thread-safe. - ReusableStringStream rss; - (void)rss; - } - - RunContext::~RunContext() { - updateTotalsFromAtomics(); - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); - } - - Totals RunContext::runTest(TestCaseHandle const& testCase) { - updateTotalsFromAtomics(); - const Totals prevTotals = m_totals; - - auto const& testInfo = testCase.getTestCaseInfo(); - m_reporter->testCaseStarting(testInfo); - testCase.prepareTestCase(); - m_activeTestCase = &testCase; - - - ITracker& rootTracker = m_trackerContext.startRun(); - assert(rootTracker.isSectionTracker()); - rootTracker.setFilters( &m_config->getPathFilters(), - m_config->useNewFilterBehaviour() ); - - // We intentionally only seed the internal RNG once per test case, - // before it is first invoked. The reason for that is a complex - // interplay of generator/section implementation details and the - // Random*Generator types. - // - // The issue boils down to us needing to seed the Random*Generators - // with different seed each, so that they return different sequences - // of random numbers. We do this by giving them a number from the - // shared RNG instance as their seed. - // - // However, this runs into an issue if the reseeding happens each - // time the test case is entered (as opposed to first time only), - // because multiple generators could get the same seed, e.g. in - // ```cpp - // TEST_CASE() { - // auto i = GENERATE(take(10, random(0, 100)); - // SECTION("A") { - // auto j = GENERATE(take(10, random(0, 100)); - // } - // SECTION("B") { - // auto k = GENERATE(take(10, random(0, 100)); - // } - // } - // ``` - // `i` and `j` would properly return values from different sequences, - // but `i` and `k` would return the same sequence, because their seed - // would be the same. - // (The reason their seeds would be the same is that the generator - // for k would be initialized when the test case is entered the second - // time, after the shared RNG instance was reset to the same value - // it had when the generator for i was initialized.) - seedRng( *m_config ); - - uint64_t testRuns = 0; - std::string redirectedCout; - std::string redirectedCerr; - do { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo)); - - m_reporter->testCasePartialStarting(testInfo, testRuns); - - updateTotalsFromAtomics(); - const auto beforeRunTotals = m_totals; - runCurrentTest(); - std::string oneRunCout = m_outputRedirect->getStdout(); - std::string oneRunCerr = m_outputRedirect->getStderr(); - m_outputRedirect->clearBuffers(); - redirectedCout += oneRunCout; - redirectedCerr += oneRunCerr; - - updateTotalsFromAtomics(); - const auto singleRunTotals = m_totals.delta(beforeRunTotals); - auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); - m_reporter->testCasePartialEnded(statsForOneRun, testRuns); - - ++testRuns; - } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); - - Totals deltaTotals = m_totals.delta(prevTotals); - if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - testCase.tearDownTestCase(); - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - CATCH_MOVE(redirectedCout), - CATCH_MOVE(redirectedCerr), - aborting())); - - m_activeTestCase = nullptr; - m_testCaseTracker = nullptr; - - return deltaTotals; - } - - - void RunContext::assertionEnded(AssertionResult&& result) { - Detail::g_lastKnownLineInfo = result.m_info.lineInfo; - if (result.getResultType() == ResultWas::Ok) { - m_atomicAssertionCount.passed++; - Detail::g_lastAssertionPassed = true; - } else if (result.getResultType() == ResultWas::ExplicitSkip) { - m_atomicAssertionCount.skipped++; - Detail::g_lastAssertionPassed = true; - } else if (!result.succeeded()) { - Detail::g_lastAssertionPassed = false; - if (result.isOk()) { - } - else if( m_activeTestCase->getTestCaseInfo().okToFail() ) // Read from a shared state established before the threads could start, this is fine - m_atomicAssertionCount.failedButOk++; - else - m_atomicAssertionCount.failed++; - } - else { - Detail::g_lastAssertionPassed = true; - } - - auto& msgHolder = Detail::g_messageHolder(); - msgHolder.repairUnscopedMessageInvariant(); - - // From here, we are touching shared state and need mutex. - Detail::LockGuard lock( m_assertionMutex ); - { - auto _ = scopedDeactivate( *m_outputRedirect ); - updateTotalsFromAtomics(); - m_reporter->assertionEnded( AssertionStats( result, msgHolder.getMessages(), m_totals ) ); - } - - if ( result.getResultType() != ResultWas::Warning ) { - msgHolder.removeUnscopedMessages(); - } - - // Reset working state. assertion info will be reset after - // populateReaction is run if it is needed - m_lastResult = CATCH_MOVE( result ); - } - - void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { - if (m_reportAssertionStarting) { - Detail::LockGuard lock( m_assertionMutex ); - auto _ = scopedDeactivate( *m_outputRedirect ); - m_reporter->assertionStarting( info ); - } - } - - bool RunContext::sectionStarted( StringRef sectionName, - SourceLineInfo const& sectionLineInfo, - Counts& assertions ) { - ITracker& sectionTracker = - SectionTracker::acquire( m_trackerContext, - TestCaseTracking::NameAndLocationRef( - sectionName, sectionLineInfo ) ); - - if (!sectionTracker.isOpen()) - return false; - m_activeSections.push_back(§ionTracker); - - SectionInfo sectionInfo( sectionLineInfo, static_cast(sectionName) ); - Detail::g_lastKnownLineInfo = sectionLineInfo; - - { - auto _ = scopedDeactivate( *m_outputRedirect ); - m_reporter->sectionStarting( sectionInfo ); - } - - updateTotalsFromAtomics(); - assertions = m_totals.assertions; - - return true; - } - IGeneratorTracker* - RunContext::acquireGeneratorTracker( StringRef generatorName, - SourceLineInfo const& lineInfo ) { - auto* tracker = Generators::GeneratorTracker::acquire( - m_trackerContext, - TestCaseTracking::NameAndLocationRef( - generatorName, lineInfo ) ); - Detail::g_lastKnownLineInfo = lineInfo; - return tracker; - } - - IGeneratorTracker* RunContext::createGeneratorTracker( - StringRef generatorName, - SourceLineInfo lineInfo, - Generators::GeneratorBasePtr&& generator ) { - - // TBD: Do we want to avoid the warning if the generator is filtered? - if ( m_config->warnAboutInfiniteGenerators() && - !generator->isFinite() ) { - // We want the semantics of `FAIL()`, but we inline it - // to avoid issues with conditionally prefixed macros - INTERNAL_CATCH_MSG( "FAIL", - Catch::ResultWas::ExplicitFailure, - Catch::ResultDisposition::Normal, - "GENERATE() would run infinitely" ); - } - - auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast( generatorName ), lineInfo ); - auto& currentTracker = m_trackerContext.currentTracker(); - assert( - currentTracker.nameAndLocation() != nameAndLoc && - "Trying to create tracker for a generator that already has one" ); - - auto newTracker = - Catch::Detail::make_unique( - CATCH_MOVE( nameAndLoc ), - m_trackerContext, - ¤tTracker, - CATCH_MOVE( generator ) ); - auto ret = newTracker.get(); - currentTracker.addChild( CATCH_MOVE( newTracker ) ); - - ret->open(); - return ret; - } - - bool RunContext::testForMissingAssertions(Counts& assertions) { - if (assertions.total() != 0) - return false; - if (!m_config->warnAboutMissingAssertions()) - return false; - if (m_trackerContext.currentTracker().hasChildren()) - return false; + m_totals.testCases += deltaTotals.testCases; + testCase.tearDownTestCase(); + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + CATCH_MOVE(redirectedCout), + CATCH_MOVE(redirectedCerr), + aborting())); + + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + void RunContext::assertionEnded(AssertionResult &&result) { + Detail::g_lastKnownLineInfo = result.m_info.lineInfo; + if (result.getResultType() == ResultWas::Ok) { + m_atomicAssertionCount.passed++; + Detail::g_lastAssertionPassed = true; + } else if (result.getResultType() == ResultWas::ExplicitSkip) { + m_atomicAssertionCount.skipped++; + Detail::g_lastAssertionPassed = true; + } else if (!result.succeeded()) { + Detail::g_lastAssertionPassed = false; + if (result.isOk()) { + } else if (m_activeTestCase->getTestCaseInfo().okToFail()) // Read from a shared state established before the threads could start, this is fine + m_atomicAssertionCount.failedButOk++; + else m_atomicAssertionCount.failed++; - assertions.failed++; - return true; + } else { + Detail::g_lastAssertionPassed = true; } - void RunContext::sectionEnded(SectionEndInfo&& endInfo) { - updateTotalsFromAtomics(); - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); + auto &msgHolder = Detail::g_messageHolder(); + msgHolder.repairUnscopedMessageInvariant(); - if (!m_activeSections.empty()) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } - - { - auto _ = scopedDeactivate( *m_outputRedirect ); - m_reporter->sectionEnded( - SectionStats( CATCH_MOVE( endInfo.sectionInfo ), - assertions, - endInfo.durationInSeconds, - missingAssertions ) ); - } + // From here, we are touching shared state and need mutex. + Detail::LockGuard lock(m_assertionMutex); + { + auto _ = scopedDeactivate(*m_outputRedirect); + updateTotalsFromAtomics(); + m_reporter->assertionEnded(AssertionStats(result, msgHolder.getMessages(), m_totals)); } - void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) { - if ( m_unfinishedSections.empty() ) { - m_activeSections.back()->fail(); - } else { - m_activeSections.back()->close(); - } - m_activeSections.pop_back(); - - m_unfinishedSections.push_back(CATCH_MOVE(endInfo)); + if (result.getResultType() != ResultWas::Warning) { + msgHolder.removeUnscopedMessages(); } - void RunContext::benchmarkPreparing( StringRef name ) { - auto _ = scopedDeactivate( *m_outputRedirect ); - m_reporter->benchmarkPreparing( name ); + // Reset working state. assertion info will be reset after + // populateReaction is run if it is needed + m_lastResult = CATCH_MOVE(result); + } + + void RunContext::notifyAssertionStarted(AssertionInfo const &info) { + if (m_reportAssertionStarting) { + Detail::LockGuard lock(m_assertionMutex); + auto _ = scopedDeactivate(*m_outputRedirect); + m_reporter->assertionStarting(info); } - void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { - auto _ = scopedDeactivate( *m_outputRedirect ); - m_reporter->benchmarkStarting( info ); - } - void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { - auto _ = scopedDeactivate( *m_outputRedirect ); - m_reporter->benchmarkEnded( stats ); - } - void RunContext::benchmarkFailed( StringRef error ) { - auto _ = scopedDeactivate( *m_outputRedirect ); - m_reporter->benchmarkFailed( error ); + } + + bool RunContext::sectionStarted(StringRef sectionName, + SourceLineInfo const §ionLineInfo, + Counts &assertions) { + ITracker §ionTracker = SectionTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocationRef( + sectionName, sectionLineInfo)); + + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + SectionInfo sectionInfo(sectionLineInfo, static_cast(sectionName)); + Detail::g_lastKnownLineInfo = sectionLineInfo; + + { + auto _ = scopedDeactivate(*m_outputRedirect); + m_reporter->sectionStarting(sectionInfo); } - std::string RunContext::getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : std::string(); + updateTotalsFromAtomics(); + assertions = m_totals.assertions; + + return true; + } + IGeneratorTracker * + RunContext::acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) { + auto *tracker = Generators::GeneratorTracker::acquire( + m_trackerContext, + TestCaseTracking::NameAndLocationRef( + generatorName, lineInfo)); + Detail::g_lastKnownLineInfo = lineInfo; + return tracker; + } + + IGeneratorTracker *RunContext::createGeneratorTracker( + StringRef generatorName, + SourceLineInfo lineInfo, + Generators::GeneratorBasePtr &&generator) { + // TBD: Do we want to avoid the warning if the generator is filtered? + if (m_config->warnAboutInfiniteGenerators() && !generator->isFinite()) { + // We want the semantics of `FAIL()`, but we inline it + // to avoid issues with conditionally prefixed macros + INTERNAL_CATCH_MSG("FAIL", + Catch::ResultWas::ExplicitFailure, + Catch::ResultDisposition::Normal, + "GENERATE() would run infinitely"); } - const AssertionResult * RunContext::getLastResult() const { - // m_lastResult is updated inside the assertion slow-path, under - // a mutex, so the read needs to happen under mutex as well. + auto nameAndLoc = TestCaseTracking::NameAndLocation(static_cast(generatorName), lineInfo); + auto ¤tTracker = m_trackerContext.currentTracker(); + assert( + currentTracker.nameAndLocation() != nameAndLoc && "Trying to create tracker for a generator that already has one"); - // TBD: The last result only makes sense if it is a thread-local - // thing, because the answer is different per thread, like - // last line info, whether last assertion passed, and so on. - // - // However, the last result was also never updated in the - // assertion fast path, so it was always somewhat broken, - // and since IResultCapture::getLastResult is deprecated, - // we will leave it as is, until it is finally removed. - Detail::LockGuard _( m_assertionMutex ); - return &(*m_lastResult); + auto newTracker = Catch::Detail::make_unique( + CATCH_MOVE(nameAndLoc), + m_trackerContext, + ¤tTracker, + CATCH_MOVE(generator)); + auto ret = newTracker.get(); + currentTracker.addChild(CATCH_MOVE(newTracker)); + + ret->open(); + return ret; + } + + bool RunContext::testForMissingAssertions(Counts &assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_atomicAssertionCount.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo &&endInfo) { + updateTotalsFromAtomics(); + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); } - void RunContext::exceptionEarlyReported() { - m_shouldReportUnexpected = false; + { + auto _ = scopedDeactivate(*m_outputRedirect); + m_reporter->sectionEnded( + SectionStats(CATCH_MOVE(endInfo.sectionInfo), + assertions, + endInfo.durationInSeconds, + missingAssertions)); + } + } + + void RunContext::sectionEndedEarly(SectionEndInfo &&endInfo) { + if (m_unfinishedSections.empty()) { + m_activeSections.back()->fail(); + } else { + m_activeSections.back()->close(); + } + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(CATCH_MOVE(endInfo)); + } + + void RunContext::benchmarkPreparing(StringRef name) { + auto _ = scopedDeactivate(*m_outputRedirect); + m_reporter->benchmarkPreparing(name); + } + void RunContext::benchmarkStarting(BenchmarkInfo const &info) { + auto _ = scopedDeactivate(*m_outputRedirect); + m_reporter->benchmarkStarting(info); + } + void RunContext::benchmarkEnded(BenchmarkStats<> const &stats) { + auto _ = scopedDeactivate(*m_outputRedirect); + m_reporter->benchmarkEnded(stats); + } + void RunContext::benchmarkFailed(StringRef error) { + auto _ = scopedDeactivate(*m_outputRedirect); + m_reporter->benchmarkFailed(error); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + const AssertionResult *RunContext::getLastResult() const { + // m_lastResult is updated inside the assertion slow-path, under + // a mutex, so the read needs to happen under mutex as well. + + // TBD: The last result only makes sense if it is a thread-local + // thing, because the answer is different per thread, like + // last line info, whether last assertion passed, and so on. + // + // However, the last result was also never updated in the + // assertion fast path, so it was always somewhat broken, + // and since IResultCapture::getLastResult is deprecated, + // we will leave it as is, until it is finally removed. + Detail::LockGuard _(m_assertionMutex); + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition(StringRef message) { + // We lock only when touching the reporters directly, to avoid + // deadlocks when we call into other functions that also want + // to lock the mutex before touching reporters. + // + // This does mean that we allow other threads to run while handling + // a fatal error, but this is all a best effort attempt anyway. + { + Detail::LockGuard lock(m_assertionMutex); + // TODO: scoped deactivate here? Just give up and do best effort? + // the deactivation can break things further, OTOH so can the + // capture + auto _ = scopedDeactivate(*m_outputRedirect); + + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); } - void RunContext::handleFatalErrorCondition( StringRef message ) { - // We lock only when touching the reporters directly, to avoid - // deadlocks when we call into other functions that also want - // to lock the mutex before touching reporters. - // - // This does mean that we allow other threads to run while handling - // a fatal error, but this is all a best effort attempt anyway. - { - Detail::LockGuard lock( m_assertionMutex ); - // TODO: scoped deactivate here? Just give up and do best effort? - // the deactivation can break things further, OTOH so can the - // capture - auto _ = scopedDeactivate( *m_outputRedirect ); + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult(ResultWas::FatalErrorCondition, {false}); + tempResult.message = static_cast(message); + AssertionResult result(makeDummyAssertionInfo(), + CATCH_MOVE(tempResult)); - // First notify reporter that bad things happened - m_reporter->fatalErrorEncountered( message ); - } + assertionEnded(CATCH_MOVE(result)); - // Don't rebuild the result -- the stringification itself can cause more fatal errors - // Instead, fake a result data. - AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); - tempResult.message = static_cast(message); - AssertionResult result( makeDummyAssertionInfo(), - CATCH_MOVE( tempResult ) ); + // At this point we touch sections/test cases from this thread + // to try and end them. Technically that is not supported when + // using multiple threads, but the worst thing that can happen + // is that the process aborts harder :-D + Detail::LockGuard lock(m_assertionMutex); - assertionEnded(CATCH_MOVE(result) ); + // Best effort cleanup for sections that have not been destructed yet + // Since this is a fatal error, we have not had and won't have the opportunity to destruct them properly + while (!m_activeSections.empty()) { + auto const &nl = m_activeSections.back()->nameAndLocation(); + SectionEndInfo endInfo{SectionInfo(nl.location, nl.name), {}, 0.0}; + sectionEndedEarly(CATCH_MOVE(endInfo)); + } + handleUnfinishedSections(); + // Recreate section for test case (as we will lose the one that was in scope) + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); - // At this point we touch sections/test cases from this thread - // to try and end them. Technically that is not supported when - // using multiple threads, but the worst thing that can happen - // is that the process aborts harder :-D - Detail::LockGuard lock( m_assertionMutex ); + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); - // Best effort cleanup for sections that have not been destructed yet - // Since this is a fatal error, we have not had and won't have the opportunity to destruct them properly - while (!m_activeSections.empty()) { - auto const& nl = m_activeSections.back()->nameAndLocation(); - SectionEndInfo endInfo{ SectionInfo(nl.location, nl.name), {}, 0.0 }; - sectionEndedEarly(CATCH_MOVE(endInfo)); - } - handleUnfinishedSections(); + auto const &testInfo = m_activeTestCase->getTestCaseInfo(); - // Recreate section for test case (as we will lose the one that was in scope) - auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + updateTotalsFromAtomics(); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false); - m_reporter->sectionEnded( testCaseSectionStats ); + bool RunContext::lastAssertionPassed() { + return Detail::g_lastAssertionPassed; + } - auto const& testInfo = m_activeTestCase->getTestCaseInfo(); + void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) { + // We want to save the line info for better experience with unexpected assertions + Detail::g_lastKnownLineInfo = lineInfo; + ++m_atomicAssertionCount.passed; + Detail::g_lastAssertionPassed = true; + Detail::g_clearMessageScopes = true; + } - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - deltaTotals.assertions.failed = 1; - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - std::string(), - std::string(), - false)); - m_totals.testCases.failed++; - updateTotalsFromAtomics(); - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + void RunContext::updateTotalsFromAtomics() { + m_totals.assertions = Counts{ + m_atomicAssertionCount.passed, + m_atomicAssertionCount.failed, + m_atomicAssertionCount.failedButOk, + m_atomicAssertionCount.skipped, + }; + } + + bool RunContext::aborting() const { + return m_atomicAssertionCount.failed >= m_abortAfterXFailedAssertions; + } + + void RunContext::runCurrentTest() { + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + updateTotalsFromAtomics(); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + Detail::g_lastKnownLineInfo = testCaseInfo.lineInfo; + + Timer timer; + CATCH_TRY { + { + auto _ = scopedActivate(*m_outputRedirect); + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } + CATCH_CATCH_ANON(TestFailureException &) { + // This just means the test was aborted due to failure + } + CATCH_CATCH_ANON(TestSkipException &) { + // This just means the test was explicitly skipped + } + CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException(makeDummyAssertionInfo(), + translateActiveException(), + dummyReaction); + } + } + updateTotalsFromAtomics(); + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + auto &msgHolder = Detail::g_messageHolder(); + msgHolder.removeUnscopedMessages(); + assert(msgHolder.getMessages().empty() && "There should be no leftover messages after the test ends"); + + SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + // We need to engage a handler for signals/structured exceptions + // before running the tests themselves, or the binary can crash + // without failed test being reported. + FatalConditionHandlerGuard _(&m_fatalConditionhandler); + // We keep having issue where some compilers warn about an unused + // variable, even though the type has non-trivial constructor and + // destructor. This is annoying and ugly, but it makes them stfu. + (void)_; + + m_activeTestCase->invoke(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) { + sectionEnded(CATCH_MOVE(*it)); + } + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr( + AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction) { + bool negated = isFalseTest(info.resultDisposition); + bool result = expr.getResult() != negated; + + if (result) { + if (!m_includeSuccessfulResults) { + assertionPassedFastPath(info.lineInfo); + } else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated); + populateReaction( + reaction, info.resultDisposition & ResultDisposition::Normal); + } + } + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated) { + Detail::g_lastKnownLineInfo = info.lineInfo; + AssertionResultData data(resultType, LazyExpression(negated)); + + AssertionResult assertionResult{info, CATCH_MOVE(data)}; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded(CATCH_MOVE(assertionResult)); + } + + void RunContext::handleMessage( + AssertionInfo const &info, + ResultWas::OfType resultType, + std::string &&message, + AssertionReaction &reaction) { + Detail::g_lastKnownLineInfo = info.lineInfo; + + AssertionResultData data(resultType, LazyExpression(false)); + data.message = CATCH_MOVE(message); + AssertionResult assertionResult{info, + CATCH_MOVE(data)}; + + const auto isOk = assertionResult.isOk(); + assertionEnded(CATCH_MOVE(assertionResult)); + if (!isOk) { + populateReaction( + reaction, info.resultDisposition & ResultDisposition::Normal); + } else if (resultType == ResultWas::ExplicitSkip) { + // TODO: Need to handle this explicitly, as ExplicitSkip is + // considered "OK" + reaction.shouldSkip = true; + } + } + + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const &info, + AssertionReaction &reaction) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException( + AssertionInfo const &info, + std::string &&message, + AssertionReaction &reaction) { + Detail::g_lastKnownLineInfo = info.lineInfo; + + AssertionResultData data(ResultWas::ThrewException, LazyExpression(false)); + data.message = CATCH_MOVE(message); + AssertionResult assertionResult{info, CATCH_MOVE(data)}; + assertionEnded(CATCH_MOVE(assertionResult)); + populateReaction(reaction, + info.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::populateReaction(AssertionReaction &reaction, + bool has_normal_disposition) { + reaction.shouldDebugBreak = m_shouldDebugBreak; + reaction.shouldThrow = aborting() || has_normal_disposition; + } + + AssertionInfo RunContext::makeDummyAssertionInfo() { + const bool testCaseJustStarted = Detail::g_lastKnownLineInfo == m_activeTestCase->getTestCaseInfo().lineInfo; + + return AssertionInfo{ + testCaseJustStarted ? "TEST_CASE"_sr : StringRef(), + Detail::g_lastKnownLineInfo, + testCaseJustStarted ? StringRef() : "{Unknown expression after the reported line}"_sr, + ResultDisposition::Normal}; + } + + void RunContext::handleIncomplete( + AssertionInfo const &info) { + using namespace std::string_literals; + Detail::g_lastKnownLineInfo = info.lineInfo; + + 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(CATCH_MOVE(assertionResult)); + } + + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction) { + AssertionResultData data(resultType, LazyExpression(false)); + AssertionResult assertionResult{info, CATCH_MOVE(data)}; + + const auto isOk = assertionResult.isOk(); + if (isOk && !m_includeSuccessfulResults) { + assertionPassedFastPath(info.lineInfo); + return; } - bool RunContext::lastAssertionPassed() { - return Detail::g_lastAssertionPassed; + assertionEnded(CATCH_MOVE(assertionResult)); + if (!isOk) { + populateReaction( + reaction, info.resultDisposition & ResultDisposition::Normal); } + } - void RunContext::assertionPassedFastPath(SourceLineInfo lineInfo) { - // We want to save the line info for better experience with unexpected assertions - Detail::g_lastKnownLineInfo = lineInfo; - ++m_atomicAssertionCount.passed; - Detail::g_lastAssertionPassed = true; - Detail::g_clearMessageScopes = true; - } + void IResultCapture::pushScopedMessage(MessageInfo &&message) { + Detail::g_messageHolder().addScopedMessage(CATCH_MOVE(message)); + } - void RunContext::updateTotalsFromAtomics() { - m_totals.assertions = Counts{ - m_atomicAssertionCount.passed, - m_atomicAssertionCount.failed, - m_atomicAssertionCount.failedButOk, - m_atomicAssertionCount.skipped, - }; - } + void IResultCapture::popScopedMessage(unsigned int messageId) { + Detail::g_messageHolder().removeMessage(messageId); + } - bool RunContext::aborting() const { - return m_atomicAssertionCount.failed >= m_abortAfterXFailedAssertions; - } + void IResultCapture::emplaceUnscopedMessage(MessageBuilder &&builder) { + Detail::g_messageHolder().addUnscopedMessage(CATCH_MOVE(builder)); + } - void RunContext::runCurrentTest() { - auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); - m_reporter->sectionStarting(testCaseSection); - updateTotalsFromAtomics(); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - m_shouldReportUnexpected = true; - Detail::g_lastKnownLineInfo = testCaseInfo.lineInfo; + void IResultCapture::addUnscopedMessage(MessageInfo &&message) { + Detail::g_messageHolder().addUnscopedMessage(CATCH_MOVE(message)); + } - Timer timer; - CATCH_TRY { - { - auto _ = scopedActivate( *m_outputRedirect ); - timer.start(); - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } CATCH_CATCH_ANON (TestFailureException&) { - // This just means the test was aborted due to failure - } CATCH_CATCH_ANON (TestSkipException&) { - // This just means the test was explicitly skipped - } CATCH_CATCH_ALL { - // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions - // are reported without translation at the point of origin. - if ( m_shouldReportUnexpected ) { - AssertionReaction dummyReaction; - handleUnexpectedInflightException( makeDummyAssertionInfo(), - translateActiveException(), - dummyReaction ); - } - } - updateTotalsFromAtomics(); - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); - - m_testCaseTracker->close(); - handleUnfinishedSections(); - auto& msgHolder = Detail::g_messageHolder(); - msgHolder.removeUnscopedMessages(); - assert( msgHolder.getMessages().empty() && - "There should be no leftover messages after the test ends" ); - - SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions); - m_reporter->sectionEnded(testCaseSectionStats); - } - - void RunContext::invokeActiveTestCase() { - // We need to engage a handler for signals/structured exceptions - // before running the tests themselves, or the binary can crash - // without failed test being reported. - FatalConditionHandlerGuard _(&m_fatalConditionhandler); - // We keep having issue where some compilers warn about an unused - // variable, even though the type has non-trivial constructor and - // destructor. This is annoying and ugly, but it makes them stfu. - (void)_; - - m_activeTestCase->invoke(); - } - - void RunContext::handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for ( auto it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it ) { - sectionEnded( CATCH_MOVE( *it ) ); - } - m_unfinishedSections.clear(); - } - - void RunContext::handleExpr( - AssertionInfo const& info, - ITransientExpression const& expr, - AssertionReaction& reaction - ) { - bool negated = isFalseTest( info.resultDisposition ); - bool result = expr.getResult() != negated; - - if( result ) { - if (!m_includeSuccessfulResults) { - assertionPassedFastPath(info.lineInfo); - } - else { - reportExpr(info, ResultWas::Ok, &expr, negated); - } - } - else { - reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); - populateReaction( - reaction, info.resultDisposition & ResultDisposition::Normal ); - } - } - void RunContext::reportExpr( - AssertionInfo const &info, - ResultWas::OfType resultType, - ITransientExpression const *expr, - bool negated ) { - - Detail::g_lastKnownLineInfo = info.lineInfo; - AssertionResultData data( resultType, LazyExpression( negated ) ); - - AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - - assertionEnded( CATCH_MOVE(assertionResult) ); - } - - void RunContext::handleMessage( - AssertionInfo const& info, - ResultWas::OfType resultType, - std::string&& message, - AssertionReaction& reaction - ) { - Detail::g_lastKnownLineInfo = info.lineInfo; - - AssertionResultData data( resultType, LazyExpression( false ) ); - data.message = CATCH_MOVE( message ); - AssertionResult assertionResult{ info, - CATCH_MOVE( data ) }; - - const auto isOk = assertionResult.isOk(); - assertionEnded( CATCH_MOVE(assertionResult) ); - if ( !isOk ) { - populateReaction( - reaction, info.resultDisposition & ResultDisposition::Normal ); - } else if ( resultType == ResultWas::ExplicitSkip ) { - // TODO: Need to handle this explicitly, as ExplicitSkip is - // considered "OK" - reaction.shouldSkip = true; - } - } - - void RunContext::handleUnexpectedExceptionNotThrown( - AssertionInfo const& info, - AssertionReaction& reaction - ) { - handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); - } - - void RunContext::handleUnexpectedInflightException( - AssertionInfo const& info, - std::string&& message, - AssertionReaction& reaction - ) { - Detail::g_lastKnownLineInfo = info.lineInfo; - - AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = CATCH_MOVE(message); - AssertionResult assertionResult{ info, CATCH_MOVE(data) }; - assertionEnded( CATCH_MOVE(assertionResult) ); - populateReaction( reaction, - info.resultDisposition & ResultDisposition::Normal ); - } - - void RunContext::populateReaction( AssertionReaction& reaction, - bool has_normal_disposition ) { - reaction.shouldDebugBreak = m_shouldDebugBreak; - reaction.shouldThrow = aborting() || has_normal_disposition; - } - - AssertionInfo RunContext::makeDummyAssertionInfo() { - const bool testCaseJustStarted = - Detail::g_lastKnownLineInfo == - m_activeTestCase->getTestCaseInfo().lineInfo; - - return AssertionInfo{ - testCaseJustStarted ? "TEST_CASE"_sr : StringRef(), - Detail::g_lastKnownLineInfo, - testCaseJustStarted ? StringRef() : "{Unknown expression after the reported line}"_sr, - ResultDisposition::Normal - }; - } - - void RunContext::handleIncomplete( - AssertionInfo const& info - ) { - using namespace std::string_literals; - Detail::g_lastKnownLineInfo = info.lineInfo; - - 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( CATCH_MOVE(assertionResult) ); - } - - void RunContext::handleNonExpr( - AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction - ) { - AssertionResultData data( resultType, LazyExpression( false ) ); - AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - - const auto isOk = assertionResult.isOk(); - if ( isOk && !m_includeSuccessfulResults ) { - assertionPassedFastPath( info.lineInfo ); - return; - } - - assertionEnded( CATCH_MOVE(assertionResult) ); - if ( !isOk ) { - populateReaction( - reaction, info.resultDisposition & ResultDisposition::Normal ); - } - } - - void IResultCapture::pushScopedMessage( MessageInfo&& message ) { - Detail::g_messageHolder().addScopedMessage( CATCH_MOVE( message ) ); - } - - void IResultCapture::popScopedMessage( unsigned int messageId ) { - Detail::g_messageHolder().removeMessage( messageId ); - } - - void IResultCapture::emplaceUnscopedMessage( MessageBuilder&& builder ) { - Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( builder ) ); - } - - void IResultCapture::addUnscopedMessage( MessageInfo&& message ) { - Detail::g_messageHolder().addUnscopedMessage( CATCH_MOVE( message ) ); - } - - void seedRng(IConfig const& config) { - sharedRng().seed(config.rngSeed()); - } - - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } - -} + void seedRng(IConfig const &config) { + sharedRng().seed(config.rngSeed()); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } +} // namespace Catch namespace Catch { - Section::Section( SectionInfo&& info ): - m_info( CATCH_MOVE( info ) ), - m_sectionIncluded( - getResultCapture().sectionStarted( m_info.name, m_info.lineInfo, m_assertions ) ) { - // Non-"included" sections will not use the timing information - // anyway, so don't bother with the potential syscall. - if (m_sectionIncluded) { - m_timer.start(); - } + Section::Section(SectionInfo &&info) + : m_info(CATCH_MOVE(info)), m_sectionIncluded(getResultCapture().sectionStarted(m_info.name, m_info.lineInfo, m_assertions)) { + // Non-"included" sections will not use the timing information + // anyway, so don't bother with the potential syscall. + if (m_sectionIncluded) { + m_timer.start(); } + } - Section::Section( SourceLineInfo const& _lineInfo, - StringRef _name, - const char* const ): - m_info( { "invalid", static_cast( -1 ) }, std::string{} ), - m_sectionIncluded( - getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) { - // We delay initialization the SectionInfo member until we know - // this section needs it, so we avoid allocating std::string for name. - // We also delay timer start to avoid the potential syscall unless we - // will actually use the result. - if ( m_sectionIncluded ) { - m_info.name = static_cast( _name ); - m_info.lineInfo = _lineInfo; - m_timer.start(); - } + Section::Section(SourceLineInfo const &_lineInfo, + StringRef _name, + const char *const) + : m_info({"invalid", static_cast(-1)}, std::string{}), m_sectionIncluded(getResultCapture().sectionStarted(_name, _lineInfo, m_assertions)) { + // We delay initialization the SectionInfo member until we know + // this section needs it, so we avoid allocating std::string for name. + // We also delay timer start to avoid the potential syscall unless we + // will actually use the result. + if (m_sectionIncluded) { + m_info.name = static_cast(_name); + m_info.lineInfo = _lineInfo; + m_timer.start(); } + } - Section::~Section() { - if( m_sectionIncluded ) { - SectionEndInfo endInfo{ CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds() }; - if ( uncaught_exceptions() ) { - getResultCapture().sectionEndedEarly( CATCH_MOVE(endInfo) ); - } else { - getResultCapture().sectionEnded( CATCH_MOVE( endInfo ) ); - } - } - } - - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; + Section::~Section() { + if (m_sectionIncluded) { + SectionEndInfo endInfo{CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds()}; + if (uncaught_exceptions()) { + getResultCapture().sectionEndedEarly(CATCH_MOVE(endInfo)); + } else { + getResultCapture().sectionEnded(CATCH_MOVE(endInfo)); + } } + } + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } } // end namespace Catch - - #include namespace Catch { - namespace { - static auto getSingletons() -> std::vector*& { - static std::vector* g_singletons = nullptr; - if( !g_singletons ) - g_singletons = new std::vector(); - return g_singletons; - } + namespace { + static auto getSingletons() -> std::vector *& { + static std::vector *g_singletons = nullptr; + if (!g_singletons) + g_singletons = new std::vector(); + return g_singletons; } + } // namespace - ISingleton::~ISingleton() = default; + ISingleton::~ISingleton() = default; - void addSingleton(ISingleton* singleton ) { - getSingletons()->push_back( singleton ); - } - void cleanupSingletons() { - auto& singletons = getSingletons(); - for( auto singleton : *singletons ) - delete singleton; - delete singletons; - singletons = nullptr; - } + void addSingleton(ISingleton *singleton) { + getSingletons()->push_back(singleton); + } + void cleanupSingletons() { + auto &singletons = getSingletons(); + for (auto singleton : *singletons) + delete singleton; + delete singletons; + singletons = nullptr; + } } // namespace Catch - - #include #include namespace Catch { - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { - return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); - } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { - // We can assume that the same file will usually have the same pointer. - // Thus, if the pointers are the same, there is no point in calling the strcmp - return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); - } + bool SourceLineInfo::operator==(SourceLineInfo const &other) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator<(SourceLineInfo const &other) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || (line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { + std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info) { #ifndef __GNUG__ - os << info.file << '(' << info.line << ')'; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ':' << info.line; + os << info.file << ':' << info.line; #endif - return os; - } + return os; + } } // end namespace Catch - - - namespace Catch { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { - CATCH_TRY { - m_exceptions.push_back(exception); - } CATCH_CATCH_ALL { - // If we run out of memory during start-up there's really not a lot more we can do about it - std::terminate(); - } + void StartupExceptionRegistry::add(std::exception_ptr const &exception) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); } + CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } - std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { - return m_exceptions; - } + std::vector const &StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } #endif } // end namespace Catch - - - - #include namespace Catch { // If you #define this you must implement these functions -#if !defined( CATCH_CONFIG_NOSTDOUT ) - std::ostream& cout() { return std::cout; } - std::ostream& cerr() { return std::cerr; } - std::ostream& clog() { return std::clog; } +#if !defined(CATCH_CONFIG_NOSTDOUT) + std::ostream &cout() { + return std::cout; + } + std::ostream &cerr() { + return std::cerr; + } + std::ostream &clog() { + return std::clog; + } #endif } // namespace Catch - - -#include -#include #include +#include +#include #include namespace Catch { - bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + bool startsWith(std::string const &s, std::string const &prefix) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith(StringRef s, char prefix) { + return !s.empty() && s[0] == prefix; + } + bool endsWith(std::string const &s, std::string const &suffix) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith(std::string const &s, char suffix) { + return !s.empty() && s[s.size() - 1] == suffix; + } + bool contains(std::string const &s, std::string const &infix) { + return s.find(infix) != std::string::npos; + } + void toLowerInPlace(std::string &s) { + for (char &c : s) { + c = toLower(c); } - bool startsWith( StringRef s, char prefix ) { - return !s.empty() && s[0] == prefix; + } + std::string toLower(std::string const &s) { + std::string lc = s; + toLowerInPlace(lc); + return lc; + } + char toLower(char c) { + return static_cast(std::tolower(static_cast(c))); + } + + std::string trim(std::string const &str) { + static char const *whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of(whitespaceChars); + std::string::size_type end = str.find_last_not_of(whitespaceChars); + + return start != std::string::npos ? str.substr(start, 1 + end - start) : std::string(); + } + + StringRef trim(StringRef ref) { + const auto is_ws = [](char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + }; + size_t real_begin = 0; + while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } + size_t real_end = ref.size(); + while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } + + return ref.substr(real_begin, real_end - real_begin); + } + + bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis) { + std::size_t i = str.find(replaceThis); + if (i == std::string::npos) { + return false; } - bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + std::size_t copyBegin = 0; + std::string origStr = CATCH_MOVE(str); + str.clear(); + // There is at least one replacement, so reserve with the best guess + // we can make without actually counting the number of occurrences. + str.reserve(origStr.size() - replaceThis.size() + withThis.size()); + do { + str.append(origStr, copyBegin, i - copyBegin); + str += withThis; + copyBegin = i + replaceThis.size(); + if (copyBegin < origStr.size()) + i = origStr.find(replaceThis, copyBegin); + else + i = std::string::npos; + } while (i != std::string::npos); + if (copyBegin < origStr.size()) { + str.append(origStr, copyBegin, origStr.size()); } - bool endsWith( std::string const& s, char suffix ) { - return !s.empty() && s[s.size()-1] == suffix; - } - bool contains( std::string const& s, std::string const& infix ) { - return s.find( infix ) != std::string::npos; - } - void toLowerInPlace( std::string& s ) { - for ( char& c : s ) { - c = toLower( c ); - } - } - std::string toLower( std::string const& s ) { - std::string lc = s; - toLowerInPlace( lc ); - return lc; - } - char toLower(char c) { - return static_cast(std::tolower(static_cast(c))); + return true; + } + + std::vector splitStringRef(StringRef str, char delimiter) { + std::vector subStrings; + std::size_t start = 0; + for (std::size_t pos = 0; pos < str.size(); ++pos) { + if (str[pos] == delimiter) { + if (pos - start > 1) + subStrings.push_back(str.substr(start, pos - start)); + start = pos + 1; + } } + if (start < str.size()) + subStrings.push_back(str.substr(start, str.size() - start)); + return subStrings; + } - std::string trim( std::string const& str ) { - static char const* whitespaceChars = "\n\r\t "; - std::string::size_type start = str.find_first_not_of( whitespaceChars ); - std::string::size_type end = str.find_last_not_of( whitespaceChars ); - - return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); - } - - StringRef trim(StringRef ref) { - const auto is_ws = [](char c) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r'; - }; - size_t real_begin = 0; - while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } - size_t real_end = ref.size(); - while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } - - return ref.substr(real_begin, real_end - real_begin); - } - - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { - std::size_t i = str.find( replaceThis ); - if (i == std::string::npos) { - return false; - } - std::size_t copyBegin = 0; - std::string origStr = CATCH_MOVE(str); - str.clear(); - // There is at least one replacement, so reserve with the best guess - // we can make without actually counting the number of occurrences. - str.reserve(origStr.size() - replaceThis.size() + withThis.size()); - do { - str.append(origStr, copyBegin, i-copyBegin ); - str += withThis; - copyBegin = i + replaceThis.size(); - if( copyBegin < origStr.size() ) - i = origStr.find( replaceThis, copyBegin ); - else - i = std::string::npos; - } while( i != std::string::npos ); - if ( copyBegin < origStr.size() ) { - str.append(origStr, copyBegin, origStr.size() ); - } - return true; - } - - std::vector splitStringRef( StringRef str, char delimiter ) { - std::vector subStrings; - std::size_t start = 0; - for(std::size_t pos = 0; pos < str.size(); ++pos ) { - if( str[pos] == delimiter ) { - if( pos - start > 1 ) - subStrings.push_back( str.substr( start, pos-start ) ); - start = pos+1; - } - } - if( start < str.size() ) - subStrings.push_back( str.substr( start, str.size()-start ) ); - return subStrings; - } - - std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << ' ' << pluraliser.m_label; - if( pluraliser.m_count != 1 ) - os << 's'; - return os; - } - -} - - - -#include -#include -#include - -namespace Catch { - StringRef::StringRef( char const* rawChars ) noexcept - : StringRef( rawChars, std::strlen(rawChars) ) - {} - - - bool StringRef::operator<(StringRef rhs) const noexcept { - if (m_size < rhs.m_size) { - return strncmp(m_start, rhs.m_start, m_size) <= 0; - } - return strncmp(m_start, rhs.m_start, rhs.m_size) < 0; - } - - int StringRef::compare( StringRef rhs ) const { - auto cmpResult = - strncmp( m_start, rhs.m_start, std::min( m_size, rhs.m_size ) ); - - // This means that strncmp found a difference before the strings - // ended, and we can return it directly - if ( cmpResult != 0 ) { - return cmpResult; - } - - // If strings are equal up to length, then their comparison results on - // their size - if ( m_size < rhs.m_size ) { - return -1; - } else if ( m_size > rhs.m_size ) { - return 1; - } else { - return 0; - } - } - - auto operator << ( std::ostream& os, StringRef str ) -> std::ostream& { - return os.write(str.data(), static_cast(str.size())); - } - - std::string operator+(StringRef lhs, StringRef rhs) { - std::string ret; - ret.reserve(lhs.size() + rhs.size()); - ret += lhs; - ret += rhs; - return ret; - } - - auto operator+=( std::string& lhs, StringRef rhs ) -> std::string& { - lhs.append(rhs.data(), rhs.size()); - return lhs; - } + std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if (pluraliser.m_count != 1) + os << 's'; + return os; + } } // namespace Catch +#include +#include +#include +namespace Catch { + StringRef::StringRef(char const *rawChars) noexcept + : StringRef(rawChars, std::strlen(rawChars)) {} + + bool StringRef::operator<(StringRef rhs) const noexcept { + if (m_size < rhs.m_size) { + return strncmp(m_start, rhs.m_start, m_size) <= 0; + } + return strncmp(m_start, rhs.m_start, rhs.m_size) < 0; + } + + int StringRef::compare(StringRef rhs) const { + auto cmpResult = strncmp(m_start, rhs.m_start, std::min(m_size, rhs.m_size)); + + // This means that strncmp found a difference before the strings + // ended, and we can return it directly + if (cmpResult != 0) { + return cmpResult; + } + + // If strings are equal up to length, then their comparison results on + // their size + if (m_size < rhs.m_size) { + return -1; + } else if (m_size > rhs.m_size) { + return 1; + } else { + return 0; + } + } + + auto operator<<(std::ostream &os, StringRef str) -> std::ostream & { + return os.write(str.data(), static_cast(str.size())); + } + + std::string operator+(StringRef lhs, StringRef rhs) { + std::string ret; + ret.reserve(lhs.size() + rhs.size()); + ret += lhs; + ret += rhs; + return ret; + } + + auto operator+=(std::string &lhs, StringRef rhs) -> std::string & { + lhs.append(rhs.data(), rhs.size()); + return lhs; + } + +} // namespace Catch namespace Catch { - TagAliasRegistry::~TagAliasRegistry() = default; + TagAliasRegistry::~TagAliasRegistry() = default; - TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { - auto it = m_registry.find( alias ); - if( it != m_registry.end() ) - return &(it->second); - else - return nullptr; + TagAlias const *TagAliasRegistry::find(std::string const &alias) const { + auto it = m_registry.find(alias); + if (it != m_registry.end()) + return &(it->second); + else + return nullptr; + } + + std::string TagAliasRegistry::expandAliases(std::string const &unexpandedTestSpec) const { + std::string expandedTestSpec = unexpandedTestSpec; + for (auto const ®istryKvp : m_registry) { + std::size_t pos = expandedTestSpec.find(registryKvp.first); + if (pos != std::string::npos) { + expandedTestSpec = expandedTestSpec.substr(0, pos) + registryKvp.second.tag + expandedTestSpec.substr(pos + registryKvp.first.size()); + } } + return expandedTestSpec; + } - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( auto const& registryKvp : m_registry ) { - std::size_t pos = expandedTestSpec.find( registryKvp.first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - registryKvp.second.tag + - expandedTestSpec.substr( pos + registryKvp.first.size() ); - } - } - return expandedTestSpec; - } + void TagAliasRegistry::add(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) { + CATCH_ENFORCE(startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" + << lineInfo); - void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), - "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + CATCH_ENFORCE(m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo); + } - CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, - "error: tag alias, '" << alias << "' already registered.\n" - << "\tFirst seen at: " << find(alias)->lineInfo << "\n" - << "\tRedefined at: " << lineInfo ); - } + ITagAliasRegistry::~ITagAliasRegistry() = default; - ITagAliasRegistry::~ITagAliasRegistry() = default; - - ITagAliasRegistry const& ITagAliasRegistry::get() { - return getRegistryHub().getTagAliasRegistry(); - } + ITagAliasRegistry const &ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } } // end namespace Catch - - - namespace Catch { - TestCaseInfoHasher::TestCaseInfoHasher( hash_t seed ): m_seed( seed ) {} + TestCaseInfoHasher::TestCaseInfoHasher(hash_t seed) + : m_seed(seed) {} - uint32_t TestCaseInfoHasher::operator()( TestCaseInfo const& t ) const { - // FNV-1a hash algorithm that is designed for uniqueness: - const hash_t prime = 1099511628211u; - hash_t hash = 14695981039346656037u; - for ( const char c : t.name ) { - hash ^= c; - hash *= prime; - } - for ( const char c : t.className ) { - hash ^= c; - hash *= prime; - } - for ( const Tag& tag : t.tags ) { - for ( const char c : tag.original ) { - hash ^= c; - hash *= prime; - } - } - hash ^= m_seed; - hash *= prime; - const uint32_t low{ static_cast( hash ) }; - const uint32_t high{ static_cast( hash >> 32 ) }; - return low * high; + uint32_t TestCaseInfoHasher::operator()(TestCaseInfo const &t) const { + // FNV-1a hash algorithm that is designed for uniqueness: + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for (const char c : t.name) { + hash ^= c; + hash *= prime; } + for (const char c : t.className) { + hash ^= c; + hash *= prime; + } + for (const Tag &tag : t.tags) { + for (const char c : tag.original) { + hash ^= c; + hash *= prime; + } + } + hash ^= m_seed; + hash *= prime; + const uint32_t low{static_cast(hash)}; + const uint32_t high{static_cast(hash >> 32)}; + return low * high; + } } // namespace Catch - - - #include #include 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 ); - } + 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: + return unsortedTestCases; + + case TestRunOrder::LexicographicallySorted: { + std::vector sorted = unsortedTestCases; + std::sort( + sorted.begin(), + sorted.end(), + [](TestCaseHandle const &lhs, TestCaseHandle const &rhs) { + return lhs.getTestCaseInfo() < rhs.getTestCaseInfo(); + }); + return sorted; + } + case TestRunOrder::Randomized: { + using TestWithHash = std::pair; + + TestCaseInfoHasher h{config.rngSeed()}; + std::vector indexed_tests; + indexed_tests.reserve(unsortedTestCases.size()); + + for (auto const &handle : unsortedTestCases) { + indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle); } - static bool matchTest( TestCaseHandle const& testCase, - TestSpec const& testSpec, - IConfig const& config ) { - return testSpec.matches( testCase.getTestCaseInfo() ) && - isThrowSafe( testCase, config ); + std::sort(indexed_tests.begin(), + indexed_tests.end(), + [](TestWithHash const &lhs, TestWithHash const &rhs) { + if (lhs.first == rhs.first) { + return lhs.second.getTestCaseInfo() < rhs.second.getTestCaseInfo(); + } + return lhs.first < rhs.first; + }); + + std::vector randomized; + randomized.reserve(indexed_tests.size()); + + for (auto const &indexed : indexed_tests) { + randomized.push_back(indexed.second); } - } // end unnamed namespace - - std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { - switch (config.runOrder()) { - case TestRunOrder::Declared: - return unsortedTestCases; - - case TestRunOrder::LexicographicallySorted: { - std::vector sorted = unsortedTestCases; - std::sort( - sorted.begin(), - sorted.end(), - []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) { - return lhs.getTestCaseInfo() < rhs.getTestCaseInfo(); - } - ); - return sorted; - } - case TestRunOrder::Randomized: { - using TestWithHash = std::pair; - - TestCaseInfoHasher h{ config.rngSeed() }; - std::vector indexed_tests; - indexed_tests.reserve(unsortedTestCases.size()); - - for (auto const& handle : unsortedTestCases) { - indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle); - } - - std::sort( indexed_tests.begin(), - indexed_tests.end(), - []( TestWithHash const& lhs, TestWithHash const& rhs ) { - if ( lhs.first == rhs.first ) { - return lhs.second.getTestCaseInfo() < - rhs.second.getTestCaseInfo(); - } - return lhs.first < rhs.first; - } ); - - std::vector randomized; - randomized.reserve(indexed_tests.size()); - - for (auto const& indexed : indexed_tests) { - randomized.push_back(indexed.second); - } - - return randomized; - } - } - - CATCH_INTERNAL_ERROR("Unknown test order value!"); + return randomized; + } } - bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ) { - return !testCase.getTestCaseInfo().throws() || config.allowThrows(); - } + CATCH_INTERNAL_ERROR("Unknown test order value!"); + } - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for (auto const& testCase : testCases) { - if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) || - (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { - filtered.push_back(testCase); - } - } - return createShard(filtered, config.shardCount(), config.shardIndex()); - } - std::vector const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); - } + bool isThrowSafe(TestCaseHandle const &testCase, IConfig const &config) { + return !testCase.getTestCaseInfo().throws() || config.allowThrows(); + } - TestRegistry::~TestRegistry() = default; - - void TestRegistry::registerTest(Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker) { - m_handles.emplace_back(testInfo.get(), testInvoker.get()); - m_viewed_test_infos.push_back(testInfo.get()); - m_owned_test_infos.push_back(CATCH_MOVE(testInfo)); - m_invokers.push_back(CATCH_MOVE(testInvoker)); + std::vector filterTests(std::vector const &testCases, TestSpec const &testSpec, IConfig const &config) { + std::vector filtered; + filtered.reserve(testCases.size()); + for (auto const &testCase : testCases) { + if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) || (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } } + return createShard(filtered, config.shardCount(), config.shardIndex()); + } + std::vector const &getAllTestCasesSorted(IConfig const &config) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted(config); + } - std::vector const& TestRegistry::getAllInfos() const { - return m_viewed_test_infos; - } + TestRegistry::~TestRegistry() = default; - std::vector const& TestRegistry::getAllTests() const { - return m_handles; - } - std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { - if( m_sortedFunctions.empty() ) - enforceNoDuplicateTestCases( m_handles ); + void TestRegistry::registerTest(Detail::unique_ptr testInfo, Detail::unique_ptr testInvoker) { + m_handles.emplace_back(testInfo.get(), testInvoker.get()); + m_viewed_test_infos.push_back(testInfo.get()); + m_owned_test_infos.push_back(CATCH_MOVE(testInfo)); + m_invokers.push_back(CATCH_MOVE(testInvoker)); + } - if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { - m_sortedFunctions = sortTests( config, m_handles ); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; + std::vector const &TestRegistry::getAllInfos() const { + return m_viewed_test_infos; + } + + std::vector const &TestRegistry::getAllTests() const { + return m_handles; + } + std::vector const &TestRegistry::getAllTestsSorted(IConfig const &config) const { + if (m_sortedFunctions.empty()) + enforceNoDuplicateTestCases(m_handles); + + if (m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty()) { + m_sortedFunctions = sortTests(config, m_handles); + m_currentSortOrder = config.runOrder(); } + return m_sortedFunctions; + } } // end namespace Catch - - - #include #include #if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" #endif namespace Catch { -namespace TestCaseTracking { + namespace TestCaseTracking { - NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location ) - : name( CATCH_MOVE(_name) ), - location( _location ) - {} + NameAndLocation::NameAndLocation(std::string &&_name, SourceLineInfo const &_location) + : name(CATCH_MOVE(_name)), location(_location) {} - ITracker::ITracker( NameAndLocation&& nameAndLoc, ITracker* parent ): - m_nameAndLocation( CATCH_MOVE( nameAndLoc ) ), m_parent( parent ) { - if ( m_parent ) { - m_allTrackerDepth = m_parent->m_allTrackerDepth + 1; - // We leave section trackers to bump themselves up, as - // we cannot use `isSectionTracker` in constructor - m_sectionOnlyDepth = m_parent->m_sectionOnlyDepth; - m_filterRef = m_parent->m_filterRef; - m_newStyleFilters = m_parent->m_newStyleFilters; - } + ITracker::ITracker(NameAndLocation &&nameAndLoc, ITracker *parent) + : m_nameAndLocation(CATCH_MOVE(nameAndLoc)), m_parent(parent) { + if (m_parent) { + m_allTrackerDepth = m_parent->m_allTrackerDepth + 1; + // We leave section trackers to bump themselves up, as + // we cannot use `isSectionTracker` in constructor + m_sectionOnlyDepth = m_parent->m_sectionOnlyDepth; + m_filterRef = m_parent->m_filterRef; + m_newStyleFilters = m_parent->m_newStyleFilters; + } } ITracker::~ITracker() = default; void ITracker::markAsNeedingAnotherRun() { - m_runState = NeedsAnotherRun; + m_runState = NeedsAnotherRun; } - void ITracker::addChild( ITrackerPtr&& child ) { - m_children.push_back( CATCH_MOVE(child) ); + void ITracker::addChild(ITrackerPtr &&child) { + m_children.push_back(CATCH_MOVE(child)); } - ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) { - auto it = std::find_if( - m_children.begin(), - m_children.end(), - [&nameAndLocation]( ITrackerPtr const& tracker ) { - auto const& tnameAndLoc = tracker->nameAndLocation(); - if ( tnameAndLoc.location.line != - nameAndLocation.location.line ) { - return false; - } - return tnameAndLoc == nameAndLocation; - } ); - return ( it != m_children.end() ) ? it->get() : nullptr; + ITracker *ITracker::findChild(NameAndLocationRef const &nameAndLocation) { + auto it = std::find_if( + m_children.begin(), + m_children.end(), + [&nameAndLocation](ITrackerPtr const &tracker) { + auto const &tnameAndLoc = tracker->nameAndLocation(); + if (tnameAndLoc.location.line != nameAndLocation.location.line) { + return false; + } + return tnameAndLoc == nameAndLocation; + }); + return (it != m_children.end()) ? it->get() : nullptr; } - bool ITracker::isSectionTracker() const { return false; } - bool ITracker::isGeneratorTracker() const { return false; } + bool ITracker::isSectionTracker() const { + return false; + } + bool ITracker::isGeneratorTracker() const { + return false; + } bool ITracker::isOpen() const { - return m_runState != NotStarted && !isComplete(); + return m_runState != NotStarted && !isComplete(); } - bool ITracker::hasStarted() const { return m_runState != NotStarted; } + bool ITracker::hasStarted() const { + return m_runState != NotStarted; + } void ITracker::openChild() { - if (m_runState != ExecutingChildren) { - m_runState = ExecutingChildren; - if (m_parent) { - m_parent->openChild(); - } + if (m_runState != ExecutingChildren) { + m_runState = ExecutingChildren; + if (m_parent) { + m_parent->openChild(); } + } } - ITracker& TrackerContext::startRun() { - using namespace std::string_literals; - m_rootTracker = Catch::Detail::make_unique( - NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ), - *this, - nullptr ); - m_currentTracker = nullptr; - m_runState = Executing; - return *m_rootTracker; + ITracker &TrackerContext::startRun() { + using namespace std::string_literals; + m_rootTracker = Catch::Detail::make_unique( + NameAndLocation("{root}"s, CATCH_INTERNAL_LINEINFO), + *this, + nullptr); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; } void TrackerContext::completeCycle() { - m_runState = CompletedCycle; + m_runState = CompletedCycle; } bool TrackerContext::completedCycle() const { - return m_runState == CompletedCycle; + return m_runState == CompletedCycle; } - void TrackerContext::setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; + void TrackerContext::setCurrentTracker(ITracker *tracker) { + m_currentTracker = tracker; } - - TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ): - ITracker(CATCH_MOVE(nameAndLocation), parent), - m_ctx( ctx ) - {} + TrackerBase::TrackerBase(NameAndLocation &&nameAndLocation, TrackerContext &ctx, ITracker *parent) + : ITracker(CATCH_MOVE(nameAndLocation), parent), m_ctx(ctx) {} bool TrackerBase::isComplete() const { - return m_runState == CompletedSuccessfully || m_runState == Failed; + return m_runState == CompletedSuccessfully || m_runState == Failed; } void TrackerBase::open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); + m_runState = Executing; + moveToThis(); + if (m_parent) + m_parent->openChild(); } void TrackerBase::close() { + // Close any still open children (e.g. generators) + while (&m_ctx.currentTracker() != this) + m_ctx.currentTracker().close(); - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); + switch (m_runState) { + case NeedsAnotherRun: + break; - switch( m_runState ) { - case NeedsAnotherRun: - break; + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if (std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const &t) { return t->isComplete(); })) + m_runState = CompletedSuccessfully; + break; - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) ) - m_runState = CompletedSuccessfully; - break; + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR("Illogical state: " << m_runState); - case NotStarted: - case CompletedSuccessfully: - case Failed: - CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); - - default: - CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); - } - moveToParent(); - m_ctx.completeCycle(); + default: + CATCH_INTERNAL_ERROR("Unknown state: " << m_runState); + } + moveToParent(); + m_ctx.completeCycle(); } void TrackerBase::fail() { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); + m_runState = Failed; + if (m_parent) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); } void TrackerBase::moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); + assert(m_parent); + m_ctx.setCurrentTracker(m_parent); } void TrackerBase::moveToThis() { - m_ctx.setCurrentTracker( this ); + m_ctx.setCurrentTracker(this); } - SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), - m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) - { - if( m_parent ) { - ++m_sectionOnlyDepth; - } + SectionTracker::SectionTracker(NameAndLocation &&nameAndLocation, TrackerContext &ctx, ITracker *parent) + : TrackerBase(CATCH_MOVE(nameAndLocation), ctx, parent), m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) { + if (m_parent) { + ++m_sectionOnlyDepth; + } } bool SectionTracker::isComplete() const { - // If there are active filters AND we do not pass them, - // the section is always "completed" - const size_t filterIndex = - m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth; - if ( filterIndex < m_filterRef->size() ) { - // There is active filter, check it - // 1) New style filter must explicitly target section - if ( m_newStyleFilters && ( *m_filterRef )[filterIndex].type != - PathFilter::For::Section ) { - return true; - } - // 2) Both style filters must match the trimmed name exactly - if ( m_trimmed_name != - StringRef( ( *m_filterRef )[filterIndex].filter ) ) { - return true; - } + // If there are active filters AND we do not pass them, + // the section is always "completed" + const size_t filterIndex = m_newStyleFilters ? m_allTrackerDepth : m_sectionOnlyDepth; + if (filterIndex < m_filterRef->size()) { + // There is active filter, check it + // 1) New style filter must explicitly target section + if (m_newStyleFilters && (*m_filterRef)[filterIndex].type != PathFilter::For::Section) { + return true; } + // 2) Both style filters must match the trimmed name exactly + if (m_trimmed_name != StringRef((*m_filterRef)[filterIndex].filter)) { + return true; + } + } - // Otherwise we delegate to the generic processing - return TrackerBase::isComplete(); + // Otherwise we delegate to the generic processing + return TrackerBase::isComplete(); } - bool SectionTracker::isSectionTracker() const { return true; } + bool SectionTracker::isSectionTracker() const { + return true; + } - SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) { - SectionTracker* tracker; + SectionTracker &SectionTracker::acquire(TrackerContext &ctx, NameAndLocationRef const &nameAndLocation) { + SectionTracker *tracker; - ITracker& currentTracker = ctx.currentTracker(); - if ( ITracker* childTracker = - currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - tracker = static_cast( childTracker ); - } else { - auto newTracker = Catch::Detail::make_unique( - NameAndLocation{ static_cast(nameAndLocation.name), - nameAndLocation.location }, - ctx, - ¤tTracker ); - tracker = newTracker.get(); - currentTracker.addChild( CATCH_MOVE( newTracker ) ); - } + ITracker ¤tTracker = ctx.currentTracker(); + if (ITracker *childTracker = currentTracker.findChild(nameAndLocation)) { + assert(childTracker); + assert(childTracker->isSectionTracker()); + tracker = static_cast(childTracker); + } else { + auto newTracker = Catch::Detail::make_unique( + NameAndLocation{static_cast(nameAndLocation.name), + nameAndLocation.location}, + ctx, + ¤tTracker); + tracker = newTracker.get(); + currentTracker.addChild(CATCH_MOVE(newTracker)); + } - if ( !ctx.completedCycle() ) { - tracker->tryOpen(); - } + if (!ctx.completedCycle()) { + tracker->tryOpen(); + } - return *tracker; + return *tracker; } void SectionTracker::tryOpen() { - if( !isComplete() ) - open(); + if (!isComplete()) + open(); } StringRef SectionTracker::trimmedName() const { - return m_trimmed_name; + return m_trimmed_name; } -} // namespace TestCaseTracking + } // namespace TestCaseTracking } // namespace Catch #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif - - - namespace Catch { - void throw_test_failure_exception() { -#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw TestFailureException{}; + void throw_test_failure_exception() { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw TestFailureException{}; #else - CATCH_ERROR( "Test failure requires aborting test!" ); + CATCH_ERROR("Test failure requires aborting test!"); #endif - } + } - void throw_test_skip_exception() { -#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); + void throw_test_skip_exception() { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestSkipException(); #else - CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); + CATCH_ERROR("Explicitly skipping tests during runtime requires exceptions"); #endif - } + } } // namespace Catch - - #include #include namespace Catch { - void ITestInvoker::prepareTestCase() {} - void ITestInvoker::tearDownTestCase() {} - ITestInvoker::~ITestInvoker() = default; + void ITestInvoker::prepareTestCase() {} + void ITestInvoker::tearDownTestCase() {} + ITestInvoker::~ITestInvoker() = default; - namespace { - static StringRef extractClassName( StringRef classOrMethodName ) { - if ( !startsWith( classOrMethodName, '&' ) ) { - return classOrMethodName; - } + namespace { + static StringRef extractClassName(StringRef classOrMethodName) { + if (!startsWith(classOrMethodName, '&')) { + return classOrMethodName; + } - // Remove the leading '&' to avoid having to special case it later - const auto methodName = - classOrMethodName.substr( 1, classOrMethodName.size() ); + // Remove the leading '&' to avoid having to special case it later + const auto methodName = classOrMethodName.substr(1, classOrMethodName.size()); - auto reverseStart = std::make_reverse_iterator( methodName.end() ); - auto reverseEnd = std::make_reverse_iterator( methodName.begin() ); + auto reverseStart = std::make_reverse_iterator(methodName.end()); + auto reverseEnd = std::make_reverse_iterator(methodName.begin()); - // We make a simplifying assumption that ":" is only present - // in the input as part of "::" from C++ typenames (this is - // relatively safe assumption because the input is generated - // as stringification of type through preprocessor). - auto lastColons = std::find( reverseStart, reverseEnd, ':' ) + 1; - auto secondLastColons = - std::find( lastColons + 1, reverseEnd, ':' ); + // We make a simplifying assumption that ":" is only present + // in the input as part of "::" from C++ typenames (this is + // relatively safe assumption because the input is generated + // as stringification of type through preprocessor). + auto lastColons = std::find(reverseStart, reverseEnd, ':') + 1; + auto secondLastColons = std::find(lastColons + 1, reverseEnd, ':'); - auto const startIdx = reverseEnd - secondLastColons; - auto const classNameSize = secondLastColons - lastColons - 1; + auto const startIdx = reverseEnd - secondLastColons; + auto const classNameSize = secondLastColons - lastColons - 1; - return methodName.substr( - static_cast( startIdx ), - static_cast( classNameSize ) ); - } - - class TestInvokerAsFunction final : public ITestInvoker { - using TestType = void ( * )(); - TestType m_testAsFunction; - - public: - constexpr TestInvokerAsFunction( TestType testAsFunction ) noexcept: - m_testAsFunction( testAsFunction ) {} - - void invoke() const override { m_testAsFunction(); } - }; - - } // namespace - - Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ) { - return Detail::make_unique( testAsFunction ); + return methodName.substr( + static_cast(startIdx), + static_cast(classNameSize)); } - AutoReg::AutoReg( Detail::unique_ptr invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { - CATCH_TRY { - getMutableRegistryHub() - .registerTest( - makeTestCaseInfo( - extractClassName( classOrMethod ), - nameAndTags, - lineInfo), - CATCH_MOVE(invoker) - ); - } CATCH_CATCH_ALL { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } + class TestInvokerAsFunction final : public ITestInvoker { + using TestType = void (*)(); + TestType m_testAsFunction; + + public: + constexpr TestInvokerAsFunction(TestType testAsFunction) noexcept + : m_testAsFunction(testAsFunction) {} + + void invoke() const override { m_testAsFunction(); } + }; + + } // namespace + + Detail::unique_ptr makeTestInvoker(void (*testAsFunction)()) { + return Detail::make_unique(testAsFunction); + } + + AutoReg::AutoReg(Detail::unique_ptr invoker, SourceLineInfo const &lineInfo, StringRef classOrMethod, NameAndTags const &nameAndTags) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCaseInfo( + extractClassName(classOrMethod), + nameAndTags, + lineInfo), + CATCH_MOVE(invoker)); } -} - - - - + CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } +} // namespace Catch namespace Catch { - TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser::TestSpecParser(ITagAliasRegistry const &tagAliases) + : m_tagAliases(&tagAliases) {} - TestSpecParser& TestSpecParser::parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_arg = m_tagAliases->expandAliases( arg ); - m_escapeChars.clear(); - m_substring.reserve(m_arg.size()); - m_patternName.reserve(m_arg.size()); - m_realPatternPos = 0; + TestSpecParser &TestSpecParser::parse(std::string const &arg) { + m_mode = None; + m_exclusion = false; + m_arg = m_tagAliases->expandAliases(arg); + m_escapeChars.clear(); + m_substring.reserve(m_arg.size()); + m_patternName.reserve(m_arg.size()); + m_realPatternPos = 0; - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - //if visitChar fails - if( !visitChar( m_arg[m_pos] ) ){ - m_testSpec.m_invalidSpecs.push_back(arg); - break; - } - endMode(); - return *this; - } - TestSpec TestSpecParser::testSpec() { - addFilter(); - return CATCH_MOVE(m_testSpec); - } - bool TestSpecParser::visitChar( char c ) { - if( (m_mode != EscapedName) && (c == '\\') ) { - escape(); - addCharToPattern(c); - return true; - }else if((m_mode != EscapedName) && (c == ',') ) { - return separate(); - } - - switch( m_mode ) { - case None: - if( processNoneChar( c ) ) - return true; - break; - case Name: - processNameChar( c ); - break; - case EscapedName: - endMode(); - addCharToPattern(c); - return true; - default: - case Tag: - case QuotedName: - if( processOtherChar( c ) ) - return true; - break; - } - - m_substring += c; - if( !isControlChar( c ) ) { - m_patternName += c; - m_realPatternPos++; - } - return true; - } - // Two of the processing methods return true to signal the caller to return - // without adding the given character to the current pattern strings - bool TestSpecParser::processNoneChar( char c ) { - switch( c ) { - case ' ': - return true; - case '~': - m_exclusion = true; - return false; - case '[': - startNewMode( Tag ); - return false; - case '"': - startNewMode( QuotedName ); - return false; - default: - startNewMode( Name ); - return false; - } - } - void TestSpecParser::processNameChar( char c ) { - if( c == '[' ) { - if( m_substring == "exclude:" ) - m_exclusion = true; - else - endMode(); - startNewMode( Tag ); - } - } - bool TestSpecParser::processOtherChar( char c ) { - if( !isControlChar( c ) ) - return false; - m_substring += c; - endMode(); - return true; - } - void TestSpecParser::startNewMode( Mode mode ) { - m_mode = mode; - } - void TestSpecParser::endMode() { - switch( m_mode ) { - case Name: - case QuotedName: - return addNamePattern(); - case Tag: - return addTagPattern(); - case EscapedName: - revertBackToLastMode(); - return; - case None: - default: - return startNewMode( None ); - } - } - void TestSpecParser::escape() { - saveLastMode(); - m_mode = EscapedName; - m_escapeChars.push_back(m_realPatternPos); - } - bool TestSpecParser::isControlChar( char c ) const { - switch( m_mode ) { - default: - return false; - case None: - return c == '~'; - case Name: - return c == '['; - case EscapedName: - return true; - case QuotedName: - return c == '"'; - case Tag: - return c == '[' || c == ']'; - } - } - - void TestSpecParser::addFilter() { - if( !m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty() ) { - m_testSpec.m_filters.push_back( CATCH_MOVE(m_currentFilter) ); - m_currentFilter = TestSpec::Filter(); - } - } - - void TestSpecParser::saveLastMode() { - lastMode = m_mode; - } - - void TestSpecParser::revertBackToLastMode() { - m_mode = lastMode; - } - - bool TestSpecParser::separate() { - if( (m_mode==QuotedName) || (m_mode==Tag) ){ - //invalid argument, signal failure to previous scope. - m_mode = None; - m_pos = m_arg.size(); - m_substring.clear(); - m_patternName.clear(); - m_realPatternPos = 0; - return false; + for (m_pos = 0; m_pos < m_arg.size(); ++m_pos) + // if visitChar fails + if (!visitChar(m_arg[m_pos])) { + m_testSpec.m_invalidSpecs.push_back(arg); + break; } - endMode(); - addFilter(); - return true; //success + endMode(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return CATCH_MOVE(m_testSpec); + } + bool TestSpecParser::visitChar(char c) { + if ((m_mode != EscapedName) && (c == '\\')) { + escape(); + addCharToPattern(c); + return true; + } else if ((m_mode != EscapedName) && (c == ',')) { + return separate(); } - std::string TestSpecParser::preprocessPattern() { - std::string token = m_patternName; - for (std::size_t i = 0; i < m_escapeChars.size(); ++i) - token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); - m_escapeChars.clear(); - if (startsWith(token, "exclude:")) { - m_exclusion = true; - token = token.substr(8); + switch (m_mode) { + case None: + if (processNoneChar(c)) + return true; + break; + case Name: + processNameChar(c); + break; + case EscapedName: + endMode(); + addCharToPattern(c); + return true; + default: + case Tag: + case QuotedName: + if (processOtherChar(c)) + return true; + break; + } + + m_substring += c; + if (!isControlChar(c)) { + m_patternName += c; + m_realPatternPos++; + } + return true; + } + // Two of the processing methods return true to signal the caller to return + // without adding the given character to the current pattern strings + bool TestSpecParser::processNoneChar(char c) { + switch (c) { + case ' ': + return true; + case '~': + m_exclusion = true; + return false; + case '[': + startNewMode(Tag); + return false; + case '"': + startNewMode(QuotedName); + return false; + default: + startNewMode(Name); + return false; + } + } + void TestSpecParser::processNameChar(char c) { + if (c == '[') { + if (m_substring == "exclude:") + m_exclusion = true; + else + endMode(); + startNewMode(Tag); + } + } + bool TestSpecParser::processOtherChar(char c) { + if (!isControlChar(c)) + return false; + m_substring += c; + endMode(); + return true; + } + void TestSpecParser::startNewMode(Mode mode) { + m_mode = mode; + } + void TestSpecParser::endMode() { + switch (m_mode) { + case Name: + case QuotedName: + return addNamePattern(); + case Tag: + return addTagPattern(); + case EscapedName: + revertBackToLastMode(); + return; + case None: + default: + return startNewMode(None); + } + } + void TestSpecParser::escape() { + saveLastMode(); + m_mode = EscapedName; + m_escapeChars.push_back(m_realPatternPos); + } + bool TestSpecParser::isControlChar(char c) const { + switch (m_mode) { + default: + return false; + case None: + return c == '~'; + case Name: + return c == '['; + case EscapedName: + return true; + case QuotedName: + return c == '"'; + case Tag: + return c == '[' || c == ']'; + } + } + + void TestSpecParser::addFilter() { + if (!m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty()) { + m_testSpec.m_filters.push_back(CATCH_MOVE(m_currentFilter)); + m_currentFilter = TestSpec::Filter(); + } + } + + void TestSpecParser::saveLastMode() { + lastMode = m_mode; + } + + void TestSpecParser::revertBackToLastMode() { + m_mode = lastMode; + } + + bool TestSpecParser::separate() { + if ((m_mode == QuotedName) || (m_mode == Tag)) { + // invalid argument, signal failure to previous scope. + m_mode = None; + m_pos = m_arg.size(); + m_substring.clear(); + m_patternName.clear(); + m_realPatternPos = 0; + return false; + } + endMode(); + addFilter(); + return true; // success + } + + std::string TestSpecParser::preprocessPattern() { + std::string token = m_patternName; + for (std::size_t i = 0; i < m_escapeChars.size(); ++i) + token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); + m_escapeChars.clear(); + if (startsWith(token, "exclude:")) { + m_exclusion = true; + token = token.substr(8); + } + + m_patternName.clear(); + m_realPatternPos = 0; + + return token; + } + + void TestSpecParser::addNamePattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); + } + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + + void TestSpecParser::addTagPattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) + // we have to create a separate hide tag and shorten the real one + if (token.size() > 1 && token[0] == '.') { + token.erase(token.begin()); + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(".", m_substring)); + } else { + m_currentFilter.m_required.emplace_back(Detail::make_unique(".", m_substring)); } - - m_patternName.clear(); - m_realPatternPos = 0; - - return token; - } - - void TestSpecParser::addNamePattern() { - auto token = preprocessPattern(); - - if (!token.empty()) { - if (m_exclusion) { - m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); - } else { - m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); - } - } - m_substring.clear(); - m_exclusion = false; - m_mode = None; - } - - void TestSpecParser::addTagPattern() { - auto token = preprocessPattern(); - - if (!token.empty()) { - // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) - // we have to create a separate hide tag and shorten the real one - if (token.size() > 1 && token[0] == '.') { - token.erase(token.begin()); - if (m_exclusion) { - m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(".", m_substring)); - } else { - m_currentFilter.m_required.emplace_back(Detail::make_unique(".", m_substring)); - } - } - if (m_exclusion) { - m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); - } else { - m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); - } - } - m_substring.clear(); - m_exclusion = false; - m_mode = None; + } + if (m_exclusion) { + m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); + } else { + m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); + } } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } } // namespace Catch - - #include #include #include namespace { - bool isWhitespace( char c ) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r'; - } + bool isWhitespace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + } - bool isBreakableBefore( char c ) { - static const char chars[] = "[({<|"; - return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; - } + bool isBreakableBefore(char c) { + static const char chars[] = "[({<|"; + return std::memchr(chars, c, sizeof(chars) - 1) != nullptr; + } - bool isBreakableAfter( char c ) { - static const char chars[] = "])}>.,:;*+-=&/\\"; - return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; - } + bool isBreakableAfter(char c) { + static const char chars[] = "])}>.,:;*+-=&/\\"; + return std::memchr(chars, c, sizeof(chars) - 1) != nullptr; + } - bool isUtf8ContinuationByte( char c ) { - return ( static_cast( c ) & 0xC0 ) == 0x80; - } + bool isUtf8ContinuationByte(char c) { + return (static_cast(c) & 0xC0) == 0x80; + } } // namespace namespace Catch { - namespace TextFlow { - void AnsiSkippingString::preprocessString() { - for ( auto it = m_string.begin(); it != m_string.end(); ) { - // try to read through an ansi sequence - while ( it != m_string.end() && *it == '\033' && - it + 1 != m_string.end() && *( it + 1 ) == '[' ) { - auto cursor = it + 2; - while ( cursor != m_string.end() && - ( isdigit( *cursor ) || *cursor == ';' ) ) { - ++cursor; - } - if ( cursor == m_string.end() || *cursor != 'm' ) { - break; - } - // 'm' -> 0xff - *cursor = AnsiSkippingString::sentinel; - // if we've read an ansi sequence, set the iterator and - // return to the top of the loop - it = cursor + 1; - } - if ( it != m_string.end() ) { - ++m_size; - ++it; - // Skip UTF-8 continuation bytes - while ( it != m_string.end() && - isUtf8ContinuationByte( *it ) ) { - ++it; - } - } - } + namespace TextFlow { + void AnsiSkippingString::preprocessString() { + for (auto it = m_string.begin(); it != m_string.end();) { + // try to read through an ansi sequence + while (it != m_string.end() && *it == '\033' && it + 1 != m_string.end() && *(it + 1) == '[') { + auto cursor = it + 2; + while (cursor != m_string.end() && (isdigit(*cursor) || *cursor == ';')) { + ++cursor; + } + if (cursor == m_string.end() || *cursor != 'm') { + break; + } + // 'm' -> 0xff + *cursor = AnsiSkippingString::sentinel; + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + it = cursor + 1; + } + if (it != m_string.end()) { + ++m_size; + ++it; + // Skip UTF-8 continuation bytes + while (it != m_string.end() && isUtf8ContinuationByte(*it)) { + ++it; + } + } + } + } + + AnsiSkippingString::AnsiSkippingString(std::string const &text) + : m_string(text) { + preprocessString(); + } + + AnsiSkippingString::AnsiSkippingString(std::string &&text) + : m_string(CATCH_MOVE(text)) { + preprocessString(); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::begin() const { + return const_iterator(m_string); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::end() const { + return const_iterator(m_string, const_iterator::EndTag{}); + } + + std::string AnsiSkippingString::substring(const_iterator begin, + const_iterator end) const { + // There's one caveat here to an otherwise simple substring: when + // making a begin iterator we might have skipped ansi sequences at + // the start. If `begin` here is a begin iterator, skipped over + // initial ansi sequences, we'll use the true beginning of the + // string. Lastly: We need to transform any chars we replaced with + // 0xff back to 'm' + auto str = std::string(begin == this->begin() ? m_string.begin() + : begin.m_it, + end.m_it); + std::transform(str.begin(), str.end(), str.begin(), [](char c) { + return c == AnsiSkippingString::sentinel ? 'm' : c; + }); + return str; + } + + void AnsiSkippingString::const_iterator::tryParseAnsiEscapes() { + // check if we've landed on an ansi sequence, and if so read through + // it + while (m_it != m_string->end() && *m_it == '\033' && m_it + 1 != m_string->end() && *(m_it + 1) == '[') { + auto cursor = m_it + 2; + while (cursor != m_string->end() && (isdigit(*cursor) || *cursor == ';')) { + ++cursor; + } + if (cursor == m_string->end() || *cursor != AnsiSkippingString::sentinel) { + break; + } + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + m_it = cursor + 1; + } + } + + void AnsiSkippingString::const_iterator::advance() { + assert(m_it != m_string->end()); + m_it++; + // Skip UTF-8 continuation bytes + while (m_it != m_string->end() && isUtf8ContinuationByte(*m_it)) { + m_it++; + } + tryParseAnsiEscapes(); + } + + void AnsiSkippingString::const_iterator::unadvance() { + assert(m_it != m_string->begin()); + m_it--; + // if *m_it is 0xff, scan back to the \033 and then m_it-- once more + // (and repeat check) + while (*m_it == AnsiSkippingString::sentinel) { + while (*m_it != '\033') { + assert(m_it != m_string->begin()); + m_it--; + } + // if this happens, we must have been a begin iterator that had + // skipped over ansi sequences at the start of a string + assert(m_it != m_string->begin()); + assert(*m_it == '\033'); + m_it--; + } + // Skip back over UTF-8 continuation bytes to the leading byte + while (isUtf8ContinuationByte(*m_it)) { + assert(m_it != m_string->begin()); + m_it--; + } + } + + static bool isBoundary(AnsiSkippingString const &line, + AnsiSkippingString::const_iterator it) { + return it == line.end() || (isWhitespace(*it) && !isWhitespace(*it.oneBefore())) || isBreakableBefore(*it) || isBreakableAfter(*it.oneBefore()); + } + + void Column::const_iterator::calcLength() { + m_addHyphen = false; + m_parsedTo = m_lineStart; + AnsiSkippingString const ¤t_line = m_column.m_string; + + if (m_parsedTo == current_line.end()) { + m_lineEnd = m_parsedTo; + return; + } + + assert(m_lineStart != current_line.end()); + if (*m_lineStart == '\n') { ++m_parsedTo; } + + const auto maxLineLength = m_column.m_width - indentSize(); + std::size_t lineLength = 0; + while (m_parsedTo != current_line.end() && lineLength < maxLineLength && *m_parsedTo != '\n') { + ++m_parsedTo; + ++lineLength; + } + + // If we encountered a newline before the column is filled, + // then we linebreak at the newline and consider this line + // finished. + if (lineLength < maxLineLength) { + m_lineEnd = m_parsedTo; + } else { + // Look for a natural linebreak boundary in the column + // (We look from the end, so that the first found boundary is + // the right one) + m_lineEnd = m_parsedTo; + while (lineLength > 0 && !isBoundary(current_line, m_lineEnd)) { + --lineLength; + --m_lineEnd; + } + while (lineLength > 0 && isWhitespace(*m_lineEnd.oneBefore())) { + --lineLength; + --m_lineEnd; } - AnsiSkippingString::AnsiSkippingString( std::string const& text ): - m_string( text ) { - preprocessString(); + // If we found one, then that is where we linebreak, otherwise + // we have to split text with a hyphen + if (lineLength == 0) { + m_addHyphen = true; + m_lineEnd = m_parsedTo.oneBefore(); } + } + } - AnsiSkippingString::AnsiSkippingString( std::string&& text ): - m_string( CATCH_MOVE( text ) ) { - preprocessString(); + size_t Column::const_iterator::indentSize() const { + auto initial = m_lineStart == m_column.m_string.begin() + ? m_column.m_initialIndent + : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + std::string Column::const_iterator::addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end) const { + std::string ret; + const auto desired_indent = indentSize(); + // ret.reserve( desired_indent + (end - start) + m_addHyphen ); + ret.append(desired_indent, ' '); + // ret.append( start, end ); + ret += m_column.m_string.substring(start, end); + if (m_addHyphen) { ret.push_back('-'); } + + return ret; + } + + Column::const_iterator::const_iterator(Column const &column) + : m_column(column), m_lineStart(column.m_string.begin()), m_lineEnd(column.m_string.begin()), m_parsedTo(column.m_string.begin()) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_lineStart == m_lineEnd) { + m_lineStart = m_column.m_string.end(); + } + } + + std::string Column::const_iterator::operator*() const { + assert(m_lineStart <= m_parsedTo); + return addIndentAndSuffix(m_lineStart, m_lineEnd); + } + + Column::const_iterator &Column::const_iterator::operator++() { + m_lineStart = m_lineEnd; + AnsiSkippingString const ¤t_line = m_column.m_string; + if (m_lineStart != current_line.end() && *m_lineStart == '\n') { + m_lineStart++; + } else { + while (m_lineStart != current_line.end() && isWhitespace(*m_lineStart)) { + ++m_lineStart; } + } - AnsiSkippingString::const_iterator AnsiSkippingString::begin() const { - return const_iterator( m_string ); + if (m_lineStart != current_line.end()) { calcLength(); } + return *this; + } + + Column::const_iterator Column::const_iterator::operator++(int) { + const_iterator prev(*this); + operator++(); + return prev; + } + + std::ostream &operator<<(std::ostream &os, Column const &col) { + bool first = true; + for (auto line : col) { + if (first) { + first = false; + } else { + os << '\n'; } + os << line; + } + return os; + } - AnsiSkippingString::const_iterator AnsiSkippingString::end() const { - return const_iterator( m_string, const_iterator::EndTag{} ); + Column Spacer(size_t spaceWidth) { + Column ret{""}; + ret.width(spaceWidth); + return ret; + } + + Columns::iterator::iterator(Columns const &columns, EndTag) + : m_columns(columns.m_columns), m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + for (auto const &col : m_columns) { + m_iterators.push_back(col.end()); + } + } + + Columns::iterator::iterator(Columns const &columns) + : m_columns(columns.m_columns), m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + for (auto const &col : m_columns) { + m_iterators.push_back(col.begin()); + } + } + + std::string Columns::iterator::operator*() const { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + const auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding; + row += col; + + padding.clear(); + if (col.size() < width) { + padding.append(width - col.size(), ' '); + } + } else { + padding.append(width, ' '); } + } + return row; + } - std::string AnsiSkippingString::substring( const_iterator begin, - const_iterator end ) const { - // There's one caveat here to an otherwise simple substring: when - // making a begin iterator we might have skipped ansi sequences at - // the start. If `begin` here is a begin iterator, skipped over - // initial ansi sequences, we'll use the true beginning of the - // string. Lastly: We need to transform any chars we replaced with - // 0xff back to 'm' - auto str = std::string( begin == this->begin() ? m_string.begin() - : begin.m_it, - end.m_it ); - std::transform( str.begin(), str.end(), str.begin(), []( char c ) { - return c == AnsiSkippingString::sentinel ? 'm' : c; - } ); - return str; + Columns::iterator &Columns::iterator::operator++() { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) { + ++m_iterators[i]; } + } + return *this; + } - void AnsiSkippingString::const_iterator::tryParseAnsiEscapes() { - // check if we've landed on an ansi sequence, and if so read through - // it - while ( m_it != m_string->end() && *m_it == '\033' && - m_it + 1 != m_string->end() && *( m_it + 1 ) == '[' ) { - auto cursor = m_it + 2; - while ( cursor != m_string->end() && - ( isdigit( *cursor ) || *cursor == ';' ) ) { - ++cursor; - } - if ( cursor == m_string->end() || - *cursor != AnsiSkippingString::sentinel ) { - break; - } - // if we've read an ansi sequence, set the iterator and - // return to the top of the loop - m_it = cursor + 1; - } + Columns::iterator Columns::iterator::operator++(int) { + iterator prev(*this); + operator++(); + return prev; + } + + std::ostream &operator<<(std::ostream &os, Columns const &cols) { + bool first = true; + for (auto line : cols) { + if (first) { + first = false; + } else { + os << '\n'; } + os << line; + } + return os; + } - void AnsiSkippingString::const_iterator::advance() { - assert( m_it != m_string->end() ); - m_it++; - // Skip UTF-8 continuation bytes - while ( m_it != m_string->end() && - isUtf8ContinuationByte( *m_it ) ) { - m_it++; - } - tryParseAnsiEscapes(); - } + Columns operator+(Column const &lhs, Column const &rhs) { + Columns cols; + cols += lhs; + cols += rhs; + return cols; + } + Columns operator+(Column &&lhs, Column &&rhs) { + Columns cols; + cols += CATCH_MOVE(lhs); + cols += CATCH_MOVE(rhs); + return cols; + } - void AnsiSkippingString::const_iterator::unadvance() { - assert( m_it != m_string->begin() ); - m_it--; - // if *m_it is 0xff, scan back to the \033 and then m_it-- once more - // (and repeat check) - while ( *m_it == AnsiSkippingString::sentinel ) { - while ( *m_it != '\033' ) { - assert( m_it != m_string->begin() ); - m_it--; - } - // if this happens, we must have been a begin iterator that had - // skipped over ansi sequences at the start of a string - assert( m_it != m_string->begin() ); - assert( *m_it == '\033' ); - m_it--; - } - // Skip back over UTF-8 continuation bytes to the leading byte - while ( isUtf8ContinuationByte( *m_it ) ) { - assert( m_it != m_string->begin() ); - m_it--; - } - } + Columns &operator+=(Columns &lhs, Column const &rhs) { + lhs.m_columns.push_back(rhs); + return lhs; + } + Columns &operator+=(Columns &lhs, Column &&rhs) { + lhs.m_columns.push_back(CATCH_MOVE(rhs)); + return lhs; + } + Columns operator+(Columns const &lhs, Column const &rhs) { + auto combined(lhs); + combined += rhs; + return combined; + } + Columns operator+(Columns &&lhs, Column &&rhs) { + lhs += CATCH_MOVE(rhs); + return CATCH_MOVE(lhs); + } - static bool isBoundary( AnsiSkippingString const& line, - AnsiSkippingString::const_iterator it ) { - return it == line.end() || - ( isWhitespace( *it ) && - !isWhitespace( *it.oneBefore() ) ) || - isBreakableBefore( *it ) || - isBreakableAfter( *it.oneBefore() ); - } - - void Column::const_iterator::calcLength() { - m_addHyphen = false; - m_parsedTo = m_lineStart; - AnsiSkippingString const& current_line = m_column.m_string; - - if ( m_parsedTo == current_line.end() ) { - m_lineEnd = m_parsedTo; - return; - } - - assert( m_lineStart != current_line.end() ); - if ( *m_lineStart == '\n' ) { ++m_parsedTo; } - - const auto maxLineLength = m_column.m_width - indentSize(); - std::size_t lineLength = 0; - while ( m_parsedTo != current_line.end() && - lineLength < maxLineLength && *m_parsedTo != '\n' ) { - ++m_parsedTo; - ++lineLength; - } - - // If we encountered a newline before the column is filled, - // then we linebreak at the newline and consider this line - // finished. - if ( lineLength < maxLineLength ) { - m_lineEnd = m_parsedTo; - } else { - // Look for a natural linebreak boundary in the column - // (We look from the end, so that the first found boundary is - // the right one) - m_lineEnd = m_parsedTo; - while ( lineLength > 0 && - !isBoundary( current_line, m_lineEnd ) ) { - --lineLength; - --m_lineEnd; - } - while ( lineLength > 0 && - isWhitespace( *m_lineEnd.oneBefore() ) ) { - --lineLength; - --m_lineEnd; - } - - // If we found one, then that is where we linebreak, otherwise - // we have to split text with a hyphen - if ( lineLength == 0 ) { - m_addHyphen = true; - m_lineEnd = m_parsedTo.oneBefore(); - } - } - } - - size_t Column::const_iterator::indentSize() const { - auto initial = m_lineStart == m_column.m_string.begin() - ? m_column.m_initialIndent - : std::string::npos; - return initial == std::string::npos ? m_column.m_indent : initial; - } - - std::string Column::const_iterator::addIndentAndSuffix( - AnsiSkippingString::const_iterator start, - AnsiSkippingString::const_iterator end ) const { - std::string ret; - const auto desired_indent = indentSize(); - // ret.reserve( desired_indent + (end - start) + m_addHyphen ); - ret.append( desired_indent, ' ' ); - // ret.append( start, end ); - ret += m_column.m_string.substring( start, end ); - if ( m_addHyphen ) { ret.push_back( '-' ); } - - return ret; - } - - Column::const_iterator::const_iterator( Column const& column ): - m_column( column ), - m_lineStart( column.m_string.begin() ), - m_lineEnd( column.m_string.begin() ), - m_parsedTo( column.m_string.begin() ) { - assert( m_column.m_width > m_column.m_indent ); - assert( m_column.m_initialIndent == std::string::npos || - m_column.m_width > m_column.m_initialIndent ); - calcLength(); - if ( m_lineStart == m_lineEnd ) { - m_lineStart = m_column.m_string.end(); - } - } - - std::string Column::const_iterator::operator*() const { - assert( m_lineStart <= m_parsedTo ); - return addIndentAndSuffix( m_lineStart, m_lineEnd ); - } - - Column::const_iterator& Column::const_iterator::operator++() { - m_lineStart = m_lineEnd; - AnsiSkippingString const& current_line = m_column.m_string; - if ( m_lineStart != current_line.end() && *m_lineStart == '\n' ) { - m_lineStart++; - } else { - while ( m_lineStart != current_line.end() && - isWhitespace( *m_lineStart ) ) { - ++m_lineStart; - } - } - - if ( m_lineStart != current_line.end() ) { calcLength(); } - return *this; - } - - Column::const_iterator Column::const_iterator::operator++( int ) { - const_iterator prev( *this ); - operator++(); - return prev; - } - - std::ostream& operator<<( std::ostream& os, Column const& col ) { - bool first = true; - for ( auto line : col ) { - if ( first ) { - first = false; - } else { - os << '\n'; - } - os << line; - } - return os; - } - - Column Spacer( size_t spaceWidth ) { - Column ret{ "" }; - ret.width( spaceWidth ); - return ret; - } - - Columns::iterator::iterator( Columns const& columns, EndTag ): - m_columns( columns.m_columns ), m_activeIterators( 0 ) { - - m_iterators.reserve( m_columns.size() ); - for ( auto const& col : m_columns ) { - m_iterators.push_back( col.end() ); - } - } - - Columns::iterator::iterator( Columns const& columns ): - m_columns( columns.m_columns ), - m_activeIterators( m_columns.size() ) { - - m_iterators.reserve( m_columns.size() ); - for ( auto const& col : m_columns ) { - m_iterators.push_back( col.begin() ); - } - } - - std::string Columns::iterator::operator*() const { - std::string row, padding; - - for ( size_t i = 0; i < m_columns.size(); ++i ) { - const auto width = m_columns[i].width(); - if ( m_iterators[i] != m_columns[i].end() ) { - std::string col = *m_iterators[i]; - row += padding; - row += col; - - padding.clear(); - if ( col.size() < width ) { - padding.append( width - col.size(), ' ' ); - } - } else { - padding.append( width, ' ' ); - } - } - return row; - } - - Columns::iterator& Columns::iterator::operator++() { - for ( size_t i = 0; i < m_columns.size(); ++i ) { - if ( m_iterators[i] != m_columns[i].end() ) { - ++m_iterators[i]; - } - } - return *this; - } - - Columns::iterator Columns::iterator::operator++( int ) { - iterator prev( *this ); - operator++(); - return prev; - } - - std::ostream& operator<<( std::ostream& os, Columns const& cols ) { - bool first = true; - for ( auto line : cols ) { - if ( first ) { - first = false; - } else { - os << '\n'; - } - os << line; - } - return os; - } - - Columns operator+( Column const& lhs, Column const& rhs ) { - Columns cols; - cols += lhs; - cols += rhs; - return cols; - } - Columns operator+( Column&& lhs, Column&& rhs ) { - Columns cols; - cols += CATCH_MOVE( lhs ); - cols += CATCH_MOVE( rhs ); - return cols; - } - - Columns& operator+=( Columns& lhs, Column const& rhs ) { - lhs.m_columns.push_back( rhs ); - return lhs; - } - Columns& operator+=( Columns& lhs, Column&& rhs ) { - lhs.m_columns.push_back( CATCH_MOVE( rhs ) ); - return lhs; - } - Columns operator+( Columns const& lhs, Column const& rhs ) { - auto combined( lhs ); - combined += rhs; - return combined; - } - Columns operator+( Columns&& lhs, Column&& rhs ) { - lhs += CATCH_MOVE( rhs ); - return CATCH_MOVE( lhs ); - } - - } // namespace TextFlow + } // namespace TextFlow } // namespace Catch - - - #include namespace Catch { - bool uncaught_exceptions() { + bool uncaught_exceptions() { #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - return false; + return false; #elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) - return std::uncaught_exceptions() > 0; + return std::uncaught_exceptions() > 0; #else - return std::uncaught_exception(); + return std::uncaught_exception(); #endif } } // end namespace Catch - - namespace Catch { - WildcardPattern::WildcardPattern( std::string const& pattern, - CaseSensitive caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_pattern( normaliseString( pattern ) ) - { - if( startsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } + WildcardPattern::WildcardPattern(std::string const &pattern, + CaseSensitive caseSensitivity) + : m_caseSensitivity(caseSensitivity), m_pattern(normaliseString(pattern)) { + if (startsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(1); + m_wildcard = WildcardAtStart; } - - bool WildcardPattern::matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == normaliseString( str ); - case WildcardAtStart: - return endsWith( normaliseString( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( normaliseString( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( normaliseString( str ), m_pattern ); - default: - CATCH_INTERNAL_ERROR( "Unknown enum" ); - } + if (endsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(0, m_pattern.size() - 1); + m_wildcard = static_cast(m_wildcard | WildcardAtEnd); } + } - std::string WildcardPattern::normaliseString( std::string const& str ) const { - return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str ); + bool WildcardPattern::matches(std::string const &str) const { + switch (m_wildcard) { + case NoWildcard: + return m_pattern == normaliseString(str); + case WildcardAtStart: + return endsWith(normaliseString(str), m_pattern); + case WildcardAtEnd: + return startsWith(normaliseString(str), m_pattern); + case WildcardAtBothEnds: + return contains(normaliseString(str), m_pattern); + default: + CATCH_INTERNAL_ERROR("Unknown enum"); } -} + } + std::string WildcardPattern::normaliseString(std::string const &str) const { + return trim(m_caseSensitivity == CaseSensitive::No ? toLower(str) : str); + } +} // namespace Catch // Note: swapping these two includes around causes MSVC to error out // while in /permissive- mode. No, I don't know why. @@ -8340,486 +7932,463 @@ namespace Catch { namespace Catch { -namespace { + namespace { size_t trailingBytes(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return 2; - } - if ((c & 0xF0) == 0xE0) { - return 3; - } - if ((c & 0xF8) == 0xF0) { - return 4; - } - CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } uint32_t headerValue(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return c & 0x1F; - } - if ((c & 0xF0) == 0xE0) { - return c & 0x0F; - } - if ((c & 0xF8) == 0xF0) { - return c & 0x07; - } - CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } - void hexEscapeChar(std::ostream& os, unsigned char c) { - std::ios_base::fmtflags f(os.flags()); - os << "\\x"_sr - << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast(c); - os.flags(f); + void hexEscapeChar(std::ostream &os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x"_sr + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); } constexpr bool shouldNewline(XmlFormatting fmt) { - return !!(static_cast>(fmt & XmlFormatting::Newline)); + return !!(static_cast>(fmt & XmlFormatting::Newline)); } constexpr bool shouldIndent(XmlFormatting fmt) { - return !!(static_cast>(fmt & XmlFormatting::Indent)); + return !!(static_cast>(fmt & XmlFormatting::Indent)); } -} // anonymous namespace + } // anonymous namespace - void XmlEncode::encodeTo( std::ostream& os ) const { - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - size_t last_start = 0; - auto write_to = [&]( size_t idx ) { - if ( last_start < idx ) { - os << m_str.substr( last_start, idx - last_start ); - } - last_start = idx + 1; - }; + void XmlEncode::encodeTo(std::ostream &os) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + size_t last_start = 0; + auto write_to = [&](size_t idx) { + if (last_start < idx) { + os << m_str.substr(last_start, idx - last_start); + } + last_start = idx + 1; + }; - for ( std::size_t idx = 0; idx < m_str.size(); ++idx ) { - unsigned char c = static_cast( m_str[idx] ); - switch ( c ) { - case '<': - write_to( idx ); - os << "<"_sr; - break; - case '&': - write_to( idx ); - os << "&"_sr; - break; + for (std::size_t idx = 0; idx < m_str.size(); ++idx) { + unsigned char c = static_cast(m_str[idx]); + switch (c) { + case '<': + write_to(idx); + os << "<"_sr; + break; + case '&': + write_to(idx); + os << "&"_sr; + break; - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if ( idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']' ) { - write_to( idx ); - os << ">"_sr; - } - break; + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') { + write_to(idx); + os << ">"_sr; + } + break; - case '\"': - if ( m_forWhat == ForAttributes ) { - write_to( idx ); - os << """_sr; - } - break; + case '\"': + if (m_forWhat == ForAttributes) { + write_to(idx); + os << """_sr; + } + break; - default: - // Check for control characters and invalid utf-8 + default: + // Check for control characters and invalid utf-8 - // Escape control characters in standard ascii - // see - // http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - if ( c < 0x09 || ( c > 0x0D && c < 0x20 ) || c == 0x7F ) { - write_to( idx ); - hexEscapeChar( os, c ); - break; - } + // Escape control characters in standard ascii + // see + // http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + write_to(idx); + hexEscapeChar(os, c); + break; + } - // Plain ASCII: Write it to stream - if ( c < 0x7F ) { - break; - } + // Plain ASCII: Write it to stream + if (c < 0x7F) { + break; + } - // UTF-8 territory - // Check if the encoding is valid and if it is not, hex escape - // bytes. Important: We do not check the exact decoded values for - // validity, only the encoding format First check that this bytes is - // a valid lead byte: This means that it is not encoded as 1111 1XXX - // Or as 10XX XXXX - if ( c < 0xC0 || c >= 0xF8 ) { - write_to( idx ); - hexEscapeChar( os, c ); - break; - } + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape + // bytes. Important: We do not check the exact decoded values for + // validity, only the encoding format First check that this bytes is + // a valid lead byte: This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || c >= 0xF8) { + write_to(idx); + hexEscapeChar(os, c); + break; + } - auto encBytes = trailingBytes( c ); - // Are there enough bytes left to avoid accessing out-of-bounds - // memory? - if ( idx + encBytes - 1 >= m_str.size() ) { - write_to( idx ); - hexEscapeChar( os, c ); - break; - } - // The header is valid, check data - // The next encBytes bytes must together be a valid utf-8 - // This means: bitpattern 10XX XXXX and the extracted value is sane - // (ish) - bool valid = true; - uint32_t value = headerValue( c ); - for ( std::size_t n = 1; n < encBytes; ++n ) { - unsigned char nc = static_cast( m_str[idx + n] ); - valid &= ( ( nc & 0xC0 ) == 0x80 ); - value = ( value << 6 ) | ( nc & 0x3F ); - } + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds + // memory? + if (idx + encBytes - 1 >= m_str.size()) { + write_to(idx); + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane + // (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + unsigned char nc = static_cast(m_str[idx + n]); + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } - if ( - // Wrong bit pattern of following bytes - ( !valid ) || - // Overlong encodings - ( value < 0x80 ) || - ( 0x80 <= value && value < 0x800 && encBytes > 2 ) || - ( 0x800 < value && value < 0x10000 && encBytes > 3 ) || - // Encoded value out of range - ( value >= 0x110000 ) ) { - write_to( idx ); - hexEscapeChar( os, c ); - break; - } + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || (0x80 <= value && value < 0x800 && encBytes > 2) || (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000)) { + write_to(idx); + hexEscapeChar(os, c); + break; + } - // If we got here, this is in fact a valid(ish) utf-8 sequence - idx += encBytes - 1; - break; - } - } - - write_to( m_str.size() ); + // If we got here, this is in fact a valid(ish) utf-8 sequence + idx += encBytes - 1; + break; + } } - std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; + write_to(m_str.size()); + } + + std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode) { + xmlEncode.encodeTo(os); + return os; + } + + XmlWriter::ScopedElement::ScopedElement(XmlWriter *writer, XmlFormatting fmt) + : m_writer(writer), m_fmt(fmt) {} + + XmlWriter::ScopedElement::ScopedElement(ScopedElement &&other) noexcept + : m_writer(other.m_writer), m_fmt(other.m_fmt) { + other.m_writer = nullptr; + other.m_fmt = XmlFormatting::None; + } + XmlWriter::ScopedElement &XmlWriter::ScopedElement::operator=(ScopedElement &&other) noexcept { + if (m_writer) { + m_writer->endElement(); } + m_writer = other.m_writer; + other.m_writer = nullptr; + m_fmt = other.m_fmt; + other.m_fmt = XmlFormatting::None; + return *this; + } - XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt ) - : m_writer( writer ), - m_fmt(fmt) - {} - - XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept - : m_writer( other.m_writer ), - m_fmt(other.m_fmt) - { - other.m_writer = nullptr; - other.m_fmt = XmlFormatting::None; + XmlWriter::ScopedElement::~ScopedElement() { + if (m_writer) { + m_writer->endElement(m_fmt); } - XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { - if ( m_writer ) { - m_writer->endElement(); - } - m_writer = other.m_writer; - other.m_writer = nullptr; - m_fmt = other.m_fmt; - other.m_fmt = XmlFormatting::None; - return *this; + } + + XmlWriter::ScopedElement & + XmlWriter::ScopedElement::writeText(StringRef text, XmlFormatting fmt) { + m_writer->writeText(text, fmt); + return *this; + } + + XmlWriter::ScopedElement & + XmlWriter::ScopedElement::writeAttribute(StringRef name, + StringRef attribute) { + m_writer->writeAttribute(name, attribute); + return *this; + } + + XmlWriter::XmlWriter(std::ostream &os) + : m_os(os) { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while (!m_tags.empty()) { + endElement(); } + newlineIfNecessary(); + } - - XmlWriter::ScopedElement::~ScopedElement() { - if (m_writer) { - m_writer->endElement(m_fmt); - } + XmlWriter &XmlWriter::startElement(std::string const &name, XmlFormatting fmt) { + ensureTagClosed(); + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + m_indent += " "; } + m_os << '<' << name; + m_tags.push_back(name); + m_tagIsOpen = true; + applyFormatting(fmt); + return *this; + } - XmlWriter::ScopedElement& - XmlWriter::ScopedElement::writeText( StringRef text, XmlFormatting fmt ) { - m_writer->writeText( text, fmt ); - return *this; + XmlWriter::ScopedElement XmlWriter::scopedElement(std::string const &name, XmlFormatting fmt) { + ScopedElement scoped(this, fmt); + startElement(name, fmt); + return scoped; + } + + XmlWriter &XmlWriter::endElement(XmlFormatting fmt) { + m_indent = m_indent.substr(0, m_indent.size() - 2); + + if (m_tagIsOpen) { + m_os << "/>"; + m_tagIsOpen = false; + } else { + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << "'; } + m_os << std::flush; + applyFormatting(fmt); + m_tags.pop_back(); + return *this; + } - XmlWriter::ScopedElement& - XmlWriter::ScopedElement::writeAttribute( StringRef name, - StringRef attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; + XmlWriter &XmlWriter::writeAttribute(StringRef name, + StringRef attribute) { + if (!name.empty() && !attribute.empty()) + m_os << ' ' << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << '"'; + return *this; + } + + XmlWriter &XmlWriter::writeAttribute(StringRef name, bool attribute) { + writeAttribute(name, (attribute ? "true"_sr : "false"_sr)); + return *this; + } + + XmlWriter &XmlWriter::writeAttribute(StringRef name, + char const *attribute) { + writeAttribute(name, StringRef(attribute)); + return *this; + } + + XmlWriter &XmlWriter::writeText(StringRef text, XmlFormatting fmt) { + CATCH_ENFORCE(!m_tags.empty(), "Cannot write text as top level element"); + if (!text.empty()) { + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if (tagWasOpen && shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << XmlEncode(text, XmlEncode::ForTextNodes); + applyFormatting(fmt); } + return *this; + } - - XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) - { - writeDeclaration(); + XmlWriter &XmlWriter::writeComment(StringRef text, XmlFormatting fmt) { + ensureTagClosed(); + if (shouldIndent(fmt)) { + m_os << m_indent; } + m_os << ""; + applyFormatting(fmt); + return *this; + } - XmlWriter::~XmlWriter() { - while (!m_tags.empty()) { - endElement(); - } - newlineIfNecessary(); + void XmlWriter::writeStylesheetRef(StringRef url) { + m_os << R"()" << '\n'; + } + + void XmlWriter::ensureTagClosed() { + if (m_tagIsOpen) { + m_os << '>' << std::flush; + newlineIfNecessary(); + m_tagIsOpen = false; } + } - XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) { - ensureTagClosed(); - newlineIfNecessary(); - if (shouldIndent(fmt)) { - m_os << m_indent; - m_indent += " "; - } - m_os << '<' << name; - m_tags.push_back( name ); - m_tagIsOpen = true; - applyFormatting(fmt); - return *this; + void XmlWriter::applyFormatting(XmlFormatting fmt) { + m_needsNewline = shouldNewline(fmt); + } + + void XmlWriter::writeDeclaration() { + m_os << R"()" << '\n'; + } + + void XmlWriter::newlineIfNecessary() { + if (m_needsNewline) { + m_os << '\n' + << std::flush; + m_needsNewline = false; } - - XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) { - ScopedElement scoped( this, fmt ); - startElement( name, fmt ); - return scoped; - } - - XmlWriter& XmlWriter::endElement(XmlFormatting fmt) { - m_indent = m_indent.substr(0, m_indent.size() - 2); - - if( m_tagIsOpen ) { - m_os << "/>"; - m_tagIsOpen = false; - } else { - newlineIfNecessary(); - if (shouldIndent(fmt)) { - m_os << m_indent; - } - m_os << "'; - } - m_os << std::flush; - applyFormatting(fmt); - m_tags.pop_back(); - return *this; - } - - XmlWriter& XmlWriter::writeAttribute( StringRef name, - StringRef attribute ) { - if( !name.empty() && !attribute.empty() ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } - - XmlWriter& XmlWriter::writeAttribute( StringRef name, bool attribute ) { - writeAttribute(name, (attribute ? "true"_sr : "false"_sr)); - return *this; - } - - XmlWriter& XmlWriter::writeAttribute( StringRef name, - char const* attribute ) { - writeAttribute( name, StringRef( attribute ) ); - return *this; - } - - XmlWriter& XmlWriter::writeText( StringRef text, XmlFormatting fmt ) { - CATCH_ENFORCE(!m_tags.empty(), "Cannot write text as top level element"); - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if (tagWasOpen && shouldIndent(fmt)) { - m_os << m_indent; - } - m_os << XmlEncode( text, XmlEncode::ForTextNodes ); - applyFormatting(fmt); - } - return *this; - } - - XmlWriter& XmlWriter::writeComment( StringRef text, XmlFormatting fmt ) { - ensureTagClosed(); - if (shouldIndent(fmt)) { - m_os << m_indent; - } - m_os << ""; - applyFormatting(fmt); - return *this; - } - - void XmlWriter::writeStylesheetRef( StringRef url ) { - m_os << R"()" << '\n'; - } - - void XmlWriter::ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << '>' << std::flush; - newlineIfNecessary(); - m_tagIsOpen = false; - } - } - - void XmlWriter::applyFormatting(XmlFormatting fmt) { - m_needsNewline = shouldNewline(fmt); - } - - void XmlWriter::writeDeclaration() { - m_os << R"()" << '\n'; - } - - void XmlWriter::newlineIfNecessary() { - if( m_needsNewline ) { - m_os << '\n' << std::flush; - m_needsNewline = false; - } - } -} - - - - + } +} // namespace Catch namespace Catch { -namespace Matchers { + namespace Matchers { std::string MatcherUntypedBase::toString() const { - if (m_cachedToString.empty()) { - m_cachedToString = describe(); - } - return m_cachedToString; + if (m_cachedToString.empty()) { + m_cachedToString = describe(); + } + return m_cachedToString; } MatcherUntypedBase::~MatcherUntypedBase() = default; -} // namespace Matchers + } // namespace Matchers } // namespace Catch - - - namespace Catch { -namespace Matchers { + namespace Matchers { std::string IsEmptyMatcher::describe() const { - return "is empty"; + return "is empty"; } std::string HasSizeMatcher::describe() const { - ReusableStringStream sstr; - sstr << "has size == " << m_target_size; - return sstr.str(); + ReusableStringStream sstr; + sstr << "has size == " << m_target_size; + return sstr.str(); } IsEmptyMatcher IsEmpty() { - return {}; + return {}; } HasSizeMatcher SizeIs(std::size_t sz) { - return HasSizeMatcher{ sz }; + return HasSizeMatcher{sz}; } -} // end namespace Matchers + } // end namespace Matchers } // end namespace Catch - - namespace Catch { -namespace Matchers { + namespace Matchers { -bool ExceptionMessageMatcher::match(std::exception const& ex) const { - return ex.what() == m_message; -} + bool ExceptionMessageMatcher::match(std::exception const &ex) const { + return ex.what() == m_message; + } -std::string ExceptionMessageMatcher::describe() const { - return "exception message matches \"" + m_message + '"'; -} + std::string ExceptionMessageMatcher::describe() const { + return "exception message matches \"" + m_message + '"'; + } -ExceptionMessageMatcher Message(std::string const& message) { - return ExceptionMessageMatcher(message); -} + ExceptionMessageMatcher Message(std::string const &message) { + return ExceptionMessageMatcher(message); + } -} // namespace Matchers + } // namespace Matchers } // namespace Catch - - #include #include -#include #include -#include +#include #include #include - +#include namespace Catch { -namespace { + namespace { - template + template bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { - // Comparison with NaN should always be false. - // This way we can rule it out before getting into the ugly details - if (Catch::isnan(lhs) || Catch::isnan(rhs)) { - return false; - } + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } - // This should also handle positive and negative zeros, infinities - const auto ulpDist = ulpDistance(lhs, rhs); + // This should also handle positive and negative zeros, infinities + const auto ulpDist = ulpDistance(lhs, rhs); - return ulpDist <= maxUlpDiff; + return ulpDist <= maxUlpDiff; } - -template -FP step(FP start, FP direction, uint64_t steps) { - for (uint64_t i = 0; i < steps; ++i) { + template + FP step(FP start, FP direction, uint64_t steps) { + for (uint64_t i = 0; i < steps; ++i) { start = Catch::nextafter(start, direction); - } - return start; -} - -// Performs equivalent check of std::fabs(lhs - rhs) <= margin -// But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { - return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} - -template -void write(std::ostream& out, FloatingPoint num) { - out << std::scientific - << std::setprecision(std::numeric_limits::max_digits10 - 1) - << num; -} - -} // end anonymous namespace - -namespace Matchers { -namespace Detail { - - enum class FloatingPointKind : uint8_t { - Float, - Double - }; - -} // end namespace Detail - - - WithinAbsMatcher::WithinAbsMatcher(double target, double margin) - :m_target{ target }, m_margin{ margin } { - CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' - << " Margin has to be non-negative."); + } + return start; } // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison - bool WithinAbsMatcher::match(double const& matchee) const { - return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); + } + + template + void write(std::ostream &out, FloatingPoint num) { + out << std::scientific + << std::setprecision(std::numeric_limits::max_digits10 - 1) + << num; + } + + } // end anonymous namespace + + namespace Matchers { + namespace Detail { + + enum class FloatingPointKind : uint8_t { + Float, + Double + }; + + } // end namespace Detail + + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + : m_target{target}, m_margin{margin} { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const &matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); } std::string WithinAbsMatcher::describe() const { - return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); } - WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType) - :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { - CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double - || m_ulps < (std::numeric_limits::max)(), - "Provided ULP is impossibly large for a float comparison."); - CATCH_ENFORCE( std::numeric_limits::is_iec559, - "WithinUlp matcher only supports platforms with " - "IEEE-754 compatible floating point representation" ); + : m_target{target}, m_ulps{ulps}, m_type{baseType} { + CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double + || m_ulps < (std::numeric_limits::max)(), + "Provided ULP is impossibly large for a float comparison."); + CATCH_ENFORCE(std::numeric_limits::is_iec559, + "WithinUlp matcher only supports platforms with " + "IEEE-754 compatible floating point representation"); } #if defined(__clang__) @@ -8828,15 +8397,15 @@ namespace Detail { #pragma clang diagnostic ignored "-Wunreachable-code" #endif - bool WithinUlpsMatcher::match(double const& matchee) const { - switch (m_type) { + bool WithinUlpsMatcher::match(double const &matchee) const { + switch (m_type) { case Detail::FloatingPointKind::Float: - return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); + return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); case Detail::FloatingPointKind::Double: - return almostEqualUlps(matchee, m_target, m_ulps); + return almostEqualUlps(matchee, m_target, m_ulps); default: - CATCH_INTERNAL_ERROR( "Unknown Detail::FloatingPointKind value" ); - } + CATCH_INTERNAL_ERROR("Unknown Detail::FloatingPointKind value"); + } } #if defined(__clang__) @@ -8844,371 +8413,348 @@ namespace Detail { #endif std::string WithinUlpsMatcher::describe() const { - std::stringstream ret; + std::stringstream ret; - ret << "is within " << m_ulps << " ULPs of "; + ret << "is within " << m_ulps << " ULPs of "; - if (m_type == Detail::FloatingPointKind::Float) { - write(ret, static_cast(m_target)); - ret << 'f'; - } else { - write(ret, m_target); - } + if (m_type == Detail::FloatingPointKind::Float) { + write(ret, static_cast(m_target)); + ret << 'f'; + } else { + write(ret, m_target); + } - ret << " (["; - if (m_type == Detail::FloatingPointKind::Double) { - write( ret, - step( m_target, - -std::numeric_limits::infinity(), - m_ulps ) ); - ret << ", "; - write( ret, - step( m_target, - std::numeric_limits::infinity(), - m_ulps ) ); - } else { - // We have to cast INFINITY to float because of MinGW, see #1782 - write( ret, - step( static_cast( m_target ), - -std::numeric_limits::infinity(), - m_ulps ) ); - ret << ", "; - write( ret, - step( static_cast( m_target ), - std::numeric_limits::infinity(), - m_ulps ) ); - } - ret << "])"; + ret << " (["; + if (m_type == Detail::FloatingPointKind::Double) { + write(ret, + step(m_target, + -std::numeric_limits::infinity(), + m_ulps)); + ret << ", "; + write(ret, + step(m_target, + std::numeric_limits::infinity(), + m_ulps)); + } else { + // We have to cast INFINITY to float because of MinGW, see #1782 + write(ret, + step(static_cast(m_target), + -std::numeric_limits::infinity(), + m_ulps)); + ret << ", "; + write(ret, + step(static_cast(m_target), + std::numeric_limits::infinity(), + m_ulps)); + } + ret << "])"; - return ret.str(); + return ret.str(); } - WithinRelMatcher::WithinRelMatcher(double target, double epsilon): - m_target(target), - m_epsilon(epsilon){ - CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); - CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); + WithinRelMatcher::WithinRelMatcher(double target, double epsilon) + : m_target(target), m_epsilon(epsilon) { + CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); + CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); } - bool WithinRelMatcher::match(double const& matchee) const { - const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); - return marginComparison(matchee, m_target, - std::isinf(relMargin)? 0 : relMargin); + bool WithinRelMatcher::match(double const &matchee) const { + const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); + return marginComparison(matchee, m_target, + std::isinf(relMargin) ? 0 : relMargin); } std::string WithinRelMatcher::describe() const { - Catch::ReusableStringStream sstr; - sstr << "and " << ::Catch::Detail::stringify(m_target) << " are within " << m_epsilon * 100. << "% of each other"; - return sstr.str(); + Catch::ReusableStringStream sstr; + sstr << "and " << ::Catch::Detail::stringify(m_target) << " are within " << m_epsilon * 100. << "% of each other"; + return sstr.str(); } + WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { + return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Double); + } -WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { - return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Double); -} + WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { + return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Float); + } -WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { - return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Float); -} + WithinAbsMatcher WithinAbs(double target, double margin) { + return WithinAbsMatcher(target, margin); + } -WithinAbsMatcher WithinAbs(double target, double margin) { - return WithinAbsMatcher(target, margin); -} + WithinRelMatcher WithinRel(double target, double eps) { + return WithinRelMatcher(target, eps); + } -WithinRelMatcher WithinRel(double target, double eps) { - return WithinRelMatcher(target, eps); -} + WithinRelMatcher WithinRel(double target) { + return WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); + } -WithinRelMatcher WithinRel(double target) { - return WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); -} + WithinRelMatcher WithinRel(float target, float eps) { + return WithinRelMatcher(target, eps); + } -WithinRelMatcher WithinRel(float target, float eps) { - return WithinRelMatcher(target, eps); -} + WithinRelMatcher WithinRel(float target) { + return WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); + } -WithinRelMatcher WithinRel(float target) { - return WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); -} + bool IsNaNMatcher::match(double const &matchee) const { + return std::isnan(matchee); + } + std::string IsNaNMatcher::describe() const { + using namespace std::string_literals; + return "is NaN"s; + } + IsNaNMatcher IsNaN() { + return IsNaNMatcher(); + } -bool IsNaNMatcher::match( double const& matchee ) const { - return std::isnan( matchee ); -} - -std::string IsNaNMatcher::describe() const { - using namespace std::string_literals; - return "is NaN"s; -} - -IsNaNMatcher IsNaN() { return IsNaNMatcher(); } - - } // namespace Matchers + } // namespace Matchers } // namespace Catch - - - -std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) { - if (desc.empty()) { - return "matches undescribed predicate"; - } else { - return "matches predicate: \"" + desc + '"'; - } +std::string Catch::Matchers::Detail::finalizeDescription(const std::string &desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } } - - namespace Catch { - namespace Matchers { - std::string AllTrueMatcher::describe() const { return "contains only true"; } + namespace Matchers { + std::string AllTrueMatcher::describe() const { + return "contains only true"; + } - AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } + AllTrueMatcher AllTrue() { + return AllTrueMatcher{}; + } - std::string NoneTrueMatcher::describe() const { return "contains no true"; } + std::string NoneTrueMatcher::describe() const { + return "contains no true"; + } - NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } + NoneTrueMatcher NoneTrue() { + return NoneTrueMatcher{}; + } - std::string AnyTrueMatcher::describe() const { return "contains at least one true"; } + std::string AnyTrueMatcher::describe() const { + return "contains at least one true"; + } - AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } - } // namespace Matchers + AnyTrueMatcher AnyTrue() { + return AnyTrueMatcher{}; + } + } // namespace Matchers } // namespace Catch - - #include namespace Catch { -namespace Matchers { + namespace Matchers { - CasedString::CasedString( std::string const& str, CaseSensitive caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string CasedString::adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; + CasedString::CasedString(std::string const &str, CaseSensitive caseSensitivity) + : m_caseSensitivity(caseSensitivity), m_str(adjustString(str)) {} + std::string CasedString::adjustString(std::string const &str) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower(str) + : str; } StringRef CasedString::caseSensitivitySuffix() const { - return m_caseSensitivity == CaseSensitive::Yes - ? StringRef() - : " (case insensitive)"_sr; + return m_caseSensitivity == CaseSensitive::Yes + ? StringRef() + : " (case insensitive)"_sr; } - - StringMatcherBase::StringMatcherBase( StringRef operation, CasedString const& comparator ) - : m_comparator( comparator ), - m_operation( operation ) { + StringMatcherBase::StringMatcherBase(StringRef operation, CasedString const &comparator) + : m_comparator(comparator), m_operation(operation) { } std::string StringMatcherBase::describe() const { - std::string description; - description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + - m_comparator.caseSensitivitySuffix().size()); - description += m_operation; - description += ": \""; - description += m_comparator.m_str; - description += '"'; - description += m_comparator.caseSensitivitySuffix(); - return description; + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += '"'; + description += m_comparator.caseSensitivitySuffix(); + return description; } - StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals"_sr, comparator ) {} + StringEqualsMatcher::StringEqualsMatcher(CasedString const &comparator) + : StringMatcherBase("equals"_sr, comparator) {} - bool StringEqualsMatcher::match( std::string const& source ) const { - return m_comparator.adjustString( source ) == m_comparator.m_str; + bool StringEqualsMatcher::match(std::string const &source) const { + return m_comparator.adjustString(source) == m_comparator.m_str; } + StringContainsMatcher::StringContainsMatcher(CasedString const &comparator) + : StringMatcherBase("contains"_sr, comparator) {} - StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains"_sr, comparator ) {} - - bool StringContainsMatcher::match( std::string const& source ) const { - return contains( m_comparator.adjustString( source ), m_comparator.m_str ); + bool StringContainsMatcher::match(std::string const &source) const { + return contains(m_comparator.adjustString(source), m_comparator.m_str); } + StartsWithMatcher::StartsWithMatcher(CasedString const &comparator) + : StringMatcherBase("starts with"_sr, comparator) {} - StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with"_sr, comparator ) {} - - bool StartsWithMatcher::match( std::string const& source ) const { - return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + bool StartsWithMatcher::match(std::string const &source) const { + return startsWith(m_comparator.adjustString(source), m_comparator.m_str); } + EndsWithMatcher::EndsWithMatcher(CasedString const &comparator) + : StringMatcherBase("ends with"_sr, comparator) {} - EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with"_sr, comparator ) {} - - bool EndsWithMatcher::match( std::string const& source ) const { - return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); + bool EndsWithMatcher::match(std::string const &source) const { + return endsWith(m_comparator.adjustString(source), m_comparator.m_str); } + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity) + : m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {} - - RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {} - - bool RegexMatcher::match(std::string const& matchee) const { - auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway - if (m_caseSensitivity == CaseSensitive::No) { - flags |= std::regex::icase; - } - auto reg = std::regex(m_regex, flags); - return std::regex_match(matchee, reg); + bool RegexMatcher::match(std::string const &matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); } std::string RegexMatcher::describe() const { - return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Yes)? " case sensitively" : " case insensitively"); + return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Yes) ? " case sensitively" : " case insensitively"); } - - StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity ) { - return StringEqualsMatcher( CasedString( str, caseSensitivity) ); + StringEqualsMatcher Equals(std::string const &str, CaseSensitive caseSensitivity) { + return StringEqualsMatcher(CasedString(str, caseSensitivity)); } - StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity ) { - return StringContainsMatcher( CasedString( str, caseSensitivity) ); + StringContainsMatcher ContainsSubstring(std::string const &str, CaseSensitive caseSensitivity) { + return StringContainsMatcher(CasedString(str, caseSensitivity)); } - EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity ) { - return EndsWithMatcher( CasedString( str, caseSensitivity) ); + EndsWithMatcher EndsWith(std::string const &str, CaseSensitive caseSensitivity) { + return EndsWithMatcher(CasedString(str, caseSensitivity)); } - StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity ) { - return StartsWithMatcher( CasedString( str, caseSensitivity) ); + StartsWithMatcher StartsWith(std::string const &str, CaseSensitive caseSensitivity) { + return StartsWithMatcher(CasedString(str, caseSensitivity)); } - RegexMatcher Matches(std::string const& regex, CaseSensitive caseSensitivity) { - return RegexMatcher(regex, caseSensitivity); + RegexMatcher Matches(std::string const ®ex, CaseSensitive caseSensitivity) { + return RegexMatcher(regex, caseSensitivity); } -} // namespace Matchers + } // namespace Matchers } // namespace Catch - - namespace Catch { -namespace Matchers { + namespace Matchers { MatcherGenericBase::~MatcherGenericBase() = default; namespace Detail { - std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end) { - std::string description; - std::size_t combined_size = 4; - for ( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { - combined_size += desc->size(); - } - combined_size += static_cast(descriptions_end - descriptions_begin - 1) * combine.size(); - - description.reserve(combined_size); - - description += "( "; - bool first = true; - for( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { - if( first ) - first = false; - else - description += combine; - description += *desc; - } - description += " )"; - return description; + std::string describe_multi_matcher(StringRef combine, std::string const *descriptions_begin, std::string const *descriptions_end) { + std::string description; + std::size_t combined_size = 4; + for (auto desc = descriptions_begin; desc != descriptions_end; ++desc) { + combined_size += desc->size(); } + combined_size += static_cast(descriptions_end - descriptions_begin - 1) * combine.size(); + + description.reserve(combined_size); + + description += "( "; + bool first = true; + for (auto desc = descriptions_begin; desc != descriptions_end; ++desc) { + if (first) + first = false; + else + description += combine; + description += *desc; + } + description += " )"; + return description; + } } // namespace Detail -} // namespace Matchers + } // namespace Matchers } // namespace Catch - - - namespace Catch { - // This is the general overload that takes a any string matcher - // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers - // the Equals matcher (so the header does not mention matchers) - void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher ) { - std::string exceptionMessage = Catch::translateActiveException(); - MatchExpr expr( CATCH_MOVE(exceptionMessage), matcher ); - handler.handleExpr( expr ); - } + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &matcher) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr(CATCH_MOVE(exceptionMessage), matcher); + handler.handleExpr(expr); + } } // namespace Catch - - #include namespace Catch { - AutomakeReporter::~AutomakeReporter() = default; + AutomakeReporter::~AutomakeReporter() = default; - void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { - // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. - m_stream << ":test-result: "; - if ( _testCaseStats.totals.testCases.skipped > 0 ) { - m_stream << "SKIP"; - } else if (_testCaseStats.totals.assertions.allPassed()) { - m_stream << "PASS"; - } else if (_testCaseStats.totals.assertions.allOk()) { - m_stream << "XFAIL"; - } else { - m_stream << "FAIL"; - } - m_stream << ' ' << _testCaseStats.testInfo->name << '\n'; - StreamingReporterBase::testCaseEnded(_testCaseStats); + void AutomakeReporter::testCaseEnded(TestCaseStats const &_testCaseStats) { + // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. + m_stream << ":test-result: "; + if (_testCaseStats.totals.testCases.skipped > 0) { + m_stream << "SKIP"; + } else if (_testCaseStats.totals.assertions.allPassed()) { + m_stream << "PASS"; + } else if (_testCaseStats.totals.assertions.allOk()) { + m_stream << "XFAIL"; + } else { + m_stream << "FAIL"; } + m_stream << ' ' << _testCaseStats.testInfo->name << '\n'; + StreamingReporterBase::testCaseEnded(_testCaseStats); + } - void AutomakeReporter::skipTest(TestCaseInfo const& testInfo) { - m_stream << ":test-result: SKIP " << testInfo.name << '\n'; - } + void AutomakeReporter::skipTest(TestCaseInfo const &testInfo) { + m_stream << ":test-result: SKIP " << testInfo.name << '\n'; + } } // end namespace Catch - - - - - namespace Catch { - ReporterBase::ReporterBase( ReporterConfig&& config ): - IEventListener( config.fullConfig() ), - m_wrapped_stream( CATCH_MOVE(config).takeStream() ), - m_stream( m_wrapped_stream->stream() ), - m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.get() ) ), - m_customOptions( config.customOptions() ) - {} + ReporterBase::ReporterBase(ReporterConfig &&config) + : IEventListener(config.fullConfig()), m_wrapped_stream(CATCH_MOVE(config).takeStream()), m_stream(m_wrapped_stream->stream()), m_colour(makeColourImpl(config.colourMode(), m_wrapped_stream.get())), m_customOptions(config.customOptions()) {} - ReporterBase::~ReporterBase() = default; + ReporterBase::~ReporterBase() = default; - void ReporterBase::listReporters( - std::vector const& descriptions ) { - defaultListReporters( m_stream, descriptions, m_config->verbosity() ); - } + void ReporterBase::listReporters( + std::vector const &descriptions) { + defaultListReporters(m_stream, descriptions, m_config->verbosity()); + } - void ReporterBase::listListeners( - std::vector const& descriptions ) { - defaultListListeners( m_stream, descriptions, m_config->verbosity() ); - } + void ReporterBase::listListeners( + std::vector const &descriptions) { + defaultListListeners(m_stream, descriptions, m_config->verbosity()); + } - void ReporterBase::listTests(std::vector const& tests) { - defaultListTests(m_stream, - m_colour.get(), - tests, - m_config->hasTestFilters(), - m_config->verbosity()); - } + void ReporterBase::listTests(std::vector const &tests) { + defaultListTests(m_stream, + m_colour.get(), + tests, + m_config->hasTestFilters(), + m_config->verbosity()); + } - void ReporterBase::listTags(std::vector const& tags) { - defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_config->verbosity() ); - } + void ReporterBase::listTags(std::vector const &tags) { + defaultListTags(m_stream, tags, m_config->hasTestFilters(), m_config->verbosity()); + } } // namespace Catch - - - #include namespace Catch { -namespace { + namespace { // Colour::LightGrey static constexpr Colour::Code compactDimColour = Colour::FileName; @@ -9221,806 +8767,786 @@ namespace { static constexpr Catch::StringRef compactPassedString = "passed"_sr; #endif -// Implementation of CompactReporter formatting -class AssertionPrinter { -public: - AssertionPrinter& operator= (AssertionPrinter const&) = delete; - AssertionPrinter(AssertionPrinter const&) = delete; - AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages, ColourImpl* colourImpl_) - : stream(_stream) - , result(_stats.assertionResult) - , messages(_stats.infoMessages) - , itMessage(_stats.infoMessages.begin()) - , printInfoMessages(_printInfoMessages) - , colourImpl(colourImpl_) - {} + // Implementation of CompactReporter formatting + class AssertionPrinter { + public: + AssertionPrinter &operator=(AssertionPrinter const &) = delete; + AssertionPrinter(AssertionPrinter const &) = delete; + AssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, bool _printInfoMessages, ColourImpl *colourImpl_) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) + , colourImpl(colourImpl_) {} - void print() { + void print() { printSourceInfo(); itMessage = messages.begin(); switch (result.getResultType()) { - case ResultWas::Ok: + case ResultWas::Ok: printResultType(Colour::ResultSuccess, compactPassedString); printOriginalExpression(); printReconstructedExpression(); if (!result.hasExpression()) - printRemainingMessages(Colour::None); + printRemainingMessages(Colour::None); else - printRemainingMessages(); + printRemainingMessages(); break; - case ResultWas::ExpressionFailed: + case ResultWas::ExpressionFailed: if (result.isOk()) - printResultType(Colour::ResultSuccess, compactFailedString + " - but was ok"_sr); + printResultType(Colour::ResultSuccess, compactFailedString + " - but was ok"_sr); else - printResultType(Colour::Error, compactFailedString); + printResultType(Colour::Error, compactFailedString); printOriginalExpression(); printReconstructedExpression(); printRemainingMessages(); break; - case ResultWas::ThrewException: + case ResultWas::ThrewException: printResultType(Colour::Error, compactFailedString); printIssue("unexpected exception with message:"); printMessage(); printExpressionWas(); printRemainingMessages(); break; - case ResultWas::FatalErrorCondition: + case ResultWas::FatalErrorCondition: printResultType(Colour::Error, compactFailedString); printIssue("fatal error condition with message:"); printMessage(); printExpressionWas(); printRemainingMessages(); break; - case ResultWas::DidntThrowException: + case ResultWas::DidntThrowException: printResultType(Colour::Error, compactFailedString); printIssue("expected exception, got none"); printExpressionWas(); printRemainingMessages(); break; - case ResultWas::Info: + case ResultWas::Info: printResultType(Colour::None, "info"_sr); printMessage(); printRemainingMessages(); break; - case ResultWas::Warning: + case ResultWas::Warning: printResultType(Colour::None, "warning"_sr); printMessage(); printRemainingMessages(); break; - case ResultWas::ExplicitFailure: + case ResultWas::ExplicitFailure: printResultType(Colour::Error, compactFailedString); printIssue("explicitly"); printRemainingMessages(Colour::None); break; - case ResultWas::ExplicitSkip: + case ResultWas::ExplicitSkip: printResultType(Colour::Skip, "skipped"_sr); printMessage(); printRemainingMessages(); break; // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: printResultType(Colour::Error, "** internal error **"); break; } - } + } -private: - void printSourceInfo() const { - stream << colourImpl->guardColour( Colour::FileName ) + private: + void printSourceInfo() const { + stream << colourImpl->guardColour(Colour::FileName) << result.getSourceInfo() << ':'; - } + } - void printResultType(Colour::Code colour, StringRef passOrFail) const { + void printResultType(Colour::Code colour, StringRef passOrFail) const { if (!passOrFail.empty()) { - stream << colourImpl->guardColour(colour) << ' ' << passOrFail; - stream << ':'; + stream << colourImpl->guardColour(colour) << ' ' << passOrFail; + stream << ':'; } - } + } - void printIssue(char const* issue) const { + void printIssue(char const *issue) const { stream << ' ' << issue; - } + } - void printExpressionWas() { + void printExpressionWas() { if (result.hasExpression()) { - stream << ';'; - { - stream << colourImpl->guardColour(compactDimColour) << " expression was:"; - } - printOriginalExpression(); + stream << ';'; + { + stream << colourImpl->guardColour(compactDimColour) << " expression was:"; + } + printOriginalExpression(); } - } + } - void printOriginalExpression() const { + void printOriginalExpression() const { if (result.hasExpression()) { - stream << ' ' << result.getExpression(); + stream << ' ' << result.getExpression(); } - } + } - void printReconstructedExpression() const { + void printReconstructedExpression() const { if (result.hasExpandedExpression()) { - stream << colourImpl->guardColour(compactDimColour) << " for: "; - stream << result.getExpandedExpression(); + stream << colourImpl->guardColour(compactDimColour) << " for: "; + stream << result.getExpandedExpression(); } - } + } - void printMessage() { + void printMessage() { if (itMessage != messages.end()) { - stream << " '" << itMessage->message << '\''; - ++itMessage; + stream << " '" << itMessage->message << '\''; + ++itMessage; } - } + } - void printRemainingMessages(Colour::Code colour = compactDimColour) { + void printRemainingMessages(Colour::Code colour = compactDimColour) { if (itMessage == messages.end()) - return; + return; const auto itEnd = messages.cend(); const auto N = static_cast(itEnd - itMessage); - stream << colourImpl->guardColour( colour ) << " with " - << pluralise( N, "message"_sr ) << ':'; + stream << colourImpl->guardColour(colour) << " with " + << pluralise(N, "message"_sr) << ':'; while (itMessage != itEnd) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || itMessage->type != ResultWas::Info) { - printMessage(); - if (itMessage != itEnd) { - stream << colourImpl->guardColour(compactDimColour) << " and"; - } - continue; + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + printMessage(); + if (itMessage != itEnd) { + stream << colourImpl->guardColour(compactDimColour) << " and"; } - ++itMessage; + continue; + } + ++itMessage; } + } + + private: + std::ostream &stream; + AssertionResult const &result; + std::vector const &messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + ColourImpl *colourImpl; + }; + + } // namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + void CompactReporter::noMatchingTestCases(StringRef unmatchedSpec) { + m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; + } + + void CompactReporter::testRunStarting(TestRunInfo const &) { + if (m_config->testSpec().hasFilters()) { + m_stream << m_colour->guardColour(Colour::BrightYellow) + << "Filters: " + << m_config->testSpec() + << '\n'; + } + m_stream << "RNG seed: " << getSeed() << '\n' + << std::flush; + } + + void CompactReporter::assertionEnded(AssertionStats const &_assertionStats) { + AssertionResult const &result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if (!m_config->includeSuccessfulResults() && result.isOk()) { + if (result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip) + return; + printInfoMessages = false; } -private: - std::ostream& stream; - AssertionResult const& result; - std::vector const& messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - ColourImpl* colourImpl; -}; + AssertionPrinter printer(m_stream, _assertionStats, printInfoMessages, m_colour.get()); + printer.print(); -} // anon namespace + m_stream << '\n' + << std::flush; + } - std::string CompactReporter::getDescription() { - return "Reports test results on a single line, suitable for IDEs"; - } + void CompactReporter::sectionEnded(SectionStats const &_sectionStats) { + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + m_stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' + << std::flush; + } + } - void CompactReporter::noMatchingTestCases( StringRef unmatchedSpec ) { - m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; - } + void CompactReporter::testRunEnded(TestRunStats const &_testRunStats) { + printTestRunTotals(m_stream, *m_colour, _testRunStats.totals); + m_stream << "\n\n" + << std::flush; + StreamingReporterBase::testRunEnded(_testRunStats); + } - void CompactReporter::testRunStarting( TestRunInfo const& ) { - if ( m_config->testSpec().hasFilters() ) { - m_stream << m_colour->guardColour( Colour::BrightYellow ) - << "Filters: " - << m_config->testSpec() - << '\n'; - } - m_stream << "RNG seed: " << getSeed() << '\n' - << std::flush; - } - - void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip ) - return; - printInfoMessages = false; - } - - AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages, m_colour.get() ); - printer.print(); - - m_stream << '\n' << std::flush; - } - - void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { - double dur = _sectionStats.durationInSeconds; - if ( shouldShowDuration( *m_config, dur ) ) { - m_stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush; - } - } - - void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { - printTestRunTotals( m_stream, *m_colour, _testRunStats.totals ); - m_stream << "\n\n" << std::flush; - StreamingReporterBase::testRunEnded( _testRunStats ); - } - - CompactReporter::~CompactReporter() = default; + CompactReporter::~CompactReporter() = default; } // end namespace Catch - - - #include #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch - // Note that 4062 (not all labels are handled and default is missing) is enabled +#pragma warning(disable : 4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled and default is missing) is enabled #endif #if defined(__clang__) -# pragma clang diagnostic push +#pragma clang diagnostic push // For simplicity, benchmarking-only helpers are always enabled -# pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wunused-function" #endif - - namespace Catch { -namespace { + namespace { -// Formatter impl for ConsoleReporter -class ConsoleAssertionPrinter { -public: - ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; - ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; - ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, ColourImpl* colourImpl_, bool _printInfoMessages) - : stream(_stream), - stats(_stats), - result(_stats.assertionResult), - colour(Colour::None), - messages(_stats.infoMessages), - colourImpl(colourImpl_), - printInfoMessages(_printInfoMessages) { + // Formatter impl for ConsoleReporter + class ConsoleAssertionPrinter { + public: + ConsoleAssertionPrinter &operator=(ConsoleAssertionPrinter const &) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const &) = delete; + ConsoleAssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, ColourImpl *colourImpl_, bool _printInfoMessages) + : stream(_stream), stats(_stats), result(_stats.assertionResult), colour(Colour::None), messages(_stats.infoMessages), colourImpl(colourImpl_), printInfoMessages(_printInfoMessages) { switch (result.getResultType()) { - case ResultWas::Ok: + case ResultWas::Ok: colour = Colour::Success; passOrFail = "PASSED"_sr; - //if( result.hasMessage() ) + // if( result.hasMessage() ) if (messages.size() == 1) - messageLabel = "with message"_sr; + messageLabel = "with message"_sr; if (messages.size() > 1) - messageLabel = "with messages"_sr; + messageLabel = "with messages"_sr; break; - case ResultWas::ExpressionFailed: + case ResultWas::ExpressionFailed: if (result.isOk()) { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"_sr; + colour = Colour::Success; + passOrFail = "FAILED - but was ok"_sr; } else { - colour = Colour::Error; - passOrFail = "FAILED"_sr; + colour = Colour::Error; + passOrFail = "FAILED"_sr; } if (messages.size() == 1) - messageLabel = "with message"_sr; + messageLabel = "with message"_sr; if (messages.size() > 1) - messageLabel = "with messages"_sr; + messageLabel = "with messages"_sr; break; - case ResultWas::ThrewException: + case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"_sr; // todo switch - switch (messages.size()) { case 0: + switch (messages.size()) { + case 0: messageLabel = "due to unexpected exception with "_sr; break; - case 1: + case 1: messageLabel = "due to unexpected exception with message"_sr; break; - default: + default: messageLabel = "due to unexpected exception with messages"_sr; break; } break; - case ResultWas::FatalErrorCondition: + case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"_sr; messageLabel = "due to a fatal error condition"_sr; break; - case ResultWas::DidntThrowException: + case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"_sr; messageLabel = "because no exception was thrown where one was expected"_sr; break; - case ResultWas::Info: + case ResultWas::Info: messageLabel = "info"_sr; break; - case ResultWas::Warning: + case ResultWas::Warning: messageLabel = "warning"_sr; break; - case ResultWas::ExplicitFailure: + case ResultWas::ExplicitFailure: passOrFail = "FAILED"_sr; colour = Colour::Error; if (messages.size() == 1) - messageLabel = "explicitly with message"_sr; + messageLabel = "explicitly with message"_sr; if (messages.size() > 1) - messageLabel = "explicitly with messages"_sr; + messageLabel = "explicitly with messages"_sr; break; - case ResultWas::ExplicitSkip: + case ResultWas::ExplicitSkip: colour = Colour::Skip; passOrFail = "SKIPPED"_sr; if (messages.size() == 1) - messageLabel = "explicitly with message"_sr; + messageLabel = "explicitly with message"_sr; if (messages.size() > 1) - messageLabel = "explicitly with messages"_sr; + messageLabel = "explicitly with messages"_sr; break; // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: passOrFail = "** internal error **"_sr; colour = Colour::Error; break; } - } + } - void print() const { + void print() const { printSourceInfo(); if (stats.totals.assertions.total() > 0) { - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); } else { - stream << '\n'; + stream << '\n'; } printMessage(); - } + } -private: - void printResultType() const { + private: + void printResultType() const { if (!passOrFail.empty()) { - stream << colourImpl->guardColour(colour) << passOrFail << ":\n"; + stream << colourImpl->guardColour(colour) << passOrFail << ":\n"; } - } - void printOriginalExpression() const { + } + void printOriginalExpression() const { if (result.hasExpression()) { - stream << colourImpl->guardColour( Colour::OriginalExpression ) - << " " << result.getExpressionInMacro() << '\n'; + stream << colourImpl->guardColour(Colour::OriginalExpression) + << " " << result.getExpressionInMacro() << '\n'; } - } - void printReconstructedExpression() const { + } + void printReconstructedExpression() const { if (result.hasExpandedExpression()) { - stream << "with expansion:\n"; - stream << colourImpl->guardColour( Colour::ReconstructedExpression ) - << TextFlow::Column( result.getExpandedExpression() ) - .indent( 2 ) - << '\n'; + stream << "with expansion:\n"; + stream << colourImpl->guardColour(Colour::ReconstructedExpression) + << TextFlow::Column(result.getExpandedExpression()) + .indent(2) + << '\n'; } - } - void printMessage() const { + } + void printMessage() const { if (!messageLabel.empty()) - stream << messageLabel << ':' << '\n'; - for (auto const& msg : messages) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || msg.type != ResultWas::Info) - stream << TextFlow::Column(msg.message).indent(2) << '\n'; + stream << messageLabel << ':' << '\n'; + for (auto const &msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << TextFlow::Column(msg.message).indent(2) << '\n'; } - } - void printSourceInfo() const { - stream << colourImpl->guardColour( Colour::FileName ) + } + void printSourceInfo() const { + stream << colourImpl->guardColour(Colour::FileName) << result.getSourceInfo() << ": "; + } + + std::ostream &stream; + AssertionStats const &stats; + AssertionResult const &result; + Colour::Code colour; + StringRef passOrFail; + StringRef messageLabel; + std::vector const &messages; + ColourImpl *colourImpl; + bool printInfoMessages; + }; + + std::size_t makeRatio(std::uint64_t number, std::uint64_t total) { + const auto ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : static_cast(ratio); } - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - Colour::Code colour; - StringRef passOrFail; - StringRef messageLabel; - std::vector const& messages; - ColourImpl* colourImpl; - bool printInfoMessages; -}; - -std::size_t makeRatio( std::uint64_t number, std::uint64_t total ) { - const auto ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; - return (ratio == 0 && number > 0) ? 1 : static_cast(ratio); -} - -std::size_t& -findMax( std::size_t& i, std::size_t& j, std::size_t& k, std::size_t& l ) { - if (i > j && i > k && i > l) + std::size_t & + findMax(std::size_t &i, std::size_t &j, std::size_t &k, std::size_t &l) { + if (i > j && i > k && i > l) return i; - else if (j > k && j > l) + else if (j > k && j > l) return j; - else if (k > l) + else if (k > l) return k; - else + else return l; -} + } -struct ColumnBreak {}; -struct RowBreak {}; -struct OutputFlush {}; + struct ColumnBreak {}; + struct RowBreak {}; + struct OutputFlush {}; -class Duration { - enum class Unit : uint8_t { + class Duration { + enum class Unit : uint8_t { Auto, Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes - }; - static const uint64_t s_nanosecondsInAMicrosecond = 1000; - static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; - static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; - static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; - double m_inNanoseconds; - Unit m_units; + double m_inNanoseconds; + Unit m_units; -public: - explicit Duration(double inNanoseconds, Unit units = Unit::Auto) - : m_inNanoseconds(inNanoseconds), - m_units(units) { + public: + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), m_units(units) { if (m_units == Unit::Auto) { - if (m_inNanoseconds < s_nanosecondsInAMicrosecond) - m_units = Unit::Nanoseconds; - else if (m_inNanoseconds < s_nanosecondsInAMillisecond) - m_units = Unit::Microseconds; - else if (m_inNanoseconds < s_nanosecondsInASecond) - m_units = Unit::Milliseconds; - else if (m_inNanoseconds < s_nanosecondsInAMinute) - m_units = Unit::Seconds; - else - m_units = Unit::Minutes; + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; } + } - } - - auto value() const -> double { + auto value() const -> double { switch (m_units) { - case Unit::Microseconds: + case Unit::Microseconds: return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); - case Unit::Milliseconds: + case Unit::Milliseconds: return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); - case Unit::Seconds: + case Unit::Seconds: return m_inNanoseconds / static_cast(s_nanosecondsInASecond); - case Unit::Minutes: + case Unit::Minutes: return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); - default: + default: return m_inNanoseconds; } - } - StringRef unitsAsString() const { + } + StringRef unitsAsString() const { switch (m_units) { - case Unit::Nanoseconds: + case Unit::Nanoseconds: return "ns"_sr; - case Unit::Microseconds: + case Unit::Microseconds: return "us"_sr; - case Unit::Milliseconds: + case Unit::Milliseconds: return "ms"_sr; - case Unit::Seconds: + case Unit::Seconds: return "s"_sr; - case Unit::Minutes: + case Unit::Minutes: return "m"_sr; - default: + default: return "** internal error **"_sr; } - - } - friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { + } + friend auto operator<<(std::ostream &os, Duration const &duration) -> std::ostream & { return os << duration.value() << ' ' << duration.unitsAsString(); - } -}; -} // end anon namespace + } + }; + } // namespace -enum class Justification : uint8_t { + enum class Justification : uint8_t { Left, Right -}; + }; -struct ColumnInfo { + struct ColumnInfo { std::string name; std::size_t width; Justification justification; -}; + }; -class TablePrinter { - std::ostream& m_os; + class TablePrinter { + std::ostream &m_os; std::vector m_columnInfos; ReusableStringStream m_oss; int m_currentColumn = -1; bool m_isOpen = false; -public: - TablePrinter( std::ostream& os, std::vector columnInfos ) - : m_os( os ), - m_columnInfos( CATCH_MOVE( columnInfos ) ) {} + public: + TablePrinter(std::ostream &os, std::vector columnInfos) + : m_os(os), m_columnInfos(CATCH_MOVE(columnInfos)) {} - auto columnInfos() const -> std::vector const& { - return m_columnInfos; + auto columnInfos() const -> std::vector const & { + return m_columnInfos; } void open() { - if (!m_isOpen) { - m_isOpen = true; - *this << RowBreak(); + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); - TextFlow::Columns headerCols; - for (auto const& info : m_columnInfos) { - assert(info.width > 2); - headerCols += TextFlow::Column(info.name).width(info.width - 2); - headerCols += TextFlow::Spacer( 2 ); - } - m_os << headerCols << '\n'; - - m_os << lineOfChars('-') << '\n'; + TextFlow::Columns headerCols; + for (auto const &info : m_columnInfos) { + assert(info.width > 2); + headerCols += TextFlow::Column(info.name).width(info.width - 2); + headerCols += TextFlow::Spacer(2); } + m_os << headerCols << '\n'; + + m_os << lineOfChars('-') << '\n'; + } } void close() { - if (m_isOpen) { - *this << RowBreak(); - m_os << '\n' << std::flush; - m_isOpen = false; - } + if (m_isOpen) { + *this << RowBreak(); + m_os << '\n' + << std::flush; + m_isOpen = false; + } } template - friend TablePrinter& operator<< (TablePrinter& tp, T const& value) { - tp.m_oss << value; - return tp; + friend TablePrinter &operator<<(TablePrinter &tp, T const &value) { + tp.m_oss << value; + return tp; } - friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) { - auto colStr = tp.m_oss.str(); - const auto strSize = colStr.size(); - tp.m_oss.str(""); - tp.open(); - if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { - tp.m_currentColumn = -1; - tp.m_os << '\n'; - } - tp.m_currentColumn++; + friend TablePrinter &operator<<(TablePrinter &tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + const auto strSize = colStr.size(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << '\n'; + } + tp.m_currentColumn++; - auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; - auto padding = (strSize + 1 < colInfo.width) - ? std::string(colInfo.width - (strSize + 1), ' ') - : std::string(); - if (colInfo.justification == Justification::Left) - tp.m_os << colStr << padding << ' '; - else - tp.m_os << padding << colStr << ' '; - return tp; + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 1 < colInfo.width) + ? std::string(colInfo.width - (strSize + 1), ' ') + : std::string(); + if (colInfo.justification == Justification::Left) + tp.m_os << colStr << padding << ' '; + else + tp.m_os << padding << colStr << ' '; + return tp; } - friend TablePrinter& operator<< (TablePrinter& tp, RowBreak) { - if (tp.m_currentColumn > 0) { - tp.m_os << '\n'; - tp.m_currentColumn = -1; - } - return tp; + friend TablePrinter &operator<<(TablePrinter &tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << '\n'; + tp.m_currentColumn = -1; + } + return tp; } - friend TablePrinter& operator<<(TablePrinter& tp, OutputFlush) { - tp.m_os << std::flush; - return tp; + friend TablePrinter &operator<<(TablePrinter &tp, OutputFlush) { + tp.m_os << std::flush; + return tp; } -}; + }; -ConsoleReporter::ConsoleReporter(ReporterConfig&& config): - StreamingReporterBase( CATCH_MOVE( config ) ), - m_tablePrinter(Detail::make_unique(m_stream, - [&config]() -> std::vector { - if (config.fullConfig()->benchmarkNoAnalysis()) - { - return{ - { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, - { " samples", 14, Justification::Right }, - { " iterations", 14, Justification::Right }, - { " mean", 14, Justification::Right } - }; + ConsoleReporter::ConsoleReporter(ReporterConfig &&config) + : StreamingReporterBase(CATCH_MOVE(config)), m_tablePrinter(Detail::make_unique(m_stream, [&config]() -> std::vector { + if (config.fullConfig()->benchmarkNoAnalysis()) { + return { + {"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left}, + {" samples", 14, Justification::Right}, + {" iterations", 14, Justification::Right}, + {" mean", 14, Justification::Right}}; + } else { + return { + {"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left}, + {"samples mean std dev", 14, Justification::Right}, + {"iterations low mean low std dev", 14, Justification::Right}, + {"est run time high mean high std dev", 14, Justification::Right}}; } - else - { - return{ - { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, - { "samples mean std dev", 14, Justification::Right }, - { "iterations low mean low std dev", 14, Justification::Right }, - { "est run time high mean high std dev", 14, Justification::Right } - }; - } - }())) { + }())) { m_preferences.shouldReportAllAssertionStarts = false; -} + } -ConsoleReporter::~ConsoleReporter() = default; + ConsoleReporter::~ConsoleReporter() = default; -std::string ConsoleReporter::getDescription() { + std::string ConsoleReporter::getDescription() { return "Reports test results as plain lines of text"; -} + } -void ConsoleReporter::noMatchingTestCases( StringRef unmatchedSpec ) { + void ConsoleReporter::noMatchingTestCases(StringRef unmatchedSpec) { m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; -} + } -void ConsoleReporter::reportInvalidTestSpec( StringRef arg ) { + void ConsoleReporter::reportInvalidTestSpec(StringRef arg) { m_stream << "Invalid Filter: " << arg << '\n'; -} + } -void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { - AssertionResult const& result = _assertionStats.assertionResult; + void ConsoleReporter::assertionEnded(AssertionStats const &_assertionStats) { + AssertionResult const &result = _assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); // Drop out if result was successful but we're not printing them. // TODO: Make configurable whether skips should be printed if (!includeResults && result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip) - return; + return; lazyPrint(); ConsoleAssertionPrinter printer(m_stream, _assertionStats, m_colour.get(), includeResults); printer.print(); - m_stream << '\n' << std::flush; -} + m_stream << '\n' + << std::flush; + } -void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + void ConsoleReporter::sectionStarting(SectionInfo const &_sectionInfo) { m_tablePrinter->close(); m_headerPrinted = false; StreamingReporterBase::sectionStarting(_sectionInfo); -} -void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + } + void ConsoleReporter::sectionEnded(SectionStats const &_sectionStats) { m_tablePrinter->close(); if (_sectionStats.missingAssertions) { - lazyPrint(); - auto guard = - m_colour->guardColour( Colour::ResultError ).engage( m_stream ); - if (m_sectionStack.size() > 1) - m_stream << "\nNo assertions in section"; - else - m_stream << "\nNo assertions in test case"; - m_stream << " '" << _sectionStats.sectionInfo.name << "'\n\n" << std::flush; + lazyPrint(); + auto guard = m_colour->guardColour(Colour::ResultError).engage(m_stream); + if (m_sectionStack.size() > 1) + m_stream << "\nNo assertions in section"; + else + m_stream << "\nNo assertions in test case"; + m_stream << " '" << _sectionStats.sectionInfo.name << "'\n\n" + << std::flush; } double dur = _sectionStats.durationInSeconds; if (shouldShowDuration(*m_config, dur)) { - m_stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush; + m_stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' + << std::flush; } if (m_headerPrinted) { - m_headerPrinted = false; + m_headerPrinted = false; } StreamingReporterBase::sectionEnded(_sectionStats); -} + } -void ConsoleReporter::benchmarkPreparing( StringRef name ) { - lazyPrintWithoutClosingBenchmarkTable(); + void ConsoleReporter::benchmarkPreparing(StringRef name) { + lazyPrintWithoutClosingBenchmarkTable(); - auto nameCol = TextFlow::Column( static_cast( name ) ) - .width( m_tablePrinter->columnInfos()[0].width - 2 ); + auto nameCol = TextFlow::Column(static_cast(name)) + .width(m_tablePrinter->columnInfos()[0].width - 2); - bool firstLine = true; - for (auto line : nameCol) { - if (!firstLine) - (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); - else - firstLine = false; + bool firstLine = true; + for (auto line : nameCol) { + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; - (*m_tablePrinter) << line << ColumnBreak(); - } -} + (*m_tablePrinter) << line << ColumnBreak(); + } + } -void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { + void ConsoleReporter::benchmarkStarting(BenchmarkInfo const &info) { (*m_tablePrinter) << info.samples << ColumnBreak() - << info.iterations << ColumnBreak(); - if ( !m_config->benchmarkNoAnalysis() ) { - ( *m_tablePrinter ) - << Duration( info.estimatedDuration ) << ColumnBreak(); + << info.iterations << ColumnBreak(); + if (!m_config->benchmarkNoAnalysis()) { + (*m_tablePrinter) + << Duration(info.estimatedDuration) << ColumnBreak(); } - ( *m_tablePrinter ) << OutputFlush{}; -} -void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { - if (m_config->benchmarkNoAnalysis()) - { - (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); + (*m_tablePrinter) << OutputFlush{}; + } + void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const &stats) { + if (m_config->benchmarkNoAnalysis()) { + (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); + } else { + (*m_tablePrinter) << ColumnBreak() + << Duration(stats.mean.point.count()) << ColumnBreak() + << Duration(stats.mean.lower_bound.count()) << ColumnBreak() + << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() + << Duration(stats.standardDeviation.point.count()) << ColumnBreak() + << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() + << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); } - else - { - (*m_tablePrinter) << ColumnBreak() - << Duration(stats.mean.point.count()) << ColumnBreak() - << Duration(stats.mean.lower_bound.count()) << ColumnBreak() - << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() - << Duration(stats.standardDeviation.point.count()) << ColumnBreak() - << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() - << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); - } -} + } -void ConsoleReporter::benchmarkFailed( StringRef error ) { - auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream ); + void ConsoleReporter::benchmarkFailed(StringRef error) { + auto guard = m_colour->guardColour(Colour::Red).engage(m_stream); (*m_tablePrinter) << "Benchmark failed (" << error << ')' << ColumnBreak() << RowBreak(); -} + } -void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + void ConsoleReporter::testCaseEnded(TestCaseStats const &_testCaseStats) { m_tablePrinter->close(); StreamingReporterBase::testCaseEnded(_testCaseStats); m_headerPrinted = false; -} -void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + } + void ConsoleReporter::testRunEnded(TestRunStats const &_testRunStats) { printTotalsDivider(_testRunStats.totals); - printTestRunTotals( m_stream, *m_colour, _testRunStats.totals ); - m_stream << '\n' << std::flush; + printTestRunTotals(m_stream, *m_colour, _testRunStats.totals); + m_stream << '\n' + << std::flush; StreamingReporterBase::testRunEnded(_testRunStats); -} -void ConsoleReporter::testRunStarting(TestRunInfo const& _testRunInfo) { + } + void ConsoleReporter::testRunStarting(TestRunInfo const &_testRunInfo) { StreamingReporterBase::testRunStarting(_testRunInfo); - if ( m_config->testSpec().hasFilters() ) { - m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: " - << m_config->testSpec() << '\n'; + if (m_config->testSpec().hasFilters()) { + m_stream << m_colour->guardColour(Colour::BrightYellow) << "Filters: " + << m_config->testSpec() << '\n'; } m_stream << "Randomness seeded to: " << getSeed() << '\n' << std::flush; -} - -void ConsoleReporter::lazyPrint() { + } + void ConsoleReporter::lazyPrint() { m_tablePrinter->close(); lazyPrintWithoutClosingBenchmarkTable(); -} + } -void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { - - if ( !m_testRunInfoPrinted ) { - lazyPrintRunInfo(); + void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + if (!m_testRunInfoPrinted) { + lazyPrintRunInfo(); } if (!m_headerPrinted) { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; + printTestCaseAndSectionHeader(); + m_headerPrinted = true; } -} -void ConsoleReporter::lazyPrintRunInfo() { + } + void ConsoleReporter::lazyPrintRunInfo() { m_stream << '\n' - << lineOfChars( '~' ) << '\n' - << m_colour->guardColour( Colour::SecondaryText ) + << lineOfChars('~') << '\n' + << m_colour->guardColour(Colour::SecondaryText) << currentTestRunInfo.name << " is a Catch2 v" << libraryVersion() << " host application.\n" << "Run with -? for options\n\n"; m_testRunInfoPrinted = true; -} -void ConsoleReporter::printTestCaseAndSectionHeader() { + } + void ConsoleReporter::printTestCaseAndSectionHeader() { assert(!m_sectionStack.empty()); printOpenHeader(currentTestCaseInfo->name); if (m_sectionStack.size() > 1) { - auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream ); + auto guard = m_colour->guardColour(Colour::Headers).engage(m_stream); - auto - it = m_sectionStack.begin() + 1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for (; it != itEnd; ++it) - printHeaderString(it->name, 2); + auto + it + = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); } SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; - - m_stream << lineOfChars( '-' ) << '\n' - << m_colour->guardColour( Colour::FileName ) << lineInfo << '\n' - << lineOfChars( '.' ) << "\n\n" + m_stream << lineOfChars('-') << '\n' + << m_colour->guardColour(Colour::FileName) << lineInfo << '\n' + << lineOfChars('.') << "\n\n" << std::flush; -} + } -void ConsoleReporter::printClosedHeader(std::string const& _name) { + void ConsoleReporter::printClosedHeader(std::string const &_name) { printOpenHeader(_name); m_stream << lineOfChars('.') << '\n'; -} -void ConsoleReporter::printOpenHeader(std::string const& _name) { + } + void ConsoleReporter::printOpenHeader(std::string const &_name) { m_stream << lineOfChars('-') << '\n'; { - auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream ); - printHeaderString(_name); + auto guard = m_colour->guardColour(Colour::Headers).engage(m_stream); + printHeaderString(_name); } -} + } -void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + void ConsoleReporter::printHeaderString(std::string const &_string, std::size_t indent) { // We want to get a bit fancy with line breaking here, so that subsequent // lines start after ":" if one is present, e.g. // ``` @@ -10041,48 +9567,48 @@ void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t // ``` // So we limit the prefix indentation check to first quarter of the possible // width - std::size_t idx = _string.find( ": " ); - if ( idx != std::string::npos && idx < CATCH_CONFIG_CONSOLE_WIDTH / 4 ) { - idx += 2; + std::size_t idx = _string.find(": "); + if (idx != std::string::npos && idx < CATCH_CONFIG_CONSOLE_WIDTH / 4) { + idx += 2; } else { - idx = 0; + idx = 0; } - m_stream << TextFlow::Column( _string ) - .indent( indent + idx ) - .initialIndent( indent ) - << '\n'; -} + m_stream << TextFlow::Column(_string) + .indent(indent + idx) + .initialIndent(indent) + << '\n'; + } -void ConsoleReporter::printTotalsDivider(Totals const& totals) { + void ConsoleReporter::printTotalsDivider(Totals const &totals) { if (totals.testCases.total() > 0) { - std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); - std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); - std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); - std::size_t skippedRatio = makeRatio(totals.testCases.skipped, totals.testCases.total()); - while (failedRatio + failedButOkRatio + passedRatio + skippedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)++; - while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)--; + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + std::size_t skippedRatio = makeRatio(totals.testCases.skipped, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio + skippedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)--; - m_stream << m_colour->guardColour( Colour::Error ) - << std::string( failedRatio, '=' ) - << m_colour->guardColour( Colour::ResultExpectedFailure ) - << std::string( failedButOkRatio, '=' ); - if ( totals.testCases.allPassed() ) { - m_stream << m_colour->guardColour( Colour::ResultSuccess ) - << std::string( passedRatio, '=' ); - } else { - m_stream << m_colour->guardColour( Colour::Success ) - << std::string( passedRatio, '=' ); - } - m_stream << m_colour->guardColour( Colour::Skip ) - << std::string( skippedRatio, '=' ); + m_stream << m_colour->guardColour(Colour::Error) + << std::string(failedRatio, '=') + << m_colour->guardColour(Colour::ResultExpectedFailure) + << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) { + m_stream << m_colour->guardColour(Colour::ResultSuccess) + << std::string(passedRatio, '='); + } else { + m_stream << m_colour->guardColour(Colour::Success) + << std::string(passedRatio, '='); + } + m_stream << m_colour->guardColour(Colour::Skip) + << std::string(skippedRatio, '='); } else { - m_stream << m_colour->guardColour( Colour::Warning ) - << std::string( CATCH_CONFIG_CONSOLE_WIDTH - 1, '=' ); + m_stream << m_colour->guardColour(Colour::Warning) + << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); } m_stream << '\n'; -} + } } // end namespace Catch @@ -10091,2301 +9617,2225 @@ void ConsoleReporter::printTotalsDivider(Totals const& totals) { #endif #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif - - - #include #include namespace Catch { - namespace { - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ): m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) = default; - bool operator()( - Detail::unique_ptr const& - node ) const { - return ( - ( node->stats.sectionInfo.name == m_other.name ) && - ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) ); - } - void operator=( BySectionInfo const& ) = delete; + namespace { + struct BySectionInfo { + BySectionInfo(SectionInfo const &other) + : m_other(other) {} + BySectionInfo(BySectionInfo const &other) = default; + bool operator()( + Detail::unique_ptr const & + node) const { + return ( + (node->stats.sectionInfo.name == m_other.name) && (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const &) = delete; - private: - SectionInfo const& m_other; - }; + private: + SectionInfo const &m_other; + }; - } // namespace + } // namespace - namespace Detail { - AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( - AssertionStats const& assertion ): - m_assertion( assertion ) {} + namespace Detail { + AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( + AssertionStats const &assertion) + : m_assertion(assertion) {} - AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( - BenchmarkStats<> const& benchmark ): - m_benchmark( benchmark ) {} - - bool AssertionOrBenchmarkResult::isAssertion() const { - return m_assertion.some(); - } - bool AssertionOrBenchmarkResult::isBenchmark() const { - return m_benchmark.some(); - } - - AssertionStats const& AssertionOrBenchmarkResult::asAssertion() const { - assert(m_assertion.some()); - - return *m_assertion; - } - BenchmarkStats<> const& AssertionOrBenchmarkResult::asBenchmark() const { - assert(m_benchmark.some()); - - return *m_benchmark; - } + AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( + BenchmarkStats<> const &benchmark) + : m_benchmark(benchmark) {} + bool AssertionOrBenchmarkResult::isAssertion() const { + return m_assertion.some(); + } + bool AssertionOrBenchmarkResult::isBenchmark() const { + return m_benchmark.some(); } - CumulativeReporterBase::~CumulativeReporterBase() = default; + AssertionStats const &AssertionOrBenchmarkResult::asAssertion() const { + assert(m_assertion.some()); - void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { - m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats); + return *m_assertion; + } + BenchmarkStats<> const &AssertionOrBenchmarkResult::asBenchmark() const { + assert(m_benchmark.some()); + + return *m_benchmark; } - void - CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) { - // We need a copy, because SectionStats expect to take ownership - SectionStats incompleteStats( SectionInfo(sectionInfo), Counts(), 0, false ); - SectionNode* node; - if ( m_sectionStack.empty() ) { - if ( !m_rootSection ) { - m_rootSection = - Detail::make_unique( incompleteStats ); - } - node = m_rootSection.get(); - } else { - SectionNode& parentNode = *m_sectionStack.back(); - auto it = std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if ( it == parentNode.childSections.end() ) { - auto newNode = - Detail::make_unique( incompleteStats ); - node = newNode.get(); - parentNode.childSections.push_back( CATCH_MOVE( newNode ) ); - } else { - node = it->get(); - } - } + } // namespace Detail - m_deepestSection = node; - m_sectionStack.push_back( node ); + CumulativeReporterBase::~CumulativeReporterBase() = default; + + void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const &benchmarkStats) { + m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats); + } + + void + CumulativeReporterBase::sectionStarting(SectionInfo const §ionInfo) { + // We need a copy, because SectionStats expect to take ownership + SectionStats incompleteStats(SectionInfo(sectionInfo), Counts(), 0, false); + SectionNode *node; + if (m_sectionStack.empty()) { + if (!m_rootSection) { + m_rootSection = Detail::make_unique(incompleteStats); + } + node = m_rootSection.get(); + } else { + SectionNode &parentNode = *m_sectionStack.back(); + auto it = std::find_if(parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo(sectionInfo)); + if (it == parentNode.childSections.end()) { + auto newNode = Detail::make_unique(incompleteStats); + node = newNode.get(); + parentNode.childSections.push_back(CATCH_MOVE(newNode)); + } else { + node = it->get(); + } } - void CumulativeReporterBase::assertionEnded( - AssertionStats const& assertionStats ) { - assert( !m_sectionStack.empty() ); - // AssertionResult holds a pointer to a temporary DecomposedExpression, - // which getExpandedExpression() calls to build the expression string. - // Our section stack copy of the assertionResult will likely outlive the - // temporary, so it must be expanded or discarded now to avoid calling - // a destroyed object later. - if ( m_shouldStoreFailedAssertions && - !assertionStats.assertionResult.isOk() ) { - static_cast( - assertionStats.assertionResult.getExpandedExpression() ); - } - if ( m_shouldStoreSuccesfulAssertions && - assertionStats.assertionResult.isOk() ) { - static_cast( - assertionStats.assertionResult.getExpandedExpression() ); - } - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats ); + m_deepestSection = node; + m_sectionStack.push_back(node); + } + + void CumulativeReporterBase::assertionEnded( + AssertionStats const &assertionStats) { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + if (m_shouldStoreFailedAssertions && !assertionStats.assertionResult.isOk()) { + static_cast( + assertionStats.assertionResult.getExpandedExpression()); } - - void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) { - assert( !m_sectionStack.empty() ); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); + if (m_shouldStoreSuccesfulAssertions && assertionStats.assertionResult.isOk()) { + static_cast( + assertionStats.assertionResult.getExpandedExpression()); } + SectionNode §ionNode = *m_sectionStack.back(); + sectionNode.assertionsAndBenchmarks.emplace_back(assertionStats); + } - void CumulativeReporterBase::testCaseEnded( - TestCaseStats const& testCaseStats ) { - auto node = Detail::make_unique( testCaseStats ); - assert( m_sectionStack.size() == 0 ); - node->children.push_back( CATCH_MOVE(m_rootSection) ); - m_testCases.push_back( CATCH_MOVE(node) ); + void CumulativeReporterBase::sectionEnded(SectionStats const §ionStats) { + assert(!m_sectionStack.empty()); + SectionNode &node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } - assert( m_deepestSection ); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } + void CumulativeReporterBase::testCaseEnded( + TestCaseStats const &testCaseStats) { + auto node = Detail::make_unique(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(CATCH_MOVE(m_rootSection)); + m_testCases.push_back(CATCH_MOVE(node)); + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } - void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) { - assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run"); - m_testRun = Detail::make_unique( testRunStats ); - m_testRun->children.swap( m_testCases ); - testRunEndedCumulative(); - } + void CumulativeReporterBase::testRunEnded(TestRunStats const &testRunStats) { + assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run"); + m_testRun = Detail::make_unique(testRunStats); + m_testRun->children.swap(m_testCases); + testRunEndedCumulative(); + } - bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const { - return std::any_of( - assertionsAndBenchmarks.begin(), - assertionsAndBenchmarks.end(), - []( Detail::AssertionOrBenchmarkResult const& res ) { - return res.isAssertion(); - } ); - } + bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const { + return std::any_of( + assertionsAndBenchmarks.begin(), + assertionsAndBenchmarks.end(), + [](Detail::AssertionOrBenchmarkResult const &res) { + return res.isAssertion(); + }); + } } // end namespace Catch - - - namespace Catch { - void EventListenerBase::fatalErrorEncountered( StringRef ) {} + void EventListenerBase::fatalErrorEncountered(StringRef) {} - void EventListenerBase::benchmarkPreparing( StringRef ) {} - void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {} - void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {} - void EventListenerBase::benchmarkFailed( StringRef ) {} + void EventListenerBase::benchmarkPreparing(StringRef) {} + void EventListenerBase::benchmarkStarting(BenchmarkInfo const &) {} + void EventListenerBase::benchmarkEnded(BenchmarkStats<> const &) {} + void EventListenerBase::benchmarkFailed(StringRef) {} - void EventListenerBase::assertionStarting( AssertionInfo const& ) {} + void EventListenerBase::assertionStarting(AssertionInfo const &) {} - void EventListenerBase::assertionEnded( AssertionStats const& ) {} - void EventListenerBase::listReporters( - std::vector const& ) {} - void EventListenerBase::listListeners( - std::vector const& ) {} - void EventListenerBase::listTests( std::vector const& ) {} - void EventListenerBase::listTags( std::vector const& ) {} - void EventListenerBase::noMatchingTestCases( StringRef ) {} - void EventListenerBase::reportInvalidTestSpec( StringRef ) {} - void EventListenerBase::testRunStarting( TestRunInfo const& ) {} - void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {} - void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {} - void EventListenerBase::sectionStarting( SectionInfo const& ) {} - void EventListenerBase::sectionEnded( SectionStats const& ) {} - void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {} - void EventListenerBase::testCaseEnded( TestCaseStats const& ) {} - void EventListenerBase::testRunEnded( TestRunStats const& ) {} - void EventListenerBase::skipTest( TestCaseInfo const& ) {} + void EventListenerBase::assertionEnded(AssertionStats const &) {} + void EventListenerBase::listReporters( + std::vector const &) {} + void EventListenerBase::listListeners( + std::vector const &) {} + void EventListenerBase::listTests(std::vector const &) {} + void EventListenerBase::listTags(std::vector const &) {} + void EventListenerBase::noMatchingTestCases(StringRef) {} + void EventListenerBase::reportInvalidTestSpec(StringRef) {} + void EventListenerBase::testRunStarting(TestRunInfo const &) {} + void EventListenerBase::testCaseStarting(TestCaseInfo const &) {} + void EventListenerBase::testCasePartialStarting(TestCaseInfo const &, uint64_t) {} + void EventListenerBase::sectionStarting(SectionInfo const &) {} + void EventListenerBase::sectionEnded(SectionStats const &) {} + void EventListenerBase::testCasePartialEnded(TestCaseStats const &, uint64_t) {} + void EventListenerBase::testCaseEnded(TestCaseStats const &) {} + void EventListenerBase::testRunEnded(TestRunStats const &) {} + void EventListenerBase::skipTest(TestCaseInfo const &) {} } // namespace Catch - - - #include #include #include #include -#include #include +#include namespace Catch { - namespace { - void listTestNamesOnly(std::ostream& out, - std::vector const& tests) { - for (auto const& test : tests) { - auto const& testCaseInfo = test.getTestCaseInfo(); + namespace { + void listTestNamesOnly(std::ostream &out, + std::vector const &tests) { + for (auto const &test : tests) { + auto const &testCaseInfo = test.getTestCaseInfo(); - if (startsWith(testCaseInfo.name, '#')) { - out << '"' << testCaseInfo.name << '"'; - } else { - out << testCaseInfo.name; - } - - out << '\n'; - } - out << std::flush; + if (startsWith(testCaseInfo.name, '#')) { + out << '"' << testCaseInfo.name << '"'; + } else { + out << testCaseInfo.name; } - } // end unnamed namespace + out << '\n'; + } + out << std::flush; + } + } // end unnamed namespace - // Because formatting using c++ streams is stateful, drop down to C is - // required Alternatively we could use stringstream, but its performance - // is... not good. - std::string getFormattedDuration( double duration ) { - // Max exponent + 1 is required to represent the whole part - // + 1 for decimal point - // + 3 for the 3 decimal places - // + 1 for null terminator - const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; - char buffer[maxDoubleSize]; + // Because formatting using c++ streams is stateful, drop down to C is + // required Alternatively we could use stringstream, but its performance + // is... not good. + std::string getFormattedDuration(double duration) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; - // Save previous errno, to prevent sprintf from overwriting it - ErrnoGuard guard; + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; #ifdef _MSC_VER - size_t printedLength = static_cast( - sprintf_s( buffer, "%.3f", duration ) ); + size_t printedLength = static_cast( + sprintf_s(buffer, "%.3f", duration)); #else - size_t printedLength = static_cast( - std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) ); + size_t printedLength = static_cast( + std::snprintf(buffer, maxDoubleSize, "%.3f", duration)); #endif - return std::string( buffer, printedLength ); + return std::string(buffer, printedLength); + } + + bool shouldShowDuration(IConfig const &config, double duration) { + if (config.showDurations() == ShowDurations::Always) { + return true; + } + if (config.showDurations() == ShowDurations::Never) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } + + std::string serializeFilters(std::vector const &filters) { + // We add a ' ' separator between each filter + size_t serialized_size = filters.size() - 1; + for (auto const &filter : filters) { + serialized_size += filter.size(); } - bool shouldShowDuration( IConfig const& config, double duration ) { - if ( config.showDurations() == ShowDurations::Always ) { - return true; - } - if ( config.showDurations() == ShowDurations::Never ) { - return false; - } - const double min = config.minDuration(); - return min >= 0 && duration >= min; + std::string serialized; + serialized.reserve(serialized_size); + bool first = true; + + for (auto const &filter : filters) { + if (!first) { + serialized.push_back(' '); + } + first = false; + serialized.append(filter); } - std::string serializeFilters( std::vector const& filters ) { - // We add a ' ' separator between each filter - size_t serialized_size = filters.size() - 1; - for (auto const& filter : filters) { - serialized_size += filter.size(); - } + return serialized; + } - std::string serialized; - serialized.reserve(serialized_size); - bool first = true; + std::ostream &operator<<(std::ostream &out, lineOfChars value) { + for (size_t idx = 0; idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++idx) { + out.put(value.c); + } + return out; + } - for (auto const& filter : filters) { - if (!first) { - serialized.push_back(' '); - } - first = false; - serialized.append(filter); - } + void + defaultListReporters(std::ostream &out, + std::vector const &descriptions, + Verbosity verbosity) { + out << "Available reporters:\n"; + const auto maxNameLen = std::max_element(descriptions.begin(), + descriptions.end(), + [](ReporterDescription const &lhs, + ReporterDescription const &rhs) { + return lhs.name.size() < rhs.name.size(); + }) + ->name.size(); - return serialized; + for (auto const &desc : descriptions) { + if (verbosity == Verbosity::Quiet) { + out << TextFlow::Column(desc.name) + .indent(2) + .width(5 + maxNameLen) + << '\n'; + } else { + out << TextFlow::Column(desc.name + ':') + .indent(2) + .width(5 + maxNameLen) + + TextFlow::Column(desc.description) + .initialIndent(0) + .indent(2) + .width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8) + << '\n'; + } + } + out << '\n' + << std::flush; + } + + void defaultListListeners(std::ostream &out, + std::vector const &descriptions, + Verbosity verbosity) { + if (verbosity == Verbosity::Quiet) { + for (auto const &desc : descriptions) { + out << desc.name << '\n'; + } + return; } - std::ostream& operator<<( std::ostream& out, lineOfChars value ) { - for ( size_t idx = 0; idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++idx ) { - out.put( value.c ); - } - return out; + out << "Registered listeners:\n"; + + if (descriptions.empty()) { + return; } - void - defaultListReporters( std::ostream& out, - std::vector const& descriptions, - Verbosity verbosity ) { - out << "Available reporters:\n"; - const auto maxNameLen = - std::max_element( descriptions.begin(), - descriptions.end(), - []( ReporterDescription const& lhs, - ReporterDescription const& rhs ) { - return lhs.name.size() < rhs.name.size(); - } ) - ->name.size(); + const auto maxNameLen = std::max_element(descriptions.begin(), + descriptions.end(), + [](ListenerDescription const &lhs, + ListenerDescription const &rhs) { + return lhs.name.size() < rhs.name.size(); + }) + ->name.size(); - for ( auto const& desc : descriptions ) { - if ( verbosity == Verbosity::Quiet ) { - out << TextFlow::Column( desc.name ) - .indent( 2 ) - .width( 5 + maxNameLen ) - << '\n'; - } else { - out << TextFlow::Column( desc.name + ':' ) - .indent( 2 ) - .width( 5 + maxNameLen ) + - TextFlow::Column( desc.description ) - .initialIndent( 0 ) - .indent( 2 ) - .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) - << '\n'; - } - } - out << '\n' << std::flush; + for (auto const &desc : descriptions) { + out << TextFlow::Column(static_cast(desc.name) + ':') + .indent(2) + .width(maxNameLen + 5) + + TextFlow::Column(desc.description) + .initialIndent(0) + .indent(2) + .width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8) + << '\n'; } - void defaultListListeners( std::ostream& out, - std::vector const& descriptions, - Verbosity verbosity ) { - if ( verbosity == Verbosity::Quiet ) { - for ( auto const& desc : descriptions ) { - out << desc.name << '\n'; - } - return; - } + out << '\n' + << std::flush; + } - out << "Registered listeners:\n"; - - if(descriptions.empty()) { - return; - } - - const auto maxNameLen = - std::max_element( descriptions.begin(), - descriptions.end(), - []( ListenerDescription const& lhs, - ListenerDescription const& rhs ) { - return lhs.name.size() < rhs.name.size(); - } ) - ->name.size(); - - for ( auto const& desc : descriptions ) { - out << TextFlow::Column( static_cast( desc.name ) + - ':' ) - .indent( 2 ) - .width( maxNameLen + 5 ) + - TextFlow::Column( desc.description ) - .initialIndent( 0 ) - .indent( 2 ) - .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) - << '\n'; - } - - out << '\n' << std::flush; + void defaultListTags(std::ostream &out, + std::vector const &tags, + bool isFiltered, + Verbosity verbosity) { + if (verbosity == Verbosity::Quiet) { + for (auto const &tagCount : tags) { + out << tagCount.all() << '\n'; + } + return; + } + if (isFiltered) { + out << "Tags for matching test cases:\n"; + } else { + out << "All available tags:\n"; } - void defaultListTags( std::ostream& out, - std::vector const& tags, - bool isFiltered, - Verbosity verbosity ) { - if (verbosity == Verbosity::Quiet) { - for (auto const& tagCount : tags) { - out << tagCount.all() << '\n'; - } - return; - } - if ( isFiltered ) { - out << "Tags for matching test cases:\n"; + // minimum whitespace to pad tag counts, possibly overwritten below + size_t maxTagCountLen = 2; + + // determine necessary padding for tag count column + if (!tags.empty()) { + const auto maxTagCount = std::max_element(tags.begin(), + tags.end(), + [](auto const &lhs, auto const &rhs) { + return lhs.count < rhs.count; + }) + ->count; + + // more padding necessary for 3+ digits + if (maxTagCount >= 100) { + auto numDigits = 1 + std::floor(std::log10(maxTagCount)); + maxTagCountLen = static_cast(numDigits); + } + } + + for (auto const &tagCount : tags) { + ReusableStringStream rss; + rss << " " << std::setw(maxTagCountLen) << tagCount.count << " "; + auto str = rss.str(); + auto wrapper = TextFlow::Column(tagCount.all()) + .initialIndent(0) + .indent(str.size()) + .width(CATCH_CONFIG_CONSOLE_WIDTH - 10); + out << str << wrapper << '\n'; + } + out << pluralise(tags.size(), "tag"_sr) << "\n\n" + << std::flush; + } + + void defaultListTests(std::ostream &out, ColourImpl *streamColour, std::vector const &tests, bool isFiltered, Verbosity verbosity) { + // We special case this to provide the equivalent of old + // `--list-test-names-only`, which could then be used by the + // `--input-file` option. + if (verbosity == Verbosity::Quiet) { + listTestNamesOnly(out, tests); + return; + } + + if (isFiltered) { + out << "Matching test cases:\n"; + } else { + out << "All available test cases:\n"; + } + + for (auto const &test : tests) { + auto const &testCaseInfo = test.getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + auto colourGuard = streamColour->guardColour(colour).engage(out); + + out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n'; + if (verbosity >= Verbosity::High) { + out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n'; + } + if (!testCaseInfo.tags.empty() && verbosity > Verbosity::Quiet) { + out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n'; + } + } + + if (isFiltered) { + out << pluralise(tests.size(), "matching test case"_sr); + } else { + out << pluralise(tests.size(), "test case"_sr); + } + out << "\n\n" + << std::flush; + } + + namespace { + class SummaryColumn { + public: + SummaryColumn(std::string suffix, Colour::Code colour) + : m_suffix(CATCH_MOVE(suffix)), m_colour(colour) {} + + SummaryColumn &&addRow(std::uint64_t count) && { + std::string row = std::to_string(count); + auto const new_width = std::max(m_width, row.size()); + if (new_width > m_width) { + for (auto &oldRow : m_rows) { + oldRow.insert(0, new_width - m_width, ' '); + } } else { - out << "All available tags:\n"; + row.insert(0, m_width - row.size(), ' '); } + m_width = new_width; + m_rows.push_back(row); + return std::move(*this); + } - // minimum whitespace to pad tag counts, possibly overwritten below - size_t maxTagCountLen = 2; + std::string const &getSuffix() const { return m_suffix; } + Colour::Code getColour() const { return m_colour; } + std::string const &getRow(std::size_t index) const { + return m_rows[index]; + } - // determine necessary padding for tag count column - if ( ! tags.empty() ) { - const auto maxTagCount = - std::max_element( tags.begin(), - tags.end(), - []( auto const& lhs, auto const& rhs ) { - return lhs.count < rhs.count; - } ) - ->count; + private: + std::string m_suffix; + Colour::Code m_colour; + std::size_t m_width = 0; + std::vector m_rows; + }; - // more padding necessary for 3+ digits - if (maxTagCount >= 100) { - auto numDigits = 1 + std::floor( std::log10( maxTagCount ) ); - maxTagCountLen = static_cast( numDigits ); - } + void printSummaryRow(std::ostream &stream, + ColourImpl &colour, + StringRef label, + std::vector const &cols, + std::size_t row) { + for (auto const &col : cols) { + auto const &value = col.getRow(row); + auto const &suffix = col.getSuffix(); + if (suffix.empty()) { + stream << label << ": "; + if (value != "0") { + stream << value; + } else { + stream << colour.guardColour(Colour::Warning) + << "- none -"; + } + } else if (value != "0") { + stream << colour.guardColour(Colour::LightGrey) << " | " + << colour.guardColour(col.getColour()) << value + << ' ' << suffix; } + } + stream << '\n'; + } + } // namespace - for ( auto const& tagCount : tags ) { - ReusableStringStream rss; - rss << " " << std::setw( maxTagCountLen ) << tagCount.count << " "; - auto str = rss.str(); - auto wrapper = TextFlow::Column( tagCount.all() ) - .initialIndent( 0 ) - .indent( str.size() ) - .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 ); - out << str << wrapper << '\n'; - } - out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush; + void printTestRunTotals(std::ostream &stream, + ColourImpl &streamColour, + Totals const &totals) { + if (totals.testCases.total() == 0) { + stream << streamColour.guardColour(Colour::Warning) + << "No tests ran\n"; + return; } - void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector const& tests, bool isFiltered, Verbosity verbosity) { - // We special case this to provide the equivalent of old - // `--list-test-names-only`, which could then be used by the - // `--input-file` option. - if (verbosity == Verbosity::Quiet) { - listTestNamesOnly(out, tests); - return; - } - - if (isFiltered) { - out << "Matching test cases:\n"; - } else { - out << "All available test cases:\n"; - } - - for (auto const& test : tests) { - auto const& testCaseInfo = test.getTestCaseInfo(); - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - auto colourGuard = streamColour->guardColour( colour ).engage( out ); - - out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n'; - if (verbosity >= Verbosity::High) { - out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n'; - } - if (!testCaseInfo.tags.empty() && - verbosity > Verbosity::Quiet) { - out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n'; - } - } - - if (isFiltered) { - out << pluralise(tests.size(), "matching test case"_sr); - } else { - out << pluralise(tests.size(), "test case"_sr); - } - out << "\n\n" << std::flush; + if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << streamColour.guardColour(Colour::ResultSuccess) + << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion"_sr) + << " in " + << pluralise(totals.testCases.passed, "test case"_sr) + << ')' << '\n'; + return; } - namespace { - class SummaryColumn { - public: - SummaryColumn( std::string suffix, Colour::Code colour ): - m_suffix( CATCH_MOVE( suffix ) ), m_colour( colour ) {} - - SummaryColumn&& addRow( std::uint64_t count ) && { - std::string row = std::to_string(count); - auto const new_width = std::max( m_width, row.size() ); - if ( new_width > m_width ) { - for ( auto& oldRow : m_rows ) { - oldRow.insert( 0, new_width - m_width, ' ' ); - } - } else { - row.insert( 0, m_width - row.size(), ' ' ); - } - m_width = new_width; - m_rows.push_back( row ); - return std::move( *this ); - } - - std::string const& getSuffix() const { return m_suffix; } - Colour::Code getColour() const { return m_colour; } - std::string const& getRow( std::size_t index ) const { - return m_rows[index]; - } - - private: - std::string m_suffix; - Colour::Code m_colour; - std::size_t m_width = 0; - std::vector m_rows; - }; - - void printSummaryRow( std::ostream& stream, - ColourImpl& colour, - StringRef label, - std::vector const& cols, - std::size_t row ) { - for ( auto const& col : cols ) { - auto const& value = col.getRow( row ); - auto const& suffix = col.getSuffix(); - if ( suffix.empty() ) { - stream << label << ": "; - if ( value != "0" ) { - stream << value; - } else { - stream << colour.guardColour( Colour::Warning ) - << "- none -"; - } - } else if ( value != "0" ) { - stream << colour.guardColour( Colour::LightGrey ) << " | " - << colour.guardColour( col.getColour() ) << value - << ' ' << suffix; - } - } - stream << '\n'; - } - } // namespace - - void printTestRunTotals( std::ostream& stream, - ColourImpl& streamColour, - Totals const& totals ) { - if ( totals.testCases.total() == 0 ) { - stream << streamColour.guardColour( Colour::Warning ) - << "No tests ran\n"; - return; - } - - if ( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { - stream << streamColour.guardColour( Colour::ResultSuccess ) - << "All tests passed"; - stream << " (" - << pluralise( totals.assertions.passed, "assertion"_sr ) - << " in " - << pluralise( totals.testCases.passed, "test case"_sr ) - << ')' << '\n'; - return; - } - - std::vector columns; - // Don't include "skipped assertions" in total count - const auto totalAssertionCount = - totals.assertions.total() - totals.assertions.skipped; - columns.push_back( SummaryColumn( "", Colour::None ) - .addRow( totals.testCases.total() ) - .addRow( totalAssertionCount ) ); - columns.push_back( SummaryColumn( "passed", Colour::Success ) - .addRow( totals.testCases.passed ) - .addRow( totals.assertions.passed ) ); - columns.push_back( SummaryColumn( "failed", Colour::ResultError ) - .addRow( totals.testCases.failed ) - .addRow( totals.assertions.failed ) ); - columns.push_back( SummaryColumn( "skipped", Colour::Skip ) - .addRow( totals.testCases.skipped ) - // Don't print "skipped assertions" - .addRow( 0 ) ); - columns.push_back( - SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) - .addRow( totals.testCases.failedButOk ) - .addRow( totals.assertions.failedButOk ) ); - printSummaryRow( stream, streamColour, "test cases"_sr, columns, 0 ); - printSummaryRow( stream, streamColour, "assertions"_sr, columns, 1 ); - } + std::vector columns; + // Don't include "skipped assertions" in total count + const auto totalAssertionCount = totals.assertions.total() - totals.assertions.skipped; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totalAssertionCount)); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("skipped", Colour::Skip) + .addRow(totals.testCases.skipped) + // Don't print "skipped assertions" + .addRow(0)); + columns.push_back( + SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + printSummaryRow(stream, streamColour, "test cases"_sr, columns, 0); + printSummaryRow(stream, streamColour, "assertions"_sr, columns, 1); + } } // namespace Catch - // namespace Catch { - namespace { - void writeSourceInfo( JsonObjectWriter& writer, - SourceLineInfo const& sourceInfo ) { - auto source_location_writer = - writer.write( "source-location"_sr ).writeObject(); - source_location_writer.write( "filename"_sr ) - .write( sourceInfo.file ); - source_location_writer.write( "line"_sr ).write( sourceInfo.line ); + namespace { + void writeSourceInfo(JsonObjectWriter &writer, + SourceLineInfo const &sourceInfo) { + auto source_location_writer = writer.write("source-location"_sr).writeObject(); + source_location_writer.write("filename"_sr) + .write(sourceInfo.file); + source_location_writer.write("line"_sr).write(sourceInfo.line); + } + + void writeTags(JsonArrayWriter writer, std::vector const &tags) { + for (auto const &tag : tags) { + writer.write(tag.original); + } + } + + void writeProperties(JsonArrayWriter writer, + TestCaseInfo const &info) { + if (info.isHidden()) { writer.write("is-hidden"_sr); } + if (info.okToFail()) { writer.write("ok-to-fail"_sr); } + if (info.expectedToFail()) { + writer.write("expected-to-fail"_sr); + } + if (info.throws()) { writer.write("throws"_sr); } + } + + } // namespace + + JsonReporter::JsonReporter(ReporterConfig &&config) + : StreamingReporterBase{CATCH_MOVE(config)} { + m_preferences.shouldRedirectStdOut = true; + // TBD: Do we want to report all assertions? XML reporter does + // not, but for machine-parseable reporters I think the answer + // should be yes. + m_preferences.shouldReportAllAssertions = true; + // We only handle assertions when they end + m_preferences.shouldReportAllAssertionStarts = false; + + m_objectWriters.emplace(m_stream); + m_writers.emplace(Writer::Object); + auto &writer = m_objectWriters.top(); + + writer.write("version"_sr).write(1); + + { + auto metadata_writer = writer.write("metadata"_sr).writeObject(); + metadata_writer.write("name"_sr).write(m_config->name()); + metadata_writer.write("rng-seed"_sr).write(m_config->rngSeed()); + metadata_writer.write("catch2-version"_sr) + .write(libraryVersion()); + if (m_config->testSpec().hasFilters()) { + metadata_writer.write("filters"_sr) + .write(m_config->testSpec()); + } + } + } + + JsonReporter::~JsonReporter() { + endListing(); + // TODO: Ensure this closes the top level object, add asserts + assert(m_writers.size() == 1 && "Only the top level object should be open"); + assert(m_writers.top() == Writer::Object); + endObject(); + m_stream << '\n' + << std::flush; + assert(m_writers.empty()); + } + + JsonArrayWriter &JsonReporter::startArray() { + m_arrayWriters.emplace(m_arrayWriters.top().writeArray()); + m_writers.emplace(Writer::Array); + return m_arrayWriters.top(); + } + JsonArrayWriter &JsonReporter::startArray(StringRef key) { + m_arrayWriters.emplace( + m_objectWriters.top().write(key).writeArray()); + m_writers.emplace(Writer::Array); + return m_arrayWriters.top(); + } + + JsonObjectWriter &JsonReporter::startObject() { + m_objectWriters.emplace(m_arrayWriters.top().writeObject()); + m_writers.emplace(Writer::Object); + return m_objectWriters.top(); + } + JsonObjectWriter &JsonReporter::startObject(StringRef key) { + m_objectWriters.emplace( + m_objectWriters.top().write(key).writeObject()); + m_writers.emplace(Writer::Object); + return m_objectWriters.top(); + } + + void JsonReporter::endObject() { + assert(isInside(Writer::Object)); + m_objectWriters.pop(); + m_writers.pop(); + } + void JsonReporter::endArray() { + assert(isInside(Writer::Array)); + m_arrayWriters.pop(); + m_writers.pop(); + } + + bool JsonReporter::isInside(Writer writer) { + return !m_writers.empty() && m_writers.top() == writer; + } + + void JsonReporter::startListing() { + if (!m_startedListing) { startObject("listings"_sr); } + m_startedListing = true; + } + void JsonReporter::endListing() { + if (m_startedListing) { endObject(); } + m_startedListing = false; + } + + std::string JsonReporter::getDescription() { + return "Outputs listings as JSON. Test listing is Work-in-Progress!"; + } + + void JsonReporter::testRunStarting(TestRunInfo const &runInfo) { + StreamingReporterBase::testRunStarting(runInfo); + endListing(); + + assert(isInside(Writer::Object)); + startObject("test-run"_sr); + startArray("test-cases"_sr); + } + + static void writeCounts(JsonObjectWriter &&writer, Counts const &counts) { + writer.write("passed"_sr).write(counts.passed); + writer.write("failed"_sr).write(counts.failed); + writer.write("fail-but-ok"_sr).write(counts.failedButOk); + writer.write("skipped"_sr).write(counts.skipped); + } + + void JsonReporter::testRunEnded(TestRunStats const &runStats) { + assert(isInside(Writer::Array)); + // End "test-cases" + endArray(); + + { + auto totals = m_objectWriters.top().write("totals"_sr).writeObject(); + writeCounts(totals.write("assertions"_sr).writeObject(), + runStats.totals.assertions); + writeCounts(totals.write("test-cases"_sr).writeObject(), + runStats.totals.testCases); + } + + // End the "test-run" object + endObject(); + } + + void JsonReporter::testCaseStarting(TestCaseInfo const &tcInfo) { + StreamingReporterBase::testCaseStarting(tcInfo); + + assert(isInside(Writer::Array) && "We should be in the 'test-cases' array"); + startObject(); + // "test-info" prelude + { + auto testInfo = m_objectWriters.top().write("test-info"_sr).writeObject(); + // TODO: handle testName vs className!! + testInfo.write("name"_sr).write(tcInfo.name); + writeSourceInfo(testInfo, tcInfo.lineInfo); + writeTags(testInfo.write("tags"_sr).writeArray(), tcInfo.tags); + writeProperties(testInfo.write("properties"_sr).writeArray(), + tcInfo); + } + + // Start the array for individual test runs (testCasePartial pairs) + startArray("runs"_sr); + } + + void JsonReporter::testCaseEnded(TestCaseStats const &tcStats) { + StreamingReporterBase::testCaseEnded(tcStats); + + // We need to close the 'runs' array before finishing the test case + assert(isInside(Writer::Array)); + endArray(); + + { + auto totals = m_objectWriters.top().write("totals"_sr).writeObject(); + writeCounts(totals.write("assertions"_sr).writeObject(), + tcStats.totals.assertions); + // We do not write the test case totals, because there will always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in partial result? + } + // We do not write out stderr/stdout, because we instead wrote those out in partial runs + + // TODO: aborting? + + // And we also close this test case's object + assert(isInside(Writer::Object)); + endObject(); + } + + void JsonReporter::testCasePartialStarting(TestCaseInfo const & /*tcInfo*/, + uint64_t index) { + startObject(); + m_objectWriters.top().write("run-idx"_sr).write(index); + startArray("path"_sr); + // TODO: we want to delay most of the printing to the 'root' section + // TODO: childSection key name? + } + + void JsonReporter::testCasePartialEnded(TestCaseStats const &tcStats, + uint64_t /*index*/) { + // Fixme: the top level section handles this. + //// path object + endArray(); + if (!tcStats.stdOut.empty()) { + m_objectWriters.top() + .write("captured-stdout"_sr) + .write(tcStats.stdOut); + } + if (!tcStats.stdErr.empty()) { + m_objectWriters.top() + .write("captured-stderr"_sr) + .write(tcStats.stdErr); + } + { + auto totals = m_objectWriters.top().write("totals"_sr).writeObject(); + writeCounts(totals.write("assertions"_sr).writeObject(), + tcStats.totals.assertions); + // We do not write the test case totals, because there will + // always be just one test case here. + // TODO: overall "result" -> success, skip, fail here? Or in + // partial result? + } + // TODO: aborting? + // run object + endObject(); + } + + void JsonReporter::sectionStarting(SectionInfo const §ionInfo) { + assert(isInside(Writer::Array) && "Section should always start inside an object"); + // We want to nest top level sections, even though it shares name + // and source loc with the TEST_CASE + auto §ionObject = startObject(); + sectionObject.write("kind"_sr).write("section"_sr); + sectionObject.write("name"_sr).write(sectionInfo.name); + writeSourceInfo(m_objectWriters.top(), sectionInfo.lineInfo); + + // TBD: Do we want to create this event lazily? It would become + // rather complex, but we could do it, and it would look + // better for empty sections. OTOH, empty sections should + // be rare. + startArray("path"_sr); + } + void JsonReporter::sectionEnded(SectionStats const & /*sectionStats */) { + // End the subpath array + endArray(); + // TODO: metadata + // TODO: what info do we have here? + + // End the section object + endObject(); + } + + void JsonReporter::assertionEnded(AssertionStats const &assertionStats) { + // TODO: There is lot of different things to handle here, but + // we can fill it in later, after we show that the basic + // outline and streaming reporter impl works well enough. + // if ( !m_config->includeSuccessfulResults() + // && assertionStats.assertionResult.isOk() ) { + // return; + //} + assert(isInside(Writer::Array)); + auto assertionObject = m_arrayWriters.top().writeObject(); + + assertionObject.write("kind"_sr).write("assertion"_sr); + writeSourceInfo(assertionObject, + assertionStats.assertionResult.getSourceInfo()); + assertionObject.write("status"_sr) + .write(assertionStats.assertionResult.isOk()); + // TODO: handling of result. + // TODO: messages + // TODO: totals? + } + + void JsonReporter::benchmarkPreparing(StringRef name) { + (void)name; + } + void JsonReporter::benchmarkStarting(BenchmarkInfo const &) {} + void JsonReporter::benchmarkEnded(BenchmarkStats<> const &) {} + void JsonReporter::benchmarkFailed(StringRef error) { + (void)error; + } + + void JsonReporter::listReporters( + std::vector const &descriptions) { + startListing(); + + auto writer = m_objectWriters.top().write("reporters"_sr).writeArray(); + for (auto const &desc : descriptions) { + auto desc_writer = writer.writeObject(); + desc_writer.write("name"_sr).write(desc.name); + desc_writer.write("description"_sr).write(desc.description); + } + } + void JsonReporter::listListeners( + std::vector const &descriptions) { + startListing(); + + auto writer = m_objectWriters.top().write("listeners"_sr).writeArray(); + + for (auto const &desc : descriptions) { + auto desc_writer = writer.writeObject(); + desc_writer.write("name"_sr).write(desc.name); + desc_writer.write("description"_sr).write(desc.description); + } + } + void JsonReporter::listTests(std::vector const &tests) { + startListing(); + + auto writer = m_objectWriters.top().write("tests"_sr).writeArray(); + + for (auto const &test : tests) { + auto desc_writer = writer.writeObject(); + auto const &info = test.getTestCaseInfo(); + + desc_writer.write("name"_sr).write(info.name); + desc_writer.write("class-name"_sr).write(info.className); + { + auto tag_writer = desc_writer.write("tags"_sr).writeArray(); + for (auto const &tag : info.tags) { + tag_writer.write(tag.original); } + } + writeSourceInfo(desc_writer, info.lineInfo); + } + } + void JsonReporter::listTags(std::vector const &tags) { + startListing(); - void writeTags( JsonArrayWriter writer, std::vector const& tags ) { - for ( auto const& tag : tags ) { - writer.write( tag.original ); - } - } - - void writeProperties( JsonArrayWriter writer, - TestCaseInfo const& info ) { - if ( info.isHidden() ) { writer.write( "is-hidden"_sr ); } - if ( info.okToFail() ) { writer.write( "ok-to-fail"_sr ); } - if ( info.expectedToFail() ) { - writer.write( "expected-to-fail"_sr ); - } - if ( info.throws() ) { writer.write( "throws"_sr ); } - } - - } // namespace - - JsonReporter::JsonReporter( ReporterConfig&& config ): - StreamingReporterBase{ CATCH_MOVE( config ) } { - - m_preferences.shouldRedirectStdOut = true; - // TBD: Do we want to report all assertions? XML reporter does - // not, but for machine-parseable reporters I think the answer - // should be yes. - m_preferences.shouldReportAllAssertions = true; - // We only handle assertions when they end - m_preferences.shouldReportAllAssertionStarts = false; - - m_objectWriters.emplace( m_stream ); - m_writers.emplace( Writer::Object ); - auto& writer = m_objectWriters.top(); - - writer.write( "version"_sr ).write( 1 ); - - { - auto metadata_writer = writer.write( "metadata"_sr ).writeObject(); - metadata_writer.write( "name"_sr ).write( m_config->name() ); - metadata_writer.write( "rng-seed"_sr ).write( m_config->rngSeed() ); - metadata_writer.write( "catch2-version"_sr ) - .write( libraryVersion() ); - if ( m_config->testSpec().hasFilters() ) { - metadata_writer.write( "filters"_sr ) - .write( m_config->testSpec() ); - } - } - } - - JsonReporter::~JsonReporter() { - endListing(); - // TODO: Ensure this closes the top level object, add asserts - assert( m_writers.size() == 1 && "Only the top level object should be open" ); - assert( m_writers.top() == Writer::Object ); - endObject(); - m_stream << '\n' << std::flush; - assert( m_writers.empty() ); - } - - JsonArrayWriter& JsonReporter::startArray() { - m_arrayWriters.emplace( m_arrayWriters.top().writeArray() ); - m_writers.emplace( Writer::Array ); - return m_arrayWriters.top(); - } - JsonArrayWriter& JsonReporter::startArray( StringRef key ) { - m_arrayWriters.emplace( - m_objectWriters.top().write( key ).writeArray() ); - m_writers.emplace( Writer::Array ); - return m_arrayWriters.top(); - } - - JsonObjectWriter& JsonReporter::startObject() { - m_objectWriters.emplace( m_arrayWriters.top().writeObject() ); - m_writers.emplace( Writer::Object ); - return m_objectWriters.top(); - } - JsonObjectWriter& JsonReporter::startObject( StringRef key ) { - m_objectWriters.emplace( - m_objectWriters.top().write( key ).writeObject() ); - m_writers.emplace( Writer::Object ); - return m_objectWriters.top(); - } - - void JsonReporter::endObject() { - assert( isInside( Writer::Object ) ); - m_objectWriters.pop(); - m_writers.pop(); - } - void JsonReporter::endArray() { - assert( isInside( Writer::Array ) ); - m_arrayWriters.pop(); - m_writers.pop(); - } - - bool JsonReporter::isInside( Writer writer ) { - return !m_writers.empty() && m_writers.top() == writer; - } - - void JsonReporter::startListing() { - if ( !m_startedListing ) { startObject( "listings"_sr ); } - m_startedListing = true; - } - void JsonReporter::endListing() { - if ( m_startedListing ) { endObject(); } - m_startedListing = false; - } - - std::string JsonReporter::getDescription() { - return "Outputs listings as JSON. Test listing is Work-in-Progress!"; - } - - void JsonReporter::testRunStarting( TestRunInfo const& runInfo ) { - StreamingReporterBase::testRunStarting( runInfo ); - endListing(); - - assert( isInside( Writer::Object ) ); - startObject( "test-run"_sr ); - startArray( "test-cases"_sr ); - } - - static void writeCounts( JsonObjectWriter&& writer, Counts const& counts ) { - writer.write( "passed"_sr ).write( counts.passed ); - writer.write( "failed"_sr ).write( counts.failed ); - writer.write( "fail-but-ok"_sr ).write( counts.failedButOk ); - writer.write( "skipped"_sr ).write( counts.skipped ); - } - - void JsonReporter::testRunEnded(TestRunStats const& runStats) { - assert( isInside( Writer::Array ) ); - // End "test-cases" - endArray(); - - { - auto totals = - m_objectWriters.top().write( "totals"_sr ).writeObject(); - writeCounts( totals.write( "assertions"_sr ).writeObject(), - runStats.totals.assertions ); - writeCounts( totals.write( "test-cases"_sr ).writeObject(), - runStats.totals.testCases ); - } - - // End the "test-run" object - endObject(); - } - - void JsonReporter::testCaseStarting( TestCaseInfo const& tcInfo ) { - StreamingReporterBase::testCaseStarting( tcInfo ); - - assert( isInside( Writer::Array ) && - "We should be in the 'test-cases' array" ); - startObject(); - // "test-info" prelude - { - auto testInfo = - m_objectWriters.top().write( "test-info"_sr ).writeObject(); - // TODO: handle testName vs className!! - testInfo.write( "name"_sr ).write( tcInfo.name ); - writeSourceInfo(testInfo, tcInfo.lineInfo); - writeTags( testInfo.write( "tags"_sr ).writeArray(), tcInfo.tags ); - writeProperties( testInfo.write( "properties"_sr ).writeArray(), - tcInfo ); - } - - - // Start the array for individual test runs (testCasePartial pairs) - startArray( "runs"_sr ); - } - - void JsonReporter::testCaseEnded( TestCaseStats const& tcStats ) { - StreamingReporterBase::testCaseEnded( tcStats ); - - // We need to close the 'runs' array before finishing the test case - assert( isInside( Writer::Array ) ); - endArray(); - - { - auto totals = - m_objectWriters.top().write( "totals"_sr ).writeObject(); - writeCounts( totals.write( "assertions"_sr ).writeObject(), - tcStats.totals.assertions ); - // We do not write the test case totals, because there will always be just one test case here. - // TODO: overall "result" -> success, skip, fail here? Or in partial result? - } - // We do not write out stderr/stdout, because we instead wrote those out in partial runs - - // TODO: aborting? - - // And we also close this test case's object - assert( isInside( Writer::Object ) ); - endObject(); - } - - void JsonReporter::testCasePartialStarting( TestCaseInfo const& /*tcInfo*/, - uint64_t index ) { - startObject(); - m_objectWriters.top().write( "run-idx"_sr ).write( index ); - startArray( "path"_sr ); - // TODO: we want to delay most of the printing to the 'root' section - // TODO: childSection key name? - } - - void JsonReporter::testCasePartialEnded( TestCaseStats const& tcStats, - uint64_t /*index*/ ) { - // Fixme: the top level section handles this. - //// path object - endArray(); - if ( !tcStats.stdOut.empty() ) { - m_objectWriters.top() - .write( "captured-stdout"_sr ) - .write( tcStats.stdOut ); - } - if ( !tcStats.stdErr.empty() ) { - m_objectWriters.top() - .write( "captured-stderr"_sr ) - .write( tcStats.stdErr ); - } - { - auto totals = - m_objectWriters.top().write( "totals"_sr ).writeObject(); - writeCounts( totals.write( "assertions"_sr ).writeObject(), - tcStats.totals.assertions ); - // We do not write the test case totals, because there will - // always be just one test case here. - // TODO: overall "result" -> success, skip, fail here? Or in - // partial result? - } - // TODO: aborting? - // run object - endObject(); - } - - void JsonReporter::sectionStarting( SectionInfo const& sectionInfo ) { - assert( isInside( Writer::Array ) && - "Section should always start inside an object" ); - // We want to nest top level sections, even though it shares name - // and source loc with the TEST_CASE - auto& sectionObject = startObject(); - sectionObject.write( "kind"_sr ).write( "section"_sr ); - sectionObject.write( "name"_sr ).write( sectionInfo.name ); - writeSourceInfo( m_objectWriters.top(), sectionInfo.lineInfo ); - - - // TBD: Do we want to create this event lazily? It would become - // rather complex, but we could do it, and it would look - // better for empty sections. OTOH, empty sections should - // be rare. - startArray( "path"_sr ); - } - void JsonReporter::sectionEnded( SectionStats const& /*sectionStats */) { - // End the subpath array - endArray(); - // TODO: metadata - // TODO: what info do we have here? - - // End the section object - endObject(); - } - - void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) { - // TODO: There is lot of different things to handle here, but - // we can fill it in later, after we show that the basic - // outline and streaming reporter impl works well enough. - //if ( !m_config->includeSuccessfulResults() - // && assertionStats.assertionResult.isOk() ) { - // return; - //} - assert( isInside( Writer::Array ) ); - auto assertionObject = m_arrayWriters.top().writeObject(); - - assertionObject.write( "kind"_sr ).write( "assertion"_sr ); - writeSourceInfo( assertionObject, - assertionStats.assertionResult.getSourceInfo() ); - assertionObject.write( "status"_sr ) - .write( assertionStats.assertionResult.isOk() ); - // TODO: handling of result. - // TODO: messages - // TODO: totals? - } - - - void JsonReporter::benchmarkPreparing( StringRef name ) { (void)name; } - void JsonReporter::benchmarkStarting( BenchmarkInfo const& ) {} - void JsonReporter::benchmarkEnded( BenchmarkStats<> const& ) {} - void JsonReporter::benchmarkFailed( StringRef error ) { (void)error; } - - void JsonReporter::listReporters( - std::vector const& descriptions ) { - startListing(); - - auto writer = - m_objectWriters.top().write( "reporters"_sr ).writeArray(); - for ( auto const& desc : descriptions ) { - auto desc_writer = writer.writeObject(); - desc_writer.write( "name"_sr ).write( desc.name ); - desc_writer.write( "description"_sr ).write( desc.description ); - } - } - void JsonReporter::listListeners( - std::vector const& descriptions ) { - startListing(); - - auto writer = - m_objectWriters.top().write( "listeners"_sr ).writeArray(); - - for ( auto const& desc : descriptions ) { - auto desc_writer = writer.writeObject(); - desc_writer.write( "name"_sr ).write( desc.name ); - desc_writer.write( "description"_sr ).write( desc.description ); - } - } - void JsonReporter::listTests( std::vector const& tests ) { - startListing(); - - auto writer = m_objectWriters.top().write( "tests"_sr ).writeArray(); - - for ( auto const& test : tests ) { - auto desc_writer = writer.writeObject(); - auto const& info = test.getTestCaseInfo(); - - desc_writer.write( "name"_sr ).write( info.name ); - desc_writer.write( "class-name"_sr ).write( info.className ); - { - auto tag_writer = desc_writer.write( "tags"_sr ).writeArray(); - for ( auto const& tag : info.tags ) { - tag_writer.write( tag.original ); - } - } - writeSourceInfo( desc_writer, info.lineInfo ); - } - } - void JsonReporter::listTags( std::vector const& tags ) { - startListing(); - - auto writer = m_objectWriters.top().write( "tags"_sr ).writeArray(); - for ( auto const& tag : tags ) { - auto tag_writer = writer.writeObject(); - { - auto aliases_writer = - tag_writer.write( "aliases"_sr ).writeArray(); - for ( auto alias : tag.spellings ) { - aliases_writer.write( alias ); - } - } - tag_writer.write( "count"_sr ).write( tag.count ); + auto writer = m_objectWriters.top().write("tags"_sr).writeArray(); + for (auto const &tag : tags) { + auto tag_writer = writer.writeObject(); + { + auto aliases_writer = tag_writer.write("aliases"_sr).writeArray(); + for (auto alias : tag.spellings) { + aliases_writer.write(alias); } + } + tag_writer.write("count"_sr).write(tag.count); } + } } // namespace Catch - - - +#include #include #include -#include #include namespace Catch { - namespace { - std::string getCurrentTimestamp() { - time_t rawtime; - std::time(&rawtime); + namespace { + std::string getCurrentTimestamp() { + time_t rawtime; + std::time(&rawtime); - std::tm timeInfo = {}; -#if defined (_MSC_VER) || defined (__MINGW32__) - gmtime_s(&timeInfo, &rawtime); -#elif defined (CATCH_PLATFORM_PLAYSTATION) - gmtime_s(&rawtime, &timeInfo); -#elif defined (__IAR_SYSTEMS_ICC__) - timeInfo = *std::gmtime(&rawtime); + std::tm timeInfo = {}; +#if defined(_MSC_VER) || defined(__MINGW32__) + 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); + gmtime_r(&rawtime, &timeInfo); #endif - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); - return std::string(timeStamp, timeStampSize - 1); - } - - std::string fileNameTag(std::vector const& tags) { - auto it = std::find_if(begin(tags), - end(tags), - [] (Tag const& tag) { - return tag.original.size() > 0 - && tag.original[0] == '#'; }); - if (it != tags.end()) { - return static_cast( - it->original.substr(1, it->original.size() - 1) - ); - } - return std::string(); - } - - // Formats the duration in seconds to 3 decimal places. - // This is done because some genius defined Maven Surefire schema - // in a way that only accepts 3 decimal places, and tools like - // Jenkins use that schema for validation JUnit reporter output. - std::string formatDuration( double seconds ) { - ReusableStringStream rss; - rss << std::fixed << std::setprecision( 3 ) << seconds; - return rss.str(); - } - - static void normalizeNamespaceMarkers(std::string& str) { - std::size_t pos = str.find( "::" ); - while ( pos != std::string::npos ) { - str.replace( pos, 2, "." ); - pos += 1; - pos = str.find( "::", pos ); - } - } - - } // anonymous namespace - - JunitReporter::JunitReporter( ReporterConfig&& _config ) - : CumulativeReporterBase( CATCH_MOVE(_config) ), - xml( m_stream ) - { - m_preferences.shouldRedirectStdOut = true; - m_preferences.shouldReportAllAssertions = false; - m_preferences.shouldReportAllAssertionStarts = false; - m_shouldStoreSuccesfulAssertions = false; - } - - std::string JunitReporter::getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; + return std::string(timeStamp, timeStampSize - 1); } - void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - suiteTimer.start(); - stdOutForSuite.clear(); - stdErrForSuite.clear(); - unexpectedExceptions = 0; + std::string fileNameTag(std::vector const &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [](Tag const &tag) { return tag.original.size() > 0 + && tag.original[0] == '#'; }); + if (it != tags.end()) { + return static_cast( + it->original.substr(1, it->original.size() - 1)); + } + return std::string(); } - void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { - m_okToFail = testCaseInfo.okToFail(); + // Formats the duration in seconds to 3 decimal places. + // This is done because some genius defined Maven Surefire schema + // in a way that only accepts 3 decimal places, and tools like + // Jenkins use that schema for validation JUnit reporter output. + std::string formatDuration(double seconds) { + ReusableStringStream rss; + rss << std::fixed << std::setprecision(3) << seconds; + return rss.str(); } - void JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) - unexpectedExceptions++; - CumulativeReporterBase::assertionEnded( assertionStats ); + static void normalizeNamespaceMarkers(std::string &str) { + std::size_t pos = str.find("::"); + while (pos != std::string::npos) { + str.replace(pos, 2, "."); + pos += 1; + pos = str.find("::", pos); + } } - void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { - stdOutForSuite += testCaseStats.stdOut; - stdErrForSuite += testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); + } // anonymous namespace + + JunitReporter::JunitReporter(ReporterConfig &&_config) + : CumulativeReporterBase(CATCH_MOVE(_config)), xml(m_stream) { + m_preferences.shouldRedirectStdOut = true; + m_preferences.shouldReportAllAssertions = false; + m_preferences.shouldReportAllAssertionStarts = false; + m_shouldStoreSuccesfulAssertions = false; + } + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::testRunStarting(TestRunInfo const &runInfo) { + CumulativeReporterBase::testRunStarting(runInfo); + xml.startElement("testsuites"); + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + } + + void JunitReporter::testCaseStarting(TestCaseInfo const &testCaseInfo) { + m_okToFail = testCaseInfo.okToFail(); + } + + void JunitReporter::assertionEnded(AssertionStats const &assertionStats) { + if (assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail) + unexpectedExceptions++; + CumulativeReporterBase::assertionEnded(assertionStats); + } + + void JunitReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded(testCaseStats); + } + + void JunitReporter::testRunEndedCumulative() { + const auto suiteTime = suiteTimer.getElapsedSeconds(); + writeRun(*m_testRun, suiteTime); + xml.endElement(); + } + + void JunitReporter::writeRun(TestRunNode const &testRunNode, double suiteTime) { + XmlWriter::ScopedElement e = xml.scopedElement("testsuite"); + + TestRunStats const &stats = testRunNode.value; + xml.writeAttribute("name"_sr, stats.runInfo.name); + xml.writeAttribute("errors"_sr, unexpectedExceptions); + xml.writeAttribute("failures"_sr, stats.totals.assertions.failed - unexpectedExceptions); + xml.writeAttribute("skipped"_sr, stats.totals.assertions.skipped); + xml.writeAttribute("tests"_sr, stats.totals.assertions.total()); + xml.writeAttribute("hostname"_sr, "tbd"_sr); // !TBD + if (m_config->showDurations() == ShowDurations::Never) + xml.writeAttribute("time"_sr, ""_sr); + else + xml.writeAttribute("time"_sr, formatDuration(suiteTime)); + xml.writeAttribute("timestamp"_sr, getCurrentTimestamp()); + + // Write properties + { + auto properties = xml.scopedElement("properties"); + xml.scopedElement("property") + .writeAttribute("name"_sr, "random-seed"_sr) + .writeAttribute("value"_sr, m_config->rngSeed()); + if (m_config->testSpec().hasFilters()) { + xml.scopedElement("property") + .writeAttribute("name"_sr, "filters"_sr) + .writeAttribute("value"_sr, m_config->testSpec()); + } } - void JunitReporter::testRunEndedCumulative() { - const auto suiteTime = suiteTimer.getElapsedSeconds(); - writeRun( *m_testRun, suiteTime ); - xml.endElement(); + // Write test cases + for (auto const &child : testRunNode.children) + writeTestCase(*child); + + xml.scopedElement("system-out").writeText(trim(stdOutForSuite), XmlFormatting::Newline); + xml.scopedElement("system-err").writeText(trim(stdErrForSuite), XmlFormatting::Newline); + } + + void JunitReporter::writeTestCase(TestCaseNode const &testCaseNode) { + TestCaseStats const &stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const &rootSection = *testCaseNode.children.front(); + + std::string className = static_cast(stats.testInfo->className); + + if (className.empty()) { + className = fileNameTag(stats.testInfo->tags); + if (className.empty()) { + className = "global"; + } } - void JunitReporter::writeRun( TestRunNode const& testRunNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + if (!m_config->name().empty()) + className = static_cast(m_config->name()) + '.' + className; - TestRunStats const& stats = testRunNode.value; - xml.writeAttribute( "name"_sr, stats.runInfo.name ); - xml.writeAttribute( "errors"_sr, unexpectedExceptions ); - xml.writeAttribute( "failures"_sr, stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "skipped"_sr, stats.totals.assertions.skipped ); - xml.writeAttribute( "tests"_sr, stats.totals.assertions.total() ); - xml.writeAttribute( "hostname"_sr, "tbd"_sr ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time"_sr, ""_sr ); - else - xml.writeAttribute( "time"_sr, formatDuration( suiteTime ) ); - xml.writeAttribute( "timestamp"_sr, getCurrentTimestamp() ); + normalizeNamespaceMarkers(className); - // Write properties - { - auto properties = xml.scopedElement("properties"); - xml.scopedElement("property") - .writeAttribute("name"_sr, "random-seed"_sr) - .writeAttribute("value"_sr, m_config->rngSeed()); - if (m_config->testSpec().hasFilters()) { - xml.scopedElement("property") - .writeAttribute("name"_sr, "filters"_sr) - .writeAttribute("value"_sr, m_config->testSpec()); - } + writeSection(className, "", rootSection, stats.testInfo->okToFail()); + } + + void JunitReporter::writeSection(std::string const &className, + std::string const &rootName, + SectionNode const §ionNode, + bool testOkToFail) { + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + '/' + name; + + if (sectionNode.stats.assertions.total() > 0 + || !sectionNode.stdOut.empty() + || !sectionNode.stdErr.empty()) { + XmlWriter::ScopedElement e = xml.scopedElement("testcase"); + if (className.empty()) { + xml.writeAttribute("classname"_sr, name); + xml.writeAttribute("name"_sr, "root"_sr); + } else { + xml.writeAttribute("classname"_sr, className); + xml.writeAttribute("name"_sr, name); + } + xml.writeAttribute("time"_sr, formatDuration(sectionNode.stats.durationInSeconds)); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute("status"_sr, "run"_sr); + + if (sectionNode.stats.assertions.failedButOk) { + xml.scopedElement("skipped") + .writeAttribute("message", "TEST_CASE tagged with !mayfail"); + } + + writeAssertions(sectionNode); + + if (!sectionNode.stdOut.empty()) + xml.scopedElement("system-out").writeText(trim(sectionNode.stdOut), XmlFormatting::Newline); + if (!sectionNode.stdErr.empty()) + xml.scopedElement("system-err").writeText(trim(sectionNode.stdErr), XmlFormatting::Newline); + } + for (auto const &childNode : sectionNode.childSections) + if (className.empty()) + writeSection(name, "", *childNode, testOkToFail); + else + writeSection(className, name, *childNode, testOkToFail); + } + + void JunitReporter::writeAssertions(SectionNode const §ionNode) { + for (auto const &assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { + if (assertionOrBenchmark.isAssertion()) { + // JUnit XML format supports only 1 error/failure/skip + // assertion elements per test case + if (writeAssertion(assertionOrBenchmark.asAssertion())) { + break; } + } + } + } - // Write test cases - for( auto const& child : testRunNode.children ) - writeTestCase( *child ); - - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); + bool JunitReporter::writeAssertion(AssertionStats const &stats) { + AssertionResult const &result = stats.assertionResult; + if (result.isOk() && result.getResultType() != ResultWas::ExplicitSkip) { + return false; + } + std::string elementName; + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + case ResultWas::ExplicitSkip: + elementName = "skipped"; + break; + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; } - void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; + XmlWriter::ScopedElement e = xml.scopedElement(elementName); - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); + xml.writeAttribute("message"_sr, result.getExpression()); + xml.writeAttribute("type"_sr, result.getTestMacroName()); - std::string className = - static_cast( stats.testInfo->className ); - - if( className.empty() ) { - className = fileNameTag(stats.testInfo->tags); - if ( className.empty() ) { - className = "global"; - } - } - - if ( !m_config->name().empty() ) - className = static_cast(m_config->name()) + '.' + className; - - normalizeNamespaceMarkers(className); - - writeSection( className, "", rootSection, stats.testInfo->okToFail() ); + ReusableStringStream rss; + if (result.getResultType() == ResultWas::ExplicitSkip) { + rss << "SKIPPED\n"; + } else { + rss << "FAILED:\n"; + if (result.hasExpression()) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; + } + if (result.hasExpandedExpression()) { + rss << "with expansion:\n"; + rss << TextFlow::Column(result.getExpandedExpression()) + .indent(2) + << '\n'; + } } - void JunitReporter::writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode, - bool testOkToFail) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + '/' + name; - - if ( sectionNode.stats.assertions.total() > 0 - || !sectionNode.stdOut.empty() - || !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname"_sr, name ); - xml.writeAttribute( "name"_sr, "root"_sr ); - } - else { - xml.writeAttribute( "classname"_sr, className ); - xml.writeAttribute( "name"_sr, name ); - } - xml.writeAttribute( "time"_sr, formatDuration( sectionNode.stats.durationInSeconds ) ); - // This is not ideal, but it should be enough to mimic gtest's - // junit output. - // Ideally the JUnit reporter would also handle `skipTest` - // events and write those out appropriately. - xml.writeAttribute( "status"_sr, "run"_sr ); - - if (sectionNode.stats.assertions.failedButOk) { - xml.scopedElement("skipped") - .writeAttribute("message", "TEST_CASE tagged with !mayfail"); - } - - writeAssertions( sectionNode ); - - - if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); - if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); - } - for( auto const& childNode : sectionNode.childSections ) - if( className.empty() ) - writeSection( name, "", *childNode, testOkToFail ); - else - writeSection( className, name, *childNode, testOkToFail ); + if (result.hasMessage()) { rss << result.getMessage() << '\n'; } + for (auto const &msg : stats.infoMessages) { + if (msg.type == ResultWas::Info) { rss << msg.message << '\n'; } } - void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { - for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { - if (assertionOrBenchmark.isAssertion()) { - // JUnit XML format supports only 1 error/failure/skip - // assertion elements per test case - if (writeAssertion(assertionOrBenchmark.asAssertion())) { - break; - } - } - } - } - - bool JunitReporter::writeAssertion( AssertionStats const& stats ) { - AssertionResult const& result = stats.assertionResult; - if ( result.isOk() && - result.getResultType() != ResultWas::ExplicitSkip ) { - return false; - } - std::string elementName; - switch ( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - case ResultWas::ExpressionFailed: - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - case ResultWas::ExplicitSkip: - elementName = "skipped"; - break; - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } - - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - - xml.writeAttribute( "message"_sr, result.getExpression() ); - xml.writeAttribute( "type"_sr, result.getTestMacroName() ); - - ReusableStringStream rss; - if ( result.getResultType() == ResultWas::ExplicitSkip ) { - rss << "SKIPPED\n"; - } else { - rss << "FAILED:\n"; - if ( result.hasExpression() ) { - rss << " "; - rss << result.getExpressionInMacro(); - rss << '\n'; - } - if ( result.hasExpandedExpression() ) { - rss << "with expansion:\n"; - rss << TextFlow::Column( result.getExpandedExpression() ) - .indent( 2 ) - << '\n'; - } - } - - if ( result.hasMessage() ) { rss << result.getMessage() << '\n'; } - for ( auto const& msg : stats.infoMessages ) { - if ( msg.type == ResultWas::Info ) { rss << msg.message << '\n'; } - } - - rss << "at " << result.getSourceInfo(); - xml.writeText( rss.str(), XmlFormatting::Newline ); - return true; - } + rss << "at " << result.getSourceInfo(); + xml.writeText(rss.str(), XmlFormatting::Newline); + return true; + } } // end namespace Catch - - - #include namespace Catch { - void MultiReporter::updatePreferences(IEventListener const& reporterish) { - m_preferences.shouldRedirectStdOut |= - reporterish.getPreferences().shouldRedirectStdOut; - m_preferences.shouldReportAllAssertions |= - reporterish.getPreferences().shouldReportAllAssertions; - m_preferences.shouldReportAllAssertionStarts |= - reporterish.getPreferences().shouldReportAllAssertionStarts; + void MultiReporter::updatePreferences(IEventListener const &reporterish) { + m_preferences.shouldRedirectStdOut |= reporterish.getPreferences().shouldRedirectStdOut; + m_preferences.shouldReportAllAssertions |= reporterish.getPreferences().shouldReportAllAssertions; + m_preferences.shouldReportAllAssertionStarts |= reporterish.getPreferences().shouldReportAllAssertionStarts; + } + + void MultiReporter::addListener(IEventListenerPtr &&listener) { + updatePreferences(*listener); + m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener)); + ++m_insertedListeners; + } + + void MultiReporter::addReporter(IEventListenerPtr &&reporter) { + updatePreferences(*reporter); + + // We will need to output the captured stdout if there are reporters + // that do not want it captured. + // We do not consider listeners, because it is generally assumed that + // listeners are output-transparent, even though they can ask for stdout + // capture to do something with it. + m_haveNoncapturingReporters |= !reporter->getPreferences().shouldRedirectStdOut; + + // Reporters can always be placed to the back without breaking the + // reporting order + m_reporterLikes.push_back(CATCH_MOVE(reporter)); + } + + void MultiReporter::noMatchingTestCases(StringRef unmatchedSpec) { + for (auto &reporterish : m_reporterLikes) { + reporterish->noMatchingTestCases(unmatchedSpec); + } + } + + void MultiReporter::fatalErrorEncountered(StringRef error) { + for (auto &reporterish : m_reporterLikes) { + reporterish->fatalErrorEncountered(error); + } + } + + void MultiReporter::reportInvalidTestSpec(StringRef arg) { + for (auto &reporterish : m_reporterLikes) { + reporterish->reportInvalidTestSpec(arg); + } + } + + void MultiReporter::benchmarkPreparing(StringRef name) { + for (auto &reporterish : m_reporterLikes) { + reporterish->benchmarkPreparing(name); + } + } + void MultiReporter::benchmarkStarting(BenchmarkInfo const &benchmarkInfo) { + for (auto &reporterish : m_reporterLikes) { + reporterish->benchmarkStarting(benchmarkInfo); + } + } + void MultiReporter::benchmarkEnded(BenchmarkStats<> const &benchmarkStats) { + for (auto &reporterish : m_reporterLikes) { + reporterish->benchmarkEnded(benchmarkStats); + } + } + + void MultiReporter::benchmarkFailed(StringRef error) { + for (auto &reporterish : m_reporterLikes) { + reporterish->benchmarkFailed(error); + } + } + + void MultiReporter::testRunStarting(TestRunInfo const &testRunInfo) { + for (auto &reporterish : m_reporterLikes) { + reporterish->testRunStarting(testRunInfo); + } + } + + void MultiReporter::testCaseStarting(TestCaseInfo const &testInfo) { + for (auto &reporterish : m_reporterLikes) { + reporterish->testCaseStarting(testInfo); + } + } + + void + MultiReporter::testCasePartialStarting(TestCaseInfo const &testInfo, + uint64_t partNumber) { + for (auto &reporterish : m_reporterLikes) { + reporterish->testCasePartialStarting(testInfo, partNumber); + } + } + + void MultiReporter::sectionStarting(SectionInfo const §ionInfo) { + for (auto &reporterish : m_reporterLikes) { + reporterish->sectionStarting(sectionInfo); + } + } + + void MultiReporter::assertionStarting(AssertionInfo const &assertionInfo) { + for (auto &reporterish : m_reporterLikes) { + reporterish->assertionStarting(assertionInfo); + } + } + + void MultiReporter::assertionEnded(AssertionStats const &assertionStats) { + const bool reportByDefault = assertionStats.assertionResult.getResultType() != ResultWas::Ok || m_config->includeSuccessfulResults(); + + for (auto &reporterish : m_reporterLikes) { + if (reportByDefault || reporterish->getPreferences().shouldReportAllAssertions) { + reporterish->assertionEnded(assertionStats); + } + } + } + + void MultiReporter::sectionEnded(SectionStats const §ionStats) { + for (auto &reporterish : m_reporterLikes) { + reporterish->sectionEnded(sectionStats); + } + } + + void MultiReporter::testCasePartialEnded(TestCaseStats const &testStats, + uint64_t partNumber) { + if (m_preferences.shouldRedirectStdOut && m_haveNoncapturingReporters) { + if (!testStats.stdOut.empty()) { + Catch::cout() << testStats.stdOut << std::flush; + } + if (!testStats.stdErr.empty()) { + Catch::cerr() << testStats.stdErr << std::flush; + } } - void MultiReporter::addListener( IEventListenerPtr&& listener ) { - updatePreferences(*listener); - m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener) ); - ++m_insertedListeners; + for (auto &reporterish : m_reporterLikes) { + reporterish->testCasePartialEnded(testStats, partNumber); } + } - void MultiReporter::addReporter( IEventListenerPtr&& reporter ) { - updatePreferences(*reporter); - - // We will need to output the captured stdout if there are reporters - // that do not want it captured. - // We do not consider listeners, because it is generally assumed that - // listeners are output-transparent, even though they can ask for stdout - // capture to do something with it. - m_haveNoncapturingReporters |= !reporter->getPreferences().shouldRedirectStdOut; - - // Reporters can always be placed to the back without breaking the - // reporting order - m_reporterLikes.push_back( CATCH_MOVE( reporter ) ); + void MultiReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + for (auto &reporterish : m_reporterLikes) { + reporterish->testCaseEnded(testCaseStats); } + } - void MultiReporter::noMatchingTestCases( StringRef unmatchedSpec ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->noMatchingTestCases( unmatchedSpec ); - } + void MultiReporter::testRunEnded(TestRunStats const &testRunStats) { + for (auto &reporterish : m_reporterLikes) { + reporterish->testRunEnded(testRunStats); } + } - void MultiReporter::fatalErrorEncountered( StringRef error ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->fatalErrorEncountered( error ); - } + void MultiReporter::skipTest(TestCaseInfo const &testInfo) { + for (auto &reporterish : m_reporterLikes) { + reporterish->skipTest(testInfo); } + } - void MultiReporter::reportInvalidTestSpec( StringRef arg ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->reportInvalidTestSpec( arg ); - } + void MultiReporter::listReporters(std::vector const &descriptions) { + for (auto &reporterish : m_reporterLikes) { + reporterish->listReporters(descriptions); } + } - void MultiReporter::benchmarkPreparing( StringRef name ) { - for (auto& reporterish : m_reporterLikes) { - reporterish->benchmarkPreparing(name); - } + void MultiReporter::listListeners( + std::vector const &descriptions) { + for (auto &reporterish : m_reporterLikes) { + reporterish->listListeners(descriptions); } - void MultiReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->benchmarkStarting( benchmarkInfo ); - } + } + + void MultiReporter::listTests(std::vector const &tests) { + for (auto &reporterish : m_reporterLikes) { + reporterish->listTests(tests); } - void MultiReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->benchmarkEnded( benchmarkStats ); - } - } - - void MultiReporter::benchmarkFailed( StringRef error ) { - for (auto& reporterish : m_reporterLikes) { - reporterish->benchmarkFailed(error); - } - } - - void MultiReporter::testRunStarting( TestRunInfo const& testRunInfo ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->testRunStarting( testRunInfo ); - } - } - - void MultiReporter::testCaseStarting( TestCaseInfo const& testInfo ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->testCaseStarting( testInfo ); - } - } - - void - MultiReporter::testCasePartialStarting( TestCaseInfo const& testInfo, - uint64_t partNumber ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->testCasePartialStarting( testInfo, partNumber ); - } - } - - void MultiReporter::sectionStarting( SectionInfo const& sectionInfo ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->sectionStarting( sectionInfo ); - } - } - - void MultiReporter::assertionStarting( AssertionInfo const& assertionInfo ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->assertionStarting( assertionInfo ); - } - } - - void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { - const bool reportByDefault = - assertionStats.assertionResult.getResultType() != ResultWas::Ok || - m_config->includeSuccessfulResults(); - - for ( auto & reporterish : m_reporterLikes ) { - if ( reportByDefault || - reporterish->getPreferences().shouldReportAllAssertions ) { - reporterish->assertionEnded( assertionStats ); - } - } - } - - void MultiReporter::sectionEnded( SectionStats const& sectionStats ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->sectionEnded( sectionStats ); - } - } - - void MultiReporter::testCasePartialEnded( TestCaseStats const& testStats, - uint64_t partNumber ) { - if ( m_preferences.shouldRedirectStdOut && - m_haveNoncapturingReporters ) { - if ( !testStats.stdOut.empty() ) { - Catch::cout() << testStats.stdOut << std::flush; - } - if ( !testStats.stdErr.empty() ) { - Catch::cerr() << testStats.stdErr << std::flush; - } - } - - for ( auto& reporterish : m_reporterLikes ) { - reporterish->testCasePartialEnded( testStats, partNumber ); - } - } - - void MultiReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->testCaseEnded( testCaseStats ); - } - } - - void MultiReporter::testRunEnded( TestRunStats const& testRunStats ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->testRunEnded( testRunStats ); - } - } - - - void MultiReporter::skipTest( TestCaseInfo const& testInfo ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->skipTest( testInfo ); - } - } - - void MultiReporter::listReporters(std::vector const& descriptions) { - for (auto& reporterish : m_reporterLikes) { - reporterish->listReporters(descriptions); - } - } - - void MultiReporter::listListeners( - std::vector const& descriptions ) { - for ( auto& reporterish : m_reporterLikes ) { - reporterish->listListeners( descriptions ); - } - } - - void MultiReporter::listTests(std::vector const& tests) { - for (auto& reporterish : m_reporterLikes) { - reporterish->listTests(tests); - } - } - - void MultiReporter::listTags(std::vector const& tags) { - for (auto& reporterish : m_reporterLikes) { - reporterish->listTags(tags); - } + } + + void MultiReporter::listTags(std::vector const &tags) { + for (auto &reporterish : m_reporterLikes) { + reporterish->listTags(tags); } + } } // end namespace Catch - - - - namespace Catch { - namespace Detail { + namespace Detail { - void registerReporterImpl( std::string const& name, - IReporterFactoryPtr reporterPtr ) { - CATCH_TRY { - getMutableRegistryHub().registerReporter( - name, CATCH_MOVE( reporterPtr ) ); - } - CATCH_CATCH_ALL { - // Do not throw when constructing global objects, instead - // register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } - } + void registerReporterImpl(std::string const &name, + IReporterFactoryPtr reporterPtr) { + CATCH_TRY { + getMutableRegistryHub().registerReporter( + name, CATCH_MOVE(reporterPtr)); + } + CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead + // register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } - void registerListenerImpl( Detail::unique_ptr listenerFactory ) { - getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) ); - } + void registerListenerImpl(Detail::unique_ptr listenerFactory) { + getMutableRegistryHub().registerListener(CATCH_MOVE(listenerFactory)); + } - - } // namespace Detail + } // namespace Detail } // namespace Catch - - - #include namespace Catch { - namespace { - std::string createMetadataString(IConfig const& config) { - ReusableStringStream sstr; - if ( config.testSpec().hasFilters() ) { - sstr << "filters='" - << config.testSpec() - << "' "; - } - sstr << "rng-seed=" << config.rngSeed(); - return sstr.str(); + namespace { + std::string createMetadataString(IConfig const &config) { + ReusableStringStream sstr; + if (config.testSpec().hasFilters()) { + sstr << "filters='" + << config.testSpec() + << "' "; + } + sstr << "rng-seed=" << config.rngSeed(); + return sstr.str(); + } + } // namespace + + void SonarQubeReporter::testRunStarting(TestRunInfo const &testRunInfo) { + CumulativeReporterBase::testRunStarting(testRunInfo); + + xml.writeComment(createMetadataString(*m_config)); + xml.startElement("testExecutions"); + xml.writeAttribute("version"_sr, '1'); + } + + void SonarQubeReporter::writeRun(TestRunNode const &runNode) { + std::map> testsPerFile; + + for (auto const &child : runNode.children) { + testsPerFile[child->value.testInfo->lineInfo.file].push_back( + child.get()); + } + + for (auto const &kv : testsPerFile) { + writeTestFile(kv.first, kv.second); + } + } + + void SonarQubeReporter::writeTestFile(StringRef filename, std::vector const &testCaseNodes) { + XmlWriter::ScopedElement e = xml.scopedElement("file"); + xml.writeAttribute("path"_sr, filename); + + for (auto const &child : testCaseNodes) + writeTestCase(*child); + } + + void SonarQubeReporter::writeTestCase(TestCaseNode const &testCaseNode) { + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const &rootSection = *testCaseNode.children.front(); + writeSection("", rootSection, testCaseNode.value.testInfo->okToFail()); + } + + void SonarQubeReporter::writeSection(std::string const &rootName, SectionNode const §ionNode, bool okToFail) { + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + '/' + name; + + if (sectionNode.stats.assertions.total() > 0 + || !sectionNode.stdOut.empty() + || !sectionNode.stdErr.empty()) { + XmlWriter::ScopedElement e = xml.scopedElement("testCase"); + xml.writeAttribute("name"_sr, name); + xml.writeAttribute("duration"_sr, static_cast(sectionNode.stats.durationInSeconds * 1000)); + + writeAssertions(sectionNode, okToFail); + } + + for (auto const &childNode : sectionNode.childSections) + writeSection(name, *childNode, okToFail); + } + + void SonarQubeReporter::writeAssertions(SectionNode const §ionNode, bool okToFail) { + for (auto const &assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { + if (assertionOrBenchmark.isAssertion()) { + writeAssertion(assertionOrBenchmark.asAssertion(), okToFail); + } + } + } + + void SonarQubeReporter::writeAssertion(AssertionStats const &stats, bool okToFail) { + AssertionResult const &result = stats.assertionResult; + if (!result.isOk() || result.getResultType() == ResultWas::ExplicitSkip) { + std::string elementName; + if (okToFail) { + elementName = "skipped"; + } else { + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + case ResultWas::ExplicitSkip: + elementName = "skipped"; + break; + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; } - } + } - void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) { - CumulativeReporterBase::testRunStarting(testRunInfo); + XmlWriter::ScopedElement e = xml.scopedElement(elementName); - xml.writeComment( createMetadataString( *m_config ) ); - xml.startElement("testExecutions"); - xml.writeAttribute("version"_sr, '1'); - } + ReusableStringStream messageRss; + messageRss << result.getTestMacroName() << '(' << result.getExpression() << ')'; + xml.writeAttribute("message"_sr, messageRss.str()); - void SonarQubeReporter::writeRun( TestRunNode const& runNode ) { - std::map> testsPerFile; - - for ( auto const& child : runNode.children ) { - testsPerFile[child->value.testInfo->lineInfo.file].push_back( - child.get() ); + ReusableStringStream textRss; + if (result.getResultType() == ResultWas::ExplicitSkip) { + textRss << "SKIPPED\n"; + } else { + textRss << "FAILED:\n"; + if (result.hasExpression()) { + textRss << '\t' << result.getExpressionInMacro() << '\n'; } - - for ( auto const& kv : testsPerFile ) { - writeTestFile( kv.first, kv.second ); - } - } - - void SonarQubeReporter::writeTestFile(StringRef filename, std::vector const& testCaseNodes) { - XmlWriter::ScopedElement e = xml.scopedElement("file"); - xml.writeAttribute("path"_sr, filename); - - for (auto const& child : testCaseNodes) - writeTestCase(*child); - } - - void SonarQubeReporter::writeTestCase(TestCaseNode const& testCaseNode) { - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert(testCaseNode.children.size() == 1); - SectionNode const& rootSection = *testCaseNode.children.front(); - writeSection("", rootSection, testCaseNode.value.testInfo->okToFail()); - } - - void SonarQubeReporter::writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) { - std::string name = trim(sectionNode.stats.sectionInfo.name); - if (!rootName.empty()) - name = rootName + '/' + name; - - if ( sectionNode.stats.assertions.total() > 0 - || !sectionNode.stdOut.empty() - || !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement("testCase"); - xml.writeAttribute("name"_sr, name); - xml.writeAttribute("duration"_sr, static_cast(sectionNode.stats.durationInSeconds * 1000)); - - writeAssertions(sectionNode, okToFail); - } - - for (auto const& childNode : sectionNode.childSections) - writeSection(name, *childNode, okToFail); - } - - void SonarQubeReporter::writeAssertions(SectionNode const& sectionNode, bool okToFail) { - for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { - if (assertionOrBenchmark.isAssertion()) { - writeAssertion(assertionOrBenchmark.asAssertion(), okToFail); - } - } - } - - void SonarQubeReporter::writeAssertion(AssertionStats const& stats, bool okToFail) { - AssertionResult const& result = stats.assertionResult; - if ( !result.isOk() || - result.getResultType() == ResultWas::ExplicitSkip ) { - std::string elementName; - if (okToFail) { - elementName = "skipped"; - } else { - switch (result.getResultType()) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - case ResultWas::ExpressionFailed: - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - case ResultWas::ExplicitSkip: - elementName = "skipped"; - break; - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } - } - - XmlWriter::ScopedElement e = xml.scopedElement(elementName); - - ReusableStringStream messageRss; - messageRss << result.getTestMacroName() << '(' << result.getExpression() << ')'; - xml.writeAttribute("message"_sr, messageRss.str()); - - ReusableStringStream textRss; - if ( result.getResultType() == ResultWas::ExplicitSkip ) { - textRss << "SKIPPED\n"; - } else { - textRss << "FAILED:\n"; - if (result.hasExpression()) { - textRss << '\t' << result.getExpressionInMacro() << '\n'; - } - if (result.hasExpandedExpression()) { - textRss << "with expansion:\n\t" << result.getExpandedExpression() << '\n'; - } - } - - if (result.hasMessage()) - textRss << result.getMessage() << '\n'; - - for (auto const& msg : stats.infoMessages) - if (msg.type == ResultWas::Info) - textRss << msg.message << '\n'; - - textRss << "at " << result.getSourceInfo(); - xml.writeText(textRss.str(), XmlFormatting::Newline); + if (result.hasExpandedExpression()) { + textRss << "with expansion:\n\t" << result.getExpandedExpression() << '\n'; } + } + + if (result.hasMessage()) + textRss << result.getMessage() << '\n'; + + for (auto const &msg : stats.infoMessages) + if (msg.type == ResultWas::Info) + textRss << msg.message << '\n'; + + textRss << "at " << result.getSourceInfo(); + xml.writeText(textRss.str(), XmlFormatting::Newline); } + } } // end namespace Catch - - namespace Catch { - StreamingReporterBase::~StreamingReporterBase() = default; + StreamingReporterBase::~StreamingReporterBase() = default; - void - StreamingReporterBase::testRunStarting( TestRunInfo const& _testRunInfo ) { - currentTestRunInfo = _testRunInfo; - } + void + StreamingReporterBase::testRunStarting(TestRunInfo const &_testRunInfo) { + currentTestRunInfo = _testRunInfo; + } - void StreamingReporterBase::testRunEnded( TestRunStats const& ) { - currentTestCaseInfo = nullptr; - } + void StreamingReporterBase::testRunEnded(TestRunStats const &) { + currentTestCaseInfo = nullptr; + } } // end namespace Catch - - #include #include namespace Catch { - namespace { - // Yes, this has to be outside the class and namespaced by naming. - // Making older compiler happy is hard. - static constexpr StringRef tapFailedString = "not ok"_sr; - static constexpr StringRef tapPassedString = "ok"_sr; - static constexpr Colour::Code tapDimColour = Colour::FileName; + namespace { + // Yes, this has to be outside the class and namespaced by naming. + // Making older compiler happy is hard. + static constexpr StringRef tapFailedString = "not ok"_sr; + static constexpr StringRef tapPassedString = "ok"_sr; + static constexpr Colour::Code tapDimColour = Colour::FileName; - class TapAssertionPrinter { - public: - TapAssertionPrinter& operator= (TapAssertionPrinter const&) = delete; - TapAssertionPrinter(TapAssertionPrinter const&) = delete; - TapAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter, ColourImpl* colour_) - : stream(_stream) - , result(_stats.assertionResult) - , messages(_stats.infoMessages) - , itMessage(_stats.infoMessages.begin()) - , printInfoMessages(true) - , counter(_counter) - , colourImpl( colour_ ) {} + class TapAssertionPrinter { + public: + TapAssertionPrinter &operator=(TapAssertionPrinter const &) = delete; + TapAssertionPrinter(TapAssertionPrinter const &) = delete; + TapAssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, std::size_t _counter, ColourImpl *colour_) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(true) + , counter(_counter) + , colourImpl(colour_) {} - void print() { - itMessage = messages.begin(); + void print() { + itMessage = messages.begin(); - switch (result.getResultType()) { - case ResultWas::Ok: - printResultType(tapPassedString); - printOriginalExpression(); - printReconstructedExpression(); - if (!result.hasExpression()) - printRemainingMessages(Colour::None); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if (result.isOk()) { - printResultType(tapPassedString); - } else { - printResultType(tapFailedString); - } - printOriginalExpression(); - printReconstructedExpression(); - if (result.isOk()) { - printIssue(" # TODO"); - } - printRemainingMessages(); - break; - case ResultWas::ThrewException: - printResultType(tapFailedString); - printIssue("unexpected exception with message:"_sr); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition: - printResultType(tapFailedString); - printIssue("fatal error condition with message:"_sr); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException: - printResultType(tapFailedString); - printIssue("expected exception, got none"_sr); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info: - printResultType("info"_sr); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning: - printResultType("warning"_sr); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure: - printResultType(tapFailedString); - printIssue("explicitly"_sr); - printRemainingMessages(Colour::None); - break; - case ResultWas::ExplicitSkip: - printResultType(tapPassedString); - printIssue(" # SKIP"_sr); - printMessage(); - printRemainingMessages(); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - printResultType("** internal error **"_sr); - break; - } + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(tapPassedString); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + printResultType(tapPassedString); + } else { + printResultType(tapFailedString); } - - private: - void printResultType(StringRef passOrFail) const { - if (!passOrFail.empty()) { - stream << passOrFail << ' ' << counter << " -"; - } + printOriginalExpression(); + printReconstructedExpression(); + if (result.isOk()) { + printIssue(" # TODO"); } - - void printIssue(StringRef issue) const { - stream << ' ' << issue; - } - - void printExpressionWas() { - if (result.hasExpression()) { - stream << ';'; - stream << colourImpl->guardColour( tapDimColour ) - << " expression was:"; - printOriginalExpression(); - } - } - - void printOriginalExpression() const { - if (result.hasExpression()) { - stream << ' ' << result.getExpression(); - } - } - - void printReconstructedExpression() const { - if (result.hasExpandedExpression()) { - stream << colourImpl->guardColour( tapDimColour ) << " for: "; - - std::string expr = result.getExpandedExpression(); - std::replace(expr.begin(), expr.end(), '\n', ' '); - stream << expr; - } - } - - void printMessage() { - if (itMessage != messages.end()) { - stream << " '" << itMessage->message << '\''; - ++itMessage; - } - } - - void printRemainingMessages(Colour::Code colour = tapDimColour) { - if (itMessage == messages.end()) { - return; - } - - // using messages.end() directly (or auto) yields compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(itEnd - itMessage); - - stream << colourImpl->guardColour( colour ) << " with " - << pluralise( N, "message"_sr ) << ':'; - - for (; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || itMessage->type != ResultWas::Info) { - stream << " '" << itMessage->message << '\''; - if (++itMessage != itEnd) { - stream << colourImpl->guardColour(tapDimColour) << " and"; - } - } - } - } - - private: - std::ostream& stream; - AssertionResult const& result; - std::vector const& messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - std::size_t counter; - ColourImpl* colourImpl; - }; - - } // End anonymous namespace - - void TAPReporter::testRunStarting( TestRunInfo const& ) { - if ( m_config->testSpec().hasFilters() ) { - m_stream << "# filters: " << m_config->testSpec() << '\n'; + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(tapFailedString); + printIssue("unexpected exception with message:"_sr); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(tapFailedString); + printIssue("fatal error condition with message:"_sr); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(tapFailedString); + printIssue("expected exception, got none"_sr); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType("info"_sr); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType("warning"_sr); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(tapFailedString); + printIssue("explicitly"_sr); + printRemainingMessages(Colour::None); + break; + case ResultWas::ExplicitSkip: + printResultType(tapPassedString); + printIssue(" # SKIP"_sr); + printMessage(); + printRemainingMessages(); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType("** internal error **"_sr); + break; } - m_stream << "# rng-seed: " << m_config->rngSeed() << '\n' - << std::flush; - } + } - void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) { - m_stream << "# No test cases matched '" << unmatchedSpec << "'\n"; - } - - void TAPReporter::assertionEnded(AssertionStats const& _assertionStats) { - ++counter; - - m_stream << "# " << currentTestCaseInfo->name << '\n'; - TapAssertionPrinter printer(m_stream, _assertionStats, counter, m_colour.get()); - printer.print(); - - m_stream << '\n' << std::flush; - } - - void TAPReporter::testRunEnded(TestRunStats const& _testRunStats) { - m_stream << "1.." << _testRunStats.totals.assertions.total(); - if (_testRunStats.totals.testCases.total() == 0) { - m_stream << " # Skipped: No tests ran."; + private: + void printResultType(StringRef passOrFail) const { + if (!passOrFail.empty()) { + stream << passOrFail << ' ' << counter << " -"; } - m_stream << "\n\n" << std::flush; - StreamingReporterBase::testRunEnded(_testRunStats); + } + + void printIssue(StringRef issue) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + stream << colourImpl->guardColour(tapDimColour) + << " expression was:"; + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << colourImpl->guardColour(tapDimColour) << " for: "; + + std::string expr = result.getExpandedExpression(); + std::replace(expr.begin(), expr.end(), '\n', ' '); + stream << expr; + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = tapDimColour) { + if (itMessage == messages.end()) { + return; + } + + // using messages.end() directly (or auto) yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast(itEnd - itMessage); + + stream << colourImpl->guardColour(colour) << " with " + << pluralise(N, "message"_sr) << ':'; + + for (; itMessage != itEnd;) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << '\''; + if (++itMessage != itEnd) { + stream << colourImpl->guardColour(tapDimColour) << " and"; + } + } + } + } + + private: + std::ostream &stream; + AssertionResult const &result; + std::vector const &messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + std::size_t counter; + ColourImpl *colourImpl; + }; + + } // End anonymous namespace + + void TAPReporter::testRunStarting(TestRunInfo const &) { + if (m_config->testSpec().hasFilters()) { + m_stream << "# filters: " << m_config->testSpec() << '\n'; } + m_stream << "# rng-seed: " << m_config->rngSeed() << '\n' + << std::flush; + } + void TAPReporter::noMatchingTestCases(StringRef unmatchedSpec) { + m_stream << "# No test cases matched '" << unmatchedSpec << "'\n"; + } + void TAPReporter::assertionEnded(AssertionStats const &_assertionStats) { + ++counter; + m_stream << "# " << currentTestCaseInfo->name << '\n'; + TapAssertionPrinter printer(m_stream, _assertionStats, counter, m_colour.get()); + printer.print(); + + m_stream << '\n' + << std::flush; + } + + void TAPReporter::testRunEnded(TestRunStats const &_testRunStats) { + m_stream << "1.." << _testRunStats.totals.assertions.total(); + if (_testRunStats.totals.testCases.total() == 0) { + m_stream << " # Skipped: No tests ran."; + } + m_stream << "\n\n" + << std::flush; + StreamingReporterBase::testRunEnded(_testRunStats); + } } // end namespace Catch - - - #include #include namespace Catch { - namespace { - // if string has a : in first line will set indent to follow it on - // subsequent lines - void printHeaderString(std::ostream& os, std::string const& _string, std::size_t indent = 0) { - std::size_t i = _string.find(": "); - if (i != std::string::npos) - i += 2; - else - i = 0; - os << TextFlow::Column(_string) - .indent(indent + i) - .initialIndent(indent) << '\n'; - } - - std::string escape(StringRef str) { - std::string escaped = static_cast(str); - replaceInPlace(escaped, "|", "||"); - replaceInPlace(escaped, "'", "|'"); - replaceInPlace(escaped, "\n", "|n"); - replaceInPlace(escaped, "\r", "|r"); - replaceInPlace(escaped, "[", "|["); - replaceInPlace(escaped, "]", "|]"); - return escaped; - } - } // end anonymous namespace - - - TeamCityReporter::~TeamCityReporter() = default; - - void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { - m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name ) - << "']\n"; + namespace { + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::ostream &os, std::string const &_string, std::size_t indent = 0) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + os << TextFlow::Column(_string) + .indent(indent + i) + .initialIndent(indent) + << '\n'; } - void TeamCityReporter::testRunEnded( TestRunStats const& runStats ) { - m_stream << "##teamcity[testSuiteFinished name='" - << escape( runStats.runInfo.name ) << "']\n"; + std::string escape(StringRef str) { + std::string escaped = static_cast(str); + replaceInPlace(escaped, "|", "||"); + replaceInPlace(escaped, "'", "|'"); + replaceInPlace(escaped, "\n", "|n"); + replaceInPlace(escaped, "\r", "|r"); + replaceInPlace(escaped, "[", "|["); + replaceInPlace(escaped, "]", "|]"); + return escaped; + } + } // end anonymous namespace + + TeamCityReporter::~TeamCityReporter() = default; + + void TeamCityReporter::testRunStarting(TestRunInfo const &runInfo) { + m_stream << "##teamcity[testSuiteStarted name='" << escape(runInfo.name) + << "']\n"; + } + + void TeamCityReporter::testRunEnded(TestRunStats const &runStats) { + m_stream << "##teamcity[testSuiteFinished name='" + << escape(runStats.runInfo.name) << "']\n"; + } + + void TeamCityReporter::assertionEnded(AssertionStats const &assertionStats) { + AssertionResult const &result = assertionStats.assertionResult; + if (!result.isOk() || result.getResultType() == ResultWas::ExplicitSkip) { + ReusableStringStream msg; + if (!m_headerPrintedForThisSection) + printSectionHeader(msg.get()); + m_headerPrintedForThisSection = true; + + msg << result.getSourceInfo() << '\n'; + + switch (result.getResultType()) { + case ResultWas::ExpressionFailed: + msg << "expression failed"; + break; + case ResultWas::ThrewException: + msg << "unexpected exception"; + break; + case ResultWas::FatalErrorCondition: + msg << "fatal error condition"; + break; + case ResultWas::DidntThrowException: + msg << "no exception was thrown where one was expected"; + break; + case ResultWas::ExplicitFailure: + msg << "explicit failure"; + break; + case ResultWas::ExplicitSkip: + msg << "explicit skip"; + break; + + // We shouldn't get here because of the isOk() test + case ResultWas::Ok: + case ResultWas::Info: + case ResultWas::Warning: + CATCH_ERROR("Internal error in TeamCity reporter"); + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + CATCH_ERROR("Not implemented"); + } + if (assertionStats.infoMessages.size() == 1) + msg << " with message:"; + if (assertionStats.infoMessages.size() > 1) + msg << " with messages:"; + for (auto const &messageInfo : assertionStats.infoMessages) + msg << "\n \"" << messageInfo.message << '"'; + + if (result.hasExpression()) { + msg << "\n " << result.getExpressionInMacro() << "\n" + "with expansion:\n" + " " + << result.getExpandedExpression() << '\n'; + } + + if (result.getResultType() == ResultWas::ExplicitSkip) { + m_stream << "##teamcity[testIgnored"; + } else if (currentTestCaseInfo->okToFail()) { + msg << "- failure ignore as test marked as 'ok to fail'\n"; + m_stream << "##teamcity[testIgnored"; + } else { + m_stream << "##teamcity[testFailed"; + } + m_stream << " name='" << escape(currentTestCaseInfo->name) << '\'' + << " message='" << escape(msg.str()) << '\'' << "]\n"; + } + m_stream.flush(); + } + + void TeamCityReporter::testCaseStarting(TestCaseInfo const &testInfo) { + m_testTimer.start(); + StreamingReporterBase::testCaseStarting(testInfo); + m_stream << "##teamcity[testStarted name='" + << escape(testInfo.name) << "']\n"; + m_stream.flush(); + } + + void TeamCityReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + StreamingReporterBase::testCaseEnded(testCaseStats); + auto const &testCaseInfo = *testCaseStats.testInfo; + if (!testCaseStats.stdOut.empty()) + m_stream << "##teamcity[testStdOut name='" + << escape(testCaseInfo.name) + << "' out='" << escape(testCaseStats.stdOut) << "']\n"; + if (!testCaseStats.stdErr.empty()) + m_stream << "##teamcity[testStdErr name='" + << escape(testCaseInfo.name) + << "' out='" << escape(testCaseStats.stdErr) << "']\n"; + m_stream << "##teamcity[testFinished name='" + << escape(testCaseInfo.name) << "' duration='" + << m_testTimer.getElapsedMilliseconds() << "']\n"; + m_stream.flush(); + } + + void TeamCityReporter::printSectionHeader(std::ostream &os) { + assert(!m_sectionStack.empty()); + + if (m_sectionStack.size() > 1) { + os << lineOfChars('-') << '\n'; + + std::vector::const_iterator + it + = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(os, it->name); + os << lineOfChars('-') << '\n'; } - void TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) { - AssertionResult const& result = assertionStats.assertionResult; - if ( !result.isOk() || - result.getResultType() == ResultWas::ExplicitSkip ) { + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; - ReusableStringStream msg; - if (!m_headerPrintedForThisSection) - printSectionHeader(msg.get()); - m_headerPrintedForThisSection = true; - - msg << result.getSourceInfo() << '\n'; - - switch (result.getResultType()) { - case ResultWas::ExpressionFailed: - msg << "expression failed"; - break; - case ResultWas::ThrewException: - msg << "unexpected exception"; - break; - case ResultWas::FatalErrorCondition: - msg << "fatal error condition"; - break; - case ResultWas::DidntThrowException: - msg << "no exception was thrown where one was expected"; - break; - case ResultWas::ExplicitFailure: - msg << "explicit failure"; - break; - case ResultWas::ExplicitSkip: - msg << "explicit skip"; - break; - - // We shouldn't get here because of the isOk() test - case ResultWas::Ok: - case ResultWas::Info: - case ResultWas::Warning: - CATCH_ERROR("Internal error in TeamCity reporter"); - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - CATCH_ERROR("Not implemented"); - } - if (assertionStats.infoMessages.size() == 1) - msg << " with message:"; - if (assertionStats.infoMessages.size() > 1) - msg << " with messages:"; - for (auto const& messageInfo : assertionStats.infoMessages) - msg << "\n \"" << messageInfo.message << '"'; - - - if (result.hasExpression()) { - msg << - "\n " << result.getExpressionInMacro() << "\n" - "with expansion:\n" - " " << result.getExpandedExpression() << '\n'; - } - - if ( result.getResultType() == ResultWas::ExplicitSkip ) { - m_stream << "##teamcity[testIgnored"; - } else if ( currentTestCaseInfo->okToFail() ) { - msg << "- failure ignore as test marked as 'ok to fail'\n"; - m_stream << "##teamcity[testIgnored"; - } else { - m_stream << "##teamcity[testFailed"; - } - m_stream << " name='" << escape( currentTestCaseInfo->name ) << '\'' - << " message='" << escape( msg.str() ) << '\'' << "]\n"; - } - m_stream.flush(); - } - - void TeamCityReporter::testCaseStarting(TestCaseInfo const& testInfo) { - m_testTimer.start(); - StreamingReporterBase::testCaseStarting(testInfo); - m_stream << "##teamcity[testStarted name='" - << escape(testInfo.name) << "']\n"; - m_stream.flush(); - } - - void TeamCityReporter::testCaseEnded(TestCaseStats const& testCaseStats) { - StreamingReporterBase::testCaseEnded(testCaseStats); - auto const& testCaseInfo = *testCaseStats.testInfo; - if (!testCaseStats.stdOut.empty()) - m_stream << "##teamcity[testStdOut name='" - << escape(testCaseInfo.name) - << "' out='" << escape(testCaseStats.stdOut) << "']\n"; - if (!testCaseStats.stdErr.empty()) - m_stream << "##teamcity[testStdErr name='" - << escape(testCaseInfo.name) - << "' out='" << escape(testCaseStats.stdErr) << "']\n"; - m_stream << "##teamcity[testFinished name='" - << escape(testCaseInfo.name) << "' duration='" - << m_testTimer.getElapsedMilliseconds() << "']\n"; - m_stream.flush(); - } - - void TeamCityReporter::printSectionHeader(std::ostream& os) { - assert(!m_sectionStack.empty()); - - if (m_sectionStack.size() > 1) { - os << lineOfChars('-') << '\n'; - - std::vector::const_iterator - it = m_sectionStack.begin() + 1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for (; it != itEnd; ++it) - printHeaderString(os, it->name); - os << lineOfChars('-') << '\n'; - } - - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; - - os << lineInfo << '\n'; - os << lineOfChars('.') << "\n\n"; - } + os << lineInfo << '\n'; + os << lineOfChars('.') << "\n\n"; + } } // end namespace Catch - - - #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch - // Note that 4062 (not all labels are handled - // and default is missing) is enabled +#pragma warning(disable : 4061) // Not all labels are EXPLICITLY handled in switch + // Note that 4062 (not all labels are handled + // and default is missing) is enabled #endif namespace Catch { - XmlReporter::XmlReporter( ReporterConfig&& _config ) - : StreamingReporterBase( CATCH_MOVE(_config) ), - m_xml(m_stream) - { - m_preferences.shouldRedirectStdOut = true; - m_preferences.shouldReportAllAssertions = true; - m_preferences.shouldReportAllAssertionStarts = false; + XmlReporter::XmlReporter(ReporterConfig &&_config) + : StreamingReporterBase(CATCH_MOVE(_config)), m_xml(m_stream) { + m_preferences.shouldRedirectStdOut = true; + m_preferences.shouldReportAllAssertions = true; + m_preferences.shouldReportAllAssertionStarts = false; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo(SourceLineInfo const &sourceInfo) { + m_xml + .writeAttribute("filename"_sr, sourceInfo.file) + .writeAttribute("line"_sr, sourceInfo.line); + } + + void XmlReporter::testRunStarting(TestRunInfo const &testInfo) { + StreamingReporterBase::testRunStarting(testInfo); + std::string stylesheetRef = getStylesheetRef(); + if (!stylesheetRef.empty()) + m_xml.writeStylesheetRef(stylesheetRef); + m_xml.startElement("Catch2TestRun") + .writeAttribute("name"_sr, m_config->name()) + .writeAttribute("rng-seed"_sr, m_config->rngSeed()) + .writeAttribute("xml-format-version"_sr, 3) + .writeAttribute("catch2-version"_sr, libraryVersion()); + if (m_config->testSpec().hasFilters()) { + m_xml.writeAttribute("filters"_sr, m_config->testSpec()); } + } - XmlReporter::~XmlReporter() = default; + void XmlReporter::testCaseStarting(TestCaseInfo const &testInfo) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement("TestCase") + .writeAttribute("name"_sr, trim(StringRef(testInfo.name))) + .writeAttribute("tags"_sr, testInfo.tagsAsString()); - std::string XmlReporter::getDescription() { - return "Reports test results as an XML document"; + writeSourceInfo(testInfo.lineInfo); + + if (m_config->showDurations() == ShowDurations::Always) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting(SectionInfo const §ionInfo) { + StreamingReporterBase::sectionStarting(sectionInfo); + if (m_sectionDepth++ > 0) { + m_xml.startElement("Section") + .writeAttribute("name"_sr, trim(StringRef(sectionInfo.name))); + writeSourceInfo(sectionInfo.lineInfo); + m_xml.ensureTagClosed(); } + } - std::string XmlReporter::getStylesheetRef() const { - return std::string(); - } + void XmlReporter::assertionEnded(AssertionStats const &assertionStats) { + AssertionResult const &result = assertionStats.assertionResult; - void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { - m_xml - .writeAttribute( "filename"_sr, sourceInfo.file ) - .writeAttribute( "line"_sr, sourceInfo.line ); - } + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { - StreamingReporterBase::testRunStarting( testInfo ); - std::string stylesheetRef = getStylesheetRef(); - if( !stylesheetRef.empty() ) - m_xml.writeStylesheetRef( stylesheetRef ); - m_xml.startElement("Catch2TestRun") - .writeAttribute("name"_sr, m_config->name()) - .writeAttribute("rng-seed"_sr, m_config->rngSeed()) - .writeAttribute("xml-format-version"_sr, 3) - .writeAttribute("catch2-version"_sr, libraryVersion()); - if ( m_config->testSpec().hasFilters() ) { - m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); + if (includeResults || result.getResultType() == ResultWas::Warning) { + // Print any info messages in tags. + for (auto const &msg : assertionStats.infoMessages) { + if (msg.type == ResultWas::Info && includeResults) { + auto t = m_xml.scopedElement("Info"); + writeSourceInfo(msg.lineInfo); + t.writeText(msg.message); + } else if (msg.type == ResultWas::Warning) { + auto t = m_xml.scopedElement("Warning"); + writeSourceInfo(msg.lineInfo); + t.writeText(msg.message); } + } } - void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ) - .writeAttribute( "name"_sr, trim( StringRef(testInfo.name) ) ) - .writeAttribute( "tags"_sr, testInfo.tagsAsString() ); - - writeSourceInfo( testInfo.lineInfo ); - - if ( m_config->showDurations() == ShowDurations::Always ) - m_testCaseTimer.start(); - m_xml.ensureTagClosed(); + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip) { + return; } - void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { - StreamingReporterBase::sectionStarting( sectionInfo ); - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name"_sr, trim( StringRef(sectionInfo.name) ) ); - writeSourceInfo( sectionInfo.lineInfo ); - m_xml.ensureTagClosed(); - } + // Print the expression if there is one. + if (result.hasExpression()) { + m_xml.startElement("Expression") + .writeAttribute("success"_sr, result.succeeded()) + .writeAttribute("type"_sr, result.getTestMacroName()); + + writeSourceInfo(result.getSourceInfo()); + + m_xml.scopedElement("Original") + .writeText(result.getExpression()); + m_xml.scopedElement("Expanded") + .writeText(result.getExpandedExpression()); } - void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { - - AssertionResult const& result = assertionStats.assertionResult; - - bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - - if( includeResults || result.getResultType() == ResultWas::Warning ) { - // Print any info messages in tags. - for( auto const& msg : assertionStats.infoMessages ) { - if( msg.type == ResultWas::Info && includeResults ) { - auto t = m_xml.scopedElement( "Info" ); - writeSourceInfo( msg.lineInfo ); - t.writeText( msg.message ); - } else if ( msg.type == ResultWas::Warning ) { - auto t = m_xml.scopedElement( "Warning" ); - writeSourceInfo( msg.lineInfo ); - t.writeText( msg.message ); - } - } - } - - // Drop out if result was successful but we're not printing them. - if ( !includeResults && result.getResultType() != ResultWas::Warning && - result.getResultType() != ResultWas::ExplicitSkip ) { - return; - } - - // Print the expression if there is one. - if( result.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success"_sr, result.succeeded() ) - .writeAttribute( "type"_sr, result.getTestMacroName() ); - - writeSourceInfo( result.getSourceInfo() ); - - m_xml.scopedElement( "Original" ) - .writeText( result.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( result.getExpandedExpression() ); - } - - // And... Print a result applicable to each result type. - switch( result.getResultType() ) { - case ResultWas::ThrewException: - m_xml.startElement( "Exception" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::FatalErrorCondition: - m_xml.startElement( "FatalErrorCondition" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( result.getMessage() ); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.startElement( "Failure" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::ExplicitSkip: - m_xml.startElement( "Skip" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - default: - break; - } - - if( result.hasExpression() ) - m_xml.endElement(); - } - - void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { - StreamingReporterBase::sectionEnded( sectionStats ); - if ( --m_sectionDepth > 0 ) { - { - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); - e.writeAttribute( "successes"_sr, sectionStats.assertions.passed ); - e.writeAttribute( "failures"_sr, sectionStats.assertions.failed ); - e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk ); - e.writeAttribute( "skipped"_sr, sectionStats.assertions.skipped > 0 ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds ); - } - // Ends assertion tag - m_xml.endElement(); - } - } - - void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { - StreamingReporterBase::testCaseEnded( testCaseStats ); - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); - e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() ); - e.writeAttribute( "skips"_sr, testCaseStats.totals.assertions.skipped ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() ); - if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline ); - if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline ); - + // And... Print a result applicable to each result type. + switch (result.getResultType()) { + case ResultWas::ThrewException: + m_xml.startElement("Exception"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); m_xml.endElement(); - } - - void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { - StreamingReporterBase::testRunEnded( testRunStats ); - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed ) - .writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk ) - .writeAttribute( "skips"_sr, testRunStats.totals.assertions.skipped ); - m_xml.scopedElement( "OverallResultsCases") - .writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed ) - .writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed ) - .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk ) - .writeAttribute( "skips"_sr, testRunStats.totals.testCases.skipped ); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement("FatalErrorCondition"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); m_xml.endElement(); - } - - void XmlReporter::benchmarkPreparing( StringRef name ) { - m_xml.startElement("BenchmarkResults") - .writeAttribute("name"_sr, name); - } - - void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { - m_xml.writeAttribute("samples"_sr, info.samples) - .writeAttribute("resamples"_sr, info.resamples) - .writeAttribute("iterations"_sr, info.iterations) - .writeAttribute("clockResolution"_sr, info.clockResolution) - .writeAttribute("estimatedDuration"_sr, info.estimatedDuration) - .writeComment("All values in nano seconds"_sr); - } - - void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { - m_xml.scopedElement("mean") - .writeAttribute("value"_sr, benchmarkStats.mean.point.count()) - .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count()) - .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count()) - .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval); - m_xml.scopedElement("standardDeviation") - .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count()) - .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count()) - .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count()) - .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval); - m_xml.scopedElement("outliers") - .writeAttribute("variance"_sr, benchmarkStats.outlierVariance) - .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild) - .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe) - .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild) - .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe); + break; + case ResultWas::Info: + m_xml.scopedElement("Info") + .writeText(result.getMessage()); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement("Failure"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); m_xml.endElement(); - } - - void XmlReporter::benchmarkFailed(StringRef error) { - m_xml.scopedElement("failed"). - writeAttribute("message"_sr, error); + break; + case ResultWas::ExplicitSkip: + m_xml.startElement("Skip"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); m_xml.endElement(); + break; + default: + break; } - void XmlReporter::listReporters(std::vector const& descriptions) { - auto outerTag = m_xml.scopedElement("AvailableReporters"); - for (auto const& reporter : descriptions) { - auto inner = m_xml.scopedElement("Reporter"); - m_xml.startElement("Name", XmlFormatting::Indent) - .writeText(reporter.name, XmlFormatting::None) - .endElement(XmlFormatting::Newline); - m_xml.startElement("Description", XmlFormatting::Indent) - .writeText(reporter.description, XmlFormatting::None) - .endElement(XmlFormatting::Newline); - } - } + if (result.hasExpression()) + m_xml.endElement(); + } - void XmlReporter::listListeners(std::vector const& descriptions) { - auto outerTag = m_xml.scopedElement( "RegisteredListeners" ); - for ( auto const& listener : descriptions ) { - auto inner = m_xml.scopedElement( "Listener" ); - m_xml.startElement( "Name", XmlFormatting::Indent ) - .writeText( listener.name, XmlFormatting::None ) - .endElement( XmlFormatting::Newline ); - m_xml.startElement( "Description", XmlFormatting::Indent ) - .writeText( listener.description, XmlFormatting::None ) - .endElement( XmlFormatting::Newline ); - } - } + void XmlReporter::sectionEnded(SectionStats const §ionStats) { + StreamingReporterBase::sectionEnded(sectionStats); + if (--m_sectionDepth > 0) { + { + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResults"); + e.writeAttribute("successes"_sr, sectionStats.assertions.passed); + e.writeAttribute("failures"_sr, sectionStats.assertions.failed); + e.writeAttribute("expectedFailures"_sr, sectionStats.assertions.failedButOk); + e.writeAttribute("skipped"_sr, sectionStats.assertions.skipped > 0); - void XmlReporter::listTests(std::vector const& tests) { - auto outerTag = m_xml.scopedElement("MatchingTests"); - for (auto const& test : tests) { - auto innerTag = m_xml.scopedElement("TestCase"); - auto const& testInfo = test.getTestCaseInfo(); - m_xml.startElement("Name", XmlFormatting::Indent) - .writeText(testInfo.name, XmlFormatting::None) - .endElement(XmlFormatting::Newline); - m_xml.startElement("ClassName", XmlFormatting::Indent) - .writeText(testInfo.className, XmlFormatting::None) - .endElement(XmlFormatting::Newline); - m_xml.startElement("Tags", XmlFormatting::Indent) - .writeText(testInfo.tagsAsString(), XmlFormatting::None) - .endElement(XmlFormatting::Newline); - - auto sourceTag = m_xml.scopedElement("SourceInfo"); - m_xml.startElement("File", XmlFormatting::Indent) - .writeText(testInfo.lineInfo.file, XmlFormatting::None) - .endElement(XmlFormatting::Newline); - m_xml.startElement("Line", XmlFormatting::Indent) - .writeText(std::to_string(testInfo.lineInfo.line), XmlFormatting::None) - .endElement(XmlFormatting::Newline); - } + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds"_sr, sectionStats.durationInSeconds); + } + // Ends assertion tag + m_xml.endElement(); } + } - void XmlReporter::listTags(std::vector const& tags) { - auto outerTag = m_xml.scopedElement("TagsFromMatchingTests"); - for (auto const& tag : tags) { - auto innerTag = m_xml.scopedElement("Tag"); - m_xml.startElement("Count", XmlFormatting::Indent) - .writeText(std::to_string(tag.count), XmlFormatting::None) - .endElement(XmlFormatting::Newline); - auto aliasTag = m_xml.scopedElement("Aliases"); - for (auto const& alias : tag.spellings) { - m_xml.startElement("Alias", XmlFormatting::Indent) - .writeText(alias, XmlFormatting::None) - .endElement(XmlFormatting::Newline); - } - } + void XmlReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + StreamingReporterBase::testCaseEnded(testCaseStats); + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResult"); + e.writeAttribute("success"_sr, testCaseStats.totals.assertions.allOk()); + e.writeAttribute("skips"_sr, testCaseStats.totals.assertions.skipped); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds()); + if (!testCaseStats.stdOut.empty()) + m_xml.scopedElement("StdOut").writeText(trim(StringRef(testCaseStats.stdOut)), XmlFormatting::Newline); + if (!testCaseStats.stdErr.empty()) + m_xml.scopedElement("StdErr").writeText(trim(StringRef(testCaseStats.stdErr)), XmlFormatting::Newline); + + m_xml.endElement(); + } + + void XmlReporter::testRunEnded(TestRunStats const &testRunStats) { + StreamingReporterBase::testRunEnded(testRunStats); + m_xml.scopedElement("OverallResults") + .writeAttribute("successes"_sr, testRunStats.totals.assertions.passed) + .writeAttribute("failures"_sr, testRunStats.totals.assertions.failed) + .writeAttribute("expectedFailures"_sr, testRunStats.totals.assertions.failedButOk) + .writeAttribute("skips"_sr, testRunStats.totals.assertions.skipped); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes"_sr, testRunStats.totals.testCases.passed) + .writeAttribute("failures"_sr, testRunStats.totals.testCases.failed) + .writeAttribute("expectedFailures"_sr, testRunStats.totals.testCases.failedButOk) + .writeAttribute("skips"_sr, testRunStats.totals.testCases.skipped); + m_xml.endElement(); + } + + void XmlReporter::benchmarkPreparing(StringRef name) { + m_xml.startElement("BenchmarkResults") + .writeAttribute("name"_sr, name); + } + + void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { + m_xml.writeAttribute("samples"_sr, info.samples) + .writeAttribute("resamples"_sr, info.resamples) + .writeAttribute("iterations"_sr, info.iterations) + .writeAttribute("clockResolution"_sr, info.clockResolution) + .writeAttribute("estimatedDuration"_sr, info.estimatedDuration) + .writeComment("All values in nano seconds"_sr); + } + + void XmlReporter::benchmarkEnded(BenchmarkStats<> const &benchmarkStats) { + m_xml.scopedElement("mean") + .writeAttribute("value"_sr, benchmarkStats.mean.point.count()) + .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count()) + .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count()) + .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval); + m_xml.scopedElement("standardDeviation") + .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count()) + .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count()) + .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count()) + .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval); + m_xml.scopedElement("outliers") + .writeAttribute("variance"_sr, benchmarkStats.outlierVariance) + .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild) + .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe) + .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild) + .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe); + m_xml.endElement(); + } + + void XmlReporter::benchmarkFailed(StringRef error) { + m_xml.scopedElement("failed").writeAttribute("message"_sr, error); + m_xml.endElement(); + } + + void XmlReporter::listReporters(std::vector const &descriptions) { + auto outerTag = m_xml.scopedElement("AvailableReporters"); + for (auto const &reporter : descriptions) { + auto inner = m_xml.scopedElement("Reporter"); + m_xml.startElement("Name", XmlFormatting::Indent) + .writeText(reporter.name, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("Description", XmlFormatting::Indent) + .writeText(reporter.description, XmlFormatting::None) + .endElement(XmlFormatting::Newline); } + } + + void XmlReporter::listListeners(std::vector const &descriptions) { + auto outerTag = m_xml.scopedElement("RegisteredListeners"); + for (auto const &listener : descriptions) { + auto inner = m_xml.scopedElement("Listener"); + m_xml.startElement("Name", XmlFormatting::Indent) + .writeText(listener.name, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("Description", XmlFormatting::Indent) + .writeText(listener.description, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + } + } + + void XmlReporter::listTests(std::vector const &tests) { + auto outerTag = m_xml.scopedElement("MatchingTests"); + for (auto const &test : tests) { + auto innerTag = m_xml.scopedElement("TestCase"); + auto const &testInfo = test.getTestCaseInfo(); + m_xml.startElement("Name", XmlFormatting::Indent) + .writeText(testInfo.name, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("ClassName", XmlFormatting::Indent) + .writeText(testInfo.className, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("Tags", XmlFormatting::Indent) + .writeText(testInfo.tagsAsString(), XmlFormatting::None) + .endElement(XmlFormatting::Newline); + + auto sourceTag = m_xml.scopedElement("SourceInfo"); + m_xml.startElement("File", XmlFormatting::Indent) + .writeText(testInfo.lineInfo.file, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + m_xml.startElement("Line", XmlFormatting::Indent) + .writeText(std::to_string(testInfo.lineInfo.line), XmlFormatting::None) + .endElement(XmlFormatting::Newline); + } + } + + void XmlReporter::listTags(std::vector const &tags) { + auto outerTag = m_xml.scopedElement("TagsFromMatchingTests"); + for (auto const &tag : tags) { + auto innerTag = m_xml.scopedElement("Tag"); + m_xml.startElement("Count", XmlFormatting::Indent) + .writeText(std::to_string(tag.count), XmlFormatting::None) + .endElement(XmlFormatting::Newline); + auto aliasTag = m_xml.scopedElement("Aliases"); + for (auto const &alias : tag.spellings) { + m_xml.startElement("Alias", XmlFormatting::Indent) + .writeText(alias, XmlFormatting::None) + .endElement(XmlFormatting::Newline); + } + } + } } // end namespace Catch diff --git a/unittests/catch_amalgamated.hpp b/unittests/catch_amalgamated.hpp index c8503078..6590fde2 100644 --- a/unittests/catch_amalgamated.hpp +++ b/unittests/catch_amalgamated.hpp @@ -18,11 +18,9 @@ #undef max #endif - #ifndef CATCH_AMALGAMATED_HPP_INCLUDED #define CATCH_AMALGAMATED_HPP_INCLUDED - /** \file * This is a convenience header for Catch2. It includes **all** of Catch2 headers. * @@ -40,8 +38,6 @@ #ifndef CATCH_ALL_HPP_INCLUDED #define CATCH_ALL_HPP_INCLUDED - - /** \file * This is a convenience header for Catch2's benchmarking. It includes * **all** of Catch2 headers related to benchmarking. @@ -58,15 +54,11 @@ #ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED #define CATCH_BENCHMARK_ALL_HPP_INCLUDED - - // Adapted from donated nonius code. #ifndef CATCH_BENCHMARK_HPP_INCLUDED #define CATCH_BENCHMARK_HPP_INCLUDED - - #ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED #define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -86,40 +78,37 @@ // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. - - #ifndef CATCH_PLATFORM_HPP_INCLUDED #define CATCH_PLATFORM_HPP_INCLUDED // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# ifndef __has_extension -# define __has_extension(x) 0 -# endif -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif +#ifndef __has_extension +#define __has_extension(x) 0 +#endif +#include +#if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +#define CATCH_PLATFORM_MAC +#elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +#define CATCH_PLATFORM_IPHONE +#endif #elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX +#define CATCH_PLATFORM_LINUX #elif defined(__QNX__) -# define CATCH_PLATFORM_QNX +#define CATCH_PLATFORM_QNX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS +#define CATCH_PLATFORM_WINDOWS -# if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP ) -# define CATCH_PLATFORM_WINDOWS_UWP -# endif +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define CATCH_PLATFORM_WINDOWS_UWP +#endif #elif defined(__ORBIS__) || defined(__PROSPERO__) -# define CATCH_PLATFORM_PLAYSTATION +#define CATCH_PLATFORM_PLAYSTATION #endif @@ -127,104 +116,104 @@ #ifdef __cplusplus -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif +#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define CATCH_CPP17_OR_GREATER +#endif -# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) -# define CATCH_CPP20_OR_GREATER -# endif +#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) +#define CATCH_CPP20_OR_GREATER +#endif #endif // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("GCC diagnostic pop") // This only works on GCC 9+. so we have to also add a global suppression of Wparentheses // for older versions of GCC. -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +#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_RESULT \ + _Pragma("GCC diagnostic ignored \"-Wunused-result\"") -# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) +#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_USELESS_CAST_WARNINGS \ + _Pragma("GCC diagnostic ignored \"-Wuseless-cast\"") -# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ - _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) +#define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma("GCC diagnostic ignored \"-Wshadow\"") -# define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P +#define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P #endif #if defined(__NVCOMPILER) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" ) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" ) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("diag push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("diag pop") +#define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma("diag_suppress declared_but_not_referenced") #endif #if defined(__CUDACC__) && !defined(__clang__) -# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ +#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ // New pragmas introduced in CUDA 11.5+ -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic pop" ) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" ) -# else -# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress 177" ) -# endif +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("nv_diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("nv_diagnostic pop") +#define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma("nv_diag_suppress 177") +#else +#define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma("diag_suppress 177") +#endif #endif // clang-cl defines _MSC_VER as well as __clang__, which could cause the // start/stop internal suppression macros to be double defined. #if defined(__clang__) && !defined(_MSC_VER) -# define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) +#define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("clang diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("clang diagnostic pop") #endif // __clang__ && !_MSC_VER #if defined(__clang__) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ + _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wparentheses\"") -# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wunused-variable\"") -# if (__clang_major__ >= 20) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wvariadic-macro-arguments-omitted\"" ) -# elif (__clang_major__ == 19) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wc++20-extensions\"" ) -# else -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) -# endif +#if (__clang_major__ >= 20) +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wvariadic-macro-arguments-omitted\"") +#elif (__clang_major__ == 19) +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wc++20-extensions\"") +#else +#define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"") +#endif -# if ( __clang_major__ >= 22 ) -# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wc2y-extensions\"" ) -# endif +#if (__clang_major__ >= 22) +#define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wc2y-extensions\"") +#endif -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) +#define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wunused-template\"") -# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) +#define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wcomma\"") -# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) +#define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma("clang diagnostic ignored \"-Wshadow\"") #endif // __clang__ @@ -244,52 +233,42 @@ // https://developer.nvidia.com/nvidia_bug/3321845. // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -#if defined( __ibmxl__ ) || defined( __CUDACC__ ) || defined( __NVCOMPILER ) -# define CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P +#if defined(__ibmxl__) || defined(__CUDACC__) || defined(__NVCOMPILER) +#define CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P #endif - - //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals -#if defined( CATCH_PLATFORM_WINDOWS ) || \ - defined( CATCH_PLATFORM_PLAYSTATION ) || \ - defined( __CYGWIN__ ) || \ - defined( __QNX__ ) || \ - defined( __EMSCRIPTEN__ ) || \ - defined( __DJGPP__ ) || \ - defined( __OS400__ ) -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#if defined(CATCH_PLATFORM_WINDOWS) || defined(CATCH_PLATFORM_PLAYSTATION) || defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) || defined(__OS400__) +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #else -# define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// // Assume that some platforms do not support getenv. -#if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ - defined( CATCH_PLATFORM_PLAYSTATION ) || \ - defined( _GAMING_XBOX ) -# define CATCH_INTERNAL_CONFIG_NO_GETENV +#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION) || defined(_GAMING_XBOX) +#define CATCH_INTERNAL_CONFIG_NO_GETENV #else -# define CATCH_INTERNAL_CONFIG_GETENV +#define CATCH_INTERNAL_CONFIG_GETENV #endif //////////////////////////////////////////////////////////////////////////////// // Android somehow still does not support std::to_string #if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING #endif //////////////////////////////////////////////////////////////////////////////// // Not all Windows environments support SEH properly #if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #endif //////////////////////////////////////////////////////////////////////////////// // PS4 #if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE #endif //////////////////////////////////////////////////////////////////////////////// @@ -298,15 +277,15 @@ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE +#define _BSD_SOURCE // some versions of cygwin (most) do not support std::to_string. Use the libstd check. // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) +#if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +#define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# endif +#endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// @@ -315,57 +294,56 @@ // We want to defer to nvcc-specific warning suppression if we are compiled // with nvcc masquerading for MSVC. -# if !defined( __CUDACC__ ) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - __pragma( warning( push ) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - __pragma( warning( pop ) ) -# endif +#if !defined(__CUDACC__) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + __pragma(warning(push)) +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + __pragma(warning(pop)) +#endif // Suppress MSVC C++ Core Guidelines checker warning 26426: // "Global initializer calls a non-constexpr function (i.22)" -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - __pragma( warning( disable : 26426 ) ) +#define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + __pragma(warning(disable : 26426)) // Universal Windows platform does not support SEH -# if !defined(CATCH_PLATFORM_WINDOWS_UWP) -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif +#if !defined(CATCH_PLATFORM_WINDOWS_UWP) +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +#endif // Only some Windows platform families support the console -# if defined(WINAPI_FAMILY_PARTITION) -# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) -# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 -# endif -# endif +#if defined(WINAPI_FAMILY_PARTITION) +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 +#endif +#endif // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(__clang__) // Handle Clang masquerading for msvc -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL -# endif // __clang__ +#if !defined(__clang__) // Handle Clang masquerading for msvc +#if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +#define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif // MSVC_TRADITIONAL +#endif // __clang__ #endif // _MSC_VER #if defined(_REENTRANT) || defined(_MSC_VER) // Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Check if we are compiled with -fno-exceptions or equivalent #if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif - //////////////////////////////////////////////////////////////////////////////// // Embarcadero C++Build #if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif //////////////////////////////////////////////////////////////////////////////// @@ -374,9 +352,9 @@ // This means that it is detected as Windows, but does not provide // the same set of capabilities as real Windows does. #if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 +#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#define CATCH_INTERNAL_CONFIG_NO_ASYNC +#define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 #endif #if !defined(_GLIBCXX_USE_C99_MATH_TR1) @@ -385,164 +363,156 @@ // Various stdlib support checks that require __has_include #if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif +// Check if string_view is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW +#endif - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +// Check if optional is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +// Check if byte is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#include +#if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) +#define CATCH_INTERNAL_CONFIG_CPP17_BYTE +#endif +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +// Check if variant is available and usable +#if __has_include() && defined(CATCH_CPP17_OR_GREATER) +#if defined(__clang__) && (__clang_major__ < 8) +// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 +// fix should be in clang 8, workaround in libstdc++ 8.2 +#include +#if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +#define CATCH_CONFIG_NO_CPP17_VARIANT +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) +#else +#define CATCH_INTERNAL_CONFIG_CPP17_VARIANT +#endif // defined(__clang__) && (__clang_major__ < 8) +#endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) #endif // defined(__has_include) - #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH +#define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. #if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS +#define CATCH_CONFIG_POSIX_SIGNALS #endif #if defined(CATCH_INTERNAL_CONFIG_GETENV) && !defined(CATCH_INTERNAL_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_GETENV) -# define CATCH_CONFIG_GETENV +#define CATCH_CONFIG_GETENV #endif #if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING +#define CATCH_CONFIG_CPP11_TO_STRING #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL +#define CATCH_CONFIG_CPP17_OPTIONAL #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW +#define CATCH_CONFIG_CPP17_STRING_VIEW #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT +#define CATCH_CONFIG_CPP17_VARIANT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE +#define CATCH_CONFIG_CPP17_BYTE #endif - #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif #if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE +#define CATCH_CONFIG_NEW_CAPTURE #endif -#if !defined( CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED ) && \ - !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) && \ - !defined( CATCH_CONFIG_NO_DISABLE_EXCEPTIONS ) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_DISABLE_EXCEPTIONS) +#define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif #if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN +#define CATCH_CONFIG_POLYFILL_ISNAN #endif -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +#define CATCH_CONFIG_USE_ASYNC #endif #if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#define CATCH_CONFIG_GLOBAL_NEXTAFTER #endif - // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... -#if defined( CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P ) && \ - !defined( CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P ) && !defined(CATCH_CONFIG_USE_BUILTIN_CONSTANT_P) +#if defined(CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P) && !defined(CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P) && !defined(CATCH_CONFIG_USE_BUILTIN_CONSTANT_P) #define CATCH_CONFIG_USE_BUILTIN_CONSTANT_P #endif -#if defined( CATCH_CONFIG_USE_BUILTIN_CONSTANT_P ) && \ - !defined( CATCH_CONFIG_NO_USE_BUILTIN_CONSTANT_P ) -# define CATCH_INTERNAL_IGNORE_BUT_WARN( ... ) \ - (void)__builtin_constant_p( __VA_ARGS__ ) /* NOLINT(cppcoreguidelines-pro-type-vararg, \ - hicpp-vararg) */ +#if defined(CATCH_CONFIG_USE_BUILTIN_CONSTANT_P) && !defined(CATCH_CONFIG_NO_USE_BUILTIN_CONSTANT_P) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) \ + (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, \ + hicpp-vararg) */ #else -# define CATCH_INTERNAL_IGNORE_BUT_WARN( ... ) +#define CATCH_INTERNAL_IGNORE_BUT_WARN(...) #endif // Even if we do not think the compiler has that warning, we still have // to provide a macro that can be used by the code. #if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define 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 +#define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define 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 +#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 +#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 +#if !defined(CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS #endif -#if !defined( CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS ) -# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS +#if !defined(CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS) +#define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS #endif #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif - #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) #define CATCH_CATCH_ALL if ((false)) @@ -557,117 +527,105 @@ #define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif -#if defined( CATCH_PLATFORM_WINDOWS ) && \ - !defined( CATCH_CONFIG_COLOUR_WIN32 ) && \ - !defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \ - !defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 ) -# define CATCH_CONFIG_COLOUR_WIN32 +#if defined(CATCH_PLATFORM_WINDOWS) && !defined(CATCH_CONFIG_COLOUR_WIN32) && !defined(CATCH_CONFIG_NO_COLOUR_WIN32) && !defined(CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32) +#define CATCH_CONFIG_COLOUR_WIN32 #endif -#if defined( CATCH_CONFIG_SHARED_LIBRARY ) && defined( _MSC_VER ) && \ - !defined( CATCH_CONFIG_STATIC ) -# ifdef Catch2_EXPORTS -# define CATCH_EXPORT //__declspec( dllexport ) // not needed -# else -# define CATCH_EXPORT __declspec( dllimport ) -# endif +#if defined(CATCH_CONFIG_SHARED_LIBRARY) && defined(_MSC_VER) && !defined(CATCH_CONFIG_STATIC) +#ifdef Catch2_EXPORTS +#define CATCH_EXPORT //__declspec( dllexport ) // not needed #else -# define CATCH_EXPORT +#define CATCH_EXPORT __declspec(dllimport) +#endif +#else +#define CATCH_EXPORT #endif #endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED - #ifndef CATCH_CONTEXT_HPP_INCLUDED #define CATCH_CONTEXT_HPP_INCLUDED - namespace Catch { - class IResultCapture; - class IConfig; + class IResultCapture; + class IConfig; - class Context { - IConfig const* m_config = nullptr; - IResultCapture* m_resultCapture = nullptr; + class Context { + IConfig const *m_config = nullptr; + IResultCapture *m_resultCapture = nullptr; - CATCH_EXPORT static Context currentContext; - friend Context& getCurrentMutableContext(); - friend Context const& getCurrentContext(); + CATCH_EXPORT static Context currentContext; + friend Context &getCurrentMutableContext(); + friend Context const &getCurrentContext(); - public: - constexpr IResultCapture* getResultCapture() const { - return m_resultCapture; - } - constexpr IConfig const* getConfig() const { return m_config; } - constexpr void setResultCapture( IResultCapture* resultCapture ) { - m_resultCapture = resultCapture; - } - constexpr void setConfig( IConfig const* config ) { m_config = config; } - }; - - Context& getCurrentMutableContext(); - - inline Context const& getCurrentContext() { - return Context::currentContext; + public: + constexpr IResultCapture *getResultCapture() const { + return m_resultCapture; } + constexpr IConfig const *getConfig() const { return m_config; } + constexpr void setResultCapture(IResultCapture *resultCapture) { + m_resultCapture = resultCapture; + } + constexpr void setConfig(IConfig const *config) { m_config = config; } + }; - class SimplePcg32; - SimplePcg32& sharedRng(); -} + Context &getCurrentMutableContext(); + + inline Context const &getCurrentContext() { + return Context::currentContext; + } + + class SimplePcg32; + SimplePcg32 &sharedRng(); +} // namespace Catch #endif // CATCH_CONTEXT_HPP_INCLUDED - #ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #include //! Replacement for std::move with better compile time performance -#define CATCH_MOVE(...) static_cast&&>(__VA_ARGS__) +#define CATCH_MOVE(...) static_cast &&>(__VA_ARGS__) //! Replacement for std::forward with better compile time performance -#define CATCH_FORWARD(...) static_cast(__VA_ARGS__) +#define CATCH_FORWARD(...) static_cast(__VA_ARGS__) #endif // CATCH_MOVE_AND_FORWARD_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{}; - //! Used to signal that the remainder of a test should be skipped - struct TestSkipException {}; + //! Used to signal that an assertion macro failed + struct TestFailureException {}; + //! Used to signal that the remainder of a test should be skipped + struct TestSkipException {}; - /** - * Outlines throwing of `TestFailureException` into a single TU - * - * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. - */ - [[noreturn]] void throw_test_failure_exception(); + /** + * Outlines throwing of `TestFailureException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_failure_exception(); - /** - * Outlines throwing of `TestSkipException` into a single TU - * - * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. - */ - [[noreturn]] void throw_test_skip_exception(); + /** + * Outlines throwing of `TestSkipException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_skip_exception(); } // namespace Catch #endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED - #ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED #define CATCH_UNIQUE_NAME_HPP_INCLUDED - - - /** \file * Wrapper for the CONFIG configuration option * @@ -680,18 +638,14 @@ namespace Catch { #ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED #define CATCH_CONFIG_COUNTER_HPP_INCLUDED - -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER +#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 +#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 // Fixme: Clang 22 has an annoying bug where the localized suppression @@ -700,355 +654,340 @@ namespace Catch { // whole TU. Hopefully Clang 23 fixes this before full release. // As AppleClang does its own thing version-wise, we ignore it // completely. -#if defined( __clang__ ) && ( __clang_major__ >= 22 ) && !defined( __APPLE__ ) +#if defined(__clang__) && (__clang_major__ >= 22) && !defined(__APPLE__) CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS #endif -#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 ) +#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__ ) +#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__ ) +#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 - - #ifndef CATCH_STRINGREF_HPP_INCLUDED #define CATCH_STRINGREF_HPP_INCLUDED - - - #ifndef CATCH_LIFETIMEBOUND_HPP_INCLUDED #define CATCH_LIFETIMEBOUND_HPP_INCLUDED -#if !defined( __has_cpp_attribute ) -# define CATCH_ATTR_LIFETIMEBOUND -#elif __has_cpp_attribute( msvc::lifetimebound ) -# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]] -#elif __has_cpp_attribute( clang::lifetimebound ) -# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] -#elif __has_cpp_attribute( lifetimebound ) -# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]] +#if !defined(__has_cpp_attribute) +#define CATCH_ATTR_LIFETIMEBOUND +#elif __has_cpp_attribute(msvc::lifetimebound) +#define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]] +#elif __has_cpp_attribute(clang::lifetimebound) +#define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]] +#elif __has_cpp_attribute(lifetimebound) +#define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]] #else -# define CATCH_ATTR_LIFETIMEBOUND +#define CATCH_ATTR_LIFETIMEBOUND #endif #endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED -#include -#include -#include #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*; + /// 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 *; - static constexpr size_type npos{ static_cast( -1 ) }; + static constexpr size_type npos{static_cast(-1)}; - private: - static constexpr char const* const s_empty = ""; + private: + static constexpr char const *const s_empty = ""; - char const* m_start = s_empty; - size_type m_size = 0; + char const *m_start = s_empty; + size_type m_size = 0; - public: // construction - constexpr StringRef() noexcept = default; + public: // construction + constexpr StringRef() noexcept = default; - StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept; + StringRef(char const *rawChars CATCH_ATTR_LIFETIMEBOUND) noexcept; - constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND, - size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} + constexpr StringRef(char const *rawChars CATCH_ATTR_LIFETIMEBOUND, + size_type size) noexcept + : m_start(rawChars), m_size(size) {} - StringRef( - std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} + StringRef( + std::string const &stdString CATCH_ATTR_LIFETIMEBOUND) 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, 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 CATCH_ATTR_LIFETIMEBOUND { - 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 rhs); - friend std::ostream& operator << (std::ostream& os, StringRef str); - 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 ); + 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, 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 CATCH_ATTR_LIFETIMEBOUND { + 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 rhs); + friend std::ostream &operator<<(std::ostream &os, StringRef str); + 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 ); +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 #define CATCH_RESULT_TYPE_HPP_INCLUDED namespace Catch { - // ResultWas::OfType enum - struct ResultWas { enum OfType { - Unknown = -1, - Ok = 0, - Info = 1, - Warning = 2, - // TODO: Should explicit skip be considered "not OK" (cf. isOk)? I.e., should it have the failure bit? - ExplicitSkip = 4, + // ResultWas::OfType enum + struct ResultWas { + enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + // TODO: Should explicit skip be considered "not OK" (cf. isOk)? I.e., should it have the failure bit? + ExplicitSkip = 4, - FailureBit = 0x10, + FailureBit = 0x10, - ExpressionFailed = FailureBit | 1, - ExplicitFailure = FailureBit | 2, + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, - Exception = 0x100 | FailureBit, + Exception = 0x100 | FailureBit, - ThrewException = Exception | 1, - DidntThrowException = Exception | 2, + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, - FatalErrorCondition = 0x200 | FailureBit + FatalErrorCondition = 0x200 | FailureBit - }; }; + }; + }; - constexpr bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - constexpr bool isJustInfo( int flags ) { return flags == ResultWas::Info; } + constexpr bool isOk(ResultWas::OfType resultType) { + return (resultType & ResultWas::FailureBit) == 0; + } + constexpr bool isJustInfo(int flags) { + return flags == ResultWas::Info; + } + // ResultDisposition::Flags enum + struct ResultDisposition { + enum Flags { + Normal = 0x01, - // ResultDisposition::Flags enum - struct ResultDisposition { enum Flags { - Normal = 0x01, + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; + }; - ContinueOnFailure = 0x02, // Failures fail test, but execution continues - FalseTest = 0x04, // Prefix expression with ! - SuppressFail = 0x08 // Failures are reported but do not fail the test - }; }; + constexpr ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, + ResultDisposition::Flags rhs) { + return static_cast(static_cast(lhs) | static_cast(rhs)); + } - constexpr ResultDisposition::Flags operator|( ResultDisposition::Flags lhs, - ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | - static_cast( rhs ) ); - } - - constexpr bool isFalseTest( int flags ) { - return ( flags & ResultDisposition::FalseTest ) != 0; - } - constexpr bool shouldSuppressFailure( int flags ) { - return ( flags & ResultDisposition::SuppressFail ) != 0; - } + constexpr bool isFalseTest(int flags) { + return (flags & ResultDisposition::FalseTest) != 0; + } + constexpr bool shouldSuppressFailure(int flags) { + return (flags & ResultDisposition::SuppressFail) != 0; + } } // end namespace Catch #endif // CATCH_RESULT_TYPE_HPP_INCLUDED - #ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED #define CATCH_UNIQUE_PTR_HPP_INCLUDED #include #include - namespace Catch { -namespace Detail { + namespace Detail { /** * A reimplementation of `std::unique_ptr` for improved compilation performance * * Does not support arrays nor custom deleters. */ - template + template class unique_ptr { - T* m_ptr; + T *m_ptr; + public: - constexpr unique_ptr(std::nullptr_t = nullptr): - m_ptr{} - {} - explicit constexpr unique_ptr(T* ptr): - m_ptr(ptr) - {} + constexpr unique_ptr(std::nullptr_t = nullptr) + : m_ptr{} {} + explicit constexpr unique_ptr(T *ptr) + : m_ptr(ptr) {} - template ::value>> - unique_ptr(unique_ptr&& from): - m_ptr(from.release()) - {} + template::value>> + unique_ptr(unique_ptr &&from) + : m_ptr(from.release()) {} - template ::value>> - unique_ptr& operator=(unique_ptr&& from) { - reset(from.release()); + template::value>> + unique_ptr &operator=(unique_ptr &&from) { + reset(from.release()); - return *this; - } + return *this; + } - unique_ptr(unique_ptr const&) = delete; - unique_ptr& operator=(unique_ptr const&) = delete; + unique_ptr(unique_ptr const &) = delete; + unique_ptr &operator=(unique_ptr const &) = delete; - unique_ptr(unique_ptr&& rhs) noexcept: - m_ptr(rhs.m_ptr) { - rhs.m_ptr = nullptr; - } - unique_ptr& operator=(unique_ptr&& rhs) noexcept { - reset(rhs.release()); + unique_ptr(unique_ptr &&rhs) noexcept + : m_ptr(rhs.m_ptr) { + rhs.m_ptr = nullptr; + } + unique_ptr &operator=(unique_ptr &&rhs) noexcept { + reset(rhs.release()); - return *this; - } + return *this; + } - ~unique_ptr() { - delete m_ptr; - } + ~unique_ptr() { + delete m_ptr; + } - T& operator*() { - assert(m_ptr); - return *m_ptr; - } - T const& operator*() const { - assert(m_ptr); - return *m_ptr; - } - T* operator->() noexcept { - assert(m_ptr); - return m_ptr; - } - T const* operator->() const noexcept { - assert(m_ptr); - return m_ptr; - } + T &operator*() { + assert(m_ptr); + return *m_ptr; + } + T const &operator*() const { + assert(m_ptr); + return *m_ptr; + } + T *operator->() noexcept { + assert(m_ptr); + return m_ptr; + } + T const *operator->() const noexcept { + assert(m_ptr); + return m_ptr; + } - T* get() { return m_ptr; } - T const* get() const { return m_ptr; } + T *get() { return m_ptr; } + T const *get() const { return m_ptr; } - void reset(T* ptr = nullptr) { - delete m_ptr; - m_ptr = ptr; - } + void reset(T *ptr = nullptr) { + delete m_ptr; + m_ptr = ptr; + } - T* release() { - auto temp = m_ptr; - m_ptr = nullptr; - return temp; - } + T *release() { + auto temp = m_ptr; + m_ptr = nullptr; + return temp; + } - explicit operator bool() const { - return m_ptr != nullptr; - } + explicit operator bool() const { + return m_ptr != nullptr; + } - friend void swap(unique_ptr& lhs, unique_ptr& rhs) { - auto temp = lhs.m_ptr; - lhs.m_ptr = rhs.m_ptr; - rhs.m_ptr = temp; - } + friend void swap(unique_ptr &lhs, unique_ptr &rhs) { + auto temp = lhs.m_ptr; + lhs.m_ptr = rhs.m_ptr; + rhs.m_ptr = temp; + } }; //! Specialization to cause compile-time error for arrays - template + template class unique_ptr; - template - unique_ptr make_unique(Args&&... args) { - return unique_ptr(new T(CATCH_FORWARD(args)...)); + template + unique_ptr make_unique(Args &&...args) { + return unique_ptr(new T(CATCH_FORWARD(args)...)); } - -} // end namespace Detail + } // end namespace Detail } // end namespace Catch #endif // CATCH_UNIQUE_PTR_HPP_INCLUDED - #ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED namespace Catch { - namespace Detail { - struct DummyTemplateArgPlaceholder; - } + namespace Detail { + struct DummyTemplateArgPlaceholder; + } - // 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 dependencies - template - struct BenchmarkStats; + // 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 dependencies + template + struct BenchmarkStats; } // end namespace Catch @@ -1056,131 +995,128 @@ namespace Catch { namespace Catch { - class AssertionResult; - struct AssertionInfo; - struct SectionInfo; - struct SectionEndInfo; - struct MessageInfo; - struct MessageBuilder; - struct Counts; - struct AssertionReaction; - struct SourceLineInfo; + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct AssertionReaction; + struct SourceLineInfo; - class ITransientExpression; - class IGeneratorTracker; + class ITransientExpression; + class IGeneratorTracker; - struct BenchmarkInfo; + struct BenchmarkInfo; - namespace Generators { - class GeneratorUntypedBase; - using GeneratorBasePtr = Catch::Detail::unique_ptr; + namespace Generators { + class GeneratorUntypedBase; + using GeneratorBasePtr = Catch::Detail::unique_ptr; + } // namespace Generators + + class IResultCapture { + public: + virtual ~IResultCapture(); + + virtual void notifyAssertionStarted(AssertionInfo const &info) = 0; + virtual bool sectionStarted(StringRef sectionName, + SourceLineInfo const §ionLineInfo, + Counts &assertions) + = 0; + virtual void sectionEnded(SectionEndInfo &&endInfo) = 0; + virtual void sectionEndedEarly(SectionEndInfo &&endInfo) = 0; + + virtual IGeneratorTracker * + acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) + = 0; + virtual IGeneratorTracker * + createGeneratorTracker(StringRef generatorName, + SourceLineInfo lineInfo, + Generators::GeneratorBasePtr &&generator) + = 0; + + virtual void benchmarkPreparing(StringRef name) = 0; + virtual void benchmarkStarting(BenchmarkInfo const &info) = 0; + virtual void benchmarkEnded(BenchmarkStats<> const &stats) = 0; + virtual void benchmarkFailed(StringRef error) = 0; + + static void pushScopedMessage(MessageInfo &&message); + static void popScopedMessage(unsigned int messageId); + static void addUnscopedMessage(MessageInfo &&message); + static void emplaceUnscopedMessage(MessageBuilder &&builder); + + virtual void handleFatalErrorCondition(StringRef message) = 0; + + virtual void handleExpr(AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction) + = 0; + virtual void handleMessage(AssertionInfo const &info, + ResultWas::OfType resultType, + std::string &&message, + AssertionReaction &reaction) + = 0; + virtual void handleUnexpectedExceptionNotThrown(AssertionInfo const &info, + AssertionReaction &reaction) + = 0; + virtual void handleUnexpectedInflightException(AssertionInfo const &info, + std::string &&message, + AssertionReaction &reaction) + = 0; + virtual void handleIncomplete(AssertionInfo const &info) = 0; + virtual void handleNonExpr(AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction) + = 0; + + virtual bool lastAssertionPassed() = 0; + + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult *getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + namespace Detail { + [[noreturn]] + void missingCaptureInstance(); + } + inline IResultCapture &getResultCapture() { + if (auto *capture = getCurrentContext().getResultCapture()) { + return *capture; + } else { + Detail::missingCaptureInstance(); } + } - - class IResultCapture { - public: - virtual ~IResultCapture(); - - virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; - virtual bool sectionStarted( StringRef sectionName, - SourceLineInfo const& sectionLineInfo, - Counts& assertions ) = 0; - virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0; - virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0; - - virtual IGeneratorTracker* - acquireGeneratorTracker( StringRef generatorName, - SourceLineInfo const& lineInfo ) = 0; - virtual IGeneratorTracker* - createGeneratorTracker( StringRef generatorName, - SourceLineInfo lineInfo, - Generators::GeneratorBasePtr&& generator ) = 0; - - virtual void benchmarkPreparing( StringRef name ) = 0; - virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; - virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; - virtual void benchmarkFailed( StringRef error ) = 0; - - static void pushScopedMessage( MessageInfo&& message ); - static void popScopedMessage( unsigned int messageId ); - static void addUnscopedMessage( MessageInfo&& message ); - static void emplaceUnscopedMessage( MessageBuilder&& builder ); - - virtual void handleFatalErrorCondition( StringRef message ) = 0; - - virtual void handleExpr - ( AssertionInfo const& info, - ITransientExpression const& expr, - AssertionReaction& reaction ) = 0; - virtual void handleMessage - ( AssertionInfo const& info, - ResultWas::OfType resultType, - std::string&& message, - AssertionReaction& reaction ) = 0; - virtual void handleUnexpectedExceptionNotThrown - ( AssertionInfo const& info, - AssertionReaction& reaction ) = 0; - virtual void handleUnexpectedInflightException - ( AssertionInfo const& info, - std::string&& message, - AssertionReaction& reaction ) = 0; - virtual void handleIncomplete - ( AssertionInfo const& info ) = 0; - virtual void handleNonExpr - ( AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction ) = 0; - - - virtual bool lastAssertionPassed() = 0; - - // Deprecated, do not use: - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; - virtual void exceptionEarlyReported() = 0; - }; - - namespace Detail { - [[noreturn]] - void missingCaptureInstance(); - } - inline IResultCapture& getResultCapture() { - if (auto* capture = getCurrentContext().getResultCapture()) { - return *capture; - } else { - Detail::missingCaptureInstance(); - } - } - -} +} // 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 { + namespace Detail { - //! Deriving classes become noncopyable and nonmovable - class NonCopyable { - public: - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable&& ) = delete; - NonCopyable& operator=( NonCopyable const& ) = delete; - NonCopyable& operator=( NonCopyable&& ) = delete; + //! Deriving classes become noncopyable and nonmovable + class NonCopyable { + public: + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable &operator=(NonCopyable const &) = delete; + NonCopyable &operator=(NonCopyable &&) = delete; - protected: - NonCopyable() noexcept = default; - }; + protected: + NonCopyable() noexcept = default; + }; - } // namespace Detail + } // namespace Detail } // namespace Catch #endif // CATCH_NONCOPYABLE_HPP_INCLUDED @@ -1191,159 +1127,157 @@ namespace Catch { namespace Catch { - enum class Verbosity { - Quiet = 0, - Normal, - High + 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, + //! The resulting generator in GENERATE is infinite + InfiniteGenerator = 0x04, }; + }; - 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, - //! The resulting generator in GENERATE is infinite - InfiniteGenerator = 0x04, - }; }; - - enum class ShowDurations { - DefaultForReporter, - Always, - Never + 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 }; - 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; - struct PathFilter; + class TestSpec; + class IStream; + struct PathFilter; - class IConfig : public Detail::NonCopyable { - public: - virtual ~IConfig(); + 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 warnAboutInfiniteGenerators() 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& getPathFilters() const = 0; - virtual bool useNewFilterBehaviour() const = 0; + 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 warnAboutInfiniteGenerators() 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 &getPathFilters() const = 0; + virtual bool useNewFilterBehaviour() const = 0; - virtual Verbosity verbosity() 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; - }; -} + 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; + }; +} // namespace Catch #endif // CATCH_INTERFACES_CONFIG_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 ReporterRegistry; - class IReporterFactory; - class ITagAliasRegistry; - class ITestInvoker; - class IMutableEnumValuesRegistry; - struct SourceLineInfo; + class TestCaseHandle; + struct TestCaseInfo; + class ITestCaseRegistry; + class IExceptionTranslatorRegistry; + class IExceptionTranslator; + class ReporterRegistry; + class IReporterFactory; + class ITagAliasRegistry; + class ITestInvoker; + class IMutableEnumValuesRegistry; + struct SourceLineInfo; - class StartupExceptionRegistry; - class EventListenerFactory; + class StartupExceptionRegistry; + class EventListenerFactory; - using IReporterFactoryPtr = Detail::unique_ptr; + using IReporterFactoryPtr = Detail::unique_ptr; - class IRegistryHub { - public: - virtual ~IRegistryHub(); // = default + 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 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; + }; - 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; + }; - 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(); - IRegistryHub const& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} +} // namespace Catch #endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - #ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_HPP_INCLUDED - - // Adapted from donated nonius code. #ifndef CATCH_CLOCK_HPP_INCLUDED @@ -1352,59 +1286,57 @@ namespace Catch { #include namespace Catch { - namespace Benchmark { - using IDuration = std::chrono::nanoseconds; - using FDuration = std::chrono::duration; + namespace Benchmark { + using IDuration = std::chrono::nanoseconds; + using FDuration = std::chrono::duration; - template - using TimePoint = typename Clock::time_point; + template + using TimePoint = typename Clock::time_point; - using default_clock = std::chrono::steady_clock; - } // namespace Benchmark + using default_clock = std::chrono::steady_clock; + } // namespace Benchmark } // namespace Catch #endif // CATCH_CLOCK_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_ESTIMATE_HPP_INCLUDED #define CATCH_ESTIMATE_HPP_INCLUDED namespace Catch { - namespace Benchmark { - template - struct Estimate { - Type point; - Type lower_bound; - Type upper_bound; - double confidence_interval; - }; - } // namespace Benchmark + namespace Benchmark { + template + struct Estimate { + Type point; + Type lower_bound; + Type upper_bound; + double confidence_interval; + }; + } // namespace Benchmark } // namespace Catch #endif // CATCH_ESTIMATE_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED #define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED namespace Catch { - namespace Benchmark { - struct OutlierClassification { - int samples_seen = 0; - int low_severe = 0; // more than 3 times IQR below Q1 - int low_mild = 0; // 1.5 to 3 times IQR below Q1 - int high_mild = 0; // 1.5 to 3 times IQR above Q3 - int high_severe = 0; // more than 3 times IQR above Q3 + namespace Benchmark { + struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 - constexpr int total() const { - return low_severe + low_mild + high_mild + high_severe; - } - }; - } // namespace Benchmark + constexpr int total() const { + return low_severe + low_mild + high_mild + high_severe; + } + }; + } // namespace Benchmark } // namespace Catch #endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED @@ -1416,248 +1348,237 @@ namespace Catch { namespace Catch { - struct BenchmarkInfo { - std::string name; - double estimatedDuration; - int iterations; - unsigned int samples; - unsigned int resamples; - double clockResolution; - double clockCost; - }; + struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + unsigned int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; - // We need to keep template parameter for backwards compatibility, - // but we also do not want to use the template paraneter. - template - struct BenchmarkStats { - BenchmarkInfo info; - - std::vector samples; - Benchmark::Estimate mean; - Benchmark::Estimate standardDeviation; - Benchmark::OutlierClassification outliers; - double outlierVariance; - }; + // We need to keep template parameter for backwards compatibility, + // but we also do not want to use the template paraneter. + template + struct BenchmarkStats { + BenchmarkInfo info; + std::vector samples; + Benchmark::Estimate mean; + Benchmark::Estimate standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; + }; } // end namespace Catch #endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_ENVIRONMENT_HPP_INCLUDED #define CATCH_ENVIRONMENT_HPP_INCLUDED - namespace Catch { - namespace Benchmark { - struct EnvironmentEstimate { - FDuration mean; - OutlierClassification outliers; - }; - struct Environment { - EnvironmentEstimate clock_resolution; - EnvironmentEstimate clock_cost; - }; - } // namespace Benchmark + namespace Benchmark { + struct EnvironmentEstimate { + FDuration mean; + OutlierClassification outliers; + }; + struct Environment { + 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) || defined(__IAR_SYSTEMS_ICC__) -# include // atomic_thread_fence +#include // atomic_thread_fence #endif - #include namespace Catch { - namespace Benchmark { + namespace Benchmark { #if defined(__GNUC__) || defined(__clang__) - template - inline void keep_memory(T* p) { - asm volatile("" : : "g"(p) : "memory"); - } - inline void keep_memory() { - asm volatile("" : : : "memory"); - } + template + inline void keep_memory(T *p) { + asm volatile("" : : "g"(p) : "memory"); + } + inline void keep_memory() { + asm volatile("" : : : "memory"); + } - namespace Detail { - inline void optimizer_barrier() { keep_memory(); } - } // namespace Detail + namespace Detail { + inline void optimizer_barrier() { + keep_memory(); + } + } // namespace Detail #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 +#pragma optimize = disable #endif - template - inline void keep_memory(T* p) { - // thanks @milleniumbug - *reinterpret_cast(p) = *reinterpret_cast(p); - } - // TODO equivalent keep_memory() + 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() { - std::atomic_thread_fence(std::memory_order_seq_cst); - } - } // namespace Detail + namespace Detail { + inline void optimizer_barrier() { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + } // namespace Detail #endif - template - inline void deoptimize_value(T&& x) { - keep_memory(&x); - } + template + inline void deoptimize_value(T &&x) { + keep_memory(&x); + } - template - inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { - deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...)); - } + template + inline auto invoke_deoptimized(Fn &&fn, Args &&...args) -> std::enable_if_t::value> { + deoptimize_value(CATCH_FORWARD(fn)(CATCH_FORWARD(args)...)); + } - template - inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { - CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...); - } - } // namespace Benchmark + template + inline auto invoke_deoptimized(Fn &&fn, Args &&...args) -> std::enable_if_t::value> { + CATCH_FORWARD((fn))(CATCH_FORWARD(args)...); + } + } // namespace Benchmark } // namespace Catch #endif // CATCH_OPTIMIZER_HPP_INCLUDED - #ifndef CATCH_META_HPP_INCLUDED #define CATCH_META_HPP_INCLUDED #include namespace Catch { - template - struct true_given : std::true_type {}; + template + struct true_given : std::true_type {}; - struct is_callable_tester { - template - static true_given()(std::declval()...))> test(int); - template - static std::false_type test(...); - }; + struct is_callable_tester { + template + static true_given()(std::declval()...))> test(int); + template + static std::false_type test(...); + }; - template - struct is_callable; - - template - struct is_callable : decltype(is_callable_tester::test(0)) {}; + template + struct is_callable; + template + struct is_callable : decltype(is_callable_tester::test(0)) {}; #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 - // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is - // replaced with std::invoke_result here. - template - using FunctionReturnType = std::remove_reference_t>>; + // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is + // replaced with std::invoke_result here. + template + using FunctionReturnType = std::remove_reference_t>>; #else - template - using FunctionReturnType = std::remove_reference_t>>; + template + using FunctionReturnType = std::remove_reference_t>>; #endif } // namespace Catch -namespace mpl_{ - struct na; +namespace mpl_ { + struct na; } #endif // CATCH_META_HPP_INCLUDED namespace Catch { - namespace Benchmark { - namespace Detail { - struct ChronometerConcept { - virtual void start() = 0; - virtual void finish() = 0; - virtual ~ChronometerConcept(); // = default; + namespace Benchmark { + namespace Detail { + struct ChronometerConcept { + virtual void start() = 0; + virtual void finish() = 0; + virtual ~ChronometerConcept(); // = default; - ChronometerConcept() = default; - ChronometerConcept(ChronometerConcept const&) = default; - ChronometerConcept& operator=(ChronometerConcept const&) = default; - }; - template - struct ChronometerModel final : public ChronometerConcept { - void start() override { started = Clock::now(); } - void finish() override { finished = Clock::now(); } + ChronometerConcept() = default; + ChronometerConcept(ChronometerConcept const &) = default; + ChronometerConcept &operator=(ChronometerConcept const &) = default; + }; + template + struct ChronometerModel final : public ChronometerConcept { + void start() override { started = Clock::now(); } + void finish() override { finished = Clock::now(); } - IDuration elapsed() const { - return std::chrono::duration_cast( - finished - started ); - } + IDuration elapsed() const { + return std::chrono::duration_cast( + finished - started); + } - TimePoint started; - TimePoint finished; - }; - } // namespace Detail + TimePoint started; + TimePoint finished; + }; + } // namespace Detail - struct Chronometer { - public: - template - void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable()); } + struct Chronometer { + public: + template + void measure(Fun &&fun) { measure(CATCH_FORWARD(fun), is_callable()); } - int runs() const { return repeats; } + int runs() const { return repeats; } - Chronometer(Detail::ChronometerConcept& meter, int repeats_) - : impl(&meter) - , repeats(repeats_) {} + Chronometer(Detail::ChronometerConcept &meter, int repeats_) + : impl(&meter) + , repeats(repeats_) {} - private: - template - void measure(Fun&& fun, std::false_type) { - measure([&fun](int) { return fun(); }, std::true_type()); - } + private: + template + void measure(Fun &&fun, std::false_type) { + measure([&fun](int) { return fun(); }, std::true_type()); + } - template - void measure(Fun&& fun, std::true_type) { - Detail::optimizer_barrier(); - impl->start(); - for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); - impl->finish(); - Detail::optimizer_barrier(); - } + template + void measure(Fun &&fun, std::true_type) { + Detail::optimizer_barrier(); + impl->start(); + for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); + impl->finish(); + Detail::optimizer_barrier(); + } - Detail::ChronometerConcept* impl; - int repeats; - }; - } // namespace Benchmark + Detail::ChronometerConcept *impl; + int repeats; + }; + } // namespace Benchmark } // namespace Catch #endif // CATCH_CHRONOMETER_HPP_INCLUDED @@ -1665,75 +1586,76 @@ namespace Catch { #include namespace Catch { - namespace Benchmark { - namespace Detail { - template - static constexpr bool is_related_v = std::is_same, std::decay_t>::value; + namespace Benchmark { + namespace Detail { + template + static constexpr bool is_related_v = std::is_same, std::decay_t>::value; - /// We need to reinvent std::function because every piece of code that might add overhead - /// in a measurement context needs to have consistent performance characteristics so that we - /// can account for it in the measurement. - /// Implementations of std::function with optimizations that aren't always applicable, like - /// small buffer optimizations, are not uncommon. - /// This is effectively an implementation of std::function without any such optimizations; - /// it may be slow, but it is consistently slow. - struct BenchmarkFunction { - private: - struct callable { - virtual void call(Chronometer meter) const = 0; - virtual ~callable(); // = default; + /// We need to reinvent std::function because every piece of code that might add overhead + /// in a measurement context needs to have consistent performance characteristics so that we + /// can account for it in the measurement. + /// Implementations of std::function with optimizations that aren't always applicable, like + /// small buffer optimizations, are not uncommon. + /// This is effectively an implementation of std::function without any such optimizations; + /// it may be slow, but it is consistently slow. + struct BenchmarkFunction { + private: + struct callable { + virtual void call(Chronometer meter) const = 0; + virtual ~callable(); // = default; - callable() = default; - callable(callable&&) = default; - callable& operator=(callable&&) = default; - }; - template - struct model : public callable { - model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} - model(Fun const& fun_) : fun(fun_) {} + callable() = default; + callable(callable &&) = default; + callable &operator=(callable &&) = default; + }; + template + struct model : public callable { + model(Fun &&fun_) + : fun(CATCH_MOVE(fun_)) {} + model(Fun const &fun_) + : fun(fun_) {} - void call(Chronometer meter) const override { - call(meter, is_callable()); - } - void call(Chronometer meter, std::true_type) const { - fun(meter); - } - void call(Chronometer meter, std::false_type) const { - meter.measure(fun); - } + void call(Chronometer meter) const override { + call(meter, is_callable()); + } + void call(Chronometer meter, std::true_type) const { + fun(meter); + } + void call(Chronometer meter, std::false_type) const { + meter.measure(fun); + } - Fun fun; - }; + Fun fun; + }; - public: - BenchmarkFunction(); + public: + BenchmarkFunction(); - template , int> = 0> - BenchmarkFunction(Fun&& fun) - : f(new model>(CATCH_FORWARD(fun))) {} + template, int> = 0> + BenchmarkFunction(Fun &&fun) + : f(new model>(CATCH_FORWARD(fun))) {} - BenchmarkFunction( BenchmarkFunction&& that ) noexcept: - f( CATCH_MOVE( that.f ) ) {} + BenchmarkFunction(BenchmarkFunction &&that) noexcept + : f(CATCH_MOVE(that.f)) {} - BenchmarkFunction& - operator=( BenchmarkFunction&& that ) noexcept { - f = CATCH_MOVE( that.f ); - return *this; - } + BenchmarkFunction & + operator=(BenchmarkFunction &&that) noexcept { + f = CATCH_MOVE(that.f); + return *this; + } - void operator()(Chronometer meter) const { f->call(meter); } + void operator()(Chronometer meter) const { f->call(meter); } - private: - Catch::Detail::unique_ptr f; - }; - } // namespace Detail - } // namespace Benchmark + private: + Catch::Detail::unique_ptr f; + }; + } // namespace Detail + } // namespace Benchmark } // namespace Catch #endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_REPEAT_HPP_INCLUDED @@ -1742,127 +1664,123 @@ namespace Catch { #include namespace Catch { - namespace Benchmark { - namespace Detail { - template - struct repeater { - void operator()(int k) const { - for (int i = 0; i < k; ++i) { - fun(); - } - } - Fun fun; - }; - template - repeater> repeat(Fun&& fun) { - return { CATCH_FORWARD(fun) }; - } - } // namespace Detail - } // namespace Benchmark + namespace Benchmark { + namespace Detail { + template + struct repeater { + void operator()(int k) const { + for (int i = 0; i < k; ++i) { + fun(); + } + } + Fun fun; + }; + template + repeater> repeat(Fun &&fun) { + return {CATCH_FORWARD(fun)}; + } + } // namespace Detail + } // namespace Benchmark } // namespace Catch #endif // CATCH_REPEAT_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED #define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED - - // Adapted from donated nonius code. #ifndef CATCH_MEASURE_HPP_INCLUDED #define CATCH_MEASURE_HPP_INCLUDED - - // 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 {}; }; + namespace Benchmark { + namespace Detail { + template + struct CompleteType { + using type = T; + }; + template<> + struct CompleteType { + struct type {}; + }; - template - using CompleteType_t = typename CompleteType::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)); + template + struct CompleteInvoker { + template + static Result invoke(Fun &&fun, Args &&...args) { + return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); } - } // namespace Benchmark + }; + 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 #define CATCH_TIMING_HPP_INCLUDED - namespace Catch { - namespace Benchmark { - template - struct Timing { - IDuration elapsed; - Result result; - int iterations; - }; - template - using TimingOf = Timing>>; - } // namespace Benchmark + namespace Benchmark { + template + struct Timing { + IDuration elapsed; + Result result; + int iterations; + }; + template + using TimingOf = Timing>>; + } // namespace Benchmark } // namespace Catch #endif // CATCH_TIMING_HPP_INCLUDED namespace Catch { - namespace Benchmark { - namespace Detail { - template - TimingOf measure(Fun&& fun, Args&&... args) { - auto start = Clock::now(); - auto&& r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); - auto end = Clock::now(); - auto delta = end - start; - return { delta, CATCH_FORWARD(r), 1 }; - } - } // namespace Detail - } // namespace Benchmark + namespace Benchmark { + namespace Detail { + template + TimingOf measure(Fun &&fun, Args &&...args) { + auto start = Clock::now(); + auto &&r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); + auto end = Clock::now(); + auto delta = end - start; + return {delta, CATCH_FORWARD(r), 1}; + } + } // namespace Detail + } // namespace Benchmark } // namespace Catch #endif // CATCH_MEASURE_HPP_INCLUDED @@ -1870,45 +1788,44 @@ namespace Catch { #include namespace Catch { - namespace Benchmark { - namespace Detail { - template - TimingOf measure_one(Fun&& fun, int iters, std::false_type) { - return Detail::measure(fun, iters); - } - template - TimingOf measure_one(Fun&& fun, int iters, std::true_type) { - Detail::ChronometerModel meter; - auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); + namespace Benchmark { + namespace Detail { + template + TimingOf measure_one(Fun &&fun, int iters, std::false_type) { + return Detail::measure(fun, iters); + } + template + TimingOf measure_one(Fun &&fun, int iters, std::true_type) { + Detail::ChronometerModel meter; + auto &&result = Detail::complete_invoke(fun, Chronometer(meter, iters)); - return { meter.elapsed(), CATCH_MOVE(result), iters }; - } + return {meter.elapsed(), CATCH_MOVE(result), iters}; + } - template - using run_for_at_least_argument_t = std::conditional_t::value, Chronometer, int>; + template + using run_for_at_least_argument_t = std::conditional_t::value, Chronometer, int>; + [[noreturn]] + void throw_optimized_away_error(); - [[noreturn]] - void throw_optimized_away_error(); + template + TimingOf> + run_for_at_least(IDuration how_long, + const int initial_iterations, + Fun &&fun) { + auto iters = initial_iterations; + while (iters < (1 << 30)) { + auto &&Timing = measure_one(fun, iters, is_callable()); - template - TimingOf> - run_for_at_least(IDuration how_long, - const int initial_iterations, - Fun&& fun) { - auto iters = initial_iterations; - while (iters < (1 << 30)) { - auto&& Timing = measure_one(fun, iters, is_callable()); - - if (Timing.elapsed >= how_long) { - return { Timing.elapsed, CATCH_MOVE(Timing.result), iters }; - } - iters *= 2; - } - throw_optimized_away_error(); - } - } // namespace Detail - } // namespace Benchmark + if (Timing.elapsed >= how_long) { + return {Timing.elapsed, CATCH_MOVE(Timing.result), iters}; + } + iters *= 2; + } + throw_optimized_away_error(); + } + } // namespace Detail + } // namespace Benchmark } // namespace Catch #endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED @@ -1916,466 +1833,444 @@ namespace Catch { #include namespace Catch { - namespace Benchmark { - struct ExecutionPlan { - int iterations_per_sample; - FDuration estimated_duration; - Detail::BenchmarkFunction benchmark; - FDuration warmup_time; - int warmup_iterations; + namespace Benchmark { + struct ExecutionPlan { + int iterations_per_sample; + FDuration estimated_duration; + Detail::BenchmarkFunction benchmark; + FDuration warmup_time; + int warmup_iterations; - template - std::vector run(const IConfig &cfg, Environment env) const { - // warmup a bit - Detail::run_for_at_least( - std::chrono::duration_cast( warmup_time ), - warmup_iterations, - Detail::repeat( []() { return Clock::now(); } ) - ); + template + std::vector run(const IConfig &cfg, Environment env) const { + // warmup a bit + Detail::run_for_at_least( + std::chrono::duration_cast(warmup_time), + warmup_iterations, + Detail::repeat([]() { return Clock::now(); })); - std::vector times; - 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 ) ); - auto sample_time = model.elapsed() - env.clock_cost.mean; - if ( sample_time < FDuration::zero() ) { - sample_time = FDuration::zero(); - } - times.push_back(sample_time / iterations_per_sample); - } - return times; - } - }; - } // namespace Benchmark + std::vector times; + 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)); + auto sample_time = model.elapsed() - env.clock_cost.mean; + if (sample_time < FDuration::zero()) { + sample_time = FDuration::zero(); + } + times.push_back(sample_time / iterations_per_sample); + } + return times; + } + }; + } // namespace Benchmark } // namespace Catch #endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED #define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED - - // Adapted from donated nonius code. #ifndef CATCH_STATS_HPP_INCLUDED #define CATCH_STATS_HPP_INCLUDED - #include namespace Catch { - namespace Benchmark { - namespace Detail { - using sample = std::vector; + namespace Benchmark { + namespace Detail { + using sample = std::vector; - double weighted_average_quantile( int k, - int q, - double* first, - double* last ); + double weighted_average_quantile(int k, + int q, + double *first, + double *last); - OutlierClassification - classify_outliers( double const* first, double const* last ); + OutlierClassification + classify_outliers(double const *first, double const *last); - double mean( double const* first, double const* last ); + double mean(double const *first, double const *last); - double normal_cdf( double x ); + double normal_cdf(double x); - double erfc_inv(double x); + double erfc_inv(double x); - double normal_quantile(double p); + double normal_quantile(double p); - Estimate - bootstrap( double confidence_level, - double* first, - double* last, - sample const& resample, - double ( *estimator )( double const*, double const* ) ); + Estimate + bootstrap(double confidence_level, + double *first, + double *last, + sample const &resample, + double (*estimator)(double const *, double const *)); - struct bootstrap_analysis { - Estimate mean; - Estimate standard_deviation; - double outlier_variance; - }; + struct bootstrap_analysis { + Estimate mean; + Estimate standard_deviation; + double outlier_variance; + }; - bootstrap_analysis analyse_samples(double confidence_level, - unsigned int n_resamples, - double* first, - double* last); - } // namespace Detail - } // namespace Benchmark + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + double *first, + double *last); + } // namespace Detail + } // namespace Benchmark } // namespace Catch #endif // CATCH_STATS_HPP_INCLUDED #include -#include #include +#include namespace Catch { - namespace Benchmark { - namespace Detail { - template - std::vector resolution(int k) { - const size_t points = static_cast( k + 1 ); - // To avoid overhead from the branch inside vector::push_back, - // we allocate them all and then overwrite. - std::vector> times(points); - for ( auto& time : times ) { - time = Clock::now(); - } + namespace Benchmark { + namespace Detail { + template + std::vector resolution(int k) { + const size_t points = static_cast(k + 1); + // To avoid overhead from the branch inside vector::push_back, + // we allocate them all and then overwrite. + std::vector> times(points); + for (auto &time : times) { + time = Clock::now(); + } - std::vector deltas; - deltas.reserve(static_cast(k)); - for ( size_t idx = 1; idx < points; ++idx ) { - deltas.push_back( static_cast( - ( times[idx] - times[idx - 1] ).count() ) ); - } + std::vector deltas; + deltas.reserve(static_cast(k)); + for (size_t idx = 1; idx < points; ++idx) { + deltas.push_back(static_cast( + (times[idx] - times[idx - 1]).count())); + } - return deltas; - } + return deltas; + } - 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; + 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() { - return run_for_at_least(warmup_time, warmup_seed, &resolution) - .iterations; - } - template - EnvironmentEstimate estimate_clock_resolution(int iterations) { - auto r = run_for_at_least(clock_resolution_estimation_time, iterations, &resolution) - .result; - return { - FDuration(mean(r.data(), r.data() + r.size())), - classify_outliers(r.data(), r.data() + r.size()), - }; - } - template - EnvironmentEstimate estimate_clock_cost(FDuration resolution) { - auto time_limit = (std::min)( - resolution * clock_cost_estimation_tick_limit, - FDuration(clock_cost_estimation_time_limit)); - auto time_clock = [](int k) { - return Detail::measure([k] { - for (int i = 0; i < k; ++i) { - volatile auto ignored = Clock::now(); - (void)ignored; - } - }).elapsed; - }; - time_clock(1); - int iters = clock_cost_estimation_iterations; - auto&& r = run_for_at_least(clock_cost_estimation_time, iters, time_clock); - std::vector times; - int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); - times.reserve(static_cast(nsamples)); - for ( int s = 0; s < nsamples; ++s ) { - times.push_back( static_cast( - ( time_clock( r.iterations ) / r.iterations ) - .count() ) ); - } - return { - FDuration(mean(times.data(), times.data() + times.size())), - classify_outliers(times.data(), times.data() + times.size()), - }; - } + template + int warmup() { + return run_for_at_least(warmup_time, warmup_seed, &resolution) + .iterations; + } + template + EnvironmentEstimate estimate_clock_resolution(int iterations) { + auto r = run_for_at_least(clock_resolution_estimation_time, iterations, &resolution) + .result; + return { + FDuration(mean(r.data(), r.data() + r.size())), + classify_outliers(r.data(), r.data() + r.size()), + }; + } + template + EnvironmentEstimate estimate_clock_cost(FDuration resolution) { + auto time_limit = (std::min)( + resolution * clock_cost_estimation_tick_limit, + FDuration(clock_cost_estimation_time_limit)); + auto time_clock = [](int k) { + return Detail::measure([k] { + for (int i = 0; i < k; ++i) { + volatile auto ignored = Clock::now(); + (void)ignored; + } + }) + .elapsed; + }; + time_clock(1); + int iters = clock_cost_estimation_iterations; + auto &&r = run_for_at_least(clock_cost_estimation_time, iters, time_clock); + std::vector times; + int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); + times.reserve(static_cast(nsamples)); + for (int s = 0; s < nsamples; ++s) { + times.push_back(static_cast( + (time_clock(r.iterations) / r.iterations) + .count())); + } + return { + FDuration(mean(times.data(), times.data() + times.size())), + classify_outliers(times.data(), times.data() + times.size()), + }; + } - template - Environment measure_environment() { + template + Environment measure_environment() { #if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" #endif - static Catch::Detail::unique_ptr env; + static Catch::Detail::unique_ptr env; #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif - if (env) { - return *env; - } + if (env) { + return *env; + } - auto iters = Detail::warmup(); - auto resolution = Detail::estimate_clock_resolution(iters); - auto cost = Detail::estimate_clock_cost(resolution.mean); + auto iters = Detail::warmup(); + auto resolution = Detail::estimate_clock_resolution(iters); + auto cost = Detail::estimate_clock_cost(resolution.mean); - env = Catch::Detail::make_unique( Environment{resolution, cost} ); - return *env; - } - } // namespace Detail - } // namespace Benchmark + env = Catch::Detail::make_unique(Environment{resolution, cost}); + return *env; + } + } // namespace Detail + } // namespace Benchmark } // namespace Catch #endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_ANALYSE_HPP_INCLUDED #define CATCH_ANALYSE_HPP_INCLUDED - - // Adapted from donated nonius code. #ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED #define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED - #include namespace Catch { - namespace Benchmark { - struct SampleAnalysis { - std::vector samples; - Estimate mean; - Estimate standard_deviation; - OutlierClassification outliers; - double outlier_variance; - }; - } // namespace Benchmark + namespace Benchmark { + struct SampleAnalysis { + std::vector samples; + Estimate mean; + Estimate standard_deviation; + OutlierClassification outliers; + double outlier_variance; + }; + } // namespace Benchmark } // namespace Catch #endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED - namespace Catch { - class IConfig; + class IConfig; - namespace Benchmark { - namespace Detail { - SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); - } // namespace Detail - } // namespace Benchmark + namespace Benchmark { + namespace Detail { + SampleAnalysis analyse(const IConfig &cfg, FDuration *first, FDuration *last); + } // namespace Detail + } // namespace Benchmark } // namespace Catch #endif // CATCH_ANALYSE_HPP_INCLUDED #include #include +#include #include #include -#include namespace Catch { - namespace Benchmark { - struct Benchmark { - Benchmark(std::string&& benchmarkName) - : name(CATCH_MOVE(benchmarkName)) {} + namespace Benchmark { + struct Benchmark { + Benchmark(std::string &&benchmarkName) + : name(CATCH_MOVE(benchmarkName)) {} - template - Benchmark(std::string&& benchmarkName , FUN &&func) - : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} + template + Benchmark(std::string &&benchmarkName, FUN &&func) + : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} - template - ExecutionPlan prepare(const IConfig &cfg, Environment env) { - auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; - auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); - auto&& test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun); - int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); - return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; - } + template + ExecutionPlan prepare(const IConfig &cfg, Environment env) { + auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; + auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); + auto &&test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun); + int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); + return {new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations}; + } - template - void run() { - static_assert( Clock::is_steady, - "Benchmarking clock should be steady" ); - auto const* cfg = getCurrentContext().getConfig(); + template + void run() { + static_assert(Clock::is_steady, + "Benchmarking clock should be steady"); + auto const *cfg = getCurrentContext().getConfig(); - auto env = Detail::measure_environment(); + auto env = Detail::measure_environment(); - getResultCapture().benchmarkPreparing(name); - CATCH_TRY{ - auto plan = user_code([&] { - return prepare(*cfg, env); - }); + getResultCapture().benchmarkPreparing(name); + CATCH_TRY { + auto plan = user_code([&] { + return prepare(*cfg, env); + }); - BenchmarkInfo info { - CATCH_MOVE(name), - plan.estimated_duration.count(), - plan.iterations_per_sample, - cfg->benchmarkSamples(), - cfg->benchmarkResamples(), - env.clock_resolution.mean.count(), - env.clock_cost.mean.count() - }; + BenchmarkInfo info{ + CATCH_MOVE(name), + plan.estimated_duration.count(), + plan.iterations_per_sample, + cfg->benchmarkSamples(), + cfg->benchmarkResamples(), + env.clock_resolution.mean.count(), + env.clock_cost.mean.count()}; - getResultCapture().benchmarkStarting(info); + getResultCapture().benchmarkStarting(info); - auto samples = user_code([&] { - return plan.template run(*cfg, env); - }); + auto samples = user_code([&] { + return plan.template run(*cfg, env); + }); - auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); - BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; - getResultCapture().benchmarkEnded(stats); - } CATCH_CATCH_ALL { - getResultCapture().benchmarkFailed(translateActiveException()); - // We let the exception go further up so that the - // test case is marked as failed. - std::rethrow_exception(std::current_exception()); - } - } + auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); + BenchmarkStats<> stats{CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance}; + getResultCapture().benchmarkEnded(stats); + } + CATCH_CATCH_ALL { + getResultCapture().benchmarkFailed(translateActiveException()); + // We let the exception go further up so that the + // test case is marked as failed. + std::rethrow_exception(std::current_exception()); + } + } - // sets lambda to be used in fun *and* executes benchmark! - template , int> = 0> - Benchmark & operator=(Fun func) { - auto const* cfg = getCurrentContext().getConfig(); - if (!cfg->skipBenchmarks()) { - fun = Detail::BenchmarkFunction(func); - run(); - } - return *this; - } + // sets lambda to be used in fun *and* executes benchmark! + template, int> = 0> + Benchmark &operator=(Fun func) { + auto const *cfg = getCurrentContext().getConfig(); + if (!cfg->skipBenchmarks()) { + fun = Detail::BenchmarkFunction(func); + run(); + } + return *this; + } - explicit operator bool() { - return true; - } + explicit operator bool() { + return true; + } - private: - Detail::BenchmarkFunction fun; - std::string name; - }; - } + private: + Detail::BenchmarkFunction fun; + std::string name; + }; + } // namespace Benchmark } // namespace Catch #define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 #define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 -#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ - if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ - BenchmarkName = [&](int benchmarkIndex) +#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex) \ + if (Catch::Benchmark::Benchmark BenchmarkName{name}) \ + BenchmarkName = [&](int benchmarkIndex) -#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ - if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ - BenchmarkName = [&] +#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name) \ + if (Catch::Benchmark::Benchmark BenchmarkName{name}) \ + BenchmarkName = [&] #if defined(CATCH_CONFIG_PREFIX_ALL) #define CATCH_BENCHMARK(...) \ - INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) #define CATCH_BENCHMARK_ADVANCED(name) \ - INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) #else #define BENCHMARK(...) \ - INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) #define BENCHMARK_ADVANCED(name) \ - INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) #endif #endif // CATCH_BENCHMARK_HPP_INCLUDED - // Adapted from donated nonius code. #ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED #define CATCH_CONSTRUCTOR_HPP_INCLUDED - #include namespace Catch { - namespace Benchmark { - namespace Detail { - template - struct ObjectStorage - { - ObjectStorage() = default; + namespace Benchmark { + namespace Detail { + template + struct ObjectStorage { + ObjectStorage() = default; - ObjectStorage(const ObjectStorage& other) - { - new(&data) T(other.stored_object()); - } + ObjectStorage(const ObjectStorage &other) { + new (&data) T(other.stored_object()); + } - ObjectStorage(ObjectStorage&& other) - { - new(data) T(CATCH_MOVE(other.stored_object())); - } + ObjectStorage(ObjectStorage &&other) { + new (data) T(CATCH_MOVE(other.stored_object())); + } - ~ObjectStorage() { destruct_on_exit(); } + ~ObjectStorage() { destruct_on_exit(); } - template - void construct(Args&&... args) - { - new (data) T(CATCH_FORWARD(args)...); - } + template + void construct(Args &&...args) { + new (data) T(CATCH_FORWARD(args)...); + } - template - std::enable_if_t destruct() - { - stored_object().~T(); - } + template + std::enable_if_t destruct() { + stored_object().~T(); + } - private: - // If this is a constructor benchmark, destruct the underlying object - template - void destruct_on_exit(std::enable_if_t* = nullptr) { destruct(); } - // Otherwise, don't - template - void destruct_on_exit(std::enable_if_t* = nullptr) { } + private: + // If this is a constructor benchmark, destruct the underlying object + template + void destruct_on_exit(std::enable_if_t * = nullptr) { destruct(); } + // Otherwise, don't + template + void destruct_on_exit(std::enable_if_t * = nullptr) {} -#if defined( __GNUC__ ) && __GNUC__ <= 6 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wstrict-aliasing" +#if defined(__GNUC__) && __GNUC__ <= 6 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif - T& stored_object() { return *reinterpret_cast( data ); } + T &stored_object() { return *reinterpret_cast(data); } - T const& stored_object() const { - return *reinterpret_cast( data ); - } -#if defined( __GNUC__ ) && __GNUC__ <= 6 -# pragma GCC diagnostic pop + T const &stored_object() const { + return *reinterpret_cast(data); + } +#if defined(__GNUC__) && __GNUC__ <= 6 +#pragma GCC diagnostic pop #endif - alignas( T ) unsigned char data[sizeof( T )]{}; - }; - } // namespace Detail + alignas(T) unsigned char data[sizeof(T)]{}; + }; + } // namespace Detail - template - using storage_for = Detail::ObjectStorage; + template + using storage_for = Detail::ObjectStorage; - template - using destructable_object = Detail::ObjectStorage; - } // namespace Benchmark + template + using destructable_object = Detail::ObjectStorage; + } // namespace Benchmark } // namespace Catch #endif // CATCH_CONSTRUCTOR_HPP_INCLUDED #endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED - #ifndef CATCH_APPROX_HPP_INCLUDED #define CATCH_APPROX_HPP_INCLUDED - - #ifndef CATCH_TOSTRING_HPP_INCLUDED #define CATCH_TOSTRING_HPP_INCLUDED -#include -#include #include -#include +#include #include - - - +#include +#include /** \file * Wrapper for the WCHAR configuration option @@ -2388,45 +2283,41 @@ 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. #if defined(__DJGPP__) -# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#define CATCH_INTERNAL_CONFIG_NO_WCHAR #endif // __DJGPP__ -#if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \ - !defined( CATCH_CONFIG_NO_WCHAR ) && \ - !defined( CATCH_CONFIG_WCHAR ) -# define CATCH_CONFIG_WCHAR +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +#define CATCH_CONFIG_WCHAR #endif #endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED - #ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED #define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED - -#include #include +#include #include #include namespace Catch { - class ReusableStringStream : Detail::NonCopyable { - std::size_t m_index; - std::ostream* m_oss; - public: - ReusableStringStream(); - ~ReusableStringStream(); + class ReusableStringStream : Detail::NonCopyable { + std::size_t m_index; + std::ostream *m_oss; - //! Returns the serialized state - std::string str() const; - //! Sets internal state to `str` - void str(std::string const& str); + public: + ReusableStringStream(); + ~ReusableStringStream(); + + //! Returns the serialized state + std::string str() const; + //! Sets internal state to `str` + void str(std::string const &str); #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push @@ -2440,79 +2331,76 @@ namespace Catch { #pragma GCC diagnostic ignored "-Wnonnull-compare" #endif - template - auto operator << ( T const& value ) -> ReusableStringStream& { - *m_oss << value; - return *this; - } + template + auto operator<<(T const &value) -> ReusableStringStream & { + *m_oss << value; + return *this; + } #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif - auto get() -> std::ostream& { return *m_oss; } - }; -} + auto get() -> std::ostream & { return *m_oss; } + }; +} // namespace Catch #endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED - #ifndef CATCH_VOID_TYPE_HPP_INCLUDED #define CATCH_VOID_TYPE_HPP_INCLUDED - namespace Catch { - namespace Detail { + namespace Detail { - template - struct make_void { using type = void; }; + template + struct make_void { + using type = void; + }; - template - using void_t = typename make_void::type; + template + using void_t = typename make_void::type; - } // namespace Detail + } // namespace Detail } // namespace Catch - #endif // CATCH_VOID_TYPE_HPP_INCLUDED - #ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED #define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED - #include namespace Catch { - namespace Detail { - struct EnumInfo { - StringRef m_name; - std::vector> m_values; + namespace Detail { + struct EnumInfo { + StringRef m_name; + std::vector> m_values; - ~EnumInfo(); + ~EnumInfo(); - StringRef lookup( int value ) const; - }; - } // namespace Detail - - class IMutableEnumValuesRegistry { - public: - virtual ~IMutableEnumValuesRegistry(); // = default; - - virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; - - template - Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { - static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); - std::vector intValues; - intValues.reserve( values.size() ); - for( auto enumValue : values ) - intValues.push_back( static_cast( enumValue ) ); - return registerEnum( enumName, allEnums, intValues ); - } + StringRef lookup(int value) const; }; + } // namespace Detail -} // Catch + class IMutableEnumValuesRegistry { + public: + virtual ~IMutableEnumValuesRegistry(); // = default; + + virtual Detail::EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::vector const &values) = 0; + + template + Detail::EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::initializer_list values) { + static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); + std::vector intValues; + intValues.reserve(values.size()); + for (auto enumValue : values) + intValues.push_back(static_cast(enumValue)); + return registerEnum(enumName, allEnums, intValues); + } + }; + +} // namespace Catch #endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED @@ -2522,330 +2410,331 @@ namespace Catch { #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#pragma warning(disable : 4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless #endif // We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy{}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); +struct Catch_global_namespace_dummy {}; +std::ostream &operator<<(std::ostream &, Catch_global_namespace_dummy); namespace Catch { - // Bring in global namespace operator<< for ADL lookup in - // `IsStreamInsertable` below. - using ::operator<<; + // Bring in global namespace operator<< for ADL lookup in + // `IsStreamInsertable` below. + using ::operator<<; - namespace Detail { + namespace Detail { - std::size_t catch_strnlen(const char *str, std::size_t n); + std::size_t catch_strnlen(const char *str, std::size_t n); - std::string formatTimeT( std::time_t time ); + std::string formatTimeT(std::time_t time); - constexpr StringRef unprintableString = "{?}"_sr; + constexpr StringRef unprintableString = "{?}"_sr; - //! Encases `string in quotes, and optionally escapes invisibles - std::string convertIntoString( StringRef string, bool escapeInvisibles ); + //! Encases `string in quotes, and optionally escapes invisibles + std::string convertIntoString(StringRef string, bool escapeInvisibles); - //! Encases `string` in quotes, and escapes invisibles if user requested - //! it via CLI - std::string convertIntoString( StringRef string ); + //! Encases `string` in quotes, and escapes invisibles if user requested + //! it via CLI + std::string convertIntoString(StringRef string); - std::string rawMemoryToString( const void *object, std::size_t size ); + std::string rawMemoryToString(const void *object, std::size_t size); - template - std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } + template + std::string rawMemoryToString(const T &object) { + return rawMemoryToString(&object, sizeof(object)); + } - template - static constexpr bool IsStreamInsertable_v = false; + template + static constexpr bool IsStreamInsertable_v = false; - template - static constexpr bool IsStreamInsertable_v< - T, - decltype( void( std::declval() << std::declval() ) )> = - true; + template + static constexpr bool IsStreamInsertable_v< + T, + decltype(void(std::declval() << std::declval()))> + = true; - template - std::string convertUnknownEnumToString( E e ); + template + std::string convertUnknownEnumToString(E e); - template - std::enable_if_t< - !std::is_enum::value && !std::is_base_of::value, - std::string> convertUnstreamable( T const& ) { - return std::string(Detail::unprintableString); - } - template - std::enable_if_t< - !std::is_enum::value && std::is_base_of::value, - std::string> convertUnstreamable(T const& ex) { - return ex.what(); - } + template + std::enable_if_t< + !std::is_enum::value && !std::is_base_of::value, + std::string> + convertUnstreamable(T const &) { + return std::string(Detail::unprintableString); + } + template + std::enable_if_t< + !std::is_enum::value && std::is_base_of::value, + std::string> + convertUnstreamable(T const &ex) { + return ex.what(); + } - - template - std::enable_if_t< - std::is_enum::value, - std::string> convertUnstreamable( T const& value ) { - return convertUnknownEnumToString( value ); - } + template + std::enable_if_t< + std::is_enum::value, + std::string> + convertUnstreamable(T const &value) { + return convertUnknownEnumToString(value); + } #if defined(_MANAGED) - //! Convert a CLR string to a utf8 std::string - template - std::string clrReferenceToString( T^ ref ) { - if (ref == nullptr) - return std::string("null"); - auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); - cli::pin_ptr p = &bytes[0]; - return std::string(reinterpret_cast(p), bytes->Length); - } + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString(T ^ ref) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } #endif - } // namespace Detail + } // namespace Detail + template + struct StringMaker { + template + static std::enable_if_t<::Catch::Detail::IsStreamInsertable_v, std::string> + convert(const Fake &value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } - template - struct StringMaker { - template - static - std::enable_if_t<::Catch::Detail::IsStreamInsertable_v, std::string> - convert(const Fake& value) { - ReusableStringStream rss; - // NB: call using the function-like syntax to avoid ambiguity with - // user-defined templated operator<< under clang. - rss.operator<<(value); - return rss.str(); - } - - template - static - std::enable_if_t, std::string> - convert( const Fake& value ) { + template + static std::enable_if_t, std::string> + convert(const Fake &value) { #if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) - return Detail::convertUnstreamable(value); + return Detail::convertUnstreamable(value); #else - return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); #endif - } - }; + } + }; - namespace Detail { + namespace Detail { - std::string makeExceptionHappenedString(); + std::string makeExceptionHappenedString(); - // This function dispatches all stringification requests inside of Catch. - // Should be preferably called fully qualified, like ::Catch::Detail::stringify - template - std::string stringify( const T& e ) { - CATCH_TRY { - return ::Catch::StringMaker< - std::remove_cv_t>>::convert( e ); - } - CATCH_CATCH_ALL { return makeExceptionHappenedString(); } - } + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T &e) { + CATCH_TRY { + return ::Catch::StringMaker< + std::remove_cv_t>>::convert(e); + } + CATCH_CATCH_ALL { + return makeExceptionHappenedString(); + } + } - template - std::string convertUnknownEnumToString( E e ) { - return ::Catch::Detail::stringify(static_cast>(e)); - } + template + std::string convertUnknownEnumToString(E e) { + return ::Catch::Detail::stringify(static_cast>(e)); + } #if defined(_MANAGED) - template - std::string stringify( T^ e ) { - return ::Catch::StringMaker::convert(e); - } + template + std::string stringify(T ^ e) { + return ::Catch::StringMaker::convert(e); + } #endif - } // namespace Detail + } // namespace Detail - // Some predefined specializations + // Some predefined specializations - template<> - struct StringMaker { - static std::string convert(const std::string& str); - }; + template<> + struct StringMaker { + static std::string convert(const std::string &str); + }; #ifdef CATCH_CONFIG_CPP17_STRING_VIEW - template<> - struct StringMaker { - static std::string convert(std::string_view str); - }; + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; #endif - template<> - struct StringMaker { - static std::string convert(char const * str); - }; - template<> - struct StringMaker { - static std::string convert(char * str); - }; + template<> + struct StringMaker { + static std::string convert(char const *str); + }; + template<> + struct StringMaker { + static std::string convert(char *str); + }; #if defined(CATCH_CONFIG_WCHAR) - template<> - struct StringMaker { - static std::string convert(const std::wstring& wstr); - }; + template<> + struct StringMaker { + static std::string convert(const std::wstring &wstr); + }; -# ifdef CATCH_CONFIG_CPP17_STRING_VIEW - template<> - struct StringMaker { - static std::string convert(std::wstring_view str); - }; -# endif - - template<> - struct StringMaker { - static std::string convert(wchar_t const * str); - }; - template<> - struct StringMaker { - static std::string convert(wchar_t * str); - }; -#endif // CATCH_CONFIG_WCHAR - - template - struct StringMaker { - static std::string convert(char const* str) { - return Detail::convertIntoString( - StringRef( str, Detail::catch_strnlen( str, SZ ) ) ); - } - }; - template - struct StringMaker { - static std::string convert(signed char const* str) { - auto reinterpreted = reinterpret_cast(str); - return Detail::convertIntoString( - StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); - } - }; - template - struct StringMaker { - static std::string convert(unsigned char const* str) { - auto reinterpreted = reinterpret_cast(str); - return Detail::convertIntoString( - StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); - } - }; - -#if defined(CATCH_CONFIG_CPP17_BYTE) - template<> - struct StringMaker { - static std::string convert(std::byte value); - }; -#endif // defined(CATCH_CONFIG_CPP17_BYTE) - template<> - struct StringMaker { - static std::string convert(int value); - }; - template<> - struct StringMaker { - static std::string convert(long value); - }; - template<> - struct StringMaker { - static std::string convert(long long value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned int value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned long value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned long long value); - }; - - template<> - struct StringMaker { - static std::string convert(bool b) { - using namespace std::string_literals; - return b ? "true"s : "false"s; - } - }; - - template<> - struct StringMaker { - static std::string convert(char c); - }; - template<> - struct StringMaker { - static std::string convert(signed char value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned char value); - }; - - template<> - struct StringMaker { - static std::string convert(std::nullptr_t) { - using namespace std::string_literals; - return "nullptr"s; - } - }; - - template<> - struct StringMaker { - static std::string convert(float value); - CATCH_EXPORT static int precision; - }; - - template<> - struct StringMaker { - static std::string convert(double value); - CATCH_EXPORT static int precision; - }; - - template - struct StringMaker { - template - static std::string convert(U* p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } - }; - - template - struct StringMaker { - static std::string convert(R C::* p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } - }; - -#if defined(_MANAGED) - template - struct StringMaker { - static std::string convert( T^ ref ) { - return ::Catch::Detail::clrReferenceToString(ref); - } - }; +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; #endif - namespace Detail { - template - std::string rangeToString(InputIterator first, Sentinel last) { - ReusableStringStream rss; - rss << "{ "; - if (first != last) { - rss << ::Catch::Detail::stringify(*first); - for (++first; first != last; ++first) - rss << ", " << ::Catch::Detail::stringify(*first); - } - rss << " }"; - return rss.str(); - } + template<> + struct StringMaker { + static std::string convert(wchar_t const *str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t *str); + }; +#endif // CATCH_CONFIG_WCHAR + + template + struct StringMaker { + static std::string convert(char const *str) { + return Detail::convertIntoString( + StringRef(str, Detail::catch_strnlen(str, SZ))); } + }; + template + struct StringMaker { + static std::string convert(signed char const *str) { + auto reinterpreted = reinterpret_cast(str); + return Detail::convertIntoString( + StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const *str) { + auto reinterpreted = reinterpret_cast(str); + return Detail::convertIntoString( + StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); + } + }; + +#if defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker { + static std::string convert(std::byte value); + }; +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b) { + using namespace std::string_literals; + return b ? "true"s : "false"s; + } + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char value); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t) { + using namespace std::string_literals; + return "nullptr"s; + } + }; + + template<> + struct StringMaker { + static std::string convert(float value); + CATCH_EXPORT static int precision; + }; + + template<> + struct StringMaker { + static std::string convert(double value); + CATCH_EXPORT static int precision; + }; + + template + struct StringMaker { + template + static std::string convert(U *p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::*p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert(T ^ ref) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, Sentinel last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } // namespace Detail } // namespace Catch @@ -2854,290 +2743,287 @@ namespace Catch { // This means that we do not bring in their headers #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) -# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER -# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER -# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER -# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER +#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +#define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +#define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER #endif // Separate std::pair specialization #if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) #include namespace Catch { - template - struct StringMaker > { - static std::string convert(const std::pair& pair) { - ReusableStringStream rss; - rss << "{ " - << ::Catch::Detail::stringify(pair.first) - << ", " - << ::Catch::Detail::stringify(pair.second) - << " }"; - return rss.str(); - } - }; -} + template + struct StringMaker> { + static std::string convert(const std::pair &pair) { + ReusableStringStream rss; + rss << "{ " + << ::Catch::Detail::stringify(pair.first) + << ", " + << ::Catch::Detail::stringify(pair.second) + << " }"; + return rss.str(); + } + }; +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) #include namespace Catch { - template - struct StringMaker > { - static std::string convert(const std::optional& optional) { - if (optional.has_value()) { - return ::Catch::Detail::stringify(*optional); - } else { - return "{ }"; - } - } - }; - template <> - struct StringMaker { - static std::string convert(const std::nullopt_t&) { - return "{ }"; - } - }; -} + template + struct StringMaker> { + static std::string convert(const std::optional &optional) { + if (optional.has_value()) { + return ::Catch::Detail::stringify(*optional); + } else { + return "{ }"; + } + } + }; + template<> + struct StringMaker { + static std::string convert(const std::nullopt_t &) { + return "{ }"; + } + }; +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) -# include -# include +#include +#include namespace Catch { - namespace Detail { - template - void PrintTuple( const Tuple& tuple, - std::ostream& os, - std::index_sequence ) { - // 1 + Account for when the tuple is empty - char a[1 + sizeof...( Is )] = { - ( ( os << ( Is ? ", " : " " ) - << ::Catch::Detail::stringify( std::get( tuple ) ) ), - '\0' )... }; - (void)a; - } + namespace Detail { + template + void PrintTuple(const Tuple &tuple, + std::ostream &os, + std::index_sequence) { + // 1 + Account for when the tuple is empty + char a[1 + sizeof...(Is)] = { + ((os << (Is ? ", " : " ") + << ::Catch::Detail::stringify(std::get(tuple))), + '\0')...}; + (void)a; + } - } // namespace Detail + } // namespace Detail - template - struct StringMaker> { - static std::string convert( const std::tuple& tuple ) { - ReusableStringStream rss; - rss << '{'; - Detail::PrintTuple( - tuple, - rss.get(), - std::make_index_sequence{} ); - rss << " }"; - return rss.str(); - } - }; + template + struct StringMaker> { + static std::string convert(const std::tuple &tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::PrintTuple( + tuple, + rss.get(), + std::make_index_sequence{}); + rss << " }"; + return rss.str(); + } + }; } // namespace Catch #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) #include namespace Catch { - template<> - struct StringMaker { - static std::string convert(const std::monostate&) { - return "{ }"; - } - }; + template<> + struct StringMaker { + static std::string convert(const std::monostate &) { + return "{ }"; + } + }; - template - struct StringMaker> { - static std::string convert(const std::variant& variant) { - if (variant.valueless_by_exception()) { - return "{valueless variant}"; - } else { - return std::visit( - [](const auto& value) { - return ::Catch::Detail::stringify(value); - }, - variant - ); - } - } - }; -} + template + struct StringMaker> { + static std::string convert(const std::variant &variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit( + [](const auto &value) { + return ::Catch::Detail::stringify(value); + }, + variant); + } + } + }; +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER namespace Catch { - // Import begin/ end from std here - using std::begin; - using std::end; + // Import begin/ end from std here + using std::begin; + using std::end; - namespace Detail { - template - struct is_range_impl : std::false_type {}; + namespace Detail { + template + struct is_range_impl : std::false_type {}; - template - struct is_range_impl()))>> : std::true_type {}; - } // namespace Detail + template + struct is_range_impl()))>> : std::true_type {}; + } // namespace Detail - template - struct is_range : Detail::is_range_impl {}; + template + struct is_range : Detail::is_range_impl {}; #if defined(_MANAGED) // Managed types are never ranges - template - struct is_range { - static const bool value = false; - }; + template + struct is_range { + static const bool value = false; + }; #endif - template - std::string rangeToString( Range const& range ) { - return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + template + std::string rangeToString(Range const &range) { + return ::Catch::Detail::rangeToString(begin(range), end(range)); + } + + // Handle vector specially + template + std::string rangeToString(std::vector const &v) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for (bool b : v) { + if (first) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify(b); } + rss << " }"; + return rss.str(); + } - // Handle vector specially - template - std::string rangeToString( std::vector const& v ) { - ReusableStringStream rss; - rss << "{ "; - bool first = true; - for( bool b : v ) { - if( first ) - first = false; - else - rss << ", "; - rss << ::Catch::Detail::stringify( b ); - } - rss << " }"; - return rss.str(); + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable_v>> { + static std::string convert(R const &range) { + return rangeToString(range); } + }; - template - struct StringMaker::value && !::Catch::Detail::IsStreamInsertable_v>> { - static std::string convert( R const& range ) { - return rangeToString( range ); - } - }; - - template - struct StringMaker { - static std::string convert(T const(&arr)[SZ]) { - return rangeToString(arr); - } - }; - + template + struct StringMaker { + static std::string convert(T const (&arr)[SZ]) { + return rangeToString(arr); + } + }; } // namespace Catch // Separate std::chrono::duration specialization +#include #include #include -#include - namespace Catch { -template -struct ratio_string { + template + struct ratio_string { static std::string symbol() { - Catch::ReusableStringStream rss; - rss << '[' << Ratio::num << '/' - << Ratio::den << ']'; - return rss.str(); + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); } -}; + }; -template <> -struct ratio_string { + template<> + struct ratio_string { static char symbol() { return 'a'; } -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static char symbol() { return 'f'; } -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static char symbol() { return 'p'; } -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static char symbol() { return 'n'; } -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static char symbol() { return 'u'; } -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static char symbol() { return 'm'; } -}; + }; - //////////// - // std::chrono::duration specializations - template - struct StringMaker> { - static std::string convert(std::chrono::duration const& duration) { - ReusableStringStream rss; - rss << duration.count() << ' ' << ratio_string::symbol() << 's'; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " s"; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " m"; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " h"; - return rss.str(); - } - }; + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const &duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; - //////////// - // std::chrono::time_point specialization - // Generic time_point cannot be specialized, only std::chrono::time_point - template - struct StringMaker> { - static std::string convert(std::chrono::time_point const& time_point) { - return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; - } - }; - // std::chrono::time_point specialization - template - struct StringMaker> { - static std::string convert(std::chrono::time_point const& time_point) { - const auto systemish = std::chrono::time_point_cast< - std::chrono::system_clock::duration>( time_point ); - const auto as_time_t = std::chrono::system_clock::to_time_t( systemish ); - return ::Catch::Detail::formatTimeT( as_time_t ); - } - }; -} + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const &time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const &time_point) { + const auto systemish = std::chrono::time_point_cast< + std::chrono::system_clock::duration>(time_point); + const auto as_time_t = std::chrono::system_clock::to_time_t(systemish); + return ::Catch::Detail::formatTimeT(as_time_t); + } + }; +} // namespace Catch +#define INTERNAL_CATCH_REGISTER_ENUM(enumName, ...) \ + namespace Catch { \ + template<> \ + struct StringMaker { \ + static std::string convert(enumName value) { \ + static const auto &enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum(#enumName, #__VA_ARGS__, {__VA_ARGS__}); \ + return static_cast(enumInfo.lookup(static_cast(value))); \ + } \ + }; \ + } -#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ -namespace Catch { \ - template<> struct StringMaker { \ - static std::string convert( enumName value ) { \ - static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ - return static_cast(enumInfo.lookup( static_cast( value ) )); \ - } \ - }; \ -} - -#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) +#define CATCH_REGISTER_ENUM(enumName, ...) INTERNAL_CATCH_REGISTER_ENUM(enumName, __VA_ARGS__) #ifdef _MSC_VER #pragma warning(pop) @@ -3149,125 +3035,121 @@ namespace Catch { \ namespace Catch { - class Approx { - private: - bool equalityComparisonImpl(double other) const; - // Sets and validates the new margin (margin >= 0) - void setMargin(double margin); - // Sets and validates the new epsilon (0 < epsilon < 1) - void setEpsilon(double epsilon); + class Approx { + private: + bool equalityComparisonImpl(double other) const; + // Sets and validates the new margin (margin >= 0) + void setMargin(double margin); + // Sets and validates the new epsilon (0 < epsilon < 1) + void setEpsilon(double epsilon); - public: - explicit Approx ( double value ); + public: + explicit Approx(double value); - static Approx custom(); + static Approx custom(); - Approx operator-() const; + Approx operator-() const; - template ::value>> - Approx operator()( T const& value ) const { - Approx approx( static_cast(value) ); - approx.m_epsilon = m_epsilon; - approx.m_margin = m_margin; - approx.m_scale = m_scale; - return approx; - } + template::value>> + Approx operator()(T const &value) const { + Approx approx(static_cast(value)); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } - template ::value>> - explicit Approx( T const& value ): Approx(static_cast(value)) - {} + template::value>> + explicit Approx(T const &value) + : Approx(static_cast(value)) {} + template::value>> + friend bool operator==(const T &lhs, Approx const &rhs) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } - template ::value>> - friend bool operator == ( const T& lhs, Approx const& rhs ) { - auto lhs_v = static_cast(lhs); - return rhs.equalityComparisonImpl(lhs_v); - } + template::value>> + friend bool operator==(Approx const &lhs, const T &rhs) { + return operator==(rhs, lhs); + } - template ::value>> - friend bool operator == ( Approx const& lhs, const T& rhs ) { - return operator==( rhs, lhs ); - } + template::value>> + friend bool operator!=(T const &lhs, Approx const &rhs) { + return !operator==(lhs, rhs); + } - template ::value>> - friend bool operator != ( T const& lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } + template::value>> + friend bool operator!=(Approx const &lhs, T const &rhs) { + return !operator==(rhs, lhs); + } - template ::value>> - friend bool operator != ( Approx const& lhs, T const& rhs ) { - return !operator==( rhs, lhs ); - } + template::value>> + friend bool operator<=(T const &lhs, Approx const &rhs) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } - template ::value>> - friend bool operator <= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) < rhs.m_value || lhs == rhs; - } + template::value>> + friend bool operator<=(Approx const &lhs, T const &rhs) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } - template ::value>> - friend bool operator <= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value < static_cast(rhs) || lhs == rhs; - } + template::value>> + friend bool operator>=(T const &lhs, Approx const &rhs) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } - template ::value>> - friend bool operator >= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) > rhs.m_value || lhs == rhs; - } + template::value>> + friend bool operator>=(Approx const &lhs, T const &rhs) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } - template ::value>> - friend bool operator >= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value > static_cast(rhs) || lhs == rhs; - } + template::value>> + Approx &epsilon(T const &newEpsilon) { + const auto epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } - template ::value>> - Approx& epsilon( T const& newEpsilon ) { - const auto epsilonAsDouble = static_cast(newEpsilon); - setEpsilon(epsilonAsDouble); - return *this; - } + template::value>> + Approx &margin(T const &newMargin) { + const auto marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } - template ::value>> - Approx& margin( T const& newMargin ) { - const auto marginAsDouble = static_cast(newMargin); - setMargin(marginAsDouble); - return *this; - } + template::value>> + Approx &scale(T const &newScale) { + m_scale = static_cast(newScale); + return *this; + } - template ::value>> - Approx& scale( T const& newScale ) { - m_scale = static_cast(newScale); - return *this; - } + std::string toString() const; - std::string toString() const; + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; - private: - double m_epsilon; - double m_margin; - double m_scale; - double m_value; - }; + namespace literals { + Approx operator""_a(long double val); + Approx operator""_a(unsigned long long val); + } // end namespace literals -namespace literals { - Approx operator ""_a(long double val); - Approx operator ""_a(unsigned long long val); -} // end namespace literals - -template<> -struct StringMaker { - static std::string convert(Catch::Approx const& value); -}; + template<> + struct StringMaker { + static std::string convert(Catch::Approx const &value); + }; } // end namespace Catch #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 @@ -3276,50 +3158,44 @@ struct StringMaker { namespace Catch { - struct SourceLineInfo { + struct SourceLineInfo { + SourceLineInfo() = delete; + constexpr SourceLineInfo(char const *_file, std::size_t _line) noexcept + : file(_file), line(_line) {} - 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; - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; + char const *file; + std::size_t line; - char const* file; - std::size_t line; - - friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); - }; -} + friend std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info); + }; +} // namespace Catch #define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + ::Catch::SourceLineInfo(__FILE__, static_cast(__LINE__)) #endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED namespace Catch { - struct AssertionInfo { - // AssertionInfo() = delete; + struct AssertionInfo { + // AssertionInfo() = delete; - StringRef macroName; - SourceLineInfo lineInfo; - StringRef capturedExpression; - ResultDisposition::Flags resultDisposition; - }; + 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 @@ -3327,28 +3203,28 @@ namespace Catch { namespace Catch { - class ITransientExpression; + class ITransientExpression; - class LazyExpression { - friend class AssertionHandler; - friend struct AssertionStats; - friend class RunContext; + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; - ITransientExpression const* m_transientExpression = nullptr; - bool m_isNegated; - public: - constexpr LazyExpression( bool isNegated ): - m_isNegated(isNegated) - {} - constexpr LazyExpression(LazyExpression const& other) = default; - LazyExpression& operator = ( LazyExpression const& ) = delete; + ITransientExpression const *m_transientExpression = nullptr; + bool m_isNegated; - constexpr explicit operator bool() const { - return m_transientExpression != nullptr; - } + public: + constexpr LazyExpression(bool isNegated) + : m_isNegated(isNegated) {} + constexpr LazyExpression(LazyExpression const &other) = default; + LazyExpression &operator=(LazyExpression const &) = delete; - friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; - }; + constexpr explicit operator bool() const { + return m_transientExpression != nullptr; + } + + friend auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream &; + }; } // namespace Catch @@ -3358,65 +3234,61 @@ namespace Catch { namespace Catch { - struct AssertionResultData - { - AssertionResultData() = delete; + struct AssertionResultData { + AssertionResultData() = delete; - AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression); - std::string message; - mutable std::string reconstructedExpression; - LazyExpression lazyExpression; - ResultWas::OfType resultType; + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; - std::string reconstructExpression() const; - }; + std::string reconstructExpression() const; + }; - class AssertionResult { - public: - AssertionResult() = delete; - AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); + 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; + 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; - }; + // protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch #endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED - #ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED #define CATCH_CASE_SENSITIVE_HPP_INCLUDED namespace Catch { - enum class CaseSensitive { Yes, No }; + enum class CaseSensitive { Yes, + No }; } // namespace Catch #endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED - #ifndef CATCH_CONFIG_HPP_INCLUDED #define CATCH_CONFIG_HPP_INCLUDED - - #ifndef CATCH_TEST_SPEC_HPP_INCLUDED #define CATCH_TEST_SPEC_HPP_INCLUDED @@ -3425,36 +3297,31 @@ namespace Catch { #pragma clang diagnostic ignored "-Wpadded" #endif - - #ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED #define CATCH_WILDCARD_PATTERN_HPP_INCLUDED - #include -namespace Catch -{ - class WildcardPattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - - WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity ); - bool matches( std::string const& str ) const; - - private: - std::string normaliseString( std::string const& str ) const; - CaseSensitive m_caseSensitivity; - WildcardPosition m_wildcard = NoWildcard; - std::string m_pattern; +namespace Catch { + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd }; -} + + public: + WildcardPattern(std::string const &pattern, CaseSensitive caseSensitivity); + bool matches(std::string const &str) const; + + private: + std::string normaliseString(std::string const &str) const; + CaseSensitive m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} // namespace Catch #endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED @@ -3464,95 +3331,97 @@ namespace Catch namespace Catch { - class IConfig; - struct TestCaseInfo; - class TestCaseHandle; - - class TestSpec { - - class Pattern { - public: - explicit Pattern( std::string const& name ); - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - std::string const& name() const; - private: - virtual void serializeTo( std::ostream& out ) const = 0; - // Writes string that would be reparsed into the pattern - friend std::ostream& operator<<(std::ostream& out, - Pattern const& pattern) { - pattern.serializeTo( out ); - return out; - } - - std::string const m_name; - }; - - class NamePattern : public Pattern { - public: - explicit NamePattern( std::string const& name, std::string const& filterString ); - bool matches( TestCaseInfo const& testCase ) const override; - private: - void serializeTo( std::ostream& out ) const override; - - WildcardPattern m_wildcardPattern; - }; - - class TagPattern : public Pattern { - public: - explicit TagPattern( std::string const& tag, std::string const& filterString ); - bool matches( TestCaseInfo const& testCase ) const override; - private: - void serializeTo( std::ostream& out ) const override; - - std::string m_tag; - }; - - struct Filter { - std::vector> m_required; - std::vector> m_forbidden; - - //! Serializes this filter into a string that would be parsed into - //! an equivalent filter - void serializeTo( std::ostream& out ) const; - friend std::ostream& operator<<(std::ostream& out, Filter const& f) { - f.serializeTo( out ); - return out; - } - - bool matches( TestCaseInfo const& testCase ) const; - }; - - static std::string extractFilterName( Filter const& filter ); + class IConfig; + struct TestCaseInfo; + class TestCaseHandle; + class TestSpec { + class Pattern { public: - struct FilterMatch { - std::string name; - std::vector tests; - }; - using Matches = std::vector; - using vectorStrings = std::vector; - - bool hasFilters() const; - bool matches( TestCaseInfo const& testCase ) const; - Matches matchesByFilter( std::vector const& testCases, IConfig const& config ) const; - const vectorStrings & getInvalidSpecs() const; + explicit Pattern(std::string const &name); + virtual ~Pattern(); + virtual bool matches(TestCaseInfo const &testCase) const = 0; + std::string const &name() const; private: - std::vector m_filters; - std::vector m_invalidSpecs; + virtual void serializeTo(std::ostream &out) const = 0; + // Writes string that would be reparsed into the pattern + friend std::ostream &operator<<(std::ostream &out, + Pattern const &pattern) { + pattern.serializeTo(out); + return out; + } - friend class TestSpecParser; - //! Serializes this test spec into a string that would be parsed into - //! equivalent test spec - void serializeTo( std::ostream& out ) const; - friend std::ostream& operator<<(std::ostream& out, - TestSpec const& spec) { - spec.serializeTo( out ); - return out; - } + std::string const m_name; }; -} + + class NamePattern : public Pattern { + public: + explicit NamePattern(std::string const &name, std::string const &filterString); + bool matches(TestCaseInfo const &testCase) const override; + + private: + void serializeTo(std::ostream &out) const override; + + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + explicit TagPattern(std::string const &tag, std::string const &filterString); + bool matches(TestCaseInfo const &testCase) const override; + + private: + void serializeTo(std::ostream &out) const override; + + std::string m_tag; + }; + + struct Filter { + std::vector> m_required; + std::vector> m_forbidden; + + //! Serializes this filter into a string that would be parsed into + //! an equivalent filter + void serializeTo(std::ostream &out) const; + friend std::ostream &operator<<(std::ostream &out, Filter const &f) { + f.serializeTo(out); + return out; + } + + bool matches(TestCaseInfo const &testCase) const; + }; + + static std::string extractFilterName(Filter const &filter); + + public: + struct FilterMatch { + std::string name; + std::vector tests; + }; + using Matches = std::vector; + using vectorStrings = std::vector; + + bool hasFilters() const; + bool matches(TestCaseInfo const &testCase) const; + Matches matchesByFilter(std::vector const &testCases, IConfig const &config) const; + const vectorStrings &getInvalidSpecs() const; + + private: + std::vector m_filters; + std::vector m_invalidSpecs; + + friend class TestSpecParser; + //! Serializes this test spec into a string that would be parsed into + //! equivalent test spec + void serializeTo(std::ostream &out) const; + friend std::ostream &operator<<(std::ostream &out, + TestSpec const &spec) { + spec.serializeTo(out); + return out; + } + }; +} // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop @@ -3560,145 +3429,141 @@ namespace Catch { #endif // CATCH_TEST_SPEC_HPP_INCLUDED - #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED - #include namespace Catch { - // An optional type - template - class Optional { - public: - Optional(): nullableValue( nullptr ) {} - ~Optional() { reset(); } + // An optional type + template + class Optional { + public: + Optional() + : nullableValue(nullptr) {} + ~Optional() { reset(); } - Optional( T const& _value ): - nullableValue( new ( storage ) T( _value ) ) {} - Optional( T&& _value ): - nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} + 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=(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( Optional const& _other ): - nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} - Optional( Optional&& _other ): - nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) - : nullptr ) {} + 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 ); } - } - return *this; - } - Optional& operator=( Optional&& _other ) { - if ( &_other != this ) { - reset(); - if ( _other ) { - nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); - } - } - return *this; + Optional &operator=(Optional const &_other) { + if (&_other != this) { + reset(); + if (_other) { nullableValue = new (storage) T(*_other); } + } + return *this; + } + 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(); } - nullableValue = nullptr; - } + void reset() { + if (nullableValue) { nullableValue->~T(); } + nullableValue = nullptr; + } - T& operator*() { - assert(nullableValue); - return *nullableValue; - } - T const& operator*() const { - assert(nullableValue); - return *nullableValue; - } - T* operator->() { - assert(nullableValue); - return nullableValue; - } - const T* operator->() const { - assert(nullableValue); - return nullableValue; - } + T &operator*() { + assert(nullableValue); + return *nullableValue; + } + T const &operator*() const { + assert(nullableValue); + return *nullableValue; + } + T *operator->() { + assert(nullableValue); + return nullableValue; + } + const T *operator->() const { + assert(nullableValue); + return nullableValue; + } - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } + T valueOr(T const &defaultValue) const { + return nullableValue ? *nullableValue : defaultValue; + } - bool some() const { return nullableValue != nullptr; } - bool none() const { return nullableValue == nullptr; } + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } - bool operator !() const { return nullableValue == nullptr; } - explicit operator bool() const { - return some(); - } + bool operator!() const { return nullableValue == nullptr; } + explicit operator bool() const { + return some(); + } - friend bool operator==(Optional const& a, Optional const& b) { - if (a.none() && b.none()) { - return true; - } else if (a.some() && b.some()) { - return *a == *b; - } else { - return false; - } - } - friend bool operator!=(Optional const& a, Optional const& b) { - return !( a == b ); - } + friend bool operator==(Optional const &a, Optional const &b) { + if (a.none() && b.none()) { + return true; + } else if (a.some() && b.some()) { + return *a == *b; + } else { + return false; + } + } + friend bool operator!=(Optional const &a, Optional const &b) { + return !(a == b); + } - private: - T* nullableValue; - alignas(alignof(T)) char storage[sizeof(T)]; - }; + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; } // end namespace Catch #endif // CATCH_OPTIONAL_HPP_INCLUDED - #ifndef CATCH_PATH_FILTER_HPP_INCLUDED #define CATCH_PATH_FILTER_HPP_INCLUDED - #include namespace Catch { - struct PathFilter { - enum class For { - Section, - Generator, - }; - PathFilter( For type_, std::string filter_ ): - type( type_ ), filter( CATCH_MOVE( filter_ ) ) {} - - For type; - std::string filter; - - friend bool operator==( PathFilter const& lhs, PathFilter const& rhs ); + struct PathFilter { + enum class For { + Section, + Generator, }; + PathFilter(For type_, std::string filter_) + : type(type_), filter(CATCH_MOVE(filter_)) {} + + For type; + std::string filter; + + friend bool operator==(PathFilter const &lhs, PathFilter const &rhs); + }; } // end namespace Catch #endif // CATCH_PATH_FILTER_HPP_INCLUDED - #ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED #define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED @@ -3706,93 +3571,91 @@ namespace Catch { namespace Catch { - enum class GenerateFrom { - Time, - RandomDevice, - //! Currently equivalent to RandomDevice, but can change at any point - Default - }; + enum class GenerateFrom { + Time, + RandomDevice, + //! Currently equivalent to RandomDevice, but can change at any point + Default + }; - std::uint32_t generateRandomSeed(GenerateFrom from); + std::uint32_t generateRandomSeed(GenerateFrom from); } // end namespace Catch #endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED - #ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED #define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED - #include #include #include namespace Catch { - enum class ColourMode : std::uint8_t; + enum class ColourMode : std::uint8_t; - namespace Detail { - //! Splits the reporter spec into reporter name and kv-pair options - std::vector splitReporterSpec( StringRef reporterSpec ); + namespace Detail { + //! Splits the reporter spec into reporter name and kv-pair options + std::vector splitReporterSpec(StringRef reporterSpec); - Optional stringToColourMode( StringRef colourMode ); + Optional stringToColourMode(StringRef colourMode); + } // namespace Detail + + /** + * Structured reporter spec that a reporter can be created from + * + * Parsing has been validated, but semantics have not. This means e.g. + * that the colour mode is known to Catch2, but it might not be + * compiled into the binary, and the output filename might not be + * openable. + */ + class ReporterSpec { + std::string m_name; + Optional m_outputFileName; + Optional m_colourMode; + std::map m_customOptions; + + friend bool operator==(ReporterSpec const &lhs, + ReporterSpec const &rhs); + friend bool operator!=(ReporterSpec const &lhs, + ReporterSpec const &rhs) { + return !(lhs == rhs); } - /** - * Structured reporter spec that a reporter can be created from - * - * Parsing has been validated, but semantics have not. This means e.g. - * that the colour mode is known to Catch2, but it might not be - * compiled into the binary, and the output filename might not be - * openable. - */ - class ReporterSpec { - std::string m_name; - Optional m_outputFileName; - Optional m_colourMode; - std::map m_customOptions; + public: + ReporterSpec( + std::string name, + Optional outputFileName, + Optional colourMode, + std::map customOptions); - friend bool operator==( ReporterSpec const& lhs, - ReporterSpec const& rhs ); - friend bool operator!=( ReporterSpec const& lhs, - ReporterSpec const& rhs ) { - return !( lhs == rhs ); - } + std::string const &name() const { return m_name; } - public: - ReporterSpec( - std::string name, - Optional outputFileName, - Optional colourMode, - std::map customOptions ); + Optional const &outputFile() const { + return m_outputFileName; + } - std::string const& name() const { return m_name; } + Optional const &colourMode() const { return m_colourMode; } - Optional const& outputFile() const { - return m_outputFileName; - } + std::map const &customOptions() const { + return m_customOptions; + } + }; - Optional const& colourMode() const { return m_colourMode; } + /** + * Parses provided reporter spec string into + * + * Returns empty optional on errors, e.g. + * * field that is not first and not a key+value pair + * * duplicated keys in kv pair + * * unknown catch reporter option + * * empty key/value in an custom kv pair + * * ... + */ + Optional parseReporterSpec(StringRef reporterSpec); - std::map const& customOptions() const { - return m_customOptions; - } - }; - - /** - * Parses provided reporter spec string into - * - * Returns empty optional on errors, e.g. - * * field that is not first and not a key+value pair - * * duplicated keys in kv pair - * * unknown catch reporter option - * * empty key/value in an custom kv pair - * * ... - */ - Optional parseReporterSpec( StringRef reporterSpec ); - -} +} // namespace Catch #endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED @@ -3803,162 +3666,154 @@ namespace Catch { namespace Catch { - class IStream; + class IStream; - /** - * `ReporterSpec` but with the defaults filled in. - * - * Like `ReporterSpec`, the semantics are unchecked. - */ - struct ProcessedReporterSpec { - std::string name; - std::string outputFilename; - ColourMode colourMode; - std::map customOptions; - friend bool operator==( ProcessedReporterSpec const& lhs, - ProcessedReporterSpec const& rhs ); - friend bool operator!=( ProcessedReporterSpec const& lhs, - ProcessedReporterSpec const& rhs ) { - return !( lhs == rhs ); - } - }; + /** + * `ReporterSpec` but with the defaults filled in. + * + * Like `ReporterSpec`, the semantics are unchecked. + */ + struct ProcessedReporterSpec { + std::string name; + std::string outputFilename; + ColourMode colourMode; + std::map customOptions; + friend bool operator==(ProcessedReporterSpec const &lhs, + ProcessedReporterSpec const &rhs); + friend bool operator!=(ProcessedReporterSpec const &lhs, + ProcessedReporterSpec const &rhs) { + return !(lhs == rhs); + } + }; - struct ConfigData { + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listListeners = false; - bool listTests = false; - bool listTags = false; - bool listReporters = false; - bool listListeners = false; + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + bool allowZeroTests = false; - bool showSuccessfulTests = false; - bool shouldDebugBreak = false; - bool noThrow = false; - bool showHelp = false; - bool showInvisibles = false; - bool filenamesAsTags = false; - bool libIdentify = false; - bool allowZeroTests = false; + int abortAfter = -1; + uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default); - int abortAfter = -1; - uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default); + unsigned int shardCount = 1; + unsigned int shardIndex = 0; - unsigned int shardCount = 1; - unsigned int shardIndex = 0; + bool skipBenchmarks = false; + bool benchmarkNoAnalysis = false; + unsigned int benchmarkSamples = 100; + double benchmarkConfidenceInterval = 0.95; + unsigned int benchmarkResamples = 100'000; + std::chrono::milliseconds::rep benchmarkWarmupTime = 100; - bool skipBenchmarks = false; - bool benchmarkNoAnalysis = false; - unsigned int benchmarkSamples = 100; - double benchmarkConfidenceInterval = 0.95; - unsigned int benchmarkResamples = 100'000; - std::chrono::milliseconds::rep benchmarkWarmupTime = 100; + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; + TestRunOrder runOrder = TestRunOrder::Randomized; + ColourMode defaultColourMode = ColourMode::PlatformDefault; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; - Verbosity verbosity = Verbosity::Normal; - WarnAbout::What warnings = WarnAbout::Nothing; - ShowDurations showDurations = ShowDurations::DefaultForReporter; - double minDuration = -1; - TestRunOrder runOrder = TestRunOrder::Randomized; - ColourMode defaultColourMode = ColourMode::PlatformDefault; - WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + std::string defaultOutputFilename; + std::string name; + std::string processName; + std::vector reporterSpecifications; - std::string defaultOutputFilename; - std::string name; - std::string processName; - std::vector reporterSpecifications; + std::vector testsOrTags; + std::vector pathFilters; + bool useNewPathFilteringBehaviour = false; - std::vector testsOrTags; - std::vector pathFilters; - bool useNewPathFilteringBehaviour = false; + std::string prematureExitGuardFilePath; + }; - std::string prematureExitGuardFilePath; - }; + class Config : public IConfig { + public: + Config() = default; + Config(ConfigData const &data); + ~Config() override; // = default in the cpp file + bool listTests() const; + bool listTags() const; + bool listReporters() const; + bool listListeners() const; - class Config : public IConfig { - public: + std::vector const &getReporterSpecs() const; + std::vector const & + getProcessedReporterSpecs() const; - Config() = default; - Config( ConfigData const& data ); - ~Config() override; // = default in the cpp file + std::vector const &getTestsOrTags() const override; + std::vector const &getPathFilters() const override; + bool useNewFilterBehaviour() const override; - bool listTests() const; - bool listTags() const; - bool listReporters() const; - bool listListeners() const; + TestSpec const &testSpec() const override; + bool hasTestFilters() const override; - std::vector const& getReporterSpecs() const; - std::vector const& - getProcessedReporterSpecs() const; + bool showHelp() const; - std::vector const& getTestsOrTags() const override; - std::vector const& getPathFilters() const override; - bool useNewFilterBehaviour() const override; + std::string const &getExitGuardFilePath() const; - TestSpec const& testSpec() const override; - bool hasTestFilters() const override; + // IConfig interface + bool allowThrows() const override; + StringRef name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutUnmatchedTestSpecs() const override; + bool warnAboutInfiniteGenerators() const override; + bool zeroTestsCountAsSuccess() const override; + ShowDurations showDurations() const override; + double minDuration() const override; + TestRunOrder runOrder() const override; + uint32_t rngSeed() const override; + unsigned int shardCount() const override; + unsigned int shardIndex() const override; + ColourMode defaultColourMode() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + bool skipBenchmarks() const override; + bool benchmarkNoAnalysis() const override; + unsigned int benchmarkSamples() const override; + double benchmarkConfidenceInterval() const override; + unsigned int benchmarkResamples() const override; + std::chrono::milliseconds benchmarkWarmupTime() const override; - bool showHelp() const; + private: + // Reads Bazel env vars and applies them to the config + void readBazelEnvVars(); - std::string const& getExitGuardFilePath() const; - - // IConfig interface - bool allowThrows() const override; - StringRef name() const override; - bool includeSuccessfulResults() const override; - bool warnAboutMissingAssertions() const override; - bool warnAboutUnmatchedTestSpecs() const override; - bool warnAboutInfiniteGenerators() const override; - bool zeroTestsCountAsSuccess() const override; - ShowDurations showDurations() const override; - double minDuration() const override; - TestRunOrder runOrder() const override; - uint32_t rngSeed() const override; - unsigned int shardCount() const override; - unsigned int shardIndex() const override; - ColourMode defaultColourMode() const override; - bool shouldDebugBreak() const override; - int abortAfter() const override; - bool showInvisibles() const override; - Verbosity verbosity() const override; - bool skipBenchmarks() const override; - bool benchmarkNoAnalysis() const override; - unsigned int benchmarkSamples() const override; - double benchmarkConfidenceInterval() const override; - unsigned int benchmarkResamples() const override; - std::chrono::milliseconds benchmarkWarmupTime() const override; - - private: - // Reads Bazel env vars and applies them to the config - void readBazelEnvVars(); - - ConfigData m_data; - std::vector m_processedReporterSpecs; - TestSpec m_testSpec; - bool m_hasTestFilters = false; - }; + ConfigData m_data; + std::vector m_processedReporterSpecs; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; } // end namespace Catch #endif // CATCH_CONFIG_HPP_INCLUDED - #ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED #define CATCH_GET_RANDOM_SEED_HPP_INCLUDED #include namespace Catch { - //! Returns Catch2's current RNG seed. - std::uint32_t getSeed(); -} + //! Returns Catch2's current RNG seed. + std::uint32_t getSeed(); +} // namespace Catch #endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED - #ifndef CATCH_MESSAGE_HPP_INCLUDED #define CATCH_MESSAGE_HPP_INCLUDED - - - /** \file * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option * @@ -3973,51 +3828,44 @@ namespace Catch { #ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED #define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED - #if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) - #define CATCH_CONFIG_PREFIX_MESSAGES +#define CATCH_CONFIG_PREFIX_MESSAGES #endif #endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED - #ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED #define CATCH_STREAM_END_STOP_HPP_INCLUDED - namespace Catch { - // Use this in variadic streaming macros to allow - // << +StreamEndStop - // as well as - // << stuff +StreamEndStop - struct StreamEndStop { - constexpr StringRef operator+() const { return StringRef(); } + // Use this in variadic streaming macros to allow + // << +StreamEndStop + // as well as + // << stuff +StreamEndStop + struct StreamEndStop { + constexpr StringRef operator+() const { return StringRef(); } - template - constexpr friend T const& operator+( T const& value, StreamEndStop ) { - return value; - } - }; + template + constexpr friend T const &operator+(T const &value, StreamEndStop) { + return value; + } + }; } // namespace Catch #endif // CATCH_STREAM_END_STOP_HPP_INCLUDED - #ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED #define CATCH_MESSAGE_INFO_HPP_INCLUDED - - #ifndef CATCH_DEPRECATION_MACRO_HPP_INCLUDED #define CATCH_DEPRECATION_MACRO_HPP_INCLUDED - -#if !defined( CATCH_CONFIG_NO_DEPRECATION_ANNOTATIONS ) -# define CATCH_DEPRECATED( msg ) [[deprecated( msg )]] +#if !defined(CATCH_CONFIG_NO_DEPRECATION_ANNOTATIONS) +#define CATCH_DEPRECATED(msg) [[deprecated(msg)]] #else -# define CATCH_DEPRECATED( msg ) +#define CATCH_DEPRECATED(msg) #endif #endif // CATCH_DEPRECATION_MACRO_HPP_INCLUDED @@ -4026,27 +3874,27 @@ namespace Catch { namespace Catch { - struct MessageInfo { - MessageInfo( StringRef _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ); + struct MessageInfo { + MessageInfo(StringRef _macroName, + SourceLineInfo const &_lineInfo, + ResultWas::OfType _type); - StringRef macroName; - std::string message; - SourceLineInfo lineInfo; - ResultWas::OfType type; - // The "ID" of the message, used to know when to remove it from reporter context. - unsigned int sequence; + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + // The "ID" of the message, used to know when to remove it from reporter context. + unsigned int sequence; - CATCH_DEPRECATED( "Explicitly use the 'sequence' member instead" ) - bool operator == (MessageInfo const& other) const { - return sequence == other.sequence; - } - CATCH_DEPRECATED( "Explicitly use the 'sequence' member instead" ) - bool operator < (MessageInfo const& other) const { - return sequence < other.sequence; - } - }; + CATCH_DEPRECATED("Explicitly use the 'sequence' member instead") + bool operator==(MessageInfo const &other) const { + return sequence == other.sequence; + } + CATCH_DEPRECATED("Explicitly use the 'sequence' member instead") + bool operator<(MessageInfo const &other) const { + return sequence < other.sequence; + } + }; } // end namespace Catch @@ -4057,145 +3905,138 @@ namespace Catch { namespace Catch { - struct SourceLineInfo; - class IResultCapture; + struct SourceLineInfo; + class IResultCapture; - struct MessageStream { + struct MessageStream { + template + MessageStream &operator<<(T const &value) { + m_stream << value; + return *this; + } - template - MessageStream& operator << ( T const& value ) { - m_stream << value; - return *this; - } + ReusableStringStream m_stream; + }; - ReusableStringStream m_stream; - }; + struct MessageBuilder : MessageStream { + MessageBuilder(StringRef macroName, + SourceLineInfo const &lineInfo, + ResultWas::OfType type) + : m_info(macroName, lineInfo, type) {} - struct MessageBuilder : MessageStream { - MessageBuilder( StringRef macroName, - SourceLineInfo const& lineInfo, - ResultWas::OfType type ): - m_info(macroName, lineInfo, type) {} + template + MessageBuilder &&operator<<(T const &value) && { + m_stream << value; + return CATCH_MOVE(*this); + } - template - MessageBuilder&& operator << ( T const& value ) && { - m_stream << value; - return CATCH_MOVE(*this); - } + MessageInfo m_info; + }; - MessageInfo m_info; - }; + class ScopedMessage { + public: + explicit ScopedMessage(MessageBuilder &&builder); + ScopedMessage(ScopedMessage &duplicate) = delete; + ScopedMessage(ScopedMessage &&old) noexcept; + ~ScopedMessage(); - class ScopedMessage { - public: - explicit ScopedMessage( MessageBuilder&& builder ); - ScopedMessage( ScopedMessage& duplicate ) = delete; - ScopedMessage( ScopedMessage&& old ) noexcept; - ~ScopedMessage(); + unsigned int m_messageId; + bool m_moved = false; + }; - unsigned int m_messageId; - bool m_moved = false; - }; + class Capturer { + std::vector m_messages; + size_t m_captured = 0; + bool m_isScoped = false; - class Capturer { - std::vector m_messages; - size_t m_captured = 0; - bool m_isScoped = false; - public: - Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names, bool isScoped ); + public: + Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, StringRef names, bool isScoped); - Capturer(Capturer const&) = delete; - Capturer& operator=(Capturer const&) = delete; + Capturer(Capturer const &) = delete; + Capturer &operator=(Capturer const &) = delete; - ~Capturer(); + ~Capturer(); - void captureValue( size_t index, std::string const& value ); + void captureValue(size_t index, std::string const &value); - template - void captureValues( size_t index, T const& value ) { - captureValue( index, Catch::Detail::stringify( value ) ); - } + template + void captureValues(size_t index, T const &value) { + captureValue(index, Catch::Detail::stringify(value)); + } - template - void captureValues( size_t index, T const& value, Ts const&... values ) { - captureValue( index, Catch::Detail::stringify(value) ); - captureValues( index+1, values... ); - } - }; + template + void captureValues(size_t index, T const &value, Ts const &...values) { + captureValue(index, Catch::Detail::stringify(value)); + captureValues(index + 1, values...); + } + }; } // end namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ - catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ - catchAssertionHandler.complete(); \ - } while( false ) +#define INTERNAL_CATCH_MSG(macroName, messageType, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition); \ + catchAssertionHandler.handleMessage(messageType, (Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop()).m_stream.str()); \ + catchAssertionHandler.complete(); \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_CAPTURE( varName, macroName, scopedCapture, ... ) \ - Catch::Capturer varName( macroName##_catch_sr, \ - CATCH_INTERNAL_LINEINFO, \ - Catch::ResultWas::Info, \ - #__VA_ARGS__##_catch_sr, \ - scopedCapture ); \ - varName.captureValues( 0, __VA_ARGS__ ) +#define INTERNAL_CATCH_CAPTURE(varName, macroName, scopedCapture, ...) \ + Catch::Capturer varName(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info, \ + #__VA_ARGS__##_catch_sr, \ + scopedCapture); \ + varName.captureValues(0, __VA_ARGS__) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( macroName, log ) \ - const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) +#define INTERNAL_CATCH_INFO(macroName, log) \ + const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME(scopedMessage)(Catch::MessageBuilder(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info) << log) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ - Catch::IResultCapture::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) - +#define INTERNAL_CATCH_UNSCOPED_INFO(macroName, log) \ + Catch::IResultCapture::emplaceUnscopedMessage(Catch::MessageBuilder(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info) << log) #if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) - #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) - #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) - #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", true, __VA_ARGS__ ) - #define CATCH_UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_UNSCOPED_CAPTURE", false, __VA_ARGS__ ) +#define CATCH_INFO(msg) INTERNAL_CATCH_INFO("CATCH_INFO", msg) +#define CATCH_UNSCOPED_INFO(msg) INTERNAL_CATCH_UNSCOPED_INFO("CATCH_UNSCOPED_INFO", msg) +#define CATCH_WARN(msg) INTERNAL_CATCH_MSG("CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg) +#define CATCH_CAPTURE(...) INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", true, __VA_ARGS__) +#define CATCH_UNSCOPED_CAPTURE(...) INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_UNSCOPED_CAPTURE", false, __VA_ARGS__) #elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) - #define CATCH_INFO( msg ) (void)(0) - #define CATCH_UNSCOPED_INFO( msg ) (void)(0) - #define CATCH_WARN( msg ) (void)(0) - #define CATCH_CAPTURE( ... ) (void)(0) - #define CATCH_UNSCOPED_CAPTURE( ... ) (void)(0) +#define CATCH_INFO(msg) (void)(0) +#define CATCH_UNSCOPED_INFO(msg) (void)(0) +#define CATCH_WARN(msg) (void)(0) +#define CATCH_CAPTURE(...) (void)(0) +#define CATCH_UNSCOPED_CAPTURE(...) (void)(0) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) - #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) - #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) - #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) - #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", true, __VA_ARGS__ ) - #define UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "UNSCOPED_CAPTURE", false, __VA_ARGS__ ) +#define INFO(msg) INTERNAL_CATCH_INFO("INFO", msg) +#define UNSCOPED_INFO(msg) INTERNAL_CATCH_UNSCOPED_INFO("UNSCOPED_INFO", msg) +#define WARN(msg) INTERNAL_CATCH_MSG("WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg) +#define CAPTURE(...) INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", true, __VA_ARGS__) +#define UNSCOPED_CAPTURE(...) INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), "UNSCOPED_CAPTURE", false, __VA_ARGS__) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) - #define INFO( msg ) (void)(0) - #define UNSCOPED_INFO( msg ) (void)(0) - #define WARN( msg ) (void)(0) - #define CAPTURE( ... ) (void)(0) - #define UNSCOPED_CAPTURE( ... ) (void)(0) +#define INFO(msg) (void)(0) +#define UNSCOPED_INFO(msg) (void)(0) +#define WARN(msg) (void)(0) +#define CAPTURE(...) (void)(0) +#define UNSCOPED_CAPTURE(...) (void)(0) #endif // end of user facing macro declarations - - - #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 @@ -4203,31 +4044,30 @@ namespace Catch { namespace Catch { - struct Counts { - Counts operator - ( Counts const& other ) const; - Counts& operator += ( Counts const& other ); + 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 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; - }; + std::uint64_t passed = 0; + std::uint64_t failed = 0; + std::uint64_t failedButOk = 0; + std::uint64_t skipped = 0; + }; - struct Totals { + struct Totals { + Totals operator-(Totals const &other) const; + Totals &operator+=(Totals const &other); - Totals operator - ( Totals const& other ) const; - Totals& operator += ( Totals const& other ); + Totals delta(Totals const &prevTotals) const; - Totals delta( Totals const& prevTotals ) const; - - Counts assertions; - Counts testCases; - }; -} + Counts assertions; + Counts testCases; + }; +} // namespace Catch #endif // CATCH_TOTALS_HPP_INCLUDED @@ -4235,65 +4075,57 @@ namespace Catch { 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) - {} + 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; - }; + std::string name; + SourceLineInfo lineInfo; + }; - struct SectionEndInfo { - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; + 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 - - #ifndef CATCH_COMMANDLINE_HPP_INCLUDED #define CATCH_COMMANDLINE_HPP_INCLUDED - - #ifndef CATCH_CLARA_HPP_INCLUDED #define CATCH_CLARA_HPP_INCLUDED -#if defined( __clang__ ) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wweak-vtables" -# pragma clang diagnostic ignored "-Wshadow" -# pragma clang diagnostic ignored "-Wdeprecated" +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wdeprecated" #endif -#if defined( __GNUC__ ) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wsign-conversion" +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" #endif #ifndef CLARA_CONFIG_OPTIONAL_TYPE -# ifdef __has_include -# if __has_include( ) && __cplusplus >= 201703L -# include -# define CLARA_CONFIG_OPTIONAL_TYPE std::optional -# endif -# endif +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif #endif - #include #include @@ -4304,715 +4136,722 @@ namespace Catch { #include namespace Catch { - namespace Clara { - - class Args; - class Parser; - - // enum of result types from a parse - enum class ParseResultType { - Matched, - NoMatch, - ShortCircuitAll, - ShortCircuitSame - }; - - struct accept_many_t {}; - constexpr accept_many_t accept_many {}; - - namespace Detail { - struct fake_arg { - template - operator T(); - }; - - template - static constexpr bool is_unary_function_v = false; - - template - static constexpr bool is_unary_function_v< - F, - Catch::Detail::void_t()( - fake_arg() ) )>> = true; - - // Traits for extracting arg and return type of lambdas (for single - // argument lambdas) - template - struct UnaryLambdaTraits - : UnaryLambdaTraits {}; - - template - struct UnaryLambdaTraits { - static const bool isValid = false; - }; - - template - struct UnaryLambdaTraits { - static const bool isValid = true; - using ArgType = std::remove_const_t>; - using ReturnType = ReturnT; - }; - - class TokenStream; - - // Wraps a token coming from a token stream. These may not directly - // correspond to strings as a single string may encode an option + - // its argument if the : or = form is used - enum class TokenType { Option, Argument }; - struct Token { - TokenType type; - StringRef token; - }; - - // Abstracts iterators into args as a stream of tokens, with option - // arguments uniformly handled - class TokenStream { - using Iterator = std::vector::const_iterator; - Iterator it; - Iterator itEnd; - std::vector m_tokenBuffer; - void loadBuffer(); - - public: - explicit TokenStream( Args const& args ); - TokenStream( Iterator it, Iterator itEnd ); - - explicit operator bool() const { - return !m_tokenBuffer.empty() || it != itEnd; - } - - size_t count() const { - return m_tokenBuffer.size() + ( itEnd - it ); - } - - Token operator*() const { - assert( !m_tokenBuffer.empty() ); - return m_tokenBuffer.front(); - } - - Token const* operator->() const { - assert( !m_tokenBuffer.empty() ); - return &m_tokenBuffer.front(); - } - - TokenStream& operator++(); - }; - - //! Denotes type of a parsing result - enum class ResultType { - Ok, ///< No errors - LogicError, ///< Error in user-specified arguments for - ///< construction - RuntimeError ///< Error in parsing inputs - }; - - class ResultBase { - protected: - ResultBase( ResultType type ): m_type( type ) {} - virtual ~ResultBase(); // = default; - - - ResultBase(ResultBase const&) = default; - ResultBase& operator=(ResultBase const&) = default; - ResultBase(ResultBase&&) = default; - ResultBase& operator=(ResultBase&&) = default; - - virtual void enforceOk() const = 0; - - ResultType m_type; - }; - - template - class ResultValueBase : public ResultBase { - public: - T const& value() const& { - enforceOk(); - return m_value; - } - T&& value() && { - enforceOk(); - return CATCH_MOVE( m_value ); - } - - protected: - ResultValueBase( ResultType type ): ResultBase( type ) {} - - ResultValueBase( ResultValueBase const& other ): - ResultBase( other ) { - if ( m_type == ResultType::Ok ) - new ( &m_value ) T( other.m_value ); - } - ResultValueBase( ResultValueBase&& other ): - ResultBase( other ) { - if ( m_type == ResultType::Ok ) - new ( &m_value ) T( CATCH_MOVE(other.m_value) ); - } - - - ResultValueBase( ResultType, T const& value ): - ResultBase( ResultType::Ok ) { - new ( &m_value ) T( value ); - } - ResultValueBase( ResultType, T&& value ): - ResultBase( ResultType::Ok ) { - new ( &m_value ) T( CATCH_MOVE(value) ); - } - - ResultValueBase& operator=( ResultValueBase const& other ) { - if ( m_type == ResultType::Ok ) - m_value.~T(); - ResultBase::operator=( other ); - if ( m_type == ResultType::Ok ) - new ( &m_value ) T( other.m_value ); - return *this; - } - ResultValueBase& operator=( ResultValueBase&& other ) { - if ( m_type == ResultType::Ok ) m_value.~T(); - ResultBase::operator=( other ); - if ( m_type == ResultType::Ok ) - new ( &m_value ) T( CATCH_MOVE(other.m_value) ); - return *this; - } - - - ~ResultValueBase() override { - if ( m_type == ResultType::Ok ) - m_value.~T(); - } - - union { - T m_value; - }; - }; - - template <> class ResultValueBase : public ResultBase { - protected: - using ResultBase::ResultBase; - }; - - template - class BasicResult : public ResultValueBase { - public: - template - explicit BasicResult( BasicResult const& other ): - ResultValueBase( other.type() ), - m_errorMessage( other.errorMessage() ) { - assert( type() != ResultType::Ok ); - } - - template - static auto ok( U&& value ) -> BasicResult { - return { ResultType::Ok, CATCH_FORWARD(value) }; - } - static auto ok() -> BasicResult { return { ResultType::Ok }; } - static auto logicError( std::string&& message ) - -> BasicResult { - return { ResultType::LogicError, CATCH_MOVE(message) }; - } - static auto runtimeError( std::string&& message ) - -> BasicResult { - return { ResultType::RuntimeError, CATCH_MOVE(message) }; - } - - explicit operator bool() const { - return m_type == ResultType::Ok; - } - auto type() const -> ResultType { return m_type; } - auto errorMessage() const -> std::string const& { - return m_errorMessage; - } - - protected: - void enforceOk() const override { - - // Errors shouldn't reach this point, but if they do - // the actual error message will be in m_errorMessage - assert( m_type != ResultType::LogicError ); - assert( m_type != ResultType::RuntimeError ); - if ( m_type != ResultType::Ok ) - std::abort(); - } - - std::string - m_errorMessage; // Only populated if resultType is an error - - BasicResult( ResultType type, - std::string&& message ): - ResultValueBase( type ), m_errorMessage( CATCH_MOVE(message) ) { - assert( m_type != ResultType::Ok ); - } - - using ResultValueBase::ResultValueBase; - using ResultBase::m_type; - }; - - class ParseState { - public: - ParseState( ParseResultType type, - TokenStream remainingTokens ); - - ParseResultType type() const { return m_type; } - TokenStream const& remainingTokens() const& { - return m_remainingTokens; - } - TokenStream&& remainingTokens() && { - return CATCH_MOVE( m_remainingTokens ); - } - - private: - ParseResultType m_type; - TokenStream m_remainingTokens; - }; - - using Result = BasicResult; - using ParserResult = BasicResult; - using InternalParseResult = BasicResult; - - struct HelpColumns { - std::string left; - StringRef descriptions; - }; - - template - ParserResult convertInto( std::string const& source, T& target ) { - std::stringstream ss( source ); - ss >> target; - if ( ss.fail() ) { - return ParserResult::runtimeError( - "Unable to convert '" + source + - "' to destination type" ); - } else { - return ParserResult::ok( ParseResultType::Matched ); - } - } - ParserResult convertInto( std::string const& source, - std::string& target ); - ParserResult convertInto( std::string const& source, bool& target ); - -#ifdef CLARA_CONFIG_OPTIONAL_TYPE - template - auto convertInto( std::string const& source, - CLARA_CONFIG_OPTIONAL_TYPE& target ) - -> ParserResult { - T temp; - auto result = convertInto( source, temp ); - if ( result ) - target = CATCH_MOVE( temp ); - return result; - } -#endif // CLARA_CONFIG_OPTIONAL_TYPE - - struct BoundRef : Catch::Detail::NonCopyable { - virtual ~BoundRef() = default; - virtual bool isContainer() const; - virtual bool isFlag() const; - }; - struct BoundValueRefBase : BoundRef { - virtual auto setValue( std::string const& arg ) - -> ParserResult = 0; - }; - struct BoundFlagRefBase : BoundRef { - virtual auto setFlag( bool flag ) -> ParserResult = 0; - bool isFlag() const override; - }; - - template struct BoundValueRef : BoundValueRefBase { - T& m_ref; - - explicit BoundValueRef( T& ref ): m_ref( ref ) {} - - ParserResult setValue( std::string const& arg ) override { - return convertInto( arg, m_ref ); - } - }; - - template - struct BoundValueRef> : BoundValueRefBase { - std::vector& m_ref; - - explicit BoundValueRef( std::vector& ref ): m_ref( ref ) {} - - auto isContainer() const -> bool override { return true; } - - auto setValue( std::string const& arg ) - -> ParserResult override { - T temp; - auto result = convertInto( arg, temp ); - if ( result ) - m_ref.push_back( temp ); - return result; - } - }; - - struct BoundFlagRef : BoundFlagRefBase { - bool& m_ref; - - explicit BoundFlagRef( bool& ref ): m_ref( ref ) {} - - ParserResult setFlag( bool flag ) override; - }; - - template struct LambdaInvoker { - static_assert( - std::is_same::value, - "Lambda must return void or clara::ParserResult" ); - - template - static auto invoke( L const& lambda, ArgType const& arg ) - -> ParserResult { - return lambda( arg ); - } - }; - - template <> struct LambdaInvoker { - template - static auto invoke( L const& lambda, ArgType const& arg ) - -> ParserResult { - lambda( arg ); - return ParserResult::ok( ParseResultType::Matched ); - } - }; - - template - auto invokeLambda( L const& lambda, std::string const& arg ) - -> ParserResult { - ArgType temp{}; - auto result = convertInto( arg, temp ); - return !result ? result - : LambdaInvoker::ReturnType>::invoke( lambda, temp ); - } - - template struct BoundLambda : BoundValueRefBase { - L m_lambda; - - static_assert( - UnaryLambdaTraits::isValid, - "Supplied lambda must take exactly one argument" ); - explicit BoundLambda( L const& lambda ): m_lambda( lambda ) {} - - auto setValue( std::string const& arg ) - -> ParserResult override { - return invokeLambda::ArgType>( - m_lambda, arg ); - } - }; - - template struct BoundManyLambda : BoundLambda { - explicit BoundManyLambda( L const& lambda ): BoundLambda( lambda ) {} - bool isContainer() const override { return true; } - }; - - template struct BoundFlagLambda : BoundFlagRefBase { - L m_lambda; - - static_assert( - UnaryLambdaTraits::isValid, - "Supplied lambda must take exactly one argument" ); - static_assert( - std::is_same::ArgType, - bool>::value, - "flags must be boolean" ); - - explicit BoundFlagLambda( L const& lambda ): - m_lambda( lambda ) {} - - auto setFlag( bool flag ) -> ParserResult override { - return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); - } - }; - - enum class Optionality { Optional, Required }; - - class ParserBase { - public: - virtual ~ParserBase() = default; - virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse( std::string const& exeName, - TokenStream tokens ) const - -> InternalParseResult = 0; - virtual size_t cardinality() const; - - InternalParseResult parse( Args const& args ) const; - }; - - template - class ComposableParserImpl : public ParserBase { - public: - template - auto operator|( T const& other ) const -> Parser; - }; - - // Common code and state for Args and Opts - template - class ParserRefImpl : public ComposableParserImpl { - protected: - Optionality m_optionality = Optionality::Optional; - std::shared_ptr m_ref; - StringRef m_hint; - StringRef m_description; - - explicit ParserRefImpl( std::shared_ptr const& ref ): - m_ref( ref ) {} - - public: - template - ParserRefImpl( accept_many_t, - LambdaT const& ref, - StringRef hint ): - m_ref( std::make_shared>( ref ) ), - m_hint( hint ) {} - - template >> - ParserRefImpl( T& ref, StringRef hint ): - m_ref( std::make_shared>( ref ) ), - m_hint( hint ) {} - - template >> - ParserRefImpl( LambdaT const& ref, StringRef hint ): - m_ref( std::make_shared>( ref ) ), - m_hint( hint ) {} - - DerivedT& operator()( StringRef description ) & { - m_description = description; - return static_cast( *this ); - } - DerivedT&& operator()( StringRef description ) && { - m_description = description; - return static_cast( *this ); - } - - auto optional() -> DerivedT& { - m_optionality = Optionality::Optional; - return static_cast( *this ); - } - - auto required() -> DerivedT& { - m_optionality = Optionality::Required; - return static_cast( *this ); - } - - auto isOptional() const -> bool { - return m_optionality == Optionality::Optional; - } - - auto cardinality() const -> size_t override { - if ( m_ref->isContainer() ) - return 0; - else - return 1; - } - - StringRef hint() const { return m_hint; } - }; - - } // namespace detail - - - // A parser for arguments - class Arg : public Detail::ParserRefImpl { - public: - using ParserRefImpl::ParserRefImpl; - using ParserBase::parse; - - Detail::InternalParseResult - parse(std::string const&, - Detail::TokenStream tokens) const override; - }; - - // A parser for options - class Opt : public Detail::ParserRefImpl { - protected: - std::vector m_optNames; - - public: - template - explicit Opt(LambdaT const& ref) : - ParserRefImpl( - std::make_shared>(ref)) {} - - explicit Opt(bool& ref); - - template >> - Opt( LambdaT const& ref, StringRef hint ): - ParserRefImpl( ref, hint ) {} - - template - Opt( accept_many_t, LambdaT const& ref, StringRef hint ): - ParserRefImpl( accept_many, ref, hint ) {} - - template >> - Opt( T& ref, StringRef hint ): - ParserRefImpl( ref, hint ) {} - - Opt& operator[]( StringRef optName ) & { - m_optNames.push_back(optName); - return *this; - } - Opt&& operator[]( StringRef optName ) && { - m_optNames.push_back( optName ); - return CATCH_MOVE(*this); - } - - Detail::HelpColumns getHelpColumns() const; - - bool isMatch(StringRef optToken) const; - - using ParserBase::parse; - - Detail::InternalParseResult - parse(std::string const&, - Detail::TokenStream tokens) const override; - - Detail::Result validate() const override; - }; - - // Specifies the name of the executable - class ExeName : public Detail::ComposableParserImpl { - std::shared_ptr m_name; - std::shared_ptr m_ref; - - public: - ExeName(); - explicit ExeName(std::string& ref); - - template - explicit ExeName(LambdaT const& lambda) : ExeName() { - m_ref = std::make_shared>(lambda); - } - - // The exe name is not parsed out of the normal tokens, but is - // handled specially - Detail::InternalParseResult - parse(std::string const&, - Detail::TokenStream tokens) const override; - - std::string const& name() const { return *m_name; } - Detail::ParserResult set(std::string const& newName); - }; - - - // A Combined parser - class Parser : Detail::ParserBase { - mutable ExeName m_exeName; - std::vector m_options; - std::vector m_args; - - public: - - auto operator|=(ExeName const& exeName) -> Parser& { - m_exeName = exeName; - return *this; - } - - auto operator|=(Arg const& arg) -> Parser& { - m_args.push_back(arg); - return *this; - } - - friend Parser& operator|=( Parser& p, Opt const& opt ) { - p.m_options.push_back( opt ); - return p; - } - friend Parser& operator|=( Parser& p, Opt&& opt ) { - p.m_options.push_back( CATCH_MOVE(opt) ); - return p; - } - - Parser& operator|=(Parser const& other); - - template - friend Parser operator|( Parser const& p, T&& rhs ) { - Parser temp( p ); - temp |= rhs; - return temp; - } - - template - friend Parser operator|( Parser&& p, T&& rhs ) { - p |= CATCH_FORWARD(rhs); - return CATCH_MOVE(p); - } - - std::vector getHelpColumns() const; - - void writeToStream(std::ostream& os) const; - - friend auto operator<<(std::ostream& os, Parser const& parser) - -> std::ostream& { - parser.writeToStream(os); - return os; - } - - Detail::Result validate() const override; - - using ParserBase::parse; - Detail::InternalParseResult - parse(std::string const& exeName, - Detail::TokenStream tokens) const override; - }; - - /** - * Wrapper over argc + argv, assumes that the inputs outlive it - */ - class Args { - friend Detail::TokenStream; - StringRef m_exeName; - std::vector m_args; - - public: - Args(int argc, char const* const* argv); - // Helper constructor for testing - Args(std::initializer_list args); - - StringRef exeName() const { return m_exeName; } - }; - - - // Convenience wrapper for option parser that specifies the help option - struct Help : Opt { - Help(bool& showHelpFlag); - }; - - // Result type for parser operation - using Detail::ParserResult; - - namespace Detail { - template - template - Parser - ComposableParserImpl::operator|(T const& other) const { - return Parser() | static_cast(*this) | other; - } + namespace Clara { + + class Args; + class Parser; + + // enum of result types from a parse + enum class ParseResultType { + Matched, + NoMatch, + ShortCircuitAll, + ShortCircuitSame + }; + + struct accept_many_t {}; + constexpr accept_many_t accept_many{}; + + namespace Detail { + struct fake_arg { + template + operator T(); + }; + + template + static constexpr bool is_unary_function_v = false; + + template + static constexpr bool is_unary_function_v< + F, + Catch::Detail::void_t()( + fake_arg()))>> + = true; + + // Traits for extracting arg and return type of lambdas (for single + // argument lambdas) + template + struct UnaryLambdaTraits + : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = std::remove_const_t>; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Wraps a token coming from a token stream. These may not directly + // correspond to strings as a single string may encode an option + + // its argument if the : or = form is used + enum class TokenType { Option, + Argument }; + struct Token { + TokenType type; + StringRef token; + }; + + // Abstracts iterators into args as a stream of tokens, with option + // arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + void loadBuffer(); + + public: + explicit TokenStream(Args const &args); + TokenStream(Iterator it, Iterator itEnd); + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; } - } // namespace Clara + size_t count() const { + return m_tokenBuffer.size() + (itEnd - it); + } + + Token operator*() const { + assert(!m_tokenBuffer.empty()); + return m_tokenBuffer.front(); + } + + Token const *operator->() const { + assert(!m_tokenBuffer.empty()); + return &m_tokenBuffer.front(); + } + + TokenStream &operator++(); + }; + + //! Denotes type of a parsing result + enum class ResultType { + Ok, ///< No errors + LogicError, ///< Error in user-specified arguments for + ///< construction + RuntimeError ///< Error in parsing inputs + }; + + class ResultBase { + protected: + ResultBase(ResultType type) + : m_type(type) {} + virtual ~ResultBase(); // = default; + + ResultBase(ResultBase const &) = default; + ResultBase &operator=(ResultBase const &) = default; + ResultBase(ResultBase &&) = default; + ResultBase &operator=(ResultBase &&) = default; + + virtual void enforceOk() const = 0; + + ResultType m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + T const &value() const & { + enforceOk(); + return m_value; + } + T &&value() && { + enforceOk(); + return CATCH_MOVE(m_value); + } + + protected: + ResultValueBase(ResultType type) + : ResultBase(type) {} + + ResultValueBase(ResultValueBase const &other) + : ResultBase(other) { + if (m_type == ResultType::Ok) + new (&m_value) T(other.m_value); + } + ResultValueBase(ResultValueBase &&other) + : ResultBase(other) { + if (m_type == ResultType::Ok) + new (&m_value) T(CATCH_MOVE(other.m_value)); + } + + ResultValueBase(ResultType, T const &value) + : ResultBase(ResultType::Ok) { + new (&m_value) T(value); + } + ResultValueBase(ResultType, T &&value) + : ResultBase(ResultType::Ok) { + new (&m_value) T(CATCH_MOVE(value)); + } + + ResultValueBase &operator=(ResultValueBase const &other) { + if (m_type == ResultType::Ok) + m_value.~T(); + ResultBase::operator=(other); + if (m_type == ResultType::Ok) + new (&m_value) T(other.m_value); + return *this; + } + ResultValueBase &operator=(ResultValueBase &&other) { + if (m_type == ResultType::Ok) m_value.~T(); + ResultBase::operator=(other); + if (m_type == ResultType::Ok) + new (&m_value) T(CATCH_MOVE(other.m_value)); + return *this; + } + + ~ResultValueBase() override { + if (m_type == ResultType::Ok) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult(BasicResult const &other) + : ResultValueBase(other.type()), m_errorMessage(other.errorMessage()) { + assert(type() != ResultType::Ok); + } + + template + static auto ok(U &&value) -> BasicResult { + return {ResultType::Ok, CATCH_FORWARD(value)}; + } + static auto ok() -> BasicResult { return {ResultType::Ok}; } + static auto logicError(std::string &&message) + -> BasicResult { + return {ResultType::LogicError, CATCH_MOVE(message)}; + } + static auto runtimeError(std::string &&message) + -> BasicResult { + return {ResultType::RuntimeError, CATCH_MOVE(message)}; + } + + explicit operator bool() const { + return m_type == ResultType::Ok; + } + auto type() const -> ResultType { return m_type; } + auto errorMessage() const -> std::string const & { + return m_errorMessage; + } + + protected: + void enforceOk() const override { + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert(m_type != ResultType::LogicError); + assert(m_type != ResultType::RuntimeError); + if (m_type != ResultType::Ok) + std::abort(); + } + + std::string + m_errorMessage; // Only populated if resultType is an error + + BasicResult(ResultType type, + std::string &&message) + : ResultValueBase(type), m_errorMessage(CATCH_MOVE(message)) { + assert(m_type != ResultType::Ok); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + class ParseState { + public: + ParseState(ParseResultType type, + TokenStream remainingTokens); + + ParseResultType type() const { return m_type; } + TokenStream const &remainingTokens() const & { + return m_remainingTokens; + } + TokenStream &&remainingTokens() && { + return CATCH_MOVE(m_remainingTokens); + } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + StringRef descriptions; + }; + + template + ParserResult convertInto(std::string const &source, T &target) { + std::stringstream ss(source); + ss >> target; + if (ss.fail()) { + return ParserResult::runtimeError( + "Unable to convert '" + source + "' to destination type"); + } else { + return ParserResult::ok(ParseResultType::Matched); + } + } + ParserResult convertInto(std::string const &source, + std::string &target); + ParserResult convertInto(std::string const &source, bool &target); + +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + auto convertInto(std::string const &source, + CLARA_CONFIG_OPTIONAL_TYPE &target) + -> ParserResult { + T temp; + auto result = convertInto(source, temp); + if (result) + target = CATCH_MOVE(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct BoundRef : Catch::Detail::NonCopyable { + virtual ~BoundRef() = default; + virtual bool isContainer() const; + virtual bool isFlag() const; + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue(std::string const &arg) + -> ParserResult + = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag(bool flag) -> ParserResult = 0; + bool isFlag() const override; + }; + + template + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef(T &ref) + : m_ref(ref) {} + + ParserResult setValue(std::string const &arg) override { + return convertInto(arg, m_ref); + } + }; + + template + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef(std::vector &ref) + : m_ref(ref) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue(std::string const &arg) + -> ParserResult override { + T temp; + auto result = convertInto(arg, temp); + if (result) + m_ref.push_back(temp); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef(bool &ref) + : m_ref(ref) {} + + ParserResult setFlag(bool flag) override; + }; + + template + struct LambdaInvoker { + static_assert( + std::is_same::value, + "Lambda must return void or clara::ParserResult"); + + template + static auto invoke(L const &lambda, ArgType const &arg) + -> ParserResult { + return lambda(arg); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke(L const &lambda, ArgType const &arg) + -> ParserResult { + lambda(arg); + return ParserResult::ok(ParseResultType::Matched); + } + }; + + template + auto invokeLambda(L const &lambda, std::string const &arg) + -> ParserResult { + ArgType temp{}; + auto result = convertInto(arg, temp); + return !result ? result + : LambdaInvoker::ReturnType>::invoke(lambda, temp); + } + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( + UnaryLambdaTraits::isValid, + "Supplied lambda must take exactly one argument"); + explicit BoundLambda(L const &lambda) + : m_lambda(lambda) {} + + auto setValue(std::string const &arg) + -> ParserResult override { + return invokeLambda::ArgType>( + m_lambda, arg); + } + }; + + template + struct BoundManyLambda : BoundLambda { + explicit BoundManyLambda(L const &lambda) + : BoundLambda(lambda) {} + bool isContainer() const override { return true; } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( + UnaryLambdaTraits::isValid, + "Supplied lambda must take exactly one argument"); + static_assert( + std::is_same::ArgType, + bool>::value, + "flags must be boolean"); + + explicit BoundFlagLambda(L const &lambda) + : m_lambda(lambda) {} + + auto setFlag(bool flag) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + } + }; + + enum class Optionality { Optional, + Required }; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse(std::string const &exeName, + TokenStream tokens) const + -> InternalParseResult + = 0; + virtual size_t cardinality() const; + + InternalParseResult parse(Args const &args) const; + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|(T const &other) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + StringRef m_hint; + StringRef m_description; + + explicit ParserRefImpl(std::shared_ptr const &ref) + : m_ref(ref) {} + + public: + template + ParserRefImpl(accept_many_t, + LambdaT const &ref, + StringRef hint) + : m_ref(std::make_shared>(ref)), m_hint(hint) {} + + template>> + ParserRefImpl(T &ref, StringRef hint) + : m_ref(std::make_shared>(ref)), m_hint(hint) {} + + template>> + ParserRefImpl(LambdaT const &ref, StringRef hint) + : m_ref(std::make_shared>(ref)), m_hint(hint) {} + + DerivedT &operator()(StringRef description) & { + m_description = description; + return static_cast(*this); + } + DerivedT &&operator()(StringRef description) && { + m_description = description; + return static_cast(*this); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast(*this); + } + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast(*this); + } + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if (m_ref->isContainer()) + return 0; + else + return 1; + } + + StringRef hint() const { return m_hint; } + }; + + } // namespace Detail + + // A parser for arguments + class Arg : public Detail::ParserRefImpl { + public: + using ParserBase::parse; + using ParserRefImpl::ParserRefImpl; + + Detail::InternalParseResult + parse(std::string const &, + Detail::TokenStream tokens) const override; + }; + + // A parser for options + class Opt : public Detail::ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt(LambdaT const &ref) + : ParserRefImpl( + std::make_shared>(ref)) {} + + explicit Opt(bool &ref); + + template>> + Opt(LambdaT const &ref, StringRef hint) + : ParserRefImpl(ref, hint) {} + + template + Opt(accept_many_t, LambdaT const &ref, StringRef hint) + : ParserRefImpl(accept_many, ref, hint) {} + + template>> + Opt(T &ref, StringRef hint) + : ParserRefImpl(ref, hint) {} + + Opt &operator[](StringRef optName) & { + m_optNames.push_back(optName); + return *this; + } + Opt &&operator[](StringRef optName) && { + m_optNames.push_back(optName); + return CATCH_MOVE(*this); + } + + Detail::HelpColumns getHelpColumns() const; + + bool isMatch(StringRef optToken) const; + + using ParserBase::parse; + + Detail::InternalParseResult + parse(std::string const &, + Detail::TokenStream tokens) const override; + + Detail::Result validate() const override; + }; + + // Specifies the name of the executable + class ExeName : public Detail::ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + public: + ExeName(); + explicit ExeName(std::string &ref); + + template + explicit ExeName(LambdaT const &lambda) + : ExeName() { + m_ref = std::make_shared>(lambda); + } + + // The exe name is not parsed out of the normal tokens, but is + // handled specially + Detail::InternalParseResult + parse(std::string const &, + Detail::TokenStream tokens) const override; + + std::string const &name() const { return *m_name; } + Detail::ParserResult set(std::string const &newName); + }; + + // A Combined parser + class Parser : Detail::ParserBase { + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + public: + auto operator|=(ExeName const &exeName) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=(Arg const &arg) -> Parser & { + m_args.push_back(arg); + return *this; + } + + friend Parser &operator|=(Parser &p, Opt const &opt) { + p.m_options.push_back(opt); + return p; + } + friend Parser &operator|=(Parser &p, Opt &&opt) { + p.m_options.push_back(CATCH_MOVE(opt)); + return p; + } + + Parser &operator|=(Parser const &other); + + template + friend Parser operator|(Parser const &p, T &&rhs) { + Parser temp(p); + temp |= rhs; + return temp; + } + + template + friend Parser operator|(Parser &&p, T &&rhs) { + p |= CATCH_FORWARD(rhs); + return CATCH_MOVE(p); + } + + std::vector getHelpColumns() const; + + void writeToStream(std::ostream &os) const; + + friend auto operator<<(std::ostream &os, Parser const &parser) + -> std::ostream & { + parser.writeToStream(os); + return os; + } + + Detail::Result validate() const override; + + using ParserBase::parse; + Detail::InternalParseResult + parse(std::string const &exeName, + Detail::TokenStream tokens) const override; + }; + + /** + * Wrapper over argc + argv, assumes that the inputs outlive it + */ + class Args { + friend Detail::TokenStream; + StringRef m_exeName; + std::vector m_args; + + public: + Args(int argc, char const *const *argv); + // Helper constructor for testing + Args(std::initializer_list args); + + StringRef exeName() const { return m_exeName; } + }; + + // Convenience wrapper for option parser that specifies the help option + struct Help : Opt { + Help(bool &showHelpFlag); + }; + + // Result type for parser operation + using Detail::ParserResult; + + namespace Detail { + template + template + Parser + ComposableParserImpl::operator|(T const &other) const { + return Parser() | static_cast(*this) | other; + } + } // namespace Detail + + } // namespace Clara } // namespace Catch -#if defined( __clang__ ) -# pragma clang diagnostic pop +#if defined(__clang__) +#pragma clang diagnostic pop #endif -#if defined( __GNUC__ ) -# pragma GCC diagnostic pop +#if defined(__GNUC__) +#pragma GCC diagnostic pop #endif #endif // CATCH_CLARA_HPP_INCLUDED namespace Catch { - struct ConfigData; + struct ConfigData; - Clara::Parser makeCommandLineParser( ConfigData& config ); + Clara::Parser makeCommandLineParser(ConfigData &config); } // end namespace Catch @@ -5020,104 +4859,99 @@ namespace Catch { namespace Catch { - // TODO: Use C++17 `inline` variables - constexpr int UnspecifiedErrorExitCode = 1; - constexpr int NoTestsRunExitCode = 2; - constexpr int UnmatchedTestSpecExitCode = 3; - constexpr int AllTestsSkippedExitCode = 4; - constexpr int InvalidTestSpecExitCode = 5; - constexpr int TestFailureExitCode = 42; + // TODO: Use C++17 `inline` variables + constexpr int UnspecifiedErrorExitCode = 1; + constexpr int NoTestsRunExitCode = 2; + constexpr int UnmatchedTestSpecExitCode = 3; + constexpr int AllTestsSkippedExitCode = 4; + constexpr int InvalidTestSpecExitCode = 5; + constexpr int TestFailureExitCode = 42; - class Session : Detail::NonCopyable { - public: + class Session : Detail::NonCopyable { + public: + Session(); + ~Session(); - Session(); - ~Session(); + void showHelp() const; + void libIdentify(); - void showHelp() const; - void libIdentify(); + int applyCommandLine(int argc, char const *const *argv); +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int applyCommandLine(int argc, wchar_t const *const *argv); +#endif - int applyCommandLine( int argc, char const * const * argv ); - #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) - int applyCommandLine( int argc, wchar_t const * const * argv ); - #endif + void useConfigData(ConfigData const &configData); - void useConfigData( ConfigData const& configData ); + template + int run(int argc, CharT const *const argv[]) { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } - template - int run(int argc, CharT const * const argv[]) { - if (m_startupExceptions) - return 1; - int returnCode = applyCommandLine(argc, argv); - if (returnCode == 0) - returnCode = run(); - return returnCode; - } + int run(); - int run(); + Clara::Parser const &cli() const; + void cli(Clara::Parser const &newParser); + ConfigData &configData(); + Config &config(); - Clara::Parser const& cli() const; - void cli( Clara::Parser const& newParser ); - ConfigData& configData(); - Config& config(); - private: - int runInternal(); + private: + int runInternal(); - Clara::Parser m_cli; - ConfigData m_configData; - Detail::unique_ptr m_config; - bool m_startupExceptions = false; - }; + Clara::Parser m_cli; + ConfigData m_configData; + Detail::unique_ptr m_config; + bool m_startupExceptions = false; + }; } // end namespace Catch #endif // CATCH_SESSION_HPP_INCLUDED - #ifndef CATCH_TAG_ALIAS_HPP_INCLUDED #define CATCH_TAG_ALIAS_HPP_INCLUDED - #include namespace Catch { - struct TagAlias { - TagAlias(std::string const& _tag, SourceLineInfo _lineInfo): - tag(_tag), - lineInfo(_lineInfo) - {} + struct TagAlias { + TagAlias(std::string const &_tag, SourceLineInfo _lineInfo) + : tag(_tag), lineInfo(_lineInfo) {} - std::string tag; - SourceLineInfo lineInfo; - }; + std::string tag; + SourceLineInfo lineInfo; + }; } // end namespace Catch #endif // CATCH_TAG_ALIAS_HPP_INCLUDED - #ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED #define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED - namespace Catch { - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; + struct RegistrarForTagAliases { + RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo); + }; } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ const Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#define CATCH_REGISTER_TAG_ALIAS(alias, spec) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + const Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME(AutoRegisterTagAlias)(alias, spec, CATCH_INTERNAL_LINEINFO); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED - #ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED #define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED @@ -5129,107 +4963,93 @@ namespace Catch { #pragma GCC diagnostic ignored "-Wparentheses" #endif - - - #ifndef CATCH_TEST_MACROS_HPP_INCLUDED #define CATCH_TEST_MACROS_HPP_INCLUDED - - #ifndef CATCH_TEST_MACRO_IMPL_HPP_INCLUDED #define CATCH_TEST_MACRO_IMPL_HPP_INCLUDED - - #ifndef CATCH_ASSERTION_HANDLER_HPP_INCLUDED #define CATCH_ASSERTION_HANDLER_HPP_INCLUDED - - #ifndef CATCH_DECOMPOSER_HPP_INCLUDED #define CATCH_DECOMPOSER_HPP_INCLUDED - - #ifndef CATCH_COMPARE_TRAITS_HPP_INCLUDED #define CATCH_COMPARE_TRAITS_HPP_INCLUDED - #include namespace Catch { - namespace Detail { + namespace Detail { -#if defined( __GNUC__ ) && !defined( __clang__ ) -# pragma GCC diagnostic push +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push // GCC likes to complain about comparing bool with 0, in the decltype() // that defines the comparable traits below. -# pragma GCC diagnostic ignored "-Wbool-compare" +#pragma GCC diagnostic ignored "-Wbool-compare" // "ordered comparison of pointer with integer zero" same as above, // but it does not have a separate warning flag to suppress -# pragma GCC diagnostic ignored "-Wextra" +#pragma GCC diagnostic ignored "-Wextra" // Did you know that comparing floats with `0` directly // is super-duper dangerous in unevaluated context? -# pragma GCC diagnostic ignored "-Wfloat-equal" +#pragma GCC diagnostic ignored "-Wfloat-equal" #endif -#if defined( __clang__ ) -# pragma clang diagnostic push +#if defined(__clang__) +#pragma clang diagnostic push // Did you know that comparing floats with `0` directly // is super-duper dangerous in unevaluated context? -# pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wfloat-equal" #endif -#define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \ - template \ - struct is_##id##_comparable : std::false_type {}; \ - template \ - struct is_##id##_comparable< \ - T, \ - U, \ - void_t() op std::declval() )>> \ - : std::true_type {}; \ - template \ - struct is_##id##_0_comparable : std::false_type {}; \ - template \ - struct is_##id##_0_comparable() op 0 )>> \ - : std::true_type {}; +#define CATCH_DEFINE_COMPARABLE_TRAIT(id, op) \ + template \ + struct is_##id##_comparable : std::false_type {}; \ + template \ + struct is_##id##_comparable< \ + T, \ + U, \ + void_t() op std::declval())>> \ + : std::true_type {}; \ + template \ + struct is_##id##_0_comparable : std::false_type {}; \ + template \ + struct is_##id##_0_comparable() op 0)>> \ + : std::true_type {}; - // We need all 6 pre-spaceship comparison ops: <, <=, >, >=, ==, != - CATCH_DEFINE_COMPARABLE_TRAIT( lt, < ) - CATCH_DEFINE_COMPARABLE_TRAIT( le, <= ) - CATCH_DEFINE_COMPARABLE_TRAIT( gt, > ) - CATCH_DEFINE_COMPARABLE_TRAIT( ge, >= ) - CATCH_DEFINE_COMPARABLE_TRAIT( eq, == ) - CATCH_DEFINE_COMPARABLE_TRAIT( ne, != ) + // We need all 6 pre-spaceship comparison ops: <, <=, >, >=, ==, != + CATCH_DEFINE_COMPARABLE_TRAIT(lt, <) + CATCH_DEFINE_COMPARABLE_TRAIT(le, <=) + CATCH_DEFINE_COMPARABLE_TRAIT(gt, >) + CATCH_DEFINE_COMPARABLE_TRAIT(ge, >=) + CATCH_DEFINE_COMPARABLE_TRAIT(eq, ==) + CATCH_DEFINE_COMPARABLE_TRAIT(ne, !=) #undef CATCH_DEFINE_COMPARABLE_TRAIT -#if defined( __GNUC__ ) && !defined( __clang__ ) -# pragma GCC diagnostic pop +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop #endif -#if defined( __clang__ ) -# pragma clang diagnostic pop +#if defined(__clang__) +#pragma clang diagnostic pop #endif - - } // namespace Detail + } // namespace Detail } // namespace Catch #endif // CATCH_COMPARE_TRAITS_HPP_INCLUDED - #ifndef CATCH_LOGICAL_TRAITS_HPP_INCLUDED #define CATCH_LOGICAL_TRAITS_HPP_INCLUDED #include namespace Catch { -namespace Detail { + namespace Detail { -#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510 +#if defined(__cpp_lib_logical_traits) && __cpp_lib_logical_traits >= 201510 using std::conjunction; using std::disjunction; @@ -5237,30 +5057,34 @@ namespace Detail { #else - template struct conjunction : std::true_type {}; - template struct conjunction : B1 {}; - template + template + struct conjunction : std::true_type {}; + template + struct conjunction : B1 {}; + template struct conjunction - : std::conditional_t, B1> {}; + : std::conditional_t, B1> {}; - template struct disjunction : std::false_type {}; - template struct disjunction : B1 {}; - template + template + struct disjunction : std::false_type {}; + template + struct disjunction : B1 {}; + template struct disjunction - : std::conditional_t> {}; + : std::conditional_t> {}; - template + template struct negation : std::integral_constant {}; #endif -} // namespace Detail + } // namespace Detail } // namespace Catch #endif // CATCH_LOGICAL_TRAITS_HPP_INCLUDED -#include #include +#include /** \file * Why does decomposing look the way it does: @@ -5345,357 +5169,344 @@ namespace Detail { #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#pragma warning(disable:4018) // more "signed/unsigned mismatch" -#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) -#pragma warning(disable:4180) // qualifier applied to function type has no meaning -#pragma warning(disable:4800) // Forcing result to true or false +#pragma warning(disable : 4389) // '==' : signed/unsigned mismatch +#pragma warning(disable : 4018) // more "signed/unsigned mismatch" +#pragma warning(disable : 4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable : 4180) // qualifier applied to function type has no meaning +#pragma warning(disable : 4800) // Forcing result to true or false #endif #ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wsign-compare" -# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" #elif defined __GNUC__ -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wsign-compare" -# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif #if defined(CATCH_CPP20_OR_GREATER) && __has_include() -# include -# if defined( __cpp_lib_three_way_comparison ) && \ - __cpp_lib_three_way_comparison >= 201907L -# define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS -# endif +#include +#if defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L +#define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS +#endif #endif namespace Catch { - namespace Detail { - // This was added in C++20, but we require only C++14 for now. - template - using RemoveCVRef_t = std::remove_cv_t>; - } + namespace Detail { + // This was added in C++20, but we require only C++14 for now. + template + using RemoveCVRef_t = std::remove_cv_t>; + } // namespace Detail - // Note: This is about as much as we can currently reasonably support. - // In an ideal world, we could capture by value small trivially - // copyable types, but the actual `std::is_trivially_copyable` - // trait is a huge mess with standard-violating results on - // GCC and Clang, which are unlikely to be fixed soon due to ABI - // concerns. - // `std::is_scalar` also causes issues due to the `is_pointer` - // component, which causes ambiguity issues with (references-to) - // function pointer. If those are resolved, we still need to - // disambiguate the overload set for arrays, through explicit - // overload for references to sized arrays. - template - struct capture_by_value - : std::integral_constant::value || - std::is_enum::value> {}; + // Note: This is about as much as we can currently reasonably support. + // In an ideal world, we could capture by value small trivially + // copyable types, but the actual `std::is_trivially_copyable` + // trait is a huge mess with standard-violating results on + // GCC and Clang, which are unlikely to be fixed soon due to ABI + // concerns. + // `std::is_scalar` also causes issues due to the `is_pointer` + // component, which causes ambiguity issues with (references-to) + // function pointer. If those are resolved, we still need to + // disambiguate the overload set for arrays, through explicit + // overload for references to sized arrays. + template + struct capture_by_value + : std::integral_constant::value || std::is_enum::value> {}; -#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) - template <> - struct capture_by_value : std::true_type {}; - template <> - struct capture_by_value : std::true_type {}; - template <> - struct capture_by_value : std::true_type {}; +#if defined(CATCH_CONFIG_CPP20_COMPARE_OVERLOADS) + template<> + struct capture_by_value : std::true_type {}; + template<> + struct capture_by_value : std::true_type {}; + template<> + struct capture_by_value : std::true_type {}; #endif - template - struct always_false : std::false_type {}; + template + struct always_false : std::false_type {}; - class ITransientExpression { - bool m_isBinaryExpression; - bool m_result; + class ITransientExpression { + bool m_isBinaryExpression; + bool m_result; - protected: - ~ITransientExpression() = default; + protected: + ~ITransientExpression() = default; - public: - constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } - constexpr auto getResult() const -> bool { return m_result; } - //! This function **has** to be overridden by the derived class. - virtual void streamReconstructedExpression( std::ostream& os ) const; + public: + constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + constexpr auto getResult() const -> bool { return m_result; } + //! This function **has** to be overridden by the derived class. + virtual void streamReconstructedExpression(std::ostream &os) const; - constexpr ITransientExpression( bool isBinaryExpression, bool result ) - : m_isBinaryExpression( isBinaryExpression ), - m_result( result ) - {} + constexpr ITransientExpression(bool isBinaryExpression, bool result) + : m_isBinaryExpression(isBinaryExpression), m_result(result) {} - constexpr ITransientExpression( ITransientExpression const& ) = default; - constexpr ITransientExpression& operator=( ITransientExpression const& ) = default; + constexpr ITransientExpression(ITransientExpression const &) = default; + constexpr ITransientExpression &operator=(ITransientExpression const &) = default; - friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { - expr.streamReconstructedExpression(out); - return out; - } - }; + friend std::ostream &operator<<(std::ostream &out, ITransientExpression const &expr) { + expr.streamReconstructedExpression(out); + return out; + } + }; - void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); + void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs); - template - class BinaryExpr : public ITransientExpression { - LhsT m_lhs; - StringRef m_op; - RhsT m_rhs; + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; - void streamReconstructedExpression( std::ostream &os ) const override { - formatReconstructedExpression - ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); - } - - public: - constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) - : ITransientExpression{ true, comparisonResult }, - m_lhs( lhs ), - m_op( op ), - m_rhs( rhs ) - {} - - template - auto operator && ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator || ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator == ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator != ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator > ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator < ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator >= ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator <= ( T ) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - }; - - template - class UnaryExpr : public ITransientExpression { - LhsT m_lhs; - - void streamReconstructedExpression( std::ostream &os ) const override { - os << Catch::Detail::stringify( m_lhs ); - } - - public: - explicit constexpr UnaryExpr( LhsT lhs ) - : ITransientExpression{ false, static_cast(lhs) }, - m_lhs( lhs ) - {} - }; - - - template - class ExprLhs { - LhsT m_lhs; - public: - explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} - -#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction, \ - Detail::negation>>>::value, \ - BinaryExpr> { \ - return { \ - static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ - } \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction, \ - capture_by_value>::value, \ - BinaryExpr> { \ - return { \ - static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ - } \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction< \ - Detail::negation>, \ - Detail::is_eq_0_comparable, \ - /* We allow long because we want `ptr op NULL` to be accepted */ \ - Detail::disjunction, \ - std::is_same>>::value, \ - BinaryExpr> { \ - if ( rhs != 0 ) { throw_test_failure_exception(); } \ - return { \ - static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ - } \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction< \ - Detail::negation>, \ - Detail::is_eq_0_comparable, \ - /* We allow long because we want `ptr op NULL` to be accepted */ \ - Detail::disjunction, \ - std::is_same>>::value, \ - BinaryExpr> { \ - if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ - return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + void streamReconstructedExpression(std::ostream &os) const override { + formatReconstructedExpression(os, Catch::Detail::stringify(m_lhs), m_op, Catch::Detail::stringify(m_rhs)); } - CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) - CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) + public: + constexpr BinaryExpr(bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs) + : ITransientExpression{true, comparisonResult}, m_lhs(lhs), m_op(op), m_rhs(rhs) {} - #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR - - -#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction, \ - Detail::negation>>>::value, \ - BinaryExpr> { \ - return { \ - static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ - } \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction, \ - capture_by_value>::value, \ - BinaryExpr> { \ - return { \ - static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ - } \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction< \ - Detail::negation>, \ - Detail::is_##id##_0_comparable, \ - std::is_same>::value, \ - BinaryExpr> { \ - if ( rhs != 0 ) { throw_test_failure_exception(); } \ - return { \ - static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ - } \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t< \ - Detail::conjunction< \ - Detail::negation>, \ - Detail::is_##id##_0_comparable, \ - std::is_same>::value, \ - BinaryExpr> { \ - if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ - return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + template + auto operator&&(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); } - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < ) - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= ) - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > ) - CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= ) - - #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR - - -#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ - -> std::enable_if_t< \ - !capture_by_value>::value, \ - BinaryExpr> { \ - return { \ - static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ - } \ - template \ - constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ - -> std::enable_if_t::value, \ - BinaryExpr> { \ - return { \ - static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ + template + auto operator||(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); } - CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) - CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) - CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) + template + auto operator==(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } - #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR + template + auto operator!=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } - template - friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr { - static_assert(always_false::value, - "operator&& is not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } + template + auto operator>(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } - template - friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr { - static_assert(always_false::value, - "operator|| is not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } + template + auto operator<(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } - constexpr auto makeUnaryExpr() const -> UnaryExpr { - return UnaryExpr{ m_lhs }; - } - }; + template + auto operator>=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } - struct Decomposer { - template >::value, - int> = 0> - constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { - return ExprLhs{ lhs }; - } + template + auto operator<=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; - template ::value, int> = 0> - constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { - return ExprLhs{ value }; - } - }; + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression(std::ostream &os) const override { + os << Catch::Detail::stringify(m_lhs); + } + + public: + explicit constexpr UnaryExpr(LhsT lhs) + : ITransientExpression{false, static_cast(lhs)}, m_lhs(lhs) {} + }; + + template + class ExprLhs { + LhsT m_lhs; + + public: + explicit constexpr ExprLhs(LhsT lhs) + : m_lhs(lhs) {} + +#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR(id, op) \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT &&rhs) \ + ->std::enable_if_t< \ + Detail::conjunction, \ + Detail::negation>>>::value, \ + BinaryExpr> { \ + return { \ + static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT rhs) \ + ->std::enable_if_t< \ + Detail::conjunction, \ + capture_by_value>::value, \ + BinaryExpr> { \ + return { \ + static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT rhs) \ + ->std::enable_if_t< \ + Detail::conjunction< \ + Detail::negation>, \ + Detail::is_eq_0_comparable, /* We allow long because we want `ptr op NULL` to be accepted */ \ + Detail::disjunction, \ + std::is_same>>::value, \ + BinaryExpr> { \ + if (rhs != 0) { throw_test_failure_exception(); } \ + return { \ + static_cast(lhs.m_lhs op 0), lhs.m_lhs, #op##_sr, rhs}; \ + } \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT rhs) \ + ->std::enable_if_t< \ + Detail::conjunction< \ + Detail::negation>, \ + Detail::is_eq_0_comparable, /* We allow long because we want `ptr op NULL` to be accepted */ \ + Detail::disjunction, \ + std::is_same>>::value, \ + BinaryExpr> { \ + if (lhs.m_lhs != 0) { throw_test_failure_exception(); } \ + return {static_cast(0 op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } + + CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR(eq, ==) + CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR(ne, !=) + +#undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR + +#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR(id, op) \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT &&rhs) \ + ->std::enable_if_t< \ + Detail::conjunction, \ + Detail::negation>>>::value, \ + BinaryExpr> { \ + return { \ + static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT rhs) \ + ->std::enable_if_t< \ + Detail::conjunction, \ + capture_by_value>::value, \ + BinaryExpr> { \ + return { \ + static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT rhs) \ + ->std::enable_if_t< \ + Detail::conjunction< \ + Detail::negation>, \ + Detail::is_##id##_0_comparable, \ + std::is_same>::value, \ + BinaryExpr> { \ + if (rhs != 0) { throw_test_failure_exception(); } \ + return { \ + static_cast(lhs.m_lhs op 0), lhs.m_lhs, #op##_sr, rhs}; \ + } \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT rhs) \ + ->std::enable_if_t< \ + Detail::conjunction< \ + Detail::negation>, \ + Detail::is_##id##_0_comparable, \ + std::is_same>::value, \ + BinaryExpr> { \ + if (lhs.m_lhs != 0) { throw_test_failure_exception(); } \ + return {static_cast(0 op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } + + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR(lt, <) + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR(le, <=) + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR(gt, >) + CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR(ge, >=) + +#undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR + +#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT &&rhs) \ + ->std::enable_if_t< \ + !capture_by_value>::value, \ + BinaryExpr> { \ + return { \ + static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } \ + template \ + constexpr friend auto operator op(ExprLhs &&lhs, RhsT rhs) \ + ->std::enable_if_t::value, \ + BinaryExpr> { \ + return { \ + static_cast(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs}; \ + } + + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) + CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) + +#undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR + + template + friend auto operator&&(ExprLhs &&, RhsT &&) -> BinaryExpr { + static_assert(always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + friend auto operator||(ExprLhs &&, RhsT &&) -> BinaryExpr { + static_assert(always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + constexpr auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{m_lhs}; + } + }; + + struct Decomposer { + template>::value, + int> + = 0> + constexpr friend auto operator<=(Decomposer &&, T &&lhs) -> ExprLhs { + return ExprLhs{lhs}; + } + + template::value, int> = 0> + constexpr friend auto operator<=(Decomposer &&, T value) -> ExprLhs { + return ExprLhs{value}; + } + }; } // end namespace Catch @@ -5703,9 +5514,9 @@ namespace Catch { #pragma warning(pop) #endif #ifdef __clang__ -# pragma clang diagnostic pop +#pragma clang diagnostic pop #elif defined __GNUC__ -# pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif #endif // CATCH_DECOMPOSER_HPP_INCLUDED @@ -5714,66 +5525,62 @@ namespace Catch { namespace Catch { - struct AssertionReaction { - bool shouldDebugBreak = false; - bool shouldThrow = false; - bool shouldSkip = false; - }; + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + bool shouldSkip = false; + }; - class AssertionHandler { - AssertionInfo m_assertionInfo; - AssertionReaction m_reaction; - bool m_completed = false; - IResultCapture& m_resultCapture; + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture &m_resultCapture; - public: - AssertionHandler - ( StringRef macroName, - SourceLineInfo const& lineInfo, - StringRef capturedExpression, - ResultDisposition::Flags resultDisposition ); - ~AssertionHandler() { - if ( !m_completed ) { - m_resultCapture.handleIncomplete( m_assertionInfo ); - } - } + public: + AssertionHandler(StringRef macroName, + SourceLineInfo const &lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition); + ~AssertionHandler() { + if (!m_completed) { + m_resultCapture.handleIncomplete(m_assertionInfo); + } + } + template + constexpr void handleExpr(ExprLhs const &expr) { + handleExpr(expr.makeUnaryExpr()); + } + void handleExpr(ITransientExpression const &expr); - template - constexpr void handleExpr( ExprLhs const& expr ) { - handleExpr( expr.makeUnaryExpr() ); - } - void handleExpr( ITransientExpression const& expr ); + void handleMessage(ResultWas::OfType resultType, std::string &&message); - void handleMessage(ResultWas::OfType resultType, std::string&& message); + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); - void handleExceptionThrownAsExpected(); - void handleUnexpectedExceptionNotThrown(); - void handleExceptionNotThrownAsExpected(); - void handleThrowingCallSkipped(); - void handleUnexpectedInflightException(); + void complete(); - void complete(); + // query + auto allowThrows() const -> bool; + }; - // query - auto allowThrows() const -> bool; - }; - - void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ); + void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str); } // 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 +#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr #else - #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr +#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr #endif #endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED @@ -5781,7 +5588,7 @@ namespace Catch { // 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 - #pragma GCC diagnostic ignored "-Wparentheses" +#pragma GCC diagnostic ignored "-Wparentheses" #endif #if !defined(CATCH_CONFIG_DISABLE) @@ -5792,138 +5599,131 @@ namespace Catch { // Another way to speed-up compilation is to omit local try-catch for REQUIRE* // macros. #define INTERNAL_CATCH_TRY -#define INTERNAL_CATCH_CATCH( capturer ) +#define INTERNAL_CATCH_CATCH(capturer) #else // CATCH_CONFIG_FAST_COMPILE #define INTERNAL_CATCH_TRY try -#define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } +#define INTERNAL_CATCH_CATCH(handler) \ + catch (...) { \ + (handler).handleUnexpectedInflightException(); \ + } #endif /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ - do { /* NOLINT(bugprone-infinite-loop) */ \ - /* The expression should not be evaluated, but warnings should hopefully be checked */ \ - CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ - Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ - INTERNAL_CATCH_TRY { \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - catchAssertionHandler.complete(); \ - } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look - // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. +#define INTERNAL_CATCH_TEST(macroName, resultDisposition, ...) \ + do { /* NOLINT(bugprone-infinite-loop) */ \ + /* The expression should not be evaluated, but warnings should hopefully be checked */ \ + CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr(Catch::Decomposer() <= __VA_ARGS__); /* NOLINT(bugprone-chained-comparison) */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + } \ + INTERNAL_CATCH_CATCH(catchAssertionHandler) \ + catchAssertionHandler.complete(); \ + } while ((void)0, (false) && static_cast(!!(__VA_ARGS__))) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( Catch::getResultCapture().lastAssertionPassed() ) +#define INTERNAL_CATCH_IF(macroName, resultDisposition, ...) \ + INTERNAL_CATCH_TEST(macroName, resultDisposition, __VA_ARGS__); \ + if (Catch::getResultCapture().lastAssertionPassed()) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( !Catch::getResultCapture().lastAssertionPassed() ) +#define INTERNAL_CATCH_ELSE(macroName, resultDisposition, ...) \ + INTERNAL_CATCH_TEST(macroName, resultDisposition, __VA_ARGS__); \ + if (!Catch::getResultCapture().lastAssertionPassed()) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ - try { \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ - static_cast(__VA_ARGS__); \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ - } \ - catch( ... ) { \ - catchAssertionHandler.handleUnexpectedInflightException(); \ - } \ - catchAssertionHandler.complete(); \ - } while( false ) +#define INTERNAL_CATCH_NO_THROW(macroName, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + try { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ + static_cast(__VA_ARGS__); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } catch (...) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + catchAssertionHandler.complete(); \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ - 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 \ - catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ - } \ - catch( ... ) { \ - catchAssertionHandler.handleExceptionThrownAsExpected(); \ - } \ - else \ - catchAssertionHandler.handleThrowingCallSkipped(); \ - catchAssertionHandler.complete(); \ - } while( false ) +#define INTERNAL_CATCH_THROWS(macroName, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ + 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 \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } catch (...) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + catchAssertionHandler.complete(); \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ - 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 \ - catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ - } \ - catch( exceptionType const& ) { \ - catchAssertionHandler.handleExceptionThrownAsExpected(); \ - } \ - catch( ... ) { \ - catchAssertionHandler.handleUnexpectedInflightException(); \ - } \ - else \ - catchAssertionHandler.handleThrowingCallSkipped(); \ - catchAssertionHandler.complete(); \ - } while( false ) - - +#define INTERNAL_CATCH_THROWS_AS(macroName, exceptionType, resultDisposition, expr) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition); \ + 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 \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } catch (exceptionType const &) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } catch (...) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + catchAssertionHandler.complete(); \ + } while (false) /////////////////////////////////////////////////////////////////////////////// // Although this is matcher-based, it can be used with just a string -#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - 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 \ - catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ - } \ - catch( ... ) { \ - Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher ); \ - } \ - else \ - catchAssertionHandler.handleThrowingCallSkipped(); \ - catchAssertionHandler.complete(); \ - } while( false ) +#define INTERNAL_CATCH_THROWS_STR_MATCHES(macroName, resultDisposition, matcher, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition); \ + 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 \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } catch (...) { \ + Catch::handleExceptionMatchExpr(catchAssertionHandler, matcher); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + catchAssertionHandler.complete(); \ + } while (false) #endif // CATCH_CONFIG_DISABLE #endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED - #ifndef CATCH_SECTION_HPP_INCLUDED #define CATCH_SECTION_HPP_INCLUDED - - - /** \file * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option * @@ -5936,21 +5736,16 @@ namespace Catch { #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 +#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 +#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 @@ -5958,15 +5753,16 @@ namespace Catch { namespace Catch { - class Timer { - uint64_t m_nanoseconds = 0; - public: - void start(); - auto getElapsedNanoseconds() const -> uint64_t; - auto getElapsedMicroseconds() const -> uint64_t; - auto getElapsedMilliseconds() const -> unsigned int; - auto getElapsedSeconds() const -> double; - }; + class Timer { + uint64_t m_nanoseconds = 0; + + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; } // namespace Catch @@ -5974,46 +5770,46 @@ namespace Catch { namespace Catch { - class Section : Detail::NonCopyable { - public: - Section( SectionInfo&& info ); - Section( SourceLineInfo const& _lineInfo, - StringRef _name, - const char* const = nullptr ); - ~Section(); + class Section : Detail::NonCopyable { + public: + Section(SectionInfo &&info); + Section(SourceLineInfo const &_lineInfo, + StringRef _name, + const char *const = nullptr); + ~Section(); - // This indicates whether the section should be executed or not - explicit operator bool() const; + // This indicates whether the section should be executed or not + explicit operator bool() const; - private: - SectionInfo m_info; + private: + SectionInfo m_info; - Counts m_assertions; - bool m_sectionIncluded; - Timer m_timer; - }; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; } // end namespace Catch #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_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 +#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 @@ -6022,77 +5818,68 @@ namespace Catch { // 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. - // The arguments are used as a dummy for checking warnings in the passed - // expressions. - int GetNewSectionHint( StringRef, const char* const = nullptr ); - } // namespace Detail + namespace Detail { + // Intentionally without linkage, as it should only be used as a dummy + // symbol for static analysis. + // The arguments are used as a dummy for checking warnings in the passed + // expressions. + int GetNewSectionHint(StringRef, const char *const = nullptr); + } // 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]] const int catchInternalPreviousSectionHint = catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint(__VA_ARGS__); \ + catchInternalPreviousSectionHint == __LINE__) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -# define INTERNAL_CATCH_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ - if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ - catchInternalSectionHint, \ - catchInternalSectionHint = \ - Catch::Detail::GetNewSectionHint(__VA_ARGS__); \ - 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]] const int catchInternalPreviousSectionHint = \ - catchInternalSectionHint, \ - catchInternalSectionHint = Catch::Detail::GetNewSectionHint( \ - ( Catch::ReusableStringStream() << __VA_ARGS__ ).str()); \ - 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]] const int catchInternalPreviousSectionHint = catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint( \ + (Catch::ReusableStringStream() << __VA_ARGS__).str()); \ + catchInternalPreviousSectionHint == __LINE__) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif - #endif // CATCH_SECTION_HPP_INCLUDED - #ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED #define CATCH_TEST_REGISTRY_HPP_INCLUDED - - #ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED #define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED namespace Catch { - class ITestInvoker { - public: - virtual void prepareTestCase(); - virtual void tearDownTestCase(); - virtual void invoke() const = 0; - virtual ~ITestInvoker(); // = default - }; + class ITestInvoker { + public: + virtual void prepareTestCase(); + virtual void tearDownTestCase(); + virtual void invoke() const = 0; + virtual ~ITestInvoker(); // = default + }; } // namespace Catch #endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED - #ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED #define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED -#define INTERNAL_CATCH_EXPAND1( param ) INTERNAL_CATCH_EXPAND2( param ) -#define INTERNAL_CATCH_EXPAND2( ... ) INTERNAL_CATCH_NO##__VA_ARGS__ -#define INTERNAL_CATCH_DEF( ... ) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO##__VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_REMOVE_PARENS( ... ) \ - INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ ) +#define INTERNAL_CATCH_REMOVE_PARENS(...) \ + INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) #endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED @@ -6103,109 +5890,108 @@ namespace Catch { #pragma GCC diagnostic ignored "-Wunused-variable" #endif - - namespace Catch { -template -class TestInvokerAsMethod : public ITestInvoker { + template + class TestInvokerAsMethod : public ITestInvoker { void (C::*m_testAsMethod)(); -public: - constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept: - m_testAsMethod( testAsMethod ) {} + + public: + constexpr TestInvokerAsMethod(void (C::*testAsMethod)()) noexcept + : m_testAsMethod(testAsMethod) {} void invoke() const override { - C obj; - (obj.*m_testAsMethod)(); + C obj; + (obj.*m_testAsMethod)(); } -}; + }; -Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ); + Detail::unique_ptr makeTestInvoker(void (*testAsFunction)()); -template -Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { - return Detail::make_unique>( testAsMethod ); -} + template + Detail::unique_ptr makeTestInvoker(void (C::*testAsMethod)()) { + return Detail::make_unique>(testAsMethod); + } -template -class TestInvokerFixture : public ITestInvoker { - void ( C::*m_testAsMethod )() const; + template + class TestInvokerFixture : public ITestInvoker { + void (C::*m_testAsMethod)() const; Detail::unique_ptr m_fixture = nullptr; -public: - constexpr TestInvokerFixture( void ( C::*testAsMethod )() const ) noexcept: - m_testAsMethod( testAsMethod ) {} + public: + constexpr TestInvokerFixture(void (C::*testAsMethod)() const) noexcept + : m_testAsMethod(testAsMethod) {} void prepareTestCase() override { - m_fixture = Detail::make_unique(); + m_fixture = Detail::make_unique(); } void tearDownTestCase() override { - m_fixture.reset(); + m_fixture.reset(); } void invoke() const override { - auto* f = m_fixture.get(); - ( f->*m_testAsMethod )(); + auto *f = m_fixture.get(); + (f->*m_testAsMethod)(); } -}; + }; -template -Detail::unique_ptr makeTestInvokerFixture( void ( C::*testAsMethod )() const ) { - return Detail::make_unique>( testAsMethod ); -} + template + Detail::unique_ptr makeTestInvokerFixture(void (C::*testAsMethod)() const) { + return Detail::make_unique>(testAsMethod); + } -struct NameAndTags { - constexpr NameAndTags( StringRef name_ = StringRef(), - StringRef tags_ = StringRef() ) noexcept: - name( name_ ), tags( tags_ ) {} + struct NameAndTags { + constexpr NameAndTags(StringRef name_ = StringRef(), + StringRef tags_ = StringRef()) noexcept + : name(name_), tags(tags_) {} StringRef name; StringRef tags; -}; + }; -struct AutoReg : Detail::NonCopyable { - AutoReg( Detail::unique_ptr invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; -}; + struct AutoReg : Detail::NonCopyable { + AutoReg(Detail::unique_ptr invoker, SourceLineInfo const &lineInfo, StringRef classOrMethod, NameAndTags const &nameAndTags) noexcept; + }; } // end namespace Catch #if defined(CATCH_CONFIG_DISABLE) - #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ - static inline void TestName() - #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ - namespace{ \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ - void test(); \ - }; \ - } \ - void TestName::test() +#define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(TestName, ...) \ + static inline void TestName() +#define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(TestName, ClassName, ...) \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() #endif - #if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ - static void TestName(); \ - 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( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( ... ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) - -#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TESTCASE2(TestName, ...) \ + static void TestName(); \ + 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(&TestName), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{__VA_ARGS__}); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static void TestName() +#define INTERNAL_CATCH_TESTCASE(...) \ + INTERNAL_CATCH_TESTCASE2(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_), __VA_ARGS__) +#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 ), Catch::NameAndTags const& ); - }; - } // namespace Detail + namespace Detail { + struct DummyUse { + DummyUse(void (*)(int), Catch::NameAndTags const &); + }; + } // namespace Detail } // namespace Catch // Note that both the presence of the argument and its exact name are @@ -6215,92 +6001,87 @@ namespace Catch { // 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::NameAndTags{ __VA_ARGS__ } ); \ - 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 ), __VA_ARGS__ ) - +#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::NameAndTags{__VA_ARGS__}); \ + 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), __VA_ARGS__) #endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - namespace{ \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ - void test(); \ - }; \ - const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ - Catch::makeTestInvoker( &TestName::test ), \ - CATCH_INTERNAL_LINEINFO, \ - #ClassName##_catch_sr, \ - Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ - } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - void TestName::test() - #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_TEST_CASE_METHOD2(TestName, ClassName, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)( \ + Catch::makeTestInvoker(&TestName::test), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName##_catch_sr, \ + Catch::NameAndTags{__VA_ARGS__}); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() +#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_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - namespace { \ - struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) { \ - void test() const; \ - }; \ - const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ - Catch::makeTestInvokerFixture( &TestName::test ), \ - CATCH_INTERNAL_LINEINFO, \ - #ClassName##_catch_sr, \ - Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ - } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - void TestName::test() const - #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... ) \ - INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2(TestName, ClassName, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test() const; \ + }; \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)( \ + Catch::makeTestInvokerFixture(&TestName::test), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName##_catch_sr, \ + Catch::NameAndTags{__VA_ARGS__}); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() const +#define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE(ClassName, ...) \ + INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2(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_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 { \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ - } while(false) - +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, ...) \ + do { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(Catch::makeTestInvoker(Function), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{__VA_ARGS__}); /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + } while (false) #endif // CATCH_TEST_REGISTRY_HPP_INCLUDED - #ifndef CATCH_UNREACHABLE_HPP_INCLUDED #define CATCH_UNREACHABLE_HPP_INCLUDED @@ -6315,43 +6096,42 @@ static int catchInternalSectionHint = 0; #include -#if defined( __cpp_lib_unreachable ) && __cpp_lib_unreachable > 202202L -# include +#if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable > 202202L +#include namespace Catch { - namespace Detail { - using Unreachable = std::unreachable; - } + namespace Detail { + using Unreachable = std::unreachable; + } } // namespace Catch #else // vv If we do not have std::unreachable, we implement something similar namespace Catch { - namespace Detail { + namespace Detail { - [[noreturn]] - inline void Unreachable() noexcept { -# if defined( NDEBUG ) -# if defined( _MSC_VER ) && !defined( __clang__ ) - __assume( false ); -# elif defined( __GNUC__ ) - __builtin_unreachable(); -# else // vv platform without known optimization hint - std::terminate(); -# endif -# else // ^^ NDEBUG - // For non-release builds, we prefer termination on bug over UB - std::terminate(); -# endif // - } + [[noreturn]] + inline void Unreachable() noexcept { +#if defined(NDEBUG) +#if defined(_MSC_VER) && !defined(__clang__) + __assume(false); +#elif defined(__GNUC__) + __builtin_unreachable(); +#else // vv platform without known optimization hint + std::terminate(); +#endif +#else // ^^ NDEBUG + // For non-release builds, we prefer termination on bug over UB + std::terminate(); +#endif // + } - } // namespace Detail + } // namespace Detail } // end namespace Catch #endif #endif // CATCH_UNREACHABLE_HPP_INCLUDED - // All of our user-facing macros support configuration toggle, that // forces them to be defined prefixed with CATCH_. We also like to // support another toggle that can minimize (disable) their implementation. @@ -6359,217 +6139,234 @@ namespace Catch { #if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) - #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) - #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_REQUIRE(...) INTERNAL_CATCH_TEST("CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define CATCH_REQUIRE_FALSE(...) INTERNAL_CATCH_TEST("CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__) - #define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) - #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) - #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define CATCH_REQUIRE_THROWS(...) INTERNAL_CATCH_THROWS("CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS("CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr) +#define CATCH_REQUIRE_NOTHROW(...) INTERNAL_CATCH_NO_THROW("CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__) - #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) - #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) - #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) - #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +#define CATCH_CHECK(...) INTERNAL_CATCH_TEST("CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_CHECK_FALSE(...) INTERNAL_CATCH_TEST("CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__) +#define CATCH_CHECKED_IF(...) INTERNAL_CATCH_IF("CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__) +#define CATCH_CHECKED_ELSE(...) INTERNAL_CATCH_ELSE("CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__) +#define CATCH_CHECK_NOFAIL(...) INTERNAL_CATCH_TEST("CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__) - #define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) - #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_CHECK_THROWS(...) INTERNAL_CATCH_THROWS("CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_CHECK_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS("CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr) +#define CATCH_CHECK_NOTHROW(...) INTERNAL_CATCH_NO_THROW("CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) - #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) - #define CATCH_FAIL( ... ) do { \ - INTERNAL_CATCH_MSG("CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \ - Catch::Detail::Unreachable(); \ - } while ( false ) - #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CATCH_SKIP( ... ) do { \ - INTERNAL_CATCH_MSG( "CATCH_SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \ - Catch::Detail::Unreachable(); \ - } while (false) +#define CATCH_TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define CATCH_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define CATCH_TEST_CASE_PERSISTENT_FIXTURE(className, ...) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE(className, __VA_ARGS__) +#define CATCH_REGISTER_TEST_CASE(Function, ...) INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define CATCH_SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define CATCH_DYNAMIC_SECTION(...) INTERNAL_CATCH_DYNAMIC_SECTION(__VA_ARGS__) +#define CATCH_FAIL(...) \ + do { \ + INTERNAL_CATCH_MSG("CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__); \ + Catch::Detail::Unreachable(); \ + } while (false) +#define CATCH_FAIL_CHECK(...) INTERNAL_CATCH_MSG("CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_SUCCEED(...) INTERNAL_CATCH_MSG("CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_SKIP(...) \ + do { \ + INTERNAL_CATCH_MSG("CATCH_SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__); \ + Catch::Detail::Unreachable(); \ + } while (false) +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + CATCH_SUCCEED(#__VA_ARGS__) +#define CATCH_STATIC_REQUIRE_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + CATCH_SUCCEED(#__VA_ARGS__) +#define CATCH_STATIC_CHECK(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + CATCH_SUCCEED(#__VA_ARGS__) +#define CATCH_STATIC_CHECK_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + CATCH_SUCCEED(#__VA_ARGS__) +#else +#define CATCH_STATIC_REQUIRE(...) CATCH_REQUIRE(__VA_ARGS__) +#define CATCH_STATIC_REQUIRE_FALSE(...) CATCH_REQUIRE_FALSE(__VA_ARGS__) +#define CATCH_STATIC_CHECK(...) CATCH_CHECK(__VA_ARGS__) +#define CATCH_STATIC_CHECK_FALSE(...) CATCH_CHECK_FALSE(__VA_ARGS__) +#endif - #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) - #define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) - #define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) - #define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) - #define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) - #else - #define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) - #define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) - #define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ ) - #define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ ) - #endif - - - // "BDD-style" convenience wrappers - #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) - #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) - #define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) - #define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) - #define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) - #define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) - #define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) - #define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO(...) CATCH_TEST_CASE("Scenario: " __VA_ARGS__) +#define CATCH_SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#define CATCH_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Given: " << desc) +#define CATCH_AND_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION("And given: " << desc) +#define CATCH_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" When: " << desc) +#define CATCH_AND_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And when: " << desc) +#define CATCH_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Then: " << desc) +#define CATCH_AND_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And: " << desc) #elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled - #define CATCH_REQUIRE( ... ) (void)(0) - #define CATCH_REQUIRE_FALSE( ... ) (void)(0) +#define CATCH_REQUIRE(...) (void)(0) +#define CATCH_REQUIRE_FALSE(...) (void)(0) - #define CATCH_REQUIRE_THROWS( ... ) (void)(0) - #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) - #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) +#define CATCH_REQUIRE_THROWS(...) (void)(0) +#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) (void)(0) +#define CATCH_REQUIRE_NOTHROW(...) (void)(0) - #define CATCH_CHECK( ... ) (void)(0) - #define CATCH_CHECK_FALSE( ... ) (void)(0) - #define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) - #define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) - #define CATCH_CHECK_NOFAIL( ... ) (void)(0) +#define CATCH_CHECK(...) (void)(0) +#define CATCH_CHECK_FALSE(...) (void)(0) +#define CATCH_CHECKED_IF(...) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE(...) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL(...) (void)(0) - #define CATCH_CHECK_THROWS( ... ) (void)(0) - #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) - #define CATCH_CHECK_NOTHROW( ... ) (void)(0) +#define CATCH_CHECK_THROWS(...) (void)(0) +#define CATCH_CHECK_THROWS_AS(expr, exceptionType) (void)(0) +#define CATCH_CHECK_NOTHROW(...) (void)(0) - #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) - #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) - #define CATCH_METHOD_AS_TEST_CASE( method, ... ) - #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) - #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) - #define CATCH_SECTION( ... ) - #define CATCH_DYNAMIC_SECTION( ... ) - #define CATCH_FAIL( ... ) (void)(0) - #define CATCH_FAIL_CHECK( ... ) (void)(0) - #define CATCH_SUCCEED( ... ) (void)(0) - #define CATCH_SKIP( ... ) (void)(0) +#define CATCH_TEST_CASE(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_)) +#define CATCH_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_)) +#define CATCH_METHOD_AS_TEST_CASE(method, ...) +#define CATCH_TEST_CASE_PERSISTENT_FIXTURE(className, ...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_)) +#define CATCH_REGISTER_TEST_CASE(Function, ...) (void)(0) +#define CATCH_SECTION(...) +#define CATCH_DYNAMIC_SECTION(...) +#define CATCH_FAIL(...) (void)(0) +#define CATCH_FAIL_CHECK(...) (void)(0) +#define CATCH_SUCCEED(...) (void)(0) +#define CATCH_SKIP(...) (void)(0) - #define CATCH_STATIC_REQUIRE( ... ) (void)(0) - #define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) - #define CATCH_STATIC_CHECK( ... ) (void)(0) - #define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0) +#define CATCH_STATIC_REQUIRE(...) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE(...) (void)(0) +#define CATCH_STATIC_CHECK(...) (void)(0) +#define CATCH_STATIC_CHECK_FALSE(...) (void)(0) - // "BDD-style" convenience wrappers - #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) - #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) - #define CATCH_GIVEN( desc ) - #define CATCH_AND_GIVEN( desc ) - #define CATCH_WHEN( desc ) - #define CATCH_AND_WHEN( desc ) - #define CATCH_THEN( desc ) - #define CATCH_AND_THEN( desc ) +// "BDD-style" convenience wrappers +#define CATCH_SCENARIO(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_)) +#define CATCH_SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_), className) +#define CATCH_GIVEN(desc) +#define CATCH_AND_GIVEN(desc) +#define CATCH_WHEN(desc) +#define CATCH_AND_WHEN(desc) +#define CATCH_THEN(desc) +#define CATCH_AND_THEN(desc) #elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented - #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) - #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define REQUIRE(...) INTERNAL_CATCH_TEST("REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define REQUIRE_FALSE(...) INTERNAL_CATCH_TEST("REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__) - #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) - #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) - #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_THROWS(...) INTERNAL_CATCH_THROWS("REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define REQUIRE_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS("REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr) +#define REQUIRE_NOTHROW(...) INTERNAL_CATCH_NO_THROW("REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__) - #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) - #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) - #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) - #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +#define CHECK(...) INTERNAL_CATCH_TEST("CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CHECK_FALSE(...) INTERNAL_CATCH_TEST("CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__) +#define CHECKED_IF(...) INTERNAL_CATCH_IF("CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__) +#define CHECKED_ELSE(...) INTERNAL_CATCH_ELSE("CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__) +#define CHECK_NOFAIL(...) INTERNAL_CATCH_TEST("CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__) - #define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) - #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_THROWS(...) INTERNAL_CATCH_THROWS("CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CHECK_THROWS_AS(expr, exceptionType) INTERNAL_CATCH_THROWS_AS("CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr) +#define CHECK_NOTHROW(...) INTERNAL_CATCH_NO_THROW("CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) - #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) - #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) - #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) - #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) - #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) - #define FAIL( ... ) do { \ - INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \ - Catch::Detail::Unreachable(); \ - } while (false) - #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) - #define SKIP( ... ) do { \ - INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ); \ - Catch::Detail::Unreachable(); \ - } while (false) +#define TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define TEST_CASE_PERSISTENT_FIXTURE(className, ...) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE(className, __VA_ARGS__) +#define REGISTER_TEST_CASE(Function, ...) INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define DYNAMIC_SECTION(...) INTERNAL_CATCH_DYNAMIC_SECTION(__VA_ARGS__) +#define FAIL(...) \ + do { \ + INTERNAL_CATCH_MSG("FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__); \ + Catch::Detail::Unreachable(); \ + } while (false) +#define FAIL_CHECK(...) INTERNAL_CATCH_MSG("FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define SUCCEED(...) INTERNAL_CATCH_MSG("SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define SKIP(...) \ + do { \ + INTERNAL_CATCH_MSG("SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__); \ + Catch::Detail::Unreachable(); \ + } while (false) +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + SUCCEED(#__VA_ARGS__) +#define STATIC_REQUIRE_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + SUCCEED("!(" #__VA_ARGS__ ")") +#define STATIC_CHECK(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + SUCCEED(#__VA_ARGS__) +#define STATIC_CHECK_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + SUCCEED("!(" #__VA_ARGS__ ")") +#else +#define STATIC_REQUIRE(...) REQUIRE(__VA_ARGS__) +#define STATIC_REQUIRE_FALSE(...) REQUIRE_FALSE(__VA_ARGS__) +#define STATIC_CHECK(...) CHECK(__VA_ARGS__) +#define STATIC_CHECK_FALSE(...) CHECK_FALSE(__VA_ARGS__) +#endif - #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) - #define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) - #define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) - #define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) - #define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) - #else - #define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) - #define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) - #define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ ) - #define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ ) - #endif - - // "BDD-style" convenience wrappers - #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) - #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) - #define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) - #define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) - #define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) - #define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) - #define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) - #define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) +// "BDD-style" convenience wrappers +#define SCENARIO(...) TEST_CASE("Scenario: " __VA_ARGS__) +#define SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#define GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Given: " << desc) +#define AND_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION("And given: " << desc) +#define WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" When: " << desc) +#define AND_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And when: " << desc) +#define THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Then: " << desc) +#define AND_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And: " << desc) #elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled - #define REQUIRE( ... ) (void)(0) - #define REQUIRE_FALSE( ... ) (void)(0) +#define REQUIRE(...) (void)(0) +#define REQUIRE_FALSE(...) (void)(0) - #define REQUIRE_THROWS( ... ) (void)(0) - #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) - #define REQUIRE_NOTHROW( ... ) (void)(0) +#define REQUIRE_THROWS(...) (void)(0) +#define REQUIRE_THROWS_AS(expr, exceptionType) (void)(0) +#define REQUIRE_NOTHROW(...) (void)(0) - #define CHECK( ... ) (void)(0) - #define CHECK_FALSE( ... ) (void)(0) - #define CHECKED_IF( ... ) if (__VA_ARGS__) - #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) - #define CHECK_NOFAIL( ... ) (void)(0) +#define CHECK(...) (void)(0) +#define CHECK_FALSE(...) (void)(0) +#define CHECKED_IF(...) if (__VA_ARGS__) +#define CHECKED_ELSE(...) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL(...) (void)(0) - #define CHECK_THROWS( ... ) (void)(0) - #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) - #define CHECK_NOTHROW( ... ) (void)(0) +#define CHECK_THROWS(...) (void)(0) +#define CHECK_THROWS_AS(expr, exceptionType) (void)(0) +#define CHECK_NOTHROW(...) (void)(0) - #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) - #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) - #define METHOD_AS_TEST_CASE( method, ... ) - #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) - #define REGISTER_TEST_CASE( Function, ... ) (void)(0) - #define SECTION( ... ) - #define DYNAMIC_SECTION( ... ) - #define FAIL( ... ) (void)(0) - #define FAIL_CHECK( ... ) (void)(0) - #define SUCCEED( ... ) (void)(0) - #define SKIP( ... ) (void)(0) +#define TEST_CASE(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_), __VA_ARGS__) +#define TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_)) +#define METHOD_AS_TEST_CASE(method, ...) +#define TEST_CASE_PERSISTENT_FIXTURE(className, ...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_), __VA_ARGS__) +#define REGISTER_TEST_CASE(Function, ...) (void)(0) +#define SECTION(...) +#define DYNAMIC_SECTION(...) +#define FAIL(...) (void)(0) +#define FAIL_CHECK(...) (void)(0) +#define SUCCEED(...) (void)(0) +#define SKIP(...) (void)(0) - #define STATIC_REQUIRE( ... ) (void)(0) - #define STATIC_REQUIRE_FALSE( ... ) (void)(0) - #define STATIC_CHECK( ... ) (void)(0) - #define STATIC_CHECK_FALSE( ... ) (void)(0) +#define STATIC_REQUIRE(...) (void)(0) +#define STATIC_REQUIRE_FALSE(...) (void)(0) +#define STATIC_CHECK(...) (void)(0) +#define STATIC_CHECK_FALSE(...) (void)(0) - // "BDD-style" convenience wrappers - #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) ) - #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) +// "BDD-style" convenience wrappers +#define SCENARIO(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_)) +#define SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_TEST_), className) - #define GIVEN( desc ) - #define AND_GIVEN( desc ) - #define WHEN( desc ) - #define AND_WHEN( desc ) - #define THEN( desc ) - #define AND_THEN( desc ) +#define GIVEN(desc) +#define AND_GIVEN(desc) +#define WHEN(desc) +#define AND_WHEN(desc) +#define THEN(desc) +#define AND_THEN(desc) #endif // ^^ unprefixed, disabled @@ -6577,30 +6374,25 @@ namespace Catch { #endif // CATCH_TEST_MACROS_HPP_INCLUDED - #ifndef CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED #define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED - - #ifndef CATCH_PREPROCESSOR_HPP_INCLUDED #define CATCH_PREPROCESSOR_HPP_INCLUDED - #if defined(__GNUC__) // We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work #pragma GCC system_header #endif - namespace Catch { - namespace Detail { - template - struct priority_tag : priority_tag {}; - template <> - struct priority_tag<0> {}; - } -} + namespace Detail { + template + struct priority_tag : priority_tag {}; + template<> + struct priority_tag<0> {}; + } // namespace Detail +} // namespace Catch #define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ #define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) @@ -6613,9 +6405,9 @@ namespace Catch { #define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ // MSVC needs more evaluations #define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) #else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) #endif #define CATCH_REC_END(...) @@ -6628,16 +6420,16 @@ namespace Catch { #define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 #define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 #define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER(CATCH_REC_NEXT0)(test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, __VA_ARGS__) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST0))(f, peek, __VA_ARGS__) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1))(f, peek, __VA_ARGS__) -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD))(f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD))(f, userdata, peek, __VA_ARGS__) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER(CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD))(f, userdata, peek, __VA_ARGS__) // Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, // and passes userdata as the first parameter to each invocation, @@ -6668,8 +6460,8 @@ namespace Catch { #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...) \ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST, __VA_ARGS__) #define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) #define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) @@ -6685,145 +6477,172 @@ namespace Catch { #define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper(Catch::Detail::priority_tag<1>) noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template