15#include "catch_amalgamated.hpp"
18#ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
19#define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED
22#if defined(CATCH_PLATFORM_WINDOWS)
29#if !defined(WIN32_LEAN_AND_MEAN)
30# define WIN32_LEAN_AND_MEAN
45 ChronometerConcept::~ChronometerConcept() =
default;
56 BenchmarkFunction::callable::~callable() =
default;
70 const char*
what()
const noexcept override;
74 return "could not measure benchmark, maybe it was optimized away";
96#if defined(CATCH_CONFIG_USE_ASYNC)
101 namespace Benchmark {
105 template <
typename URng,
typename Estimator>
108 unsigned int resamples,
109 std::vector<double>::const_iterator
first,
110 std::vector<double>::const_iterator last,
111 Estimator& estimator ) {
113 std::uniform_int_distribution<
decltype(
n )> dist( 0,
117 out.reserve( resamples );
120 std::vector<double> resampled;
121 resampled.reserve(
n );
122 for (
size_t i = 0;
i < resamples; ++
i ) {
124 for (
size_t s = 0;
s <
n; ++
s ) {
126 first[
static_cast<std::ptrdiff_t
>(
129 const auto estimate =
130 estimator( resampled.begin(), resampled.end() );
131 out.push_back( estimate );
133 std::sort( out.begin(), out.end() );
137 static double outlier_variance( Estimate<double>
mean,
138 Estimate<double> stddev,
140 double sb = stddev.point;
141 double mn =
mean.point /
n;
142 double mg_min = mn / 2.;
143 double sg = (std::min)( mg_min / 4., sb / std::sqrt(
n ) );
144 double sg2 = sg * sg;
145 double sb2 = sb * sb;
147 auto c_max = [
n, mn, sb2, sg2](
double x ) ->
double {
152 double k1 = sb2 -
n * sg2 + nd;
153 double det = k1 * k1 - 4 * sg2 * k0;
154 return static_cast<int>( -2. * k0 /
155 ( k1 + std::sqrt( det ) ) );
158 auto var_out = [
n, sb2, sg2](
double c ) {
160 return ( nc /
n ) * ( sb2 - nc * sg2 );
163 return (std::min)( var_out( 1 ),
165 (std::min)( c_max( 0. ),
166 c_max( mg_min ) ) ) ) /
170 static double erf_inv(
double x ) {
175 w = -log( ( 1.0 -
x ) * ( 1.0 +
x ) );
177 if (
w < 6.250000 ) {
179 p = -3.6444120640178196996e-21;
180 p = -1.685059138182016589e-19 + p *
w;
181 p = 1.2858480715256400167e-18 + p *
w;
182 p = 1.115787767802518096e-17 + p *
w;
183 p = -1.333171662854620906e-16 + p *
w;
184 p = 2.0972767875968561637e-17 + p *
w;
185 p = 6.6376381343583238325e-15 + p *
w;
186 p = -4.0545662729752068639e-14 + p *
w;
187 p = -8.1519341976054721522e-14 + p *
w;
188 p = 2.6335093153082322977e-12 + p *
w;
189 p = -1.2975133253453532498e-11 + p *
w;
190 p = -5.4154120542946279317e-11 + p *
w;
191 p = 1.051212273321532285e-09 + p *
w;
192 p = -4.1126339803469836976e-09 + p *
w;
193 p = -2.9070369957882005086e-08 + p *
w;
194 p = 4.2347877827932403518e-07 + p *
w;
195 p = -1.3654692000834678645e-06 + p *
w;
196 p = -1.3882523362786468719e-05 + p *
w;
197 p = 0.0001867342080340571352 + p *
w;
198 p = -0.00074070253416626697512 + p *
w;
199 p = -0.0060336708714301490533 + p *
w;
200 p = 0.24015818242558961693 + p *
w;
201 p = 1.6536545626831027356 + p *
w;
202 }
else if (
w < 16.000000 ) {
203 w = sqrt(
w ) - 3.250000;
204 p = 2.2137376921775787049e-09;
205 p = 9.0756561938885390979e-08 + p *
w;
206 p = -2.7517406297064545428e-07 + p *
w;
207 p = 1.8239629214389227755e-08 + p *
w;
208 p = 1.5027403968909827627e-06 + p *
w;
209 p = -4.013867526981545969e-06 + p *
w;
210 p = 2.9234449089955446044e-06 + p *
w;
211 p = 1.2475304481671778723e-05 + p *
w;
212 p = -4.7318229009055733981e-05 + p *
w;
213 p = 6.8284851459573175448e-05 + p *
w;
214 p = 2.4031110387097893999e-05 + p *
w;
215 p = -0.0003550375203628474796 + p *
w;
216 p = 0.00095328937973738049703 + p *
w;
217 p = -0.0016882755560235047313 + p *
w;
218 p = 0.0024914420961078508066 + p *
w;
219 p = -0.0037512085075692412107 + p *
w;
220 p = 0.005370914553590063617 + p *
w;
221 p = 1.0052589676941592334 + p *
w;
222 p = 3.0838856104922207635 + p *
w;
224 w = sqrt(
w ) - 5.000000;
225 p = -2.7109920616438573243e-11;
226 p = -2.5556418169965252055e-10 + p *
w;
227 p = 1.5076572693500548083e-09 + p *
w;
228 p = -3.7894654401267369937e-09 + p *
w;
229 p = 7.6157012080783393804e-09 + p *
w;
230 p = -1.4960026627149240478e-08 + p *
w;
231 p = 2.9147953450901080826e-08 + p *
w;
232 p = -6.7711997758452339498e-08 + p *
w;
233 p = 2.2900482228026654717e-07 + p *
w;
234 p = -9.9298272942317002539e-07 + p *
w;
235 p = 4.5260625972231537039e-06 + p *
w;
236 p = -1.9681778105531670567e-05 + p *
w;
237 p = 7.5995277030017761139e-05 + p *
w;
238 p = -0.00021503011930044477347 + p *
w;
239 p = -0.00013871931833623122026 + p *
w;
240 p = 1.0103004648645343977 + p *
w;
241 p = 4.8499064014085844221 + p *
w;
247 standard_deviation( std::vector<double>::const_iterator
first,
248 std::vector<double>::const_iterator last ) {
251 std::accumulate(
first,
254 [
m](
double a,
double b ) {
256 return a + diff * diff;
259 return std::sqrt( variance );
268 namespace Benchmark {
271#if defined( __GNUC__ ) || defined( __clang__ )
272# pragma GCC diagnostic push
273# pragma GCC diagnostic ignored "-Wfloat-equal"
276#if defined( __GNUC__ ) || defined( __clang__ )
277# pragma GCC diagnostic pop
282 double idx = (
count - 1) * k /
static_cast<double>(q);
283 int j =
static_cast<int>(idx);
291 auto xj1 = *std::min_element(
first + (
j + 1), last);
292 return xj +
g * (xj1 - xj);
295 OutlierClassification
297 std::vector<double>::const_iterator last ) {
298 std::vector<double> copy(
first, last );
303 auto los = q1 - ( iqr * 3. );
304 auto lom = q1 - ( iqr * 1.5 );
305 auto him = q3 + ( iqr * 1.5 );
306 auto his = q3 + ( iqr * 3. );
308 OutlierClassification
o;
310 const double t = *
first;
313 }
else if ( t < lom ) {
315 }
else if ( t > his ) {
317 }
else if ( t > him ) {
326 std::vector<double>::const_iterator last ) {
329 while (
first != last) {
333 return sum /
static_cast<double>(
count);
338 return erf_inv(1.0 -
x);
342 static const double ROOT_TWO = std::sqrt(2.0);
345 assert(p >= 0 && p <= 1);
346 if (p < 0 || p > 1) {
358 unsigned int n_resamples,
359 std::vector<double>::iterator
first,
360 std::vector<double>::iterator last) {
361 CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
362 CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
363 static std::random_device entropy;
364 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
366 auto n =
static_cast<int>(last -
first);
369 auto stddev = &standard_deviation;
371#if defined(CATCH_CONFIG_USE_ASYNC)
372 auto Estimate = [=](double(*
f)(std::vector<double>::const_iterator,
373 std::vector<double>::const_iterator)) {
374 auto seed = entropy();
375 return std::async(std::launch::async, [=] {
376 std::mt19937 rng(seed);
377 auto resampled = resample(rng, n_resamples,
first, last,
f);
378 return bootstrap(confidence_level,
first, last, resampled,
f);
382 auto mean_future = Estimate(
mean);
383 auto stddev_future = Estimate(stddev);
385 auto mean_estimate = mean_future.get();
386 auto stddev_estimate = stddev_future.get();
388 auto Estimate = [=](double(*
f)(std::vector<double>::const_iterator,
389 std::vector<double>::const_iterator)) {
390 auto seed = entropy();
391 std::mt19937 rng(seed);
392 auto resampled = resample(rng, n_resamples,
first, last,
f);
393 return bootstrap(confidence_level,
first, last, resampled,
f);
396 auto mean_estimate = Estimate(
mean);
397 auto stddev_estimate = Estimate(stddev);
400 double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate,
n);
402 return { mean_estimate, stddev_estimate, outlier_variance };
417bool marginComparison(
double lhs,
double rhs,
double margin) {
418 return (lhs + margin >= rhs) && (
rhs + margin >=
lhs);
425 Approx::Approx (
double value )
426 : m_epsilon(
std::numeric_limits<
float>::epsilon()*100. ),
432 Approx Approx::custom() {
436 Approx Approx::operator-()
const {
438 temp.m_value = -temp.m_value;
443 std::string Approx::toString()
const {
444 ReusableStringStream rss;
445 rss <<
"Approx( " << ::Catch::Detail::stringify( m_value ) <<
" )";
449 bool Approx::equalityComparisonImpl(
const double other)
const {
452 return marginComparison(m_value,
other, m_margin)
453 || marginComparison(m_value,
other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
456 void Approx::setMargin(
double newMargin) {
457 CATCH_ENFORCE(newMargin >= 0,
458 "Invalid Approx::margin: " << newMargin <<
'.'
459 <<
" Approx::Margin has to be non-negative.");
460 m_margin = newMargin;
463 void Approx::setEpsilon(
double newEpsilon) {
464 CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
465 "Invalid Approx::epsilon: " << newEpsilon <<
'.'
466 <<
" Approx::epsilon has to be in [0, 1]");
467 m_epsilon = newEpsilon;
471 Approx
operator "" _a(
long double val) {
474 Approx
operator "" _a(
unsigned long long val) {
479std::string StringMaker<Catch::Approx>::convert(Catch::Approx
const&
value) {
480 return value.toString();
489 AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression
const & _lazyExpression):
490 lazyExpression(_lazyExpression),
491 resultType(_resultType) {}
493 std::string AssertionResultData::reconstructExpression()
const {
495 if( reconstructedExpression.empty() ) {
496 if( lazyExpression ) {
497 ReusableStringStream rss;
498 rss << lazyExpression;
499 reconstructedExpression = rss.str();
502 return reconstructedExpression;
505 AssertionResult::AssertionResult( AssertionInfo
const& info, AssertionResultData&&
data )
507 m_resultData( CATCH_MOVE(
data) )
511 bool AssertionResult::succeeded()
const {
516 bool AssertionResult::isOk()
const {
520 ResultWas::OfType AssertionResult::getResultType()
const {
521 return m_resultData.resultType;
524 bool AssertionResult::hasExpression()
const {
525 return !m_info.capturedExpression.empty();
528 bool AssertionResult::hasMessage()
const {
529 return !m_resultData.message.empty();
532 std::string AssertionResult::getExpression()
const {
534 std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
535 if (isFalseTest(m_info.resultDisposition)) {
538 expr += m_info.capturedExpression;
539 if (isFalseTest(m_info.resultDisposition)) {
545 std::string AssertionResult::getExpressionInMacro()
const {
546 if ( m_info.macroName.empty() ) {
547 return static_cast<std::string
>( m_info.capturedExpression );
550 expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
551 expr += m_info.macroName;
553 expr += m_info.capturedExpression;
558 bool AssertionResult::hasExpandedExpression()
const {
559 return hasExpression() && getExpandedExpression() != getExpression();
562 std::string AssertionResult::getExpandedExpression()
const {
563 std::string expr = m_resultData.reconstructExpression();
569 StringRef AssertionResult::getMessage()
const {
570 return m_resultData.message;
572 SourceLineInfo AssertionResult::getSourceInfo()
const {
573 return m_info.lineInfo;
576 StringRef AssertionResult::getTestMacroName()
const {
577 return m_info.macroName;
589 static bool enableBazelEnvSupport() {
590#if defined( CATCH_CONFIG_BAZEL_SUPPORT )
597 struct bazelShardingOptions {
602 static Optional<bazelShardingOptions> readBazelShardingOptions() {
603 const auto bazelShardIndex =
Detail::getEnv(
"TEST_SHARD_INDEX" );
604 const auto bazelShardTotal =
Detail::getEnv(
"TEST_TOTAL_SHARDS" );
605 const auto bazelShardInfoFile =
Detail::getEnv(
"TEST_SHARD_STATUS_FILE" );
609 bazelShardIndex && bazelShardTotal && bazelShardInfoFile;
613 auto warn = [](
const char* env_var ) {
615 <<
"Warning: Bazel shard configuration is missing '"
616 << env_var <<
"'. Shard configuration is skipped.\n";
618 if ( !bazelShardIndex ) {
619 warn(
"TEST_SHARD_INDEX" );
621 if ( !bazelShardTotal ) {
622 warn(
"TEST_TOTAL_SHARDS" );
624 if ( !bazelShardInfoFile ) {
625 warn(
"TEST_SHARD_STATUS_FILE" );
633 <<
"Warning: could not parse 'TEST_SHARD_INDEX' ('" << bazelShardIndex
634 <<
"') as unsigned int.\n";
637 auto shardTotal =
parseUInt( bazelShardTotal );
640 <<
"Warning: could not parse 'TEST_TOTAL_SHARD' ('"
641 << bazelShardTotal <<
"') as unsigned int.\n";
645 return bazelShardingOptions{
646 *
shardIndex, *shardTotal, bazelShardInfoFile };
653 ProcessedReporterSpec
const& rhs ) {
654 return lhs.name == rhs.name &&
655 lhs.outputFilename == rhs.outputFilename &&
656 lhs.colourMode == rhs.colourMode &&
657 lhs.customOptions == rhs.customOptions;
660 Config::Config( ConfigData
const&
data ):
666 for (
auto& elem : m_data.testsOrTags) {
669 for (
auto& elem : m_data.sectionsToRun) {
674 if ( m_data.reporterSpecifications.empty() ) {
675 m_data.reporterSpecifications.push_back( {
676#if defined( CATCH_CONFIG_DEFAULT_REPORTER )
677 CATCH_CONFIG_DEFAULT_REPORTER,
685 if ( enableBazelEnvSupport() ) {
691 TestSpecParser parser( ITagAliasRegistry::get() );
692 if ( !m_data.testsOrTags.empty() ) {
693 m_hasTestFilters =
true;
694 for (
auto const& testOrTags : m_data.testsOrTags ) {
695 parser.parse( testOrTags );
698 m_testSpec = parser.testSpec();
703 bool defaultOutputUsed =
false;
704 for (
auto const& reporterSpec : m_data.reporterSpecifications ) {
708 if ( reporterSpec.outputFile().none() ) {
709 CATCH_ENFORCE( !defaultOutputUsed,
710 "Internal error: cannot use default output for "
711 "multiple reporters" );
712 defaultOutputUsed =
true;
715 m_processedReporterSpecs.push_back( ProcessedReporterSpec{
717 reporterSpec.outputFile() ? *reporterSpec.outputFile()
718 :
data.defaultOutputFilename,
719 reporterSpec.colourMode().valueOr(
data.defaultColourMode ),
720 reporterSpec.customOptions() } );
724 Config::~Config() =
default;
727 bool Config::listTests()
const {
return m_data.listTests; }
728 bool Config::listTags()
const {
return m_data.listTags; }
729 bool Config::listReporters()
const {
return m_data.listReporters; }
730 bool Config::listListeners()
const {
return m_data.listListeners; }
732 std::vector<std::string>
const& Config::getTestsOrTags()
const {
return m_data.testsOrTags; }
733 std::vector<std::string>
const& Config::getSectionsToRun()
const {
return m_data.sectionsToRun; }
735 std::vector<ReporterSpec>
const& Config::getReporterSpecs()
const {
736 return m_data.reporterSpecifications;
739 std::vector<ProcessedReporterSpec>
const&
740 Config::getProcessedReporterSpecs()
const {
741 return m_processedReporterSpecs;
744 TestSpec
const& Config::testSpec()
const {
return m_testSpec; }
745 bool Config::hasTestFilters()
const {
return m_hasTestFilters; }
747 bool Config::showHelp()
const {
return m_data.showHelp; }
750 bool Config::allowThrows()
const {
return !m_data.noThrow; }
751 StringRef Config::name()
const {
return m_data.name.empty() ? m_data.processName : m_data.name; }
752 bool Config::includeSuccessfulResults()
const {
return m_data.showSuccessfulTests; }
753 bool Config::warnAboutMissingAssertions()
const {
754 return !!( m_data.warnings & WarnAbout::NoAssertions );
756 bool Config::warnAboutUnmatchedTestSpecs()
const {
757 return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec );
759 bool Config::zeroTestsCountAsSuccess()
const {
return m_data.allowZeroTests; }
760 ShowDurations Config::showDurations()
const {
return m_data.showDurations; }
761 double Config::minDuration()
const {
return m_data.minDuration; }
762 TestRunOrder Config::runOrder()
const {
return m_data.runOrder; }
763 uint32_t Config::rngSeed()
const {
return m_data.rngSeed; }
764 unsigned int Config::shardCount()
const {
return m_data.shardCount; }
765 unsigned int Config::shardIndex()
const {
return m_data.shardIndex; }
766 ColourMode Config::defaultColourMode()
const {
return m_data.defaultColourMode; }
767 bool Config::shouldDebugBreak()
const {
return m_data.shouldDebugBreak; }
768 int Config::abortAfter()
const {
return m_data.abortAfter; }
769 bool Config::showInvisibles()
const {
return m_data.showInvisibles; }
770 Verbosity Config::verbosity()
const {
return m_data.verbosity; }
772 bool Config::skipBenchmarks()
const {
return m_data.skipBenchmarks; }
773 bool Config::benchmarkNoAnalysis()
const {
return m_data.benchmarkNoAnalysis; }
774 unsigned int Config::benchmarkSamples()
const {
return m_data.benchmarkSamples; }
775 double Config::benchmarkConfidenceInterval()
const {
return m_data.benchmarkConfidenceInterval; }
776 unsigned int Config::benchmarkResamples()
const {
return m_data.benchmarkResamples; }
777 std::chrono::milliseconds Config::benchmarkWarmupTime()
const {
return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
779 void Config::readBazelEnvVars() {
787 if ( bazelOutputFile ) {
788 m_data.reporterSpecifications.push_back(
789 {
"junit", std::string( bazelOutputFile ), {}, {} } );
792 const auto bazelTestSpec =
Detail::getEnv(
"TESTBRIDGE_TEST_ONLY" );
793 if ( bazelTestSpec ) {
796 m_data.testsOrTags.clear();
797 m_data.testsOrTags.push_back( bazelTestSpec );
800 const auto bazelShardOptions = readBazelShardingOptions();
801 if ( bazelShardOptions ) {
802 std::ofstream
f( bazelShardOptions->shardFilePath,
803 std::ios_base::out | std::ios_base::trunc );
806 m_data.shardIndex = bazelShardOptions->shardIndex;
807 m_data.shardCount = bazelShardOptions->shardCount;
820 return getCurrentContext().getConfig()->rngSeed();
834 ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
835 m_info( CATCH_MOVE(builder.m_info) ) {
836 m_info.message = builder.m_stream.str();
840 ScopedMessage::ScopedMessage( ScopedMessage&& old )
noexcept:
841 m_info( CATCH_MOVE( old.m_info ) ) {
845 ScopedMessage::~ScopedMessage() {
852 Capturer::Capturer( StringRef macroName,
853 SourceLineInfo
const& lineInfo,
854 ResultWas::OfType resultType,
857 auto trimmed = [&] (
size_t start,
size_t end) {
858 while (names[
start] ==
',' || isspace(
static_cast<unsigned char>(names[
start]))) {
861 while (names[
end] ==
',' || isspace(
static_cast<unsigned char>(names[
end]))) {
866 auto skipq = [&] (
size_t start,
char quote) {
867 for (
auto i =
start + 1;
i < names.size() ; ++
i) {
868 if (names[
i] == quote)
870 if (names[
i] ==
'\\')
873 CATCH_INTERNAL_ERROR(
"CAPTURE parsing encountered unmatched quote");
877 std::stack<char> openings;
878 for (
size_t pos = 0;
pos < names.size(); ++
pos) {
900 if (
start !=
pos && openings.empty()) {
901 m_messages.emplace_back(macroName, lineInfo, resultType);
902 m_messages.back().message =
static_cast<std::string
>(trimmed(
start,
pos));
903 m_messages.back().message +=
" := ";
908 assert(openings.empty() &&
"Mismatched openings");
909 m_messages.emplace_back(macroName, lineInfo, resultType);
910 m_messages.back().message =
static_cast<std::string
>(trimmed(
start, names.size() - 1));
911 m_messages.back().message +=
" := ";
913 Capturer::~Capturer() {
915 assert( m_captured == m_messages.size() );
916 for(
size_t i = 0;
i < m_captured; ++
i )
917 m_resultCapture.popScopedMessage( m_messages[
i] );
921 void Capturer::captureValue(
size_t index, std::string
const&
value ) {
922 assert(
index < m_messages.size() );
924 m_resultCapture.pushScopedMessage( m_messages[
index] );
939 class RegistryHub :
public IRegistryHub,
940 public IMutableRegistryHub,
941 private Detail::NonCopyable {
944 RegistryHub() =
default;
945 ReporterRegistry
const& getReporterRegistry()
const override {
946 return m_reporterRegistry;
948 ITestCaseRegistry
const& getTestCaseRegistry()
const override {
949 return m_testCaseRegistry;
951 IExceptionTranslatorRegistry
const& getExceptionTranslatorRegistry()
const override {
952 return m_exceptionTranslatorRegistry;
954 ITagAliasRegistry
const& getTagAliasRegistry()
const override {
955 return m_tagAliasRegistry;
957 StartupExceptionRegistry
const& getStartupExceptionRegistry()
const override {
958 return m_exceptionRegistry;
962 void registerReporter( std::string
const&
name, IReporterFactoryPtr factory )
override {
963 m_reporterRegistry.registerReporter(
name, CATCH_MOVE(factory) );
965 void registerListener( Detail::unique_ptr<EventListenerFactory> factory )
override {
966 m_reporterRegistry.registerListener( CATCH_MOVE(factory) );
968 void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker )
override {
969 m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) );
971 void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator )
override {
972 m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) );
974 void registerTagAlias( std::string
const& alias, std::string
const& tag, SourceLineInfo
const& lineInfo )
override {
975 m_tagAliasRegistry.add( alias, tag, lineInfo );
977 void registerStartupException() noexcept
override {
978#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
979 m_exceptionRegistry.add(std::current_exception());
981 CATCH_INTERNAL_ERROR(
"Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
984 IMutableEnumValuesRegistry& getMutableEnumValuesRegistry()
override {
985 return m_enumValuesRegistry;
989 TestRegistry m_testCaseRegistry;
990 ReporterRegistry m_reporterRegistry;
991 ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
992 TagAliasRegistry m_tagAliasRegistry;
993 StartupExceptionRegistry m_exceptionRegistry;
994 Detail::EnumValuesRegistry m_enumValuesRegistry;
1001 return RegistryHubSingleton::get();
1004 return RegistryHubSingleton::getMutable();
1011 return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
1028 const int MaxExitCode = 255;
1030 IEventListenerPtr createReporter(std::string
const& reporterName, ReporterConfig&& config) {
1031 auto reporter =
Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config));
1032 CATCH_ENFORCE(reporter,
"No reporter registered with name: '" << reporterName <<
'\'');
1037 IEventListenerPtr prepareReporters(Config
const* config) {
1039 && config->getProcessedReporterSpecs().size() == 1) {
1040 auto const& spec = config->getProcessedReporterSpecs()[0];
1041 return createReporter(
1043 ReporterConfig( config,
1046 spec.customOptions ) );
1049 auto multi = Detail::make_unique<MultiReporter>(config);
1052 for (
auto const& listener : listeners) {
1053 multi->addListener(listener->create(config));
1056 for (
auto const& reporterSpec : config->getProcessedReporterSpecs() ) {
1057 multi->addReporter( createReporter(
1059 ReporterConfig( config,
1061 reporterSpec.colourMode,
1062 reporterSpec.customOptions ) ) );
1070 explicit TestGroup(IEventListenerPtr&& reporter, Config
const* config):
1071 m_reporter(reporter.
get()),
1073 m_context{config, CATCH_MOVE(reporter)} {
1075 assert( m_config->testSpec().getInvalidSpecs().empty() &&
1076 "Invalid test specs should be handled before running tests" );
1079 auto const& testSpec = m_config->testSpec();
1080 if ( !testSpec.hasFilters() ) {
1081 for (
auto const&
test : allTestCases ) {
1082 if ( !
test.getTestCaseInfo().isHidden() ) {
1083 m_tests.emplace( &
test );
1088 testSpec.matchesByFilter( allTestCases, *m_config );
1089 for (
auto const&
match : m_matches ) {
1090 m_tests.insert(
match.tests.begin(),
1091 match.tests.end() );
1095 m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex());
1100 for (
auto const& testCase : m_tests) {
1101 if (!m_context.aborting())
1102 totals += m_context.runTest(*testCase);
1104 m_reporter->skipTest(testCase->getTestCaseInfo());
1107 for (
auto const&
match : m_matches) {
1108 if (
match.tests.empty()) {
1109 m_unmatchedTestSpecs =
true;
1110 m_reporter->noMatchingTestCases(
match.name );
1117 bool hadUnmatchedTestSpecs()
const {
1118 return m_unmatchedTestSpecs;
1123 IEventListener* m_reporter;
1125 RunContext m_context;
1126 std::set<TestCaseHandle const*> m_tests;
1127 TestSpec::Matches m_matches;
1128 bool m_unmatchedTestSpecs =
false;
1131 void applyFilenamesAsTags() {
1132 for (
auto const& testInfo :
getRegistryHub().getTestCaseRegistry().getAllInfos()) {
1133 testInfo->addFilenameTag();
1139 Session::Session() {
1140 static bool alreadyInstantiated =
false;
1141 if( alreadyInstantiated ) {
1142 CATCH_TRY { CATCH_INTERNAL_ERROR(
"Only one instance of Catch::Session can ever be used" ); }
1147#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
1148 const auto& exceptions =
getRegistryHub().getStartupExceptionRegistry().getExceptions();
1149 if ( !exceptions.empty() ) {
1151 getCurrentMutableContext().setConfig(m_config.get());
1153 m_startupExceptions =
true;
1155 auto colourImpl = makeColourImpl(
1156 ColourMode::PlatformDefault, errStream.get() );
1157 auto guard = colourImpl->guardColour( Colour::Red );
1158 errStream->stream() <<
"Errors occurred during startup!" <<
'\n';
1160 for (
const auto& ex_ptr : exceptions ) {
1162 std::rethrow_exception(ex_ptr);
1163 }
catch ( std::exception
const& ex ) {
1164 errStream->stream() << TextFlow::Column( ex.what() ).indent(2) <<
'\n';
1170 alreadyInstantiated =
true;
1173 Session::~Session() {
1177 void Session::showHelp()
const {
1181 <<
"For more detailed usage please see the project docs\n\n" << std::flush;
1183 void Session::libIdentify() {
1185 << std::left << std::setw(16) <<
"description: " <<
"A Catch2 test executable\n"
1186 << std::left << std::setw(16) <<
"category: " <<
"testframework\n"
1187 << std::left << std::setw(16) <<
"framework: " <<
"Catch2\n"
1188 << std::left << std::setw(16) <<
"version: " <<
libraryVersion() <<
'\n' << std::flush;
1191 int Session::applyCommandLine(
int argc,
char const *
const * argv ) {
1192 if( m_startupExceptions )
1195 auto result = m_cli.parse( Clara::Args( argc, argv ) );
1199 getCurrentMutableContext().setConfig(m_config.get());
1201 auto colour = makeColourImpl( ColourMode::PlatformDefault, errStream.get() );
1204 << colour->guardColour( Colour::Red )
1205 <<
"\nError(s) in input:\n"
1206 << TextFlow::Column(
result.errorMessage() ).indent( 2 )
1208 errStream->stream() <<
"Run with -? for usage\n\n" << std::flush;
1212 if( m_configData.showHelp )
1214 if( m_configData.libIdentify )
1221#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
1222 int Session::applyCommandLine(
int argc,
wchar_t const *
const * argv ) {
1224 char **utf8Argv =
new char *[ argc ];
1226 for (
int i = 0;
i < argc; ++
i ) {
1227 int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[
i], -1,
nullptr, 0,
nullptr,
nullptr );
1229 utf8Argv[
i ] =
new char[
bufSize ];
1231 WideCharToMultiByte( CP_UTF8, 0, argv[
i], -1, utf8Argv[
i],
bufSize,
nullptr,
nullptr );
1234 int returnCode = applyCommandLine( argc, utf8Argv );
1236 for (
int i = 0;
i < argc; ++
i )
1237 delete [] utf8Argv[
i ];
1245 void Session::useConfigData( ConfigData
const& configData ) {
1246 m_configData = configData;
1250 int Session::run() {
1251 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
1252 Catch::cout() <<
"...waiting for enter/ return before starting\n" << std::flush;
1253 static_cast<void>(std::getchar());
1255 int exitCode = runInternal();
1256 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
1257 Catch::cout() <<
"...waiting for enter/ return before exiting, with code: " << exitCode <<
'\n' << std::flush;
1258 static_cast<void>(std::getchar());
1263 Clara::Parser
const& Session::cli()
const {
1266 void Session::cli( Clara::Parser
const& newParser ) {
1269 ConfigData& Session::configData() {
1270 return m_configData;
1272 Config& Session::config() {
1274 m_config = Detail::make_unique<Config>( m_configData );
1278 int Session::runInternal() {
1279 if( m_startupExceptions )
1282 if (m_configData.showHelp || m_configData.libIdentify) {
1286 if ( m_configData.shardIndex >= m_configData.shardCount ) {
1287 Catch::cerr() <<
"The shard count (" << m_configData.shardCount
1288 <<
") must be greater than the shard index ("
1289 << m_configData.shardIndex <<
")\n"
1299 if (m_configData.filenamesAsTags) {
1300 applyFilenamesAsTags();
1304 getCurrentMutableContext().setConfig(m_config.get());
1307 auto reporter = prepareReporters(m_config.get());
1309 auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs();
1310 if ( !invalidSpecs.empty() ) {
1311 for (
auto const& spec : invalidSpecs ) {
1312 reporter->reportInvalidTestSpec( spec );
1319 if (
list(*reporter, *m_config)) {
1323 TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
1324 auto const totals = tests.execute();
1326 if ( tests.hadUnmatchedTestSpecs()
1327 && m_config->warnAboutUnmatchedTestSpecs() ) {
1331 if ( totals.testCases.total() == 0
1332 && !m_config->zeroTestsCountAsSuccess() ) {
1336 if ( totals.testCases.total() > 0 &&
1337 totals.testCases.total() == totals.testCases.skipped
1338 && !m_config->zeroTestsCountAsSuccess() ) {
1345 return (std::min) (MaxExitCode,
static_cast<int>(totals.assertions.failed));
1347#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
1348 catch( std::exception& ex ) {
1362 RegistrarForTagAliases::RegistrarForTagAliases(
char const* alias,
char const* tag, SourceLineInfo
const& lineInfo) {
1382 using TCP_underlying_type =
uint8_t;
1383 static_assert(
sizeof(TestCaseProperties) ==
sizeof(TCP_underlying_type),
1384 "The size of the TestCaseProperties is different from the assumed size");
1386 TestCaseProperties
operator|(TestCaseProperties lhs, TestCaseProperties rhs) {
1387 return static_cast<TestCaseProperties
>(
1388 static_cast<TCP_underlying_type
>(
lhs) |
static_cast<TCP_underlying_type
>(rhs)
1392 TestCaseProperties&
operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) {
1393 lhs =
static_cast<TestCaseProperties
>(
1394 static_cast<TCP_underlying_type
>(
lhs) |
static_cast<TCP_underlying_type
>(rhs)
1399 TestCaseProperties
operator&(TestCaseProperties lhs, TestCaseProperties rhs) {
1400 return static_cast<TestCaseProperties
>(
1401 static_cast<TCP_underlying_type
>(
lhs) &
static_cast<TCP_underlying_type
>(rhs)
1405 bool applies(TestCaseProperties tcp) {
1406 static_assert(
static_cast<TCP_underlying_type
>(TestCaseProperties::None) == 0,
1407 "TestCaseProperties::None must be equal to 0");
1408 return tcp != TestCaseProperties::None;
1411 TestCaseProperties parseSpecialTag( StringRef tag ) {
1412 if( !tag.empty() && tag[0] ==
'.' )
1413 return TestCaseProperties::IsHidden;
1414 else if( tag ==
"!throws"_sr )
1415 return TestCaseProperties::Throws;
1416 else if( tag ==
"!shouldfail"_sr )
1417 return TestCaseProperties::ShouldFail;
1418 else if( tag ==
"!mayfail"_sr )
1419 return TestCaseProperties::MayFail;
1420 else if( tag ==
"!nonportable"_sr )
1421 return TestCaseProperties::NonPortable;
1422 else if( tag ==
"!benchmark"_sr )
1423 return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden;
1425 return TestCaseProperties::None;
1427 bool isReservedTag( StringRef tag ) {
1428 return parseSpecialTag( tag ) == TestCaseProperties::None
1430 && !std::isalnum(
static_cast<unsigned char>(tag[0]) );
1432 void enforceNotReservedTag( StringRef tag, SourceLineInfo
const& _lineInfo ) {
1433 CATCH_ENFORCE( !isReservedTag(tag),
1434 "Tag name: [" << tag <<
"] is not allowed.\n"
1435 <<
"Tag names starting with non alphanumeric characters are reserved\n"
1439 std::string makeDefaultName() {
1444 StringRef extractFilenamePart(StringRef
filename) {
1446 while (lastDot > 0 &&
filename[lastDot - 1] !=
'.') {
1451 size_t nameStart = lastDot;
1452 while (nameStart > 0 &&
filename[nameStart - 1] !=
'/' &&
filename[nameStart - 1] !=
'\\') {
1456 return filename.substr(nameStart, lastDot - nameStart);
1460 size_t sizeOfExtraTags(StringRef filepath) {
1462 const size_t extras = 3 + 3;
1463 return extractFilenamePart(filepath).size() + extras;
1467 bool operator<( Tag
const& lhs, Tag
const& rhs ) {
1468 Detail::CaseInsensitiveLess
cmp;
1469 return cmp(
lhs.original,
rhs.original );
1471 bool operator==( Tag
const& lhs, Tag
const& rhs ) {
1472 Detail::CaseInsensitiveEqualTo
cmp;
1473 return cmp(
lhs.original,
rhs.original );
1476 Detail::unique_ptr<TestCaseInfo>
1477 makeTestCaseInfo(StringRef _className,
1478 NameAndTags
const& nameAndTags,
1479 SourceLineInfo
const& _lineInfo ) {
1480 return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo);
1483 TestCaseInfo::TestCaseInfo(StringRef _className,
1484 NameAndTags
const& _nameAndTags,
1485 SourceLineInfo
const& _lineInfo):
1487 className( _className ),
1488 lineInfo( _lineInfo )
1490 StringRef originalTags = _nameAndTags.tags;
1493 auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file);
1494 backingTags.reserve(requiredSize);
1498 size_t tagStart = 0;
1501 for (
size_t idx = 0;
idx < originalTags.size(); ++
idx) {
1502 auto c = originalTags[
idx];
1506 "Found '[' inside a tag while registering test case '"
1507 << _nameAndTags.name <<
"' at " << _lineInfo );
1515 "Found unmatched ']' while registering test case '"
1516 << _nameAndTags.name <<
"' at " << _lineInfo );
1520 assert(tagStart < tagEnd);
1525 StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
1526 CATCH_ENFORCE( !tagStr.empty(),
1527 "Found an empty tag while registering test case '"
1528 << _nameAndTags.name <<
"' at "
1531 enforceNotReservedTag(tagStr, lineInfo);
1532 properties |= parseSpecialTag(tagStr);
1536 if (tagStr.size() > 1 && tagStr[0] ==
'.') {
1537 tagStr = tagStr.substr(1, tagStr.size() - 1);
1542 internalAppendTag(tagStr);
1545 CATCH_ENFORCE( !inTag,
1546 "Found an unclosed tag while registering test case '"
1547 << _nameAndTags.name <<
"' at " << _lineInfo );
1552 internalAppendTag(
"."_sr);
1557 tags.erase(std::unique(
begin(tags),
end(tags)),
1561 bool TestCaseInfo::isHidden()
const {
1562 return applies( properties & TestCaseProperties::IsHidden );
1564 bool TestCaseInfo::throws()
const {
1565 return applies( properties & TestCaseProperties::Throws );
1567 bool TestCaseInfo::okToFail()
const {
1568 return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) );
1570 bool TestCaseInfo::expectedToFail()
const {
1571 return applies( properties & (TestCaseProperties::ShouldFail) );
1574 void TestCaseInfo::addFilenameTag() {
1575 std::string combined(
"#");
1576 combined += extractFilenamePart(lineInfo.file);
1577 internalAppendTag(combined);
1580 std::string TestCaseInfo::tagsAsString()
const {
1583 std::size_t full_size = 2 * tags.size();
1584 for (
const auto& tag : tags) {
1585 full_size += tag.original.size();
1587 ret.reserve(full_size);
1588 for (
const auto& tag : tags) {
1590 ret += tag.original;
1597 void TestCaseInfo::internalAppendTag(StringRef tagStr) {
1599 const auto backingStart = backingTags.size();
1600 backingTags += tagStr;
1601 const auto backingEnd = backingTags.size();
1603 tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart));
1606 bool operator<( TestCaseInfo
const& lhs, TestCaseInfo
const& rhs ) {
1610 const auto cmpName =
lhs.name.compare(
rhs.name );
1611 if ( cmpName != 0 ) {
1614 const auto cmpClassName =
lhs.className.compare(
rhs.className );
1615 if ( cmpClassName != 0 ) {
1616 return cmpClassName < 0;
1618 return lhs.tags <
rhs.tags;
1621 TestCaseInfo
const& TestCaseHandle::getTestCaseInfo()
const {
1636 TestSpec::Pattern::Pattern( std::string
const&
name )
1640 TestSpec::Pattern::~Pattern() =
default;
1642 std::string
const& TestSpec::Pattern::name()
const {
1647 TestSpec::NamePattern::NamePattern( std::string
const&
name, std::string
const& filterString )
1648 : Pattern( filterString )
1652 bool TestSpec::NamePattern::matches( TestCaseInfo
const& testCase )
const {
1653 return m_wildcardPattern.matches( testCase.name );
1656 void TestSpec::NamePattern::serializeTo( std::ostream& out )
const {
1657 out <<
'"' <<
name() <<
'"';
1661 TestSpec::TagPattern::TagPattern( std::string
const& tag, std::string
const& filterString )
1662 : Pattern( filterString )
1666 bool TestSpec::TagPattern::matches( TestCaseInfo
const& testCase )
const {
1667 return std::find(
begin( testCase.tags ),
1668 end( testCase.tags ),
1669 Tag( m_tag ) ) !=
end( testCase.tags );
1672 void TestSpec::TagPattern::serializeTo( std::ostream& out )
const {
1676 bool TestSpec::Filter::matches( TestCaseInfo
const& testCase )
const {
1677 bool should_use = !testCase.isHidden();
1678 for (
auto const&
pattern : m_required) {
1680 if (!
pattern->matches(testCase)) {
1684 for (
auto const&
pattern : m_forbidden) {
1685 if (
pattern->matches(testCase)) {
1692 void TestSpec::Filter::serializeTo( std::ostream& out )
const {
1694 for (
auto const&
pattern : m_required ) {
1701 for (
auto const&
pattern : m_forbidden ) {
1711 std::string TestSpec::extractFilterName( Filter
const&
filter ) {
1712 Catch::ReusableStringStream sstr;
1717 bool TestSpec::hasFilters()
const {
1718 return !m_filters.empty();
1721 bool TestSpec::matches( TestCaseInfo
const& testCase )
const {
1722 return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter
const&
f ){ return f.matches( testCase ); } );
1725 TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle>
const& testCases, IConfig
const& config )
const {
1727 matches.reserve( m_filters.size() );
1728 for (
auto const&
filter : m_filters ) {
1729 std::vector<TestCaseHandle const*> currentMatches;
1730 for (
auto const&
test : testCases )
1733 currentMatches.emplace_back( &
test );
1735 FilterMatch{ extractFilterName(
filter ), currentMatches } );
1740 const TestSpec::vectorStrings& TestSpec::getInvalidSpecs()
const {
1741 return m_invalidSpecs;
1744 void TestSpec::serializeTo( std::ostream& out )
const {
1746 for (
auto const&
filter : m_filters ) {
1764 static auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
1765 return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
1769 void Timer::start() {
1770 m_nanoseconds = getCurrentNanosecondsSinceEpoch();
1772 auto Timer::getElapsedNanoseconds() const -> uint64_t {
1773 return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
1775 auto Timer::getElapsedMicroseconds() const -> uint64_t {
1776 return getElapsedNanoseconds()/1000;
1778 auto Timer::getElapsedMilliseconds() const ->
unsigned int {
1779 return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
1781 auto Timer::getElapsedSeconds() const ->
double {
1782 return getElapsedMicroseconds()/1000000.0;
1799 const int hexThreshold = 255;
1802 enum Arch { Big, Little };
1804 static Arch which() {
1808 auto value = *
reinterpret_cast<char*
>(&
one);
1809 return value ? Little : Big;
1813 template<
typename T>
1819 ReusableStringStream rss;
1823 std::string d = rss.str();
1824 std::size_t
i = d.find_last_not_of(
'0');
1825 if (
i != std::string::npos &&
i != d.size() - 1) {
1828 d = d.substr(0,
i + 1);
1834 std::string convertIntoString(StringRef
string,
bool escape_invisibles) {
1838 ret.reserve(
string.
size() + 2);
1840 if (!escape_invisibles) {
1872 std::string convertIntoString(StringRef
string) {
1873 return convertIntoString(
string, getCurrentContext().getConfig()->showInvisibles());
1876 std::string rawMemoryToString(
const void *
object, std::size_t
size ) {
1878 int i = 0,
end =
static_cast<int>(
size ), inc = 1;
1879 if( Endianness::which() == Endianness::Little ) {
1884 unsigned char const *bytes =
static_cast<unsigned char const *
>(
object);
1885 ReusableStringStream rss;
1886 rss <<
"0x" << std::setfill(
'0') << std::hex;
1887 for( ;
i !=
end;
i += inc )
1888 rss << std::setw(2) <<
static_cast<unsigned>(bytes[
i]);
1901std::string StringMaker<std::string>::convert(
const std::string&
str) {
1902 return Detail::convertIntoString(
str );
1905#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
1906std::string StringMaker<std::string_view>::convert(std::string_view
str) {
1907 return Detail::convertIntoString( StringRef(
str.data(),
str.size() ) );
1911std::string StringMaker<char const*>::convert(
char const*
str) {
1913 return Detail::convertIntoString(
str );
1915 return{
"{null string}" };
1918std::string StringMaker<char*>::convert(
char*
str) {
1920 return Detail::convertIntoString(
str );
1922 return{
"{null string}" };
1926#ifdef CATCH_CONFIG_WCHAR
1927std::string StringMaker<std::wstring>::convert(
const std::wstring& wstr) {
1929 s.reserve(wstr.size());
1930 for (
auto c : wstr) {
1931 s += (c <= 0xff) ? static_cast<char>(
c) :
'?';
1933 return ::Catch::Detail::stringify(s);
1936# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
1937std::string StringMaker<std::wstring_view>::convert(std::wstring_view
str) {
1938 return StringMaker<std::wstring>::convert(std::wstring(
str));
1942std::string StringMaker<wchar_t const*>::convert(
wchar_t const *
str) {
1944 return ::Catch::Detail::stringify(std::wstring{
str });
1946 return{
"{null string}" };
1949std::string StringMaker<wchar_t *>::convert(
wchar_t *
str) {
1951 return ::Catch::Detail::stringify(std::wstring{
str });
1953 return{
"{null string}" };
1958#if defined(CATCH_CONFIG_CPP17_BYTE)
1960std::string StringMaker<std::byte>::convert(std::byte
value) {
1961 return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(
value));
1965std::string StringMaker<int>::convert(
int value) {
1966 return ::Catch::Detail::stringify(
static_cast<long long>(
value));
1968std::string StringMaker<long>::convert(
long value) {
1969 return ::Catch::Detail::stringify(
static_cast<long long>(
value));
1971std::string StringMaker<long long>::convert(
long long value) {
1972 ReusableStringStream rss;
1974 if (
value > Detail::hexThreshold) {
1975 rss <<
" (0x" << std::hex <<
value <<
')';
1980std::string StringMaker<unsigned int>::convert(
unsigned int value) {
1981 return ::Catch::Detail::stringify(
static_cast<unsigned long long>(
value));
1983std::string StringMaker<unsigned long>::convert(
unsigned long value) {
1984 return ::Catch::Detail::stringify(
static_cast<unsigned long long>(
value));
1986std::string StringMaker<unsigned long long>::convert(
unsigned long long value) {
1987 ReusableStringStream rss;
1989 if (
value > Detail::hexThreshold) {
1990 rss <<
" (0x" << std::hex <<
value <<
')';
1995std::string StringMaker<signed char>::convert(
signed char value) {
1996 if (
value ==
'\r') {
1998 }
else if (
value ==
'\f') {
2000 }
else if (
value ==
'\n') {
2002 }
else if (
value ==
'\t') {
2005 return ::Catch::Detail::stringify(
static_cast<unsigned int>(
value));
2007 char chstr[] =
"' '";
2012std::string StringMaker<char>::convert(
char c) {
2013 return ::Catch::Detail::stringify(
static_cast<signed char>(
c));
2015std::string StringMaker<unsigned char>::convert(
unsigned char c) {
2016 return ::Catch::Detail::stringify(
static_cast<char>(
c));
2019int StringMaker<float>::precision = 5;
2021std::string StringMaker<float>::convert(
float value) {
2025int StringMaker<double>::precision = 10;
2027std::string StringMaker<double>::convert(
double value) {
2037 Counts Counts::operator - ( Counts
const&
other )
const {
2039 diff.passed = passed -
other.passed;
2041 diff.failedButOk = failedButOk -
other.failedButOk;
2042 diff.skipped = skipped -
other.skipped;
2046 Counts& Counts::operator += ( Counts
const&
other ) {
2047 passed +=
other.passed;
2049 failedButOk +=
other.failedButOk;
2050 skipped +=
other.skipped;
2054 std::uint64_t Counts::total()
const {
2055 return passed +
failed + failedButOk + skipped;
2057 bool Counts::allPassed()
const {
2058 return failed == 0 && failedButOk == 0 && skipped == 0;
2060 bool Counts::allOk()
const {
2064 Totals Totals::operator - ( Totals
const&
other )
const {
2066 diff.assertions = assertions -
other.assertions;
2067 diff.testCases = testCases -
other.testCases;
2071 Totals& Totals::operator += ( Totals
const&
other ) {
2072 assertions +=
other.assertions;
2073 testCases +=
other.testCases;
2077 Totals Totals::delta( Totals
const& prevTotals )
const {
2078 Totals diff = *
this - prevTotals;
2079 if( diff.assertions.failed > 0 )
2080 ++diff.testCases.failed;
2081 else if( diff.assertions.failedButOk > 0 )
2082 ++diff.testCases.failedButOk;
2083 else if ( diff.assertions.skipped > 0 )
2084 ++ diff.testCases.skipped;
2086 ++diff.testCases.passed;
2098 Detail::unique_ptr<IExceptionTranslator>&& translator ) {
2100 CATCH_MOVE( translator ) );
2111 (
unsigned int _majorVersion,
2112 unsigned int _minorVersion,
2113 unsigned int _patchNumber,
2114 char const *
const _branchName,
2115 unsigned int _buildNumber )
2116 : majorVersion( _majorVersion ),
2117 minorVersion( _minorVersion ),
2118 patchNumber( _patchNumber ),
2119 branchName( _branchName ),
2120 buildNumber( _buildNumber )
2124 os <<
version.majorVersion <<
'.'
2125 <<
version.minorVersion <<
'.'
2129 os <<
'-' <<
version.branchName
2130 <<
'.' <<
version.buildNumber;
2136 static Version
version( 3, 4, 0,
"", 0 );
2147 const char* GeneratorException::what() const noexcept {
2158 IGeneratorTracker::~IGeneratorTracker() =
default;
2160namespace Generators {
2166 Catch::throw_exception(GeneratorException{
msg });
2170 GeneratorUntypedBase::~GeneratorUntypedBase() =
default;
2173 return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
2177 SourceLineInfo lineInfo,
2178 GeneratorBasePtr&& generator ) {
2180 generatorName, lineInfo, CATCH_MOVE( generator ) );
2190std::uint32_t Catch::Generators::Detail::getSeed() {
return sharedRng()(); }
2196 IResultCapture::~IResultCapture() =
default;
2203 IConfig::~IConfig() =
default;
2210 IExceptionTranslator::~IExceptionTranslator() =
default;
2211 IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() =
default;
2219 namespace Generators {
2221 bool GeneratorUntypedBase::countedNext() {
2224 m_stringReprCache.clear();
2225 ++m_currentElementIndex;
2230 StringRef GeneratorUntypedBase::currentElementAsString()
const {
2231 if ( m_stringReprCache.empty() ) {
2232 m_stringReprCache = stringifyImpl();
2234 return m_stringReprCache;
2244 IRegistryHub::~IRegistryHub() =
default;
2245 IMutableRegistryHub::~IMutableRegistryHub() =
default;
2256 ReporterConfig::ReporterConfig(
2257 IConfig
const* _fullConfig,
2258 Detail::unique_ptr<IStream> _stream,
2259 ColourMode colourMode,
2260 std::map<std::string, std::string> customOptions ):
2261 m_stream( CATCH_MOVE(_stream) ),
2262 m_fullConfig( _fullConfig ),
2263 m_colourMode( colourMode ),
2264 m_customOptions( CATCH_MOVE( customOptions ) ) {}
2266 Detail::unique_ptr<IStream> ReporterConfig::takeStream() && {
2268 return CATCH_MOVE( m_stream );
2270 IConfig
const * ReporterConfig::fullConfig()
const {
return m_fullConfig; }
2271 ColourMode ReporterConfig::colourMode()
const {
return m_colourMode; }
2273 std::map<std::string, std::string>
const&
2274 ReporterConfig::customOptions()
const {
2275 return m_customOptions;
2278 ReporterConfig::~ReporterConfig() =
default;
2280 AssertionStats::AssertionStats( AssertionResult
const& _assertionResult,
2281 std::vector<MessageInfo>
const& _infoMessages,
2282 Totals
const& _totals )
2283 : assertionResult( _assertionResult ),
2284 infoMessages( _infoMessages ),
2287 if( assertionResult.hasMessage() ) {
2290 MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
2291 builder.m_info.message =
static_cast<std::string
>(assertionResult.getMessage());
2293 infoMessages.push_back( CATCH_MOVE(builder.m_info) );
2297 SectionStats::SectionStats( SectionInfo&& _sectionInfo,
2298 Counts
const& _assertions,
2299 double _durationInSeconds,
2300 bool _missingAssertions )
2301 : sectionInfo( CATCH_MOVE(_sectionInfo) ),
2302 assertions( _assertions ),
2303 durationInSeconds( _durationInSeconds ),
2304 missingAssertions( _missingAssertions )
2308 TestCaseStats::TestCaseStats( TestCaseInfo
const& _testInfo,
2309 Totals
const& _totals,
2310 std::string&& _stdOut,
2311 std::string&& _stdErr,
2313 : testInfo( &_testInfo ),
2315 stdOut( CATCH_MOVE(_stdOut) ),
2316 stdErr( CATCH_MOVE(_stdErr) ),
2317 aborting( _aborting )
2321 TestRunStats::TestRunStats( TestRunInfo
const& _runInfo,
2322 Totals
const& _totals,
2324 : runInfo( _runInfo ),
2326 aborting( _aborting )
2329 IEventListener::~IEventListener() =
default;
2337 IReporterFactory::~IReporterFactory() =
default;
2338 EventListenerFactory::~EventListenerFactory() =
default;
2345 ITestCaseRegistry::~ITestCaseRegistry() =
default;
2352 AssertionHandler::AssertionHandler
2353 ( StringRef macroName,
2354 SourceLineInfo
const& lineInfo,
2355 StringRef capturedExpression,
2356 ResultDisposition::Flags resultDisposition )
2357 : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
2360 m_resultCapture.notifyAssertionStarted( m_assertionInfo );
2363 void AssertionHandler::handleExpr( ITransientExpression
const& expr ) {
2364 m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
2366 void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef
message) {
2367 m_resultCapture.handleMessage( m_assertionInfo, resultType,
message, m_reaction );
2370 auto AssertionHandler::allowThrows() const ->
bool {
2371 return getCurrentContext().getConfig()->allowThrows();
2374 void AssertionHandler::complete() {
2376 if( m_reaction.shouldDebugBreak ) {
2382 CATCH_BREAK_INTO_DEBUGGER();
2384 if (m_reaction.shouldThrow) {
2387 if ( m_reaction.shouldSkip ) {
2392 void AssertionHandler::handleUnexpectedInflightException() {
2396 void AssertionHandler::handleExceptionThrownAsExpected() {
2397 m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
2399 void AssertionHandler::handleExceptionNotThrownAsExpected() {
2400 m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
2403 void AssertionHandler::handleUnexpectedExceptionNotThrown() {
2404 m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
2407 void AssertionHandler::handleThrowingCallSkipped() {
2408 m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
2427 bool CaseInsensitiveLess::operator()( StringRef lhs,
2428 StringRef rhs )
const {
2429 return std::lexicographical_compare(
2432 [](
char l,
char r ) { return toLower( l ) < toLower( r ); } );
2436 CaseInsensitiveEqualTo::operator()( StringRef lhs,
2437 StringRef rhs )
const {
2441 [](
char l,
char r ) { return toLower( l ) == toLower( r ); } );
2454 bool isOptPrefix(
char c ) {
2456#ifdef CATCH_PLATFORM_WINDOWS
2462 std::string normaliseOpt( std::string
const& optName ) {
2463#ifdef CATCH_PLATFORM_WINDOWS
2464 if ( optName[0] ==
'/' )
2465 return "-" + optName.substr( 1 );
2477 void TokenStream::loadBuffer() {
2478 m_tokenBuffer.clear();
2481 while ( it != itEnd && it->empty() ) {
2485 if ( it != itEnd ) {
2486 auto const& next = *it;
2487 if ( isOptPrefix( next[0] ) ) {
2488 auto delimiterPos = next.find_first_of(
" :=" );
2489 if ( delimiterPos != std::string::npos ) {
2490 m_tokenBuffer.push_back(
2491 { TokenType::Option,
2492 next.substr( 0, delimiterPos ) } );
2493 m_tokenBuffer.push_back(
2494 { TokenType::Argument,
2495 next.substr( delimiterPos + 1 ) } );
2497 if ( next[1] !=
'-' && next.size() > 2 ) {
2498 std::string opt =
"- ";
2499 for (
size_t i = 1;
i < next.size(); ++
i ) {
2501 m_tokenBuffer.push_back(
2502 { TokenType::Option, opt } );
2505 m_tokenBuffer.push_back(
2506 { TokenType::Option, next } );
2510 m_tokenBuffer.push_back(
2511 { TokenType::Argument, next } );
2516 TokenStream::TokenStream( Args
const& args ):
2517 TokenStream( args.m_args.begin(), args.m_args.end() ) {}
2519 TokenStream::TokenStream( Iterator it_, Iterator itEnd_ ):
2520 it( it_ ), itEnd( itEnd_ ) {
2524 TokenStream& TokenStream::operator++() {
2525 if ( m_tokenBuffer.size() >= 2 ) {
2526 m_tokenBuffer.erase( m_tokenBuffer.begin() );
2538 return ParserResult::ok( ParseResultType::Matched );
2545 if ( srcLC ==
"y" || srcLC ==
"1" || srcLC ==
"true" ||
2546 srcLC ==
"yes" || srcLC ==
"on" ) {
2548 }
else if ( srcLC ==
"n" || srcLC ==
"0" || srcLC ==
"false" ||
2549 srcLC ==
"no" || srcLC ==
"off" ) {
2552 return ParserResult::runtimeError(
2553 "Expected a boolean value but did not recognise: '" +
2556 return ParserResult::ok( ParseResultType::Matched );
2559 size_t ParserBase::cardinality()
const {
return 1; }
2561 InternalParseResult ParserBase::parse( Args
const& args )
const {
2562 return parse( args.exeName(), TokenStream( args ) );
2565 ParseState::ParseState( ParseResultType
type,
2566 TokenStream
const& remainingTokens ):
2567 m_type(
type ), m_remainingTokens( remainingTokens ) {}
2569 ParserResult BoundFlagRef::setFlag(
bool flag ) {
2571 return ParserResult::ok( ParseResultType::Matched );
2574 ResultBase::~ResultBase() =
default;
2576 bool BoundRef::isContainer()
const {
return false; }
2578 bool BoundRef::isFlag()
const {
return false; }
2580 bool BoundFlagRefBase::isFlag()
const {
return true; }
2584 Detail::InternalParseResult Arg::parse(std::string
const&,
2585 Detail::TokenStream
const& tokens)
const {
2586 auto validationResult = validate();
2587 if (!validationResult)
2588 return Detail::InternalParseResult(validationResult);
2590 auto remainingTokens = tokens;
2591 auto const& token = *remainingTokens;
2592 if (token.type != Detail::TokenType::Argument)
2593 return Detail::InternalParseResult::ok(Detail::ParseState(
2594 ParseResultType::NoMatch, remainingTokens));
2596 assert(!m_ref->isFlag());
2598 static_cast<Detail::BoundValueRefBase*
>(m_ref.get());
2600 auto result = valueRef->setValue(remainingTokens->token);
2602 return Detail::InternalParseResult(
result);
2604 return Detail::InternalParseResult::ok(Detail::ParseState(
2605 ParseResultType::Matched, ++remainingTokens));
2608 Opt::Opt(
bool&
ref) :
2609 ParserRefImpl(
std::make_shared<Detail::BoundFlagRef>(
ref)) {}
2611 std::vector<Detail::HelpColumns> Opt::getHelpColumns()
const {
2612 std::ostringstream oss;
2614 for (
auto const& opt : m_optNames) {
2621 if (!m_hint.empty())
2622 oss <<
" <" << m_hint <<
'>';
2623 return { { oss.str(), m_description } };
2626 bool Opt::isMatch(std::string
const& optToken)
const {
2627 auto normalisedToken = normaliseOpt(optToken);
2628 for (
auto const&
name : m_optNames) {
2629 if (normaliseOpt(
name) == normalisedToken)
2635 Detail::InternalParseResult Opt::parse(std::string
const&,
2636 Detail::TokenStream
const& tokens)
const {
2637 auto validationResult = validate();
2638 if (!validationResult)
2639 return Detail::InternalParseResult(validationResult);
2641 auto remainingTokens = tokens;
2642 if (remainingTokens &&
2643 remainingTokens->type == Detail::TokenType::Option) {
2644 auto const& token = *remainingTokens;
2645 if (isMatch(token.token)) {
2646 if (m_ref->isFlag()) {
2648 static_cast<Detail::BoundFlagRefBase*
>(
2650 auto result = flagRef->setFlag(
true);
2652 return Detail::InternalParseResult(
result);
2654 ParseResultType::ShortCircuitAll)
2655 return Detail::InternalParseResult::ok(Detail::ParseState(
2656 result.value(), remainingTokens));
2659 static_cast<Detail::BoundValueRefBase*
>(
2662 if (!remainingTokens)
2663 return Detail::InternalParseResult::runtimeError(
2664 "Expected argument following " +
2666 auto const& argToken = *remainingTokens;
2667 if (argToken.type != Detail::TokenType::Argument)
2668 return Detail::InternalParseResult::runtimeError(
2669 "Expected argument following " +
2671 const auto result = valueRef->setValue(argToken.token);
2673 return Detail::InternalParseResult(
result);
2675 ParseResultType::ShortCircuitAll)
2676 return Detail::InternalParseResult::ok(Detail::ParseState(
2677 result.value(), remainingTokens));
2679 return Detail::InternalParseResult::ok(Detail::ParseState(
2680 ParseResultType::Matched, ++remainingTokens));
2683 return Detail::InternalParseResult::ok(
2684 Detail::ParseState(ParseResultType::NoMatch, remainingTokens));
2687 Detail::Result Opt::validate()
const {
2688 if (m_optNames.empty())
2689 return Detail::Result::logicError(
"No options supplied to Opt");
2690 for (
auto const&
name : m_optNames) {
2692 return Detail::Result::logicError(
2693 "Option name cannot be empty");
2694#ifdef CATCH_PLATFORM_WINDOWS
2695 if (
name[0] !=
'-' &&
name[0] !=
'/')
2696 return Detail::Result::logicError(
2697 "Option name must begin with '-' or '/'");
2700 return Detail::Result::logicError(
2701 "Option name must begin with '-'");
2704 return ParserRefImpl::validate();
2707 ExeName::ExeName() :
2708 m_name(
std::make_shared<
std::
string>(
"<executable>")) {}
2710 ExeName::ExeName(std::string&
ref) : ExeName() {
2711 m_ref = std::make_shared<Detail::BoundValueRef<std::string>>(
ref);
2714 Detail::InternalParseResult
2715 ExeName::parse(std::string
const&,
2716 Detail::TokenStream
const& tokens)
const {
2717 return Detail::InternalParseResult::ok(
2718 Detail::ParseState(ParseResultType::NoMatch, tokens));
2721 ParserResult ExeName::set(std::string
const& newName) {
2722 auto lastSlash = newName.find_last_of(
"\\/");
2723 auto filename = (lastSlash == std::string::npos)
2725 : newName.substr(lastSlash + 1);
2731 return ParserResult::ok(ParseResultType::Matched);
2737 Parser& Parser::operator|=( Parser
const&
other ) {
2738 m_options.insert( m_options.end(),
2739 other.m_options.begin(),
2740 other.m_options.end() );
2742 m_args.end(),
other.m_args.begin(),
other.m_args.end() );
2746 std::vector<Detail::HelpColumns> Parser::getHelpColumns()
const {
2747 std::vector<Detail::HelpColumns>
cols;
2748 for (
auto const&
o : m_options ) {
2749 auto childCols =
o.getHelpColumns();
2750 cols.insert(
cols.end(), childCols.begin(), childCols.end() );
2755 void Parser::writeToStream( std::ostream& os )
const {
2756 if ( !m_exeName.name().empty() ) {
2758 <<
" " << m_exeName.name() <<
' ';
2759 bool required =
true,
first =
true;
2760 for (
auto const& arg : m_args ) {
2765 if ( arg.isOptional() && required ) {
2769 os <<
'<' << arg.hint() <<
'>';
2770 if ( arg.cardinality() == 0 )
2775 if ( !m_options.empty() )
2777 os <<
"\n\nwhere options are:\n";
2780 auto rows = getHelpColumns();
2781 size_t consoleWidth = CATCH_CONFIG_CONSOLE_WIDTH;
2782 size_t optWidth = 0;
2786 optWidth = ( std::min )( optWidth, consoleWidth / 2 );
2789 auto row = TextFlow::Column(
cols.left )
2793 TextFlow::Column(
cols.right )
2794 .width( consoleWidth - 7 - optWidth );
2799 Detail::Result Parser::validate()
const {
2800 for (
auto const& opt : m_options ) {
2801 auto result = opt.validate();
2805 for (
auto const& arg : m_args ) {
2806 auto result = arg.validate();
2810 return Detail::Result::ok();
2813 Detail::InternalParseResult
2814 Parser::parse( std::string
const& exeName,
2815 Detail::TokenStream
const& tokens )
const {
2818 ParserBase
const* parser =
nullptr;
2821 std::vector<ParserInfo> parseInfos;
2822 parseInfos.reserve( m_options.size() + m_args.size() );
2823 for (
auto const& opt : m_options ) {
2824 parseInfos.push_back( { &opt, 0 } );
2826 for (
auto const& arg : m_args ) {
2827 parseInfos.push_back( { &arg, 0 } );
2830 m_exeName.set( exeName );
2832 auto result = Detail::InternalParseResult::ok(
2833 Detail::ParseState( ParseResultType::NoMatch, tokens ) );
2834 while (
result.value().remainingTokens() ) {
2835 bool tokenParsed =
false;
2837 for (
auto& parseInfo : parseInfos ) {
2838 if ( parseInfo.parser->cardinality() == 0 ||
2839 parseInfo.count < parseInfo.parser->cardinality() ) {
2840 result = parseInfo.parser->parse(
2841 exeName,
result.value().remainingTokens() );
2844 if (
result.value().type() !=
2845 ParseResultType::NoMatch ) {
2853 if (
result.value().type() == ParseResultType::ShortCircuitAll )
2856 return Detail::InternalParseResult::runtimeError(
2857 "Unrecognised token: " +
2858 result.value().remainingTokens()->token );
2864 Args::Args(
int argc,
char const*
const* argv) :
2865 m_exeName(argv[0]), m_args(argv + 1, argv + argc) {}
2867 Args::Args(std::initializer_list<std::string> args) :
2868 m_exeName(*args.
begin()),
2869 m_args(args.
begin() + 1, args.
end()) {}
2872 Help::Help(
bool& showHelpFlag ):
2873 Opt( [&](
bool flag ) {
2874 showHelpFlag = flag;
2875 return ParserResult::ok( ParseResultType::ShortCircuitAll );
2877 static_cast<Opt&
> ( *this )(
2878 "display usage information" )[
"-?"][
"-h"][
"--help"]
2895 using namespace Clara;
2897 auto const setWarning = [&]( std::string
const& warning ) {
2898 if ( warning ==
"NoAssertions" ) {
2899 config.warnings =
static_cast<WarnAbout::What
>(config.warnings | WarnAbout::NoAssertions);
2900 return ParserResult::ok( ParseResultType::Matched );
2901 }
else if ( warning ==
"UnmatchedTestSpec" ) {
2902 config.warnings =
static_cast<WarnAbout::What
>(config.warnings | WarnAbout::UnmatchedTestSpec);
2903 return ParserResult::ok( ParseResultType::Matched );
2906 return ParserResult ::runtimeError(
2907 "Unrecognised warning option: '" + warning +
'\'' );
2909 auto const loadTestNamesFromFile = [&]( std::string
const&
filename ) {
2912 return ParserResult::runtimeError(
"Unable to load input file: '" +
filename +
'\'' );
2915 while( std::getline(
f, line ) ) {
2917 if( !line.empty() && !
startsWith( line,
'#' ) ) {
2919 line =
'"' + line +
'"';
2920 config.testsOrTags.push_back( line );
2921 config.testsOrTags.emplace_back(
"," );
2925 if(!config.testsOrTags.empty())
2926 config.testsOrTags.erase( config.testsOrTags.end()-1 );
2928 return ParserResult::ok( ParseResultType::Matched );
2930 auto const setTestOrder = [&]( std::string
const& order ) {
2932 config.runOrder = TestRunOrder::Declared;
2934 config.runOrder = TestRunOrder::LexicographicallySorted;
2936 config.runOrder = TestRunOrder::Randomized;
2938 return ParserResult::runtimeError(
"Unrecognised ordering: '" + order +
'\'' );
2939 return ParserResult::ok( ParseResultType::Matched );
2941 auto const setRngSeed = [&]( std::string
const& seed ) {
2942 if( seed ==
"time" ) {
2944 return ParserResult::ok(ParseResultType::Matched);
2945 }
else if (seed ==
"random-device") {
2947 return ParserResult::ok(ParseResultType::Matched);
2953 if ( !parsedSeed ) {
2954 return ParserResult::runtimeError(
"Could not parse '" + seed +
"' as seed" );
2956 config.rngSeed = *parsedSeed;
2957 return ParserResult::ok( ParseResultType::Matched );
2959 auto const setDefaultColourMode = [&]( std::string
const& colourMode ) {
2962 return ParserResult::runtimeError(
2963 "colour mode must be one of: default, ansi, win32, "
2965 colourMode +
"' is not recognised" );
2967 auto mode = *maybeMode;
2968 if ( !isColourImplAvailable(
mode ) ) {
2969 return ParserResult::runtimeError(
2970 "colour mode '" + colourMode +
2971 "' is not supported in this binary" );
2973 config.defaultColourMode =
mode;
2974 return ParserResult::ok( ParseResultType::Matched );
2976 auto const setWaitForKeypress = [&]( std::string
const& keypress ) {
2977 auto keypressLc =
toLower( keypress );
2978 if (keypressLc ==
"never")
2979 config.waitForKeypress = WaitForKeypress::Never;
2980 else if( keypressLc ==
"start" )
2981 config.waitForKeypress = WaitForKeypress::BeforeStart;
2982 else if( keypressLc ==
"exit" )
2983 config.waitForKeypress = WaitForKeypress::BeforeExit;
2984 else if( keypressLc ==
"both" )
2985 config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
2987 return ParserResult::runtimeError(
"keypress argument must be one of: never, start, exit or both. '" + keypress +
"' not recognised" );
2988 return ParserResult::ok( ParseResultType::Matched );
2990 auto const setVerbosity = [&]( std::string
const&
verbosity ) {
2992 if( lcVerbosity ==
"quiet" )
2993 config.verbosity = Verbosity::Quiet;
2994 else if( lcVerbosity ==
"normal" )
2995 config.verbosity = Verbosity::Normal;
2996 else if( lcVerbosity ==
"high" )
2997 config.verbosity = Verbosity::High;
2999 return ParserResult::runtimeError(
"Unrecognised verbosity, '" +
verbosity +
'\'' );
3000 return ParserResult::ok( ParseResultType::Matched );
3002 auto const setReporter = [&]( std::string
const& userReporterSpec ) {
3003 if ( userReporterSpec.empty() ) {
3004 return ParserResult::runtimeError(
"Received empty reporter spec." );
3007 Optional<ReporterSpec> parsed =
3010 return ParserResult::runtimeError(
3011 "Could not parse reporter spec '" + userReporterSpec +
3015 auto const& reporterSpec = *parsed;
3017 auto const& factories =
3019 auto result = factories.find( reporterSpec.name() );
3021 if (
result == factories.end() ) {
3022 return ParserResult::runtimeError(
3023 "Unrecognized reporter, '" + reporterSpec.name() +
3024 "'. Check available with --list-reporters" );
3028 const bool hadOutputFile = reporterSpec.outputFile().some();
3029 config.reporterSpecifications.push_back( CATCH_MOVE( *parsed ) );
3034 if (!hadOutputFile) {
3035 int n_reporters_without_file = 0;
3036 for (
auto const& spec : config.reporterSpecifications) {
3037 if (spec.outputFile().none()) {
3038 n_reporters_without_file++;
3041 if (n_reporters_without_file > 1) {
3042 return ParserResult::runtimeError(
"Only one reporter may have unspecified output file." );
3046 return ParserResult::ok( ParseResultType::Matched );
3048 auto const setShardCount = [&]( std::string
const&
shardCount ) {
3050 if ( !parsedCount ) {
3051 return ParserResult::runtimeError(
3052 "Could not parse '" +
shardCount +
"' as shard count" );
3054 if ( *parsedCount == 0 ) {
3055 return ParserResult::runtimeError(
3056 "Shard count must be positive" );
3058 config.shardCount = *parsedCount;
3059 return ParserResult::ok( ParseResultType::Matched );
3062 auto const setShardIndex = [&](std::string
const&
shardIndex) {
3064 if ( !parsedIndex ) {
3065 return ParserResult::runtimeError(
3066 "Could not parse '" +
shardIndex +
"' as shard index" );
3068 config.shardIndex = *parsedIndex;
3069 return ParserResult::ok( ParseResultType::Matched );
3073 = ExeName( config.processName )
3074 | Help( config.showHelp )
3075 | Opt( config.showSuccessfulTests )
3077 (
"include successful tests in output" )
3078 | Opt( config.shouldDebugBreak )
3080 (
"break into debugger on failure" )
3081 | Opt( config.noThrow )
3083 (
"skip exception tests" )
3084 | Opt( config.showInvisibles )
3085 [
"-i"][
"--invisibles"]
3086 (
"show invisibles (tabs, newlines)" )
3087 | Opt( config.defaultOutputFilename,
"filename" )
3089 (
"default output filename" )
3090 | Opt( accept_many, setReporter,
"name[::key=value]*" )
3091 [
"-r"][
"--reporter"]
3092 (
"reporter to use (defaults to console)" )
3093 | Opt( config.name,
"name" )
3096 | Opt( [&](
bool ){ config.abortAfter = 1; } )
3098 (
"abort at first failure" )
3099 | Opt( [&](
int x ){ config.abortAfter =
x; },
"no. failures" )
3101 (
"abort after x failures" )
3102 | Opt( accept_many, setWarning,
"warning name" )
3104 (
"enable warnings" )
3105 | Opt( [&](
bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; },
"yes|no" )
3106 [
"-d"][
"--durations"]
3107 (
"show test durations" )
3108 | Opt( config.minDuration,
"seconds" )
3109 [
"-D"][
"--min-duration"]
3110 (
"show test durations for tests taking at least the given number of seconds" )
3111 | Opt( loadTestNamesFromFile,
"filename" )
3112 [
"-f"][
"--input-file"]
3113 (
"load test names to run from a file" )
3114 | Opt( config.filenamesAsTags )
3115 [
"-#"][
"--filenames-as-tags"]
3116 (
"adds a tag for the filename" )
3117 | Opt( config.sectionsToRun,
"section name" )
3119 (
"specify section to run" )
3120 | Opt( setVerbosity,
"quiet|normal|high" )
3121 [
"-v"][
"--verbosity"]
3122 (
"set output verbosity" )
3123 | Opt( config.listTests )
3125 (
"list all/matching test cases" )
3126 | Opt( config.listTags )
3128 (
"list all/matching tags" )
3129 | Opt( config.listReporters )
3130 [
"--list-reporters"]
3131 (
"list all available reporters" )
3132 | Opt( config.listListeners )
3133 [
"--list-listeners"]
3134 (
"list all listeners" )
3135 | Opt( setTestOrder,
"decl|lex|rand" )
3137 (
"test case order (defaults to decl)" )
3138 | Opt( setRngSeed,
"'time'|'random-device'|number" )
3140 (
"set a specific seed for random numbers" )
3141 | Opt( setDefaultColourMode,
"ansi|win32|none|default" )
3143 (
"what color mode should be used as default" )
3144 | Opt( config.libIdentify )
3146 (
"report name and version according to libidentify standard" )
3147 | Opt( setWaitForKeypress,
"never|start|exit|both" )
3148 [
"--wait-for-keypress"]
3149 (
"waits for a keypress before exiting" )
3150 | Opt( config.skipBenchmarks)
3151 [
"--skip-benchmarks"]
3152 (
"disable running benchmarks")
3153 | Opt( config.benchmarkSamples,
"samples" )
3154 [
"--benchmark-samples"]
3155 (
"number of samples to collect (default: 100)" )
3156 | Opt( config.benchmarkResamples,
"resamples" )
3157 [
"--benchmark-resamples"]
3158 (
"number of resamples for the bootstrap (default: 100000)" )
3159 | Opt( config.benchmarkConfidenceInterval,
"confidence interval" )
3160 [
"--benchmark-confidence-interval"]
3161 (
"confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
3162 | Opt( config.benchmarkNoAnalysis )
3163 [
"--benchmark-no-analysis"]
3164 (
"perform only measurements; do not perform any analysis" )
3165 | Opt( config.benchmarkWarmupTime,
"benchmarkWarmupTime" )
3166 [
"--benchmark-warmup-time"]
3167 (
"amount of time in milliseconds spent on warming up each test (default: 100)" )
3168 | Opt( setShardCount,
"shard count" )
3170 (
"split the tests to execute into this many groups" )
3171 | Opt( setShardIndex,
"shard index" )
3173 (
"index of the group of tests to execute (see --shard-count)" ) |
3174 Opt( config.allowZeroTests )
3175 [
"--allow-running-no-tests"]
3176 (
"Treat 'No tests run' as a success" )
3177 | Arg( config.testsOrTags,
"test name|pattern|tags" )
3178 (
"which test or tests to use" );
3186#if defined(__clang__)
3187# pragma clang diagnostic push
3188# pragma clang diagnostic ignored "-Wexit-time-destructors"
3199 ColourImpl::~ColourImpl() =
default;
3201 ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) {
3202 return ColourGuard(colourCode,
this );
3205 void ColourImpl::ColourGuard::engageImpl( std::ostream&
stream ) {
3206 assert( &
stream == &m_colourImpl->m_stream->stream() &&
3207 "Engaging colour guard for different stream than used by the "
3208 "parent colour implementation" );
3209 static_cast<void>(
stream );
3212 m_colourImpl->use( m_code );
3215 ColourImpl::ColourGuard::ColourGuard( Colour::Code code,
3216 ColourImpl
const* colour ):
3217 m_colourImpl( colour ), m_code( code ) {
3219 ColourImpl::ColourGuard::ColourGuard( ColourGuard&& rhs )
noexcept:
3220 m_colourImpl(
rhs.m_colourImpl ),
3221 m_code(
rhs.m_code ),
3222 m_engaged(
rhs.m_engaged ) {
3223 rhs.m_engaged =
false;
3225 ColourImpl::ColourGuard&
3226 ColourImpl::ColourGuard::operator=( ColourGuard&& rhs )
noexcept {
3228 swap( m_colourImpl,
rhs.m_colourImpl );
3229 swap( m_code,
rhs.m_code );
3230 swap( m_engaged,
rhs.m_engaged );
3234 ColourImpl::ColourGuard::~ColourGuard() {
3236 m_colourImpl->use( Colour::None );
3240 ColourImpl::ColourGuard&
3241 ColourImpl::ColourGuard::engage( std::ostream&
stream ) & {
3246 ColourImpl::ColourGuard&&
3247 ColourImpl::ColourGuard::engage( std::ostream&
stream ) && {
3249 return CATCH_MOVE(*
this);
3255 class NoColourImpl final :
public ColourImpl {
3257 NoColourImpl( IStream*
stream ): ColourImpl(
stream ) {}
3260 void use( Colour::Code )
const override {}
3268#if defined ( CATCH_CONFIG_COLOUR_WIN32 )
3273 class Win32ColourImpl final :
public ColourImpl {
3275 Win32ColourImpl(IStream*
stream):
3277 CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
3278 GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ),
3280 originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
3281 originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
3284 static bool useImplementationForStream(IStream
const&
stream) {
3288 return stream.isConsole();
3292 void use( Colour::Code _colourCode )
const override {
3293 switch( _colourCode ) {
3294 case Colour::None:
return setTextAttribute( originalForegroundAttributes );
3295 case Colour::White:
return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
3296 case Colour::Red:
return setTextAttribute( FOREGROUND_RED );
3297 case Colour::Green:
return setTextAttribute( FOREGROUND_GREEN );
3298 case Colour::Blue:
return setTextAttribute( FOREGROUND_BLUE );
3299 case Colour::Cyan:
return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
3300 case Colour::Yellow:
return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
3301 case Colour::Grey:
return setTextAttribute( 0 );
3303 case Colour::LightGrey:
return setTextAttribute( FOREGROUND_INTENSITY );
3304 case Colour::BrightRed:
return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
3305 case Colour::BrightGreen:
return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
3306 case Colour::BrightWhite:
return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
3307 case Colour::BrightYellow:
return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
3309 case Colour::Bright: CATCH_INTERNAL_ERROR(
"not a colour" );
3312 CATCH_ERROR(
"Unknown colour requested" );
3316 void setTextAttribute( WORD _textAttribute )
const {
3317 SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ),
3319 originalBackgroundAttributes );
3321 WORD originalForegroundAttributes;
3322 WORD originalBackgroundAttributes;
3331#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC )
3332# define CATCH_INTERNAL_HAS_ISATTY
3339 class ANSIColourImpl final :
public ColourImpl {
3341 ANSIColourImpl( IStream*
stream ): ColourImpl(
stream ) {}
3343 static bool useImplementationForStream(IStream
const&
stream) {
3351 bool useColour =
stream.isConsole();
3352#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \
3353 !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) )
3355 useColour = useColour && isatty( STDOUT_FILENO );
3357# if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE )
3358 useColour = useColour && !isDebuggerActive();
3365 void use( Colour::Code _colourCode )
const override {
3366 auto setColour = [&out =
3367 m_stream->stream()](
char const* escapeCode ) {
3371 out <<
'\033' << escapeCode << std::flush;
3373 switch( _colourCode ) {
3375 case Colour::White:
return setColour(
"[0m" );
3376 case Colour::Red:
return setColour(
"[0;31m" );
3377 case Colour::Green:
return setColour(
"[0;32m" );
3378 case Colour::Blue:
return setColour(
"[0;34m" );
3379 case Colour::Cyan:
return setColour(
"[0;36m" );
3380 case Colour::Yellow:
return setColour(
"[0;33m" );
3381 case Colour::Grey:
return setColour(
"[1;30m" );
3383 case Colour::LightGrey:
return setColour(
"[0;37m" );
3384 case Colour::BrightRed:
return setColour(
"[1;31m" );
3385 case Colour::BrightGreen:
return setColour(
"[1;32m" );
3386 case Colour::BrightWhite:
return setColour(
"[1;37m" );
3387 case Colour::BrightYellow:
return setColour(
"[1;33m" );
3389 case Colour::Bright: CATCH_INTERNAL_ERROR(
"not a colour" );
3390 default: CATCH_INTERNAL_ERROR(
"Unknown colour requested" );
3400 Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode implSelection,
3402#if defined( CATCH_CONFIG_COLOUR_WIN32 )
3403 if ( implSelection == ColourMode::Win32 ) {
3404 return Detail::make_unique<Win32ColourImpl>(
stream );
3407 if ( implSelection == ColourMode::ANSI ) {
3408 return Detail::make_unique<ANSIColourImpl>(
stream );
3410 if ( implSelection == ColourMode::None ) {
3411 return Detail::make_unique<NoColourImpl>(
stream );
3414 if ( implSelection == ColourMode::PlatformDefault) {
3415#if defined( CATCH_CONFIG_COLOUR_WIN32 )
3416 if ( Win32ColourImpl::useImplementationForStream( *
stream ) ) {
3417 return Detail::make_unique<Win32ColourImpl>(
stream );
3420 if ( ANSIColourImpl::useImplementationForStream( *
stream ) ) {
3421 return Detail::make_unique<ANSIColourImpl>(
stream );
3423 return Detail::make_unique<NoColourImpl>(
stream );
3426 CATCH_ERROR(
"Could not create colour impl for selection " <<
static_cast<int>(implSelection) );
3429 bool isColourImplAvailable( ColourMode colourSelection ) {
3430 switch ( colourSelection ) {
3431#if defined( CATCH_CONFIG_COLOUR_WIN32 )
3432 case ColourMode::Win32:
3434 case ColourMode::ANSI:
3435 case ColourMode::None:
3436 case ColourMode::PlatformDefault:
3446#if defined(__clang__)
3447# pragma clang diagnostic pop
3455 Context* Context::currentContext =
nullptr;
3457 void cleanUpContext() {
3458 delete Context::currentContext;
3459 Context::currentContext =
nullptr;
3461 void Context::createContext() {
3462 currentContext =
new Context();
3465 Context& getCurrentMutableContext() {
3466 if ( !Context::currentContext ) { Context::createContext(); }
3468 return *Context::currentContext;
3471 void Context::setResultCapture( IResultCapture* resultCapture ) {
3472 m_resultCapture = resultCapture;
3475 void Context::setConfig( IConfig
const* config ) { m_config = config; }
3477 SimplePcg32& sharedRng() {
3478 static SimplePcg32 s_rng;
3490#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
3491#include <android/log.h>
3494 void writeToDebugConsole( std::string
const& text ) {
3495 __android_log_write( ANDROID_LOG_DEBUG,
"Catch", text.c_str() );
3499#elif defined(CATCH_PLATFORM_WINDOWS)
3502 void writeToDebugConsole( std::string
const& text ) {
3503 ::OutputDebugStringA( text.c_str() );
3510 void writeToDebugConsole( std::string
const& text ) {
3520#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
3523# include <sys/types.h>
3528#ifdef __apple_build_version__
3531# include <sys/sysctl.h>
3535 #ifdef __apple_build_version__
3541 bool isDebuggerActive(){
3543 struct kinfo_proc info;
3549 info.kp_proc.p_flag = 0;
3556 mib[2] = KERN_PROC_PID;
3561 size =
sizeof(info);
3562 if( sysctl(mib,
sizeof(mib) /
sizeof(*mib), &info, &
size,
nullptr, 0) != 0 ) {
3563 Catch::cerr() <<
"\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush;
3569 return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
3572 bool isDebuggerActive() {
3579#elif defined(CATCH_PLATFORM_LINUX)
3591 bool isDebuggerActive(){
3595 std::ifstream in(
"/proc/self/status");
3596 for( std::string line; std::getline(in, line); ) {
3597 static const int PREFIX_LEN = 11;
3598 if( line.compare(0, PREFIX_LEN,
"TracerPid:\t") == 0 ) {
3602 return line.length() > PREFIX_LEN && line[PREFIX_LEN] !=
'0';
3609#elif defined(_MSC_VER)
3610 extern "C" __declspec(dllimport)
int __stdcall IsDebuggerPresent();
3612 bool isDebuggerActive() {
3613 return IsDebuggerPresent() != 0;
3616#elif defined(__MINGW32__)
3617 extern "C" __declspec(dllimport)
int __stdcall IsDebuggerPresent();
3619 bool isDebuggerActive() {
3620 return IsDebuggerPresent() != 0;
3625 bool isDebuggerActive() {
return false; }
3634 ITransientExpression::~ITransientExpression() =
default;
3636 void formatReconstructedExpression( std::ostream &os, std::string
const& lhs, StringRef
op, std::string
const& rhs ) {
3637 if(
lhs.size() +
rhs.size() < 40 &&
3638 lhs.find(
'\n') == std::string::npos &&
3639 rhs.find(
'\n') == std::string::npos )
3640 os <<
lhs <<
' ' <<
op <<
' ' <<
rhs;
3642 os <<
lhs <<
'\n' <<
op <<
'\n' <<
rhs;
3652#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
3654 void throw_exception(std::exception
const& e) {
3655 Catch::cerr() <<
"Catch will terminate because it needed to throw an exception.\n"
3656 <<
"The message was: " << e.what() <<
'\n';
3662 void throw_logic_error(std::string
const&
msg) {
3663 throw_exception(std::logic_error(
msg));
3667 void throw_domain_error(std::string
const&
msg) {
3668 throw_exception(std::domain_error(
msg));
3672 void throw_runtime_error(std::string
const&
msg) {
3673 throw_exception(std::runtime_error(
msg));
3686 IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() =
default;
3693 StringRef extractInstanceName(StringRef enumInstance) {
3695 size_t name_start = enumInstance.size();
3696 while (name_start > 0 && enumInstance[name_start - 1] !=
':') {
3699 return enumInstance.substr(name_start, enumInstance.size() - name_start);
3703 std::vector<StringRef> parseEnums( StringRef enums ) {
3705 std::vector<StringRef> parsed;
3706 parsed.reserve( enumValues.size() );
3707 for(
auto const& enumValue : enumValues ) {
3708 parsed.push_back(
trim(extractInstanceName(enumValue)));
3713 EnumInfo::~EnumInfo() {}
3715 StringRef EnumInfo::lookup(
int value )
const {
3716 for(
auto const& valueToName : m_values ) {
3717 if( valueToName.first ==
value )
3718 return valueToName.second;
3720 return "{** unexpected enum value **}"_sr;
3723 Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int>
const&
values ) {
3724 auto enumInfo = Catch::Detail::make_unique<EnumInfo>();
3725 enumInfo->m_name = enumName;
3726 enumInfo->m_values.reserve(
values.size() );
3728 const auto valueNames = Catch::Detail::parseEnums( allValueNames );
3729 assert( valueNames.size() ==
values.size() );
3732 enumInfo->m_values.emplace_back(
value, valueNames[
i++]);
3737 EnumInfo
const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int>
const&
values ) {
3738 m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames,
values));
3739 return *m_enumInfos.back();
3752 ErrnoGuard::ErrnoGuard():m_oldErrno(errno){}
3753 ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; }
3763 static std::string tryTranslators(
3765 Detail::unique_ptr<IExceptionTranslator const>>
const& translators ) {
3766 if ( translators.empty() ) {
3767 std::rethrow_exception( std::current_exception() );
3769 return translators[0]->translate( translators.begin() + 1,
3770 translators.end() );
3776 ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
3779 void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) {
3780 m_translators.push_back( CATCH_MOVE( translator ) );
3783#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
3784 std::string ExceptionTranslatorRegistry::translateActiveException()
const {
3793 if ( std::current_exception() ==
nullptr ) {
3794 return "Non C++ exception. Possibly a CLR exception.";
3800 return tryTranslators(m_translators);
3804 catch( TestFailureException& ) {
3805 std::rethrow_exception(std::current_exception());
3807 catch( TestSkipException& ) {
3808 std::rethrow_exception(std::current_exception());
3810 catch( std::exception
const& ex ) {
3813 catch( std::string
const&
msg ) {
3816 catch(
const char*
msg ) {
3820 return "Unknown exception";
3825 std::string ExceptionTranslatorRegistry::translateActiveException()
const {
3826 CATCH_INTERNAL_ERROR(
"Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
3856#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
3862 void FatalConditionHandler::engage_platform() {}
3863 void FatalConditionHandler::disengage_platform() noexcept {}
3864 FatalConditionHandler::FatalConditionHandler() =
default;
3865 FatalConditionHandler::~FatalConditionHandler() =
default;
3871#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
3872#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
3875#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
3879 void reportFatal(
char const *
const message ) {
3880 Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition(
message );
3886 constexpr std::size_t minStackSizeForErrors = 32 * 1024;
3891#if defined( CATCH_CONFIG_WINDOWS_SEH )
3895 struct SignalDefs { DWORD
id;
const char*
name; };
3900 static SignalDefs signalDefs[] = {
3901 { EXCEPTION_ILLEGAL_INSTRUCTION,
"SIGILL - Illegal instruction signal" },
3902 { EXCEPTION_STACK_OVERFLOW,
"SIGSEGV - Stack overflow" },
3903 { EXCEPTION_ACCESS_VIOLATION,
"SIGSEGV - Segmentation violation signal" },
3904 { EXCEPTION_INT_DIVIDE_BY_ZERO,
"Divide by zero error" },
3907 static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
3908 for (
auto const& def : signalDefs) {
3909 if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
3910 reportFatal(def.name);
3915 return EXCEPTION_CONTINUE_SEARCH;
3921 static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter =
nullptr;
3926 FatalConditionHandler::FatalConditionHandler() {
3927 ULONG guaranteeSize =
static_cast<ULONG
>(minStackSizeForErrors);
3928 if (!SetThreadStackGuarantee(&guaranteeSize)) {
3932 <<
"Failed to reserve piece of stack."
3933 <<
" Stack overflows will not be reported successfully.";
3939 FatalConditionHandler::~FatalConditionHandler() =
default;
3942 void FatalConditionHandler::engage_platform() {
3944 previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter);
3947 void FatalConditionHandler::disengage_platform() noexcept {
3948 if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) {
3950 <<
"Unexpected SEH unhandled exception filter on disengage."
3951 <<
" The filter was restored, but might be rolled back unexpectedly.";
3953 previousTopLevelExceptionFilter =
nullptr;
3960#if defined( CATCH_CONFIG_POSIX_SIGNALS )
3972 { SIGINT,
"SIGINT - Terminal interrupt signal" },
3973 { SIGILL,
"SIGILL - Illegal instruction signal" },
3974 { SIGFPE,
"SIGFPE - Floating point error signal" },
3975 { SIGSEGV,
"SIGSEGV - Segmentation violation signal" },
3976 { SIGTERM,
"SIGTERM - Termination request signal" },
3977 { SIGABRT,
"SIGABRT - Abort (abnormal termination) signal" }
3983#if defined(__GNUC__)
3984# pragma GCC diagnostic push
3985# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
3988 static char* altStackMem =
nullptr;
3989 static std::size_t altStackSize = 0;
3990 static stack_t oldSigStack{};
3991 static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
3993 static void restorePreviousSignalHandlers() noexcept {
3998 for (std::size_t
i = 0;
i <
sizeof(signalDefs) /
sizeof(SignalDefs); ++
i) {
3999 sigaction(signalDefs[
i].
id, &oldSigActions[
i],
nullptr);
4002 sigaltstack(&oldSigStack,
nullptr);
4005 static void handleSignal(
int sig ) {
4006 char const *
name =
"<unknown signal>";
4007 for (
auto const& def : signalDefs) {
4008 if (sig == def.id) {
4016 restorePreviousSignalHandlers();
4017 reportFatal(
name );
4021 FatalConditionHandler::FatalConditionHandler() {
4022 assert(!altStackMem &&
"Cannot initialize POSIX signal handler when one already exists");
4023 if (altStackSize == 0) {
4024 altStackSize = std::max(
static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
4026 altStackMem =
new char[altStackSize]();
4029 FatalConditionHandler::~FatalConditionHandler() {
4030 delete[] altStackMem;
4033 altStackMem =
nullptr;
4036 void FatalConditionHandler::engage_platform() {
4038 sigStack.ss_sp = altStackMem;
4039 sigStack.ss_size = altStackSize;
4040 sigStack.ss_flags = 0;
4041 sigaltstack(&sigStack, &oldSigStack);
4042 struct sigaction sa = { };
4044 sa.sa_handler = handleSignal;
4045 sa.sa_flags = SA_ONSTACK;
4046 for (std::size_t
i = 0;
i <
sizeof(signalDefs)/
sizeof(SignalDefs); ++
i) {
4047 sigaction(signalDefs[
i].
id, &sa, &oldSigActions[
i]);
4051#if defined(__GNUC__)
4052# pragma GCC diagnostic pop
4056 void FatalConditionHandler::disengage_platform() noexcept {
4057 restorePreviousSignalHandlers();
4073 static_assert(
sizeof(float) ==
sizeof(uint32_t),
"Important ULP matcher assumption violated");
4075 std::memcpy(&
i, &
f,
sizeof(
f));
4080 static_assert(
sizeof(double) ==
sizeof(uint64_t),
"Important ULP matcher assumption violated");
4082 std::memcpy(&
i, &d,
sizeof(d));
4099#if !defined (CATCH_CONFIG_GETENV)
4100 char const*
getEnv(
char const* ) {
return nullptr; }
4104# if defined( _MSC_VER )
4105# pragma warning( push )
4106# pragma warning( disable : 4996 )
4109 return std::getenv( varName );
4111# if defined( _MSC_VER )
4112# pragma warning( pop )
4129 Catch::IStream::~IStream() =
default;
4133 template<
typename WriterF, std::
size_t bufferSize=256>
4134 class StreamBufImpl final :
public std::streambuf {
4135 char data[bufferSize];
4143 ~StreamBufImpl() noexcept
override {
4144 StreamBufImpl::sync();
4148 int overflow(
int c )
override {
4152 if( pbase() == epptr() )
4153 m_writer( std::string( 1,
static_cast<char>(
c ) ) );
4155 sputc(
static_cast<char>(
c ) );
4160 int sync()
override {
4161 if( pbase() != pptr() ) {
4162 m_writer( std::string( pbase(),
static_cast<std::string::size_type
>( pptr() - pbase() ) ) );
4163 setp( pbase(), epptr() );
4171 struct OutputDebugWriter {
4173 void operator()( std::string
const&
str ) {
4174 if ( !
str.empty() ) {
4175 writeToDebugConsole(
str );
4182 class FileStream final :
public IStream {
4183 std::ofstream m_ofs;
4185 FileStream( std::string
const&
filename ) {
4187 CATCH_ENFORCE( !m_ofs.fail(),
"Unable to open file: '" <<
filename <<
'\'' );
4188 m_ofs << std::unitbuf;
4190 ~FileStream()
override =
default;
4192 std::ostream&
stream()
override {
4199 class CoutStream final :
public IStream {
4204 CoutStream() : m_os(
Catch::
cout().rdbuf() ) {}
4205 ~CoutStream()
override =
default;
4208 std::ostream&
stream()
override {
return m_os; }
4209 bool isConsole()
const override {
return true; }
4212 class CerrStream :
public IStream {
4218 CerrStream(): m_os(
Catch::
cerr().rdbuf() ) {}
4219 ~CerrStream()
override =
default;
4222 std::ostream&
stream()
override {
return m_os; }
4223 bool isConsole()
const override {
return true; }
4228 class DebugOutStream final :
public IStream {
4229 Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
4233 : m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ),
4234 m_os( m_streamBuf.
get() )
4237 ~DebugOutStream()
override =
default;
4240 std::ostream&
stream()
override {
return m_os; }
4250 return Detail::make_unique<Detail::CoutStream>();
4254 return Detail::make_unique<Detail::DebugOutStream>();
4255 }
else if (
filename ==
"%stderr" ) {
4256 return Detail::make_unique<Detail::CerrStream>();
4257 }
else if (
filename ==
"%stdout" ) {
4258 return Detail::make_unique<Detail::CoutStream>();
4260 CATCH_ERROR(
"Unrecognised stream: '" <<
filename <<
'\'' );
4263 return Detail::make_unique<Detail::FileStream>(
filename );
4273 auto operator << (std::ostream& os, LazyExpression
const& lazyExpr) -> std::ostream& {
4274 if (lazyExpr.m_isNegated)
4278 if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression())
4279 os <<
'(' << *lazyExpr.m_transientExpression <<
')';
4281 os << *lazyExpr.m_transientExpression;
4283 os <<
"{** error - unchecked empty expression requested **}";
4293#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
4298 LeakDetector::LeakDetector() {
4299 int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
4300 flag |= _CRTDBG_LEAK_CHECK_DF;
4301 flag |= _CRTDBG_ALLOC_MEM_DF;
4302 _CrtSetDbgFlag(flag);
4303 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
4304 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
4306 _CrtSetBreakAlloc(-1);
4312 Catch::LeakDetector::LeakDetector() {}
4316Catch::LeakDetector::~LeakDetector() {
4327 void listTests(IEventListener& reporter, IConfig
const& config) {
4328 auto const& testSpec = config.testSpec();
4330 reporter.listTests(matchedTestCases);
4333 void listTags(IEventListener& reporter, IConfig
const& config) {
4334 auto const& testSpec = config.testSpec();
4337 std::map<StringRef, TagInfo, Detail::CaseInsensitiveLess> tagCounts;
4338 for (
auto const& testCase : matchedTestCases) {
4339 for (
auto const& tagName : testCase.getTestCaseInfo().tags) {
4340 auto it = tagCounts.find(tagName.original);
4341 if (it == tagCounts.end())
4342 it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first;
4343 it->second.add(tagName.original);
4347 std::vector<TagInfo> infos; infos.reserve(tagCounts.size());
4348 for (
auto& tagc : tagCounts) {
4349 infos.push_back(CATCH_MOVE(tagc.second));
4352 reporter.listTags(infos);
4355 void listReporters(IEventListener& reporter) {
4356 std::vector<ReporterDescription> descriptions;
4358 auto const& factories =
getRegistryHub().getReporterRegistry().getFactories();
4359 descriptions.reserve(factories.size());
4360 for (
auto const& fac : factories) {
4361 descriptions.push_back({ fac.first, fac.second->getDescription() });
4364 reporter.listReporters(descriptions);
4367 void listListeners(IEventListener& reporter) {
4368 std::vector<ListenerDescription> descriptions;
4370 auto const& factories =
4372 descriptions.reserve( factories.size() );
4373 for (
auto const& fac : factories ) {
4374 descriptions.push_back( { fac->getName(), fac->getDescription() } );
4377 reporter.listListeners( descriptions );
4382 void TagInfo::add( StringRef spelling ) {
4384 spellings.insert( spelling );
4387 std::string TagInfo::all()
const {
4389 size_t size = spellings.size() * 2;
4390 for (
auto const& spelling : spellings) {
4391 size += spelling.size();
4394 std::string out; out.reserve(
size);
4395 for (
auto const& spelling : spellings) {
4403 bool list( IEventListener& reporter, Config
const& config ) {
4404 bool listed =
false;
4405 if (config.listTests()) {
4407 listTests(reporter, config);
4409 if (config.listTags()) {
4411 listTags(reporter, config);
4413 if (config.listReporters()) {
4415 listReporters(reporter);
4417 if ( config.listListeners() ) {
4419 listListeners( reporter );
4429 CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
4430 CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
4431 static LeakDetector leakDetector;
4432 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
4436#if !defined(CATCH_AMALGAMATED_CUSTOM_MAIN)
4438#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
4440extern "C" int __cdecl wmain (
int argc,
wchar_t * argv[],
wchar_t * []) {
4443int main (
int argc,
char * argv[]) {
4448 (
void)&Catch::leakDetector;
4450 return Catch::Session().run( argc, argv );
4460 MessageInfo::MessageInfo( StringRef _macroName,
4461 SourceLineInfo
const& _lineInfo,
4462 ResultWas::OfType _type )
4463 : macroName( _macroName ),
4464 lineInfo( _lineInfo ),
4466 sequence( ++globalCount )
4470 unsigned int MessageInfo::globalCount = 0;
4480#if defined(CATCH_CONFIG_NEW_CAPTURE)
4481 #if defined(_MSC_VER)
4485 #define fileno _fileno
4494 RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
4495 : m_originalStream( originalStream ),
4496 m_redirectionStream( redirectionStream ),
4497 m_prevBuf( m_originalStream.rdbuf() )
4499 m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
4502 RedirectedStream::~RedirectedStream() {
4503 m_originalStream.rdbuf( m_prevBuf );
4506 RedirectedStdOut::RedirectedStdOut() : m_cout(
Catch::
cout(), m_rss.
get() ) {}
4507 auto RedirectedStdOut::str() const ->
std::
string {
return m_rss.str(); }
4509 RedirectedStdErr::RedirectedStdErr()
4513 auto RedirectedStdErr::str() const ->
std::
string {
return m_rss.str(); }
4515 RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
4516 : m_redirectedCout(redirectedCout),
4517 m_redirectedCerr(redirectedCerr)
4520 RedirectedStreams::~RedirectedStreams() {
4521 m_redirectedCout += m_redirectedStdOut.str();
4522 m_redirectedCerr += m_redirectedStdErr.str();
4525#if defined(CATCH_CONFIG_NEW_CAPTURE)
4527#if defined(_MSC_VER)
4528 TempFile::TempFile() {
4529 if (tmpnam_s(m_buffer)) {
4530 CATCH_RUNTIME_ERROR(
"Could not get a temp filename");
4532 if (fopen_s(&m_file, m_buffer,
"w+")) {
4534 if (strerror_s(
buffer, errno)) {
4535 CATCH_RUNTIME_ERROR(
"Could not translate errno to a string");
4537 CATCH_RUNTIME_ERROR(
"Could not open the temp file: '" << m_buffer <<
"' because: " <<
buffer);
4541 TempFile::TempFile() {
4542 m_file = std::tmpfile();
4544 CATCH_RUNTIME_ERROR(
"Could not create a temp file.");
4550 TempFile::~TempFile() {
4552 std::fclose(m_file);
4555#if defined(_MSC_VER)
4556 std::remove(m_buffer);
4561 FILE* TempFile::getFile() {
4565 std::string TempFile::getContents() {
4566 std::stringstream sstr;
4568 std::rewind(m_file);
4575 OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
4576 m_originalStdout(dup(1)),
4577 m_originalStderr(dup(2)),
4578 m_stdoutDest(stdout_dest),
4579 m_stderrDest(stderr_dest) {
4580 dup2(fileno(m_stdoutFile.getFile()), 1);
4581 dup2(fileno(m_stderrFile.getFile()), 2);
4584 OutputRedirect::~OutputRedirect() {
4593 dup2(m_originalStdout, 1);
4594 dup2(m_originalStderr, 2);
4596 m_stdoutDest += m_stdoutFile.getContents();
4597 m_stderrDest += m_stderrFile.getContents();
4604#if defined(CATCH_CONFIG_NEW_CAPTURE)
4605 #if defined(_MSC_VER)
4620 Optional<unsigned int>
parseUInt(std::string
const& input,
int base) {
4621 auto trimmed =
trim( input );
4624 if ( trimmed.empty() || trimmed[0] ==
'-' ) {
4630 const auto ret = std::stoull( trimmed, &
pos, base );
4636 if (
pos != trimmed.size() ) {
4640 if ( ret > std::numeric_limits<unsigned int>::max() ) {
4643 return static_cast<unsigned int>(ret);
4645 CATCH_CATCH_ANON( std::invalid_argument
const& ) {
4648 CATCH_CATCH_ANON( std::out_of_range
const& ) {
4663#if !defined(CATCH_CONFIG_POLYFILL_ISNAN)
4665 return std::isnan(
f);
4668 return std::isnan(d);
4673 return std::_isnan(
f);
4675 bool isnan(
double d) {
4676 return std::_isnan(d);
4688#if defined(_MSC_VER)
4689#pragma warning(push)
4690#pragma warning(disable:4146)
4693 uint32_t rotate_right(uint32_t
val, uint32_t
count) {
4694 const uint32_t
mask = 31;
4699#if defined(_MSC_VER)
4706 SimplePcg32::SimplePcg32(result_type seed_) {
4711 void SimplePcg32::seed(result_type seed_) {
4718 void SimplePcg32::discard(uint64_t skip) {
4721 for (uint64_t s = 0;
s < skip; ++
s) {
4722 static_cast<void>((*this)());
4726 SimplePcg32::result_type SimplePcg32::operator()() {
4728 const uint32_t xorshifted =
static_cast<uint32_t
>(((m_state >> 18u) ^ m_state) >> 27u);
4729 const auto output = rotate_right(xorshifted, m_state >> 59u);
4732 m_state = m_state * 6364136223846793005ULL + s_inc;
4737 bool operator==(SimplePcg32
const& lhs, SimplePcg32
const& rhs) {
4738 return lhs.m_state == rhs.m_state;
4741 bool operator!=(SimplePcg32
const& lhs, SimplePcg32
const& rhs) {
4742 return lhs.m_state != rhs.m_state;
4757 case GenerateFrom::Time:
4758 return static_cast<std::uint32_t
>( std::time(
nullptr ) );
4760 case GenerateFrom::Default:
4761 case GenerateFrom::RandomDevice:
4764 return static_cast<std::uint32_t
>( std::random_device{}() );
4767 CATCH_ERROR(
"Unknown generation method");
4778 std::vector<Detail::unique_ptr<EventListenerFactory>>
listeners;
4779 std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>
4783 ReporterRegistry::ReporterRegistry():
4787 m_impl->factories[
"Automake"] =
4788 Detail::make_unique<ReporterFactory<AutomakeReporter>>();
4789 m_impl->factories[
"compact"] =
4790 Detail::make_unique<ReporterFactory<CompactReporter>>();
4791 m_impl->factories[
"console"] =
4792 Detail::make_unique<ReporterFactory<ConsoleReporter>>();
4793 m_impl->factories[
"JUnit"] =
4794 Detail::make_unique<ReporterFactory<JunitReporter>>();
4795 m_impl->factories[
"SonarQube"] =
4796 Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
4797 m_impl->factories[
"TAP"] =
4798 Detail::make_unique<ReporterFactory<TAPReporter>>();
4799 m_impl->factories[
"TeamCity"] =
4800 Detail::make_unique<ReporterFactory<TeamCityReporter>>();
4801 m_impl->factories[
"XML"] =
4802 Detail::make_unique<ReporterFactory<XmlReporter>>();
4805 ReporterRegistry::~ReporterRegistry() =
default;
4808 ReporterRegistry::create( std::string
const&
name,
4809 ReporterConfig&& config )
const {
4810 auto it = m_impl->factories.find(
name );
4811 if ( it == m_impl->factories.end() )
return nullptr;
4812 return it->second->create( CATCH_MOVE( config ) );
4815 void ReporterRegistry::registerReporter( std::string
const&
name,
4816 IReporterFactoryPtr factory ) {
4817 CATCH_ENFORCE(
name.find(
"::" ) ==
name.npos,
4818 "'::' is not allowed in reporter name: '" +
name +
4820 auto ret = m_impl->factories.emplace(
name, CATCH_MOVE( factory ) );
4821 CATCH_ENFORCE( ret.second,
4822 "reporter using '" +
name +
4823 "' as name was already registered" );
4825 void ReporterRegistry::registerListener(
4826 Detail::unique_ptr<EventListenerFactory> factory ) {
4827 m_impl->listeners.push_back( CATCH_MOVE( factory ) );
4830 std::map<std::string,
4831 IReporterFactoryPtr,
4832 Detail::CaseInsensitiveLess>
const&
4833 ReporterRegistry::getFactories()
const {
4834 return m_impl->factories;
4837 std::vector<Detail::unique_ptr<EventListenerFactory>>
const&
4838 ReporterRegistry::getListeners()
const {
4839 return m_impl->listeners;
4856 kvPair splitKVPair(StringRef kvString) {
4857 auto splitPos =
static_cast<size_t>(
4858 std::find( kvString.begin(), kvString.end(),
'=' ) -
4861 return { kvString.substr( 0, splitPos ),
4862 kvString.substr( splitPos + 1, kvString.size() ) };
4868 static constexpr auto separator =
"::";
4869 static constexpr size_t separatorSize = 2;
4871 size_t separatorPos = 0;
4872 auto findNextSeparator = [&reporterSpec](
size_t startPos ) {
4875 "The code below currently assumes 2 char separator" );
4877 auto currentPos = startPos;
4879 while ( currentPos < reporterSpec.size() &&
4880 reporterSpec[currentPos] != separator[0] ) {
4883 if ( currentPos + 1 < reporterSpec.size() &&
4884 reporterSpec[currentPos + 1] == separator[1] ) {
4888 }
while ( currentPos < reporterSpec.size() );
4890 return static_cast<size_t>( -1 );
4893 std::vector<std::string> parts;
4895 while ( separatorPos < reporterSpec.size() ) {
4896 const auto nextSeparator = findNextSeparator( separatorPos );
4897 parts.push_back(
static_cast<std::string
>( reporterSpec.substr(
4898 separatorPos, nextSeparator - separatorPos ) ) );
4900 if ( nextSeparator ==
static_cast<size_t>( -1 ) ) {
4903 separatorPos = nextSeparator + separatorSize;
4909 if ( separatorPos == reporterSpec.size() ) {
4910 parts.emplace_back();
4917 if ( colourMode ==
"default" ) {
4918 return ColourMode::PlatformDefault;
4919 }
else if ( colourMode ==
"ansi" ) {
4920 return ColourMode::ANSI;
4921 }
else if ( colourMode ==
"win32" ) {
4922 return ColourMode::Win32;
4923 }
else if ( colourMode ==
"none" ) {
4924 return ColourMode::None;
4932 bool operator==( ReporterSpec
const& lhs, ReporterSpec
const& rhs ) {
4933 return lhs.m_name == rhs.m_name &&
4934 lhs.m_outputFileName == rhs.m_outputFileName &&
4935 lhs.m_colourMode == rhs.m_colourMode &&
4936 lhs.m_customOptions == rhs.m_customOptions;
4942 assert( parts.size() > 0 &&
"Split should never return empty vector" );
4944 std::map<std::string, std::string> kvPairs;
4945 Optional<std::string> outputFileName;
4946 Optional<ColourMode> colourMode;
4949 for (
size_t i = 1;
i < parts.size(); ++
i ) {
4950 auto kv = splitKVPair( parts[
i] );
4951 auto key = kv.key,
value = kv.value;
4953 if (
key.empty() ||
value.empty() ) {
4955 }
else if (
key[0] ==
'X' ) {
4958 if (
key.size() == 1 ) {
4962 auto ret = kvPairs.emplace( std::string(kv.key), std::string(kv.value) );
4963 if ( !ret.second ) {
4968 }
else if (
key ==
"out" ) {
4970 if ( outputFileName ) {
4973 outputFileName =
static_cast<std::string
>(
value );
4974 }
else if (
key ==
"colour-mode" ) {
4981 if ( !colourMode ) {
4990 return ReporterSpec{ CATCH_MOVE( parts[0] ),
4991 CATCH_MOVE( outputFileName ),
4992 CATCH_MOVE( colourMode ),
4993 CATCH_MOVE( kvPairs ) };
4996ReporterSpec::ReporterSpec(
4998 Optional<std::string> outputFileName,
4999 Optional<ColourMode> colourMode,
5000 std::map<std::string, std::string> customOptions ):
5001 m_name( CATCH_MOVE(
name ) ),
5002 m_outputFileName( CATCH_MOVE( outputFileName ) ),
5003 m_colourMode( CATCH_MOVE( colourMode ) ),
5004 m_customOptions( CATCH_MOVE( customOptions ) ) {}
5012 bool isOk( ResultWas::OfType resultType ) {
5013 return ( resultType & ResultWas::FailureBit ) == 0;
5016 return flags == ResultWas::Info;
5019 ResultDisposition::Flags
operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
5020 return static_cast<ResultDisposition::Flags
>(
static_cast<int>( lhs ) |
static_cast<int>( rhs ) );
5038 std::vector<Detail::unique_ptr<std::ostringstream>>
m_streams;
5044 m_streams.push_back( Detail::make_unique<std::ostringstream>() );
5060 ReusableStringStream::ReusableStringStream()
5061 : m_index( Singleton<StringStreams>::getMutable().add() ),
5062 m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
5065 ReusableStringStream::~ReusableStringStream() {
5066 static_cast<std::ostringstream*
>( m_oss )->
str(
"");
5068 Singleton<StringStreams>::getMutable().release( m_index );
5071 std::string ReusableStringStream::str()
const {
5072 return static_cast<std::ostringstream*
>( m_oss )->
str();
5075 void ReusableStringStream::str( std::string
const&
str ) {
5076 static_cast<std::ostringstream*
>( m_oss )->
str(
str );
5090 namespace Generators {
5092 struct GeneratorTracker final : TestCaseTracking::TrackerBase,
5097 TestCaseTracking::NameAndLocation&& nameAndLocation,
5098 TrackerContext& ctx,
5100 TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {}
5101 ~GeneratorTracker()
override =
default;
5103 static GeneratorTracker*
5104 acquire( TrackerContext& ctx,
5105 TestCaseTracking::NameAndLocationRef
const&
5107 GeneratorTracker* tracker;
5109 ITracker& currentTracker = ctx.currentTracker();
5121 if ( currentTracker.nameAndLocation() == nameAndLocation ) {
5122 auto thisTracker = currentTracker.parent()->findChild(
5124 assert( thisTracker );
5125 assert( thisTracker->isGeneratorTracker() );
5126 tracker =
static_cast<GeneratorTracker*
>( thisTracker );
5127 }
else if ( ITracker* childTracker =
5128 currentTracker.findChild(
5129 nameAndLocation ) ) {
5130 assert( childTracker );
5131 assert( childTracker->isGeneratorTracker() );
5133 static_cast<GeneratorTracker*
>( childTracker );
5138 if ( !tracker->isComplete() ) { tracker->open(); }
5144 bool isGeneratorTracker()
const override {
return true; }
5145 auto hasGenerator() const ->
bool override {
5148 void close()
override {
5149 TrackerBase::close();
5158 const bool should_wait_for_child = [&]() {
5160 if ( m_children.empty() ) {
return false; }
5165 []( TestCaseTracking::ITrackerPtr
const&
5167 return tracker->hasStarted();
5168 } ) != m_children.end() ) {
5176 ITracker* parent = m_parent;
5179 while ( !parent->isSectionTracker() ) {
5180 parent = parent->parent();
5183 "Missing root (test case) level section" );
5185 auto const& parentSection =
5186 static_cast<SectionTracker const&
>( *parent );
5187 auto const& filters = parentSection.getFilters();
5189 if ( filters.empty() ) {
return true; }
5191 for (
auto const& child : m_children ) {
5192 if ( child->isSectionTracker() &&
5193 std::find( filters.begin(),
5195 static_cast<SectionTracker const&
>(
5209 assert( m_generator &&
"Tracker without generator" );
5210 if ( should_wait_for_child ||
5211 ( m_runState == CompletedSuccessfully &&
5214 m_runState = Executing;
5219 auto getGenerator() const -> GeneratorBasePtr const&
override {
5222 void setGenerator( GeneratorBasePtr&& generator )
override {
5229 RunContext::RunContext(IConfig
const* _config, IEventListenerPtr&& reporter)
5230 : m_runInfo(_config->
name()),
5232 m_reporter(CATCH_MOVE(reporter)),
5233 m_lastAssertionInfo{ StringRef(), SourceLineInfo(
"",0), StringRef(), ResultDisposition::Normal },
5234 m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
5236 getCurrentMutableContext().setResultCapture(
this );
5237 m_reporter->testRunStarting(m_runInfo);
5240 RunContext::~RunContext() {
5241 m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
5244 Totals RunContext::runTest(TestCaseHandle
const& testCase) {
5245 const Totals prevTotals = m_totals;
5247 auto const& testInfo = testCase.getTestCaseInfo();
5248 m_reporter->testCaseStarting(testInfo);
5249 m_activeTestCase = &testCase;
5252 ITracker& rootTracker = m_trackerContext.startRun();
5253 assert(rootTracker.isSectionTracker());
5254 static_cast<SectionTracker&
>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
5289 uint64_t testRuns = 0;
5290 std::string redirectedCout;
5291 std::string redirectedCerr;
5293 m_trackerContext.startCycle();
5294 m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo));
5296 m_reporter->testCasePartialStarting(testInfo, testRuns);
5298 const auto beforeRunTotals = m_totals;
5299 std::string oneRunCout, oneRunCerr;
5300 runCurrentTest(oneRunCout, oneRunCerr);
5301 redirectedCout += oneRunCout;
5302 redirectedCerr += oneRunCerr;
5304 const auto singleRunTotals = m_totals.delta(beforeRunTotals);
5305 auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
5307 m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
5309 }
while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
5311 Totals deltaTotals = m_totals.delta(prevTotals);
5312 if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) {
5313 deltaTotals.assertions.failed++;
5314 deltaTotals.testCases.passed--;
5315 deltaTotals.testCases.failed++;
5317 m_totals.testCases += deltaTotals.testCases;
5318 m_reporter->testCaseEnded(TestCaseStats(testInfo,
5320 CATCH_MOVE(redirectedCout),
5321 CATCH_MOVE(redirectedCerr),
5324 m_activeTestCase =
nullptr;
5325 m_testCaseTracker =
nullptr;
5331 void RunContext::assertionEnded(AssertionResult&&
result) {
5332 if (
result.getResultType() == ResultWas::Ok) {
5333 m_totals.assertions.passed++;
5334 m_lastAssertionPassed =
true;
5335 }
else if (
result.getResultType() == ResultWas::ExplicitSkip) {
5336 m_totals.assertions.skipped++;
5337 m_lastAssertionPassed =
true;
5338 }
else if (!
result.succeeded()) {
5339 m_lastAssertionPassed =
false;
5342 else if( m_activeTestCase->getTestCaseInfo().okToFail() )
5343 m_totals.assertions.failedButOk++;
5345 m_totals.assertions.failed++;
5348 m_lastAssertionPassed =
true;
5351 m_reporter->assertionEnded(AssertionStats(
result, m_messages, m_totals));
5353 if (
result.getResultType() != ResultWas::Warning ) {
5354 m_messageScopes.clear();
5358 resetAssertionInfo();
5359 m_lastResult = CATCH_MOVE(
result );
5361 void RunContext::resetAssertionInfo() {
5362 m_lastAssertionInfo.macroName = StringRef();
5363 m_lastAssertionInfo.capturedExpression =
"{Unknown expression after the reported line}"_sr;
5366 void RunContext::notifyAssertionStarted( AssertionInfo
const& info ) {
5367 m_reporter->assertionStarting( info );
5370 bool RunContext::sectionStarted( StringRef sectionName,
5371 SourceLineInfo
const& sectionLineInfo,
5372 Counts& assertions ) {
5373 ITracker& sectionTracker =
5374 SectionTracker::acquire( m_trackerContext,
5375 TestCaseTracking::NameAndLocationRef(
5376 sectionName, sectionLineInfo ) );
5378 if (!sectionTracker.isOpen())
5380 m_activeSections.push_back(§ionTracker);
5382 SectionInfo sectionInfo( sectionLineInfo,
static_cast<std::string
>(sectionName) );
5383 m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
5385 m_reporter->sectionStarting(sectionInfo);
5387 assertions = m_totals.assertions;
5392 RunContext::acquireGeneratorTracker( StringRef generatorName,
5393 SourceLineInfo
const& lineInfo ) {
5394 using namespace Generators;
5395 GeneratorTracker* tracker = GeneratorTracker::acquire(
5397 TestCaseTracking::NameAndLocationRef(
5398 generatorName, lineInfo ) );
5399 m_lastAssertionInfo.lineInfo = lineInfo;
5403 IGeneratorTracker* RunContext::createGeneratorTracker(
5404 StringRef generatorName,
5405 SourceLineInfo lineInfo,
5406 Generators::GeneratorBasePtr&& generator ) {
5408 auto nameAndLoc = TestCaseTracking::NameAndLocation(
static_cast<std::string
>( generatorName ), lineInfo );
5409 auto& currentTracker = m_trackerContext.currentTracker();
5411 currentTracker.nameAndLocation() != nameAndLoc &&
5412 "Trying to create tracker for a genreator that already has one" );
5414 auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>(
5415 CATCH_MOVE(nameAndLoc), m_trackerContext, ¤tTracker );
5416 auto ret = newTracker.get();
5417 currentTracker.addChild( CATCH_MOVE( newTracker ) );
5419 ret->setGenerator( CATCH_MOVE( generator ) );
5424 bool RunContext::testForMissingAssertions(Counts& assertions) {
5425 if (assertions.total() != 0)
5427 if (!m_config->warnAboutMissingAssertions())
5429 if (m_trackerContext.currentTracker().hasChildren())
5431 m_totals.assertions.failed++;
5432 assertions.failed++;
5436 void RunContext::sectionEnded(SectionEndInfo&& endInfo) {
5437 Counts assertions = m_totals.assertions - endInfo.prevAssertions;
5438 bool missingAssertions = testForMissingAssertions(assertions);
5440 if (!m_activeSections.empty()) {
5441 m_activeSections.back()->close();
5442 m_activeSections.pop_back();
5445 m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions));
5447 m_messageScopes.clear();
5450 void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) {
5451 if ( m_unfinishedSections.empty() ) {
5452 m_activeSections.back()->fail();
5454 m_activeSections.back()->close();
5456 m_activeSections.pop_back();
5458 m_unfinishedSections.push_back(CATCH_MOVE(endInfo));
5461 void RunContext::benchmarkPreparing( StringRef
name ) {
5462 m_reporter->benchmarkPreparing(
name);
5464 void RunContext::benchmarkStarting( BenchmarkInfo
const& info ) {
5465 m_reporter->benchmarkStarting( info );
5467 void RunContext::benchmarkEnded( BenchmarkStats<>
const& stats ) {
5468 m_reporter->benchmarkEnded( stats );
5470 void RunContext::benchmarkFailed( StringRef error ) {
5471 m_reporter->benchmarkFailed( error );
5474 void RunContext::pushScopedMessage(MessageInfo
const &
message) {
5475 m_messages.push_back(
message);
5478 void RunContext::popScopedMessage(MessageInfo
const &
message) {
5479 m_messages.erase(std::remove(m_messages.begin(), m_messages.end(),
message), m_messages.end());
5482 void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
5483 m_messageScopes.emplace_back( CATCH_MOVE(builder) );
5486 std::string RunContext::getCurrentTestName()
const {
5487 return m_activeTestCase
5488 ? m_activeTestCase->getTestCaseInfo().name
5492 const AssertionResult * RunContext::getLastResult()
const {
5493 return &(*m_lastResult);
5496 void RunContext::exceptionEarlyReported() {
5497 m_shouldReportUnexpected =
false;
5500 void RunContext::handleFatalErrorCondition( StringRef
message ) {
5502 m_reporter->fatalErrorEncountered(
message);
5506 AssertionResultData tempResult( ResultWas::FatalErrorCondition, {
false } );
5507 tempResult.message =
static_cast<std::string
>(
message);
5508 AssertionResult
result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
5510 assertionEnded(CATCH_MOVE(
result) );
5512 handleUnfinishedSections();
5515 auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
5516 SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
5519 assertions.failed = 1;
5520 SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0,
false);
5521 m_reporter->sectionEnded(testCaseSectionStats);
5523 auto const& testInfo = m_activeTestCase->getTestCaseInfo();
5526 deltaTotals.testCases.failed = 1;
5527 deltaTotals.assertions.failed = 1;
5528 m_reporter->testCaseEnded(TestCaseStats(testInfo,
5533 m_totals.testCases.failed++;
5534 m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals,
false));
5537 bool RunContext::lastAssertionPassed() {
5538 return m_lastAssertionPassed;
5541 void RunContext::assertionPassed() {
5542 m_lastAssertionPassed =
true;
5543 ++m_totals.assertions.passed;
5544 resetAssertionInfo();
5545 m_messageScopes.clear();
5548 bool RunContext::aborting()
const {
5549 return m_totals.assertions.failed >=
static_cast<std::size_t
>(m_config->abortAfter());
5552 void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
5553 auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
5554 SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
5555 m_reporter->sectionStarting(testCaseSection);
5556 Counts prevAssertions = m_totals.assertions;
5557 double duration = 0;
5558 m_shouldReportUnexpected =
true;
5559 m_lastAssertionInfo = {
"TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
5563 if (m_reporter->getPreferences().shouldRedirectStdOut) {
5564#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
5565 RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
5568 invokeActiveTestCase();
5570 OutputRedirect
r(redirectedCout, redirectedCerr);
5572 invokeActiveTestCase();
5576 invokeActiveTestCase();
5578 duration = timer.getElapsedSeconds();
5579 } CATCH_CATCH_ANON (TestFailureException&) {
5581 } CATCH_CATCH_ANON (TestSkipException&) {
5586 if( m_shouldReportUnexpected ) {
5587 AssertionReaction dummyReaction;
5591 Counts assertions = m_totals.assertions - prevAssertions;
5592 bool missingAssertions = testForMissingAssertions(assertions);
5594 m_testCaseTracker->close();
5595 handleUnfinishedSections();
5597 m_messageScopes.clear();
5599 SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
5600 m_reporter->sectionEnded(testCaseSectionStats);
5603 void RunContext::invokeActiveTestCase() {
5607 FatalConditionHandlerGuard _(&m_fatalConditionhandler);
5613 m_activeTestCase->invoke();
5616 void RunContext::handleUnfinishedSections() {
5619 for (
auto it = m_unfinishedSections.rbegin(),
5620 itEnd = m_unfinishedSections.rend();
5623 sectionEnded(CATCH_MOVE(*it));
5624 m_unfinishedSections.clear();
5627 void RunContext::handleExpr(
5628 AssertionInfo
const& info,
5629 ITransientExpression
const& expr,
5630 AssertionReaction& reaction
5632 bool negated = isFalseTest( info.resultDisposition );
5633 bool result = expr.getResult() != negated;
5636 if (!m_includeSuccessfulResults) {
5640 reportExpr(info, ResultWas::Ok, &expr, negated);
5644 reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
5645 populateReaction( reaction );
5648 void RunContext::reportExpr(
5649 AssertionInfo
const &info,
5650 ResultWas::OfType resultType,
5651 ITransientExpression
const *expr,
5654 m_lastAssertionInfo = info;
5655 AssertionResultData
data( resultType, LazyExpression( negated ) );
5657 AssertionResult assertionResult{ info, CATCH_MOVE(
data ) };
5658 assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
5660 assertionEnded( CATCH_MOVE(assertionResult) );
5663 void RunContext::handleMessage(
5664 AssertionInfo
const& info,
5665 ResultWas::OfType resultType,
5667 AssertionReaction& reaction
5669 m_lastAssertionInfo = info;
5671 AssertionResultData
data( resultType, LazyExpression(
false ) );
5673 AssertionResult assertionResult{ m_lastAssertionInfo,
5674 CATCH_MOVE(
data ) };
5676 const auto isOk = assertionResult.isOk();
5677 assertionEnded( CATCH_MOVE(assertionResult) );
5679 populateReaction( reaction );
5680 }
else if ( resultType == ResultWas::ExplicitSkip ) {
5683 reaction.shouldSkip =
true;
5686 void RunContext::handleUnexpectedExceptionNotThrown(
5687 AssertionInfo
const& info,
5688 AssertionReaction& reaction
5690 handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
5693 void RunContext::handleUnexpectedInflightException(
5694 AssertionInfo
const& info,
5696 AssertionReaction& reaction
5698 m_lastAssertionInfo = info;
5700 AssertionResultData
data( ResultWas::ThrewException, LazyExpression(
false ) );
5702 AssertionResult assertionResult{ info, CATCH_MOVE(
data) };
5703 assertionEnded( CATCH_MOVE(assertionResult) );
5704 populateReaction( reaction );
5707 void RunContext::populateReaction( AssertionReaction& reaction ) {
5708 reaction.shouldDebugBreak = m_config->shouldDebugBreak();
5709 reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
5712 void RunContext::handleIncomplete(
5713 AssertionInfo
const& info
5715 using namespace std::string_literals;
5716 m_lastAssertionInfo = info;
5718 AssertionResultData
data( ResultWas::ThrewException, LazyExpression(
false ) );
5719 data.message =
"Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
5720 AssertionResult assertionResult{ info, CATCH_MOVE(
data ) };
5721 assertionEnded( CATCH_MOVE(assertionResult) );
5723 void RunContext::handleNonExpr(
5724 AssertionInfo
const &info,
5725 ResultWas::OfType resultType,
5726 AssertionReaction &reaction
5728 m_lastAssertionInfo = info;
5730 AssertionResultData
data( resultType, LazyExpression(
false ) );
5731 AssertionResult assertionResult{ info, CATCH_MOVE(
data ) };
5733 const auto isOk = assertionResult.isOk();
5734 assertionEnded( CATCH_MOVE(assertionResult) );
5735 if ( !isOk ) { populateReaction( reaction ); }
5743 CATCH_INTERNAL_ERROR(
"No result capture instance");
5747 sharedRng().seed(config.rngSeed());
5751 return getCurrentContext().getConfig()->rngSeed();
5760 Section::Section( SectionInfo&& info ):
5761 m_info( CATCH_MOVE( info ) ),
5766 if (m_sectionIncluded) {
5771 Section::Section( SourceLineInfo
const& _lineInfo,
5773 const char*
const ):
5774 m_info( {
"invalid",
static_cast<std::size_t
>( -1 ) }, std::string{} ),
5781 if ( m_sectionIncluded ) {
5782 m_info.name =
static_cast<std::string
>( _name );
5783 m_info.lineInfo = _lineInfo;
5788 Section::~Section() {
5789 if( m_sectionIncluded ) {
5790 SectionEndInfo endInfo{ CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds() };
5800 Section::operator
bool()
const {
5801 return m_sectionIncluded;
5814 static auto getSingletons() -> std::vector<ISingleton*>*& {
5815 static std::vector<ISingleton*>* g_singletons =
nullptr;
5817 g_singletons =
new std::vector<ISingleton*>();
5818 return g_singletons;
5822 ISingleton::~ISingleton() =
default;
5825 getSingletons()->push_back( singleton );
5828 auto& singletons = getSingletons();
5829 for(
auto singleton : *singletons )
5832 singletons =
nullptr;
5844 bool SourceLineInfo::operator == ( SourceLineInfo
const&
other )
const noexcept {
5847 bool SourceLineInfo::operator < ( SourceLineInfo
const&
other )
const noexcept {
5853 std::ostream&
operator << ( std::ostream& os, SourceLineInfo
const& info ) {
5855 os << info.file <<
'(' << info.line <<
')';
5857 os << info.file <<
':' << info.line;
5868#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
5869 void StartupExceptionRegistry::add( std::exception_ptr
const& exception )
noexcept {
5871 m_exceptions.push_back(exception);
5878 std::vector<std::exception_ptr>
const& StartupExceptionRegistry::getExceptions() const noexcept {
5879 return m_exceptions;
5894#if !defined( CATCH_CONFIG_NOSTDOUT )
5895 std::ostream&
cout() {
return std::cout; }
5896 std::ostream&
cerr() {
return std::cerr; }
5897 std::ostream&
clog() {
return std::clog; }
5911 bool startsWith( std::string
const& s, std::string
const& prefix ) {
5912 return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
5915 return !s.empty() && s[0] == prefix;
5917 bool endsWith( std::string
const& s, std::string
const& suffix ) {
5918 return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
5921 return !s.empty() && s[s.size()-1] == suffix;
5923 bool contains( std::string
const& s, std::string
const& infix ) {
5924 return s.find( infix ) != std::string::npos;
5927 for (
char&
c : s ) {
5937 return static_cast<char>(std::tolower(
static_cast<unsigned char>(
c)));
5941 static char const* whitespaceChars =
"\n\r\t ";
5942 std::string::size_type
start =
str.find_first_not_of( whitespaceChars );
5943 std::string::size_type
end =
str.find_last_not_of( whitespaceChars );
5949 const auto is_ws = [](
char c) {
5950 return c ==
' ' ||
c ==
'\t' ||
c ==
'\n' ||
c ==
'\r';
5952 size_t real_begin = 0;
5953 while (real_begin <
ref.size() && is_ws(
ref[real_begin])) { ++real_begin; }
5954 size_t real_end =
ref.size();
5955 while (real_end > real_begin && is_ws(
ref[real_end - 1])) { --real_end; }
5957 return ref.substr(real_begin, real_end - real_begin);
5960 bool replaceInPlace( std::string&
str, std::string
const& replaceThis, std::string
const& withThis ) {
5961 bool replaced =
false;
5962 std::size_t
i =
str.find( replaceThis );
5963 while(
i != std::string::npos ) {
5965 str =
str.substr( 0,
i ) + withThis +
str.substr(
i+replaceThis.size() );
5966 if(
i <
str.size()-withThis.size() )
5967 i =
str.find( replaceThis,
i+withThis.size() );
5969 i = std::string::npos;
5975 std::vector<StringRef> subStrings;
5976 std::size_t
start = 0;
5978 if(
str[
pos] == delimiter ) {
5989 std::ostream&
operator << ( std::ostream& os, pluralise
const& pluraliser ) {
5990 os << pluraliser.m_count <<
' ' << pluraliser.m_label;
5991 if( pluraliser.m_count != 1 )
6006 StringRef::StringRef(
char const* rawChars ) noexcept
6007 : StringRef( rawChars, std::strlen(rawChars) )
6011 bool StringRef::operator<(StringRef rhs)
const noexcept {
6012 if (m_size <
rhs.m_size) {
6013 return strncmp(m_start,
rhs.m_start, m_size) <= 0;
6015 return strncmp(m_start,
rhs.m_start,
rhs.m_size) < 0;
6018 int StringRef::compare( StringRef rhs )
const {
6020 strncmp( m_start,
rhs.m_start, std::min( m_size,
rhs.m_size ) );
6024 if ( cmpResult != 0 ) {
6030 if ( m_size <
rhs.m_size ) {
6032 }
else if ( m_size >
rhs.m_size ) {
6040 return os.write(
str.data(),
static_cast<std::streamsize
>(
str.size()));
6045 ret.reserve(lhs.size() + rhs.size());
6051 auto operator+=( std::string& lhs, StringRef rhs ) -> std::string& {
6052 lhs.append(rhs.data(), rhs.size());
6062 TagAliasRegistry::~TagAliasRegistry() {}
6064 TagAlias
const* TagAliasRegistry::find( std::string
const& alias )
const {
6065 auto it = m_registry.find( alias );
6066 if( it != m_registry.end() )
6067 return &(it->second);
6072 std::string TagAliasRegistry::expandAliases( std::string
const& unexpandedTestSpec )
const {
6073 std::string expandedTestSpec = unexpandedTestSpec;
6074 for(
auto const& registryKvp : m_registry ) {
6075 std::size_t
pos = expandedTestSpec.find( registryKvp.first );
6076 if(
pos != std::string::npos ) {
6077 expandedTestSpec = expandedTestSpec.substr( 0,
pos ) +
6078 registryKvp.second.tag +
6079 expandedTestSpec.substr(
pos + registryKvp.first.size() );
6082 return expandedTestSpec;
6085 void TagAliasRegistry::add( std::string
const& alias, std::string
const& tag, SourceLineInfo
const& lineInfo ) {
6087 "error: tag alias, '" << alias <<
"' is not of the form [@alias name].\n" << lineInfo );
6089 CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second,
6090 "error: tag alias, '" << alias <<
"' already registered.\n"
6091 <<
"\tFirst seen at: " << find(alias)->lineInfo <<
"\n"
6092 <<
"\tRedefined at: " << lineInfo );
6095 ITagAliasRegistry::~ITagAliasRegistry() =
default;
6097 ITagAliasRegistry
const& ITagAliasRegistry::get() {
6107 TestCaseInfoHasher::TestCaseInfoHasher( hash_t seed ): m_seed( seed ) {}
6109 uint32_t TestCaseInfoHasher::operator()( TestCaseInfo
const& t )
const {
6111 const hash_t prime = 1099511628211u;
6112 hash_t hash = 14695981039346656037u;
6113 for (
const char c : t.
name ) {
6117 for (
const char c : t.className ) {
6121 for (
const Tag& tag : t.tags ) {
6122 for (
const char c : tag.original ) {
6129 const uint32_t low{
static_cast<uint32_t
>( hash ) };
6130 const uint32_t high{
static_cast<uint32_t
>( hash >> 32 ) };
6144 static void enforceNoDuplicateTestCases(
6145 std::vector<TestCaseHandle>
const& tests ) {
6146 auto testInfoCmp = []( TestCaseInfo
const*
lhs,
6147 TestCaseInfo
const*
rhs ) {
6150 std::set<TestCaseInfo
const*,
decltype( testInfoCmp )&> seenTests(
6152 for (
auto const&
test : tests ) {
6153 const auto infoPtr = &
test.getTestCaseInfo();
6154 const auto prev = seenTests.insert( infoPtr );
6155 CATCH_ENFORCE( prev.second,
6156 "error: test case \""
6157 << infoPtr->name <<
"\", with tags \""
6158 << infoPtr->tagsAsString()
6159 <<
"\" already defined.\n"
6160 <<
"\tFirst seen at "
6161 << ( *prev.first )->lineInfo <<
"\n"
6162 <<
"\tRedefined at " << infoPtr->lineInfo );
6166 static bool matchTest( TestCaseHandle
const& testCase,
6167 TestSpec
const& testSpec,
6168 IConfig
const& config ) {
6169 return testSpec.matches( testCase.getTestCaseInfo() ) &&
6175 std::vector<TestCaseHandle>
sortTests( IConfig
const& config, std::vector<TestCaseHandle>
const& unsortedTestCases ) {
6176 switch (config.runOrder()) {
6177 case TestRunOrder::Declared:
6178 return unsortedTestCases;
6180 case TestRunOrder::LexicographicallySorted: {
6181 std::vector<TestCaseHandle> sorted = unsortedTestCases;
6185 []( TestCaseHandle
const& lhs, TestCaseHandle
const& rhs ) {
6186 return lhs.getTestCaseInfo() < rhs.getTestCaseInfo();
6191 case TestRunOrder::Randomized: {
6193 using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>;
6195 TestCaseInfoHasher
h{ config.rngSeed() };
6196 std::vector<TestWithHash> indexed_tests;
6197 indexed_tests.reserve(unsortedTestCases.size());
6199 for (
auto const& handle : unsortedTestCases) {
6200 indexed_tests.emplace_back(
h(handle.getTestCaseInfo()), handle);
6203 std::sort( indexed_tests.begin(),
6204 indexed_tests.end(),
6205 []( TestWithHash
const& lhs, TestWithHash
const& rhs ) {
6206 if ( lhs.first == rhs.first ) {
6207 return lhs.second.getTestCaseInfo() <
6208 rhs.second.getTestCaseInfo();
6210 return lhs.first < rhs.first;
6213 std::vector<TestCaseHandle> randomized;
6214 randomized.reserve(indexed_tests.size());
6216 for (
auto const& indexed : indexed_tests) {
6217 randomized.push_back(indexed.second);
6224 CATCH_INTERNAL_ERROR(
"Unknown test order value!");
6227 bool isThrowSafe( TestCaseHandle
const& testCase, IConfig
const& config ) {
6228 return !testCase.getTestCaseInfo().throws() || config.allowThrows();
6231 std::vector<TestCaseHandle>
filterTests( std::vector<TestCaseHandle>
const& testCases, TestSpec
const& testSpec, IConfig
const& config ) {
6232 std::vector<TestCaseHandle> filtered;
6233 filtered.reserve( testCases.size() );
6234 for (
auto const& testCase : testCases) {
6235 if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) ||
6236 (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
6237 filtered.push_back(testCase);
6240 return createShard(filtered, config.shardCount(), config.shardIndex());
6243 return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
6246 void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
6247 m_handles.emplace_back(testInfo.get(), testInvoker.get());
6248 m_viewed_test_infos.push_back(testInfo.get());
6249 m_owned_test_infos.push_back(CATCH_MOVE(testInfo));
6250 m_invokers.push_back(CATCH_MOVE(testInvoker));
6253 std::vector<TestCaseInfo*>
const& TestRegistry::getAllInfos()
const {
6254 return m_viewed_test_infos;
6257 std::vector<TestCaseHandle>
const& TestRegistry::getAllTests()
const {
6260 std::vector<TestCaseHandle>
const& TestRegistry::getAllTestsSorted( IConfig
const& config )
const {
6261 if( m_sortedFunctions.empty() )
6262 enforceNoDuplicateTestCases( m_handles );
6264 if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
6265 m_sortedFunctions =
sortTests( config, m_handles );
6266 m_currentSortOrder = config.runOrder();
6268 return m_sortedFunctions;
6279#if defined(__clang__)
6280# pragma clang diagnostic push
6281# pragma clang diagnostic ignored "-Wexit-time-destructors"
6285namespace TestCaseTracking {
6287 NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo
const& _location )
6288 :
name( CATCH_MOVE(_name) ),
6293 ITracker::~ITracker() =
default;
6295 void ITracker::markAsNeedingAnotherRun() {
6296 m_runState = NeedsAnotherRun;
6299 void ITracker::addChild( ITrackerPtr&& child ) {
6300 m_children.push_back( CATCH_MOVE(child) );
6303 ITracker* ITracker::findChild( NameAndLocationRef
const& nameAndLocation ) {
6304 auto it = std::find_if(
6307 [&nameAndLocation]( ITrackerPtr
const& tracker ) {
6308 auto const& tnameAndLoc = tracker->nameAndLocation();
6309 if ( tnameAndLoc.location.line !=
6310 nameAndLocation.location.line ) {
6313 return tnameAndLoc == nameAndLocation;
6315 return ( it != m_children.end() ) ? it->get() :
nullptr;
6318 bool ITracker::isSectionTracker()
const {
return false; }
6319 bool ITracker::isGeneratorTracker()
const {
return false; }
6321 bool ITracker::isOpen()
const {
6322 return m_runState != NotStarted && !isComplete();
6325 bool ITracker::hasStarted()
const {
return m_runState != NotStarted; }
6327 void ITracker::openChild() {
6328 if (m_runState != ExecutingChildren) {
6329 m_runState = ExecutingChildren;
6331 m_parent->openChild();
6336 ITracker& TrackerContext::startRun() {
6337 using namespace std::string_literals;
6338 m_rootTracker = Catch::Detail::make_unique<SectionTracker>(
6339 NameAndLocation(
"{root}"s, CATCH_INTERNAL_LINEINFO ),
6342 m_currentTracker =
nullptr;
6343 m_runState = Executing;
6344 return *m_rootTracker;
6347 void TrackerContext::completeCycle() {
6348 m_runState = CompletedCycle;
6351 bool TrackerContext::completedCycle()
const {
6352 return m_runState == CompletedCycle;
6354 void TrackerContext::setCurrentTracker( ITracker* tracker ) {
6355 m_currentTracker = tracker;
6359 TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
6360 ITracker(CATCH_MOVE(nameAndLocation), parent),
6364 bool TrackerBase::isComplete()
const {
6365 return m_runState == CompletedSuccessfully || m_runState == Failed;
6368 void TrackerBase::open() {
6369 m_runState = Executing;
6372 m_parent->openChild();
6375 void TrackerBase::close() {
6378 while( &m_ctx.currentTracker() !=
this )
6379 m_ctx.currentTracker().close();
6381 switch( m_runState ) {
6382 case NeedsAnotherRun:
6386 m_runState = CompletedSuccessfully;
6388 case ExecutingChildren:
6389 if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr
const& t){ return t->isComplete(); }) )
6390 m_runState = CompletedSuccessfully;
6394 case CompletedSuccessfully:
6396 CATCH_INTERNAL_ERROR(
"Illogical state: " << m_runState );
6399 CATCH_INTERNAL_ERROR(
"Unknown state: " << m_runState );
6402 m_ctx.completeCycle();
6404 void TrackerBase::fail() {
6405 m_runState = Failed;
6407 m_parent->markAsNeedingAnotherRun();
6409 m_ctx.completeCycle();
6412 void TrackerBase::moveToParent() {
6414 m_ctx.setCurrentTracker( m_parent );
6416 void TrackerBase::moveToThis() {
6417 m_ctx.setCurrentTracker(
this );
6420 SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent )
6421 : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ),
6422 m_trimmed_name(
trim(StringRef(ITracker::nameAndLocation().
name)))
6425 while ( !parent->isSectionTracker() ) {
6426 parent = parent->parent();
6429 SectionTracker& parentSection =
static_cast<SectionTracker&
>( *parent );
6430 addNextFilters( parentSection.m_filters );
6434 bool SectionTracker::isComplete()
const {
6435 bool complete =
true;
6437 if (m_filters.empty()
6438 || m_filters[0].empty()
6439 || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
6440 complete = TrackerBase::isComplete();
6445 bool SectionTracker::isSectionTracker()
const {
return true; }
6447 SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef
const& nameAndLocation ) {
6448 SectionTracker* tracker;
6450 ITracker& currentTracker = ctx.currentTracker();
6451 if ( ITracker* childTracker =
6452 currentTracker.findChild( nameAndLocation ) ) {
6453 assert( childTracker );
6454 assert( childTracker->isSectionTracker() );
6455 tracker =
static_cast<SectionTracker*
>( childTracker );
6457 auto newTracker = Catch::Detail::make_unique<SectionTracker>(
6458 NameAndLocation{
static_cast<std::string
>(nameAndLocation.name),
6459 nameAndLocation.location },
6462 tracker = newTracker.get();
6463 currentTracker.addChild( CATCH_MOVE( newTracker ) );
6466 if ( !ctx.completedCycle() ) {
6473 void SectionTracker::tryOpen() {
6478 void SectionTracker::addInitialFilters( std::vector<std::string>
const& filters ) {
6479 if( !filters.empty() ) {
6480 m_filters.reserve( m_filters.size() + filters.size() + 2 );
6481 m_filters.emplace_back(StringRef{});
6482 m_filters.emplace_back(StringRef{});
6483 m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
6486 void SectionTracker::addNextFilters( std::vector<StringRef>
const& filters ) {
6487 if( filters.size() > 1 )
6488 m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
6491 StringRef SectionTracker::trimmedName()
const {
6492 return m_trimmed_name;
6499#if defined(__clang__)
6500# pragma clang diagnostic pop
6509#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
6510 throw TestFailureException{};
6512 CATCH_ERROR(
"Test failure requires aborting test!" );
6517#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
6518 throw Catch::TestSkipException();
6520 CATCH_ERROR(
"Explicitly skipping tests during runtime requires exceptions" );
6532 ITestInvoker::~ITestInvoker() =
default;
6535 static StringRef extractClassName( StringRef classOrMethodName ) {
6536 if ( !
startsWith( classOrMethodName,
'&' ) ) {
6537 return classOrMethodName;
6541 const auto methodName =
6542 classOrMethodName.substr( 1, classOrMethodName.size() );
6544 auto reverseStart = std::make_reverse_iterator( methodName.end() );
6545 auto reverseEnd = std::make_reverse_iterator( methodName.begin() );
6551 auto lastColons = std::find( reverseStart, reverseEnd,
':' ) + 1;
6552 auto secondLastColons =
6553 std::find( lastColons + 1, reverseEnd,
':' );
6555 auto const startIdx = reverseEnd - secondLastColons;
6556 auto const classNameSize = secondLastColons - lastColons - 1;
6558 return methodName.substr(
6559 static_cast<std::size_t
>( startIdx ),
6560 static_cast<std::size_t
>( classNameSize ) );
6563 class TestInvokerAsFunction final :
public ITestInvoker {
6564 using TestType =
void ( * )();
6565 TestType m_testAsFunction;
6568 TestInvokerAsFunction( TestType testAsFunction )
noexcept:
6569 m_testAsFunction( testAsFunction ) {}
6571 void invoke()
const override { m_testAsFunction(); }
6577 return Detail::make_unique<TestInvokerAsFunction>( testAsFunction );
6580 AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo
const& lineInfo, StringRef classOrMethod, NameAndTags
const& nameAndTags )
noexcept {
6585 extractClassName( classOrMethod ),
6603 TestSpecParser::TestSpecParser( ITagAliasRegistry
const& tagAliases ) : m_tagAliases( &tagAliases ) {}
6605 TestSpecParser& TestSpecParser::parse( std::string
const& arg ) {
6607 m_exclusion =
false;
6608 m_arg = m_tagAliases->expandAliases( arg );
6609 m_escapeChars.clear();
6610 m_substring.reserve(m_arg.size());
6611 m_patternName.reserve(m_arg.size());
6612 m_realPatternPos = 0;
6614 for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
6616 if( !visitChar( m_arg[m_pos] ) ){
6617 m_testSpec.m_invalidSpecs.push_back(arg);
6623 TestSpec TestSpecParser::testSpec() {
6625 return CATCH_MOVE(m_testSpec);
6627 bool TestSpecParser::visitChar(
char c ) {
6628 if( (m_mode != EscapedName) && (
c ==
'\\') ) {
6630 addCharToPattern(
c);
6632 }
else if((m_mode != EscapedName) && (
c ==
',') ) {
6638 if( processNoneChar(
c ) )
6642 processNameChar(
c );
6646 addCharToPattern(
c);
6651 if( processOtherChar(
c ) )
6657 if( !isControlChar(
c ) ) {
6665 bool TestSpecParser::processNoneChar(
char c ) {
6673 startNewMode( Tag );
6676 startNewMode( QuotedName );
6679 startNewMode( Name );
6683 void TestSpecParser::processNameChar(
char c ) {
6685 if( m_substring ==
"exclude:" )
6689 startNewMode( Tag );
6692 bool TestSpecParser::processOtherChar(
char c ) {
6693 if( !isControlChar(
c ) )
6699 void TestSpecParser::startNewMode(
Mode mode ) {
6702 void TestSpecParser::endMode() {
6706 return addNamePattern();
6708 return addTagPattern();
6710 revertBackToLastMode();
6714 return startNewMode(
None );
6717 void TestSpecParser::escape() {
6719 m_mode = EscapedName;
6720 m_escapeChars.push_back(m_realPatternPos);
6722 bool TestSpecParser::isControlChar(
char c )
const {
6735 return c ==
'[' ||
c ==
']';
6739 void TestSpecParser::addFilter() {
6740 if( !m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty() ) {
6741 m_testSpec.m_filters.push_back( CATCH_MOVE(m_currentFilter) );
6742 m_currentFilter = TestSpec::Filter();
6746 void TestSpecParser::saveLastMode() {
6750 void TestSpecParser::revertBackToLastMode() {
6754 bool TestSpecParser::separate() {
6755 if( (m_mode==QuotedName) || (m_mode==Tag) ){
6758 m_pos = m_arg.size();
6759 m_substring.clear();
6760 m_patternName.clear();
6761 m_realPatternPos = 0;
6769 std::string TestSpecParser::preprocessPattern() {
6770 std::string token = m_patternName;
6771 for (std::size_t
i = 0;
i < m_escapeChars.size(); ++
i)
6772 token = token.substr(0, m_escapeChars[
i] -
i) + token.substr(m_escapeChars[
i] -
i + 1);
6773 m_escapeChars.clear();
6776 token = token.substr(8);
6779 m_patternName.clear();
6780 m_realPatternPos = 0;
6785 void TestSpecParser::addNamePattern() {
6786 auto token = preprocessPattern();
6788 if (!token.empty()) {
6790 m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::NamePattern>(token, m_substring));
6792 m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::NamePattern>(token, m_substring));
6795 m_substring.clear();
6796 m_exclusion =
false;
6800 void TestSpecParser::addTagPattern() {
6801 auto token = preprocessPattern();
6803 if (!token.empty()) {
6806 if (token.size() > 1 && token[0] ==
'.') {
6807 token.erase(token.begin());
6809 m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(
".", m_substring));
6811 m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(
".", m_substring));
6815 m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
6817 m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring));
6820 m_substring.clear();
6821 m_exclusion =
false;
6834 bool isWhitespace(
char c ) {
6835 return c ==
' ' ||
c ==
'\t' ||
c ==
'\n' ||
c ==
'\r';
6838 bool isBreakableBefore(
char c ) {
6839 static const char chars[] =
"[({<|";
6840 return std::memchr( chars,
c,
sizeof( chars ) - 1 ) !=
nullptr;
6843 bool isBreakableAfter(
char c ) {
6844 static const char chars[] =
"])}>.,:;*+-=&/\\";
6845 return std::memchr( chars,
c,
sizeof( chars ) - 1 ) !=
nullptr;
6848 bool isBoundary( std::string
const& line,
size_t at ) {
6850 assert( at <= line.size() );
6852 return at == line.size() ||
6853 ( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) ||
6854 isBreakableBefore( line[at] ) ||
6855 isBreakableAfter( line[at - 1] );
6861 namespace TextFlow {
6863 void Column::const_iterator::calcLength() {
6864 m_addHyphen =
false;
6865 m_parsedTo = m_lineStart;
6867 std::string
const& current_line = m_column.m_string;
6868 if ( current_line[m_lineStart] ==
'\n' ) {
6872 const auto maxLineLength = m_column.m_width - indentSize();
6873 const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength);
6874 while ( m_parsedTo < maxParseTo &&
6875 current_line[m_parsedTo] !=
'\n' ) {
6882 if ( m_parsedTo < m_lineStart + maxLineLength ) {
6883 m_lineLength = m_parsedTo - m_lineStart;
6888 size_t newLineLength = maxLineLength;
6889 while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) {
6892 while ( newLineLength > 0 &&
6893 isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) {
6898 if ( newLineLength > 0 ) {
6899 m_lineLength = newLineLength;
6903 m_lineLength = maxLineLength - 1;
6908 size_t Column::const_iterator::indentSize()
const {
6910 m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos;
6911 return initial == std::string::npos ? m_column.m_indent : initial;
6915 Column::const_iterator::addIndentAndSuffix(
size_t position,
6918 const auto desired_indent = indentSize();
6919 ret.reserve( desired_indent +
length + m_addHyphen );
6920 ret.append( desired_indent,
' ' );
6921 ret.append( m_column.m_string, position,
length );
6922 if ( m_addHyphen ) {
6923 ret.push_back(
'-' );
6929 Column::const_iterator::const_iterator( Column
const& column ): m_column( column ) {
6930 assert( m_column.m_width > m_column.m_indent );
6931 assert( m_column.m_initialIndent == std::string::npos ||
6932 m_column.m_width > m_column.m_initialIndent );
6934 if ( m_lineLength == 0 ) {
6935 m_lineStart = m_column.m_string.size();
6939 std::string Column::const_iterator::operator*()
const {
6940 assert( m_lineStart <= m_parsedTo );
6941 return addIndentAndSuffix( m_lineStart, m_lineLength );
6944 Column::const_iterator& Column::const_iterator::operator++() {
6945 m_lineStart += m_lineLength;
6946 std::string
const& current_line = m_column.m_string;
6947 if ( m_lineStart < current_line.size() && current_line[m_lineStart] ==
'\n' ) {
6950 while ( m_lineStart < current_line.size() &&
6951 isWhitespace( current_line[m_lineStart] ) ) {
6956 if ( m_lineStart != current_line.size() ) {
6962 Column::const_iterator Column::const_iterator::operator++(
int ) {
6963 const_iterator prev( *
this );
6970 for (
auto line :
col ) {
6983 ret.width( spaceWidth );
6987 Columns::iterator::iterator( Columns
const& columns, EndTag ):
6988 m_columns( columns.m_columns ), m_activeIterators( 0 ) {
6990 m_iterators.reserve( m_columns.size() );
6991 for (
auto const&
col : m_columns ) {
6992 m_iterators.push_back(
col.end() );
6996 Columns::iterator::iterator( Columns
const& columns ):
6997 m_columns( columns.m_columns ),
6998 m_activeIterators( m_columns.
size() ) {
7000 m_iterators.reserve( m_columns.size() );
7001 for (
auto const&
col : m_columns ) {
7002 m_iterators.push_back(
col.begin() );
7006 std::string Columns::iterator::operator*()
const {
7009 for (
size_t i = 0;
i < m_columns.size(); ++
i ) {
7010 const auto width = m_columns[
i].width();
7011 if ( m_iterators[
i] != m_columns[
i].
end() ) {
7012 std::string
col = *m_iterators[
i];
7027 Columns::iterator& Columns::iterator::operator++() {
7028 for (
size_t i = 0;
i < m_columns.size(); ++
i ) {
7029 if ( m_iterators[
i] != m_columns[
i].
end() ) {
7036 Columns::iterator Columns::iterator::operator++(
int ) {
7037 iterator prev( *
this );
7044 for (
auto line :
cols ) {
7055 Columns Column::operator+( Column
const&
other ) {
7062 Columns& Columns::operator+=( Column
const&
col ) {
7063 m_columns.push_back(
col );
7067 Columns Columns::operator+( Column
const&
col ) {
7068 Columns combined = *
this;
7083#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
7085#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
7086 return std::uncaught_exceptions() > 0;
7088 return std::uncaught_exception();
7097 WildcardPattern::WildcardPattern( std::string
const&
pattern,
7098 CaseSensitive caseSensitivity )
7099 : m_caseSensitivity( caseSensitivity ),
7100 m_pattern( normaliseString(
pattern ) )
7103 m_pattern = m_pattern.substr( 1 );
7104 m_wildcard = WildcardAtStart;
7107 m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
7108 m_wildcard =
static_cast<WildcardPosition
>( m_wildcard | WildcardAtEnd );
7112 bool WildcardPattern::matches( std::string
const&
str )
const {
7113 switch( m_wildcard ) {
7115 return m_pattern == normaliseString(
str );
7116 case WildcardAtStart:
7117 return endsWith( normaliseString(
str ), m_pattern );
7120 case WildcardAtBothEnds:
7121 return contains( normaliseString(
str ), m_pattern );
7123 CATCH_INTERNAL_ERROR(
"Unknown enum" );
7127 std::string WildcardPattern::normaliseString( std::string
const&
str )
const {
7139#include <type_traits>
7145 size_t trailingBytes(
unsigned char c) {
7146 if ((
c & 0xE0) == 0xC0) {
7149 if ((
c & 0xF0) == 0xE0) {
7152 if ((
c & 0xF8) == 0xF0) {
7155 CATCH_INTERNAL_ERROR(
"Invalid multibyte utf-8 start byte encountered");
7158 uint32_t headerValue(
unsigned char c) {
7159 if ((
c & 0xE0) == 0xC0) {
7162 if ((
c & 0xF0) == 0xE0) {
7165 if ((
c & 0xF8) == 0xF0) {
7168 CATCH_INTERNAL_ERROR(
"Invalid multibyte utf-8 start byte encountered");
7171 void hexEscapeChar(std::ostream& os,
unsigned char c) {
7172 std::ios_base::fmtflags
f(os.flags());
7174 << std::uppercase << std::hex << std::setfill(
'0') << std::setw(2)
7175 <<
static_cast<int>(
c);
7179 bool shouldNewline(XmlFormatting
fmt) {
7180 return !!(
static_cast<std::underlying_type_t<XmlFormatting>
>(
fmt & XmlFormatting::Newline));
7183 bool shouldIndent(XmlFormatting
fmt) {
7184 return !!(
static_cast<std::underlying_type_t<XmlFormatting>
>(
fmt & XmlFormatting::Indent));
7189 XmlFormatting
operator | (XmlFormatting lhs, XmlFormatting rhs) {
7190 return static_cast<XmlFormatting
>(
7191 static_cast<std::underlying_type_t<XmlFormatting>
>(lhs) |
7192 static_cast<std::underlying_type_t<XmlFormatting>
>(rhs)
7196 XmlFormatting
operator & (XmlFormatting lhs, XmlFormatting rhs) {
7197 return static_cast<XmlFormatting
>(
7198 static_cast<std::underlying_type_t<XmlFormatting>
>(lhs) &
7199 static_cast<std::underlying_type_t<XmlFormatting>
>(rhs)
7204 XmlEncode::XmlEncode( StringRef
str, ForWhat forWhat )
7206 m_forWhat( forWhat )
7209 void XmlEncode::encodeTo( std::ostream& os )
const {
7213 for( std::size_t idx = 0;
idx < m_str.size(); ++
idx ) {
7214 unsigned char c =
static_cast<unsigned char>(m_str[
idx]);
7216 case '<': os <<
"<";
break;
7217 case '&': os <<
"&";
break;
7221 if (idx > 2 && m_str[idx - 1] ==
']' && m_str[idx - 2] ==
']')
7228 if (m_forWhat == ForAttributes)
7239 if (
c < 0x09 || (
c > 0x0D &&
c < 0x20) ||
c == 0x7F) {
7240 hexEscapeChar(os,
c);
7258 hexEscapeChar(os,
c);
7262 auto encBytes = trailingBytes(
c);
7264 if (idx + encBytes - 1 >= m_str.size()) {
7265 hexEscapeChar(os,
c);
7272 uint32_t
value = headerValue(
c);
7273 for (std::size_t
n = 1;
n < encBytes; ++
n) {
7274 unsigned char nc =
static_cast<unsigned char>(m_str[
idx +
n]);
7275 valid &= ((nc & 0xC0) == 0x80);
7284 (0x80 <=
value && value < 0x800 && encBytes > 2) ||
7285 (0x800 <
value && value < 0x10000 && encBytes > 3) ||
7289 hexEscapeChar(os,
c);
7294 for (std::size_t
n = 0;
n < encBytes; ++
n) {
7295 os << m_str[
idx +
n];
7297 idx += encBytes - 1;
7303 std::ostream&
operator << ( std::ostream& os, XmlEncode
const& xmlEncode ) {
7304 xmlEncode.encodeTo( os );
7308 XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting
fmt )
7309 : m_writer( writer ),
7313 XmlWriter::ScopedElement::ScopedElement( ScopedElement&&
other ) noexcept
7314 : m_writer(
other.m_writer ),
7317 other.m_writer =
nullptr;
7318 other.m_fmt = XmlFormatting::None;
7320 XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&&
other )
noexcept {
7322 m_writer->endElement();
7324 m_writer =
other.m_writer;
7325 other.m_writer =
nullptr;
7326 m_fmt =
other.m_fmt;
7327 other.m_fmt = XmlFormatting::None;
7332 XmlWriter::ScopedElement::~ScopedElement() {
7334 m_writer->endElement(m_fmt);
7338 XmlWriter::ScopedElement&
7339 XmlWriter::ScopedElement::writeText( StringRef text, XmlFormatting
fmt ) {
7340 m_writer->writeText( text,
fmt );
7344 XmlWriter::ScopedElement&
7345 XmlWriter::ScopedElement::writeAttribute( StringRef
name,
7346 StringRef attribute ) {
7347 m_writer->writeAttribute(
name, attribute );
7352 XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
7357 XmlWriter::~XmlWriter() {
7358 while (!m_tags.empty()) {
7361 newlineIfNecessary();
7364 XmlWriter& XmlWriter::startElement( std::string
const&
name, XmlFormatting
fmt ) {
7366 newlineIfNecessary();
7367 if (shouldIndent(
fmt)) {
7371 m_os <<
'<' <<
name;
7372 m_tags.push_back(
name );
7374 applyFormatting(
fmt);
7378 XmlWriter::ScopedElement XmlWriter::scopedElement( std::string
const&
name, XmlFormatting
fmt ) {
7379 ScopedElement scoped(
this,
fmt );
7384 XmlWriter& XmlWriter::endElement(XmlFormatting
fmt) {
7385 m_indent = m_indent.substr(0, m_indent.size() - 2);
7389 m_tagIsOpen =
false;
7391 newlineIfNecessary();
7392 if (shouldIndent(
fmt)) {
7395 m_os <<
"</" << m_tags.back() <<
'>';
7398 applyFormatting(
fmt);
7403 XmlWriter& XmlWriter::writeAttribute( StringRef
name,
7404 StringRef attribute ) {
7405 if( !
name.empty() && !attribute.empty() )
7406 m_os <<
' ' <<
name <<
"=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) <<
'"';
7410 XmlWriter& XmlWriter::writeAttribute( StringRef
name,
bool attribute ) {
7411 writeAttribute(
name, (attribute ?
"true"_sr :
"false"_sr));
7415 XmlWriter& XmlWriter::writeAttribute( StringRef
name,
7416 char const* attribute ) {
7417 writeAttribute(
name, StringRef( attribute ) );
7421 XmlWriter& XmlWriter::writeText( StringRef text, XmlFormatting
fmt ) {
7422 CATCH_ENFORCE(!m_tags.empty(),
"Cannot write text as top level element");
7423 if( !text.empty() ){
7424 bool tagWasOpen = m_tagIsOpen;
7426 if (tagWasOpen && shouldIndent(
fmt)) {
7429 m_os << XmlEncode( text, XmlEncode::ForTextNodes );
7430 applyFormatting(
fmt);
7435 XmlWriter& XmlWriter::writeComment( StringRef text, XmlFormatting
fmt ) {
7437 if (shouldIndent(
fmt)) {
7440 m_os <<
"<!-- " << text <<
" -->";
7441 applyFormatting(
fmt);
7445 void XmlWriter::writeStylesheetRef( StringRef url ) {
7446 m_os << R
"(<?xml-stylesheet type="text/xsl" href=")" << url << R"("?>)" << '\n';
7449 void XmlWriter::ensureTagClosed() {
7451 m_os <<
'>' << std::flush;
7452 newlineIfNecessary();
7453 m_tagIsOpen =
false;
7457 void XmlWriter::applyFormatting(XmlFormatting
fmt) {
7458 m_needsNewline = shouldNewline(
fmt);
7461 void XmlWriter::writeDeclaration() {
7462 m_os << R
"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
7465 void XmlWriter::newlineIfNecessary() {
7466 if( m_needsNewline ) {
7467 m_os <<
'\n' << std::flush;
7468 m_needsNewline =
false;
7480 std::string MatcherUntypedBase::toString()
const {
7481 if (m_cachedToString.empty()) {
7482 m_cachedToString = describe();
7484 return m_cachedToString;
7487 MatcherUntypedBase::~MatcherUntypedBase() =
default;
7498 std::string IsEmptyMatcher::describe()
const {
7502 std::string HasSizeMatcher::describe()
const {
7503 ReusableStringStream sstr;
7504 sstr <<
"has size == " << m_target_size;
7513 return HasSizeMatcher{ sz };
7524bool ExceptionMessageMatcher::match(std::exception
const& ex)
const {
7525 return ex.what() == m_message;
7528std::string ExceptionMessageMatcher::describe()
const {
7529 return "exception message matches \"" + m_message +
'"';
7533 return ExceptionMessageMatcher(
message);
7553 template <
typename FP>
7554 bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
7562 const auto ulpDist = ulpDistance(lhs, rhs);
7564 return ulpDist <= maxUlpDiff;
7567#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
7569 float nextafter(
float x,
float y) {
7570 return ::nextafterf(
x,
y);
7573 double nextafter(
double x,
double y) {
7574 return ::nextafter(
x,
y);
7579template <
typename FP>
7580FP
step(FP
start, FP direction, uint64_t steps) {
7581 for (uint64_t
i = 0;
i < steps; ++
i) {
7582#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
7593bool marginComparison(
double lhs,
double rhs,
double margin) {
7594 return (lhs + margin >= rhs) && (
rhs + margin >=
lhs);
7597template <
typename FloatingPo
int>
7598void write(std::ostream& out, FloatingPoint
num) {
7599 out << std::scientific
7600 << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
7617 WithinAbsMatcher::WithinAbsMatcher(
double target,
double margin)
7618 :m_target{
target }, m_margin{ margin } {
7619 CATCH_ENFORCE(margin >= 0,
"Invalid margin: " << margin <<
'.'
7620 <<
" Margin has to be non-negative.");
7625 bool WithinAbsMatcher::match(
double const& matchee)
const {
7626 return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
7629 std::string WithinAbsMatcher::describe()
const {
7630 return "is within " + ::Catch::Detail::stringify(m_margin) +
" of " + ::Catch::Detail::stringify(m_target);
7635 :m_target{
target }, m_ulps{ ulps }, m_type{ baseType } {
7637 || m_ulps < (std::numeric_limits<uint32_t>::max)(),
7638 "Provided ULP is impossibly large for a float comparison.");
7639 CATCH_ENFORCE( std::numeric_limits<double>::is_iec559,
7640 "WithinUlp matcher only supports platforms with "
7641 "IEEE-754 compatible floating point representation" );
7644#if defined(__clang__)
7645#pragma clang diagnostic push
7647#pragma clang diagnostic ignored "-Wunreachable-code"
7650 bool WithinUlpsMatcher::match(
double const& matchee)
const {
7653 return almostEqualUlps<float>(
static_cast<float>(matchee),
static_cast<float>(m_target), m_ulps);
7655 return almostEqualUlps<double>(matchee, m_target, m_ulps);
7657 CATCH_INTERNAL_ERROR(
"Unknown Detail::FloatingPointKind value" );
7661#if defined(__clang__)
7662#pragma clang diagnostic pop
7665 std::string WithinUlpsMatcher::describe()
const {
7666 std::stringstream ret;
7668 ret <<
"is within " << m_ulps <<
" ULPs of ";
7671 write(ret,
static_cast<float>(m_target));
7674 write(ret, m_target);
7681 -std::numeric_limits<double>::infinity(),
7686 std::numeric_limits<double>::infinity(),
7691 step(
static_cast<float>( m_target ),
7692 -std::numeric_limits<float>::infinity(),
7696 step(
static_cast<float>( m_target ),
7697 std::numeric_limits<float>::infinity(),
7705 WithinRelMatcher::WithinRelMatcher(
double target,
double epsilon):
7708 CATCH_ENFORCE(m_epsilon >= 0.,
"Relative comparison with epsilon < 0 does not make sense.");
7709 CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense.
");
7712 bool WithinRelMatcher::match(double const& matchee) const {
7713 const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
7714 return marginComparison(matchee, m_target,
7715 std::isinf(relMargin)? 0 : relMargin);
7718 std::string WithinRelMatcher::describe() const {
7719 Catch::ReusableStringStream sstr;
7720 sstr << "and
" << m_target << " are within
" << m_epsilon * 100. << "%
of each
other";
7725WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
7726 return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Double);
7729WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
7730 return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Float);
7733WithinAbsMatcher WithinAbs(double target, double margin) {
7734 return WithinAbsMatcher(target, margin);
7737WithinRelMatcher WithinRel(double target, double eps) {
7738 return WithinRelMatcher(target, eps);
7741WithinRelMatcher WithinRel(double target) {
7742 return WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
7745WithinRelMatcher WithinRel(float target, float eps) {
7746 return WithinRelMatcher(target, eps);
7749WithinRelMatcher WithinRel(float target) {
7750 return WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
7755bool IsNaNMatcher::match( double const& matchee ) const {
7756 return std::isnan( matchee );
7759std::string IsNaNMatcher::describe() const {
7760 using namespace std::string_literals;
7764IsNaNMatcher IsNaN() { return IsNaNMatcher(); }
7766 } // namespace Matchers
7772std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) {
7774 return "matches undescribed predicate
";
7776 return "matches predicate: \
"" + desc +
'"';
7783 namespace Matchers {
7784 std::string AllTrueMatcher::describe()
const {
return "contains only true"; }
7786 AllTrueMatcher
AllTrue() {
return AllTrueMatcher{}; }
7788 std::string NoneTrueMatcher::describe()
const {
return "contains no true"; }
7790 NoneTrueMatcher
NoneTrue() {
return NoneTrueMatcher{}; }
7792 std::string AnyTrueMatcher::describe()
const {
return "contains at least one true"; }
7794 AnyTrueMatcher
AnyTrue() {
return AnyTrueMatcher{}; }
7805 CasedString::CasedString( std::string
const&
str, CaseSensitive caseSensitivity )
7806 : m_caseSensitivity( caseSensitivity ),
7807 m_str( adjustString(
str ) )
7809 std::string CasedString::adjustString( std::string
const&
str )
const {
7810 return m_caseSensitivity == CaseSensitive::No
7814 StringRef CasedString::caseSensitivitySuffix()
const {
7815 return m_caseSensitivity == CaseSensitive::Yes
7817 :
" (case insensitive)"_sr;
7821 StringMatcherBase::StringMatcherBase( StringRef operation, CasedString
const& comparator )
7822 : m_comparator( comparator ),
7823 m_operation( operation ) {
7826 std::string StringMatcherBase::describe()
const {
7827 std::string description;
7828 description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
7829 m_comparator.caseSensitivitySuffix().size());
7830 description += m_operation;
7831 description +=
": \"";
7832 description += m_comparator.m_str;
7834 description += m_comparator.caseSensitivitySuffix();
7838 StringEqualsMatcher::StringEqualsMatcher( CasedString
const& comparator ) : StringMatcherBase(
"equals"_sr, comparator ) {}
7840 bool StringEqualsMatcher::match( std::string
const&
source )
const {
7841 return m_comparator.adjustString(
source ) == m_comparator.m_str;
7845 StringContainsMatcher::StringContainsMatcher( CasedString
const& comparator ) : StringMatcherBase(
"contains"_sr, comparator ) {}
7847 bool StringContainsMatcher::match( std::string
const&
source )
const {
7848 return contains( m_comparator.adjustString(
source ), m_comparator.m_str );
7852 StartsWithMatcher::StartsWithMatcher( CasedString
const& comparator ) : StringMatcherBase(
"starts with"_sr, comparator ) {}
7854 bool StartsWithMatcher::match( std::string
const&
source )
const {
7855 return startsWith( m_comparator.adjustString(
source ), m_comparator.m_str );
7859 EndsWithMatcher::EndsWithMatcher( CasedString
const& comparator ) : StringMatcherBase(
"ends with"_sr, comparator ) {}
7861 bool EndsWithMatcher::match( std::string
const&
source )
const {
7862 return endsWith( m_comparator.adjustString(
source ), m_comparator.m_str );
7867 RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {}
7869 bool RegexMatcher::match(std::string
const& matchee)
const {
7870 auto flags = std::regex::ECMAScript;
7871 if (m_caseSensitivity == CaseSensitive::No) {
7872 flags |= std::regex::icase;
7874 auto reg = std::regex(m_regex,
flags);
7875 return std::regex_match(matchee, reg);
7878 std::string RegexMatcher::describe()
const {
7879 return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Yes)?
" case sensitively" :
" case insensitively");
7883 StringEqualsMatcher
Equals( std::string
const&
str, CaseSensitive caseSensitivity ) {
7884 return StringEqualsMatcher( CasedString(
str, caseSensitivity) );
7887 return StringContainsMatcher( CasedString(
str, caseSensitivity) );
7889 EndsWithMatcher
EndsWith( std::string
const&
str, CaseSensitive caseSensitivity ) {
7890 return EndsWithMatcher( CasedString(
str, caseSensitivity) );
7892 StartsWithMatcher
StartsWith( std::string
const&
str, CaseSensitive caseSensitivity ) {
7893 return StartsWithMatcher( CasedString(
str, caseSensitivity) );
7896 RegexMatcher
Matches(std::string
const& regex, CaseSensitive caseSensitivity) {
7897 return RegexMatcher(regex, caseSensitivity);
7907 MatcherGenericBase::~MatcherGenericBase() =
default;
7911 std::string
describe_multi_matcher(StringRef combine, std::string
const* descriptions_begin, std::string
const* descriptions_end) {
7912 std::string description;
7913 std::size_t combined_size = 4;
7914 for (
auto desc = descriptions_begin; desc != descriptions_end; ++desc ) {
7915 combined_size += desc->size();
7917 combined_size +=
static_cast<size_t>(descriptions_end - descriptions_begin - 1) * combine.size();
7919 description.reserve(combined_size);
7921 description +=
"( ";
7923 for(
auto desc = descriptions_begin; desc != descriptions_end; ++desc ) {
7927 description += combine;
7928 description += *desc;
7930 description +=
" )";
7948 MatchExpr<std::string, StringMatcher const&> expr( CATCH_MOVE(exceptionMessage), matcher );
7949 handler.handleExpr( expr );
7960 AutomakeReporter::~AutomakeReporter() {}
7962 void AutomakeReporter::testCaseEnded(TestCaseStats
const& _testCaseStats) {
7964 m_stream <<
":test-result: ";
7965 if ( _testCaseStats.totals.testCases.skipped > 0 ) {
7967 }
else if (_testCaseStats.totals.assertions.allPassed()) {
7969 }
else if (_testCaseStats.totals.assertions.allOk()) {
7970 m_stream <<
"XFAIL";
7974 m_stream <<
' ' << _testCaseStats.testInfo->name <<
'\n';
7975 StreamingReporterBase::testCaseEnded(_testCaseStats);
7978 void AutomakeReporter::skipTest(TestCaseInfo
const& testInfo) {
7979 m_stream <<
":test-result: SKIP " << testInfo.name <<
'\n';
7990 ReporterBase::ReporterBase( ReporterConfig&& config ):
7991 IEventListener( config.fullConfig() ),
7992 m_wrapped_stream( CATCH_MOVE(config).takeStream() ),
7993 m_stream( m_wrapped_stream->
stream() ),
7994 m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.
get() ) ),
7995 m_customOptions( config.customOptions() )
7998 ReporterBase::~ReporterBase() =
default;
8000 void ReporterBase::listReporters(
8001 std::vector<ReporterDescription>
const& descriptions ) {
8002 defaultListReporters(m_stream, descriptions, m_config->verbosity());
8005 void ReporterBase::listListeners(
8006 std::vector<ListenerDescription>
const& descriptions ) {
8007 defaultListListeners( m_stream, descriptions );
8010 void ReporterBase::listTests(std::vector<TestCaseHandle>
const& tests) {
8011 defaultListTests(m_stream,
8014 m_config->hasTestFilters(),
8015 m_config->verbosity());
8018 void ReporterBase::listTags(std::vector<TagInfo>
const& tags) {
8019 defaultListTags( m_stream, tags, m_config->hasTestFilters() );
8033 static constexpr Colour::Code compactDimColour = Colour::FileName;
8035#ifdef CATCH_PLATFORM_MAC
8036 static constexpr Catch::StringRef compactFailedString =
"FAILED"_sr;
8037 static constexpr Catch::StringRef compactPassedString =
"PASSED"_sr;
8039 static constexpr Catch::StringRef compactFailedString =
"failed"_sr;
8040 static constexpr Catch::StringRef compactPassedString =
"passed"_sr;
8044class AssertionPrinter {
8046 AssertionPrinter& operator= (AssertionPrinter
const&) =
delete;
8047 AssertionPrinter(AssertionPrinter
const&) =
delete;
8048 AssertionPrinter(std::ostream& _stream, AssertionStats
const& _stats,
bool _printInfoMessages, ColourImpl* colourImpl_)
8050 ,
result(_stats.assertionResult)
8051 , messages(_stats.infoMessages)
8052 , itMessage(_stats.infoMessages.
begin())
8053 , printInfoMessages(_printInfoMessages)
8054 , colourImpl(colourImpl_)
8060 itMessage = messages.begin();
8062 switch (
result.getResultType()) {
8064 printResultType(Colour::ResultSuccess, compactPassedString);
8065 printOriginalExpression();
8066 printReconstructedExpression();
8067 if (!
result.hasExpression())
8068 printRemainingMessages(Colour::None);
8070 printRemainingMessages();
8072 case ResultWas::ExpressionFailed:
8074 printResultType(Colour::ResultSuccess, compactFailedString +
" - but was ok"_sr);
8076 printResultType(Colour::Error, compactFailedString);
8077 printOriginalExpression();
8078 printReconstructedExpression();
8079 printRemainingMessages();
8081 case ResultWas::ThrewException:
8082 printResultType(Colour::Error, compactFailedString);
8083 printIssue(
"unexpected exception with message:");
8085 printExpressionWas();
8086 printRemainingMessages();
8088 case ResultWas::FatalErrorCondition:
8089 printResultType(Colour::Error, compactFailedString);
8090 printIssue(
"fatal error condition with message:");
8092 printExpressionWas();
8093 printRemainingMessages();
8095 case ResultWas::DidntThrowException:
8096 printResultType(Colour::Error, compactFailedString);
8097 printIssue(
"expected exception, got none");
8098 printExpressionWas();
8099 printRemainingMessages();
8101 case ResultWas::Info:
8102 printResultType(Colour::None,
"info"_sr);
8104 printRemainingMessages();
8106 case ResultWas::Warning:
8107 printResultType(Colour::None,
"warning"_sr);
8109 printRemainingMessages();
8111 case ResultWas::ExplicitFailure:
8112 printResultType(Colour::Error, compactFailedString);
8113 printIssue(
"explicitly");
8114 printRemainingMessages(Colour::None);
8116 case ResultWas::ExplicitSkip:
8117 printResultType(Colour::Skip,
"skipped"_sr);
8119 printRemainingMessages();
8122 case ResultWas::Unknown:
8123 case ResultWas::FailureBit:
8124 case ResultWas::Exception:
8125 printResultType(Colour::Error,
"** internal error **");
8131 void printSourceInfo()
const {
8132 stream << colourImpl->guardColour( Colour::FileName )
8133 <<
result.getSourceInfo() <<
':';
8136 void printResultType(Colour::Code colour, StringRef passOrFail)
const {
8137 if (!passOrFail.empty()) {
8138 stream << colourImpl->guardColour(colour) <<
' ' << passOrFail;
8143 void printIssue(
char const* issue)
const {
8147 void printExpressionWas() {
8148 if (
result.hasExpression()) {
8151 stream << colourImpl->guardColour(compactDimColour) <<
" expression was:";
8153 printOriginalExpression();
8157 void printOriginalExpression()
const {
8158 if (
result.hasExpression()) {
8163 void printReconstructedExpression()
const {
8164 if (
result.hasExpandedExpression()) {
8165 stream << colourImpl->guardColour(compactDimColour) <<
" for: ";
8170 void printMessage() {
8171 if (itMessage != messages.end()) {
8172 stream <<
" '" << itMessage->message <<
'\'';
8177 void printRemainingMessages(Colour::Code colour = compactDimColour) {
8178 if (itMessage == messages.end())
8181 const auto itEnd = messages.cend();
8182 const auto N =
static_cast<std::size_t
>(itEnd - itMessage);
8184 stream << colourImpl->guardColour( colour ) <<
" with "
8185 << pluralise( N,
"message"_sr ) <<
':';
8187 while (itMessage != itEnd) {
8189 if (printInfoMessages || itMessage->type != ResultWas::Info) {
8191 if (itMessage != itEnd) {
8192 stream << colourImpl->guardColour(compactDimColour) <<
" and";
8202 AssertionResult
const&
result;
8203 std::vector<MessageInfo>
const& messages;
8204 std::vector<MessageInfo>::const_iterator itMessage;
8205 bool printInfoMessages;
8206 ColourImpl* colourImpl;
8211 std::string CompactReporter::getDescription() {
8212 return "Reports test results on a single line, suitable for IDEs";
8215 void CompactReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
8216 m_stream <<
"No test cases matched '" << unmatchedSpec <<
"'\n";
8219 void CompactReporter::testRunStarting( TestRunInfo
const& ) {
8220 if ( m_config->testSpec().hasFilters() ) {
8221 m_stream << m_colour->guardColour( Colour::BrightYellow )
8223 << m_config->testSpec()
8226 m_stream <<
"RNG seed: " <<
getSeed() <<
'\n';
8229 void CompactReporter::assertionEnded( AssertionStats
const& _assertionStats ) {
8230 AssertionResult
const&
result = _assertionStats.assertionResult;
8232 bool printInfoMessages =
true;
8235 if( !m_config->includeSuccessfulResults() &&
result.isOk() ) {
8236 if(
result.getResultType() != ResultWas::Warning &&
result.getResultType() != ResultWas::ExplicitSkip )
8238 printInfoMessages =
false;
8241 AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages, m_colour.get() );
8244 m_stream <<
'\n' << std::flush;
8247 void CompactReporter::sectionEnded(SectionStats
const& _sectionStats) {
8248 double dur = _sectionStats.durationInSeconds;
8249 if ( shouldShowDuration( *m_config, dur ) ) {
8250 m_stream << getFormattedDuration( dur ) <<
" s: " << _sectionStats.sectionInfo.name <<
'\n' << std::flush;
8254 void CompactReporter::testRunEnded( TestRunStats
const& _testRunStats ) {
8255 printTestRunTotals( m_stream, *m_colour, _testRunStats.totals );
8256 m_stream <<
"\n\n" << std::flush;
8257 StreamingReporterBase::testRunEnded( _testRunStats );
8260 CompactReporter::~CompactReporter() {}
8269#if defined(_MSC_VER)
8270#pragma warning(push)
8271#pragma warning(disable:4061)
8275#if defined(__clang__)
8276# pragma clang diagnostic push
8278# pragma clang diagnostic ignored "-Wunused-function"
8288class ConsoleAssertionPrinter {
8290 ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter
const&) =
delete;
8291 ConsoleAssertionPrinter(ConsoleAssertionPrinter
const&) =
delete;
8292 ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats
const& _stats, ColourImpl* colourImpl_,
bool _printInfoMessages)
8295 result(_stats.assertionResult),
8296 colour(Colour::
None),
8297 messages(_stats.infoMessages),
8298 colourImpl(colourImpl_),
8299 printInfoMessages(_printInfoMessages) {
8300 switch (
result.getResultType()) {
8302 colour = Colour::Success;
8303 passOrFail =
"PASSED"_sr;
8305 if (messages.size() == 1)
8306 messageLabel =
"with message"_sr;
8307 if (messages.size() > 1)
8308 messageLabel =
"with messages"_sr;
8310 case ResultWas::ExpressionFailed:
8312 colour = Colour::Success;
8313 passOrFail =
"FAILED - but was ok"_sr;
8315 colour = Colour::Error;
8316 passOrFail =
"FAILED"_sr;
8318 if (messages.size() == 1)
8319 messageLabel =
"with message"_sr;
8320 if (messages.size() > 1)
8321 messageLabel =
"with messages"_sr;
8323 case ResultWas::ThrewException:
8324 colour = Colour::Error;
8325 passOrFail =
"FAILED"_sr;
8327 switch (messages.size()) {
case 0:
8328 messageLabel =
"due to unexpected exception with "_sr;
8331 messageLabel =
"due to unexpected exception with message"_sr;
8334 messageLabel =
"due to unexpected exception with messages"_sr;
8338 case ResultWas::FatalErrorCondition:
8339 colour = Colour::Error;
8340 passOrFail =
"FAILED"_sr;
8341 messageLabel =
"due to a fatal error condition"_sr;
8343 case ResultWas::DidntThrowException:
8344 colour = Colour::Error;
8345 passOrFail =
"FAILED"_sr;
8346 messageLabel =
"because no exception was thrown where one was expected"_sr;
8348 case ResultWas::Info:
8349 messageLabel =
"info"_sr;
8351 case ResultWas::Warning:
8352 messageLabel =
"warning"_sr;
8354 case ResultWas::ExplicitFailure:
8355 passOrFail =
"FAILED"_sr;
8356 colour = Colour::Error;
8357 if (messages.size() == 1)
8358 messageLabel =
"explicitly with message"_sr;
8359 if (messages.size() > 1)
8360 messageLabel =
"explicitly with messages"_sr;
8362 case ResultWas::ExplicitSkip:
8363 colour = Colour::Skip;
8364 passOrFail =
"SKIPPED"_sr;
8365 if (messages.size() == 1)
8366 messageLabel =
"explicitly with message"_sr;
8367 if (messages.size() > 1)
8368 messageLabel =
"explicitly with messages"_sr;
8371 case ResultWas::Unknown:
8372 case ResultWas::FailureBit:
8373 case ResultWas::Exception:
8374 passOrFail =
"** internal error **"_sr;
8375 colour = Colour::Error;
8380 void print()
const {
8382 if (stats.totals.assertions.total() > 0) {
8384 printOriginalExpression();
8385 printReconstructedExpression();
8393 void printResultType()
const {
8394 if (!passOrFail.empty()) {
8395 stream << colourImpl->guardColour(colour) << passOrFail <<
":\n";
8398 void printOriginalExpression()
const {
8399 if (
result.hasExpression()) {
8400 stream << colourImpl->guardColour( Colour::OriginalExpression )
8401 <<
" " <<
result.getExpressionInMacro() <<
'\n';
8404 void printReconstructedExpression()
const {
8405 if (
result.hasExpandedExpression()) {
8406 stream <<
"with expansion:\n";
8407 stream << colourImpl->guardColour( Colour::ReconstructedExpression )
8408 << TextFlow::Column(
result.getExpandedExpression() )
8413 void printMessage()
const {
8414 if (!messageLabel.empty())
8415 stream << messageLabel <<
':' <<
'\n';
8416 for (
auto const&
msg : messages) {
8418 if (printInfoMessages ||
msg.type != ResultWas::Info)
8419 stream << TextFlow::Column(
msg.message).indent(2) <<
'\n';
8422 void printSourceInfo()
const {
8423 stream << colourImpl->guardColour( Colour::FileName )
8424 <<
result.getSourceInfo() <<
": ";
8428 AssertionStats
const& stats;
8429 AssertionResult
const&
result;
8430 Colour::Code colour;
8431 StringRef passOrFail;
8432 StringRef messageLabel;
8433 std::vector<MessageInfo>
const& messages;
8434 ColourImpl* colourImpl;
8435 bool printInfoMessages;
8438std::size_t makeRatio( std::uint64_t number, std::uint64_t total ) {
8439 const auto ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
8440 return (ratio == 0 && number > 0) ? 1 :
static_cast<std::size_t
>(ratio);
8444findMax( std::size_t&
i, std::size_t&
j, std::size_t& k, std::size_t& l ) {
8445 if (
i >
j &&
i > k &&
i > l)
8447 else if (
j > k &&
j > l)
8455enum class Justification {
Left,
Right };
8462struct ColumnBreak {};
8474 static const uint64_t s_nanosecondsInAMicrosecond = 1000;
8475 static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
8476 static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
8477 static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
8479 double m_inNanoseconds;
8483 explicit Duration(
double inNanoseconds, Unit
units = Unit::Auto)
8484 : m_inNanoseconds(inNanoseconds),
8486 if (m_units == Unit::Auto) {
8487 if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
8488 m_units = Unit::Nanoseconds;
8489 else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
8490 m_units = Unit::Microseconds;
8491 else if (m_inNanoseconds < s_nanosecondsInASecond)
8492 m_units = Unit::Milliseconds;
8493 else if (m_inNanoseconds < s_nanosecondsInAMinute)
8494 m_units = Unit::Seconds;
8496 m_units = Unit::Minutes;
8501 auto value() const ->
double {
8503 case Unit::Microseconds:
8504 return m_inNanoseconds /
static_cast<double>(s_nanosecondsInAMicrosecond);
8505 case Unit::Milliseconds:
8506 return m_inNanoseconds /
static_cast<double>(s_nanosecondsInAMillisecond);
8508 return m_inNanoseconds /
static_cast<double>(s_nanosecondsInASecond);
8510 return m_inNanoseconds /
static_cast<double>(s_nanosecondsInAMinute);
8512 return m_inNanoseconds;
8515 StringRef unitsAsString()
const {
8517 case Unit::Nanoseconds:
8519 case Unit::Microseconds:
8521 case Unit::Milliseconds:
8528 return "** internal error **"_sr;
8533 return os << duration.value() <<
' ' << duration.unitsAsString();
8540 std::vector<ColumnInfo> m_columnInfos;
8541 ReusableStringStream m_oss;
8542 int m_currentColumn = -1;
8543 bool m_isOpen =
false;
8546 TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
8548 m_columnInfos( CATCH_MOVE( columnInfos ) ) {}
8550 auto columnInfos() const ->
std::vector<ColumnInfo> const& {
8551 return m_columnInfos;
8557 *
this << RowBreak();
8559 TextFlow::Columns headerCols;
8561 for (
auto const& info : m_columnInfos) {
8562 assert(info.width > 2);
8563 headerCols += TextFlow::Column(info.name).width(info.width - 2);
8564 headerCols += spacer;
8566 m_os << headerCols <<
'\n';
8568 m_os << lineOfChars(
'-') <<
'\n';
8573 *
this << RowBreak();
8574 m_os <<
'\n' << std::flush;
8579 template<
typename T>
8580 friend TablePrinter& operator << (TablePrinter& tp, T
const&
value) {
8585 friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
8586 auto colStr = tp.m_oss.str();
8587 const auto strSize = colStr.size();
8590 if (tp.m_currentColumn ==
static_cast<int>(tp.m_columnInfos.size() - 1)) {
8591 tp.m_currentColumn = -1;
8594 tp.m_currentColumn++;
8596 auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
8597 auto padding = (strSize + 1 < colInfo.width)
8598 ? std::string(colInfo.width - (strSize + 1),
' ')
8600 if (colInfo.justification == Justification::Left)
8601 tp.m_os << colStr <<
padding <<
' ';
8603 tp.m_os <<
padding << colStr <<
' ';
8607 friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
8608 if (tp.m_currentColumn > 0) {
8610 tp.m_currentColumn = -1;
8616ConsoleReporter::ConsoleReporter(ReporterConfig&& config):
8617 StreamingReporterBase( CATCH_MOVE( config ) ),
8618 m_tablePrinter(Detail::make_unique<TablePrinter>(m_stream,
8619 [&config]() ->
std::
vector<ColumnInfo> {
8620 if (config.fullConfig()->benchmarkNoAnalysis())
8623 {
"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left },
8624 {
" samples", 14, Justification::Right },
8625 {
" iterations", 14, Justification::Right },
8626 {
" mean", 14, Justification::Right }
8632 {
"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left },
8633 {
"samples mean std dev", 14, Justification::Right },
8634 {
"iterations low mean low std dev", 14, Justification::Right },
8635 {
"estimated high mean high std dev", 14, Justification::Right }
8639ConsoleReporter::~ConsoleReporter() =
default;
8641std::string ConsoleReporter::getDescription() {
8642 return "Reports test results as plain lines of text";
8645void ConsoleReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
8646 m_stream <<
"No test cases matched '" << unmatchedSpec <<
"'\n";
8649void ConsoleReporter::reportInvalidTestSpec( StringRef arg ) {
8650 m_stream <<
"Invalid Filter: " << arg <<
'\n';
8653void ConsoleReporter::assertionStarting(AssertionInfo
const&) {}
8655void ConsoleReporter::assertionEnded(AssertionStats
const& _assertionStats) {
8656 AssertionResult
const&
result = _assertionStats.assertionResult;
8658 bool includeResults = m_config->includeSuccessfulResults() || !
result.isOk();
8662 if (!includeResults &&
result.getResultType() != ResultWas::Warning &&
result.getResultType() != ResultWas::ExplicitSkip)
8667 ConsoleAssertionPrinter printer(m_stream, _assertionStats, m_colour.get(), includeResults);
8669 m_stream <<
'\n' << std::flush;
8672void ConsoleReporter::sectionStarting(SectionInfo
const& _sectionInfo) {
8673 m_tablePrinter->close();
8674 m_headerPrinted =
false;
8675 StreamingReporterBase::sectionStarting(_sectionInfo);
8677void ConsoleReporter::sectionEnded(SectionStats
const& _sectionStats) {
8678 m_tablePrinter->close();
8679 if (_sectionStats.missingAssertions) {
8682 m_colour->guardColour( Colour::ResultError ).engage( m_stream );
8683 if (m_sectionStack.size() > 1)
8684 m_stream <<
"\nNo assertions in section";
8686 m_stream <<
"\nNo assertions in test case";
8687 m_stream <<
" '" << _sectionStats.sectionInfo.name <<
"'\n\n" << std::flush;
8689 double dur = _sectionStats.durationInSeconds;
8690 if (shouldShowDuration(*m_config, dur)) {
8691 m_stream << getFormattedDuration(dur) <<
" s: " << _sectionStats.sectionInfo.name <<
'\n' << std::flush;
8693 if (m_headerPrinted) {
8694 m_headerPrinted =
false;
8696 StreamingReporterBase::sectionEnded(_sectionStats);
8699void ConsoleReporter::benchmarkPreparing( StringRef
name ) {
8700 lazyPrintWithoutClosingBenchmarkTable();
8702 auto nameCol = TextFlow::Column(
static_cast<std::string
>(
name ) )
8703 .width( m_tablePrinter->columnInfos()[0].width - 2 );
8705 bool firstLine =
true;
8706 for (
auto line : nameCol) {
8708 (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
8712 (*m_tablePrinter) << line << ColumnBreak();
8716void ConsoleReporter::benchmarkStarting(BenchmarkInfo
const& info) {
8717 (*m_tablePrinter) << info.samples << ColumnBreak()
8718 << info.iterations << ColumnBreak();
8719 if (!m_config->benchmarkNoAnalysis())
8720 (*m_tablePrinter) <<
Duration(info.estimatedDuration) << ColumnBreak();
8722void ConsoleReporter::benchmarkEnded(BenchmarkStats<>
const& stats) {
8723 if (m_config->benchmarkNoAnalysis())
8725 (*m_tablePrinter) <<
Duration(stats.mean.point.count()) << ColumnBreak();
8729 (*m_tablePrinter) << ColumnBreak()
8730 <<
Duration(stats.mean.point.count()) << ColumnBreak()
8731 <<
Duration(stats.mean.lower_bound.count()) << ColumnBreak()
8732 <<
Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
8733 <<
Duration(stats.standardDeviation.point.count()) << ColumnBreak()
8734 <<
Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
8735 <<
Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
8739void ConsoleReporter::benchmarkFailed( StringRef error ) {
8740 auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream );
8742 <<
"Benchmark failed (" << error <<
')'
8743 << ColumnBreak() << RowBreak();
8746void ConsoleReporter::testCaseEnded(TestCaseStats
const& _testCaseStats) {
8747 m_tablePrinter->close();
8748 StreamingReporterBase::testCaseEnded(_testCaseStats);
8749 m_headerPrinted =
false;
8751void ConsoleReporter::testRunEnded(TestRunStats
const& _testRunStats) {
8752 printTotalsDivider(_testRunStats.totals);
8753 printTestRunTotals( m_stream, *m_colour, _testRunStats.totals );
8754 m_stream <<
'\n' << std::flush;
8755 StreamingReporterBase::testRunEnded(_testRunStats);
8757void ConsoleReporter::testRunStarting(TestRunInfo
const& _testInfo) {
8758 StreamingReporterBase::testRunStarting(_testInfo);
8759 if ( m_config->testSpec().hasFilters() ) {
8760 m_stream << m_colour->guardColour( Colour::BrightYellow ) <<
"Filters: "
8761 << m_config->testSpec() <<
'\n';
8763 m_stream <<
"Randomness seeded to: " <<
getSeed() <<
'\n';
8766void ConsoleReporter::lazyPrint() {
8768 m_tablePrinter->close();
8769 lazyPrintWithoutClosingBenchmarkTable();
8772void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
8774 if ( !m_testRunInfoPrinted ) {
8777 if (!m_headerPrinted) {
8778 printTestCaseAndSectionHeader();
8779 m_headerPrinted =
true;
8782void ConsoleReporter::lazyPrintRunInfo() {
8784 << lineOfChars(
'~' ) <<
'\n'
8785 << m_colour->guardColour( Colour::SecondaryText )
8786 << currentTestRunInfo.name <<
" is a Catch2 v" <<
libraryVersion()
8787 <<
" host application.\n"
8788 <<
"Run with -? for options\n\n";
8790 m_testRunInfoPrinted =
true;
8792void ConsoleReporter::printTestCaseAndSectionHeader() {
8793 assert(!m_sectionStack.empty());
8794 printOpenHeader(currentTestCaseInfo->name);
8796 if (m_sectionStack.size() > 1) {
8797 auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream );
8800 it = m_sectionStack.begin() + 1,
8801 itEnd = m_sectionStack.end();
8802 for (; it != itEnd; ++it)
8803 printHeaderString(it->name, 2);
8806 SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
8809 m_stream << lineOfChars(
'-' ) <<
'\n'
8810 << m_colour->guardColour( Colour::FileName ) << lineInfo <<
'\n'
8811 << lineOfChars(
'.' ) <<
"\n\n"
8815void ConsoleReporter::printClosedHeader(std::string
const& _name) {
8816 printOpenHeader(_name);
8817 m_stream << lineOfChars(
'.') <<
'\n';
8819void ConsoleReporter::printOpenHeader(std::string
const& _name) {
8820 m_stream << lineOfChars(
'-') <<
'\n';
8822 auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream );
8823 printHeaderString(_name);
8827void ConsoleReporter::printHeaderString(std::string
const& _string, std::size_t indent) {
8848 std::size_t
idx = _string.find(
": " );
8849 if ( idx != std::string::npos && idx < CATCH_CONFIG_CONSOLE_WIDTH / 4 ) {
8854 m_stream << TextFlow::Column( _string )
8855 .indent( indent + idx )
8856 .initialIndent( indent )
8860void ConsoleReporter::printTotalsDivider(Totals
const& totals) {
8861 if (totals.testCases.total() > 0) {
8862 std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
8863 std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
8864 std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
8865 std::size_t skippedRatio = makeRatio(totals.testCases.skipped, totals.testCases.total());
8866 while (failedRatio + failedButOkRatio + passedRatio + skippedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
8867 findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)++;
8868 while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
8869 findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)--;
8871 m_stream << m_colour->guardColour( Colour::Error )
8872 << std::string( failedRatio,
'=' )
8873 << m_colour->guardColour( Colour::ResultExpectedFailure )
8874 << std::string( failedButOkRatio,
'=' );
8875 if ( totals.testCases.allPassed() ) {
8876 m_stream << m_colour->guardColour( Colour::ResultSuccess )
8877 << std::string( passedRatio,
'=' );
8879 m_stream << m_colour->guardColour( Colour::Success )
8880 << std::string( passedRatio,
'=' );
8882 m_stream << m_colour->guardColour( Colour::Skip )
8883 << std::string( skippedRatio,
'=' );
8885 m_stream << m_colour->guardColour( Colour::Warning )
8886 << std::string( CATCH_CONFIG_CONSOLE_WIDTH - 1,
'=' );
8893#if defined(_MSC_VER)
8897#if defined(__clang__)
8898# pragma clang diagnostic pop
8909 struct BySectionInfo {
8910 BySectionInfo( SectionInfo
const&
other ): m_other(
other ) {}
8911 BySectionInfo( BySectionInfo
const&
other ):
8912 m_other(
other.m_other ) {}
8914 Detail::unique_ptr<CumulativeReporterBase::SectionNode>
const&
8917 ( node->stats.sectionInfo.name == m_other.name ) &&
8918 ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) );
8920 void operator=( BySectionInfo
const& ) =
delete;
8923 SectionInfo
const& m_other;
8929 AssertionOrBenchmarkResult::AssertionOrBenchmarkResult(
8930 AssertionStats
const& assertion ):
8931 m_assertion( assertion ) {}
8933 AssertionOrBenchmarkResult::AssertionOrBenchmarkResult(
8934 BenchmarkStats<>
const& benchmark ):
8935 m_benchmark( benchmark ) {}
8937 bool AssertionOrBenchmarkResult::isAssertion()
const {
8938 return m_assertion.some();
8940 bool AssertionOrBenchmarkResult::isBenchmark()
const {
8941 return m_benchmark.some();
8944 AssertionStats
const& AssertionOrBenchmarkResult::asAssertion()
const {
8945 assert(m_assertion.some());
8947 return *m_assertion;
8949 BenchmarkStats<>
const& AssertionOrBenchmarkResult::asBenchmark()
const {
8950 assert(m_benchmark.some());
8952 return *m_benchmark;
8957 CumulativeReporterBase::~CumulativeReporterBase() =
default;
8959 void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<>
const& benchmarkStats) {
8960 m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats);
8964 CumulativeReporterBase::sectionStarting( SectionInfo
const& sectionInfo ) {
8966 SectionStats incompleteStats( SectionInfo(sectionInfo), Counts(), 0,
false );
8968 if ( m_sectionStack.empty() ) {
8969 if ( !m_rootSection ) {
8971 Detail::make_unique<SectionNode>( incompleteStats );
8973 node = m_rootSection.get();
8975 SectionNode& parentNode = *m_sectionStack.back();
8976 auto it = std::find_if( parentNode.childSections.begin(),
8977 parentNode.childSections.end(),
8978 BySectionInfo( sectionInfo ) );
8979 if ( it == parentNode.childSections.end() ) {
8981 Detail::make_unique<SectionNode>( incompleteStats );
8982 node = newNode.get();
8983 parentNode.childSections.push_back( CATCH_MOVE( newNode ) );
8989 m_deepestSection = node;
8990 m_sectionStack.push_back( node );
8993 void CumulativeReporterBase::assertionEnded(
8994 AssertionStats
const& assertionStats ) {
8995 assert( !m_sectionStack.empty() );
9001 if ( m_shouldStoreFailedAssertions &&
9002 !assertionStats.assertionResult.isOk() ) {
9004 assertionStats.assertionResult.getExpandedExpression() );
9006 if ( m_shouldStoreSuccesfulAssertions &&
9007 assertionStats.assertionResult.isOk() ) {
9009 assertionStats.assertionResult.getExpandedExpression() );
9011 SectionNode& sectionNode = *m_sectionStack.back();
9012 sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats );
9015 void CumulativeReporterBase::sectionEnded( SectionStats
const& sectionStats ) {
9016 assert( !m_sectionStack.empty() );
9017 SectionNode& node = *m_sectionStack.back();
9018 node.stats = sectionStats;
9019 m_sectionStack.pop_back();
9022 void CumulativeReporterBase::testCaseEnded(
9023 TestCaseStats
const& testCaseStats ) {
9024 auto node = Detail::make_unique<TestCaseNode>( testCaseStats );
9025 assert( m_sectionStack.size() == 0 );
9026 node->children.push_back( CATCH_MOVE(m_rootSection) );
9027 m_testCases.push_back( CATCH_MOVE(node) );
9029 assert( m_deepestSection );
9030 m_deepestSection->stdOut = testCaseStats.stdOut;
9031 m_deepestSection->stdErr = testCaseStats.stdErr;
9035 void CumulativeReporterBase::testRunEnded( TestRunStats
const& testRunStats ) {
9036 assert(!m_testRun &&
"CumulativeReporterBase assumes there can only be one test run");
9037 m_testRun = Detail::make_unique<TestRunNode>( testRunStats );
9038 m_testRun->children.swap( m_testCases );
9039 testRunEndedCumulative();
9042 bool CumulativeReporterBase::SectionNode::hasAnyAssertions()
const {
9044 assertionsAndBenchmarks.begin(),
9045 assertionsAndBenchmarks.end(),
9046 []( Detail::AssertionOrBenchmarkResult
const&
res ) {
9047 return res.isAssertion();
9058 void EventListenerBase::fatalErrorEncountered( StringRef ) {}
9060 void EventListenerBase::benchmarkPreparing( StringRef ) {}
9061 void EventListenerBase::benchmarkStarting( BenchmarkInfo
const& ) {}
9062 void EventListenerBase::benchmarkEnded( BenchmarkStats<>
const& ) {}
9063 void EventListenerBase::benchmarkFailed( StringRef ) {}
9065 void EventListenerBase::assertionStarting( AssertionInfo
const& ) {}
9067 void EventListenerBase::assertionEnded( AssertionStats
const& ) {}
9068 void EventListenerBase::listReporters(
9069 std::vector<ReporterDescription>
const& ) {}
9070 void EventListenerBase::listListeners(
9071 std::vector<ListenerDescription>
const& ) {}
9072 void EventListenerBase::listTests( std::vector<TestCaseHandle>
const& ) {}
9073 void EventListenerBase::listTags( std::vector<TagInfo>
const& ) {}
9074 void EventListenerBase::noMatchingTestCases( StringRef ) {}
9075 void EventListenerBase::reportInvalidTestSpec( StringRef ) {}
9076 void EventListenerBase::testRunStarting( TestRunInfo
const& ) {}
9077 void EventListenerBase::testCaseStarting( TestCaseInfo
const& ) {}
9078 void EventListenerBase::testCasePartialStarting(TestCaseInfo
const&, uint64_t) {}
9079 void EventListenerBase::sectionStarting( SectionInfo
const& ) {}
9080 void EventListenerBase::sectionEnded( SectionStats
const& ) {}
9081 void EventListenerBase::testCasePartialEnded(TestCaseStats
const&, uint64_t) {}
9082 void EventListenerBase::testCaseEnded( TestCaseStats
const& ) {}
9083 void EventListenerBase::testRunEnded( TestRunStats
const& ) {}
9084 void EventListenerBase::skipTest( TestCaseInfo
const& ) {}
9099 void listTestNamesOnly(std::ostream& out,
9100 std::vector<TestCaseHandle>
const& tests) {
9101 for (
auto const&
test : tests) {
9102 auto const& testCaseInfo =
test.getTestCaseInfo();
9105 out <<
'"' << testCaseInfo.name <<
'"';
9107 out << testCaseInfo.name;
9120 std::string getFormattedDuration(
double duration ) {
9125 const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
9126 char buffer[maxDoubleSize];
9131 size_t printedLength =
static_cast<size_t>(
9132 sprintf_s(
buffer,
"%.3f", duration ) );
9134 size_t printedLength =
static_cast<size_t>(
9135 std::snprintf(
buffer, maxDoubleSize,
"%.3f", duration ) );
9137 return std::string(
buffer, printedLength );
9140 bool shouldShowDuration( IConfig
const& config,
double duration ) {
9141 if ( config.showDurations() == ShowDurations::Always ) {
9144 if ( config.showDurations() == ShowDurations::Never ) {
9147 const double min = config.minDuration();
9148 return min >= 0 && duration >=
min;
9151 std::string serializeFilters( std::vector<std::string>
const& filters ) {
9153 size_t serialized_size = filters.size() - 1;
9154 for (
auto const&
filter : filters) {
9155 serialized_size +=
filter.size();
9158 std::string serialized;
9159 serialized.reserve(serialized_size);
9162 for (
auto const&
filter : filters) {
9164 serialized.push_back(
' ');
9167 serialized.append(
filter);
9174 for (
size_t idx = 0;
idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++
idx ) {
9181 defaultListReporters( std::ostream& out,
9182 std::vector<ReporterDescription>
const& descriptions,
9184 out <<
"Available reporters:\n";
9185 const auto maxNameLen =
9186 std::max_element( descriptions.begin(),
9188 []( ReporterDescription
const& lhs,
9189 ReporterDescription
const& rhs ) {
9190 return lhs.name.size() < rhs.name.size();
9194 for (
auto const& desc : descriptions ) {
9196 out << TextFlow::Column( desc.name )
9198 .width( 5 + maxNameLen )
9201 out << TextFlow::Column( desc.name +
':' )
9203 .width( 5 + maxNameLen ) +
9204 TextFlow::Column( desc.description )
9207 .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 )
9211 out <<
'\n' << std::flush;
9214 void defaultListListeners( std::ostream& out,
9215 std::vector<ListenerDescription>
const& descriptions ) {
9216 out <<
"Registered listeners:\n";
9218 if(descriptions.empty()) {
9222 const auto maxNameLen =
9223 std::max_element( descriptions.begin(),
9225 []( ListenerDescription
const& lhs,
9226 ListenerDescription
const& rhs ) {
9227 return lhs.name.size() < rhs.name.size();
9231 for (
auto const& desc : descriptions ) {
9232 out << TextFlow::Column( static_cast<std::string>( desc.name ) +
9235 .width( maxNameLen + 5 ) +
9236 TextFlow::Column( desc.description )
9239 .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 )
9243 out <<
'\n' << std::flush;
9246 void defaultListTags( std::ostream& out,
9247 std::vector<TagInfo>
const& tags,
9250 out <<
"Tags for matching test cases:\n";
9252 out <<
"All available tags:\n";
9255 for (
auto const& tagCount : tags ) {
9256 ReusableStringStream rss;
9257 rss <<
" " << std::setw( 2 ) << tagCount.count <<
" ";
9258 auto str = rss.str();
9259 auto wrapper = TextFlow::Column( tagCount.all() )
9261 .indent(
str.size() )
9262 .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 );
9263 out <<
str << wrapper <<
'\n';
9265 out << pluralise(tags.size(),
"tag"_sr) <<
"\n\n" << std::flush;
9268 void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector<TestCaseHandle>
const& tests,
bool isFiltered,
Verbosity verbosity) {
9273 listTestNamesOnly(out, tests);
9278 out <<
"Matching test cases:\n";
9280 out <<
"All available test cases:\n";
9283 for (
auto const&
test : tests) {
9284 auto const& testCaseInfo =
test.getTestCaseInfo();
9285 Colour::Code colour = testCaseInfo.isHidden()
9286 ? Colour::SecondaryText
9288 auto colourGuard = streamColour->guardColour( colour ).engage( out );
9290 out << TextFlow::Column(testCaseInfo.name).indent(2) <<
'\n';
9292 out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) <<
'\n';
9294 if (!testCaseInfo.tags.empty() &&
9296 out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) <<
'\n';
9301 out << pluralise(tests.size(),
"matching test case"_sr);
9303 out << pluralise(tests.size(),
"test case"_sr);
9305 out <<
"\n\n" << std::flush;
9309 class SummaryColumn {
9311 SummaryColumn( std::string suffix, Colour::Code colour ):
9312 m_suffix( CATCH_MOVE( suffix ) ), m_colour( colour ) {}
9314 SummaryColumn&& addRow( std::uint64_t
count ) && {
9316 auto const new_width = std::max( m_width,
row.size() );
9317 if ( new_width > m_width ) {
9318 for (
auto& oldRow : m_rows ) {
9319 oldRow.insert( 0, new_width - m_width,
' ' );
9322 row.insert( 0, m_width -
row.size(),
' ' );
9324 m_width = new_width;
9325 m_rows.push_back(
row );
9326 return std::move( *
this );
9329 std::string
const& getSuffix()
const {
return m_suffix; }
9330 Colour::Code getColour()
const {
return m_colour; }
9331 std::string
const& getRow( std::size_t
index )
const {
9332 return m_rows[
index];
9336 std::string m_suffix;
9337 Colour::Code m_colour;
9338 std::size_t m_width = 0;
9339 std::vector<std::string> m_rows;
9342 void printSummaryRow( std::ostream&
stream,
9345 std::vector<SummaryColumn>
const&
cols,
9347 for (
auto const&
col :
cols ) {
9349 auto const& suffix =
col.getSuffix();
9350 if ( suffix.empty() ) {
9352 if (
value !=
"0" ) {
9355 stream << colour.guardColour( Colour::Warning )
9358 }
else if (
value !=
"0" ) {
9359 stream << colour.guardColour( Colour::LightGrey ) <<
" | "
9360 << colour.guardColour(
col.getColour() ) <<
value
9368 void printTestRunTotals( std::ostream&
stream,
9369 ColourImpl& streamColour,
9370 Totals
const& totals ) {
9371 if ( totals.testCases.total() == 0 ) {
9372 stream << streamColour.guardColour( Colour::Warning )
9373 <<
"No tests ran\n";
9377 if ( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
9378 stream << streamColour.guardColour( Colour::ResultSuccess )
9379 <<
"All tests passed";
9381 << pluralise( totals.assertions.passed,
"assertion"_sr )
9383 << pluralise( totals.testCases.passed,
"test case"_sr )
9388 std::vector<SummaryColumn> columns;
9390 const auto totalAssertionCount =
9391 totals.assertions.total() - totals.assertions.skipped;
9392 columns.push_back( SummaryColumn(
"", Colour::None )
9393 .addRow( totals.testCases.total() )
9394 .addRow( totalAssertionCount ) );
9395 columns.push_back( SummaryColumn(
"passed", Colour::Success )
9396 .addRow( totals.testCases.passed )
9397 .addRow( totals.assertions.passed ) );
9398 columns.push_back( SummaryColumn(
"failed", Colour::ResultError )
9399 .addRow( totals.testCases.failed )
9400 .addRow( totals.assertions.failed ) );
9401 columns.push_back( SummaryColumn(
"skipped", Colour::Skip )
9402 .addRow( totals.testCases.skipped )
9406 SummaryColumn(
"failed as expected", Colour::ResultExpectedFailure )
9407 .addRow( totals.testCases.failedButOk )
9408 .addRow( totals.assertions.failedButOk ) );
9409 printSummaryRow(
stream, streamColour,
"test cases"_sr, columns, 0 );
9410 printSummaryRow(
stream, streamColour,
"assertions"_sr, columns, 1 );
9428 std::time(&rawtime);
9430 std::tm timeInfo = {};
9431#if defined (_MSC_VER) || defined (__MINGW32__)
9432 gmtime_s(&timeInfo, &rawtime);
9433#elif defined (CATCH_PLATFORM_PLAYSTATION)
9434 gmtime_s(&rawtime, &timeInfo);
9435#elif defined (__IAR_SYSTEMS_ICC__)
9436 timeInfo = *std::gmtime(&rawtime);
9438 gmtime_r(&rawtime, &timeInfo);
9441 auto const timeStampSize =
sizeof(
"2017-01-16T17:06:45Z");
9442 char timeStamp[timeStampSize];
9443 const char *
const fmt =
"%Y-%m-%dT%H:%M:%SZ";
9445 std::strftime(timeStamp, timeStampSize,
fmt, &timeInfo);
9447 return std::string(timeStamp, timeStampSize - 1);
9450 std::string fileNameTag(std::vector<Tag>
const& tags) {
9451 auto it = std::find_if(
begin(tags),
9453 [] (Tag
const& tag) {
9454 return tag.original.size() > 0
9455 && tag.original[0] ==
'#'; });
9456 if (it != tags.end()) {
9457 return static_cast<std::string
>(
9458 it->original.substr(1, it->original.size() - 1)
9461 return std::string();
9468 std::string formatDuration(
double seconds ) {
9469 ReusableStringStream rss;
9470 rss << std::fixed << std::setprecision( 3 ) << seconds;
9474 static void normalizeNamespaceMarkers(std::string&
str) {
9475 std::size_t
pos =
str.find(
"::" );
9476 while (
pos !=
str.npos ) {
9477 str.replace(
pos, 2,
"." );
9485 JunitReporter::JunitReporter( ReporterConfig&& _config )
9486 : CumulativeReporterBase( CATCH_MOVE(_config) ),
9489 m_preferences.shouldRedirectStdOut =
true;
9490 m_preferences.shouldReportAllAssertions =
true;
9491 m_shouldStoreSuccesfulAssertions =
false;
9494 std::string JunitReporter::getDescription() {
9495 return "Reports test results in an XML format that looks like Ant's junitreport target";
9498 void JunitReporter::testRunStarting( TestRunInfo
const& runInfo ) {
9499 CumulativeReporterBase::testRunStarting( runInfo );
9500 xml.startElement(
"testsuites" );
9502 stdOutForSuite.clear();
9503 stdErrForSuite.clear();
9504 unexpectedExceptions = 0;
9507 void JunitReporter::testCaseStarting( TestCaseInfo
const& testCaseInfo ) {
9508 m_okToFail = testCaseInfo.okToFail();
9511 void JunitReporter::assertionEnded( AssertionStats
const& assertionStats ) {
9512 if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
9513 unexpectedExceptions++;
9514 CumulativeReporterBase::assertionEnded( assertionStats );
9517 void JunitReporter::testCaseEnded( TestCaseStats
const& testCaseStats ) {
9518 stdOutForSuite += testCaseStats.stdOut;
9519 stdErrForSuite += testCaseStats.stdErr;
9520 CumulativeReporterBase::testCaseEnded( testCaseStats );
9523 void JunitReporter::testRunEndedCumulative() {
9524 const auto suiteTime = suiteTimer.getElapsedSeconds();
9525 writeRun( *m_testRun, suiteTime );
9529 void JunitReporter::writeRun( TestRunNode
const& testRunNode,
double suiteTime ) {
9530 XmlWriter::ScopedElement e = xml.scopedElement(
"testsuite" );
9532 TestRunStats
const& stats = testRunNode.value;
9533 xml.writeAttribute(
"name"_sr, stats.runInfo.name );
9534 xml.writeAttribute(
"errors"_sr, unexpectedExceptions );
9535 xml.writeAttribute(
"failures"_sr, stats.totals.assertions.failed-unexpectedExceptions );
9536 xml.writeAttribute(
"skipped"_sr, stats.totals.assertions.skipped );
9537 xml.writeAttribute(
"tests"_sr, stats.totals.assertions.total() );
9538 xml.writeAttribute(
"hostname"_sr,
"tbd"_sr );
9539 if( m_config->showDurations() == ShowDurations::Never )
9540 xml.writeAttribute(
"time"_sr,
""_sr );
9542 xml.writeAttribute(
"time"_sr, formatDuration( suiteTime ) );
9547 auto properties = xml.scopedElement(
"properties");
9548 xml.scopedElement(
"property")
9549 .writeAttribute(
"name"_sr,
"random-seed"_sr)
9550 .writeAttribute(
"value"_sr, m_config->rngSeed());
9551 if (m_config->testSpec().hasFilters()) {
9552 xml.scopedElement(
"property")
9553 .writeAttribute(
"name"_sr,
"filters"_sr)
9554 .writeAttribute(
"value"_sr, m_config->testSpec());
9559 for(
auto const& child : testRunNode.children )
9560 writeTestCase( *child );
9562 xml.scopedElement(
"system-out" ).writeText(
trim( stdOutForSuite ), XmlFormatting::Newline );
9563 xml.scopedElement(
"system-err" ).writeText(
trim( stdErrForSuite ), XmlFormatting::Newline );
9566 void JunitReporter::writeTestCase( TestCaseNode
const& testCaseNode ) {
9567 TestCaseStats
const& stats = testCaseNode.value;
9571 assert( testCaseNode.children.size() == 1 );
9572 SectionNode
const& rootSection = *testCaseNode.children.front();
9574 std::string className =
9575 static_cast<std::string
>( stats.testInfo->className );
9577 if( className.empty() ) {
9578 className = fileNameTag(stats.testInfo->tags);
9579 if ( className.empty() ) {
9580 className =
"global";
9584 if ( !m_config->name().empty() )
9585 className =
static_cast<std::string
>(m_config->name()) +
'.' + className;
9587 normalizeNamespaceMarkers(className);
9589 writeSection( className,
"", rootSection, stats.testInfo->okToFail() );
9592 void JunitReporter::writeSection( std::string
const& className,
9593 std::string
const& rootName,
9594 SectionNode
const& sectionNode,
9595 bool testOkToFail) {
9596 std::string
name =
trim( sectionNode.stats.sectionInfo.name );
9597 if( !rootName.empty() )
9600 if( sectionNode.hasAnyAssertions()
9601 || !sectionNode.stdOut.empty()
9602 || !sectionNode.stdErr.empty() ) {
9603 XmlWriter::ScopedElement e = xml.scopedElement(
"testcase" );
9604 if( className.empty() ) {
9605 xml.writeAttribute(
"classname"_sr,
name );
9606 xml.writeAttribute(
"name"_sr,
"root"_sr );
9609 xml.writeAttribute(
"classname"_sr, className );
9610 xml.writeAttribute(
"name"_sr,
name );
9612 xml.writeAttribute(
"time"_sr, formatDuration( sectionNode.stats.durationInSeconds ) );
9617 xml.writeAttribute(
"status"_sr,
"run"_sr );
9619 if (sectionNode.stats.assertions.failedButOk) {
9620 xml.scopedElement(
"skipped")
9621 .writeAttribute(
"message",
"TEST_CASE tagged with !mayfail");
9624 writeAssertions( sectionNode );
9627 if( !sectionNode.stdOut.empty() )
9628 xml.scopedElement(
"system-out" ).writeText(
trim( sectionNode.stdOut ), XmlFormatting::Newline );
9629 if( !sectionNode.stdErr.empty() )
9630 xml.scopedElement(
"system-err" ).writeText(
trim( sectionNode.stdErr ), XmlFormatting::Newline );
9632 for(
auto const& childNode : sectionNode.childSections )
9634 writeSection(
name,
"", *childNode, testOkToFail );
9636 writeSection( className,
name, *childNode, testOkToFail );
9639 void JunitReporter::writeAssertions( SectionNode
const& sectionNode ) {
9640 for (
auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) {
9641 if (assertionOrBenchmark.isAssertion()) {
9642 writeAssertion(assertionOrBenchmark.asAssertion());
9647 void JunitReporter::writeAssertion( AssertionStats
const& stats ) {
9648 AssertionResult
const&
result = stats.assertionResult;
9650 result.getResultType() == ResultWas::ExplicitSkip ) {
9651 std::string elementName;
9652 switch(
result.getResultType() ) {
9653 case ResultWas::ThrewException:
9654 case ResultWas::FatalErrorCondition:
9655 elementName =
"error";
9657 case ResultWas::ExplicitFailure:
9658 case ResultWas::ExpressionFailed:
9659 case ResultWas::DidntThrowException:
9660 elementName =
"failure";
9662 case ResultWas::ExplicitSkip:
9663 elementName =
"skipped";
9666 case ResultWas::Info:
9667 case ResultWas::Warning:
9669 case ResultWas::Unknown:
9670 case ResultWas::FailureBit:
9671 case ResultWas::Exception:
9672 elementName =
"internalError";
9676 XmlWriter::ScopedElement e = xml.scopedElement( elementName );
9678 xml.writeAttribute(
"message"_sr,
result.getExpression() );
9679 xml.writeAttribute(
"type"_sr,
result.getTestMacroName() );
9681 ReusableStringStream rss;
9682 if (
result.getResultType() == ResultWas::ExplicitSkip ) {
9685 rss <<
"FAILED" <<
":\n";
9686 if (
result.hasExpression()) {
9688 rss <<
result.getExpressionInMacro();
9691 if (
result.hasExpandedExpression()) {
9692 rss <<
"with expansion:\n";
9693 rss << TextFlow::Column(
result.getExpandedExpression()).indent(2) <<
'\n';
9697 if(
result.hasMessage() )
9698 rss <<
result.getMessage() <<
'\n';
9699 for(
auto const&
msg : stats.infoMessages )
9703 rss <<
"at " <<
result.getSourceInfo();
9704 xml.writeText( rss.str(), XmlFormatting::Newline );
9716 void MultiReporter::updatePreferences(IEventListener
const& reporterish) {
9717 m_preferences.shouldRedirectStdOut |=
9718 reporterish.getPreferences().shouldRedirectStdOut;
9719 m_preferences.shouldReportAllAssertions |=
9720 reporterish.getPreferences().shouldReportAllAssertions;
9723 void MultiReporter::addListener( IEventListenerPtr&& listener ) {
9724 updatePreferences(*listener);
9725 m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener) );
9726 ++m_insertedListeners;
9729 void MultiReporter::addReporter( IEventListenerPtr&& reporter ) {
9730 updatePreferences(*reporter);
9737 m_haveNoncapturingReporters |= !reporter->getPreferences().shouldRedirectStdOut;
9741 m_reporterLikes.push_back( CATCH_MOVE( reporter ) );
9744 void MultiReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
9745 for (
auto& reporterish : m_reporterLikes ) {
9746 reporterish->noMatchingTestCases( unmatchedSpec );
9750 void MultiReporter::fatalErrorEncountered( StringRef error ) {
9751 for (
auto& reporterish : m_reporterLikes ) {
9752 reporterish->fatalErrorEncountered( error );
9756 void MultiReporter::reportInvalidTestSpec( StringRef arg ) {
9757 for (
auto& reporterish : m_reporterLikes ) {
9758 reporterish->reportInvalidTestSpec( arg );
9762 void MultiReporter::benchmarkPreparing( StringRef
name ) {
9763 for (
auto& reporterish : m_reporterLikes) {
9764 reporterish->benchmarkPreparing(
name);
9767 void MultiReporter::benchmarkStarting( BenchmarkInfo
const& benchmarkInfo ) {
9768 for (
auto& reporterish : m_reporterLikes ) {
9769 reporterish->benchmarkStarting( benchmarkInfo );
9772 void MultiReporter::benchmarkEnded( BenchmarkStats<>
const& benchmarkStats ) {
9773 for (
auto& reporterish : m_reporterLikes ) {
9774 reporterish->benchmarkEnded( benchmarkStats );
9778 void MultiReporter::benchmarkFailed( StringRef error ) {
9779 for (
auto& reporterish : m_reporterLikes) {
9780 reporterish->benchmarkFailed(error);
9784 void MultiReporter::testRunStarting( TestRunInfo
const& testRunInfo ) {
9785 for (
auto& reporterish : m_reporterLikes ) {
9786 reporterish->testRunStarting( testRunInfo );
9790 void MultiReporter::testCaseStarting( TestCaseInfo
const& testInfo ) {
9791 for (
auto& reporterish : m_reporterLikes ) {
9792 reporterish->testCaseStarting( testInfo );
9797 MultiReporter::testCasePartialStarting( TestCaseInfo
const& testInfo,
9798 uint64_t partNumber ) {
9799 for (
auto& reporterish : m_reporterLikes ) {
9800 reporterish->testCasePartialStarting( testInfo, partNumber );
9804 void MultiReporter::sectionStarting( SectionInfo
const& sectionInfo ) {
9805 for (
auto& reporterish : m_reporterLikes ) {
9806 reporterish->sectionStarting( sectionInfo );
9810 void MultiReporter::assertionStarting( AssertionInfo
const& assertionInfo ) {
9811 for (
auto& reporterish : m_reporterLikes ) {
9812 reporterish->assertionStarting( assertionInfo );
9816 void MultiReporter::assertionEnded( AssertionStats
const& assertionStats ) {
9817 const bool reportByDefault =
9818 assertionStats.assertionResult.getResultType() != ResultWas::Ok ||
9819 m_config->includeSuccessfulResults();
9821 for (
auto & reporterish : m_reporterLikes ) {
9822 if ( reportByDefault ||
9823 reporterish->getPreferences().shouldReportAllAssertions ) {
9824 reporterish->assertionEnded( assertionStats );
9829 void MultiReporter::sectionEnded( SectionStats
const& sectionStats ) {
9830 for (
auto& reporterish : m_reporterLikes ) {
9831 reporterish->sectionEnded( sectionStats );
9835 void MultiReporter::testCasePartialEnded( TestCaseStats
const& testStats,
9836 uint64_t partNumber ) {
9837 if ( m_preferences.shouldRedirectStdOut &&
9838 m_haveNoncapturingReporters ) {
9839 if ( !testStats.stdOut.empty() ) {
9842 if ( !testStats.stdErr.empty() ) {
9847 for (
auto& reporterish : m_reporterLikes ) {
9848 reporterish->testCasePartialEnded( testStats, partNumber );
9852 void MultiReporter::testCaseEnded( TestCaseStats
const& testCaseStats ) {
9853 for (
auto& reporterish : m_reporterLikes ) {
9854 reporterish->testCaseEnded( testCaseStats );
9858 void MultiReporter::testRunEnded( TestRunStats
const& testRunStats ) {
9859 for (
auto& reporterish : m_reporterLikes ) {
9860 reporterish->testRunEnded( testRunStats );
9865 void MultiReporter::skipTest( TestCaseInfo
const& testInfo ) {
9866 for (
auto& reporterish : m_reporterLikes ) {
9867 reporterish->skipTest( testInfo );
9871 void MultiReporter::listReporters(std::vector<ReporterDescription>
const& descriptions) {
9872 for (
auto& reporterish : m_reporterLikes) {
9873 reporterish->listReporters(descriptions);
9877 void MultiReporter::listListeners(
9878 std::vector<ListenerDescription>
const& descriptions ) {
9879 for (
auto& reporterish : m_reporterLikes ) {
9880 reporterish->listListeners( descriptions );
9884 void MultiReporter::listTests(std::vector<TestCaseHandle>
const& tests) {
9885 for (
auto& reporterish : m_reporterLikes) {
9886 reporterish->listTests(tests);
9890 void MultiReporter::listTags(std::vector<TagInfo>
const& tags) {
9891 for (
auto& reporterish : m_reporterLikes) {
9892 reporterish->listTags(tags);
9905 void registerReporterImpl( std::string
const&
name,
9906 IReporterFactoryPtr reporterPtr ) {
9909 name, CATCH_MOVE( reporterPtr ) );
9918 void registerListenerImpl( Detail::unique_ptr<EventListenerFactory> listenerFactory ) {
9934 std::string createMetadataString(IConfig
const& config) {
9935 ReusableStringStream sstr;
9936 if ( config.testSpec().hasFilters() ) {
9938 << config.testSpec()
9941 sstr <<
"rng-seed=" << config.rngSeed();
9946 void SonarQubeReporter::testRunStarting(TestRunInfo
const& testRunInfo) {
9947 CumulativeReporterBase::testRunStarting(testRunInfo);
9949 xml.writeComment( createMetadataString( *m_config ) );
9950 xml.startElement(
"testExecutions");
9951 xml.writeAttribute(
"version"_sr,
'1');
9954 void SonarQubeReporter::writeRun( TestRunNode
const& runNode ) {
9955 std::map<StringRef, std::vector<TestCaseNode const*>> testsPerFile;
9957 for (
auto const& child : runNode.children ) {
9958 testsPerFile[child->value.testInfo->lineInfo.file].push_back(
9962 for (
auto const& kv : testsPerFile ) {
9963 writeTestFile( kv.first, kv.second );
9967 void SonarQubeReporter::writeTestFile(StringRef
filename, std::vector<TestCaseNode const*>
const& testCaseNodes) {
9968 XmlWriter::ScopedElement e = xml.scopedElement(
"file");
9969 xml.writeAttribute(
"path"_sr,
filename);
9971 for (
auto const& child : testCaseNodes)
9972 writeTestCase(*child);
9975 void SonarQubeReporter::writeTestCase(TestCaseNode
const& testCaseNode) {
9978 assert(testCaseNode.children.size() == 1);
9979 SectionNode
const& rootSection = *testCaseNode.children.front();
9980 writeSection(
"", rootSection, testCaseNode.value.testInfo->okToFail());
9983 void SonarQubeReporter::writeSection(std::string
const& rootName, SectionNode
const& sectionNode,
bool okToFail) {
9984 std::string
name =
trim(sectionNode.stats.sectionInfo.name);
9985 if (!rootName.empty())
9988 if ( sectionNode.hasAnyAssertions()
9989 || !sectionNode.stdOut.empty()
9990 || !sectionNode.stdErr.empty() ) {
9991 XmlWriter::ScopedElement e = xml.scopedElement(
"testCase");
9992 xml.writeAttribute(
"name"_sr,
name);
9993 xml.writeAttribute(
"duration"_sr,
static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
9995 writeAssertions(sectionNode, okToFail);
9998 for (
auto const& childNode : sectionNode.childSections)
9999 writeSection(
name, *childNode, okToFail);
10002 void SonarQubeReporter::writeAssertions(SectionNode
const& sectionNode,
bool okToFail) {
10003 for (
auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) {
10004 if (assertionOrBenchmark.isAssertion()) {
10005 writeAssertion(assertionOrBenchmark.asAssertion(), okToFail);
10010 void SonarQubeReporter::writeAssertion(AssertionStats
const& stats,
bool okToFail) {
10011 AssertionResult
const&
result = stats.assertionResult;
10013 result.getResultType() == ResultWas::ExplicitSkip ) {
10014 std::string elementName;
10016 elementName =
"skipped";
10018 switch (
result.getResultType()) {
10019 case ResultWas::ThrewException:
10020 case ResultWas::FatalErrorCondition:
10021 elementName =
"error";
10023 case ResultWas::ExplicitFailure:
10024 case ResultWas::ExpressionFailed:
10025 case ResultWas::DidntThrowException:
10026 elementName =
"failure";
10028 case ResultWas::ExplicitSkip:
10029 elementName =
"skipped";
10032 case ResultWas::Info:
10033 case ResultWas::Warning:
10034 case ResultWas::Ok:
10035 case ResultWas::Unknown:
10036 case ResultWas::FailureBit:
10037 case ResultWas::Exception:
10038 elementName =
"internalError";
10043 XmlWriter::ScopedElement e = xml.scopedElement(elementName);
10045 ReusableStringStream messageRss;
10046 messageRss <<
result.getTestMacroName() <<
'(' <<
result.getExpression() <<
')';
10047 xml.writeAttribute(
"message"_sr, messageRss.str());
10049 ReusableStringStream textRss;
10050 if (
result.getResultType() == ResultWas::ExplicitSkip ) {
10051 textRss <<
"SKIPPED\n";
10053 textRss <<
"FAILED:\n";
10054 if (
result.hasExpression()) {
10055 textRss <<
'\t' <<
result.getExpressionInMacro() <<
'\n';
10057 if (
result.hasExpandedExpression()) {
10058 textRss <<
"with expansion:\n\t" <<
result.getExpandedExpression() <<
'\n';
10062 if (
result.hasMessage())
10063 textRss <<
result.getMessage() <<
'\n';
10065 for (
auto const&
msg : stats.infoMessages)
10069 textRss <<
"at " <<
result.getSourceInfo();
10070 xml.writeText(textRss.str(), XmlFormatting::Newline);
10080 StreamingReporterBase::~StreamingReporterBase() =
default;
10083 StreamingReporterBase::testRunStarting( TestRunInfo
const& _testRunInfo ) {
10084 currentTestRunInfo = _testRunInfo;
10087 void StreamingReporterBase::testRunEnded( TestRunStats
const& ) {
10088 currentTestCaseInfo =
nullptr;
10095#include <algorithm>
10103 static constexpr StringRef tapFailedString =
"not ok"_sr;
10104 static constexpr StringRef tapPassedString =
"ok"_sr;
10105 static constexpr Colour::Code tapDimColour = Colour::FileName;
10107 class TapAssertionPrinter {
10109 TapAssertionPrinter& operator= (TapAssertionPrinter
const&) =
delete;
10110 TapAssertionPrinter(TapAssertionPrinter
const&) =
delete;
10111 TapAssertionPrinter(std::ostream& _stream, AssertionStats
const& _stats, std::size_t _counter, ColourImpl* colour_)
10113 ,
result(_stats.assertionResult)
10114 , messages(_stats.infoMessages)
10115 , itMessage(_stats.infoMessages.
begin())
10116 , printInfoMessages(true)
10118 , colourImpl( colour_ ) {}
10121 itMessage = messages.begin();
10123 switch (
result.getResultType()) {
10124 case ResultWas::Ok:
10125 printResultType(tapPassedString);
10126 printOriginalExpression();
10127 printReconstructedExpression();
10128 if (!
result.hasExpression())
10129 printRemainingMessages(Colour::None);
10131 printRemainingMessages();
10133 case ResultWas::ExpressionFailed:
10135 printResultType(tapPassedString);
10137 printResultType(tapFailedString);
10139 printOriginalExpression();
10140 printReconstructedExpression();
10142 printIssue(
" # TODO");
10144 printRemainingMessages();
10146 case ResultWas::ThrewException:
10147 printResultType(tapFailedString);
10148 printIssue(
"unexpected exception with message:"_sr);
10150 printExpressionWas();
10151 printRemainingMessages();
10153 case ResultWas::FatalErrorCondition:
10154 printResultType(tapFailedString);
10155 printIssue(
"fatal error condition with message:"_sr);
10157 printExpressionWas();
10158 printRemainingMessages();
10160 case ResultWas::DidntThrowException:
10161 printResultType(tapFailedString);
10162 printIssue(
"expected exception, got none"_sr);
10163 printExpressionWas();
10164 printRemainingMessages();
10166 case ResultWas::Info:
10167 printResultType(
"info"_sr);
10169 printRemainingMessages();
10171 case ResultWas::Warning:
10172 printResultType(
"warning"_sr);
10174 printRemainingMessages();
10176 case ResultWas::ExplicitFailure:
10177 printResultType(tapFailedString);
10178 printIssue(
"explicitly"_sr);
10179 printRemainingMessages(Colour::None);
10181 case ResultWas::ExplicitSkip:
10182 printResultType(tapPassedString);
10183 printIssue(
" # SKIP"_sr);
10185 printRemainingMessages();
10188 case ResultWas::Unknown:
10189 case ResultWas::FailureBit:
10190 case ResultWas::Exception:
10191 printResultType(
"** internal error **"_sr);
10197 void printResultType(StringRef passOrFail)
const {
10198 if (!passOrFail.empty()) {
10203 void printIssue(StringRef issue)
const {
10207 void printExpressionWas() {
10208 if (
result.hasExpression()) {
10210 stream << colourImpl->guardColour( tapDimColour )
10211 <<
" expression was:";
10212 printOriginalExpression();
10216 void printOriginalExpression()
const {
10217 if (
result.hasExpression()) {
10222 void printReconstructedExpression()
const {
10223 if (
result.hasExpandedExpression()) {
10224 stream << colourImpl->guardColour( tapDimColour ) <<
" for: ";
10226 std::string expr =
result.getExpandedExpression();
10227 std::replace(expr.begin(), expr.end(),
'\n',
' ');
10232 void printMessage() {
10233 if (itMessage != messages.end()) {
10234 stream <<
" '" << itMessage->message <<
'\'';
10239 void printRemainingMessages(Colour::Code colour = tapDimColour) {
10240 if (itMessage == messages.end()) {
10245 std::vector<MessageInfo>::const_iterator itEnd = messages.end();
10246 const std::size_t N =
static_cast<std::size_t
>(itEnd - itMessage);
10248 stream << colourImpl->guardColour( colour ) <<
" with "
10249 << pluralise( N,
"message"_sr ) <<
':';
10251 for (; itMessage != itEnd; ) {
10253 if (printInfoMessages || itMessage->type != ResultWas::Info) {
10254 stream <<
" '" << itMessage->message <<
'\'';
10255 if (++itMessage != itEnd) {
10256 stream << colourImpl->guardColour(tapDimColour) <<
" and";
10264 AssertionResult
const&
result;
10265 std::vector<MessageInfo>
const& messages;
10266 std::vector<MessageInfo>::const_iterator itMessage;
10267 bool printInfoMessages;
10269 ColourImpl* colourImpl;
10274 void TAPReporter::testRunStarting( TestRunInfo
const& ) {
10275 if ( m_config->testSpec().hasFilters() ) {
10276 m_stream <<
"# filters: " << m_config->testSpec() <<
'\n';
10278 m_stream <<
"# rng-seed: " << m_config->rngSeed() <<
'\n';
10281 void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
10282 m_stream <<
"# No test cases matched '" << unmatchedSpec <<
"'\n";
10285 void TAPReporter::assertionEnded(AssertionStats
const& _assertionStats) {
10288 m_stream <<
"# " << currentTestCaseInfo->name <<
'\n';
10289 TapAssertionPrinter printer(m_stream, _assertionStats,
counter, m_colour.get());
10292 m_stream <<
'\n' << std::flush;
10295 void TAPReporter::testRunEnded(TestRunStats
const& _testRunStats) {
10296 m_stream <<
"1.." << _testRunStats.totals.assertions.total();
10297 if (_testRunStats.totals.testCases.total() == 0) {
10298 m_stream <<
" # Skipped: No tests ran.";
10300 m_stream <<
"\n\n" << std::flush;
10301 StreamingReporterBase::testRunEnded(_testRunStats);
10320 void printHeaderString(std::ostream& os, std::string
const& _string, std::size_t indent = 0) {
10321 std::size_t
i = _string.find(
": ");
10322 if (
i != std::string::npos)
10326 os << TextFlow::Column(_string)
10327 .indent(indent +
i)
10328 .initialIndent(indent) <<
'\n';
10331 std::string escape(StringRef
str) {
10332 std::string escaped =
static_cast<std::string
>(
str);
10344 TeamCityReporter::~TeamCityReporter() {}
10346 void TeamCityReporter::testRunStarting( TestRunInfo
const& runInfo ) {
10347 m_stream <<
"##teamcity[testSuiteStarted name='" << escape( runInfo.name )
10351 void TeamCityReporter::testRunEnded( TestRunStats
const& runStats ) {
10352 m_stream <<
"##teamcity[testSuiteFinished name='"
10353 << escape( runStats.runInfo.name ) <<
"']\n";
10356 void TeamCityReporter::assertionEnded(AssertionStats
const& assertionStats) {
10357 AssertionResult
const&
result = assertionStats.assertionResult;
10359 result.getResultType() == ResultWas::ExplicitSkip ) {
10361 ReusableStringStream
msg;
10362 if (!m_headerPrintedForThisSection)
10363 printSectionHeader(
msg.get());
10364 m_headerPrintedForThisSection =
true;
10368 switch (
result.getResultType()) {
10369 case ResultWas::ExpressionFailed:
10370 msg <<
"expression failed";
10372 case ResultWas::ThrewException:
10373 msg <<
"unexpected exception";
10375 case ResultWas::FatalErrorCondition:
10376 msg <<
"fatal error condition";
10378 case ResultWas::DidntThrowException:
10379 msg <<
"no exception was thrown where one was expected";
10381 case ResultWas::ExplicitFailure:
10382 msg <<
"explicit failure";
10384 case ResultWas::ExplicitSkip:
10385 msg <<
"explicit skip";
10389 case ResultWas::Ok:
10390 case ResultWas::Info:
10391 case ResultWas::Warning:
10392 CATCH_ERROR(
"Internal error in TeamCity reporter");
10394 case ResultWas::Unknown:
10395 case ResultWas::FailureBit:
10396 case ResultWas::Exception:
10397 CATCH_ERROR(
"Not implemented");
10399 if (assertionStats.infoMessages.size() == 1)
10400 msg <<
" with message:";
10401 if (assertionStats.infoMessages.size() > 1)
10402 msg <<
" with messages:";
10403 for (
auto const& messageInfo : assertionStats.infoMessages)
10404 msg <<
"\n \"" << messageInfo.
message <<
'"';
10407 if (
result.hasExpression()) {
10409 "\n " <<
result.getExpressionInMacro() <<
"\n"
10410 "with expansion:\n"
10411 " " <<
result.getExpandedExpression() <<
'\n';
10414 if (
result.getResultType() == ResultWas::ExplicitSkip ) {
10415 m_stream <<
"##teamcity[testIgnored";
10416 }
else if ( currentTestCaseInfo->okToFail() ) {
10417 msg <<
"- failure ignore as test marked as 'ok to fail'\n";
10418 m_stream <<
"##teamcity[testIgnored";
10420 m_stream <<
"##teamcity[testFailed";
10422 m_stream <<
" name='" << escape( currentTestCaseInfo->name ) <<
'\''
10423 <<
" message='" << escape(
msg.str() ) <<
'\'' <<
"]\n";
10428 void TeamCityReporter::testCaseStarting(TestCaseInfo
const& testInfo) {
10429 m_testTimer.start();
10430 StreamingReporterBase::testCaseStarting(testInfo);
10431 m_stream <<
"##teamcity[testStarted name='"
10432 << escape(testInfo.name) <<
"']\n";
10436 void TeamCityReporter::testCaseEnded(TestCaseStats
const& testCaseStats) {
10437 StreamingReporterBase::testCaseEnded(testCaseStats);
10438 auto const& testCaseInfo = *testCaseStats.testInfo;
10439 if (!testCaseStats.stdOut.empty())
10440 m_stream <<
"##teamcity[testStdOut name='"
10441 << escape(testCaseInfo.name)
10442 <<
"' out='" << escape(testCaseStats.stdOut) <<
"']\n";
10443 if (!testCaseStats.stdErr.empty())
10444 m_stream <<
"##teamcity[testStdErr name='"
10445 << escape(testCaseInfo.name)
10446 <<
"' out='" << escape(testCaseStats.stdErr) <<
"']\n";
10447 m_stream <<
"##teamcity[testFinished name='"
10448 << escape(testCaseInfo.name) <<
"' duration='"
10449 << m_testTimer.getElapsedMilliseconds() <<
"']\n";
10453 void TeamCityReporter::printSectionHeader(std::ostream& os) {
10454 assert(!m_sectionStack.empty());
10456 if (m_sectionStack.size() > 1) {
10457 os << lineOfChars(
'-') <<
'\n';
10459 std::vector<SectionInfo>::const_iterator
10460 it = m_sectionStack.begin() + 1,
10461 itEnd = m_sectionStack.end();
10462 for (; it != itEnd; ++it)
10463 printHeaderString(os, it->name);
10464 os << lineOfChars(
'-') <<
'\n';
10467 SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
10469 os << lineInfo <<
'\n';
10470 os << lineOfChars(
'.') <<
"\n\n";
10478#if defined(_MSC_VER)
10479#pragma warning(push)
10480#pragma warning(disable:4061)
10486 XmlReporter::XmlReporter( ReporterConfig&& _config )
10487 : StreamingReporterBase( CATCH_MOVE(_config) ),
10490 m_preferences.shouldRedirectStdOut =
true;
10491 m_preferences.shouldReportAllAssertions =
true;
10494 XmlReporter::~XmlReporter() =
default;
10496 std::string XmlReporter::getDescription() {
10497 return "Reports test results as an XML document";
10500 std::string XmlReporter::getStylesheetRef()
const {
10501 return std::string();
10504 void XmlReporter::writeSourceInfo( SourceLineInfo
const& sourceInfo ) {
10506 .writeAttribute(
"filename"_sr, sourceInfo.file )
10507 .writeAttribute(
"line"_sr, sourceInfo.line );
10510 void XmlReporter::testRunStarting( TestRunInfo
const& testInfo ) {
10511 StreamingReporterBase::testRunStarting( testInfo );
10512 std::string stylesheetRef = getStylesheetRef();
10513 if( !stylesheetRef.empty() )
10514 m_xml.writeStylesheetRef( stylesheetRef );
10515 m_xml.startElement(
"Catch2TestRun")
10516 .writeAttribute(
"name"_sr, m_config->name())
10517 .writeAttribute(
"rng-seed"_sr, m_config->rngSeed())
10518 .writeAttribute(
"xml-format-version"_sr, 3)
10520 if ( m_config->testSpec().hasFilters() ) {
10521 m_xml.writeAttribute(
"filters"_sr, m_config->testSpec() );
10525 void XmlReporter::testCaseStarting( TestCaseInfo
const& testInfo ) {
10526 StreamingReporterBase::testCaseStarting(testInfo);
10527 m_xml.startElement(
"TestCase" )
10528 .writeAttribute(
"name"_sr,
trim( StringRef(testInfo.name) ) )
10529 .writeAttribute(
"tags"_sr, testInfo.tagsAsString() );
10531 writeSourceInfo( testInfo.lineInfo );
10533 if ( m_config->showDurations() == ShowDurations::Always )
10534 m_testCaseTimer.start();
10535 m_xml.ensureTagClosed();
10538 void XmlReporter::sectionStarting( SectionInfo
const& sectionInfo ) {
10539 StreamingReporterBase::sectionStarting( sectionInfo );
10540 if( m_sectionDepth++ > 0 ) {
10541 m_xml.startElement(
"Section" )
10542 .writeAttribute(
"name"_sr,
trim( StringRef(sectionInfo.name) ) );
10543 writeSourceInfo( sectionInfo.lineInfo );
10544 m_xml.ensureTagClosed();
10548 void XmlReporter::assertionStarting( AssertionInfo
const& ) { }
10550 void XmlReporter::assertionEnded( AssertionStats
const& assertionStats ) {
10552 AssertionResult
const&
result = assertionStats.assertionResult;
10554 bool includeResults = m_config->includeSuccessfulResults() || !
result.isOk();
10556 if( includeResults ||
result.getResultType() == ResultWas::Warning ) {
10558 for(
auto const&
msg : assertionStats.infoMessages ) {
10559 if(
msg.type == ResultWas::Info && includeResults ) {
10560 auto t = m_xml.scopedElement(
"Info" );
10561 writeSourceInfo(
msg.lineInfo );
10562 t.writeText(
msg.message );
10563 }
else if (
msg.type == ResultWas::Warning ) {
10564 auto t = m_xml.scopedElement(
"Warning" );
10565 writeSourceInfo(
msg.lineInfo );
10566 t.writeText(
msg.message );
10572 if ( !includeResults &&
result.getResultType() != ResultWas::Warning &&
10573 result.getResultType() != ResultWas::ExplicitSkip ) {
10578 if(
result.hasExpression() ) {
10579 m_xml.startElement(
"Expression" )
10580 .writeAttribute(
"success"_sr,
result.succeeded() )
10581 .writeAttribute(
"type"_sr,
result.getTestMacroName() );
10583 writeSourceInfo(
result.getSourceInfo() );
10585 m_xml.scopedElement(
"Original" )
10586 .writeText(
result.getExpression() );
10587 m_xml.scopedElement(
"Expanded" )
10588 .writeText(
result.getExpandedExpression() );
10592 switch(
result.getResultType() ) {
10593 case ResultWas::ThrewException:
10594 m_xml.startElement(
"Exception" );
10595 writeSourceInfo(
result.getSourceInfo() );
10596 m_xml.writeText(
result.getMessage() );
10597 m_xml.endElement();
10599 case ResultWas::FatalErrorCondition:
10600 m_xml.startElement(
"FatalErrorCondition" );
10601 writeSourceInfo(
result.getSourceInfo() );
10602 m_xml.writeText(
result.getMessage() );
10603 m_xml.endElement();
10605 case ResultWas::Info:
10606 m_xml.scopedElement(
"Info" )
10607 .writeText(
result.getMessage() );
10609 case ResultWas::Warning:
10612 case ResultWas::ExplicitFailure:
10613 m_xml.startElement(
"Failure" );
10614 writeSourceInfo(
result.getSourceInfo() );
10615 m_xml.writeText(
result.getMessage() );
10616 m_xml.endElement();
10618 case ResultWas::ExplicitSkip:
10619 m_xml.startElement(
"Skip" );
10620 writeSourceInfo(
result.getSourceInfo() );
10621 m_xml.writeText(
result.getMessage() );
10622 m_xml.endElement();
10628 if(
result.hasExpression() )
10629 m_xml.endElement();
10632 void XmlReporter::sectionEnded( SectionStats
const& sectionStats ) {
10633 StreamingReporterBase::sectionEnded( sectionStats );
10634 if ( --m_sectionDepth > 0 ) {
10636 XmlWriter::ScopedElement e = m_xml.scopedElement(
"OverallResults" );
10637 e.writeAttribute(
"successes"_sr, sectionStats.assertions.passed );
10638 e.writeAttribute(
"failures"_sr, sectionStats.assertions.failed );
10639 e.writeAttribute(
"expectedFailures"_sr, sectionStats.assertions.failedButOk );
10640 e.writeAttribute(
"skipped"_sr, sectionStats.assertions.skipped > 0 );
10642 if ( m_config->showDurations() == ShowDurations::Always )
10643 e.writeAttribute(
"durationInSeconds"_sr, sectionStats.durationInSeconds );
10646 m_xml.endElement();
10650 void XmlReporter::testCaseEnded( TestCaseStats
const& testCaseStats ) {
10651 StreamingReporterBase::testCaseEnded( testCaseStats );
10652 XmlWriter::ScopedElement e = m_xml.scopedElement(
"OverallResult" );
10653 e.writeAttribute(
"success"_sr, testCaseStats.totals.assertions.allOk() );
10654 e.writeAttribute(
"skips"_sr, testCaseStats.totals.assertions.skipped );
10656 if ( m_config->showDurations() == ShowDurations::Always )
10657 e.writeAttribute(
"durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() );
10658 if( !testCaseStats.stdOut.empty() )
10659 m_xml.scopedElement(
"StdOut" ).writeText(
trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline );
10660 if( !testCaseStats.stdErr.empty() )
10661 m_xml.scopedElement(
"StdErr" ).writeText(
trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline );
10663 m_xml.endElement();
10666 void XmlReporter::testRunEnded( TestRunStats
const& testRunStats ) {
10667 StreamingReporterBase::testRunEnded( testRunStats );
10668 m_xml.scopedElement(
"OverallResults" )
10669 .writeAttribute(
"successes"_sr, testRunStats.totals.assertions.passed )
10670 .writeAttribute(
"failures"_sr, testRunStats.totals.assertions.failed )
10671 .writeAttribute(
"expectedFailures"_sr, testRunStats.totals.assertions.failedButOk )
10672 .writeAttribute(
"skips"_sr, testRunStats.totals.assertions.skipped );
10673 m_xml.scopedElement(
"OverallResultsCases")
10674 .writeAttribute(
"successes"_sr, testRunStats.totals.testCases.passed )
10675 .writeAttribute(
"failures"_sr, testRunStats.totals.testCases.failed )
10676 .writeAttribute(
"expectedFailures"_sr, testRunStats.totals.testCases.failedButOk )
10677 .writeAttribute(
"skips"_sr, testRunStats.totals.testCases.skipped );
10678 m_xml.endElement();
10681 void XmlReporter::benchmarkPreparing( StringRef
name ) {
10682 m_xml.startElement(
"BenchmarkResults")
10683 .writeAttribute(
"name"_sr,
name);
10686 void XmlReporter::benchmarkStarting(BenchmarkInfo
const &info) {
10687 m_xml.writeAttribute(
"samples"_sr, info.samples)
10688 .writeAttribute(
"resamples"_sr, info.resamples)
10689 .writeAttribute(
"iterations"_sr, info.iterations)
10690 .writeAttribute(
"clockResolution"_sr, info.clockResolution)
10691 .writeAttribute(
"estimatedDuration"_sr, info.estimatedDuration)
10692 .writeComment(
"All values in nano seconds"_sr);
10695 void XmlReporter::benchmarkEnded(BenchmarkStats<>
const& benchmarkStats) {
10696 m_xml.startElement(
"mean")
10697 .writeAttribute(
"value"_sr, benchmarkStats.mean.point.count())
10698 .writeAttribute(
"lowerBound"_sr, benchmarkStats.mean.lower_bound.count())
10699 .writeAttribute(
"upperBound"_sr, benchmarkStats.mean.upper_bound.count())
10700 .writeAttribute(
"ci"_sr, benchmarkStats.mean.confidence_interval);
10701 m_xml.endElement();
10702 m_xml.startElement(
"standardDeviation")
10703 .writeAttribute(
"value"_sr, benchmarkStats.standardDeviation.point.count())
10704 .writeAttribute(
"lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count())
10705 .writeAttribute(
"upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count())
10706 .writeAttribute(
"ci"_sr, benchmarkStats.standardDeviation.confidence_interval);
10707 m_xml.endElement();
10708 m_xml.startElement(
"outliers")
10709 .writeAttribute(
"variance"_sr, benchmarkStats.outlierVariance)
10710 .writeAttribute(
"lowMild"_sr, benchmarkStats.outliers.low_mild)
10711 .writeAttribute(
"lowSevere"_sr, benchmarkStats.outliers.low_severe)
10712 .writeAttribute(
"highMild"_sr, benchmarkStats.outliers.high_mild)
10713 .writeAttribute(
"highSevere"_sr, benchmarkStats.outliers.high_severe);
10714 m_xml.endElement();
10715 m_xml.endElement();
10718 void XmlReporter::benchmarkFailed(StringRef error) {
10719 m_xml.scopedElement(
"failed").
10720 writeAttribute(
"message"_sr, error);
10721 m_xml.endElement();
10724 void XmlReporter::listReporters(std::vector<ReporterDescription>
const& descriptions) {
10725 auto outerTag = m_xml.scopedElement(
"AvailableReporters");
10726 for (
auto const& reporter : descriptions) {
10727 auto inner = m_xml.scopedElement(
"Reporter");
10728 m_xml.startElement(
"Name", XmlFormatting::Indent)
10729 .writeText(reporter.name, XmlFormatting::None)
10730 .endElement(XmlFormatting::Newline);
10731 m_xml.startElement(
"Description", XmlFormatting::Indent)
10732 .writeText(reporter.description, XmlFormatting::None)
10733 .endElement(XmlFormatting::Newline);
10737 void XmlReporter::listListeners(std::vector<ListenerDescription>
const& descriptions) {
10738 auto outerTag = m_xml.scopedElement(
"RegisteredListeners" );
10739 for (
auto const& listener : descriptions ) {
10740 auto inner = m_xml.scopedElement(
"Listener" );
10741 m_xml.startElement(
"Name", XmlFormatting::Indent )
10742 .writeText( listener.name, XmlFormatting::None )
10743 .endElement( XmlFormatting::Newline );
10744 m_xml.startElement(
"Description", XmlFormatting::Indent )
10745 .writeText( listener.description, XmlFormatting::None )
10746 .endElement( XmlFormatting::Newline );
10750 void XmlReporter::listTests(std::vector<TestCaseHandle>
const& tests) {
10751 auto outerTag = m_xml.scopedElement(
"MatchingTests");
10752 for (
auto const&
test : tests) {
10753 auto innerTag = m_xml.scopedElement(
"TestCase");
10754 auto const& testInfo =
test.getTestCaseInfo();
10755 m_xml.startElement(
"Name", XmlFormatting::Indent)
10756 .writeText(testInfo.name, XmlFormatting::None)
10757 .endElement(XmlFormatting::Newline);
10758 m_xml.startElement(
"ClassName", XmlFormatting::Indent)
10759 .writeText(testInfo.className, XmlFormatting::None)
10760 .endElement(XmlFormatting::Newline);
10761 m_xml.startElement(
"Tags", XmlFormatting::Indent)
10762 .writeText(testInfo.tagsAsString(), XmlFormatting::None)
10763 .endElement(XmlFormatting::Newline);
10765 auto sourceTag = m_xml.scopedElement(
"SourceInfo");
10766 m_xml.startElement(
"File", XmlFormatting::Indent)
10767 .writeText(testInfo.lineInfo.file, XmlFormatting::None)
10768 .endElement(XmlFormatting::Newline);
10769 m_xml.startElement(
"Line", XmlFormatting::Indent)
10770 .writeText(
std::to_string(testInfo.lineInfo.line), XmlFormatting::None)
10771 .endElement(XmlFormatting::Newline);
10775 void XmlReporter::listTags(std::vector<TagInfo>
const& tags) {
10776 auto outerTag = m_xml.scopedElement(
"TagsFromMatchingTests");
10777 for (
auto const& tag : tags) {
10778 auto innerTag = m_xml.scopedElement(
"Tag");
10779 m_xml.startElement(
"Count", XmlFormatting::Indent)
10781 .endElement(XmlFormatting::Newline);
10782 auto aliasTag = m_xml.scopedElement(
"Aliases");
10783 for (
auto const& alias : tag.spellings) {
10784 m_xml.startElement(
"Alias", XmlFormatting::Indent)
10785 .writeText(alias, XmlFormatting::None)
10786 .endElement(XmlFormatting::Newline);
10793#if defined(_MSC_VER)
10794#pragma warning(pop)
std::ostream & operator<<(std::ostream &os, const o2::math_utils::Rotation2Df_t &t)
o2::monitoring::Verbosity Verbosity
std::chrono::duration< double, std::ratio< 1, 1 > > Duration
GeneratorBasePtr m_generator
Justification justification
std::string shardFilePath
Class for time synchronization of RawReader instances.
float sum(float s, o2::dcs::DataPointValue v)
bool match(const std::vector< std::string > &queries, const char *pattern)
GLsizei const GLchar *const * string
GLuint const GLchar * name
GLboolean GLboolean GLboolean b
GLsizei GLsizei GLchar * source
GLsizei const GLfloat * value
GLint GLint GLsizei GLint GLenum GLenum type
GLenum GLsizei GLsizei GLint * values
GLuint GLsizei GLsizei * length
GLuint GLsizei const GLchar * label
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLuint GLsizei const GLchar * message
GLboolean GLboolean GLboolean GLboolean a
GLubyte GLubyte GLubyte GLubyte w
GLenum GLint GLint * precision
OutlierClassification classify_outliers(std::vector< double >::const_iterator first, std::vector< double >::const_iterator last)
bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector< double >::iterator first, std::vector< double >::iterator last)
double erfc_inv(double x)
bool directCompare(double lhs, double rhs)
void throw_optimized_away_error()
double normal_quantile(double p)
double mean(std::vector< double >::const_iterator first, std::vector< double >::const_iterator last)
double weighted_average_quantile(int k, int q, std::vector< double >::iterator first, std::vector< double >::iterator last)
ParserResult convertInto(std::string const &source, std::string &target)
void registerTranslatorImpl(Detail::unique_ptr< IExceptionTranslator > &&translator)
Optional< ColourMode > stringToColourMode(StringRef colourMode)
std::vector< std::string > splitReporterSpec(StringRef reporterSpec)
char const * getEnv(char const *varName)
uint32_t convertToBits(float f)
void throw_generator_exception(char const *msg)
IGeneratorTracker * createGeneratorTracker(StringRef generatorName, SourceLineInfo lineInfo, GeneratorBasePtr &&generator)
IGeneratorTracker * acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo)
std::string describe_multi_matcher(StringRef combine, std::string const *descriptions_begin, std::string const *descriptions_end)
NoneTrueMatcher NoneTrue()
EndsWithMatcher EndsWith(std::string const &str, CaseSensitive caseSensitivity)
StringContainsMatcher ContainsSubstring(std::string const &str, CaseSensitive caseSensitivity)
ExceptionMessageMatcher Message(std::string const &message)
HasSizeMatcher SizeIs(std::size_t sz)
StringEqualsMatcher Equals(std::string const &str, CaseSensitive caseSensitivity)
StartsWithMatcher StartsWith(std::string const &str, CaseSensitive caseSensitivity)
RegexMatcher Matches(std::string const ®ex, CaseSensitive caseSensitivity)
Column Spacer(size_t spaceWidth)
std::ostream & operator<<(std::ostream &os, Column const &col)
void toLowerInPlace(std::string &s)
std::string trim(std::string const &str)
bool isThrowSafe(TestCaseHandle const &testCase, IConfig const &config)
void seedRng(IConfig const &config)
std::uint32_t generateRandomSeed(GenerateFrom from)
auto operator+=(std::string &lhs, StringRef rhs) -> std::string &
void throw_test_failure_exception()
void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str)
bool operator==(ProcessedReporterSpec const &lhs, ProcessedReporterSpec const &rhs)
Version const & libraryVersion()
Optional< unsigned int > parseUInt(std::string const &input, int base)
auto makeStream(std::string const &filename) -> Detail::unique_ptr< IStream >
Detail::unique_ptr< ITestInvoker > makeTestInvoker(void(*testAsFunction)())
bool uncaught_exceptions()
bool isOk(ResultWas::OfType resultType)
bool isJustInfo(int flags)
IMutableRegistryHub & getMutableRegistryHub()
IRegistryHub const & getRegistryHub()
XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs)
std::vector< StringRef > splitStringRef(StringRef str, char delimiter)
bool startsWith(std::string const &s, std::string const &prefix)
void addSingleton(ISingleton *singleton)
void throw_test_skip_exception()
bool shouldContinueOnFailure(int flags)
Clara::Parser makeCommandLineParser(ConfigData &config)
bool contains(std::string const &s, std::string const &infix)
std::ostream & operator<<(std::ostream &os, Version const &version)
std::vector< TestCaseHandle > const & getAllTestCasesSorted(IConfig const &config)
ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs)
bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs)
bool shouldSuppressFailure(int flags)
std::string translateActiveException()
std::string toLower(std::string const &s)
std::vector< TestCaseHandle > sortTests(IConfig const &config, std::vector< TestCaseHandle > const &unsortedTestCases)
bool endsWith(std::string const &s, std::string const &suffix)
std::string operator+(StringRef lhs, StringRef rhs)
Optional< ReporterSpec > parseReporterSpec(StringRef reporterSpec)
std::vector< TestCaseHandle > filterTests(std::vector< TestCaseHandle > const &testCases, TestSpec const &testSpec, IConfig const &config)
IResultCapture & getResultCapture()
Singleton< RegistryHub, IRegistryHub, IMutableRegistryHub > RegistryHubSingleton
bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis)
uint8_t itsSharedClusterMap uint8_t
long getCurrentTimestamp()
returns the timestamp in long corresponding to "now"
bool operator<(EntryPM const &entryPM1, EntryPM const &entryPM2)
Defining PrimaryVertex explicitly as messageable.
@ Timer
A message which is created whenever a Timer expires.
D const SVectorGPU< T, D > & rhs
if(!okForPhiMin(phi0, phi1))
Polygon< T > close(Polygon< T > polygon)
ColumnData & operator|=(ColumnData &col1, const ColumnData &col2)
std::vector< T, o2::pmr::polymorphic_allocator< T > > vector
@ Auto
Automatically decide based on DCS settings in CCDB.
Enum< T >::Iterator begin(Enum< T >)
bool endsWith(const std::string &str, const std::string &suffix)
Defining DataPointCompositeObject explicitly as copiable.
std::string to_string(gsl::span< T, Size > span)
FIXME: do not use data model tables.
const char * what() const noexcept override
std::vector< Detail::unique_ptr< EventListenerFactory > > listeners
std::map< std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess > factories
std::vector< Detail::unique_ptr< std::ostringstream > > m_streams
auto add() -> std::size_t
std::ostringstream m_referenceStream
std::vector< std::size_t > m_unused
void release(std::size_t index)
VectorOfTObjectPtrs other
std::array< uint16_t, 5 > pattern
std::vector< ReadoutWindowData > rows
char const *restrict const cmp
uint64_t const void const *restrict const msg