2020-09-08 15:53:08 +02:00
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
2022-01-03 23:16:39 +01:00
// Catch v3.0.0-preview.4
// Generated: 2022-01-03 23:14:23.198909
2020-09-08 15:53:08 +02:00
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
// ----------------------------------------------------------
# include "catch_amalgamated.hpp"
// Adapted from donated nonius code.
# include <cassert>
# include <iterator>
# include <random>
# if defined(CATCH_CONFIG_USE_ASYNC)
# include <future>
# endif
namespace {
using Catch : : Benchmark : : Detail : : sample ;
template < typename URng , typename Estimator >
2022-01-03 23:16:39 +01:00
sample resample ( URng & rng , unsigned int resamples , std : : vector < double > : : iterator first , std : : vector < double > : : iterator last , Estimator & estimator ) {
auto n = static_cast < size_t > ( last - first ) ;
2020-09-08 15:53:08 +02:00
std : : uniform_int_distribution < decltype ( n ) > dist ( 0 , n - 1 ) ;
sample out ;
out . reserve ( resamples ) ;
std : : generate_n ( std : : back_inserter ( out ) , resamples , [ n , first , & estimator , & dist , & rng ] {
std : : vector < double > resampled ;
resampled . reserve ( n ) ;
2022-01-03 23:16:39 +01:00
std : : generate_n ( std : : back_inserter ( resampled ) , n , [ first , & dist , & rng ] { return first [ static_cast < std : : ptrdiff_t > ( dist ( rng ) ) ] ; } ) ;
2020-09-08 15:53:08 +02:00
return estimator ( resampled . begin ( ) , resampled . end ( ) ) ;
} ) ;
std : : sort ( out . begin ( ) , out . end ( ) ) ;
return out ;
}
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 ) ) ;
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 ;
}
double standard_deviation ( std : : vector < double > : : iterator first , std : : vector < double > : : iterator last ) {
auto m = Catch : : Benchmark : : Detail : : mean ( first , last ) ;
double variance = std : : accumulate ( first , last , 0. , [ m ] ( double a , double b ) {
double diff = b - m ;
return a + diff * diff ;
} ) / ( last - first ) ;
return std : : sqrt ( variance ) ;
}
}
namespace Catch {
namespace Benchmark {
namespace Detail {
double weighted_average_quantile ( int k , int q , std : : vector < double > : : iterator first , std : : vector < double > : : iterator 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 ( g = = 0 ) return xj ;
auto xj1 = * std : : min_element ( first + ( j + 1 ) , last ) ;
return xj + g * ( xj1 - xj ) ;
}
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 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 ;
}
double outlier_variance ( Estimate < double > mean , Estimate < double > stddev , int n ) {
double sb = stddev . point ;
double mn = mean . point / n ;
double mg_min = mn / 2. ;
2022-01-03 23:16:39 +01:00
double sg = ( std : : min ) ( mg_min / 4. , sb / std : : sqrt ( n ) ) ;
2020-09-08 15:53:08 +02:00
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 ;
2022-01-03 23:16:39 +01:00
return static_cast < int > ( - 2. * k0 / ( k1 + std : : sqrt ( det ) ) ) ;
2020-09-08 15:53:08 +02:00
} ;
auto var_out = [ n , sb2 , sg2 ] ( double c ) {
double nc = n - c ;
return ( nc / n ) * ( sb2 - nc * sg2 ) ;
} ;
2022-01-03 23:16:39 +01:00
return ( std : : min ) ( var_out ( 1 ) , var_out ( ( std : : min ) ( c_max ( 0. ) , c_max ( mg_min ) ) ) ) / sb2 ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
bootstrap_analysis analyse_samples ( double confidence_level , unsigned int n_resamples , std : : vector < double > : : iterator first , std : : vector < double > : : iterator last ) {
2020-09-08 15:53:08 +02:00
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
static std : : random_device entropy ;
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
auto n = static_cast < int > ( last - first ) ; // seriously, one can't use integral types without hell in C++
auto mean = & Detail : : mean < std : : vector < double > : : iterator > ;
auto stddev = & standard_deviation ;
# if defined(CATCH_CONFIG_USE_ASYNC)
auto Estimate = [ = ] ( double ( * f ) ( std : : vector < double > : : iterator , std : : vector < double > : : iterator ) ) {
auto seed = entropy ( ) ;
return std : : async ( std : : launch : : async , [ = ] {
std : : mt19937 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_estimate = mean_future . get ( ) ;
auto stddev_estimate = stddev_future . get ( ) ;
# else
auto Estimate = [ = ] ( double ( * f ) ( std : : vector < double > : : iterator , std : : vector < double > : : iterator ) ) {
auto seed = entropy ( ) ;
std : : mt19937 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 ) ;
# endif // CATCH_USE_ASYNC
double outlier_variance = Detail : : outlier_variance ( mean_estimate , stddev_estimate , n ) ;
return { mean_estimate , stddev_estimate , outlier_variance } ;
}
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
/** \file
* This is a special TU that combines what would otherwise be a very
* small benchmarking - related TUs into one bigger TU .
*
* The reason for this is compilation performance improvements by
* avoiding reparsing headers for many small TUs , instead having this
* one TU include bit more , but having it all parsed only once .
*
* To avoid heavy - tail problem with compilation times , each " subpart "
* of Catch2 has its own combined TU like this .
*/
////////////////////////////////////////////
// vvv formerly catch_chronometer.cpp vvv //
////////////////////////////////////////////
namespace Catch {
namespace Benchmark {
namespace Detail {
ChronometerConcept : : ~ ChronometerConcept ( ) = default ;
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
///////////////////////////////////////////////////
// vvv formerly catch_benchmark_function.cpp vvv //
///////////////////////////////////////////////////
namespace Catch {
namespace Benchmark {
namespace Detail {
BenchmarkFunction : : callable : : ~ callable ( ) = default ;
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
/////////////////////////////////////////////////
// vvv formerly catch_run_for_at_least.cpp vvv //
/////////////////////////////////////////////////
# include <exception>
namespace Catch {
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 " ;
}
void throw_optimized_away_error ( ) {
Catch : : throw_exception ( optimized_away_error { } ) ;
}
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
# include <cmath>
# include <limits>
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 ) {
return ( lhs + margin > = rhs ) & & ( rhs + margin > = lhs ) ;
}
}
namespace Catch {
Approx : : Approx ( double value )
2022-01-03 23:16:39 +01:00
: m_epsilon ( std : : numeric_limits < float > : : epsilon ( ) * 100. ) ,
2020-09-08 15:53:08 +02:00
m_margin ( 0.0 ) ,
m_scale ( 0.0 ) ,
m_value ( value )
{ }
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 operator " " _a ( unsigned long long val ) {
return Approx ( val ) ;
}
} // end namespace literals
std : : string StringMaker < Catch : : Approx > : : 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 ) { }
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 const & data )
: m_info ( info ) ,
m_resultData ( data )
{ }
// 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 ) ;
}
ResultWas : : OfType AssertionResult : : getResultType ( ) const {
return m_resultData . resultType ;
}
bool AssertionResult : : hasExpression ( ) const {
return ! m_info . capturedExpression . 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 : : getExpressionInMacro ( ) const {
std : : string expr ;
if ( m_info . macroName . empty ( ) )
expr = static_cast < std : : string > ( m_info . capturedExpression ) ;
else {
expr . reserve ( m_info . macroName . size ( ) + m_info . capturedExpression . size ( ) + 4 ) ;
expr + = m_info . macroName ;
expr + = " ( " ;
expr + = m_info . capturedExpression ;
expr + = " ) " ;
}
return expr ;
}
bool AssertionResult : : hasExpandedExpression ( ) const {
return hasExpression ( ) & & getExpandedExpression ( ) ! = getExpression ( ) ;
}
std : : string AssertionResult : : getExpandedExpression ( ) const {
std : : string expr = m_resultData . reconstructExpression ( ) ;
return expr . empty ( )
? getExpression ( )
: expr ;
}
2022-01-03 23:16:39 +01:00
StringRef AssertionResult : : getMessage ( ) const {
2020-09-08 15:53:08 +02:00
return m_resultData . message ;
}
SourceLineInfo AssertionResult : : getSourceInfo ( ) const {
return m_info . lineInfo ;
}
StringRef AssertionResult : : getTestMacroName ( ) const {
return m_info . macroName ;
}
} // end namespace Catch
2022-01-03 23:16:39 +01:00
# include <ostream>
2020-09-08 15:53:08 +02:00
namespace Catch {
2022-01-03 23:16:39 +01:00
namespace Detail {
namespace {
class RDBufStream : public IStream {
mutable std : : ostream m_os ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
public :
//! The streambuf `sb` must outlive the constructed object.
RDBufStream ( std : : streambuf * sb ) : m_os ( sb ) { }
~ RDBufStream ( ) override = default ;
public : // IStream
std : : ostream & stream ( ) const override { return m_os ; }
} ;
} // unnamed namespace
} // namespace Detail
std : : ostream & operator < < ( std : : ostream & os ,
ConfigData : : ReporterAndFile const & reporter ) {
os < < " { " < < reporter . reporterName < < " , " ;
if ( reporter . outputFileName ) {
os < < * reporter . outputFileName ;
} else {
os < < " <default-output> " ;
}
return os < < " } " ;
}
Config : : Config ( ConfigData const & data ) :
m_data ( data ) ,
m_defaultStream ( openStream ( data . defaultOutputFilename ) ) {
2020-09-08 15:53:08 +02:00
// 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 ) ;
}
for ( auto & elem : m_data . sectionsToRun ) {
elem = trim ( elem ) ;
}
TestSpecParser parser ( ITagAliasRegistry : : get ( ) ) ;
if ( ! m_data . testsOrTags . empty ( ) ) {
m_hasTestFilters = true ;
for ( auto const & testOrTags : m_data . testsOrTags ) {
parser . parse ( testOrTags ) ;
}
}
m_testSpec = parser . testSpec ( ) ;
2022-01-03 23:16:39 +01:00
m_reporterStreams . reserve ( m_data . reporterSpecifications . size ( ) ) ;
for ( auto const & reporterAndFile : m_data . reporterSpecifications ) {
if ( reporterAndFile . outputFileName . none ( ) ) {
m_reporterStreams . emplace_back ( new Detail : : RDBufStream (
m_defaultStream - > stream ( ) . rdbuf ( ) ) ) ;
} else {
m_reporterStreams . emplace_back (
openStream ( * reporterAndFile . outputFileName ) ) ;
}
}
2020-09-08 15:53:08 +02:00
}
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 ; }
std : : vector < std : : string > const & Config : : getTestsOrTags ( ) const { return m_data . testsOrTags ; }
std : : vector < std : : string > const & Config : : getSectionsToRun ( ) const { return m_data . sectionsToRun ; }
2022-01-03 23:16:39 +01:00
std : : vector < ConfigData : : ReporterAndFile > const & Config : : getReportersAndOutputFiles ( ) const {
return m_data . reporterSpecifications ;
}
std : : ostream & Config : : getReporterOutputStream ( std : : size_t reporterIdx ) const {
return m_reporterStreams . at ( reporterIdx ) - > stream ( ) ;
}
2020-09-08 15:53:08 +02:00
TestSpec const & Config : : testSpec ( ) const { return m_testSpec ; }
bool Config : : hasTestFilters ( ) const { return m_hasTestFilters ; }
bool Config : : showHelp ( ) const { return m_data . showHelp ; }
// IConfig interface
bool Config : : allowThrows ( ) const { return ! m_data . noThrow ; }
2022-01-03 23:16:39 +01:00
std : : ostream & Config : : defaultStream ( ) const { return m_defaultStream - > stream ( ) ; }
StringRef Config : : name ( ) const { return m_data . name . empty ( ) ? m_data . processName : m_data . name ; }
2020-09-08 15:53:08 +02:00
bool Config : : includeSuccessfulResults ( ) const { return m_data . showSuccessfulTests ; }
2022-01-03 23:16:39 +01:00
bool Config : : warnAboutMissingAssertions ( ) const {
return ! ! ( m_data . warnings & WarnAbout : : NoAssertions ) ;
}
bool Config : : warnAboutUnmatchedTestSpecs ( ) const {
return ! ! ( m_data . warnings & WarnAbout : : UnmatchedTestSpec ) ;
}
bool Config : : zeroTestsCountAsSuccess ( ) const { return m_data . allowZeroTests ; }
2020-09-08 15:53:08 +02:00
ShowDurations Config : : showDurations ( ) const { return m_data . showDurations ; }
double Config : : minDuration ( ) const { return m_data . minDuration ; }
TestRunOrder Config : : runOrder ( ) const { return m_data . runOrder ; }
2022-01-03 23:16:39 +01:00
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 ; }
2020-09-08 15:53:08 +02:00
UseColour Config : : useColour ( ) const { return m_data . useColour ; }
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 : : benchmarkNoAnalysis ( ) const { return m_data . benchmarkNoAnalysis ; }
2022-01-03 23:16:39 +01:00
unsigned int Config : : benchmarkSamples ( ) const { return m_data . benchmarkSamples ; }
2020-09-08 15:53:08 +02:00
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 ) ; }
2022-01-03 23:16:39 +01:00
Detail : : unique_ptr < IStream const > Config : : openStream ( std : : string const & outputFileName ) {
return Catch : : makeStream ( outputFileName ) ;
2020-09-08 15:53:08 +02:00
}
} // end namespace Catch
# include <cassert>
# include <stack>
namespace Catch {
////////////////////////////////////////////////////////////////////////////
ScopedMessage : : ScopedMessage ( MessageBuilder const & builder ) :
m_info ( builder . m_info ) {
m_info . message = builder . m_stream . str ( ) ;
getResultCapture ( ) . pushScopedMessage ( m_info ) ;
}
ScopedMessage : : ScopedMessage ( ScopedMessage & & old ) noexcept :
2022-01-03 23:16:39 +01:00
m_info ( CATCH_MOVE ( old . m_info ) ) {
2020-09-08 15:53:08 +02:00
old . m_moved = true ;
}
ScopedMessage : : ~ ScopedMessage ( ) {
if ( ! uncaught_exceptions ( ) & & ! m_moved ) {
getResultCapture ( ) . popScopedMessage ( m_info ) ;
}
}
Capturer : : Capturer ( StringRef macroName , SourceLineInfo const & lineInfo , ResultWas : : OfType resultType , StringRef names ) {
auto trimmed = [ & ] ( size_t start , size_t end ) {
while ( names [ start ] = = ' , ' | | isspace ( static_cast < unsigned char > ( names [ start ] ) ) ) {
+ + start ;
}
while ( names [ end ] = = ' , ' | | isspace ( static_cast < unsigned char > ( 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 < char > 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 = static_cast < std : : string > ( trimmed ( start , pos ) ) ;
m_messages . back ( ) . message + = " := " ;
start = pos ;
}
}
}
assert ( openings . empty ( ) & & " Mismatched openings " ) ;
m_messages . emplace_back ( macroName , lineInfo , resultType ) ;
m_messages . back ( ) . message = static_cast < std : : string > ( trimmed ( start , names . size ( ) - 1 ) ) ;
m_messages . back ( ) . message + = " := " ;
}
Capturer : : ~ Capturer ( ) {
if ( ! uncaught_exceptions ( ) ) {
assert ( m_captured = = m_messages . size ( ) ) ;
for ( size_t i = 0 ; i < m_captured ; + + i )
m_resultCapture . popScopedMessage ( m_messages [ i ] ) ;
}
}
void Capturer : : captureValue ( size_t index , std : : string const & value ) {
assert ( index < m_messages . size ( ) ) ;
m_messages [ index ] . message + = value ;
m_resultCapture . pushScopedMessage ( m_messages [ index ] ) ;
m_captured + + ;
}
} // end namespace Catch
namespace Catch {
namespace {
class RegistryHub : public IRegistryHub ,
public IMutableRegistryHub ,
private Detail : : NonCopyable {
public : // IRegistryHub
RegistryHub ( ) = default ;
IReporterRegistry 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 {
2022-01-03 23:16:39 +01:00
m_reporterRegistry . registerReporter ( name , CATCH_MOVE ( factory ) ) ;
2020-09-08 15:53:08 +02:00
}
void registerListener ( IReporterFactoryPtr factory ) override {
2022-01-03 23:16:39 +01:00
m_reporterRegistry . registerListener ( CATCH_MOVE ( factory ) ) ;
2020-09-08 15:53:08 +02:00
}
void registerTest ( Detail : : unique_ptr < TestCaseInfo > & & testInfo , Detail : : unique_ptr < ITestInvoker > & & invoker ) override {
2022-01-03 23:16:39 +01:00
m_testCaseRegistry . registerTest ( CATCH_MOVE ( testInfo ) , CATCH_MOVE ( invoker ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void registerTranslator ( Detail : : unique_ptr < IExceptionTranslator > & & translator ) override {
m_exceptionTranslatorRegistry . registerTranslator ( CATCH_MOVE ( translator ) ) ;
2020-09-08 15:53:08 +02:00
}
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 ( ) ) ;
# else
CATCH_INTERNAL_ERROR ( " Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS! " ) ;
# endif
}
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 ;
} ;
}
using RegistryHubSingleton = Singleton < RegistryHub , IRegistryHub , IMutableRegistryHub > ;
IRegistryHub const & getRegistryHub ( ) {
return RegistryHubSingleton : : get ( ) ;
}
IMutableRegistryHub & getMutableRegistryHub ( ) {
return RegistryHubSingleton : : getMutable ( ) ;
}
void cleanUp ( ) {
cleanupSingletons ( ) ;
cleanUpContext ( ) ;
}
std : : string translateActiveException ( ) {
return getRegistryHub ( ) . getExceptionTranslatorRegistry ( ) . translateActiveException ( ) ;
}
} // end namespace Catch
# include <algorithm>
2022-01-03 23:16:39 +01:00
# include <cassert>
2020-09-08 15:53:08 +02:00
# include <iomanip>
# include <set>
namespace Catch {
namespace {
const int MaxExitCode = 255 ;
2022-01-03 23:16:39 +01:00
IStreamingReporterPtr createReporter ( std : : string const & reporterName , ReporterConfig const & config ) {
2020-09-08 15:53:08 +02:00
auto reporter = Catch : : getRegistryHub ( ) . getReporterRegistry ( ) . create ( reporterName , config ) ;
2022-01-03 23:16:39 +01:00
CATCH_ENFORCE ( reporter , " No reporter registered with name: ' " < < reporterName < < ' \' ' ) ;
2020-09-08 15:53:08 +02:00
return reporter ;
}
IStreamingReporterPtr makeReporter ( Config const * config ) {
2022-01-03 23:16:39 +01:00
if ( Catch : : getRegistryHub ( ) . getReporterRegistry ( ) . getListeners ( ) . empty ( )
& & config - > getReportersAndOutputFiles ( ) . size ( ) = = 1 ) {
auto & stream = config - > getReporterOutputStream ( 0 ) ;
return createReporter ( config - > getReportersAndOutputFiles ( ) [ 0 ] . reporterName , ReporterConfig ( config , stream ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
auto multi = Detail : : make_unique < ListeningReporter > ( config ) ;
2020-09-08 15:53:08 +02:00
auto const & listeners = Catch : : getRegistryHub ( ) . getReporterRegistry ( ) . getListeners ( ) ;
for ( auto const & listener : listeners ) {
2022-01-03 23:16:39 +01:00
multi - > addListener ( listener - > create ( Catch : : ReporterConfig ( config , config - > defaultStream ( ) ) ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
std : : size_t reporterIdx = 0 ;
for ( auto const & reporterAndFile : config - > getReportersAndOutputFiles ( ) ) {
auto & stream = config - > getReporterOutputStream ( reporterIdx ) ;
multi - > addReporter ( createReporter ( reporterAndFile . reporterName , ReporterConfig ( config , stream ) ) ) ;
reporterIdx + + ;
}
return multi ;
2020-09-08 15:53:08 +02:00
}
class TestGroup {
public :
explicit TestGroup ( IStreamingReporterPtr & & reporter , Config const * config ) :
m_reporter ( reporter . get ( ) ) ,
m_config { config } ,
2022-01-03 23:16:39 +01:00
m_context { config , CATCH_MOVE ( reporter ) } {
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
assert ( m_config - > testSpec ( ) . getInvalidSpecs ( ) . empty ( ) & &
" Invalid test specs should be handled before running tests " ) ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
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 ) ;
}
}
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
m_matches =
testSpec . matchesByFilter ( allTestCases , * m_config ) ;
for ( auto const & match : m_matches ) {
m_tests . insert ( match . tests . begin ( ) ,
match . tests . end ( ) ) ;
}
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
m_tests = createShard ( m_tests , m_config - > shardCount ( ) , m_config - > shardIndex ( ) ) ;
2020-09-08 15:53:08 +02:00
}
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 ( ) ) {
2022-01-03 23:16:39 +01:00
m_unmatchedTestSpecs = true ;
m_reporter - > noMatchingTestCases ( match . name ) ;
2020-09-08 15:53:08 +02:00
}
}
return totals ;
}
2022-01-03 23:16:39 +01:00
bool hadUnmatchedTestSpecs ( ) const {
return m_unmatchedTestSpecs ;
}
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
private :
2020-09-08 15:53:08 +02:00
IStreamingReporter * m_reporter ;
Config const * m_config ;
RunContext m_context ;
2022-01-03 23:16:39 +01:00
std : : set < TestCaseHandle const * > m_tests ;
2020-09-08 15:53:08 +02:00
TestSpec : : Matches m_matches ;
2022-01-03 23:16:39 +01:00
bool m_unmatchedTestSpecs = false ;
2020-09-08 15:53:08 +02:00
} ;
void applyFilenamesAsTags ( ) {
for ( auto const & testInfo : getRegistryHub ( ) . getTestCaseRegistry ( ) . getAllInfos ( ) ) {
testInfo - > addFilenameTag ( ) ;
}
}
} // anon 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 ( ) ; }
}
// 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 ( ) ) ;
m_startupExceptions = true ;
Colour colourGuard ( Colour : : Red ) ;
Catch : : cerr ( ) < < " 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 ) {
Catch : : cerr ( ) < < TextFlow : : Column ( ex . what ( ) ) . indent ( 2 ) < < ' \n ' ;
}
}
}
# endif
alreadyInstantiated = true ;
m_cli = makeCommandLineParser ( m_configData ) ;
}
Session : : ~ Session ( ) {
Catch : : cleanUp ( ) ;
}
void Session : : showHelp ( ) const {
Catch : : cout ( )
2022-01-03 23:16:39 +01:00
< < " \n Catch v " < < libraryVersion ( ) < < ' \n '
< < m_cli < < ' \n '
< < " For more detailed usage please see the project docs \n \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
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: " < < " Catch Test \n "
2022-01-03 23:16:39 +01:00
< < std : : left < < std : : setw ( 16 ) < < " version: " < < libraryVersion ( ) < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
int Session : : applyCommandLine ( int argc , char const * const * argv ) {
if ( m_startupExceptions )
return 1 ;
auto result = m_cli . parse ( Clara : : Args ( argc , argv ) ) ;
2022-01-03 23:16:39 +01:00
2020-09-08 15:53:08 +02:00
if ( ! result ) {
config ( ) ;
getCurrentMutableContext ( ) . setConfig ( m_config . get ( ) ) ;
Catch : : cerr ( )
< < Colour ( Colour : : Red )
< < " \n Error(s) in input: \n "
< < TextFlow : : Column ( result . errorMessage ( ) ) . indent ( 2 )
< < " \n \n " ;
2022-01-03 23:16:39 +01:00
Catch : : cerr ( ) < < " Run with -? for usage \n \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
return MaxExitCode ;
}
if ( m_configData . showHelp )
showHelp ( ) ;
if ( m_configData . libIdentify )
libIdentify ( ) ;
2022-01-03 23:16:39 +01:00
2020-09-08 15:53:08 +02:00
m_config . reset ( ) ;
return 0 ;
}
# if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
int Session : : applyCommandLine ( int argc , wchar_t const * const * argv ) {
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 ) ;
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 ;
}
# endif
void Session : : useConfigData ( ConfigData const & configData ) {
m_configData = configData ;
m_config . reset ( ) ;
}
int Session : : run ( ) {
if ( ( m_configData . waitForKeypress & WaitForKeypress : : BeforeStart ) ! = 0 ) {
2022-01-03 23:16:39 +01:00
Catch : : cout ( ) < < " ...waiting for enter/ return before starting \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
static_cast < void > ( std : : getchar ( ) ) ;
}
int exitCode = runInternal ( ) ;
if ( ( m_configData . waitForKeypress & WaitForKeypress : : BeforeExit ) ! = 0 ) {
2022-01-03 23:16:39 +01:00
Catch : : cout ( ) < < " ...waiting for enter/ return before exiting, with code: " < < exitCode < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
static_cast < void > ( 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 < Config > ( m_configData ) ;
return * m_config ;
}
int Session : : runInternal ( ) {
if ( m_startupExceptions )
return 1 ;
if ( m_configData . showHelp | | m_configData . libIdentify ) {
return 0 ;
}
2022-01-03 23:16:39 +01:00
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 1 ;
}
2020-09-08 15:53:08 +02:00
CATCH_TRY {
config ( ) ; // Force config to be constructed
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 = makeReporter ( m_config . get ( ) ) ;
2022-01-03 23:16:39 +01:00
auto const & invalidSpecs = m_config - > testSpec ( ) . getInvalidSpecs ( ) ;
if ( ! invalidSpecs . empty ( ) ) {
for ( auto const & spec : invalidSpecs ) {
reporter - > reportInvalidTestSpec ( spec ) ;
}
return 1 ;
}
2020-09-08 15:53:08 +02:00
// Handle list request
if ( list ( * reporter , * m_config ) ) {
return 0 ;
}
2022-01-03 23:16:39 +01:00
TestGroup tests { CATCH_MOVE ( reporter ) , m_config . get ( ) } ;
2020-09-08 15:53:08 +02:00
auto const totals = tests . execute ( ) ;
2022-01-03 23:16:39 +01:00
if ( tests . hadUnmatchedTestSpecs ( )
& & m_config - > warnAboutUnmatchedTestSpecs ( ) ) {
return 3 ;
}
if ( totals . testCases . total ( ) = = 0
& & ! m_config - > zeroTestsCountAsSuccess ( ) ) {
2020-09-08 15:53:08 +02:00
return 2 ;
2022-01-03 23:16:39 +01:00
}
2020-09-08 15:53:08 +02:00
// Note that on unices only the lower 8 bits are usually used, clamping
// the return value to 255 prevents false negative when some multiple
// of 256 tests has failed
return ( std : : min ) ( MaxExitCode , ( std : : max ) ( totals . error , static_cast < int > ( totals . assertions . failed ) ) ) ;
}
# if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
catch ( std : : exception & ex ) {
2022-01-03 23:16:39 +01:00
Catch : : cerr ( ) < < ex . what ( ) < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
return MaxExitCode ;
}
# endif
}
} // end namespace Catch
# include <cassert>
# include <cctype>
# include <algorithm>
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 " ) ;
TestCaseProperties operator | ( TestCaseProperties lhs , TestCaseProperties rhs ) {
return static_cast < TestCaseProperties > (
static_cast < TCP_underlying_type > ( lhs ) | static_cast < TCP_underlying_type > ( rhs )
) ;
}
TestCaseProperties & operator | = ( TestCaseProperties & lhs , TestCaseProperties rhs ) {
lhs = static_cast < TestCaseProperties > (
static_cast < TCP_underlying_type > ( lhs ) | static_cast < TCP_underlying_type > ( rhs )
) ;
return lhs ;
}
TestCaseProperties operator & ( TestCaseProperties lhs , TestCaseProperties rhs ) {
return static_cast < TestCaseProperties > (
static_cast < TCP_underlying_type > ( lhs ) & static_cast < TCP_underlying_type > ( rhs )
) ;
}
bool applies ( TestCaseProperties tcp ) {
static_assert ( static_cast < TCP_underlying_type > ( 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 static_cast < TestCaseProperties > ( TestCaseProperties : : Benchmark | TestCaseProperties : : IsHidden ) ;
else
return TestCaseProperties : : None ;
}
bool isReservedTag ( StringRef tag ) {
return parseSpecialTag ( tag ) = = TestCaseProperties : : None
& & tag . size ( ) > 0
& & ! std : : isalnum ( static_cast < unsigned char > ( 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 ) ;
}
StringRef extractFilenamePart ( StringRef filename ) {
size_t lastDot = filename . size ( ) ;
while ( lastDot > 0 & & filename [ lastDot - 1 ] ! = ' . ' ) {
- - lastDot ;
}
- - 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]+[.])
size_t sizeOfExtraTags ( StringRef filepath ) {
// [.] is 3, [#] is another 3
const size_t extras = 3 + 3 ;
return extractFilenamePart ( filepath ) . size ( ) + extras ;
}
2022-01-03 23:16:39 +01:00
} // 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 ) ;
2020-09-08 15:53:08 +02:00
}
Detail : : unique_ptr < TestCaseInfo >
2022-01-03 23:16:39 +01:00
makeTestCaseInfo ( StringRef _className ,
2020-09-08 15:53:08 +02:00
NameAndTags const & nameAndTags ,
SourceLineInfo const & _lineInfo ) {
2021-05-28 23:10:33 +02:00
return Detail : : make_unique < TestCaseInfo > ( _className , nameAndTags , _lineInfo ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
TestCaseInfo : : TestCaseInfo ( StringRef _className ,
2020-09-08 15:53:08 +02:00
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 = = ' [ ' ) {
assert ( ! inTag ) ;
inTag = true ;
tagStart = idx ;
}
if ( c = = ' ] ' ) {
assert ( inTag ) ;
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 ) ;
2022-01-03 23:16:39 +01:00
CATCH_ENFORCE ( ! tagStr . empty ( ) , " Empty tags are not allowed " ) ;
2020-09-08 15:53:08 +02:00
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 ) ;
}
( void ) inTag ; // Silence "set-but-unused" warning in release mode.
}
// Add [.] if relevant
if ( isHidden ( ) ) {
internalAppendTag ( " . " _sr ) ;
}
// Sort and prepare tags
2022-01-03 23:16:39 +01:00
std : : sort ( begin ( tags ) , end ( tags ) ) ;
tags . erase ( std : : unique ( begin ( tags ) , end ( tags ) ) ,
2020-09-08 15:53:08 +02:00
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 ( ) ;
}
ret . reserve ( full_size ) ;
for ( const auto & tag : tags ) {
ret . push_back ( ' [ ' ) ;
ret + = tag . original ;
ret . push_back ( ' ] ' ) ;
}
return ret ;
}
void TestCaseInfo : : internalAppendTag ( StringRef tagStr ) {
backingTags + = ' [ ' ;
const auto backingStart = backingTags . size ( ) ;
backingTags + = tagStr ;
const auto backingEnd = backingTags . size ( ) ;
backingTags + = ' ] ' ;
2022-01-03 23:16:39 +01:00
tags . emplace_back ( StringRef ( backingTags . c_str ( ) + backingStart , backingEnd - backingStart ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
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 ;
2020-09-08 15:53:08 +02:00
}
TestCaseInfo const & TestCaseHandle : : getTestCaseInfo ( ) const {
return * m_info ;
}
} // end namespace Catch
# include <algorithm>
# include <string>
# include <vector>
namespace Catch {
TestSpec : : Pattern : : Pattern ( std : : string const & name )
: m_name ( name )
{ }
TestSpec : : Pattern : : ~ Pattern ( ) = default ;
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 ) ;
}
TestSpec : : TagPattern : : TagPattern ( std : : string const & tag , std : : string const & filterString )
: Pattern ( filterString )
2022-01-03 23:16:39 +01:00
, m_tag ( tag )
2020-09-08 15:53:08 +02:00
{ }
bool TestSpec : : TagPattern : : matches ( TestCaseInfo const & testCase ) const {
2022-01-03 23:16:39 +01:00
return std : : find ( begin ( testCase . tags ) ,
end ( testCase . tags ) ,
Tag ( m_tag ) ) ! = end ( testCase . tags ) ;
2020-09-08 15:53:08 +02:00
}
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 ;
}
std : : string TestSpec : : Filter : : name ( ) const {
std : : string name ;
for ( auto const & p : m_required ) {
name + = p - > name ( ) ;
}
for ( auto const & p : m_forbidden ) {
name + = p - > name ( ) ;
}
return name ;
}
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 < TestCaseHandle > const & testCases , IConfig const & config ) const
{
Matches matches ( m_filters . size ( ) ) ;
std : : transform ( m_filters . begin ( ) , m_filters . end ( ) , matches . begin ( ) , [ & ] ( Filter const & filter ) {
std : : vector < TestCaseHandle const * > currentMatches ;
for ( auto const & test : testCases )
if ( isThrowSafe ( test , config ) & & filter . matches ( test . getTestCaseInfo ( ) ) )
currentMatches . emplace_back ( & test ) ;
return FilterMatch { filter . name ( ) , currentMatches } ;
} ) ;
return matches ;
}
2022-01-03 23:16:39 +01:00
const TestSpec : : vectorStrings & TestSpec : : getInvalidSpecs ( ) const {
return m_invalidSpecs ;
2020-09-08 15:53:08 +02:00
}
}
# include <chrono>
namespace Catch {
namespace {
2022-01-03 23:16:39 +01:00
static auto getCurrentNanosecondsSinceEpoch ( ) - > uint64_t {
return std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( std : : chrono : : high_resolution_clock : : now ( ) . time_since_epoch ( ) ) . count ( ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
} // end unnamed namespace
2020-09-08 15:53:08 +02:00
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 < unsigned int > ( getElapsedMicroseconds ( ) / 1000 ) ;
}
auto Timer : : getElapsedSeconds ( ) const - > double {
return getElapsedMicroseconds ( ) / 1000000.0 ;
}
} // namespace Catch
# include <cmath>
# include <iomanip>
namespace Catch {
namespace Detail {
namespace {
const int hexThreshold = 255 ;
struct Endianness {
enum Arch { 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 < char * > ( & one ) ;
return value ? Little : Big ;
}
} ;
template < typename T >
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
2022-01-03 23:16:39 +01:00
std : : string convertIntoString ( StringRef string , bool escape_invisibles ) {
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 ( ! escape_invisibles ) {
ret + = ' " ' ;
ret + = string ;
ret + = ' " ' ;
return ret ;
}
ret + = ' " ' ;
for ( char c : string ) {
switch ( c ) {
case ' \r ' :
ret . append ( " \\ r " ) ;
break ;
case ' \n ' :
ret . append ( " \\ n " ) ;
break ;
case ' \t ' :
ret . append ( " \\ t " ) ;
break ;
case ' \f ' :
ret . append ( " \\ f " ) ;
break ;
default :
ret . push_back ( c ) ;
break ;
}
}
ret + = ' " ' ;
return ret ;
}
std : : string convertIntoString ( StringRef string ) {
return convertIntoString ( string , getCurrentContext ( ) . getConfig ( ) - > showInvisibles ( ) ) ;
}
2020-09-08 15:53:08 +02:00
std : : string rawMemoryToString ( const void * object , std : : size_t size ) {
// Reverse order for little endian architectures
int i = 0 , end = static_cast < int > ( size ) , inc = 1 ;
if ( Endianness : : which ( ) = = Endianness : : Little ) {
i = end - 1 ;
end = inc = - 1 ;
}
unsigned char const * bytes = static_cast < unsigned char const * > ( object ) ;
ReusableStringStream rss ;
rss < < " 0x " < < std : : setfill ( ' 0 ' ) < < std : : hex ;
for ( ; i ! = end ; i + = inc )
rss < < std : : setw ( 2 ) < < static_cast < unsigned > ( bytes [ i ] ) ;
return rss . str ( ) ;
}
} // end Detail namespace
//// ======================================================= ////
//
// Out-of-line defs for full specialization of StringMaker
//
//// ======================================================= ////
std : : string StringMaker < std : : string > : : convert ( const std : : string & str ) {
2022-01-03 23:16:39 +01:00
return Detail : : convertIntoString ( str ) ;
2020-09-08 15:53:08 +02:00
}
# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
std : : string StringMaker < std : : string_view > : : convert ( std : : string_view str ) {
2022-01-03 23:16:39 +01:00
return Detail : : convertIntoString ( StringRef ( str . data ( ) , str . size ( ) ) ) ;
2020-09-08 15:53:08 +02:00
}
# endif
std : : string StringMaker < char const * > : : convert ( char const * str ) {
if ( str ) {
2022-01-03 23:16:39 +01:00
return Detail : : convertIntoString ( str ) ;
2020-09-08 15:53:08 +02:00
} else {
return { " {null string} " } ;
}
}
std : : string StringMaker < char * > : : convert ( char * str ) {
if ( str ) {
2022-01-03 23:16:39 +01:00
return Detail : : convertIntoString ( str ) ;
2020-09-08 15:53:08 +02:00
} else {
return { " {null string} " } ;
}
}
# ifdef CATCH_CONFIG_WCHAR
std : : string StringMaker < std : : wstring > : : convert ( const std : : wstring & wstr ) {
std : : string s ;
s . reserve ( wstr . size ( ) ) ;
for ( auto c : wstr ) {
s + = ( c < = 0xff ) ? static_cast < char > ( c ) : ' ? ' ;
}
return : : Catch : : Detail : : stringify ( s ) ;
}
# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
std : : string StringMaker < std : : wstring_view > : : convert ( std : : wstring_view str ) {
return StringMaker < std : : wstring > : : convert ( std : : wstring ( str ) ) ;
}
# endif
std : : string StringMaker < wchar_t const * > : : convert ( wchar_t const * str ) {
if ( str ) {
return : : Catch : : Detail : : stringify ( std : : wstring { str } ) ;
} else {
return { " {null string} " } ;
}
}
std : : string StringMaker < wchar_t * > : : convert ( wchar_t * str ) {
if ( str ) {
return : : Catch : : Detail : : stringify ( std : : wstring { str } ) ;
} else {
return { " {null string} " } ;
}
}
# endif
# if defined(CATCH_CONFIG_CPP17_BYTE)
# include <cstddef>
std : : string StringMaker < std : : byte > : : convert ( std : : byte value ) {
return : : Catch : : Detail : : stringify ( std : : to_integer < unsigned long long > ( value ) ) ;
}
# endif // defined(CATCH_CONFIG_CPP17_BYTE)
std : : string StringMaker < int > : : convert ( int value ) {
return : : Catch : : Detail : : stringify ( static_cast < long long > ( value ) ) ;
}
std : : string StringMaker < long > : : convert ( long value ) {
return : : Catch : : Detail : : stringify ( static_cast < long long > ( value ) ) ;
}
std : : string StringMaker < long long > : : convert ( long long value ) {
ReusableStringStream rss ;
rss < < value ;
if ( value > Detail : : hexThreshold ) {
rss < < " (0x " < < std : : hex < < value < < ' ) ' ;
}
return rss . str ( ) ;
}
std : : string StringMaker < unsigned int > : : convert ( unsigned int value ) {
return : : Catch : : Detail : : stringify ( static_cast < unsigned long long > ( value ) ) ;
}
std : : string StringMaker < unsigned long > : : convert ( unsigned long value ) {
return : : Catch : : Detail : : stringify ( static_cast < unsigned long long > ( value ) ) ;
}
std : : string StringMaker < unsigned long long > : : convert ( unsigned long long value ) {
ReusableStringStream rss ;
rss < < value ;
if ( value > Detail : : hexThreshold ) {
rss < < " (0x " < < std : : hex < < value < < ' ) ' ;
}
return rss . str ( ) ;
}
std : : string StringMaker < signed char > : : convert ( signed char value ) {
if ( value = = ' \r ' ) {
return " ' \\ r' " ;
} else if ( value = = ' \f ' ) {
return " ' \\ f' " ;
} else if ( value = = ' \n ' ) {
return " ' \\ n' " ;
} else if ( value = = ' \t ' ) {
return " ' \\ t' " ;
} else if ( ' \0 ' < = value & & value < ' ' ) {
return : : Catch : : Detail : : stringify ( static_cast < unsigned int > ( value ) ) ;
} else {
char chstr [ ] = " ' ' " ;
chstr [ 1 ] = value ;
return chstr ;
}
}
std : : string StringMaker < char > : : convert ( char c ) {
return : : Catch : : Detail : : stringify ( static_cast < signed char > ( c ) ) ;
}
std : : string StringMaker < unsigned char > : : convert ( unsigned char c ) {
return : : Catch : : Detail : : stringify ( static_cast < char > ( c ) ) ;
}
int StringMaker < float > : : precision = 5 ;
std : : string StringMaker < float > : : convert ( float value ) {
return Detail : : fpToString ( value , precision ) + ' f ' ;
}
int StringMaker < double > : : precision = 10 ;
std : : string StringMaker < double > : : 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 ;
return diff ;
}
Counts & Counts : : operator + = ( Counts const & other ) {
passed + = other . passed ;
failed + = other . failed ;
failedButOk + = other . failedButOk ;
return * this ;
}
2022-01-03 23:16:39 +01:00
std : : uint64_t Counts : : total ( ) const {
2020-09-08 15:53:08 +02:00
return passed + failed + failedButOk ;
}
bool Counts : : allPassed ( ) const {
return failed = = 0 & & failedButOk = = 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 ) {
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
+ + diff . testCases . passed ;
return diff ;
}
}
# include <ostream>
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 )
{ }
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 ( ) {
2022-01-03 23:16:39 +01:00
static Version version ( 3 , 0 , 0 , " preview " , 4 ) ;
2020-09-08 15:53:08 +02:00
return version ;
}
}
/** \file
* This is a special TU that combines what would otherwise be a very
* small generator - related TUs into one bigger TU .
*
* The reason for this is compilation performance improvements by
* avoiding reparsing headers for many small TUs , instead having this
* one TU include bit more , but having it all parsed only once .
*
* To avoid heavy - tail problem with compilation times , each " subpart "
* of Catch2 has its own combined TU like this .
*/
////////////////////////////////////////////////////
// vvv formerly catch_generator_exception.cpp vvv //
////////////////////////////////////////////////////
namespace Catch {
const char * GeneratorException : : what ( ) const noexcept {
return m_msg ;
}
} // end namespace Catch
///////////////////////////////////////////
// vvv formerly catch_generators.cpp vvv //
///////////////////////////////////////////
namespace Catch {
IGeneratorTracker : : ~ IGeneratorTracker ( ) = default ;
namespace Generators {
namespace Detail {
[[noreturn]]
void throw_generator_exception ( char const * msg ) {
Catch : : throw_exception ( GeneratorException { msg } ) ;
}
} // end namespace Detail
GeneratorUntypedBase : : ~ GeneratorUntypedBase ( ) = default ;
auto acquireGeneratorTracker ( StringRef generatorName , SourceLineInfo const & lineInfo ) - > IGeneratorTracker & {
return getResultCapture ( ) . acquireGeneratorTracker ( generatorName , lineInfo ) ;
}
} // namespace Generators
} // namespace Catch
/** \file
* This is a special TU that combines what would otherwise be a very
* small interfaces - related TUs into one bigger TU .
*
* The reason for this is compilation performance improvements by
* avoiding reparsing headers for many small TUs , instead having this
* one TU include bit more , but having it all parsed only once .
*
* To avoid heavy - tail problem with compilation times , each " subpart "
* of Catch2 has its own combined TU like this .
*/
///////////////////////////////////////////////////
// vvv formerly catch_interfaces_capture.cpp vvv //
///////////////////////////////////////////////////
namespace Catch {
IResultCapture : : ~ IResultCapture ( ) = default ;
}
//////////////////////////////////////////////////
// vvv formerly catch_interfaces_config.cpp vvv //
//////////////////////////////////////////////////
namespace Catch {
IConfig : : ~ IConfig ( ) = default ;
}
/////////////////////////////////////////////////////
// vvv formerly catch_interfaces_exception.cpp vvv //
/////////////////////////////////////////////////////
namespace Catch {
IExceptionTranslator : : ~ IExceptionTranslator ( ) = default ;
IExceptionTranslatorRegistry : : ~ IExceptionTranslatorRegistry ( ) = default ;
}
////////////////////////////////////////////////////////
// vvv formerly catch_interfaces_registry_hub.cpp vvv //
////////////////////////////////////////////////////////
namespace Catch {
IRegistryHub : : ~ IRegistryHub ( ) = default ;
IMutableRegistryHub : : ~ IMutableRegistryHub ( ) = default ;
}
////////////////////////////////////////////////////
// vvv formerly catch_interfaces_testcase.cpp vvv //
////////////////////////////////////////////////////
namespace Catch {
ITestInvoker : : ~ ITestInvoker ( ) = default ;
ITestCaseRegistry : : ~ ITestCaseRegistry ( ) = default ;
}
2022-01-03 23:16:39 +01:00
2020-09-08 15:53:08 +02:00
namespace Catch {
IReporterRegistry : : ~ IReporterRegistry ( ) = default ;
}
namespace Catch {
IReporterFactory : : ~ IReporterFactory ( ) = default ;
}
# include <algorithm>
# include <iomanip>
namespace Catch {
ReporterConfig : : ReporterConfig ( IConfig const * _fullConfig , std : : ostream & _stream )
: m_stream ( & _stream ) , m_fullConfig ( _fullConfig ) { }
std : : ostream & ReporterConfig : : stream ( ) const { return * m_stream ; }
IConfig const * ReporterConfig : : fullConfig ( ) const { return m_fullConfig ; }
2022-01-03 23:16:39 +01:00
AssertionStats : : AssertionStats ( AssertionResult const & _assertionResult ,
std : : vector < MessageInfo > const & _infoMessages ,
Totals const & _totals )
2020-09-08 15:53:08 +02:00
: assertionResult ( _assertionResult ) ,
infoMessages ( _infoMessages ) ,
totals ( _totals )
{
assertionResult . m_resultData . lazyExpression . m_transientExpression = _assertionResult . m_resultData . lazyExpression . m_transientExpression ;
if ( assertionResult . hasMessage ( ) ) {
// Copy message into messages list.
// !TBD This should have been done earlier, somewhere
MessageBuilder builder ( assertionResult . getTestMacroName ( ) , assertionResult . getSourceInfo ( ) , assertionResult . getResultType ( ) ) ;
builder < < assertionResult . getMessage ( ) ;
builder . m_info . message = builder . m_stream . str ( ) ;
infoMessages . push_back ( builder . m_info ) ;
}
}
SectionStats : : SectionStats ( SectionInfo const & _sectionInfo ,
Counts const & _assertions ,
double _durationInSeconds ,
bool _missingAssertions )
: sectionInfo ( _sectionInfo ) ,
assertions ( _assertions ) ,
durationInSeconds ( _durationInSeconds ) ,
missingAssertions ( _missingAssertions )
{ }
TestCaseStats : : TestCaseStats ( TestCaseInfo const & _testInfo ,
Totals const & _totals ,
std : : string const & _stdOut ,
std : : string const & _stdErr ,
bool _aborting )
: testInfo ( & _testInfo ) ,
totals ( _totals ) ,
stdOut ( _stdOut ) ,
stdErr ( _stdErr ) ,
aborting ( _aborting )
{ }
TestRunStats : : TestRunStats ( TestRunInfo const & _runInfo ,
Totals const & _totals ,
bool _aborting )
: runInfo ( _runInfo ) ,
totals ( _totals ) ,
aborting ( _aborting )
{ }
2022-01-03 23:16:39 +01:00
IStreamingReporter : : ~ IStreamingReporter ( ) = default ;
2020-09-08 15:53:08 +02:00
} // end namespace Catch
namespace Catch {
AssertionHandler : : AssertionHandler
2022-01-03 23:16:39 +01:00
( StringRef macroName ,
2020-09-08 15:53:08 +02:00
SourceLineInfo const & lineInfo ,
StringRef capturedExpression ,
ResultDisposition : : Flags resultDisposition )
: m_assertionInfo { macroName , lineInfo , capturedExpression , resultDisposition } ,
m_resultCapture ( getResultCapture ( ) )
{ }
void AssertionHandler : : handleExpr ( ITransientExpression const & expr ) {
m_resultCapture . handleExpr ( m_assertionInfo , expr , m_reaction ) ;
}
2022-01-03 23:16:39 +01:00
void AssertionHandler : : handleMessage ( ResultWas : : OfType resultType , StringRef message ) {
2020-09-08 15:53:08 +02:00
m_resultCapture . handleMessage ( m_assertionInfo , resultType , message , m_reaction ) ;
}
auto AssertionHandler : : allowThrows ( ) const - > bool {
return getCurrentContext ( ) . getConfig ( ) - > allowThrows ( ) ;
}
void AssertionHandler : : complete ( ) {
setCompleted ( ) ;
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 ) {
# if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
throw Catch : : TestFailureException ( ) ;
# else
CATCH_ERROR ( " Test failure requires aborting test! " ) ;
# endif
}
}
void AssertionHandler : : setCompleted ( ) {
m_completed = true ;
}
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
2022-01-03 23:16:39 +01:00
void handleExceptionMatchExpr ( AssertionHandler & handler , std : : string const & str , StringRef matcherString ) {
2020-09-08 15:53:08 +02:00
handleExceptionMatchExpr ( handler , Matchers : : Equals ( str ) , matcherString ) ;
}
} // namespace Catch
2022-01-03 23:16:39 +01:00
# include <algorithm>
namespace Catch {
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
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 Catch
2020-09-08 15:53:08 +02:00
# include <algorithm>
namespace {
bool isOptPrefix ( char c ) {
return c = = ' - '
# ifdef CATCH_PLATFORM_WINDOWS
| | c = = ' / '
# endif
;
}
std : : string normaliseOpt ( std : : string const & optName ) {
# ifdef CATCH_PLATFORM_WINDOWS
if ( optName [ 0 ] = = ' / ' )
return " - " + optName . substr ( 1 ) ;
else
# endif
return optName ;
}
} // namespace
namespace Catch {
namespace Clara {
namespace Detail {
void TokenStream : : loadBuffer ( ) {
m_tokenBuffer . clear ( ) ;
// Skip any empty strings
while ( it ! = itEnd & & it - > empty ( ) ) {
+ + it ;
}
if ( it ! = itEnd ) {
auto const & next = * it ;
if ( isOptPrefix ( next [ 0 ] ) ) {
auto delimiterPos = next . find_first_of ( " := " ) ;
if ( delimiterPos ! = std : : string : : npos ) {
m_tokenBuffer . push_back (
{ TokenType : : Option ,
next . substr ( 0 , delimiterPos ) } ) ;
m_tokenBuffer . push_back (
{ TokenType : : Argument ,
next . substr ( delimiterPos + 1 ) } ) ;
} else {
if ( next [ 1 ] ! = ' - ' & & next . size ( ) > 2 ) {
std : : string opt = " - " ;
for ( size_t i = 1 ; i < next . size ( ) ; + + i ) {
opt [ 1 ] = next [ i ] ;
m_tokenBuffer . push_back (
{ TokenType : : Option , opt } ) ;
}
} 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: ' " +
2022-01-03 23:16:39 +01:00
source + ' \' ' ) ;
2020-09-08 15:53:08 +02:00
}
return ParserResult : : ok ( ParseResultType : : Matched ) ;
}
size_t ParserBase : : cardinality ( ) const { return 1 ; }
InternalParseResult ParserBase : : parse ( Args const & args ) const {
return parse ( args . exeName ( ) , TokenStream ( args ) ) ;
}
ParseState : : ParseState ( ParseResultType type ,
TokenStream const & remainingTokens ) :
m_type ( type ) , m_remainingTokens ( 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 const & tokens ) const {
auto validationResult = validate ( ) ;
if ( ! validationResult )
return Detail : : InternalParseResult ( validationResult ) ;
auto remainingTokens = tokens ;
auto const & token = * remainingTokens ;
if ( token . type ! = Detail : : TokenType : : Argument )
return Detail : : InternalParseResult : : ok ( Detail : : ParseState (
ParseResultType : : NoMatch , remainingTokens ) ) ;
assert ( ! m_ref - > isFlag ( ) ) ;
auto valueRef =
static_cast < Detail : : BoundValueRefBase * > ( m_ref . get ( ) ) ;
auto result = valueRef - > setValue ( remainingTokens - > token ) ;
if ( ! result )
return Detail : : InternalParseResult ( result ) ;
else
return Detail : : InternalParseResult : : ok ( Detail : : ParseState (
ParseResultType : : Matched , + + remainingTokens ) ) ;
}
Opt : : Opt ( bool & ref ) :
ParserRefImpl ( std : : make_shared < Detail : : BoundFlagRef > ( ref ) ) { }
std : : vector < Detail : : HelpColumns > Opt : : getHelpColumns ( ) const {
std : : ostringstream 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 ( std : : string const & 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 const & tokens ) const {
auto validationResult = validate ( ) ;
if ( ! validationResult )
return Detail : : InternalParseResult ( validationResult ) ;
auto remainingTokens = tokens ;
if ( remainingTokens & &
remainingTokens - > type = = Detail : : TokenType : : Option ) {
auto const & token = * remainingTokens ;
if ( isMatch ( token . token ) ) {
if ( m_ref - > isFlag ( ) ) {
auto flagRef =
static_cast < Detail : : BoundFlagRefBase * > (
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 ( ) , remainingTokens ) ) ;
} else {
auto valueRef =
static_cast < Detail : : BoundValueRefBase * > (
m_ref . get ( ) ) ;
+ + remainingTokens ;
if ( ! remainingTokens )
return Detail : : InternalParseResult : : runtimeError (
" Expected argument following " +
token . token ) ;
auto const & argToken = * remainingTokens ;
if ( argToken . type ! = Detail : : TokenType : : Argument )
return Detail : : InternalParseResult : : runtimeError (
" Expected argument following " +
token . token ) ;
auto result = valueRef - > setValue ( argToken . token ) ;
if ( ! result )
return Detail : : InternalParseResult ( result ) ;
if ( result . value ( ) = =
ParseResultType : : ShortCircuitAll )
return Detail : : InternalParseResult : : ok ( Detail : : ParseState (
result . value ( ) , remainingTokens ) ) ;
}
return Detail : : InternalParseResult : : ok ( Detail : : ParseState (
ParseResultType : : Matched , + + remainingTokens ) ) ;
}
}
return Detail : : InternalParseResult : : ok (
Detail : : ParseState ( ParseResultType : : NoMatch , remainingTokens ) ) ;
}
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 '/' " ) ;
# else
if ( name [ 0 ] ! = ' - ' )
return Detail : : Result : : logicError (
" Option name must begin with '-' " ) ;
# endif
}
return ParserRefImpl : : validate ( ) ;
}
ExeName : : ExeName ( ) :
m_name ( std : : make_shared < std : : string > ( " <executable> " ) ) { }
ExeName : : ExeName ( std : : string & ref ) : ExeName ( ) {
m_ref = std : : make_shared < Detail : : BoundValueRef < std : : string > > ( ref ) ;
}
Detail : : InternalParseResult
ExeName : : parse ( std : : string const & ,
Detail : : TokenStream const & tokens ) const {
return Detail : : InternalParseResult : : ok (
Detail : : ParseState ( ParseResultType : : NoMatch , 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 < Detail : : HelpColumns > Parser : : getHelpColumns ( ) const {
std : : vector < Detail : : HelpColumns > cols ;
for ( auto const & o : m_options ) {
auto childCols = o . getHelpColumns ( ) ;
cols . insert ( cols . end ( ) , childCols . begin ( ) , childCols . end ( ) ) ;
}
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 \n where 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 const & cols : rows ) {
auto row = TextFlow : : Column ( cols . left )
. width ( optWidth )
. indent ( 2 ) +
TextFlow : : Spacer ( 4 ) +
TextFlow : : Column ( cols . right )
. 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 const & tokens ) const {
struct ParserInfo {
ParserBase const * parser = nullptr ;
size_t count = 0 ;
} ;
std : : vector < ParserInfo > 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 , 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 , 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 < std : : string > 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 < Opt & > ( * this ) (
" display usage information " ) [ " -? " ] [ " -h " ] [ " --help " ]
. optional ( ) ;
}
} // namespace Clara
} // namespace Catch
/** \file
* This is a special TU that combines what would otherwise be a very
* small top - level TUs into one bigger TU .
*
* The reason for this is compilation performance improvements by
* avoiding reparsing headers for many small TUs , instead having this
* one TU include bit more , but having it all parsed only once .
*
* To avoid heavy - tail problem with compilation times , each " subpart "
* of Catch2 has its own combined TU like this .
*/
////////////////////////////////////////////////////////
// vvv formerly catch_tag_alias_autoregistrar.cpp vvv //
////////////////////////////////////////////////////////
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 ( ) ;
}
}
}
//////////////////////////////////////////
// vvv formerly catch_polyfills.cpp vvv //
//////////////////////////////////////////
# include <cmath>
namespace Catch {
# if !defined(CATCH_CONFIG_POLYFILL_ISNAN)
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 ) ;
}
# endif
} // end namespace Catch
////////////////////////////////////////////////////
// vvv formerly catch_uncaught_exceptions.cpp vvv //
////////////////////////////////////////////////////
2020-10-08 15:26:30 +02:00
2020-09-08 15:53:08 +02:00
# include <exception>
namespace Catch {
bool uncaught_exceptions ( ) {
# if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
return false ;
2020-10-08 15:26:30 +02:00
# elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
2020-09-08 15:53:08 +02:00
return std : : uncaught_exceptions ( ) > 0 ;
# else
return std : : uncaught_exception ( ) ;
# endif
}
} // end namespace Catch
////////////////////////////////////////////
// vvv formerly catch_errno_guard.cpp vvv //
////////////////////////////////////////////
# include <cerrno>
namespace Catch {
ErrnoGuard : : ErrnoGuard ( ) : m_oldErrno ( errno ) { }
ErrnoGuard : : ~ ErrnoGuard ( ) { errno = m_oldErrno ; }
}
///////////////////////////////////////////
// vvv formerly catch_decomposer.cpp vvv //
///////////////////////////////////////////
namespace Catch {
ITransientExpression : : ~ ITransientExpression ( ) = default ;
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 ;
}
}
///////////////////////////////////////////////////////////
// vvv formerly catch_startup_exception_registry.cpp vvv //
///////////////////////////////////////////////////////////
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 ( ) ;
}
}
std : : vector < std : : exception_ptr > const & StartupExceptionRegistry : : getExceptions ( ) const noexcept {
return m_exceptions ;
}
# endif
} // end namespace Catch
//////////////////////////////////////////////
// vvv formerly catch_leak_detector.cpp vvv //
//////////////////////////////////////////////
# ifdef CATCH_CONFIG_WINDOWS_CRTDBG
# include <crtdbg.h>
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 ) ;
}
}
# else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv
Catch : : LeakDetector : : LeakDetector ( ) { }
# endif // CATCH_CONFIG_WINDOWS_CRTDBG
Catch : : LeakDetector : : ~ LeakDetector ( ) {
Catch : : cleanUp ( ) ;
}
/////////////////////////////////////////////
// vvv formerly catch_message_info.cpp vvv //
/////////////////////////////////////////////
namespace Catch {
2022-01-03 23:16:39 +01:00
MessageInfo : : MessageInfo ( StringRef _macroName ,
2020-09-08 15:53:08 +02:00
SourceLineInfo const & _lineInfo ,
ResultWas : : OfType _type )
: macroName ( _macroName ) ,
lineInfo ( _lineInfo ) ,
type ( _type ) ,
sequence ( + + globalCount )
{ }
// This may need protecting if threading support is added
unsigned int MessageInfo : : globalCount = 0 ;
} // end namespace Catch
//////////////////////////////////////////
// vvv formerly catch_lazy_expr.cpp vvv //
//////////////////////////////////////////
namespace Catch {
auto operator < < ( std : : ostream & os , LazyExpression const & lazyExpr ) - > std : : ostream & {
if ( lazyExpr . m_isNegated )
2022-01-03 23:16:39 +01:00
os < < ' ! ' ;
2020-09-08 15:53:08 +02:00
if ( lazyExpr ) {
if ( lazyExpr . m_isNegated & & lazyExpr . m_transientExpression - > isBinaryExpression ( ) )
2022-01-03 23:16:39 +01:00
os < < ' ( ' < < * lazyExpr . m_transientExpression < < ' ) ' ;
2020-09-08 15:53:08 +02:00
else
os < < * lazyExpr . m_transientExpression ;
} else {
os < < " {** error - unchecked empty expression requested **} " ;
}
return os ;
}
} // namespace Catch
2022-01-03 23:16:39 +01:00
# include <algorithm>
2020-09-08 15:53:08 +02:00
# include <fstream>
2022-01-03 23:16:39 +01:00
# include <string>
2020-09-08 15:53:08 +02:00
namespace Catch {
Clara : : Parser makeCommandLineParser ( ConfigData & config ) {
using namespace Clara ;
auto const setWarning = [ & ] ( std : : string const & warning ) {
2022-01-03 23:16:39 +01:00
if ( warning = = " NoAssertions " ) {
config . warnings = static_cast < WarnAbout : : What > ( config . warnings | WarnAbout : : NoAssertions ) ;
2020-09-08 15:53:08 +02:00
return ParserResult : : ok ( ParseResultType : : Matched ) ;
2022-01-03 23:16:39 +01:00
} else if ( warning = = " UnmatchedTestSpec " ) {
config . warnings = static_cast < WarnAbout : : What > ( config . warnings | WarnAbout : : UnmatchedTestSpec ) ;
return ParserResult : : ok ( ParseResultType : : Matched ) ;
}
return ParserResult : : runtimeError (
" Unrecognised warning option: ' " + warning + ' \' ' ) ;
} ;
2020-09-08 15:53:08 +02:00
auto const loadTestNamesFromFile = [ & ] ( std : : string const & filename ) {
std : : ifstream f ( filename . c_str ( ) ) ;
if ( ! f . is_open ( ) )
2022-01-03 23:16:39 +01:00
return ParserResult : : runtimeError ( " Unable to load input file: ' " + filename + ' \' ' ) ;
2020-09-08 15:53:08 +02:00
std : : string line ;
while ( std : : getline ( f , line ) ) {
line = trim ( line ) ;
if ( ! line . empty ( ) & & ! startsWith ( line , ' # ' ) ) {
if ( ! startsWith ( line , ' " ' ) )
line = ' " ' + 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
2022-01-03 23:16:39 +01:00
return ParserResult : : runtimeError ( " Unrecognised ordering: ' " + order + ' \' ' ) ;
2020-09-08 15:53:08 +02:00
return ParserResult : : ok ( ParseResultType : : Matched ) ;
} ;
auto const setRngSeed = [ & ] ( std : : string const & seed ) {
2022-01-03 23:16:39 +01:00
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 ) ;
}
CATCH_TRY {
std : : size_t parsedTo = 0 ;
unsigned long parsedSeed = std : : stoul ( seed , & parsedTo , 0 ) ;
if ( parsedTo ! = seed . size ( ) ) {
return ParserResult : : runtimeError ( " Could not parse ' " + seed + " ' as seed " ) ;
}
// TODO: Ideally we could parse unsigned int directly,
// but the stdlib doesn't provide helper for that
// type. After this is refactored to use fixed size
// type, we should check the parsed value is in range
// of the underlying type.
config . rngSeed = static_cast < unsigned int > ( parsedSeed ) ;
return ParserResult : : ok ( ParseResultType : : Matched ) ;
} CATCH_CATCH_ANON ( std : : exception const & ) {
return ParserResult : : runtimeError ( " Could not parse ' " + seed + " ' as seed " ) ;
}
2020-09-08 15:53:08 +02:00
} ;
auto const setColourUsage = [ & ] ( std : : string const & useColour ) {
auto mode = toLower ( useColour ) ;
if ( mode = = " yes " )
config . useColour = UseColour : : Yes ;
else if ( mode = = " no " )
config . useColour = UseColour : : No ;
else if ( mode = = " auto " )
config . useColour = UseColour : : Auto ;
else
return ParserResult : : runtimeError ( " colour mode must be one of: auto, yes or no. ' " + useColour + " ' not recognised " ) ;
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
2022-01-03 23:16:39 +01:00
return ParserResult : : runtimeError ( " Unrecognised verbosity, ' " + verbosity + ' \' ' ) ;
2020-09-08 15:53:08 +02:00
return ParserResult : : ok ( ParseResultType : : Matched ) ;
} ;
2022-01-03 23:16:39 +01:00
auto const setReporter = [ & ] ( std : : string const & reporterSpec ) {
if ( reporterSpec . empty ( ) ) {
return ParserResult : : runtimeError ( " Received empty reporter spec. " ) ;
}
2020-09-08 15:53:08 +02:00
IReporterRegistry : : FactoryMap const & factories = getRegistryHub ( ) . getReporterRegistry ( ) . getFactories ( ) ;
2022-01-03 23:16:39 +01:00
// clear the default reporter
if ( ! config . _nonDefaultReporterSpecifications ) {
config . reporterSpecifications . clear ( ) ;
config . _nonDefaultReporterSpecifications = true ;
}
// Exactly one of the reporters may be specified without an output
// file, in which case it defaults to the output specified by "-o"
// (or standard output).
static constexpr auto separator = " :: " ;
static constexpr size_t separatorSize = 2 ;
auto fileNameSeparatorPos = reporterSpec . find ( separator ) ;
const bool containsFileName = fileNameSeparatorPos ! = reporterSpec . npos ;
if ( containsFileName ) {
auto nextSeparatorPos = reporterSpec . find (
separator , fileNameSeparatorPos + separatorSize ) ;
if ( nextSeparatorPos ! = reporterSpec . npos ) {
return ParserResult : : runtimeError (
" Too many separators in reporter spec ' " + reporterSpec + ' \' ' ) ;
}
}
std : : string reporterName ;
Optional < std : : string > outputFileName ;
reporterName = reporterSpec . substr ( 0 , fileNameSeparatorPos ) ;
if ( reporterName . empty ( ) ) {
return ParserResult : : runtimeError ( " Reporter name cannot be empty. " ) ;
}
if ( containsFileName ) {
outputFileName = reporterSpec . substr (
fileNameSeparatorPos + separatorSize , reporterSpec . size ( ) ) ;
}
auto result = factories . find ( reporterName ) ;
if ( result = = factories . end ( ) )
return ParserResult : : runtimeError ( " Unrecognized reporter, ' " + reporterName + " '. Check available with --list-reporters " ) ;
if ( containsFileName & & outputFileName - > empty ( ) )
return ParserResult : : runtimeError ( " Reporter ' " + reporterName + " ' has empty filename specified as its output. Supply a filename or remove the colons to use the default output. " ) ;
config . reporterSpecifications . push_back ( { std : : move ( reporterName ) , std : : move ( outputFileName ) } ) ;
// 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 ( ! containsFileName ) {
int n_reporters_without_file = 0 ;
for ( auto const & spec : config . reporterSpecifications ) {
if ( spec . outputFileName . none ( ) ) {
n_reporters_without_file + + ;
}
}
if ( n_reporters_without_file > 1 ) {
return ParserResult : : runtimeError ( " Only one reporter may have unspecified output file. " ) ;
}
}
2020-09-08 15:53:08 +02:00
return ParserResult : : ok ( ParseResultType : : Matched ) ;
} ;
2022-01-03 23:16:39 +01:00
auto const setShardCount = [ & ] ( std : : string const & shardCount ) {
CATCH_TRY {
std : : size_t parsedTo = 0 ;
int64_t parsedCount = std : : stoll ( shardCount , & parsedTo , 0 ) ;
if ( parsedTo ! = shardCount . size ( ) ) {
return ParserResult : : runtimeError ( " Could not parse ' " + shardCount + " ' as shard count " ) ;
}
if ( parsedCount < = 0 ) {
return ParserResult : : runtimeError ( " Shard count must be a positive number " ) ;
}
config . shardCount = static_cast < unsigned int > ( parsedCount ) ;
return ParserResult : : ok ( ParseResultType : : Matched ) ;
} CATCH_CATCH_ANON ( std : : exception const & ) {
return ParserResult : : runtimeError ( " Could not parse ' " + shardCount + " ' as shard count " ) ;
}
} ;
auto const setShardIndex = [ & ] ( std : : string const & shardIndex ) {
CATCH_TRY {
std : : size_t parsedTo = 0 ;
int64_t parsedIndex = std : : stoll ( shardIndex , & parsedTo , 0 ) ;
if ( parsedTo ! = shardIndex . size ( ) ) {
return ParserResult : : runtimeError ( " Could not parse ' " + shardIndex + " ' as shard index " ) ;
}
if ( parsedIndex < 0 ) {
return ParserResult : : runtimeError ( " Shard index must be a non-negative number " ) ;
}
config . shardIndex = static_cast < unsigned int > ( parsedIndex ) ;
return ParserResult : : ok ( ParseResultType : : Matched ) ;
} CATCH_CATCH_ANON ( std : : exception const & ) {
return ParserResult : : runtimeError ( " Could not parse ' " + shardIndex + " ' as shard index " ) ;
}
} ;
2020-09-08 15:53:08 +02:00
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) " )
2022-01-03 23:16:39 +01:00
| Opt ( config . defaultOutputFilename , " filename " )
2020-09-08 15:53:08 +02:00
[ " -o " ] [ " --out " ]
2022-01-03 23:16:39 +01:00
( " default output filename " )
| Opt ( accept_many , setReporter , " name[:output-file] " )
2020-09-08 15:53:08 +02:00
[ " -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 " )
2022-01-03 23:16:39 +01:00
| Opt ( accept_many , setWarning , " warning name " )
2020-09-08 15:53:08 +02:00
[ " -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 ( config . sectionsToRun , " section name " )
[ " -c " ] [ " --section " ]
( " specify section to run " )
| Opt ( setVerbosity , " quiet|normal|high " )
[ " -v " ] [ " --verbosity " ]
( " set output verbosity " )
2022-01-03 23:16:39 +01:00
| Opt ( config . listTests )
[ " --list-tests " ]
( " list all/matching test cases " )
| Opt ( config . listTags )
[ " --list-tags " ]
( " list all/matching tags " )
2020-09-08 15:53:08 +02:00
| Opt ( config . listReporters )
[ " --list-reporters " ]
( " list all reporters " )
| Opt ( setTestOrder , " decl|lex|rand " )
[ " --order " ]
( " test case order (defaults to decl) " )
2022-01-03 23:16:39 +01:00
| Opt ( setRngSeed , " 'time'|'random-device'|number " )
2020-09-08 15:53:08 +02:00
[ " --rng-seed " ]
( " set a specific seed for random numbers " )
| Opt ( setColourUsage , " yes|no " )
[ " --use-colour " ]
( " should output be colourised " )
| 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 . benchmarkSamples , " 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) " )
2022-01-03 23:16:39 +01:00
| 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 " )
2020-09-08 15:53:08 +02:00
| Arg ( config . testsOrTags , " test name|pattern|tags " )
( " which test or tests to use " ) ;
return cli ;
}
} // end namespace Catch
# if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors"
# endif
# include <ostream>
namespace Catch {
namespace {
struct IColourImpl {
virtual ~ IColourImpl ( ) = default ;
virtual void use ( Colour : : Code _colourCode ) = 0 ;
} ;
struct NoColourImpl : IColourImpl {
void use ( Colour : : Code ) override { }
static IColourImpl * instance ( ) {
static NoColourImpl s_instance ;
return & s_instance ;
}
} ;
} // anon namespace
} // namespace Catch
# if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
# ifdef CATCH_PLATFORM_WINDOWS
# define CATCH_CONFIG_COLOUR_WINDOWS
# else
# define CATCH_CONFIG_COLOUR_ANSI
# endif
# endif
# if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
namespace Catch {
namespace {
class Win32ColourImpl : public IColourImpl {
public :
Win32ColourImpl ( ) : stdoutHandle ( GetStdHandle ( STD_OUTPUT_HANDLE ) )
{
CONSOLE_SCREEN_BUFFER_INFO csbiInfo ;
GetConsoleScreenBufferInfo ( stdoutHandle , & csbiInfo ) ;
originalForegroundAttributes = csbiInfo . wAttributes & ~ ( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ) ;
originalBackgroundAttributes = csbiInfo . wAttributes & ~ ( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ) ;
}
void use ( Colour : : Code _colourCode ) 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 : : Bright : CATCH_INTERNAL_ERROR ( " not a colour " ) ;
default :
CATCH_ERROR ( " Unknown colour requested " ) ;
}
}
private :
void setTextAttribute ( WORD _textAttribute ) {
SetConsoleTextAttribute ( stdoutHandle , _textAttribute | originalBackgroundAttributes ) ;
}
HANDLE stdoutHandle ;
WORD originalForegroundAttributes ;
WORD originalBackgroundAttributes ;
} ;
IColourImpl * platformColourInstance ( ) {
static Win32ColourImpl s_instance ;
auto const * config = getCurrentContext ( ) . getConfig ( ) ;
UseColour colourMode = config ?
config - > useColour ( ) : UseColour : : Auto ;
if ( colourMode = = UseColour : : Auto )
colourMode = UseColour : : Yes ;
return colourMode = = UseColour : : Yes
? & s_instance
: NoColourImpl : : instance ( ) ;
}
} // end anon namespace
} // end namespace Catch
# elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
# include <unistd.h>
namespace Catch {
namespace {
// use POSIX/ ANSI console terminal codes
// Thanks to Adam Strzelecki for original contribution
// (http://github.com/nanoant)
// https://github.com/philsquared/Catch/pull/131
class PosixColourImpl : public IColourImpl {
public :
void use ( Colour : : Code _colourCode ) override {
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 : : Bright : CATCH_INTERNAL_ERROR ( " not a colour " ) ;
default : CATCH_INTERNAL_ERROR ( " Unknown colour requested " ) ;
}
}
static IColourImpl * instance ( ) {
static PosixColourImpl s_instance ;
return & s_instance ;
}
private :
void setColour ( const char * _escapeCode ) {
// The escape sequence must be flushed to console, otherwise if
// stdin and stderr are intermixed, we'd get accidentally coloured output.
2022-01-03 23:16:39 +01:00
getCurrentContext ( ) . getConfig ( ) - > defaultStream ( )
2020-09-08 15:53:08 +02:00
< < ' \033 ' < < _escapeCode < < std : : flush ;
}
} ;
bool useColourOnPlatform ( ) {
return
# if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
! isDebuggerActive ( ) & &
# endif
# if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
isatty ( STDOUT_FILENO )
# else
false
# endif
;
}
IColourImpl * platformColourInstance ( ) {
ErrnoGuard guard ;
auto const * config = getCurrentContext ( ) . getConfig ( ) ;
UseColour colourMode = config
? config - > useColour ( )
: UseColour : : Auto ;
if ( colourMode = = UseColour : : Auto )
colourMode = useColourOnPlatform ( )
? UseColour : : Yes
: UseColour : : No ;
return colourMode = = UseColour : : Yes
? PosixColourImpl : : instance ( )
: NoColourImpl : : instance ( ) ;
}
} // end anon namespace
} // end namespace Catch
# else // not Windows or ANSI ///////////////////////////////////////////////
namespace Catch {
static IColourImpl * platformColourInstance ( ) { return NoColourImpl : : instance ( ) ; }
} // end namespace Catch
# endif // Windows/ ANSI/ None
namespace Catch {
Colour : : Colour ( Code _colourCode ) { use ( _colourCode ) ; }
Colour : : Colour ( Colour & & other ) noexcept {
m_moved = other . m_moved ;
other . m_moved = true ;
}
Colour & Colour : : operator = ( Colour & & other ) noexcept {
m_moved = other . m_moved ;
other . m_moved = true ;
return * this ;
}
Colour : : ~ Colour ( ) { if ( ! m_moved ) use ( None ) ; }
void Colour : : use ( Code _colourCode ) {
static IColourImpl * impl = platformColourInstance ( ) ;
// Strictly speaking, this cannot possibly happen.
// However, under some conditions it does happen (see #1626),
// and this change is small enough that we can let practicality
// triumph over purity in this case.
if ( impl ! = nullptr ) {
impl - > use ( _colourCode ) ;
}
}
std : : ostream & operator < < ( std : : ostream & os , Colour const & ) {
return os ;
}
} // end namespace Catch
# if defined(__clang__)
# pragma clang diagnostic pop
# endif
namespace Catch {
class Context : public IMutableContext , private Detail : : NonCopyable {
public : // IContext
IResultCapture * getResultCapture ( ) override {
return m_resultCapture ;
}
IConfig const * getConfig ( ) const override {
return m_config ;
}
~ Context ( ) override ;
public : // IMutableContext
void setResultCapture ( IResultCapture * resultCapture ) override {
m_resultCapture = resultCapture ;
}
void setConfig ( IConfig const * config ) override {
m_config = config ;
}
friend IMutableContext & getCurrentMutableContext ( ) ;
private :
IConfig const * m_config = nullptr ;
IResultCapture * m_resultCapture = nullptr ;
} ;
IMutableContext * IMutableContext : : currentContext = nullptr ;
void IMutableContext : : createContext ( )
{
currentContext = new Context ( ) ;
}
void cleanUpContext ( ) {
delete IMutableContext : : currentContext ;
IMutableContext : : currentContext = nullptr ;
}
IContext : : ~ IContext ( ) = default ;
IMutableContext : : ~ IMutableContext ( ) = default ;
Context : : ~ Context ( ) = default ;
SimplePcg32 & rng ( ) {
static SimplePcg32 s_rng ;
return s_rng ;
}
}
# if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
# include <android/log.h>
namespace Catch {
void writeToDebugConsole ( std : : string const & text ) {
__android_log_write ( ANDROID_LOG_DEBUG , " Catch " , text . c_str ( ) ) ;
}
}
# elif defined(CATCH_PLATFORM_WINDOWS)
namespace Catch {
void writeToDebugConsole ( std : : string const & text ) {
: : OutputDebugStringA ( text . c_str ( ) ) ;
}
}
# else
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 <cassert>
# include <sys / types.h>
# include <unistd.h>
# include <cstddef>
# include <ostream>
# ifdef __apple_build_version__
// These headers will only compile with AppleClang (XCode)
// For other compilers (Clang, GCC, ... ) we need to exclude them
# include <sys / sysctl.h>
# 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
// 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.
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.
mib [ 0 ] = CTL_KERN ;
mib [ 1 ] = KERN_PROC ;
mib [ 2 ] = KERN_PROC_PID ;
mib [ 3 ] = getpid ( ) ;
// Call sysctl.
size = sizeof ( info ) ;
if ( sysctl ( mib , sizeof ( mib ) / sizeof ( * mib ) , & info , & size , nullptr , 0 ) ! = 0 ) {
2022-01-03 23:16:39 +01:00
Catch : : cerr ( ) < < " \n ** Call to sysctl failed - unable to determine if debugger is active ** \n \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
return false ;
}
// 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
# elif defined(CATCH_PLATFORM_LINUX)
# include <fstream>
# include <string>
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
# elif defined(_MSC_VER)
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 ;
}
}
# else
namespace Catch {
bool isDebuggerActive ( ) { return false ; }
}
# endif // Platform
# include <stdexcept>
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 ( ) ;
}
# endif
[[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;
# include <cassert>
namespace Catch {
2022-01-03 23:16:39 +01:00
IMutableEnumValuesRegistry : : ~ IMutableEnumValuesRegistry ( ) = default ;
2020-09-08 15:53:08 +02:00
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 ) {
2022-01-03 23:16:39 +01:00
// Find last occurrence of ":"
2020-09-08 15:53:08 +02:00
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 ) ;
}
}
std : : vector < StringRef > parseEnums ( StringRef enums ) {
auto enumValues = splitStringRef ( enums , ' , ' ) ;
std : : vector < StringRef > parsed ;
parsed . reserve ( enumValues . size ( ) ) ;
for ( auto const & enumValue : enumValues ) {
parsed . push_back ( trim ( extractInstanceName ( enumValue ) ) ) ;
}
return parsed ;
}
EnumInfo : : ~ EnumInfo ( ) { }
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 < EnumInfo > makeEnumInfo ( StringRef enumName , StringRef allValueNames , std : : vector < int > const & values ) {
auto enumInfo = Catch : : Detail : : make_unique < EnumInfo > ( ) ;
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 < int > const & values ) {
m_enumInfos . push_back ( makeEnumInfo ( enumName , allValueNames , values ) ) ;
return * m_enumInfos . back ( ) ;
}
} // Detail
} // Catch
namespace Catch {
ExceptionTranslatorRegistry : : ~ ExceptionTranslatorRegistry ( ) {
}
2022-01-03 23:16:39 +01:00
void ExceptionTranslatorRegistry : : registerTranslator ( Detail : : unique_ptr < IExceptionTranslator > & & translator ) {
m_translators . push_back ( CATCH_MOVE ( translator ) ) ;
2020-09-08 15:53:08 +02:00
}
# if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
std : : string ExceptionTranslatorRegistry : : translateActiveException ( ) const {
2022-01-03 23:16:39 +01:00
// 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.
2020-09-08 15:53:08 +02:00
try {
return tryTranslators ( ) ;
}
2022-01-03 23:16:39 +01:00
// To avoid having to handle TFE explicitly everywhere, we just
// rethrow it so that it goes back up the caller.
2020-09-08 15:53:08 +02:00
catch ( TestFailureException & ) {
std : : rethrow_exception ( std : : current_exception ( ) ) ;
}
2022-01-03 23:16:39 +01:00
catch ( std : : exception const & ex ) {
2020-09-08 15:53:08 +02:00
return ex . what ( ) ;
}
2022-01-03 23:16:39 +01:00
catch ( std : : string const & msg ) {
2020-09-08 15:53:08 +02:00
return msg ;
}
catch ( const char * msg ) {
return msg ;
}
catch ( . . . ) {
return " Unknown exception " ;
}
}
std : : string ExceptionTranslatorRegistry : : tryTranslators ( ) const {
if ( m_translators . empty ( ) ) {
std : : rethrow_exception ( std : : current_exception ( ) ) ;
} else {
return m_translators [ 0 ] - > translate ( m_translators . begin ( ) + 1 , m_translators . end ( ) ) ;
}
}
# else // ^^ Exceptions are enabled // Exceptions are disabled vv
std : : string ExceptionTranslatorRegistry : : translateActiveException ( ) const {
CATCH_INTERNAL_ERROR ( " Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS! " ) ;
}
std : : string ExceptionTranslatorRegistry : : tryTranslators ( ) const {
CATCH_INTERNAL_ERROR ( " Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS! " ) ;
}
# endif
}
2022-01-03 23:16:39 +01:00
/** \file
* This file provides platform specific implementations of FatalConditionHandler
*
* This means that there is a lot of conditional compilation , and platform
* specific code . Currently , Catch2 supports a dummy handler ( if no
* handler is desired ) , and 2 platform specific handlers :
* * Windows ' SEH
* * POSIX signals
*
* Consequently , various pieces of code below are compiled if either of
* the platform specific handlers is enabled , or if none of them are
* enabled . It is assumed that both cannot be enabled at the same time ,
* and doing so should cause a compilation error .
*
* If another platform specific handler is added , the compile guards
* below will need to be updated taking these assumptions into account .
*/
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
# include <algorithm>
# 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 ( ) { }
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 )
# 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
2020-09-08 15:53:08 +02:00
# if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace {
2022-01-03 23:16:39 +01:00
//! Signals fatal error message to the run context
2020-09-08 15:53:08 +02:00
void reportFatal ( char const * const message ) {
Catch : : getCurrentContext ( ) . getResultCapture ( ) - > handleFatalErrorCondition ( message ) ;
}
2022-01-03 23:16:39 +01:00
//! 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
2020-09-08 15:53:08 +02:00
# if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch {
2022-01-03 23:16:39 +01:00
2020-09-08 15:53:08 +02:00
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 SignalDefs signalDefs [ ] = {
{ static_cast < DWORD > ( EXCEPTION_ILLEGAL_INSTRUCTION ) , " SIGILL - Illegal instruction signal " } ,
{ static_cast < DWORD > ( EXCEPTION_STACK_OVERFLOW ) , " SIGSEGV - Stack overflow " } ,
{ static_cast < DWORD > ( EXCEPTION_ACCESS_VIOLATION ) , " SIGSEGV - Segmentation violation signal " } ,
{ static_cast < DWORD > ( EXCEPTION_INT_DIVIDE_BY_ZERO ) , " Divide by zero error " } ,
} ;
2022-01-03 23:16:39 +01:00
static LONG CALLBACK topLevelExceptionFilter ( PEXCEPTION_POINTERS ExceptionInfo ) {
2020-09-08 15:53:08 +02:00
for ( auto const & def : signalDefs ) {
if ( ExceptionInfo - > ExceptionRecord - > ExceptionCode = = def . id ) {
reportFatal ( def . name ) ;
}
}
// If its not an exception we care about, pass it along.
// This stops us from eating debugger breaks etc.
return EXCEPTION_CONTINUE_SEARCH ;
}
2022-01-03 23:16:39 +01:00
// 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 ;
// For MSVC, we reserve part of the stack memory for handling
// memory overflow structured exception.
2020-09-08 15:53:08 +02:00
FatalConditionHandler : : FatalConditionHandler ( ) {
2022-01-03 23:16:39 +01:00
ULONG guaranteeSize = static_cast < ULONG > ( 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. " ;
}
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
// 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 ( ) {
if ( SetUnhandledExceptionFilter ( reinterpret_cast < LPTOP_LEVEL_EXCEPTION_FILTER > ( previousTopLevelExceptionFilter ) ) ! = topLevelExceptionFilter ) {
CATCH_RUNTIME_ERROR ( " Could not restore previous top level exception filter " ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
previousTopLevelExceptionFilter = nullptr ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
} // end namespace Catch
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
# endif // CATCH_CONFIG_WINDOWS_SEH
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
# if defined( CATCH_CONFIG_POSIX_SIGNALS )
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
# include <signal.h>
2020-09-08 15:53:08 +02:00
namespace Catch {
struct SignalDefs {
int id ;
const char * name ;
} ;
static 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 " }
} ;
2022-01-03 23:16:39 +01:00
// 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"
# endif
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 ( ) {
// 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 ) ;
}
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
static void handleSignal ( int sig ) {
2020-09-08 15:53:08 +02:00
char const * name = " <unknown signal> " ;
for ( auto const & def : signalDefs ) {
if ( sig = = def . id ) {
name = def . name ;
break ;
}
}
2022-01-03 23:16:39 +01:00
// 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 ) ;
2020-09-08 15:53:08 +02:00
raise ( sig ) ;
}
FatalConditionHandler : : FatalConditionHandler ( ) {
2022-01-03 23:16:39 +01:00
assert ( ! altStackMem & & " Cannot initialize POSIX signal handler when one already exists " ) ;
if ( altStackSize = = 0 ) {
altStackSize = std : : max ( static_cast < size_t > ( 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 ( ) {
2020-09-08 15:53:08 +02:00
stack_t sigStack ;
sigStack . ss_sp = altStackMem ;
2022-01-03 23:16:39 +01:00
sigStack . ss_size = altStackSize ;
2020-09-08 15:53:08 +02:00
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 ] ) ;
}
}
2022-01-03 23:16:39 +01:00
# if defined(__GNUC__)
# pragma GCC diagnostic pop
# endif
void FatalConditionHandler : : disengage_platform ( ) {
restorePreviousSignalHandlers ( ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
} // end namespace Catch
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
# endif // CATCH_CONFIG_POSIX_SIGNALS
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
# include <cstring>
namespace Catch {
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 ;
}
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 ;
}
} // end namespace Detail
} // end namespace Catch
2020-09-08 15:53:08 +02:00
namespace Catch {
namespace {
void listTests ( IStreamingReporter & reporter , IConfig const & config ) {
auto const & testSpec = config . testSpec ( ) ;
auto matchedTestCases = filterTests ( getAllTestCasesSorted ( config ) , testSpec , config ) ;
2022-01-03 23:16:39 +01:00
reporter . listTests ( matchedTestCases ) ;
2020-09-08 15:53:08 +02:00
}
void listTags ( IStreamingReporter & reporter , IConfig const & config ) {
auto const & testSpec = config . testSpec ( ) ;
std : : vector < TestCaseHandle > matchedTestCases = filterTests ( getAllTestCasesSorted ( config ) , testSpec , config ) ;
2022-01-03 23:16:39 +01:00
std : : map < StringRef , TagInfo , Detail : : CaseInsensitiveLess > tagCounts ;
2020-09-08 15:53:08 +02:00
for ( auto const & testCase : matchedTestCases ) {
for ( auto const & tagName : testCase . getTestCaseInfo ( ) . tags ) {
2022-01-03 23:16:39 +01:00
auto it = tagCounts . find ( tagName . original ) ;
2020-09-08 15:53:08 +02:00
if ( it = = tagCounts . end ( ) )
2022-01-03 23:16:39 +01:00
it = tagCounts . insert ( std : : make_pair ( tagName . original , TagInfo ( ) ) ) . first ;
2020-09-08 15:53:08 +02:00
it - > second . add ( tagName . original ) ;
}
}
std : : vector < TagInfo > infos ; infos . reserve ( tagCounts . size ( ) ) ;
for ( auto & tagc : tagCounts ) {
2022-01-03 23:16:39 +01:00
infos . push_back ( CATCH_MOVE ( tagc . second ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
reporter . listTags ( infos ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void listReporters ( IStreamingReporter & reporter ) {
2020-09-08 15:53:08 +02:00
std : : vector < ReporterDescription > descriptions ;
IReporterRegistry : : FactoryMap const & factories = getRegistryHub ( ) . getReporterRegistry ( ) . getFactories ( ) ;
descriptions . reserve ( factories . size ( ) ) ;
for ( auto const & fac : factories ) {
descriptions . push_back ( { fac . first , fac . second - > getDescription ( ) } ) ;
}
2022-01-03 23:16:39 +01:00
reporter . listReporters ( descriptions ) ;
2020-09-08 15:53:08 +02:00
}
} // 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 ( IStreamingReporter & 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 ;
2022-01-03 23:16:39 +01:00
listReporters ( reporter ) ;
2020-09-08 15:53:08 +02:00
}
return listed ;
}
} // end namespace Catch
namespace Catch {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
LeakDetector leakDetector ;
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
}
2022-01-03 23:16:39 +01:00
// Allow users of amalgamated .cpp file to remove our main and provide their own.
# if !defined(CATCH_AMALGAMATED_CUSTOM_MAIN)
2020-09-08 15:53:08 +02:00
# if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
// Standard C/C++ Win32 Unicode wmain entry point
2022-01-03 23:16:39 +01:00
extern " C " int __cdecl wmain ( int argc , wchar_t * argv [ ] , wchar_t * [ ] ) {
2020-09-08 15:53:08 +02:00
# else
// Standard C/C++ main entry point
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 ;
return Catch : : Session ( ) . run ( argc , argv ) ;
}
2022-01-03 23:16:39 +01:00
# endif // !defined(CATCH_AMALGAMATED_CUSTOM_MAIN
2020-09-08 15:53:08 +02:00
# include <cstdio>
# include <cstring>
# include <sstream>
# if defined(CATCH_CONFIG_NEW_CAPTURE)
# if defined(_MSC_VER)
# include <io.h> //_dup and _dup2
# define dup _dup
# define dup2 _dup2
# define fileno _fileno
# else
# include <unistd.h> // dup and dup2
# endif
# endif
namespace Catch {
RedirectedStream : : RedirectedStream ( std : : ostream & originalStream , std : : ostream & redirectionStream )
: m_originalStream ( originalStream ) ,
m_redirectionStream ( redirectionStream ) ,
m_prevBuf ( m_originalStream . rdbuf ( ) )
{
m_originalStream . rdbuf ( m_redirectionStream . rdbuf ( ) ) ;
}
RedirectedStream : : ~ RedirectedStream ( ) {
m_originalStream . rdbuf ( m_prevBuf ) ;
}
RedirectedStdOut : : RedirectedStdOut ( ) : m_cout ( Catch : : cout ( ) , m_rss . get ( ) ) { }
auto RedirectedStdOut : : str ( ) const - > std : : string { return m_rss . str ( ) ; }
RedirectedStdErr : : RedirectedStdErr ( )
: m_cerr ( Catch : : cerr ( ) , m_rss . get ( ) ) ,
m_clog ( Catch : : clog ( ) , m_rss . get ( ) )
{ }
auto RedirectedStdErr : : str ( ) const - > std : : string { return m_rss . str ( ) ; }
RedirectedStreams : : RedirectedStreams ( std : : string & redirectedCout , std : : string & redirectedCerr )
: m_redirectedCout ( redirectedCout ) ,
m_redirectedCerr ( redirectedCerr )
{ }
RedirectedStreams : : ~ RedirectedStreams ( ) {
m_redirectedCout + = m_redirectedStdOut . str ( ) ;
m_redirectedCerr + = m_redirectedStdErr . str ( ) ;
}
# if defined(CATCH_CONFIG_NEW_CAPTURE)
# if defined(_MSC_VER)
TempFile : : TempFile ( ) {
if ( tmpnam_s ( m_buffer ) ) {
CATCH_RUNTIME_ERROR ( " Could not get a temp filename " ) ;
}
2020-10-08 15:26:30 +02:00
if ( fopen_s ( & m_file , m_buffer , " w+ " ) ) {
2020-09-08 15:53:08 +02:00
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 : : TempFile ( ) {
m_file = std : : tmpfile ( ) ;
if ( ! m_file ) {
CATCH_RUNTIME_ERROR ( " Could not create a temp file. " ) ;
}
}
# endif
TempFile : : ~ 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
}
FILE * TempFile : : getFile ( ) {
return m_file ;
}
std : : string TempFile : : getContents ( ) {
std : : stringstream sstr ;
char buffer [ 100 ] = { } ;
std : : rewind ( m_file ) ;
while ( std : : fgets ( buffer , sizeof ( buffer ) , m_file ) ) {
sstr < < buffer ;
}
return sstr . str ( ) ;
}
OutputRedirect : : OutputRedirect ( std : : string & stdout_dest , std : : string & stderr_dest ) :
m_originalStdout ( dup ( 1 ) ) ,
m_originalStderr ( dup ( 2 ) ) ,
m_stdoutDest ( stdout_dest ) ,
m_stderrDest ( stderr_dest ) {
dup2 ( fileno ( m_stdoutFile . getFile ( ) ) , 1 ) ;
dup2 ( fileno ( m_stderrFile . getFile ( ) ) , 2 ) ;
}
OutputRedirect : : ~ OutputRedirect ( ) {
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 ) ;
dup2 ( m_originalStdout , 1 ) ;
dup2 ( m_originalStderr , 2 ) ;
m_stdoutDest + = m_stdoutFile . getContents ( ) ;
m_stderrDest + = m_stderrFile . getContents ( ) ;
}
# endif // CATCH_CONFIG_NEW_CAPTURE
} // namespace Catch
# if defined(CATCH_CONFIG_NEW_CAPTURE)
# if defined(_MSC_VER)
# undef dup
# undef dup2
# undef fileno
# endif
# endif
namespace Catch {
namespace {
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable:4146) // we negate uint32 during the rotate
# endif
// Safe rotr implementation thanks to John Regehr
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
}
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 < void > ( ( * this ) ( ) ) ;
}
}
SimplePcg32 : : result_type SimplePcg32 : : operator ( ) ( ) {
// prepare the output value
const uint32_t xorshifted = static_cast < uint32_t > ( ( ( m_state > > 18u ) ^ m_state ) > > 27u ) ;
const auto output = rotate_right ( xorshifted , 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 ;
}
}
2022-01-03 23:16:39 +01:00
# include <ctime>
# include <random>
namespace Catch {
std : : uint32_t generateRandomSeed ( GenerateFrom from ) {
switch ( from ) {
case GenerateFrom : : Time :
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 { } ( ) ) ;
default :
CATCH_ERROR ( " Unknown generation method " ) ;
}
}
} // end namespace Catch
2020-09-08 15:53:08 +02:00
namespace Catch {
ReporterRegistry : : ReporterRegistry ( ) {
// Because it is impossible to move out of initializer list,
// we have to add the elements manually
2022-01-03 23:16:39 +01:00
m_factories [ " Automake " ] = Detail : : make_unique < ReporterFactory < AutomakeReporter > > ( ) ;
2020-09-08 15:53:08 +02:00
m_factories [ " compact " ] = Detail : : make_unique < ReporterFactory < CompactReporter > > ( ) ;
m_factories [ " console " ] = Detail : : make_unique < ReporterFactory < ConsoleReporter > > ( ) ;
2022-01-03 23:16:39 +01:00
m_factories [ " JUnit " ] = Detail : : make_unique < ReporterFactory < JunitReporter > > ( ) ;
m_factories [ " SonarQube " ] = Detail : : make_unique < ReporterFactory < SonarQubeReporter > > ( ) ;
m_factories [ " TAP " ] = Detail : : make_unique < ReporterFactory < TAPReporter > > ( ) ;
m_factories [ " TeamCity " ] = Detail : : make_unique < ReporterFactory < TeamCityReporter > > ( ) ;
m_factories [ " XML " ] = Detail : : make_unique < ReporterFactory < XmlReporter > > ( ) ;
2020-09-08 15:53:08 +02:00
}
ReporterRegistry : : ~ ReporterRegistry ( ) = default ;
2022-01-03 23:16:39 +01:00
IStreamingReporterPtr ReporterRegistry : : create ( std : : string const & name , ReporterConfig const & config ) const {
2020-09-08 15:53:08 +02:00
auto it = m_factories . find ( name ) ;
if ( it = = m_factories . end ( ) )
return nullptr ;
2022-01-03 23:16:39 +01:00
return it - > second - > create ( config ) ;
2020-09-08 15:53:08 +02:00
}
void ReporterRegistry : : registerReporter ( std : : string const & name , IReporterFactoryPtr factory ) {
2022-01-03 23:16:39 +01:00
m_factories . emplace ( name , CATCH_MOVE ( factory ) ) ;
2020-09-08 15:53:08 +02:00
}
void ReporterRegistry : : registerListener ( IReporterFactoryPtr factory ) {
2022-01-03 23:16:39 +01:00
m_listeners . push_back ( CATCH_MOVE ( factory ) ) ;
2020-09-08 15:53:08 +02:00
}
IReporterRegistry : : FactoryMap const & ReporterRegistry : : getFactories ( ) const {
return m_factories ;
}
IReporterRegistry : : Listeners const & ReporterRegistry : : getListeners ( ) const {
return m_listeners ;
}
}
namespace Catch {
bool isOk ( ResultWas : : OfType resultType ) {
return ( resultType & ResultWas : : FailureBit ) = = 0 ;
}
bool isJustInfo ( int flags ) {
return flags = = ResultWas : : Info ;
}
ResultDisposition : : Flags operator | ( ResultDisposition : : Flags lhs , ResultDisposition : : Flags rhs ) {
return static_cast < ResultDisposition : : Flags > ( static_cast < int > ( lhs ) | static_cast < int > ( rhs ) ) ;
}
bool shouldContinueOnFailure ( int flags ) { return ( flags & ResultDisposition : : ContinueOnFailure ) ! = 0 ; }
bool shouldSuppressFailure ( int flags ) { return ( flags & ResultDisposition : : SuppressFail ) ! = 0 ; }
} // end namespace Catch
# include <cassert>
# include <algorithm>
namespace Catch {
namespace Generators {
struct GeneratorTracker : TestCaseTracking : : TrackerBase , IGeneratorTracker {
GeneratorBasePtr m_generator ;
GeneratorTracker ( TestCaseTracking : : NameAndLocation const & nameAndLocation , TrackerContext & ctx , ITracker * parent )
: TrackerBase ( nameAndLocation , ctx , parent )
{ }
~ GeneratorTracker ( ) ;
static GeneratorTracker & acquire ( TrackerContext & ctx , TestCaseTracking : : NameAndLocation const & nameAndLocation ) {
2022-01-03 23:16:39 +01:00
GeneratorTracker * tracker ;
2020-09-08 15:53:08 +02:00
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.
2022-01-03 23:16:39 +01:00
if ( currentTracker . nameAndLocation ( ) = = nameAndLocation ) {
auto thisTracker =
currentTracker . parent ( ) - > findChild ( nameAndLocation ) ;
assert ( thisTracker ) ;
assert ( thisTracker - > isGeneratorTracker ( ) ) ;
tracker = static_cast < GeneratorTracker * > ( thisTracker ) ;
} else if ( ITracker * childTracker =
currentTracker . findChild ( nameAndLocation ) ) {
2020-09-08 15:53:08 +02:00
assert ( childTracker ) ;
assert ( childTracker - > isGeneratorTracker ( ) ) ;
2022-01-03 23:16:39 +01:00
tracker = static_cast < GeneratorTracker * > ( childTracker ) ;
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
auto newTracker =
Catch : : Detail : : make_unique < GeneratorTracker > (
nameAndLocation , ctx , & currentTracker ) ;
tracker = newTracker . get ( ) ;
currentTracker . addChild ( CATCH_MOVE ( newTracker ) ) ;
2020-09-08 15:53:08 +02:00
}
if ( ! tracker - > isComplete ( ) ) {
tracker - > open ( ) ;
}
return * tracker ;
}
// TrackerBase interface
bool isGeneratorTracker ( ) const override { return true ; }
auto hasGenerator ( ) const - > bool override {
return ! ! m_generator ;
}
void close ( ) override {
TrackerBase : : close ( ) ;
// If a generator has a child (it is followed by a section)
// and none of its children have started, then we must wait
// until later to start consuming its values.
// This catches cases where `GENERATE` is placed between two
// `SECTION`s.
// **The check for m_children.empty cannot be removed**.
// doing so would break `GENERATE` _not_ followed by `SECTION`s.
2022-01-03 23:16:39 +01:00
const bool should_wait_for_child = [ & ] ( ) {
// No children -> nobody to wait for
if ( m_children . empty ( ) ) {
return false ;
}
// If at least one child started executing, don't wait
if ( std : : find_if (
m_children . begin ( ) ,
m_children . end ( ) ,
[ ] ( TestCaseTracking : : ITrackerPtr const & tracker ) {
return tracker - > hasStarted ( ) ;
} ) ! = m_children . end ( ) ) {
return false ;
}
// No children have started. We need to check if they _can_
// start, and thus we should wait for them, or they cannot
// start (due to filters), and we shouldn't wait for them
ITracker * parent = m_parent ;
// This is safe: there is always at least one section
// tracker in a test case tracking tree
while ( ! parent - > isSectionTracker ( ) ) {
parent = parent - > parent ( ) ;
}
assert ( parent & &
" Missing root (test case) level section " ) ;
auto const & parentSection =
static_cast < SectionTracker const & > ( * parent ) ;
auto const & filters = parentSection . getFilters ( ) ;
// No filters -> no restrictions on running sections
if ( filters . empty ( ) ) {
return true ;
}
for ( auto const & child : m_children ) {
if ( child - > isSectionTracker ( ) & &
std : : find (
filters . begin ( ) ,
filters . end ( ) ,
static_cast < SectionTracker const & > ( * child )
. trimmedName ( ) ) ! = filters . end ( ) ) {
return true ;
}
}
return false ;
} ( ) ;
2020-09-08 15:53:08 +02:00
// 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.
if ( should_wait_for_child | |
( m_runState = = CompletedSuccessfully & &
m_generator - > next ( ) ) ) {
m_children . clear ( ) ;
m_runState = Executing ;
}
}
// IGeneratorTracker interface
auto getGenerator ( ) const - > GeneratorBasePtr const & override {
return m_generator ;
}
void setGenerator ( GeneratorBasePtr & & generator ) override {
2022-01-03 23:16:39 +01:00
m_generator = CATCH_MOVE ( generator ) ;
2020-09-08 15:53:08 +02:00
}
} ;
2022-01-03 23:16:39 +01:00
GeneratorTracker : : ~ GeneratorTracker ( ) = default ;
2020-09-08 15:53:08 +02:00
}
RunContext : : RunContext ( IConfig const * _config , IStreamingReporterPtr & & reporter )
: m_runInfo ( _config - > name ( ) ) ,
m_context ( getCurrentMutableContext ( ) ) ,
m_config ( _config ) ,
2022-01-03 23:16:39 +01:00
m_reporter ( CATCH_MOVE ( reporter ) ) ,
2020-09-08 15:53:08 +02:00
m_lastAssertionInfo { StringRef ( ) , SourceLineInfo ( " " , 0 ) , StringRef ( ) , ResultDisposition : : Normal } ,
m_includeSuccessfulResults ( m_config - > includeSuccessfulResults ( ) | | m_reporter - > getPreferences ( ) . shouldReportAllAssertions )
{
m_context . setResultCapture ( this ) ;
m_reporter - > testRunStarting ( m_runInfo ) ;
}
RunContext : : ~ RunContext ( ) {
m_reporter - > testRunEnded ( TestRunStats ( m_runInfo , m_totals , aborting ( ) ) ) ;
}
Totals RunContext : : runTest ( TestCaseHandle const & testCase ) {
2022-01-03 23:16:39 +01:00
const Totals prevTotals = m_totals ;
2020-09-08 15:53:08 +02:00
std : : string redirectedCout ;
std : : string redirectedCerr ;
auto const & testInfo = testCase . getTestCaseInfo ( ) ;
m_reporter - > testCaseStarting ( testInfo ) ;
m_activeTestCase = & testCase ;
ITracker & rootTracker = m_trackerContext . startRun ( ) ;
assert ( rootTracker . isSectionTracker ( ) ) ;
static_cast < SectionTracker & > ( rootTracker ) . addInitialFilters ( m_config - > getSectionsToRun ( ) ) ;
2022-01-03 23:16:39 +01:00
uint64_t testRuns = 0 ;
2020-09-08 15:53:08 +02:00
do {
m_trackerContext . startCycle ( ) ;
m_testCaseTracker = & SectionTracker : : acquire ( m_trackerContext , TestCaseTracking : : NameAndLocation ( testInfo . name , testInfo . lineInfo ) ) ;
2022-01-03 23:16:39 +01:00
m_reporter - > testCasePartialStarting ( testInfo , testRuns ) ;
const auto beforeRunTotals = m_totals ;
std : : string oneRunCout , oneRunCerr ;
runCurrentTest ( oneRunCout , oneRunCerr ) ;
redirectedCout + = oneRunCout ;
redirectedCerr + = oneRunCerr ;
const auto singleRunTotals = m_totals . delta ( beforeRunTotals ) ;
auto statsForOneRun = TestCaseStats ( testInfo , singleRunTotals , oneRunCout , oneRunCerr , aborting ( ) ) ;
m_reporter - > testCasePartialEnded ( statsForOneRun , testRuns ) ;
+ + testRuns ;
2020-09-08 15:53:08 +02:00
} 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 ;
m_reporter - > testCaseEnded ( TestCaseStats ( testInfo ,
deltaTotals ,
redirectedCout ,
redirectedCerr ,
aborting ( ) ) ) ;
m_activeTestCase = nullptr ;
m_testCaseTracker = nullptr ;
return deltaTotals ;
}
void RunContext : : assertionEnded ( AssertionResult const & result ) {
if ( result . getResultType ( ) = = ResultWas : : Ok ) {
m_totals . assertions . passed + + ;
m_lastAssertionPassed = true ;
2022-01-03 23:16:39 +01:00
} else if ( ! result . succeeded ( ) ) {
2020-09-08 15:53:08 +02:00
m_lastAssertionPassed = false ;
2022-01-03 23:16:39 +01:00
if ( result . isOk ( ) ) {
}
else if ( m_activeTestCase - > getTestCaseInfo ( ) . okToFail ( ) )
2020-09-08 15:53:08 +02:00
m_totals . assertions . failedButOk + + ;
else
m_totals . assertions . failed + + ;
}
else {
m_lastAssertionPassed = true ;
}
2022-01-03 23:16:39 +01:00
m_reporter - > assertionEnded ( AssertionStats ( result , m_messages , m_totals ) ) ;
2020-09-08 15:53:08 +02:00
if ( result . getResultType ( ) ! = ResultWas : : Warning )
m_messageScopes . clear ( ) ;
// Reset working state
resetAssertionInfo ( ) ;
m_lastResult = result ;
}
void RunContext : : resetAssertionInfo ( ) {
m_lastAssertionInfo . macroName = StringRef ( ) ;
m_lastAssertionInfo . capturedExpression = " {Unknown expression after the reported line} " _sr ;
}
bool RunContext : : sectionStarted ( SectionInfo const & sectionInfo , Counts & assertions ) {
ITracker & sectionTracker = SectionTracker : : acquire ( m_trackerContext , TestCaseTracking : : NameAndLocation ( sectionInfo . name , sectionInfo . lineInfo ) ) ;
if ( ! sectionTracker . isOpen ( ) )
return false ;
m_activeSections . push_back ( & sectionTracker ) ;
m_lastAssertionInfo . lineInfo = sectionInfo . lineInfo ;
m_reporter - > sectionStarting ( sectionInfo ) ;
assertions = m_totals . assertions ;
return true ;
}
auto RunContext : : acquireGeneratorTracker ( StringRef generatorName , SourceLineInfo const & lineInfo ) - > IGeneratorTracker & {
using namespace Generators ;
GeneratorTracker & tracker = GeneratorTracker : : acquire ( m_trackerContext ,
TestCaseTracking : : NameAndLocation ( static_cast < std : : string > ( generatorName ) , lineInfo ) ) ;
m_lastAssertionInfo . lineInfo = lineInfo ;
return tracker ;
}
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 . assertions . failed + + ;
assertions . failed + + ;
return true ;
}
void RunContext : : sectionEnded ( SectionEndInfo const & endInfo ) {
Counts assertions = m_totals . assertions - endInfo . prevAssertions ;
bool missingAssertions = testForMissingAssertions ( assertions ) ;
if ( ! m_activeSections . empty ( ) ) {
m_activeSections . back ( ) - > close ( ) ;
m_activeSections . pop_back ( ) ;
}
m_reporter - > sectionEnded ( SectionStats ( endInfo . sectionInfo , assertions , endInfo . durationInSeconds , missingAssertions ) ) ;
m_messages . clear ( ) ;
m_messageScopes . clear ( ) ;
}
void RunContext : : sectionEndedEarly ( SectionEndInfo const & endInfo ) {
if ( m_unfinishedSections . empty ( ) )
m_activeSections . back ( ) - > fail ( ) ;
else
m_activeSections . back ( ) - > close ( ) ;
m_activeSections . pop_back ( ) ;
m_unfinishedSections . push_back ( endInfo ) ;
}
2022-01-03 23:16:39 +01:00
void RunContext : : benchmarkPreparing ( StringRef name ) {
2020-09-08 15:53:08 +02:00
m_reporter - > benchmarkPreparing ( name ) ;
}
void RunContext : : benchmarkStarting ( BenchmarkInfo const & info ) {
m_reporter - > benchmarkStarting ( info ) ;
}
void RunContext : : benchmarkEnded ( BenchmarkStats < > const & stats ) {
m_reporter - > benchmarkEnded ( stats ) ;
}
2022-01-03 23:16:39 +01:00
void RunContext : : benchmarkFailed ( StringRef error ) {
m_reporter - > benchmarkFailed ( error ) ;
2020-09-08 15:53:08 +02:00
}
void RunContext : : pushScopedMessage ( MessageInfo const & message ) {
m_messages . push_back ( message ) ;
}
void RunContext : : popScopedMessage ( MessageInfo const & message ) {
m_messages . erase ( std : : remove ( m_messages . begin ( ) , m_messages . end ( ) , message ) , m_messages . end ( ) ) ;
}
void RunContext : : emplaceUnscopedMessage ( MessageBuilder const & builder ) {
m_messageScopes . emplace_back ( builder ) ;
}
std : : string RunContext : : getCurrentTestName ( ) const {
return m_activeTestCase
? m_activeTestCase - > getTestCaseInfo ( ) . name
: std : : string ( ) ;
}
const AssertionResult * RunContext : : getLastResult ( ) const {
return & ( * m_lastResult ) ;
}
void RunContext : : exceptionEarlyReported ( ) {
m_shouldReportUnexpected = false ;
}
void RunContext : : handleFatalErrorCondition ( StringRef message ) {
// First notify reporter that bad things happened
m_reporter - > fatalErrorEncountered ( message ) ;
// 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 < std : : string > ( message ) ;
AssertionResult result ( m_lastAssertionInfo , tempResult ) ;
assertionEnded ( result ) ;
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 ) ;
Counts assertions ;
assertions . failed = 1 ;
SectionStats testCaseSectionStats ( testCaseSection , assertions , 0 , false ) ;
m_reporter - > sectionEnded ( testCaseSectionStats ) ;
auto const & testInfo = m_activeTestCase - > getTestCaseInfo ( ) ;
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 + + ;
m_reporter - > testRunEnded ( TestRunStats ( m_runInfo , m_totals , false ) ) ;
}
bool RunContext : : lastAssertionPassed ( ) {
return m_lastAssertionPassed ;
}
void RunContext : : assertionPassed ( ) {
m_lastAssertionPassed = true ;
+ + m_totals . assertions . passed ;
resetAssertionInfo ( ) ;
m_messageScopes . clear ( ) ;
}
bool RunContext : : aborting ( ) const {
return m_totals . assertions . failed > = static_cast < std : : size_t > ( m_config - > abortAfter ( ) ) ;
}
void RunContext : : runCurrentTest ( std : : string & redirectedCout , std : : string & redirectedCerr ) {
auto const & testCaseInfo = m_activeTestCase - > getTestCaseInfo ( ) ;
SectionInfo testCaseSection ( testCaseInfo . lineInfo , testCaseInfo . name ) ;
m_reporter - > sectionStarting ( testCaseSection ) ;
Counts prevAssertions = m_totals . assertions ;
double duration = 0 ;
m_shouldReportUnexpected = true ;
m_lastAssertionInfo = { " TEST_CASE " _sr , testCaseInfo . lineInfo , StringRef ( ) , ResultDisposition : : Normal } ;
seedRng ( * m_config ) ;
Timer timer ;
CATCH_TRY {
if ( m_reporter - > getPreferences ( ) . shouldRedirectStdOut ) {
# if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
RedirectedStreams redirectedStreams ( redirectedCout , redirectedCerr ) ;
timer . start ( ) ;
invokeActiveTestCase ( ) ;
# else
OutputRedirect r ( redirectedCout , redirectedCerr ) ;
timer . start ( ) ;
invokeActiveTestCase ( ) ;
# endif
} else {
timer . start ( ) ;
invokeActiveTestCase ( ) ;
}
duration = timer . getElapsedSeconds ( ) ;
} CATCH_CATCH_ANON ( TestFailureException & ) {
// This just means the test was aborted due to failure
} 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 ( m_lastAssertionInfo , translateActiveException ( ) , dummyReaction ) ;
}
}
Counts assertions = m_totals . assertions - prevAssertions ;
bool missingAssertions = testForMissingAssertions ( assertions ) ;
m_testCaseTracker - > close ( ) ;
handleUnfinishedSections ( ) ;
m_messages . clear ( ) ;
m_messageScopes . clear ( ) ;
SectionStats testCaseSectionStats ( testCaseSection , assertions , duration , missingAssertions ) ;
m_reporter - > sectionEnded ( testCaseSectionStats ) ;
}
void RunContext : : invokeActiveTestCase ( ) {
2022-01-03 23:16:39 +01:00
// We need to engage a handler for signals/structured exceptions
2020-09-08 15:53:08 +02:00
// before running the tests themselves, or the binary can crash
// without failed test being reported.
2022-01-03 23:16:39 +01:00
FatalConditionHandlerGuard _ ( & m_fatalConditionhandler ) ;
2020-09-08 15:53:08 +02:00
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 ( * it ) ;
m_unfinishedSections . clear ( ) ;
}
void RunContext : : handleExpr (
AssertionInfo const & info ,
ITransientExpression const & expr ,
AssertionReaction & reaction
) {
m_reporter - > assertionStarting ( info ) ;
bool negated = isFalseTest ( info . resultDisposition ) ;
bool result = expr . getResult ( ) ! = negated ;
if ( result ) {
if ( ! m_includeSuccessfulResults ) {
assertionPassed ( ) ;
}
else {
reportExpr ( info , ResultWas : : Ok , & expr , negated ) ;
}
}
else {
reportExpr ( info , ResultWas : : ExpressionFailed , & expr , negated ) ;
populateReaction ( reaction ) ;
}
}
void RunContext : : reportExpr (
AssertionInfo const & info ,
ResultWas : : OfType resultType ,
ITransientExpression const * expr ,
bool negated ) {
m_lastAssertionInfo = info ;
AssertionResultData data ( resultType , LazyExpression ( negated ) ) ;
AssertionResult assertionResult { info , data } ;
assertionResult . m_resultData . lazyExpression . m_transientExpression = expr ;
assertionEnded ( assertionResult ) ;
}
void RunContext : : handleMessage (
AssertionInfo const & info ,
ResultWas : : OfType resultType ,
2022-01-03 23:16:39 +01:00
StringRef message ,
2020-09-08 15:53:08 +02:00
AssertionReaction & reaction
) {
m_reporter - > assertionStarting ( info ) ;
m_lastAssertionInfo = info ;
AssertionResultData data ( resultType , LazyExpression ( false ) ) ;
data . message = static_cast < std : : string > ( message ) ;
AssertionResult assertionResult { m_lastAssertionInfo , data } ;
assertionEnded ( assertionResult ) ;
if ( ! assertionResult . isOk ( ) )
populateReaction ( reaction ) ;
}
void RunContext : : handleUnexpectedExceptionNotThrown (
AssertionInfo const & info ,
AssertionReaction & reaction
) {
handleNonExpr ( info , Catch : : ResultWas : : DidntThrowException , reaction ) ;
}
void RunContext : : handleUnexpectedInflightException (
AssertionInfo const & info ,
std : : string const & message ,
AssertionReaction & reaction
) {
m_lastAssertionInfo = info ;
AssertionResultData data ( ResultWas : : ThrewException , LazyExpression ( false ) ) ;
data . message = message ;
AssertionResult assertionResult { info , data } ;
assertionEnded ( assertionResult ) ;
populateReaction ( reaction ) ;
}
void RunContext : : populateReaction ( AssertionReaction & reaction ) {
reaction . shouldDebugBreak = m_config - > shouldDebugBreak ( ) ;
reaction . shouldThrow = aborting ( ) | | ( m_lastAssertionInfo . resultDisposition & ResultDisposition : : Normal ) ;
}
void RunContext : : handleIncomplete (
AssertionInfo const & info
) {
m_lastAssertionInfo = info ;
AssertionResultData data ( ResultWas : : ThrewException , LazyExpression ( false ) ) ;
data . message = " Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE " ;
AssertionResult assertionResult { info , data } ;
assertionEnded ( assertionResult ) ;
}
void RunContext : : handleNonExpr (
AssertionInfo const & info ,
ResultWas : : OfType resultType ,
AssertionReaction & reaction
) {
m_lastAssertionInfo = info ;
AssertionResultData data ( resultType , LazyExpression ( false ) ) ;
AssertionResult assertionResult { info , data } ;
assertionEnded ( assertionResult ) ;
if ( ! assertionResult . isOk ( ) )
populateReaction ( reaction ) ;
}
IResultCapture & getResultCapture ( ) {
if ( auto * capture = getCurrentContext ( ) . getResultCapture ( ) )
return * capture ;
else
CATCH_INTERNAL_ERROR ( " No result capture instance " ) ;
}
void seedRng ( IConfig const & config ) {
if ( config . rngSeed ( ) ! = 0 ) {
std : : srand ( config . rngSeed ( ) ) ;
rng ( ) . seed ( config . rngSeed ( ) ) ;
}
}
unsigned int rngSeed ( ) {
return getCurrentContext ( ) . getConfig ( ) - > rngSeed ( ) ;
}
}
namespace Catch {
Section : : Section ( SectionInfo & & info ) :
2022-01-03 23:16:39 +01:00
m_info ( CATCH_MOVE ( info ) ) ,
2020-09-08 15:53:08 +02:00
m_sectionIncluded (
getResultCapture ( ) . sectionStarted ( m_info , 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 ( ) {
if ( m_sectionIncluded ) {
SectionEndInfo endInfo { m_info , m_assertions , m_timer . getElapsedSeconds ( ) } ;
if ( uncaught_exceptions ( ) )
getResultCapture ( ) . sectionEndedEarly ( endInfo ) ;
else
getResultCapture ( ) . sectionEnded ( endInfo ) ;
}
}
// This indicates whether the section should be executed or not
Section : : operator bool ( ) const {
return m_sectionIncluded ;
}
} // end namespace Catch
# include <vector>
namespace Catch {
namespace {
static auto getSingletons ( ) - > std : : vector < ISingleton * > * & {
static std : : vector < ISingleton * > * g_singletons = nullptr ;
if ( ! g_singletons )
g_singletons = new std : : vector < ISingleton * > ( ) ;
return g_singletons ;
}
}
2022-01-03 23:16:39 +01:00
ISingleton : : ~ ISingleton ( ) = default ;
2020-09-08 15:53:08 +02:00
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
2022-01-03 23:16:39 +01:00
# include <cstring>
# include <ostream>
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 ) ) ;
}
std : : ostream & operator < < ( std : : ostream & os , SourceLineInfo const & info ) {
# ifndef __GNUG__
os < < info . file < < ' ( ' < < info . line < < ' ) ' ;
# else
os < < info . file < < ' : ' < < info . line ;
# endif
return os ;
}
} // end namespace Catch
2020-09-08 15:53:08 +02:00
# include <cstdio>
# include <iostream>
# include <fstream>
# include <sstream>
# include <vector>
namespace Catch {
Catch : : IStream : : ~ IStream ( ) = default ;
namespace Detail {
namespace {
template < typename WriterF , std : : size_t bufferSize = 256 >
class StreamBufImpl : public std : : streambuf {
char data [ bufferSize ] ;
WriterF m_writer ;
public :
StreamBufImpl ( ) {
setp ( data , data + sizeof ( data ) ) ;
}
~ StreamBufImpl ( ) noexcept {
StreamBufImpl : : sync ( ) ;
}
private :
int overflow ( int c ) override {
sync ( ) ;
if ( c ! = EOF ) {
if ( pbase ( ) = = epptr ( ) )
m_writer ( std : : string ( 1 , static_cast < char > ( c ) ) ) ;
else
sputc ( static_cast < char > ( c ) ) ;
}
return 0 ;
}
int sync ( ) override {
if ( pbase ( ) ! = pptr ( ) ) {
m_writer ( std : : string ( pbase ( ) , static_cast < std : : string : : size_type > ( pptr ( ) - pbase ( ) ) ) ) ;
setp ( pbase ( ) , epptr ( ) ) ;
}
return 0 ;
}
} ;
///////////////////////////////////////////////////////////////////////////
struct OutputDebugWriter {
2022-01-03 23:16:39 +01:00
void operator ( ) ( std : : string const & str ) {
if ( ! str . empty ( ) ) {
writeToDebugConsole ( str ) ;
}
2020-09-08 15:53:08 +02:00
}
} ;
///////////////////////////////////////////////////////////////////////////
class FileStream : public IStream {
mutable std : : ofstream m_ofs ;
public :
2022-01-03 23:16:39 +01:00
FileStream ( std : : string const & filename ) {
2020-09-08 15:53:08 +02:00
m_ofs . open ( filename . c_str ( ) ) ;
2022-01-03 23:16:39 +01:00
CATCH_ENFORCE ( ! m_ofs . fail ( ) , " Unable to open file: ' " < < filename < < ' \' ' ) ;
2020-09-08 15:53:08 +02:00
}
~ FileStream ( ) override = default ;
public : // IStream
std : : ostream & stream ( ) const override {
return m_ofs ;
}
} ;
///////////////////////////////////////////////////////////////////////////
class CoutStream : public IStream {
mutable 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 ( ) ) { }
~ CoutStream ( ) override = default ;
public : // IStream
std : : ostream & stream ( ) const override { return m_os ; }
} ;
///////////////////////////////////////////////////////////////////////////
class DebugOutStream : public IStream {
Detail : : unique_ptr < StreamBufImpl < OutputDebugWriter > > m_streamBuf ;
mutable std : : ostream m_os ;
public :
DebugOutStream ( )
: m_streamBuf ( Detail : : make_unique < StreamBufImpl < OutputDebugWriter > > ( ) ) ,
m_os ( m_streamBuf . get ( ) )
{ }
~ DebugOutStream ( ) override = default ;
public : // IStream
std : : ostream & stream ( ) const override { return m_os ; }
} ;
} // unnamed namespace
} // namespace Detail
///////////////////////////////////////////////////////////////////////////
2022-01-03 23:16:39 +01:00
auto makeStream ( std : : string const & filename ) - > Detail : : unique_ptr < IStream const > {
if ( filename . empty ( ) | | filename = = " - " ) {
return Detail : : make_unique < Detail : : CoutStream > ( ) ;
}
2020-09-08 15:53:08 +02:00
else if ( filename [ 0 ] = = ' % ' ) {
if ( filename = = " %debug " )
2022-01-03 23:16:39 +01:00
return Detail : : make_unique < Detail : : DebugOutStream > ( ) ;
2020-09-08 15:53:08 +02:00
else
2022-01-03 23:16:39 +01:00
CATCH_ERROR ( " Unrecognised stream: ' " < < filename < < ' \' ' ) ;
2020-09-08 15:53:08 +02:00
}
else
2022-01-03 23:16:39 +01:00
return Detail : : make_unique < Detail : : FileStream > ( filename ) ;
2020-09-08 15:53:08 +02:00
}
// This class encapsulates the idea of a pool of ostringstreams that can be reused.
struct StringStreams {
std : : vector < Detail : : unique_ptr < std : : ostringstream > > m_streams ;
std : : vector < std : : size_t > m_unused ;
std : : ostringstream m_referenceStream ; // Used for copy state/ flags from
auto add ( ) - > std : : size_t {
if ( m_unused . empty ( ) ) {
2022-01-03 23:16:39 +01:00
m_streams . push_back ( Detail : : make_unique < std : : ostringstream > ( ) ) ;
2020-09-08 15:53:08 +02:00
return m_streams . size ( ) - 1 ;
}
else {
auto index = m_unused . back ( ) ;
m_unused . pop_back ( ) ;
return index ;
}
}
void release ( std : : size_t index ) {
m_streams [ index ] - > copyfmt ( m_referenceStream ) ; // Restore initial flags and other state
m_unused . push_back ( index ) ;
}
} ;
ReusableStringStream : : ReusableStringStream ( )
: m_index ( Singleton < StringStreams > : : getMutable ( ) . add ( ) ) ,
m_oss ( Singleton < StringStreams > : : getMutable ( ) . m_streams [ m_index ] . get ( ) )
{ }
ReusableStringStream : : ~ ReusableStringStream ( ) {
static_cast < std : : ostringstream * > ( m_oss ) - > str ( " " ) ;
m_oss - > clear ( ) ;
Singleton < StringStreams > : : getMutable ( ) . release ( m_index ) ;
}
std : : string ReusableStringStream : : str ( ) const {
return static_cast < std : : ostringstream * > ( m_oss ) - > str ( ) ;
}
void ReusableStringStream : : str ( std : : string const & str ) {
static_cast < std : : ostringstream * > ( m_oss ) - > str ( str ) ;
}
///////////////////////////////////////////////////////////////////////////
# ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
std : : ostream & cout ( ) { return std : : cout ; }
std : : ostream & cerr ( ) { return std : : cerr ; }
std : : ostream & clog ( ) { return std : : clog ; }
# endif
}
# include <algorithm>
# include <ostream>
# include <cstring>
# include <cctype>
# include <vector>
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 ( ) ) ;
}
2022-01-03 23:16:39 +01:00
bool startsWith ( StringRef s , char prefix ) {
2020-09-08 15:53:08 +02:00
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 ) {
2022-01-03 23:16:39 +01:00
std : : transform ( s . begin ( ) , s . end ( ) , s . begin ( ) , [ ] ( char c ) {
return toLower ( c ) ;
} ) ;
2020-09-08 15:53:08 +02:00
}
std : : string toLower ( std : : string const & s ) {
std : : string lc = s ;
toLowerInPlace ( lc ) ;
return lc ;
}
2022-01-03 23:16:39 +01:00
char toLower ( char c ) {
return static_cast < char > ( std : : tolower ( static_cast < unsigned char > ( c ) ) ) ;
}
2020-09-08 15:53:08 +02:00
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 ) {
bool replaced = false ;
std : : size_t i = str . find ( replaceThis ) ;
while ( i ! = std : : string : : npos ) {
replaced = true ;
str = str . substr ( 0 , i ) + withThis + str . substr ( i + replaceThis . size ( ) ) ;
if ( i < str . size ( ) - withThis . size ( ) )
i = str . find ( replaceThis , i + withThis . size ( ) ) ;
else
i = std : : string : : npos ;
}
return replaced ;
}
std : : vector < StringRef > splitStringRef ( StringRef str , char delimiter ) {
std : : vector < StringRef > 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 <algorithm>
# include <ostream>
# include <cstring>
# include <cstdint>
namespace Catch {
StringRef : : StringRef ( char const * rawChars ) noexcept
: StringRef ( rawChars , static_cast < StringRef : : size_type > ( std : : strlen ( rawChars ) ) )
{ }
2022-01-03 23:16:39 +01:00
auto StringRef : : operator = = ( StringRef other ) const noexcept - > bool {
2020-09-08 15:53:08 +02:00
return m_size = = other . m_size
& & ( std : : memcmp ( m_start , other . m_start , m_size ) = = 0 ) ;
}
2022-01-03 23:16:39 +01:00
bool StringRef : : operator < ( StringRef rhs ) const noexcept {
2020-09-08 15:53:08 +02:00
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 ;
}
2022-01-03 23:16:39 +01:00
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 < std : : streamsize > ( str . size ( ) ) ) ;
2020-09-08 15:53:08 +02:00
}
std : : string operator + ( StringRef lhs , StringRef rhs ) {
std : : string ret ;
ret . reserve ( lhs . size ( ) + rhs . size ( ) ) ;
ret + = lhs ;
ret + = rhs ;
return ret ;
}
2022-01-03 23:16:39 +01:00
auto operator + = ( std : : string & lhs , StringRef rhs ) - > std : : string & {
2020-09-08 15:53:08 +02:00
lhs . append ( rhs . data ( ) , rhs . size ( ) ) ;
return lhs ;
}
} // namespace Catch
namespace Catch {
TagAliasRegistry : : ~ TagAliasRegistry ( ) { }
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 & 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 ) ;
CATCH_ENFORCE ( m_registry . insert ( std : : make_pair ( alias , TagAlias ( tag , lineInfo ) ) ) . second ,
" error: tag alias, ' " < < alias < < " ' already registered. \n "
< < " \t First seen at: " < < find ( alias ) - > lineInfo < < " \n "
< < " \t Redefined at: " < < lineInfo ) ;
}
2022-01-03 23:16:39 +01:00
ITagAliasRegistry : : ~ ITagAliasRegistry ( ) = default ;
2020-09-08 15:53:08 +02:00
ITagAliasRegistry const & ITagAliasRegistry : : get ( ) {
return getRegistryHub ( ) . getTagAliasRegistry ( ) ;
}
} // end namespace Catch
# include <algorithm>
# include <set>
namespace Catch {
namespace {
2022-01-03 23:16:39 +01:00
struct TestHasher {
using hash_t = uint64_t ;
explicit TestHasher ( hash_t hashSuffix ) :
m_hashSuffix ( hashSuffix ) { }
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
uint64_t m_hashSuffix ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
uint32_t operator ( ) ( TestCaseInfo const & t ) const {
// FNV-1a hash with multiplication fold.
const hash_t prime = 1099511628211u ;
hash_t hash = 14695981039346656037u ;
2020-09-08 15:53:08 +02:00
for ( const char c : t . name ) {
hash ^ = c ;
hash * = prime ;
}
2022-01-03 23:16:39 +01:00
hash ^ = m_hashSuffix ;
hash * = prime ;
const uint32_t low { static_cast < uint32_t > ( hash ) } ;
const uint32_t high { static_cast < uint32_t > ( hash > > 32 ) } ;
return low * high ;
2020-09-08 15:53:08 +02:00
}
} ;
} // end anonymous namespace
std : : vector < TestCaseHandle > sortTests ( IConfig const & config , std : : vector < TestCaseHandle > const & unsortedTestCases ) {
switch ( config . runOrder ( ) ) {
case TestRunOrder : : Declared :
return unsortedTestCases ;
case TestRunOrder : : LexicographicallySorted : {
std : : vector < TestCaseHandle > sorted = unsortedTestCases ;
2022-01-03 23:16:39 +01:00
std : : sort (
sorted . begin ( ) ,
sorted . end ( ) ,
[ ] ( TestCaseHandle const & lhs , TestCaseHandle const & rhs ) {
return lhs . getTestCaseInfo ( ) < rhs . getTestCaseInfo ( ) ;
}
) ;
2020-09-08 15:53:08 +02:00
return sorted ;
}
case TestRunOrder : : Randomized : {
seedRng ( config ) ;
2022-01-03 23:16:39 +01:00
using TestWithHash = std : : pair < TestHasher : : hash_t , TestCaseHandle > ;
TestHasher h { config . rngSeed ( ) } ;
std : : vector < TestWithHash > indexed_tests ;
2020-09-08 15:53:08 +02:00
indexed_tests . reserve ( unsortedTestCases . size ( ) ) ;
for ( auto const & handle : unsortedTestCases ) {
indexed_tests . emplace_back ( h ( handle . getTestCaseInfo ( ) ) , handle ) ;
}
2022-01-03 23:16:39 +01:00
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 ;
} ) ;
2020-09-08 15:53:08 +02:00
std : : vector < TestCaseHandle > 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! " ) ;
}
bool isThrowSafe ( TestCaseHandle const & testCase , IConfig const & config ) {
return ! testCase . getTestCaseInfo ( ) . throws ( ) | | config . allowThrows ( ) ;
}
bool matchTest ( TestCaseHandle const & testCase , TestSpec const & testSpec , IConfig const & config ) {
return testSpec . matches ( testCase . getTestCaseInfo ( ) ) & & isThrowSafe ( testCase , config ) ;
}
2022-01-03 23:16:39 +01:00
void
enforceNoDuplicateTestCases ( std : : vector < TestCaseHandle > const & tests ) {
auto testInfoCmp = [ ] ( TestCaseInfo const * lhs ,
TestCaseInfo const * rhs ) {
return * lhs < * rhs ;
} ;
std : : set < TestCaseInfo const * , decltype ( testInfoCmp ) > 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 "
< < " \t First seen at " < < ( * prev . first ) - > lineInfo < < " \n "
< < " \t Redefined at " < < infoPtr - > lineInfo ) ;
2020-09-08 15:53:08 +02:00
}
}
std : : vector < TestCaseHandle > filterTests ( std : : vector < TestCaseHandle > const & testCases , TestSpec const & testSpec , IConfig const & config ) {
std : : vector < TestCaseHandle > 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 ) ;
}
}
2022-01-03 23:16:39 +01:00
return createShard ( filtered , config . shardCount ( ) , config . shardIndex ( ) ) ;
2020-09-08 15:53:08 +02:00
}
std : : vector < TestCaseHandle > const & getAllTestCasesSorted ( IConfig const & config ) {
return getRegistryHub ( ) . getTestCaseRegistry ( ) . getAllTestsSorted ( config ) ;
}
void TestRegistry : : registerTest ( Detail : : unique_ptr < TestCaseInfo > testInfo , Detail : : unique_ptr < ITestInvoker > testInvoker ) {
m_handles . emplace_back ( testInfo . get ( ) , testInvoker . get ( ) ) ;
m_viewed_test_infos . push_back ( testInfo . get ( ) ) ;
2022-01-03 23:16:39 +01:00
m_owned_test_infos . push_back ( CATCH_MOVE ( testInfo ) ) ;
m_invokers . push_back ( CATCH_MOVE ( testInvoker ) ) ;
2020-09-08 15:53:08 +02:00
}
std : : vector < TestCaseInfo * > const & TestRegistry : : getAllInfos ( ) const {
return m_viewed_test_infos ;
}
std : : vector < TestCaseHandle > const & TestRegistry : : getAllTests ( ) const {
return m_handles ;
}
std : : vector < TestCaseHandle > 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 ;
}
///////////////////////////////////////////////////////////////////////////
void TestInvokerAsFunction : : invoke ( ) const {
m_testAsFunction ( ) ;
}
} // end namespace Catch
# include <algorithm>
# include <cassert>
# if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors"
# endif
namespace Catch {
namespace TestCaseTracking {
NameAndLocation : : NameAndLocation ( std : : string const & _name , SourceLineInfo const & _location )
: name ( _name ) ,
location ( _location )
{ }
ITracker : : ~ ITracker ( ) = default ;
2022-01-03 23:16:39 +01:00
void ITracker : : markAsNeedingAnotherRun ( ) {
m_runState = NeedsAnotherRun ;
}
void ITracker : : addChild ( ITrackerPtr & & child ) {
m_children . push_back ( CATCH_MOVE ( child ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
ITracker * ITracker : : findChild ( NameAndLocation const & nameAndLocation ) {
2020-09-08 15:53:08 +02:00
auto it = std : : find_if (
m_children . begin ( ) ,
m_children . end ( ) ,
[ & nameAndLocation ] ( ITrackerPtr const & tracker ) {
return tracker - > nameAndLocation ( ) . location = =
nameAndLocation . location & &
tracker - > nameAndLocation ( ) . name = = nameAndLocation . name ;
} ) ;
2022-01-03 23:16:39 +01:00
return ( it ! = m_children . end ( ) ) ? it - > get ( ) : nullptr ;
}
bool ITracker : : isSectionTracker ( ) const { return false ; }
bool ITracker : : isGeneratorTracker ( ) const { return false ; }
bool ITracker : : isSuccessfullyCompleted ( ) const {
return m_runState = = CompletedSuccessfully ;
}
bool ITracker : : isOpen ( ) const {
return m_runState ! = NotStarted & & ! isComplete ( ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
bool ITracker : : hasStarted ( ) const { return m_runState ! = NotStarted ; }
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
void ITracker : : openChild ( ) {
if ( m_runState ! = ExecutingChildren ) {
m_runState = ExecutingChildren ;
if ( m_parent ) {
m_parent - > openChild ( ) ;
}
}
}
2020-09-08 15:53:08 +02:00
ITracker & TrackerContext : : startRun ( ) {
using namespace std : : string_literals ;
2022-01-03 23:16:39 +01:00
m_rootTracker = Catch : : Detail : : make_unique < SectionTracker > (
NameAndLocation ( " {root} " s , CATCH_INTERNAL_LINEINFO ) ,
* this ,
nullptr ) ;
2020-09-08 15:53:08 +02:00
m_currentTracker = nullptr ;
m_runState = Executing ;
return * m_rootTracker ;
}
void TrackerContext : : endRun ( ) {
m_rootTracker . reset ( ) ;
m_currentTracker = nullptr ;
m_runState = NotStarted ;
}
void TrackerContext : : startCycle ( ) {
m_currentTracker = m_rootTracker . get ( ) ;
m_runState = Executing ;
}
void TrackerContext : : completeCycle ( ) {
m_runState = CompletedCycle ;
}
bool TrackerContext : : completedCycle ( ) const {
return m_runState = = CompletedCycle ;
}
ITracker & TrackerContext : : currentTracker ( ) {
return * m_currentTracker ;
}
void TrackerContext : : setCurrentTracker ( ITracker * tracker ) {
m_currentTracker = tracker ;
}
TrackerBase : : TrackerBase ( NameAndLocation const & nameAndLocation , TrackerContext & ctx , ITracker * parent ) :
2022-01-03 23:16:39 +01:00
ITracker ( nameAndLocation , parent ) ,
m_ctx ( ctx )
2020-09-08 15:53:08 +02:00
{ }
bool TrackerBase : : isComplete ( ) const {
return m_runState = = CompletedSuccessfully | | m_runState = = Failed ;
}
void TrackerBase : : open ( ) {
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 ( ) ;
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 NotStarted :
case CompletedSuccessfully :
case Failed :
CATCH_INTERNAL_ERROR ( " Illogical state: " < < m_runState ) ;
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 ( ) ;
}
void TrackerBase : : moveToParent ( ) {
assert ( m_parent ) ;
m_ctx . setCurrentTracker ( m_parent ) ;
}
void TrackerBase : : moveToThis ( ) {
m_ctx . setCurrentTracker ( this ) ;
}
SectionTracker : : SectionTracker ( NameAndLocation const & nameAndLocation , TrackerContext & ctx , ITracker * parent )
: TrackerBase ( nameAndLocation , ctx , parent ) ,
m_trimmed_name ( trim ( nameAndLocation . name ) )
{
if ( parent ) {
while ( ! parent - > isSectionTracker ( ) )
2022-01-03 23:16:39 +01:00
parent = parent - > parent ( ) ;
2020-09-08 15:53:08 +02:00
SectionTracker & parentSection = static_cast < SectionTracker & > ( * parent ) ;
addNextFilters ( parentSection . m_filters ) ;
}
}
bool SectionTracker : : isComplete ( ) const {
bool complete = true ;
if ( m_filters . empty ( )
2022-01-03 23:16:39 +01:00
| | m_filters [ 0 ] . empty ( )
2020-09-08 15:53:08 +02:00
| | std : : find ( m_filters . begin ( ) , m_filters . end ( ) , m_trimmed_name ) ! = m_filters . end ( ) ) {
complete = TrackerBase : : isComplete ( ) ;
}
return complete ;
}
bool SectionTracker : : isSectionTracker ( ) const { return true ; }
SectionTracker & SectionTracker : : acquire ( TrackerContext & ctx , NameAndLocation const & nameAndLocation ) {
2022-01-03 23:16:39 +01:00
SectionTracker * section ;
2020-09-08 15:53:08 +02:00
ITracker & currentTracker = ctx . currentTracker ( ) ;
2022-01-03 23:16:39 +01:00
if ( ITracker * childTracker =
currentTracker . findChild ( nameAndLocation ) ) {
2020-09-08 15:53:08 +02:00
assert ( childTracker ) ;
assert ( childTracker - > isSectionTracker ( ) ) ;
2022-01-03 23:16:39 +01:00
section = static_cast < SectionTracker * > ( childTracker ) ;
} else {
auto newSection = Catch : : Detail : : make_unique < SectionTracker > (
nameAndLocation , ctx , & currentTracker ) ;
section = newSection . get ( ) ;
currentTracker . addChild ( CATCH_MOVE ( newSection ) ) ;
2020-09-08 15:53:08 +02:00
}
if ( ! ctx . completedCycle ( ) )
section - > tryOpen ( ) ;
return * section ;
}
void SectionTracker : : tryOpen ( ) {
if ( ! isComplete ( ) )
open ( ) ;
}
void SectionTracker : : addInitialFilters ( std : : vector < std : : string > const & filters ) {
if ( ! filters . empty ( ) ) {
m_filters . reserve ( m_filters . size ( ) + filters . size ( ) + 2 ) ;
2022-01-03 23:16:39 +01:00
m_filters . emplace_back ( StringRef { } ) ; // Root - should never be consulted
m_filters . emplace_back ( StringRef { } ) ; // Test Case - not a section filter
2020-09-08 15:53:08 +02:00
m_filters . insert ( m_filters . end ( ) , filters . begin ( ) , filters . end ( ) ) ;
}
}
2022-01-03 23:16:39 +01:00
void SectionTracker : : addNextFilters ( std : : vector < StringRef > const & filters ) {
2020-09-08 15:53:08 +02:00
if ( filters . size ( ) > 1 )
m_filters . insert ( m_filters . end ( ) , filters . begin ( ) + 1 , filters . end ( ) ) ;
}
2022-01-03 23:16:39 +01:00
std : : vector < StringRef > const & SectionTracker : : getFilters ( ) const {
return m_filters ;
}
StringRef SectionTracker : : trimmedName ( ) const {
return m_trimmed_name ;
}
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
} // namespace TestCaseTracking
2020-09-08 15:53:08 +02:00
} // namespace Catch
# if defined(__clang__)
# pragma clang diagnostic pop
# endif
2022-01-03 23:16:39 +01:00
# include <algorithm>
# include <iterator>
2020-09-08 15:53:08 +02:00
namespace Catch {
2022-01-03 23:16:39 +01:00
namespace {
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 ( ) ) ;
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 , ' : ' ) ;
auto const startIdx = reverseEnd - secondLastColons ;
auto const classNameSize = secondLastColons - lastColons - 1 ;
return methodName . substr (
static_cast < std : : size_t > ( startIdx ) ,
static_cast < std : : size_t > ( classNameSize ) ) ;
}
} // namespace
2020-09-08 15:53:08 +02:00
Detail : : unique_ptr < ITestInvoker > makeTestInvoker ( void ( * testAsFunction ) ( ) ) {
2022-01-03 23:16:39 +01:00
return Detail : : make_unique < TestInvokerAsFunction > ( testAsFunction ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
AutoReg : : AutoReg ( Detail : : unique_ptr < ITestInvoker > invoker , SourceLineInfo const & lineInfo , StringRef classOrMethod , NameAndTags const & nameAndTags ) noexcept {
2020-09-08 15:53:08 +02:00
CATCH_TRY {
getMutableRegistryHub ( )
. registerTest (
makeTestCaseInfo (
extractClassName ( classOrMethod ) ,
nameAndTags ,
lineInfo ) ,
2022-01-03 23:16:39 +01:00
CATCH_MOVE ( invoker )
2020-09-08 15:53:08 +02:00
) ;
} CATCH_CATCH_ALL {
// Do not throw when constructing global objects, instead register the exception to be processed later
getMutableRegistryHub ( ) . registerStartupException ( ) ;
}
}
}
namespace Catch {
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 ;
for ( m_pos = 0 ; m_pos < m_arg . size ( ) ; + + m_pos )
//if visitChar fails
if ( ! visitChar ( m_arg [ m_pos ] ) ) {
2022-01-03 23:16:39 +01:00
m_testSpec . m_invalidSpecs . push_back ( arg ) ;
2020-09-08 15:53:08 +02:00
break ;
}
endMode ( ) ;
return * this ;
}
TestSpec TestSpecParser : : testSpec ( ) {
addFilter ( ) ;
2022-01-03 23:16:39 +01:00
return CATCH_MOVE ( m_testSpec ) ;
2020-09-08 15:53:08 +02:00
}
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 ( ) ) {
2022-01-03 23:16:39 +01:00
m_testSpec . m_filters . push_back ( CATCH_MOVE ( m_currentFilter ) ) ;
2020-09-08 15:53:08 +02:00
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 < TestSpec : : NamePattern > ( token , m_substring ) ) ;
} else {
m_currentFilter . m_required . emplace_back ( Detail : : make_unique < TestSpec : : NamePattern > ( 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 < TestSpec : : TagPattern > ( " . " , m_substring ) ) ;
m_currentFilter . m_forbidden . emplace_back ( Detail : : make_unique < TestSpec : : TagPattern > ( token , m_substring ) ) ;
} else {
m_currentFilter . m_required . emplace_back ( Detail : : make_unique < TestSpec : : TagPattern > ( " . " , m_substring ) ) ;
m_currentFilter . m_required . emplace_back ( Detail : : make_unique < TestSpec : : TagPattern > ( token , m_substring ) ) ;
}
}
if ( m_exclusion ) {
m_currentFilter . m_forbidden . emplace_back ( Detail : : make_unique < TestSpec : : TagPattern > ( token , m_substring ) ) ;
} else {
m_currentFilter . m_required . emplace_back ( Detail : : make_unique < TestSpec : : TagPattern > ( token , m_substring ) ) ;
}
}
m_substring . clear ( ) ;
m_exclusion = false ;
m_mode = None ;
}
TestSpec parseTestSpec ( std : : string const & arg ) {
return TestSpecParser ( ITagAliasRegistry : : get ( ) ) . parse ( arg ) . testSpec ( ) ;
}
} // namespace Catch
2022-01-03 23:16:39 +01:00
# include <algorithm>
2020-09-08 15:53:08 +02:00
# include <cstring>
# include <ostream>
namespace {
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 isBreakableAfter ( char c ) {
static const char chars [ ] = " ])}>.,:;*+-=&/ \\ " ;
return std : : memchr ( chars , c , sizeof ( chars ) - 1 ) ! = nullptr ;
}
bool isBoundary ( std : : string const & line , size_t at ) {
assert ( at > 0 ) ;
assert ( at < = line . size ( ) ) ;
return at = = line . size ( ) | |
( isWhitespace ( line [ at ] ) & & ! isWhitespace ( line [ at - 1 ] ) ) | |
isBreakableBefore ( line [ at ] ) | |
isBreakableAfter ( line [ at - 1 ] ) ;
}
} // namespace
namespace Catch {
namespace TextFlow {
2022-01-03 23:16:39 +01:00
void Column : : const_iterator : : calcLength ( ) {
m_addHyphen = false ;
m_parsedTo = m_lineStart ;
2020-09-08 15:53:08 +02:00
std : : string const & current_line = m_column . m_string ;
2022-01-03 23:16:39 +01:00
if ( current_line [ m_lineStart ] = = ' \n ' ) {
+ + m_parsedTo ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
const auto maxLineLength = m_column . m_width - indentSize ( ) ;
const auto maxParseTo = std : : min ( current_line . size ( ) , m_lineStart + maxLineLength ) ;
while ( m_parsedTo < maxParseTo & &
current_line [ m_parsedTo ] ! = ' \n ' ) {
+ + m_parsedTo ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
// If we encountered a newline before the column is filled,
// then we linebreak at the newline and consider this line
// finished.
if ( m_parsedTo < m_lineStart + maxLineLength ) {
m_lineLength = m_parsedTo - m_lineStart ;
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
// Look for a natural linebreak boundary in the column
// (We look from the end, so that the first found boundary is
// the right one)
size_t newLineLength = maxLineLength ;
while ( newLineLength > 0 & & ! isBoundary ( current_line , m_lineStart + newLineLength ) ) {
- - newLineLength ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
while ( newLineLength > 0 & &
isWhitespace ( current_line [ m_lineStart + newLineLength - 1 ] ) ) {
- - newLineLength ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
// If we found one, then that is where we linebreak
if ( newLineLength > 0 ) {
m_lineLength = newLineLength ;
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
// Otherwise we have to split text with a hyphen
m_addHyphen = true ;
m_lineLength = maxLineLength - 1 ;
2020-09-08 15:53:08 +02:00
}
}
}
2022-01-03 23:16:39 +01:00
size_t Column : : const_iterator : : indentSize ( ) const {
2020-09-08 15:53:08 +02:00
auto initial =
2022-01-03 23:16:39 +01:00
m_lineStart = = 0 ? m_column . m_initialIndent : std : : string : : npos ;
2020-09-08 15:53:08 +02:00
return initial = = std : : string : : npos ? m_column . m_indent : initial ;
}
std : : string
2022-01-03 23:16:39 +01:00
Column : : const_iterator : : addIndentAndSuffix ( size_t position ,
2020-09-08 15:53:08 +02:00
size_t length ) const {
std : : string ret ;
2022-01-03 23:16:39 +01:00
const auto desired_indent = indentSize ( ) ;
ret . reserve ( desired_indent + length + m_addHyphen ) ;
2020-09-08 15:53:08 +02:00
ret . append ( desired_indent , ' ' ) ;
ret . append ( m_column . m_string , position , length ) ;
2022-01-03 23:16:39 +01:00
if ( m_addHyphen ) {
2020-09-08 15:53:08 +02:00
ret . push_back ( ' - ' ) ;
}
return ret ;
}
2022-01-03 23:16:39 +01:00
Column : : const_iterator : : const_iterator ( Column const & column ) : m_column ( column ) {
2020-09-08 15:53:08 +02:00
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 ( ) ;
2022-01-03 23:16:39 +01:00
if ( m_lineLength = = 0 ) {
m_lineStart = m_column . m_string . size ( ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
std : : string Column : : const_iterator : : operator * ( ) const {
assert ( m_lineStart < = m_parsedTo ) ;
return addIndentAndSuffix ( m_lineStart , m_lineLength ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
Column : : const_iterator & Column : : const_iterator : : operator + + ( ) {
m_lineStart + = m_lineLength ;
2020-09-08 15:53:08 +02:00
std : : string const & current_line = m_column . m_string ;
2022-01-03 23:16:39 +01:00
if ( m_lineStart < current_line . size ( ) & & current_line [ m_lineStart ] = = ' \n ' ) {
m_lineStart + = 1 ;
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
while ( m_lineStart < current_line . size ( ) & &
isWhitespace ( current_line [ m_lineStart ] ) ) {
+ + m_lineStart ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
if ( m_lineStart ! = current_line . size ( ) ) {
2020-09-08 15:53:08 +02:00
calcLength ( ) ;
}
return * this ;
}
2022-01-03 23:16:39 +01:00
Column : : const_iterator Column : : const_iterator : : operator + + ( int ) {
const_iterator prev ( * this ) ;
2020-09-08 15:53:08 +02:00
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 Column : : operator + ( Column const & other ) {
Columns cols ;
cols + = * this ;
cols + = other ;
return cols ;
}
Columns & Columns : : operator + = ( Column const & col ) {
m_columns . push_back ( col ) ;
return * this ;
}
Columns Columns : : operator + ( Column const & col ) {
Columns combined = * this ;
combined + = col ;
return combined ;
}
} // namespace TextFlow
} // 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 < WildcardPosition > ( m_wildcard | WildcardAtEnd ) ;
}
}
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 ) ;
}
}
2022-01-03 23:16:39 +01:00
// Note: swapping these two includes around causes MSVC to error out
// while in /permissive- mode. No, I don't know why.
// Tested on VS 2019, 18.{3, 4}.x
2020-09-08 15:53:08 +02:00
# include <iomanip>
# include <type_traits>
namespace Catch {
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 " ) ;
}
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 " ) ;
}
void hexEscapeChar ( std : : ostream & os , unsigned char c ) {
std : : ios_base : : fmtflags f ( os . flags ( ) ) ;
os < < " \\ x "
< < std : : uppercase < < std : : hex < < std : : setfill ( ' 0 ' ) < < std : : setw ( 2 )
< < static_cast < int > ( c ) ;
os . flags ( f ) ;
}
bool shouldNewline ( XmlFormatting fmt ) {
return ! ! ( static_cast < std : : underlying_type_t < XmlFormatting > > ( fmt & XmlFormatting : : Newline ) ) ;
}
bool shouldIndent ( XmlFormatting fmt ) {
return ! ! ( static_cast < std : : underlying_type_t < XmlFormatting > > ( fmt & XmlFormatting : : Indent ) ) ;
}
} // anonymous namespace
XmlFormatting operator | ( XmlFormatting lhs , XmlFormatting rhs ) {
return static_cast < XmlFormatting > (
static_cast < std : : underlying_type_t < XmlFormatting > > ( lhs ) |
static_cast < std : : underlying_type_t < XmlFormatting > > ( rhs )
) ;
}
XmlFormatting operator & ( XmlFormatting lhs , XmlFormatting rhs ) {
return static_cast < XmlFormatting > (
static_cast < std : : underlying_type_t < XmlFormatting > > ( lhs ) &
static_cast < std : : underlying_type_t < XmlFormatting > > ( rhs )
) ;
}
2022-01-03 23:16:39 +01:00
XmlEncode : : XmlEncode ( StringRef str , ForWhat forWhat )
2020-09-08 15:53:08 +02:00
: m_str ( str ) ,
m_forWhat ( forWhat )
{ }
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)
for ( std : : size_t idx = 0 ; idx < m_str . size ( ) ; + + idx ) {
2022-01-03 23:16:39 +01:00
unsigned char c = static_cast < unsigned char > ( m_str [ idx ] ) ;
2020-09-08 15:53:08 +02:00
switch ( c ) {
case ' < ' : os < < " < " ; break ;
case ' & ' : os < < " & " ; break ;
case ' > ' :
// See: http://www.w3.org/TR/xml/#syntax
if ( idx > 2 & & m_str [ idx - 1 ] = = ' ] ' & & m_str [ idx - 2 ] = = ' ] ' )
os < < " > " ;
else
os < < c ;
break ;
case ' \" ' :
if ( m_forWhat = = ForAttributes )
os < < " " " ;
else
os < < c ;
break ;
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 ) {
hexEscapeChar ( os , c ) ;
break ;
}
// Plain ASCII: Write it to stream
if ( c < 0x7F ) {
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 ) {
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 ( ) ) {
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 ) {
2022-01-03 23:16:39 +01:00
unsigned char nc = static_cast < unsigned char > ( m_str [ idx + n ] ) ;
2020-09-08 15:53:08 +02:00
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 )
) {
hexEscapeChar ( os , c ) ;
break ;
}
// If we got here, this is in fact a valid(ish) utf-8 sequence
for ( std : : size_t n = 0 ; n < encBytes ; + + n ) {
os < < m_str [ idx + n ] ;
}
idx + = encBytes - 1 ;
break ;
}
}
}
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 ( ) {
if ( m_writer ) {
m_writer - > endElement ( m_fmt ) ;
}
}
2022-01-03 23:16:39 +01:00
XmlWriter : : ScopedElement &
XmlWriter : : ScopedElement : : writeText ( StringRef text , XmlFormatting fmt ) {
2020-09-08 15:53:08 +02:00
m_writer - > writeText ( text , fmt ) ;
return * this ;
}
2022-01-03 23:16:39 +01:00
XmlWriter : : ScopedElement &
XmlWriter : : ScopedElement : : writeAttribute ( StringRef name ,
StringRef attribute ) {
m_writer - > writeAttribute ( name , attribute ) ;
return * this ;
}
2020-09-08 15:53:08 +02:00
XmlWriter : : XmlWriter ( std : : ostream & os ) : m_os ( os )
{
writeDeclaration ( ) ;
}
XmlWriter : : ~ XmlWriter ( ) {
while ( ! m_tags . empty ( ) ) {
endElement ( ) ;
}
newlineIfNecessary ( ) ;
}
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 ( 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 ;
}
2022-01-03 23:16:39 +01:00
m_os < < " </ " < < m_tags . back ( ) < < ' > ' ;
2020-09-08 15:53:08 +02:00
}
m_os < < std : : flush ;
applyFormatting ( fmt ) ;
m_tags . pop_back ( ) ;
return * this ;
}
2022-01-03 23:16:39 +01:00
XmlWriter & XmlWriter : : writeAttribute ( StringRef name ,
StringRef attribute ) {
2020-09-08 15:53:08 +02:00
if ( ! name . empty ( ) & & ! attribute . empty ( ) )
m_os < < ' ' < < name < < " = \" " < < XmlEncode ( attribute , XmlEncode : : ForAttributes ) < < ' " ' ;
return * this ;
}
2022-01-03 23:16:39 +01:00
XmlWriter & XmlWriter : : writeAttribute ( StringRef name , bool attribute ) {
writeAttribute ( name , ( attribute ? " true " _sr : " false " _sr ) ) ;
2020-09-08 15:53:08 +02:00
return * this ;
}
2022-01-03 23:16:39 +01:00
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 " ) ;
2020-09-08 15:53:08 +02:00
if ( ! text . empty ( ) ) {
bool tagWasOpen = m_tagIsOpen ;
ensureTagClosed ( ) ;
if ( tagWasOpen & & shouldIndent ( fmt ) ) {
m_os < < m_indent ;
}
2022-01-03 23:16:39 +01:00
m_os < < XmlEncode ( text , XmlEncode : : ForTextNodes ) ;
2020-09-08 15:53:08 +02:00
applyFormatting ( fmt ) ;
}
return * this ;
}
2022-01-03 23:16:39 +01:00
XmlWriter & XmlWriter : : writeComment ( StringRef text , XmlFormatting fmt ) {
2020-09-08 15:53:08 +02:00
ensureTagClosed ( ) ;
if ( shouldIndent ( fmt ) ) {
m_os < < m_indent ;
}
2022-01-03 23:16:39 +01:00
m_os < < " <!-- " < < text < < " --> " ;
2020-09-08 15:53:08 +02:00
applyFormatting ( fmt ) ;
return * this ;
}
2022-01-03 23:16:39 +01:00
void XmlWriter : : writeStylesheetRef ( StringRef url ) {
m_os < < R " (<?xml-stylesheet type= " text / xsl " href= " ) " << url << R " ( " ?>) " < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
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 ( ) {
2022-01-03 23:16:39 +01:00
m_os < < R " (<?xml version= " 1.0 " encoding= " UTF - 8 " ?>) " < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
void XmlWriter : : newlineIfNecessary ( ) {
if ( m_needsNewline ) {
2022-01-03 23:16:39 +01:00
m_os < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
m_needsNewline = false ;
}
}
}
# include <algorithm>
# include <cmath>
# include <cstdlib>
# include <cstdint>
# include <sstream>
# include <iomanip>
# include <limits>
namespace Catch {
namespace {
template < typename FP >
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 ;
}
2022-01-03 23:16:39 +01:00
// This should also handle positive and negative zeros, infinities
const auto ulpDist = ulpDistance ( lhs , rhs ) ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
return ulpDist < = maxUlpDiff ;
2020-09-08 15:53:08 +02:00
}
# 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 ;
}
// 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 < typename FloatingPoint >
void write ( std : : ostream & out , FloatingPoint num ) {
out < < std : : scientific
< < std : : setprecision ( std : : numeric_limits < FloatingPoint > : : 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 ) ;
}
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 < uint32_t > : : max ) ( ) ,
" Provided ULP is impossibly large for a float comparison. " ) ;
2022-01-03 23:16:39 +01:00
CATCH_ENFORCE ( std : : numeric_limits < double > : : is_iec559 ,
" WithinUlp matcher only supports platforms with "
" IEEE-754 compatible floating point representation " ) ;
2020-09-08 15:53:08 +02:00
}
# if defined(__clang__)
# pragma clang diagnostic push
// Clang <3.5 reports on the default branch in the switch below
# pragma clang diagnostic ignored "-Wunreachable-code"
# endif
bool WithinUlpsMatcher : : match ( double const & matchee ) const {
switch ( m_type ) {
case Detail : : FloatingPointKind : : Float :
return almostEqualUlps < float > ( static_cast < float > ( matchee ) , static_cast < float > ( m_target ) , m_ulps ) ;
case Detail : : FloatingPointKind : : Double :
return almostEqualUlps < double > ( matchee , m_target , m_ulps ) ;
default :
CATCH_INTERNAL_ERROR ( " Unknown Detail::FloatingPointKind value " ) ;
}
}
# if defined(__clang__)
# pragma clang diagnostic pop
# endif
std : : string WithinUlpsMatcher : : describe ( ) const {
std : : stringstream ret ;
ret < < " is within " < < m_ulps < < " ULPs of " ;
if ( m_type = = Detail : : FloatingPointKind : : Float ) {
write ( ret , static_cast < float > ( m_target ) ) ;
ret < < ' f ' ;
} else {
write ( ret , m_target ) ;
}
ret < < " ([ " ;
if ( m_type = = Detail : : FloatingPointKind : : Double ) {
write ( ret , step ( m_target , static_cast < double > ( - INFINITY ) , m_ulps ) ) ;
ret < < " , " ;
write ( ret , step ( m_target , static_cast < double > ( INFINITY ) , m_ulps ) ) ;
} else {
// We have to cast INFINITY to float because of MinGW, see #1782
write ( ret , step ( static_cast < float > ( m_target ) , static_cast < float > ( - INFINITY ) , m_ulps ) ) ;
ret < < " , " ;
write ( ret , step ( static_cast < float > ( m_target ) , static_cast < float > ( INFINITY ) , m_ulps ) ) ;
}
ret < < " ]) " ;
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. " ) ;
}
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 " < < 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 ( float target , uint64_t maxUlpDiff ) {
return WithinUlpsMatcher ( target , maxUlpDiff , Detail : : FloatingPointKind : : Float ) ;
}
WithinAbsMatcher WithinAbs ( double target , double margin ) {
return WithinAbsMatcher ( target , margin ) ;
}
WithinRelMatcher WithinRel ( double target , double eps ) {
return WithinRelMatcher ( target , eps ) ;
}
WithinRelMatcher WithinRel ( double target ) {
return WithinRelMatcher ( target , std : : numeric_limits < double > : : epsilon ( ) * 100 ) ;
}
WithinRelMatcher WithinRel ( float target , float eps ) {
return WithinRelMatcher ( target , eps ) ;
}
WithinRelMatcher WithinRel ( float target ) {
return WithinRelMatcher ( target , std : : numeric_limits < float > : : epsilon ( ) * 100 ) ;
}
} // namespace Matchers
} // namespace Catch
# include <regex>
namespace Catch {
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 ;
}
StringRef CasedString : : caseSensitivitySuffix ( ) const {
return m_caseSensitivity = = CaseSensitive : : Yes
? StringRef ( )
: " (case insensitive) " _sr ;
}
StringMatcherBase : : StringMatcherBase ( std : : string const & 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 ;
2022-01-03 23:16:39 +01:00
description + = ' " ' ;
2020-09-08 15:53:08 +02:00
description + = m_comparator . caseSensitivitySuffix ( ) ;
return description ;
}
StringEqualsMatcher : : StringEqualsMatcher ( CasedString const & comparator ) : StringMatcherBase ( " equals " , comparator ) { }
bool StringEqualsMatcher : : match ( std : : string const & source ) const {
return m_comparator . adjustString ( source ) = = m_comparator . m_str ;
}
StringContainsMatcher : : StringContainsMatcher ( CasedString const & comparator ) : StringMatcherBase ( " contains " , comparator ) { }
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 " , comparator ) { }
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 " , comparator ) { }
bool EndsWithMatcher : : match ( std : : string const & source ) const {
return endsWith ( m_comparator . adjustString ( source ) , m_comparator . m_str ) ;
}
2022-01-03 23:16:39 +01:00
RegexMatcher : : RegexMatcher ( std : : string regex , CaseSensitive caseSensitivity ) : m_regex ( CATCH_MOVE ( regex ) ) , m_caseSensitivity ( caseSensitivity ) { }
2020-09-08 15:53:08 +02:00
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 " ) ;
}
StringEqualsMatcher Equals ( std : : string const & str , CaseSensitive caseSensitivity ) {
return StringEqualsMatcher ( CasedString ( str , caseSensitivity ) ) ;
}
2022-01-03 23:16:39 +01:00
StringContainsMatcher ContainsSubstring ( std : : string const & str , CaseSensitive caseSensitivity ) {
2020-09-08 15:53:08 +02:00
return StringContainsMatcher ( 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 ) ) ;
}
RegexMatcher Matches ( std : : string const & regex , CaseSensitive caseSensitivity ) {
return RegexMatcher ( regex , caseSensitivity ) ;
}
} // namespace Matchers
} // namespace Catch
namespace Catch {
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 ( ) ;
}
2022-01-03 23:16:39 +01:00
combined_size + = static_cast < size_t > ( descriptions_end - descriptions_begin - 1 ) * combine . size ( ) ;
2020-09-08 15:53:08 +02:00
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 Catch
/** \file
* This is a special TU that combines what would otherwise be a very
* small matcher - related TUs into one bigger TU .
*
* The reason for this is compilation performance improvements by
* avoiding reparsing headers for many small TUs , instead having this
* one TU include bit more , but having it all parsed only once .
*
* To avoid heavy - tail problem with compilation times , each " subpart "
* of Catch2 has its own combined TU like this .
*/
//////////////////////////////////////////////
// vvv formerly catch_matchers_impl.cpp vvv //
//////////////////////////////////////////////
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)
2022-01-03 23:16:39 +01:00
void handleExceptionMatchExpr ( AssertionHandler & handler , StringMatcher const & matcher , StringRef matcherString ) {
2020-09-08 15:53:08 +02:00
std : : string exceptionMessage = Catch : : translateActiveException ( ) ;
2022-01-03 23:16:39 +01:00
MatchExpr < std : : string , StringMatcher const & > expr ( CATCH_MOVE ( exceptionMessage ) , matcher , matcherString ) ;
2020-09-08 15:53:08 +02:00
handler . handleExpr ( expr ) ;
}
} // namespace Catch
//////////////////////////////////////////////////////////////
// vvv formerly catch_matchers_container_properties.cpp vvv //
//////////////////////////////////////////////////////////////
namespace Catch {
namespace Matchers {
std : : string IsEmptyMatcher : : describe ( ) const {
return " is empty " ;
}
std : : string HasSizeMatcher : : describe ( ) const {
ReusableStringStream sstr ;
sstr < < " has size == " < < m_target_size ;
return sstr . str ( ) ;
}
IsEmptyMatcher IsEmpty ( ) {
return { } ;
}
HasSizeMatcher SizeIs ( std : : size_t sz ) {
return HasSizeMatcher { sz } ;
}
} // end namespace Matchers
} // end namespace Catch
/////////////////////////////////////////
// vvv formerly catch_matchers.cpp vvv //
/////////////////////////////////////////
namespace Catch {
namespace Matchers {
std : : string MatcherUntypedBase : : toString ( ) const {
if ( m_cachedToString . empty ( ) ) {
m_cachedToString = describe ( ) ;
}
return m_cachedToString ;
}
MatcherUntypedBase : : ~ MatcherUntypedBase ( ) = default ;
} // namespace Matchers
} // namespace Catch
///////////////////////////////////////////////////
// vvv formerly catch_matchers_predicate.cpp vvv //
///////////////////////////////////////////////////
std : : string Catch : : Matchers : : Detail : : finalizeDescription ( const std : : string & desc ) {
if ( desc . empty ( ) ) {
return " matches undescribed predicate " ;
} else {
return " matches predicate: \" " + desc + ' " ' ;
}
}
///////////////////////////////////////////////////
// vvv formerly catch_matchers_exception.cpp vvv //
///////////////////////////////////////////////////
namespace Catch {
namespace Matchers {
bool ExceptionMessageMatcher : : match ( std : : exception const & ex ) const {
return ex . what ( ) = = m_message ;
}
std : : string ExceptionMessageMatcher : : describe ( ) const {
2022-01-03 23:16:39 +01:00
return " exception message matches \" " + m_message + ' " ' ;
2020-09-08 15:53:08 +02:00
}
ExceptionMessageMatcher Message ( std : : string const & message ) {
return ExceptionMessageMatcher ( message ) ;
}
} // namespace Matchers
} // namespace Catch
# include <ostream>
namespace Catch {
AutomakeReporter : : ~ AutomakeReporter ( ) { }
void AutomakeReporter : : testCaseEnded ( TestCaseStats const & _testCaseStats ) {
// Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR.
2022-01-03 23:16:39 +01:00
m_stream < < " :test-result: " ;
2020-09-08 15:53:08 +02:00
if ( _testCaseStats . totals . assertions . allPassed ( ) ) {
2022-01-03 23:16:39 +01:00
m_stream < < " PASS " ;
2020-09-08 15:53:08 +02:00
} else if ( _testCaseStats . totals . assertions . allOk ( ) ) {
2022-01-03 23:16:39 +01:00
m_stream < < " XFAIL " ;
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
m_stream < < " FAIL " ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
m_stream < < ' ' < < _testCaseStats . testInfo - > name < < ' \n ' ;
2020-09-08 15:53:08 +02:00
StreamingReporterBase : : testCaseEnded ( _testCaseStats ) ;
}
void AutomakeReporter : : skipTest ( TestCaseInfo const & testInfo ) {
2022-01-03 23:16:39 +01:00
m_stream < < " :test-result: SKIP " < < testInfo . name < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
} // end namespace Catch
/** \file
* This is a special TU that combines what would otherwise be a very
* small reporter - related TUs into one bigger TU .
*
* The reason for this is compilation performance improvements by
* avoiding reparsing headers for many small TUs , instead having this
* one TU include bit more , but having it all parsed only once .
*
* To avoid heavy - tail problem with compilation times , each " subpart "
* of Catch2 has its own combined TU like this .
*/
2022-01-03 23:16:39 +01:00
# include <algorithm>
2020-09-08 15:53:08 +02:00
# include <cfloat>
# include <cstdio>
# include <ostream>
2022-01-03 23:16:39 +01:00
# include <iomanip>
2020-09-08 15:53:08 +02:00
namespace Catch {
2022-01-03 23:16:39 +01:00
namespace {
void listTestNamesOnly ( std : : ostream & out ,
std : : vector < TestCaseHandle > 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 ;
}
} // end unnamed namespace
2020-09-08 15:53:08 +02:00
// 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 ;
# ifdef _MSC_VER
2022-01-03 23:16:39 +01:00
size_t printedLength = static_cast < size_t > (
sprintf_s ( buffer , " %.3f " , duration ) ) ;
2020-09-08 15:53:08 +02:00
# else
2022-01-03 23:16:39 +01:00
size_t printedLength = static_cast < size_t > (
std : : snprintf ( buffer , maxDoubleSize , " %.3f " , duration ) ) ;
2020-09-08 15:53:08 +02:00
# endif
2022-01-03 23:16:39 +01:00
return std : : string ( buffer , printedLength ) ;
2020-09-08 15:53:08 +02:00
}
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 < std : : string > 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 ( ) ;
}
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 ) ;
}
return serialized ;
}
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 ;
}
2022-01-03 23:16:39 +01:00
void
defaultListReporters ( std : : ostream & out ,
std : : vector < ReporterDescription > 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 ( ) ;
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 defaultListTags ( std : : ostream & out ,
std : : vector < TagInfo > const & tags ,
bool isFiltered ) {
if ( isFiltered ) {
out < < " Tags for matching test cases: \n " ;
} else {
out < < " All available tags: \n " ;
}
for ( auto const & tagCount : tags ) {
ReusableStringStream rss ;
rss < < " " < < std : : setw ( 2 ) < < 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 , std : : vector < TestCaseHandle > 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 ;
Colour colourGuard ( colour ) ;
out < < TextFlow : : Column ( testCaseInfo . name ) . initialIndent ( 2 ) . indent ( 4 ) < < ' \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 ;
}
2020-09-08 15:53:08 +02:00
} // namespace Catch
namespace Catch {
2022-01-03 23:16:39 +01:00
void EventListenerBase : : fatalErrorEncountered ( StringRef ) { }
void EventListenerBase : : benchmarkPreparing ( StringRef ) { }
void EventListenerBase : : benchmarkStarting ( BenchmarkInfo const & ) { }
void EventListenerBase : : benchmarkEnded ( BenchmarkStats < > const & ) { }
void EventListenerBase : : benchmarkFailed ( StringRef ) { }
2020-09-08 15:53:08 +02:00
void EventListenerBase : : assertionStarting ( AssertionInfo const & ) { }
2022-01-03 23:16:39 +01:00
void EventListenerBase : : assertionEnded ( AssertionStats const & ) { }
void EventListenerBase : : listReporters (
std : : vector < ReporterDescription > const & ) { }
void EventListenerBase : : listTests ( std : : vector < TestCaseHandle > const & ) { }
void EventListenerBase : : listTags ( std : : vector < TagInfo > const & ) { }
void EventListenerBase : : noMatchingTestCases ( StringRef ) { }
void EventListenerBase : : reportInvalidTestSpec ( StringRef ) { }
2020-09-08 15:53:08 +02:00
void EventListenerBase : : testRunStarting ( TestRunInfo const & ) { }
void EventListenerBase : : testCaseStarting ( TestCaseInfo const & ) { }
2022-01-03 23:16:39 +01:00
void EventListenerBase : : testCasePartialStarting ( TestCaseInfo const & , uint64_t ) { }
2020-09-08 15:53:08 +02:00
void EventListenerBase : : sectionStarting ( SectionInfo const & ) { }
void EventListenerBase : : sectionEnded ( SectionStats const & ) { }
2022-01-03 23:16:39 +01:00
void EventListenerBase : : testCasePartialEnded ( TestCaseStats const & , uint64_t ) { }
2020-09-08 15:53:08 +02:00
void EventListenerBase : : testCaseEnded ( TestCaseStats const & ) { }
void EventListenerBase : : testRunEnded ( TestRunStats const & ) { }
void EventListenerBase : : skipTest ( TestCaseInfo const & ) { }
} // namespace Catch
# include <ostream>
namespace {
// Colour::LightGrey
2022-01-03 23:16:39 +01:00
constexpr Catch : : Colour : : Code dimColour ( ) { return Catch : : Colour : : FileName ; }
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
constexpr Catch : : StringRef bothOrAll ( std : : uint64_t count ) {
2020-09-08 15:53:08 +02:00
switch ( count ) {
case 1 :
return Catch : : StringRef { } ;
case 2 :
return " both " _catch_sr ;
default :
return " all " _catch_sr ;
}
}
} // anon namespace
namespace Catch {
namespace {
# ifdef CATCH_PLATFORM_MAC
static constexpr Catch : : StringRef compactFailedString = " FAILED " _sr ;
static constexpr Catch : : StringRef compactPassedString = " PASSED " _sr ;
# else
static constexpr Catch : : StringRef compactFailedString = " failed " _sr ;
static constexpr Catch : : StringRef compactPassedString = " passed " _sr ;
# endif
// Colour, message variants:
// - white: No tests ran.
// - red: Failed [both/all] N test cases, failed [both/all] M assertions.
// - white: Passed [both/all] N test cases (no assertions).
// - red: Failed N tests cases, failed M assertions.
// - green: Passed [both/all] N tests cases with M assertions.
void printTotals ( std : : ostream & out , const Totals & totals ) {
if ( totals . testCases . total ( ) = = 0 ) {
out < < " No tests ran. " ;
} else if ( totals . testCases . failed = = totals . testCases . total ( ) ) {
Colour colour ( Colour : : ResultError ) ;
const StringRef qualify_assertions_failed =
totals . assertions . failed = = totals . assertions . total ( ) ?
bothOrAll ( totals . assertions . failed ) : StringRef { } ;
out < <
" Failed " < < bothOrAll ( totals . testCases . failed )
2022-01-03 23:16:39 +01:00
< < pluralise ( totals . testCases . failed , " test case " _sr ) < < " , "
2020-09-08 15:53:08 +02:00
" failed " < < qualify_assertions_failed < <
2022-01-03 23:16:39 +01:00
pluralise ( totals . assertions . failed , " assertion " _sr ) < < ' . ' ;
2020-09-08 15:53:08 +02:00
} else if ( totals . assertions . total ( ) = = 0 ) {
out < <
" Passed " < < bothOrAll ( totals . testCases . total ( ) )
2022-01-03 23:16:39 +01:00
< < pluralise ( totals . testCases . total ( ) , " test case " _sr )
2020-09-08 15:53:08 +02:00
< < " (no assertions). " ;
} else if ( totals . assertions . failed ) {
Colour colour ( Colour : : ResultError ) ;
out < <
2022-01-03 23:16:39 +01:00
" Failed " < < pluralise ( totals . testCases . failed , " test case " _sr ) < < " , "
" failed " < < pluralise ( totals . assertions . failed , " assertion " _sr ) < < ' . ' ;
2020-09-08 15:53:08 +02:00
} else {
Colour colour ( Colour : : ResultSuccess ) ;
out < <
" Passed " < < bothOrAll ( totals . testCases . passed )
2022-01-03 23:16:39 +01:00
< < pluralise ( totals . testCases . passed , " test case " _sr ) < <
" with " < < pluralise ( totals . assertions . passed , " assertion " _sr ) < < ' . ' ;
2020-09-08 15:53:08 +02:00
}
}
// 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 )
: stream ( _stream )
, result ( _stats . assertionResult )
, messages ( _stats . infoMessages )
, itMessage ( _stats . infoMessages . begin ( ) )
, printInfoMessages ( _printInfoMessages ) { }
void print ( ) {
printSourceInfo ( ) ;
itMessage = messages . begin ( ) ;
switch ( result . getResultType ( ) ) {
case ResultWas : : Ok :
printResultType ( Colour : : ResultSuccess , compactPassedString ) ;
printOriginalExpression ( ) ;
printReconstructedExpression ( ) ;
if ( ! result . hasExpression ( ) )
printRemainingMessages ( Colour : : None ) ;
else
printRemainingMessages ( ) ;
break ;
case ResultWas : : ExpressionFailed :
if ( result . isOk ( ) )
printResultType ( Colour : : ResultSuccess , compactFailedString + " - but was ok " _sr ) ;
else
printResultType ( Colour : : Error , compactFailedString ) ;
printOriginalExpression ( ) ;
printReconstructedExpression ( ) ;
printRemainingMessages ( ) ;
break ;
case ResultWas : : ThrewException :
printResultType ( Colour : : Error , compactFailedString ) ;
printIssue ( " unexpected exception with message: " ) ;
printMessage ( ) ;
printExpressionWas ( ) ;
printRemainingMessages ( ) ;
break ;
case ResultWas : : FatalErrorCondition :
printResultType ( Colour : : Error , compactFailedString ) ;
printIssue ( " fatal error condition with message: " ) ;
printMessage ( ) ;
printExpressionWas ( ) ;
printRemainingMessages ( ) ;
break ;
case ResultWas : : DidntThrowException :
printResultType ( Colour : : Error , compactFailedString ) ;
printIssue ( " expected exception, got none " ) ;
printExpressionWas ( ) ;
printRemainingMessages ( ) ;
break ;
case ResultWas : : Info :
printResultType ( Colour : : None , " info " _sr ) ;
printMessage ( ) ;
printRemainingMessages ( ) ;
break ;
case ResultWas : : Warning :
printResultType ( Colour : : None , " warning " _sr ) ;
printMessage ( ) ;
printRemainingMessages ( ) ;
break ;
case ResultWas : : ExplicitFailure :
printResultType ( Colour : : Error , compactFailedString ) ;
printIssue ( " explicitly " ) ;
printRemainingMessages ( Colour : : None ) ;
break ;
// These cases are here to prevent compiler warnings
case ResultWas : : Unknown :
case ResultWas : : FailureBit :
case ResultWas : : Exception :
printResultType ( Colour : : Error , " ** internal error ** " ) ;
break ;
}
}
private :
void printSourceInfo ( ) const {
Colour colourGuard ( Colour : : FileName ) ;
stream < < result . getSourceInfo ( ) < < ' : ' ;
}
void printResultType ( Colour : : Code colour , StringRef passOrFail ) const {
if ( ! passOrFail . empty ( ) ) {
{
Colour colourGuard ( colour ) ;
stream < < ' ' < < passOrFail ;
}
stream < < ' : ' ;
}
}
void printIssue ( char const * issue ) const {
stream < < ' ' < < issue ;
}
void printExpressionWas ( ) {
if ( result . hasExpression ( ) ) {
stream < < ' ; ' ;
{
Colour colour ( dimColour ( ) ) ;
stream < < " expression was: " ;
}
printOriginalExpression ( ) ;
}
}
void printOriginalExpression ( ) const {
if ( result . hasExpression ( ) ) {
stream < < ' ' < < result . getExpression ( ) ;
}
}
void printReconstructedExpression ( ) const {
if ( result . hasExpandedExpression ( ) ) {
{
Colour colour ( dimColour ( ) ) ;
stream < < " for: " ;
}
stream < < result . getExpandedExpression ( ) ;
}
}
void printMessage ( ) {
if ( itMessage ! = messages . end ( ) ) {
stream < < " ' " < < itMessage - > message < < ' \' ' ;
+ + itMessage ;
}
}
void printRemainingMessages ( Colour : : Code colour = dimColour ( ) ) {
if ( itMessage = = messages . end ( ) )
return ;
const auto itEnd = messages . cend ( ) ;
const auto N = static_cast < std : : size_t > ( std : : distance ( itMessage , itEnd ) ) ;
{
Colour colourGuard ( colour ) ;
2022-01-03 23:16:39 +01:00
stream < < " with " < < pluralise ( N , " message " _sr ) < < ' : ' ;
2020-09-08 15:53:08 +02:00
}
while ( itMessage ! = itEnd ) {
// If this assertion is a warning ignore any INFO messages
if ( printInfoMessages | | itMessage - > type ! = ResultWas : : Info ) {
printMessage ( ) ;
if ( itMessage ! = itEnd ) {
Colour colourGuard ( dimColour ( ) ) ;
stream < < " and " ;
}
continue ;
}
+ + itMessage ;
}
}
private :
std : : ostream & stream ;
AssertionResult const & result ;
std : : vector < MessageInfo > messages ;
std : : vector < MessageInfo > : : const_iterator itMessage ;
bool printInfoMessages ;
} ;
} // anon namespace
std : : string CompactReporter : : getDescription ( ) {
return " Reports test results on a single line, suitable for IDEs " ;
}
2022-01-03 23:16:39 +01:00
void CompactReporter : : noMatchingTestCases ( StringRef unmatchedSpec ) {
m_stream < < " No test cases matched ' " < < unmatchedSpec < < " ' \n " ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void CompactReporter : : assertionEnded ( AssertionStats const & _assertionStats ) {
2020-09-08 15:53:08 +02:00
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 )
2022-01-03 23:16:39 +01:00
return ;
2020-09-08 15:53:08 +02:00
printInfoMessages = false ;
}
2022-01-03 23:16:39 +01:00
AssertionPrinter printer ( m_stream , _assertionStats , printInfoMessages ) ;
2020-09-08 15:53:08 +02:00
printer . print ( ) ;
2022-01-03 23:16:39 +01:00
m_stream < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
void CompactReporter : : sectionEnded ( SectionStats const & _sectionStats ) {
double dur = _sectionStats . durationInSeconds ;
if ( shouldShowDuration ( * m_config , dur ) ) {
2022-01-03 23:16:39 +01:00
m_stream < < getFormattedDuration ( dur ) < < " s: " < < _sectionStats . sectionInfo . name < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
}
void CompactReporter : : testRunEnded ( TestRunStats const & _testRunStats ) {
2022-01-03 23:16:39 +01:00
printTotals ( m_stream , _testRunStats . totals ) ;
m_stream < < " \n \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
StreamingReporterBase : : testRunEnded ( _testRunStats ) ;
}
CompactReporter : : ~ CompactReporter ( ) { }
} // end namespace Catch
# include <cstdio>
# 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
# endif
# if defined(__clang__)
# pragma clang diagnostic push
// For simplicity, benchmarking-only helpers are always enabled
# pragma clang diagnostic ignored "-Wunused-function"
# endif
namespace Catch {
namespace {
// Formatter impl for ConsoleReporter
class ConsoleAssertionPrinter {
public :
ConsoleAssertionPrinter & operator = ( ConsoleAssertionPrinter const & ) = delete ;
ConsoleAssertionPrinter ( ConsoleAssertionPrinter const & ) = delete ;
ConsoleAssertionPrinter ( std : : ostream & _stream , AssertionStats const & _stats , bool _printInfoMessages )
: stream ( _stream ) ,
stats ( _stats ) ,
result ( _stats . assertionResult ) ,
colour ( Colour : : None ) ,
message ( result . getMessage ( ) ) ,
messages ( _stats . infoMessages ) ,
printInfoMessages ( _printInfoMessages ) {
switch ( result . getResultType ( ) ) {
case ResultWas : : Ok :
colour = Colour : : Success ;
2022-01-03 23:16:39 +01:00
passOrFail = " PASSED " _sr ;
2020-09-08 15:53:08 +02:00
//if( result.hasMessage() )
if ( _stats . infoMessages . size ( ) = = 1 )
messageLabel = " with message " ;
if ( _stats . infoMessages . size ( ) > 1 )
messageLabel = " with messages " ;
break ;
case ResultWas : : ExpressionFailed :
if ( result . isOk ( ) ) {
colour = Colour : : Success ;
2022-01-03 23:16:39 +01:00
passOrFail = " FAILED - but was ok " _sr ;
2020-09-08 15:53:08 +02:00
} else {
colour = Colour : : Error ;
2022-01-03 23:16:39 +01:00
passOrFail = " FAILED " _sr ;
2020-09-08 15:53:08 +02:00
}
if ( _stats . infoMessages . size ( ) = = 1 )
messageLabel = " with message " ;
if ( _stats . infoMessages . size ( ) > 1 )
messageLabel = " with messages " ;
break ;
case ResultWas : : ThrewException :
colour = Colour : : Error ;
2022-01-03 23:16:39 +01:00
passOrFail = " FAILED " _sr ;
2020-09-08 15:53:08 +02:00
messageLabel = " due to unexpected exception with " ;
if ( _stats . infoMessages . size ( ) = = 1 )
messageLabel + = " message " ;
if ( _stats . infoMessages . size ( ) > 1 )
messageLabel + = " messages " ;
break ;
case ResultWas : : FatalErrorCondition :
colour = Colour : : Error ;
2022-01-03 23:16:39 +01:00
passOrFail = " FAILED " _sr ;
2020-09-08 15:53:08 +02:00
messageLabel = " due to a fatal error condition " ;
break ;
case ResultWas : : DidntThrowException :
colour = Colour : : Error ;
2022-01-03 23:16:39 +01:00
passOrFail = " FAILED " _sr ;
2020-09-08 15:53:08 +02:00
messageLabel = " because no exception was thrown where one was expected " ;
break ;
case ResultWas : : Info :
messageLabel = " info " ;
break ;
case ResultWas : : Warning :
messageLabel = " warning " ;
break ;
case ResultWas : : ExplicitFailure :
2022-01-03 23:16:39 +01:00
passOrFail = " FAILED " _sr ;
2020-09-08 15:53:08 +02:00
colour = Colour : : Error ;
if ( _stats . infoMessages . size ( ) = = 1 )
messageLabel = " explicitly with message " ;
if ( _stats . infoMessages . size ( ) > 1 )
messageLabel = " explicitly with messages " ;
break ;
// These cases are here to prevent compiler warnings
case ResultWas : : Unknown :
case ResultWas : : FailureBit :
case ResultWas : : Exception :
2022-01-03 23:16:39 +01:00
passOrFail = " ** internal error ** " _sr ;
2020-09-08 15:53:08 +02:00
colour = Colour : : Error ;
break ;
}
}
void print ( ) const {
printSourceInfo ( ) ;
if ( stats . totals . assertions . total ( ) > 0 ) {
printResultType ( ) ;
printOriginalExpression ( ) ;
printReconstructedExpression ( ) ;
} else {
stream < < ' \n ' ;
}
printMessage ( ) ;
}
private :
void printResultType ( ) const {
if ( ! passOrFail . empty ( ) ) {
Colour colourGuard ( colour ) ;
stream < < passOrFail < < " : \n " ;
}
}
void printOriginalExpression ( ) const {
if ( result . hasExpression ( ) ) {
Colour colourGuard ( Colour : : OriginalExpression ) ;
stream < < " " ;
stream < < result . getExpressionInMacro ( ) ;
stream < < ' \n ' ;
}
}
void printReconstructedExpression ( ) const {
if ( result . hasExpandedExpression ( ) ) {
stream < < " with expansion: \n " ;
Colour colourGuard ( Colour : : ReconstructedExpression ) ;
stream < < TextFlow : : Column ( result . getExpandedExpression ( ) ) . indent ( 2 ) < < ' \n ' ;
}
}
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 ' ;
}
}
void printSourceInfo ( ) const {
Colour colourGuard ( Colour : : FileName ) ;
stream < < result . getSourceInfo ( ) < < " : " ;
}
std : : ostream & stream ;
AssertionStats const & stats ;
AssertionResult const & result ;
Colour : : Code colour ;
2022-01-03 23:16:39 +01:00
StringRef passOrFail ;
2020-09-08 15:53:08 +02:00
std : : string messageLabel ;
std : : string message ;
std : : vector < MessageInfo > messages ;
bool printInfoMessages ;
} ;
2022-01-03 23:16:39 +01:00
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 < std : : size_t > ( ratio ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
std : : size_t & findMax ( std : : size_t & i , std : : size_t & j , std : : size_t & k ) {
2020-09-08 15:53:08 +02:00
if ( i > j & & i > k )
return i ;
else if ( j > k )
return j ;
else
return k ;
}
enum class Justification { Left , Right } ;
struct ColumnInfo {
std : : string name ;
2022-01-03 23:16:39 +01:00
std : : size_t width ;
2020-09-08 15:53:08 +02:00
Justification justification ;
} ;
struct ColumnBreak { } ;
struct RowBreak { } ;
class Duration {
enum class Unit {
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 ;
double m_inNanoseconds ;
Unit m_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 ;
}
}
auto value ( ) const - > double {
switch ( m_units ) {
case Unit : : Microseconds :
return m_inNanoseconds / static_cast < double > ( s_nanosecondsInAMicrosecond ) ;
case Unit : : Milliseconds :
return m_inNanoseconds / static_cast < double > ( s_nanosecondsInAMillisecond ) ;
case Unit : : Seconds :
return m_inNanoseconds / static_cast < double > ( s_nanosecondsInASecond ) ;
case Unit : : Minutes :
return m_inNanoseconds / static_cast < double > ( s_nanosecondsInAMinute ) ;
default :
return m_inNanoseconds ;
}
}
StringRef unitsAsString ( ) const {
switch ( m_units ) {
case Unit : : Nanoseconds :
return " ns " _sr ;
case Unit : : Microseconds :
return " us " _sr ;
case Unit : : Milliseconds :
return " ms " _sr ;
case Unit : : Seconds :
return " s " _sr ;
case Unit : : Minutes :
return " m " _sr ;
default :
return " ** internal error ** " _sr ;
}
}
friend auto operator < < ( std : : ostream & os , Duration const & duration ) - > std : : ostream & {
return os < < duration . value ( ) < < ' ' < < duration . unitsAsString ( ) ;
}
} ;
} // end anon namespace
class TablePrinter {
std : : ostream & m_os ;
std : : vector < ColumnInfo > m_columnInfos ;
ReusableStringStream m_oss ;
int m_currentColumn = - 1 ;
bool m_isOpen = false ;
public :
TablePrinter ( std : : ostream & os , std : : vector < ColumnInfo > columnInfos )
: m_os ( os ) ,
2022-01-03 23:16:39 +01:00
m_columnInfos ( CATCH_MOVE ( columnInfos ) ) { }
2020-09-08 15:53:08 +02:00
auto columnInfos ( ) const - > std : : vector < ColumnInfo > const & {
return m_columnInfos ;
}
void open ( ) {
if ( ! m_isOpen ) {
m_isOpen = true ;
* this < < RowBreak ( ) ;
TextFlow : : Columns headerCols ;
auto spacer = TextFlow : : Spacer ( 2 ) ;
for ( auto const & info : m_columnInfos ) {
2022-01-03 23:16:39 +01:00
assert ( info . width > 2 ) ;
headerCols + = TextFlow : : Column ( info . name ) . width ( info . width - 2 ) ;
2020-09-08 15:53:08 +02:00
headerCols + = spacer ;
}
m_os < < headerCols < < ' \n ' ;
m_os < < lineOfChars ( ' - ' ) < < ' \n ' ;
}
}
void close ( ) {
if ( m_isOpen ) {
* this < < RowBreak ( ) ;
2022-01-03 23:16:39 +01:00
m_os < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
m_isOpen = false ;
}
}
template < typename T >
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 < int > ( tp . m_columnInfos . size ( ) - 1 ) ) {
tp . m_currentColumn = - 1 ;
tp . m_os < < ' \n ' ;
}
tp . m_currentColumn + + ;
auto colInfo = tp . m_columnInfos [ tp . m_currentColumn ] ;
2022-01-03 23:16:39 +01:00
auto padding = ( strSize + 1 < colInfo . width )
2020-09-08 15:53:08 +02:00
? 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 ;
}
} ;
ConsoleReporter : : ConsoleReporter ( ReporterConfig const & config )
: StreamingReporterBase ( config ) ,
2022-01-03 23:16:39 +01:00
m_tablePrinter ( Detail : : make_unique < TablePrinter > ( config . stream ( ) ,
2020-09-08 15:53:08 +02:00
[ & config ] ( ) - > std : : vector < ColumnInfo > {
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 } ,
{ " estimated high mean high std dev " , 14 , Justification : : Right }
} ;
}
} ( ) ) ) { }
ConsoleReporter : : ~ ConsoleReporter ( ) = default ;
std : : string ConsoleReporter : : getDescription ( ) {
return " Reports test results as plain lines of text " ;
}
2022-01-03 23:16:39 +01:00
void ConsoleReporter : : noMatchingTestCases ( StringRef unmatchedSpec ) {
m_stream < < " No test cases matched ' " < < unmatchedSpec < < " ' \n " ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void ConsoleReporter : : reportInvalidTestSpec ( StringRef arg ) {
m_stream < < " Invalid Filter: " < < arg < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
void ConsoleReporter : : assertionStarting ( AssertionInfo const & ) { }
2022-01-03 23:16:39 +01:00
void ConsoleReporter : : assertionEnded ( AssertionStats const & _assertionStats ) {
2020-09-08 15:53:08 +02:00
AssertionResult const & result = _assertionStats . assertionResult ;
bool includeResults = m_config - > includeSuccessfulResults ( ) | | ! result . isOk ( ) ;
// Drop out if result was successful but we're not printing them.
if ( ! includeResults & & result . getResultType ( ) ! = ResultWas : : Warning )
2022-01-03 23:16:39 +01:00
return ;
2020-09-08 15:53:08 +02:00
lazyPrint ( ) ;
2022-01-03 23:16:39 +01:00
ConsoleAssertionPrinter printer ( m_stream , _assertionStats , includeResults ) ;
2020-09-08 15:53:08 +02:00
printer . print ( ) ;
2022-01-03 23:16:39 +01:00
m_stream < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
void ConsoleReporter : : sectionStarting ( SectionInfo const & _sectionInfo ) {
m_tablePrinter - > close ( ) ;
m_headerPrinted = false ;
StreamingReporterBase : : sectionStarting ( _sectionInfo ) ;
}
void ConsoleReporter : : sectionEnded ( SectionStats const & _sectionStats ) {
m_tablePrinter - > close ( ) ;
if ( _sectionStats . missingAssertions ) {
lazyPrint ( ) ;
Colour colour ( Colour : : ResultError ) ;
if ( m_sectionStack . size ( ) > 1 )
2022-01-03 23:16:39 +01:00
m_stream < < " \n No assertions in section " ;
2020-09-08 15:53:08 +02:00
else
2022-01-03 23:16:39 +01:00
m_stream < < " \n No assertions in test case " ;
m_stream < < " ' " < < _sectionStats . sectionInfo . name < < " ' \n \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
double dur = _sectionStats . durationInSeconds ;
if ( shouldShowDuration ( * m_config , dur ) ) {
2022-01-03 23:16:39 +01:00
m_stream < < getFormattedDuration ( dur ) < < " s: " < < _sectionStats . sectionInfo . name < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
if ( m_headerPrinted ) {
m_headerPrinted = false ;
}
StreamingReporterBase : : sectionEnded ( _sectionStats ) ;
}
2022-01-03 23:16:39 +01:00
void ConsoleReporter : : benchmarkPreparing ( StringRef name ) {
2020-09-08 15:53:08 +02:00
lazyPrintWithoutClosingBenchmarkTable ( ) ;
2022-01-03 23:16:39 +01:00
auto nameCol = TextFlow : : Column ( static_cast < std : : string > ( name ) )
. width ( m_tablePrinter - > columnInfos ( ) [ 0 ] . width - 2 ) ;
2020-09-08 15:53:08 +02:00
bool firstLine = true ;
for ( auto line : nameCol ) {
if ( ! firstLine )
( * m_tablePrinter ) < < ColumnBreak ( ) < < ColumnBreak ( ) < < ColumnBreak ( ) ;
else
firstLine = false ;
( * m_tablePrinter ) < < line < < ColumnBreak ( ) ;
}
}
void ConsoleReporter : : benchmarkStarting ( BenchmarkInfo const & info ) {
( * m_tablePrinter ) < < info . samples < < ColumnBreak ( )
< < info . iterations < < ColumnBreak ( ) ;
if ( ! m_config - > benchmarkNoAnalysis ( ) )
( * m_tablePrinter ) < < Duration ( info . estimatedDuration ) < < ColumnBreak ( ) ;
}
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 ( ) ;
}
}
2022-01-03 23:16:39 +01:00
void ConsoleReporter : : benchmarkFailed ( StringRef error ) {
2020-09-08 15:53:08 +02:00
Colour colour ( Colour : : Red ) ;
( * m_tablePrinter )
< < " Benchmark failed ( " < < error < < ' ) '
< < ColumnBreak ( ) < < RowBreak ( ) ;
}
void ConsoleReporter : : testCaseEnded ( TestCaseStats const & _testCaseStats ) {
m_tablePrinter - > close ( ) ;
StreamingReporterBase : : testCaseEnded ( _testCaseStats ) ;
m_headerPrinted = false ;
}
void ConsoleReporter : : testRunEnded ( TestRunStats const & _testRunStats ) {
printTotalsDivider ( _testRunStats . totals ) ;
printTotals ( _testRunStats . totals ) ;
2022-01-03 23:16:39 +01:00
m_stream < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
StreamingReporterBase : : testRunEnded ( _testRunStats ) ;
}
void ConsoleReporter : : testRunStarting ( TestRunInfo const & _testInfo ) {
StreamingReporterBase : : testRunStarting ( _testInfo ) ;
printTestFilters ( ) ;
}
void ConsoleReporter : : lazyPrint ( ) {
m_tablePrinter - > close ( ) ;
lazyPrintWithoutClosingBenchmarkTable ( ) ;
}
void ConsoleReporter : : lazyPrintWithoutClosingBenchmarkTable ( ) {
2022-01-03 23:16:39 +01:00
if ( ! m_testRunInfoPrinted ) {
2020-09-08 15:53:08 +02:00
lazyPrintRunInfo ( ) ;
2022-01-03 23:16:39 +01:00
}
2020-09-08 15:53:08 +02:00
if ( ! m_headerPrinted ) {
printTestCaseAndSectionHeader ( ) ;
m_headerPrinted = true ;
}
}
void ConsoleReporter : : lazyPrintRunInfo ( ) {
2022-01-03 23:16:39 +01:00
m_stream < < ' \n ' < < lineOfChars ( ' ~ ' ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
Colour colour ( Colour : : SecondaryText ) ;
2022-01-03 23:16:39 +01:00
m_stream < < currentTestRunInfo . name
2020-09-08 15:53:08 +02:00
< < " is a Catch v " < < libraryVersion ( ) < < " host application. \n "
< < " Run with -? for options \n \n " ;
2022-01-03 23:16:39 +01:00
m_stream < < " Randomness seeded to: " < < m_config - > rngSeed ( ) < < " \n \n " ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
m_testRunInfoPrinted = true ;
2020-09-08 15:53:08 +02:00
}
void ConsoleReporter : : printTestCaseAndSectionHeader ( ) {
assert ( ! m_sectionStack . empty ( ) ) ;
printOpenHeader ( currentTestCaseInfo - > name ) ;
if ( m_sectionStack . size ( ) > 1 ) {
Colour colourGuard ( Colour : : Headers ) ;
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 ;
2022-01-03 23:16:39 +01:00
m_stream < < lineOfChars ( ' - ' ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
Colour colourGuard ( Colour : : FileName ) ;
2022-01-03 23:16:39 +01:00
m_stream < < lineInfo < < ' \n ' ;
m_stream < < lineOfChars ( ' . ' ) < < " \n \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
void ConsoleReporter : : printClosedHeader ( std : : string const & _name ) {
printOpenHeader ( _name ) ;
2022-01-03 23:16:39 +01:00
m_stream < < lineOfChars ( ' . ' ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
void ConsoleReporter : : printOpenHeader ( std : : string const & _name ) {
2022-01-03 23:16:39 +01:00
m_stream < < lineOfChars ( ' - ' ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
{
Colour colourGuard ( Colour : : Headers ) ;
printHeaderString ( _name ) ;
}
}
void ConsoleReporter : : printHeaderString ( std : : string const & _string , std : : size_t indent ) {
2022-01-03 23:16:39 +01:00
// We want to get a bit fancy with line breaking here, so that subsequent
// lines start after ":" if one is present, e.g.
// ```
// blablabla: Fancy
// linebreaking
// ```
// but we also want to avoid problems with overly long indentation causing
// the text to take up too many lines, e.g.
// ```
// blablabla: F
// a
// n
// c
// y
// .
// .
// .
// ```
// 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 ;
} else {
idx = 0 ;
}
m_stream < < TextFlow : : Column ( _string )
. indent ( indent + idx )
. initialIndent ( indent )
< < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
struct SummaryColumn {
SummaryColumn ( std : : string _label , Colour : : Code _colour )
2022-01-03 23:16:39 +01:00
: label ( CATCH_MOVE ( _label ) ) ,
2020-09-08 15:53:08 +02:00
colour ( _colour ) { }
2022-01-03 23:16:39 +01:00
SummaryColumn addRow ( std : : uint64_t count ) {
2020-09-08 15:53:08 +02:00
ReusableStringStream rss ;
rss < < count ;
std : : string row = rss . str ( ) ;
for ( auto & oldRow : rows ) {
while ( oldRow . size ( ) < row . size ( ) )
oldRow = ' ' + oldRow ;
while ( oldRow . size ( ) > row . size ( ) )
row = ' ' + row ;
}
rows . push_back ( row ) ;
return * this ;
}
std : : string label ;
Colour : : Code colour ;
std : : vector < std : : string > rows ;
} ;
void ConsoleReporter : : printTotals ( Totals const & totals ) {
if ( totals . testCases . total ( ) = = 0 ) {
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : Warning ) < < " No tests ran \n " ;
2020-09-08 15:53:08 +02:00
} else if ( totals . assertions . total ( ) > 0 & & totals . testCases . allPassed ( ) ) {
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : ResultSuccess ) < < " All tests passed " ;
m_stream < < " ( "
< < pluralise ( totals . assertions . passed , " assertion " _sr ) < < " in "
< < pluralise ( totals . testCases . passed , " test case " _sr ) < < ' ) '
2020-09-08 15:53:08 +02:00
< < ' \n ' ;
} else {
std : : vector < SummaryColumn > columns ;
columns . push_back ( SummaryColumn ( " " , Colour : : None )
. addRow ( totals . testCases . total ( ) )
. addRow ( totals . assertions . total ( ) ) ) ;
columns . push_back ( SummaryColumn ( " passed " , Colour : : Success )
. addRow ( totals . testCases . passed )
. addRow ( totals . assertions . passed ) ) ;
columns . push_back ( SummaryColumn ( " failed " , Colour : : ResultError )
. addRow ( totals . testCases . failed )
. addRow ( totals . assertions . failed ) ) ;
columns . push_back ( SummaryColumn ( " failed as expected " , Colour : : ResultExpectedFailure )
. addRow ( totals . testCases . failedButOk )
. addRow ( totals . assertions . failedButOk ) ) ;
2022-01-03 23:16:39 +01:00
printSummaryRow ( " test cases " _sr , columns , 0 ) ;
printSummaryRow ( " assertions " _sr , columns , 1 ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ConsoleReporter : : printSummaryRow ( StringRef label , std : : vector < SummaryColumn > const & cols , std : : size_t row ) {
2020-09-08 15:53:08 +02:00
for ( auto col : cols ) {
std : : string value = col . rows [ row ] ;
if ( col . label . empty ( ) ) {
2022-01-03 23:16:39 +01:00
m_stream < < label < < " : " ;
2020-09-08 15:53:08 +02:00
if ( value ! = " 0 " )
2022-01-03 23:16:39 +01:00
m_stream < < value ;
2020-09-08 15:53:08 +02:00
else
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : Warning ) < < " - none - " ;
2020-09-08 15:53:08 +02:00
} else if ( value ! = " 0 " ) {
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : LightGrey ) < < " | " ;
m_stream < < Colour ( col . colour )
2020-09-08 15:53:08 +02:00
< < value < < ' ' < < col . label ;
}
}
2022-01-03 23:16:39 +01:00
m_stream < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
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 ( ) ) ;
while ( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1 )
findMax ( failedRatio , failedButOkRatio , passedRatio ) + + ;
while ( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1 )
findMax ( failedRatio , failedButOkRatio , passedRatio ) - - ;
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : Error ) < < std : : string ( failedRatio , ' = ' ) ;
m_stream < < Colour ( Colour : : ResultExpectedFailure ) < < std : : string ( failedButOkRatio , ' = ' ) ;
2020-09-08 15:53:08 +02:00
if ( totals . testCases . allPassed ( ) )
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : ResultSuccess ) < < std : : string ( passedRatio , ' = ' ) ;
2020-09-08 15:53:08 +02:00
else
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : Success ) < < std : : string ( passedRatio , ' = ' ) ;
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
m_stream < < Colour ( Colour : : Warning ) < < std : : string ( CATCH_CONFIG_CONSOLE_WIDTH - 1 , ' = ' ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
m_stream < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
void ConsoleReporter : : printSummaryDivider ( ) {
2022-01-03 23:16:39 +01:00
m_stream < < lineOfChars ( ' - ' ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
void ConsoleReporter : : printTestFilters ( ) {
if ( m_config - > testSpec ( ) . hasFilters ( ) ) {
Colour guard ( Colour : : BrightYellow ) ;
2022-01-03 23:16:39 +01:00
m_stream < < " Filters: " < < serializeFilters ( m_config - > getTestsOrTags ( ) ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
}
} // end namespace Catch
# if defined(_MSC_VER)
# pragma warning(pop)
# endif
# if defined(__clang__)
# pragma clang diagnostic pop
# endif
# include <algorithm>
# include <cassert>
namespace Catch {
namespace {
struct BySectionInfo {
BySectionInfo ( SectionInfo const & other ) : m_other ( other ) { }
BySectionInfo ( BySectionInfo const & other ) :
m_other ( other . m_other ) { }
bool operator ( ) (
2022-01-03 23:16:39 +01:00
Detail : : unique_ptr < CumulativeReporterBase : : SectionNode > const &
2020-09-08 15:53:08 +02:00
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 ;
} ;
} // namespace
2022-01-03 23:16:39 +01:00
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 ;
}
}
2020-09-08 15:53:08 +02:00
CumulativeReporterBase : : ~ CumulativeReporterBase ( ) = default ;
2022-01-03 23:16:39 +01:00
void CumulativeReporterBase : : benchmarkEnded ( BenchmarkStats < > const & benchmarkStats ) {
m_sectionStack . back ( ) - > assertionsAndBenchmarks . emplace_back ( benchmarkStats ) ;
}
2020-09-08 15:53:08 +02:00
void
CumulativeReporterBase : : sectionStarting ( SectionInfo const & sectionInfo ) {
SectionStats incompleteStats ( sectionInfo , Counts ( ) , 0 , false ) ;
2022-01-03 23:16:39 +01:00
SectionNode * node ;
2020-09-08 15:53:08 +02:00
if ( m_sectionStack . empty ( ) ) {
2022-01-03 23:16:39 +01:00
if ( ! m_rootSection ) {
2020-09-08 15:53:08 +02:00
m_rootSection =
2022-01-03 23:16:39 +01:00
Detail : : make_unique < SectionNode > ( incompleteStats ) ;
}
node = m_rootSection . get ( ) ;
2020-09-08 15:53:08 +02:00
} else {
SectionNode & parentNode = * m_sectionStack . back ( ) ;
auto it = std : : find_if ( parentNode . childSections . begin ( ) ,
parentNode . childSections . end ( ) ,
BySectionInfo ( sectionInfo ) ) ;
if ( it = = parentNode . childSections . end ( ) ) {
2022-01-03 23:16:39 +01:00
auto newNode =
Detail : : make_unique < SectionNode > ( incompleteStats ) ;
node = newNode . get ( ) ;
parentNode . childSections . push_back ( CATCH_MOVE ( newNode ) ) ;
2020-09-08 15:53:08 +02:00
} else {
2022-01-03 23:16:39 +01:00
node = it - > get ( ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
m_deepestSection = node ;
2020-09-08 15:53:08 +02:00
m_sectionStack . push_back ( node ) ;
}
2022-01-03 23:16:39 +01:00
void CumulativeReporterBase : : assertionEnded (
2020-09-08 15:53:08 +02:00
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.
2022-01-03 23:16:39 +01:00
if ( m_shouldStoreFailedAssertions & &
! assertionStats . assertionResult . isOk ( ) ) {
static_cast < void > (
assertionStats . assertionResult . getExpandedExpression ( ) ) ;
}
if ( m_shouldStoreSuccesfulAssertions & &
assertionStats . assertionResult . isOk ( ) ) {
static_cast < void > (
assertionStats . assertionResult . getExpandedExpression ( ) ) ;
}
2020-09-08 15:53:08 +02:00
SectionNode & sectionNode = * m_sectionStack . back ( ) ;
2022-01-03 23:16:39 +01:00
sectionNode . assertionsAndBenchmarks . emplace_back ( assertionStats ) ;
2020-09-08 15:53:08 +02:00
}
void CumulativeReporterBase : : sectionEnded ( SectionStats const & sectionStats ) {
assert ( ! m_sectionStack . empty ( ) ) ;
SectionNode & node = * m_sectionStack . back ( ) ;
node . stats = sectionStats ;
m_sectionStack . pop_back ( ) ;
}
void CumulativeReporterBase : : testCaseEnded (
TestCaseStats const & testCaseStats ) {
2022-01-03 23:16:39 +01:00
auto node = Detail : : make_unique < TestCaseNode > ( testCaseStats ) ;
2020-09-08 15:53:08 +02:00
assert ( m_sectionStack . size ( ) = = 0 ) ;
2022-01-03 23:16:39 +01:00
node - > children . push_back ( CATCH_MOVE ( m_rootSection ) ) ;
m_testCases . push_back ( CATCH_MOVE ( node ) ) ;
2020-09-08 15:53:08 +02:00
assert ( m_deepestSection ) ;
m_deepestSection - > stdOut = testCaseStats . stdOut ;
m_deepestSection - > stdErr = testCaseStats . stdErr ;
}
void CumulativeReporterBase : : testRunEnded ( TestRunStats const & testRunStats ) {
2022-01-03 23:16:39 +01:00
assert ( ! m_testRun & & " CumulativeReporterBase assumes there can only be one test run " ) ;
m_testRun = Detail : : make_unique < TestRunNode > ( testRunStats ) ;
m_testRun - > children . swap ( m_testCases ) ;
2020-09-08 15:53:08 +02:00
testRunEndedCumulative ( ) ;
}
2022-01-03 23:16:39 +01:00
void CumulativeReporterBase : : listReporters ( std : : vector < ReporterDescription > const & descriptions ) {
defaultListReporters ( m_stream , descriptions , m_config - > verbosity ( ) ) ;
}
void CumulativeReporterBase : : listTests ( std : : vector < TestCaseHandle > const & tests ) {
defaultListTests ( m_stream ,
tests ,
m_config - > hasTestFilters ( ) ,
m_config - > verbosity ( ) ) ;
}
void CumulativeReporterBase : : listTags ( std : : vector < TagInfo > const & tags ) {
defaultListTags ( m_stream , tags , m_config - > hasTestFilters ( ) ) ;
}
bool CumulativeReporterBase : : SectionNode : : hasAnyAssertions ( ) const {
return std : : any_of (
assertionsAndBenchmarks . begin ( ) ,
assertionsAndBenchmarks . end ( ) ,
[ ] ( Detail : : AssertionOrBenchmarkResult const & res ) {
return res . isAssertion ( ) ;
} ) ;
}
2020-09-08 15:53:08 +02:00
} // end namespace Catch
# include <cassert>
# include <ctime>
# include <algorithm>
2022-01-03 23:16:39 +01:00
# include <iomanip>
2020-09-08 15:53:08 +02:00
namespace Catch {
namespace {
std : : string getCurrentTimestamp ( ) {
time_t rawtime ;
std : : time ( & rawtime ) ;
std : : tm timeInfo = { } ;
2022-01-03 23:16:39 +01:00
# if defined (_MSC_VER) || defined (__MINGW32__)
2020-09-08 15:53:08 +02:00
gmtime_s ( & timeInfo , & rawtime ) ;
# else
2022-01-03 23:16:39 +01:00
gmtime_r ( & rawtime , & timeInfo ) ;
2020-09-08 15:53:08 +02:00
# endif
2022-01-03 23:16:39 +01:00
auto const timeStampSize = sizeof ( " 2017-01-16T17:06:45Z " ) ;
2020-09-08 15:53:08 +02:00
char timeStamp [ timeStampSize ] ;
const char * const fmt = " %Y-%m-%dT%H:%M:%SZ " ;
std : : strftime ( timeStamp , timeStampSize , fmt , & timeInfo ) ;
2022-01-03 23:16:39 +01:00
return std : : string ( timeStamp , timeStampSize - 1 ) ;
2020-09-08 15:53:08 +02:00
}
std : : string fileNameTag ( std : : vector < Tag > 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 < std : : string > (
it - > original . substr ( 1 , it - > original . size ( ) - 1 )
) ;
}
return std : : string ( ) ;
}
2022-01-03 23:16:39 +01:00
// 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 ( ) ;
}
2020-09-08 15:53:08 +02:00
} // anonymous namespace
JunitReporter : : JunitReporter ( ReporterConfig const & _config )
: CumulativeReporterBase ( _config ) ,
2022-01-03 23:16:39 +01:00
xml ( m_stream )
2020-09-08 15:53:08 +02:00
{
m_preferences . shouldRedirectStdOut = true ;
m_preferences . shouldReportAllAssertions = true ;
2022-01-03 23:16:39 +01:00
m_shouldStoreSuccesfulAssertions = false ;
2020-09-08 15:53:08 +02:00
}
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 ( ) ;
}
2022-01-03 23:16:39 +01:00
void JunitReporter : : assertionEnded ( AssertionStats const & assertionStats ) {
2020-09-08 15:53:08 +02:00
if ( assertionStats . assertionResult . getResultType ( ) = = ResultWas : : ThrewException & & ! m_okToFail )
unexpectedExceptions + + ;
2022-01-03 23:16:39 +01:00
CumulativeReporterBase : : assertionEnded ( assertionStats ) ;
2020-09-08 15:53:08 +02:00
}
void JunitReporter : : testCaseEnded ( TestCaseStats const & testCaseStats ) {
stdOutForSuite + = testCaseStats . stdOut ;
stdErrForSuite + = testCaseStats . stdErr ;
CumulativeReporterBase : : testCaseEnded ( testCaseStats ) ;
}
void JunitReporter : : testRunEndedCumulative ( ) {
2022-01-03 23:16:39 +01:00
const auto suiteTime = suiteTimer . getElapsedSeconds ( ) ;
writeRun ( * m_testRun , suiteTime ) ;
2020-09-08 15:53:08 +02:00
xml . endElement ( ) ;
}
2022-01-03 23:16:39 +01:00
void JunitReporter : : writeRun ( TestRunNode const & testRunNode , double suiteTime ) {
2020-09-08 15:53:08 +02:00
XmlWriter : : ScopedElement e = xml . scopedElement ( " testsuite " ) ;
2022-01-03 23:16:39 +01:00
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 ( " tests " _sr , stats . totals . assertions . total ( ) ) ;
xml . writeAttribute ( " hostname " _sr , " tbd " _sr ) ; // !TBD
2020-09-08 15:53:08 +02:00
if ( m_config - > showDurations ( ) = = ShowDurations : : Never )
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " time " _sr , " " _sr ) ;
2020-09-08 15:53:08 +02:00
else
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " time " _sr , formatDuration ( suiteTime ) ) ;
xml . writeAttribute ( " timestamp " _sr , getCurrentTimestamp ( ) ) ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
// Write properties
{
2020-09-08 15:53:08 +02:00
auto properties = xml . scopedElement ( " properties " ) ;
2022-01-03 23:16:39 +01:00
xml . scopedElement ( " property " )
. writeAttribute ( " name " _sr , " random-seed " _sr )
. writeAttribute ( " value " _sr , m_config - > rngSeed ( ) ) ;
2020-09-08 15:53:08 +02:00
if ( m_config - > hasTestFilters ( ) ) {
xml . scopedElement ( " property " )
2022-01-03 23:16:39 +01:00
. writeAttribute ( " name " _sr , " filters " _sr )
. writeAttribute ( " value " _sr , serializeFilters ( m_config - > getTestsOrTags ( ) ) ) ;
2020-09-08 15:53:08 +02:00
}
}
// Write test cases
2022-01-03 23:16:39 +01:00
for ( auto const & child : testRunNode . children )
2020-09-08 15:53:08 +02:00
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 ( ) ;
2022-01-03 23:16:39 +01:00
std : : string className =
static_cast < std : : string > ( stats . testInfo - > className ) ;
2020-09-08 15:53:08 +02:00
if ( className . empty ( ) ) {
className = fileNameTag ( stats . testInfo - > tags ) ;
2022-01-03 23:16:39 +01:00
if ( className . empty ( ) ) {
2020-09-08 15:53:08 +02:00
className = " global " ;
2022-01-03 23:16:39 +01:00
}
2020-09-08 15:53:08 +02:00
}
if ( ! m_config - > name ( ) . empty ( ) )
2022-01-03 23:16:39 +01:00
className = static_cast < std : : string > ( m_config - > name ( ) ) + ' . ' + className ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
writeSection ( className , " " , rootSection , stats . testInfo - > okToFail ( ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void JunitReporter : : writeSection ( std : : string const & className ,
std : : string const & rootName ,
SectionNode const & sectionNode ,
bool testOkToFail ) {
2020-09-08 15:53:08 +02:00
std : : string name = trim ( sectionNode . stats . sectionInfo . name ) ;
if ( ! rootName . empty ( ) )
name = rootName + ' / ' + name ;
2022-01-03 23:16:39 +01:00
if ( sectionNode . hasAnyAssertions ( )
| | ! sectionNode . stdOut . empty ( )
| | ! sectionNode . stdErr . empty ( ) ) {
2020-09-08 15:53:08 +02:00
XmlWriter : : ScopedElement e = xml . scopedElement ( " testcase " ) ;
if ( className . empty ( ) ) {
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " classname " _sr , name ) ;
xml . writeAttribute ( " name " _sr , " root " _sr ) ;
2020-09-08 15:53:08 +02:00
}
else {
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " classname " _sr , className ) ;
xml . writeAttribute ( " name " _sr , name ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " time " _sr , formatDuration ( sectionNode . stats . durationInSeconds ) ) ;
2020-09-08 15:53:08 +02:00
// 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.
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " status " _sr , " run " _sr ) ;
if ( sectionNode . stats . assertions . failedButOk ) {
xml . scopedElement ( " skipped " )
. writeAttribute ( " message " , " TEST_CASE tagged with !mayfail " ) ;
}
2020-09-08 15:53:08 +02:00
writeAssertions ( sectionNode ) ;
2022-01-03 23:16:39 +01:00
2020-09-08 15:53:08 +02:00
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 ( ) )
2022-01-03 23:16:39 +01:00
writeSection ( name , " " , * childNode , testOkToFail ) ;
2020-09-08 15:53:08 +02:00
else
2022-01-03 23:16:39 +01:00
writeSection ( className , name , * childNode , testOkToFail ) ;
2020-09-08 15:53:08 +02:00
}
void JunitReporter : : writeAssertions ( SectionNode const & sectionNode ) {
2022-01-03 23:16:39 +01:00
for ( auto const & assertionOrBenchmark : sectionNode . assertionsAndBenchmarks ) {
if ( assertionOrBenchmark . isAssertion ( ) ) {
writeAssertion ( assertionOrBenchmark . asAssertion ( ) ) ;
}
}
2020-09-08 15:53:08 +02:00
}
void JunitReporter : : writeAssertion ( AssertionStats const & stats ) {
AssertionResult const & result = stats . assertionResult ;
if ( ! result . isOk ( ) ) {
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 ;
// 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 ) ;
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " message " _sr , result . getExpression ( ) ) ;
xml . writeAttribute ( " type " _sr , result . getTestMacroName ( ) ) ;
2020-09-08 15:53:08 +02:00
ReusableStringStream rss ;
if ( stats . totals . assertions . total ( ) > 0 ) {
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 ' ;
}
} else {
rss < < ' \n ' ;
}
if ( ! result . getMessage ( ) . empty ( ) )
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 ) ;
}
}
} // end namespace Catch
2022-01-03 23:16:39 +01:00
2020-09-08 15:53:08 +02:00
# include <cassert>
namespace Catch {
2022-01-03 23:16:39 +01:00
void ListeningReporter : : updatePreferences ( IStreamingReporter const & reporterish ) {
m_preferences . shouldRedirectStdOut | =
reporterish . getPreferences ( ) . shouldRedirectStdOut ;
m_preferences . shouldReportAllAssertions | =
reporterish . getPreferences ( ) . shouldReportAllAssertions ;
2020-09-08 15:53:08 +02:00
}
void ListeningReporter : : addListener ( IStreamingReporterPtr & & listener ) {
2022-01-03 23:16:39 +01:00
updatePreferences ( * listener ) ;
m_reporterLikes . insert ( m_reporterLikes . begin ( ) + m_insertedListeners , CATCH_MOVE ( listener ) ) ;
+ + m_insertedListeners ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : addReporter ( IStreamingReporterPtr & & 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 ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : noMatchingTestCases ( StringRef unmatchedSpec ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > noMatchingTestCases ( unmatchedSpec ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : fatalErrorEncountered ( StringRef error ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > fatalErrorEncountered ( error ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : reportInvalidTestSpec ( StringRef arg ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > reportInvalidTestSpec ( arg ) ;
}
}
void ListeningReporter : : benchmarkPreparing ( StringRef name ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > benchmarkPreparing ( name ) ;
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : benchmarkStarting ( BenchmarkInfo const & benchmarkInfo ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > benchmarkStarting ( benchmarkInfo ) ;
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : benchmarkEnded ( BenchmarkStats < > const & benchmarkStats ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > benchmarkEnded ( benchmarkStats ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : benchmarkFailed ( StringRef error ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > benchmarkFailed ( error ) ;
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : testRunStarting ( TestRunInfo const & testRunInfo ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > testRunStarting ( testRunInfo ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : testCaseStarting ( TestCaseInfo const & testInfo ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > testCaseStarting ( testInfo ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void
ListeningReporter : : testCasePartialStarting ( TestCaseInfo const & testInfo ,
uint64_t partNumber ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > testCasePartialStarting ( testInfo , partNumber ) ;
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : sectionStarting ( SectionInfo const & sectionInfo ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > sectionStarting ( sectionInfo ) ;
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : assertionStarting ( AssertionInfo const & assertionInfo ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > assertionStarting ( assertionInfo ) ;
2020-09-08 15:53:08 +02:00
}
}
// The return value indicates if the messages buffer should be cleared:
2022-01-03 23:16:39 +01:00
void ListeningReporter : : 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 ) ;
}
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : sectionEnded ( SectionStats const & sectionStats ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > sectionEnded ( sectionStats ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : 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 ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : testCaseEnded ( TestCaseStats const & testCaseStats ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > testCaseEnded ( testCaseStats ) ;
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : testRunEnded ( TestRunStats const & testRunStats ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > testRunEnded ( testRunStats ) ;
2020-09-08 15:53:08 +02:00
}
}
void ListeningReporter : : skipTest ( TestCaseInfo const & testInfo ) {
2022-01-03 23:16:39 +01:00
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > skipTest ( testInfo ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : listReporters ( std : : vector < ReporterDescription > const & descriptions ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > listReporters ( descriptions ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : listTests ( std : : vector < TestCaseHandle > const & tests ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > listTests ( tests ) ;
2020-09-08 15:53:08 +02:00
}
}
2022-01-03 23:16:39 +01:00
void ListeningReporter : : listTags ( std : : vector < TagInfo > const & tags ) {
for ( auto & reporterish : m_reporterLikes ) {
reporterish - > listTags ( tags ) ;
2020-09-08 15:53:08 +02:00
}
}
} // end namespace Catch
# include <map>
namespace Catch {
void SonarQubeReporter : : testRunStarting ( TestRunInfo const & testRunInfo ) {
CumulativeReporterBase : : testRunStarting ( testRunInfo ) ;
xml . startElement ( " testExecutions " ) ;
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " version " _sr , ' 1 ' ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void SonarQubeReporter : : writeRun ( TestRunNode const & runNode ) {
std : : map < std : : string , std : : vector < TestCaseNode const * > > testsPerFile ;
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
for ( auto const & child : runNode . children ) {
testsPerFile [ child - > value . testInfo - > lineInfo . file ] . push_back (
child . get ( ) ) ;
}
2020-09-08 15:53:08 +02:00
2022-01-03 23:16:39 +01:00
for ( auto const & kv : testsPerFile ) {
writeTestFile ( kv . first , kv . second ) ;
}
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void SonarQubeReporter : : writeTestFile ( std : : string const & filename , std : : vector < TestCaseNode const * > const & testCaseNodes ) {
2020-09-08 15:53:08 +02:00
XmlWriter : : ScopedElement e = xml . scopedElement ( " file " ) ;
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " path " _sr , filename ) ;
2020-09-08 15:53:08 +02:00
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 ;
2022-01-03 23:16:39 +01:00
if ( sectionNode . hasAnyAssertions ( )
| | ! sectionNode . stdOut . empty ( )
| | ! sectionNode . stdErr . empty ( ) ) {
2020-09-08 15:53:08 +02:00
XmlWriter : : ScopedElement e = xml . scopedElement ( " testCase " ) ;
2022-01-03 23:16:39 +01:00
xml . writeAttribute ( " name " _sr , name ) ;
xml . writeAttribute ( " duration " _sr , static_cast < long > ( sectionNode . stats . durationInSeconds * 1000 ) ) ;
2020-09-08 15:53:08 +02:00
writeAssertions ( sectionNode , okToFail ) ;
}
for ( auto const & childNode : sectionNode . childSections )
writeSection ( name , * childNode , okToFail ) ;
}
void SonarQubeReporter : : writeAssertions ( SectionNode const & sectionNode , bool okToFail ) {
2022-01-03 23:16:39 +01:00
for ( auto const & assertionOrBenchmark : sectionNode . assertionsAndBenchmarks ) {
if ( assertionOrBenchmark . isAssertion ( ) ) {
writeAssertion ( assertionOrBenchmark . asAssertion ( ) , okToFail ) ;
}
}
2020-09-08 15:53:08 +02:00
}
void SonarQubeReporter : : writeAssertion ( AssertionStats const & stats , bool okToFail ) {
AssertionResult const & result = stats . assertionResult ;
if ( ! result . isOk ( ) ) {
std : : string elementName ;
if ( okToFail ) {
elementName = " skipped " ;
} else {
switch ( result . getResultType ( ) ) {
case ResultWas : : ThrewException :
case ResultWas : : FatalErrorCondition :
elementName = " error " ;
break ;
case ResultWas : : ExplicitFailure :
elementName = " failure " ;
break ;
case ResultWas : : ExpressionFailed :
elementName = " failure " ;
break ;
case ResultWas : : DidntThrowException :
elementName = " failure " ;
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 ;
2022-01-03 23:16:39 +01:00
messageRss < < result . getTestMacroName ( ) < < ' ( ' < < result . getExpression ( ) < < ' ) ' ;
xml . writeAttribute ( " message " _sr , messageRss . str ( ) ) ;
2020-09-08 15:53:08 +02:00
ReusableStringStream textRss ;
if ( stats . totals . assertions . total ( ) > 0 ) {
textRss < < " FAILED: \n " ;
if ( result . hasExpression ( ) ) {
2022-01-03 23:16:39 +01:00
textRss < < ' \t ' < < result . getExpressionInMacro ( ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
if ( result . hasExpandedExpression ( ) ) {
2022-01-03 23:16:39 +01:00
textRss < < " with expansion: \n \t " < < result . getExpandedExpression ( ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
}
}
if ( ! result . getMessage ( ) . empty ( ) )
2022-01-03 23:16:39 +01:00
textRss < < result . getMessage ( ) < < ' \n ' ;
2020-09-08 15:53:08 +02:00
for ( auto const & msg : stats . infoMessages )
if ( msg . type = = ResultWas : : Info )
2022-01-03 23:16:39 +01:00
textRss < < msg . message < < ' \n ' ;
2020-09-08 15:53:08 +02:00
textRss < < " at " < < result . getSourceInfo ( ) ;
xml . writeText ( textRss . str ( ) , XmlFormatting : : Newline ) ;
}
}
} // end namespace Catch
namespace Catch {
StreamingReporterBase : : ~ StreamingReporterBase ( ) = default ;
void
StreamingReporterBase : : testRunStarting ( TestRunInfo const & _testRunInfo ) {
currentTestRunInfo = _testRunInfo ;
}
2022-01-03 23:16:39 +01:00
void StreamingReporterBase : : testRunEnded ( TestRunStats const & ) {
currentTestCaseInfo = nullptr ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void StreamingReporterBase : : listReporters ( std : : vector < ReporterDescription > const & descriptions ) {
defaultListReporters ( m_stream , descriptions , m_config - > verbosity ( ) ) ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void StreamingReporterBase : : listTests ( std : : vector < TestCaseHandle > const & tests ) {
defaultListTests ( m_stream ,
tests ,
m_config - > hasTestFilters ( ) ,
m_config - > verbosity ( ) ) ;
}
void StreamingReporterBase : : listTags ( std : : vector < TagInfo > const & tags ) {
defaultListTags ( m_stream , tags , m_config - > hasTestFilters ( ) ) ;
2020-09-08 15:53:08 +02:00
}
} // end namespace Catch
# include <algorithm>
# include <ostream>
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 ;
class TapAssertionPrinter {
public :
TapAssertionPrinter & operator = ( TapAssertionPrinter const & ) = delete ;
TapAssertionPrinter ( TapAssertionPrinter const & ) = delete ;
TapAssertionPrinter ( std : : ostream & _stream , AssertionStats const & _stats , std : : size_t _counter )
: stream ( _stream )
, result ( _stats . assertionResult )
, messages ( _stats . infoMessages )
, itMessage ( _stats . infoMessages . begin ( ) )
, printInfoMessages ( true )
, counter ( _counter ) { }
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 ;
// These cases are here to prevent compiler warnings
case ResultWas : : Unknown :
case ResultWas : : FailureBit :
case ResultWas : : Exception :
printResultType ( " ** internal error ** " _sr ) ;
break ;
}
}
private :
static Colour : : Code dimColour ( ) { return Colour : : FileName ; }
void printSourceInfo ( ) const {
Colour colourGuard ( dimColour ( ) ) ;
stream < < result . getSourceInfo ( ) < < ' : ' ;
}
void printResultType ( StringRef passOrFail ) const {
if ( ! passOrFail . empty ( ) ) {
stream < < passOrFail < < ' ' < < counter < < " - " ;
}
}
void printIssue ( StringRef issue ) const {
stream < < ' ' < < issue ;
}
void printExpressionWas ( ) {
if ( result . hasExpression ( ) ) {
stream < < ' ; ' ;
{
Colour colour ( dimColour ( ) ) ;
stream < < " expression was: " ;
}
printOriginalExpression ( ) ;
}
}
void printOriginalExpression ( ) const {
if ( result . hasExpression ( ) ) {
stream < < ' ' < < result . getExpression ( ) ;
}
}
void printReconstructedExpression ( ) const {
if ( result . hasExpandedExpression ( ) ) {
{
Colour colour ( dimColour ( ) ) ;
stream < < " 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 = dimColour ( ) ) {
if ( itMessage = = messages . end ( ) ) {
return ;
}
// using messages.end() directly (or auto) yields compilation error:
std : : vector < MessageInfo > : : const_iterator itEnd = messages . end ( ) ;
const std : : size_t N = static_cast < std : : size_t > ( std : : distance ( itMessage , itEnd ) ) ;
{
Colour colourGuard ( colour ) ;
2022-01-03 23:16:39 +01:00
stream < < " with " < < pluralise ( N , " message " _sr ) < < ' : ' ;
2020-09-08 15:53:08 +02:00
}
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 ) {
Colour colourGuard ( dimColour ( ) ) ;
stream < < " and " ;
}
}
}
}
private :
std : : ostream & stream ;
AssertionResult const & result ;
std : : vector < MessageInfo > messages ;
std : : vector < MessageInfo > : : const_iterator itMessage ;
bool printInfoMessages ;
std : : size_t counter ;
} ;
} // End anonymous namespace
2022-01-03 23:16:39 +01:00
void TAPReporter : : noMatchingTestCases ( StringRef unmatchedSpec ) {
m_stream < < " # No test cases matched ' " < < unmatchedSpec < < " ' \n " ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void TAPReporter : : assertionEnded ( AssertionStats const & _assertionStats ) {
2020-09-08 15:53:08 +02:00
+ + counter ;
2022-01-03 23:16:39 +01:00
m_stream < < " # " < < currentTestCaseInfo - > name < < ' \n ' ;
TapAssertionPrinter printer ( m_stream , _assertionStats , counter ) ;
2020-09-08 15:53:08 +02:00
printer . print ( ) ;
2022-01-03 23:16:39 +01:00
m_stream < < ' \n ' < < std : : flush ;
2020-09-08 15:53:08 +02:00
}
void TAPReporter : : testRunEnded ( TestRunStats const & _testRunStats ) {
2022-01-03 23:16:39 +01:00
m_stream < < " 1.. " < < _testRunStats . totals . assertions . total ( ) ;
2020-09-08 15:53:08 +02:00
if ( _testRunStats . totals . testCases . total ( ) = = 0 ) {
2022-01-03 23:16:39 +01:00
m_stream < < " # Skipped: No tests ran. " ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
m_stream < < " \n \n " < < std : : flush ;
2020-09-08 15:53:08 +02:00
StreamingReporterBase : : testRunEnded ( _testRunStats ) ;
}
} // end namespace Catch
# include <cassert>
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 ' ;
}
2022-01-03 23:16:39 +01:00
std : : string escape ( StringRef str ) {
std : : string escaped = static_cast < std : : string > ( str ) ;
2020-09-08 15:53:08 +02:00
replaceInPlace ( escaped , " | " , " || " ) ;
replaceInPlace ( escaped , " ' " , " |' " ) ;
replaceInPlace ( escaped , " \n " , " |n " ) ;
replaceInPlace ( escaped , " \r " , " |r " ) ;
replaceInPlace ( escaped , " [ " , " |[ " ) ;
replaceInPlace ( escaped , " ] " , " |] " ) ;
return escaped ;
}
} // end anonymous namespace
TeamCityReporter : : ~ TeamCityReporter ( ) { }
2022-01-03 23:16:39 +01:00
void TeamCityReporter : : testRunStarting ( TestRunInfo const & runInfo ) {
m_stream < < " ##teamcity[testSuiteStarted name=' " < < escape ( runInfo . name )
< < " '] \n " ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void TeamCityReporter : : testRunEnded ( TestRunStats const & runStats ) {
m_stream < < " ##teamcity[testSuiteFinished name=' "
< < escape ( runStats . runInfo . name ) < < " '] \n " ;
2020-09-08 15:53:08 +02:00
}
2022-01-03 23:16:39 +01:00
void TeamCityReporter : : assertionEnded ( AssertionStats const & assertionStats ) {
2020-09-08 15:53:08 +02:00
AssertionResult const & result = assertionStats . assertionResult ;
if ( ! result . isOk ( ) ) {
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 ;
// 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 ( currentTestCaseInfo - > okToFail ( ) ) {
msg < < " - failure ignore as test marked as 'ok to fail' \n " ;
2022-01-03 23:16:39 +01:00
m_stream < < " ##teamcity[testIgnored "
2020-09-08 15:53:08 +02:00
< < " name=' " < < escape ( currentTestCaseInfo - > name ) < < ' \' '
< < " message=' " < < escape ( msg . str ( ) ) < < ' \' '
< < " ] \n " ;
} else {
2022-01-03 23:16:39 +01:00
m_stream < < " ##teamcity[testFailed "
2020-09-08 15:53:08 +02:00
< < " name=' " < < escape ( currentTestCaseInfo - > name ) < < ' \' '
< < " message=' " < < escape ( msg . str ( ) ) < < ' \' '
< < " ] \n " ;
}
}
2022-01-03 23:16:39 +01:00
m_stream . flush ( ) ;
2020-09-08 15:53:08 +02:00
}
void TeamCityReporter : : testCaseStarting ( TestCaseInfo const & testInfo ) {
m_testTimer . start ( ) ;
StreamingReporterBase : : testCaseStarting ( testInfo ) ;
2022-01-03 23:16:39 +01:00
m_stream < < " ##teamcity[testStarted name=' "
2020-09-08 15:53:08 +02:00
< < escape ( testInfo . name ) < < " '] \n " ;
2022-01-03 23:16:39 +01:00
m_stream . flush ( ) ;
2020-09-08 15:53:08 +02:00
}
void TeamCityReporter : : testCaseEnded ( TestCaseStats const & testCaseStats ) {
StreamingReporterBase : : testCaseEnded ( testCaseStats ) ;
auto const & testCaseInfo = * testCaseStats . testInfo ;
if ( ! testCaseStats . stdOut . empty ( ) )
2022-01-03 23:16:39 +01:00
m_stream < < " ##teamcity[testStdOut name=' "
2020-09-08 15:53:08 +02:00
< < escape ( testCaseInfo . name )
< < " ' out=' " < < escape ( testCaseStats . stdOut ) < < " '] \n " ;
if ( ! testCaseStats . stdErr . empty ( ) )
2022-01-03 23:16:39 +01:00
m_stream < < " ##teamcity[testStdErr name=' "
2020-09-08 15:53:08 +02:00
< < escape ( testCaseInfo . name )
< < " ' out=' " < < escape ( testCaseStats . stdErr ) < < " '] \n " ;
2022-01-03 23:16:39 +01:00
m_stream < < " ##teamcity[testFinished name=' "
2020-09-08 15:53:08 +02:00
< < escape ( testCaseInfo . name ) < < " ' duration=' "
< < m_testTimer . getElapsedMilliseconds ( ) < < " '] \n " ;
2022-01-03 23:16:39 +01:00
m_stream . flush ( ) ;
2020-09-08 15:53:08 +02:00
}
void TeamCityReporter : : printSectionHeader ( std : : ostream & os ) {
assert ( ! m_sectionStack . empty ( ) ) ;
if ( m_sectionStack . size ( ) > 1 ) {
os < < lineOfChars ( ' - ' ) < < ' \n ' ;
std : : vector < SectionInfo > : : 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 " ;
}
} // 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
# endif
namespace Catch {
XmlReporter : : XmlReporter ( ReporterConfig const & _config )
: StreamingReporterBase ( _config ) ,
m_xml ( _config . stream ( ) )
{
m_preferences . shouldRedirectStdOut = true ;
m_preferences . shouldReportAllAssertions = true ;
}
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
2022-01-03 23:16:39 +01:00
. writeAttribute ( " filename " _sr , sourceInfo . file )
. writeAttribute ( " line " _sr , sourceInfo . line ) ;
2020-09-08 15:53:08 +02:00
}
void XmlReporter : : testRunStarting ( TestRunInfo const & testInfo ) {
StreamingReporterBase : : testRunStarting ( testInfo ) ;
std : : string stylesheetRef = getStylesheetRef ( ) ;
if ( ! stylesheetRef . empty ( ) )
m_xml . writeStylesheetRef ( stylesheetRef ) ;
2022-01-03 23:16:39 +01:00
m_xml . startElement ( " Catch2TestRun " )
. writeAttribute ( " name " _sr , m_config - > name ( ) )
. writeAttribute ( " rng-seed " _sr , m_config - > rngSeed ( ) ) ;
2020-09-08 15:53:08 +02:00
if ( m_config - > testSpec ( ) . hasFilters ( ) )
2022-01-03 23:16:39 +01:00
m_xml . writeAttribute ( " filters " _sr , serializeFilters ( m_config - > getTestsOrTags ( ) ) ) ;
2020-09-08 15:53:08 +02:00
}
void XmlReporter : : testCaseStarting ( TestCaseInfo const & testInfo ) {
StreamingReporterBase : : testCaseStarting ( testInfo ) ;
m_xml . startElement ( " TestCase " )
2022-01-03 23:16:39 +01:00
. writeAttribute ( " name " _sr , trim ( testInfo . name ) )
. writeAttribute ( " tags " _sr , testInfo . tagsAsString ( ) ) ;
2020-09-08 15:53:08 +02:00
writeSourceInfo ( testInfo . lineInfo ) ;
if ( m_config - > showDurations ( ) = = ShowDurations : : Always )
m_testCaseTimer . start ( ) ;
m_xml . ensureTagClosed ( ) ;
}
void XmlReporter : : sectionStarting ( SectionInfo const & sectionInfo ) {
StreamingReporterBase : : sectionStarting ( sectionInfo ) ;
if ( m_sectionDepth + + > 0 ) {
m_xml . startElement ( " Section " )
2022-01-03 23:16:39 +01:00
. writeAttribute ( " name " _sr , trim ( sectionInfo . name ) ) ;
2020-09-08 15:53:08 +02:00
writeSourceInfo ( sectionInfo . lineInfo ) ;
m_xml . ensureTagClosed ( ) ;
}
}
void XmlReporter : : assertionStarting ( AssertionInfo const & ) { }
2022-01-03 23:16:39 +01:00
void XmlReporter : : assertionEnded ( AssertionStats const & assertionStats ) {
2020-09-08 15:53:08 +02:00
AssertionResult const & result = assertionStats . assertionResult ;
bool includeResults = m_config - > includeSuccessfulResults ( ) | | ! result . isOk ( ) ;
if ( includeResults | | result . getResultType ( ) = = ResultWas : : Warning ) {
// Print any info messages in <Info> tags.
for ( auto const & msg : assertionStats . infoMessages ) {
if ( msg . type = = ResultWas : : Info & & includeResults ) {
m_xml . scopedElement ( " Info " )
. writeText ( msg . message ) ;
} else if ( msg . type = = ResultWas : : Warning ) {
m_xml . scopedElement ( " Warning " )
. writeText ( msg . message ) ;
}
}
}
// Drop out if result was successful but we're not printing them.
if ( ! includeResults & & result . getResultType ( ) ! = ResultWas : : Warning )
2022-01-03 23:16:39 +01:00
return ;
2020-09-08 15:53:08 +02:00
// Print the expression if there is one.
if ( result . hasExpression ( ) ) {
m_xml . startElement ( " Expression " )
2022-01-03 23:16:39 +01:00
. writeAttribute ( " success " _sr , result . succeeded ( ) )
. writeAttribute ( " type " _sr , result . getTestMacroName ( ) ) ;
2020-09-08 15:53:08 +02:00
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 " )
2022-01-03 23:16:39 +01:00
. writeText ( result . getMessage ( ) ) ;
2020-09-08 15:53:08 +02:00
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 ;
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 " ) ;
2022-01-03 23:16:39 +01:00
e . writeAttribute ( " successes " _sr , sectionStats . assertions . passed ) ;
e . writeAttribute ( " failures " _sr , sectionStats . assertions . failed ) ;
e . writeAttribute ( " expectedFailures " _sr , sectionStats . assertions . failedButOk ) ;
2020-09-08 15:53:08 +02:00
if ( m_config - > showDurations ( ) = = ShowDurations : : Always )
2022-01-03 23:16:39 +01:00
e . writeAttribute ( " durationInSeconds " _sr , sectionStats . durationInSeconds ) ;
2020-09-08 15:53:08 +02:00
m_xml . endElement ( ) ;
}
}
void XmlReporter : : testCaseEnded ( TestCaseStats const & testCaseStats ) {
StreamingReporterBase : : testCaseEnded ( testCaseStats ) ;
XmlWriter : : ScopedElement e = m_xml . scopedElement ( " OverallResult " ) ;
2022-01-03 23:16:39 +01:00
e . writeAttribute ( " success " _sr , testCaseStats . totals . assertions . allOk ( ) ) ;
2020-09-08 15:53:08 +02:00
if ( m_config - > showDurations ( ) = = ShowDurations : : Always )
2022-01-03 23:16:39 +01:00
e . writeAttribute ( " durationInSeconds " _sr , m_testCaseTimer . getElapsedSeconds ( ) ) ;
2020-09-08 15:53:08 +02:00
if ( ! testCaseStats . stdOut . empty ( ) )
m_xml . scopedElement ( " StdOut " ) . writeText ( trim ( testCaseStats . stdOut ) , XmlFormatting : : Newline ) ;
if ( ! testCaseStats . stdErr . empty ( ) )
m_xml . scopedElement ( " StdErr " ) . writeText ( trim ( testCaseStats . stdErr ) , XmlFormatting : : Newline ) ;
m_xml . endElement ( ) ;
}
void XmlReporter : : testRunEnded ( TestRunStats const & testRunStats ) {
StreamingReporterBase : : testRunEnded ( testRunStats ) ;
m_xml . scopedElement ( " OverallResults " )
2022-01-03 23:16:39 +01:00
. writeAttribute ( " successes " _sr , testRunStats . totals . assertions . passed )
. writeAttribute ( " failures " _sr , testRunStats . totals . assertions . failed )
. writeAttribute ( " expectedFailures " _sr , testRunStats . totals . assertions . failedButOk ) ;
2020-09-08 15:53:08 +02:00
m_xml . scopedElement ( " OverallResultsCases " )
2022-01-03 23:16:39 +01:00
. writeAttribute ( " successes " _sr , testRunStats . totals . testCases . passed )
. writeAttribute ( " failures " _sr , testRunStats . totals . testCases . failed )
. writeAttribute ( " expectedFailures " _sr , testRunStats . totals . testCases . failedButOk ) ;
2020-09-08 15:53:08 +02:00
m_xml . endElement ( ) ;
}
2022-01-03 23:16:39 +01:00
void XmlReporter : : benchmarkPreparing ( StringRef name ) {
2020-09-08 15:53:08 +02:00
m_xml . startElement ( " BenchmarkResults " )
2022-01-03 23:16:39 +01:00
. writeAttribute ( " name " _sr , name ) ;
2020-09-08 15:53:08 +02:00
}
void XmlReporter : : benchmarkStarting ( BenchmarkInfo const & info ) {
2022-01-03 23:16:39 +01:00
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 ) ;
2020-09-08 15:53:08 +02:00
}
void XmlReporter : : benchmarkEnded ( BenchmarkStats < > const & benchmarkStats ) {
m_xml . startElement ( " mean " )
2022-01-03 23:16:39 +01:00
. 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 ) ;
2020-09-08 15:53:08 +02:00
m_xml . endElement ( ) ;
m_xml . startElement ( " standardDeviation " )
2022-01-03 23:16:39 +01:00
. 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 ) ;
2020-09-08 15:53:08 +02:00
m_xml . endElement ( ) ;
m_xml . startElement ( " outliers " )
2022-01-03 23:16:39 +01:00
. 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 ) ;
2020-09-08 15:53:08 +02:00
m_xml . endElement ( ) ;
m_xml . endElement ( ) ;
}
2022-01-03 23:16:39 +01:00
void XmlReporter : : benchmarkFailed ( StringRef error ) {
2020-09-08 15:53:08 +02:00
m_xml . scopedElement ( " failed " ) .
2022-01-03 23:16:39 +01:00
writeAttribute ( " message " _sr , error ) ;
2020-09-08 15:53:08 +02:00
m_xml . endElement ( ) ;
}
2022-01-03 23:16:39 +01:00
void XmlReporter : : listReporters ( std : : vector < ReporterDescription > const & descriptions ) {
2020-09-08 15:53:08 +02:00
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 ) ;
}
}
2022-01-03 23:16:39 +01:00
void XmlReporter : : listTests ( std : : vector < TestCaseHandle > const & tests ) {
2020-09-08 15:53:08 +02:00
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 ) ;
}
}
2022-01-03 23:16:39 +01:00
void XmlReporter : : listTags ( std : : vector < TagInfo > const & tags ) {
2020-09-08 15:53:08 +02:00
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 )
2022-01-03 23:16:39 +01:00
. writeText ( alias , XmlFormatting : : None )
2020-09-08 15:53:08 +02:00
. endElement ( XmlFormatting : : Newline ) ;
}
}
}
} // end namespace Catch
# if defined(_MSC_VER)
# pragma warning(pop)
# endif