From d36c15c3caf96c2e751f4ad83a386c1fd047c2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Thu, 7 Nov 2019 12:39:07 +0100 Subject: [PATCH] Store tags in one big pre-allocated string and only work with refs This should decrease the number of allocations before main is entered significantly, but complicates the code somewhat in return. Assuming I used `massif` right, doing just `SelfTest --list-tests` went from 929 allocations at "Remove gcc-4.9 from the travis builds" (2 commits up), to 614 allocations with this commit. --- examples/210-Evt-EventListeners.cpp | 58 ++-- include/internal/catch_interfaces_testcase.h | 3 + include/internal/catch_list.cpp | 19 +- include/internal/catch_list.h | 4 +- include/internal/catch_session.cpp | 28 +- include/internal/catch_stringref.cpp | 7 + include/internal/catch_stringref.h | 3 +- include/internal/catch_test_case_info.cpp | 247 ++++++++++++------ include/internal/catch_test_case_info.h | 52 ++-- .../catch_test_case_registry_impl.cpp | 4 + .../internal/catch_test_case_registry_impl.h | 1 + include/internal/catch_test_spec.cpp | 8 +- include/reporters/catch_reporter_junit.cpp | 13 +- include/reporters/catch_reporter_xml.cpp | 2 +- .../Baselines/compact.sw.approved.txt | 2 +- .../Baselines/console.sw.approved.txt | 4 +- .../SelfTest/Baselines/xml.sw.approved.txt | 20 +- .../SelfTest/IntrospectiveTests/Tag.tests.cpp | 8 +- 18 files changed, 296 insertions(+), 187 deletions(-) diff --git a/examples/210-Evt-EventListeners.cpp b/examples/210-Evt-EventListeners.cpp index d1827807..f9113197 100644 --- a/examples/210-Evt-EventListeners.cpp +++ b/examples/210-Evt-EventListeners.cpp @@ -21,6 +21,10 @@ std::string ws(int const level) { return std::string( 2 * level, ' ' ); } +std::ostream& operator<<(std::ostream& out, Catch::Tag t) { + return out << "original: " << t.original << "lower cased: " << t.lowerCased; +} + template< typename T > std::ostream& operator<<( std::ostream& os, std::vector const& v ) { os << "{ "; @@ -119,31 +123,36 @@ void print( std::ostream& os, int const level, std::string const& title, Catch:: os << ws(level+1) << "- aborting: " << info.aborting << "\n"; } -// struct TestCaseInfo { -// enum SpecialProperties{ -// None = 0, -// IsHidden = 1 << 1, -// ShouldFail = 1 << 2, -// MayFail = 1 << 3, -// Throws = 1 << 4, -// NonPortable = 1 << 5, -// Benchmark = 1 << 6 -// }; +// struct Tag { +// StringRef original, lowerCased; +// }; // -// bool isHidden() const; -// bool throws() const; -// bool okToFail() const; -// bool expectedToFail() const; // -// std::string tagsAsString() const; +// enum class TestCaseProperties : uint8_t { +// None = 0, +// IsHidden = 1 << 1, +// ShouldFail = 1 << 2, +// MayFail = 1 << 3, +// Throws = 1 << 4, +// NonPortable = 1 << 5, +// Benchmark = 1 << 6 +// }; // -// std::string name; -// std::string className; -// std::vector tags; -// std::vector lcaseTags; -// SourceLineInfo lineInfo; -// SpecialProperties properties; -// }; +// +// struct TestCaseInfo : NonCopyable { +// +// bool isHidden() const; +// bool throws() const; +// bool okToFail() const; +// bool expectedToFail() const; +// +// +// std::string name; +// std::string className; +// std::vector tags; +// SourceLineInfo lineInfo; +// TestCaseProperties properties = TestCaseProperties::None; +// }; void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseInfo const& info ) { os << ws(level ) << title << ":\n" @@ -154,10 +163,9 @@ void print( std::ostream& os, int const level, std::string const& title, Catch:: << ws(level+1) << "- tagsAsString(): '" << info.tagsAsString() << "'\n" << ws(level+1) << "- name: '" << info.name << "'\n" << ws(level+1) << "- className: '" << info.className << "'\n" - << ws(level+1) << "- tags: " << info.tags << "\n" - << ws(level+1) << "- lcaseTags: " << info.lcaseTags << "\n"; + << ws(level+1) << "- tags: " << info.tags << "\n"; print( os, level+1 , "- lineInfo", info.lineInfo ); - os << ws(level+1) << "- properties (flags): 0x" << std::hex << info.properties << std::dec << "\n"; + os << ws(level+1) << "- properties (flags): 0x" << std::hex << static_cast(info.properties) << std::dec << "\n"; } // struct TestCaseStats { diff --git a/include/internal/catch_interfaces_testcase.h b/include/internal/catch_interfaces_testcase.h index b59b814d..d176b490 100644 --- a/include/internal/catch_interfaces_testcase.h +++ b/include/internal/catch_interfaces_testcase.h @@ -9,10 +9,12 @@ #define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED #include +#include namespace Catch { class TestSpec; + struct TestCaseInfo; struct ITestInvoker { virtual void invoke () const = 0; @@ -24,6 +26,7 @@ namespace Catch { struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); + virtual std::vector> const& getAllInfos() const = 0; virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; diff --git a/include/internal/catch_list.cpp b/include/internal/catch_list.cpp index db88ff16..cd003433 100644 --- a/include/internal/catch_list.cpp +++ b/include/internal/catch_list.cpp @@ -38,14 +38,13 @@ namespace Catch { TestSpec testSpec = config.testSpec(); std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - std::map tagCounts; + std::map tagCounts; for (auto const& testCase : matchedTestCases) { for (auto const& tagName : testCase.getTestCaseInfo().tags) { - std::string lcaseTagName = toLower(tagName); - auto countIt = tagCounts.find(lcaseTagName); - if (countIt == tagCounts.end()) - countIt = tagCounts.insert(std::make_pair(lcaseTagName, TagInfo())).first; - countIt->second.add(tagName); + auto it = tagCounts.find(tagName.lowerCased); + if (it == tagCounts.end()) + it = tagCounts.insert(std::make_pair(tagName.lowerCased, TagInfo())).first; + it->second.add(tagName.original); } } @@ -71,16 +70,16 @@ namespace Catch { } // end anonymous namespace - void TagInfo::add( std::string const& spelling ) { + void TagInfo::add( StringRef spelling ) { ++count; spellings.insert( spelling ); } std::string TagInfo::all() const { - size_t size = 0; + // 2 per tag for brackets '[' and ']' + size_t size = spellings.size() * 2; for (auto const& spelling : spellings) { - // Add 2 for the brackes - size += spelling.size() + 2; + size += spelling.size(); } std::string out; out.reserve(size); diff --git a/include/internal/catch_list.h b/include/internal/catch_list.h index eff8b09e..17df5355 100644 --- a/include/internal/catch_list.h +++ b/include/internal/catch_list.h @@ -23,10 +23,10 @@ namespace Catch { }; struct TagInfo { - void add(std::string const& spelling); + void add(StringRef spelling); std::string all() const; - std::set spellings; + std::set spellings; std::size_t count = 0; }; diff --git a/include/internal/catch_session.cpp b/include/internal/catch_session.cpp index 87b52d44..a4d5c016 100644 --- a/include/internal/catch_session.cpp +++ b/include/internal/catch_session.cpp @@ -116,26 +116,9 @@ namespace Catch { TestSpec::Matches m_matches; }; - void applyFilenamesAsTags(Catch::IConfig const& config) { - for (auto const& testCase : getAllTestCasesSorted(config)) { - // Yeah, sue me. This will be removed soon. - auto& testInfo = const_cast(testCase.getTestCaseInfo()); - - std::string filename = testInfo.lineInfo.file; - auto lastSlash = filename.find_last_of("\\/"); - if (lastSlash != std::string::npos) { - filename.erase(0, lastSlash); - filename[0] = '#'; - } - - auto lastDot = filename.find_last_of('.'); - if (lastDot != std::string::npos) { - filename.erase(lastDot); - } - - auto tags = testInfo.tags; - tags.push_back(std::move(filename)); - setTags(testInfo, tags); + void applyFilenamesAsTags() { + for (auto const& testInfo : getRegistryHub().getTestCaseRegistry().getAllInfos()) { + testInfo->addFilenameTag(); } } @@ -285,8 +268,9 @@ namespace Catch { seedRng( *m_config ); - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); + if (m_configData.filenamesAsTags) { + applyFilenamesAsTags(); + } // Create reporter(s) so we can route listings through them auto reporter = makeReporter(m_config); diff --git a/include/internal/catch_stringref.cpp b/include/internal/catch_stringref.cpp index 215feef3..ad26b34c 100644 --- a/include/internal/catch_stringref.cpp +++ b/include/internal/catch_stringref.cpp @@ -38,6 +38,13 @@ namespace Catch { && (std::memcmp( m_start, other.m_start, m_size ) == 0); } + bool StringRef::operator<(StringRef const& rhs) const noexcept { + if (m_size < rhs.m_size) { + return strncmp(m_start, rhs.m_start, m_size) <= 0; + } + return strncmp(m_start, rhs.m_start, rhs.m_size) < 0; + } + auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { return os.write(str.data(), str.size()); } diff --git a/include/internal/catch_stringref.h b/include/internal/catch_stringref.h index dc2e748c..9d5522a5 100644 --- a/include/internal/catch_stringref.h +++ b/include/internal/catch_stringref.h @@ -58,6 +58,8 @@ namespace Catch { return m_start[index]; } + bool operator<(StringRef const& rhs) const noexcept; + public: // named queries constexpr auto empty() const noexcept -> bool { return m_size == 0; @@ -91,7 +93,6 @@ namespace Catch { auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } diff --git a/include/internal/catch_test_case_info.cpp b/include/internal/catch_test_case_info.cpp index 7ea729fa..35a57e6c 100644 --- a/include/internal/catch_test_case_info.cpp +++ b/include/internal/catch_test_case_info.cpp @@ -20,27 +20,58 @@ namespace Catch { namespace { - TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, '.' ) || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else if( tag == "!nonportable" ) - return TestCaseInfo::NonPortable; - else if( tag == "!benchmark" ) - return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + using TCP_underlying_type = uint8_t; + static_assert(sizeof(TestCaseProperties) == sizeof(TCP_underlying_type), + "The size of the TestCaseProperties is different from the assumed size"); + + TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { + return static_cast( + static_cast(lhs) | static_cast(rhs) + ); + } + + TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { + lhs = static_cast( + static_cast(lhs) | static_cast(rhs) + ); + return lhs; + } + + TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { + return static_cast( + static_cast(lhs) & static_cast(rhs) + ); + } + + bool applies(TestCaseProperties tcp) { + static_assert(static_cast(TestCaseProperties::None) == 0, + "TestCaseProperties::None must be equal to 0"); + return tcp != TestCaseProperties::None; + } + + TestCaseProperties parseSpecialTag( StringRef tag ) { + if( (!tag.empty() && tag[0] == '.') + || tag == "!hide"_sr ) + return TestCaseProperties::IsHidden; + else if( tag == "!throws"_sr ) + return TestCaseProperties::Throws; + else if( tag == "!shouldfail"_sr ) + return TestCaseProperties::ShouldFail; + else if( tag == "!mayfail"_sr ) + return TestCaseProperties::MayFail; + else if( tag == "!nonportable"_sr ) + return TestCaseProperties::NonPortable; + else if( tag == "!benchmark"_sr ) + return static_cast(TestCaseProperties::Benchmark | TestCaseProperties::IsHidden ); else - return TestCaseInfo::None; + return TestCaseProperties::None; } - bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast(tag[0]) ); + bool isReservedTag( StringRef tag ) { + return parseSpecialTag( tag ) == TestCaseProperties::None + && tag.size() > 0 + && !std::isalnum( static_cast(tag[0]) ); } - void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + void enforceNotReservedTag( StringRef tag, SourceLineInfo const& _lineInfo ) { CATCH_ENFORCE( !isReservedTag(tag), "Tag name: [" << tag << "] is not allowed.\n" << "Tag names starting with non alphanumeric characters are reserved\n" @@ -51,90 +82,117 @@ namespace Catch { static size_t counter = 0; return "Anonymous test case " + std::to_string(++counter); } + + StringRef extractFilenamePart(StringRef filename) { + size_t lastDot = filename.size(); + while (lastDot > 0 && filename[lastDot - 1] != '.') { + --lastDot; + } + --lastDot; + + size_t nameStart = lastDot; + while (nameStart > 0 && filename[nameStart - 1] != '/' && filename[nameStart - 1] != '\\') { + --nameStart; + } + + return filename.substr(nameStart, lastDot - nameStart); + } + + // Returns the upper bound on size of extra tags ([#file]+[.]) + size_t sizeOfExtraTags(StringRef filepath) { + // [.] is 3, [#] is another 3 + const size_t extras = 3 + 3; + return extractFilenamePart(filepath).size() + extras; + } } std::unique_ptr makeTestCaseInfo(std::string const& _className, - NameAndTags const& nameAndTags, - SourceLineInfo const& _lineInfo ) - { - bool isHidden = false; - - // Parse out tags - std::vector tags; - std::string tag; - bool inTag = false; - for (char c : nameAndTags.tags) { - if( !inTag ) { - if (c == '[') { - inTag = true; - } - } else { - if( c == ']' ) { - TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( ( prop & TestCaseInfo::IsHidden ) != 0 ) - isHidden = true; - else if( prop == TestCaseInfo::None ) - enforceNotReservedTag( tag, _lineInfo ); - - // Merged hide tags like `[.approvals]` should be added as - // `[.][approvals]`. The `[.]` is added at later point, so - // we only strip the prefix - if (startsWith(tag, '.') && tag.size() > 1) { - tag.erase(0, 1); - } - tags.push_back( tag ); - tag.clear(); - inTag = false; - } - else - tag += c; - } - } - if( isHidden ) { - tags.push_back( "." ); - } - - return std::make_unique(static_cast(nameAndTags.name), - _className, tags, _lineInfo); + NameAndTags const& nameAndTags, + SourceLineInfo const& _lineInfo ) { + return std::make_unique(_className, nameAndTags, _lineInfo); } - void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { - std::sort(begin(tags), end(tags)); - tags.erase(std::unique(begin(tags), end(tags)), end(tags)); - testCaseInfo.lcaseTags.clear(); - - for( auto const& tag : tags ) { - std::string lcaseTag = toLower( tag ); - testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.push_back( lcaseTag ); - } - testCaseInfo.tags = std::move(tags); - } - - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::vector const& _tags, - SourceLineInfo const& _lineInfo ) - : name( _name.empty() ? makeDefaultName() : _name ), + TestCaseInfo::TestCaseInfo(std::string const& _className, + NameAndTags const& _nameAndTags, + SourceLineInfo const& _lineInfo): + name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ), className( _className ), - lineInfo( _lineInfo ), - properties( None ) + lineInfo( _lineInfo ) { - setTags( *this, _tags ); + StringRef originalTags = _nameAndTags.tags; + // We need to reserve enough space to store all of the tags + // (including optional hidden tag and filename tag) + auto requiredSize = originalTags.size() + sizeOfExtraTags(_lineInfo.file); + backingTags.reserve(requiredSize); + backingLCaseTags.reserve(requiredSize); + + // We cannot copy the tags directly, as we need to normalize + // some tags, so that [.foo] is copied as [.][foo]. + size_t tagStart = 0; + size_t tagEnd = 0; + bool inTag = false; + for (size_t idx = 0; idx < originalTags.size(); ++idx) { + auto c = originalTags[idx]; + if (c == '[') { + assert(!inTag); + inTag = true; + tagStart = idx; + } + if (c == ']') { + assert(inTag); + inTag = false; + tagEnd = idx; + assert(tagStart < tagEnd); + + // We need to check the tag for special meanings, copy + // it over to backing storage and actually reference the + // backing storage in the saved tags + StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1); + enforceNotReservedTag(tagStr, lineInfo); + properties |= parseSpecialTag(tagStr); + // When copying a tag to the backing storage, we need to + // check if it is a merged hide tag, such as [.foo], and + // if it is, we need to handle it as if it was [foo]. + if (tagStr.size() > 1 && tagStr[0] == '.') { + tagStr = tagStr.substr(1, tagStr.size() - 1); + } + // We skip over dealing with the [.] tag, as we will add + // it later unconditionally and then sort and unique all + // the tags. + internalAppendTag(tagStr); + } + (void)inTag; // Silence "set-but-unused" warning in release mode. + } + // Add [.] if relevant + if (isHidden()) { + internalAppendTag("."_sr); + } + + // Sort and prepare tags + toLowerInPlace(backingLCaseTags); + std::sort(begin(tags), end(tags), [](Tag lhs, Tag rhs) { return lhs.lowerCased < rhs.lowerCased; }); + tags.erase(std::unique(begin(tags), end(tags), [](Tag lhs, Tag rhs) {return lhs.lowerCased == rhs.lowerCased; }), + end(tags)); } bool TestCaseInfo::isHidden() const { - return ( properties & IsHidden ) != 0; + return applies( properties & TestCaseProperties::IsHidden ); } bool TestCaseInfo::throws() const { - return ( properties & Throws ) != 0; + return applies( properties & TestCaseProperties::Throws ); } bool TestCaseInfo::okToFail() const { - return ( properties & (ShouldFail | MayFail ) ) != 0; + return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) ); } bool TestCaseInfo::expectedToFail() const { - return ( properties & (ShouldFail ) ) != 0; + return applies( properties & (TestCaseProperties::ShouldFail) ); + } + + void TestCaseInfo::addFilenameTag() { + std::string combined("#"); + combined += extractFilenamePart(lineInfo.file); + internalAppendTag(combined); } std::string TestCaseInfo::tagsAsString() const { @@ -142,18 +200,33 @@ namespace Catch { // '[' and ']' per tag std::size_t full_size = 2 * tags.size(); for (const auto& tag : tags) { - full_size += tag.size(); + full_size += tag.original.size(); } ret.reserve(full_size); for (const auto& tag : tags) { ret.push_back('['); - ret.append(tag); + ret += tag.original; ret.push_back(']'); } return ret; } + void TestCaseInfo::internalAppendTag(StringRef tagStr) { + backingTags += '['; + const auto backingStart = backingTags.size(); + backingTags += tagStr; + const auto backingEnd = backingTags.size(); + backingTags += ']'; + backingLCaseTags += '['; + // We append the tag to the lower-case backing storage as-is, + // because we will perform the lower casing later, in bulk + backingLCaseTags += tagStr; + backingLCaseTags += ']'; + tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart), + StringRef(backingLCaseTags.c_str() + backingStart, backingEnd - backingStart)); + } + bool TestCaseHandle::operator == ( TestCaseHandle const& rhs ) const { return m_invoker == rhs.m_invoker diff --git a/include/internal/catch_test_case_info.h b/include/internal/catch_test_case_info.h index aa7353b2..5016e5d0 100644 --- a/include/internal/catch_test_case_info.h +++ b/include/internal/catch_test_case_info.h @@ -9,6 +9,7 @@ #define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED #include "catch_common.h" +#include "catch_stringref.h" #include "catch_test_registry.h" #include @@ -22,39 +23,54 @@ namespace Catch { + struct Tag { + Tag(StringRef original_, StringRef lowerCased_): + original(original_), lowerCased(lowerCased_) + {} + StringRef original, lowerCased; + }; + struct ITestInvoker; + enum class TestCaseProperties : uint8_t { + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; + + struct TestCaseInfo : NonCopyable { - enum SpecialProperties{ - None = 0, - IsHidden = 1 << 1, - ShouldFail = 1 << 2, - MayFail = 1 << 3, - Throws = 1 << 4, - NonPortable = 1 << 5, - Benchmark = 1 << 6 - }; - TestCaseInfo( std::string const& _name, - std::string const& _className, - std::vector const& _tags, - SourceLineInfo const& _lineInfo ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); + TestCaseInfo(std::string const& _className, + NameAndTags const& _tags, + SourceLineInfo const& _lineInfo); bool isHidden() const; bool throws() const; bool okToFail() const; bool expectedToFail() const; + // Adds the tag(s) with test's filename (for the -# flag) + void addFilenameTag(); + + std::string tagsAsString() const; std::string name; std::string className; - std::vector tags; - std::vector lcaseTags; + private: + std::string backingTags, backingLCaseTags; + // Internally we copy tags to the backing storage and then add + // refs to this storage to the tags vector. + void internalAppendTag(StringRef tagString); + public: + std::vector tags; SourceLineInfo lineInfo; - SpecialProperties properties; + TestCaseProperties properties = TestCaseProperties::None; }; class TestCaseHandle { diff --git a/include/internal/catch_test_case_registry_impl.cpp b/include/internal/catch_test_case_registry_impl.cpp index 61f54afb..6891a53b 100644 --- a/include/internal/catch_test_case_registry_impl.cpp +++ b/include/internal/catch_test_case_registry_impl.cpp @@ -78,6 +78,10 @@ namespace Catch { m_invokers.push_back(std::move(testInvoker)); } + std::vector> const& TestRegistry::getAllInfos() const { + return m_infos; + } + std::vector const& TestRegistry::getAllTests() const { return m_handles; } diff --git a/include/internal/catch_test_case_registry_impl.h b/include/internal/catch_test_case_registry_impl.h index 1f72cc0f..d6fa6e14 100644 --- a/include/internal/catch_test_case_registry_impl.h +++ b/include/internal/catch_test_case_registry_impl.h @@ -37,6 +37,7 @@ namespace Catch { virtual void registerTest( std::unique_ptr testInfo, std::unique_ptr testInvoker ); + std::vector> const& getAllInfos() const override; std::vector const& getAllTests() const override; std::vector const& getAllTestsSorted( IConfig const& config ) const override; diff --git a/include/internal/catch_test_spec.cpp b/include/internal/catch_test_spec.cpp index 0292673a..6c63d4ce 100644 --- a/include/internal/catch_test_spec.cpp +++ b/include/internal/catch_test_spec.cpp @@ -43,9 +43,11 @@ namespace Catch { {} bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { - return std::find(begin(testCase.lcaseTags), - end(testCase.lcaseTags), - m_tag) != end(testCase.lcaseTags); + return std::find_if(begin(testCase.tags), + end(testCase.tags), + [&](Tag const& tag) { + return tag.lowerCased == m_tag; + }) != end(testCase.tags); } bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { diff --git a/include/reporters/catch_reporter_junit.cpp b/include/reporters/catch_reporter_junit.cpp index 5fd27218..27f7c1c1 100644 --- a/include/reporters/catch_reporter_junit.cpp +++ b/include/reporters/catch_reporter_junit.cpp @@ -48,12 +48,17 @@ namespace Catch { return std::string(timeStamp); } - std::string fileNameTag(const std::vector &tags) { + std::string fileNameTag(std::vector const& tags) { auto it = std::find_if(begin(tags), end(tags), - [] (std::string const& tag) {return tag.front() == '#'; }); - if (it != tags.end()) - return it->substr(1); + [] (Tag const& tag) { + return tag.original.size() > 0 + && tag.original[0] == '#'; }); + if (it != tags.end()) { + return static_cast( + it->original.substr(1, it->original.size() - 1) + ); + } return std::string(); } } // anonymous namespace diff --git a/include/reporters/catch_reporter_xml.cpp b/include/reporters/catch_reporter_xml.cpp index fa2ed0c7..d9a3cef3 100644 --- a/include/reporters/catch_reporter_xml.cpp +++ b/include/reporters/catch_reporter_xml.cpp @@ -312,7 +312,7 @@ namespace Catch { auto aliasTag = m_xml.scopedElement("Aliases"); for (auto const& alias : tag.spellings) { m_xml.startElement("Alias", XmlFormatting::Indent) - .writeText(alias, XmlFormatting::None) + .writeText(static_cast(alias), XmlFormatting::None) .endElement(XmlFormatting::Newline); } } diff --git a/projects/SelfTest/Baselines/compact.sw.approved.txt b/projects/SelfTest/Baselines/compact.sw.approved.txt index d7dfb5cc..6853df4e 100644 --- a/projects/SelfTest/Baselines/compact.sw.approved.txt +++ b/projects/SelfTest/Baselines/compact.sw.approved.txt @@ -1662,7 +1662,7 @@ StringManip.tests.cpp:: passed: Catch::replaceInPlace(s, "'", "|'") StringManip.tests.cpp:: passed: s == "didn|'t" for: "didn|'t" == "didn|'t" Misc.tests.cpp:: failed: false with 1 message: '3' Message.tests.cpp:: failed: false with 2 messages: 'hi' and 'i := 7' -Tag.tests.cpp:: passed: testcase->tags, Catch::VectorContains(std::string("magic-tag")) && Catch::VectorContains(std::string(".")) for: { ".", "magic-tag" } ( Contains: "magic-tag" and Contains: "." ) +Tag.tests.cpp:: passed: tags, Catch::VectorContains("magic-tag"_catch_sr) && Catch::VectorContains("."_catch_sr) for: { ., magic-tag } ( Contains: magic-tag and Contains: . ) StringManip.tests.cpp:: passed: splitStringRef("", ','), Equals(std::vector()) for: { } Equals: { } StringManip.tests.cpp:: passed: splitStringRef("abc", ','), Equals(std::vector{"abc"}) for: { abc } Equals: { abc } StringManip.tests.cpp:: passed: splitStringRef("abc,def", ','), Equals(std::vector{"abc", "def"}) for: { abc, def } Equals: { abc, def } diff --git a/projects/SelfTest/Baselines/console.sw.approved.txt b/projects/SelfTest/Baselines/console.sw.approved.txt index 9ed692cf..e8259948 100644 --- a/projects/SelfTest/Baselines/console.sw.approved.txt +++ b/projects/SelfTest/Baselines/console.sw.approved.txt @@ -12341,9 +12341,9 @@ Tag.tests.cpp: ............................................................................... Tag.tests.cpp:: PASSED: - REQUIRE_THAT( testcase->tags, Catch::VectorContains(std::string("magic-tag")) && Catch::VectorContains(std::string(".")) ) + REQUIRE_THAT( tags, Catch::VectorContains("magic-tag"_catch_sr) && Catch::VectorContains("."_catch_sr) ) with expansion: - { ".", "magic-tag" } ( Contains: "magic-tag" and Contains: "." ) + { ., magic-tag } ( Contains: magic-tag and Contains: . ) ------------------------------------------------------------------------------- splitString diff --git a/projects/SelfTest/Baselines/xml.sw.approved.txt b/projects/SelfTest/Baselines/xml.sw.approved.txt index effb76f4..d57f0b86 100644 --- a/projects/SelfTest/Baselines/xml.sw.approved.txt +++ b/projects/SelfTest/Baselines/xml.sw.approved.txt @@ -1730,7 +1730,7 @@ Nor would this - + &o1 == &o2 @@ -2510,7 +2510,7 @@ Nor would this - + a @@ -10985,7 +10985,7 @@ Message from section two - +
@@ -13709,13 +13709,13 @@ There is no extra whitespace here - + Uncomment the code in this test to check that it gives a sensible compiler error - + Uncomment the code in this test to check that it gives a sensible compiler error @@ -13724,7 +13724,7 @@ There is no extra whitespace here - + @@ -14504,7 +14504,7 @@ loose text artifact - +
@@ -14861,10 +14861,10 @@ loose text artifact - testcase->tags, Catch::VectorContains(std::string("magic-tag")) && Catch::VectorContains(std::string(".")) + tags, Catch::VectorContains("magic-tag"_catch_sr) && Catch::VectorContains("."_catch_sr) - { ".", "magic-tag" } ( Contains: "magic-tag" and Contains: "." ) + { ., magic-tag } ( Contains: magic-tag and Contains: . ) @@ -15050,7 +15050,7 @@ loose text artifact - + std::string( "first" ) == "second" diff --git a/projects/SelfTest/IntrospectiveTests/Tag.tests.cpp b/projects/SelfTest/IntrospectiveTests/Tag.tests.cpp index 79657226..5120d8ed 100644 --- a/projects/SelfTest/IntrospectiveTests/Tag.tests.cpp +++ b/projects/SelfTest/IntrospectiveTests/Tag.tests.cpp @@ -42,6 +42,12 @@ TEST_CASE( "Tag alias can be registered against tag patterns" ) { } TEST_CASE("shortened hide tags are split apart") { + using Catch::StringRef; auto testcase = Catch::makeTestCaseInfo("", {"fake test name", "[.magic-tag]"}, CATCH_INTERNAL_LINEINFO); - REQUIRE_THAT(testcase->tags, Catch::VectorContains(std::string("magic-tag")) && Catch::VectorContains(std::string("."))); + // Transform ... + std::vector tags; + for (auto const& tag : testcase->tags) { + tags.push_back(tag.original); + } + REQUIRE_THAT(tags, Catch::VectorContains("magic-tag"_catch_sr) && Catch::VectorContains("."_catch_sr)); }