The open source OpenXR runtime
1 2// Copyright Catch2 Authors 3// Distributed under the Boost Software License, Version 1.0. 4// (See accompanying file LICENSE.txt or copy at 5// https://www.boost.org/LICENSE_1_0.txt) 6 7// SPDX-License-Identifier: BSL-1.0 8 9// Catch v3.6.0 10// Generated: 2024-05-05 20:53:27.562886 11// ---------------------------------------------------------- 12// This file is an amalgamation of multiple different files. 13// You probably shouldn't edit it directly. 14// ---------------------------------------------------------- 15 16#include "catch_amalgamated.hpp" 17 18 19#ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED 20#define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED 21 22 23#if defined(CATCH_PLATFORM_WINDOWS) 24 25// We might end up with the define made globally through the compiler, 26// and we don't want to trigger warnings for this 27#if !defined(NOMINMAX) 28# define NOMINMAX 29#endif 30#if !defined(WIN32_LEAN_AND_MEAN) 31# define WIN32_LEAN_AND_MEAN 32#endif 33 34#include <windows.h> 35 36#endif // defined(CATCH_PLATFORM_WINDOWS) 37 38#endif // CATCH_WINDOWS_H_PROXY_HPP_INCLUDED 39 40 41 42 43namespace Catch { 44 namespace Benchmark { 45 namespace Detail { 46 ChronometerConcept::~ChronometerConcept() = default; 47 } // namespace Detail 48 } // namespace Benchmark 49} // namespace Catch 50 51 52// Adapted from donated nonius code. 53 54 55#include <vector> 56 57namespace Catch { 58 namespace Benchmark { 59 namespace Detail { 60 SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) { 61 if (!cfg.benchmarkNoAnalysis()) { 62 std::vector<double> samples; 63 samples.reserve(static_cast<size_t>(last - first)); 64 for (auto current = first; current != last; ++current) { 65 samples.push_back( current->count() ); 66 } 67 68 auto analysis = Catch::Benchmark::Detail::analyse_samples( 69 cfg.benchmarkConfidenceInterval(), 70 cfg.benchmarkResamples(), 71 samples.data(), 72 samples.data() + samples.size() ); 73 auto outliers = Catch::Benchmark::Detail::classify_outliers( 74 samples.data(), samples.data() + samples.size() ); 75 76 auto wrap_estimate = [](Estimate<double> e) { 77 return Estimate<FDuration> { 78 FDuration(e.point), 79 FDuration(e.lower_bound), 80 FDuration(e.upper_bound), 81 e.confidence_interval, 82 }; 83 }; 84 std::vector<FDuration> samples2; 85 samples2.reserve(samples.size()); 86 for (auto s : samples) { 87 samples2.push_back( FDuration( s ) ); 88 } 89 90 return { 91 CATCH_MOVE(samples2), 92 wrap_estimate(analysis.mean), 93 wrap_estimate(analysis.standard_deviation), 94 outliers, 95 analysis.outlier_variance, 96 }; 97 } else { 98 std::vector<FDuration> samples; 99 samples.reserve(static_cast<size_t>(last - first)); 100 101 FDuration mean = FDuration(0); 102 int i = 0; 103 for (auto it = first; it < last; ++it, ++i) { 104 samples.push_back(*it); 105 mean += *it; 106 } 107 mean /= i; 108 109 return SampleAnalysis{ 110 CATCH_MOVE(samples), 111 Estimate<FDuration>{ mean, mean, mean, 0.0 }, 112 Estimate<FDuration>{ FDuration( 0 ), 113 FDuration( 0 ), 114 FDuration( 0 ), 115 0.0 }, 116 OutlierClassification{}, 117 0.0 118 }; 119 } 120 } 121 } // namespace Detail 122 } // namespace Benchmark 123} // namespace Catch 124 125 126 127 128namespace Catch { 129 namespace Benchmark { 130 namespace Detail { 131 BenchmarkFunction::callable::~callable() = default; 132 } // namespace Detail 133 } // namespace Benchmark 134} // namespace Catch 135 136 137 138 139#include <exception> 140 141namespace Catch { 142 namespace Benchmark { 143 namespace Detail { 144 struct optimized_away_error : std::exception { 145 const char* what() const noexcept override; 146 }; 147 148 const char* optimized_away_error::what() const noexcept { 149 return "could not measure benchmark, maybe it was optimized away"; 150 } 151 152 void throw_optimized_away_error() { 153 Catch::throw_exception(optimized_away_error{}); 154 } 155 156 } // namespace Detail 157 } // namespace Benchmark 158} // namespace Catch 159 160 161// Adapted from donated nonius code. 162 163 164 165#include <algorithm> 166#include <cassert> 167#include <cmath> 168#include <cstddef> 169#include <numeric> 170#include <random> 171 172 173#if defined(CATCH_CONFIG_USE_ASYNC) 174#include <future> 175#endif 176 177namespace Catch { 178 namespace Benchmark { 179 namespace Detail { 180 namespace { 181 182 template <typename URng, typename Estimator> 183 static sample 184 resample( URng& rng, 185 unsigned int resamples, 186 double const* first, 187 double const* last, 188 Estimator& estimator ) { 189 auto n = static_cast<size_t>( last - first ); 190 Catch::uniform_integer_distribution<size_t> dist( 0, n - 1 ); 191 192 sample out; 193 out.reserve( resamples ); 194 std::vector<double> resampled; 195 resampled.reserve( n ); 196 for ( size_t i = 0; i < resamples; ++i ) { 197 resampled.clear(); 198 for ( size_t s = 0; s < n; ++s ) { 199 resampled.push_back( first[dist( rng )] ); 200 } 201 const auto estimate = 202 estimator( resampled.data(), resampled.data() + resampled.size() ); 203 out.push_back( estimate ); 204 } 205 std::sort( out.begin(), out.end() ); 206 return out; 207 } 208 209 static double outlier_variance( Estimate<double> mean, 210 Estimate<double> stddev, 211 int n ) { 212 double sb = stddev.point; 213 double mn = mean.point / n; 214 double mg_min = mn / 2.; 215 double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) ); 216 double sg2 = sg * sg; 217 double sb2 = sb * sb; 218 219 auto c_max = [n, mn, sb2, sg2]( double x ) -> double { 220 double k = mn - x; 221 double d = k * k; 222 double nd = n * d; 223 double k0 = -n * nd; 224 double k1 = sb2 - n * sg2 + nd; 225 double det = k1 * k1 - 4 * sg2 * k0; 226 return static_cast<int>( -2. * k0 / 227 ( k1 + std::sqrt( det ) ) ); 228 }; 229 230 auto var_out = [n, sb2, sg2]( double c ) { 231 double nc = n - c; 232 return ( nc / n ) * ( sb2 - nc * sg2 ); 233 }; 234 235 return (std::min)( var_out( 1 ), 236 var_out( 237 (std::min)( c_max( 0. ), 238 c_max( mg_min ) ) ) ) / 239 sb2; 240 } 241 242 static double erf_inv( double x ) { 243 // Code accompanying the article "Approximating the erfinv 244 // function" in GPU Computing Gems, Volume 2 245 double w, p; 246 247 w = -log( ( 1.0 - x ) * ( 1.0 + x ) ); 248 249 if ( w < 6.250000 ) { 250 w = w - 3.125000; 251 p = -3.6444120640178196996e-21; 252 p = -1.685059138182016589e-19 + p * w; 253 p = 1.2858480715256400167e-18 + p * w; 254 p = 1.115787767802518096e-17 + p * w; 255 p = -1.333171662854620906e-16 + p * w; 256 p = 2.0972767875968561637e-17 + p * w; 257 p = 6.6376381343583238325e-15 + p * w; 258 p = -4.0545662729752068639e-14 + p * w; 259 p = -8.1519341976054721522e-14 + p * w; 260 p = 2.6335093153082322977e-12 + p * w; 261 p = -1.2975133253453532498e-11 + p * w; 262 p = -5.4154120542946279317e-11 + p * w; 263 p = 1.051212273321532285e-09 + p * w; 264 p = -4.1126339803469836976e-09 + p * w; 265 p = -2.9070369957882005086e-08 + p * w; 266 p = 4.2347877827932403518e-07 + p * w; 267 p = -1.3654692000834678645e-06 + p * w; 268 p = -1.3882523362786468719e-05 + p * w; 269 p = 0.0001867342080340571352 + p * w; 270 p = -0.00074070253416626697512 + p * w; 271 p = -0.0060336708714301490533 + p * w; 272 p = 0.24015818242558961693 + p * w; 273 p = 1.6536545626831027356 + p * w; 274 } else if ( w < 16.000000 ) { 275 w = sqrt( w ) - 3.250000; 276 p = 2.2137376921775787049e-09; 277 p = 9.0756561938885390979e-08 + p * w; 278 p = -2.7517406297064545428e-07 + p * w; 279 p = 1.8239629214389227755e-08 + p * w; 280 p = 1.5027403968909827627e-06 + p * w; 281 p = -4.013867526981545969e-06 + p * w; 282 p = 2.9234449089955446044e-06 + p * w; 283 p = 1.2475304481671778723e-05 + p * w; 284 p = -4.7318229009055733981e-05 + p * w; 285 p = 6.8284851459573175448e-05 + p * w; 286 p = 2.4031110387097893999e-05 + p * w; 287 p = -0.0003550375203628474796 + p * w; 288 p = 0.00095328937973738049703 + p * w; 289 p = -0.0016882755560235047313 + p * w; 290 p = 0.0024914420961078508066 + p * w; 291 p = -0.0037512085075692412107 + p * w; 292 p = 0.005370914553590063617 + p * w; 293 p = 1.0052589676941592334 + p * w; 294 p = 3.0838856104922207635 + p * w; 295 } else { 296 w = sqrt( w ) - 5.000000; 297 p = -2.7109920616438573243e-11; 298 p = -2.5556418169965252055e-10 + p * w; 299 p = 1.5076572693500548083e-09 + p * w; 300 p = -3.7894654401267369937e-09 + p * w; 301 p = 7.6157012080783393804e-09 + p * w; 302 p = -1.4960026627149240478e-08 + p * w; 303 p = 2.9147953450901080826e-08 + p * w; 304 p = -6.7711997758452339498e-08 + p * w; 305 p = 2.2900482228026654717e-07 + p * w; 306 p = -9.9298272942317002539e-07 + p * w; 307 p = 4.5260625972231537039e-06 + p * w; 308 p = -1.9681778105531670567e-05 + p * w; 309 p = 7.5995277030017761139e-05 + p * w; 310 p = -0.00021503011930044477347 + p * w; 311 p = -0.00013871931833623122026 + p * w; 312 p = 1.0103004648645343977 + p * w; 313 p = 4.8499064014085844221 + p * w; 314 } 315 return p * x; 316 } 317 318 static double 319 standard_deviation( double const* first, double const* last ) { 320 auto m = Catch::Benchmark::Detail::mean( first, last ); 321 double variance = 322 std::accumulate( first, 323 last, 324 0., 325 [m]( double a, double b ) { 326 double diff = b - m; 327 return a + diff * diff; 328 } ) / 329 ( last - first ); 330 return std::sqrt( variance ); 331 } 332 333 static sample jackknife( double ( *estimator )( double const*, 334 double const* ), 335 double* first, 336 double* last ) { 337 const auto second = first + 1; 338 sample results; 339 results.reserve( static_cast<size_t>( last - first ) ); 340 341 for ( auto it = first; it != last; ++it ) { 342 std::iter_swap( it, first ); 343 results.push_back( estimator( second, last ) ); 344 } 345 346 return results; 347 } 348 349 350 } // namespace 351 } // namespace Detail 352 } // namespace Benchmark 353} // namespace Catch 354 355namespace Catch { 356 namespace Benchmark { 357 namespace Detail { 358 359 double weighted_average_quantile( int k, 360 int q, 361 double* first, 362 double* last ) { 363 auto count = last - first; 364 double idx = (count - 1) * k / static_cast<double>(q); 365 int j = static_cast<int>(idx); 366 double g = idx - j; 367 std::nth_element(first, first + j, last); 368 auto xj = first[j]; 369 if ( Catch::Detail::directCompare( g, 0 ) ) { 370 return xj; 371 } 372 373 auto xj1 = *std::min_element(first + (j + 1), last); 374 return xj + g * (xj1 - xj); 375 } 376 377 OutlierClassification 378 classify_outliers( double const* first, double const* last ) { 379 std::vector<double> copy( first, last ); 380 381 auto q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() ); 382 auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() ); 383 auto iqr = q3 - q1; 384 auto los = q1 - ( iqr * 3. ); 385 auto lom = q1 - ( iqr * 1.5 ); 386 auto him = q3 + ( iqr * 1.5 ); 387 auto his = q3 + ( iqr * 3. ); 388 389 OutlierClassification o; 390 for ( ; first != last; ++first ) { 391 const double t = *first; 392 if ( t < los ) { 393 ++o.low_severe; 394 } else if ( t < lom ) { 395 ++o.low_mild; 396 } else if ( t > his ) { 397 ++o.high_severe; 398 } else if ( t > him ) { 399 ++o.high_mild; 400 } 401 ++o.samples_seen; 402 } 403 return o; 404 } 405 406 double mean( double const* first, double const* last ) { 407 auto count = last - first; 408 double sum = 0.; 409 while (first != last) { 410 sum += *first; 411 ++first; 412 } 413 return sum / static_cast<double>(count); 414 } 415 416 double normal_cdf( double x ) { 417 return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0; 418 } 419 420 double erfc_inv(double x) { 421 return erf_inv(1.0 - x); 422 } 423 424 double normal_quantile(double p) { 425 static const double ROOT_TWO = std::sqrt(2.0); 426 427 double result = 0.0; 428 assert(p >= 0 && p <= 1); 429 if (p < 0 || p > 1) { 430 return result; 431 } 432 433 result = -erfc_inv(2.0 * p); 434 // result *= normal distribution standard deviation (1.0) * sqrt(2) 435 result *= /*sd * */ ROOT_TWO; 436 // result += normal disttribution mean (0) 437 return result; 438 } 439 440 Estimate<double> 441 bootstrap( double confidence_level, 442 double* first, 443 double* last, 444 sample const& resample, 445 double ( *estimator )( double const*, double const* ) ) { 446 auto n_samples = last - first; 447 448 double point = estimator( first, last ); 449 // Degenerate case with a single sample 450 if ( n_samples == 1 ) 451 return { point, point, point, confidence_level }; 452 453 sample jack = jackknife( estimator, first, last ); 454 double jack_mean = 455 mean( jack.data(), jack.data() + jack.size() ); 456 double sum_squares = 0, sum_cubes = 0; 457 for ( double x : jack ) { 458 auto difference = jack_mean - x; 459 auto square = difference * difference; 460 auto cube = square * difference; 461 sum_squares += square; 462 sum_cubes += cube; 463 } 464 465 double accel = sum_cubes / ( 6 * std::pow( sum_squares, 1.5 ) ); 466 long n = static_cast<long>( resample.size() ); 467 double prob_n = 468 std::count_if( resample.begin(), 469 resample.end(), 470 [point]( double x ) { return x < point; } ) / 471 static_cast<double>( n ); 472 // degenerate case with uniform samples 473 if ( Catch::Detail::directCompare( prob_n, 0. ) ) { 474 return { point, point, point, confidence_level }; 475 } 476 477 double bias = normal_quantile( prob_n ); 478 double z1 = normal_quantile( ( 1. - confidence_level ) / 2. ); 479 480 auto cumn = [n]( double x ) -> long { 481 return std::lround( normal_cdf( x ) * 482 static_cast<double>( n ) ); 483 }; 484 auto a = [bias, accel]( double b ) { 485 return bias + b / ( 1. - accel * b ); 486 }; 487 double b1 = bias + z1; 488 double b2 = bias - z1; 489 double a1 = a( b1 ); 490 double a2 = a( b2 ); 491 auto lo = static_cast<size_t>( (std::max)( cumn( a1 ), 0l ) ); 492 auto hi = 493 static_cast<size_t>( (std::min)( cumn( a2 ), n - 1 ) ); 494 495 return { point, resample[lo], resample[hi], confidence_level }; 496 } 497 498 bootstrap_analysis analyse_samples(double confidence_level, 499 unsigned int n_resamples, 500 double* first, 501 double* last) { 502 auto mean = &Detail::mean; 503 auto stddev = &standard_deviation; 504 505#if defined(CATCH_CONFIG_USE_ASYNC) 506 auto Estimate = [=](double(*f)(double const*, double const*)) { 507 std::random_device rd; 508 auto seed = rd(); 509 return std::async(std::launch::async, [=] { 510 SimplePcg32 rng( seed ); 511 auto resampled = resample(rng, n_resamples, first, last, f); 512 return bootstrap(confidence_level, first, last, resampled, f); 513 }); 514 }; 515 516 auto mean_future = Estimate(mean); 517 auto stddev_future = Estimate(stddev); 518 519 auto mean_estimate = mean_future.get(); 520 auto stddev_estimate = stddev_future.get(); 521#else 522 auto Estimate = [=](double(*f)(double const* , double const*)) { 523 std::random_device rd; 524 auto seed = rd(); 525 SimplePcg32 rng( seed ); 526 auto resampled = resample(rng, n_resamples, first, last, f); 527 return bootstrap(confidence_level, first, last, resampled, f); 528 }; 529 530 auto mean_estimate = Estimate(mean); 531 auto stddev_estimate = Estimate(stddev); 532#endif // CATCH_USE_ASYNC 533 534 auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++ 535 double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); 536 537 return { mean_estimate, stddev_estimate, outlier_variance }; 538 } 539 } // namespace Detail 540 } // namespace Benchmark 541} // namespace Catch 542 543 544 545#include <cmath> 546#include <limits> 547 548namespace { 549 550// Performs equivalent check of std::fabs(lhs - rhs) <= margin 551// But without the subtraction to allow for INFINITY in comparison 552bool marginComparison(double lhs, double rhs, double margin) { 553 return (lhs + margin >= rhs) && (rhs + margin >= lhs); 554} 555 556} 557 558namespace Catch { 559 560 Approx::Approx ( double value ) 561 : m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ), 562 m_margin( 0.0 ), 563 m_scale( 0.0 ), 564 m_value( value ) 565 {} 566 567 Approx Approx::custom() { 568 return Approx( 0 ); 569 } 570 571 Approx Approx::operator-() const { 572 auto temp(*this); 573 temp.m_value = -temp.m_value; 574 return temp; 575 } 576 577 578 std::string Approx::toString() const { 579 ReusableStringStream rss; 580 rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; 581 return rss.str(); 582 } 583 584 bool Approx::equalityComparisonImpl(const double other) const { 585 // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value 586 // Thanks to Richard Harris for his help refining the scaled margin value 587 return marginComparison(m_value, other, m_margin) 588 || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); 589 } 590 591 void Approx::setMargin(double newMargin) { 592 CATCH_ENFORCE(newMargin >= 0, 593 "Invalid Approx::margin: " << newMargin << '.' 594 << " Approx::Margin has to be non-negative."); 595 m_margin = newMargin; 596 } 597 598 void Approx::setEpsilon(double newEpsilon) { 599 CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, 600 "Invalid Approx::epsilon: " << newEpsilon << '.' 601 << " Approx::epsilon has to be in [0, 1]"); 602 m_epsilon = newEpsilon; 603 } 604 605namespace literals { 606 Approx operator ""_a(long double val) { 607 return Approx(val); 608 } 609 Approx operator ""_a(unsigned long long val) { 610 return Approx(val); 611 } 612} // end namespace literals 613 614std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) { 615 return value.toString(); 616} 617 618} // end namespace Catch 619 620 621 622namespace Catch { 623 624 AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): 625 lazyExpression(_lazyExpression), 626 resultType(_resultType) {} 627 628 std::string AssertionResultData::reconstructExpression() const { 629 630 if( reconstructedExpression.empty() ) { 631 if( lazyExpression ) { 632 ReusableStringStream rss; 633 rss << lazyExpression; 634 reconstructedExpression = rss.str(); 635 } 636 } 637 return reconstructedExpression; 638 } 639 640 AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data ) 641 : m_info( info ), 642 m_resultData( CATCH_MOVE(data) ) 643 {} 644 645 // Result was a success 646 bool AssertionResult::succeeded() const { 647 return Catch::isOk( m_resultData.resultType ); 648 } 649 650 // Result was a success, or failure is suppressed 651 bool AssertionResult::isOk() const { 652 return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); 653 } 654 655 ResultWas::OfType AssertionResult::getResultType() const { 656 return m_resultData.resultType; 657 } 658 659 bool AssertionResult::hasExpression() const { 660 return !m_info.capturedExpression.empty(); 661 } 662 663 bool AssertionResult::hasMessage() const { 664 return !m_resultData.message.empty(); 665 } 666 667 std::string AssertionResult::getExpression() const { 668 // Possibly overallocating by 3 characters should be basically free 669 std::string expr; expr.reserve(m_info.capturedExpression.size() + 3); 670 if (isFalseTest(m_info.resultDisposition)) { 671 expr += "!("; 672 } 673 expr += m_info.capturedExpression; 674 if (isFalseTest(m_info.resultDisposition)) { 675 expr += ')'; 676 } 677 return expr; 678 } 679 680 std::string AssertionResult::getExpressionInMacro() const { 681 if ( m_info.macroName.empty() ) { 682 return static_cast<std::string>( m_info.capturedExpression ); 683 } 684 std::string expr; 685 expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); 686 expr += m_info.macroName; 687 expr += "( "; 688 expr += m_info.capturedExpression; 689 expr += " )"; 690 return expr; 691 } 692 693 bool AssertionResult::hasExpandedExpression() const { 694 return hasExpression() && getExpandedExpression() != getExpression(); 695 } 696 697 std::string AssertionResult::getExpandedExpression() const { 698 std::string expr = m_resultData.reconstructExpression(); 699 return expr.empty() 700 ? getExpression() 701 : expr; 702 } 703 704 StringRef AssertionResult::getMessage() const { 705 return m_resultData.message; 706 } 707 SourceLineInfo AssertionResult::getSourceInfo() const { 708 return m_info.lineInfo; 709 } 710 711 StringRef AssertionResult::getTestMacroName() const { 712 return m_info.macroName; 713 } 714 715} // end namespace Catch 716 717 718 719#include <fstream> 720 721namespace Catch { 722 723 namespace { 724 static bool enableBazelEnvSupport() { 725#if defined( CATCH_CONFIG_BAZEL_SUPPORT ) 726 return true; 727#else 728 return Detail::getEnv( "BAZEL_TEST" ) != nullptr; 729#endif 730 } 731 732 struct bazelShardingOptions { 733 unsigned int shardIndex, shardCount; 734 std::string shardFilePath; 735 }; 736 737 static Optional<bazelShardingOptions> readBazelShardingOptions() { 738 const auto bazelShardIndex = Detail::getEnv( "TEST_SHARD_INDEX" ); 739 const auto bazelShardTotal = Detail::getEnv( "TEST_TOTAL_SHARDS" ); 740 const auto bazelShardInfoFile = Detail::getEnv( "TEST_SHARD_STATUS_FILE" ); 741 742 743 const bool has_all = 744 bazelShardIndex && bazelShardTotal && bazelShardInfoFile; 745 if ( !has_all ) { 746 // We provide nice warning message if the input is 747 // misconfigured. 748 auto warn = []( const char* env_var ) { 749 Catch::cerr() 750 << "Warning: Bazel shard configuration is missing '" 751 << env_var << "'. Shard configuration is skipped.\n"; 752 }; 753 if ( !bazelShardIndex ) { 754 warn( "TEST_SHARD_INDEX" ); 755 } 756 if ( !bazelShardTotal ) { 757 warn( "TEST_TOTAL_SHARDS" ); 758 } 759 if ( !bazelShardInfoFile ) { 760 warn( "TEST_SHARD_STATUS_FILE" ); 761 } 762 return {}; 763 } 764 765 auto shardIndex = parseUInt( bazelShardIndex ); 766 if ( !shardIndex ) { 767 Catch::cerr() 768 << "Warning: could not parse 'TEST_SHARD_INDEX' ('" << bazelShardIndex 769 << "') as unsigned int.\n"; 770 return {}; 771 } 772 auto shardTotal = parseUInt( bazelShardTotal ); 773 if ( !shardTotal ) { 774 Catch::cerr() 775 << "Warning: could not parse 'TEST_TOTAL_SHARD' ('" 776 << bazelShardTotal << "') as unsigned int.\n"; 777 return {}; 778 } 779 780 return bazelShardingOptions{ 781 *shardIndex, *shardTotal, bazelShardInfoFile }; 782 783 } 784 } // end namespace 785 786 787 bool operator==( ProcessedReporterSpec const& lhs, 788 ProcessedReporterSpec const& rhs ) { 789 return lhs.name == rhs.name && 790 lhs.outputFilename == rhs.outputFilename && 791 lhs.colourMode == rhs.colourMode && 792 lhs.customOptions == rhs.customOptions; 793 } 794 795 Config::Config( ConfigData const& data ): 796 m_data( data ) { 797 // We need to trim filter specs to avoid trouble with superfluous 798 // whitespace (esp. important for bdd macros, as those are manually 799 // aligned with whitespace). 800 801 for (auto& elem : m_data.testsOrTags) { 802 elem = trim(elem); 803 } 804 for (auto& elem : m_data.sectionsToRun) { 805 elem = trim(elem); 806 } 807 808 // Insert the default reporter if user hasn't asked for a specific one 809 if ( m_data.reporterSpecifications.empty() ) { 810#if defined( CATCH_CONFIG_DEFAULT_REPORTER ) 811 const auto default_spec = CATCH_CONFIG_DEFAULT_REPORTER; 812#else 813 const auto default_spec = "console"; 814#endif 815 auto parsed = parseReporterSpec(default_spec); 816 CATCH_ENFORCE( parsed, 817 "Cannot parse the provided default reporter spec: '" 818 << default_spec << '\'' ); 819 m_data.reporterSpecifications.push_back( std::move( *parsed ) ); 820 } 821 822 if ( enableBazelEnvSupport() ) { 823 readBazelEnvVars(); 824 } 825 826 // Bazel support can modify the test specs, so parsing has to happen 827 // after reading Bazel env vars. 828 TestSpecParser parser( ITagAliasRegistry::get() ); 829 if ( !m_data.testsOrTags.empty() ) { 830 m_hasTestFilters = true; 831 for ( auto const& testOrTags : m_data.testsOrTags ) { 832 parser.parse( testOrTags ); 833 } 834 } 835 m_testSpec = parser.testSpec(); 836 837 838 // We now fixup the reporter specs to handle default output spec, 839 // default colour spec, etc 840 bool defaultOutputUsed = false; 841 for ( auto const& reporterSpec : m_data.reporterSpecifications ) { 842 // We do the default-output check separately, while always 843 // using the default output below to make the code simpler 844 // and avoid superfluous copies. 845 if ( reporterSpec.outputFile().none() ) { 846 CATCH_ENFORCE( !defaultOutputUsed, 847 "Internal error: cannot use default output for " 848 "multiple reporters" ); 849 defaultOutputUsed = true; 850 } 851 852 m_processedReporterSpecs.push_back( ProcessedReporterSpec{ 853 reporterSpec.name(), 854 reporterSpec.outputFile() ? *reporterSpec.outputFile() 855 : data.defaultOutputFilename, 856 reporterSpec.colourMode().valueOr( data.defaultColourMode ), 857 reporterSpec.customOptions() } ); 858 } 859 } 860 861 Config::~Config() = default; 862 863 864 bool Config::listTests() const { return m_data.listTests; } 865 bool Config::listTags() const { return m_data.listTags; } 866 bool Config::listReporters() const { return m_data.listReporters; } 867 bool Config::listListeners() const { return m_data.listListeners; } 868 869 std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; } 870 std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } 871 872 std::vector<ReporterSpec> const& Config::getReporterSpecs() const { 873 return m_data.reporterSpecifications; 874 } 875 876 std::vector<ProcessedReporterSpec> const& 877 Config::getProcessedReporterSpecs() const { 878 return m_processedReporterSpecs; 879 } 880 881 TestSpec const& Config::testSpec() const { return m_testSpec; } 882 bool Config::hasTestFilters() const { return m_hasTestFilters; } 883 884 bool Config::showHelp() const { return m_data.showHelp; } 885 886 // IConfig interface 887 bool Config::allowThrows() const { return !m_data.noThrow; } 888 StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } 889 bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } 890 bool Config::warnAboutMissingAssertions() const { 891 return !!( m_data.warnings & WarnAbout::NoAssertions ); 892 } 893 bool Config::warnAboutUnmatchedTestSpecs() const { 894 return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec ); 895 } 896 bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; } 897 ShowDurations Config::showDurations() const { return m_data.showDurations; } 898 double Config::minDuration() const { return m_data.minDuration; } 899 TestRunOrder Config::runOrder() const { return m_data.runOrder; } 900 uint32_t Config::rngSeed() const { return m_data.rngSeed; } 901 unsigned int Config::shardCount() const { return m_data.shardCount; } 902 unsigned int Config::shardIndex() const { return m_data.shardIndex; } 903 ColourMode Config::defaultColourMode() const { return m_data.defaultColourMode; } 904 bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } 905 int Config::abortAfter() const { return m_data.abortAfter; } 906 bool Config::showInvisibles() const { return m_data.showInvisibles; } 907 Verbosity Config::verbosity() const { return m_data.verbosity; } 908 909 bool Config::skipBenchmarks() const { return m_data.skipBenchmarks; } 910 bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } 911 unsigned int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } 912 double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } 913 unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } 914 std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } 915 916 void Config::readBazelEnvVars() { 917 // Register a JUnit reporter for Bazel. Bazel sets an environment 918 // variable with the path to XML output. If this file is written to 919 // during test, Bazel will not generate a default XML output. 920 // This allows the XML output file to contain higher level of detail 921 // than what is possible otherwise. 922 const auto bazelOutputFile = Detail::getEnv( "XML_OUTPUT_FILE" ); 923 924 if ( bazelOutputFile ) { 925 m_data.reporterSpecifications.push_back( 926 { "junit", std::string( bazelOutputFile ), {}, {} } ); 927 } 928 929 const auto bazelTestSpec = Detail::getEnv( "TESTBRIDGE_TEST_ONLY" ); 930 if ( bazelTestSpec ) { 931 // Presumably the test spec from environment should overwrite 932 // the one we got from CLI (if we got any) 933 m_data.testsOrTags.clear(); 934 m_data.testsOrTags.push_back( bazelTestSpec ); 935 } 936 937 const auto bazelShardOptions = readBazelShardingOptions(); 938 if ( bazelShardOptions ) { 939 std::ofstream f( bazelShardOptions->shardFilePath, 940 std::ios_base::out | std::ios_base::trunc ); 941 if ( f.is_open() ) { 942 f << ""; 943 m_data.shardIndex = bazelShardOptions->shardIndex; 944 m_data.shardCount = bazelShardOptions->shardCount; 945 } 946 } 947 } 948 949} // end namespace Catch 950 951 952 953 954 955namespace Catch { 956 std::uint32_t getSeed() { 957 return getCurrentContext().getConfig()->rngSeed(); 958 } 959} 960 961 962 963#include <cassert> 964#include <stack> 965 966namespace Catch { 967 968 //////////////////////////////////////////////////////////////////////////// 969 970 971 ScopedMessage::ScopedMessage( MessageBuilder&& builder ): 972 m_info( CATCH_MOVE(builder.m_info) ) { 973 m_info.message = builder.m_stream.str(); 974 getResultCapture().pushScopedMessage( m_info ); 975 } 976 977 ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept: 978 m_info( CATCH_MOVE( old.m_info ) ) { 979 old.m_moved = true; 980 } 981 982 ScopedMessage::~ScopedMessage() { 983 if ( !uncaught_exceptions() && !m_moved ){ 984 getResultCapture().popScopedMessage(m_info); 985 } 986 } 987 988 989 Capturer::Capturer( StringRef macroName, 990 SourceLineInfo const& lineInfo, 991 ResultWas::OfType resultType, 992 StringRef names ): 993 m_resultCapture( getResultCapture() ) { 994 auto trimmed = [&] (size_t start, size_t end) { 995 while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) { 996 ++start; 997 } 998 while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) { 999 --end; 1000 } 1001 return names.substr(start, end - start + 1); 1002 }; 1003 auto skipq = [&] (size_t start, char quote) { 1004 for (auto i = start + 1; i < names.size() ; ++i) { 1005 if (names[i] == quote) 1006 return i; 1007 if (names[i] == '\\') 1008 ++i; 1009 } 1010 CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); 1011 }; 1012 1013 size_t start = 0; 1014 std::stack<char> openings; 1015 for (size_t pos = 0; pos < names.size(); ++pos) { 1016 char c = names[pos]; 1017 switch (c) { 1018 case '[': 1019 case '{': 1020 case '(': 1021 // It is basically impossible to disambiguate between 1022 // comparison and start of template args in this context 1023// case '<': 1024 openings.push(c); 1025 break; 1026 case ']': 1027 case '}': 1028 case ')': 1029// case '>': 1030 openings.pop(); 1031 break; 1032 case '"': 1033 case '\'': 1034 pos = skipq(pos, c); 1035 break; 1036 case ',': 1037 if (start != pos && openings.empty()) { 1038 m_messages.emplace_back(macroName, lineInfo, resultType); 1039 m_messages.back().message = static_cast<std::string>(trimmed(start, pos)); 1040 m_messages.back().message += " := "; 1041 start = pos; 1042 } 1043 default:; // noop 1044 } 1045 } 1046 assert(openings.empty() && "Mismatched openings"); 1047 m_messages.emplace_back(macroName, lineInfo, resultType); 1048 m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1)); 1049 m_messages.back().message += " := "; 1050 } 1051 Capturer::~Capturer() { 1052 if ( !uncaught_exceptions() ){ 1053 assert( m_captured == m_messages.size() ); 1054 for( size_t i = 0; i < m_captured; ++i ) 1055 m_resultCapture.popScopedMessage( m_messages[i] ); 1056 } 1057 } 1058 1059 void Capturer::captureValue( size_t index, std::string const& value ) { 1060 assert( index < m_messages.size() ); 1061 m_messages[index].message += value; 1062 m_resultCapture.pushScopedMessage( m_messages[index] ); 1063 m_captured++; 1064 } 1065 1066} // end namespace Catch 1067 1068 1069 1070 1071#include <exception> 1072 1073namespace Catch { 1074 1075 namespace { 1076 1077 class RegistryHub : public IRegistryHub, 1078 public IMutableRegistryHub, 1079 private Detail::NonCopyable { 1080 1081 public: // IRegistryHub 1082 RegistryHub() = default; 1083 ReporterRegistry const& getReporterRegistry() const override { 1084 return m_reporterRegistry; 1085 } 1086 ITestCaseRegistry const& getTestCaseRegistry() const override { 1087 return m_testCaseRegistry; 1088 } 1089 IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { 1090 return m_exceptionTranslatorRegistry; 1091 } 1092 ITagAliasRegistry const& getTagAliasRegistry() const override { 1093 return m_tagAliasRegistry; 1094 } 1095 StartupExceptionRegistry const& getStartupExceptionRegistry() const override { 1096 return m_exceptionRegistry; 1097 } 1098 1099 public: // IMutableRegistryHub 1100 void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override { 1101 m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) ); 1102 } 1103 void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) override { 1104 m_reporterRegistry.registerListener( CATCH_MOVE(factory) ); 1105 } 1106 void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker ) override { 1107 m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) ); 1108 } 1109 void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) override { 1110 m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) ); 1111 } 1112 void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { 1113 m_tagAliasRegistry.add( alias, tag, lineInfo ); 1114 } 1115 void registerStartupException() noexcept override { 1116#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 1117 m_exceptionRegistry.add(std::current_exception()); 1118#else 1119 CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); 1120#endif 1121 } 1122 IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { 1123 return m_enumValuesRegistry; 1124 } 1125 1126 private: 1127 TestRegistry m_testCaseRegistry; 1128 ReporterRegistry m_reporterRegistry; 1129 ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; 1130 TagAliasRegistry m_tagAliasRegistry; 1131 StartupExceptionRegistry m_exceptionRegistry; 1132 Detail::EnumValuesRegistry m_enumValuesRegistry; 1133 }; 1134 } 1135 1136 using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>; 1137 1138 IRegistryHub const& getRegistryHub() { 1139 return RegistryHubSingleton::get(); 1140 } 1141 IMutableRegistryHub& getMutableRegistryHub() { 1142 return RegistryHubSingleton::getMutable(); 1143 } 1144 void cleanUp() { 1145 cleanupSingletons(); 1146 cleanUpContext(); 1147 } 1148 std::string translateActiveException() { 1149 return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); 1150 } 1151 1152 1153} // end namespace Catch 1154 1155 1156 1157#include <algorithm> 1158#include <cassert> 1159#include <exception> 1160#include <iomanip> 1161#include <set> 1162 1163namespace Catch { 1164 1165 namespace { 1166 const int MaxExitCode = 255; 1167 1168 IEventListenerPtr createReporter(std::string const& reporterName, ReporterConfig&& config) { 1169 auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, CATCH_MOVE(config)); 1170 CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << '\''); 1171 1172 return reporter; 1173 } 1174 1175 IEventListenerPtr prepareReporters(Config const* config) { 1176 if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty() 1177 && config->getProcessedReporterSpecs().size() == 1) { 1178 auto const& spec = config->getProcessedReporterSpecs()[0]; 1179 return createReporter( 1180 spec.name, 1181 ReporterConfig( config, 1182 makeStream( spec.outputFilename ), 1183 spec.colourMode, 1184 spec.customOptions ) ); 1185 } 1186 1187 auto multi = Detail::make_unique<MultiReporter>(config); 1188 1189 auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); 1190 for (auto const& listener : listeners) { 1191 multi->addListener(listener->create(config)); 1192 } 1193 1194 for ( auto const& reporterSpec : config->getProcessedReporterSpecs() ) { 1195 multi->addReporter( createReporter( 1196 reporterSpec.name, 1197 ReporterConfig( config, 1198 makeStream( reporterSpec.outputFilename ), 1199 reporterSpec.colourMode, 1200 reporterSpec.customOptions ) ) ); 1201 } 1202 1203 return multi; 1204 } 1205 1206 class TestGroup { 1207 public: 1208 explicit TestGroup(IEventListenerPtr&& reporter, Config const* config): 1209 m_reporter(reporter.get()), 1210 m_config{config}, 1211 m_context{config, CATCH_MOVE(reporter)} { 1212 1213 assert( m_config->testSpec().getInvalidSpecs().empty() && 1214 "Invalid test specs should be handled before running tests" ); 1215 1216 auto const& allTestCases = getAllTestCasesSorted(*m_config); 1217 auto const& testSpec = m_config->testSpec(); 1218 if ( !testSpec.hasFilters() ) { 1219 for ( auto const& test : allTestCases ) { 1220 if ( !test.getTestCaseInfo().isHidden() ) { 1221 m_tests.emplace( &test ); 1222 } 1223 } 1224 } else { 1225 m_matches = 1226 testSpec.matchesByFilter( allTestCases, *m_config ); 1227 for ( auto const& match : m_matches ) { 1228 m_tests.insert( match.tests.begin(), 1229 match.tests.end() ); 1230 } 1231 } 1232 1233 m_tests = createShard(m_tests, m_config->shardCount(), m_config->shardIndex()); 1234 } 1235 1236 Totals execute() { 1237 Totals totals; 1238 for (auto const& testCase : m_tests) { 1239 if (!m_context.aborting()) 1240 totals += m_context.runTest(*testCase); 1241 else 1242 m_reporter->skipTest(testCase->getTestCaseInfo()); 1243 } 1244 1245 for (auto const& match : m_matches) { 1246 if (match.tests.empty()) { 1247 m_unmatchedTestSpecs = true; 1248 m_reporter->noMatchingTestCases( match.name ); 1249 } 1250 } 1251 1252 return totals; 1253 } 1254 1255 bool hadUnmatchedTestSpecs() const { 1256 return m_unmatchedTestSpecs; 1257 } 1258 1259 1260 private: 1261 IEventListener* m_reporter; 1262 Config const* m_config; 1263 RunContext m_context; 1264 std::set<TestCaseHandle const*> m_tests; 1265 TestSpec::Matches m_matches; 1266 bool m_unmatchedTestSpecs = false; 1267 }; 1268 1269 void applyFilenamesAsTags() { 1270 for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) { 1271 testInfo->addFilenameTag(); 1272 } 1273 } 1274 1275 } // anon namespace 1276 1277 Session::Session() { 1278 static bool alreadyInstantiated = false; 1279 if( alreadyInstantiated ) { 1280 CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } 1281 CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } 1282 } 1283 1284 // There cannot be exceptions at startup in no-exception mode. 1285#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 1286 const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); 1287 if ( !exceptions.empty() ) { 1288 config(); 1289 getCurrentMutableContext().setConfig(m_config.get()); 1290 1291 m_startupExceptions = true; 1292 auto errStream = makeStream( "%stderr" ); 1293 auto colourImpl = makeColourImpl( 1294 ColourMode::PlatformDefault, errStream.get() ); 1295 auto guard = colourImpl->guardColour( Colour::Red ); 1296 errStream->stream() << "Errors occurred during startup!" << '\n'; 1297 // iterate over all exceptions and notify user 1298 for ( const auto& ex_ptr : exceptions ) { 1299 try { 1300 std::rethrow_exception(ex_ptr); 1301 } catch ( std::exception const& ex ) { 1302 errStream->stream() << TextFlow::Column( ex.what() ).indent(2) << '\n'; 1303 } 1304 } 1305 } 1306#endif 1307 1308 alreadyInstantiated = true; 1309 m_cli = makeCommandLineParser( m_configData ); 1310 } 1311 Session::~Session() { 1312 Catch::cleanUp(); 1313 } 1314 1315 void Session::showHelp() const { 1316 Catch::cout() 1317 << "\nCatch2 v" << libraryVersion() << '\n' 1318 << m_cli << '\n' 1319 << "For more detailed usage please see the project docs\n\n" << std::flush; 1320 } 1321 void Session::libIdentify() { 1322 Catch::cout() 1323 << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" 1324 << std::left << std::setw(16) << "category: " << "testframework\n" 1325 << std::left << std::setw(16) << "framework: " << "Catch2\n" 1326 << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush; 1327 } 1328 1329 int Session::applyCommandLine( int argc, char const * const * argv ) { 1330 if( m_startupExceptions ) 1331 return 1; 1332 1333 auto result = m_cli.parse( Clara::Args( argc, argv ) ); 1334 1335 if( !result ) { 1336 config(); 1337 getCurrentMutableContext().setConfig(m_config.get()); 1338 auto errStream = makeStream( "%stderr" ); 1339 auto colour = makeColourImpl( ColourMode::PlatformDefault, errStream.get() ); 1340 1341 errStream->stream() 1342 << colour->guardColour( Colour::Red ) 1343 << "\nError(s) in input:\n" 1344 << TextFlow::Column( result.errorMessage() ).indent( 2 ) 1345 << "\n\n"; 1346 errStream->stream() << "Run with -? for usage\n\n" << std::flush; 1347 return MaxExitCode; 1348 } 1349 1350 if( m_configData.showHelp ) 1351 showHelp(); 1352 if( m_configData.libIdentify ) 1353 libIdentify(); 1354 1355 m_config.reset(); 1356 return 0; 1357 } 1358 1359#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) 1360 int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { 1361 1362 char **utf8Argv = new char *[ argc ]; 1363 1364 for ( int i = 0; i < argc; ++i ) { 1365 int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr ); 1366 1367 utf8Argv[ i ] = new char[ bufSize ]; 1368 1369 WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr ); 1370 } 1371 1372 int returnCode = applyCommandLine( argc, utf8Argv ); 1373 1374 for ( int i = 0; i < argc; ++i ) 1375 delete [] utf8Argv[ i ]; 1376 1377 delete [] utf8Argv; 1378 1379 return returnCode; 1380 } 1381#endif 1382 1383 void Session::useConfigData( ConfigData const& configData ) { 1384 m_configData = configData; 1385 m_config.reset(); 1386 } 1387 1388 int Session::run() { 1389 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { 1390 Catch::cout() << "...waiting for enter/ return before starting\n" << std::flush; 1391 static_cast<void>(std::getchar()); 1392 } 1393 int exitCode = runInternal(); 1394 if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { 1395 Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush; 1396 static_cast<void>(std::getchar()); 1397 } 1398 return exitCode; 1399 } 1400 1401 Clara::Parser const& Session::cli() const { 1402 return m_cli; 1403 } 1404 void Session::cli( Clara::Parser const& newParser ) { 1405 m_cli = newParser; 1406 } 1407 ConfigData& Session::configData() { 1408 return m_configData; 1409 } 1410 Config& Session::config() { 1411 if( !m_config ) 1412 m_config = Detail::make_unique<Config>( m_configData ); 1413 return *m_config; 1414 } 1415 1416 int Session::runInternal() { 1417 if( m_startupExceptions ) 1418 return 1; 1419 1420 if (m_configData.showHelp || m_configData.libIdentify) { 1421 return 0; 1422 } 1423 1424 if ( m_configData.shardIndex >= m_configData.shardCount ) { 1425 Catch::cerr() << "The shard count (" << m_configData.shardCount 1426 << ") must be greater than the shard index (" 1427 << m_configData.shardIndex << ")\n" 1428 << std::flush; 1429 return 1; 1430 } 1431 1432 CATCH_TRY { 1433 config(); // Force config to be constructed 1434 1435 seedRng( *m_config ); 1436 1437 if (m_configData.filenamesAsTags) { 1438 applyFilenamesAsTags(); 1439 } 1440 1441 // Set up global config instance before we start calling into other functions 1442 getCurrentMutableContext().setConfig(m_config.get()); 1443 1444 // Create reporter(s) so we can route listings through them 1445 auto reporter = prepareReporters(m_config.get()); 1446 1447 auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs(); 1448 if ( !invalidSpecs.empty() ) { 1449 for ( auto const& spec : invalidSpecs ) { 1450 reporter->reportInvalidTestSpec( spec ); 1451 } 1452 return 1; 1453 } 1454 1455 1456 // Handle list request 1457 if (list(*reporter, *m_config)) { 1458 return 0; 1459 } 1460 1461 TestGroup tests { CATCH_MOVE(reporter), m_config.get() }; 1462 auto const totals = tests.execute(); 1463 1464 if ( tests.hadUnmatchedTestSpecs() 1465 && m_config->warnAboutUnmatchedTestSpecs() ) { 1466 return 3; 1467 } 1468 1469 if ( totals.testCases.total() == 0 1470 && !m_config->zeroTestsCountAsSuccess() ) { 1471 return 2; 1472 } 1473 1474 if ( totals.testCases.total() > 0 && 1475 totals.testCases.total() == totals.testCases.skipped 1476 && !m_config->zeroTestsCountAsSuccess() ) { 1477 return 4; 1478 } 1479 1480 // Note that on unices only the lower 8 bits are usually used, clamping 1481 // the return value to 255 prevents false negative when some multiple 1482 // of 256 tests has failed 1483 return (std::min) (MaxExitCode, static_cast<int>(totals.assertions.failed)); 1484 } 1485#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 1486 catch( std::exception& ex ) { 1487 Catch::cerr() << ex.what() << '\n' << std::flush; 1488 return MaxExitCode; 1489 } 1490#endif 1491 } 1492 1493} // end namespace Catch 1494 1495 1496 1497 1498namespace Catch { 1499 1500 RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { 1501 CATCH_TRY { 1502 getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); 1503 } CATCH_CATCH_ALL { 1504 // Do not throw when constructing global objects, instead register the exception to be processed later 1505 getMutableRegistryHub().registerStartupException(); 1506 } 1507 } 1508 1509} 1510 1511 1512 1513#include <cassert> 1514#include <cctype> 1515#include <algorithm> 1516 1517namespace Catch { 1518 1519 namespace { 1520 using TCP_underlying_type = uint8_t; 1521 static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type), 1522 "The size of the TestCaseProperties is different from the assumed size"); 1523 1524 TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { 1525 return static_cast<TestCaseProperties>( 1526 static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs) 1527 ); 1528 } 1529 1530 TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { 1531 lhs = static_cast<TestCaseProperties>( 1532 static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs) 1533 ); 1534 return lhs; 1535 } 1536 1537 TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { 1538 return static_cast<TestCaseProperties>( 1539 static_cast<TCP_underlying_type>(lhs) & static_cast<TCP_underlying_type>(rhs) 1540 ); 1541 } 1542 1543 bool applies(TestCaseProperties tcp) { 1544 static_assert(static_cast<TCP_underlying_type>(TestCaseProperties::None) == 0, 1545 "TestCaseProperties::None must be equal to 0"); 1546 return tcp != TestCaseProperties::None; 1547 } 1548 1549 TestCaseProperties parseSpecialTag( StringRef tag ) { 1550 if( !tag.empty() && tag[0] == '.' ) 1551 return TestCaseProperties::IsHidden; 1552 else if( tag == "!throws"_sr ) 1553 return TestCaseProperties::Throws; 1554 else if( tag == "!shouldfail"_sr ) 1555 return TestCaseProperties::ShouldFail; 1556 else if( tag == "!mayfail"_sr ) 1557 return TestCaseProperties::MayFail; 1558 else if( tag == "!nonportable"_sr ) 1559 return TestCaseProperties::NonPortable; 1560 else if( tag == "!benchmark"_sr ) 1561 return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden; 1562 else 1563 return TestCaseProperties::None; 1564 } 1565 bool isReservedTag( StringRef tag ) { 1566 return parseSpecialTag( tag ) == TestCaseProperties::None 1567 && tag.size() > 0 1568 && !std::isalnum( static_cast<unsigned char>(tag[0]) ); 1569 } 1570 void enforceNotReservedTag( StringRef tag, SourceLineInfo const& _lineInfo ) { 1571 CATCH_ENFORCE( !isReservedTag(tag), 1572 "Tag name: [" << tag << "] is not allowed.\n" 1573 << "Tag names starting with non alphanumeric characters are reserved\n" 1574 << _lineInfo ); 1575 } 1576 1577 std::string makeDefaultName() { 1578 static size_t counter = 0; 1579 return "Anonymous test case " + std::to_string(++counter); 1580 } 1581 1582 StringRef extractFilenamePart(StringRef filename) { 1583 size_t lastDot = filename.size(); 1584 while (lastDot > 0 && filename[lastDot - 1] != '.') { 1585 --lastDot; 1586 } 1587 // In theory we could have filename without any extension in it 1588 if ( lastDot == 0 ) { return StringRef(); } 1589 1590 --lastDot; 1591 size_t nameStart = lastDot; 1592 while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') { 1593 --nameStart; 1594 } 1595 1596 return filename.substr(nameStart, lastDot - nameStart); 1597 } 1598 1599 // Returns the upper bound on size of extra tags ([#file]+[.]) 1600 size_t sizeOfExtraTags(StringRef filepath) { 1601 // [.] is 3, [#] is another 3 1602 const size_t extras = 3 + 3; 1603 return extractFilenamePart(filepath).size() + extras; 1604 } 1605 } // end unnamed namespace 1606 1607 bool operator<( Tag const& lhs, Tag const& rhs ) { 1608 Detail::CaseInsensitiveLess cmp; 1609 return cmp( lhs.original, rhs.original ); 1610 } 1611 bool operator==( Tag const& lhs, Tag const& rhs ) { 1612 Detail::CaseInsensitiveEqualTo cmp; 1613 return cmp( lhs.original, rhs.original ); 1614 } 1615 1616 Detail::unique_ptr<TestCaseInfo> 1617 makeTestCaseInfo(StringRef _className, 1618 NameAndTags const& nameAndTags, 1619 SourceLineInfo const& _lineInfo ) { 1620 return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo); 1621 } 1622 1623 TestCaseInfo::TestCaseInfo(StringRef _className, 1624 NameAndTags const& _nameAndTags, 1625 SourceLineInfo const& _lineInfo): 1626 name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ), 1627 className( _className ), 1628 lineInfo( _lineInfo ) 1629 { 1630 StringRef originalTags = _nameAndTags.tags; 1631 // We need to reserve enough space to store all of the tags 1632 // (including optional hidden tag and filename tag) 1633 auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file); 1634 backingTags.reserve(requiredSize); 1635 1636 // We cannot copy the tags directly, as we need to normalize 1637 // some tags, so that [.foo] is copied as [.][foo]. 1638 size_t tagStart = 0; 1639 size_t tagEnd = 0; 1640 bool inTag = false; 1641 for (size_t idx = 0; idx < originalTags.size(); ++idx) { 1642 auto c = originalTags[idx]; 1643 if (c == '[') { 1644 CATCH_ENFORCE( 1645 !inTag, 1646 "Found '[' inside a tag while registering test case '" 1647 << _nameAndTags.name << "' at " << _lineInfo ); 1648 1649 inTag = true; 1650 tagStart = idx; 1651 } 1652 if (c == ']') { 1653 CATCH_ENFORCE( 1654 inTag, 1655 "Found unmatched ']' while registering test case '" 1656 << _nameAndTags.name << "' at " << _lineInfo ); 1657 1658 inTag = false; 1659 tagEnd = idx; 1660 assert(tagStart < tagEnd); 1661 1662 // We need to check the tag for special meanings, copy 1663 // it over to backing storage and actually reference the 1664 // backing storage in the saved tags 1665 StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1); 1666 CATCH_ENFORCE( !tagStr.empty(), 1667 "Found an empty tag while registering test case '" 1668 << _nameAndTags.name << "' at " 1669 << _lineInfo ); 1670 1671 enforceNotReservedTag(tagStr, lineInfo); 1672 properties |= parseSpecialTag(tagStr); 1673 // When copying a tag to the backing storage, we need to 1674 // check if it is a merged hide tag, such as [.foo], and 1675 // if it is, we need to handle it as if it was [foo]. 1676 if (tagStr.size() > 1 && tagStr[0] == '.') { 1677 tagStr = tagStr.substr(1, tagStr.size() - 1); 1678 } 1679 // We skip over dealing with the [.] tag, as we will add 1680 // it later unconditionally and then sort and unique all 1681 // the tags. 1682 internalAppendTag(tagStr); 1683 } 1684 } 1685 CATCH_ENFORCE( !inTag, 1686 "Found an unclosed tag while registering test case '" 1687 << _nameAndTags.name << "' at " << _lineInfo ); 1688 1689 1690 // Add [.] if relevant 1691 if (isHidden()) { 1692 internalAppendTag("."_sr); 1693 } 1694 1695 // Sort and prepare tags 1696 std::sort(begin(tags), end(tags)); 1697 tags.erase(std::unique(begin(tags), end(tags)), 1698 end(tags)); 1699 } 1700 1701 bool TestCaseInfo::isHidden() const { 1702 return applies( properties & TestCaseProperties::IsHidden ); 1703 } 1704 bool TestCaseInfo::throws() const { 1705 return applies( properties & TestCaseProperties::Throws ); 1706 } 1707 bool TestCaseInfo::okToFail() const { 1708 return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) ); 1709 } 1710 bool TestCaseInfo::expectedToFail() const { 1711 return applies( properties & (TestCaseProperties::ShouldFail) ); 1712 } 1713 1714 void TestCaseInfo::addFilenameTag() { 1715 std::string combined("#"); 1716 combined += extractFilenamePart(lineInfo.file); 1717 internalAppendTag(combined); 1718 } 1719 1720 std::string TestCaseInfo::tagsAsString() const { 1721 std::string ret; 1722 // '[' and ']' per tag 1723 std::size_t full_size = 2 * tags.size(); 1724 for (const auto& tag : tags) { 1725 full_size += tag.original.size(); 1726 } 1727 ret.reserve(full_size); 1728 for (const auto& tag : tags) { 1729 ret.push_back('['); 1730 ret += tag.original; 1731 ret.push_back(']'); 1732 } 1733 1734 return ret; 1735 } 1736 1737 void TestCaseInfo::internalAppendTag(StringRef tagStr) { 1738 backingTags += '['; 1739 const auto backingStart = backingTags.size(); 1740 backingTags += tagStr; 1741 const auto backingEnd = backingTags.size(); 1742 backingTags += ']'; 1743 tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart)); 1744 } 1745 1746 bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) { 1747 // We want to avoid redoing the string comparisons multiple times, 1748 // so we store the result of a three-way comparison before using 1749 // it in the actual comparison logic. 1750 const auto cmpName = lhs.name.compare( rhs.name ); 1751 if ( cmpName != 0 ) { 1752 return cmpName < 0; 1753 } 1754 const auto cmpClassName = lhs.className.compare( rhs.className ); 1755 if ( cmpClassName != 0 ) { 1756 return cmpClassName < 0; 1757 } 1758 return lhs.tags < rhs.tags; 1759 } 1760 1761 TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const { 1762 return *m_info; 1763 } 1764 1765} // end namespace Catch 1766 1767 1768 1769#include <algorithm> 1770#include <string> 1771#include <vector> 1772#include <ostream> 1773 1774namespace Catch { 1775 1776 TestSpec::Pattern::Pattern( std::string const& name ) 1777 : m_name( name ) 1778 {} 1779 1780 TestSpec::Pattern::~Pattern() = default; 1781 1782 std::string const& TestSpec::Pattern::name() const { 1783 return m_name; 1784 } 1785 1786 1787 TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString ) 1788 : Pattern( filterString ) 1789 , m_wildcardPattern( toLower( name ), CaseSensitive::No ) 1790 {} 1791 1792 bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { 1793 return m_wildcardPattern.matches( testCase.name ); 1794 } 1795 1796 void TestSpec::NamePattern::serializeTo( std::ostream& out ) const { 1797 out << '"' << name() << '"'; 1798 } 1799 1800 1801 TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString ) 1802 : Pattern( filterString ) 1803 , m_tag( tag ) 1804 {} 1805 1806 bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { 1807 return std::find( begin( testCase.tags ), 1808 end( testCase.tags ), 1809 Tag( m_tag ) ) != end( testCase.tags ); 1810 } 1811 1812 void TestSpec::TagPattern::serializeTo( std::ostream& out ) const { 1813 out << name(); 1814 } 1815 1816 bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { 1817 bool should_use = !testCase.isHidden(); 1818 for (auto const& pattern : m_required) { 1819 should_use = true; 1820 if (!pattern->matches(testCase)) { 1821 return false; 1822 } 1823 } 1824 for (auto const& pattern : m_forbidden) { 1825 if (pattern->matches(testCase)) { 1826 return false; 1827 } 1828 } 1829 return should_use; 1830 } 1831 1832 void TestSpec::Filter::serializeTo( std::ostream& out ) const { 1833 bool first = true; 1834 for ( auto const& pattern : m_required ) { 1835 if ( !first ) { 1836 out << ' '; 1837 } 1838 out << *pattern; 1839 first = false; 1840 } 1841 for ( auto const& pattern : m_forbidden ) { 1842 if ( !first ) { 1843 out << ' '; 1844 } 1845 out << *pattern; 1846 first = false; 1847 } 1848 } 1849 1850 1851 std::string TestSpec::extractFilterName( Filter const& filter ) { 1852 Catch::ReusableStringStream sstr; 1853 sstr << filter; 1854 return sstr.str(); 1855 } 1856 1857 bool TestSpec::hasFilters() const { 1858 return !m_filters.empty(); 1859 } 1860 1861 bool TestSpec::matches( TestCaseInfo const& testCase ) const { 1862 return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); 1863 } 1864 1865 TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const { 1866 Matches matches; 1867 matches.reserve( m_filters.size() ); 1868 for ( auto const& filter : m_filters ) { 1869 std::vector<TestCaseHandle const*> currentMatches; 1870 for ( auto const& test : testCases ) 1871 if ( isThrowSafe( test, config ) && 1872 filter.matches( test.getTestCaseInfo() ) ) 1873 currentMatches.emplace_back( &test ); 1874 matches.push_back( 1875 FilterMatch{ extractFilterName( filter ), currentMatches } ); 1876 } 1877 return matches; 1878 } 1879 1880 const TestSpec::vectorStrings& TestSpec::getInvalidSpecs() const { 1881 return m_invalidSpecs; 1882 } 1883 1884 void TestSpec::serializeTo( std::ostream& out ) const { 1885 bool first = true; 1886 for ( auto const& filter : m_filters ) { 1887 if ( !first ) { 1888 out << ','; 1889 } 1890 out << filter; 1891 first = false; 1892 } 1893 } 1894 1895} 1896 1897 1898 1899#include <chrono> 1900 1901namespace Catch { 1902 1903 namespace { 1904 static auto getCurrentNanosecondsSinceEpoch() -> uint64_t { 1905 return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 1906 } 1907 } // end unnamed namespace 1908 1909 void Timer::start() { 1910 m_nanoseconds = getCurrentNanosecondsSinceEpoch(); 1911 } 1912 auto Timer::getElapsedNanoseconds() const -> uint64_t { 1913 return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; 1914 } 1915 auto Timer::getElapsedMicroseconds() const -> uint64_t { 1916 return getElapsedNanoseconds()/1000; 1917 } 1918 auto Timer::getElapsedMilliseconds() const -> unsigned int { 1919 return static_cast<unsigned int>(getElapsedMicroseconds()/1000); 1920 } 1921 auto Timer::getElapsedSeconds() const -> double { 1922 return getElapsedMicroseconds()/1000000.0; 1923 } 1924 1925 1926} // namespace Catch 1927 1928 1929 1930 1931#include <cmath> 1932#include <iomanip> 1933 1934namespace Catch { 1935 1936namespace Detail { 1937 1938 namespace { 1939 const int hexThreshold = 255; 1940 1941 struct Endianness { 1942 enum Arch { Big, Little }; 1943 1944 static Arch which() { 1945 int one = 1; 1946 // If the lowest byte we read is non-zero, we can assume 1947 // that little endian format is used. 1948 auto value = *reinterpret_cast<char*>(&one); 1949 return value ? Little : Big; 1950 } 1951 }; 1952 1953 template<typename T> 1954 std::string fpToString(T value, int precision) { 1955 if (Catch::isnan(value)) { 1956 return "nan"; 1957 } 1958 1959 ReusableStringStream rss; 1960 rss << std::setprecision(precision) 1961 << std::fixed 1962 << value; 1963 std::string d = rss.str(); 1964 std::size_t i = d.find_last_not_of('0'); 1965 if (i != std::string::npos && i != d.size() - 1) { 1966 if (d[i] == '.') 1967 i++; 1968 d = d.substr(0, i + 1); 1969 } 1970 return d; 1971 } 1972 } // end unnamed namespace 1973 1974 std::string convertIntoString(StringRef string, bool escapeInvisibles) { 1975 std::string ret; 1976 // This is enough for the "don't escape invisibles" case, and a good 1977 // lower bound on the "escape invisibles" case. 1978 ret.reserve(string.size() + 2); 1979 1980 if (!escapeInvisibles) { 1981 ret += '"'; 1982 ret += string; 1983 ret += '"'; 1984 return ret; 1985 } 1986 1987 ret += '"'; 1988 for (char c : string) { 1989 switch (c) { 1990 case '\r': 1991 ret.append("\\r"); 1992 break; 1993 case '\n': 1994 ret.append("\\n"); 1995 break; 1996 case '\t': 1997 ret.append("\\t"); 1998 break; 1999 case '\f': 2000 ret.append("\\f"); 2001 break; 2002 default: 2003 ret.push_back(c); 2004 break; 2005 } 2006 } 2007 ret += '"'; 2008 2009 return ret; 2010 } 2011 2012 std::string convertIntoString(StringRef string) { 2013 return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles()); 2014 } 2015 2016 std::string rawMemoryToString( const void *object, std::size_t size ) { 2017 // Reverse order for little endian architectures 2018 int i = 0, end = static_cast<int>( size ), inc = 1; 2019 if( Endianness::which() == Endianness::Little ) { 2020 i = end-1; 2021 end = inc = -1; 2022 } 2023 2024 unsigned char const *bytes = static_cast<unsigned char const *>(object); 2025 ReusableStringStream rss; 2026 rss << "0x" << std::setfill('0') << std::hex; 2027 for( ; i != end; i += inc ) 2028 rss << std::setw(2) << static_cast<unsigned>(bytes[i]); 2029 return rss.str(); 2030 } 2031} // end Detail namespace 2032 2033 2034 2035//// ======================================================= //// 2036// 2037// Out-of-line defs for full specialization of StringMaker 2038// 2039//// ======================================================= //// 2040 2041std::string StringMaker<std::string>::convert(const std::string& str) { 2042 return Detail::convertIntoString( str ); 2043} 2044 2045#ifdef CATCH_CONFIG_CPP17_STRING_VIEW 2046std::string StringMaker<std::string_view>::convert(std::string_view str) { 2047 return Detail::convertIntoString( StringRef( str.data(), str.size() ) ); 2048} 2049#endif 2050 2051std::string StringMaker<char const*>::convert(char const* str) { 2052 if (str) { 2053 return Detail::convertIntoString( str ); 2054 } else { 2055 return{ "{null string}" }; 2056 } 2057} 2058std::string StringMaker<char*>::convert(char* str) { // NOLINT(readability-non-const-parameter) 2059 if (str) { 2060 return Detail::convertIntoString( str ); 2061 } else { 2062 return{ "{null string}" }; 2063 } 2064} 2065 2066#ifdef CATCH_CONFIG_WCHAR 2067std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) { 2068 std::string s; 2069 s.reserve(wstr.size()); 2070 for (auto c : wstr) { 2071 s += (c <= 0xff) ? static_cast<char>(c) : '?'; 2072 } 2073 return ::Catch::Detail::stringify(s); 2074} 2075 2076# ifdef CATCH_CONFIG_CPP17_STRING_VIEW 2077std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) { 2078 return StringMaker<std::wstring>::convert(std::wstring(str)); 2079} 2080# endif 2081 2082std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) { 2083 if (str) { 2084 return ::Catch::Detail::stringify(std::wstring{ str }); 2085 } else { 2086 return{ "{null string}" }; 2087 } 2088} 2089std::string StringMaker<wchar_t *>::convert(wchar_t * str) { 2090 if (str) { 2091 return ::Catch::Detail::stringify(std::wstring{ str }); 2092 } else { 2093 return{ "{null string}" }; 2094 } 2095} 2096#endif 2097 2098#if defined(CATCH_CONFIG_CPP17_BYTE) 2099#include <cstddef> 2100std::string StringMaker<std::byte>::convert(std::byte value) { 2101 return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value)); 2102} 2103#endif // defined(CATCH_CONFIG_CPP17_BYTE) 2104 2105std::string StringMaker<int>::convert(int value) { 2106 return ::Catch::Detail::stringify(static_cast<long long>(value)); 2107} 2108std::string StringMaker<long>::convert(long value) { 2109 return ::Catch::Detail::stringify(static_cast<long long>(value)); 2110} 2111std::string StringMaker<long long>::convert(long long value) { 2112 ReusableStringStream rss; 2113 rss << value; 2114 if (value > Detail::hexThreshold) { 2115 rss << " (0x" << std::hex << value << ')'; 2116 } 2117 return rss.str(); 2118} 2119 2120std::string StringMaker<unsigned int>::convert(unsigned int value) { 2121 return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); 2122} 2123std::string StringMaker<unsigned long>::convert(unsigned long value) { 2124 return ::Catch::Detail::stringify(static_cast<unsigned long long>(value)); 2125} 2126std::string StringMaker<unsigned long long>::convert(unsigned long long value) { 2127 ReusableStringStream rss; 2128 rss << value; 2129 if (value > Detail::hexThreshold) { 2130 rss << " (0x" << std::hex << value << ')'; 2131 } 2132 return rss.str(); 2133} 2134 2135std::string StringMaker<signed char>::convert(signed char value) { 2136 if (value == '\r') { 2137 return "'\\r'"; 2138 } else if (value == '\f') { 2139 return "'\\f'"; 2140 } else if (value == '\n') { 2141 return "'\\n'"; 2142 } else if (value == '\t') { 2143 return "'\\t'"; 2144 } else if ('\0' <= value && value < ' ') { 2145 return ::Catch::Detail::stringify(static_cast<unsigned int>(value)); 2146 } else { 2147 char chstr[] = "' '"; 2148 chstr[1] = value; 2149 return chstr; 2150 } 2151} 2152std::string StringMaker<char>::convert(char c) { 2153 return ::Catch::Detail::stringify(static_cast<signed char>(c)); 2154} 2155std::string StringMaker<unsigned char>::convert(unsigned char value) { 2156 return ::Catch::Detail::stringify(static_cast<char>(value)); 2157} 2158 2159int StringMaker<float>::precision = std::numeric_limits<float>::max_digits10; 2160 2161std::string StringMaker<float>::convert(float value) { 2162 return Detail::fpToString(value, precision) + 'f'; 2163} 2164 2165int StringMaker<double>::precision = std::numeric_limits<double>::max_digits10; 2166 2167std::string StringMaker<double>::convert(double value) { 2168 return Detail::fpToString(value, precision); 2169} 2170 2171} // end namespace Catch 2172 2173 2174 2175namespace Catch { 2176 2177 Counts Counts::operator - ( Counts const& other ) const { 2178 Counts diff; 2179 diff.passed = passed - other.passed; 2180 diff.failed = failed - other.failed; 2181 diff.failedButOk = failedButOk - other.failedButOk; 2182 diff.skipped = skipped - other.skipped; 2183 return diff; 2184 } 2185 2186 Counts& Counts::operator += ( Counts const& other ) { 2187 passed += other.passed; 2188 failed += other.failed; 2189 failedButOk += other.failedButOk; 2190 skipped += other.skipped; 2191 return *this; 2192 } 2193 2194 std::uint64_t Counts::total() const { 2195 return passed + failed + failedButOk + skipped; 2196 } 2197 bool Counts::allPassed() const { 2198 return failed == 0 && failedButOk == 0 && skipped == 0; 2199 } 2200 bool Counts::allOk() const { 2201 return failed == 0; 2202 } 2203 2204 Totals Totals::operator - ( Totals const& other ) const { 2205 Totals diff; 2206 diff.assertions = assertions - other.assertions; 2207 diff.testCases = testCases - other.testCases; 2208 return diff; 2209 } 2210 2211 Totals& Totals::operator += ( Totals const& other ) { 2212 assertions += other.assertions; 2213 testCases += other.testCases; 2214 return *this; 2215 } 2216 2217 Totals Totals::delta( Totals const& prevTotals ) const { 2218 Totals diff = *this - prevTotals; 2219 if( diff.assertions.failed > 0 ) 2220 ++diff.testCases.failed; 2221 else if( diff.assertions.failedButOk > 0 ) 2222 ++diff.testCases.failedButOk; 2223 else if ( diff.assertions.skipped > 0 ) 2224 ++ diff.testCases.skipped; 2225 else 2226 ++diff.testCases.passed; 2227 return diff; 2228 } 2229 2230} 2231 2232 2233 2234 2235namespace Catch { 2236 namespace Detail { 2237 void registerTranslatorImpl( 2238 Detail::unique_ptr<IExceptionTranslator>&& translator ) { 2239 getMutableRegistryHub().registerTranslator( 2240 CATCH_MOVE( translator ) ); 2241 } 2242 } // namespace Detail 2243} // namespace Catch 2244 2245 2246#include <ostream> 2247 2248namespace Catch { 2249 2250 Version::Version 2251 ( unsigned int _majorVersion, 2252 unsigned int _minorVersion, 2253 unsigned int _patchNumber, 2254 char const * const _branchName, 2255 unsigned int _buildNumber ) 2256 : majorVersion( _majorVersion ), 2257 minorVersion( _minorVersion ), 2258 patchNumber( _patchNumber ), 2259 branchName( _branchName ), 2260 buildNumber( _buildNumber ) 2261 {} 2262 2263 std::ostream& operator << ( std::ostream& os, Version const& version ) { 2264 os << version.majorVersion << '.' 2265 << version.minorVersion << '.' 2266 << version.patchNumber; 2267 // branchName is never null -> 0th char is \0 if it is empty 2268 if (version.branchName[0]) { 2269 os << '-' << version.branchName 2270 << '.' << version.buildNumber; 2271 } 2272 return os; 2273 } 2274 2275 Version const& libraryVersion() { 2276 static Version version( 3, 6, 0, "", 0 ); 2277 return version; 2278 } 2279 2280} 2281 2282 2283 2284 2285namespace Catch { 2286 2287 const char* GeneratorException::what() const noexcept { 2288 return m_msg; 2289 } 2290 2291} // end namespace Catch 2292 2293 2294 2295 2296namespace Catch { 2297 2298 IGeneratorTracker::~IGeneratorTracker() = default; 2299 2300namespace Generators { 2301 2302namespace Detail { 2303 2304 [[noreturn]] 2305 void throw_generator_exception(char const* msg) { 2306 Catch::throw_exception(GeneratorException{ msg }); 2307 } 2308} // end namespace Detail 2309 2310 GeneratorUntypedBase::~GeneratorUntypedBase() = default; 2311 2312 IGeneratorTracker* acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) { 2313 return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); 2314 } 2315 2316 IGeneratorTracker* createGeneratorTracker( StringRef generatorName, 2317 SourceLineInfo lineInfo, 2318 GeneratorBasePtr&& generator ) { 2319 return getResultCapture().createGeneratorTracker( 2320 generatorName, lineInfo, CATCH_MOVE( generator ) ); 2321 } 2322 2323} // namespace Generators 2324} // namespace Catch 2325 2326 2327 2328 2329#include <random> 2330 2331namespace Catch { 2332 namespace Generators { 2333 namespace Detail { 2334 std::uint32_t getSeed() { return sharedRng()(); } 2335 } // namespace Detail 2336 2337 struct RandomFloatingGenerator<long double>::PImpl { 2338 PImpl( long double a, long double b, uint32_t seed ): 2339 rng( seed ), dist( a, b ) {} 2340 2341 Catch::SimplePcg32 rng; 2342 std::uniform_real_distribution<long double> dist; 2343 }; 2344 2345 RandomFloatingGenerator<long double>::RandomFloatingGenerator( 2346 long double a, long double b, std::uint32_t seed) : 2347 m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) { 2348 static_cast<void>( next() ); 2349 } 2350 2351 RandomFloatingGenerator<long double>::~RandomFloatingGenerator() = 2352 default; 2353 bool RandomFloatingGenerator<long double>::next() { 2354 m_current_number = m_pimpl->dist( m_pimpl->rng ); 2355 return true; 2356 } 2357 } // namespace Generators 2358} // namespace Catch 2359 2360 2361 2362 2363namespace Catch { 2364 IResultCapture::~IResultCapture() = default; 2365} 2366 2367 2368 2369 2370namespace Catch { 2371 IConfig::~IConfig() = default; 2372} 2373 2374 2375 2376 2377namespace Catch { 2378 IExceptionTranslator::~IExceptionTranslator() = default; 2379 IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; 2380} 2381 2382 2383 2384#include <string> 2385 2386namespace Catch { 2387 namespace Generators { 2388 2389 bool GeneratorUntypedBase::countedNext() { 2390 auto ret = next(); 2391 if ( ret ) { 2392 m_stringReprCache.clear(); 2393 ++m_currentElementIndex; 2394 } 2395 return ret; 2396 } 2397 2398 StringRef GeneratorUntypedBase::currentElementAsString() const { 2399 if ( m_stringReprCache.empty() ) { 2400 m_stringReprCache = stringifyImpl(); 2401 } 2402 return m_stringReprCache; 2403 } 2404 2405 } // namespace Generators 2406} // namespace Catch 2407 2408 2409 2410 2411namespace Catch { 2412 IRegistryHub::~IRegistryHub() = default; 2413 IMutableRegistryHub::~IMutableRegistryHub() = default; 2414} 2415 2416 2417 2418#include <cassert> 2419 2420namespace Catch { 2421 2422 ReporterConfig::ReporterConfig( 2423 IConfig const* _fullConfig, 2424 Detail::unique_ptr<IStream> _stream, 2425 ColourMode colourMode, 2426 std::map<std::string, std::string> customOptions ): 2427 m_stream( CATCH_MOVE(_stream) ), 2428 m_fullConfig( _fullConfig ), 2429 m_colourMode( colourMode ), 2430 m_customOptions( CATCH_MOVE( customOptions ) ) {} 2431 2432 Detail::unique_ptr<IStream> ReporterConfig::takeStream() && { 2433 assert( m_stream ); 2434 return CATCH_MOVE( m_stream ); 2435 } 2436 IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; } 2437 ColourMode ReporterConfig::colourMode() const { return m_colourMode; } 2438 2439 std::map<std::string, std::string> const& 2440 ReporterConfig::customOptions() const { 2441 return m_customOptions; 2442 } 2443 2444 ReporterConfig::~ReporterConfig() = default; 2445 2446 AssertionStats::AssertionStats( AssertionResult const& _assertionResult, 2447 std::vector<MessageInfo> const& _infoMessages, 2448 Totals const& _totals ) 2449 : assertionResult( _assertionResult ), 2450 infoMessages( _infoMessages ), 2451 totals( _totals ) 2452 { 2453 if( assertionResult.hasMessage() ) { 2454 // Copy message into messages list. 2455 // !TBD This should have been done earlier, somewhere 2456 MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); 2457 builder.m_info.message = static_cast<std::string>(assertionResult.getMessage()); 2458 2459 infoMessages.push_back( CATCH_MOVE(builder.m_info) ); 2460 } 2461 } 2462 2463 SectionStats::SectionStats( SectionInfo&& _sectionInfo, 2464 Counts const& _assertions, 2465 double _durationInSeconds, 2466 bool _missingAssertions ) 2467 : sectionInfo( CATCH_MOVE(_sectionInfo) ), 2468 assertions( _assertions ), 2469 durationInSeconds( _durationInSeconds ), 2470 missingAssertions( _missingAssertions ) 2471 {} 2472 2473 2474 TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, 2475 Totals const& _totals, 2476 std::string&& _stdOut, 2477 std::string&& _stdErr, 2478 bool _aborting ) 2479 : testInfo( &_testInfo ), 2480 totals( _totals ), 2481 stdOut( CATCH_MOVE(_stdOut) ), 2482 stdErr( CATCH_MOVE(_stdErr) ), 2483 aborting( _aborting ) 2484 {} 2485 2486 2487 TestRunStats::TestRunStats( TestRunInfo const& _runInfo, 2488 Totals const& _totals, 2489 bool _aborting ) 2490 : runInfo( _runInfo ), 2491 totals( _totals ), 2492 aborting( _aborting ) 2493 {} 2494 2495 IEventListener::~IEventListener() = default; 2496 2497} // end namespace Catch 2498 2499 2500 2501 2502namespace Catch { 2503 IReporterFactory::~IReporterFactory() = default; 2504 EventListenerFactory::~EventListenerFactory() = default; 2505} 2506 2507 2508 2509 2510namespace Catch { 2511 ITestCaseRegistry::~ITestCaseRegistry() = default; 2512} 2513 2514 2515 2516namespace Catch { 2517 2518 AssertionHandler::AssertionHandler 2519 ( StringRef macroName, 2520 SourceLineInfo const& lineInfo, 2521 StringRef capturedExpression, 2522 ResultDisposition::Flags resultDisposition ) 2523 : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, 2524 m_resultCapture( getResultCapture() ) 2525 { 2526 m_resultCapture.notifyAssertionStarted( m_assertionInfo ); 2527 } 2528 2529 void AssertionHandler::handleExpr( ITransientExpression const& expr ) { 2530 m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); 2531 } 2532 void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) { 2533 m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); 2534 } 2535 2536 auto AssertionHandler::allowThrows() const -> bool { 2537 return getCurrentContext().getConfig()->allowThrows(); 2538 } 2539 2540 void AssertionHandler::complete() { 2541 m_completed = true; 2542 if( m_reaction.shouldDebugBreak ) { 2543 2544 // If you find your debugger stopping you here then go one level up on the 2545 // call-stack for the code that caused it (typically a failed assertion) 2546 2547 // (To go back to the test and change execution, jump over the throw, next) 2548 CATCH_BREAK_INTO_DEBUGGER(); 2549 } 2550 if (m_reaction.shouldThrow) { 2551 throw_test_failure_exception(); 2552 } 2553 if ( m_reaction.shouldSkip ) { 2554 throw_test_skip_exception(); 2555 } 2556 } 2557 2558 void AssertionHandler::handleUnexpectedInflightException() { 2559 m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); 2560 } 2561 2562 void AssertionHandler::handleExceptionThrownAsExpected() { 2563 m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); 2564 } 2565 void AssertionHandler::handleExceptionNotThrownAsExpected() { 2566 m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); 2567 } 2568 2569 void AssertionHandler::handleUnexpectedExceptionNotThrown() { 2570 m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); 2571 } 2572 2573 void AssertionHandler::handleThrowingCallSkipped() { 2574 m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); 2575 } 2576 2577 // This is the overload that takes a string and infers the Equals matcher from it 2578 // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp 2579 void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ) { 2580 handleExceptionMatchExpr( handler, Matchers::Equals( str ) ); 2581 } 2582 2583} // namespace Catch 2584 2585 2586 2587 2588#include <algorithm> 2589 2590namespace Catch { 2591 namespace Detail { 2592 2593 bool CaseInsensitiveLess::operator()( StringRef lhs, 2594 StringRef rhs ) const { 2595 return std::lexicographical_compare( 2596 lhs.begin(), lhs.end(), 2597 rhs.begin(), rhs.end(), 2598 []( char l, char r ) { return toLower( l ) < toLower( r ); } ); 2599 } 2600 2601 bool 2602 CaseInsensitiveEqualTo::operator()( StringRef lhs, 2603 StringRef rhs ) const { 2604 return std::equal( 2605 lhs.begin(), lhs.end(), 2606 rhs.begin(), rhs.end(), 2607 []( char l, char r ) { return toLower( l ) == toLower( r ); } ); 2608 } 2609 2610 } // namespace Detail 2611} // namespace Catch 2612 2613 2614 2615 2616#include <algorithm> 2617#include <ostream> 2618 2619namespace { 2620 bool isOptPrefix( char c ) { 2621 return c == '-' 2622#ifdef CATCH_PLATFORM_WINDOWS 2623 || c == '/' 2624#endif 2625 ; 2626 } 2627 2628 Catch::StringRef normaliseOpt( Catch::StringRef optName ) { 2629 if ( optName[0] == '-' 2630#if defined(CATCH_PLATFORM_WINDOWS) 2631 || optName[0] == '/' 2632#endif 2633 ) { 2634 return optName.substr( 1, optName.size() ); 2635 } 2636 2637 return optName; 2638 } 2639 2640 static size_t find_first_separator(Catch::StringRef sr) { 2641 auto is_separator = []( char c ) { 2642 return c == ' ' || c == ':' || c == '='; 2643 }; 2644 size_t pos = 0; 2645 while (pos < sr.size()) { 2646 if (is_separator(sr[pos])) { return pos; } 2647 ++pos; 2648 } 2649 2650 return Catch::StringRef::npos; 2651 } 2652 2653} // namespace 2654 2655namespace Catch { 2656 namespace Clara { 2657 namespace Detail { 2658 2659 void TokenStream::loadBuffer() { 2660 m_tokenBuffer.clear(); 2661 2662 // Skip any empty strings 2663 while ( it != itEnd && it->empty() ) { 2664 ++it; 2665 } 2666 2667 if ( it != itEnd ) { 2668 StringRef next = *it; 2669 if ( isOptPrefix( next[0] ) ) { 2670 auto delimiterPos = find_first_separator(next); 2671 if ( delimiterPos != StringRef::npos ) { 2672 m_tokenBuffer.push_back( 2673 { TokenType::Option, 2674 next.substr( 0, delimiterPos ) } ); 2675 m_tokenBuffer.push_back( 2676 { TokenType::Argument, 2677 next.substr( delimiterPos + 1, next.size() ) } ); 2678 } else { 2679 if ( next[1] != '-' && next.size() > 2 ) { 2680 // Combined short args, e.g. "-ab" for "-a -b" 2681 for ( size_t i = 1; i < next.size(); ++i ) { 2682 m_tokenBuffer.push_back( 2683 { TokenType::Option, 2684 next.substr( i, 1 ) } ); 2685 } 2686 } else { 2687 m_tokenBuffer.push_back( 2688 { TokenType::Option, next } ); 2689 } 2690 } 2691 } else { 2692 m_tokenBuffer.push_back( 2693 { TokenType::Argument, next } ); 2694 } 2695 } 2696 } 2697 2698 TokenStream::TokenStream( Args const& args ): 2699 TokenStream( args.m_args.begin(), args.m_args.end() ) {} 2700 2701 TokenStream::TokenStream( Iterator it_, Iterator itEnd_ ): 2702 it( it_ ), itEnd( itEnd_ ) { 2703 loadBuffer(); 2704 } 2705 2706 TokenStream& TokenStream::operator++() { 2707 if ( m_tokenBuffer.size() >= 2 ) { 2708 m_tokenBuffer.erase( m_tokenBuffer.begin() ); 2709 } else { 2710 if ( it != itEnd ) 2711 ++it; 2712 loadBuffer(); 2713 } 2714 return *this; 2715 } 2716 2717 ParserResult convertInto( std::string const& source, 2718 std::string& target ) { 2719 target = source; 2720 return ParserResult::ok( ParseResultType::Matched ); 2721 } 2722 2723 ParserResult convertInto( std::string const& source, 2724 bool& target ) { 2725 std::string srcLC = toLower( source ); 2726 2727 if ( srcLC == "y" || srcLC == "1" || srcLC == "true" || 2728 srcLC == "yes" || srcLC == "on" ) { 2729 target = true; 2730 } else if ( srcLC == "n" || srcLC == "0" || srcLC == "false" || 2731 srcLC == "no" || srcLC == "off" ) { 2732 target = false; 2733 } else { 2734 return ParserResult::runtimeError( 2735 "Expected a boolean value but did not recognise: '" + 2736 source + '\'' ); 2737 } 2738 return ParserResult::ok( ParseResultType::Matched ); 2739 } 2740 2741 size_t ParserBase::cardinality() const { return 1; } 2742 2743 InternalParseResult ParserBase::parse( Args const& args ) const { 2744 return parse( static_cast<std::string>(args.exeName()), TokenStream( args ) ); 2745 } 2746 2747 ParseState::ParseState( ParseResultType type, 2748 TokenStream remainingTokens ): 2749 m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {} 2750 2751 ParserResult BoundFlagRef::setFlag( bool flag ) { 2752 m_ref = flag; 2753 return ParserResult::ok( ParseResultType::Matched ); 2754 } 2755 2756 ResultBase::~ResultBase() = default; 2757 2758 bool BoundRef::isContainer() const { return false; } 2759 2760 bool BoundRef::isFlag() const { return false; } 2761 2762 bool BoundFlagRefBase::isFlag() const { return true; } 2763 2764} // namespace Detail 2765 2766 Detail::InternalParseResult Arg::parse(std::string const&, 2767 Detail::TokenStream tokens) const { 2768 auto validationResult = validate(); 2769 if (!validationResult) 2770 return Detail::InternalParseResult(validationResult); 2771 2772 auto token = *tokens; 2773 if (token.type != Detail::TokenType::Argument) 2774 return Detail::InternalParseResult::ok(Detail::ParseState( 2775 ParseResultType::NoMatch, CATCH_MOVE(tokens))); 2776 2777 assert(!m_ref->isFlag()); 2778 auto valueRef = 2779 static_cast<Detail::BoundValueRefBase*>(m_ref.get()); 2780 2781 auto result = valueRef->setValue(static_cast<std::string>(token.token)); 2782 if ( !result ) 2783 return Detail::InternalParseResult( result ); 2784 else 2785 return Detail::InternalParseResult::ok( 2786 Detail::ParseState( ParseResultType::Matched, 2787 CATCH_MOVE( ++tokens ) ) ); 2788 } 2789 2790 Opt::Opt(bool& ref) : 2791 ParserRefImpl(std::make_shared<Detail::BoundFlagRef>(ref)) {} 2792 2793 Detail::HelpColumns Opt::getHelpColumns() const { 2794 ReusableStringStream oss; 2795 bool first = true; 2796 for (auto const& opt : m_optNames) { 2797 if (first) 2798 first = false; 2799 else 2800 oss << ", "; 2801 oss << opt; 2802 } 2803 if (!m_hint.empty()) 2804 oss << " <" << m_hint << '>'; 2805 return { oss.str(), m_description }; 2806 } 2807 2808 bool Opt::isMatch(StringRef optToken) const { 2809 auto normalisedToken = normaliseOpt(optToken); 2810 for (auto const& name : m_optNames) { 2811 if (normaliseOpt(name) == normalisedToken) 2812 return true; 2813 } 2814 return false; 2815 } 2816 2817 Detail::InternalParseResult Opt::parse(std::string const&, 2818 Detail::TokenStream tokens) const { 2819 auto validationResult = validate(); 2820 if (!validationResult) 2821 return Detail::InternalParseResult(validationResult); 2822 2823 if (tokens && 2824 tokens->type == Detail::TokenType::Option) { 2825 auto const& token = *tokens; 2826 if (isMatch(token.token)) { 2827 if (m_ref->isFlag()) { 2828 auto flagRef = 2829 static_cast<Detail::BoundFlagRefBase*>( 2830 m_ref.get()); 2831 auto result = flagRef->setFlag(true); 2832 if (!result) 2833 return Detail::InternalParseResult(result); 2834 if (result.value() == 2835 ParseResultType::ShortCircuitAll) 2836 return Detail::InternalParseResult::ok(Detail::ParseState( 2837 result.value(), CATCH_MOVE(tokens))); 2838 } else { 2839 auto valueRef = 2840 static_cast<Detail::BoundValueRefBase*>( 2841 m_ref.get()); 2842 ++tokens; 2843 if (!tokens) 2844 return Detail::InternalParseResult::runtimeError( 2845 "Expected argument following " + 2846 token.token); 2847 auto const& argToken = *tokens; 2848 if (argToken.type != Detail::TokenType::Argument) 2849 return Detail::InternalParseResult::runtimeError( 2850 "Expected argument following " + 2851 token.token); 2852 const auto result = valueRef->setValue(static_cast<std::string>(argToken.token)); 2853 if (!result) 2854 return Detail::InternalParseResult(result); 2855 if (result.value() == 2856 ParseResultType::ShortCircuitAll) 2857 return Detail::InternalParseResult::ok(Detail::ParseState( 2858 result.value(), CATCH_MOVE(tokens))); 2859 } 2860 return Detail::InternalParseResult::ok(Detail::ParseState( 2861 ParseResultType::Matched, CATCH_MOVE(++tokens))); 2862 } 2863 } 2864 return Detail::InternalParseResult::ok( 2865 Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); 2866 } 2867 2868 Detail::Result Opt::validate() const { 2869 if (m_optNames.empty()) 2870 return Detail::Result::logicError("No options supplied to Opt"); 2871 for (auto const& name : m_optNames) { 2872 if (name.empty()) 2873 return Detail::Result::logicError( 2874 "Option name cannot be empty"); 2875#ifdef CATCH_PLATFORM_WINDOWS 2876 if (name[0] != '-' && name[0] != '/') 2877 return Detail::Result::logicError( 2878 "Option name must begin with '-' or '/'"); 2879#else 2880 if (name[0] != '-') 2881 return Detail::Result::logicError( 2882 "Option name must begin with '-'"); 2883#endif 2884 } 2885 return ParserRefImpl::validate(); 2886 } 2887 2888 ExeName::ExeName() : 2889 m_name(std::make_shared<std::string>("<executable>")) {} 2890 2891 ExeName::ExeName(std::string& ref) : ExeName() { 2892 m_ref = std::make_shared<Detail::BoundValueRef<std::string>>(ref); 2893 } 2894 2895 Detail::InternalParseResult 2896 ExeName::parse(std::string const&, 2897 Detail::TokenStream tokens) const { 2898 return Detail::InternalParseResult::ok( 2899 Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens))); 2900 } 2901 2902 ParserResult ExeName::set(std::string const& newName) { 2903 auto lastSlash = newName.find_last_of("\\/"); 2904 auto filename = (lastSlash == std::string::npos) 2905 ? newName 2906 : newName.substr(lastSlash + 1); 2907 2908 *m_name = filename; 2909 if (m_ref) 2910 return m_ref->setValue(filename); 2911 else 2912 return ParserResult::ok(ParseResultType::Matched); 2913 } 2914 2915 2916 2917 2918 Parser& Parser::operator|=( Parser const& other ) { 2919 m_options.insert( m_options.end(), 2920 other.m_options.begin(), 2921 other.m_options.end() ); 2922 m_args.insert( 2923 m_args.end(), other.m_args.begin(), other.m_args.end() ); 2924 return *this; 2925 } 2926 2927 std::vector<Detail::HelpColumns> Parser::getHelpColumns() const { 2928 std::vector<Detail::HelpColumns> cols; 2929 cols.reserve( m_options.size() ); 2930 for ( auto const& o : m_options ) { 2931 cols.push_back(o.getHelpColumns()); 2932 } 2933 return cols; 2934 } 2935 2936 void Parser::writeToStream( std::ostream& os ) const { 2937 if ( !m_exeName.name().empty() ) { 2938 os << "usage:\n" 2939 << " " << m_exeName.name() << ' '; 2940 bool required = true, first = true; 2941 for ( auto const& arg : m_args ) { 2942 if ( first ) 2943 first = false; 2944 else 2945 os << ' '; 2946 if ( arg.isOptional() && required ) { 2947 os << '['; 2948 required = false; 2949 } 2950 os << '<' << arg.hint() << '>'; 2951 if ( arg.cardinality() == 0 ) 2952 os << " ... "; 2953 } 2954 if ( !required ) 2955 os << ']'; 2956 if ( !m_options.empty() ) 2957 os << " options"; 2958 os << "\n\nwhere options are:\n"; 2959 } 2960 2961 auto rows = getHelpColumns(); 2962 size_t consoleWidth = CATCH_CONFIG_CONSOLE_WIDTH; 2963 size_t optWidth = 0; 2964 for ( auto const& cols : rows ) 2965 optWidth = ( std::max )( optWidth, cols.left.size() + 2 ); 2966 2967 optWidth = ( std::min )( optWidth, consoleWidth / 2 ); 2968 2969 for ( auto& cols : rows ) { 2970 auto row = TextFlow::Column( CATCH_MOVE(cols.left) ) 2971 .width( optWidth ) 2972 .indent( 2 ) + 2973 TextFlow::Spacer( 4 ) + 2974 TextFlow::Column( static_cast<std::string>(cols.descriptions) ) 2975 .width( consoleWidth - 7 - optWidth ); 2976 os << row << '\n'; 2977 } 2978 } 2979 2980 Detail::Result Parser::validate() const { 2981 for ( auto const& opt : m_options ) { 2982 auto result = opt.validate(); 2983 if ( !result ) 2984 return result; 2985 } 2986 for ( auto const& arg : m_args ) { 2987 auto result = arg.validate(); 2988 if ( !result ) 2989 return result; 2990 } 2991 return Detail::Result::ok(); 2992 } 2993 2994 Detail::InternalParseResult 2995 Parser::parse( std::string const& exeName, 2996 Detail::TokenStream tokens ) const { 2997 2998 struct ParserInfo { 2999 ParserBase const* parser = nullptr; 3000 size_t count = 0; 3001 }; 3002 std::vector<ParserInfo> parseInfos; 3003 parseInfos.reserve( m_options.size() + m_args.size() ); 3004 for ( auto const& opt : m_options ) { 3005 parseInfos.push_back( { &opt, 0 } ); 3006 } 3007 for ( auto const& arg : m_args ) { 3008 parseInfos.push_back( { &arg, 0 } ); 3009 } 3010 3011 m_exeName.set( exeName ); 3012 3013 auto result = Detail::InternalParseResult::ok( 3014 Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) ); 3015 while ( result.value().remainingTokens() ) { 3016 bool tokenParsed = false; 3017 3018 for ( auto& parseInfo : parseInfos ) { 3019 if ( parseInfo.parser->cardinality() == 0 || 3020 parseInfo.count < parseInfo.parser->cardinality() ) { 3021 result = parseInfo.parser->parse( 3022 exeName, CATCH_MOVE(result).value().remainingTokens() ); 3023 if ( !result ) 3024 return result; 3025 if ( result.value().type() != 3026 ParseResultType::NoMatch ) { 3027 tokenParsed = true; 3028 ++parseInfo.count; 3029 break; 3030 } 3031 } 3032 } 3033 3034 if ( result.value().type() == ParseResultType::ShortCircuitAll ) 3035 return result; 3036 if ( !tokenParsed ) 3037 return Detail::InternalParseResult::runtimeError( 3038 "Unrecognised token: " + 3039 result.value().remainingTokens()->token ); 3040 } 3041 // !TBD Check missing required options 3042 return result; 3043 } 3044 3045 Args::Args(int argc, char const* const* argv) : 3046 m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} 3047 3048 Args::Args(std::initializer_list<StringRef> args) : 3049 m_exeName(*args.begin()), 3050 m_args(args.begin() + 1, args.end()) {} 3051 3052 3053 Help::Help( bool& showHelpFlag ): 3054 Opt( [&]( bool flag ) { 3055 showHelpFlag = flag; 3056 return ParserResult::ok( ParseResultType::ShortCircuitAll ); 3057 } ) { 3058 static_cast<Opt&> ( *this )( 3059 "display usage information" )["-?"]["-h"]["--help"] 3060 .optional(); 3061 } 3062 3063 } // namespace Clara 3064} // namespace Catch 3065 3066 3067 3068 3069#include <fstream> 3070#include <string> 3071 3072namespace Catch { 3073 3074 Clara::Parser makeCommandLineParser( ConfigData& config ) { 3075 3076 using namespace Clara; 3077 3078 auto const setWarning = [&]( std::string const& warning ) { 3079 if ( warning == "NoAssertions" ) { 3080 config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::NoAssertions); 3081 return ParserResult::ok( ParseResultType::Matched ); 3082 } else if ( warning == "UnmatchedTestSpec" ) { 3083 config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::UnmatchedTestSpec); 3084 return ParserResult::ok( ParseResultType::Matched ); 3085 } 3086 3087 return ParserResult ::runtimeError( 3088 "Unrecognised warning option: '" + warning + '\'' ); 3089 }; 3090 auto const loadTestNamesFromFile = [&]( std::string const& filename ) { 3091 std::ifstream f( filename.c_str() ); 3092 if( !f.is_open() ) 3093 return ParserResult::runtimeError( "Unable to load input file: '" + filename + '\'' ); 3094 3095 std::string line; 3096 while( std::getline( f, line ) ) { 3097 line = trim(line); 3098 if( !line.empty() && !startsWith( line, '#' ) ) { 3099 if( !startsWith( line, '"' ) ) 3100 line = '"' + CATCH_MOVE(line) + '"'; 3101 config.testsOrTags.push_back( line ); 3102 config.testsOrTags.emplace_back( "," ); 3103 } 3104 } 3105 //Remove comma in the end 3106 if(!config.testsOrTags.empty()) 3107 config.testsOrTags.erase( config.testsOrTags.end()-1 ); 3108 3109 return ParserResult::ok( ParseResultType::Matched ); 3110 }; 3111 auto const setTestOrder = [&]( std::string const& order ) { 3112 if( startsWith( "declared", order ) ) 3113 config.runOrder = TestRunOrder::Declared; 3114 else if( startsWith( "lexical", order ) ) 3115 config.runOrder = TestRunOrder::LexicographicallySorted; 3116 else if( startsWith( "random", order ) ) 3117 config.runOrder = TestRunOrder::Randomized; 3118 else 3119 return ParserResult::runtimeError( "Unrecognised ordering: '" + order + '\'' ); 3120 return ParserResult::ok( ParseResultType::Matched ); 3121 }; 3122 auto const setRngSeed = [&]( std::string const& seed ) { 3123 if( seed == "time" ) { 3124 config.rngSeed = generateRandomSeed(GenerateFrom::Time); 3125 return ParserResult::ok(ParseResultType::Matched); 3126 } else if (seed == "random-device") { 3127 config.rngSeed = generateRandomSeed(GenerateFrom::RandomDevice); 3128 return ParserResult::ok(ParseResultType::Matched); 3129 } 3130 3131 // TODO: ideally we should be parsing uint32_t directly 3132 // fix this later when we add new parse overload 3133 auto parsedSeed = parseUInt( seed, 0 ); 3134 if ( !parsedSeed ) { 3135 return ParserResult::runtimeError( "Could not parse '" + seed + "' as seed" ); 3136 } 3137 config.rngSeed = *parsedSeed; 3138 return ParserResult::ok( ParseResultType::Matched ); 3139 }; 3140 auto const setDefaultColourMode = [&]( std::string const& colourMode ) { 3141 Optional<ColourMode> maybeMode = Catch::Detail::stringToColourMode(toLower( colourMode )); 3142 if ( !maybeMode ) { 3143 return ParserResult::runtimeError( 3144 "colour mode must be one of: default, ansi, win32, " 3145 "or none. '" + 3146 colourMode + "' is not recognised" ); 3147 } 3148 auto mode = *maybeMode; 3149 if ( !isColourImplAvailable( mode ) ) { 3150 return ParserResult::runtimeError( 3151 "colour mode '" + colourMode + 3152 "' is not supported in this binary" ); 3153 } 3154 config.defaultColourMode = mode; 3155 return ParserResult::ok( ParseResultType::Matched ); 3156 }; 3157 auto const setWaitForKeypress = [&]( std::string const& keypress ) { 3158 auto keypressLc = toLower( keypress ); 3159 if (keypressLc == "never") 3160 config.waitForKeypress = WaitForKeypress::Never; 3161 else if( keypressLc == "start" ) 3162 config.waitForKeypress = WaitForKeypress::BeforeStart; 3163 else if( keypressLc == "exit" ) 3164 config.waitForKeypress = WaitForKeypress::BeforeExit; 3165 else if( keypressLc == "both" ) 3166 config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; 3167 else 3168 return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" ); 3169 return ParserResult::ok( ParseResultType::Matched ); 3170 }; 3171 auto const setVerbosity = [&]( std::string const& verbosity ) { 3172 auto lcVerbosity = toLower( verbosity ); 3173 if( lcVerbosity == "quiet" ) 3174 config.verbosity = Verbosity::Quiet; 3175 else if( lcVerbosity == "normal" ) 3176 config.verbosity = Verbosity::Normal; 3177 else if( lcVerbosity == "high" ) 3178 config.verbosity = Verbosity::High; 3179 else 3180 return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + '\'' ); 3181 return ParserResult::ok( ParseResultType::Matched ); 3182 }; 3183 auto const setReporter = [&]( std::string const& userReporterSpec ) { 3184 if ( userReporterSpec.empty() ) { 3185 return ParserResult::runtimeError( "Received empty reporter spec." ); 3186 } 3187 3188 Optional<ReporterSpec> parsed = 3189 parseReporterSpec( userReporterSpec ); 3190 if ( !parsed ) { 3191 return ParserResult::runtimeError( 3192 "Could not parse reporter spec '" + userReporterSpec + 3193 "'" ); 3194 } 3195 3196 auto const& reporterSpec = *parsed; 3197 3198 auto const& factories = 3199 getRegistryHub().getReporterRegistry().getFactories(); 3200 auto result = factories.find( reporterSpec.name() ); 3201 3202 if ( result == factories.end() ) { 3203 return ParserResult::runtimeError( 3204 "Unrecognized reporter, '" + reporterSpec.name() + 3205 "'. Check available with --list-reporters" ); 3206 } 3207 3208 3209 const bool hadOutputFile = reporterSpec.outputFile().some(); 3210 config.reporterSpecifications.push_back( CATCH_MOVE( *parsed ) ); 3211 // It would be enough to check this only once at the very end, but 3212 // there is not a place where we could call this check, so do it 3213 // every time it could fail. For valid inputs, this is still called 3214 // at most once. 3215 if (!hadOutputFile) { 3216 int n_reporters_without_file = 0; 3217 for (auto const& spec : config.reporterSpecifications) { 3218 if (spec.outputFile().none()) { 3219 n_reporters_without_file++; 3220 } 3221 } 3222 if (n_reporters_without_file > 1) { 3223 return ParserResult::runtimeError( "Only one reporter may have unspecified output file." ); 3224 } 3225 } 3226 3227 return ParserResult::ok( ParseResultType::Matched ); 3228 }; 3229 auto const setShardCount = [&]( std::string const& shardCount ) { 3230 auto parsedCount = parseUInt( shardCount ); 3231 if ( !parsedCount ) { 3232 return ParserResult::runtimeError( 3233 "Could not parse '" + shardCount + "' as shard count" ); 3234 } 3235 if ( *parsedCount == 0 ) { 3236 return ParserResult::runtimeError( 3237 "Shard count must be positive" ); 3238 } 3239 config.shardCount = *parsedCount; 3240 return ParserResult::ok( ParseResultType::Matched ); 3241 }; 3242 3243 auto const setShardIndex = [&](std::string const& shardIndex) { 3244 auto parsedIndex = parseUInt( shardIndex ); 3245 if ( !parsedIndex ) { 3246 return ParserResult::runtimeError( 3247 "Could not parse '" + shardIndex + "' as shard index" ); 3248 } 3249 config.shardIndex = *parsedIndex; 3250 return ParserResult::ok( ParseResultType::Matched ); 3251 }; 3252 3253 auto cli 3254 = ExeName( config.processName ) 3255 | Help( config.showHelp ) 3256 | Opt( config.showSuccessfulTests ) 3257 ["-s"]["--success"] 3258 ( "include successful tests in output" ) 3259 | Opt( config.shouldDebugBreak ) 3260 ["-b"]["--break"] 3261 ( "break into debugger on failure" ) 3262 | Opt( config.noThrow ) 3263 ["-e"]["--nothrow"] 3264 ( "skip exception tests" ) 3265 | Opt( config.showInvisibles ) 3266 ["-i"]["--invisibles"] 3267 ( "show invisibles (tabs, newlines)" ) 3268 | Opt( config.defaultOutputFilename, "filename" ) 3269 ["-o"]["--out"] 3270 ( "default output filename" ) 3271 | Opt( accept_many, setReporter, "name[::key=value]*" ) 3272 ["-r"]["--reporter"] 3273 ( "reporter to use (defaults to console)" ) 3274 | Opt( config.name, "name" ) 3275 ["-n"]["--name"] 3276 ( "suite name" ) 3277 | Opt( [&]( bool ){ config.abortAfter = 1; } ) 3278 ["-a"]["--abort"] 3279 ( "abort at first failure" ) 3280 | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) 3281 ["-x"]["--abortx"] 3282 ( "abort after x failures" ) 3283 | Opt( accept_many, setWarning, "warning name" ) 3284 ["-w"]["--warn"] 3285 ( "enable warnings" ) 3286 | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) 3287 ["-d"]["--durations"] 3288 ( "show test durations" ) 3289 | Opt( config.minDuration, "seconds" ) 3290 ["-D"]["--min-duration"] 3291 ( "show test durations for tests taking at least the given number of seconds" ) 3292 | Opt( loadTestNamesFromFile, "filename" ) 3293 ["-f"]["--input-file"] 3294 ( "load test names to run from a file" ) 3295 | Opt( config.filenamesAsTags ) 3296 ["-#"]["--filenames-as-tags"] 3297 ( "adds a tag for the filename" ) 3298 | Opt( config.sectionsToRun, "section name" ) 3299 ["-c"]["--section"] 3300 ( "specify section to run" ) 3301 | Opt( setVerbosity, "quiet|normal|high" ) 3302 ["-v"]["--verbosity"] 3303 ( "set output verbosity" ) 3304 | Opt( config.listTests ) 3305 ["--list-tests"] 3306 ( "list all/matching test cases" ) 3307 | Opt( config.listTags ) 3308 ["--list-tags"] 3309 ( "list all/matching tags" ) 3310 | Opt( config.listReporters ) 3311 ["--list-reporters"] 3312 ( "list all available reporters" ) 3313 | Opt( config.listListeners ) 3314 ["--list-listeners"] 3315 ( "list all listeners" ) 3316 | Opt( setTestOrder, "decl|lex|rand" ) 3317 ["--order"] 3318 ( "test case order (defaults to decl)" ) 3319 | Opt( setRngSeed, "'time'|'random-device'|number" ) 3320 ["--rng-seed"] 3321 ( "set a specific seed for random numbers" ) 3322 | Opt( setDefaultColourMode, "ansi|win32|none|default" ) 3323 ["--colour-mode"] 3324 ( "what color mode should be used as default" ) 3325 | Opt( config.libIdentify ) 3326 ["--libidentify"] 3327 ( "report name and version according to libidentify standard" ) 3328 | Opt( setWaitForKeypress, "never|start|exit|both" ) 3329 ["--wait-for-keypress"] 3330 ( "waits for a keypress before exiting" ) 3331 | Opt( config.skipBenchmarks) 3332 ["--skip-benchmarks"] 3333 ( "disable running benchmarks") 3334 | Opt( config.benchmarkSamples, "samples" ) 3335 ["--benchmark-samples"] 3336 ( "number of samples to collect (default: 100)" ) 3337 | Opt( config.benchmarkResamples, "resamples" ) 3338 ["--benchmark-resamples"] 3339 ( "number of resamples for the bootstrap (default: 100000)" ) 3340 | Opt( config.benchmarkConfidenceInterval, "confidence interval" ) 3341 ["--benchmark-confidence-interval"] 3342 ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" ) 3343 | Opt( config.benchmarkNoAnalysis ) 3344 ["--benchmark-no-analysis"] 3345 ( "perform only measurements; do not perform any analysis" ) 3346 | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" ) 3347 ["--benchmark-warmup-time"] 3348 ( "amount of time in milliseconds spent on warming up each test (default: 100)" ) 3349 | Opt( setShardCount, "shard count" ) 3350 ["--shard-count"] 3351 ( "split the tests to execute into this many groups" ) 3352 | Opt( setShardIndex, "shard index" ) 3353 ["--shard-index"] 3354 ( "index of the group of tests to execute (see --shard-count)" ) 3355 | Opt( config.allowZeroTests ) 3356 ["--allow-running-no-tests"] 3357 ( "Treat 'No tests run' as a success" ) 3358 | Arg( config.testsOrTags, "test name|pattern|tags" ) 3359 ( "which test or tests to use" ); 3360 3361 return cli; 3362 } 3363 3364} // end namespace Catch 3365 3366 3367#if defined(__clang__) 3368# pragma clang diagnostic push 3369# pragma clang diagnostic ignored "-Wexit-time-destructors" 3370#endif 3371 3372 3373 3374#include <cassert> 3375#include <ostream> 3376#include <utility> 3377 3378namespace Catch { 3379 3380 ColourImpl::~ColourImpl() = default; 3381 3382 ColourImpl::ColourGuard ColourImpl::guardColour( Colour::Code colourCode ) { 3383 return ColourGuard(colourCode, this ); 3384 } 3385 3386 void ColourImpl::ColourGuard::engageImpl( std::ostream& stream ) { 3387 assert( &stream == &m_colourImpl->m_stream->stream() && 3388 "Engaging colour guard for different stream than used by the " 3389 "parent colour implementation" ); 3390 static_cast<void>( stream ); 3391 3392 m_engaged = true; 3393 m_colourImpl->use( m_code ); 3394 } 3395 3396 ColourImpl::ColourGuard::ColourGuard( Colour::Code code, 3397 ColourImpl const* colour ): 3398 m_colourImpl( colour ), m_code( code ) { 3399 } 3400 ColourImpl::ColourGuard::ColourGuard( ColourGuard&& rhs ) noexcept: 3401 m_colourImpl( rhs.m_colourImpl ), 3402 m_code( rhs.m_code ), 3403 m_engaged( rhs.m_engaged ) { 3404 rhs.m_engaged = false; 3405 } 3406 ColourImpl::ColourGuard& 3407 ColourImpl::ColourGuard::operator=( ColourGuard&& rhs ) noexcept { 3408 using std::swap; 3409 swap( m_colourImpl, rhs.m_colourImpl ); 3410 swap( m_code, rhs.m_code ); 3411 swap( m_engaged, rhs.m_engaged ); 3412 3413 return *this; 3414 } 3415 ColourImpl::ColourGuard::~ColourGuard() { 3416 if ( m_engaged ) { 3417 m_colourImpl->use( Colour::None ); 3418 } 3419 } 3420 3421 ColourImpl::ColourGuard& 3422 ColourImpl::ColourGuard::engage( std::ostream& stream ) & { 3423 engageImpl( stream ); 3424 return *this; 3425 } 3426 3427 ColourImpl::ColourGuard&& 3428 ColourImpl::ColourGuard::engage( std::ostream& stream ) && { 3429 engageImpl( stream ); 3430 return CATCH_MOVE(*this); 3431 } 3432 3433 namespace { 3434 //! A do-nothing implementation of colour, used as fallback for unknown 3435 //! platforms, and when the user asks to deactivate all colours. 3436 class NoColourImpl final : public ColourImpl { 3437 public: 3438 NoColourImpl( IStream* stream ): ColourImpl( stream ) {} 3439 3440 private: 3441 void use( Colour::Code ) const override {} 3442 }; 3443 } // namespace 3444 3445 3446} // namespace Catch 3447 3448 3449#if defined ( CATCH_CONFIG_COLOUR_WIN32 ) ///////////////////////////////////////// 3450 3451namespace Catch { 3452namespace { 3453 3454 class Win32ColourImpl final : public ColourImpl { 3455 public: 3456 Win32ColourImpl(IStream* stream): 3457 ColourImpl(stream) { 3458 CONSOLE_SCREEN_BUFFER_INFO csbiInfo; 3459 GetConsoleScreenBufferInfo( GetStdHandle( STD_OUTPUT_HANDLE ), 3460 &csbiInfo ); 3461 originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); 3462 originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); 3463 } 3464 3465 static bool useImplementationForStream(IStream const& stream) { 3466 // Win32 text colour APIs can only be used on console streams 3467 // We cannot check that the output hasn't been redirected, 3468 // so we just check that the original stream is console stream. 3469 return stream.isConsole(); 3470 } 3471 3472 private: 3473 void use( Colour::Code _colourCode ) const override { 3474 switch( _colourCode ) { 3475 case Colour::None: return setTextAttribute( originalForegroundAttributes ); 3476 case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); 3477 case Colour::Red: return setTextAttribute( FOREGROUND_RED ); 3478 case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); 3479 case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); 3480 case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); 3481 case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); 3482 case Colour::Grey: return setTextAttribute( 0 ); 3483 3484 case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); 3485 case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); 3486 case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); 3487 case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); 3488 case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); 3489 3490 case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); 3491 3492 default: 3493 CATCH_ERROR( "Unknown colour requested" ); 3494 } 3495 } 3496 3497 void setTextAttribute( WORD _textAttribute ) const { 3498 SetConsoleTextAttribute( GetStdHandle( STD_OUTPUT_HANDLE ), 3499 _textAttribute | 3500 originalBackgroundAttributes ); 3501 } 3502 WORD originalForegroundAttributes; 3503 WORD originalBackgroundAttributes; 3504 }; 3505 3506} // end anon namespace 3507} // end namespace Catch 3508 3509#endif // Windows/ ANSI/ None 3510 3511 3512#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) 3513# define CATCH_INTERNAL_HAS_ISATTY 3514# include <unistd.h> 3515#endif 3516 3517namespace Catch { 3518namespace { 3519 3520 class ANSIColourImpl final : public ColourImpl { 3521 public: 3522 ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} 3523 3524 static bool useImplementationForStream(IStream const& stream) { 3525 // This is kinda messy due to trying to support a bunch of 3526 // different platforms at once. 3527 // The basic idea is that if we are asked to do autodetection (as 3528 // opposed to being told to use posixy colours outright), then we 3529 // only want to use the colours if we are writing to console. 3530 // However, console might be redirected, so we make an attempt at 3531 // checking for that on platforms where we know how to do that. 3532 bool useColour = stream.isConsole(); 3533#if defined( CATCH_INTERNAL_HAS_ISATTY ) && \ 3534 !( defined( __DJGPP__ ) && defined( __STRICT_ANSI__ ) ) 3535 ErrnoGuard _; // for isatty 3536 useColour = useColour && isatty( STDOUT_FILENO ); 3537# endif 3538# if defined( CATCH_PLATFORM_MAC ) || defined( CATCH_PLATFORM_IPHONE ) 3539 useColour = useColour && !isDebuggerActive(); 3540# endif 3541 3542 return useColour; 3543 } 3544 3545 private: 3546 void use( Colour::Code _colourCode ) const override { 3547 auto setColour = [&out = 3548 m_stream->stream()]( char const* escapeCode ) { 3549 // The escape sequence must be flushed to console, otherwise 3550 // if stdin and stderr are intermixed, we'd get accidentally 3551 // coloured output. 3552 out << '\033' << escapeCode << std::flush; 3553 }; 3554 switch( _colourCode ) { 3555 case Colour::None: 3556 case Colour::White: return setColour( "[0m" ); 3557 case Colour::Red: return setColour( "[0;31m" ); 3558 case Colour::Green: return setColour( "[0;32m" ); 3559 case Colour::Blue: return setColour( "[0;34m" ); 3560 case Colour::Cyan: return setColour( "[0;36m" ); 3561 case Colour::Yellow: return setColour( "[0;33m" ); 3562 case Colour::Grey: return setColour( "[1;30m" ); 3563 3564 case Colour::LightGrey: return setColour( "[0;37m" ); 3565 case Colour::BrightRed: return setColour( "[1;31m" ); 3566 case Colour::BrightGreen: return setColour( "[1;32m" ); 3567 case Colour::BrightWhite: return setColour( "[1;37m" ); 3568 case Colour::BrightYellow: return setColour( "[1;33m" ); 3569 3570 case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); 3571 default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); 3572 } 3573 } 3574 }; 3575 3576} // end anon namespace 3577} // end namespace Catch 3578 3579namespace Catch { 3580 3581 Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection, 3582 IStream* stream ) { 3583#if defined( CATCH_CONFIG_COLOUR_WIN32 ) 3584 if ( colourSelection == ColourMode::Win32 ) { 3585 return Detail::make_unique<Win32ColourImpl>( stream ); 3586 } 3587#endif 3588 if ( colourSelection == ColourMode::ANSI ) { 3589 return Detail::make_unique<ANSIColourImpl>( stream ); 3590 } 3591 if ( colourSelection == ColourMode::None ) { 3592 return Detail::make_unique<NoColourImpl>( stream ); 3593 } 3594 3595 if ( colourSelection == ColourMode::PlatformDefault) { 3596#if defined( CATCH_CONFIG_COLOUR_WIN32 ) 3597 if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { 3598 return Detail::make_unique<Win32ColourImpl>( stream ); 3599 } 3600#endif 3601 if ( ANSIColourImpl::useImplementationForStream( *stream ) ) { 3602 return Detail::make_unique<ANSIColourImpl>( stream ); 3603 } 3604 return Detail::make_unique<NoColourImpl>( stream ); 3605 } 3606 3607 CATCH_ERROR( "Could not create colour impl for selection " << static_cast<int>(colourSelection) ); 3608 } 3609 3610 bool isColourImplAvailable( ColourMode colourSelection ) { 3611 switch ( colourSelection ) { 3612#if defined( CATCH_CONFIG_COLOUR_WIN32 ) 3613 case ColourMode::Win32: 3614#endif 3615 case ColourMode::ANSI: 3616 case ColourMode::None: 3617 case ColourMode::PlatformDefault: 3618 return true; 3619 default: 3620 return false; 3621 } 3622 } 3623 3624 3625} // end namespace Catch 3626 3627#if defined(__clang__) 3628# pragma clang diagnostic pop 3629#endif 3630 3631 3632 3633 3634namespace Catch { 3635 3636 Context* Context::currentContext = nullptr; 3637 3638 void cleanUpContext() { 3639 delete Context::currentContext; 3640 Context::currentContext = nullptr; 3641 } 3642 void Context::createContext() { 3643 currentContext = new Context(); 3644 } 3645 3646 Context& getCurrentMutableContext() { 3647 if ( !Context::currentContext ) { Context::createContext(); } 3648 // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) 3649 return *Context::currentContext; 3650 } 3651 3652 void Context::setResultCapture( IResultCapture* resultCapture ) { 3653 m_resultCapture = resultCapture; 3654 } 3655 3656 void Context::setConfig( IConfig const* config ) { m_config = config; } 3657 3658 SimplePcg32& sharedRng() { 3659 static SimplePcg32 s_rng; 3660 return s_rng; 3661 } 3662 3663} 3664 3665 3666 3667 3668 3669#include <ostream> 3670 3671#if defined(CATCH_CONFIG_ANDROID_LOGWRITE) 3672#include <android/log.h> 3673 3674 namespace Catch { 3675 void writeToDebugConsole( std::string const& text ) { 3676 __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() ); 3677 } 3678 } 3679 3680#elif defined(CATCH_PLATFORM_WINDOWS) 3681 3682 namespace Catch { 3683 void writeToDebugConsole( std::string const& text ) { 3684 ::OutputDebugStringA( text.c_str() ); 3685 } 3686 } 3687 3688#else 3689 3690 namespace Catch { 3691 void writeToDebugConsole( std::string const& text ) { 3692 // !TBD: Need a version for Mac/ XCode and other IDEs 3693 Catch::cout() << text; 3694 } 3695 } 3696 3697#endif // Platform 3698 3699 3700 3701#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) 3702 3703# include <cassert> 3704# include <sys/types.h> 3705# include <unistd.h> 3706# include <cstddef> 3707# include <ostream> 3708 3709#ifdef __apple_build_version__ 3710 // These headers will only compile with AppleClang (XCode) 3711 // For other compilers (Clang, GCC, ... ) we need to exclude them 3712# include <sys/sysctl.h> 3713#endif 3714 3715 namespace Catch { 3716 #ifdef __apple_build_version__ 3717 // The following function is taken directly from the following technical note: 3718 // https://developer.apple.com/library/archive/qa/qa1361/_index.html 3719 3720 // Returns true if the current process is being debugged (either 3721 // running under the debugger or has a debugger attached post facto). 3722 bool isDebuggerActive(){ 3723 int mib[4]; 3724 struct kinfo_proc info; 3725 std::size_t size; 3726 3727 // Initialize the flags so that, if sysctl fails for some bizarre 3728 // reason, we get a predictable result. 3729 3730 info.kp_proc.p_flag = 0; 3731 3732 // Initialize mib, which tells sysctl the info we want, in this case 3733 // we're looking for information about a specific process ID. 3734 3735 mib[0] = CTL_KERN; 3736 mib[1] = KERN_PROC; 3737 mib[2] = KERN_PROC_PID; 3738 mib[3] = getpid(); 3739 3740 // Call sysctl. 3741 3742 size = sizeof(info); 3743 if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { 3744 Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush; 3745 return false; 3746 } 3747 3748 // We're being debugged if the P_TRACED flag is set. 3749 3750 return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); 3751 } 3752 #else 3753 bool isDebuggerActive() { 3754 // We need to find another way to determine this for non-appleclang compilers on macOS 3755 return false; 3756 } 3757 #endif 3758 } // namespace Catch 3759 3760#elif defined(CATCH_PLATFORM_LINUX) 3761 #include <fstream> 3762 #include <string> 3763 3764 namespace Catch{ 3765 // The standard POSIX way of detecting a debugger is to attempt to 3766 // ptrace() the process, but this needs to be done from a child and not 3767 // this process itself to still allow attaching to this process later 3768 // if wanted, so is rather heavy. Under Linux we have the PID of the 3769 // "debugger" (which doesn't need to be gdb, of course, it could also 3770 // be strace, for example) in /proc/$PID/status, so just get it from 3771 // there instead. 3772 bool isDebuggerActive(){ 3773 // Libstdc++ has a bug, where std::ifstream sets errno to 0 3774 // This way our users can properly assert over errno values 3775 ErrnoGuard guard; 3776 std::ifstream in("/proc/self/status"); 3777 for( std::string line; std::getline(in, line); ) { 3778 static const int PREFIX_LEN = 11; 3779 if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { 3780 // We're traced if the PID is not 0 and no other PID starts 3781 // with 0 digit, so it's enough to check for just a single 3782 // character. 3783 return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; 3784 } 3785 } 3786 3787 return false; 3788 } 3789 } // namespace Catch 3790#elif defined(_MSC_VER) 3791 extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); 3792 namespace Catch { 3793 bool isDebuggerActive() { 3794 return IsDebuggerPresent() != 0; 3795 } 3796 } 3797#elif defined(__MINGW32__) 3798 extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); 3799 namespace Catch { 3800 bool isDebuggerActive() { 3801 return IsDebuggerPresent() != 0; 3802 } 3803 } 3804#else 3805 namespace Catch { 3806 bool isDebuggerActive() { return false; } 3807 } 3808#endif // Platform 3809 3810 3811 3812 3813namespace Catch { 3814 3815 void ITransientExpression::streamReconstructedExpression( 3816 std::ostream& os ) const { 3817 // We can't make this function pure virtual to keep ITransientExpression 3818 // constexpr, so we write error message instead 3819 os << "Some class derived from ITransientExpression without overriding streamReconstructedExpression"; 3820 } 3821 3822 void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { 3823 if( lhs.size() + rhs.size() < 40 && 3824 lhs.find('\n') == std::string::npos && 3825 rhs.find('\n') == std::string::npos ) 3826 os << lhs << ' ' << op << ' ' << rhs; 3827 else 3828 os << lhs << '\n' << op << '\n' << rhs; 3829 } 3830} 3831 3832 3833 3834#include <stdexcept> 3835 3836 3837namespace Catch { 3838#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) 3839 [[noreturn]] 3840 void throw_exception(std::exception const& e) { 3841 Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" 3842 << "The message was: " << e.what() << '\n'; 3843 std::terminate(); 3844 } 3845#endif 3846 3847 [[noreturn]] 3848 void throw_logic_error(std::string const& msg) { 3849 throw_exception(std::logic_error(msg)); 3850 } 3851 3852 [[noreturn]] 3853 void throw_domain_error(std::string const& msg) { 3854 throw_exception(std::domain_error(msg)); 3855 } 3856 3857 [[noreturn]] 3858 void throw_runtime_error(std::string const& msg) { 3859 throw_exception(std::runtime_error(msg)); 3860 } 3861 3862 3863 3864} // namespace Catch; 3865 3866 3867 3868#include <cassert> 3869 3870namespace Catch { 3871 3872 IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default; 3873 3874 namespace Detail { 3875 3876 namespace { 3877 // Extracts the actual name part of an enum instance 3878 // In other words, it returns the Blue part of Bikeshed::Colour::Blue 3879 StringRef extractInstanceName(StringRef enumInstance) { 3880 // Find last occurrence of ":" 3881 size_t name_start = enumInstance.size(); 3882 while (name_start > 0 && enumInstance[name_start - 1] != ':') { 3883 --name_start; 3884 } 3885 return enumInstance.substr(name_start, enumInstance.size() - name_start); 3886 } 3887 } 3888 3889 std::vector<StringRef> parseEnums( StringRef enums ) { 3890 auto enumValues = splitStringRef( enums, ',' ); 3891 std::vector<StringRef> parsed; 3892 parsed.reserve( enumValues.size() ); 3893 for( auto const& enumValue : enumValues ) { 3894 parsed.push_back(trim(extractInstanceName(enumValue))); 3895 } 3896 return parsed; 3897 } 3898 3899 EnumInfo::~EnumInfo() = default; 3900 3901 StringRef EnumInfo::lookup( int value ) const { 3902 for( auto const& valueToName : m_values ) { 3903 if( valueToName.first == value ) 3904 return valueToName.second; 3905 } 3906 return "{** unexpected enum value **}"_sr; 3907 } 3908 3909 Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { 3910 auto enumInfo = Catch::Detail::make_unique<EnumInfo>(); 3911 enumInfo->m_name = enumName; 3912 enumInfo->m_values.reserve( values.size() ); 3913 3914 const auto valueNames = Catch::Detail::parseEnums( allValueNames ); 3915 assert( valueNames.size() == values.size() ); 3916 std::size_t i = 0; 3917 for( auto value : values ) 3918 enumInfo->m_values.emplace_back(value, valueNames[i++]); 3919 3920 return enumInfo; 3921 } 3922 3923 EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) { 3924 m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); 3925 return *m_enumInfos.back(); 3926 } 3927 3928 } // Detail 3929} // Catch 3930 3931 3932 3933 3934 3935#include <cerrno> 3936 3937namespace Catch { 3938 ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} 3939 ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } 3940} 3941 3942 3943 3944#include <exception> 3945 3946namespace Catch { 3947 3948#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 3949 namespace { 3950 static std::string tryTranslators( 3951 std::vector< 3952 Detail::unique_ptr<IExceptionTranslator const>> const& translators ) { 3953 if ( translators.empty() ) { 3954 std::rethrow_exception( std::current_exception() ); 3955 } else { 3956 return translators[0]->translate( translators.begin() + 1, 3957 translators.end() ); 3958 } 3959 } 3960 3961 } 3962#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 3963 3964 ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default; 3965 3966 void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) { 3967 m_translators.push_back( CATCH_MOVE( translator ) ); 3968 } 3969 3970#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 3971 std::string ExceptionTranslatorRegistry::translateActiveException() const { 3972 // Compiling a mixed mode project with MSVC means that CLR 3973 // exceptions will be caught in (...) as well. However, these do 3974 // do not fill-in std::current_exception and thus lead to crash 3975 // when attempting rethrow. 3976 // /EHa switch also causes structured exceptions to be caught 3977 // here, but they fill-in current_exception properly, so 3978 // at worst the output should be a little weird, instead of 3979 // causing a crash. 3980 if ( std::current_exception() == nullptr ) { 3981 return "Non C++ exception. Possibly a CLR exception."; 3982 } 3983 3984 // First we try user-registered translators. If none of them can 3985 // handle the exception, it will be rethrown handled by our defaults. 3986 try { 3987 return tryTranslators(m_translators); 3988 } 3989 // To avoid having to handle TFE explicitly everywhere, we just 3990 // rethrow it so that it goes back up the caller. 3991 catch( TestFailureException& ) { 3992 std::rethrow_exception(std::current_exception()); 3993 } 3994 catch( TestSkipException& ) { 3995 std::rethrow_exception(std::current_exception()); 3996 } 3997 catch( std::exception const& ex ) { 3998 return ex.what(); 3999 } 4000 catch( std::string const& msg ) { 4001 return msg; 4002 } 4003 catch( const char* msg ) { 4004 return msg; 4005 } 4006 catch(...) { 4007 return "Unknown exception"; 4008 } 4009 } 4010 4011#else // ^^ Exceptions are enabled // Exceptions are disabled vv 4012 std::string ExceptionTranslatorRegistry::translateActiveException() const { 4013 CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); 4014 } 4015#endif 4016 4017} 4018 4019 4020 4021/** \file 4022 * This file provides platform specific implementations of FatalConditionHandler 4023 * 4024 * This means that there is a lot of conditional compilation, and platform 4025 * specific code. Currently, Catch2 supports a dummy handler (if no 4026 * handler is desired), and 2 platform specific handlers: 4027 * * Windows' SEH 4028 * * POSIX signals 4029 * 4030 * Consequently, various pieces of code below are compiled if either of 4031 * the platform specific handlers is enabled, or if none of them are 4032 * enabled. It is assumed that both cannot be enabled at the same time, 4033 * and doing so should cause a compilation error. 4034 * 4035 * If another platform specific handler is added, the compile guards 4036 * below will need to be updated taking these assumptions into account. 4037 */ 4038 4039 4040 4041#include <algorithm> 4042 4043#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) 4044 4045namespace Catch { 4046 4047 // If neither SEH nor signal handling is required, the handler impls 4048 // do not have to do anything, and can be empty. 4049 void FatalConditionHandler::engage_platform() {} 4050 void FatalConditionHandler::disengage_platform() noexcept {} 4051 FatalConditionHandler::FatalConditionHandler() = default; 4052 FatalConditionHandler::~FatalConditionHandler() = default; 4053 4054} // end namespace Catch 4055 4056#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS 4057 4058#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) 4059#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" 4060#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS 4061 4062#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) 4063 4064namespace { 4065 //! Signals fatal error message to the run context 4066 void reportFatal( char const * const message ) { 4067 Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); 4068 } 4069 4070 //! Minimal size Catch2 needs for its own fatal error handling. 4071 //! Picked empirically, so it might not be sufficient on all 4072 //! platforms, and for all configurations. 4073 constexpr std::size_t minStackSizeForErrors = 32 * 1024; 4074} // end unnamed namespace 4075 4076#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS 4077 4078#if defined( CATCH_CONFIG_WINDOWS_SEH ) 4079 4080namespace Catch { 4081 4082 struct SignalDefs { DWORD id; const char* name; }; 4083 4084 // There is no 1-1 mapping between signals and windows exceptions. 4085 // Windows can easily distinguish between SO and SigSegV, 4086 // but SigInt, SigTerm, etc are handled differently. 4087 static SignalDefs signalDefs[] = { 4088 { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, 4089 { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, 4090 { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, 4091 { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, 4092 }; 4093 4094 static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) { 4095 for (auto const& def : signalDefs) { 4096 if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { 4097 reportFatal(def.name); 4098 } 4099 } 4100 // If its not an exception we care about, pass it along. 4101 // This stops us from eating debugger breaks etc. 4102 return EXCEPTION_CONTINUE_SEARCH; 4103 } 4104 4105 // Since we do not support multiple instantiations, we put these 4106 // into global variables and rely on cleaning them up in outlined 4107 // constructors/destructors 4108 static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr; 4109 4110 4111 // For MSVC, we reserve part of the stack memory for handling 4112 // memory overflow structured exception. 4113 FatalConditionHandler::FatalConditionHandler() { 4114 ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors); 4115 if (!SetThreadStackGuarantee(&guaranteeSize)) { 4116 // We do not want to fully error out, because needing 4117 // the stack reserve should be rare enough anyway. 4118 Catch::cerr() 4119 << "Failed to reserve piece of stack." 4120 << " Stack overflows will not be reported successfully."; 4121 } 4122 } 4123 4124 // We do not attempt to unset the stack guarantee, because 4125 // Windows does not support lowering the stack size guarantee. 4126 FatalConditionHandler::~FatalConditionHandler() = default; 4127 4128 4129 void FatalConditionHandler::engage_platform() { 4130 // Register as a the top level exception filter. 4131 previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter); 4132 } 4133 4134 void FatalConditionHandler::disengage_platform() noexcept { 4135 if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) { 4136 Catch::cerr() 4137 << "Unexpected SEH unhandled exception filter on disengage." 4138 << " The filter was restored, but might be rolled back unexpectedly."; 4139 } 4140 previousTopLevelExceptionFilter = nullptr; 4141 } 4142 4143} // end namespace Catch 4144 4145#endif // CATCH_CONFIG_WINDOWS_SEH 4146 4147#if defined( CATCH_CONFIG_POSIX_SIGNALS ) 4148 4149#include <signal.h> 4150 4151namespace Catch { 4152 4153 struct SignalDefs { 4154 int id; 4155 const char* name; 4156 }; 4157 4158 static SignalDefs signalDefs[] = { 4159 { SIGINT, "SIGINT - Terminal interrupt signal" }, 4160 { SIGILL, "SIGILL - Illegal instruction signal" }, 4161 { SIGFPE, "SIGFPE - Floating point error signal" }, 4162 { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, 4163 { SIGTERM, "SIGTERM - Termination request signal" }, 4164 { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } 4165 }; 4166 4167// Older GCCs trigger -Wmissing-field-initializers for T foo = {} 4168// which is zero initialization, but not explicit. We want to avoid 4169// that. 4170#if defined(__GNUC__) 4171# pragma GCC diagnostic push 4172# pragma GCC diagnostic ignored "-Wmissing-field-initializers" 4173#endif 4174 4175 static char* altStackMem = nullptr; 4176 static std::size_t altStackSize = 0; 4177 static stack_t oldSigStack{}; 4178 static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; 4179 4180 static void restorePreviousSignalHandlers() noexcept { 4181 // We set signal handlers back to the previous ones. Hopefully 4182 // nobody overwrote them in the meantime, and doesn't expect 4183 // their signal handlers to live past ours given that they 4184 // installed them after ours.. 4185 for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { 4186 sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); 4187 } 4188 // Return the old stack 4189 sigaltstack(&oldSigStack, nullptr); 4190 } 4191 4192 static void handleSignal( int sig ) { 4193 char const * name = "<unknown signal>"; 4194 for (auto const& def : signalDefs) { 4195 if (sig == def.id) { 4196 name = def.name; 4197 break; 4198 } 4199 } 4200 // We need to restore previous signal handlers and let them do 4201 // their thing, so that the users can have the debugger break 4202 // when a signal is raised, and so on. 4203 restorePreviousSignalHandlers(); 4204 reportFatal( name ); 4205 raise( sig ); 4206 } 4207 4208 FatalConditionHandler::FatalConditionHandler() { 4209 assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); 4210 if (altStackSize == 0) { 4211 altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors); 4212 } 4213 altStackMem = new char[altStackSize](); 4214 } 4215 4216 FatalConditionHandler::~FatalConditionHandler() { 4217 delete[] altStackMem; 4218 // We signal that another instance can be constructed by zeroing 4219 // out the pointer. 4220 altStackMem = nullptr; 4221 } 4222 4223 void FatalConditionHandler::engage_platform() { 4224 stack_t sigStack; 4225 sigStack.ss_sp = altStackMem; 4226 sigStack.ss_size = altStackSize; 4227 sigStack.ss_flags = 0; 4228 sigaltstack(&sigStack, &oldSigStack); 4229 struct sigaction sa = { }; 4230 4231 sa.sa_handler = handleSignal; 4232 sa.sa_flags = SA_ONSTACK; 4233 for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { 4234 sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); 4235 } 4236 } 4237 4238#if defined(__GNUC__) 4239# pragma GCC diagnostic pop 4240#endif 4241 4242 4243 void FatalConditionHandler::disengage_platform() noexcept { 4244 restorePreviousSignalHandlers(); 4245 } 4246 4247} // end namespace Catch 4248 4249#endif // CATCH_CONFIG_POSIX_SIGNALS 4250 4251 4252 4253 4254#include <cstring> 4255 4256namespace Catch { 4257 namespace Detail { 4258 4259 uint32_t convertToBits(float f) { 4260 static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated"); 4261 uint32_t i; 4262 std::memcpy(&i, &f, sizeof(f)); 4263 return i; 4264 } 4265 4266 uint64_t convertToBits(double d) { 4267 static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated"); 4268 uint64_t i; 4269 std::memcpy(&i, &d, sizeof(d)); 4270 return i; 4271 } 4272 4273#if defined( __GNUC__ ) || defined( __clang__ ) 4274# pragma GCC diagnostic push 4275# pragma GCC diagnostic ignored "-Wfloat-equal" 4276#endif 4277 bool directCompare( float lhs, float rhs ) { return lhs == rhs; } 4278 bool directCompare( double lhs, double rhs ) { return lhs == rhs; } 4279#if defined( __GNUC__ ) || defined( __clang__ ) 4280# pragma GCC diagnostic pop 4281#endif 4282 4283 4284 } // end namespace Detail 4285} // end namespace Catch 4286 4287 4288 4289 4290 4291 4292#include <cstdlib> 4293 4294namespace Catch { 4295 namespace Detail { 4296 4297#if !defined (CATCH_CONFIG_GETENV) 4298 char const* getEnv( char const* ) { return nullptr; } 4299#else 4300 4301 char const* getEnv( char const* varName ) { 4302# if defined( _MSC_VER ) 4303# pragma warning( push ) 4304# pragma warning( disable : 4996 ) // use getenv_s instead of getenv 4305# endif 4306 4307 return std::getenv( varName ); 4308 4309# if defined( _MSC_VER ) 4310# pragma warning( pop ) 4311# endif 4312 } 4313#endif 4314} // namespace Detail 4315} // namespace Catch 4316 4317 4318 4319 4320#include <cstdio> 4321#include <fstream> 4322#include <sstream> 4323#include <vector> 4324 4325namespace Catch { 4326 4327 Catch::IStream::~IStream() = default; 4328 4329namespace Detail { 4330 namespace { 4331 template<typename WriterF, std::size_t bufferSize=256> 4332 class StreamBufImpl final : public std::streambuf { 4333 char data[bufferSize]; 4334 WriterF m_writer; 4335 4336 public: 4337 StreamBufImpl() { 4338 setp( data, data + sizeof(data) ); 4339 } 4340 4341 ~StreamBufImpl() noexcept override { 4342 StreamBufImpl::sync(); 4343 } 4344 4345 private: 4346 int overflow( int c ) override { 4347 sync(); 4348 4349 if( c != EOF ) { 4350 if( pbase() == epptr() ) 4351 m_writer( std::string( 1, static_cast<char>( c ) ) ); 4352 else 4353 sputc( static_cast<char>( c ) ); 4354 } 4355 return 0; 4356 } 4357 4358 int sync() override { 4359 if( pbase() != pptr() ) { 4360 m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); 4361 setp( pbase(), epptr() ); 4362 } 4363 return 0; 4364 } 4365 }; 4366 4367 /////////////////////////////////////////////////////////////////////////// 4368 4369 struct OutputDebugWriter { 4370 4371 void operator()( std::string const& str ) { 4372 if ( !str.empty() ) { 4373 writeToDebugConsole( str ); 4374 } 4375 } 4376 }; 4377 4378 /////////////////////////////////////////////////////////////////////////// 4379 4380 class FileStream final : public IStream { 4381 std::ofstream m_ofs; 4382 public: 4383 FileStream( std::string const& filename ) { 4384 m_ofs.open( filename.c_str() ); 4385 CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << '\'' ); 4386 m_ofs << std::unitbuf; 4387 } 4388 public: // IStream 4389 std::ostream& stream() override { 4390 return m_ofs; 4391 } 4392 }; 4393 4394 /////////////////////////////////////////////////////////////////////////// 4395 4396 class CoutStream final : public IStream { 4397 std::ostream m_os; 4398 public: 4399 // Store the streambuf from cout up-front because 4400 // cout may get redirected when running tests 4401 CoutStream() : m_os( Catch::cout().rdbuf() ) {} 4402 4403 public: // IStream 4404 std::ostream& stream() override { return m_os; } 4405 bool isConsole() const override { return true; } 4406 }; 4407 4408 class CerrStream : public IStream { 4409 std::ostream m_os; 4410 4411 public: 4412 // Store the streambuf from cerr up-front because 4413 // cout may get redirected when running tests 4414 CerrStream(): m_os( Catch::cerr().rdbuf() ) {} 4415 4416 public: // IStream 4417 std::ostream& stream() override { return m_os; } 4418 bool isConsole() const override { return true; } 4419 }; 4420 4421 /////////////////////////////////////////////////////////////////////////// 4422 4423 class DebugOutStream final : public IStream { 4424 Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf; 4425 std::ostream m_os; 4426 public: 4427 DebugOutStream() 4428 : m_streamBuf( Detail::make_unique<StreamBufImpl<OutputDebugWriter>>() ), 4429 m_os( m_streamBuf.get() ) 4430 {} 4431 4432 public: // IStream 4433 std::ostream& stream() override { return m_os; } 4434 }; 4435 4436 } // unnamed namespace 4437} // namespace Detail 4438 4439 /////////////////////////////////////////////////////////////////////////// 4440 4441 auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream> { 4442 if ( filename.empty() || filename == "-" ) { 4443 return Detail::make_unique<Detail::CoutStream>(); 4444 } 4445 if( filename[0] == '%' ) { 4446 if ( filename == "%debug" ) { 4447 return Detail::make_unique<Detail::DebugOutStream>(); 4448 } else if ( filename == "%stderr" ) { 4449 return Detail::make_unique<Detail::CerrStream>(); 4450 } else if ( filename == "%stdout" ) { 4451 return Detail::make_unique<Detail::CoutStream>(); 4452 } else { 4453 CATCH_ERROR( "Unrecognised stream: '" << filename << '\'' ); 4454 } 4455 } 4456 return Detail::make_unique<Detail::FileStream>( filename ); 4457 } 4458 4459} 4460 4461 4462 4463namespace Catch { 4464 void JsonUtils::indent( std::ostream& os, std::uint64_t level ) { 4465 for ( std::uint64_t i = 0; i < level; ++i ) { 4466 os << " "; 4467 } 4468 } 4469 void JsonUtils::appendCommaNewline( std::ostream& os, 4470 bool& should_comma, 4471 std::uint64_t level ) { 4472 if ( should_comma ) { os << ','; } 4473 should_comma = true; 4474 os << '\n'; 4475 indent( os, level ); 4476 } 4477 4478 JsonObjectWriter::JsonObjectWriter( std::ostream& os ): 4479 JsonObjectWriter{ os, 0 } {} 4480 4481 JsonObjectWriter::JsonObjectWriter( std::ostream& os, 4482 std::uint64_t indent_level ): 4483 m_os{ os }, m_indent_level{ indent_level } { 4484 m_os << '{'; 4485 } 4486 JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ) noexcept: 4487 m_os{ source.m_os }, 4488 m_indent_level{ source.m_indent_level }, 4489 m_should_comma{ source.m_should_comma }, 4490 m_active{ source.m_active } { 4491 source.m_active = false; 4492 } 4493 4494 JsonObjectWriter::~JsonObjectWriter() { 4495 if ( !m_active ) { return; } 4496 4497 m_os << '\n'; 4498 JsonUtils::indent( m_os, m_indent_level ); 4499 m_os << '}'; 4500 } 4501 4502 JsonValueWriter JsonObjectWriter::write( StringRef key ) { 4503 JsonUtils::appendCommaNewline( 4504 m_os, m_should_comma, m_indent_level + 1 ); 4505 4506 m_os << '"' << key << "\": "; 4507 return JsonValueWriter{ m_os, m_indent_level + 1 }; 4508 } 4509 4510 JsonArrayWriter::JsonArrayWriter( std::ostream& os ): 4511 JsonArrayWriter{ os, 0 } {} 4512 JsonArrayWriter::JsonArrayWriter( std::ostream& os, 4513 std::uint64_t indent_level ): 4514 m_os{ os }, m_indent_level{ indent_level } { 4515 m_os << '['; 4516 } 4517 JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ) noexcept: 4518 m_os{ source.m_os }, 4519 m_indent_level{ source.m_indent_level }, 4520 m_should_comma{ source.m_should_comma }, 4521 m_active{ source.m_active } { 4522 source.m_active = false; 4523 } 4524 JsonArrayWriter::~JsonArrayWriter() { 4525 if ( !m_active ) { return; } 4526 4527 m_os << '\n'; 4528 JsonUtils::indent( m_os, m_indent_level ); 4529 m_os << ']'; 4530 } 4531 4532 JsonObjectWriter JsonArrayWriter::writeObject() { 4533 JsonUtils::appendCommaNewline( 4534 m_os, m_should_comma, m_indent_level + 1 ); 4535 return JsonObjectWriter{ m_os, m_indent_level + 1 }; 4536 } 4537 4538 JsonArrayWriter JsonArrayWriter::writeArray() { 4539 JsonUtils::appendCommaNewline( 4540 m_os, m_should_comma, m_indent_level + 1 ); 4541 return JsonArrayWriter{ m_os, m_indent_level + 1 }; 4542 } 4543 4544 JsonArrayWriter& JsonArrayWriter::write( bool value ) { 4545 return writeImpl( value ); 4546 } 4547 4548 JsonValueWriter::JsonValueWriter( std::ostream& os ): 4549 JsonValueWriter{ os, 0 } {} 4550 4551 JsonValueWriter::JsonValueWriter( std::ostream& os, 4552 std::uint64_t indent_level ): 4553 m_os{ os }, m_indent_level{ indent_level } {} 4554 4555 JsonObjectWriter JsonValueWriter::writeObject() && { 4556 return JsonObjectWriter{ m_os, m_indent_level }; 4557 } 4558 4559 JsonArrayWriter JsonValueWriter::writeArray() && { 4560 return JsonArrayWriter{ m_os, m_indent_level }; 4561 } 4562 4563 void JsonValueWriter::write( Catch::StringRef value ) && { 4564 writeImpl( value, true ); 4565 } 4566 4567 void JsonValueWriter::write( bool value ) && { 4568 writeImpl( value ? "true"_sr : "false"_sr, false ); 4569 } 4570 4571 void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { 4572 if ( quote ) { m_os << '"'; } 4573 for (char c : value) { 4574 // Escape list taken from https://www.json.org/json-en.html, 4575 // string definition. 4576 // Note that while forward slash _can_ be escaped, it does 4577 // not have to be, if JSON is not further embedded somewhere 4578 // where forward slash is meaningful. 4579 if ( c == '"' ) { 4580 m_os << "\\\""; 4581 } else if ( c == '\\' ) { 4582 m_os << "\\\\"; 4583 } else if ( c == '\b' ) { 4584 m_os << "\\b"; 4585 } else if ( c == '\f' ) { 4586 m_os << "\\f"; 4587 } else if ( c == '\n' ) { 4588 m_os << "\\n"; 4589 } else if ( c == '\r' ) { 4590 m_os << "\\r"; 4591 } else if ( c == '\t' ) { 4592 m_os << "\\t"; 4593 } else { 4594 m_os << c; 4595 } 4596 } 4597 if ( quote ) { m_os << '"'; } 4598 } 4599 4600} // namespace Catch 4601 4602 4603 4604 4605namespace Catch { 4606 4607 auto operator << (std::ostream& os, LazyExpression const& lazyExpr) -> std::ostream& { 4608 if (lazyExpr.m_isNegated) 4609 os << '!'; 4610 4611 if (lazyExpr) { 4612 if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) 4613 os << '(' << *lazyExpr.m_transientExpression << ')'; 4614 else 4615 os << *lazyExpr.m_transientExpression; 4616 } else { 4617 os << "{** error - unchecked empty expression requested **}"; 4618 } 4619 return os; 4620 } 4621 4622} // namespace Catch 4623 4624 4625 4626 4627#ifdef CATCH_CONFIG_WINDOWS_CRTDBG 4628#include <crtdbg.h> 4629 4630namespace Catch { 4631 4632 LeakDetector::LeakDetector() { 4633 int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); 4634 flag |= _CRTDBG_LEAK_CHECK_DF; 4635 flag |= _CRTDBG_ALLOC_MEM_DF; 4636 _CrtSetDbgFlag(flag); 4637 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); 4638 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); 4639 // Change this to leaking allocation's number to break there 4640 _CrtSetBreakAlloc(-1); 4641 } 4642} 4643 4644#else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv 4645 4646 Catch::LeakDetector::LeakDetector() = default; 4647 4648#endif // CATCH_CONFIG_WINDOWS_CRTDBG 4649 4650Catch::LeakDetector::~LeakDetector() { 4651 Catch::cleanUp(); 4652} 4653 4654 4655 4656 4657namespace Catch { 4658 namespace { 4659 4660 void listTests(IEventListener& reporter, IConfig const& config) { 4661 auto const& testSpec = config.testSpec(); 4662 auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); 4663 reporter.listTests(matchedTestCases); 4664 } 4665 4666 void listTags(IEventListener& reporter, IConfig const& config) { 4667 auto const& testSpec = config.testSpec(); 4668 std::vector<TestCaseHandle> matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); 4669 4670 std::map<StringRef, TagInfo, Detail::CaseInsensitiveLess> tagCounts; 4671 for (auto const& testCase : matchedTestCases) { 4672 for (auto const& tagName : testCase.getTestCaseInfo().tags) { 4673 auto it = tagCounts.find(tagName.original); 4674 if (it == tagCounts.end()) 4675 it = tagCounts.insert(std::make_pair(tagName.original, TagInfo())).first; 4676 it->second.add(tagName.original); 4677 } 4678 } 4679 4680 std::vector<TagInfo> infos; infos.reserve(tagCounts.size()); 4681 for (auto& tagc : tagCounts) { 4682 infos.push_back(CATCH_MOVE(tagc.second)); 4683 } 4684 4685 reporter.listTags(infos); 4686 } 4687 4688 void listReporters(IEventListener& reporter) { 4689 std::vector<ReporterDescription> descriptions; 4690 4691 auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); 4692 descriptions.reserve(factories.size()); 4693 for (auto const& fac : factories) { 4694 descriptions.push_back({ fac.first, fac.second->getDescription() }); 4695 } 4696 4697 reporter.listReporters(descriptions); 4698 } 4699 4700 void listListeners(IEventListener& reporter) { 4701 std::vector<ListenerDescription> descriptions; 4702 4703 auto const& factories = 4704 getRegistryHub().getReporterRegistry().getListeners(); 4705 descriptions.reserve( factories.size() ); 4706 for ( auto const& fac : factories ) { 4707 descriptions.push_back( { fac->getName(), fac->getDescription() } ); 4708 } 4709 4710 reporter.listListeners( descriptions ); 4711 } 4712 4713 } // end anonymous namespace 4714 4715 void TagInfo::add( StringRef spelling ) { 4716 ++count; 4717 spellings.insert( spelling ); 4718 } 4719 4720 std::string TagInfo::all() const { 4721 // 2 per tag for brackets '[' and ']' 4722 size_t size = spellings.size() * 2; 4723 for (auto const& spelling : spellings) { 4724 size += spelling.size(); 4725 } 4726 4727 std::string out; out.reserve(size); 4728 for (auto const& spelling : spellings) { 4729 out += '['; 4730 out += spelling; 4731 out += ']'; 4732 } 4733 return out; 4734 } 4735 4736 bool list( IEventListener& reporter, Config const& config ) { 4737 bool listed = false; 4738 if (config.listTests()) { 4739 listed = true; 4740 listTests(reporter, config); 4741 } 4742 if (config.listTags()) { 4743 listed = true; 4744 listTags(reporter, config); 4745 } 4746 if (config.listReporters()) { 4747 listed = true; 4748 listReporters(reporter); 4749 } 4750 if ( config.listListeners() ) { 4751 listed = true; 4752 listListeners( reporter ); 4753 } 4754 return listed; 4755 } 4756 4757} // end namespace Catch 4758 4759 4760 4761namespace Catch { 4762 CATCH_INTERNAL_START_WARNINGS_SUPPRESSION 4763 CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS 4764 static LeakDetector leakDetector; 4765 CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION 4766} 4767 4768// Allow users of amalgamated .cpp file to remove our main and provide their own. 4769#if !defined(CATCH_AMALGAMATED_CUSTOM_MAIN) 4770 4771#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) 4772// Standard C/C++ Win32 Unicode wmain entry point 4773extern "C" int __cdecl wmain (int argc, wchar_t * argv[], wchar_t * []) { 4774#else 4775// Standard C/C++ main entry point 4776int main (int argc, char * argv[]) { 4777#endif 4778 4779 // We want to force the linker not to discard the global variable 4780 // and its constructor, as it (optionally) registers leak detector 4781 (void)&Catch::leakDetector; 4782 4783 return Catch::Session().run( argc, argv ); 4784} 4785 4786#endif // !defined(CATCH_AMALGAMATED_CUSTOM_MAIN 4787 4788 4789 4790 4791namespace Catch { 4792 4793 MessageInfo::MessageInfo( StringRef _macroName, 4794 SourceLineInfo const& _lineInfo, 4795 ResultWas::OfType _type ) 4796 : macroName( _macroName ), 4797 lineInfo( _lineInfo ), 4798 type( _type ), 4799 sequence( ++globalCount ) 4800 {} 4801 4802 // This may need protecting if threading support is added 4803 unsigned int MessageInfo::globalCount = 0; 4804 4805} // end namespace Catch 4806 4807 4808 4809#include <cstdio> 4810#include <cstring> 4811#include <sstream> 4812 4813#if defined(CATCH_CONFIG_NEW_CAPTURE) 4814 #if defined(_MSC_VER) 4815 #include <io.h> //_dup and _dup2 4816 #define dup _dup 4817 #define dup2 _dup2 4818 #define fileno _fileno 4819 #else 4820 #include <unistd.h> // dup and dup2 4821 #endif 4822#endif 4823 4824 4825namespace Catch { 4826 4827 RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) 4828 : m_originalStream( originalStream ), 4829 m_redirectionStream( redirectionStream ), 4830 m_prevBuf( m_originalStream.rdbuf() ) 4831 { 4832 m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); 4833 } 4834 4835 RedirectedStream::~RedirectedStream() { 4836 m_originalStream.rdbuf( m_prevBuf ); 4837 } 4838 4839 RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} 4840 auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } 4841 4842 RedirectedStdErr::RedirectedStdErr() 4843 : m_cerr( Catch::cerr(), m_rss.get() ), 4844 m_clog( Catch::clog(), m_rss.get() ) 4845 {} 4846 auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } 4847 4848 RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) 4849 : m_redirectedCout(redirectedCout), 4850 m_redirectedCerr(redirectedCerr) 4851 {} 4852 4853 RedirectedStreams::~RedirectedStreams() { 4854 m_redirectedCout += m_redirectedStdOut.str(); 4855 m_redirectedCerr += m_redirectedStdErr.str(); 4856 } 4857 4858#if defined(CATCH_CONFIG_NEW_CAPTURE) 4859 4860#if defined(_MSC_VER) 4861 TempFile::TempFile() { 4862 if (tmpnam_s(m_buffer)) { 4863 CATCH_RUNTIME_ERROR("Could not get a temp filename"); 4864 } 4865 if (fopen_s(&m_file, m_buffer, "w+")) { 4866 char buffer[100]; 4867 if (strerror_s(buffer, errno)) { 4868 CATCH_RUNTIME_ERROR("Could not translate errno to a string"); 4869 } 4870 CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); 4871 } 4872 } 4873#else 4874 TempFile::TempFile() { 4875 m_file = std::tmpfile(); 4876 if (!m_file) { 4877 CATCH_RUNTIME_ERROR("Could not create a temp file."); 4878 } 4879 } 4880 4881#endif 4882 4883 TempFile::~TempFile() { 4884 // TBD: What to do about errors here? 4885 std::fclose(m_file); 4886 // We manually create the file on Windows only, on Linux 4887 // it will be autodeleted 4888#if defined(_MSC_VER) 4889 std::remove(m_buffer); 4890#endif 4891 } 4892 4893 4894 FILE* TempFile::getFile() { 4895 return m_file; 4896 } 4897 4898 std::string TempFile::getContents() { 4899 std::stringstream sstr; 4900 char buffer[100] = {}; 4901 std::rewind(m_file); 4902 while (std::fgets(buffer, sizeof(buffer), m_file)) { 4903 sstr << buffer; 4904 } 4905 return sstr.str(); 4906 } 4907 4908 OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) : 4909 m_originalStdout(dup(1)), 4910 m_originalStderr(dup(2)), 4911 m_stdoutDest(stdout_dest), 4912 m_stderrDest(stderr_dest) { 4913 dup2(fileno(m_stdoutFile.getFile()), 1); 4914 dup2(fileno(m_stderrFile.getFile()), 2); 4915 } 4916 4917 OutputRedirect::~OutputRedirect() { 4918 Catch::cout() << std::flush; 4919 fflush(stdout); 4920 // Since we support overriding these streams, we flush cerr 4921 // even though std::cerr is unbuffered 4922 Catch::cerr() << std::flush; 4923 Catch::clog() << std::flush; 4924 fflush(stderr); 4925 4926 dup2(m_originalStdout, 1); 4927 dup2(m_originalStderr, 2); 4928 4929 m_stdoutDest += m_stdoutFile.getContents(); 4930 m_stderrDest += m_stderrFile.getContents(); 4931 } 4932 4933#endif // CATCH_CONFIG_NEW_CAPTURE 4934 4935} // namespace Catch 4936 4937#if defined(CATCH_CONFIG_NEW_CAPTURE) 4938 #if defined(_MSC_VER) 4939 #undef dup 4940 #undef dup2 4941 #undef fileno 4942 #endif 4943#endif 4944 4945 4946 4947 4948#include <limits> 4949#include <stdexcept> 4950 4951namespace Catch { 4952 4953 Optional<unsigned int> parseUInt(std::string const& input, int base) { 4954 auto trimmed = trim( input ); 4955 // std::stoull is annoying and accepts numbers starting with '-', 4956 // it just negates them into unsigned int 4957 if ( trimmed.empty() || trimmed[0] == '-' ) { 4958 return {}; 4959 } 4960 4961 CATCH_TRY { 4962 size_t pos = 0; 4963 const auto ret = std::stoull( trimmed, &pos, base ); 4964 4965 // We did not consume the whole input, so there is an issue 4966 // This can be bunch of different stuff, like multiple numbers 4967 // in the input, or invalid digits/characters and so on. Either 4968 // way, we do not want to return the partially parsed result. 4969 if ( pos != trimmed.size() ) { 4970 return {}; 4971 } 4972 // Too large 4973 if ( ret > std::numeric_limits<unsigned int>::max() ) { 4974 return {}; 4975 } 4976 return static_cast<unsigned int>(ret); 4977 } 4978 CATCH_CATCH_ANON( std::invalid_argument const& ) { 4979 // no conversion could be performed 4980 } 4981 CATCH_CATCH_ANON( std::out_of_range const& ) { 4982 // the input does not fit into an unsigned long long 4983 } 4984 return {}; 4985 } 4986 4987} // namespace Catch 4988 4989 4990 4991 4992#include <cmath> 4993 4994namespace Catch { 4995 4996#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) 4997 bool isnan(float f) { 4998 return std::isnan(f); 4999 } 5000 bool isnan(double d) { 5001 return std::isnan(d); 5002 } 5003#else 5004 // For now we only use this for embarcadero 5005 bool isnan(float f) { 5006 return std::_isnan(f); 5007 } 5008 bool isnan(double d) { 5009 return std::_isnan(d); 5010 } 5011#endif 5012 5013#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER ) 5014 float nextafter( float x, float y ) { return std::nextafter( x, y ); } 5015 double nextafter( double x, double y ) { return std::nextafter( x, y ); } 5016#else 5017 float nextafter( float x, float y ) { return ::nextafterf( x, y ); } 5018 double nextafter( double x, double y ) { return ::nextafter( x, y ); } 5019#endif 5020 5021} // end namespace Catch 5022 5023 5024 5025namespace Catch { 5026 5027namespace { 5028 5029#if defined(_MSC_VER) 5030#pragma warning(push) 5031#pragma warning(disable:4146) // we negate uint32 during the rotate 5032#endif 5033 // Safe rotr implementation thanks to John Regehr 5034 uint32_t rotate_right(uint32_t val, uint32_t count) { 5035 const uint32_t mask = 31; 5036 count &= mask; 5037 return (val >> count) | (val << (-count & mask)); 5038 } 5039 5040#if defined(_MSC_VER) 5041#pragma warning(pop) 5042#endif 5043 5044} 5045 5046 5047 SimplePcg32::SimplePcg32(result_type seed_) { 5048 seed(seed_); 5049 } 5050 5051 5052 void SimplePcg32::seed(result_type seed_) { 5053 m_state = 0; 5054 (*this)(); 5055 m_state += seed_; 5056 (*this)(); 5057 } 5058 5059 void SimplePcg32::discard(uint64_t skip) { 5060 // We could implement this to run in O(log n) steps, but this 5061 // should suffice for our use case. 5062 for (uint64_t s = 0; s < skip; ++s) { 5063 static_cast<void>((*this)()); 5064 } 5065 } 5066 5067 SimplePcg32::result_type SimplePcg32::operator()() { 5068 // prepare the output value 5069 const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u); 5070 const auto output = rotate_right(xorshifted, m_state >> 59u); 5071 5072 // advance state 5073 m_state = m_state * 6364136223846793005ULL + s_inc; 5074 5075 return output; 5076 } 5077 5078 bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { 5079 return lhs.m_state == rhs.m_state; 5080 } 5081 5082 bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) { 5083 return lhs.m_state != rhs.m_state; 5084 } 5085} 5086 5087 5088 5089 5090 5091#include <ctime> 5092#include <random> 5093 5094namespace Catch { 5095 5096 std::uint32_t generateRandomSeed( GenerateFrom from ) { 5097 switch ( from ) { 5098 case GenerateFrom::Time: 5099 return static_cast<std::uint32_t>( std::time( nullptr ) ); 5100 5101 case GenerateFrom::Default: 5102 case GenerateFrom::RandomDevice: { 5103 std::random_device rd; 5104 return Detail::fillBitsFrom<std::uint32_t>( rd ); 5105 } 5106 5107 default: 5108 CATCH_ERROR("Unknown generation method"); 5109 } 5110 } 5111 5112} // end namespace Catch 5113 5114 5115 5116 5117namespace Catch { 5118 struct ReporterRegistry::ReporterRegistryImpl { 5119 std::vector<Detail::unique_ptr<EventListenerFactory>> listeners; 5120 std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess> 5121 factories; 5122 }; 5123 5124 ReporterRegistry::ReporterRegistry(): 5125 m_impl( Detail::make_unique<ReporterRegistryImpl>() ) { 5126 // Because it is impossible to move out of initializer list, 5127 // we have to add the elements manually 5128 m_impl->factories["Automake"] = 5129 Detail::make_unique<ReporterFactory<AutomakeReporter>>(); 5130 m_impl->factories["compact"] = 5131 Detail::make_unique<ReporterFactory<CompactReporter>>(); 5132 m_impl->factories["console"] = 5133 Detail::make_unique<ReporterFactory<ConsoleReporter>>(); 5134 m_impl->factories["JUnit"] = 5135 Detail::make_unique<ReporterFactory<JunitReporter>>(); 5136 m_impl->factories["SonarQube"] = 5137 Detail::make_unique<ReporterFactory<SonarQubeReporter>>(); 5138 m_impl->factories["TAP"] = 5139 Detail::make_unique<ReporterFactory<TAPReporter>>(); 5140 m_impl->factories["TeamCity"] = 5141 Detail::make_unique<ReporterFactory<TeamCityReporter>>(); 5142 m_impl->factories["XML"] = 5143 Detail::make_unique<ReporterFactory<XmlReporter>>(); 5144 m_impl->factories["JSON"] = 5145 Detail::make_unique<ReporterFactory<JsonReporter>>(); 5146 } 5147 5148 ReporterRegistry::~ReporterRegistry() = default; 5149 5150 IEventListenerPtr 5151 ReporterRegistry::create( std::string const& name, 5152 ReporterConfig&& config ) const { 5153 auto it = m_impl->factories.find( name ); 5154 if ( it == m_impl->factories.end() ) return nullptr; 5155 return it->second->create( CATCH_MOVE( config ) ); 5156 } 5157 5158 void ReporterRegistry::registerReporter( std::string const& name, 5159 IReporterFactoryPtr factory ) { 5160 CATCH_ENFORCE( name.find( "::" ) == name.npos, 5161 "'::' is not allowed in reporter name: '" + name + 5162 '\'' ); 5163 auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) ); 5164 CATCH_ENFORCE( ret.second, 5165 "reporter using '" + name + 5166 "' as name was already registered" ); 5167 } 5168 void ReporterRegistry::registerListener( 5169 Detail::unique_ptr<EventListenerFactory> factory ) { 5170 m_impl->listeners.push_back( CATCH_MOVE( factory ) ); 5171 } 5172 5173 std::map<std::string, 5174 IReporterFactoryPtr, 5175 Detail::CaseInsensitiveLess> const& 5176 ReporterRegistry::getFactories() const { 5177 return m_impl->factories; 5178 } 5179 5180 std::vector<Detail::unique_ptr<EventListenerFactory>> const& 5181 ReporterRegistry::getListeners() const { 5182 return m_impl->listeners; 5183 } 5184} // namespace Catch 5185 5186 5187 5188 5189 5190#include <algorithm> 5191 5192namespace Catch { 5193 5194 namespace { 5195 struct kvPair { 5196 StringRef key, value; 5197 }; 5198 5199 kvPair splitKVPair(StringRef kvString) { 5200 auto splitPos = static_cast<size_t>( 5201 std::find( kvString.begin(), kvString.end(), '=' ) - 5202 kvString.begin() ); 5203 5204 return { kvString.substr( 0, splitPos ), 5205 kvString.substr( splitPos + 1, kvString.size() ) }; 5206 } 5207 } 5208 5209 namespace Detail { 5210 std::vector<std::string> splitReporterSpec( StringRef reporterSpec ) { 5211 static constexpr auto separator = "::"; 5212 static constexpr size_t separatorSize = 2; 5213 5214 size_t separatorPos = 0; 5215 auto findNextSeparator = [&reporterSpec]( size_t startPos ) { 5216 static_assert( 5217 separatorSize == 2, 5218 "The code below currently assumes 2 char separator" ); 5219 5220 auto currentPos = startPos; 5221 do { 5222 while ( currentPos < reporterSpec.size() && 5223 reporterSpec[currentPos] != separator[0] ) { 5224 ++currentPos; 5225 } 5226 if ( currentPos + 1 < reporterSpec.size() && 5227 reporterSpec[currentPos + 1] == separator[1] ) { 5228 return currentPos; 5229 } 5230 ++currentPos; 5231 } while ( currentPos < reporterSpec.size() ); 5232 5233 return static_cast<size_t>( -1 ); 5234 }; 5235 5236 std::vector<std::string> parts; 5237 5238 while ( separatorPos < reporterSpec.size() ) { 5239 const auto nextSeparator = findNextSeparator( separatorPos ); 5240 parts.push_back( static_cast<std::string>( reporterSpec.substr( 5241 separatorPos, nextSeparator - separatorPos ) ) ); 5242 5243 if ( nextSeparator == static_cast<size_t>( -1 ) ) { 5244 break; 5245 } 5246 separatorPos = nextSeparator + separatorSize; 5247 } 5248 5249 // Handle a separator at the end. 5250 // This is not a valid spec, but we want to do validation in a 5251 // centralized place 5252 if ( separatorPos == reporterSpec.size() ) { 5253 parts.emplace_back(); 5254 } 5255 5256 return parts; 5257 } 5258 5259 Optional<ColourMode> stringToColourMode( StringRef colourMode ) { 5260 if ( colourMode == "default" ) { 5261 return ColourMode::PlatformDefault; 5262 } else if ( colourMode == "ansi" ) { 5263 return ColourMode::ANSI; 5264 } else if ( colourMode == "win32" ) { 5265 return ColourMode::Win32; 5266 } else if ( colourMode == "none" ) { 5267 return ColourMode::None; 5268 } else { 5269 return {}; 5270 } 5271 } 5272 } // namespace Detail 5273 5274 5275 bool operator==( ReporterSpec const& lhs, ReporterSpec const& rhs ) { 5276 return lhs.m_name == rhs.m_name && 5277 lhs.m_outputFileName == rhs.m_outputFileName && 5278 lhs.m_colourMode == rhs.m_colourMode && 5279 lhs.m_customOptions == rhs.m_customOptions; 5280 } 5281 5282 Optional<ReporterSpec> parseReporterSpec( StringRef reporterSpec ) { 5283 auto parts = Detail::splitReporterSpec( reporterSpec ); 5284 5285 assert( parts.size() > 0 && "Split should never return empty vector" ); 5286 5287 std::map<std::string, std::string> kvPairs; 5288 Optional<std::string> outputFileName; 5289 Optional<ColourMode> colourMode; 5290 5291 // First part is always reporter name, so we skip it 5292 for ( size_t i = 1; i < parts.size(); ++i ) { 5293 auto kv = splitKVPair( parts[i] ); 5294 auto key = kv.key, value = kv.value; 5295 5296 if ( key.empty() || value.empty() ) { // NOLINT(bugprone-branch-clone) 5297 return {}; 5298 } else if ( key[0] == 'X' ) { 5299 // This is a reporter-specific option, we don't check these 5300 // apart from basic sanity checks 5301 if ( key.size() == 1 ) { 5302 return {}; 5303 } 5304 5305 auto ret = kvPairs.emplace( std::string(kv.key), std::string(kv.value) ); 5306 if ( !ret.second ) { 5307 // Duplicated key. We might want to handle this differently, 5308 // e.g. by overwriting the existing value? 5309 return {}; 5310 } 5311 } else if ( key == "out" ) { 5312 // Duplicated key 5313 if ( outputFileName ) { 5314 return {}; 5315 } 5316 outputFileName = static_cast<std::string>( value ); 5317 } else if ( key == "colour-mode" ) { 5318 // Duplicated key 5319 if ( colourMode ) { 5320 return {}; 5321 } 5322 colourMode = Detail::stringToColourMode( value ); 5323 // Parsing failed 5324 if ( !colourMode ) { 5325 return {}; 5326 } 5327 } else { 5328 // Unrecognized option 5329 return {}; 5330 } 5331 } 5332 5333 return ReporterSpec{ CATCH_MOVE( parts[0] ), 5334 CATCH_MOVE( outputFileName ), 5335 CATCH_MOVE( colourMode ), 5336 CATCH_MOVE( kvPairs ) }; 5337 } 5338 5339ReporterSpec::ReporterSpec( 5340 std::string name, 5341 Optional<std::string> outputFileName, 5342 Optional<ColourMode> colourMode, 5343 std::map<std::string, std::string> customOptions ): 5344 m_name( CATCH_MOVE( name ) ), 5345 m_outputFileName( CATCH_MOVE( outputFileName ) ), 5346 m_colourMode( CATCH_MOVE( colourMode ) ), 5347 m_customOptions( CATCH_MOVE( customOptions ) ) {} 5348 5349} // namespace Catch 5350 5351 5352 5353namespace Catch { 5354 5355 bool isOk( ResultWas::OfType resultType ) { 5356 return ( resultType & ResultWas::FailureBit ) == 0; 5357 } 5358 bool isJustInfo( int flags ) { 5359 return flags == ResultWas::Info; 5360 } 5361 5362 ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { 5363 return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); 5364 } 5365 5366 bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } 5367 bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } 5368 5369} // end namespace Catch 5370 5371 5372 5373#include <cstdio> 5374#include <sstream> 5375#include <vector> 5376 5377namespace Catch { 5378 5379 // This class encapsulates the idea of a pool of ostringstreams that can be reused. 5380 struct StringStreams { 5381 std::vector<Detail::unique_ptr<std::ostringstream>> m_streams; 5382 std::vector<std::size_t> m_unused; 5383 std::ostringstream m_referenceStream; // Used for copy state/ flags from 5384 5385 auto add() -> std::size_t { 5386 if( m_unused.empty() ) { 5387 m_streams.push_back( Detail::make_unique<std::ostringstream>() ); 5388 return m_streams.size()-1; 5389 } 5390 else { 5391 auto index = m_unused.back(); 5392 m_unused.pop_back(); 5393 return index; 5394 } 5395 } 5396 5397 void release( std::size_t index ) { 5398 m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state 5399 m_unused.push_back(index); 5400 } 5401 }; 5402 5403 ReusableStringStream::ReusableStringStream() 5404 : m_index( Singleton<StringStreams>::getMutable().add() ), 5405 m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() ) 5406 {} 5407 5408 ReusableStringStream::~ReusableStringStream() { 5409 static_cast<std::ostringstream*>( m_oss )->str(""); 5410 m_oss->clear(); 5411 Singleton<StringStreams>::getMutable().release( m_index ); 5412 } 5413 5414 std::string ReusableStringStream::str() const { 5415 return static_cast<std::ostringstream*>( m_oss )->str(); 5416 } 5417 5418 void ReusableStringStream::str( std::string const& str ) { 5419 static_cast<std::ostringstream*>( m_oss )->str( str ); 5420 } 5421 5422 5423} 5424 5425 5426 5427 5428#include <cassert> 5429#include <algorithm> 5430 5431namespace Catch { 5432 5433 namespace Generators { 5434 namespace { 5435 struct GeneratorTracker final : TestCaseTracking::TrackerBase, 5436 IGeneratorTracker { 5437 GeneratorBasePtr m_generator; 5438 5439 GeneratorTracker( 5440 TestCaseTracking::NameAndLocation&& nameAndLocation, 5441 TrackerContext& ctx, 5442 ITracker* parent ): 5443 TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} 5444 5445 static GeneratorTracker* 5446 acquire( TrackerContext& ctx, 5447 TestCaseTracking::NameAndLocationRef const& 5448 nameAndLocation ) { 5449 GeneratorTracker* tracker; 5450 5451 ITracker& currentTracker = ctx.currentTracker(); 5452 // Under specific circumstances, the generator we want 5453 // to acquire is also the current tracker. If this is 5454 // the case, we have to avoid looking through current 5455 // tracker's children, and instead return the current 5456 // tracker. 5457 // A case where this check is important is e.g. 5458 // for (int i = 0; i < 5; ++i) { 5459 // int n = GENERATE(1, 2); 5460 // } 5461 // 5462 // without it, the code above creates 5 nested generators. 5463 if ( currentTracker.nameAndLocation() == nameAndLocation ) { 5464 auto thisTracker = currentTracker.parent()->findChild( 5465 nameAndLocation ); 5466 assert( thisTracker ); 5467 assert( thisTracker->isGeneratorTracker() ); 5468 tracker = static_cast<GeneratorTracker*>( thisTracker ); 5469 } else if ( ITracker* childTracker = 5470 currentTracker.findChild( 5471 nameAndLocation ) ) { 5472 assert( childTracker ); 5473 assert( childTracker->isGeneratorTracker() ); 5474 tracker = 5475 static_cast<GeneratorTracker*>( childTracker ); 5476 } else { 5477 return nullptr; 5478 } 5479 5480 if ( !tracker->isComplete() ) { tracker->open(); } 5481 5482 return tracker; 5483 } 5484 5485 // TrackerBase interface 5486 bool isGeneratorTracker() const override { return true; } 5487 auto hasGenerator() const -> bool override { 5488 return !!m_generator; 5489 } 5490 void close() override { 5491 TrackerBase::close(); 5492 // If a generator has a child (it is followed by a section) 5493 // and none of its children have started, then we must wait 5494 // until later to start consuming its values. 5495 // This catches cases where `GENERATE` is placed between two 5496 // `SECTION`s. 5497 // **The check for m_children.empty cannot be removed**. 5498 // doing so would break `GENERATE` _not_ followed by 5499 // `SECTION`s. 5500 const bool should_wait_for_child = [&]() { 5501 // No children -> nobody to wait for 5502 if ( m_children.empty() ) { return false; } 5503 // If at least one child started executing, don't wait 5504 if ( std::find_if( 5505 m_children.begin(), 5506 m_children.end(), 5507 []( TestCaseTracking::ITrackerPtr const& 5508 tracker ) { 5509 return tracker->hasStarted(); 5510 } ) != m_children.end() ) { 5511 return false; 5512 } 5513 5514 // No children have started. We need to check if they 5515 // _can_ start, and thus we should wait for them, or 5516 // they cannot start (due to filters), and we shouldn't 5517 // wait for them 5518 ITracker* parent = m_parent; 5519 // This is safe: there is always at least one section 5520 // tracker in a test case tracking tree 5521 while ( !parent->isSectionTracker() ) { 5522 parent = parent->parent(); 5523 } 5524 assert( parent && 5525 "Missing root (test case) level section" ); 5526 5527 auto const& parentSection = 5528 static_cast<SectionTracker const&>( *parent ); 5529 auto const& filters = parentSection.getFilters(); 5530 // No filters -> no restrictions on running sections 5531 if ( filters.empty() ) { return true; } 5532 5533 for ( auto const& child : m_children ) { 5534 if ( child->isSectionTracker() && 5535 std::find( filters.begin(), 5536 filters.end(), 5537 static_cast<SectionTracker const&>( 5538 *child ) 5539 .trimmedName() ) != 5540 filters.end() ) { 5541 return true; 5542 } 5543 } 5544 return false; 5545 }(); 5546 5547 // This check is a bit tricky, because m_generator->next() 5548 // has a side-effect, where it consumes generator's current 5549 // value, but we do not want to invoke the side-effect if 5550 // this generator is still waiting for any child to start. 5551 assert( m_generator && "Tracker without generator" ); 5552 if ( should_wait_for_child || 5553 ( m_runState == CompletedSuccessfully && 5554 m_generator->countedNext() ) ) { 5555 m_children.clear(); 5556 m_runState = Executing; 5557 } 5558 } 5559 5560 // IGeneratorTracker interface 5561 auto getGenerator() const -> GeneratorBasePtr const& override { 5562 return m_generator; 5563 } 5564 void setGenerator( GeneratorBasePtr&& generator ) override { 5565 m_generator = CATCH_MOVE( generator ); 5566 } 5567 }; 5568 } // namespace 5569 } 5570 5571 RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) 5572 : m_runInfo(_config->name()), 5573 m_config(_config), 5574 m_reporter(CATCH_MOVE(reporter)), 5575 m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, 5576 m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) 5577 { 5578 getCurrentMutableContext().setResultCapture( this ); 5579 m_reporter->testRunStarting(m_runInfo); 5580 } 5581 5582 RunContext::~RunContext() { 5583 m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); 5584 } 5585 5586 Totals RunContext::runTest(TestCaseHandle const& testCase) { 5587 const Totals prevTotals = m_totals; 5588 5589 auto const& testInfo = testCase.getTestCaseInfo(); 5590 m_reporter->testCaseStarting(testInfo); 5591 m_activeTestCase = &testCase; 5592 5593 5594 ITracker& rootTracker = m_trackerContext.startRun(); 5595 assert(rootTracker.isSectionTracker()); 5596 static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun()); 5597 5598 // We intentionally only seed the internal RNG once per test case, 5599 // before it is first invoked. The reason for that is a complex 5600 // interplay of generator/section implementation details and the 5601 // Random*Generator types. 5602 // 5603 // The issue boils down to us needing to seed the Random*Generators 5604 // with different seed each, so that they return different sequences 5605 // of random numbers. We do this by giving them a number from the 5606 // shared RNG instance as their seed. 5607 // 5608 // However, this runs into an issue if the reseeding happens each 5609 // time the test case is entered (as opposed to first time only), 5610 // because multiple generators could get the same seed, e.g. in 5611 // ```cpp 5612 // TEST_CASE() { 5613 // auto i = GENERATE(take(10, random(0, 100)); 5614 // SECTION("A") { 5615 // auto j = GENERATE(take(10, random(0, 100)); 5616 // } 5617 // SECTION("B") { 5618 // auto k = GENERATE(take(10, random(0, 100)); 5619 // } 5620 // } 5621 // ``` 5622 // `i` and `j` would properly return values from different sequences, 5623 // but `i` and `k` would return the same sequence, because their seed 5624 // would be the same. 5625 // (The reason their seeds would be the same is that the generator 5626 // for k would be initialized when the test case is entered the second 5627 // time, after the shared RNG instance was reset to the same value 5628 // it had when the generator for i was initialized.) 5629 seedRng( *m_config ); 5630 5631 uint64_t testRuns = 0; 5632 std::string redirectedCout; 5633 std::string redirectedCerr; 5634 do { 5635 m_trackerContext.startCycle(); 5636 m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo)); 5637 5638 m_reporter->testCasePartialStarting(testInfo, testRuns); 5639 5640 const auto beforeRunTotals = m_totals; 5641 std::string oneRunCout, oneRunCerr; 5642 runCurrentTest(oneRunCout, oneRunCerr); 5643 redirectedCout += oneRunCout; 5644 redirectedCerr += oneRunCerr; 5645 5646 const auto singleRunTotals = m_totals.delta(beforeRunTotals); 5647 auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); 5648 5649 m_reporter->testCasePartialEnded(statsForOneRun, testRuns); 5650 ++testRuns; 5651 } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); 5652 5653 Totals deltaTotals = m_totals.delta(prevTotals); 5654 if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { 5655 deltaTotals.assertions.failed++; 5656 deltaTotals.testCases.passed--; 5657 deltaTotals.testCases.failed++; 5658 } 5659 m_totals.testCases += deltaTotals.testCases; 5660 m_reporter->testCaseEnded(TestCaseStats(testInfo, 5661 deltaTotals, 5662 CATCH_MOVE(redirectedCout), 5663 CATCH_MOVE(redirectedCerr), 5664 aborting())); 5665 5666 m_activeTestCase = nullptr; 5667 m_testCaseTracker = nullptr; 5668 5669 return deltaTotals; 5670 } 5671 5672 5673 void RunContext::assertionEnded(AssertionResult&& result) { 5674 if (result.getResultType() == ResultWas::Ok) { 5675 m_totals.assertions.passed++; 5676 m_lastAssertionPassed = true; 5677 } else if (result.getResultType() == ResultWas::ExplicitSkip) { 5678 m_totals.assertions.skipped++; 5679 m_lastAssertionPassed = true; 5680 } else if (!result.succeeded()) { 5681 m_lastAssertionPassed = false; 5682 if (result.isOk()) { 5683 } 5684 else if( m_activeTestCase->getTestCaseInfo().okToFail() ) 5685 m_totals.assertions.failedButOk++; 5686 else 5687 m_totals.assertions.failed++; 5688 } 5689 else { 5690 m_lastAssertionPassed = true; 5691 } 5692 5693 m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); 5694 5695 if ( result.getResultType() != ResultWas::Warning ) { 5696 m_messageScopes.clear(); 5697 } 5698 5699 // Reset working state. assertion info will be reset after 5700 // populateReaction is run if it is needed 5701 m_lastResult = CATCH_MOVE( result ); 5702 } 5703 void RunContext::resetAssertionInfo() { 5704 m_lastAssertionInfo.macroName = StringRef(); 5705 m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; 5706 m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal; 5707 } 5708 5709 void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { 5710 m_reporter->assertionStarting( info ); 5711 } 5712 5713 bool RunContext::sectionStarted( StringRef sectionName, 5714 SourceLineInfo const& sectionLineInfo, 5715 Counts& assertions ) { 5716 ITracker& sectionTracker = 5717 SectionTracker::acquire( m_trackerContext, 5718 TestCaseTracking::NameAndLocationRef( 5719 sectionName, sectionLineInfo ) ); 5720 5721 if (!sectionTracker.isOpen()) 5722 return false; 5723 m_activeSections.push_back(&sectionTracker); 5724 5725 SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) ); 5726 m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; 5727 5728 m_reporter->sectionStarting(sectionInfo); 5729 5730 assertions = m_totals.assertions; 5731 5732 return true; 5733 } 5734 IGeneratorTracker* 5735 RunContext::acquireGeneratorTracker( StringRef generatorName, 5736 SourceLineInfo const& lineInfo ) { 5737 using namespace Generators; 5738 GeneratorTracker* tracker = GeneratorTracker::acquire( 5739 m_trackerContext, 5740 TestCaseTracking::NameAndLocationRef( 5741 generatorName, lineInfo ) ); 5742 m_lastAssertionInfo.lineInfo = lineInfo; 5743 return tracker; 5744 } 5745 5746 IGeneratorTracker* RunContext::createGeneratorTracker( 5747 StringRef generatorName, 5748 SourceLineInfo lineInfo, 5749 Generators::GeneratorBasePtr&& generator ) { 5750 5751 auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo ); 5752 auto& currentTracker = m_trackerContext.currentTracker(); 5753 assert( 5754 currentTracker.nameAndLocation() != nameAndLoc && 5755 "Trying to create tracker for a genreator that already has one" ); 5756 5757 auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>( 5758 CATCH_MOVE(nameAndLoc), m_trackerContext, &currentTracker ); 5759 auto ret = newTracker.get(); 5760 currentTracker.addChild( CATCH_MOVE( newTracker ) ); 5761 5762 ret->setGenerator( CATCH_MOVE( generator ) ); 5763 ret->open(); 5764 return ret; 5765 } 5766 5767 bool RunContext::testForMissingAssertions(Counts& assertions) { 5768 if (assertions.total() != 0) 5769 return false; 5770 if (!m_config->warnAboutMissingAssertions()) 5771 return false; 5772 if (m_trackerContext.currentTracker().hasChildren()) 5773 return false; 5774 m_totals.assertions.failed++; 5775 assertions.failed++; 5776 return true; 5777 } 5778 5779 void RunContext::sectionEnded(SectionEndInfo&& endInfo) { 5780 Counts assertions = m_totals.assertions - endInfo.prevAssertions; 5781 bool missingAssertions = testForMissingAssertions(assertions); 5782 5783 if (!m_activeSections.empty()) { 5784 m_activeSections.back()->close(); 5785 m_activeSections.pop_back(); 5786 } 5787 5788 m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions)); 5789 m_messages.clear(); 5790 m_messageScopes.clear(); 5791 } 5792 5793 void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) { 5794 if ( m_unfinishedSections.empty() ) { 5795 m_activeSections.back()->fail(); 5796 } else { 5797 m_activeSections.back()->close(); 5798 } 5799 m_activeSections.pop_back(); 5800 5801 m_unfinishedSections.push_back(CATCH_MOVE(endInfo)); 5802 } 5803 5804 void RunContext::benchmarkPreparing( StringRef name ) { 5805 m_reporter->benchmarkPreparing(name); 5806 } 5807 void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { 5808 m_reporter->benchmarkStarting( info ); 5809 } 5810 void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { 5811 m_reporter->benchmarkEnded( stats ); 5812 } 5813 void RunContext::benchmarkFailed( StringRef error ) { 5814 m_reporter->benchmarkFailed( error ); 5815 } 5816 5817 void RunContext::pushScopedMessage(MessageInfo const & message) { 5818 m_messages.push_back(message); 5819 } 5820 5821 void RunContext::popScopedMessage(MessageInfo const & message) { 5822 m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); 5823 } 5824 5825 void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) { 5826 m_messageScopes.emplace_back( CATCH_MOVE(builder) ); 5827 } 5828 5829 std::string RunContext::getCurrentTestName() const { 5830 return m_activeTestCase 5831 ? m_activeTestCase->getTestCaseInfo().name 5832 : std::string(); 5833 } 5834 5835 const AssertionResult * RunContext::getLastResult() const { 5836 return &(*m_lastResult); 5837 } 5838 5839 void RunContext::exceptionEarlyReported() { 5840 m_shouldReportUnexpected = false; 5841 } 5842 5843 void RunContext::handleFatalErrorCondition( StringRef message ) { 5844 // First notify reporter that bad things happened 5845 m_reporter->fatalErrorEncountered(message); 5846 5847 // Don't rebuild the result -- the stringification itself can cause more fatal errors 5848 // Instead, fake a result data. 5849 AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); 5850 tempResult.message = static_cast<std::string>(message); 5851 AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); 5852 5853 assertionEnded(CATCH_MOVE(result) ); 5854 resetAssertionInfo(); 5855 5856 // Best effort cleanup for sections that have not been destructed yet 5857 // Since this is a fatal error, we have not had and won't have the opportunity to destruct them properly 5858 while (!m_activeSections.empty()) { 5859 auto nl = m_activeSections.back()->nameAndLocation(); 5860 SectionEndInfo endInfo{ SectionInfo(CATCH_MOVE(nl.location), CATCH_MOVE(nl.name)), {}, 0.0 }; 5861 sectionEndedEarly(CATCH_MOVE(endInfo)); 5862 } 5863 handleUnfinishedSections(); 5864 5865 // Recreate section for test case (as we will lose the one that was in scope) 5866 auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); 5867 SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); 5868 5869 Counts assertions; 5870 assertions.failed = 1; 5871 SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false); 5872 m_reporter->sectionEnded(testCaseSectionStats); 5873 5874 auto const& testInfo = m_activeTestCase->getTestCaseInfo(); 5875 5876 Totals deltaTotals; 5877 deltaTotals.testCases.failed = 1; 5878 deltaTotals.assertions.failed = 1; 5879 m_reporter->testCaseEnded(TestCaseStats(testInfo, 5880 deltaTotals, 5881 std::string(), 5882 std::string(), 5883 false)); 5884 m_totals.testCases.failed++; 5885 m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); 5886 } 5887 5888 bool RunContext::lastAssertionPassed() { 5889 return m_lastAssertionPassed; 5890 } 5891 5892 void RunContext::assertionPassed() { 5893 m_lastAssertionPassed = true; 5894 ++m_totals.assertions.passed; 5895 resetAssertionInfo(); 5896 m_messageScopes.clear(); 5897 } 5898 5899 bool RunContext::aborting() const { 5900 return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter()); 5901 } 5902 5903 void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { 5904 auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); 5905 SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); 5906 m_reporter->sectionStarting(testCaseSection); 5907 Counts prevAssertions = m_totals.assertions; 5908 double duration = 0; 5909 m_shouldReportUnexpected = true; 5910 m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; 5911 5912 Timer timer; 5913 CATCH_TRY { 5914 if (m_reporter->getPreferences().shouldRedirectStdOut) { 5915#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) 5916 RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); 5917 5918 timer.start(); 5919 invokeActiveTestCase(); 5920#else 5921 OutputRedirect r(redirectedCout, redirectedCerr); 5922 timer.start(); 5923 invokeActiveTestCase(); 5924#endif 5925 } else { 5926 timer.start(); 5927 invokeActiveTestCase(); 5928 } 5929 duration = timer.getElapsedSeconds(); 5930 } CATCH_CATCH_ANON (TestFailureException&) { 5931 // This just means the test was aborted due to failure 5932 } CATCH_CATCH_ANON (TestSkipException&) { 5933 // This just means the test was explicitly skipped 5934 } CATCH_CATCH_ALL { 5935 // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions 5936 // are reported without translation at the point of origin. 5937 if( m_shouldReportUnexpected ) { 5938 AssertionReaction dummyReaction; 5939 handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); 5940 } 5941 } 5942 Counts assertions = m_totals.assertions - prevAssertions; 5943 bool missingAssertions = testForMissingAssertions(assertions); 5944 5945 m_testCaseTracker->close(); 5946 handleUnfinishedSections(); 5947 m_messages.clear(); 5948 m_messageScopes.clear(); 5949 5950 SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions); 5951 m_reporter->sectionEnded(testCaseSectionStats); 5952 } 5953 5954 void RunContext::invokeActiveTestCase() { 5955 // We need to engage a handler for signals/structured exceptions 5956 // before running the tests themselves, or the binary can crash 5957 // without failed test being reported. 5958 FatalConditionHandlerGuard _(&m_fatalConditionhandler); 5959 // We keep having issue where some compilers warn about an unused 5960 // variable, even though the type has non-trivial constructor and 5961 // destructor. This is annoying and ugly, but it makes them stfu. 5962 (void)_; 5963 5964 m_activeTestCase->invoke(); 5965 } 5966 5967 void RunContext::handleUnfinishedSections() { 5968 // If sections ended prematurely due to an exception we stored their 5969 // infos here so we can tear them down outside the unwind process. 5970 for (auto it = m_unfinishedSections.rbegin(), 5971 itEnd = m_unfinishedSections.rend(); 5972 it != itEnd; 5973 ++it) 5974 sectionEnded(CATCH_MOVE(*it)); 5975 m_unfinishedSections.clear(); 5976 } 5977 5978 void RunContext::handleExpr( 5979 AssertionInfo const& info, 5980 ITransientExpression const& expr, 5981 AssertionReaction& reaction 5982 ) { 5983 bool negated = isFalseTest( info.resultDisposition ); 5984 bool result = expr.getResult() != negated; 5985 5986 if( result ) { 5987 if (!m_includeSuccessfulResults) { 5988 assertionPassed(); 5989 } 5990 else { 5991 reportExpr(info, ResultWas::Ok, &expr, negated); 5992 } 5993 } 5994 else { 5995 reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); 5996 populateReaction( reaction ); 5997 } 5998 resetAssertionInfo(); 5999 } 6000 void RunContext::reportExpr( 6001 AssertionInfo const &info, 6002 ResultWas::OfType resultType, 6003 ITransientExpression const *expr, 6004 bool negated ) { 6005 6006 m_lastAssertionInfo = info; 6007 AssertionResultData data( resultType, LazyExpression( negated ) ); 6008 6009 AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; 6010 assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; 6011 6012 assertionEnded( CATCH_MOVE(assertionResult) ); 6013 } 6014 6015 void RunContext::handleMessage( 6016 AssertionInfo const& info, 6017 ResultWas::OfType resultType, 6018 StringRef message, 6019 AssertionReaction& reaction 6020 ) { 6021 m_lastAssertionInfo = info; 6022 6023 AssertionResultData data( resultType, LazyExpression( false ) ); 6024 data.message = static_cast<std::string>(message); 6025 AssertionResult assertionResult{ m_lastAssertionInfo, 6026 CATCH_MOVE( data ) }; 6027 6028 const auto isOk = assertionResult.isOk(); 6029 assertionEnded( CATCH_MOVE(assertionResult) ); 6030 if ( !isOk ) { 6031 populateReaction( reaction ); 6032 } else if ( resultType == ResultWas::ExplicitSkip ) { 6033 // TODO: Need to handle this explicitly, as ExplicitSkip is 6034 // considered "OK" 6035 reaction.shouldSkip = true; 6036 } 6037 resetAssertionInfo(); 6038 } 6039 void RunContext::handleUnexpectedExceptionNotThrown( 6040 AssertionInfo const& info, 6041 AssertionReaction& reaction 6042 ) { 6043 handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); 6044 } 6045 6046 void RunContext::handleUnexpectedInflightException( 6047 AssertionInfo const& info, 6048 std::string&& message, 6049 AssertionReaction& reaction 6050 ) { 6051 m_lastAssertionInfo = info; 6052 6053 AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); 6054 data.message = CATCH_MOVE(message); 6055 AssertionResult assertionResult{ info, CATCH_MOVE(data) }; 6056 assertionEnded( CATCH_MOVE(assertionResult) ); 6057 populateReaction( reaction ); 6058 resetAssertionInfo(); 6059 } 6060 6061 void RunContext::populateReaction( AssertionReaction& reaction ) { 6062 reaction.shouldDebugBreak = m_config->shouldDebugBreak(); 6063 reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); 6064 } 6065 6066 void RunContext::handleIncomplete( 6067 AssertionInfo const& info 6068 ) { 6069 using namespace std::string_literals; 6070 m_lastAssertionInfo = info; 6071 6072 AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); 6073 data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; 6074 AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; 6075 assertionEnded( CATCH_MOVE(assertionResult) ); 6076 resetAssertionInfo(); 6077 } 6078 void RunContext::handleNonExpr( 6079 AssertionInfo const &info, 6080 ResultWas::OfType resultType, 6081 AssertionReaction &reaction 6082 ) { 6083 m_lastAssertionInfo = info; 6084 6085 AssertionResultData data( resultType, LazyExpression( false ) ); 6086 AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; 6087 6088 const auto isOk = assertionResult.isOk(); 6089 assertionEnded( CATCH_MOVE(assertionResult) ); 6090 if ( !isOk ) { populateReaction( reaction ); } 6091 resetAssertionInfo(); 6092 } 6093 6094 6095 IResultCapture& getResultCapture() { 6096 if (auto* capture = getCurrentContext().getResultCapture()) 6097 return *capture; 6098 else 6099 CATCH_INTERNAL_ERROR("No result capture instance"); 6100 } 6101 6102 void seedRng(IConfig const& config) { 6103 sharedRng().seed(config.rngSeed()); 6104 } 6105 6106 unsigned int rngSeed() { 6107 return getCurrentContext().getConfig()->rngSeed(); 6108 } 6109 6110} 6111 6112 6113 6114namespace Catch { 6115 6116 Section::Section( SectionInfo&& info ): 6117 m_info( CATCH_MOVE( info ) ), 6118 m_sectionIncluded( 6119 getResultCapture().sectionStarted( m_info.name, m_info.lineInfo, m_assertions ) ) { 6120 // Non-"included" sections will not use the timing information 6121 // anyway, so don't bother with the potential syscall. 6122 if (m_sectionIncluded) { 6123 m_timer.start(); 6124 } 6125 } 6126 6127 Section::Section( SourceLineInfo const& _lineInfo, 6128 StringRef _name, 6129 const char* const ): 6130 m_info( { "invalid", static_cast<std::size_t>( -1 ) }, std::string{} ), 6131 m_sectionIncluded( 6132 getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) { 6133 // We delay initialization the SectionInfo member until we know 6134 // this section needs it, so we avoid allocating std::string for name. 6135 // We also delay timer start to avoid the potential syscall unless we 6136 // will actually use the result. 6137 if ( m_sectionIncluded ) { 6138 m_info.name = static_cast<std::string>( _name ); 6139 m_info.lineInfo = _lineInfo; 6140 m_timer.start(); 6141 } 6142 } 6143 6144 Section::~Section() { 6145 if( m_sectionIncluded ) { 6146 SectionEndInfo endInfo{ CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds() }; 6147 if ( uncaught_exceptions() ) { 6148 getResultCapture().sectionEndedEarly( CATCH_MOVE(endInfo) ); 6149 } else { 6150 getResultCapture().sectionEnded( CATCH_MOVE( endInfo ) ); 6151 } 6152 } 6153 } 6154 6155 // This indicates whether the section should be executed or not 6156 Section::operator bool() const { 6157 return m_sectionIncluded; 6158 } 6159 6160 6161} // end namespace Catch 6162 6163 6164 6165#include <vector> 6166 6167namespace Catch { 6168 6169 namespace { 6170 static auto getSingletons() -> std::vector<ISingleton*>*& { 6171 static std::vector<ISingleton*>* g_singletons = nullptr; 6172 if( !g_singletons ) 6173 g_singletons = new std::vector<ISingleton*>(); 6174 return g_singletons; 6175 } 6176 } 6177 6178 ISingleton::~ISingleton() = default; 6179 6180 void addSingleton(ISingleton* singleton ) { 6181 getSingletons()->push_back( singleton ); 6182 } 6183 void cleanupSingletons() { 6184 auto& singletons = getSingletons(); 6185 for( auto singleton : *singletons ) 6186 delete singleton; 6187 delete singletons; 6188 singletons = nullptr; 6189 } 6190 6191} // namespace Catch 6192 6193 6194 6195#include <cstring> 6196#include <ostream> 6197 6198namespace Catch { 6199 6200 bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { 6201 return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); 6202 } 6203 bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { 6204 // We can assume that the same file will usually have the same pointer. 6205 // Thus, if the pointers are the same, there is no point in calling the strcmp 6206 return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); 6207 } 6208 6209 std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { 6210#ifndef __GNUG__ 6211 os << info.file << '(' << info.line << ')'; 6212#else 6213 os << info.file << ':' << info.line; 6214#endif 6215 return os; 6216 } 6217 6218} // end namespace Catch 6219 6220 6221 6222 6223namespace Catch { 6224#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 6225 void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { 6226 CATCH_TRY { 6227 m_exceptions.push_back(exception); 6228 } CATCH_CATCH_ALL { 6229 // If we run out of memory during start-up there's really not a lot more we can do about it 6230 std::terminate(); 6231 } 6232 } 6233 6234 std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept { 6235 return m_exceptions; 6236 } 6237#endif 6238 6239} // end namespace Catch 6240 6241 6242 6243 6244 6245#include <iostream> 6246 6247namespace Catch { 6248 6249// If you #define this you must implement these functions 6250#if !defined( CATCH_CONFIG_NOSTDOUT ) 6251 std::ostream& cout() { return std::cout; } 6252 std::ostream& cerr() { return std::cerr; } 6253 std::ostream& clog() { return std::clog; } 6254#endif 6255 6256} // namespace Catch 6257 6258 6259 6260#include <ostream> 6261#include <cstring> 6262#include <cctype> 6263#include <vector> 6264 6265namespace Catch { 6266 6267 bool startsWith( std::string const& s, std::string const& prefix ) { 6268 return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); 6269 } 6270 bool startsWith( StringRef s, char prefix ) { 6271 return !s.empty() && s[0] == prefix; 6272 } 6273 bool endsWith( std::string const& s, std::string const& suffix ) { 6274 return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); 6275 } 6276 bool endsWith( std::string const& s, char suffix ) { 6277 return !s.empty() && s[s.size()-1] == suffix; 6278 } 6279 bool contains( std::string const& s, std::string const& infix ) { 6280 return s.find( infix ) != std::string::npos; 6281 } 6282 void toLowerInPlace( std::string& s ) { 6283 for ( char& c : s ) { 6284 c = toLower( c ); 6285 } 6286 } 6287 std::string toLower( std::string const& s ) { 6288 std::string lc = s; 6289 toLowerInPlace( lc ); 6290 return lc; 6291 } 6292 char toLower(char c) { 6293 return static_cast<char>(std::tolower(static_cast<unsigned char>(c))); 6294 } 6295 6296 std::string trim( std::string const& str ) { 6297 static char const* whitespaceChars = "\n\r\t "; 6298 std::string::size_type start = str.find_first_not_of( whitespaceChars ); 6299 std::string::size_type end = str.find_last_not_of( whitespaceChars ); 6300 6301 return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); 6302 } 6303 6304 StringRef trim(StringRef ref) { 6305 const auto is_ws = [](char c) { 6306 return c == ' ' || c == '\t' || c == '\n' || c == '\r'; 6307 }; 6308 size_t real_begin = 0; 6309 while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } 6310 size_t real_end = ref.size(); 6311 while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } 6312 6313 return ref.substr(real_begin, real_end - real_begin); 6314 } 6315 6316 bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { 6317 std::size_t i = str.find( replaceThis ); 6318 if (i == std::string::npos) { 6319 return false; 6320 } 6321 std::size_t copyBegin = 0; 6322 std::string origStr = CATCH_MOVE(str); 6323 str.clear(); 6324 // There is at least one replacement, so reserve with the best guess 6325 // we can make without actually counting the number of occurences. 6326 str.reserve(origStr.size() - replaceThis.size() + withThis.size()); 6327 do { 6328 str.append(origStr, copyBegin, i-copyBegin ); 6329 str += withThis; 6330 copyBegin = i + replaceThis.size(); 6331 if( copyBegin < origStr.size() ) 6332 i = origStr.find( replaceThis, copyBegin ); 6333 else 6334 i = std::string::npos; 6335 } while( i != std::string::npos ); 6336 if ( copyBegin < origStr.size() ) { 6337 str.append(origStr, copyBegin, origStr.size() ); 6338 } 6339 return true; 6340 } 6341 6342 std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) { 6343 std::vector<StringRef> subStrings; 6344 std::size_t start = 0; 6345 for(std::size_t pos = 0; pos < str.size(); ++pos ) { 6346 if( str[pos] == delimiter ) { 6347 if( pos - start > 1 ) 6348 subStrings.push_back( str.substr( start, pos-start ) ); 6349 start = pos+1; 6350 } 6351 } 6352 if( start < str.size() ) 6353 subStrings.push_back( str.substr( start, str.size()-start ) ); 6354 return subStrings; 6355 } 6356 6357 std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { 6358 os << pluraliser.m_count << ' ' << pluraliser.m_label; 6359 if( pluraliser.m_count != 1 ) 6360 os << 's'; 6361 return os; 6362 } 6363 6364} 6365 6366 6367 6368#include <algorithm> 6369#include <ostream> 6370#include <cstring> 6371#include <cstdint> 6372 6373namespace Catch { 6374 StringRef::StringRef( char const* rawChars ) noexcept 6375 : StringRef( rawChars, std::strlen(rawChars) ) 6376 {} 6377 6378 6379 bool StringRef::operator<(StringRef rhs) const noexcept { 6380 if (m_size < rhs.m_size) { 6381 return strncmp(m_start, rhs.m_start, m_size) <= 0; 6382 } 6383 return strncmp(m_start, rhs.m_start, rhs.m_size) < 0; 6384 } 6385 6386 int StringRef::compare( StringRef rhs ) const { 6387 auto cmpResult = 6388 strncmp( m_start, rhs.m_start, std::min( m_size, rhs.m_size ) ); 6389 6390 // This means that strncmp found a difference before the strings 6391 // ended, and we can return it directly 6392 if ( cmpResult != 0 ) { 6393 return cmpResult; 6394 } 6395 6396 // If strings are equal up to length, then their comparison results on 6397 // their size 6398 if ( m_size < rhs.m_size ) { 6399 return -1; 6400 } else if ( m_size > rhs.m_size ) { 6401 return 1; 6402 } else { 6403 return 0; 6404 } 6405 } 6406 6407 auto operator << ( std::ostream& os, StringRef str ) -> std::ostream& { 6408 return os.write(str.data(), static_cast<std::streamsize>(str.size())); 6409 } 6410 6411 std::string operator+(StringRef lhs, StringRef rhs) { 6412 std::string ret; 6413 ret.reserve(lhs.size() + rhs.size()); 6414 ret += lhs; 6415 ret += rhs; 6416 return ret; 6417 } 6418 6419 auto operator+=( std::string& lhs, StringRef rhs ) -> std::string& { 6420 lhs.append(rhs.data(), rhs.size()); 6421 return lhs; 6422 } 6423 6424} // namespace Catch 6425 6426 6427 6428namespace Catch { 6429 6430 TagAliasRegistry::~TagAliasRegistry() = default; 6431 6432 TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { 6433 auto it = m_registry.find( alias ); 6434 if( it != m_registry.end() ) 6435 return &(it->second); 6436 else 6437 return nullptr; 6438 } 6439 6440 std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { 6441 std::string expandedTestSpec = unexpandedTestSpec; 6442 for( auto const& registryKvp : m_registry ) { 6443 std::size_t pos = expandedTestSpec.find( registryKvp.first ); 6444 if( pos != std::string::npos ) { 6445 expandedTestSpec = expandedTestSpec.substr( 0, pos ) + 6446 registryKvp.second.tag + 6447 expandedTestSpec.substr( pos + registryKvp.first.size() ); 6448 } 6449 } 6450 return expandedTestSpec; 6451 } 6452 6453 void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { 6454 CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), 6455 "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); 6456 6457 CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, 6458 "error: tag alias, '" << alias << "' already registered.\n" 6459 << "\tFirst seen at: " << find(alias)->lineInfo << "\n" 6460 << "\tRedefined at: " << lineInfo ); 6461 } 6462 6463 ITagAliasRegistry::~ITagAliasRegistry() = default; 6464 6465 ITagAliasRegistry const& ITagAliasRegistry::get() { 6466 return getRegistryHub().getTagAliasRegistry(); 6467 } 6468 6469} // end namespace Catch 6470 6471 6472 6473 6474namespace Catch { 6475 TestCaseInfoHasher::TestCaseInfoHasher( hash_t seed ): m_seed( seed ) {} 6476 6477 uint32_t TestCaseInfoHasher::operator()( TestCaseInfo const& t ) const { 6478 // FNV-1a hash algorithm that is designed for uniqueness: 6479 const hash_t prime = 1099511628211u; 6480 hash_t hash = 14695981039346656037u; 6481 for ( const char c : t.name ) { 6482 hash ^= c; 6483 hash *= prime; 6484 } 6485 for ( const char c : t.className ) { 6486 hash ^= c; 6487 hash *= prime; 6488 } 6489 for ( const Tag& tag : t.tags ) { 6490 for ( const char c : tag.original ) { 6491 hash ^= c; 6492 hash *= prime; 6493 } 6494 } 6495 hash ^= m_seed; 6496 hash *= prime; 6497 const uint32_t low{ static_cast<uint32_t>( hash ) }; 6498 const uint32_t high{ static_cast<uint32_t>( hash >> 32 ) }; 6499 return low * high; 6500 } 6501} // namespace Catch 6502 6503 6504 6505 6506#include <algorithm> 6507#include <set> 6508 6509namespace Catch { 6510 6511 namespace { 6512 static void enforceNoDuplicateTestCases( 6513 std::vector<TestCaseHandle> const& tests ) { 6514 auto testInfoCmp = []( TestCaseInfo const* lhs, 6515 TestCaseInfo const* rhs ) { 6516 return *lhs < *rhs; 6517 }; 6518 std::set<TestCaseInfo const*, decltype( testInfoCmp )&> seenTests( 6519 testInfoCmp ); 6520 for ( auto const& test : tests ) { 6521 const auto infoPtr = &test.getTestCaseInfo(); 6522 const auto prev = seenTests.insert( infoPtr ); 6523 CATCH_ENFORCE( prev.second, 6524 "error: test case \"" 6525 << infoPtr->name << "\", with tags \"" 6526 << infoPtr->tagsAsString() 6527 << "\" already defined.\n" 6528 << "\tFirst seen at " 6529 << ( *prev.first )->lineInfo << "\n" 6530 << "\tRedefined at " << infoPtr->lineInfo ); 6531 } 6532 } 6533 6534 static bool matchTest( TestCaseHandle const& testCase, 6535 TestSpec const& testSpec, 6536 IConfig const& config ) { 6537 return testSpec.matches( testCase.getTestCaseInfo() ) && 6538 isThrowSafe( testCase, config ); 6539 } 6540 6541 } // end unnamed namespace 6542 6543 std::vector<TestCaseHandle> sortTests( IConfig const& config, std::vector<TestCaseHandle> const& unsortedTestCases ) { 6544 switch (config.runOrder()) { 6545 case TestRunOrder::Declared: 6546 return unsortedTestCases; 6547 6548 case TestRunOrder::LexicographicallySorted: { 6549 std::vector<TestCaseHandle> sorted = unsortedTestCases; 6550 std::sort( 6551 sorted.begin(), 6552 sorted.end(), 6553 []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) { 6554 return lhs.getTestCaseInfo() < rhs.getTestCaseInfo(); 6555 } 6556 ); 6557 return sorted; 6558 } 6559 case TestRunOrder::Randomized: { 6560 using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>; 6561 6562 TestCaseInfoHasher h{ config.rngSeed() }; 6563 std::vector<TestWithHash> indexed_tests; 6564 indexed_tests.reserve(unsortedTestCases.size()); 6565 6566 for (auto const& handle : unsortedTestCases) { 6567 indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle); 6568 } 6569 6570 std::sort( indexed_tests.begin(), 6571 indexed_tests.end(), 6572 []( TestWithHash const& lhs, TestWithHash const& rhs ) { 6573 if ( lhs.first == rhs.first ) { 6574 return lhs.second.getTestCaseInfo() < 6575 rhs.second.getTestCaseInfo(); 6576 } 6577 return lhs.first < rhs.first; 6578 } ); 6579 6580 std::vector<TestCaseHandle> randomized; 6581 randomized.reserve(indexed_tests.size()); 6582 6583 for (auto const& indexed : indexed_tests) { 6584 randomized.push_back(indexed.second); 6585 } 6586 6587 return randomized; 6588 } 6589 } 6590 6591 CATCH_INTERNAL_ERROR("Unknown test order value!"); 6592 } 6593 6594 bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ) { 6595 return !testCase.getTestCaseInfo().throws() || config.allowThrows(); 6596 } 6597 6598 std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config ) { 6599 std::vector<TestCaseHandle> filtered; 6600 filtered.reserve( testCases.size() ); 6601 for (auto const& testCase : testCases) { 6602 if ((!testSpec.hasFilters() && !testCase.getTestCaseInfo().isHidden()) || 6603 (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { 6604 filtered.push_back(testCase); 6605 } 6606 } 6607 return createShard(filtered, config.shardCount(), config.shardIndex()); 6608 } 6609 std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config ) { 6610 return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); 6611 } 6612 6613 TestRegistry::~TestRegistry() = default; 6614 6615 void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) { 6616 m_handles.emplace_back(testInfo.get(), testInvoker.get()); 6617 m_viewed_test_infos.push_back(testInfo.get()); 6618 m_owned_test_infos.push_back(CATCH_MOVE(testInfo)); 6619 m_invokers.push_back(CATCH_MOVE(testInvoker)); 6620 } 6621 6622 std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const { 6623 return m_viewed_test_infos; 6624 } 6625 6626 std::vector<TestCaseHandle> const& TestRegistry::getAllTests() const { 6627 return m_handles; 6628 } 6629 std::vector<TestCaseHandle> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { 6630 if( m_sortedFunctions.empty() ) 6631 enforceNoDuplicateTestCases( m_handles ); 6632 6633 if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { 6634 m_sortedFunctions = sortTests( config, m_handles ); 6635 m_currentSortOrder = config.runOrder(); 6636 } 6637 return m_sortedFunctions; 6638 } 6639 6640} // end namespace Catch 6641 6642 6643 6644 6645#include <algorithm> 6646#include <cassert> 6647 6648#if defined(__clang__) 6649# pragma clang diagnostic push 6650# pragma clang diagnostic ignored "-Wexit-time-destructors" 6651#endif 6652 6653namespace Catch { 6654namespace TestCaseTracking { 6655 6656 NameAndLocation::NameAndLocation( std::string&& _name, SourceLineInfo const& _location ) 6657 : name( CATCH_MOVE(_name) ), 6658 location( _location ) 6659 {} 6660 6661 6662 ITracker::~ITracker() = default; 6663 6664 void ITracker::markAsNeedingAnotherRun() { 6665 m_runState = NeedsAnotherRun; 6666 } 6667 6668 void ITracker::addChild( ITrackerPtr&& child ) { 6669 m_children.push_back( CATCH_MOVE(child) ); 6670 } 6671 6672 ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) { 6673 auto it = std::find_if( 6674 m_children.begin(), 6675 m_children.end(), 6676 [&nameAndLocation]( ITrackerPtr const& tracker ) { 6677 auto const& tnameAndLoc = tracker->nameAndLocation(); 6678 if ( tnameAndLoc.location.line != 6679 nameAndLocation.location.line ) { 6680 return false; 6681 } 6682 return tnameAndLoc == nameAndLocation; 6683 } ); 6684 return ( it != m_children.end() ) ? it->get() : nullptr; 6685 } 6686 6687 bool ITracker::isSectionTracker() const { return false; } 6688 bool ITracker::isGeneratorTracker() const { return false; } 6689 6690 bool ITracker::isOpen() const { 6691 return m_runState != NotStarted && !isComplete(); 6692 } 6693 6694 bool ITracker::hasStarted() const { return m_runState != NotStarted; } 6695 6696 void ITracker::openChild() { 6697 if (m_runState != ExecutingChildren) { 6698 m_runState = ExecutingChildren; 6699 if (m_parent) { 6700 m_parent->openChild(); 6701 } 6702 } 6703 } 6704 6705 ITracker& TrackerContext::startRun() { 6706 using namespace std::string_literals; 6707 m_rootTracker = Catch::Detail::make_unique<SectionTracker>( 6708 NameAndLocation( "{root}"s, CATCH_INTERNAL_LINEINFO ), 6709 *this, 6710 nullptr ); 6711 m_currentTracker = nullptr; 6712 m_runState = Executing; 6713 return *m_rootTracker; 6714 } 6715 6716 void TrackerContext::completeCycle() { 6717 m_runState = CompletedCycle; 6718 } 6719 6720 bool TrackerContext::completedCycle() const { 6721 return m_runState == CompletedCycle; 6722 } 6723 void TrackerContext::setCurrentTracker( ITracker* tracker ) { 6724 m_currentTracker = tracker; 6725 } 6726 6727 6728 TrackerBase::TrackerBase( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ): 6729 ITracker(CATCH_MOVE(nameAndLocation), parent), 6730 m_ctx( ctx ) 6731 {} 6732 6733 bool TrackerBase::isComplete() const { 6734 return m_runState == CompletedSuccessfully || m_runState == Failed; 6735 } 6736 6737 void TrackerBase::open() { 6738 m_runState = Executing; 6739 moveToThis(); 6740 if( m_parent ) 6741 m_parent->openChild(); 6742 } 6743 6744 void TrackerBase::close() { 6745 6746 // Close any still open children (e.g. generators) 6747 while( &m_ctx.currentTracker() != this ) 6748 m_ctx.currentTracker().close(); 6749 6750 switch( m_runState ) { 6751 case NeedsAnotherRun: 6752 break; 6753 6754 case Executing: 6755 m_runState = CompletedSuccessfully; 6756 break; 6757 case ExecutingChildren: 6758 if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) ) 6759 m_runState = CompletedSuccessfully; 6760 break; 6761 6762 case NotStarted: 6763 case CompletedSuccessfully: 6764 case Failed: 6765 CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); 6766 6767 default: 6768 CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); 6769 } 6770 moveToParent(); 6771 m_ctx.completeCycle(); 6772 } 6773 void TrackerBase::fail() { 6774 m_runState = Failed; 6775 if( m_parent ) 6776 m_parent->markAsNeedingAnotherRun(); 6777 moveToParent(); 6778 m_ctx.completeCycle(); 6779 } 6780 6781 void TrackerBase::moveToParent() { 6782 assert( m_parent ); 6783 m_ctx.setCurrentTracker( m_parent ); 6784 } 6785 void TrackerBase::moveToThis() { 6786 m_ctx.setCurrentTracker( this ); 6787 } 6788 6789 SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) 6790 : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), 6791 m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) 6792 { 6793 if( parent ) { 6794 while ( !parent->isSectionTracker() ) { 6795 parent = parent->parent(); 6796 } 6797 6798 SectionTracker& parentSection = static_cast<SectionTracker&>( *parent ); 6799 addNextFilters( parentSection.m_filters ); 6800 } 6801 } 6802 6803 bool SectionTracker::isComplete() const { 6804 bool complete = true; 6805 6806 if (m_filters.empty() 6807 || m_filters[0].empty() 6808 || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { 6809 complete = TrackerBase::isComplete(); 6810 } 6811 return complete; 6812 } 6813 6814 bool SectionTracker::isSectionTracker() const { return true; } 6815 6816 SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) { 6817 SectionTracker* tracker; 6818 6819 ITracker& currentTracker = ctx.currentTracker(); 6820 if ( ITracker* childTracker = 6821 currentTracker.findChild( nameAndLocation ) ) { 6822 assert( childTracker ); 6823 assert( childTracker->isSectionTracker() ); 6824 tracker = static_cast<SectionTracker*>( childTracker ); 6825 } else { 6826 auto newTracker = Catch::Detail::make_unique<SectionTracker>( 6827 NameAndLocation{ static_cast<std::string>(nameAndLocation.name), 6828 nameAndLocation.location }, 6829 ctx, 6830 &currentTracker ); 6831 tracker = newTracker.get(); 6832 currentTracker.addChild( CATCH_MOVE( newTracker ) ); 6833 } 6834 6835 if ( !ctx.completedCycle() ) { 6836 tracker->tryOpen(); 6837 } 6838 6839 return *tracker; 6840 } 6841 6842 void SectionTracker::tryOpen() { 6843 if( !isComplete() ) 6844 open(); 6845 } 6846 6847 void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) { 6848 if( !filters.empty() ) { 6849 m_filters.reserve( m_filters.size() + filters.size() + 2 ); 6850 m_filters.emplace_back(StringRef{}); // Root - should never be consulted 6851 m_filters.emplace_back(StringRef{}); // Test Case - not a section filter 6852 m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); 6853 } 6854 } 6855 void SectionTracker::addNextFilters( std::vector<StringRef> const& filters ) { 6856 if( filters.size() > 1 ) 6857 m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); 6858 } 6859 6860 StringRef SectionTracker::trimmedName() const { 6861 return m_trimmed_name; 6862 } 6863 6864} // namespace TestCaseTracking 6865 6866} // namespace Catch 6867 6868#if defined(__clang__) 6869# pragma clang diagnostic pop 6870#endif 6871 6872 6873 6874 6875namespace Catch { 6876 6877 void throw_test_failure_exception() { 6878#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) 6879 throw TestFailureException{}; 6880#else 6881 CATCH_ERROR( "Test failure requires aborting test!" ); 6882#endif 6883 } 6884 6885 void throw_test_skip_exception() { 6886#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) 6887 throw Catch::TestSkipException(); 6888#else 6889 CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); 6890#endif 6891 } 6892 6893} // namespace Catch 6894 6895 6896 6897#include <algorithm> 6898#include <iterator> 6899 6900namespace Catch { 6901 ITestInvoker::~ITestInvoker() = default; 6902 6903 namespace { 6904 static StringRef extractClassName( StringRef classOrMethodName ) { 6905 if ( !startsWith( classOrMethodName, '&' ) ) { 6906 return classOrMethodName; 6907 } 6908 6909 // Remove the leading '&' to avoid having to special case it later 6910 const auto methodName = 6911 classOrMethodName.substr( 1, classOrMethodName.size() ); 6912 6913 auto reverseStart = std::make_reverse_iterator( methodName.end() ); 6914 auto reverseEnd = std::make_reverse_iterator( methodName.begin() ); 6915 6916 // We make a simplifying assumption that ":" is only present 6917 // in the input as part of "::" from C++ typenames (this is 6918 // relatively safe assumption because the input is generated 6919 // as stringification of type through preprocessor). 6920 auto lastColons = std::find( reverseStart, reverseEnd, ':' ) + 1; 6921 auto secondLastColons = 6922 std::find( lastColons + 1, reverseEnd, ':' ); 6923 6924 auto const startIdx = reverseEnd - secondLastColons; 6925 auto const classNameSize = secondLastColons - lastColons - 1; 6926 6927 return methodName.substr( 6928 static_cast<std::size_t>( startIdx ), 6929 static_cast<std::size_t>( classNameSize ) ); 6930 } 6931 6932 class TestInvokerAsFunction final : public ITestInvoker { 6933 using TestType = void ( * )(); 6934 TestType m_testAsFunction; 6935 6936 public: 6937 TestInvokerAsFunction( TestType testAsFunction ) noexcept: 6938 m_testAsFunction( testAsFunction ) {} 6939 6940 void invoke() const override { m_testAsFunction(); } 6941 }; 6942 6943 } // namespace 6944 6945 Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) { 6946 return Detail::make_unique<TestInvokerAsFunction>( testAsFunction ); 6947 } 6948 6949 AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept { 6950 CATCH_TRY { 6951 getMutableRegistryHub() 6952 .registerTest( 6953 makeTestCaseInfo( 6954 extractClassName( classOrMethod ), 6955 nameAndTags, 6956 lineInfo), 6957 CATCH_MOVE(invoker) 6958 ); 6959 } CATCH_CATCH_ALL { 6960 // Do not throw when constructing global objects, instead register the exception to be processed later 6961 getMutableRegistryHub().registerStartupException(); 6962 } 6963 } 6964} 6965 6966 6967 6968 6969 6970namespace Catch { 6971 6972 TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} 6973 6974 TestSpecParser& TestSpecParser::parse( std::string const& arg ) { 6975 m_mode = None; 6976 m_exclusion = false; 6977 m_arg = m_tagAliases->expandAliases( arg ); 6978 m_escapeChars.clear(); 6979 m_substring.reserve(m_arg.size()); 6980 m_patternName.reserve(m_arg.size()); 6981 m_realPatternPos = 0; 6982 6983 for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) 6984 //if visitChar fails 6985 if( !visitChar( m_arg[m_pos] ) ){ 6986 m_testSpec.m_invalidSpecs.push_back(arg); 6987 break; 6988 } 6989 endMode(); 6990 return *this; 6991 } 6992 TestSpec TestSpecParser::testSpec() { 6993 addFilter(); 6994 return CATCH_MOVE(m_testSpec); 6995 } 6996 bool TestSpecParser::visitChar( char c ) { 6997 if( (m_mode != EscapedName) && (c == '\\') ) { 6998 escape(); 6999 addCharToPattern(c); 7000 return true; 7001 }else if((m_mode != EscapedName) && (c == ',') ) { 7002 return separate(); 7003 } 7004 7005 switch( m_mode ) { 7006 case None: 7007 if( processNoneChar( c ) ) 7008 return true; 7009 break; 7010 case Name: 7011 processNameChar( c ); 7012 break; 7013 case EscapedName: 7014 endMode(); 7015 addCharToPattern(c); 7016 return true; 7017 default: 7018 case Tag: 7019 case QuotedName: 7020 if( processOtherChar( c ) ) 7021 return true; 7022 break; 7023 } 7024 7025 m_substring += c; 7026 if( !isControlChar( c ) ) { 7027 m_patternName += c; 7028 m_realPatternPos++; 7029 } 7030 return true; 7031 } 7032 // Two of the processing methods return true to signal the caller to return 7033 // without adding the given character to the current pattern strings 7034 bool TestSpecParser::processNoneChar( char c ) { 7035 switch( c ) { 7036 case ' ': 7037 return true; 7038 case '~': 7039 m_exclusion = true; 7040 return false; 7041 case '[': 7042 startNewMode( Tag ); 7043 return false; 7044 case '"': 7045 startNewMode( QuotedName ); 7046 return false; 7047 default: 7048 startNewMode( Name ); 7049 return false; 7050 } 7051 } 7052 void TestSpecParser::processNameChar( char c ) { 7053 if( c == '[' ) { 7054 if( m_substring == "exclude:" ) 7055 m_exclusion = true; 7056 else 7057 endMode(); 7058 startNewMode( Tag ); 7059 } 7060 } 7061 bool TestSpecParser::processOtherChar( char c ) { 7062 if( !isControlChar( c ) ) 7063 return false; 7064 m_substring += c; 7065 endMode(); 7066 return true; 7067 } 7068 void TestSpecParser::startNewMode( Mode mode ) { 7069 m_mode = mode; 7070 } 7071 void TestSpecParser::endMode() { 7072 switch( m_mode ) { 7073 case Name: 7074 case QuotedName: 7075 return addNamePattern(); 7076 case Tag: 7077 return addTagPattern(); 7078 case EscapedName: 7079 revertBackToLastMode(); 7080 return; 7081 case None: 7082 default: 7083 return startNewMode( None ); 7084 } 7085 } 7086 void TestSpecParser::escape() { 7087 saveLastMode(); 7088 m_mode = EscapedName; 7089 m_escapeChars.push_back(m_realPatternPos); 7090 } 7091 bool TestSpecParser::isControlChar( char c ) const { 7092 switch( m_mode ) { 7093 default: 7094 return false; 7095 case None: 7096 return c == '~'; 7097 case Name: 7098 return c == '['; 7099 case EscapedName: 7100 return true; 7101 case QuotedName: 7102 return c == '"'; 7103 case Tag: 7104 return c == '[' || c == ']'; 7105 } 7106 } 7107 7108 void TestSpecParser::addFilter() { 7109 if( !m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty() ) { 7110 m_testSpec.m_filters.push_back( CATCH_MOVE(m_currentFilter) ); 7111 m_currentFilter = TestSpec::Filter(); 7112 } 7113 } 7114 7115 void TestSpecParser::saveLastMode() { 7116 lastMode = m_mode; 7117 } 7118 7119 void TestSpecParser::revertBackToLastMode() { 7120 m_mode = lastMode; 7121 } 7122 7123 bool TestSpecParser::separate() { 7124 if( (m_mode==QuotedName) || (m_mode==Tag) ){ 7125 //invalid argument, signal failure to previous scope. 7126 m_mode = None; 7127 m_pos = m_arg.size(); 7128 m_substring.clear(); 7129 m_patternName.clear(); 7130 m_realPatternPos = 0; 7131 return false; 7132 } 7133 endMode(); 7134 addFilter(); 7135 return true; //success 7136 } 7137 7138 std::string TestSpecParser::preprocessPattern() { 7139 std::string token = m_patternName; 7140 for (std::size_t i = 0; i < m_escapeChars.size(); ++i) 7141 token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); 7142 m_escapeChars.clear(); 7143 if (startsWith(token, "exclude:")) { 7144 m_exclusion = true; 7145 token = token.substr(8); 7146 } 7147 7148 m_patternName.clear(); 7149 m_realPatternPos = 0; 7150 7151 return token; 7152 } 7153 7154 void TestSpecParser::addNamePattern() { 7155 auto token = preprocessPattern(); 7156 7157 if (!token.empty()) { 7158 if (m_exclusion) { 7159 m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::NamePattern>(token, m_substring)); 7160 } else { 7161 m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::NamePattern>(token, m_substring)); 7162 } 7163 } 7164 m_substring.clear(); 7165 m_exclusion = false; 7166 m_mode = None; 7167 } 7168 7169 void TestSpecParser::addTagPattern() { 7170 auto token = preprocessPattern(); 7171 7172 if (!token.empty()) { 7173 // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) 7174 // we have to create a separate hide tag and shorten the real one 7175 if (token.size() > 1 && token[0] == '.') { 7176 token.erase(token.begin()); 7177 if (m_exclusion) { 7178 m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring)); 7179 } else { 7180 m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(".", m_substring)); 7181 } 7182 } 7183 if (m_exclusion) { 7184 m_currentFilter.m_forbidden.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring)); 7185 } else { 7186 m_currentFilter.m_required.emplace_back(Detail::make_unique<TestSpec::TagPattern>(token, m_substring)); 7187 } 7188 } 7189 m_substring.clear(); 7190 m_exclusion = false; 7191 m_mode = None; 7192 } 7193 7194} // namespace Catch 7195 7196 7197 7198#include <algorithm> 7199#include <cstring> 7200#include <ostream> 7201 7202namespace { 7203 bool isWhitespace( char c ) { 7204 return c == ' ' || c == '\t' || c == '\n' || c == '\r'; 7205 } 7206 7207 bool isBreakableBefore( char c ) { 7208 static const char chars[] = "[({<|"; 7209 return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; 7210 } 7211 7212 bool isBreakableAfter( char c ) { 7213 static const char chars[] = "])}>.,:;*+-=&/\\"; 7214 return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; 7215 } 7216 7217} // namespace 7218 7219namespace Catch { 7220 namespace TextFlow { 7221 void AnsiSkippingString::preprocessString() { 7222 for ( auto it = m_string.begin(); it != m_string.end(); ) { 7223 // try to read through an ansi sequence 7224 while ( it != m_string.end() && *it == '\033' && 7225 it + 1 != m_string.end() && *( it + 1 ) == '[' ) { 7226 auto cursor = it + 2; 7227 while ( cursor != m_string.end() && 7228 ( isdigit( *cursor ) || *cursor == ';' ) ) { 7229 ++cursor; 7230 } 7231 if ( cursor == m_string.end() || *cursor != 'm' ) { 7232 break; 7233 } 7234 // 'm' -> 0xff 7235 *cursor = AnsiSkippingString::sentinel; 7236 // if we've read an ansi sequence, set the iterator and 7237 // return to the top of the loop 7238 it = cursor + 1; 7239 } 7240 if ( it != m_string.end() ) { 7241 ++m_size; 7242 ++it; 7243 } 7244 } 7245 } 7246 7247 AnsiSkippingString::AnsiSkippingString( std::string const& text ): 7248 m_string( text ) { 7249 preprocessString(); 7250 } 7251 7252 AnsiSkippingString::AnsiSkippingString( std::string&& text ): 7253 m_string( CATCH_MOVE( text ) ) { 7254 preprocessString(); 7255 } 7256 7257 AnsiSkippingString::const_iterator AnsiSkippingString::begin() const { 7258 return const_iterator( m_string ); 7259 } 7260 7261 AnsiSkippingString::const_iterator AnsiSkippingString::end() const { 7262 return const_iterator( m_string, const_iterator::EndTag{} ); 7263 } 7264 7265 std::string AnsiSkippingString::substring( const_iterator begin, 7266 const_iterator end ) const { 7267 // There's one caveat here to an otherwise simple substring: when 7268 // making a begin iterator we might have skipped ansi sequences at 7269 // the start. If `begin` here is a begin iterator, skipped over 7270 // initial ansi sequences, we'll use the true beginning of the 7271 // string. Lastly: We need to transform any chars we replaced with 7272 // 0xff back to 'm' 7273 auto str = std::string( begin == this->begin() ? m_string.begin() 7274 : begin.m_it, 7275 end.m_it ); 7276 std::transform( str.begin(), str.end(), str.begin(), []( char c ) { 7277 return c == AnsiSkippingString::sentinel ? 'm' : c; 7278 } ); 7279 return str; 7280 } 7281 7282 void AnsiSkippingString::const_iterator::tryParseAnsiEscapes() { 7283 // check if we've landed on an ansi sequence, and if so read through 7284 // it 7285 while ( m_it != m_string->end() && *m_it == '\033' && 7286 m_it + 1 != m_string->end() && *( m_it + 1 ) == '[' ) { 7287 auto cursor = m_it + 2; 7288 while ( cursor != m_string->end() && 7289 ( isdigit( *cursor ) || *cursor == ';' ) ) { 7290 ++cursor; 7291 } 7292 if ( cursor == m_string->end() || 7293 *cursor != AnsiSkippingString::sentinel ) { 7294 break; 7295 } 7296 // if we've read an ansi sequence, set the iterator and 7297 // return to the top of the loop 7298 m_it = cursor + 1; 7299 } 7300 } 7301 7302 void AnsiSkippingString::const_iterator::advance() { 7303 assert( m_it != m_string->end() ); 7304 m_it++; 7305 tryParseAnsiEscapes(); 7306 } 7307 7308 void AnsiSkippingString::const_iterator::unadvance() { 7309 assert( m_it != m_string->begin() ); 7310 m_it--; 7311 // if *m_it is 0xff, scan back to the \033 and then m_it-- once more 7312 // (and repeat check) 7313 while ( *m_it == AnsiSkippingString::sentinel ) { 7314 while ( *m_it != '\033' ) { 7315 assert( m_it != m_string->begin() ); 7316 m_it--; 7317 } 7318 // if this happens, we must have been a begin iterator that had 7319 // skipped over ansi sequences at the start of a string 7320 assert( m_it != m_string->begin() ); 7321 assert( *m_it == '\033' ); 7322 m_it--; 7323 } 7324 } 7325 7326 static bool isBoundary( AnsiSkippingString const& line, 7327 AnsiSkippingString::const_iterator it ) { 7328 return it == line.end() || 7329 ( isWhitespace( *it ) && 7330 !isWhitespace( *it.oneBefore() ) ) || 7331 isBreakableBefore( *it ) || 7332 isBreakableAfter( *it.oneBefore() ); 7333 } 7334 7335 void Column::const_iterator::calcLength() { 7336 m_addHyphen = false; 7337 m_parsedTo = m_lineStart; 7338 AnsiSkippingString const& current_line = m_column.m_string; 7339 7340 if ( m_parsedTo == current_line.end() ) { 7341 m_lineEnd = m_parsedTo; 7342 return; 7343 } 7344 7345 assert( m_lineStart != current_line.end() ); 7346 if ( *m_lineStart == '\n' ) { ++m_parsedTo; } 7347 7348 const auto maxLineLength = m_column.m_width - indentSize(); 7349 std::size_t lineLength = 0; 7350 while ( m_parsedTo != current_line.end() && 7351 lineLength < maxLineLength && *m_parsedTo != '\n' ) { 7352 ++m_parsedTo; 7353 ++lineLength; 7354 } 7355 7356 // If we encountered a newline before the column is filled, 7357 // then we linebreak at the newline and consider this line 7358 // finished. 7359 if ( lineLength < maxLineLength ) { 7360 m_lineEnd = m_parsedTo; 7361 } else { 7362 // Look for a natural linebreak boundary in the column 7363 // (We look from the end, so that the first found boundary is 7364 // the right one) 7365 m_lineEnd = m_parsedTo; 7366 while ( lineLength > 0 && 7367 !isBoundary( current_line, m_lineEnd ) ) { 7368 --lineLength; 7369 --m_lineEnd; 7370 } 7371 while ( lineLength > 0 && 7372 isWhitespace( *m_lineEnd.oneBefore() ) ) { 7373 --lineLength; 7374 --m_lineEnd; 7375 } 7376 7377 // If we found one, then that is where we linebreak, otherwise 7378 // we have to split text with a hyphen 7379 if ( lineLength == 0 ) { 7380 m_addHyphen = true; 7381 m_lineEnd = m_parsedTo.oneBefore(); 7382 } 7383 } 7384 } 7385 7386 size_t Column::const_iterator::indentSize() const { 7387 auto initial = m_lineStart == m_column.m_string.begin() 7388 ? m_column.m_initialIndent 7389 : std::string::npos; 7390 return initial == std::string::npos ? m_column.m_indent : initial; 7391 } 7392 7393 std::string Column::const_iterator::addIndentAndSuffix( 7394 AnsiSkippingString::const_iterator start, 7395 AnsiSkippingString::const_iterator end ) const { 7396 std::string ret; 7397 const auto desired_indent = indentSize(); 7398 // ret.reserve( desired_indent + (end - start) + m_addHyphen ); 7399 ret.append( desired_indent, ' ' ); 7400 // ret.append( start, end ); 7401 ret += m_column.m_string.substring( start, end ); 7402 if ( m_addHyphen ) { ret.push_back( '-' ); } 7403 7404 return ret; 7405 } 7406 7407 Column::const_iterator::const_iterator( Column const& column ): 7408 m_column( column ), 7409 m_lineStart( column.m_string.begin() ), 7410 m_lineEnd( column.m_string.begin() ), 7411 m_parsedTo( column.m_string.begin() ) { 7412 assert( m_column.m_width > m_column.m_indent ); 7413 assert( m_column.m_initialIndent == std::string::npos || 7414 m_column.m_width > m_column.m_initialIndent ); 7415 calcLength(); 7416 if ( m_lineStart == m_lineEnd ) { 7417 m_lineStart = m_column.m_string.end(); 7418 } 7419 } 7420 7421 std::string Column::const_iterator::operator*() const { 7422 assert( m_lineStart <= m_parsedTo ); 7423 return addIndentAndSuffix( m_lineStart, m_lineEnd ); 7424 } 7425 7426 Column::const_iterator& Column::const_iterator::operator++() { 7427 m_lineStart = m_lineEnd; 7428 AnsiSkippingString const& current_line = m_column.m_string; 7429 if ( m_lineStart != current_line.end() && *m_lineStart == '\n' ) { 7430 m_lineStart++; 7431 } else { 7432 while ( m_lineStart != current_line.end() && 7433 isWhitespace( *m_lineStart ) ) { 7434 ++m_lineStart; 7435 } 7436 } 7437 7438 if ( m_lineStart != current_line.end() ) { calcLength(); } 7439 return *this; 7440 } 7441 7442 Column::const_iterator Column::const_iterator::operator++( int ) { 7443 const_iterator prev( *this ); 7444 operator++(); 7445 return prev; 7446 } 7447 7448 std::ostream& operator<<( std::ostream& os, Column const& col ) { 7449 bool first = true; 7450 for ( auto line : col ) { 7451 if ( first ) { 7452 first = false; 7453 } else { 7454 os << '\n'; 7455 } 7456 os << line; 7457 } 7458 return os; 7459 } 7460 7461 Column Spacer( size_t spaceWidth ) { 7462 Column ret{ "" }; 7463 ret.width( spaceWidth ); 7464 return ret; 7465 } 7466 7467 Columns::iterator::iterator( Columns const& columns, EndTag ): 7468 m_columns( columns.m_columns ), m_activeIterators( 0 ) { 7469 7470 m_iterators.reserve( m_columns.size() ); 7471 for ( auto const& col : m_columns ) { 7472 m_iterators.push_back( col.end() ); 7473 } 7474 } 7475 7476 Columns::iterator::iterator( Columns const& columns ): 7477 m_columns( columns.m_columns ), 7478 m_activeIterators( m_columns.size() ) { 7479 7480 m_iterators.reserve( m_columns.size() ); 7481 for ( auto const& col : m_columns ) { 7482 m_iterators.push_back( col.begin() ); 7483 } 7484 } 7485 7486 std::string Columns::iterator::operator*() const { 7487 std::string row, padding; 7488 7489 for ( size_t i = 0; i < m_columns.size(); ++i ) { 7490 const auto width = m_columns[i].width(); 7491 if ( m_iterators[i] != m_columns[i].end() ) { 7492 std::string col = *m_iterators[i]; 7493 row += padding; 7494 row += col; 7495 7496 padding.clear(); 7497 if ( col.size() < width ) { 7498 padding.append( width - col.size(), ' ' ); 7499 } 7500 } else { 7501 padding.append( width, ' ' ); 7502 } 7503 } 7504 return row; 7505 } 7506 7507 Columns::iterator& Columns::iterator::operator++() { 7508 for ( size_t i = 0; i < m_columns.size(); ++i ) { 7509 if ( m_iterators[i] != m_columns[i].end() ) { 7510 ++m_iterators[i]; 7511 } 7512 } 7513 return *this; 7514 } 7515 7516 Columns::iterator Columns::iterator::operator++( int ) { 7517 iterator prev( *this ); 7518 operator++(); 7519 return prev; 7520 } 7521 7522 std::ostream& operator<<( std::ostream& os, Columns const& cols ) { 7523 bool first = true; 7524 for ( auto line : cols ) { 7525 if ( first ) { 7526 first = false; 7527 } else { 7528 os << '\n'; 7529 } 7530 os << line; 7531 } 7532 return os; 7533 } 7534 7535 Columns operator+( Column const& lhs, Column const& rhs ) { 7536 Columns cols; 7537 cols += lhs; 7538 cols += rhs; 7539 return cols; 7540 } 7541 Columns operator+( Column&& lhs, Column&& rhs ) { 7542 Columns cols; 7543 cols += CATCH_MOVE( lhs ); 7544 cols += CATCH_MOVE( rhs ); 7545 return cols; 7546 } 7547 7548 Columns& operator+=( Columns& lhs, Column const& rhs ) { 7549 lhs.m_columns.push_back( rhs ); 7550 return lhs; 7551 } 7552 Columns& operator+=( Columns& lhs, Column&& rhs ) { 7553 lhs.m_columns.push_back( CATCH_MOVE( rhs ) ); 7554 return lhs; 7555 } 7556 Columns operator+( Columns const& lhs, Column const& rhs ) { 7557 auto combined( lhs ); 7558 combined += rhs; 7559 return combined; 7560 } 7561 Columns operator+( Columns&& lhs, Column&& rhs ) { 7562 lhs += CATCH_MOVE( rhs ); 7563 return CATCH_MOVE( lhs ); 7564 } 7565 7566 } // namespace TextFlow 7567} // namespace Catch 7568 7569 7570 7571 7572#include <exception> 7573 7574namespace Catch { 7575 bool uncaught_exceptions() { 7576#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) 7577 return false; 7578#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) 7579 return std::uncaught_exceptions() > 0; 7580#else 7581 return std::uncaught_exception(); 7582#endif 7583 } 7584} // end namespace Catch 7585 7586 7587 7588namespace Catch { 7589 7590 WildcardPattern::WildcardPattern( std::string const& pattern, 7591 CaseSensitive caseSensitivity ) 7592 : m_caseSensitivity( caseSensitivity ), 7593 m_pattern( normaliseString( pattern ) ) 7594 { 7595 if( startsWith( m_pattern, '*' ) ) { 7596 m_pattern = m_pattern.substr( 1 ); 7597 m_wildcard = WildcardAtStart; 7598 } 7599 if( endsWith( m_pattern, '*' ) ) { 7600 m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); 7601 m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd ); 7602 } 7603 } 7604 7605 bool WildcardPattern::matches( std::string const& str ) const { 7606 switch( m_wildcard ) { 7607 case NoWildcard: 7608 return m_pattern == normaliseString( str ); 7609 case WildcardAtStart: 7610 return endsWith( normaliseString( str ), m_pattern ); 7611 case WildcardAtEnd: 7612 return startsWith( normaliseString( str ), m_pattern ); 7613 case WildcardAtBothEnds: 7614 return contains( normaliseString( str ), m_pattern ); 7615 default: 7616 CATCH_INTERNAL_ERROR( "Unknown enum" ); 7617 } 7618 } 7619 7620 std::string WildcardPattern::normaliseString( std::string const& str ) const { 7621 return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str ); 7622 } 7623} 7624 7625 7626// Note: swapping these two includes around causes MSVC to error out 7627// while in /permissive- mode. No, I don't know why. 7628// Tested on VS 2019, 18.{3, 4}.x 7629 7630#include <cstdint> 7631#include <iomanip> 7632#include <type_traits> 7633 7634namespace Catch { 7635 7636namespace { 7637 7638 size_t trailingBytes(unsigned char c) { 7639 if ((c & 0xE0) == 0xC0) { 7640 return 2; 7641 } 7642 if ((c & 0xF0) == 0xE0) { 7643 return 3; 7644 } 7645 if ((c & 0xF8) == 0xF0) { 7646 return 4; 7647 } 7648 CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); 7649 } 7650 7651 uint32_t headerValue(unsigned char c) { 7652 if ((c & 0xE0) == 0xC0) { 7653 return c & 0x1F; 7654 } 7655 if ((c & 0xF0) == 0xE0) { 7656 return c & 0x0F; 7657 } 7658 if ((c & 0xF8) == 0xF0) { 7659 return c & 0x07; 7660 } 7661 CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); 7662 } 7663 7664 void hexEscapeChar(std::ostream& os, unsigned char c) { 7665 std::ios_base::fmtflags f(os.flags()); 7666 os << "\\x" 7667 << std::uppercase << std::hex << std::setfill('0') << std::setw(2) 7668 << static_cast<int>(c); 7669 os.flags(f); 7670 } 7671 7672 bool shouldNewline(XmlFormatting fmt) { 7673 return !!(static_cast<std::underlying_type_t<XmlFormatting>>(fmt & XmlFormatting::Newline)); 7674 } 7675 7676 bool shouldIndent(XmlFormatting fmt) { 7677 return !!(static_cast<std::underlying_type_t<XmlFormatting>>(fmt & XmlFormatting::Indent)); 7678 } 7679 7680} // anonymous namespace 7681 7682 XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) { 7683 return static_cast<XmlFormatting>( 7684 static_cast<std::underlying_type_t<XmlFormatting>>(lhs) | 7685 static_cast<std::underlying_type_t<XmlFormatting>>(rhs) 7686 ); 7687 } 7688 7689 XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) { 7690 return static_cast<XmlFormatting>( 7691 static_cast<std::underlying_type_t<XmlFormatting>>(lhs) & 7692 static_cast<std::underlying_type_t<XmlFormatting>>(rhs) 7693 ); 7694 } 7695 7696 7697 XmlEncode::XmlEncode( StringRef str, ForWhat forWhat ) 7698 : m_str( str ), 7699 m_forWhat( forWhat ) 7700 {} 7701 7702 void XmlEncode::encodeTo( std::ostream& os ) const { 7703 // Apostrophe escaping not necessary if we always use " to write attributes 7704 // (see: http://www.w3.org/TR/xml/#syntax) 7705 7706 for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { 7707 unsigned char c = static_cast<unsigned char>(m_str[idx]); 7708 switch (c) { 7709 case '<': os << "&lt;"; break; 7710 case '&': os << "&amp;"; break; 7711 7712 case '>': 7713 // See: http://www.w3.org/TR/xml/#syntax 7714 if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') 7715 os << "&gt;"; 7716 else 7717 os << c; 7718 break; 7719 7720 case '\"': 7721 if (m_forWhat == ForAttributes) 7722 os << "&quot;"; 7723 else 7724 os << c; 7725 break; 7726 7727 default: 7728 // Check for control characters and invalid utf-8 7729 7730 // Escape control characters in standard ascii 7731 // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 7732 if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { 7733 hexEscapeChar(os, c); 7734 break; 7735 } 7736 7737 // Plain ASCII: Write it to stream 7738 if (c < 0x7F) { 7739 os << c; 7740 break; 7741 } 7742 7743 // UTF-8 territory 7744 // Check if the encoding is valid and if it is not, hex escape bytes. 7745 // Important: We do not check the exact decoded values for validity, only the encoding format 7746 // First check that this bytes is a valid lead byte: 7747 // This means that it is not encoded as 1111 1XXX 7748 // Or as 10XX XXXX 7749 if (c < 0xC0 || 7750 c >= 0xF8) { 7751 hexEscapeChar(os, c); 7752 break; 7753 } 7754 7755 auto encBytes = trailingBytes(c); 7756 // Are there enough bytes left to avoid accessing out-of-bounds memory? 7757 if (idx + encBytes - 1 >= m_str.size()) { 7758 hexEscapeChar(os, c); 7759 break; 7760 } 7761 // The header is valid, check data 7762 // The next encBytes bytes must together be a valid utf-8 7763 // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) 7764 bool valid = true; 7765 uint32_t value = headerValue(c); 7766 for (std::size_t n = 1; n < encBytes; ++n) { 7767 unsigned char nc = static_cast<unsigned char>(m_str[idx + n]); 7768 valid &= ((nc & 0xC0) == 0x80); 7769 value = (value << 6) | (nc & 0x3F); 7770 } 7771 7772 if ( 7773 // Wrong bit pattern of following bytes 7774 (!valid) || 7775 // Overlong encodings 7776 (value < 0x80) || 7777 (0x80 <= value && value < 0x800 && encBytes > 2) || 7778 (0x800 < value && value < 0x10000 && encBytes > 3) || 7779 // Encoded value out of range 7780 (value >= 0x110000) 7781 ) { 7782 hexEscapeChar(os, c); 7783 break; 7784 } 7785 7786 // If we got here, this is in fact a valid(ish) utf-8 sequence 7787 for (std::size_t n = 0; n < encBytes; ++n) { 7788 os << m_str[idx + n]; 7789 } 7790 idx += encBytes - 1; 7791 break; 7792 } 7793 } 7794 } 7795 7796 std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { 7797 xmlEncode.encodeTo( os ); 7798 return os; 7799 } 7800 7801 XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt ) 7802 : m_writer( writer ), 7803 m_fmt(fmt) 7804 {} 7805 7806 XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept 7807 : m_writer( other.m_writer ), 7808 m_fmt(other.m_fmt) 7809 { 7810 other.m_writer = nullptr; 7811 other.m_fmt = XmlFormatting::None; 7812 } 7813 XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { 7814 if ( m_writer ) { 7815 m_writer->endElement(); 7816 } 7817 m_writer = other.m_writer; 7818 other.m_writer = nullptr; 7819 m_fmt = other.m_fmt; 7820 other.m_fmt = XmlFormatting::None; 7821 return *this; 7822 } 7823 7824 7825 XmlWriter::ScopedElement::~ScopedElement() { 7826 if (m_writer) { 7827 m_writer->endElement(m_fmt); 7828 } 7829 } 7830 7831 XmlWriter::ScopedElement& 7832 XmlWriter::ScopedElement::writeText( StringRef text, XmlFormatting fmt ) { 7833 m_writer->writeText( text, fmt ); 7834 return *this; 7835 } 7836 7837 XmlWriter::ScopedElement& 7838 XmlWriter::ScopedElement::writeAttribute( StringRef name, 7839 StringRef attribute ) { 7840 m_writer->writeAttribute( name, attribute ); 7841 return *this; 7842 } 7843 7844 7845 XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) 7846 { 7847 writeDeclaration(); 7848 } 7849 7850 XmlWriter::~XmlWriter() { 7851 while (!m_tags.empty()) { 7852 endElement(); 7853 } 7854 newlineIfNecessary(); 7855 } 7856 7857 XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) { 7858 ensureTagClosed(); 7859 newlineIfNecessary(); 7860 if (shouldIndent(fmt)) { 7861 m_os << m_indent; 7862 m_indent += " "; 7863 } 7864 m_os << '<' << name; 7865 m_tags.push_back( name ); 7866 m_tagIsOpen = true; 7867 applyFormatting(fmt); 7868 return *this; 7869 } 7870 7871 XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) { 7872 ScopedElement scoped( this, fmt ); 7873 startElement( name, fmt ); 7874 return scoped; 7875 } 7876 7877 XmlWriter& XmlWriter::endElement(XmlFormatting fmt) { 7878 m_indent = m_indent.substr(0, m_indent.size() - 2); 7879 7880 if( m_tagIsOpen ) { 7881 m_os << "/>"; 7882 m_tagIsOpen = false; 7883 } else { 7884 newlineIfNecessary(); 7885 if (shouldIndent(fmt)) { 7886 m_os << m_indent; 7887 } 7888 m_os << "</" << m_tags.back() << '>'; 7889 } 7890 m_os << std::flush; 7891 applyFormatting(fmt); 7892 m_tags.pop_back(); 7893 return *this; 7894 } 7895 7896 XmlWriter& XmlWriter::writeAttribute( StringRef name, 7897 StringRef attribute ) { 7898 if( !name.empty() && !attribute.empty() ) 7899 m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; 7900 return *this; 7901 } 7902 7903 XmlWriter& XmlWriter::writeAttribute( StringRef name, bool attribute ) { 7904 writeAttribute(name, (attribute ? "true"_sr : "false"_sr)); 7905 return *this; 7906 } 7907 7908 XmlWriter& XmlWriter::writeAttribute( StringRef name, 7909 char const* attribute ) { 7910 writeAttribute( name, StringRef( attribute ) ); 7911 return *this; 7912 } 7913 7914 XmlWriter& XmlWriter::writeText( StringRef text, XmlFormatting fmt ) { 7915 CATCH_ENFORCE(!m_tags.empty(), "Cannot write text as top level element"); 7916 if( !text.empty() ){ 7917 bool tagWasOpen = m_tagIsOpen; 7918 ensureTagClosed(); 7919 if (tagWasOpen && shouldIndent(fmt)) { 7920 m_os << m_indent; 7921 } 7922 m_os << XmlEncode( text, XmlEncode::ForTextNodes ); 7923 applyFormatting(fmt); 7924 } 7925 return *this; 7926 } 7927 7928 XmlWriter& XmlWriter::writeComment( StringRef text, XmlFormatting fmt ) { 7929 ensureTagClosed(); 7930 if (shouldIndent(fmt)) { 7931 m_os << m_indent; 7932 } 7933 m_os << "<!-- " << text << " -->"; 7934 applyFormatting(fmt); 7935 return *this; 7936 } 7937 7938 void XmlWriter::writeStylesheetRef( StringRef url ) { 7939 m_os << R"(<?xml-stylesheet type="text/xsl" href=")" << url << R"("?>)" << '\n'; 7940 } 7941 7942 void XmlWriter::ensureTagClosed() { 7943 if( m_tagIsOpen ) { 7944 m_os << '>' << std::flush; 7945 newlineIfNecessary(); 7946 m_tagIsOpen = false; 7947 } 7948 } 7949 7950 void XmlWriter::applyFormatting(XmlFormatting fmt) { 7951 m_needsNewline = shouldNewline(fmt); 7952 } 7953 7954 void XmlWriter::writeDeclaration() { 7955 m_os << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n'; 7956 } 7957 7958 void XmlWriter::newlineIfNecessary() { 7959 if( m_needsNewline ) { 7960 m_os << '\n' << std::flush; 7961 m_needsNewline = false; 7962 } 7963 } 7964} 7965 7966 7967 7968 7969 7970namespace Catch { 7971namespace Matchers { 7972 7973 std::string MatcherUntypedBase::toString() const { 7974 if (m_cachedToString.empty()) { 7975 m_cachedToString = describe(); 7976 } 7977 return m_cachedToString; 7978 } 7979 7980 MatcherUntypedBase::~MatcherUntypedBase() = default; 7981 7982} // namespace Matchers 7983} // namespace Catch 7984 7985 7986 7987 7988namespace Catch { 7989namespace Matchers { 7990 7991 std::string IsEmptyMatcher::describe() const { 7992 return "is empty"; 7993 } 7994 7995 std::string HasSizeMatcher::describe() const { 7996 ReusableStringStream sstr; 7997 sstr << "has size == " << m_target_size; 7998 return sstr.str(); 7999 } 8000 8001 IsEmptyMatcher IsEmpty() { 8002 return {}; 8003 } 8004 8005 HasSizeMatcher SizeIs(std::size_t sz) { 8006 return HasSizeMatcher{ sz }; 8007 } 8008 8009} // end namespace Matchers 8010} // end namespace Catch 8011 8012 8013 8014namespace Catch { 8015namespace Matchers { 8016 8017bool ExceptionMessageMatcher::match(std::exception const& ex) const { 8018 return ex.what() == m_message; 8019} 8020 8021std::string ExceptionMessageMatcher::describe() const { 8022 return "exception message matches \"" + m_message + '"'; 8023} 8024 8025ExceptionMessageMatcher Message(std::string const& message) { 8026 return ExceptionMessageMatcher(message); 8027} 8028 8029} // namespace Matchers 8030} // namespace Catch 8031 8032 8033 8034#include <algorithm> 8035#include <cmath> 8036#include <cstdlib> 8037#include <cstdint> 8038#include <sstream> 8039#include <iomanip> 8040#include <limits> 8041 8042 8043namespace Catch { 8044namespace { 8045 8046 template <typename FP> 8047 bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { 8048 // Comparison with NaN should always be false. 8049 // This way we can rule it out before getting into the ugly details 8050 if (Catch::isnan(lhs) || Catch::isnan(rhs)) { 8051 return false; 8052 } 8053 8054 // This should also handle positive and negative zeros, infinities 8055 const auto ulpDist = ulpDistance(lhs, rhs); 8056 8057 return ulpDist <= maxUlpDiff; 8058 } 8059 8060 8061template <typename FP> 8062FP step(FP start, FP direction, uint64_t steps) { 8063 for (uint64_t i = 0; i < steps; ++i) { 8064 start = Catch::nextafter(start, direction); 8065 } 8066 return start; 8067} 8068 8069// Performs equivalent check of std::fabs(lhs - rhs) <= margin 8070// But without the subtraction to allow for INFINITY in comparison 8071bool marginComparison(double lhs, double rhs, double margin) { 8072 return (lhs + margin >= rhs) && (rhs + margin >= lhs); 8073} 8074 8075template <typename FloatingPoint> 8076void write(std::ostream& out, FloatingPoint num) { 8077 out << std::scientific 8078 << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1) 8079 << num; 8080} 8081 8082} // end anonymous namespace 8083 8084namespace Matchers { 8085namespace Detail { 8086 8087 enum class FloatingPointKind : uint8_t { 8088 Float, 8089 Double 8090 }; 8091 8092} // end namespace Detail 8093 8094 8095 WithinAbsMatcher::WithinAbsMatcher(double target, double margin) 8096 :m_target{ target }, m_margin{ margin } { 8097 CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' 8098 << " Margin has to be non-negative."); 8099 } 8100 8101 // Performs equivalent check of std::fabs(lhs - rhs) <= margin 8102 // But without the subtraction to allow for INFINITY in comparison 8103 bool WithinAbsMatcher::match(double const& matchee) const { 8104 return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); 8105 } 8106 8107 std::string WithinAbsMatcher::describe() const { 8108 return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); 8109 } 8110 8111 8112 WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType) 8113 :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { 8114 CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double 8115 || m_ulps < (std::numeric_limits<uint32_t>::max)(), 8116 "Provided ULP is impossibly large for a float comparison."); 8117 CATCH_ENFORCE( std::numeric_limits<double>::is_iec559, 8118 "WithinUlp matcher only supports platforms with " 8119 "IEEE-754 compatible floating point representation" ); 8120 } 8121 8122#if defined(__clang__) 8123#pragma clang diagnostic push 8124// Clang <3.5 reports on the default branch in the switch below 8125#pragma clang diagnostic ignored "-Wunreachable-code" 8126#endif 8127 8128 bool WithinUlpsMatcher::match(double const& matchee) const { 8129 switch (m_type) { 8130 case Detail::FloatingPointKind::Float: 8131 return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps); 8132 case Detail::FloatingPointKind::Double: 8133 return almostEqualUlps<double>(matchee, m_target, m_ulps); 8134 default: 8135 CATCH_INTERNAL_ERROR( "Unknown Detail::FloatingPointKind value" ); 8136 } 8137 } 8138 8139#if defined(__clang__) 8140#pragma clang diagnostic pop 8141#endif 8142 8143 std::string WithinUlpsMatcher::describe() const { 8144 std::stringstream ret; 8145 8146 ret << "is within " << m_ulps << " ULPs of "; 8147 8148 if (m_type == Detail::FloatingPointKind::Float) { 8149 write(ret, static_cast<float>(m_target)); 8150 ret << 'f'; 8151 } else { 8152 write(ret, m_target); 8153 } 8154 8155 ret << " (["; 8156 if (m_type == Detail::FloatingPointKind::Double) { 8157 write( ret, 8158 step( m_target, 8159 -std::numeric_limits<double>::infinity(), 8160 m_ulps ) ); 8161 ret << ", "; 8162 write( ret, 8163 step( m_target, 8164 std::numeric_limits<double>::infinity(), 8165 m_ulps ) ); 8166 } else { 8167 // We have to cast INFINITY to float because of MinGW, see #1782 8168 write( ret, 8169 step( static_cast<float>( m_target ), 8170 -std::numeric_limits<float>::infinity(), 8171 m_ulps ) ); 8172 ret << ", "; 8173 write( ret, 8174 step( static_cast<float>( m_target ), 8175 std::numeric_limits<float>::infinity(), 8176 m_ulps ) ); 8177 } 8178 ret << "])"; 8179 8180 return ret.str(); 8181 } 8182 8183 WithinRelMatcher::WithinRelMatcher(double target, double epsilon): 8184 m_target(target), 8185 m_epsilon(epsilon){ 8186 CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); 8187 CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); 8188 } 8189 8190 bool WithinRelMatcher::match(double const& matchee) const { 8191 const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); 8192 return marginComparison(matchee, m_target, 8193 std::isinf(relMargin)? 0 : relMargin); 8194 } 8195 8196 std::string WithinRelMatcher::describe() const { 8197 Catch::ReusableStringStream sstr; 8198 sstr << "and " << ::Catch::Detail::stringify(m_target) << " are within " << m_epsilon * 100. << "% of each other"; 8199 return sstr.str(); 8200 } 8201 8202 8203WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { 8204 return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Double); 8205} 8206 8207WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { 8208 return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Float); 8209} 8210 8211WithinAbsMatcher WithinAbs(double target, double margin) { 8212 return WithinAbsMatcher(target, margin); 8213} 8214 8215WithinRelMatcher WithinRel(double target, double eps) { 8216 return WithinRelMatcher(target, eps); 8217} 8218 8219WithinRelMatcher WithinRel(double target) { 8220 return WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100); 8221} 8222 8223WithinRelMatcher WithinRel(float target, float eps) { 8224 return WithinRelMatcher(target, eps); 8225} 8226 8227WithinRelMatcher WithinRel(float target) { 8228 return WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100); 8229} 8230 8231 8232 8233bool IsNaNMatcher::match( double const& matchee ) const { 8234 return std::isnan( matchee ); 8235} 8236 8237std::string IsNaNMatcher::describe() const { 8238 using namespace std::string_literals; 8239 return "is NaN"s; 8240} 8241 8242IsNaNMatcher IsNaN() { return IsNaNMatcher(); } 8243 8244 } // namespace Matchers 8245} // namespace Catch 8246 8247 8248 8249 8250std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) { 8251 if (desc.empty()) { 8252 return "matches undescribed predicate"; 8253 } else { 8254 return "matches predicate: \"" + desc + '"'; 8255 } 8256} 8257 8258 8259 8260namespace Catch { 8261 namespace Matchers { 8262 std::string AllTrueMatcher::describe() const { return "contains only true"; } 8263 8264 AllTrueMatcher AllTrue() { return AllTrueMatcher{}; } 8265 8266 std::string NoneTrueMatcher::describe() const { return "contains no true"; } 8267 8268 NoneTrueMatcher NoneTrue() { return NoneTrueMatcher{}; } 8269 8270 std::string AnyTrueMatcher::describe() const { return "contains at least one true"; } 8271 8272 AnyTrueMatcher AnyTrue() { return AnyTrueMatcher{}; } 8273 } // namespace Matchers 8274} // namespace Catch 8275 8276 8277 8278#include <regex> 8279 8280namespace Catch { 8281namespace Matchers { 8282 8283 CasedString::CasedString( std::string const& str, CaseSensitive caseSensitivity ) 8284 : m_caseSensitivity( caseSensitivity ), 8285 m_str( adjustString( str ) ) 8286 {} 8287 std::string CasedString::adjustString( std::string const& str ) const { 8288 return m_caseSensitivity == CaseSensitive::No 8289 ? toLower( str ) 8290 : str; 8291 } 8292 StringRef CasedString::caseSensitivitySuffix() const { 8293 return m_caseSensitivity == CaseSensitive::Yes 8294 ? StringRef() 8295 : " (case insensitive)"_sr; 8296 } 8297 8298 8299 StringMatcherBase::StringMatcherBase( StringRef operation, CasedString const& comparator ) 8300 : m_comparator( comparator ), 8301 m_operation( operation ) { 8302 } 8303 8304 std::string StringMatcherBase::describe() const { 8305 std::string description; 8306 description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + 8307 m_comparator.caseSensitivitySuffix().size()); 8308 description += m_operation; 8309 description += ": \""; 8310 description += m_comparator.m_str; 8311 description += '"'; 8312 description += m_comparator.caseSensitivitySuffix(); 8313 return description; 8314 } 8315 8316 StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals"_sr, comparator ) {} 8317 8318 bool StringEqualsMatcher::match( std::string const& source ) const { 8319 return m_comparator.adjustString( source ) == m_comparator.m_str; 8320 } 8321 8322 8323 StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains"_sr, comparator ) {} 8324 8325 bool StringContainsMatcher::match( std::string const& source ) const { 8326 return contains( m_comparator.adjustString( source ), m_comparator.m_str ); 8327 } 8328 8329 8330 StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with"_sr, comparator ) {} 8331 8332 bool StartsWithMatcher::match( std::string const& source ) const { 8333 return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); 8334 } 8335 8336 8337 EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with"_sr, comparator ) {} 8338 8339 bool EndsWithMatcher::match( std::string const& source ) const { 8340 return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); 8341 } 8342 8343 8344 8345 RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {} 8346 8347 bool RegexMatcher::match(std::string const& matchee) const { 8348 auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway 8349 if (m_caseSensitivity == CaseSensitive::No) { 8350 flags |= std::regex::icase; 8351 } 8352 auto reg = std::regex(m_regex, flags); 8353 return std::regex_match(matchee, reg); 8354 } 8355 8356 std::string RegexMatcher::describe() const { 8357 return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Yes)? " case sensitively" : " case insensitively"); 8358 } 8359 8360 8361 StringEqualsMatcher Equals( std::string const& str, CaseSensitive caseSensitivity ) { 8362 return StringEqualsMatcher( CasedString( str, caseSensitivity) ); 8363 } 8364 StringContainsMatcher ContainsSubstring( std::string const& str, CaseSensitive caseSensitivity ) { 8365 return StringContainsMatcher( CasedString( str, caseSensitivity) ); 8366 } 8367 EndsWithMatcher EndsWith( std::string const& str, CaseSensitive caseSensitivity ) { 8368 return EndsWithMatcher( CasedString( str, caseSensitivity) ); 8369 } 8370 StartsWithMatcher StartsWith( std::string const& str, CaseSensitive caseSensitivity ) { 8371 return StartsWithMatcher( CasedString( str, caseSensitivity) ); 8372 } 8373 8374 RegexMatcher Matches(std::string const& regex, CaseSensitive caseSensitivity) { 8375 return RegexMatcher(regex, caseSensitivity); 8376 } 8377 8378} // namespace Matchers 8379} // namespace Catch 8380 8381 8382 8383namespace Catch { 8384namespace Matchers { 8385 MatcherGenericBase::~MatcherGenericBase() = default; 8386 8387 namespace Detail { 8388 8389 std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end) { 8390 std::string description; 8391 std::size_t combined_size = 4; 8392 for ( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { 8393 combined_size += desc->size(); 8394 } 8395 combined_size += static_cast<size_t>(descriptions_end - descriptions_begin - 1) * combine.size(); 8396 8397 description.reserve(combined_size); 8398 8399 description += "( "; 8400 bool first = true; 8401 for( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { 8402 if( first ) 8403 first = false; 8404 else 8405 description += combine; 8406 description += *desc; 8407 } 8408 description += " )"; 8409 return description; 8410 } 8411 8412 } // namespace Detail 8413} // namespace Matchers 8414} // namespace Catch 8415 8416 8417 8418 8419namespace Catch { 8420 8421 // This is the general overload that takes a any string matcher 8422 // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers 8423 // the Equals matcher (so the header does not mention matchers) 8424 void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher ) { 8425 std::string exceptionMessage = Catch::translateActiveException(); 8426 MatchExpr<std::string, StringMatcher const&> expr( CATCH_MOVE(exceptionMessage), matcher ); 8427 handler.handleExpr( expr ); 8428 } 8429 8430} // namespace Catch 8431 8432 8433 8434#include <ostream> 8435 8436namespace Catch { 8437 8438 AutomakeReporter::~AutomakeReporter() = default; 8439 8440 void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { 8441 // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. 8442 m_stream << ":test-result: "; 8443 if ( _testCaseStats.totals.testCases.skipped > 0 ) { 8444 m_stream << "SKIP"; 8445 } else if (_testCaseStats.totals.assertions.allPassed()) { 8446 m_stream << "PASS"; 8447 } else if (_testCaseStats.totals.assertions.allOk()) { 8448 m_stream << "XFAIL"; 8449 } else { 8450 m_stream << "FAIL"; 8451 } 8452 m_stream << ' ' << _testCaseStats.testInfo->name << '\n'; 8453 StreamingReporterBase::testCaseEnded(_testCaseStats); 8454 } 8455 8456 void AutomakeReporter::skipTest(TestCaseInfo const& testInfo) { 8457 m_stream << ":test-result: SKIP " << testInfo.name << '\n'; 8458 } 8459 8460} // end namespace Catch 8461 8462 8463 8464 8465 8466 8467namespace Catch { 8468 ReporterBase::ReporterBase( ReporterConfig&& config ): 8469 IEventListener( config.fullConfig() ), 8470 m_wrapped_stream( CATCH_MOVE(config).takeStream() ), 8471 m_stream( m_wrapped_stream->stream() ), 8472 m_colour( makeColourImpl( config.colourMode(), m_wrapped_stream.get() ) ), 8473 m_customOptions( config.customOptions() ) 8474 {} 8475 8476 ReporterBase::~ReporterBase() = default; 8477 8478 void ReporterBase::listReporters( 8479 std::vector<ReporterDescription> const& descriptions ) { 8480 defaultListReporters(m_stream, descriptions, m_config->verbosity()); 8481 } 8482 8483 void ReporterBase::listListeners( 8484 std::vector<ListenerDescription> const& descriptions ) { 8485 defaultListListeners( m_stream, descriptions ); 8486 } 8487 8488 void ReporterBase::listTests(std::vector<TestCaseHandle> const& tests) { 8489 defaultListTests(m_stream, 8490 m_colour.get(), 8491 tests, 8492 m_config->hasTestFilters(), 8493 m_config->verbosity()); 8494 } 8495 8496 void ReporterBase::listTags(std::vector<TagInfo> const& tags) { 8497 defaultListTags( m_stream, tags, m_config->hasTestFilters() ); 8498 } 8499 8500} // namespace Catch 8501 8502 8503 8504 8505#include <ostream> 8506 8507namespace Catch { 8508namespace { 8509 8510 // Colour::LightGrey 8511 static constexpr Colour::Code compactDimColour = Colour::FileName; 8512 8513#ifdef CATCH_PLATFORM_MAC 8514 static constexpr Catch::StringRef compactFailedString = "FAILED"_sr; 8515 static constexpr Catch::StringRef compactPassedString = "PASSED"_sr; 8516#else 8517 static constexpr Catch::StringRef compactFailedString = "failed"_sr; 8518 static constexpr Catch::StringRef compactPassedString = "passed"_sr; 8519#endif 8520 8521// Implementation of CompactReporter formatting 8522class AssertionPrinter { 8523public: 8524 AssertionPrinter& operator= (AssertionPrinter const&) = delete; 8525 AssertionPrinter(AssertionPrinter const&) = delete; 8526 AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages, ColourImpl* colourImpl_) 8527 : stream(_stream) 8528 , result(_stats.assertionResult) 8529 , messages(_stats.infoMessages) 8530 , itMessage(_stats.infoMessages.begin()) 8531 , printInfoMessages(_printInfoMessages) 8532 , colourImpl(colourImpl_) 8533 {} 8534 8535 void print() { 8536 printSourceInfo(); 8537 8538 itMessage = messages.begin(); 8539 8540 switch (result.getResultType()) { 8541 case ResultWas::Ok: 8542 printResultType(Colour::ResultSuccess, compactPassedString); 8543 printOriginalExpression(); 8544 printReconstructedExpression(); 8545 if (!result.hasExpression()) 8546 printRemainingMessages(Colour::None); 8547 else 8548 printRemainingMessages(); 8549 break; 8550 case ResultWas::ExpressionFailed: 8551 if (result.isOk()) 8552 printResultType(Colour::ResultSuccess, compactFailedString + " - but was ok"_sr); 8553 else 8554 printResultType(Colour::Error, compactFailedString); 8555 printOriginalExpression(); 8556 printReconstructedExpression(); 8557 printRemainingMessages(); 8558 break; 8559 case ResultWas::ThrewException: 8560 printResultType(Colour::Error, compactFailedString); 8561 printIssue("unexpected exception with message:"); 8562 printMessage(); 8563 printExpressionWas(); 8564 printRemainingMessages(); 8565 break; 8566 case ResultWas::FatalErrorCondition: 8567 printResultType(Colour::Error, compactFailedString); 8568 printIssue("fatal error condition with message:"); 8569 printMessage(); 8570 printExpressionWas(); 8571 printRemainingMessages(); 8572 break; 8573 case ResultWas::DidntThrowException: 8574 printResultType(Colour::Error, compactFailedString); 8575 printIssue("expected exception, got none"); 8576 printExpressionWas(); 8577 printRemainingMessages(); 8578 break; 8579 case ResultWas::Info: 8580 printResultType(Colour::None, "info"_sr); 8581 printMessage(); 8582 printRemainingMessages(); 8583 break; 8584 case ResultWas::Warning: 8585 printResultType(Colour::None, "warning"_sr); 8586 printMessage(); 8587 printRemainingMessages(); 8588 break; 8589 case ResultWas::ExplicitFailure: 8590 printResultType(Colour::Error, compactFailedString); 8591 printIssue("explicitly"); 8592 printRemainingMessages(Colour::None); 8593 break; 8594 case ResultWas::ExplicitSkip: 8595 printResultType(Colour::Skip, "skipped"_sr); 8596 printMessage(); 8597 printRemainingMessages(); 8598 break; 8599 // These cases are here to prevent compiler warnings 8600 case ResultWas::Unknown: 8601 case ResultWas::FailureBit: 8602 case ResultWas::Exception: 8603 printResultType(Colour::Error, "** internal error **"); 8604 break; 8605 } 8606 } 8607 8608private: 8609 void printSourceInfo() const { 8610 stream << colourImpl->guardColour( Colour::FileName ) 8611 << result.getSourceInfo() << ':'; 8612 } 8613 8614 void printResultType(Colour::Code colour, StringRef passOrFail) const { 8615 if (!passOrFail.empty()) { 8616 stream << colourImpl->guardColour(colour) << ' ' << passOrFail; 8617 stream << ':'; 8618 } 8619 } 8620 8621 void printIssue(char const* issue) const { 8622 stream << ' ' << issue; 8623 } 8624 8625 void printExpressionWas() { 8626 if (result.hasExpression()) { 8627 stream << ';'; 8628 { 8629 stream << colourImpl->guardColour(compactDimColour) << " expression was:"; 8630 } 8631 printOriginalExpression(); 8632 } 8633 } 8634 8635 void printOriginalExpression() const { 8636 if (result.hasExpression()) { 8637 stream << ' ' << result.getExpression(); 8638 } 8639 } 8640 8641 void printReconstructedExpression() const { 8642 if (result.hasExpandedExpression()) { 8643 stream << colourImpl->guardColour(compactDimColour) << " for: "; 8644 stream << result.getExpandedExpression(); 8645 } 8646 } 8647 8648 void printMessage() { 8649 if (itMessage != messages.end()) { 8650 stream << " '" << itMessage->message << '\''; 8651 ++itMessage; 8652 } 8653 } 8654 8655 void printRemainingMessages(Colour::Code colour = compactDimColour) { 8656 if (itMessage == messages.end()) 8657 return; 8658 8659 const auto itEnd = messages.cend(); 8660 const auto N = static_cast<std::size_t>(itEnd - itMessage); 8661 8662 stream << colourImpl->guardColour( colour ) << " with " 8663 << pluralise( N, "message"_sr ) << ':'; 8664 8665 while (itMessage != itEnd) { 8666 // If this assertion is a warning ignore any INFO messages 8667 if (printInfoMessages || itMessage->type != ResultWas::Info) { 8668 printMessage(); 8669 if (itMessage != itEnd) { 8670 stream << colourImpl->guardColour(compactDimColour) << " and"; 8671 } 8672 continue; 8673 } 8674 ++itMessage; 8675 } 8676 } 8677 8678private: 8679 std::ostream& stream; 8680 AssertionResult const& result; 8681 std::vector<MessageInfo> const& messages; 8682 std::vector<MessageInfo>::const_iterator itMessage; 8683 bool printInfoMessages; 8684 ColourImpl* colourImpl; 8685}; 8686 8687} // anon namespace 8688 8689 std::string CompactReporter::getDescription() { 8690 return "Reports test results on a single line, suitable for IDEs"; 8691 } 8692 8693 void CompactReporter::noMatchingTestCases( StringRef unmatchedSpec ) { 8694 m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; 8695 } 8696 8697 void CompactReporter::testRunStarting( TestRunInfo const& ) { 8698 if ( m_config->testSpec().hasFilters() ) { 8699 m_stream << m_colour->guardColour( Colour::BrightYellow ) 8700 << "Filters: " 8701 << m_config->testSpec() 8702 << '\n'; 8703 } 8704 m_stream << "RNG seed: " << getSeed() << '\n'; 8705 } 8706 8707 void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { 8708 AssertionResult const& result = _assertionStats.assertionResult; 8709 8710 bool printInfoMessages = true; 8711 8712 // Drop out if result was successful and we're not printing those 8713 if( !m_config->includeSuccessfulResults() && result.isOk() ) { 8714 if( result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip ) 8715 return; 8716 printInfoMessages = false; 8717 } 8718 8719 AssertionPrinter printer( m_stream, _assertionStats, printInfoMessages, m_colour.get() ); 8720 printer.print(); 8721 8722 m_stream << '\n' << std::flush; 8723 } 8724 8725 void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { 8726 double dur = _sectionStats.durationInSeconds; 8727 if ( shouldShowDuration( *m_config, dur ) ) { 8728 m_stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush; 8729 } 8730 } 8731 8732 void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { 8733 printTestRunTotals( m_stream, *m_colour, _testRunStats.totals ); 8734 m_stream << "\n\n" << std::flush; 8735 StreamingReporterBase::testRunEnded( _testRunStats ); 8736 } 8737 8738 CompactReporter::~CompactReporter() = default; 8739 8740} // end namespace Catch 8741 8742 8743 8744 8745#include <cstdio> 8746 8747#if defined(_MSC_VER) 8748#pragma warning(push) 8749#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch 8750 // Note that 4062 (not all labels are handled and default is missing) is enabled 8751#endif 8752 8753#if defined(__clang__) 8754# pragma clang diagnostic push 8755// For simplicity, benchmarking-only helpers are always enabled 8756# pragma clang diagnostic ignored "-Wunused-function" 8757#endif 8758 8759 8760 8761namespace Catch { 8762 8763namespace { 8764 8765// Formatter impl for ConsoleReporter 8766class ConsoleAssertionPrinter { 8767public: 8768 ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; 8769 ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; 8770 ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, ColourImpl* colourImpl_, bool _printInfoMessages) 8771 : stream(_stream), 8772 stats(_stats), 8773 result(_stats.assertionResult), 8774 colour(Colour::None), 8775 messages(_stats.infoMessages), 8776 colourImpl(colourImpl_), 8777 printInfoMessages(_printInfoMessages) { 8778 switch (result.getResultType()) { 8779 case ResultWas::Ok: 8780 colour = Colour::Success; 8781 passOrFail = "PASSED"_sr; 8782 //if( result.hasMessage() ) 8783 if (messages.size() == 1) 8784 messageLabel = "with message"_sr; 8785 if (messages.size() > 1) 8786 messageLabel = "with messages"_sr; 8787 break; 8788 case ResultWas::ExpressionFailed: 8789 if (result.isOk()) { 8790 colour = Colour::Success; 8791 passOrFail = "FAILED - but was ok"_sr; 8792 } else { 8793 colour = Colour::Error; 8794 passOrFail = "FAILED"_sr; 8795 } 8796 if (messages.size() == 1) 8797 messageLabel = "with message"_sr; 8798 if (messages.size() > 1) 8799 messageLabel = "with messages"_sr; 8800 break; 8801 case ResultWas::ThrewException: 8802 colour = Colour::Error; 8803 passOrFail = "FAILED"_sr; 8804 // todo switch 8805 switch (messages.size()) { case 0: 8806 messageLabel = "due to unexpected exception with "_sr; 8807 break; 8808 case 1: 8809 messageLabel = "due to unexpected exception with message"_sr; 8810 break; 8811 default: 8812 messageLabel = "due to unexpected exception with messages"_sr; 8813 break; 8814 } 8815 break; 8816 case ResultWas::FatalErrorCondition: 8817 colour = Colour::Error; 8818 passOrFail = "FAILED"_sr; 8819 messageLabel = "due to a fatal error condition"_sr; 8820 break; 8821 case ResultWas::DidntThrowException: 8822 colour = Colour::Error; 8823 passOrFail = "FAILED"_sr; 8824 messageLabel = "because no exception was thrown where one was expected"_sr; 8825 break; 8826 case ResultWas::Info: 8827 messageLabel = "info"_sr; 8828 break; 8829 case ResultWas::Warning: 8830 messageLabel = "warning"_sr; 8831 break; 8832 case ResultWas::ExplicitFailure: 8833 passOrFail = "FAILED"_sr; 8834 colour = Colour::Error; 8835 if (messages.size() == 1) 8836 messageLabel = "explicitly with message"_sr; 8837 if (messages.size() > 1) 8838 messageLabel = "explicitly with messages"_sr; 8839 break; 8840 case ResultWas::ExplicitSkip: 8841 colour = Colour::Skip; 8842 passOrFail = "SKIPPED"_sr; 8843 if (messages.size() == 1) 8844 messageLabel = "explicitly with message"_sr; 8845 if (messages.size() > 1) 8846 messageLabel = "explicitly with messages"_sr; 8847 break; 8848 // These cases are here to prevent compiler warnings 8849 case ResultWas::Unknown: 8850 case ResultWas::FailureBit: 8851 case ResultWas::Exception: 8852 passOrFail = "** internal error **"_sr; 8853 colour = Colour::Error; 8854 break; 8855 } 8856 } 8857 8858 void print() const { 8859 printSourceInfo(); 8860 if (stats.totals.assertions.total() > 0) { 8861 printResultType(); 8862 printOriginalExpression(); 8863 printReconstructedExpression(); 8864 } else { 8865 stream << '\n'; 8866 } 8867 printMessage(); 8868 } 8869 8870private: 8871 void printResultType() const { 8872 if (!passOrFail.empty()) { 8873 stream << colourImpl->guardColour(colour) << passOrFail << ":\n"; 8874 } 8875 } 8876 void printOriginalExpression() const { 8877 if (result.hasExpression()) { 8878 stream << colourImpl->guardColour( Colour::OriginalExpression ) 8879 << " " << result.getExpressionInMacro() << '\n'; 8880 } 8881 } 8882 void printReconstructedExpression() const { 8883 if (result.hasExpandedExpression()) { 8884 stream << "with expansion:\n"; 8885 stream << colourImpl->guardColour( Colour::ReconstructedExpression ) 8886 << TextFlow::Column( result.getExpandedExpression() ) 8887 .indent( 2 ) 8888 << '\n'; 8889 } 8890 } 8891 void printMessage() const { 8892 if (!messageLabel.empty()) 8893 stream << messageLabel << ':' << '\n'; 8894 for (auto const& msg : messages) { 8895 // If this assertion is a warning ignore any INFO messages 8896 if (printInfoMessages || msg.type != ResultWas::Info) 8897 stream << TextFlow::Column(msg.message).indent(2) << '\n'; 8898 } 8899 } 8900 void printSourceInfo() const { 8901 stream << colourImpl->guardColour( Colour::FileName ) 8902 << result.getSourceInfo() << ": "; 8903 } 8904 8905 std::ostream& stream; 8906 AssertionStats const& stats; 8907 AssertionResult const& result; 8908 Colour::Code colour; 8909 StringRef passOrFail; 8910 StringRef messageLabel; 8911 std::vector<MessageInfo> const& messages; 8912 ColourImpl* colourImpl; 8913 bool printInfoMessages; 8914}; 8915 8916std::size_t makeRatio( std::uint64_t number, std::uint64_t total ) { 8917 const auto ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; 8918 return (ratio == 0 && number > 0) ? 1 : static_cast<std::size_t>(ratio); 8919} 8920 8921std::size_t& 8922findMax( std::size_t& i, std::size_t& j, std::size_t& k, std::size_t& l ) { 8923 if (i > j && i > k && i > l) 8924 return i; 8925 else if (j > k && j > l) 8926 return j; 8927 else if (k > l) 8928 return k; 8929 else 8930 return l; 8931} 8932 8933struct ColumnBreak {}; 8934struct RowBreak {}; 8935struct OutputFlush {}; 8936 8937class Duration { 8938 enum class Unit { 8939 Auto, 8940 Nanoseconds, 8941 Microseconds, 8942 Milliseconds, 8943 Seconds, 8944 Minutes 8945 }; 8946 static const uint64_t s_nanosecondsInAMicrosecond = 1000; 8947 static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; 8948 static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; 8949 static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; 8950 8951 double m_inNanoseconds; 8952 Unit m_units; 8953 8954public: 8955 explicit Duration(double inNanoseconds, Unit units = Unit::Auto) 8956 : m_inNanoseconds(inNanoseconds), 8957 m_units(units) { 8958 if (m_units == Unit::Auto) { 8959 if (m_inNanoseconds < s_nanosecondsInAMicrosecond) 8960 m_units = Unit::Nanoseconds; 8961 else if (m_inNanoseconds < s_nanosecondsInAMillisecond) 8962 m_units = Unit::Microseconds; 8963 else if (m_inNanoseconds < s_nanosecondsInASecond) 8964 m_units = Unit::Milliseconds; 8965 else if (m_inNanoseconds < s_nanosecondsInAMinute) 8966 m_units = Unit::Seconds; 8967 else 8968 m_units = Unit::Minutes; 8969 } 8970 8971 } 8972 8973 auto value() const -> double { 8974 switch (m_units) { 8975 case Unit::Microseconds: 8976 return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond); 8977 case Unit::Milliseconds: 8978 return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond); 8979 case Unit::Seconds: 8980 return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond); 8981 case Unit::Minutes: 8982 return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute); 8983 default: 8984 return m_inNanoseconds; 8985 } 8986 } 8987 StringRef unitsAsString() const { 8988 switch (m_units) { 8989 case Unit::Nanoseconds: 8990 return "ns"_sr; 8991 case Unit::Microseconds: 8992 return "us"_sr; 8993 case Unit::Milliseconds: 8994 return "ms"_sr; 8995 case Unit::Seconds: 8996 return "s"_sr; 8997 case Unit::Minutes: 8998 return "m"_sr; 8999 default: 9000 return "** internal error **"_sr; 9001 } 9002 9003 } 9004 friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { 9005 return os << duration.value() << ' ' << duration.unitsAsString(); 9006 } 9007}; 9008} // end anon namespace 9009 9010enum class Justification { Left, Right }; 9011 9012struct ColumnInfo { 9013 std::string name; 9014 std::size_t width; 9015 Justification justification; 9016}; 9017 9018class TablePrinter { 9019 std::ostream& m_os; 9020 std::vector<ColumnInfo> m_columnInfos; 9021 ReusableStringStream m_oss; 9022 int m_currentColumn = -1; 9023 bool m_isOpen = false; 9024 9025public: 9026 TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos ) 9027 : m_os( os ), 9028 m_columnInfos( CATCH_MOVE( columnInfos ) ) {} 9029 9030 auto columnInfos() const -> std::vector<ColumnInfo> const& { 9031 return m_columnInfos; 9032 } 9033 9034 void open() { 9035 if (!m_isOpen) { 9036 m_isOpen = true; 9037 *this << RowBreak(); 9038 9039 TextFlow::Columns headerCols; 9040 for (auto const& info : m_columnInfos) { 9041 assert(info.width > 2); 9042 headerCols += TextFlow::Column(info.name).width(info.width - 2); 9043 headerCols += TextFlow::Spacer( 2 ); 9044 } 9045 m_os << headerCols << '\n'; 9046 9047 m_os << lineOfChars('-') << '\n'; 9048 } 9049 } 9050 void close() { 9051 if (m_isOpen) { 9052 *this << RowBreak(); 9053 m_os << '\n' << std::flush; 9054 m_isOpen = false; 9055 } 9056 } 9057 9058 template<typename T> 9059 friend TablePrinter& operator<< (TablePrinter& tp, T const& value) { 9060 tp.m_oss << value; 9061 return tp; 9062 } 9063 9064 friend TablePrinter& operator<< (TablePrinter& tp, ColumnBreak) { 9065 auto colStr = tp.m_oss.str(); 9066 const auto strSize = colStr.size(); 9067 tp.m_oss.str(""); 9068 tp.open(); 9069 if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) { 9070 tp.m_currentColumn = -1; 9071 tp.m_os << '\n'; 9072 } 9073 tp.m_currentColumn++; 9074 9075 auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; 9076 auto padding = (strSize + 1 < colInfo.width) 9077 ? std::string(colInfo.width - (strSize + 1), ' ') 9078 : std::string(); 9079 if (colInfo.justification == Justification::Left) 9080 tp.m_os << colStr << padding << ' '; 9081 else 9082 tp.m_os << padding << colStr << ' '; 9083 return tp; 9084 } 9085 9086 friend TablePrinter& operator<< (TablePrinter& tp, RowBreak) { 9087 if (tp.m_currentColumn > 0) { 9088 tp.m_os << '\n'; 9089 tp.m_currentColumn = -1; 9090 } 9091 return tp; 9092 } 9093 9094 friend TablePrinter& operator<<(TablePrinter& tp, OutputFlush) { 9095 tp.m_os << std::flush; 9096 return tp; 9097 } 9098}; 9099 9100ConsoleReporter::ConsoleReporter(ReporterConfig&& config): 9101 StreamingReporterBase( CATCH_MOVE( config ) ), 9102 m_tablePrinter(Detail::make_unique<TablePrinter>(m_stream, 9103 [&config]() -> std::vector<ColumnInfo> { 9104 if (config.fullConfig()->benchmarkNoAnalysis()) 9105 { 9106 return{ 9107 { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, 9108 { " samples", 14, Justification::Right }, 9109 { " iterations", 14, Justification::Right }, 9110 { " mean", 14, Justification::Right } 9111 }; 9112 } 9113 else 9114 { 9115 return{ 9116 { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, Justification::Left }, 9117 { "samples mean std dev", 14, Justification::Right }, 9118 { "iterations low mean low std dev", 14, Justification::Right }, 9119 { "est run time high mean high std dev", 14, Justification::Right } 9120 }; 9121 } 9122 }())) {} 9123ConsoleReporter::~ConsoleReporter() = default; 9124 9125std::string ConsoleReporter::getDescription() { 9126 return "Reports test results as plain lines of text"; 9127} 9128 9129void ConsoleReporter::noMatchingTestCases( StringRef unmatchedSpec ) { 9130 m_stream << "No test cases matched '" << unmatchedSpec << "'\n"; 9131} 9132 9133void ConsoleReporter::reportInvalidTestSpec( StringRef arg ) { 9134 m_stream << "Invalid Filter: " << arg << '\n'; 9135} 9136 9137void ConsoleReporter::assertionStarting(AssertionInfo const&) {} 9138 9139void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { 9140 AssertionResult const& result = _assertionStats.assertionResult; 9141 9142 bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); 9143 9144 // Drop out if result was successful but we're not printing them. 9145 // TODO: Make configurable whether skips should be printed 9146 if (!includeResults && result.getResultType() != ResultWas::Warning && result.getResultType() != ResultWas::ExplicitSkip) 9147 return; 9148 9149 lazyPrint(); 9150 9151 ConsoleAssertionPrinter printer(m_stream, _assertionStats, m_colour.get(), includeResults); 9152 printer.print(); 9153 m_stream << '\n' << std::flush; 9154} 9155 9156void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { 9157 m_tablePrinter->close(); 9158 m_headerPrinted = false; 9159 StreamingReporterBase::sectionStarting(_sectionInfo); 9160} 9161void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { 9162 m_tablePrinter->close(); 9163 if (_sectionStats.missingAssertions) { 9164 lazyPrint(); 9165 auto guard = 9166 m_colour->guardColour( Colour::ResultError ).engage( m_stream ); 9167 if (m_sectionStack.size() > 1) 9168 m_stream << "\nNo assertions in section"; 9169 else 9170 m_stream << "\nNo assertions in test case"; 9171 m_stream << " '" << _sectionStats.sectionInfo.name << "'\n\n" << std::flush; 9172 } 9173 double dur = _sectionStats.durationInSeconds; 9174 if (shouldShowDuration(*m_config, dur)) { 9175 m_stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush; 9176 } 9177 if (m_headerPrinted) { 9178 m_headerPrinted = false; 9179 } 9180 StreamingReporterBase::sectionEnded(_sectionStats); 9181} 9182 9183void ConsoleReporter::benchmarkPreparing( StringRef name ) { 9184 lazyPrintWithoutClosingBenchmarkTable(); 9185 9186 auto nameCol = TextFlow::Column( static_cast<std::string>( name ) ) 9187 .width( m_tablePrinter->columnInfos()[0].width - 2 ); 9188 9189 bool firstLine = true; 9190 for (auto line : nameCol) { 9191 if (!firstLine) 9192 (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); 9193 else 9194 firstLine = false; 9195 9196 (*m_tablePrinter) << line << ColumnBreak(); 9197 } 9198} 9199 9200void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { 9201 (*m_tablePrinter) << info.samples << ColumnBreak() 9202 << info.iterations << ColumnBreak(); 9203 if ( !m_config->benchmarkNoAnalysis() ) { 9204 ( *m_tablePrinter ) 9205 << Duration( info.estimatedDuration ) << ColumnBreak(); 9206 } 9207 ( *m_tablePrinter ) << OutputFlush{}; 9208} 9209void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) { 9210 if (m_config->benchmarkNoAnalysis()) 9211 { 9212 (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); 9213 } 9214 else 9215 { 9216 (*m_tablePrinter) << ColumnBreak() 9217 << Duration(stats.mean.point.count()) << ColumnBreak() 9218 << Duration(stats.mean.lower_bound.count()) << ColumnBreak() 9219 << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak() 9220 << Duration(stats.standardDeviation.point.count()) << ColumnBreak() 9221 << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() 9222 << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak(); 9223 } 9224} 9225 9226void ConsoleReporter::benchmarkFailed( StringRef error ) { 9227 auto guard = m_colour->guardColour( Colour::Red ).engage( m_stream ); 9228 (*m_tablePrinter) 9229 << "Benchmark failed (" << error << ')' 9230 << ColumnBreak() << RowBreak(); 9231} 9232 9233void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { 9234 m_tablePrinter->close(); 9235 StreamingReporterBase::testCaseEnded(_testCaseStats); 9236 m_headerPrinted = false; 9237} 9238void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { 9239 printTotalsDivider(_testRunStats.totals); 9240 printTestRunTotals( m_stream, *m_colour, _testRunStats.totals ); 9241 m_stream << '\n' << std::flush; 9242 StreamingReporterBase::testRunEnded(_testRunStats); 9243} 9244void ConsoleReporter::testRunStarting(TestRunInfo const& _testRunInfo) { 9245 StreamingReporterBase::testRunStarting(_testRunInfo); 9246 if ( m_config->testSpec().hasFilters() ) { 9247 m_stream << m_colour->guardColour( Colour::BrightYellow ) << "Filters: " 9248 << m_config->testSpec() << '\n'; 9249 } 9250 m_stream << "Randomness seeded to: " << getSeed() << '\n'; 9251} 9252 9253void ConsoleReporter::lazyPrint() { 9254 9255 m_tablePrinter->close(); 9256 lazyPrintWithoutClosingBenchmarkTable(); 9257} 9258 9259void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { 9260 9261 if ( !m_testRunInfoPrinted ) { 9262 lazyPrintRunInfo(); 9263 } 9264 if (!m_headerPrinted) { 9265 printTestCaseAndSectionHeader(); 9266 m_headerPrinted = true; 9267 } 9268} 9269void ConsoleReporter::lazyPrintRunInfo() { 9270 m_stream << '\n' 9271 << lineOfChars( '~' ) << '\n' 9272 << m_colour->guardColour( Colour::SecondaryText ) 9273 << currentTestRunInfo.name << " is a Catch2 v" << libraryVersion() 9274 << " host application.\n" 9275 << "Run with -? for options\n\n"; 9276 9277 m_testRunInfoPrinted = true; 9278} 9279void ConsoleReporter::printTestCaseAndSectionHeader() { 9280 assert(!m_sectionStack.empty()); 9281 printOpenHeader(currentTestCaseInfo->name); 9282 9283 if (m_sectionStack.size() > 1) { 9284 auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream ); 9285 9286 auto 9287 it = m_sectionStack.begin() + 1, // Skip first section (test case) 9288 itEnd = m_sectionStack.end(); 9289 for (; it != itEnd; ++it) 9290 printHeaderString(it->name, 2); 9291 } 9292 9293 SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; 9294 9295 9296 m_stream << lineOfChars( '-' ) << '\n' 9297 << m_colour->guardColour( Colour::FileName ) << lineInfo << '\n' 9298 << lineOfChars( '.' ) << "\n\n" 9299 << std::flush; 9300} 9301 9302void ConsoleReporter::printClosedHeader(std::string const& _name) { 9303 printOpenHeader(_name); 9304 m_stream << lineOfChars('.') << '\n'; 9305} 9306void ConsoleReporter::printOpenHeader(std::string const& _name) { 9307 m_stream << lineOfChars('-') << '\n'; 9308 { 9309 auto guard = m_colour->guardColour( Colour::Headers ).engage( m_stream ); 9310 printHeaderString(_name); 9311 } 9312} 9313 9314void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { 9315 // We want to get a bit fancy with line breaking here, so that subsequent 9316 // lines start after ":" if one is present, e.g. 9317 // ``` 9318 // blablabla: Fancy 9319 // linebreaking 9320 // ``` 9321 // but we also want to avoid problems with overly long indentation causing 9322 // the text to take up too many lines, e.g. 9323 // ``` 9324 // blablabla: F 9325 // a 9326 // n 9327 // c 9328 // y 9329 // . 9330 // . 9331 // . 9332 // ``` 9333 // So we limit the prefix indentation check to first quarter of the possible 9334 // width 9335 std::size_t idx = _string.find( ": " ); 9336 if ( idx != std::string::npos && idx < CATCH_CONFIG_CONSOLE_WIDTH / 4 ) { 9337 idx += 2; 9338 } else { 9339 idx = 0; 9340 } 9341 m_stream << TextFlow::Column( _string ) 9342 .indent( indent + idx ) 9343 .initialIndent( indent ) 9344 << '\n'; 9345} 9346 9347void ConsoleReporter::printTotalsDivider(Totals const& totals) { 9348 if (totals.testCases.total() > 0) { 9349 std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); 9350 std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); 9351 std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); 9352 std::size_t skippedRatio = makeRatio(totals.testCases.skipped, totals.testCases.total()); 9353 while (failedRatio + failedButOkRatio + passedRatio + skippedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) 9354 findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)++; 9355 while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) 9356 findMax(failedRatio, failedButOkRatio, passedRatio, skippedRatio)--; 9357 9358 m_stream << m_colour->guardColour( Colour::Error ) 9359 << std::string( failedRatio, '=' ) 9360 << m_colour->guardColour( Colour::ResultExpectedFailure ) 9361 << std::string( failedButOkRatio, '=' ); 9362 if ( totals.testCases.allPassed() ) { 9363 m_stream << m_colour->guardColour( Colour::ResultSuccess ) 9364 << std::string( passedRatio, '=' ); 9365 } else { 9366 m_stream << m_colour->guardColour( Colour::Success ) 9367 << std::string( passedRatio, '=' ); 9368 } 9369 m_stream << m_colour->guardColour( Colour::Skip ) 9370 << std::string( skippedRatio, '=' ); 9371 } else { 9372 m_stream << m_colour->guardColour( Colour::Warning ) 9373 << std::string( CATCH_CONFIG_CONSOLE_WIDTH - 1, '=' ); 9374 } 9375 m_stream << '\n'; 9376} 9377 9378} // end namespace Catch 9379 9380#if defined(_MSC_VER) 9381#pragma warning(pop) 9382#endif 9383 9384#if defined(__clang__) 9385# pragma clang diagnostic pop 9386#endif 9387 9388 9389 9390 9391#include <algorithm> 9392#include <cassert> 9393 9394namespace Catch { 9395 namespace { 9396 struct BySectionInfo { 9397 BySectionInfo( SectionInfo const& other ): m_other( other ) {} 9398 BySectionInfo( BySectionInfo const& other ) = default; 9399 bool operator()( 9400 Detail::unique_ptr<CumulativeReporterBase::SectionNode> const& 9401 node ) const { 9402 return ( 9403 ( node->stats.sectionInfo.name == m_other.name ) && 9404 ( node->stats.sectionInfo.lineInfo == m_other.lineInfo ) ); 9405 } 9406 void operator=( BySectionInfo const& ) = delete; 9407 9408 private: 9409 SectionInfo const& m_other; 9410 }; 9411 9412 } // namespace 9413 9414 namespace Detail { 9415 AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( 9416 AssertionStats const& assertion ): 9417 m_assertion( assertion ) {} 9418 9419 AssertionOrBenchmarkResult::AssertionOrBenchmarkResult( 9420 BenchmarkStats<> const& benchmark ): 9421 m_benchmark( benchmark ) {} 9422 9423 bool AssertionOrBenchmarkResult::isAssertion() const { 9424 return m_assertion.some(); 9425 } 9426 bool AssertionOrBenchmarkResult::isBenchmark() const { 9427 return m_benchmark.some(); 9428 } 9429 9430 AssertionStats const& AssertionOrBenchmarkResult::asAssertion() const { 9431 assert(m_assertion.some()); 9432 9433 return *m_assertion; 9434 } 9435 BenchmarkStats<> const& AssertionOrBenchmarkResult::asBenchmark() const { 9436 assert(m_benchmark.some()); 9437 9438 return *m_benchmark; 9439 } 9440 9441 } 9442 9443 CumulativeReporterBase::~CumulativeReporterBase() = default; 9444 9445 void CumulativeReporterBase::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { 9446 m_sectionStack.back()->assertionsAndBenchmarks.emplace_back(benchmarkStats); 9447 } 9448 9449 void 9450 CumulativeReporterBase::sectionStarting( SectionInfo const& sectionInfo ) { 9451 // We need a copy, because SectionStats expect to take ownership 9452 SectionStats incompleteStats( SectionInfo(sectionInfo), Counts(), 0, false ); 9453 SectionNode* node; 9454 if ( m_sectionStack.empty() ) { 9455 if ( !m_rootSection ) { 9456 m_rootSection = 9457 Detail::make_unique<SectionNode>( incompleteStats ); 9458 } 9459 node = m_rootSection.get(); 9460 } else { 9461 SectionNode& parentNode = *m_sectionStack.back(); 9462 auto it = std::find_if( parentNode.childSections.begin(), 9463 parentNode.childSections.end(), 9464 BySectionInfo( sectionInfo ) ); 9465 if ( it == parentNode.childSections.end() ) { 9466 auto newNode = 9467 Detail::make_unique<SectionNode>( incompleteStats ); 9468 node = newNode.get(); 9469 parentNode.childSections.push_back( CATCH_MOVE( newNode ) ); 9470 } else { 9471 node = it->get(); 9472 } 9473 } 9474 9475 m_deepestSection = node; 9476 m_sectionStack.push_back( node ); 9477 } 9478 9479 void CumulativeReporterBase::assertionEnded( 9480 AssertionStats const& assertionStats ) { 9481 assert( !m_sectionStack.empty() ); 9482 // AssertionResult holds a pointer to a temporary DecomposedExpression, 9483 // which getExpandedExpression() calls to build the expression string. 9484 // Our section stack copy of the assertionResult will likely outlive the 9485 // temporary, so it must be expanded or discarded now to avoid calling 9486 // a destroyed object later. 9487 if ( m_shouldStoreFailedAssertions && 9488 !assertionStats.assertionResult.isOk() ) { 9489 static_cast<void>( 9490 assertionStats.assertionResult.getExpandedExpression() ); 9491 } 9492 if ( m_shouldStoreSuccesfulAssertions && 9493 assertionStats.assertionResult.isOk() ) { 9494 static_cast<void>( 9495 assertionStats.assertionResult.getExpandedExpression() ); 9496 } 9497 SectionNode& sectionNode = *m_sectionStack.back(); 9498 sectionNode.assertionsAndBenchmarks.emplace_back( assertionStats ); 9499 } 9500 9501 void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) { 9502 assert( !m_sectionStack.empty() ); 9503 SectionNode& node = *m_sectionStack.back(); 9504 node.stats = sectionStats; 9505 m_sectionStack.pop_back(); 9506 } 9507 9508 void CumulativeReporterBase::testCaseEnded( 9509 TestCaseStats const& testCaseStats ) { 9510 auto node = Detail::make_unique<TestCaseNode>( testCaseStats ); 9511 assert( m_sectionStack.size() == 0 ); 9512 node->children.push_back( CATCH_MOVE(m_rootSection) ); 9513 m_testCases.push_back( CATCH_MOVE(node) ); 9514 9515 assert( m_deepestSection ); 9516 m_deepestSection->stdOut = testCaseStats.stdOut; 9517 m_deepestSection->stdErr = testCaseStats.stdErr; 9518 } 9519 9520 9521 void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) { 9522 assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run"); 9523 m_testRun = Detail::make_unique<TestRunNode>( testRunStats ); 9524 m_testRun->children.swap( m_testCases ); 9525 testRunEndedCumulative(); 9526 } 9527 9528 bool CumulativeReporterBase::SectionNode::hasAnyAssertions() const { 9529 return std::any_of( 9530 assertionsAndBenchmarks.begin(), 9531 assertionsAndBenchmarks.end(), 9532 []( Detail::AssertionOrBenchmarkResult const& res ) { 9533 return res.isAssertion(); 9534 } ); 9535 } 9536 9537} // end namespace Catch 9538 9539 9540 9541 9542namespace Catch { 9543 9544 void EventListenerBase::fatalErrorEncountered( StringRef ) {} 9545 9546 void EventListenerBase::benchmarkPreparing( StringRef ) {} 9547 void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {} 9548 void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {} 9549 void EventListenerBase::benchmarkFailed( StringRef ) {} 9550 9551 void EventListenerBase::assertionStarting( AssertionInfo const& ) {} 9552 9553 void EventListenerBase::assertionEnded( AssertionStats const& ) {} 9554 void EventListenerBase::listReporters( 9555 std::vector<ReporterDescription> const& ) {} 9556 void EventListenerBase::listListeners( 9557 std::vector<ListenerDescription> const& ) {} 9558 void EventListenerBase::listTests( std::vector<TestCaseHandle> const& ) {} 9559 void EventListenerBase::listTags( std::vector<TagInfo> const& ) {} 9560 void EventListenerBase::noMatchingTestCases( StringRef ) {} 9561 void EventListenerBase::reportInvalidTestSpec( StringRef ) {} 9562 void EventListenerBase::testRunStarting( TestRunInfo const& ) {} 9563 void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {} 9564 void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {} 9565 void EventListenerBase::sectionStarting( SectionInfo const& ) {} 9566 void EventListenerBase::sectionEnded( SectionStats const& ) {} 9567 void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {} 9568 void EventListenerBase::testCaseEnded( TestCaseStats const& ) {} 9569 void EventListenerBase::testRunEnded( TestRunStats const& ) {} 9570 void EventListenerBase::skipTest( TestCaseInfo const& ) {} 9571} // namespace Catch 9572 9573 9574 9575 9576#include <algorithm> 9577#include <cfloat> 9578#include <cstdio> 9579#include <ostream> 9580#include <iomanip> 9581 9582namespace Catch { 9583 9584 namespace { 9585 void listTestNamesOnly(std::ostream& out, 9586 std::vector<TestCaseHandle> const& tests) { 9587 for (auto const& test : tests) { 9588 auto const& testCaseInfo = test.getTestCaseInfo(); 9589 9590 if (startsWith(testCaseInfo.name, '#')) { 9591 out << '"' << testCaseInfo.name << '"'; 9592 } else { 9593 out << testCaseInfo.name; 9594 } 9595 9596 out << '\n'; 9597 } 9598 out << std::flush; 9599 } 9600 } // end unnamed namespace 9601 9602 9603 // Because formatting using c++ streams is stateful, drop down to C is 9604 // required Alternatively we could use stringstream, but its performance 9605 // is... not good. 9606 std::string getFormattedDuration( double duration ) { 9607 // Max exponent + 1 is required to represent the whole part 9608 // + 1 for decimal point 9609 // + 3 for the 3 decimal places 9610 // + 1 for null terminator 9611 const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; 9612 char buffer[maxDoubleSize]; 9613 9614 // Save previous errno, to prevent sprintf from overwriting it 9615 ErrnoGuard guard; 9616#ifdef _MSC_VER 9617 size_t printedLength = static_cast<size_t>( 9618 sprintf_s( buffer, "%.3f", duration ) ); 9619#else 9620 size_t printedLength = static_cast<size_t>( 9621 std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) ); 9622#endif 9623 return std::string( buffer, printedLength ); 9624 } 9625 9626 bool shouldShowDuration( IConfig const& config, double duration ) { 9627 if ( config.showDurations() == ShowDurations::Always ) { 9628 return true; 9629 } 9630 if ( config.showDurations() == ShowDurations::Never ) { 9631 return false; 9632 } 9633 const double min = config.minDuration(); 9634 return min >= 0 && duration >= min; 9635 } 9636 9637 std::string serializeFilters( std::vector<std::string> const& filters ) { 9638 // We add a ' ' separator between each filter 9639 size_t serialized_size = filters.size() - 1; 9640 for (auto const& filter : filters) { 9641 serialized_size += filter.size(); 9642 } 9643 9644 std::string serialized; 9645 serialized.reserve(serialized_size); 9646 bool first = true; 9647 9648 for (auto const& filter : filters) { 9649 if (!first) { 9650 serialized.push_back(' '); 9651 } 9652 first = false; 9653 serialized.append(filter); 9654 } 9655 9656 return serialized; 9657 } 9658 9659 std::ostream& operator<<( std::ostream& out, lineOfChars value ) { 9660 for ( size_t idx = 0; idx < CATCH_CONFIG_CONSOLE_WIDTH - 1; ++idx ) { 9661 out.put( value.c ); 9662 } 9663 return out; 9664 } 9665 9666 void 9667 defaultListReporters( std::ostream& out, 9668 std::vector<ReporterDescription> const& descriptions, 9669 Verbosity verbosity ) { 9670 out << "Available reporters:\n"; 9671 const auto maxNameLen = 9672 std::max_element( descriptions.begin(), 9673 descriptions.end(), 9674 []( ReporterDescription const& lhs, 9675 ReporterDescription const& rhs ) { 9676 return lhs.name.size() < rhs.name.size(); 9677 } ) 9678 ->name.size(); 9679 9680 for ( auto const& desc : descriptions ) { 9681 if ( verbosity == Verbosity::Quiet ) { 9682 out << TextFlow::Column( desc.name ) 9683 .indent( 2 ) 9684 .width( 5 + maxNameLen ) 9685 << '\n'; 9686 } else { 9687 out << TextFlow::Column( desc.name + ':' ) 9688 .indent( 2 ) 9689 .width( 5 + maxNameLen ) + 9690 TextFlow::Column( desc.description ) 9691 .initialIndent( 0 ) 9692 .indent( 2 ) 9693 .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) 9694 << '\n'; 9695 } 9696 } 9697 out << '\n' << std::flush; 9698 } 9699 9700 void defaultListListeners( std::ostream& out, 9701 std::vector<ListenerDescription> const& descriptions ) { 9702 out << "Registered listeners:\n"; 9703 9704 if(descriptions.empty()) { 9705 return; 9706 } 9707 9708 const auto maxNameLen = 9709 std::max_element( descriptions.begin(), 9710 descriptions.end(), 9711 []( ListenerDescription const& lhs, 9712 ListenerDescription const& rhs ) { 9713 return lhs.name.size() < rhs.name.size(); 9714 } ) 9715 ->name.size(); 9716 9717 for ( auto const& desc : descriptions ) { 9718 out << TextFlow::Column( static_cast<std::string>( desc.name ) + 9719 ':' ) 9720 .indent( 2 ) 9721 .width( maxNameLen + 5 ) + 9722 TextFlow::Column( desc.description ) 9723 .initialIndent( 0 ) 9724 .indent( 2 ) 9725 .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) 9726 << '\n'; 9727 } 9728 9729 out << '\n' << std::flush; 9730 } 9731 9732 void defaultListTags( std::ostream& out, 9733 std::vector<TagInfo> const& tags, 9734 bool isFiltered ) { 9735 if ( isFiltered ) { 9736 out << "Tags for matching test cases:\n"; 9737 } else { 9738 out << "All available tags:\n"; 9739 } 9740 9741 for ( auto const& tagCount : tags ) { 9742 ReusableStringStream rss; 9743 rss << " " << std::setw( 2 ) << tagCount.count << " "; 9744 auto str = rss.str(); 9745 auto wrapper = TextFlow::Column( tagCount.all() ) 9746 .initialIndent( 0 ) 9747 .indent( str.size() ) 9748 .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 ); 9749 out << str << wrapper << '\n'; 9750 } 9751 out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush; 9752 } 9753 9754 void defaultListTests(std::ostream& out, ColourImpl* streamColour, std::vector<TestCaseHandle> const& tests, bool isFiltered, Verbosity verbosity) { 9755 // We special case this to provide the equivalent of old 9756 // `--list-test-names-only`, which could then be used by the 9757 // `--input-file` option. 9758 if (verbosity == Verbosity::Quiet) { 9759 listTestNamesOnly(out, tests); 9760 return; 9761 } 9762 9763 if (isFiltered) { 9764 out << "Matching test cases:\n"; 9765 } else { 9766 out << "All available test cases:\n"; 9767 } 9768 9769 for (auto const& test : tests) { 9770 auto const& testCaseInfo = test.getTestCaseInfo(); 9771 Colour::Code colour = testCaseInfo.isHidden() 9772 ? Colour::SecondaryText 9773 : Colour::None; 9774 auto colourGuard = streamColour->guardColour( colour ).engage( out ); 9775 9776 out << TextFlow::Column(testCaseInfo.name).indent(2) << '\n'; 9777 if (verbosity >= Verbosity::High) { 9778 out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n'; 9779 } 9780 if (!testCaseInfo.tags.empty() && 9781 verbosity > Verbosity::Quiet) { 9782 out << TextFlow::Column(testCaseInfo.tagsAsString()).indent(6) << '\n'; 9783 } 9784 } 9785 9786 if (isFiltered) { 9787 out << pluralise(tests.size(), "matching test case"_sr); 9788 } else { 9789 out << pluralise(tests.size(), "test case"_sr); 9790 } 9791 out << "\n\n" << std::flush; 9792 } 9793 9794 namespace { 9795 class SummaryColumn { 9796 public: 9797 SummaryColumn( std::string suffix, Colour::Code colour ): 9798 m_suffix( CATCH_MOVE( suffix ) ), m_colour( colour ) {} 9799 9800 SummaryColumn&& addRow( std::uint64_t count ) && { 9801 std::string row = std::to_string(count); 9802 auto const new_width = std::max( m_width, row.size() ); 9803 if ( new_width > m_width ) { 9804 for ( auto& oldRow : m_rows ) { 9805 oldRow.insert( 0, new_width - m_width, ' ' ); 9806 } 9807 } else { 9808 row.insert( 0, m_width - row.size(), ' ' ); 9809 } 9810 m_width = new_width; 9811 m_rows.push_back( row ); 9812 return std::move( *this ); 9813 } 9814 9815 std::string const& getSuffix() const { return m_suffix; } 9816 Colour::Code getColour() const { return m_colour; } 9817 std::string const& getRow( std::size_t index ) const { 9818 return m_rows[index]; 9819 } 9820 9821 private: 9822 std::string m_suffix; 9823 Colour::Code m_colour; 9824 std::size_t m_width = 0; 9825 std::vector<std::string> m_rows; 9826 }; 9827 9828 void printSummaryRow( std::ostream& stream, 9829 ColourImpl& colour, 9830 StringRef label, 9831 std::vector<SummaryColumn> const& cols, 9832 std::size_t row ) { 9833 for ( auto const& col : cols ) { 9834 auto const& value = col.getRow( row ); 9835 auto const& suffix = col.getSuffix(); 9836 if ( suffix.empty() ) { 9837 stream << label << ": "; 9838 if ( value != "0" ) { 9839 stream << value; 9840 } else { 9841 stream << colour.guardColour( Colour::Warning ) 9842 << "- none -"; 9843 } 9844 } else if ( value != "0" ) { 9845 stream << colour.guardColour( Colour::LightGrey ) << " | " 9846 << colour.guardColour( col.getColour() ) << value 9847 << ' ' << suffix; 9848 } 9849 } 9850 stream << '\n'; 9851 } 9852 } // namespace 9853 9854 void printTestRunTotals( std::ostream& stream, 9855 ColourImpl& streamColour, 9856 Totals const& totals ) { 9857 if ( totals.testCases.total() == 0 ) { 9858 stream << streamColour.guardColour( Colour::Warning ) 9859 << "No tests ran\n"; 9860 return; 9861 } 9862 9863 if ( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { 9864 stream << streamColour.guardColour( Colour::ResultSuccess ) 9865 << "All tests passed"; 9866 stream << " (" 9867 << pluralise( totals.assertions.passed, "assertion"_sr ) 9868 << " in " 9869 << pluralise( totals.testCases.passed, "test case"_sr ) 9870 << ')' << '\n'; 9871 return; 9872 } 9873 9874 std::vector<SummaryColumn> columns; 9875 // Don't include "skipped assertions" in total count 9876 const auto totalAssertionCount = 9877 totals.assertions.total() - totals.assertions.skipped; 9878 columns.push_back( SummaryColumn( "", Colour::None ) 9879 .addRow( totals.testCases.total() ) 9880 .addRow( totalAssertionCount ) ); 9881 columns.push_back( SummaryColumn( "passed", Colour::Success ) 9882 .addRow( totals.testCases.passed ) 9883 .addRow( totals.assertions.passed ) ); 9884 columns.push_back( SummaryColumn( "failed", Colour::ResultError ) 9885 .addRow( totals.testCases.failed ) 9886 .addRow( totals.assertions.failed ) ); 9887 columns.push_back( SummaryColumn( "skipped", Colour::Skip ) 9888 .addRow( totals.testCases.skipped ) 9889 // Don't print "skipped assertions" 9890 .addRow( 0 ) ); 9891 columns.push_back( 9892 SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) 9893 .addRow( totals.testCases.failedButOk ) 9894 .addRow( totals.assertions.failedButOk ) ); 9895 printSummaryRow( stream, streamColour, "test cases"_sr, columns, 0 ); 9896 printSummaryRow( stream, streamColour, "assertions"_sr, columns, 1 ); 9897 } 9898 9899} // namespace Catch 9900 9901 9902// 9903 9904namespace Catch { 9905 namespace { 9906 void writeSourceInfo( JsonObjectWriter& writer, 9907 SourceLineInfo const& sourceInfo ) { 9908 auto source_location_writer = 9909 writer.write( "source-location"_sr ).writeObject(); 9910 source_location_writer.write( "filename"_sr ) 9911 .write( sourceInfo.file ); 9912 source_location_writer.write( "line"_sr ).write( sourceInfo.line ); 9913 } 9914 9915 void writeTags( JsonArrayWriter writer, std::vector<Tag> const& tags ) { 9916 for ( auto const& tag : tags ) { 9917 writer.write( tag.original ); 9918 } 9919 } 9920 9921 void writeProperties( JsonArrayWriter writer, 9922 TestCaseInfo const& info ) { 9923 if ( info.isHidden() ) { writer.write( "is-hidden"_sr ); } 9924 if ( info.okToFail() ) { writer.write( "ok-to-fail"_sr ); } 9925 if ( info.expectedToFail() ) { 9926 writer.write( "expected-to-fail"_sr ); 9927 } 9928 if ( info.throws() ) { writer.write( "throws"_sr ); } 9929 } 9930 9931 } // namespace 9932 9933 JsonReporter::JsonReporter( ReporterConfig&& config ): 9934 StreamingReporterBase{ CATCH_MOVE( config ) } { 9935 9936 m_preferences.shouldRedirectStdOut = true; 9937 // TBD: Do we want to report all assertions? XML reporter does 9938 // not, but for machine-parseable reporters I think the answer 9939 // should be yes. 9940 m_preferences.shouldReportAllAssertions = true; 9941 9942 m_objectWriters.emplace( m_stream ); 9943 m_writers.emplace( Writer::Object ); 9944 auto& writer = m_objectWriters.top(); 9945 9946 writer.write( "version"_sr ).write( 1 ); 9947 9948 { 9949 auto metadata_writer = writer.write( "metadata"_sr ).writeObject(); 9950 metadata_writer.write( "name"_sr ).write( m_config->name() ); 9951 metadata_writer.write( "rng-seed"_sr ).write( m_config->rngSeed() ); 9952 metadata_writer.write( "catch2-version"_sr ) 9953 .write( libraryVersion() ); 9954 if ( m_config->testSpec().hasFilters() ) { 9955 metadata_writer.write( "filters"_sr ) 9956 .write( m_config->testSpec() ); 9957 } 9958 } 9959 } 9960 9961 JsonReporter::~JsonReporter() { 9962 endListing(); 9963 // TODO: Ensure this closes the top level object, add asserts 9964 assert( m_writers.size() == 1 && "Only the top level object should be open" ); 9965 assert( m_writers.top() == Writer::Object ); 9966 endObject(); 9967 m_stream << '\n' << std::flush; 9968 assert( m_writers.empty() ); 9969 } 9970 9971 JsonArrayWriter& JsonReporter::startArray() { 9972 m_arrayWriters.emplace( m_arrayWriters.top().writeArray() ); 9973 m_writers.emplace( Writer::Array ); 9974 return m_arrayWriters.top(); 9975 } 9976 JsonArrayWriter& JsonReporter::startArray( StringRef key ) { 9977 m_arrayWriters.emplace( 9978 m_objectWriters.top().write( key ).writeArray() ); 9979 m_writers.emplace( Writer::Array ); 9980 return m_arrayWriters.top(); 9981 } 9982 9983 JsonObjectWriter& JsonReporter::startObject() { 9984 m_objectWriters.emplace( m_arrayWriters.top().writeObject() ); 9985 m_writers.emplace( Writer::Object ); 9986 return m_objectWriters.top(); 9987 } 9988 JsonObjectWriter& JsonReporter::startObject( StringRef key ) { 9989 m_objectWriters.emplace( 9990 m_objectWriters.top().write( key ).writeObject() ); 9991 m_writers.emplace( Writer::Object ); 9992 return m_objectWriters.top(); 9993 } 9994 9995 void JsonReporter::endObject() { 9996 assert( isInside( Writer::Object ) ); 9997 m_objectWriters.pop(); 9998 m_writers.pop(); 9999 } 10000 void JsonReporter::endArray() { 10001 assert( isInside( Writer::Array ) ); 10002 m_arrayWriters.pop(); 10003 m_writers.pop(); 10004 } 10005 10006 bool JsonReporter::isInside( Writer writer ) { 10007 return !m_writers.empty() && m_writers.top() == writer; 10008 } 10009 10010 void JsonReporter::startListing() { 10011 if ( !m_startedListing ) { startObject( "listings"_sr ); } 10012 m_startedListing = true; 10013 } 10014 void JsonReporter::endListing() { 10015 if ( m_startedListing ) { endObject(); } 10016 m_startedListing = false; 10017 } 10018 10019 std::string JsonReporter::getDescription() { 10020 return "Outputs listings as JSON. Test listing is Work-in-Progress!"; 10021 } 10022 10023 void JsonReporter::testRunStarting( TestRunInfo const& runInfo ) { 10024 StreamingReporterBase::testRunStarting( runInfo ); 10025 endListing(); 10026 10027 assert( isInside( Writer::Object ) ); 10028 startObject( "test-run"_sr ); 10029 startArray( "test-cases"_sr ); 10030 } 10031 10032 static void writeCounts( JsonObjectWriter&& writer, Counts const& counts ) { 10033 writer.write( "passed"_sr ).write( counts.passed ); 10034 writer.write( "failed"_sr ).write( counts.failed ); 10035 writer.write( "fail-but-ok"_sr ).write( counts.failedButOk ); 10036 writer.write( "skipped"_sr ).write( counts.skipped ); 10037 } 10038 10039 void JsonReporter::testRunEnded(TestRunStats const& runStats) { 10040 assert( isInside( Writer::Array ) ); 10041 // End "test-cases" 10042 endArray(); 10043 10044 { 10045 auto totals = 10046 m_objectWriters.top().write( "totals"_sr ).writeObject(); 10047 writeCounts( totals.write( "assertions"_sr ).writeObject(), 10048 runStats.totals.assertions ); 10049 writeCounts( totals.write( "test-cases"_sr ).writeObject(), 10050 runStats.totals.testCases ); 10051 } 10052 10053 // End the "test-run" object 10054 endObject(); 10055 } 10056 10057 void JsonReporter::testCaseStarting( TestCaseInfo const& tcInfo ) { 10058 StreamingReporterBase::testCaseStarting( tcInfo ); 10059 10060 assert( isInside( Writer::Array ) && 10061 "We should be in the 'test-cases' array" ); 10062 startObject(); 10063 // "test-info" prelude 10064 { 10065 auto testInfo = 10066 m_objectWriters.top().write( "test-info"_sr ).writeObject(); 10067 // TODO: handle testName vs className!! 10068 testInfo.write( "name"_sr ).write( tcInfo.name ); 10069 writeSourceInfo(testInfo, tcInfo.lineInfo); 10070 writeTags( testInfo.write( "tags"_sr ).writeArray(), tcInfo.tags ); 10071 writeProperties( testInfo.write( "properties"_sr ).writeArray(), 10072 tcInfo ); 10073 } 10074 10075 10076 // Start the array for individual test runs (testCasePartial pairs) 10077 startArray( "runs"_sr ); 10078 } 10079 10080 void JsonReporter::testCaseEnded( TestCaseStats const& tcStats ) { 10081 StreamingReporterBase::testCaseEnded( tcStats ); 10082 10083 // We need to close the 'runs' array before finishing the test case 10084 assert( isInside( Writer::Array ) ); 10085 endArray(); 10086 10087 { 10088 auto totals = 10089 m_objectWriters.top().write( "totals"_sr ).writeObject(); 10090 writeCounts( totals.write( "assertions"_sr ).writeObject(), 10091 tcStats.totals.assertions ); 10092 // We do not write the test case totals, because there will always be just one test case here. 10093 // TODO: overall "result" -> success, skip, fail here? Or in partial result? 10094 } 10095 // We do not write out stderr/stdout, because we instead wrote those out in partial runs 10096 10097 // TODO: aborting? 10098 10099 // And we also close this test case's object 10100 assert( isInside( Writer::Object ) ); 10101 endObject(); 10102 } 10103 10104 void JsonReporter::testCasePartialStarting( TestCaseInfo const& /*tcInfo*/, 10105 uint64_t index ) { 10106 startObject(); 10107 m_objectWriters.top().write( "run-idx"_sr ).write( index ); 10108 startArray( "path"_sr ); 10109 // TODO: we want to delay most of the printing to the 'root' section 10110 // TODO: childSection key name? 10111 } 10112 10113 void JsonReporter::testCasePartialEnded( TestCaseStats const& tcStats, 10114 uint64_t /*index*/ ) { 10115 // Fixme: the top level section handles this. 10116 //// path object 10117 endArray(); 10118 if ( !tcStats.stdOut.empty() ) { 10119 m_objectWriters.top() 10120 .write( "captured-stdout"_sr ) 10121 .write( tcStats.stdOut ); 10122 } 10123 if ( !tcStats.stdErr.empty() ) { 10124 m_objectWriters.top() 10125 .write( "captured-stderr"_sr ) 10126 .write( tcStats.stdErr ); 10127 } 10128 { 10129 auto totals = 10130 m_objectWriters.top().write( "totals"_sr ).writeObject(); 10131 writeCounts( totals.write( "assertions"_sr ).writeObject(), 10132 tcStats.totals.assertions ); 10133 // We do not write the test case totals, because there will 10134 // always be just one test case here. 10135 // TODO: overall "result" -> success, skip, fail here? Or in 10136 // partial result? 10137 } 10138 // TODO: aborting? 10139 // run object 10140 endObject(); 10141 } 10142 10143 void JsonReporter::sectionStarting( SectionInfo const& sectionInfo ) { 10144 assert( isInside( Writer::Array ) && 10145 "Section should always start inside an object" ); 10146 // We want to nest top level sections, even though it shares name 10147 // and source loc with the TEST_CASE 10148 auto& sectionObject = startObject(); 10149 sectionObject.write( "kind"_sr ).write( "section"_sr ); 10150 sectionObject.write( "name"_sr ).write( sectionInfo.name ); 10151 writeSourceInfo( m_objectWriters.top(), sectionInfo.lineInfo ); 10152 10153 10154 // TBD: Do we want to create this event lazily? It would become 10155 // rather complex, but we could do it, and it would look 10156 // better for empty sections. OTOH, empty sections should 10157 // be rare. 10158 startArray( "path"_sr ); 10159 } 10160 void JsonReporter::sectionEnded( SectionStats const& /*sectionStats */) { 10161 // End the subpath array 10162 endArray(); 10163 // TODO: metadata 10164 // TODO: what info do we have here? 10165 10166 // End the section object 10167 endObject(); 10168 } 10169 10170 void JsonReporter::assertionStarting( AssertionInfo const& /*assertionInfo*/ ) {} 10171 void JsonReporter::assertionEnded( AssertionStats const& assertionStats ) { 10172 // TODO: There is lot of different things to handle here, but 10173 // we can fill it in later, after we show that the basic 10174 // outline and streaming reporter impl works well enough. 10175 //if ( !m_config->includeSuccessfulResults() 10176 // && assertionStats.assertionResult.isOk() ) { 10177 // return; 10178 //} 10179 assert( isInside( Writer::Array ) ); 10180 auto assertionObject = m_arrayWriters.top().writeObject(); 10181 10182 assertionObject.write( "kind"_sr ).write( "assertion"_sr ); 10183 writeSourceInfo( assertionObject, 10184 assertionStats.assertionResult.getSourceInfo() ); 10185 assertionObject.write( "status"_sr ) 10186 .write( assertionStats.assertionResult.isOk() ); 10187 // TODO: handling of result. 10188 // TODO: messages 10189 // TODO: totals? 10190 } 10191 10192 10193 void JsonReporter::benchmarkPreparing( StringRef name ) { (void)name; } 10194 void JsonReporter::benchmarkStarting( BenchmarkInfo const& ) {} 10195 void JsonReporter::benchmarkEnded( BenchmarkStats<> const& ) {} 10196 void JsonReporter::benchmarkFailed( StringRef error ) { (void)error; } 10197 10198 void JsonReporter::listReporters( 10199 std::vector<ReporterDescription> const& descriptions ) { 10200 startListing(); 10201 10202 auto writer = 10203 m_objectWriters.top().write( "reporters"_sr ).writeArray(); 10204 for ( auto const& desc : descriptions ) { 10205 auto desc_writer = writer.writeObject(); 10206 desc_writer.write( "name"_sr ).write( desc.name ); 10207 desc_writer.write( "description"_sr ).write( desc.description ); 10208 } 10209 } 10210 void JsonReporter::listListeners( 10211 std::vector<ListenerDescription> const& descriptions ) { 10212 startListing(); 10213 10214 auto writer = 10215 m_objectWriters.top().write( "listeners"_sr ).writeArray(); 10216 10217 for ( auto const& desc : descriptions ) { 10218 auto desc_writer = writer.writeObject(); 10219 desc_writer.write( "name"_sr ).write( desc.name ); 10220 desc_writer.write( "description"_sr ).write( desc.description ); 10221 } 10222 } 10223 void JsonReporter::listTests( std::vector<TestCaseHandle> const& tests ) { 10224 startListing(); 10225 10226 auto writer = m_objectWriters.top().write( "tests"_sr ).writeArray(); 10227 10228 for ( auto const& test : tests ) { 10229 auto desc_writer = writer.writeObject(); 10230 auto const& info = test.getTestCaseInfo(); 10231 10232 desc_writer.write( "name"_sr ).write( info.name ); 10233 desc_writer.write( "class-name"_sr ).write( info.className ); 10234 { 10235 auto tag_writer = desc_writer.write( "tags"_sr ).writeArray(); 10236 for ( auto const& tag : info.tags ) { 10237 tag_writer.write( tag.original ); 10238 } 10239 } 10240 writeSourceInfo( desc_writer, info.lineInfo ); 10241 } 10242 } 10243 void JsonReporter::listTags( std::vector<TagInfo> const& tags ) { 10244 startListing(); 10245 10246 auto writer = m_objectWriters.top().write( "tags"_sr ).writeArray(); 10247 for ( auto const& tag : tags ) { 10248 auto tag_writer = writer.writeObject(); 10249 { 10250 auto aliases_writer = 10251 tag_writer.write( "aliases"_sr ).writeArray(); 10252 for ( auto alias : tag.spellings ) { 10253 aliases_writer.write( alias ); 10254 } 10255 } 10256 tag_writer.write( "count"_sr ).write( tag.count ); 10257 } 10258 } 10259} // namespace Catch 10260 10261 10262 10263 10264#include <cassert> 10265#include <ctime> 10266#include <algorithm> 10267#include <iomanip> 10268 10269namespace Catch { 10270 10271 namespace { 10272 std::string getCurrentTimestamp() { 10273 time_t rawtime; 10274 std::time(&rawtime); 10275 10276 std::tm timeInfo = {}; 10277#if defined (_MSC_VER) || defined (__MINGW32__) 10278 gmtime_s(&timeInfo, &rawtime); 10279#elif defined (CATCH_PLATFORM_PLAYSTATION) 10280 gmtime_s(&rawtime, &timeInfo); 10281#elif defined (__IAR_SYSTEMS_ICC__) 10282 timeInfo = *std::gmtime(&rawtime); 10283#else 10284 gmtime_r(&rawtime, &timeInfo); 10285#endif 10286 10287 auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); 10288 char timeStamp[timeStampSize]; 10289 const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; 10290 10291 std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); 10292 10293 return std::string(timeStamp, timeStampSize - 1); 10294 } 10295 10296 std::string fileNameTag(std::vector<Tag> const& tags) { 10297 auto it = std::find_if(begin(tags), 10298 end(tags), 10299 [] (Tag const& tag) { 10300 return tag.original.size() > 0 10301 && tag.original[0] == '#'; }); 10302 if (it != tags.end()) { 10303 return static_cast<std::string>( 10304 it->original.substr(1, it->original.size() - 1) 10305 ); 10306 } 10307 return std::string(); 10308 } 10309 10310 // Formats the duration in seconds to 3 decimal places. 10311 // This is done because some genius defined Maven Surefire schema 10312 // in a way that only accepts 3 decimal places, and tools like 10313 // Jenkins use that schema for validation JUnit reporter output. 10314 std::string formatDuration( double seconds ) { 10315 ReusableStringStream rss; 10316 rss << std::fixed << std::setprecision( 3 ) << seconds; 10317 return rss.str(); 10318 } 10319 10320 static void normalizeNamespaceMarkers(std::string& str) { 10321 std::size_t pos = str.find( "::" ); 10322 while ( pos != std::string::npos ) { 10323 str.replace( pos, 2, "." ); 10324 pos += 1; 10325 pos = str.find( "::", pos ); 10326 } 10327 } 10328 10329 } // anonymous namespace 10330 10331 JunitReporter::JunitReporter( ReporterConfig&& _config ) 10332 : CumulativeReporterBase( CATCH_MOVE(_config) ), 10333 xml( m_stream ) 10334 { 10335 m_preferences.shouldRedirectStdOut = true; 10336 m_preferences.shouldReportAllAssertions = true; 10337 m_shouldStoreSuccesfulAssertions = false; 10338 } 10339 10340 std::string JunitReporter::getDescription() { 10341 return "Reports test results in an XML format that looks like Ant's junitreport target"; 10342 } 10343 10344 void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { 10345 CumulativeReporterBase::testRunStarting( runInfo ); 10346 xml.startElement( "testsuites" ); 10347 suiteTimer.start(); 10348 stdOutForSuite.clear(); 10349 stdErrForSuite.clear(); 10350 unexpectedExceptions = 0; 10351 } 10352 10353 void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { 10354 m_okToFail = testCaseInfo.okToFail(); 10355 } 10356 10357 void JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { 10358 if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) 10359 unexpectedExceptions++; 10360 CumulativeReporterBase::assertionEnded( assertionStats ); 10361 } 10362 10363 void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { 10364 stdOutForSuite += testCaseStats.stdOut; 10365 stdErrForSuite += testCaseStats.stdErr; 10366 CumulativeReporterBase::testCaseEnded( testCaseStats ); 10367 } 10368 10369 void JunitReporter::testRunEndedCumulative() { 10370 const auto suiteTime = suiteTimer.getElapsedSeconds(); 10371 writeRun( *m_testRun, suiteTime ); 10372 xml.endElement(); 10373 } 10374 10375 void JunitReporter::writeRun( TestRunNode const& testRunNode, double suiteTime ) { 10376 XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); 10377 10378 TestRunStats const& stats = testRunNode.value; 10379 xml.writeAttribute( "name"_sr, stats.runInfo.name ); 10380 xml.writeAttribute( "errors"_sr, unexpectedExceptions ); 10381 xml.writeAttribute( "failures"_sr, stats.totals.assertions.failed-unexpectedExceptions ); 10382 xml.writeAttribute( "skipped"_sr, stats.totals.assertions.skipped ); 10383 xml.writeAttribute( "tests"_sr, stats.totals.assertions.total() ); 10384 xml.writeAttribute( "hostname"_sr, "tbd"_sr ); // !TBD 10385 if( m_config->showDurations() == ShowDurations::Never ) 10386 xml.writeAttribute( "time"_sr, ""_sr ); 10387 else 10388 xml.writeAttribute( "time"_sr, formatDuration( suiteTime ) ); 10389 xml.writeAttribute( "timestamp"_sr, getCurrentTimestamp() ); 10390 10391 // Write properties 10392 { 10393 auto properties = xml.scopedElement("properties"); 10394 xml.scopedElement("property") 10395 .writeAttribute("name"_sr, "random-seed"_sr) 10396 .writeAttribute("value"_sr, m_config->rngSeed()); 10397 if (m_config->testSpec().hasFilters()) { 10398 xml.scopedElement("property") 10399 .writeAttribute("name"_sr, "filters"_sr) 10400 .writeAttribute("value"_sr, m_config->testSpec()); 10401 } 10402 } 10403 10404 // Write test cases 10405 for( auto const& child : testRunNode.children ) 10406 writeTestCase( *child ); 10407 10408 xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline ); 10409 xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline ); 10410 } 10411 10412 void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { 10413 TestCaseStats const& stats = testCaseNode.value; 10414 10415 // All test cases have exactly one section - which represents the 10416 // test case itself. That section may have 0-n nested sections 10417 assert( testCaseNode.children.size() == 1 ); 10418 SectionNode const& rootSection = *testCaseNode.children.front(); 10419 10420 std::string className = 10421 static_cast<std::string>( stats.testInfo->className ); 10422 10423 if( className.empty() ) { 10424 className = fileNameTag(stats.testInfo->tags); 10425 if ( className.empty() ) { 10426 className = "global"; 10427 } 10428 } 10429 10430 if ( !m_config->name().empty() ) 10431 className = static_cast<std::string>(m_config->name()) + '.' + className; 10432 10433 normalizeNamespaceMarkers(className); 10434 10435 writeSection( className, "", rootSection, stats.testInfo->okToFail() ); 10436 } 10437 10438 void JunitReporter::writeSection( std::string const& className, 10439 std::string const& rootName, 10440 SectionNode const& sectionNode, 10441 bool testOkToFail) { 10442 std::string name = trim( sectionNode.stats.sectionInfo.name ); 10443 if( !rootName.empty() ) 10444 name = rootName + '/' + name; 10445 10446 if( sectionNode.hasAnyAssertions() 10447 || !sectionNode.stdOut.empty() 10448 || !sectionNode.stdErr.empty() ) { 10449 XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); 10450 if( className.empty() ) { 10451 xml.writeAttribute( "classname"_sr, name ); 10452 xml.writeAttribute( "name"_sr, "root"_sr ); 10453 } 10454 else { 10455 xml.writeAttribute( "classname"_sr, className ); 10456 xml.writeAttribute( "name"_sr, name ); 10457 } 10458 xml.writeAttribute( "time"_sr, formatDuration( sectionNode.stats.durationInSeconds ) ); 10459 // This is not ideal, but it should be enough to mimic gtest's 10460 // junit output. 10461 // Ideally the JUnit reporter would also handle `skipTest` 10462 // events and write those out appropriately. 10463 xml.writeAttribute( "status"_sr, "run"_sr ); 10464 10465 if (sectionNode.stats.assertions.failedButOk) { 10466 xml.scopedElement("skipped") 10467 .writeAttribute("message", "TEST_CASE tagged with !mayfail"); 10468 } 10469 10470 writeAssertions( sectionNode ); 10471 10472 10473 if( !sectionNode.stdOut.empty() ) 10474 xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline ); 10475 if( !sectionNode.stdErr.empty() ) 10476 xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline ); 10477 } 10478 for( auto const& childNode : sectionNode.childSections ) 10479 if( className.empty() ) 10480 writeSection( name, "", *childNode, testOkToFail ); 10481 else 10482 writeSection( className, name, *childNode, testOkToFail ); 10483 } 10484 10485 void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { 10486 for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { 10487 if (assertionOrBenchmark.isAssertion()) { 10488 writeAssertion(assertionOrBenchmark.asAssertion()); 10489 } 10490 } 10491 } 10492 10493 void JunitReporter::writeAssertion( AssertionStats const& stats ) { 10494 AssertionResult const& result = stats.assertionResult; 10495 if ( !result.isOk() || 10496 result.getResultType() == ResultWas::ExplicitSkip ) { 10497 std::string elementName; 10498 switch( result.getResultType() ) { 10499 case ResultWas::ThrewException: 10500 case ResultWas::FatalErrorCondition: 10501 elementName = "error"; 10502 break; 10503 case ResultWas::ExplicitFailure: 10504 case ResultWas::ExpressionFailed: 10505 case ResultWas::DidntThrowException: 10506 elementName = "failure"; 10507 break; 10508 case ResultWas::ExplicitSkip: 10509 elementName = "skipped"; 10510 break; 10511 // We should never see these here: 10512 case ResultWas::Info: 10513 case ResultWas::Warning: 10514 case ResultWas::Ok: 10515 case ResultWas::Unknown: 10516 case ResultWas::FailureBit: 10517 case ResultWas::Exception: 10518 elementName = "internalError"; 10519 break; 10520 } 10521 10522 XmlWriter::ScopedElement e = xml.scopedElement( elementName ); 10523 10524 xml.writeAttribute( "message"_sr, result.getExpression() ); 10525 xml.writeAttribute( "type"_sr, result.getTestMacroName() ); 10526 10527 ReusableStringStream rss; 10528 if ( result.getResultType() == ResultWas::ExplicitSkip ) { 10529 rss << "SKIPPED\n"; 10530 } else { 10531 rss << "FAILED" << ":\n"; 10532 if (result.hasExpression()) { 10533 rss << " "; 10534 rss << result.getExpressionInMacro(); 10535 rss << '\n'; 10536 } 10537 if (result.hasExpandedExpression()) { 10538 rss << "with expansion:\n"; 10539 rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n'; 10540 } 10541 } 10542 10543 if( result.hasMessage() ) 10544 rss << result.getMessage() << '\n'; 10545 for( auto const& msg : stats.infoMessages ) 10546 if( msg.type == ResultWas::Info ) 10547 rss << msg.message << '\n'; 10548 10549 rss << "at " << result.getSourceInfo(); 10550 xml.writeText( rss.str(), XmlFormatting::Newline ); 10551 } 10552 } 10553 10554} // end namespace Catch 10555 10556 10557 10558 10559#include <ostream> 10560 10561namespace Catch { 10562 void MultiReporter::updatePreferences(IEventListener const& reporterish) { 10563 m_preferences.shouldRedirectStdOut |= 10564 reporterish.getPreferences().shouldRedirectStdOut; 10565 m_preferences.shouldReportAllAssertions |= 10566 reporterish.getPreferences().shouldReportAllAssertions; 10567 } 10568 10569 void MultiReporter::addListener( IEventListenerPtr&& listener ) { 10570 updatePreferences(*listener); 10571 m_reporterLikes.insert(m_reporterLikes.begin() + m_insertedListeners, CATCH_MOVE(listener) ); 10572 ++m_insertedListeners; 10573 } 10574 10575 void MultiReporter::addReporter( IEventListenerPtr&& reporter ) { 10576 updatePreferences(*reporter); 10577 10578 // We will need to output the captured stdout if there are reporters 10579 // that do not want it captured. 10580 // We do not consider listeners, because it is generally assumed that 10581 // listeners are output-transparent, even though they can ask for stdout 10582 // capture to do something with it. 10583 m_haveNoncapturingReporters |= !reporter->getPreferences().shouldRedirectStdOut; 10584 10585 // Reporters can always be placed to the back without breaking the 10586 // reporting order 10587 m_reporterLikes.push_back( CATCH_MOVE( reporter ) ); 10588 } 10589 10590 void MultiReporter::noMatchingTestCases( StringRef unmatchedSpec ) { 10591 for ( auto& reporterish : m_reporterLikes ) { 10592 reporterish->noMatchingTestCases( unmatchedSpec ); 10593 } 10594 } 10595 10596 void MultiReporter::fatalErrorEncountered( StringRef error ) { 10597 for ( auto& reporterish : m_reporterLikes ) { 10598 reporterish->fatalErrorEncountered( error ); 10599 } 10600 } 10601 10602 void MultiReporter::reportInvalidTestSpec( StringRef arg ) { 10603 for ( auto& reporterish : m_reporterLikes ) { 10604 reporterish->reportInvalidTestSpec( arg ); 10605 } 10606 } 10607 10608 void MultiReporter::benchmarkPreparing( StringRef name ) { 10609 for (auto& reporterish : m_reporterLikes) { 10610 reporterish->benchmarkPreparing(name); 10611 } 10612 } 10613 void MultiReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { 10614 for ( auto& reporterish : m_reporterLikes ) { 10615 reporterish->benchmarkStarting( benchmarkInfo ); 10616 } 10617 } 10618 void MultiReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) { 10619 for ( auto& reporterish : m_reporterLikes ) { 10620 reporterish->benchmarkEnded( benchmarkStats ); 10621 } 10622 } 10623 10624 void MultiReporter::benchmarkFailed( StringRef error ) { 10625 for (auto& reporterish : m_reporterLikes) { 10626 reporterish->benchmarkFailed(error); 10627 } 10628 } 10629 10630 void MultiReporter::testRunStarting( TestRunInfo const& testRunInfo ) { 10631 for ( auto& reporterish : m_reporterLikes ) { 10632 reporterish->testRunStarting( testRunInfo ); 10633 } 10634 } 10635 10636 void MultiReporter::testCaseStarting( TestCaseInfo const& testInfo ) { 10637 for ( auto& reporterish : m_reporterLikes ) { 10638 reporterish->testCaseStarting( testInfo ); 10639 } 10640 } 10641 10642 void 10643 MultiReporter::testCasePartialStarting( TestCaseInfo const& testInfo, 10644 uint64_t partNumber ) { 10645 for ( auto& reporterish : m_reporterLikes ) { 10646 reporterish->testCasePartialStarting( testInfo, partNumber ); 10647 } 10648 } 10649 10650 void MultiReporter::sectionStarting( SectionInfo const& sectionInfo ) { 10651 for ( auto& reporterish : m_reporterLikes ) { 10652 reporterish->sectionStarting( sectionInfo ); 10653 } 10654 } 10655 10656 void MultiReporter::assertionStarting( AssertionInfo const& assertionInfo ) { 10657 for ( auto& reporterish : m_reporterLikes ) { 10658 reporterish->assertionStarting( assertionInfo ); 10659 } 10660 } 10661 10662 void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { 10663 const bool reportByDefault = 10664 assertionStats.assertionResult.getResultType() != ResultWas::Ok || 10665 m_config->includeSuccessfulResults(); 10666 10667 for ( auto & reporterish : m_reporterLikes ) { 10668 if ( reportByDefault || 10669 reporterish->getPreferences().shouldReportAllAssertions ) { 10670 reporterish->assertionEnded( assertionStats ); 10671 } 10672 } 10673 } 10674 10675 void MultiReporter::sectionEnded( SectionStats const& sectionStats ) { 10676 for ( auto& reporterish : m_reporterLikes ) { 10677 reporterish->sectionEnded( sectionStats ); 10678 } 10679 } 10680 10681 void MultiReporter::testCasePartialEnded( TestCaseStats const& testStats, 10682 uint64_t partNumber ) { 10683 if ( m_preferences.shouldRedirectStdOut && 10684 m_haveNoncapturingReporters ) { 10685 if ( !testStats.stdOut.empty() ) { 10686 Catch::cout() << testStats.stdOut << std::flush; 10687 } 10688 if ( !testStats.stdErr.empty() ) { 10689 Catch::cerr() << testStats.stdErr << std::flush; 10690 } 10691 } 10692 10693 for ( auto& reporterish : m_reporterLikes ) { 10694 reporterish->testCasePartialEnded( testStats, partNumber ); 10695 } 10696 } 10697 10698 void MultiReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { 10699 for ( auto& reporterish : m_reporterLikes ) { 10700 reporterish->testCaseEnded( testCaseStats ); 10701 } 10702 } 10703 10704 void MultiReporter::testRunEnded( TestRunStats const& testRunStats ) { 10705 for ( auto& reporterish : m_reporterLikes ) { 10706 reporterish->testRunEnded( testRunStats ); 10707 } 10708 } 10709 10710 10711 void MultiReporter::skipTest( TestCaseInfo const& testInfo ) { 10712 for ( auto& reporterish : m_reporterLikes ) { 10713 reporterish->skipTest( testInfo ); 10714 } 10715 } 10716 10717 void MultiReporter::listReporters(std::vector<ReporterDescription> const& descriptions) { 10718 for (auto& reporterish : m_reporterLikes) { 10719 reporterish->listReporters(descriptions); 10720 } 10721 } 10722 10723 void MultiReporter::listListeners( 10724 std::vector<ListenerDescription> const& descriptions ) { 10725 for ( auto& reporterish : m_reporterLikes ) { 10726 reporterish->listListeners( descriptions ); 10727 } 10728 } 10729 10730 void MultiReporter::listTests(std::vector<TestCaseHandle> const& tests) { 10731 for (auto& reporterish : m_reporterLikes) { 10732 reporterish->listTests(tests); 10733 } 10734 } 10735 10736 void MultiReporter::listTags(std::vector<TagInfo> const& tags) { 10737 for (auto& reporterish : m_reporterLikes) { 10738 reporterish->listTags(tags); 10739 } 10740 } 10741 10742} // end namespace Catch 10743 10744 10745 10746 10747 10748namespace Catch { 10749 namespace Detail { 10750 10751 void registerReporterImpl( std::string const& name, 10752 IReporterFactoryPtr reporterPtr ) { 10753 CATCH_TRY { 10754 getMutableRegistryHub().registerReporter( 10755 name, CATCH_MOVE( reporterPtr ) ); 10756 } 10757 CATCH_CATCH_ALL { 10758 // Do not throw when constructing global objects, instead 10759 // register the exception to be processed later 10760 getMutableRegistryHub().registerStartupException(); 10761 } 10762 } 10763 10764 void registerListenerImpl( Detail::unique_ptr<EventListenerFactory> listenerFactory ) { 10765 getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) ); 10766 } 10767 10768 10769 } // namespace Detail 10770} // namespace Catch 10771 10772 10773 10774 10775#include <map> 10776 10777namespace Catch { 10778 10779 namespace { 10780 std::string createMetadataString(IConfig const& config) { 10781 ReusableStringStream sstr; 10782 if ( config.testSpec().hasFilters() ) { 10783 sstr << "filters='" 10784 << config.testSpec() 10785 << "' "; 10786 } 10787 sstr << "rng-seed=" << config.rngSeed(); 10788 return sstr.str(); 10789 } 10790 } 10791 10792 void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) { 10793 CumulativeReporterBase::testRunStarting(testRunInfo); 10794 10795 xml.writeComment( createMetadataString( *m_config ) ); 10796 xml.startElement("testExecutions"); 10797 xml.writeAttribute("version"_sr, '1'); 10798 } 10799 10800 void SonarQubeReporter::writeRun( TestRunNode const& runNode ) { 10801 std::map<StringRef, std::vector<TestCaseNode const*>> testsPerFile; 10802 10803 for ( auto const& child : runNode.children ) { 10804 testsPerFile[child->value.testInfo->lineInfo.file].push_back( 10805 child.get() ); 10806 } 10807 10808 for ( auto const& kv : testsPerFile ) { 10809 writeTestFile( kv.first, kv.second ); 10810 } 10811 } 10812 10813 void SonarQubeReporter::writeTestFile(StringRef filename, std::vector<TestCaseNode const*> const& testCaseNodes) { 10814 XmlWriter::ScopedElement e = xml.scopedElement("file"); 10815 xml.writeAttribute("path"_sr, filename); 10816 10817 for (auto const& child : testCaseNodes) 10818 writeTestCase(*child); 10819 } 10820 10821 void SonarQubeReporter::writeTestCase(TestCaseNode const& testCaseNode) { 10822 // All test cases have exactly one section - which represents the 10823 // test case itself. That section may have 0-n nested sections 10824 assert(testCaseNode.children.size() == 1); 10825 SectionNode const& rootSection = *testCaseNode.children.front(); 10826 writeSection("", rootSection, testCaseNode.value.testInfo->okToFail()); 10827 } 10828 10829 void SonarQubeReporter::writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) { 10830 std::string name = trim(sectionNode.stats.sectionInfo.name); 10831 if (!rootName.empty()) 10832 name = rootName + '/' + name; 10833 10834 if ( sectionNode.hasAnyAssertions() 10835 || !sectionNode.stdOut.empty() 10836 || !sectionNode.stdErr.empty() ) { 10837 XmlWriter::ScopedElement e = xml.scopedElement("testCase"); 10838 xml.writeAttribute("name"_sr, name); 10839 xml.writeAttribute("duration"_sr, static_cast<long>(sectionNode.stats.durationInSeconds * 1000)); 10840 10841 writeAssertions(sectionNode, okToFail); 10842 } 10843 10844 for (auto const& childNode : sectionNode.childSections) 10845 writeSection(name, *childNode, okToFail); 10846 } 10847 10848 void SonarQubeReporter::writeAssertions(SectionNode const& sectionNode, bool okToFail) { 10849 for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) { 10850 if (assertionOrBenchmark.isAssertion()) { 10851 writeAssertion(assertionOrBenchmark.asAssertion(), okToFail); 10852 } 10853 } 10854 } 10855 10856 void SonarQubeReporter::writeAssertion(AssertionStats const& stats, bool okToFail) { 10857 AssertionResult const& result = stats.assertionResult; 10858 if ( !result.isOk() || 10859 result.getResultType() == ResultWas::ExplicitSkip ) { 10860 std::string elementName; 10861 if (okToFail) { 10862 elementName = "skipped"; 10863 } else { 10864 switch (result.getResultType()) { 10865 case ResultWas::ThrewException: 10866 case ResultWas::FatalErrorCondition: 10867 elementName = "error"; 10868 break; 10869 case ResultWas::ExplicitFailure: 10870 case ResultWas::ExpressionFailed: 10871 case ResultWas::DidntThrowException: 10872 elementName = "failure"; 10873 break; 10874 case ResultWas::ExplicitSkip: 10875 elementName = "skipped"; 10876 break; 10877 // We should never see these here: 10878 case ResultWas::Info: 10879 case ResultWas::Warning: 10880 case ResultWas::Ok: 10881 case ResultWas::Unknown: 10882 case ResultWas::FailureBit: 10883 case ResultWas::Exception: 10884 elementName = "internalError"; 10885 break; 10886 } 10887 } 10888 10889 XmlWriter::ScopedElement e = xml.scopedElement(elementName); 10890 10891 ReusableStringStream messageRss; 10892 messageRss << result.getTestMacroName() << '(' << result.getExpression() << ')'; 10893 xml.writeAttribute("message"_sr, messageRss.str()); 10894 10895 ReusableStringStream textRss; 10896 if ( result.getResultType() == ResultWas::ExplicitSkip ) { 10897 textRss << "SKIPPED\n"; 10898 } else { 10899 textRss << "FAILED:\n"; 10900 if (result.hasExpression()) { 10901 textRss << '\t' << result.getExpressionInMacro() << '\n'; 10902 } 10903 if (result.hasExpandedExpression()) { 10904 textRss << "with expansion:\n\t" << result.getExpandedExpression() << '\n'; 10905 } 10906 } 10907 10908 if (result.hasMessage()) 10909 textRss << result.getMessage() << '\n'; 10910 10911 for (auto const& msg : stats.infoMessages) 10912 if (msg.type == ResultWas::Info) 10913 textRss << msg.message << '\n'; 10914 10915 textRss << "at " << result.getSourceInfo(); 10916 xml.writeText(textRss.str(), XmlFormatting::Newline); 10917 } 10918 } 10919 10920} // end namespace Catch 10921 10922 10923 10924namespace Catch { 10925 10926 StreamingReporterBase::~StreamingReporterBase() = default; 10927 10928 void 10929 StreamingReporterBase::testRunStarting( TestRunInfo const& _testRunInfo ) { 10930 currentTestRunInfo = _testRunInfo; 10931 } 10932 10933 void StreamingReporterBase::testRunEnded( TestRunStats const& ) { 10934 currentTestCaseInfo = nullptr; 10935 } 10936 10937} // end namespace Catch 10938 10939 10940 10941#include <algorithm> 10942#include <ostream> 10943 10944namespace Catch { 10945 10946 namespace { 10947 // Yes, this has to be outside the class and namespaced by naming. 10948 // Making older compiler happy is hard. 10949 static constexpr StringRef tapFailedString = "not ok"_sr; 10950 static constexpr StringRef tapPassedString = "ok"_sr; 10951 static constexpr Colour::Code tapDimColour = Colour::FileName; 10952 10953 class TapAssertionPrinter { 10954 public: 10955 TapAssertionPrinter& operator= (TapAssertionPrinter const&) = delete; 10956 TapAssertionPrinter(TapAssertionPrinter const&) = delete; 10957 TapAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, std::size_t _counter, ColourImpl* colour_) 10958 : stream(_stream) 10959 , result(_stats.assertionResult) 10960 , messages(_stats.infoMessages) 10961 , itMessage(_stats.infoMessages.begin()) 10962 , printInfoMessages(true) 10963 , counter(_counter) 10964 , colourImpl( colour_ ) {} 10965 10966 void print() { 10967 itMessage = messages.begin(); 10968 10969 switch (result.getResultType()) { 10970 case ResultWas::Ok: 10971 printResultType(tapPassedString); 10972 printOriginalExpression(); 10973 printReconstructedExpression(); 10974 if (!result.hasExpression()) 10975 printRemainingMessages(Colour::None); 10976 else 10977 printRemainingMessages(); 10978 break; 10979 case ResultWas::ExpressionFailed: 10980 if (result.isOk()) { 10981 printResultType(tapPassedString); 10982 } else { 10983 printResultType(tapFailedString); 10984 } 10985 printOriginalExpression(); 10986 printReconstructedExpression(); 10987 if (result.isOk()) { 10988 printIssue(" # TODO"); 10989 } 10990 printRemainingMessages(); 10991 break; 10992 case ResultWas::ThrewException: 10993 printResultType(tapFailedString); 10994 printIssue("unexpected exception with message:"_sr); 10995 printMessage(); 10996 printExpressionWas(); 10997 printRemainingMessages(); 10998 break; 10999 case ResultWas::FatalErrorCondition: 11000 printResultType(tapFailedString); 11001 printIssue("fatal error condition with message:"_sr); 11002 printMessage(); 11003 printExpressionWas(); 11004 printRemainingMessages(); 11005 break; 11006 case ResultWas::DidntThrowException: 11007 printResultType(tapFailedString); 11008 printIssue("expected exception, got none"_sr); 11009 printExpressionWas(); 11010 printRemainingMessages(); 11011 break; 11012 case ResultWas::Info: 11013 printResultType("info"_sr); 11014 printMessage(); 11015 printRemainingMessages(); 11016 break; 11017 case ResultWas::Warning: 11018 printResultType("warning"_sr); 11019 printMessage(); 11020 printRemainingMessages(); 11021 break; 11022 case ResultWas::ExplicitFailure: 11023 printResultType(tapFailedString); 11024 printIssue("explicitly"_sr); 11025 printRemainingMessages(Colour::None); 11026 break; 11027 case ResultWas::ExplicitSkip: 11028 printResultType(tapPassedString); 11029 printIssue(" # SKIP"_sr); 11030 printMessage(); 11031 printRemainingMessages(); 11032 break; 11033 // These cases are here to prevent compiler warnings 11034 case ResultWas::Unknown: 11035 case ResultWas::FailureBit: 11036 case ResultWas::Exception: 11037 printResultType("** internal error **"_sr); 11038 break; 11039 } 11040 } 11041 11042 private: 11043 void printResultType(StringRef passOrFail) const { 11044 if (!passOrFail.empty()) { 11045 stream << passOrFail << ' ' << counter << " -"; 11046 } 11047 } 11048 11049 void printIssue(StringRef issue) const { 11050 stream << ' ' << issue; 11051 } 11052 11053 void printExpressionWas() { 11054 if (result.hasExpression()) { 11055 stream << ';'; 11056 stream << colourImpl->guardColour( tapDimColour ) 11057 << " expression was:"; 11058 printOriginalExpression(); 11059 } 11060 } 11061 11062 void printOriginalExpression() const { 11063 if (result.hasExpression()) { 11064 stream << ' ' << result.getExpression(); 11065 } 11066 } 11067 11068 void printReconstructedExpression() const { 11069 if (result.hasExpandedExpression()) { 11070 stream << colourImpl->guardColour( tapDimColour ) << " for: "; 11071 11072 std::string expr = result.getExpandedExpression(); 11073 std::replace(expr.begin(), expr.end(), '\n', ' '); 11074 stream << expr; 11075 } 11076 } 11077 11078 void printMessage() { 11079 if (itMessage != messages.end()) { 11080 stream << " '" << itMessage->message << '\''; 11081 ++itMessage; 11082 } 11083 } 11084 11085 void printRemainingMessages(Colour::Code colour = tapDimColour) { 11086 if (itMessage == messages.end()) { 11087 return; 11088 } 11089 11090 // using messages.end() directly (or auto) yields compilation error: 11091 std::vector<MessageInfo>::const_iterator itEnd = messages.end(); 11092 const std::size_t N = static_cast<std::size_t>(itEnd - itMessage); 11093 11094 stream << colourImpl->guardColour( colour ) << " with " 11095 << pluralise( N, "message"_sr ) << ':'; 11096 11097 for (; itMessage != itEnd; ) { 11098 // If this assertion is a warning ignore any INFO messages 11099 if (printInfoMessages || itMessage->type != ResultWas::Info) { 11100 stream << " '" << itMessage->message << '\''; 11101 if (++itMessage != itEnd) { 11102 stream << colourImpl->guardColour(tapDimColour) << " and"; 11103 } 11104 } 11105 } 11106 } 11107 11108 private: 11109 std::ostream& stream; 11110 AssertionResult const& result; 11111 std::vector<MessageInfo> const& messages; 11112 std::vector<MessageInfo>::const_iterator itMessage; 11113 bool printInfoMessages; 11114 std::size_t counter; 11115 ColourImpl* colourImpl; 11116 }; 11117 11118 } // End anonymous namespace 11119 11120 void TAPReporter::testRunStarting( TestRunInfo const& ) { 11121 if ( m_config->testSpec().hasFilters() ) { 11122 m_stream << "# filters: " << m_config->testSpec() << '\n'; 11123 } 11124 m_stream << "# rng-seed: " << m_config->rngSeed() << '\n'; 11125 } 11126 11127 void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) { 11128 m_stream << "# No test cases matched '" << unmatchedSpec << "'\n"; 11129 } 11130 11131 void TAPReporter::assertionEnded(AssertionStats const& _assertionStats) { 11132 ++counter; 11133 11134 m_stream << "# " << currentTestCaseInfo->name << '\n'; 11135 TapAssertionPrinter printer(m_stream, _assertionStats, counter, m_colour.get()); 11136 printer.print(); 11137 11138 m_stream << '\n' << std::flush; 11139 } 11140 11141 void TAPReporter::testRunEnded(TestRunStats const& _testRunStats) { 11142 m_stream << "1.." << _testRunStats.totals.assertions.total(); 11143 if (_testRunStats.totals.testCases.total() == 0) { 11144 m_stream << " # Skipped: No tests ran."; 11145 } 11146 m_stream << "\n\n" << std::flush; 11147 StreamingReporterBase::testRunEnded(_testRunStats); 11148 } 11149 11150 11151 11152 11153} // end namespace Catch 11154 11155 11156 11157 11158#include <cassert> 11159#include <ostream> 11160 11161namespace Catch { 11162 11163 namespace { 11164 // if string has a : in first line will set indent to follow it on 11165 // subsequent lines 11166 void printHeaderString(std::ostream& os, std::string const& _string, std::size_t indent = 0) { 11167 std::size_t i = _string.find(": "); 11168 if (i != std::string::npos) 11169 i += 2; 11170 else 11171 i = 0; 11172 os << TextFlow::Column(_string) 11173 .indent(indent + i) 11174 .initialIndent(indent) << '\n'; 11175 } 11176 11177 std::string escape(StringRef str) { 11178 std::string escaped = static_cast<std::string>(str); 11179 replaceInPlace(escaped, "|", "||"); 11180 replaceInPlace(escaped, "'", "|'"); 11181 replaceInPlace(escaped, "\n", "|n"); 11182 replaceInPlace(escaped, "\r", "|r"); 11183 replaceInPlace(escaped, "[", "|["); 11184 replaceInPlace(escaped, "]", "|]"); 11185 return escaped; 11186 } 11187 } // end anonymous namespace 11188 11189 11190 TeamCityReporter::~TeamCityReporter() = default; 11191 11192 void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { 11193 m_stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name ) 11194 << "']\n"; 11195 } 11196 11197 void TeamCityReporter::testRunEnded( TestRunStats const& runStats ) { 11198 m_stream << "##teamcity[testSuiteFinished name='" 11199 << escape( runStats.runInfo.name ) << "']\n"; 11200 } 11201 11202 void TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) { 11203 AssertionResult const& result = assertionStats.assertionResult; 11204 if ( !result.isOk() || 11205 result.getResultType() == ResultWas::ExplicitSkip ) { 11206 11207 ReusableStringStream msg; 11208 if (!m_headerPrintedForThisSection) 11209 printSectionHeader(msg.get()); 11210 m_headerPrintedForThisSection = true; 11211 11212 msg << result.getSourceInfo() << '\n'; 11213 11214 switch (result.getResultType()) { 11215 case ResultWas::ExpressionFailed: 11216 msg << "expression failed"; 11217 break; 11218 case ResultWas::ThrewException: 11219 msg << "unexpected exception"; 11220 break; 11221 case ResultWas::FatalErrorCondition: 11222 msg << "fatal error condition"; 11223 break; 11224 case ResultWas::DidntThrowException: 11225 msg << "no exception was thrown where one was expected"; 11226 break; 11227 case ResultWas::ExplicitFailure: 11228 msg << "explicit failure"; 11229 break; 11230 case ResultWas::ExplicitSkip: 11231 msg << "explicit skip"; 11232 break; 11233 11234 // We shouldn't get here because of the isOk() test 11235 case ResultWas::Ok: 11236 case ResultWas::Info: 11237 case ResultWas::Warning: 11238 CATCH_ERROR("Internal error in TeamCity reporter"); 11239 // These cases are here to prevent compiler warnings 11240 case ResultWas::Unknown: 11241 case ResultWas::FailureBit: 11242 case ResultWas::Exception: 11243 CATCH_ERROR("Not implemented"); 11244 } 11245 if (assertionStats.infoMessages.size() == 1) 11246 msg << " with message:"; 11247 if (assertionStats.infoMessages.size() > 1) 11248 msg << " with messages:"; 11249 for (auto const& messageInfo : assertionStats.infoMessages) 11250 msg << "\n \"" << messageInfo.message << '"'; 11251 11252 11253 if (result.hasExpression()) { 11254 msg << 11255 "\n " << result.getExpressionInMacro() << "\n" 11256 "with expansion:\n" 11257 " " << result.getExpandedExpression() << '\n'; 11258 } 11259 11260 if ( result.getResultType() == ResultWas::ExplicitSkip ) { 11261 m_stream << "##teamcity[testIgnored"; 11262 } else if ( currentTestCaseInfo->okToFail() ) { 11263 msg << "- failure ignore as test marked as 'ok to fail'\n"; 11264 m_stream << "##teamcity[testIgnored"; 11265 } else { 11266 m_stream << "##teamcity[testFailed"; 11267 } 11268 m_stream << " name='" << escape( currentTestCaseInfo->name ) << '\'' 11269 << " message='" << escape( msg.str() ) << '\'' << "]\n"; 11270 } 11271 m_stream.flush(); 11272 } 11273 11274 void TeamCityReporter::testCaseStarting(TestCaseInfo const& testInfo) { 11275 m_testTimer.start(); 11276 StreamingReporterBase::testCaseStarting(testInfo); 11277 m_stream << "##teamcity[testStarted name='" 11278 << escape(testInfo.name) << "']\n"; 11279 m_stream.flush(); 11280 } 11281 11282 void TeamCityReporter::testCaseEnded(TestCaseStats const& testCaseStats) { 11283 StreamingReporterBase::testCaseEnded(testCaseStats); 11284 auto const& testCaseInfo = *testCaseStats.testInfo; 11285 if (!testCaseStats.stdOut.empty()) 11286 m_stream << "##teamcity[testStdOut name='" 11287 << escape(testCaseInfo.name) 11288 << "' out='" << escape(testCaseStats.stdOut) << "']\n"; 11289 if (!testCaseStats.stdErr.empty()) 11290 m_stream << "##teamcity[testStdErr name='" 11291 << escape(testCaseInfo.name) 11292 << "' out='" << escape(testCaseStats.stdErr) << "']\n"; 11293 m_stream << "##teamcity[testFinished name='" 11294 << escape(testCaseInfo.name) << "' duration='" 11295 << m_testTimer.getElapsedMilliseconds() << "']\n"; 11296 m_stream.flush(); 11297 } 11298 11299 void TeamCityReporter::printSectionHeader(std::ostream& os) { 11300 assert(!m_sectionStack.empty()); 11301 11302 if (m_sectionStack.size() > 1) { 11303 os << lineOfChars('-') << '\n'; 11304 11305 std::vector<SectionInfo>::const_iterator 11306 it = m_sectionStack.begin() + 1, // Skip first section (test case) 11307 itEnd = m_sectionStack.end(); 11308 for (; it != itEnd; ++it) 11309 printHeaderString(os, it->name); 11310 os << lineOfChars('-') << '\n'; 11311 } 11312 11313 SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; 11314 11315 os << lineInfo << '\n'; 11316 os << lineOfChars('.') << "\n\n"; 11317 } 11318 11319} // end namespace Catch 11320 11321 11322 11323 11324#if defined(_MSC_VER) 11325#pragma warning(push) 11326#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch 11327 // Note that 4062 (not all labels are handled 11328 // and default is missing) is enabled 11329#endif 11330 11331namespace Catch { 11332 XmlReporter::XmlReporter( ReporterConfig&& _config ) 11333 : StreamingReporterBase( CATCH_MOVE(_config) ), 11334 m_xml(m_stream) 11335 { 11336 m_preferences.shouldRedirectStdOut = true; 11337 m_preferences.shouldReportAllAssertions = true; 11338 } 11339 11340 XmlReporter::~XmlReporter() = default; 11341 11342 std::string XmlReporter::getDescription() { 11343 return "Reports test results as an XML document"; 11344 } 11345 11346 std::string XmlReporter::getStylesheetRef() const { 11347 return std::string(); 11348 } 11349 11350 void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { 11351 m_xml 11352 .writeAttribute( "filename"_sr, sourceInfo.file ) 11353 .writeAttribute( "line"_sr, sourceInfo.line ); 11354 } 11355 11356 void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { 11357 StreamingReporterBase::testRunStarting( testInfo ); 11358 std::string stylesheetRef = getStylesheetRef(); 11359 if( !stylesheetRef.empty() ) 11360 m_xml.writeStylesheetRef( stylesheetRef ); 11361 m_xml.startElement("Catch2TestRun") 11362 .writeAttribute("name"_sr, m_config->name()) 11363 .writeAttribute("rng-seed"_sr, m_config->rngSeed()) 11364 .writeAttribute("xml-format-version"_sr, 3) 11365 .writeAttribute("catch2-version"_sr, libraryVersion()); 11366 if ( m_config->testSpec().hasFilters() ) { 11367 m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); 11368 } 11369 } 11370 11371 void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { 11372 StreamingReporterBase::testCaseStarting(testInfo); 11373 m_xml.startElement( "TestCase" ) 11374 .writeAttribute( "name"_sr, trim( StringRef(testInfo.name) ) ) 11375 .writeAttribute( "tags"_sr, testInfo.tagsAsString() ); 11376 11377 writeSourceInfo( testInfo.lineInfo ); 11378 11379 if ( m_config->showDurations() == ShowDurations::Always ) 11380 m_testCaseTimer.start(); 11381 m_xml.ensureTagClosed(); 11382 } 11383 11384 void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { 11385 StreamingReporterBase::sectionStarting( sectionInfo ); 11386 if( m_sectionDepth++ > 0 ) { 11387 m_xml.startElement( "Section" ) 11388 .writeAttribute( "name"_sr, trim( StringRef(sectionInfo.name) ) ); 11389 writeSourceInfo( sectionInfo.lineInfo ); 11390 m_xml.ensureTagClosed(); 11391 } 11392 } 11393 11394 void XmlReporter::assertionStarting( AssertionInfo const& ) { } 11395 11396 void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { 11397 11398 AssertionResult const& result = assertionStats.assertionResult; 11399 11400 bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); 11401 11402 if( includeResults || result.getResultType() == ResultWas::Warning ) { 11403 // Print any info messages in <Info> tags. 11404 for( auto const& msg : assertionStats.infoMessages ) { 11405 if( msg.type == ResultWas::Info && includeResults ) { 11406 auto t = m_xml.scopedElement( "Info" ); 11407 writeSourceInfo( msg.lineInfo ); 11408 t.writeText( msg.message ); 11409 } else if ( msg.type == ResultWas::Warning ) { 11410 auto t = m_xml.scopedElement( "Warning" ); 11411 writeSourceInfo( msg.lineInfo ); 11412 t.writeText( msg.message ); 11413 } 11414 } 11415 } 11416 11417 // Drop out if result was successful but we're not printing them. 11418 if ( !includeResults && result.getResultType() != ResultWas::Warning && 11419 result.getResultType() != ResultWas::ExplicitSkip ) { 11420 return; 11421 } 11422 11423 // Print the expression if there is one. 11424 if( result.hasExpression() ) { 11425 m_xml.startElement( "Expression" ) 11426 .writeAttribute( "success"_sr, result.succeeded() ) 11427 .writeAttribute( "type"_sr, result.getTestMacroName() ); 11428 11429 writeSourceInfo( result.getSourceInfo() ); 11430 11431 m_xml.scopedElement( "Original" ) 11432 .writeText( result.getExpression() ); 11433 m_xml.scopedElement( "Expanded" ) 11434 .writeText( result.getExpandedExpression() ); 11435 } 11436 11437 // And... Print a result applicable to each result type. 11438 switch( result.getResultType() ) { 11439 case ResultWas::ThrewException: 11440 m_xml.startElement( "Exception" ); 11441 writeSourceInfo( result.getSourceInfo() ); 11442 m_xml.writeText( result.getMessage() ); 11443 m_xml.endElement(); 11444 break; 11445 case ResultWas::FatalErrorCondition: 11446 m_xml.startElement( "FatalErrorCondition" ); 11447 writeSourceInfo( result.getSourceInfo() ); 11448 m_xml.writeText( result.getMessage() ); 11449 m_xml.endElement(); 11450 break; 11451 case ResultWas::Info: 11452 m_xml.scopedElement( "Info" ) 11453 .writeText( result.getMessage() ); 11454 break; 11455 case ResultWas::Warning: 11456 // Warning will already have been written 11457 break; 11458 case ResultWas::ExplicitFailure: 11459 m_xml.startElement( "Failure" ); 11460 writeSourceInfo( result.getSourceInfo() ); 11461 m_xml.writeText( result.getMessage() ); 11462 m_xml.endElement(); 11463 break; 11464 case ResultWas::ExplicitSkip: 11465 m_xml.startElement( "Skip" ); 11466 writeSourceInfo( result.getSourceInfo() ); 11467 m_xml.writeText( result.getMessage() ); 11468 m_xml.endElement(); 11469 break; 11470 default: 11471 break; 11472 } 11473 11474 if( result.hasExpression() ) 11475 m_xml.endElement(); 11476 } 11477 11478 void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { 11479 StreamingReporterBase::sectionEnded( sectionStats ); 11480 if ( --m_sectionDepth > 0 ) { 11481 { 11482 XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); 11483 e.writeAttribute( "successes"_sr, sectionStats.assertions.passed ); 11484 e.writeAttribute( "failures"_sr, sectionStats.assertions.failed ); 11485 e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk ); 11486 e.writeAttribute( "skipped"_sr, sectionStats.assertions.skipped > 0 ); 11487 11488 if ( m_config->showDurations() == ShowDurations::Always ) 11489 e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds ); 11490 } 11491 // Ends assertion tag 11492 m_xml.endElement(); 11493 } 11494 } 11495 11496 void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { 11497 StreamingReporterBase::testCaseEnded( testCaseStats ); 11498 XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); 11499 e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() ); 11500 e.writeAttribute( "skips"_sr, testCaseStats.totals.assertions.skipped ); 11501 11502 if ( m_config->showDurations() == ShowDurations::Always ) 11503 e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() ); 11504 if( !testCaseStats.stdOut.empty() ) 11505 m_xml.scopedElement( "StdOut" ).writeText( trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline ); 11506 if( !testCaseStats.stdErr.empty() ) 11507 m_xml.scopedElement( "StdErr" ).writeText( trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline ); 11508 11509 m_xml.endElement(); 11510 } 11511 11512 void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { 11513 StreamingReporterBase::testRunEnded( testRunStats ); 11514 m_xml.scopedElement( "OverallResults" ) 11515 .writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed ) 11516 .writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed ) 11517 .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk ) 11518 .writeAttribute( "skips"_sr, testRunStats.totals.assertions.skipped ); 11519 m_xml.scopedElement( "OverallResultsCases") 11520 .writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed ) 11521 .writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed ) 11522 .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk ) 11523 .writeAttribute( "skips"_sr, testRunStats.totals.testCases.skipped ); 11524 m_xml.endElement(); 11525 } 11526 11527 void XmlReporter::benchmarkPreparing( StringRef name ) { 11528 m_xml.startElement("BenchmarkResults") 11529 .writeAttribute("name"_sr, name); 11530 } 11531 11532 void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { 11533 m_xml.writeAttribute("samples"_sr, info.samples) 11534 .writeAttribute("resamples"_sr, info.resamples) 11535 .writeAttribute("iterations"_sr, info.iterations) 11536 .writeAttribute("clockResolution"_sr, info.clockResolution) 11537 .writeAttribute("estimatedDuration"_sr, info.estimatedDuration) 11538 .writeComment("All values in nano seconds"_sr); 11539 } 11540 11541 void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) { 11542 m_xml.scopedElement("mean") 11543 .writeAttribute("value"_sr, benchmarkStats.mean.point.count()) 11544 .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count()) 11545 .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count()) 11546 .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval); 11547 m_xml.scopedElement("standardDeviation") 11548 .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count()) 11549 .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count()) 11550 .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count()) 11551 .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval); 11552 m_xml.scopedElement("outliers") 11553 .writeAttribute("variance"_sr, benchmarkStats.outlierVariance) 11554 .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild) 11555 .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe) 11556 .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild) 11557 .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe); 11558 m_xml.endElement(); 11559 } 11560 11561 void XmlReporter::benchmarkFailed(StringRef error) { 11562 m_xml.scopedElement("failed"). 11563 writeAttribute("message"_sr, error); 11564 m_xml.endElement(); 11565 } 11566 11567 void XmlReporter::listReporters(std::vector<ReporterDescription> const& descriptions) { 11568 auto outerTag = m_xml.scopedElement("AvailableReporters"); 11569 for (auto const& reporter : descriptions) { 11570 auto inner = m_xml.scopedElement("Reporter"); 11571 m_xml.startElement("Name", XmlFormatting::Indent) 11572 .writeText(reporter.name, XmlFormatting::None) 11573 .endElement(XmlFormatting::Newline); 11574 m_xml.startElement("Description", XmlFormatting::Indent) 11575 .writeText(reporter.description, XmlFormatting::None) 11576 .endElement(XmlFormatting::Newline); 11577 } 11578 } 11579 11580 void XmlReporter::listListeners(std::vector<ListenerDescription> const& descriptions) { 11581 auto outerTag = m_xml.scopedElement( "RegisteredListeners" ); 11582 for ( auto const& listener : descriptions ) { 11583 auto inner = m_xml.scopedElement( "Listener" ); 11584 m_xml.startElement( "Name", XmlFormatting::Indent ) 11585 .writeText( listener.name, XmlFormatting::None ) 11586 .endElement( XmlFormatting::Newline ); 11587 m_xml.startElement( "Description", XmlFormatting::Indent ) 11588 .writeText( listener.description, XmlFormatting::None ) 11589 .endElement( XmlFormatting::Newline ); 11590 } 11591 } 11592 11593 void XmlReporter::listTests(std::vector<TestCaseHandle> const& tests) { 11594 auto outerTag = m_xml.scopedElement("MatchingTests"); 11595 for (auto const& test : tests) { 11596 auto innerTag = m_xml.scopedElement("TestCase"); 11597 auto const& testInfo = test.getTestCaseInfo(); 11598 m_xml.startElement("Name", XmlFormatting::Indent) 11599 .writeText(testInfo.name, XmlFormatting::None) 11600 .endElement(XmlFormatting::Newline); 11601 m_xml.startElement("ClassName", XmlFormatting::Indent) 11602 .writeText(testInfo.className, XmlFormatting::None) 11603 .endElement(XmlFormatting::Newline); 11604 m_xml.startElement("Tags", XmlFormatting::Indent) 11605 .writeText(testInfo.tagsAsString(), XmlFormatting::None) 11606 .endElement(XmlFormatting::Newline); 11607 11608 auto sourceTag = m_xml.scopedElement("SourceInfo"); 11609 m_xml.startElement("File", XmlFormatting::Indent) 11610 .writeText(testInfo.lineInfo.file, XmlFormatting::None) 11611 .endElement(XmlFormatting::Newline); 11612 m_xml.startElement("Line", XmlFormatting::Indent) 11613 .writeText(std::to_string(testInfo.lineInfo.line), XmlFormatting::None) 11614 .endElement(XmlFormatting::Newline); 11615 } 11616 } 11617 11618 void XmlReporter::listTags(std::vector<TagInfo> const& tags) { 11619 auto outerTag = m_xml.scopedElement("TagsFromMatchingTests"); 11620 for (auto const& tag : tags) { 11621 auto innerTag = m_xml.scopedElement("Tag"); 11622 m_xml.startElement("Count", XmlFormatting::Indent) 11623 .writeText(std::to_string(tag.count), XmlFormatting::None) 11624 .endElement(XmlFormatting::Newline); 11625 auto aliasTag = m_xml.scopedElement("Aliases"); 11626 for (auto const& alias : tag.spellings) { 11627 m_xml.startElement("Alias", XmlFormatting::Indent) 11628 .writeText(alias, XmlFormatting::None) 11629 .endElement(XmlFormatting::Newline); 11630 } 11631 } 11632 } 11633 11634} // end namespace Catch 11635 11636#if defined(_MSC_VER) 11637#pragma warning(pop) 11638#endif