diff --git a/CMakeLists.txt b/CMakeLists.txt index de5e6709..f63d1d44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.15.2 # CML version placeholder, don't delete + VERSION 3.15.3 # CML version placeholder, don't delete LANGUAGES CXX HOMEPAGE_URL "https://github.com/catchorg/Catch2" DESCRIPTION "A modern, C++-native, unit test framework." diff --git a/docs/release-notes.md b/docs/release-notes.md index ffa06430..9fafa0cd 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -2,6 +2,7 @@ # Release notes **Contents**
+[3.15.3](#3153)
[3.15.2](#3152)
[3.15.1](#3151)
[3.15.0](#3150)
@@ -77,6 +78,28 @@ [Even Older versions](#even-older-versions)
+ +## 3.15.3 + +### Fixes +* The JSON reporter's number handling is locale-independent. (#3176) +* Removed leftover debug message from `catch_discover_tests` (#3177) +* Fixed typo in the "Could not jump to the Nth element" exception message (#3181) + +### Improvements +* `catch_discover_tests` registers tests in deterministic (alphabetical) order. +* `catch_discover_tests` has been rewritten to be massively faster. + * Preparing the actual CTest script is significantly faster. + * Parsing the JSON test array is significantly faster. + * Running without `ADD_TAGS_AS_LABELS` set is 10-15% faster (as opposed to being the same speed) (#3169) + * The new implementation is about 4x-5x faster, so registering 500 tests now takes ~350ms (down from 1.1s). +* JSON writing is faster + * Small improvement in writing non-string values + * ~6-40% improvement in writing string values that do not need escaping + * ~1-6% improvement in writing string values that do need escaping +* Better support for infs and NaNs in JSON output + + ## 3.15.2 ### Fixes diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index 4e1e631e..abf540b4 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.15.2 -// Generated: 2026-07-07 20:39:49.445081 +// Catch v3.15.3 +// Generated: 2026-07-26 22:17:52.418168 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -2394,7 +2394,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 15, 2, "", 0 ); + static Version version( 3, 15, 3, "", 0 ); return version; } @@ -2541,7 +2541,7 @@ namespace Catch { bool isValid = next(); if ( !isValid ) { Detail::throw_generator_exception( - "Coud not jump to Nth element: not enough elements" ); + "Could not jump to Nth element: not enough elements" ); } } } @@ -4678,12 +4678,28 @@ namespace Detail { +#include +#include + namespace Catch { namespace { - static bool needsEscape( char c ) { - return c == '"' || c == '\\' || c == '\b' || c == '\f' || - c == '\n' || c == '\r' || c == '\t'; + struct EscapeLUT { + bool escape[256] = {}; + constexpr EscapeLUT() { + escape[static_cast( '"' )] = true; + escape[static_cast( '\\' )] = true; + escape[static_cast( '\b' )] = true; + escape[static_cast( '\f' )] = true; + escape[static_cast( '\n' )] = true; + escape[static_cast( '\r' )] = true; + escape[static_cast( '\t' )] = true; + } + }; + static constexpr EscapeLUT escapeLUT{}; + + static constexpr bool needsEscape( char c ) { + return escapeLUT.escape[static_cast( c )]; } static Catch::StringRef makeEscapeStringRef( char c ) { @@ -4795,7 +4811,10 @@ namespace Catch { JsonValueWriter::JsonValueWriter( std::ostream& os, std::uint64_t indent_level ): - m_os{ os }, m_indent_level{ indent_level } {} + m_os{ os }, m_indent_level{ indent_level } { + // We use C locale so that writing of numerical values is locale-independent. + m_sstream.imbue( std::locale::classic() ); + } JsonObjectWriter JsonValueWriter::writeObject() && { return JsonObjectWriter{ m_os, m_indent_level }; @@ -4809,26 +4828,55 @@ namespace Catch { writeImpl( value, true ); } + void JsonValueWriter::write( float value ) && { + writeFloatingPoint( value ); + } + + void JsonValueWriter::write( double value ) && { + writeFloatingPoint( value ); + } + void JsonValueWriter::write( bool value ) && { writeImpl( value ? "true"_sr : "false"_sr, false ); } + template + void JsonValueWriter::writeFloatingPoint( T value ) { + if ( Catch::isnan( value ) ) { + writeImpl( "NaN"_sr, false ); + } else if ( std::isinf( value ) ) { + writeImpl( value < 0 ? "-Infinity"_sr : "Infinity"_sr, false ); + } else { + m_sstream << value; + writeImpl( m_sstream.str(), false ); + } + } + void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) { - if ( quote ) { m_os << '"'; } - size_t current_start = 0; - for ( size_t i = 0; i < value.size(); ++i ) { - if ( needsEscape( value[i] ) ) { - if ( current_start < i ) { - m_os << value.substr( current_start, i - current_start ); + // Escaping only makes sense for actual strings, which are passed + // with `quote == true`. Non-quoted values are things like bools + // and numbers, which cannot create inputs that need escaping. + if ( !quote ) { + m_os << value; + } else { + m_os << '"'; + size_t current_start = 0; + for ( size_t i = 0; i < value.size(); ++i ) { + if ( needsEscape( value[i] ) ) { + if ( current_start < i ) { + m_os + << value.substr( current_start, i - current_start ); + } + m_os << makeEscapeStringRef( value[i] ); + current_start = i + 1; } - m_os << makeEscapeStringRef( value[i] ); - current_start = i + 1; } + if ( current_start < value.size() ) { + m_os << value.substr( current_start, + value.size() - current_start ); + } + m_os << '"'; } - if ( current_start < value.size() ) { - m_os << value.substr( current_start, value.size() - current_start ); - } - if ( quote ) { m_os << '"'; } } } // namespace Catch diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 7e65eb93..a2d9a848 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.15.2 -// Generated: 2026-07-07 20:39:49.020441 +// Catch v3.15.3 +// Generated: 2026-07-26 22:17:52.004020 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -7572,7 +7572,7 @@ namespace Catch { #define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MINOR 15 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_PATCH 3 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7828,7 +7828,7 @@ namespace Generators { void skipToNthElementImpl( std::size_t n ) override { if ( n >= m_values.size() ) { Detail::throw_generator_exception( - "Coud not jump to Nth element: not enough elements" ); + "Could not jump to Nth element: not enough elements" ); } m_idx = n; } @@ -8011,7 +8011,7 @@ namespace Generators { void skipToNthElementImpl( std::size_t n ) override { if ( n >= m_target ) { Detail::throw_generator_exception( - "Coud not jump to Nth element: not enough elements" ); + "Could not jump to Nth element: not enough elements" ); } m_generator.skipToNthElement( n ); @@ -10309,11 +10309,19 @@ namespace Catch { writeImpl( value, !std::is_arithmetic::value ); } void write( StringRef value ) &&; + void write( float value ) &&; + void write( double value ) &&; void write( bool value ) &&; private: void writeImpl( StringRef value, bool quote ); + // Helper to deal with non-finite floating point values, which + // are not standard JSON, but we use JS/Python/etc. approach of + // emitting `NaN`, `Infinity`, `-Infinity` as number(like). + template + void writeFloatingPoint( T value ); + // Without this SFINAE, this overload is a better match // for `std::string`, `char const*`, `char const[N]` args. // While it would still work, it would cause code bloat diff --git a/meson.build b/meson.build index 25fb3c2d..2f01f2c8 100644 --- a/meson.build +++ b/meson.build @@ -8,7 +8,7 @@ project( 'catch2', 'cpp', - version: '3.15.2', # CML version placeholder, don't delete + version: '3.15.3', # 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 47aef8e8..f3a9f408 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, 15, 2, "", 0 ); + static Version version( 3, 15, 3, "", 0 ); return version; } diff --git a/src/catch2/catch_version_macros.hpp b/src/catch2/catch_version_macros.hpp index 71103dbf..333e113f 100644 --- a/src/catch2/catch_version_macros.hpp +++ b/src/catch2/catch_version_macros.hpp @@ -10,6 +10,6 @@ #define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MINOR 15 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_PATCH 3 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED