Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/extras/catch_amalgamated.cpp
diff options
context:
space:
mode:
authorLeitwolf2026-10-25 10:31:36 +0300
committerGitHub2026-10-25 10:31:36 +0300
commitd2e3e074bb2e1b8ffd9fc990d0b87667ea602f58 (patch)
tree0bce4462b2e190c39b3e1c5f697e3e4abd1a5e2a /extras/catch_amalgamated.cpp
parent13f81a480a93807c14dddb5abdb97355ced4d4c5 (diff)
parent126dc333564cb72b5115d9a9242d66694694b6d0 (diff)
downloadcatch3-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.cpp2739
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;
- }