diff options
| author | Leitwolf | 2026-10-25 10:31:36 +0300 |
|---|---|---|
| committer | GitHub | 2026-10-25 10:31:36 +0300 |
| commit | d2e3e074bb2e1b8ffd9fc990d0b87667ea602f58 (patch) | |
| tree | 0bce4462b2e190c39b3e1c5f697e3e4abd1a5e2a /extras/catch_amalgamated.cpp | |
| parent | 13f81a480a93807c14dddb5abdb97355ced4d4c5 (diff) | |
| parent | 126dc333564cb72b5115d9a9242d66694694b6d0 (diff) | |
| download | catch3-d2e3e074bb2e1b8ffd9fc990d0b87667ea602f58.tar.gz | |
Merge pull request #18 from Progmasoft/build/catch3-identity
Give Catch3 its own build names and headers
Diffstat (limited to 'extras/catch_amalgamated.cpp')
| -rw-r--r-- | extras/catch_amalgamated.cpp | 2739 |
1 files changed, 1368 insertions, 1371 deletions
diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index f5a8b781..0f336b53 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -7,7 +7,7 @@ // SPDX-License-Identifier: BSL-1.0 // Catch3 v0.3.0, on the Catch2 v3.16.1 engine -// Generated: 2026-10-24 19:06:40.031209 +// Generated: 2026-10-25 02:37:10.065190 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -20,8 +20,6 @@ #include "catch_amalgamated.hpp" -#ifndef CATCH_WINDOWS_H_PROXY_HPP_INCLUDED -#define CATCH_WINDOWS_H_PROXY_HPP_INCLUDED #if defined(CATCH_PLATFORM_WINDOWS) @@ -39,7 +37,6 @@ #endif // defined(CATCH_PLATFORM_WINDOWS) -#endif // CATCH_WINDOWS_H_PROXY_HPP_INCLUDED // Copyright (c) 2026 Progmasoft. // SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1 @@ -2160,6 +2157,180 @@ namespace Progmasoft::Catch3 +#include <cmath> +#include <limits> + +namespace { + +// Performs equivalent check of std::fabs(lhs - rhs) <= margin +// But without the subtraction to allow for INFINITY in comparison +bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); +} + +} + +namespace Catch { + + Approx::Approx ( double value ) + : m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ), + m_margin( 0.0 ), + m_scale( 0.0 ), + m_value( value ) + {} + + Approx Approx::custom() { + return Approx( 0 ); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } + + + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); + } + + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) + || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); + } + + void Approx::setMargin(double newMargin) { + CATCH_ENFORCE(newMargin >= 0, + "Invalid Approx::margin: " << newMargin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = newMargin; + } + + void Approx::setEpsilon(double newEpsilon) { + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " << newEpsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; + } + +namespace literals { + Approx operator ""_a(long double val) { + return Approx(val); + } + Approx operator ""_a(unsigned long long val) { + return Approx(val); + } +} // end namespace literals + +std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) { + return value.toString(); +} + +} // end namespace Catch + + + +namespace Catch { + + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const& _lazyExpression): + lazyExpression(_lazyExpression), + resultType(_resultType) {} + + std::string AssertionResultData::reconstructExpression() const { + + if( reconstructedExpression.empty() ) { + if( lazyExpression ) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; + } + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data ) + : m_info( info ), + m_resultData( CATCH_MOVE(data) ) + {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + // Possibly overallocating by 3 characters should be basically free + std::string expr; expr.reserve(m_info.capturedExpression.size() + 3); + if (isFalseTest(m_info.resultDisposition)) { + expr += "!("; + } + expr += m_info.capturedExpression; + if (isFalseTest(m_info.resultDisposition)) { + expr += ')'; + } + return expr; + } + + std::string AssertionResult::getExpressionInMacro() const { + if ( m_info.macroName.empty() ) { + return static_cast<std::string>( m_info.capturedExpression ); + } + std::string expr; + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + return expr; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } + + StringRef AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch + + + #include <algorithm> #include <chrono> @@ -2717,180 +2888,6 @@ namespace Catch { -#include <cmath> -#include <limits> - -namespace { - -// Performs equivalent check of std::fabs(lhs - rhs) <= margin -// But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { - return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} - -} - -namespace Catch { - - Approx::Approx ( double value ) - : m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ), - m_margin( 0.0 ), - m_scale( 0.0 ), - m_value( value ) - {} - - Approx Approx::custom() { - return Approx( 0 ); - } - - Approx Approx::operator-() const { - auto temp(*this); - temp.m_value = -temp.m_value; - return temp; - } - - - std::string Approx::toString() const { - ReusableStringStream rss; - rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; - return rss.str(); - } - - bool Approx::equalityComparisonImpl(const double other) const { - // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value - // Thanks to Richard Harris for his help refining the scaled margin value - return marginComparison(m_value, other, m_margin) - || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value))); - } - - void Approx::setMargin(double newMargin) { - CATCH_ENFORCE(newMargin >= 0, - "Invalid Approx::margin: " << newMargin << '.' - << " Approx::Margin has to be non-negative."); - m_margin = newMargin; - } - - void Approx::setEpsilon(double newEpsilon) { - CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, - "Invalid Approx::epsilon: " << newEpsilon << '.' - << " Approx::epsilon has to be in [0, 1]"); - m_epsilon = newEpsilon; - } - -namespace literals { - Approx operator ""_a(long double val) { - return Approx(val); - } - Approx operator ""_a(unsigned long long val) { - return Approx(val); - } -} // end namespace literals - -std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) { - return value.toString(); -} - -} // end namespace Catch - - - -namespace Catch { - - AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const& _lazyExpression): - lazyExpression(_lazyExpression), - resultType(_resultType) {} - - std::string AssertionResultData::reconstructExpression() const { - - if( reconstructedExpression.empty() ) { - if( lazyExpression ) { - ReusableStringStream rss; - rss << lazyExpression; - reconstructedExpression = rss.str(); - } - } - return reconstructedExpression; - } - - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data ) - : m_info( info ), - m_resultData( CATCH_MOVE(data) ) - {} - - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); - } - - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); - } - - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; - } - - bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); - } - - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); - } - - std::string AssertionResult::getExpression() const { - // Possibly overallocating by 3 characters should be basically free - std::string expr; expr.reserve(m_info.capturedExpression.size() + 3); - if (isFalseTest(m_info.resultDisposition)) { - expr += "!("; - } - expr += m_info.capturedExpression; - if (isFalseTest(m_info.resultDisposition)) { - expr += ')'; - } - return expr; - } - - std::string AssertionResult::getExpressionInMacro() const { - if ( m_info.macroName.empty() ) { - return static_cast<std::string>( m_info.capturedExpression ); - } - std::string expr; - expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); - expr += m_info.macroName; - expr += "( "; - expr += m_info.capturedExpression; - expr += " )"; - return expr; - } - - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); - } - - std::string AssertionResult::getExpandedExpression() const { - std::string expr = m_resultData.reconstructExpression(); - return expr.empty() - ? getExpression() - : expr; - } - - StringRef AssertionResult::getMessage() const { - return m_resultData.message; - } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; - } - - StringRef AssertionResult::getTestMacroName() const { - return m_info.macroName; - } - -} // end namespace Catch - - - namespace Catch { bool operator==( PathFilter const& lhs, PathFilter const& rhs ) { @@ -2962,1106 +2959,6 @@ namespace Catch { } // end namespace Catch - - - -namespace Catch { - std::uint32_t getSeed() { - return getCurrentContext().getConfig()->rngSeed(); - } -} - - - - -#include <cassert> -#include <stack> - -namespace Catch { - - //////////////////////////////////////////////////////////////////////////// - - - ScopedMessage::ScopedMessage( MessageBuilder&& builder ): - m_messageId( builder.m_info.sequence ) { - MessageInfo info( CATCH_MOVE( builder.m_info ) ); - info.message = builder.m_stream.str(); - Detail::pushScopedMessage( CATCH_MOVE( info ) ); - } - - ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept: - m_messageId( old.m_messageId ) { - old.m_moved = true; - } - - ScopedMessage::~ScopedMessage() { - if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); } - } - - - Capturer::Capturer( StringRef macroName, - SourceLineInfo const& lineInfo, - ResultWas::OfType resultType, - StringRef names, - bool isScoped): - m_isScoped(isScoped) { - auto trimmed = [&] (size_t start, size_t end) { - while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) { - ++start; - } - while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) { - --end; - } - return names.substr(start, end - start + 1); - }; - auto skipq = [&] (size_t start, char quote) { - for (auto i = start + 1; i < names.size() ; ++i) { - if (names[i] == quote) - return i; - if (names[i] == '\\') - ++i; - } - CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); - }; - - size_t start = 0; - std::stack<char> openings; - for (size_t pos = 0; pos < names.size(); ++pos) { - char c = names[pos]; - switch (c) { - case '[': - case '{': - case '(': - // It is basically impossible to disambiguate between - // comparison and start of template args in this context -// case '<': - openings.push(c); - break; - case ']': - case '}': - case ')': -// case '>': - openings.pop(); - break; - case '"': - case '\'': - pos = skipq(pos, c); - break; - case ',': - if (start != pos && openings.empty()) { - m_messages.emplace_back(macroName, lineInfo, resultType); - m_messages.back().message += trimmed(start, pos); - m_messages.back().message += " := "_sr; - start = pos; - } - break; - default:; // noop - } - } - assert(openings.empty() && "Mismatched openings"); - m_messages.emplace_back(macroName, lineInfo, resultType); - m_messages.back().message += trimmed(start, names.size() - 1); - m_messages.back().message += " := "_sr; - } - Capturer::~Capturer() { - assert( m_captured == m_messages.size() ); - if ( m_isScoped ) { - for ( auto const& message : m_messages ) { - Detail::popScopedMessage( message.sequence ); - } - } - } - - void Capturer::captureValue( size_t index, std::string const& value ) { - assert( index < m_messages.size() ); - m_messages[index].message += value; - if ( m_isScoped ) { - Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) ); - } else { - Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) ); - } - m_captured++; - } - -} // end namespace Catch - - - - -#include <exception> - -namespace Catch { - - namespace { - - class RegistryHub : public IRegistryHub, - public IMutableRegistryHub, - private Detail::NonCopyable { - - public: // IRegistryHub - RegistryHub() = default; - ReporterRegistry const& getReporterRegistry() const override { - return m_reporterRegistry; - } - ITestCaseRegistry const& getTestCaseRegistry() const override { - return m_testCaseRegistry; - } - IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override { - return m_exceptionTranslatorRegistry; - } - ITagAliasRegistry const& getTagAliasRegistry() const override { - return m_tagAliasRegistry; - } - StartupExceptionRegistry const& getStartupExceptionRegistry() const override { - return m_exceptionRegistry; - } - - public: // IMutableRegistryHub - void registerListener( Detail::unique_ptr<EventListenerFactory> factory ) override { - m_reporterRegistry.registerListener( CATCH_MOVE(factory) ); - } - void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker ) override { - m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) ); - } - void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) override { - m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) ); - } - void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { - 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 - } - - ITestCaseRegistry& getMutableTestCaseRegistry() override { - return m_testCaseRegistry; - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - TagAliasRegistry m_tagAliasRegistry; - StartupExceptionRegistry m_exceptionRegistry; - }; - } - - using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>; - - IRegistryHub const& getRegistryHub() { - return RegistryHubSingleton::get(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return RegistryHubSingleton::getMutable(); - } - void cleanUp() { - cleanupSingletons(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); - } - - -} // end namespace Catch - - - - -namespace Catch { - - Session::Session() { - static bool alreadyInstantiated = false; - if( alreadyInstantiated ) { - CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } - CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } - } - alreadyInstantiated = true; - } - Session::~Session() { - Catch::cleanUp(); - } - - void Session::showHelp() const { - Progmasoft::Catch3::PrintHelp( m_arguments.empty() ? std::string( "tests" ) - : m_arguments.front() ); - } - - int Session::applyCommandLine( int argc, char const * const * argv ) { - m_arguments.assign( argv, argv + argc ); - return 0; - } - -#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) - int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { - m_arguments.clear(); - for ( int i = 0; i < argc; ++i ) { - int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr ); - std::string utf8( static_cast<std::size_t>( bufSize ), '\0' ); - WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, &utf8[0], bufSize, nullptr, nullptr ); - if ( !utf8.empty() ) { utf8.pop_back(); } // the terminator - m_arguments.push_back( utf8 ); - } - return 0; - } -#endif - - int Session::run() { - return Progmasoft::Catch3::RunTests( m_arguments ); - } - -} // end namespace Catch - - - - -namespace Catch { - - RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { - CATCH_TRY { - getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); - } CATCH_CATCH_ALL { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } - } - -} - - - -#include <cassert> -#include <cctype> -#include <algorithm> -#include <utility> - -namespace Catch { - - namespace { - 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"); - - constexpr TestCaseProperties operator|(TestCaseProperties lhs, TestCaseProperties rhs) { - return static_cast<TestCaseProperties>( - static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs) - ); - } - - constexpr TestCaseProperties& operator|=(TestCaseProperties& lhs, TestCaseProperties rhs) { - lhs = static_cast<TestCaseProperties>( - static_cast<TCP_underlying_type>(lhs) | static_cast<TCP_underlying_type>(rhs) - ); - return lhs; - } - - constexpr TestCaseProperties operator&(TestCaseProperties lhs, TestCaseProperties rhs) { - return static_cast<TestCaseProperties>( - static_cast<TCP_underlying_type>(lhs) & static_cast<TCP_underlying_type>(rhs) - ); - } - - constexpr bool applies(TestCaseProperties tcp) { - static_assert(static_cast<TCP_underlying_type>(TestCaseProperties::None) == 0, - "TestCaseProperties::None must be equal to 0"); - return tcp != TestCaseProperties::None; - } - - bool isRunnerTagWithValue( StringRef tag ) { - for ( StringRef prefix : { "!resource:"_sr, "!depends:"_sr, - "!timeout:"_sr, "!retries:"_sr } ) { - if ( tag.size() >= prefix.size() && - tag.substr( 0, prefix.size() ) == prefix ) { - return true; - } - } - return false; - } - - TestCaseProperties propertiesFromTag( StringRef tag, SourceLineInfo const& _lineInfo ) { - if ( tag.empty() ) { return TestCaseProperties::None; } - // All tags starting with alphanumeric characters are normal. - if ( std::isalnum( tag[0] ) ) { return TestCaseProperties::None; } - - // Explicitly check known special tags, and error out if it is not one of them - if ( tag[0] == '.' ) { 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 TestCaseProperties::Benchmark | - TestCaseProperties::IsHidden; - } - // Read by the Catch3 runner: scheduling, limits and quarantine. - else if ( tag == "!serial"_sr || tag == "!isolated"_sr || - tag == "!quarantine"_sr ) { - return TestCaseProperties::None; - } else if ( isRunnerTagWithValue( tag ) ) { - CATCH_ENFORCE( tag[tag.size() - 1] != ':', - "Tag name: [" << tag << "] needs a value after ':'\n" - << _lineInfo ); - return TestCaseProperties::None; - } else { - CATCH_RUNTIME_ERROR( - "Tag name: [" << tag << "] is not allowed.\n" - << "Tag names starting with non-alphanumeric " - "characters are reserved.\n" - << _lineInfo ); - } - } - - std::string makeDefaultName() { - static size_t counter = 0; - return "Anonymous test case " + std::to_string(++counter); - } - - constexpr StringRef extractFilenamePart(StringRef filename) { - size_t lastDot = filename.size(); - while (lastDot > 0 && filename[lastDot - 1] != '.') { - --lastDot; - } - // In theory we could have filename without any extension in it - if ( lastDot == 0 ) { return StringRef(); } - - --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]+[.]) - constexpr size_t sizeOfExtraTags(StringRef filepath) { - // [.] is 3, [#] is another 3 - const size_t extras = 3 + 3; - return extractFilenamePart(filepath).size() + extras; - } - } // end unnamed namespace - - bool operator<( Tag const& lhs, Tag const& rhs ) { - Detail::CaseInsensitiveLess cmp; - return cmp( lhs.original, rhs.original ); - } - bool operator==( Tag const& lhs, Tag const& rhs ) { - Detail::CaseInsensitiveEqualTo cmp; - return cmp( lhs.original, rhs.original ); - } - - Detail::unique_ptr<TestCaseInfo> - makeTestCaseInfo(StringRef _className, - NameAndTags const& nameAndTags, - SourceLineInfo const& _lineInfo ) { - return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo); - } - - TestCaseInfo::TestCaseInfo(StringRef _className, - NameAndTags const& _nameAndTags, - SourceLineInfo const& _lineInfo): - name( _nameAndTags.name.empty() ? makeDefaultName() : _nameAndTags.name ), - className( _className ), - lineInfo( _lineInfo ) - { - 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); - - // Since there is usually only small number of tags on a test case, - // the geometric growth on vector does not get time to properly - // amortize the reallocations. But we can save a lot of time by - // pre-allocating space for all tags. - if ( !originalTags.empty() ) { - size_t tagCount = 1; // 1 for filename tag - for ( size_t i = 0; i < originalTags.size(); ++i ) { - if ( originalTags[i] == '[' ) { ++tagCount; } - } - tags.reserve( tagCount ); - } - - // 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 == '[') { - CATCH_ENFORCE( - !inTag, - "Found '[' inside a tag while registering test case '" - << _nameAndTags.name << "' at " << _lineInfo ); - - inTag = true; - tagStart = idx; - } - if (c == ']') { - CATCH_ENFORCE( - inTag, - "Found unmatched ']' while registering test case '" - << _nameAndTags.name << "' at " << _lineInfo ); - - 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); - CATCH_ENFORCE( !tagStr.empty(), - "Found an empty tag while registering test case '" - << _nameAndTags.name << "' at " - << _lineInfo ); - - properties |= propertiesFromTag(tagStr, _lineInfo); - // 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); - } - } - CATCH_ENFORCE( !inTag, - "Found an unclosed tag while registering test case '" - << _nameAndTags.name << "' at " << _lineInfo ); - - - // Add [.] if relevant - if (isHidden()) { - internalAppendTag("."_sr); - } - - // Tests usually have small number of tags, so we explicitly handle - // the 1 and 2 tags cases for better performance in low-opt builds. - switch ( tags.size() ) { - case 1: - break; - case 2: - if ( tags[0] == tags[1] ) { tags.pop_back(); } - else if ( tags[1] < tags[0] ) { std::swap( tags[0], tags[1] ); } - break; - default: - std::sort( begin( tags ), end( tags ) ); - tags.erase( std::unique( begin( tags ), end( tags ) ), - end( tags ) ); - } - - } - - bool TestCaseInfo::isHidden() const { - return applies( properties & TestCaseProperties::IsHidden ); - } - bool TestCaseInfo::throws() const { - return applies( properties & TestCaseProperties::Throws ); - } - bool TestCaseInfo::okToFail() const { - return applies( properties & (TestCaseProperties::ShouldFail | TestCaseProperties::MayFail ) ); - } - bool TestCaseInfo::expectedToFail() const { - return applies( properties & (TestCaseProperties::ShouldFail) ); - } - - void TestCaseInfo::addFilenameTag() { - std::string combined("#"); - combined += extractFilenamePart(lineInfo.file); - internalAppendTag(combined); - } - - std::string TestCaseInfo::tagsAsString() const { - std::string ret; - // '[' and ']' per tag - std::size_t full_size = 2 * tags.size(); - for (const auto& tag : tags) { - full_size += tag.original.size(); - } - ret.reserve(full_size); - for (const auto& tag : tags) { - ret.push_back('['); - 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 += ']'; - tags.emplace_back(StringRef(backingTags.c_str() + backingStart, backingEnd - backingStart)); - } - - bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) { - // We want to avoid redoing the string comparisons multiple times, - // so we store the result of a three-way comparison before using - // it in the actual comparison logic. - const auto cmpName = lhs.name.compare( rhs.name ); - if ( cmpName != 0 ) { - return cmpName < 0; - } |