From d63307279412de3870cf97cc6802bae8ab36089e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Thu, 7 Mar 2019 21:44:57 +0100 Subject: [PATCH] v2.7.0 --- CMakeLists.txt | 2 +- README.md | 4 +- docs/release-notes.md | 34 ++ include/catch.hpp | 4 +- include/internal/catch_version.cpp | 2 +- single_include/catch2/catch.hpp | 637 ++++++++++++++++++++--------- 6 files changed, 477 insertions(+), 206 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f50492c..ea35d24e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ if(NOT DEFINED PROJECT_NAME) set(NOT_SUBPROJECT ON) endif() -project(Catch2 LANGUAGES CXX VERSION 2.6.1) +project(Catch2 LANGUAGES CXX VERSION 2.7.0) # Provide path for scripts list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake") diff --git a/README.md b/README.md index 4db6bc54..08095f1c 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ [![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=master)](https://travis-ci.org/catchorg/Catch2) [![Build status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true)](https://ci.appveyor.com/project/catchorg/catch2) [![codecov](https://codecov.io/gh/catchorg/Catch2/branch/master/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/rJhfG8exsneDGleb) +[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/byNJIivVphHo170P) [![Join the chat in Discord: https://discord.gg/4CWS9zD](https://img.shields.io/badge/Discord-Chat!-brightgreen.svg)](https://discord.gg/4CWS9zD) -The latest version of the single header can be downloaded directly using this link +The latest version of the single header can be downloaded directly using this link ## Catch2 is released! diff --git a/docs/release-notes.md b/docs/release-notes.md index df8a11b8..34278c38 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,7 @@ # Release notes **Contents**
+[2.7.0](#270)
[2.6.1](#261)
[2.6.0](#260)
[2.5.0](#250)
@@ -20,6 +21,39 @@ [Older versions](#older-versions)
[Even Older versions](#even-older-versions)
+## 2.7.0 + +### Improvements +* `TEMPLATE_PRODUCT_TEST_CASE` now uses the resulting type in the name, instead of the serial number (#1544) +* Catch2's single header is now strictly ASCII (#1542) +* Added generator for random integral/floating point types + * The types are inferred within the `random` helper +* Added back RangeGenerator (#1526) + * RangeGenerator returns elements within a certain range +* Added ChunkGenerator generic transform (#1538) + * A ChunkGenerator returns the elements from different generator in chunks of n elements +* Added `UNSCOPED_INFO` (#415, #983, #1522) + * This is a variant of `INFO` that lives until next assertion/end of the test case. + + +### Fixes +* All calls to C stdlib functions are now `std::` qualified (#1541) + * Code brought in from Clara was also updated. +* Running tests will no longer open the specified output file twice (#1545) + * This would cause trouble when the file was not a file, but rather a named pipe + * Fixes the CLion/Resharper integration with Catch +* Fixed `-Wunreachable-code` occuring with (old) ccache+cmake+clang combination (#1540) +* Fixed `-Wdefaulted-function-deleted` warning with Clang 8 (#1537) +* Catch2's type traits and helpers are now properly namespaced inside `Catch::` (#1548) +* Fixed std{out,err} redirection for failing test (#1514, #1525) + * Somehow, this bug has been present for well over a year before it was reported + + +### Contrib +* `ParseAndAddCatchTests` now properly escapes commas in the test name + + + ## 2.6.1 ### Improvements diff --git a/include/catch.hpp b/include/catch.hpp index 88a9abe6..e15588f0 100644 --- a/include/catch.hpp +++ b/include/catch.hpp @@ -10,8 +10,8 @@ #define TWOBLUECUBES_CATCH_HPP_INCLUDED #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 6 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 7 +#define CATCH_VERSION_PATCH 0 #ifdef __clang__ # pragma clang system_header diff --git a/include/internal/catch_version.cpp b/include/internal/catch_version.cpp index d6ae3c6e..a1d08b2c 100644 --- a/include/internal/catch_version.cpp +++ b/include/internal/catch_version.cpp @@ -37,7 +37,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 6, 1, "", 0 ); + static Version version( 2, 7, 0, "", 0 ); return version; } diff --git a/single_include/catch2/catch.hpp b/single_include/catch2/catch.hpp index 994de46a..1850fff1 100644 --- a/single_include/catch2/catch.hpp +++ b/single_include/catch2/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v2.6.1 - * Generated: 2019-02-12 19:52:52.262497 + * Catch v2.7.0 + * Generated: 2019-03-07 21:34:30.252164 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved. @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 6 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 7 +#define CATCH_VERSION_PATCH 0 #ifdef __clang__ # pragma clang system_header @@ -711,6 +711,16 @@ struct is_unique : std::integral_constant #define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) @@ -725,7 +735,7 @@ struct is_unique : std::integral_constant #define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif -#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) TypeList +#define INTERNAL_CATCH_MAKE_TYPE_LIST(types) Catch::TypeList #define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(types)\ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,INTERNAL_CATCH_REMOVE_PARENS(types)) @@ -736,65 +746,59 @@ struct is_unique : std::integral_constant #include +namespace Catch { template< typename... > -struct TypeList{}; +struct TypeList {}; template< typename... > struct append; template< template class L1 - , typename...E1 - , template class L2 - , typename...E2 - > -struct append< L1, L2 > -{ - using type = L1; + , typename...E1 + , template class L2 + , typename...E2 +> +struct append< L1, L2 > { + using type = L1; }; template< template class L1 - , typename...E1 - , template class L2 - , typename...E2 - , typename...Rest - > -struct append< L1, L2, Rest...> -{ - using type = typename append< L1, Rest... >::type; + , typename...E1 + , template class L2 + , typename...E2 + , typename...Rest +> +struct append< L1, L2, Rest...> { + using type = typename append< L1, Rest... >::type; }; template< template class - , typename... - > + , typename... +> struct rewrap; template< template class Container - , template class List - , typename...elems - > -struct rewrap> -{ + , template class List + , typename...elems +> +struct rewrap> { using type = TypeList< Container< elems... > >; }; template< template class Container - , template class List - , class...Elems - , typename...Elements> -struct rewrap, Elements...> -{ + , template class List + , class...Elems + , typename...Elements> + struct rewrap, Elements...> { using type = typename append>, typename rewrap::type>::type; }; template< template class...Containers > -struct combine -{ +struct combine { template< typename...Types > - struct with_types - { + struct with_types { template< template class Final > - struct into - { + struct into { using type = typename append, typename rewrap::type...>::type; }; }; @@ -803,6 +807,8 @@ struct combine template struct always_false : std::false_type {}; +} // namespace Catch + // end catch_meta.hpp namespace Catch { @@ -949,11 +955,14 @@ struct AutoReg : NonCopyable { CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...) \ int index = 0; \ using expander = int[]; \ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */\ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ - using TestInit = combine \ + using TestInit = Catch::combine \ ::with_types::into::type; \ TestInit(); \ return 0; \ @@ -1014,11 +1023,14 @@ struct AutoReg : NonCopyable { CATCH_INTERNAL_CHECK_UNIQUE_TYPES(Types...)\ int index = 0;\ using expander = int[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + Catch::StringMaker::convert(index++), Tags } ), 0)... };/* NOLINT */ \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ + constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ - using TestInit = combine\ + using TestInit = Catch::combine\ ::with_types::into::type;\ TestInit();\ return 0;\ @@ -2062,6 +2074,7 @@ namespace Catch { struct SectionInfo; struct SectionEndInfo; struct MessageInfo; + struct MessageBuilder; struct Counts; struct BenchmarkInfo; struct BenchmarkStats; @@ -2088,6 +2101,8 @@ namespace Catch { virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; + virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0; + virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual void handleExpr @@ -2251,9 +2266,12 @@ namespace Catch { class ScopedMessage { public: explicit ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage& duplicate ) = delete; + ScopedMessage( ScopedMessage&& old ); ~ScopedMessage(); MessageInfo m_info; + bool m_moved; }; class Capturer { @@ -2316,7 +2334,7 @@ namespace Catch { CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( (void)0, false && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look + } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// @@ -2397,6 +2415,10 @@ namespace Catch { #define INTERNAL_CATCH_INFO( macroName, log ) \ Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ + Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) + /////////////////////////////////////////////////////////////////////////////// // Although this is matcher-based, it can be used with just a string #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ @@ -3642,6 +3664,36 @@ namespace Generators { return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); } + auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + + template + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah. + auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique>(generatorExpression())); + } + + auto const& generator = static_cast const&>( *tracker.getGenerator() ); + return generator.get(); + } + +} // namespace Generators +} // namespace Catch + +#define GENERATE( ... ) \ + Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { +namespace Generators { + template class TakeGenerator : public IGenerator { GeneratorWrapper m_generator; @@ -3810,31 +3862,297 @@ namespace Generators { ); } - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; - - template - // Note: The type after -> is weird, because VS2015 cannot parse - // the expression used in the typedef inside, when it is in - // return type. Yeah, ¯\_(ツ)_/¯ - auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { - using UnderlyingType = typename decltype(generatorExpression())::type; - - IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); - if (!tracker.hasGenerator()) { - tracker.setGenerator(pf::make_unique>(generatorExpression())); + template + class ChunkGenerator final : public IGenerator> { + std::vector m_chunk; + size_t m_chunk_size; + GeneratorWrapper m_generator; + bool m_used_up = false; + public: + ChunkGenerator(size_t size, GeneratorWrapper generator) : + m_chunk_size(size), m_generator(std::move(generator)) + { + m_chunk.reserve(m_chunk_size); + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } } + std::vector const& get() const override { + return m_chunk; + } + bool next() override { + m_chunk.clear(); + for (size_t idx = 0; idx < m_chunk_size; ++idx) { + if (!m_generator.next()) { + return false; + } + m_chunk.push_back(m_generator.get()); + } + return true; + } + }; - auto const& generator = static_cast const&>( *tracker.getGenerator() ); - return generator.get(); + template + GeneratorWrapper> chunk(size_t size, GeneratorWrapper&& generator) { + return GeneratorWrapper>( + pf::make_unique>(size, std::move(generator)) + ); } } // namespace Generators } // namespace Catch -#define GENERATE( ... ) \ - Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp -// end catch_generators.hpp +// start catch_context.h + +#include + +namespace Catch { + + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual IConfigPtr const& getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( IConfigPtr const& config ) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext& getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext& getCurrentMutableContext() + { + if( !IMutableContext::currentContext ) + IMutableContext::createContext(); + return *IMutableContext::currentContext; + } + + inline IContext& getCurrentContext() + { + return getCurrentMutableContext(); + } + + void cleanUpContext(); +} + +// end catch_context.h +// start catch_interfaces_config.h + +#include +#include +#include +#include + +namespace Catch { + + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; + + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual int benchmarkResolutionMultiple() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + }; + + using IConfigPtr = std::shared_ptr; +} + +// end catch_interfaces_config.h +#include + +namespace Catch { +namespace Generators { + +template +class RandomFloatingGenerator final : public IGenerator { + // FIXME: What is the right seed? + std::minstd_rand m_rand; + std::uniform_real_distribution m_dist; + Float m_current_number; +public: + + RandomFloatingGenerator(Float a, Float b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + m_dist(a, b) { + static_cast(next()); + } + + Float const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rand); + return true; + } +}; + +template +class RandomIntegerGenerator final : public IGenerator { + std::minstd_rand m_rand; + std::uniform_int_distribution m_dist; + Integer m_current_number; +public: + + RandomIntegerGenerator(Integer a, Integer b): + m_rand(getCurrentContext().getConfig()->rngSeed()), + m_dist(a, b) { + static_cast(next()); + } + + Integer const& get() const override { + return m_current_number; + } + bool next() override { + m_current_number = m_dist(m_rand); + return true; + } +}; + +// TODO: Ideally this would be also constrained against the various char types, +// but I don't expect users to run into that in practice. +template +typename std::enable_if::value && !std::is_same::value, +GeneratorWrapper>::type +random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); +} + +template +typename std::enable_if::value, +GeneratorWrapper>::type +random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); +} + +template +class RangeGenerator final : public IGenerator { + T m_current; + T m_end; + T m_step; + bool m_positive; + +public: + RangeGenerator(T const& start, T const& end, T const& step): + m_current(start), + m_end(end), + m_step(step), + m_positive(m_step > T(0)) + { + assert(m_current != m_end && "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); + } + + RangeGenerator(T const& start, T const& end): + RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) + {} + + T const& get() const override { + return m_current; + } + + bool next() override { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } +}; + +template +GeneratorWrapper range(T const& start, T const& end, T const& step) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end, step)); +} + +template +GeneratorWrapper range(T const& start, T const& end) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end)); +} + +} // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections @@ -4321,79 +4639,6 @@ namespace Catch { #endif // end catch_test_spec_parser.h -// start catch_interfaces_config.h - -#include -#include -#include -#include - -namespace Catch { - - enum class Verbosity { - Quiet = 0, - Normal, - High - }; - - struct WarnAbout { enum What { - Nothing = 0x00, - NoAssertions = 0x01, - NoTests = 0x02 - }; }; - - struct ShowDurations { enum OrNot { - DefaultForReporter, - Always, - Never - }; }; - struct RunTests { enum InWhatOrder { - InDeclarationOrder, - InLexicographicalOrder, - InRandomOrder - }; }; - struct UseColour { enum YesOrNo { - Auto, - Yes, - No - }; }; - struct WaitForKeypress { enum When { - Never, - BeforeStart = 1, - BeforeExit = 2, - BeforeStartAndExit = BeforeStart | BeforeExit - }; }; - - class TestSpec; - - struct IConfig : NonCopyable { - - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual std::ostream& stream() const = 0; - virtual std::string name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual bool warnAboutNoTests() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations::OrNot showDurations() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual bool hasTestFilters() const = 0; - virtual RunTests::InWhatOrder runOrder() const = 0; - virtual unsigned int rngSeed() const = 0; - virtual int benchmarkResolutionMultiple() const = 0; - virtual UseColour::YesOrNo useColour() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; - virtual Verbosity verbosity() const = 0; - }; - - using IConfigPtr = std::shared_ptr; -} - -// end catch_interfaces_config.h // Libstdc++ doesn't like incomplete classes for unique_ptr #include @@ -4673,8 +4918,8 @@ namespace Catch { AssertionStats( AssertionStats const& ) = default; AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = default; - AssertionStats& operator = ( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; virtual ~AssertionStats(); AssertionResult assertionResult; @@ -5758,58 +6003,6 @@ std::string StringMaker::convert(Catch::Detail::Approx co // end catch_approx.cpp // start catch_assertionhandler.cpp -// start catch_context.h - -#include - -namespace Catch { - - struct IResultCapture; - struct IRunner; - struct IConfig; - struct IMutableContext; - - using IConfigPtr = std::shared_ptr; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual IConfigPtr const& getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( IConfigPtr const& config ) = 0; - - private: - static IMutableContext *currentContext; - friend IMutableContext& getCurrentMutableContext(); - friend void cleanUpContext(); - static void createContext(); - }; - - inline IMutableContext& getCurrentMutableContext() - { - if( !IMutableContext::currentContext ) - IMutableContext::createContext(); - return *IMutableContext::currentContext; - } - - inline IContext& getCurrentContext() - { - return getCurrentMutableContext(); - } - - void cleanUpContext(); -} - -// end catch_context.h // start catch_debugger.h namespace Catch { @@ -5996,6 +6189,8 @@ namespace Catch { void pushScopedMessage( MessageInfo const& message ) override; void popScopedMessage( MessageInfo const& message ) override; + void emplaceUnscopedMessage( MessageBuilder const& builder ) override; + std::string getCurrentTestName() const override; const AssertionResult* getLastResult() const override; @@ -6043,6 +6238,7 @@ namespace Catch { Totals m_totals; IStreamingReporterPtr m_reporter; std::vector m_messages; + std::vector m_messageScopes; /* Keeps owners of so-called unscoped messages. */ AssertionInfo m_lastAssertionInfo; std::vector m_unfinishedSections; std::vector m_activeSections; @@ -6679,6 +6875,7 @@ inline auto Column::operator + (Column const& other) -> Columns { // ----------- end of #include from clara_textflow.hpp ----------- // ........... back in clara.hpp +#include #include #include #include @@ -6971,7 +7168,7 @@ namespace detail { } inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { std::string srcLC = source; - std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( std::tolower(c) ); } ); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") @@ -9374,14 +9571,20 @@ namespace Catch { //////////////////////////////////////////////////////////////////////////// ScopedMessage::ScopedMessage( MessageBuilder const& builder ) - : m_info( builder.m_info ) + : m_info( builder.m_info ), m_moved() { m_info.message = builder.m_stream.str(); getResultCapture().pushScopedMessage( m_info ); } + ScopedMessage::ScopedMessage( ScopedMessage&& old ) + : m_info( old.m_info ), m_moved() + { + old.m_moved = true; + } + ScopedMessage::~ScopedMessage() { - if ( !uncaught_exceptions() ){ + if ( !uncaught_exceptions() && !m_moved ){ getResultCapture().popScopedMessage(m_info); } } @@ -9489,6 +9692,22 @@ namespace Catch { auto str() const -> std::string; }; + class RedirectedStreams { + public: + RedirectedStreams(RedirectedStreams const&) = delete; + RedirectedStreams& operator=(RedirectedStreams const&) = delete; + RedirectedStreams(RedirectedStreams&&) = delete; + RedirectedStreams& operator=(RedirectedStreams&&) = delete; + + RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr); + ~RedirectedStreams(); + private: + std::string& m_redirectedCout; + std::string& m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; + }; + #if defined(CATCH_CONFIG_NEW_CAPTURE) // Windows's implementation of std::tmpfile is terrible (it tries @@ -9580,6 +9799,16 @@ namespace Catch { {} auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr) + : m_redirectedCout(redirectedCout), + m_redirectedCerr(redirectedCerr) + {} + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } + #if defined(CATCH_CONFIG_NEW_CAPTURE) #if defined(_MSC_VER) @@ -10161,6 +10390,9 @@ namespace Catch { // and should be let to clear themselves out. static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + // Reset working state resetAssertionInfo(); m_lastResult = result; @@ -10215,6 +10447,7 @@ namespace Catch { m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); m_messages.clear(); + m_messageScopes.clear(); } void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { @@ -10241,6 +10474,10 @@ namespace Catch { m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); } + void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) { + m_messageScopes.emplace_back( builder ); + } + std::string RunContext::getCurrentTestName() const { return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name @@ -10301,6 +10538,7 @@ namespace Catch { m_lastAssertionPassed = true; ++m_totals.assertions.passed; resetAssertionInfo(); + m_messageScopes.clear(); } bool RunContext::aborting() const { @@ -10322,13 +10560,10 @@ namespace Catch { CATCH_TRY { if (m_reporter->getPreferences().shouldRedirectStdOut) { #if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) - RedirectedStdOut redirectedStdOut; - RedirectedStdErr redirectedStdErr; + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); timer.start(); invokeActiveTestCase(); - redirectedCout += redirectedStdOut.str(); - redirectedCerr += redirectedStdErr.str(); #else OutputRedirect r(redirectedCout, redirectedCerr); timer.start(); @@ -10355,6 +10590,7 @@ namespace Catch { m_testCaseTracker->close(); handleUnfinishedSections(); m_messages.clear(); + m_messageScopes.clear(); SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); m_reporter->sectionEnded(testCaseSectionStats); @@ -10762,9 +10998,9 @@ namespace Catch { return 1; auto result = m_cli.parse( clara::Args( argc, argv ) ); - config(); - getCurrentMutableContext().setConfig( m_config ); if( !result ) { + config(); + getCurrentMutableContext().setConfig(m_config); Catch::cerr() << Colour( Colour::Red ) << "\nError(s) in input:\n" @@ -12429,7 +12665,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 6, 1, "", 0 ); + static Version version( 2, 7, 0, "", 0 ); return version; } @@ -12781,7 +13017,7 @@ namespace Catch { #ifdef _MSC_VER sprintf_s(buffer, "%.3f", duration); #else - sprintf(buffer, "%.3f", duration); + std::sprintf(buffer, "%.3f", duration); #endif return std::string(buffer); } @@ -13315,7 +13551,7 @@ public: case Unit::Nanoseconds: return "ns"; case Unit::Microseconds: - return "µs"; + return "us"; case Unit::Milliseconds: return "ms"; case Unit::Seconds: @@ -14464,6 +14700,7 @@ int main (int argc, char * const argv[]) { #endif // CATCH_CONFIG_DISABLE_MATCHERS #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) +#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )