diff --git a/debian/changelog b/debian/changelog index 55af5bf6c..89d600112 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +monado (21.0.0~dfsg1-2~ubuntu2110~20211027+2) impish; urgency=medium + + * Add d/patches/0003-Update-catch2-to-2.13.7.patch to fix FTBFS. + + -- Ryan Pavlik Wed, 27 Oct 2021 13:53:09 -0500 + monado (21.0.0~dfsg1-2~ubuntu2110~20211027+1) impish; urgency=medium * Create branch for backport, updating diff --git a/debian/patches/0003-Update-catch2-to-2.13.7.patch b/debian/patches/0003-Update-catch2-to-2.13.7.patch new file mode 100644 index 000000000..68ded8e9f --- /dev/null +++ b/debian/patches/0003-Update-catch2-to-2.13.7.patch @@ -0,0 +1,1510 @@ +From: Ryan Pavlik +Date: Wed, 27 Oct 2021 13:49:10 -0500 +Subject: Update catch2 to 2.13.7. + +Forwarded: https://gitlab.freedesktop.org/monado/monado/-/merge_requests/951 +--- + src/external/Catch2/catch/catch.hpp | 779 ++++++++++++++++++++++++------------ + 1 file changed, 516 insertions(+), 263 deletions(-) + +diff --git a/src/external/Catch2/catch/catch.hpp b/src/external/Catch2/catch/catch.hpp +index f64422a..7e706f9 100644 +--- a/src/external/Catch2/catch/catch.hpp ++++ b/src/external/Catch2/catch/catch.hpp +@@ -1,9 +1,9 @@ + /* +- * Catch v2.12.2 +- * Generated: 2020-05-25 15:09:23.791719 ++ * Catch v2.13.7 ++ * Generated: 2021-07-28 20:29:27.753164 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly +- * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. ++ * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +@@ -14,8 +14,8 @@ + + + #define CATCH_VERSION_MAJOR 2 +-#define CATCH_VERSION_MINOR 12 +-#define CATCH_VERSION_PATCH 2 ++#define CATCH_VERSION_MINOR 13 ++#define CATCH_VERSION_PATCH 7 + + #ifdef __clang__ + # pragma clang system_header +@@ -66,13 +66,16 @@ + #if !defined(CATCH_CONFIG_IMPL_ONLY) + // start catch_platform.h + ++// See e.g.: ++// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html + #ifdef __APPLE__ +-# include +-# if TARGET_OS_OSX == 1 +-# define CATCH_PLATFORM_MAC +-# elif TARGET_OS_IPHONE == 1 +-# define CATCH_PLATFORM_IPHONE +-# endif ++# include ++# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ ++ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) ++# define CATCH_PLATFORM_MAC ++# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) ++# define CATCH_PLATFORM_IPHONE ++# endif + + #elif defined(linux) || defined(__linux) || defined(__linux__) + # define CATCH_PLATFORM_LINUX +@@ -132,13 +135,9 @@ namespace Catch { + + #endif + +-#if defined(__cpp_lib_uncaught_exceptions) +-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +-#endif +- +-// We have to avoid both ICC and Clang, because they try to mask themselves +-// as gcc, and we want only GCC in this block +-#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) ++// Only GCC compiler should be used in this block, so other compilers trying to ++// mask themselves as GCC should be ignored. ++#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) + # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) + # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +@@ -162,7 +161,7 @@ namespace Catch { + // ``` + // + // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +-# if !defined(__ibmxl__) ++# if !defined(__ibmxl__) && !defined(__CUDACC__) + # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ + # endif + +@@ -244,10 +243,6 @@ namespace Catch { + # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) + # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +-# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +-# endif +- + // Universal Windows platform does not support SEH + // Or console colours (or console at all...) + # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +@@ -330,7 +325,10 @@ namespace Catch { + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) +- # define CATCH_INTERNAL_CONFIG_CPP17_BYTE ++ # include ++ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) ++ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE ++ # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable +@@ -373,10 +371,6 @@ namespace Catch { + # define CATCH_CONFIG_CPP17_OPTIONAL + #endif + +-#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +-# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +-#endif +- + #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) + # define CATCH_CONFIG_CPP17_STRING_VIEW + #endif +@@ -775,7 +769,7 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n + #define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) + #define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) + #define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +-#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) ++#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) + #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) + #define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) + #define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +@@ -1105,7 +1099,7 @@ struct AutoReg : NonCopyable { + int index = 0; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ + using expander = int[];\ +- (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ ++ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +@@ -1151,7 +1145,7 @@ struct AutoReg : NonCopyable { + 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 */\ ++ (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++)... };/* NOLINT */\ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ +@@ -1195,7 +1189,7 @@ struct AutoReg : NonCopyable { + void reg_tests() { \ + int index = 0; \ + using expander = int[]; \ +- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */\ ++ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ + } \ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ +@@ -1229,7 +1223,7 @@ struct AutoReg : NonCopyable { + int index = 0; \ + constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ + using expander = int[];\ +- (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ ++ (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +@@ -1278,7 +1272,7 @@ struct AutoReg : NonCopyable { + 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 */ \ ++ (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++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +@@ -1325,7 +1319,7 @@ struct AutoReg : NonCopyable { + void reg_tests(){\ + int index = 0;\ + using expander = int[];\ +- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */ \ ++ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ + }\ + };\ + static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ +@@ -1829,8 +1823,8 @@ namespace Catch { + #endif + + namespace Detail { +- template +- std::string rangeToString(InputIterator first, InputIterator last) { ++ template ++ std::string rangeToString(InputIterator first, Sentinel last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { +@@ -2468,7 +2462,7 @@ namespace Catch { + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + +- virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; ++ virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + + #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing( std::string const& name ) = 0; +@@ -4080,16 +4074,16 @@ namespace Generators { + return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); + } + +- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; ++ auto acquireGeneratorTracker( StringRef generatorName, 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()) { ++ auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + +- IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); ++ IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo ); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique>(generatorExpression())); + } +@@ -4102,11 +4096,17 @@ namespace Generators { + } // namespace Catch + + #define GENERATE( ... ) \ +- Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) ++ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ ++ CATCH_INTERNAL_LINEINFO, \ ++ [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + #define GENERATE_COPY( ... ) \ +- Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) ++ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ ++ CATCH_INTERNAL_LINEINFO, \ ++ [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + #define GENERATE_REF( ... ) \ +- Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) ++ Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ ++ CATCH_INTERNAL_LINEINFO, \ ++ [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + + // end catch_generators.hpp + // start catch_generators_generic.hpp +@@ -4516,6 +4516,7 @@ namespace Catch { + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; ++ virtual double minDuration() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector const& getTestsOrTags() const = 0; +@@ -5288,6 +5289,7 @@ namespace Catch { + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; ++ double minDuration = -1; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; +@@ -5338,6 +5340,7 @@ namespace Catch { + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; ++ double minDuration() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + UseColour::YesOrNo useColour() const override; +@@ -5455,6 +5458,8 @@ namespace Catch { + } // namespace Catch + + // end catch_outlier_classification.hpp ++ ++#include + #endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + #include +@@ -5715,6 +5720,9 @@ namespace Catch { + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration( double duration ); + ++ //! Should the reporter show ++ bool shouldShowDuration( IConfig const& config, double duration ); ++ + std::string serializeFilters( std::vector const& container ); + + template +@@ -6108,8 +6116,6 @@ namespace Catch { + + static std::string getDescription(); + +- ReporterPreferences getPreferences() const override; +- + void noMatchingTestCases(std::string const& spec) override; + + void assertionStarting(AssertionInfo const&) override; +@@ -6338,9 +6344,10 @@ namespace Catch { + + void writeTestCase(TestCaseNode const& testCaseNode); + +- void writeSection(std::string const& className, +- std::string const& rootName, +- SectionNode const& sectionNode); ++ void writeSection( std::string const& className, ++ std::string const& rootName, ++ SectionNode const& sectionNode, ++ bool testOkToFail ); + + void writeAssertions(SectionNode const& sectionNode); + void writeAssertion(AssertionStats const& stats); +@@ -6875,7 +6882,7 @@ namespace Catch { + } + iters *= 2; + } +- throw optimized_away_error{}; ++ Catch::throw_exception(optimized_away_error{}); + } + } // namespace Detail + } // namespace Benchmark +@@ -6883,6 +6890,7 @@ namespace Catch { + + // end catch_run_for_at_least.hpp + #include ++#include + + namespace Catch { + namespace Benchmark { +@@ -7053,8 +7061,8 @@ namespace Catch { + double b2 = bias - z1; + double a1 = a(b1); + double a2 = a(b2); +- auto lo = std::max(cumn(a1), 0); +- auto hi = std::min(cumn(a2), n - 1); ++ auto lo = (std::max)(cumn(a1), 0); ++ auto hi = (std::min)(cumn(a2), n - 1); + + return { point, resample[lo], resample[hi], confidence_level }; + } +@@ -7123,7 +7131,9 @@ namespace Catch { + } + template + EnvironmentEstimate> estimate_clock_cost(FloatDuration resolution) { +- auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration(clock_cost_estimation_time_limit)); ++ auto time_limit = (std::min)( ++ resolution * clock_cost_estimation_tick_limit, ++ FloatDuration(clock_cost_estimation_time_limit)); + auto time_clock = [](int k) { + return Detail::measure([k] { + for (int i = 0; i < k; ++i) { +@@ -7463,23 +7473,37 @@ namespace TestCaseTracking { + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); ++ friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { ++ return lhs.name == rhs.name ++ && lhs.location == rhs.location; ++ } + }; + +- struct ITracker; ++ class ITracker; + + using ITrackerPtr = std::shared_ptr; + +- struct ITracker { +- virtual ~ITracker(); ++ class ITracker { ++ NameAndLocation m_nameAndLocation; ++ ++ public: ++ ITracker(NameAndLocation const& nameAndLoc) : ++ m_nameAndLocation(nameAndLoc) ++ {} + + // static queries +- virtual NameAndLocation const& nameAndLocation() const = 0; ++ NameAndLocation const& nameAndLocation() const { ++ return m_nameAndLocation; ++ } ++ ++ virtual ~ITracker(); + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; ++ virtual bool hasStarted() const = 0; + + virtual ITracker& parent() = 0; + +@@ -7534,7 +7558,6 @@ namespace TestCaseTracking { + }; + + using Children = std::vector; +- NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; +@@ -7543,11 +7566,13 @@ namespace TestCaseTracking { + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + +- NameAndLocation const& nameAndLocation() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; ++ bool hasStarted() const override { ++ return m_runState != NotStarted; ++ } + + void addChild( ITrackerPtr const& child ) override; + +@@ -7586,6 +7611,10 @@ namespace TestCaseTracking { + + void addInitialFilters( std::vector const& filters ); + void addNextFilters( std::vector const& filters ); ++ //! Returns filters active in this tracker ++ std::vector const& getFilters() const; ++ //! Returns whitespace-trimmed name of the tracked section ++ std::string const& trimmedName() const; + }; + + } // namespace TestCaseTracking +@@ -7751,7 +7780,7 @@ namespace Catch { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; +- double sg = std::min(mg_min / 4., sb / std::sqrt(n)); ++ double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); + double sg2 = sg * sg; + double sb2 = sb * sb; + +@@ -7770,7 +7799,7 @@ namespace Catch { + return (nc / n) * (sb2 - nc * sg2); + }; + +- return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2; ++ return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; + } + + bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector::iterator first, std::vector::iterator last) { +@@ -7910,7 +7939,11 @@ namespace Catch { + + #ifdef CATCH_PLATFORM_MAC + +- #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ ++ #if defined(__i386__) || defined(__x86_64__) ++ #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ ++ #elif defined(__aarch64__) ++ #define CATCH_TRAP() __asm__(".inst 0xd4200000") ++ #endif + + #elif defined(CATCH_PLATFORM_IPHONE) + +@@ -7956,86 +7989,58 @@ namespace Catch { + + // start catch_fatal_condition.h + +-// start catch_windows_h_proxy.h +- +- +-#if defined(CATCH_PLATFORM_WINDOWS) +- +-#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +-# define CATCH_DEFINED_NOMINMAX +-# define NOMINMAX +-#endif +-#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +-# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +-# define WIN32_LEAN_AND_MEAN +-#endif +- +-#ifdef __AFXDLL +-#include +-#else +-#include +-#endif +- +-#ifdef CATCH_DEFINED_NOMINMAX +-# undef NOMINMAX +-#endif +-#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +-# undef WIN32_LEAN_AND_MEAN +-#endif +- +-#endif // defined(CATCH_PLATFORM_WINDOWS) +- +-// end catch_windows_h_proxy.h +-#if defined( CATCH_CONFIG_WINDOWS_SEH ) ++#include + + namespace Catch { + +- struct FatalConditionHandler { +- +- static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); ++ // Wrapper for platform-specific fatal error (signals/SEH) handlers ++ // ++ // Tries to be cooperative with other handlers, and not step over ++ // other handlers. This means that unknown structured exceptions ++ // are passed on, previous signal handlers are called, and so on. ++ // ++ // Can only be instantiated once, and assumes that once a signal ++ // is caught, the binary will end up terminating. Thus, there ++ class FatalConditionHandler { ++ bool m_started = false; ++ ++ // Install/disengage implementation for specific platform. ++ // Should be if-defed to work on current platform, can assume ++ // engage-disengage 1:1 pairing. ++ void engage_platform(); ++ void disengage_platform(); ++ public: ++ // Should also have platform-specific implementations as needed + FatalConditionHandler(); +- static void reset(); + ~FatalConditionHandler(); + +- private: +- static bool isSet; +- static ULONG guaranteeSize; +- static PVOID exceptionHandlerHandle; +- }; +- +-} // namespace Catch +- +-#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) +- +-#include +- +-namespace Catch { +- +- struct FatalConditionHandler { +- +- static bool isSet; +- static struct sigaction oldSigActions[]; +- static stack_t oldSigStack; +- static char altStackMem[]; +- +- static void handleSignal( int sig ); ++ void engage() { ++ assert(!m_started && "Handler cannot be installed twice."); ++ m_started = true; ++ engage_platform(); ++ } + +- FatalConditionHandler(); +- ~FatalConditionHandler(); +- static void reset(); ++ void disengage() { ++ assert(m_started && "Handler cannot be uninstalled without being installed first"); ++ m_started = false; ++ disengage_platform(); ++ } + }; + +-} // namespace Catch +- +-#else +- +-namespace Catch { +- struct FatalConditionHandler { +- void reset(); ++ //! Simple RAII guard for (dis)engaging the FatalConditionHandler ++ class FatalConditionHandlerGuard { ++ FatalConditionHandler* m_handler; ++ public: ++ FatalConditionHandlerGuard(FatalConditionHandler* handler): ++ m_handler(handler) { ++ m_handler->engage(); ++ } ++ ~FatalConditionHandlerGuard() { ++ m_handler->disengage(); ++ } + }; +-} + +-#endif ++} // end namespace Catch + + // end catch_fatal_condition.h + #include +@@ -8095,7 +8100,7 @@ namespace Catch { + void sectionEnded( SectionEndInfo const& endInfo ) override; + void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + +- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; ++ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + + #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing( std::string const& name ) override; +@@ -8161,6 +8166,7 @@ namespace Catch { + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; ++ FatalConditionHandler m_fatalConditionhandler; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; +@@ -9071,7 +9077,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( std::tolower(c) ); } ); ++ std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned 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") +@@ -9840,6 +9846,9 @@ namespace Catch { + | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) + ["-d"]["--durations"] + ( "show test durations" ) ++ | Opt( config.minDuration, "seconds" ) ++ ["-D"]["--min-duration"] ++ ( "show test durations for tests taking at least the given number of seconds" ) + | Opt( loadTestNamesFromFile, "filename" ) + ["-f"]["--input-file"] + ( "load test names to run from a file" ) +@@ -9987,6 +9996,7 @@ namespace Catch { + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } ++ double Config::minDuration() const { return m_data.minDuration; } + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } +@@ -10029,6 +10039,36 @@ namespace Catch { + } + + // end catch_errno_guard.h ++// start catch_windows_h_proxy.h ++ ++ ++#if defined(CATCH_PLATFORM_WINDOWS) ++ ++#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) ++# define CATCH_DEFINED_NOMINMAX ++# define NOMINMAX ++#endif ++#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) ++# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN ++# define WIN32_LEAN_AND_MEAN ++#endif ++ ++#ifdef __AFXDLL ++#include ++#else ++#include ++#endif ++ ++#ifdef CATCH_DEFINED_NOMINMAX ++# undef NOMINMAX ++#endif ++#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN ++# undef WIN32_LEAN_AND_MEAN ++#endif ++ ++#endif // defined(CATCH_PLATFORM_WINDOWS) ++ ++// end catch_windows_h_proxy.h + #include + + namespace Catch { +@@ -10545,7 +10585,7 @@ namespace Catch { + // Extracts the actual name part of an enum instance + // In other words, it returns the Blue part of Bikeshed::Colour::Blue + StringRef extractInstanceName(StringRef enumInstance) { +- // Find last occurence of ":" ++ // Find last occurrence of ":" + size_t name_start = enumInstance.size(); + while (name_start > 0 && enumInstance[name_start - 1] != ':') { + --name_start; +@@ -10707,25 +10747,47 @@ namespace Catch { + // end catch_exception_translator_registry.cpp + // start catch_fatal_condition.cpp + +-#if defined(__GNUC__) +-# pragma GCC diagnostic push +-# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +-#endif ++#include ++ ++#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) ++ ++namespace Catch { ++ ++ // If neither SEH nor signal handling is required, the handler impls ++ // do not have to do anything, and can be empty. ++ void FatalConditionHandler::engage_platform() {} ++ void FatalConditionHandler::disengage_platform() {} ++ FatalConditionHandler::FatalConditionHandler() = default; ++ FatalConditionHandler::~FatalConditionHandler() = default; ++ ++} // end namespace Catch ++ ++#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS ++ ++#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) ++#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" ++#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS + + #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) + + namespace { +- // Report the error condition ++ //! Signals fatal error message to the run context + void reportFatal( char const * const message ) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); + } +-} + +-#endif // signals/SEH handling ++ //! Minimal size Catch2 needs for its own fatal error handling. ++ //! Picked anecdotally, so it might not be sufficient on all ++ //! platforms, and for all configurations. ++ constexpr std::size_t minStackSizeForErrors = 32 * 1024; ++} // end unnamed namespace ++ ++#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS + + #if defined( CATCH_CONFIG_WINDOWS_SEH ) + + namespace Catch { ++ + struct SignalDefs { DWORD id; const char* name; }; + + // There is no 1-1 mapping between signals and windows exceptions. +@@ -10738,7 +10800,7 @@ namespace Catch { + { static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, + }; + +- LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { ++ static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const& def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); +@@ -10749,38 +10811,50 @@ namespace Catch { + return EXCEPTION_CONTINUE_SEARCH; + } + ++ // Since we do not support multiple instantiations, we put these ++ // into global variables and rely on cleaning them up in outlined ++ // constructors/destructors ++ static PVOID exceptionHandlerHandle = nullptr; ++ ++ // For MSVC, we reserve part of the stack memory for handling ++ // memory overflow structured exception. + FatalConditionHandler::FatalConditionHandler() { +- isSet = true; +- // 32k seems enough for Catch to handle stack overflow, +- // but the value was found experimentally, so there is no strong guarantee +- guaranteeSize = 32 * 1024; +- exceptionHandlerHandle = nullptr; ++ ULONG guaranteeSize = static_cast(minStackSizeForErrors); ++ if (!SetThreadStackGuarantee(&guaranteeSize)) { ++ // We do not want to fully error out, because needing ++ // the stack reserve should be rare enough anyway. ++ Catch::cerr() ++ << "Failed to reserve piece of stack." ++ << " Stack overflows will not be reported successfully."; ++ } ++ } ++ ++ // We do not attempt to unset the stack guarantee, because ++ // Windows does not support lowering the stack size guarantee. ++ FatalConditionHandler::~FatalConditionHandler() = default; ++ ++ void FatalConditionHandler::engage_platform() { + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); +- // Pass in guarantee size to be filled +- SetThreadStackGuarantee(&guaranteeSize); ++ if (!exceptionHandlerHandle) { ++ CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); ++ } + } + +- void FatalConditionHandler::reset() { +- if (isSet) { +- RemoveVectoredExceptionHandler(exceptionHandlerHandle); +- SetThreadStackGuarantee(&guaranteeSize); +- exceptionHandlerHandle = nullptr; +- isSet = false; ++ void FatalConditionHandler::disengage_platform() { ++ if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { ++ CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); + } ++ exceptionHandlerHandle = nullptr; + } + +- FatalConditionHandler::~FatalConditionHandler() { +- reset(); +- } ++} // end namespace Catch + +-bool FatalConditionHandler::isSet = false; +-ULONG FatalConditionHandler::guaranteeSize = 0; +-PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; ++#endif // CATCH_CONFIG_WINDOWS_SEH + +-} // namespace Catch ++#if defined( CATCH_CONFIG_POSIX_SIGNALS ) + +-#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) ++#include + + namespace Catch { + +@@ -10789,10 +10863,6 @@ namespace Catch { + const char* name; + }; + +- // 32kb for the alternate stack seems to be sufficient. However, this value +- // is experimentally determined, so that's not guaranteed. +- static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; +- + static SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, +@@ -10802,7 +10872,32 @@ namespace Catch { + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + +- void FatalConditionHandler::handleSignal( int sig ) { ++// Older GCCs trigger -Wmissing-field-initializers for T foo = {} ++// which is zero initialization, but not explicit. We want to avoid ++// that. ++#if defined(__GNUC__) ++# pragma GCC diagnostic push ++# pragma GCC diagnostic ignored "-Wmissing-field-initializers" ++#endif ++ ++ static char* altStackMem = nullptr; ++ static std::size_t altStackSize = 0; ++ static stack_t oldSigStack{}; ++ static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; ++ ++ static void restorePreviousSignalHandlers() { ++ // We set signal handlers back to the previous ones. Hopefully ++ // nobody overwrote them in the meantime, and doesn't expect ++ // their signal handlers to live past ours given that they ++ // installed them after ours.. ++ for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { ++ sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); ++ } ++ // Return the old stack ++ sigaltstack(&oldSigStack, nullptr); ++ } ++ ++ static void handleSignal( int sig ) { + char const * name = ""; + for (auto const& def : signalDefs) { + if (sig == def.id) { +@@ -10810,16 +10905,33 @@ namespace Catch { + break; + } + } +- reset(); +- reportFatal(name); ++ // We need to restore previous signal handlers and let them do ++ // their thing, so that the users can have the debugger break ++ // when a signal is raised, and so on. ++ restorePreviousSignalHandlers(); ++ reportFatal( name ); + raise( sig ); + } + + FatalConditionHandler::FatalConditionHandler() { +- isSet = true; ++ assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); ++ if (altStackSize == 0) { ++ altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); ++ } ++ altStackMem = new char[altStackSize](); ++ } ++ ++ FatalConditionHandler::~FatalConditionHandler() { ++ delete[] altStackMem; ++ // We signal that another instance can be constructed by zeroing ++ // out the pointer. ++ altStackMem = nullptr; ++ } ++ ++ void FatalConditionHandler::engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; +- sigStack.ss_size = sigStackSize; ++ sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { }; +@@ -10831,40 +10943,17 @@ namespace Catch { + } + } + +- FatalConditionHandler::~FatalConditionHandler() { +- reset(); +- } ++#if defined(__GNUC__) ++# pragma GCC diagnostic pop ++#endif + +- void FatalConditionHandler::reset() { +- if( isSet ) { +- // Set signals back to previous values -- hopefully nobody overwrote them in the meantime +- for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { +- sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); +- } +- // Return the old stack +- sigaltstack(&oldSigStack, nullptr); +- isSet = false; +- } ++ void FatalConditionHandler::disengage_platform() { ++ restorePreviousSignalHandlers(); + } + +- bool FatalConditionHandler::isSet = false; +- struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; +- stack_t FatalConditionHandler::oldSigStack = {}; +- char FatalConditionHandler::altStackMem[sigStackSize] = {}; +- +-} // namespace Catch +- +-#else +- +-namespace Catch { +- void FatalConditionHandler::reset() {} +-} +- +-#endif // signals/SEH handling ++} // end namespace Catch + +-#if defined(__GNUC__) +-# pragma GCC diagnostic pop +-#endif ++#endif // CATCH_CONFIG_POSIX_SIGNALS + // end catch_fatal_condition.cpp + // start catch_generators.cpp + +@@ -10883,8 +10972,8 @@ namespace Generators { + + GeneratorUntypedBase::~GeneratorUntypedBase() {} + +- auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { +- return getResultCapture().acquireGeneratorTracker( lineInfo ); ++ auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { ++ return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); + } + + } // namespace Generators +@@ -11419,7 +11508,8 @@ namespace { + return lhs == rhs; + } + +- auto ulpDiff = std::abs(lc - rc); ++ // static cast as a workaround for IBM XLC ++ auto ulpDiff = std::abs(static_cast(lc - rc)); + return static_cast(ulpDiff) <= maxUlpDiff; + } + +@@ -11593,7 +11683,6 @@ Floating::WithinRelMatcher WithinRel(float target) { + + } // namespace Matchers + } // namespace Catch +- + // end catch_matchers_floating.cpp + // start catch_matchers_generic.cpp + +@@ -12009,7 +12098,7 @@ namespace Catch { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); + } +- if (fopen_s(&m_file, m_buffer, "w")) { ++ if (fopen_s(&m_file, m_buffer, "w+")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); +@@ -12304,11 +12393,13 @@ namespace Catch { + namespace Catch { + + class StartupExceptionRegistry { ++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + public: + void add(std::exception_ptr const& exception) noexcept; + std::vector const& getExceptions() const noexcept; + private: + std::vector m_exceptions; ++#endif + }; + + } // end namespace Catch +@@ -12391,7 +12482,11 @@ namespace Catch { + m_tagAliasRegistry.add( alias, tag, lineInfo ); + } + void registerStartupException() noexcept override { ++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + m_exceptionRegistry.add(std::current_exception()); ++#else ++ CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); ++#endif + } + IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { + return m_enumValuesRegistry; +@@ -12495,17 +12590,32 @@ namespace Catch { + std::shared_ptr tracker; + + ITracker& currentTracker = ctx.currentTracker(); +- if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { ++ // Under specific circumstances, the generator we want ++ // to acquire is also the current tracker. If this is ++ // the case, we have to avoid looking through current ++ // tracker's children, and instead return the current ++ // tracker. ++ // A case where this check is important is e.g. ++ // for (int i = 0; i < 5; ++i) { ++ // int n = GENERATE(1, 2); ++ // } ++ // ++ // without it, the code above creates 5 nested generators. ++ if (currentTracker.nameAndLocation() == nameAndLocation) { ++ auto thisTracker = currentTracker.parent().findChild(nameAndLocation); ++ assert(thisTracker); ++ assert(thisTracker->isGeneratorTracker()); ++ tracker = std::static_pointer_cast(thisTracker); ++ } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = std::static_pointer_cast( childTracker ); +- } +- else { ++ } else { + tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( tracker ); + } + +- if( !ctx.completedCycle() && !tracker->isComplete() ) { ++ if( !tracker->isComplete() ) { + tracker->open(); + } + +@@ -12519,8 +12629,68 @@ namespace Catch { + } + void close() override { + TrackerBase::close(); +- // Generator interface only finds out if it has another item on atual move +- if (m_runState == CompletedSuccessfully && m_generator->next()) { ++ // If a generator has a child (it is followed by a section) ++ // and none of its children have started, then we must wait ++ // until later to start consuming its values. ++ // This catches cases where `GENERATE` is placed between two ++ // `SECTION`s. ++ // **The check for m_children.empty cannot be removed**. ++ // doing so would break `GENERATE` _not_ followed by `SECTION`s. ++ const bool should_wait_for_child = [&]() { ++ // No children -> nobody to wait for ++ if ( m_children.empty() ) { ++ return false; ++ } ++ // If at least one child started executing, don't wait ++ if ( std::find_if( ++ m_children.begin(), ++ m_children.end(), ++ []( TestCaseTracking::ITrackerPtr tracker ) { ++ return tracker->hasStarted(); ++ } ) != m_children.end() ) { ++ return false; ++ } ++ ++ // No children have started. We need to check if they _can_ ++ // start, and thus we should wait for them, or they cannot ++ // start (due to filters), and we shouldn't wait for them ++ auto* parent = m_parent; ++ // This is safe: there is always at least one section ++ // tracker in a test case tracking tree ++ while ( !parent->isSectionTracker() ) { ++ parent = &( parent->parent() ); ++ } ++ assert( parent && ++ "Missing root (test case) level section" ); ++ ++ auto const& parentSection = ++ static_cast( *parent ); ++ auto const& filters = parentSection.getFilters(); ++ // No filters -> no restrictions on running sections ++ if ( filters.empty() ) { ++ return true; ++ } ++ ++ for ( auto const& child : m_children ) { ++ if ( child->isSectionTracker() && ++ std::find( filters.begin(), ++ filters.end(), ++ static_cast( *child ) ++ .trimmedName() ) != ++ filters.end() ) { ++ return true; ++ } ++ } ++ return false; ++ }(); ++ ++ // This check is a bit tricky, because m_generator->next() ++ // has a side-effect, where it consumes generator's current ++ // value, but we do not want to invoke the side-effect if ++ // this generator is still waiting for any child to start. ++ if ( should_wait_for_child || ++ ( m_runState == CompletedSuccessfully && ++ m_generator->next() ) ) { + m_children.clear(); + m_runState = Executing; + } +@@ -12656,10 +12826,10 @@ namespace Catch { + + return true; + } +- auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { ++ auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + using namespace Generators; +- GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); +- assert( tracker.isOpen() ); ++ GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, ++ TestCaseTracking::NameAndLocation( static_cast(generatorName), lineInfo ) ); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } +@@ -12702,17 +12872,17 @@ namespace Catch { + + #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void RunContext::benchmarkPreparing(std::string const& name) { +- m_reporter->benchmarkPreparing(name); +- } ++ m_reporter->benchmarkPreparing(name); ++ } + void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { + m_reporter->benchmarkStarting( info ); + } + void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { + m_reporter->benchmarkEnded( stats ); + } +- void RunContext::benchmarkFailed(std::string const & error) { +- m_reporter->benchmarkFailed(error); +- } ++ void RunContext::benchmarkFailed(std::string const & error) { ++ m_reporter->benchmarkFailed(error); ++ } + #endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void RunContext::pushScopedMessage(MessageInfo const & message) { +@@ -12846,9 +13016,8 @@ namespace Catch { + } + + void RunContext::invokeActiveTestCase() { +- FatalConditionHandler fatalConditionHandler; // Handle signals ++ FatalConditionHandlerGuard _(&m_fatalConditionhandler); + m_activeTestCase->invoke(); +- fatalConditionHandler.reset(); + } + + void RunContext::handleUnfinishedSections() { +@@ -13433,6 +13602,7 @@ namespace Catch { + // end catch_singletons.cpp + // start catch_startup_exception_registry.cpp + ++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + namespace Catch { + void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { + CATCH_TRY { +@@ -13448,6 +13618,7 @@ void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexce + } + + } // end namespace Catch ++#endif + // end catch_startup_exception_registry.cpp + // start catch_stream.cpp + +@@ -13632,7 +13803,7 @@ namespace Catch { + + namespace { + char toLowerCh(char c) { +- return static_cast( std::tolower( c ) ); ++ return static_cast( std::tolower( static_cast(c) ) ); + } + } + +@@ -14015,24 +14186,28 @@ namespace Catch { + + namespace { + struct TestHasher { +- explicit TestHasher(Catch::SimplePcg32& rng) { +- basis = rng(); +- basis <<= 32; +- basis |= rng(); +- } ++ using hash_t = uint64_t; + +- uint64_t basis; ++ explicit TestHasher( hash_t hashSuffix ): ++ m_hashSuffix{ hashSuffix } {} + +- uint64_t operator()(TestCase const& t) const { +- // Modified FNV-1a hash +- static constexpr uint64_t prime = 1099511628211; +- uint64_t hash = basis; +- for (const char c : t.name) { ++ uint32_t operator()( TestCase const& t ) const { ++ // FNV-1a hash with multiplication fold. ++ const hash_t prime = 1099511628211u; ++ hash_t hash = 14695981039346656037u; ++ for ( const char c : t.name ) { + hash ^= c; + hash *= prime; + } +- return hash; ++ hash ^= m_hashSuffix; ++ hash *= prime; ++ const uint32_t low{ static_cast( hash ) }; ++ const uint32_t high{ static_cast( hash >> 32 ) }; ++ return low * high; + } ++ ++ private: ++ hash_t m_hashSuffix; + }; + } // end unnamed namespace + +@@ -14050,9 +14225,9 @@ namespace Catch { + + case RunTests::InRandomOrder: { + seedRng( config ); +- TestHasher h( rng() ); ++ TestHasher h{ config.rngSeed() }; + +- using hashedTest = std::pair; ++ using hashedTest = std::pair; + std::vector indexed_tests; + indexed_tests.reserve( unsortedTestCases.size() ); + +@@ -14215,15 +14390,12 @@ namespace TestCaseTracking { + m_currentTracker = tracker; + } + +- TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) +- : m_nameAndLocation( nameAndLocation ), ++ TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): ++ ITracker(nameAndLocation), + m_ctx( ctx ), + m_parent( parent ) + {} + +- NameAndLocation const& TrackerBase::nameAndLocation() const { +- return m_nameAndLocation; +- } + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } +@@ -14339,7 +14511,8 @@ namespace TestCaseTracking { + bool SectionTracker::isComplete() const { + bool complete = true; + +- if ((m_filters.empty() || m_filters[0] == "") ++ if (m_filters.empty() ++ || m_filters[0] == "" + || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { + complete = TrackerBase::isComplete(); + } +@@ -14384,6 +14557,14 @@ namespace TestCaseTracking { + m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); + } + ++ std::vector const& SectionTracker::getFilters() const { ++ return m_filters; ++ } ++ ++ std::string const& SectionTracker::trimmedName() const { ++ return m_trimmed_name; ++ } ++ + } // namespace TestCaseTracking + + using TestCaseTracking::ITracker; +@@ -15118,11 +15299,48 @@ namespace Catch { + // end catch_totals.cpp + // start catch_uncaught_exceptions.cpp + ++// start catch_config_uncaught_exceptions.hpp ++ ++// Copyright Catch2 Authors ++// Distributed under the Boost Software License, Version 1.0. ++// (See accompanying file LICENSE_1_0.txt or copy at ++// https://www.boost.org/LICENSE_1_0.txt) ++ ++// SPDX-License-Identifier: BSL-1.0 ++ ++#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP ++#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP ++ ++#if defined(_MSC_VER) ++# if _MSC_VER >= 1900 // Visual Studio 2015 or newer ++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS ++# endif ++#endif ++ ++#include ++ ++#if defined(__cpp_lib_uncaught_exceptions) \ ++ && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) ++ ++# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS ++#endif // __cpp_lib_uncaught_exceptions ++ ++#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \ ++ && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ ++ && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) ++ ++# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS ++#endif ++ ++#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP ++// end catch_config_uncaught_exceptions.hpp + #include + + namespace Catch { + bool uncaught_exceptions() { +-#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) ++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) ++ return false; ++#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; + #else + return std::uncaught_exception(); +@@ -15162,7 +15380,7 @@ namespace Catch { + } + + Version const& libraryVersion() { +- static Version version( 2, 12, 2, "", 0 ); ++ static Version version( 2, 13, 7, "", 0 ); + return version; + } + +@@ -15564,6 +15782,17 @@ namespace Catch { + return std::string(buffer); + } + ++ bool shouldShowDuration( IConfig const& config, double duration ) { ++ if ( config.showDurations() == ShowDurations::Always ) { ++ return true; ++ } ++ if ( config.showDurations() == ShowDurations::Never ) { ++ return false; ++ } ++ const double min = config.minDuration(); ++ return min >= 0 && duration >= min; ++ } ++ + std::string serializeFilters( std::vector const& container ) { + ReusableStringStream oss; + bool first = true; +@@ -15830,10 +16059,6 @@ private: + return "Reports test results on a single line, suitable for IDEs"; + } + +- ReporterPreferences CompactReporter::getPreferences() const { +- return m_reporterPrefs; +- } +- + void CompactReporter::noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } +@@ -15860,8 +16085,9 @@ private: + } + + void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { +- if (m_config->showDurations() == ShowDurations::Always) { +- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; ++ double dur = _sectionStats.durationInSeconds; ++ if ( shouldShowDuration( *m_config, dur ) ) { ++ stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + +@@ -16281,8 +16507,9 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } +- if (m_config->showDurations() == ShowDurations::Always) { +- stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; ++ double dur = _sectionStats.durationInSeconds; ++ if (shouldShowDuration(*m_config, dur)) { ++ stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; +@@ -16566,6 +16793,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter) + #include + #include + #include ++#include + + namespace Catch { + +@@ -16593,7 +16821,7 @@ namespace Catch { + #else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + #endif +- return std::string(timeStamp); ++ return std::string(timeStamp, timeStampSize-1); + } + + std::string fileNameTag(const std::vector &tags) { +@@ -16604,6 +16832,17 @@ namespace Catch { + return it->substr(1); + return std::string(); + } ++ ++ // Formats the duration in seconds to 3 decimal places. ++ // This is done because some genius defined Maven Surefire schema ++ // in a way that only accepts 3 decimal places, and tools like ++ // Jenkins use that schema for validation JUnit reporter output. ++ std::string formatDuration( double seconds ) { ++ ReusableStringStream rss; ++ rss << std::fixed << std::setprecision( 3 ) << seconds; ++ return rss.str(); ++ } ++ + } // anonymous namespace + + JunitReporter::JunitReporter( ReporterConfig const& _config ) +@@ -16673,7 +16912,7 @@ namespace Catch { + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else +- xml.writeAttribute( "time", suiteTime ); ++ xml.writeAttribute( "time", formatDuration( suiteTime ) ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write properties if there are any +@@ -16718,12 +16957,13 @@ namespace Catch { + if ( !m_config->name().empty() ) + className = m_config->name() + "." + className; + +- writeSection( className, "", rootSection ); ++ writeSection( className, "", rootSection, stats.testInfo.okToFail() ); + } + +- void JunitReporter::writeSection( std::string const& className, +- std::string const& rootName, +- SectionNode const& sectionNode ) { ++ void JunitReporter::writeSection( std::string const& className, ++ std::string const& rootName, ++ SectionNode const& sectionNode, ++ bool testOkToFail) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; +@@ -16740,13 +16980,18 @@ namespace Catch { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } +- xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); ++ xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) ); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute( "status", "run" ); + ++ if (sectionNode.stats.assertions.failedButOk) { ++ xml.scopedElement("skipped") ++ .writeAttribute("message", "TEST_CASE tagged with !mayfail"); ++ } ++ + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) +@@ -16756,9 +17001,9 @@ namespace Catch { + } + for( auto const& childNode : sectionNode.childSections ) + if( className.empty() ) +- writeSection( name, "", *childNode ); ++ writeSection( name, "", *childNode, testOkToFail ); + else +- writeSection( className, name, *childNode ); ++ writeSection( className, name, *childNode, testOkToFail ); + } + + void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { +@@ -17180,6 +17425,10 @@ namespace Catch { + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); ++ m_xml.scopedElement( "OverallResultsCases") ++ .writeAttribute( "successes", testGroupStats.totals.testCases.passed ) ++ .writeAttribute( "failures", testGroupStats.totals.testCases.failed ) ++ .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk ); + m_xml.endElement(); + } + +@@ -17189,6 +17438,10 @@ namespace Catch { + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); ++ m_xml.scopedElement( "OverallResultsCases") ++ .writeAttribute( "successes", testRunStats.totals.testCases.passed ) ++ .writeAttribute( "failures", testRunStats.totals.testCases.failed ) ++ .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk ); + m_xml.endElement(); + } + diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 000000000..dff08b4af --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +0003-Update-catch2-to-2.13.7.patch