diff --git a/CMakeLists.txt b/CMakeLists.txt
index bda66bc4..9c2fda01 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.9.1)
+project(Catch2 LANGUAGES CXX VERSION 2.9.2)
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
diff --git a/README.md b/README.md
index e22a82a5..98a95034 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/5icuqPwk9miJLAL1)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/8YrGVqYqqSC4Sc5R)
[![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 3b67e147..3e760c7c 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -2,6 +2,7 @@
# Release notes
**Contents**
+[2.9.2](#292)
[2.9.1](#291)
[2.9.0](#290)
[2.8.0](#280)
@@ -26,6 +27,42 @@
[Older versions](#older-versions)
[Even Older versions](#even-older-versions)
+
+## 2.9.2
+
+### Fixes
+* `ChunkGenerator` can now be used with chunks of size 0 (#1671)
+* Nested subsections are now run properly when specific section is run via the `-c` argument (#1670, #1673)
+* Catch2 now consistently uses `_WIN32` to detect Windows platform (#1676)
+* `TEMPLATE_LIST_TEST_CASE` now support non-default constructible type lists (#1697)
+* Fixed a crash in the XMLReporter when a benchmark throws exception during warmup (#1706)
+* Fixed a possible infinite loop in CompactReporter (#1715)
+* Fixed `-w NoTests` returning 0 even when no tests were matched (#1449, #1683, #1684)
+* Fixed matcher compilation under Obj-C++ (#1661)
+
+### Improvements
+* `RepeatGenerator` and `FixedValuesGenerator` now fail to compile when used with `bool` (#1692)
+ * Previously they would fail at runtime.
+* Catch2 now supports Android's debug logging for its debug output (#1710)
+* Catch2 now detects and configures itself for the RTX platform (#1693)
+ * You still need to pass `--benchmark-no-analysis` if you are using benchmarking under RTX
+* Removed a "storage class is not first" warning when compiling Catch2 with PGI compiler (#1717)
+
+### Miscellaneous
+* Documentation now contains indication when a specific feature was introduced (#1695)
+ * These start with Catch2 v2.3.0, (a bit over a year ago).
+ * `docs/contributing.md` has been updated to provide contributors guidance on how to add these to newly written documentation
+* Various other documentation improvements
+ * ToC fixes
+ * Documented `--order` and `--rng-seed` command line options
+ * Benchmarking documentation now clearly states that it requires opt-in
+ * Documented `CATCH_CONFIG_CPP17_OPTIONAL` and `CATCH_CONFIG_CPP17_BYTE` macros
+ * Properly documented built-in vector matchers
+ * Improved `*_THROWS_MATCHES` documentation a bit
+* CMake config file is now arch-independent even if `CMAKE_SIZEOF_VOID_P` is in CMake cache (#1660)
+* `CatchAddTests` now properly escapes `[` and `]` in test names (#1634, #1698)
+
+
## 2.9.1
### Fixes
diff --git a/include/catch.hpp b/include/catch.hpp
index 148d0430..dba005ad 100644
--- a/include/catch.hpp
+++ b/include/catch.hpp
@@ -11,7 +11,7 @@
#define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 9
-#define CATCH_VERSION_PATCH 1
+#define CATCH_VERSION_PATCH 2
#ifdef __clang__
# pragma clang system_header
diff --git a/include/internal/catch_version.cpp b/include/internal/catch_version.cpp
index 44b529e5..03fded91 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, 9, 1, "", 0 );
+ static Version version( 2, 9, 2, "", 0 );
return version;
}
diff --git a/single_include/catch2/catch.hpp b/single_include/catch2/catch.hpp
index 303f664f..5feb2a4b 100644
--- a/single_include/catch2/catch.hpp
+++ b/single_include/catch2/catch.hpp
@@ -1,6 +1,6 @@
/*
- * Catch v2.9.1
- * Generated: 2019-06-17 11:59:24.363643
+ * Catch v2.9.2
+ * Generated: 2019-08-08 13:35:12.279703
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved.
@@ -15,7 +15,7 @@
#define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 9
-#define CATCH_VERSION_PATCH 1
+#define CATCH_VERSION_PATCH 2
#ifdef __clang__
# pragma clang system_header
@@ -275,6 +275,17 @@ namespace Catch {
#define CATCH_INTERNAL_CONFIG_COUNTER
#endif
+////////////////////////////////////////////////////////////////////////////////
+
+// RTX is a special version of Windows that is real time.
+// This means that it is detected as Windows, but does not provide
+// the same set of capabilities as real Windows does.
+#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
+ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+ #define CATCH_INTERNAL_CONFIG_NO_ASYNC
+ #define CATCH_CONFIG_COLOUR_NONE
+#endif
+
////////////////////////////////////////////////////////////////////////////////
// Check if string_view is available and usable
// The check is split apart to work around v140 (VS2015) preprocessor issue...
@@ -292,6 +303,14 @@ namespace Catch {
# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
#endif // __has_include
+////////////////////////////////////////////////////////////////////////////////
+// Check if byte is available and usable
+#if defined(__has_include)
+# if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
+# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+#endif // __has_include
+
////////////////////////////////////////////////////////////////////////////////
// Check if variant is available and usable
#if defined(__has_include)
@@ -346,6 +365,10 @@ namespace Catch {
# define CATCH_CONFIG_CPP17_VARIANT
#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+# define CATCH_CONFIG_CPP17_BYTE
+#endif
+
#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
#endif
@@ -362,7 +385,7 @@ namespace Catch {
# define CATCH_CONFIG_POLYFILL_ISNAN
#endif
-#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
+#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
# define CATCH_CONFIG_USE_ASYNC
#endif
@@ -515,6 +538,7 @@ namespace Catch {
virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
};
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector const& getAllTestCasesSorted( IConfig const& config );
@@ -1153,7 +1177,7 @@ struct AutoReg : NonCopyable {
} \
};\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
- using TestInit = decltype(convert(TmplList {})); \
+ using TestInit = decltype(convert(std::declval())); \
TestInit t; \
t.reg_tests(); \
return 0; \
@@ -1279,7 +1303,7 @@ struct AutoReg : NonCopyable {
}\
};\
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- using TestInit = decltype(convert(TmplList {}));\
+ using TestInit = decltype(convert(std::declval()));\
TestInit t;\
t.reg_tests();\
return 0;\
@@ -1679,6 +1703,12 @@ namespace Catch {
}
};
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+ template<>
+ struct StringMaker {
+ static std::string convert(std::byte value);
+ };
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
template<>
struct StringMaker {
static std::string convert(int value);
@@ -3180,6 +3210,15 @@ namespace Matchers {
virtual bool match( ObjectT const& arg ) const = 0;
};
+#if defined(__OBJC__)
+ // Hack to fix Catch GH issue #1661. Could use id for generic Object support.
+ // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
+ template<>
+ struct MatcherMethod {
+ virtual bool match( NSString* arg ) const = 0;
+ };
+#endif
+
#ifdef __clang__
# pragma clang diagnostic pop
#endif
@@ -3829,6 +3868,9 @@ namespace Generators {
template
class FixedValuesGenerator final : public IGenerator {
+ static_assert(!std::is_same::value,
+ "ValuesGenerator does not support bools because of std::vector"
+ "specialization, use SingleValue Generator instead.");
std::vector m_values;
size_t m_idx = 0;
public:
@@ -4048,6 +4090,9 @@ namespace Generators {
template
class RepeatGenerator : public IGenerator {
+ static_assert(!std::is_same::value,
+ "RepeatGenerator currently does not support bools"
+ "because of std::vector specialization");
GeneratorWrapper m_generator;
mutable std::vector m_returned;
size_t m_target_repeats;
@@ -4162,12 +4207,14 @@ namespace Generators {
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"));
- }
+ if (m_chunk_size != 0) {
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 {
@@ -4711,7 +4758,7 @@ namespace Catch {
arcSafeRelease( m_substr );
}
- bool match( NSString* const& str ) const override {
+ bool match( NSString* str ) const override {
return false;
}
@@ -4721,7 +4768,7 @@ namespace Catch {
struct Equals : StringHolder {
Equals( NSString* substr ) : StringHolder( substr ){}
- bool match( NSString* const& str ) const override {
+ bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str isEqualToString:m_substr];
}
@@ -4734,7 +4781,7 @@ namespace Catch {
struct Contains : StringHolder {
Contains( NSString* substr ) : StringHolder( substr ){}
- bool match( NSString* const& str ) const override {
+ bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str rangeOfString:m_substr].location != NSNotFound;
}
@@ -4747,7 +4794,7 @@ namespace Catch {
struct StartsWith : StringHolder {
StartsWith( NSString* substr ) : StringHolder( substr ){}
- bool match( NSString* const& str ) const override {
+ bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str rangeOfString:m_substr].location == 0;
}
@@ -4759,7 +4806,7 @@ namespace Catch {
struct EndsWith : StringHolder {
EndsWith( NSString* substr ) : StringHolder( substr ){}
- bool match( NSString* const& str ) const override {
+ bool match( NSString* str ) const override {
return (str != nil || m_substr == nil ) &&
[str rangeOfString:m_substr].location == [str length] - [m_substr length];
}
@@ -4867,17 +4914,23 @@ namespace Catch
namespace Catch {
+ struct IConfig;
+
class TestSpec {
- struct Pattern {
+ class Pattern {
+ public:
+ explicit Pattern( std::string const& name );
virtual ~Pattern();
virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+ std::string const& name() const;
+ private:
+ std::string const m_name;
};
using PatternPtr = std::shared_ptr;
class NamePattern : public Pattern {
public:
- NamePattern( std::string const& name );
- virtual ~NamePattern();
+ explicit NamePattern( std::string const& name, std::string const& filterString );
bool matches( TestCaseInfo const& testCase ) const override;
private:
WildcardPattern m_wildcardPattern;
@@ -4885,8 +4938,7 @@ namespace Catch {
class TagPattern : public Pattern {
public:
- TagPattern( std::string const& tag );
- virtual ~TagPattern();
+ explicit TagPattern( std::string const& tag, std::string const& filterString );
bool matches( TestCaseInfo const& testCase ) const override;
private:
std::string m_tag;
@@ -4894,8 +4946,7 @@ namespace Catch {
class ExcludedPattern : public Pattern {
public:
- ExcludedPattern( PatternPtr const& underlyingPattern );
- virtual ~ExcludedPattern();
+ explicit ExcludedPattern( PatternPtr const& underlyingPattern );
bool matches( TestCaseInfo const& testCase ) const override;
private:
PatternPtr m_underlyingPattern;
@@ -4905,11 +4956,19 @@ namespace Catch {
std::vector m_patterns;
bool matches( TestCaseInfo const& testCase ) const;
+ std::string name() const;
};
public:
+ struct FilterMatch {
+ std::string name;
+ std::vector tests;
+ };
+ using Matches = std::vector;
+
bool hasFilters() const;
bool matches( TestCaseInfo const& testCase ) const;
+ Matches matchesByFilter( std::vector const& testCases, IConfig const& config ) const;
private:
std::vector m_filters;
@@ -4949,8 +5008,10 @@ namespace Catch {
enum Mode{ None, Name, QuotedName, Tag, EscapedName };
Mode m_mode = None;
bool m_exclusion = false;
- std::size_t m_start = std::string::npos, m_pos = 0;
+ std::size_t m_pos = 0;
std::string m_arg;
+ std::string m_substring;
+ std::string m_patternName;
std::vector m_escapeChars;
TestSpec::Filter m_currentFilter;
TestSpec m_testSpec;
@@ -4964,26 +5025,32 @@ namespace Catch {
private:
void visitChar( char c );
- void startNewMode( Mode mode, std::size_t start );
+ void startNewMode( Mode mode );
+ bool processNoneChar( char c );
+ void processNameChar( char c );
+ bool processOtherChar( char c );
+ void endMode();
void escape();
- std::string subString() const;
+ bool isControlChar( char c ) const;
template
void addPattern() {
- std::string token = subString();
+ std::string token = m_patternName;
for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
- token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+ token = token.substr( 0, m_escapeChars[i] - i ) + token.substr( m_escapeChars[i] -i +1 );
m_escapeChars.clear();
if( startsWith( token, "exclude:" ) ) {
m_exclusion = true;
token = token.substr( 8 );
}
if( !token.empty() ) {
- TestSpec::PatternPtr pattern = std::make_shared( token );
+ TestSpec::PatternPtr pattern = std::make_shared( token, m_substring );
if( m_exclusion )
pattern = std::make_shared( pattern );
m_currentFilter.m_patterns.push_back( pattern );
}
+ m_substring.clear();
+ m_patternName.clear();
m_exclusion = false;
m_mode = None;
}
@@ -6126,6 +6193,7 @@ namespace Catch {
void testRunEnded(TestRunStats const& testRunStats) override;
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ void benchmarkPreparing(std::string const& name) override;
void benchmarkStarting(BenchmarkInfo const&) override;
void benchmarkEnded(BenchmarkStats<> const&) override;
void benchmarkFailed(std::string const&) override;
@@ -9919,7 +9987,16 @@ namespace Catch {
}
// end catch_debug_console.h
-#ifdef CATCH_PLATFORM_WINDOWS
+#if defined(__ANDROID__)
+#include
+
+ namespace Catch {
+ void writeToDebugConsole( std::string const& text ) {
+ __android_log_print( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
+ }
+ }
+
+#elif defined(CATCH_PLATFORM_WINDOWS)
namespace Catch {
void writeToDebugConsole( std::string const& text ) {
@@ -10373,7 +10450,7 @@ namespace Catch {
// 32kb for the alternate stack seems to be sufficient. However, this value
// is experimentally determined, so that's not guaranteed.
- constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
+ static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
static SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" },
@@ -10811,9 +10888,18 @@ namespace Catch {
}
std::string TagInfo::all() const {
- std::string out;
- for( auto const& spelling : spellings )
- out += "[" + spelling + "]";
+ size_t size = 0;
+ for (auto const& spelling : spellings) {
+ // Add 2 for the brackes
+ size += spelling.size() + 2;
+ }
+
+ std::string out; out.reserve(size);
+ for (auto const& spelling : spellings) {
+ out += '[';
+ out += spelling;
+ out += ']';
+ }
return out;
}
@@ -11660,6 +11746,8 @@ namespace Catch {
struct IConfig;
std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases );
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
void enforceNoDuplicateTestCases( std::vector const& functions );
@@ -12503,7 +12591,7 @@ namespace Catch {
void libIdentify();
int applyCommandLine( int argc, char const * const * argv );
- #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
+ #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
int applyCommandLine( int argc, wchar_t const * const * argv );
#endif
@@ -12570,6 +12658,8 @@ namespace Catch {
// end catch_version.h
#include
#include
+#include
+#include
namespace Catch {
@@ -12603,45 +12693,53 @@ namespace Catch {
return ret;
}
- Catch::Totals runTests(std::shared_ptr const& config) {
- auto reporter = makeReporter(config);
+ class TestGroup {
+ public:
+ explicit TestGroup(std::shared_ptr const& config)
+ : m_config{config}
+ , m_context{config, makeReporter(config)}
+ {
+ auto const& allTestCases = getAllTestCasesSorted(*m_config);
+ m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
- RunContext context(config, std::move(reporter));
-
- Totals totals;
-
- context.testGroupStarting(config->name(), 1, 1);
-
- TestSpec testSpec = config->testSpec();
-
- auto const& allTestCases = getAllTestCasesSorted(*config);
- for (auto const& testCase : allTestCases) {
- bool matching = (!testSpec.hasFilters() && !testCase.isHidden()) ||
- (testSpec.hasFilters() && matchTest(testCase, testSpec, *config));
-
- if (!context.aborting() && matching)
- totals += context.runTest(testCase);
- else
- context.reporter().skipTest(testCase);
+ if (m_matches.empty()) {
+ for (auto const& test : allTestCases)
+ if (!test.isHidden())
+ m_tests.emplace(&test);
+ } else {
+ for (auto const& match : m_matches)
+ m_tests.insert(match.tests.begin(), match.tests.end());
+ }
}
- if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
- ReusableStringStream testConfig;
-
- bool first = true;
- for (const auto& input : config->getTestsOrTags()) {
- if (!first) { testConfig << ' '; }
- first = false;
- testConfig << input;
+ Totals execute() {
+ Totals totals;
+ m_context.testGroupStarting(m_config->name(), 1, 1);
+ for (auto const& testCase : m_tests) {
+ if (!m_context.aborting())
+ totals += m_context.runTest(*testCase);
+ else
+ m_context.reporter().skipTest(*testCase);
}
- context.reporter().noMatchingTestCases(testConfig.str());
- totals.error = -1;
+ for (auto const& match : m_matches) {
+ if (match.tests.empty()) {
+ m_context.reporter().noMatchingTestCases(match.name);
+ totals.error = -1;
+ }
+ }
+ m_context.testGroupEnded(m_config->name(), totals, 1, 1);
+ return totals;
}
- context.testGroupEnded(config->name(), totals, 1, 1);
- return totals;
- }
+ private:
+ using Tests = std::set;
+
+ std::shared_ptr m_config;
+ RunContext m_context;
+ Tests m_tests;
+ TestSpec::Matches m_matches;
+ };
void applyFilenamesAsTags(Catch::IConfig const& config) {
auto& tests = const_cast&>(getAllTestCasesSorted(config));
@@ -12741,7 +12839,7 @@ namespace Catch {
return 0;
}
-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
+#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
char **utf8Argv = new char *[ argc ];
@@ -12818,7 +12916,12 @@ namespace Catch {
if( Option listed = list( m_config ) )
return static_cast( *listed );
- auto totals = runTests( m_config );
+ TestGroup tests { m_config };
+ auto const totals = tests.execute();
+
+ if( m_config->warnAboutNoTests() && totals.error == -1 )
+ return 2;
+
// Note that on unices only the lower 8 bits are usually used, clamping
// the return value to 255 prevents false negative when some multiple
// of 256 tests has failed
@@ -13529,8 +13632,13 @@ namespace Catch {
}
return sorted;
}
+
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
+ return !testCase.throws() || config.allowThrows();
+ }
+
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
- return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+ return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
}
void enforceNoDuplicateTestCases( std::vector const& functions ) {
@@ -13733,7 +13841,7 @@ namespace TestCaseTracking {
m_runState = CompletedSuccessfully;
break;
case ExecutingChildren:
- if( m_children.empty() || m_children.back()->isComplete() )
+ if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
m_runState = CompletedSuccessfully;
break;
@@ -13876,47 +13984,77 @@ namespace Catch {
namespace Catch {
- TestSpec::Pattern::~Pattern() = default;
- TestSpec::NamePattern::~NamePattern() = default;
- TestSpec::TagPattern::~TagPattern() = default;
- TestSpec::ExcludedPattern::~ExcludedPattern() = default;
-
- TestSpec::NamePattern::NamePattern( std::string const& name )
- : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ TestSpec::Pattern::Pattern( std::string const& name )
+ : m_name( name )
{}
+
+ TestSpec::Pattern::~Pattern() = default;
+
+ std::string const& TestSpec::Pattern::name() const {
+ return m_name;
+ }
+
+ TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
+ : Pattern( filterString )
+ , m_wildcardPattern( toLower( name ), CaseSensitive::No )
+ {}
+
bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
return m_wildcardPattern.matches( toLower( testCase.name ) );
}
- TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+ TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
+ : Pattern( filterString )
+ , m_tag( toLower( tag ) )
+ {}
+
bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
return std::find(begin(testCase.lcaseTags),
end(testCase.lcaseTags),
m_tag) != end(testCase.lcaseTags);
}
- TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
- bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+ TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
+ : Pattern( underlyingPattern->name() )
+ , m_underlyingPattern( underlyingPattern )
+ {}
+
+ bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
+ return !m_underlyingPattern->matches( testCase );
+ }
bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
- // All patterns in a filter must match for the filter to be a match
- for( auto const& pattern : m_patterns ) {
- if( !pattern->matches( testCase ) )
- return false;
- }
- return true;
+ return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
+ }
+
+ std::string TestSpec::Filter::name() const {
+ std::string name;
+ for( auto const& p : m_patterns )
+ name += p->name();
+ return name;
}
bool TestSpec::hasFilters() const {
return !m_filters.empty();
}
+
bool TestSpec::matches( TestCaseInfo const& testCase ) const {
- // A TestSpec matches if any filter matches
- for( auto const& filter : m_filters )
- if( filter.matches( testCase ) )
- return true;
- return false;
+ return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
}
+
+ TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const
+ {
+ Matches matches( m_filters.size() );
+ std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
+ std::vector currentMatches;
+ for( auto const& test : testCases )
+ if( isThrowSafe( test, config ) && filter.matches( test ) )
+ currentMatches.emplace_back( &test );
+ return FilterMatch{ filter.name(), currentMatches };
+ } );
+ return matches;
+ }
+
}
// end catch_test_spec.cpp
// start catch_test_spec_parser.cpp
@@ -13928,64 +14066,125 @@ namespace Catch {
TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
m_mode = None;
m_exclusion = false;
- m_start = std::string::npos;
m_arg = m_tagAliases->expandAliases( arg );
m_escapeChars.clear();
+ m_substring.reserve(m_arg.size());
+ m_patternName.reserve(m_arg.size());
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
visitChar( m_arg[m_pos] );
- if( m_mode == Name )
- addPattern();
+ endMode();
return *this;
}
TestSpec TestSpecParser::testSpec() {
addFilter();
return m_testSpec;
}
-
void TestSpecParser::visitChar( char c ) {
- if( m_mode == None ) {
- switch( c ) {
- case ' ': return;
- case '~': m_exclusion = true; return;
- case '[': return startNewMode( Tag, ++m_pos );
- case '"': return startNewMode( QuotedName, ++m_pos );
- case '\\': return escape();
- default: startNewMode( Name, m_pos ); break;
- }
+ if( c == ',' ) {
+ endMode();
+ addFilter();
+ return;
}
- if( m_mode == Name ) {
- if( c == ',' ) {
- addPattern();
- addFilter();
- }
- else if( c == '[' ) {
- if( subString() == "exclude:" )
- m_exclusion = true;
- else
- addPattern();
- startNewMode( Tag, ++m_pos );
- }
- else if( c == '\\' )
- escape();
+
+ switch( m_mode ) {
+ case None:
+ if( processNoneChar( c ) )
+ return;
+ break;
+ case Name:
+ processNameChar( c );
+ break;
+ case EscapedName:
+ endMode();
+ break;
+ default:
+ case Tag:
+ case QuotedName:
+ if( processOtherChar( c ) )
+ return;
+ break;
}
- else if( m_mode == EscapedName )
- m_mode = Name;
- else if( m_mode == QuotedName && c == '"' )
- addPattern();
- else if( m_mode == Tag && c == ']' )
- addPattern();
+
+ m_substring += c;
+ if( !isControlChar( c ) )
+ m_patternName += c;
}
- void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
+ // Two of the processing methods return true to signal the caller to return
+ // without adding the given character to the current pattern strings
+ bool TestSpecParser::processNoneChar( char c ) {
+ switch( c ) {
+ case ' ':
+ return true;
+ case '~':
+ m_exclusion = true;
+ return false;
+ case '[':
+ startNewMode( Tag );
+ return false;
+ case '"':
+ startNewMode( QuotedName );
+ return false;
+ case '\\':
+ escape();
+ return true;
+ default:
+ startNewMode( Name );
+ return false;
+ }
+ }
+ void TestSpecParser::processNameChar( char c ) {
+ if( c == '[' ) {
+ if( m_substring == "exclude:" )
+ m_exclusion = true;
+ else
+ endMode();
+ startNewMode( Tag );
+ }
+ }
+ bool TestSpecParser::processOtherChar( char c ) {
+ if( !isControlChar( c ) )
+ return false;
+ m_substring += c;
+ endMode();
+ return true;
+ }
+ void TestSpecParser::startNewMode( Mode mode ) {
m_mode = mode;
- m_start = start;
+ }
+ void TestSpecParser::endMode() {
+ switch( m_mode ) {
+ case Name:
+ case QuotedName:
+ return addPattern();
+ case Tag:
+ return addPattern();
+ case EscapedName:
+ return startNewMode( Name );
+ case None:
+ default:
+ return startNewMode( None );
+ }
}
void TestSpecParser::escape() {
- if( m_mode == None )
- m_start = m_pos;
m_mode = EscapedName;
m_escapeChars.push_back( m_pos );
}
- std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ bool TestSpecParser::isControlChar( char c ) const {
+ switch( m_mode ) {
+ default:
+ return false;
+ case None:
+ return c == '~';
+ case Name:
+ return c == '[';
+ case EscapedName:
+ return true;
+ case QuotedName:
+ return c == '"';
+ case Tag:
+ return c == '[' || c == ']';
+ }
+ }
void TestSpecParser::addFilter() {
if( !m_currentFilter.m_patterns.empty() ) {
@@ -14225,6 +14424,13 @@ std::string StringMaker::convert(wchar_t * str) {
}
#endif
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+#include
+std::string StringMaker::convert(std::byte value) {
+ return ::Catch::Detail::stringify(std::to_integer(value));
+}
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+
std::string StringMaker::convert(int value) {
return ::Catch::Detail::stringify(static_cast(value));
}
@@ -14414,7 +14620,7 @@ namespace Catch {
}
Version const& libraryVersion() {
- static Version version( 2, 9, 1, "", 0 );
+ static Version version( 2, 9, 2, "", 0 );
return version;
}
@@ -15001,24 +15207,25 @@ private:
if (itMessage == messages.end())
return;
- // using messages.end() directly yields (or auto) compilation error:
- std::vector::const_iterator itEnd = messages.end();
- const std::size_t N = static_cast(std::distance(itMessage, itEnd));
+ const auto itEnd = messages.cend();
+ const auto N = static_cast(std::distance(itMessage, itEnd));
{
Colour colourGuard(colour);
stream << " with " << pluralise(N, "message") << ':';
}
- for (; itMessage != itEnd; ) {
+ while (itMessage != itEnd) {
// If this assertion is a warning ignore any INFO messages
if (printInfoMessages || itMessage->type != ResultWas::Info) {
- stream << " '" << itMessage->message << '\'';
- if (++itMessage != itEnd) {
+ printMessage();
+ if (itMessage != itEnd) {
Colour colourGuard(dimColour());
stream << " and";
}
+ continue;
}
+ ++itMessage;
}
}
@@ -16355,10 +16562,13 @@ namespace Catch {
}
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
- void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
+ void XmlReporter::benchmarkPreparing(std::string const& name) {
m_xml.startElement("BenchmarkResults")
- .writeAttribute("name", info.name)
- .writeAttribute("samples", info.samples)
+ .writeAttribute("name", name);
+ }
+
+ void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
+ m_xml.writeAttribute("samples", info.samples)
.writeAttribute("resamples", info.resamples)
.writeAttribute("iterations", info.iterations)
.writeAttribute("clockResolution", static_cast(info.clockResolution))