diff --git a/CMakeLists.txt b/CMakeLists.txt index 958d3823d7..b6f0590f29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.5.4 # CML version placeholder, don't delete + VERSION 3.6.0 # CML version placeholder, don't delete LANGUAGES CXX # HOMEPAGE_URL is not supported until CMake version 3.12, which # we do not target yet. diff --git a/docs/release-notes.md b/docs/release-notes.md index 87cec24be9..db40f59806 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,7 @@ # Release notes **Contents**
+[3.6.0](#360)
[3.5.4](#354)
[3.5.3](#353)
[3.5.2](#352)
@@ -62,6 +63,31 @@ [Even Older versions](#even-older-versions)
+## 3.6.0 + +### Fixes +* Fixed Windows ARM64 build by fixing the preprocessor condition guarding use `_umul128` intrinsic. +* Fixed Windows ARM64EC build by removing intrinsic pragma it does not understand. (#2858) + * Why doesn't the x64-emulation build mode understand x64 pragmas? Don't ask me, ask the MSVC guys. +* Fixed the JUnit reporter sometimes crashing when reporting a fatal error. (#1210, #2855) + * The binary will still exit, but through the original error, rather than secondary error inside the reporter. + * The underlying fix applies to all reporters, not just the JUnit one, but only JUnit was currently causing troubles. + +### Improvements +* Disable `-Wnon-virtual-dtor` in Decomposer and Matchers (#2854) +* `precision` in floating point stringmakers defaults to `max_digits10`. + * This means that floating point values will be printed with enough precision to disambiguate any two floats. +* Column wrapping ignores ansi colour codes when calculating string width (#2833, #2849) + * This makes the output much more readable when the provided messages contain colour codes. + +### Miscellaneous +* Conan support improvements + * `compatibility_cppstr` is set to False. (#2860) + * This means that Conan won't let you mix library and project with different C++ standard settings. + * The implementation library CMake target name through Conan is properly set to `Catch2::Catch2` (#2861) +* `SelfTest` target can be built through Bazel (#2857) + + ## 3.5.4 ### Fixes diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index 6aa67884c1..c080ad19ad 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.5.4 -// Generated: 2024-04-10 12:03:46.281848 +// Catch v3.6.0 +// Generated: 2024-05-05 20:53:27.562886 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -2156,13 +2156,13 @@ std::string StringMaker::convert(unsigned char value) { return ::Catch::Detail::stringify(static_cast(value)); } -int StringMaker::precision = 5; +int StringMaker::precision = std::numeric_limits::max_digits10; std::string StringMaker::convert(float value) { return Detail::fpToString(value, precision) + 'f'; } -int StringMaker::precision = 10; +int StringMaker::precision = std::numeric_limits::max_digits10; std::string StringMaker::convert(double value) { return Detail::fpToString(value, precision); @@ -2273,7 +2273,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 5, 4, "", 0 ); + static Version version( 3, 6, 0, "", 0 ); return version; } @@ -5853,6 +5853,13 @@ namespace Catch { assertionEnded(CATCH_MOVE(result) ); resetAssertionInfo(); + // Best effort cleanup for sections that have not been destructed yet + // Since this is a fatal error, we have not had and won't have the opportunity to destruct them properly + while (!m_activeSections.empty()) { + auto nl = m_activeSections.back()->nameAndLocation(); + SectionEndInfo endInfo{ SectionInfo(CATCH_MOVE(nl.location), CATCH_MOVE(nl.name)), {}, 0.0 }; + sectionEndedEarly(CATCH_MOVE(endInfo)); + } handleUnfinishedSections(); // Recreate section for test case (as we will lose the one that was in scope) @@ -7207,117 +7214,228 @@ namespace { return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr; } - bool isBoundary( std::string const& line, size_t at ) { - assert( at > 0 ); - assert( at <= line.size() ); - - return at == line.size() || - ( isWhitespace( line[at] ) && !isWhitespace( line[at - 1] ) ) || - isBreakableBefore( line[at] ) || - isBreakableAfter( line[at - 1] ); - } - } // namespace namespace Catch { namespace TextFlow { + void AnsiSkippingString::preprocessString() { + for ( auto it = m_string.begin(); it != m_string.end(); ) { + // try to read through an ansi sequence + while ( it != m_string.end() && *it == '\033' && + it + 1 != m_string.end() && *( it + 1 ) == '[' ) { + auto cursor = it + 2; + while ( cursor != m_string.end() && + ( isdigit( *cursor ) || *cursor == ';' ) ) { + ++cursor; + } + if ( cursor == m_string.end() || *cursor != 'm' ) { + break; + } + // 'm' -> 0xff + *cursor = AnsiSkippingString::sentinel; + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + it = cursor + 1; + } + if ( it != m_string.end() ) { + ++m_size; + ++it; + } + } + } + + AnsiSkippingString::AnsiSkippingString( std::string const& text ): + m_string( text ) { + preprocessString(); + } + + AnsiSkippingString::AnsiSkippingString( std::string&& text ): + m_string( CATCH_MOVE( text ) ) { + preprocessString(); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::begin() const { + return const_iterator( m_string ); + } + + AnsiSkippingString::const_iterator AnsiSkippingString::end() const { + return const_iterator( m_string, const_iterator::EndTag{} ); + } + + std::string AnsiSkippingString::substring( const_iterator begin, + const_iterator end ) const { + // There's one caveat here to an otherwise simple substring: when + // making a begin iterator we might have skipped ansi sequences at + // the start. If `begin` here is a begin iterator, skipped over + // initial ansi sequences, we'll use the true beginning of the + // string. Lastly: We need to transform any chars we replaced with + // 0xff back to 'm' + auto str = std::string( begin == this->begin() ? m_string.begin() + : begin.m_it, + end.m_it ); + std::transform( str.begin(), str.end(), str.begin(), []( char c ) { + return c == AnsiSkippingString::sentinel ? 'm' : c; + } ); + return str; + } + + void AnsiSkippingString::const_iterator::tryParseAnsiEscapes() { + // check if we've landed on an ansi sequence, and if so read through + // it + while ( m_it != m_string->end() && *m_it == '\033' && + m_it + 1 != m_string->end() && *( m_it + 1 ) == '[' ) { + auto cursor = m_it + 2; + while ( cursor != m_string->end() && + ( isdigit( *cursor ) || *cursor == ';' ) ) { + ++cursor; + } + if ( cursor == m_string->end() || + *cursor != AnsiSkippingString::sentinel ) { + break; + } + // if we've read an ansi sequence, set the iterator and + // return to the top of the loop + m_it = cursor + 1; + } + } + + void AnsiSkippingString::const_iterator::advance() { + assert( m_it != m_string->end() ); + m_it++; + tryParseAnsiEscapes(); + } + + void AnsiSkippingString::const_iterator::unadvance() { + assert( m_it != m_string->begin() ); + m_it--; + // if *m_it is 0xff, scan back to the \033 and then m_it-- once more + // (and repeat check) + while ( *m_it == AnsiSkippingString::sentinel ) { + while ( *m_it != '\033' ) { + assert( m_it != m_string->begin() ); + m_it--; + } + // if this happens, we must have been a begin iterator that had + // skipped over ansi sequences at the start of a string + assert( m_it != m_string->begin() ); + assert( *m_it == '\033' ); + m_it--; + } + } + + static bool isBoundary( AnsiSkippingString const& line, + AnsiSkippingString::const_iterator it ) { + return it == line.end() || + ( isWhitespace( *it ) && + !isWhitespace( *it.oneBefore() ) ) || + isBreakableBefore( *it ) || + isBreakableAfter( *it.oneBefore() ); + } void Column::const_iterator::calcLength() { m_addHyphen = false; m_parsedTo = m_lineStart; + AnsiSkippingString const& current_line = m_column.m_string; - std::string const& current_line = m_column.m_string; - if ( current_line[m_lineStart] == '\n' ) { - ++m_parsedTo; + if ( m_parsedTo == current_line.end() ) { + m_lineEnd = m_parsedTo; + return; } + assert( m_lineStart != current_line.end() ); + if ( *m_lineStart == '\n' ) { ++m_parsedTo; } + const auto maxLineLength = m_column.m_width - indentSize(); - const auto maxParseTo = std::min(current_line.size(), m_lineStart + maxLineLength); - while ( m_parsedTo < maxParseTo && - current_line[m_parsedTo] != '\n' ) { + std::size_t lineLength = 0; + while ( m_parsedTo != current_line.end() && + lineLength < maxLineLength && *m_parsedTo != '\n' ) { ++m_parsedTo; + ++lineLength; } // If we encountered a newline before the column is filled, // then we linebreak at the newline and consider this line // finished. - if ( m_parsedTo < m_lineStart + maxLineLength ) { - m_lineLength = m_parsedTo - m_lineStart; + if ( lineLength < maxLineLength ) { + m_lineEnd = m_parsedTo; } else { // Look for a natural linebreak boundary in the column // (We look from the end, so that the first found boundary is // the right one) - size_t newLineLength = maxLineLength; - while ( newLineLength > 0 && !isBoundary( current_line, m_lineStart + newLineLength ) ) { - --newLineLength; + m_lineEnd = m_parsedTo; + while ( lineLength > 0 && + !isBoundary( current_line, m_lineEnd ) ) { + --lineLength; + --m_lineEnd; } - while ( newLineLength > 0 && - isWhitespace( current_line[m_lineStart + newLineLength - 1] ) ) { - --newLineLength; + while ( lineLength > 0 && + isWhitespace( *m_lineEnd.oneBefore() ) ) { + --lineLength; + --m_lineEnd; } - // If we found one, then that is where we linebreak - if ( newLineLength > 0 ) { - m_lineLength = newLineLength; - } else { - // Otherwise we have to split text with a hyphen + // If we found one, then that is where we linebreak, otherwise + // we have to split text with a hyphen + if ( lineLength == 0 ) { m_addHyphen = true; - m_lineLength = maxLineLength - 1; + m_lineEnd = m_parsedTo.oneBefore(); } } } size_t Column::const_iterator::indentSize() const { - auto initial = - m_lineStart == 0 ? m_column.m_initialIndent : std::string::npos; + auto initial = m_lineStart == m_column.m_string.begin() + ? m_column.m_initialIndent + : std::string::npos; return initial == std::string::npos ? m_column.m_indent : initial; } - std::string - Column::const_iterator::addIndentAndSuffix( size_t position, - size_t length ) const { + std::string Column::const_iterator::addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end ) const { std::string ret; const auto desired_indent = indentSize(); - ret.reserve( desired_indent + length + m_addHyphen ); + // ret.reserve( desired_indent + (end - start) + m_addHyphen ); ret.append( desired_indent, ' ' ); - ret.append( m_column.m_string, position, length ); - if ( m_addHyphen ) { - ret.push_back( '-' ); - } + // ret.append( start, end ); + ret += m_column.m_string.substring( start, end ); + if ( m_addHyphen ) { ret.push_back( '-' ); } return ret; } - Column::const_iterator::const_iterator( Column const& column ): m_column( column ) { + Column::const_iterator::const_iterator( Column const& column ): + m_column( column ), + m_lineStart( column.m_string.begin() ), + m_lineEnd( column.m_string.begin() ), + m_parsedTo( column.m_string.begin() ) { assert( m_column.m_width > m_column.m_indent ); assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); calcLength(); - if ( m_lineLength == 0 ) { - m_lineStart = m_column.m_string.size(); + if ( m_lineStart == m_lineEnd ) { + m_lineStart = m_column.m_string.end(); } } std::string Column::const_iterator::operator*() const { assert( m_lineStart <= m_parsedTo ); - return addIndentAndSuffix( m_lineStart, m_lineLength ); + return addIndentAndSuffix( m_lineStart, m_lineEnd ); } Column::const_iterator& Column::const_iterator::operator++() { - m_lineStart += m_lineLength; - std::string const& current_line = m_column.m_string; - if ( m_lineStart < current_line.size() && current_line[m_lineStart] == '\n' ) { - m_lineStart += 1; + m_lineStart = m_lineEnd; + AnsiSkippingString const& current_line = m_column.m_string; + if ( m_lineStart != current_line.end() && *m_lineStart == '\n' ) { + m_lineStart++; } else { - while ( m_lineStart < current_line.size() && - isWhitespace( current_line[m_lineStart] ) ) { + while ( m_lineStart != current_line.end() && + isWhitespace( *m_lineStart ) ) { ++m_lineStart; } } - if ( m_lineStart != current_line.size() ) { - calcLength(); - } + if ( m_lineStart != current_line.end() ) { calcLength(); } return *this; } @@ -7414,25 +7532,25 @@ namespace Catch { return os; } - Columns operator+(Column const& lhs, Column const& rhs) { + Columns operator+( Column const& lhs, Column const& rhs ) { Columns cols; cols += lhs; cols += rhs; return cols; } - Columns operator+(Column&& lhs, Column&& rhs) { + Columns operator+( Column&& lhs, Column&& rhs ) { Columns cols; cols += CATCH_MOVE( lhs ); cols += CATCH_MOVE( rhs ); return cols; } - Columns& operator+=(Columns& lhs, Column const& rhs) { + Columns& operator+=( Columns& lhs, Column const& rhs ) { lhs.m_columns.push_back( rhs ); return lhs; } - Columns& operator+=(Columns& lhs, Column&& rhs) { - lhs.m_columns.push_back( CATCH_MOVE(rhs) ); + Columns& operator+=( Columns& lhs, Column&& rhs ) { + lhs.m_columns.push_back( CATCH_MOVE( rhs ) ); return lhs; } Columns operator+( Columns const& lhs, Column const& rhs ) { @@ -8077,7 +8195,7 @@ namespace Detail { std::string WithinRelMatcher::describe() const { Catch::ReusableStringStream sstr; - sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + sstr << "and " << ::Catch::Detail::stringify(m_target) << " are within " << m_epsilon * 100. << "% of each other"; return sstr.str(); } diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 7e75a5d197..6cc67e76fd 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.5.4 -// Generated: 2024-04-10 12:03:45.785902 +// Catch v3.6.0 +// Generated: 2024-05-05 20:53:27.071502 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -5242,9 +5242,11 @@ namespace Detail { #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" #elif defined __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif #if defined(CATCH_CPP20_OR_GREATER) && __has_include() @@ -7269,8 +7271,8 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 5 -#define CATCH_VERSION_PATCH 4 +#define CATCH_VERSION_MINOR 6 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7949,7 +7951,10 @@ namespace Catch { // it, and it provides an escape hatch to the users who need it. #if defined( __SIZEOF_INT128__ ) # define CATCH_CONFIG_INTERNAL_UINT128 -#elif defined( _MSC_VER ) && ( defined( _WIN64 ) || defined( _M_ARM64 ) ) +// Unlike GCC, MSVC does not polyfill umul as mulh + mul pair on ARM machines. +// Currently we do not bother doing this ourselves, but we could if it became +// important for perf. +#elif defined( _MSC_VER ) && defined( _M_X64 ) # define CATCH_CONFIG_INTERNAL_MSVC_UMUL128 #endif @@ -7964,7 +7969,6 @@ namespace Catch { !defined( CATCH_CONFIG_MSVC_UMUL128 ) # define CATCH_CONFIG_MSVC_UMUL128 # include -# pragma intrinsic( _umul128 ) #endif @@ -10944,6 +10948,107 @@ namespace Catch { class Columns; + /** + * Abstraction for a string with ansi escape sequences that + * automatically skips over escapes when iterating. Only graphical + * escape sequences are considered. + * + * Internal representation: + * An escape sequence looks like \033[39;49m + * We need bidirectional iteration and the unbound length of escape + * sequences poses a problem for operator-- To make this work we'll + * replace the last `m` with a 0xff (this is a codepoint that won't have + * any utf-8 meaning). + */ + class AnsiSkippingString { + std::string m_string; + std::size_t m_size = 0; + + // perform 0xff replacement and calculate m_size + void preprocessString(); + + public: + class const_iterator; + using iterator = const_iterator; + // note: must be u-suffixed or this will cause a "truncation of + // constant value" warning on MSVC + static constexpr char sentinel = static_cast( 0xffu ); + + explicit AnsiSkippingString( std::string const& text ); + explicit AnsiSkippingString( std::string&& text ); + + const_iterator begin() const; + const_iterator end() const; + + size_t size() const { return m_size; } + + std::string substring( const_iterator begin, + const_iterator end ) const; + }; + + class AnsiSkippingString::const_iterator { + friend AnsiSkippingString; + struct EndTag {}; + + const std::string* m_string; + std::string::const_iterator m_it; + + explicit const_iterator( const std::string& string, EndTag ): + m_string( &string ), m_it( string.end() ) {} + + void tryParseAnsiEscapes(); + void advance(); + void unadvance(); + + public: + using difference_type = std::ptrdiff_t; + using value_type = char; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::bidirectional_iterator_tag; + + explicit const_iterator( const std::string& string ): + m_string( &string ), m_it( string.begin() ) { + tryParseAnsiEscapes(); + } + + char operator*() const { return *m_it; } + + const_iterator& operator++() { + advance(); + return *this; + } + const_iterator operator++( int ) { + iterator prev( *this ); + operator++(); + return prev; + } + const_iterator& operator--() { + unadvance(); + return *this; + } + const_iterator operator--( int ) { + iterator prev( *this ); + operator--(); + return prev; + } + + bool operator==( const_iterator const& other ) const { + return m_it == other.m_it; + } + bool operator!=( const_iterator const& other ) const { + return !operator==( other ); + } + bool operator<=( const_iterator const& other ) const { + return m_it <= other.m_it; + } + + const_iterator oneBefore() const { + auto it = *this; + return --it; + } + }; + /** * Represents a column of text with specific width and indentation * @@ -10953,10 +11058,11 @@ namespace Catch { */ class Column { // String to be written out - std::string m_string; + AnsiSkippingString m_string; // Width of the column for linebreaking size_t m_width = CATCH_CONFIG_CONSOLE_WIDTH - 1; - // Indentation of other lines (including first if initial indent is unset) + // Indentation of other lines (including first if initial indent is + // unset) size_t m_indent = 0; // Indentation of the first line size_t m_initialIndent = std::string::npos; @@ -10971,16 +11077,19 @@ namespace Catch { Column const& m_column; // Where does the current line start? - size_t m_lineStart = 0; + AnsiSkippingString::const_iterator m_lineStart; // How long should the current line be? - size_t m_lineLength = 0; + AnsiSkippingString::const_iterator m_lineEnd; // How far have we checked the string to iterate? - size_t m_parsedTo = 0; + AnsiSkippingString::const_iterator m_parsedTo; // Should a '-' be appended to the line? bool m_addHyphen = false; const_iterator( Column const& column, EndTag ): - m_column( column ), m_lineStart( m_column.m_string.size() ) {} + m_column( column ), + m_lineStart( m_column.m_string.end() ), + m_lineEnd( column.m_string.end() ), + m_parsedTo( column.m_string.end() ) {} // Calculates the length of the current line void calcLength(); @@ -10990,8 +11099,9 @@ namespace Catch { // Creates an indented and (optionally) suffixed string from // current iterator position, indentation and length. - std::string addIndentAndSuffix( size_t position, - size_t length ) const; + std::string addIndentAndSuffix( + AnsiSkippingString::const_iterator start, + AnsiSkippingString::const_iterator end ) const; public: using difference_type = std::ptrdiff_t; @@ -11008,7 +11118,8 @@ namespace Catch { const_iterator operator++( int ); bool operator==( const_iterator const& other ) const { - return m_lineStart == other.m_lineStart && &m_column == &other.m_column; + return m_lineStart == other.m_lineStart && + &m_column == &other.m_column; } bool operator!=( const_iterator const& other ) const { return !operator==( other ); @@ -11018,7 +11129,7 @@ namespace Catch { explicit Column( std::string const& text ): m_string( text ) {} explicit Column( std::string&& text ): - m_string( CATCH_MOVE(text)) {} + m_string( CATCH_MOVE( text ) ) {} Column& width( size_t newWidth ) & { assert( newWidth > 0 ); @@ -11049,7 +11160,9 @@ namespace Catch { size_t width() const { return m_width; } const_iterator begin() const { return const_iterator( *this ); } - const_iterator end() const { return { *this, const_iterator::EndTag{} }; } + const_iterator end() const { + return { *this, const_iterator::EndTag{} }; + } friend std::ostream& operator<<( std::ostream& os, Column const& col ); @@ -11320,6 +11433,16 @@ namespace Catch { namespace Catch { +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + template class MatchExpr : public ITransientExpression { ArgT && m_arg; @@ -11338,6 +11461,13 @@ namespace Catch { } }; +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + + namespace Matchers { template class MatcherBase; diff --git a/meson.build b/meson.build index 7c216f96b4..15ff13edc5 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.5.4', # CML version placeholder, don't delete + version: '3.6.0', # CML version placeholder, don't delete license: 'BSL-1.0', meson_version: '>=0.54.1', ) diff --git a/src/catch2/catch_version.cpp b/src/catch2/catch_version.cpp index 9e8765b39f..63a9ec8632 100644 --- a/src/catch2/catch_version.cpp +++ b/src/catch2/catch_version.cpp @@ -36,7 +36,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 5, 4, "", 0 ); + static Version version( 3, 6, 0, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index ff50c09c97..b95580bd87 100644 --- a/src/catch2/catch_version_macros.hpp +++ b/src/catch2/catch_version_macros.hpp @@ -9,7 +9,7 @@ #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 5 -#define CATCH_VERSION_PATCH 4 +#define CATCH_VERSION_MINOR 6 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED