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