diff options
| author | Martin Hořeňovský | 2023-12-11 00:55:40 +0100 |
|---|---|---|
| committer | Martin Hořeňovský | 2023-12-11 00:55:40 +0100 |
| commit | 53d0d913a422d356b23dd927547febdf69ee9081 (patch) | |
| tree | b2b624bde9ead96c0e448c8d6ba7dcba6e5ebb40 /extras/catch_amalgamated.cpp | |
| parent | 1648c30ec3ac1aedd1417d31de18801767ab0d72 (diff) | |
| download | catch3-3.5.0.tar.gz | |
v3.5.0v3.5.0
Diffstat (limited to 'extras/catch_amalgamated.cpp')
| -rw-r--r-- | extras/catch_amalgamated.cpp | 861 |
1 files changed, 775 insertions, 86 deletions
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index eba3f00a..cfc38a46 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -1,3 +1,4 @@ + // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at @@ -5,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.4.0 -// Generated: 2023-07-13 13:23:09.554273 +// Catch v3.5.0 +// Generated: 2023-12-11 00:51:07.662625 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -48,6 +49,80 @@ namespace Catch { } // namespace Catch +// Adapted from donated nonius code. + + +#include <vector> + +namespace Catch { + namespace Benchmark { + namespace Detail { + SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector<double> samples; + samples.reserve(static_cast<size_t>(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 wrap_estimate = [](Estimate<double> e) { + return Estimate<FDuration> { + FDuration(e.point), + FDuration(e.lower_bound), + FDuration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector<FDuration> 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<FDuration> samples; + samples.reserve(static_cast<size_t>(last - first)); + + FDuration mean = FDuration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(FDuration(*it)); + mean += FDuration(*it); + } + mean /= i; + + return SampleAnalysis{ + CATCH_MOVE(samples), + Estimate<FDuration>{ mean, mean, mean, 0.0 }, + Estimate<FDuration>{ FDuration( 0 ), + FDuration( 0 ), + FDuration( 0 ), + 0.0 }, + OutlierClassification{}, + 0.0 + }; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + + namespace Catch { @@ -87,7 +162,9 @@ namespace Catch { +#include <algorithm> #include <cassert> +#include <cmath> #include <cstddef> #include <numeric> #include <random> @@ -106,8 +183,8 @@ namespace Catch { static sample resample( URng& rng, unsigned int resamples, - std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last, + double const* first, + double const* last, Estimator& estimator ) { auto n = static_cast<size_t>( last - first ); std::uniform_int_distribution<decltype( n )> dist( 0, @@ -127,7 +204,7 @@ namespace Catch { dist( rng ) )] ); } const auto estimate = - estimator( resampled.begin(), resampled.end() ); + estimator( resampled.data(), resampled.data() + resampled.size() ); out.push_back( estimate ); } std::sort( out.begin(), out.end() ); @@ -244,8 +321,7 @@ namespace Catch { } static double - standard_deviation( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ) { + standard_deviation( double const* first, double const* last ) { auto m = Catch::Benchmark::Detail::mean( first, last ); double variance = std::accumulate( first, @@ -259,6 +335,23 @@ namespace Catch { 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<size_t>( last - first ) ); + + for ( auto it = first; it != last; ++it ) { + std::iter_swap( it, first ); + results.push_back( estimator( second, last ) ); + } + + return results; + } + + } // namespace } // namespace Detail } // namespace Benchmark @@ -268,23 +361,17 @@ namespace Catch { namespace Benchmark { namespace Detail { -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wfloat-equal" -#endif - bool directCompare( double lhs, double rhs ) { return lhs == rhs; } -#if defined( __GNUC__ ) || defined( __clang__ ) -# pragma GCC diagnostic pop -#endif - - double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) { + double weighted_average_quantile( int k, + int q, + double* first, + double* last ) { auto count = last - first; double idx = (count - 1) * k / static_cast<double>(q); int j = static_cast<int>(idx); double g = idx - j; std::nth_element(first, first + j, last); auto xj = first[j]; - if ( directCompare( g, 0 ) ) { + if ( Catch::Detail::directCompare( g, 0 ) ) { return xj; } @@ -293,12 +380,11 @@ namespace Catch { } OutlierClassification - classify_outliers( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ) { + classify_outliers( double const* first, double const* last ) { std::vector<double> copy( first, last ); - auto q1 = weighted_average_quantile( 1, 4, copy.begin(), copy.end() ); - auto q3 = weighted_average_quantile( 3, 4, copy.begin(), copy.end() ); + auto 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 ); @@ -322,8 +408,7 @@ namespace Catch { return o; } - double mean( std::vector<double>::const_iterator first, - std::vector<double>::const_iterator last ) { + double mean( double const* first, double const* last ) { auto count = last - first; double sum = 0.; while (first != last) { @@ -333,6 +418,9 @@ namespace Catch { return sum / static_cast<double>(count); } + 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); @@ -354,26 +442,77 @@ namespace Catch { return result; } - bootstrap_analysis analyse_samples(double confidence_level, - unsigned int n_resamples, - std::vector<double>::iterator first, - std::vector<double>::iterator last) { - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - static std::random_device entropy; - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + Estimate<double> + 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 }; + + 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; + } - auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ + double accel = sum_cubes / ( 6 * std::pow( sum_squares, 1.5 ) ); + long n = static_cast<long>( resample.size() ); + double prob_n = + std::count_if( resample.begin(), + resample.end(), + [point]( double x ) { return x < point; } ) / + static_cast<double>( 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. ); + auto cumn = [n]( double x ) -> long { + return std::lround( normal_cdf( x ) * + static_cast<double>( 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<size_t>( (std::max)( cumn( a1 ), 0l ) ); + auto hi = + static_cast<size_t>( (std::min)( cumn( a2 ), n - 1 ) ); + + 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; #if defined(CATCH_CONFIG_USE_ASYNC) - auto Estimate = [=](double(*f)(std::vector<double>::const_iterator, - std::vector<double>::const_iterator)) { - auto seed = entropy(); + auto Estimate = [=](double(*f)(double const*, double const*)) { + std::random_device rd; + auto seed = rd(); return std::async(std::launch::async, [=] { - std::mt19937 rng(seed); + SimplePcg32 rng( seed ); auto resampled = resample(rng, n_resamples, first, last, f); return bootstrap(confidence_level, first, last, resampled, f); }); @@ -385,10 +524,10 @@ namespace Catch { auto mean_estimate = mean_future.get(); auto stddev_estimate = stddev_future.get(); #else - auto Estimate = [=](double(*f)(std::vector<double>::const_iterator, - std::vector<double>::const_iterator)) { - auto seed = entropy(); - std::mt19937 rng(seed); + 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); }; @@ -397,6 +536,7 @@ namespace Catch { auto stddev_estimate = Estimate(stddev); #endif // CATCH_USE_ASYNC + auto n = static_cast<int>(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 }; @@ -468,10 +608,10 @@ namespace Catch { } namespace literals { - Approx operator "" _a(long double val) { + Approx operator ""_a(long double val) { return Approx(val); } - Approx operator "" _a(unsigned long long val) { + Approx operator ""_a(unsigned long long val) { return Approx(val); } } // end namespace literals @@ -2133,7 +2273,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 4, 0, "", 0 ); + static Version version( 3, 5, 0, "", 0 ); return version; } @@ -2186,8 +2326,36 @@ namespace Detail { +#include <random> + +namespace Catch { + namespace Generators { + namespace Detail { + std::uint32_t getSeed() { return sharedRng()(); } + } // namespace Detail + + struct RandomFloatingGenerator<long double>::PImpl { + PImpl( long double a, long double b, uint32_t seed ): + rng( seed ), dist( a, b ) {} + + Catch::SimplePcg32 rng; + std::uniform_real_distribution<long double> dist; + }; + + RandomFloatingGenerator<long double>::RandomFloatingGenerator( + long double a, long double b, std::uint32_t seed) : + m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) { + static_cast<void>( next() ); + } -std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } + RandomFloatingGenerator<long double>::~RandomFloatingGenerator() = + default; + bool RandomFloatingGenerator<long double>::next() { + m_current_number = m_pimpl->dist( m_pimpl->rng ); + return true; + } + } // namespace Generators +} // namespace Catch @@ -3710,7 +3878,7 @@ namespace Catch { return parsed; } - EnumInfo::~EnumInfo() {} + EnumInfo::~EnumInfo() = default; StringRef EnumInfo::lookup( int value ) const { for( auto const& valueToName : m_values ) { @@ -3759,6 +3927,7 @@ namespace Catch { namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) namespace { static std::string tryTranslators( std::vector< @@ -3772,9 +3941,9 @@ namespace Catch { } } +#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { - } + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) { m_translators.push_back( CATCH_MOVE( translator ) ); @@ -4083,6 +4252,17 @@ namespace Catch { return i; } +#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 +#endif + + } // end namespace Detail } // end namespace Catch @@ -4267,6 +4447,147 @@ namespace Detail { +namespace Catch { + 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 ): + 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 ): + 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 << '"'; } + for (char c : value) { + // Escape list taken from https://www.json.org/json-en.html, + // string definition. + // Note that while forward slash _can_ be escaped, it does + // not have to be, if JSON is not further embedded somewhere + // where forward slash is meaningful. + if ( c == '"' ) { + m_os << "\\\""; + } else if ( c == '\\' ) { + m_os << "\\\\"; + } else if ( c == '\b' ) { + m_os << "\\b"; + } else if ( c == '\f' ) { + m_os << "\\f"; + } else if ( c == '\n' ) { + m_os << "\\n"; + } else if ( c == '\r' ) { + m_os << "\\r"; + } else if ( c == '\t' ) { + m_os << "\\t"; + } else { + m_os << c; + } + } + if ( quote ) { m_os << '"'; } + } + +} // namespace Catch + + + namespace Catch { @@ -4309,7 +4630,7 @@ namespace Catch { #else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv - Catch::LeakDetector::LeakDetector() {} + Catch::LeakDetector::LeakDetector() = default; #endif // CATCH_CONFIG_WINDOWS_CRTDBG @@ -4677,6 +4998,14 @@ namespace Catch { } #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 ); } +#else + float nextafter( float x, float y ) { return ::nextafterf( x, y ); } + double nextafter( double x, double y ) { return ::nextafter( x, y ); } +#endif + } // end namespace Catch @@ -4758,10 +5087,10 @@ namespace Catch { return static_cast<std::uint32_t>( std::time( nullptr ) ); case GenerateFrom::Default: - case GenerateFrom::RandomDevice: - // In theory, a platform could have random_device that returns just - // 16 bits. That is still some randomness, so we don't care too much - return static_cast<std::uint32_t>( std::random_device{}() ); + case GenerateFrom::RandomDevice: { + std::random_device rd; + return Detail::fillBitsFrom<std::uint32_t>( rd ); + } default: CATCH_ERROR("Unknown generation method"); @@ -4800,6 +5129,8 @@ namespace Catch { Detail::make_unique<ReporterFactory<TeamCityReporter>>(); m_impl->factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>(); + m_impl->factories["JSON"] = + Detail::make_unique<ReporterFactory<JsonReporter>>(); } ReporterRegistry::~ReporterRegistry() = default; @@ -5354,13 +5685,14 @@ namespace Catch { m_messageScopes.clear(); } - // Reset working state - resetAssertionInfo(); + // Reset working state. assertion info will be reset after + // populateReaction is run if it is needed m_lastResult = CATCH_MOVE( result ); } void RunContext::resetAssertionInfo() { m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal; } void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { @@ -5508,6 +5840,7 @@ namespace Catch { AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); assertionEnded(CATCH_MOVE(result) ); + resetAssertionInfo(); handleUnfinishedSections(); @@ -5644,6 +5977,7 @@ namespace Catch { reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); populateReaction( reaction ); } + resetAssertionInfo(); } void RunContext::reportExpr( AssertionInfo const &info, @@ -5682,6 +6016,7 @@ namespace Catch { // considered "OK" reaction.shouldSkip = true; } + resetAssertionInfo(); } void RunContext::handleUnexpectedExceptionNotThrown( AssertionInfo const& info, @@ -5702,6 +6037,7 @@ namespace Catch { AssertionResult assertionResult{ info, CATCH_MOVE(data) }; assertionEnded( CATCH_MOVE(assertionResult) ); populateReaction( reaction ); + resetAssertionInfo(); } void RunContext::populateReaction( AssertionReaction& reaction ) { @@ -5719,6 +6055,7 @@ namespace Catch { data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionEnded( CATCH_MOVE(assertionResult) ); + resetAssertionInfo(); } void RunContext::handleNonExpr( AssertionInfo const &info, @@ -5733,6 +6070,7 @@ namespace Catch { const auto isOk = assertionResult.isOk(); assertionEnded( CATCH_MOVE(assertionResult) ); if ( !isOk ) { populateReaction( reaction ); } + resetAssertionInfo(); } @@ -6059,7 +6397,7 @@ namespace Catch { namespace Catch { - TagAliasRegistry::~TagAliasRegistry() {} + TagAliasRegistry::~TagAliasRegistry() = default; TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { auto it = m_registry.find( alias ); @@ -7564,26 +7902,11 @@ namespace { return ulpDist <= maxUlpDiff; } -#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) - - float nextafter(float x, float y) { - return ::nextafterf(x, y); - } - - double nextafter(double x, double y) { - return ::nextafter(x, y); - } - -#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ template <typename FP> FP step(FP start, FP direction, uint64_t steps) { for (uint64_t i = 0; i < steps; ++i) { -#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) start = Catch::nextafter(start, direction); -#else - start = std::nextafter(start, direction); -#endif } return start; } @@ -7957,7 +8280,7 @@ namespace Catch { namespace Catch { - AutomakeReporter::~AutomakeReporter() {} + AutomakeReporter::~AutomakeReporter() = default; void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. @@ -8257,7 +8580,7 @@ private: StreamingReporterBase::testRunEnded( _testRunStats ); } - CompactReporter::~CompactReporter() {} + CompactReporter::~CompactReporter() = default; } // end namespace Catch @@ -8461,6 +8784,7 @@ struct ColumnInfo { }; struct ColumnBreak {}; struct RowBreak {}; +struct OutputFlush {}; class Duration { enum class Unit { @@ -8577,12 +8901,12 @@ public: } template<typename T> - friend TablePrinter& operator << (TablePrinter& tp, T const& value) { + friend TablePrinter& operator<< (TablePrinter& tp, T const& value) { tp.m_oss << value; return tp; } - friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { + friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) { auto colStr = tp.m_oss.str(); const auto strSize = colStr.size(); tp.m_oss.str(""); @@ -8604,13 +8928,18 @@ public: return tp; } - friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { + 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; + } }; ConsoleReporter::ConsoleReporter(ReporterConfig&& config): @@ -8632,7 +8961,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config): { "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 }, - { "estimated high mean high std dev", 14, Justification::Right } + { "est run time high mean high std dev", 14, Justification::Right } }; } }())) {} @@ -8716,8 +9045,11 @@ void ConsoleReporter::benchmarkPreparing( StringRef name ) { void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { (*m_tablePrinter) << info.samples << ColumnBreak() << info.iterations << ColumnBreak(); - if (!m_config->benchmarkNoAnalysis()) - (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); + if ( !m_config->benchmarkNoAnalysis() ) { + ( *m_tablePrinter ) + << Duration( info.estimatedDuration ) << ColumnBreak(); + } + ( *m_tablePrinter ) << OutputFlush{}; } void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { if (m_config->benchmarkNoAnalysis()) @@ -9413,6 +9745,366 @@ namespace Catch { } // 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 ); + } + + void writeTags( JsonArrayWriter writer, std::vector<Tag> 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; + + 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(); + |