diff --git a/CMakeLists.txt b/CMakeLists.txt index 01024e5cf8..5087361dfc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ endif() project(Catch2 - VERSION 3.0.0 # CML version placeholder, don't delete + VERSION 3.0.1 # CML version placeholder, don't delete LANGUAGES CXX HOMEPAGE_URL "https://github.com/catchorg/Catch2" DESCRIPTION "A modern, C++-native, unit test framework." diff --git a/docs/command-line.md b/docs/command-line.md index 4b9ec7a02a..86b06aa0f3 100644 --- a/docs/command-line.md +++ b/docs/command-line.md @@ -145,7 +145,7 @@ hardcoded into Catch2. Currently there are only 2, _Note that the reporter might still check the X-prefixed options for validity, and throw an error if they are wrong._ -> Support for passing arguments to reporters through the `-r`, `--reporter` flag was introduced in Catch2 X.Y.Z +> Support for passing arguments to reporters through the `-r`, `--reporter` flag was introduced in Catch2 3.0.1 There are multiple built-in reporters, you can see what they do by using the [`--list-reporter`](command-line.md#listing-available-tests-tags-or-reporters) @@ -160,7 +160,7 @@ reporter can be provided without the output-file part of reporter spec. This reporter will use the "default" output destination, based on the [`-o`, `--out`](#sending-output-to-a-file) option. -> Support for using multiple different reporters at the same time was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 X.Y.Z +> Support for using multiple different reporters at the same time was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 3.0.1 _Note: There is currently no way to escape `::` in the reporter spec, @@ -204,9 +204,9 @@ Sometimes this results in a flood of failure messages and you'd rather just see --list-listeners ``` -> The `--list*` options became customizable through reporters in Catch2 X.Y.Z +> The `--list*` options became customizable through reporters in Catch2 3.0.1 -> The `--list-listeners` option was added in Catch2 X.Y.Z +> The `--list-listeners` option was added in Catch2 3.0.1 `--list-tests` lists all registered tests matching specified test spec. Usually this listing also includes tags, and potentially also other @@ -230,7 +230,7 @@ Use this option to send all output to a file, instead of stdout. You can use `-` as the filename to explicitly send the output to stdout (this is useful e.g. when using multiple reporters). -> Support for `-` as the filename was introduced in Catch2 X.Y.Z +> Support for `-` as the filename was introduced in Catch2 3.0.1 Filenames starting with "%" (percent symbol) are reserved by Catch2 for meta purposes, e.g. using `%debug` as the filename opens stream that @@ -242,7 +242,7 @@ Catch2 currently recognizes 3 meta streams: * `%stdout` - writes to stdout * `%stderr` - writes to stderr -> Support for `%stdout` and `%stderr` was introduced in Catch2 X.Y.Z +> Support for `%stdout` and `%stderr` was introduced in Catch2 3.0.1 @@ -290,7 +290,7 @@ There are currently two warnings implemented: // not match any tests. ``` -> `UnmatchedTestSpec` was introduced in Catch2 X.Y.Z. +> `UnmatchedTestSpec` was introduced in Catch2 3.0.1. @@ -388,7 +388,7 @@ either before running any tests, after running all tests - or both, depending on ## Skip all benchmarks
--skip-benchmarks
-> [Introduced](https://github.com/catchorg/Catch2/issues/2408) in Catch X.Y.Z. +> [Introduced](https://github.com/catchorg/Catch2/issues/2408) in Catch2 3.0.1. This flag tells Catch2 to skip running all benchmarks. Benchmarks in this case mean code blocks in `BENCHMARK` and `BENCHMARK_ADVANCED` macros, not @@ -507,7 +507,7 @@ So, for example, tests within the file `~\Dev\MyProject\Ferrets.cpp` would be t ## Override output colouring
--colour-mode <ansi|win32|none|default>
-> The `--colour-mode` option replaced the old `--colour` option in Catch2 X.Y.Z +> The `--colour-mode` option replaced the old `--colour` option in Catch2 3.0.1 Catch2 support two different ways of colouring terminal output, and by @@ -531,7 +531,7 @@ when writing to a file ## Test Sharding
--shard-count <#number of shards>, --shard-index <#shard index to run>
-> [Introduced](https://github.com/catchorg/Catch2/pull/2257) in Catch2 X.Y.Z. +> [Introduced](https://github.com/catchorg/Catch2/pull/2257) in Catch2 3.0.1. When `--shard-count <#number of shards>` is used, the tests to execute will be split evenly in to the given number of sets, identified by indicies starting at 0. The tests in the set given by `--shard-index <#shard index to run>` will be executed. @@ -544,7 +544,7 @@ This is useful when you want to split test execution across multiple processes, ## Allow running the binary without tests
--allow-running-no-tests
-> Introduced in Catch2 X.Y.Z. +> Introduced in Catch2 3.0.1. By default, Catch2 test binaries return non-0 exit code if no tests were run, e.g. if the binary was compiled with no tests, or the provided test diff --git a/docs/configuration.md b/docs/configuration.md index dc7178c75d..d546334342 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -101,7 +101,7 @@ is equivalent with the out-of-the-box experience. When `CATCH_CONFIG_BAZEL_SUPPORT` is defined, Catch2 will register a `JUnit` reporter writing to a path pointed by `XML_OUTPUT_FILE` provided by Bazel. -> `CATCH_CONFIG_BAZEL_SUPPORT` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 X.Y.Z. +> `CATCH_CONFIG_BAZEL_SUPPORT` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 3.0.1. ## C++11 toggles diff --git a/docs/matchers.md b/docs/matchers.md index e76d8490af..3d0f8f110e 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -248,7 +248,7 @@ Note that `DerivedException` in the example above has to derive from ### Generic range Matchers -> Generic range matchers were introduced in Catch2 X.Y.Z +> Generic range matchers were introduced in Catch2 3.0.1 Catch2 also provides some matchers that use the new style matchers definitions to handle generic range-like types. These are: @@ -350,7 +350,7 @@ style matchers arbitrarily. ## Writing custom matchers (new style) -> New style matchers were introduced in Catch2 X.Y.Z +> New style matchers were introduced in Catch2 3.0.1 To create a new-style matcher, you have to create your own type that derives from `Catch::Matchers::MatcherGenericBase`. Your type has to diff --git a/docs/other-macros.md b/docs/other-macros.md index ce33c1d851..24a0fb6e6f 100644 --- a/docs/other-macros.md +++ b/docs/other-macros.md @@ -15,7 +15,7 @@ stringification machinery to the _expr_ and records the result. As with evaluates to `true`. `CHECKED_ELSE( expr )` work similarly, but the block is entered only if the _expr_ evaluated to `false`. -> `CHECKED_X` macros were changed to not count as failure in Catch2 X.Y.Z. +> `CHECKED_X` macros were changed to not count as failure in Catch2 3.0.1. Example: ```cpp @@ -77,7 +77,7 @@ TEST_CASE("STATIC_REQUIRE showcase", "[traits]") { } ``` -> `STATIC_CHECK` was [introduced](https://github.com/catchorg/Catch2/pull/2318) in Catch2 X.Y.Z. +> `STATIC_CHECK` was [introduced](https://github.com/catchorg/Catch2/pull/2318) in Catch2 3.0.1. `STATIC_CHECK( expr )` is equivalent to `STATIC_REQUIRE( expr )`, with the difference that when `CATCH_CONFIG_RUNTIME_STATIC_REQUIRE` is defined, it diff --git a/docs/release-notes.md b/docs/release-notes.md index 1b0188b4b5..3a5de2762e 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,7 +2,7 @@ # Release notes **Contents**
-[3.0.1 (in progress)](#301-in-progress)
+[3.0.1](#301)
[2.13.7](#2137)
[2.13.6](#2136)
[2.13.5](#2135)
@@ -49,7 +49,7 @@ [Even Older versions](#even-older-versions)
-## 3.0.1 (in progress) +## 3.0.1 **Catch2 now uses statically compiled library as its distribution model. This also means that to get all of Catch2's functionality in a test file, @@ -206,6 +206,7 @@ v3 releases. * The cumulative reporter base stores benchmark results alongside assertion results * Catch2's SE handling should no longer interferes with ASan on Windows (#2334) * Fixed Windows console colour handling for tests that redirect stdout (#2345) +* Fixed issue with the `random` generators returning the same value over and over again ### Other changes @@ -230,6 +231,7 @@ v3 releases. * `-DCATCH_CONFIG_ANDROID_LOGWRITE=OFF` does nothing (the define will not exist) + ## 2.13.7 ### Fixes diff --git a/docs/reporter-events.md b/docs/reporter-events.md index 3641656a44..7adae53cab 100644 --- a/docs/reporter-events.md +++ b/docs/reporter-events.md @@ -56,7 +56,7 @@ are handled by a different event. ### `testCasePartial` events -> Introduced in Catch2 X.Y.Z +> Introduced in Catch2 3.0.1 ```cpp void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ); @@ -135,7 +135,7 @@ benchmarking itself fails. ## Listings events -> Introduced in Catch2 X.Y.Z. +> Introduced in Catch2 3.0.1. Listings events are events that correspond to the test binary being invoked with `--list-foo` flag. diff --git a/docs/reporters.md b/docs/reporters.md index 5f86b02e13..496c61a925 100644 --- a/docs/reporters.md +++ b/docs/reporters.md @@ -30,7 +30,7 @@ reporters](#multiple-reporters) to avoid any surprises from doing so. ## Using multiple reporters -> Support for having multiple parallel reporters was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 X.Y.Z +> Support for having multiple parallel reporters was [introduced](https://github.com/catchorg/Catch2/pull/2183) in Catch2 3.0.1 Catch2 supports using multiple reporters at the same time while having them write into different destinations. The two main uses of this are @@ -169,7 +169,7 @@ Currently there are two customization options: ### Per-reporter configuration -> Per-reporter configuration was introduced in Catch2 X.Y.Z +> Per-reporter configuration was introduced in Catch2 3.0.1 Catch2 supports some configuration to happen per reporter. The configuration options fall into one of two categories: diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index 58a1c7d599..bd8c8119ba 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.0.0-preview.5 -// Generated: 2022-04-20 23:45:15.004945 +// Catch v3.0.1 +// Generated: 2022-05-17 22:08:47.054486 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -145,6 +145,15 @@ namespace Catch { namespace Benchmark { namespace Detail { +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + bool directCompare( double lhs, double rhs ) { return lhs == rhs; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last) { auto count = last - first; double idx = (count - 1) * k / static_cast(q); @@ -152,7 +161,9 @@ namespace Catch { double g = idx - j; std::nth_element(first, first + j, last); auto xj = first[j]; - if (g == 0) return xj; + if ( directCompare( g, 0 ) ) { + return xj; + } auto xj1 = *std::min_element(first + (j + 1), last); return xj + g * (xj1 - xj); @@ -594,6 +605,7 @@ namespace Catch { bool Config::listTests() const { return m_data.listTests; } bool Config::listTags() const { return m_data.listTags; } bool Config::listReporters() const { return m_data.listReporters; } + bool Config::listListeners() const { return m_data.listListeners; } std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } @@ -635,6 +647,7 @@ namespace Catch { bool Config::showInvisibles() const { return m_data.showInvisibles; } Verbosity Config::verbosity() const { return m_data.verbosity; } + bool Config::skipBenchmarks() const { return m_data.skipBenchmarks; } bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } unsigned int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } @@ -1215,7 +1228,7 @@ namespace Catch { else if( tag == "!nonportable"_sr ) return TestCaseProperties::NonPortable; else if( tag == "!benchmark"_sr ) - return static_cast(TestCaseProperties::Benchmark | TestCaseProperties::IsHidden ); + return TestCaseProperties::Benchmark | TestCaseProperties::IsHidden; else return TestCaseProperties::None; } @@ -1860,13 +1873,19 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 0, 0, "preview", 5 ); + static Version version( 3, 0, 1, "", 0 ); return version; } } + + + +std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } + + /** \file * This is a special TU that combines what would otherwise be a very * small generator-related TUs into one bigger TU. @@ -2001,6 +2020,32 @@ namespace Catch { +#include + +namespace Catch { + namespace Generators { + + bool GeneratorUntypedBase::countedNext() { + auto ret = next(); + if ( ret ) { + m_stringReprCache.clear(); + ++m_currentElementIndex; + } + return ret; + } + + StringRef GeneratorUntypedBase::currentElementAsString() const { + if ( m_stringReprCache.empty() ) { + m_stringReprCache = stringifyImpl(); + } + return m_stringReprCache; + } + + } // namespace Generators +} // namespace Catch + + + #include #include #include @@ -2412,7 +2457,7 @@ namespace Catch { return Detail::InternalParseResult::runtimeError( "Expected argument following " + token.token); - auto result = valueRef->setValue(argToken.token); + const auto result = valueRef->setValue(argToken.token); if (!result) return Detail::InternalParseResult(result); if (result.value() == @@ -3109,7 +3154,10 @@ namespace Catch { ( "list all/matching tags" ) | Opt( config.listReporters ) ["--list-reporters"] - ( "list all reporters" ) + ( "list all available reporters" ) + | Opt( config.listListeners ) + ["--list-listeners"] + ( "list all listeners" ) | Opt( setTestOrder, "decl|lex|rand" ) ["--order"] ( "test case order (defaults to decl)" ) @@ -3125,6 +3173,9 @@ namespace Catch { | Opt( setWaitForKeypress, "never|start|exit|both" ) ["--wait-for-keypress"] ( "waits for a keypress before exiting" ) + | Opt( config.skipBenchmarks) + ["--skip-benchmarks"] + ( "disable running benchmarks") | Opt( config.benchmarkSamples, "samples" ) ["--benchmark-samples"] ( "number of samples to collect (default: 100)" ) @@ -3230,7 +3281,6 @@ namespace Catch { class NoColourImpl : public ColourImpl { public: NoColourImpl( IStream* stream ): ColourImpl( stream ) {} - static bool useColourOnPlatform() { return true; } private: void use( Colour::Code ) const override {} @@ -3257,7 +3307,7 @@ namespace { originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); } - static bool useColourOnPlatform(IStream const& stream) { + static bool useImplementationForStream(IStream const& stream) { // Win32 text colour APIs can only be used on console streams // We cannot check that the output hasn't been redirected, // so we just check that the original stream is console stream. @@ -3316,7 +3366,7 @@ namespace { public: ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} - static bool useColourOnPlatform(IStream const& stream) { + static bool useImplementationForStream(IStream const& stream) { // This is kinda messy due to trying to support a bunch of // different platforms at once. // The basic idea is that if we are asked to do autodetection (as @@ -3390,13 +3440,13 @@ namespace Catch { // todo: check win32 eligibility under ifdef, otherwise ansi if ( implSelection == ColourMode::PlatformDefault) { #if defined (CATCH_CONFIG_COLOUR_WIN32) - if ( Win32ColourImpl::useColourOnPlatform( *stream ) ) { + if ( Win32ColourImpl::useImplementationForStream( *stream ) ) { return Detail::make_unique( stream ); } else { return Detail::make_unique( stream ); } #endif - if ( ANSIColourImpl::useColourOnPlatform( *stream ) ) { + if ( ANSIColourImpl::useImplementationForStream( *stream ) ) { return Detail::make_unique( stream ); } return Detail::make_unique( stream ); @@ -3475,7 +3525,7 @@ namespace Catch { Context::~Context() = default; - SimplePcg32& rng() { + SimplePcg32& sharedRng() { static SimplePcg32 s_rng; return s_rng; } @@ -4079,7 +4129,7 @@ namespace Detail { setp( data, data + sizeof(data) ); } - ~StreamBufImpl() noexcept { + ~StreamBufImpl() noexcept override { StreamBufImpl::sync(); } @@ -4250,6 +4300,19 @@ namespace Catch { reporter.listReporters(descriptions); } + void listListeners(IEventListener& reporter) { + std::vector descriptions; + + auto const& factories = + getRegistryHub().getReporterRegistry().getListeners(); + descriptions.reserve( factories.size() ); + for ( auto const& fac : factories ) { + descriptions.push_back( { fac->getName(), fac->getDescription() } ); + } + + reporter.listListeners( descriptions ); + } + } // end anonymous namespace void TagInfo::add( StringRef spelling ) { @@ -4287,6 +4350,10 @@ namespace Catch { listed = true; listReporters(reporter); } + if ( config.listListeners() ) { + listed = true; + listListeners( reporter ); + } return listed; } @@ -4297,7 +4364,7 @@ namespace Catch { namespace Catch { CATCH_INTERNAL_START_WARNINGS_SUPPRESSION CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS - LeakDetector leakDetector; + static LeakDetector leakDetector; CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION } @@ -4581,7 +4648,8 @@ namespace Catch { void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { CATCH_ENFORCE( name.find( "::" ) == name.npos, "'::' is not allowed in reporter name: '" + name + '\'' ); - m_factories.emplace(name, CATCH_MOVE(factory)); + auto ret = m_factories.emplace(name, CATCH_MOVE(factory)); + CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" ); } void ReporterRegistry::registerListener( Detail::unique_ptr factory ) { @@ -4851,7 +4919,7 @@ namespace Catch { GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : TrackerBase( nameAndLocation, ctx, parent ) {} - ~GeneratorTracker(); + ~GeneratorTracker() override; static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) { GeneratorTracker* tracker; @@ -4962,7 +5030,7 @@ namespace Catch { // this generator is still waiting for any child to start. if ( should_wait_for_child || ( m_runState == CompletedSuccessfully && - m_generator->next() ) ) { + m_generator->countedNext() ) ) { m_children.clear(); m_runState = Executing; } @@ -5012,6 +5080,39 @@ namespace Catch { assert(rootTracker.isSectionTracker()); static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + // We intentionally only seed the internal RNG once per test case, + // before it is first invoked. The reason for that is a complex + // interplay of generator/section implementation details and the + // Random*Generator types. + // + // The issue boils down to us needing to seed the Random*Generators + // with different seed each, so that they return different sequences + // of random numbers. We do this by giving them a number from the + // shared RNG instance as their seed. + // + // However, this runs into an issue if the reseeding happens each + // time the test case is entered (as opposed to first time only), + // because multiple generators could get the same seed, e.g. in + // ```cpp + // TEST_CASE() { + // auto i = GENERATE(take(10, random(0, 100)); + // SECTION("A") { + // auto j = GENERATE(take(10, random(0, 100)); + // } + // SECTION("B") { + // auto k = GENERATE(take(10, random(0, 100)); + // } + // } + // ``` + // `i` and `j` would properly return values from different sequences, + // but `i` and `k` would return the same sequence, because their seed + // would be the same. + // (The reason their seeds would be the same is that the generator + // for k would be initialized when the test case is entered the second + // time, after the shared RNG instance was reset to the same value + // it had when the generator for i was initialized.) + seedRng( *m_config ); + uint64_t testRuns = 0; do { m_trackerContext.startCycle(); @@ -5241,8 +5342,6 @@ namespace Catch { m_shouldReportUnexpected = true; m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; - seedRng(*m_config); - Timer timer; CATCH_TRY { if (m_reporter->getPreferences().shouldRedirectStdOut) { @@ -5417,10 +5516,7 @@ namespace Catch { } void seedRng(IConfig const& config) { - if (config.rngSeed() != 0) { - std::srand(config.rngSeed()); - rng().seed(config.rngSeed()); - } + sharedRng().seed(config.rngSeed()); } unsigned int rngSeed() { @@ -5642,7 +5738,7 @@ namespace Catch { namespace Catch { StringRef::StringRef( char const* rawChars ) noexcept - : StringRef( rawChars, static_cast(std::strlen(rawChars) ) ) + : StringRef( rawChars, std::strlen(rawChars) ) {} auto StringRef::operator == ( StringRef other ) const noexcept -> bool { @@ -7213,14 +7309,26 @@ namespace Detail { ret << " (["; if (m_type == Detail::FloatingPointKind::Double) { - write(ret, step(m_target, static_cast(-INFINITY), m_ulps)); + write( ret, + step( m_target, + -std::numeric_limits::infinity(), + m_ulps ) ); ret << ", "; - write(ret, step(m_target, static_cast( INFINITY), m_ulps)); + write( ret, + step( m_target, + std::numeric_limits::infinity(), + m_ulps ) ); } else { // We have to cast INFINITY to float because of MinGW, see #1782 - write(ret, step(static_cast(m_target), static_cast(-INFINITY), m_ulps)); + write( ret, + step( static_cast( m_target ), + -std::numeric_limits::infinity(), + m_ulps ) ); ret << ", "; - write(ret, step(static_cast(m_target), static_cast( INFINITY), m_ulps)); + write( ret, + step( static_cast( m_target ), + std::numeric_limits::infinity(), + m_ulps ) ); } ret << "])"; @@ -7706,6 +7814,33 @@ namespace Catch { out << '\n' << std::flush; } + void defaultListListeners( std::ostream& out, + std::vector const& descriptions ) { + const auto maxNameLen = + std::max_element( descriptions.begin(), + descriptions.end(), + []( ListenerDescription const& lhs, + ListenerDescription const& rhs ) { + return lhs.name.size() < rhs.name.size(); + } ) + ->name.size(); + + out << "Registered listeners:\n"; + for ( auto const& desc : descriptions ) { + out << TextFlow::Column( static_cast( desc.name ) + + ':' ) + .indent( 2 ) + .width( maxNameLen + 5 ) + + TextFlow::Column( desc.description ) + .initialIndent( 0 ) + .indent( 2 ) + .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8 ) + << '\n'; + } + + out << '\n' << std::flush; + } + void defaultListTags( std::ostream& out, std::vector const& tags, bool isFiltered ) { @@ -7786,6 +7921,8 @@ namespace Catch { void EventListenerBase::assertionEnded( AssertionStats const& ) {} void EventListenerBase::listReporters( std::vector const& ) {} + void EventListenerBase::listListeners( + std::vector const& ) {} void EventListenerBase::listTests( std::vector const& ) {} void EventListenerBase::listTags( std::vector const& ) {} void EventListenerBase::noMatchingTestCases( StringRef ) {} @@ -7822,6 +7959,11 @@ namespace Catch { defaultListReporters(m_stream, descriptions, m_config->verbosity()); } + void ReporterBase::listListeners( + std::vector const& descriptions ) { + defaultListListeners( m_stream, descriptions ); + } + void ReporterBase::listTests(std::vector const& tests) { defaultListTests(m_stream, m_colour.get(), @@ -9415,6 +9557,13 @@ namespace Catch { } } + void MultiReporter::listListeners( + std::vector const& descriptions ) { + for ( auto& reporterish : m_reporterLikes ) { + reporterish->listListeners( descriptions ); + } + } + void MultiReporter::listTests(std::vector const& tests) { for (auto& reporterish : m_reporterLikes) { reporterish->listTests(tests); @@ -9432,6 +9581,29 @@ namespace Catch { + +namespace Catch { + namespace Detail { + + void registerReporterImpl( std::string const& name, + IReporterFactoryPtr reporterPtr ) { + CATCH_TRY { + getMutableRegistryHub().registerReporter( + name, CATCH_MOVE( reporterPtr ) ); + } + CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead + // register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } + + } // namespace Detail +} // namespace Catch + + + + #include namespace Catch { @@ -9689,11 +9861,6 @@ namespace Catch { } private: - void printSourceInfo() const { - stream << colourImpl->guardColour( tapDimColour ) - << result.getSourceInfo() << ':'; - } - void printResultType(StringRef passOrFail) const { if (!passOrFail.empty()) { stream << passOrFail << ' ' << counter << " -"; @@ -10009,7 +10176,8 @@ namespace Catch { m_xml.writeStylesheetRef( stylesheetRef ); m_xml.startElement("Catch2TestRun") .writeAttribute("name"_sr, m_config->name()) - .writeAttribute("rng-seed"_sr, m_config->rngSeed()); + .writeAttribute("rng-seed"_sr, m_config->rngSeed()) + .writeAttribute("catch2-version"_sr, libraryVersion()); if (m_config->testSpec().hasFilters()) m_xml.writeAttribute( "filters"_sr, serializeFilters( m_config->getTestsOrTags() ) ); } @@ -10212,6 +10380,19 @@ namespace Catch { } } + void XmlReporter::listListeners(std::vector const& descriptions) { + auto outerTag = m_xml.scopedElement( "RegisteredListeners" ); + for ( auto const& listener : descriptions ) { + auto inner = m_xml.scopedElement( "Listener" ); + m_xml.startElement( "Name", XmlFormatting::Indent ) + .writeText( listener.name, XmlFormatting::None ) + .endElement( XmlFormatting::Newline ); + m_xml.startElement( "Description", XmlFormatting::Indent ) + .writeText( listener.description, XmlFormatting::None ) + .endElement( XmlFormatting::Newline ); + } + } + void XmlReporter::listTests(std::vector const& tests) { auto outerTag = m_xml.scopedElement("MatchingTests"); for (auto const& test : tests) { diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index a5f8df0af7..5c1caa361e 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.0.0-preview.5 -// Generated: 2022-04-20 23:45:13.582550 +// Catch v3.0.1 +// Generated: 2022-05-17 22:08:46.674860 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -274,6 +274,7 @@ namespace Catch { virtual std::vector const& getSectionsToRun() const = 0; virtual Verbosity verbosity() const = 0; + virtual bool skipBenchmarks() const = 0; virtual bool benchmarkNoAnalysis() const = 0; virtual unsigned int benchmarkSamples() const = 0; virtual double benchmarkConfidenceInterval() const = 0; @@ -688,7 +689,7 @@ namespace Catch { class IMutableContext : public IContext { public: - virtual ~IMutableContext(); // = default + ~IMutableContext() override; // = default virtual void setResultCapture( IResultCapture* resultCapture ) = 0; virtual void setConfig( IConfig const* config ) = 0; @@ -715,7 +716,7 @@ namespace Catch { void cleanUpContext(); class SimplePcg32; - SimplePcg32& rng(); + SimplePcg32& sharedRng(); } #endif // CATCH_CONTEXT_HPP_INCLUDED @@ -1287,6 +1288,7 @@ namespace Catch { namespace Catch { struct ReporterDescription; + struct ListenerDescription; struct TagInfo; struct TestCaseInfo; class TestCaseHandle; @@ -1509,11 +1511,12 @@ namespace Catch { //! Writes out information about provided reporters using reporter-specific format virtual void listReporters(std::vector const& descriptions) = 0; + //! Writes out the provided listeners descriptions using reporter-specific format + virtual void listListeners(std::vector const& descriptions) = 0; //! Writes out information about provided tests using reporter-specific format virtual void listTests(std::vector const& tests) = 0; //! Writes out information about the provided tags using reporter-specific format virtual void listTags(std::vector const& tags) = 0; - }; using IEventListenerPtr = Detail::unique_ptr; @@ -2226,6 +2229,10 @@ namespace Catch { namespace Detail { using sample = std::vector; + // Used when we know we want == comparison of two doubles + // to centralize warning suppression + bool directCompare( double lhs, double rhs ); + double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last); template @@ -2256,7 +2263,7 @@ namespace Catch { double mean(Iterator first, Iterator last) { auto count = last - first; double sum = std::accumulate(first, last, 0.); - return sum / count; + return sum / static_cast(count); } template @@ -2302,15 +2309,17 @@ namespace Catch { }); double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); - int n = static_cast(resample.size()); + long n = static_cast(resample.size()); double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast(n); // degenerate case with uniform samples - if (prob_n == 0) return { point, point, point, confidence_level }; + if ( directCompare( prob_n, 0. ) ) { + return { point, point, point, confidence_level }; + } double bias = normal_quantile(prob_n); double z1 = normal_quantile((1. - confidence_level) / 2.); - auto cumn = [n]( double x ) -> int { + auto cumn = [n]( double x ) -> long { return std::lround( normal_cdf( x ) * n ); }; auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; @@ -2318,7 +2327,7 @@ namespace Catch { double b2 = bias - z1; double a1 = a(b1); double a2 = a(b2); - auto lo = static_cast((std::max)(cumn(a1), 0)); + auto lo = static_cast((std::max)(cumn(a1), 0l)); auto hi = static_cast((std::min)(cumn(a2), n - 1)); return { point, resample[lo], resample[hi], confidence_level }; @@ -2619,8 +2628,11 @@ namespace Catch { // sets lambda to be used in fun *and* executes benchmark! template ::value, int> = 0> Benchmark & operator=(Fun func) { - fun = Detail::BenchmarkFunction(func); - run(); + auto const* cfg = getCurrentContext().getConfig(); + if (!cfg->skipBenchmarks()) { + fun = Detail::BenchmarkFunction(func); + run(); + } return *this; } @@ -2679,9 +2691,7 @@ namespace Catch { template struct ObjectStorage { - using TStorage = std::aligned_storage_t::value>; - - ObjectStorage() : data() {} + ObjectStorage() = default; ObjectStorage(const ObjectStorage& other) { @@ -2690,7 +2700,7 @@ namespace Catch { ObjectStorage(ObjectStorage&& other) { - new(&data) T(CATCH_MOVE(other.stored_object())); + new(data) T(CATCH_MOVE(other.stored_object())); } ~ObjectStorage() { destruct_on_exit(); } @@ -2698,7 +2708,7 @@ namespace Catch { template void construct(Args&&... args) { - new (&data) T(CATCH_FORWARD(args)...); + new (data) T(CATCH_FORWARD(args)...); } template @@ -2710,21 +2720,21 @@ namespace Catch { private: // If this is a constructor benchmark, destruct the underlying object template - void destruct_on_exit(std::enable_if_t* = 0) { destruct(); } + void destruct_on_exit(std::enable_if_t* = nullptr) { destruct(); } // Otherwise, don't template - void destruct_on_exit(std::enable_if_t* = 0) { } + void destruct_on_exit(std::enable_if_t* = nullptr) { } T& stored_object() { - return *static_cast(static_cast(&data)); + return *static_cast(static_cast(data)); } T const& stored_object() const { - return *static_cast(static_cast(&data)); + return *static_cast(static_cast(data)); } - TStorage data; + alignas( T ) unsigned char data[sizeof( T )]{}; }; } // namespace Detail @@ -4147,6 +4157,7 @@ namespace Catch { bool listTests = false; bool listTags = false; bool listReporters = false; + bool listListeners = false; bool showSuccessfulTests = false; bool shouldDebugBreak = false; @@ -4163,6 +4174,7 @@ namespace Catch { unsigned int shardCount = 1; unsigned int shardIndex = 0; + bool skipBenchmarks = false; bool benchmarkNoAnalysis = false; unsigned int benchmarkSamples = 100; double benchmarkConfidenceInterval = 0.95; @@ -4197,6 +4209,7 @@ namespace Catch { bool listTests() const; bool listTags() const; bool listReporters() const; + bool listListeners() const; std::vector const& getReporterSpecs() const; std::vector const& @@ -4228,6 +4241,7 @@ namespace Catch { int abortAfter() const override; bool showInvisibles() const override; Verbosity verbosity() const override; + bool skipBenchmarks() const override; bool benchmarkNoAnalysis() const override; unsigned int benchmarkSamples() const override; double benchmarkConfidenceInterval() const override; @@ -6993,6 +7007,7 @@ namespace Catch { #endif // CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED + #ifndef CATCH_VERSION_HPP_INCLUDED #define CATCH_VERSION_HPP_INCLUDED @@ -7032,7 +7047,7 @@ namespace Catch { #define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MINOR 0 -#define CATCH_VERSION_PATCH 0 +#define CATCH_VERSION_PATCH 1 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7090,10 +7105,30 @@ namespace Catch { #define CATCH_INTERFACES_GENERATORTRACKER_HPP_INCLUDED +#include + namespace Catch { namespace Generators { class GeneratorUntypedBase { + // Caches result from `toStringImpl`, assume that when it is an + // empty string, the cache is invalidated. + mutable std::string m_stringReprCache; + + // Counts based on `next` returning true + std::size_t m_currentElementIndex = 0; + + /** + * Attempts to move the generator to the next element + * + * Returns true iff the move succeeded (and a valid element + * can be retrieved). + */ + virtual bool next() = 0; + + //! Customization point for `currentElementAsString` + virtual std::string stringifyImpl() const = 0; + public: GeneratorUntypedBase() = default; // Generation of copy ops is deprecated (and Clang will complain) @@ -7103,11 +7138,34 @@ namespace Catch { virtual ~GeneratorUntypedBase(); // = default; - // Attempts to move the generator to the next element - // - // Returns true iff the move succeeded (and a valid element - // can be retrieved). - virtual bool next() = 0; + /** + * Attempts to move the generator to the next element + * + * Serves as a non-virtual interface to `next`, so that the + * top level interface can provide sanity checking and shared + * features. + * + * As with `next`, returns true iff the move succeeded and + * the generator has new valid element to provide. + */ + bool countedNext(); + + std::size_t currentElementIndex() const { return m_currentElementIndex; } + + /** + * Returns generator's current element as user-friendly string. + * + * By default returns string equivalent to calling + * `Catch::Detail::stringify` on the current element, but generators + * can customize their implementation as needed. + * + * Not thread-safe due to internal caching. + * + * The returned ref is valid only until the generator instance + * is destructed, or it moves onto the next element, whichever + * comes first. + */ + StringRef currentElementAsString() const; }; using GeneratorBasePtr = Catch::Detail::unique_ptr; @@ -7142,6 +7200,10 @@ namespace Detail { template class IGenerator : public GeneratorUntypedBase { + std::string stringifyImpl() const override { + return ::Catch::Detail::stringify( get() ); + } + public: ~IGenerator() override = default; IGenerator() = default; @@ -7174,7 +7236,7 @@ namespace Detail { return m_generator->get(); } bool next() { - return m_generator->next(); + return m_generator->countedNext(); } }; @@ -7641,16 +7703,21 @@ namespace Catch { namespace Catch { namespace Generators { +namespace Detail { + // Returns a suitable seed for a random floating generator based off + // the primary internal rng. It does so by taking current value from + // the rng and returning it as the seed. + std::uint32_t getSeed(); +} template class RandomFloatingGenerator final : public IGenerator { - Catch::SimplePcg32& m_rng; + Catch::SimplePcg32 m_rng; std::uniform_real_distribution m_dist; Float m_current_number; public: - - RandomFloatingGenerator(Float a, Float b): - m_rng(rng()), + RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ): + m_rng(seed), m_dist(a, b) { static_cast(next()); } @@ -7666,13 +7733,12 @@ class RandomFloatingGenerator final : public IGenerator { template class RandomIntegerGenerator final : public IGenerator { - Catch::SimplePcg32& m_rng; + Catch::SimplePcg32 m_rng; std::uniform_int_distribution m_dist; Integer m_current_number; public: - - RandomIntegerGenerator(Integer a, Integer b): - m_rng(rng()), + RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ): + m_rng(seed), m_dist(a, b) { static_cast(next()); } @@ -7693,7 +7759,7 @@ std::enable_if_t::value && !std::is_same::value, GeneratorWrapper> random(T a, T b) { return GeneratorWrapper( - Catch::Detail::make_unique>(a, b) + Catch::Detail::make_unique>(a, b, Detail::getSeed()) ); } @@ -7702,7 +7768,7 @@ std::enable_if_t::value, GeneratorWrapper> random(T a, T b) { return GeneratorWrapper( - Catch::Detail::make_unique>(a, b) + Catch::Detail::make_unique>(a, b, Detail::getSeed()) ); } @@ -7867,6 +7933,9 @@ namespace Catch { public: virtual ~EventListenerFactory(); // = default virtual IEventListenerPtr create( IConfig const* config ) const = 0; + //! Return a meaningful name for the listener, e.g. its type name + virtual StringRef getName() const = 0; + //! Return listener's description if available virtual std::string getDescription() const = 0; }; } // namespace Catch @@ -7901,6 +7970,7 @@ namespace Catch { } // namespace Catch #endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED + #include #include #include @@ -8033,6 +8103,7 @@ namespace Catch { #endif // CATCH_CONSOLE_WIDTH_HPP_INCLUDED + #ifndef CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED #define CATCH_CONTAINER_NONMEMBERS_HPP_INCLUDED @@ -8125,7 +8196,7 @@ namespace Catch { #if defined(__i386__) || defined(__x86_64__) #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ #elif defined(__aarch64__) - #define CATCH_TRAP() __asm__(".inst 0xd4200000") + #define CATCH_TRAP() __asm__(".inst 0xd43e0000") #endif #elif defined(CATCH_PLATFORM_IPHONE) @@ -8367,6 +8438,7 @@ namespace Catch { #endif // CATCH_POLYFILLS_HPP_INCLUDED +#include #include #include #include @@ -8381,6 +8453,15 @@ namespace Catch { } // end namespace Detail + +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic push + // We do a bunch of direct compensations of floating point numbers, + // because we know what we are doing and actually do want the direct + // comparison behaviour. +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + /** * Calculates the ULP distance between two floating point numbers * @@ -8440,6 +8521,11 @@ namespace Catch { return lc - rc; } +#if defined( __GNUC__ ) || defined( __clang__ ) +# pragma GCC diagnostic pop +#endif + + } // end namespace Catch #endif // CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED @@ -8523,6 +8609,10 @@ namespace Catch { struct ReporterDescription { std::string name, description; }; + struct ListenerDescription { + StringRef name; + std::string description; + }; struct TagInfo { void add(StringRef spelling); @@ -10074,7 +10164,7 @@ namespace Matchers { class MatcherGenericBase : public MatcherUntypedBase { public: MatcherGenericBase() = default; - virtual ~MatcherGenericBase(); // = default; + ~MatcherGenericBase() override; // = default; MatcherGenericBase(MatcherGenericBase&) = default; MatcherGenericBase(MatcherGenericBase&&) = default; @@ -11128,6 +11218,14 @@ namespace Catch { */ void listReporters( std::vector const& descriptions ) override; + /** + * Provides a simple default listing of listeners + * + * Looks similarly to listing of reporters, but with listener type + * instead of reporter name. + */ + void listListeners( + std::vector const& descriptions ) override; /** * Provides a simple default listing of tests. * @@ -11495,6 +11593,8 @@ namespace Catch { void listReporters( std::vector const& descriptions ) override; + void listListeners( + std::vector const& descriptions ) override; void listTests( std::vector const& tests ) override; void listTags( std::vector const& tagInfos ) override; @@ -11559,6 +11659,13 @@ namespace Catch { std::vector const& descriptions, Verbosity verbosity ); + /** + * Lists listeners descriptions to the provided stream in user-friendly + * format + */ + void defaultListListeners( std::ostream& out, + std::vector const& descriptions ); + /** * Lists tag information to the provided stream in user-friendly format * @@ -11692,6 +11799,7 @@ namespace Catch { void skipTest( TestCaseInfo const& testInfo ) override; void listReporters(std::vector const& descriptions) override; + void listListeners(std::vector const& descriptions) override; void listTests(std::vector const& tests) override; void listTags(std::vector const& tags) override; @@ -11707,8 +11815,28 @@ namespace Catch { #define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED +#include + namespace Catch { + namespace Detail { + + template + struct has_description : std::false_type {}; + + template + struct has_description< + T, + void_t> + : std::true_type {}; + + //! Indirection for reporter registration, so that the error handling is + //! independent on the reporter's concrete type + void registerReporterImpl( std::string const& name, + IReporterFactoryPtr reporterPtr ); + + } // namespace Detail + class IEventListener; using IEventListenerPtr = Detail::unique_ptr; @@ -11729,7 +11857,8 @@ namespace Catch { class ReporterRegistrar { public: explicit ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, Detail::make_unique>() ); + registerReporterImpl( name, + Detail::make_unique>() ); } }; @@ -11737,37 +11866,60 @@ namespace Catch { class ListenerRegistrar { class TypedListenerFactory : public EventListenerFactory { + StringRef m_listenerName; + + std::string getDescriptionImpl( std::true_type ) const { + return T::getDescription(); + } + + std::string getDescriptionImpl( std::false_type ) const { + return "(No description provided)"; + } + + public: + TypedListenerFactory( StringRef listenerName ): + m_listenerName( listenerName ) {} + + IEventListenerPtr create( IConfig const* config ) const override { + return Detail::make_unique( config ); + } - IEventListenerPtr - create( IConfig const* config ) const override { - return Detail::make_unique(config); + StringRef getName() const override { + return m_listenerName; } + std::string getDescription() const override { - return std::string(); + return getDescriptionImpl( Detail::has_description{} ); } }; public: - - ListenerRegistrar() { - getMutableRegistryHub().registerListener( Detail::make_unique() ); + ListenerRegistrar(StringRef listenerName) { + getMutableRegistryHub().registerListener( Detail::make_unique(listenerName) ); } }; } #if !defined(CATCH_CONFIG_DISABLE) -#define CATCH_REGISTER_REPORTER( name, reporterType ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define CATCH_REGISTER_REPORTER( name, reporterType ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ReporterRegistrar INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_RegistrarFor )( name ); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define CATCH_REGISTER_LISTENER( listenerType ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ListenerRegistrar INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_RegistrarFor )( #listenerType ); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#define CATCH_REGISTER_LISTENER( listenerType ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #else // CATCH_CONFIG_DISABLE #define CATCH_REGISTER_REPORTER(name, reporterType) @@ -11969,6 +12121,7 @@ namespace Catch { void benchmarkFailed( StringRef error ) override; void listReporters(std::vector const& descriptions) override; + void listListeners(std::vector const& descriptions) override; void listTests(std::vector const& tests) override; void listTags(std::vector const& tags) override; diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp index a481ca2ddf..ed5392970d 100644 --- a/src/catch2/catch_version.cpp +++ b/src/catch2/catch_version.cpp @@ -36,7 +36,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 0, 0, "preview", 5 ); + static Version version( 3, 0, 1, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 51e50c5ce0..29e185673b 100644 --- a/src/catch2/catch_version_macros.hpp +++ b/src/catch2/catch_version_macros.hpp @@ -10,6 +10,6 @@ #define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MINOR 0 -#define CATCH_VERSION_PATCH 0 +#define CATCH_VERSION_PATCH 1 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED