Skip to content

Commit b670de4

Browse files
committed
v3.14.0
1 parent 465e63d commit b670de4

7 files changed

Lines changed: 195 additions & 102 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
3535
endif()
3636

3737
project(Catch2
38-
VERSION 3.13.0 # CML version placeholder, don't delete
38+
VERSION 3.14.0 # CML version placeholder, don't delete
3939
LANGUAGES CXX
4040
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
4141
DESCRIPTION "A modern, C++-native, unit test framework."

docs/release-notes.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# Release notes
44
**Contents**<br>
5+
[3.14.0](#3140)<br>
56
[3.13.0](#3130)<br>
67
[3.12.0](#3120)<br>
78
[3.11.0](#3110)<br>
@@ -73,14 +74,29 @@
7374
[Even Older versions](#even-older-versions)<br>
7475

7576

77+
## 3.14.0
78+
79+
### Fixes
80+
* Added missing `<cstdint>` includes. (#3078)
81+
* Fixed suppression of empty variadic macro arguments warning on Clang <19. (#3085)
82+
* Fixed `catch_discover_tests` failing during `PRE_TEST` discovery if a target does not have discoverable tests. (#3075)
83+
* Fixed build of the main library failing with `CATCH_CONFIG_PREFIX_ALL` defined. (#3087)
84+
* JUnit reporter outputs single failed (errored/skipped) assertion per test case. (#1919)
85+
86+
### Improvements
87+
* The default implementation of `--list-tags` and `--list-listeners` has a quiet variant.
88+
* Suppressed the new Clang warning about `__COUNTER__` usage. (#3076)
89+
* Line-wrapping counts utf-8 codepoints instead of bytes. (#1022, #3086)
90+
* Combining character sequences are still miscounted, but Catch2 does not aim to fully support Unicode.
91+
92+
7693
## 3.13.0
7794

7895
### Fixes
7996
* `--benchmark-samples 0` no longer hard crashes (#3056)
8097
* The CLI validation fails instead.
8198
* Fixed warning suppression macros being doubly defined when using Clang on Windows (#3060)
8299

83-
84100
### Improvements
85101
* Suppressed static analysis 26426 diagnostic for MSVC (#3057)
86102
* Renamed the internal deprecation macro from `DEPRECATED` to `CATCH_DEPRECATED` to avoid conflicts (#3058)

extras/catch_amalgamated.cpp

Lines changed: 117 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
// SPDX-License-Identifier: BSL-1.0
88

9-
// Catch v3.13.0
10-
// Generated: 2026-02-15 22:55:00.269529
9+
// Catch v3.14.0
10+
// Generated: 2026-04-05 15:03:01.631668
1111
// ----------------------------------------------------------
1212
// This file is an amalgamation of multiple different files.
1313
// You probably shouldn't edit it directly.
@@ -2397,7 +2397,7 @@ namespace Catch {
23972397
}
23982398

23992399
Version const& libraryVersion() {
2400-
static Version version( 3, 13, 0, "", 0 );
2400+
static Version version( 3, 14, 0, "", 0 );
24012401
return version;
24022402
}
24032403

@@ -5888,9 +5888,14 @@ namespace Catch {
58885888
// can be, so the tracker has to throw for a wrong
58895889
// filter to stop the execution flow.
58905890
if (filter.type == PathFilter::For::Section) {
5891-
// TBD: Explicit SKIP, or new exception that says
5892-
// "don't continue", but doesn't show in totals?
5893-
SKIP();
5891+
// We want the semantics of `SKIP()`, but we inline it
5892+
// to avoid issues with conditionally prefixed macros
5893+
INTERNAL_CATCH_MSG(
5894+
"SKIP",
5895+
Catch::ResultWas::ExplicitSkip,
5896+
Catch::ResultDisposition::Normal,
5897+
"" );
5898+
Catch::Detail::Unreachable();
58945899
}
58955900
// '*' is the wildcard for "all elements in generator"
58965901
// used for filtering sections below the generator, but
@@ -6347,8 +6352,12 @@ namespace Catch {
63476352
// TBD: Do we want to avoid the warning if the generator is filtered?
63486353
if ( m_config->warnAboutInfiniteGenerators() &&
63496354
!generator->isFinite() ) {
6350-
// TBD: Would it be better to expand this macro inline?
6351-
FAIL( "GENERATE() would run infinitely" );
6355+
// We want the semantics of `FAIL()`, but we inline it
6356+
// to avoid issues with conditionally prefixed macros
6357+
INTERNAL_CATCH_MSG( "FAIL",
6358+
Catch::ResultWas::ExplicitFailure,
6359+
Catch::ResultDisposition::Normal,
6360+
"GENERATE() would run infinitely" );
63526361
}
63536362

63546363
auto nameAndLoc = TestCaseTracking::NameAndLocation( static_cast<std::string>( generatorName ), lineInfo );
@@ -7893,6 +7902,10 @@ namespace {
78937902
return std::memchr( chars, c, sizeof( chars ) - 1 ) != nullptr;
78947903
}
78957904

7905+
bool isUtf8ContinuationByte( char c ) {
7906+
return ( static_cast<unsigned char>( c ) & 0xC0 ) == 0x80;
7907+
}
7908+
78967909
} // namespace
78977910

78987911
namespace Catch {
@@ -7919,6 +7932,11 @@ namespace Catch {
79197932
if ( it != m_string.end() ) {
79207933
++m_size;
79217934
++it;
7935+
// Skip UTF-8 continuation bytes
7936+
while ( it != m_string.end() &&
7937+
isUtf8ContinuationByte( *it ) ) {
7938+
++it;
7939+
}
79227940
}
79237941
}
79247942
}
@@ -7981,6 +7999,11 @@ namespace Catch {
79817999
void AnsiSkippingString::const_iterator::advance() {
79828000
assert( m_it != m_string->end() );
79838001
m_it++;
8002+
// Skip UTF-8 continuation bytes
8003+
while ( m_it != m_string->end() &&
8004+
isUtf8ContinuationByte( *m_it ) ) {
8005+
m_it++;
8006+
}
79848007
tryParseAnsiEscapes();
79858008
}
79868009

@@ -8000,6 +8023,11 @@ namespace Catch {
80008023
assert( *m_it == '\033' );
80018024
m_it--;
80028025
}
8026+
// Skip back over UTF-8 continuation bytes to the leading byte
8027+
while ( isUtf8ContinuationByte( *m_it ) ) {
8028+
assert( m_it != m_string->begin() );
8029+
m_it--;
8030+
}
80038031
}
80048032

80058033
static bool isBoundary( AnsiSkippingString const& line,
@@ -9152,12 +9180,12 @@ namespace Catch {
91529180

91539181
void ReporterBase::listReporters(
91549182
std::vector<ReporterDescription> const& descriptions ) {
9155-
defaultListReporters(m_stream, descriptions, m_config->verbosity());
9183+
defaultListReporters( m_stream, descriptions, m_config->verbosity() );
91569184
}
91579185

91589186
void ReporterBase::listListeners(
91599187
std::vector<ListenerDescription> const& descriptions ) {
9160-
defaultListListeners( m_stream, descriptions );
9188+
defaultListListeners( m_stream, descriptions, m_config->verbosity() );
91619189
}
91629190

91639191
void ReporterBase::listTests(std::vector<TestCaseHandle> const& tests) {
@@ -9169,7 +9197,7 @@ namespace Catch {
91699197
}
91709198

91719199
void ReporterBase::listTags(std::vector<TagInfo> const& tags) {
9172-
defaultListTags( m_stream, tags, m_config->hasTestFilters() );
9200+
defaultListTags( m_stream, tags, m_config->hasTestFilters(), m_config->verbosity() );
91739201
}
91749202

91759203
} // namespace Catch
@@ -10380,7 +10408,15 @@ namespace Catch {
1038010408
}
1038110409

1038210410
void defaultListListeners( std::ostream& out,
10383-
std::vector<ListenerDescription> const& descriptions ) {
10411+
std::vector<ListenerDescription> const& descriptions,
10412+
Verbosity verbosity ) {
10413+
if ( verbosity == Verbosity::Quiet ) {
10414+
for ( auto const& desc : descriptions ) {
10415+
out << desc.name << '\n';
10416+
}
10417+
return;
10418+
}
10419+
1038410420
out << "Registered listeners:\n";
1038510421

1038610422
if(descriptions.empty()) {
@@ -10413,7 +10449,14 @@ namespace Catch {
1041310449

1041410450
void defaultListTags( std::ostream& out,
1041510451
std::vector<TagInfo> const& tags,
10416-
bool isFiltered ) {
10452+
bool isFiltered,
10453+
Verbosity verbosity ) {
10454+
if (verbosity == Verbosity::Quiet) {
10455+
for (auto const& tagCount : tags) {
10456+
out << tagCount.all() << '\n';
10457+
}
10458+
return;
10459+
}
1041710460
if ( isFiltered ) {
1041810461
out << "Tags for matching test cases:\n";
1041910462
} else {
@@ -10432,7 +10475,7 @@ namespace Catch {
1043210475
return lhs.count < rhs.count;
1043310476
} )
1043410477
->count;
10435-
10478+
1043610479
// more padding necessary for 3+ digits
1043710480
if (maxTagCount >= 100) {
1043810481
auto numDigits = 1 + std::floor( std::log10( maxTagCount ) );
@@ -11189,70 +11232,77 @@ namespace Catch {
1118911232
void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
1119011233
for (auto const& assertionOrBenchmark : sectionNode.assertionsAndBenchmarks) {
1119111234
if (assertionOrBenchmark.isAssertion()) {
11192-
writeAssertion(assertionOrBenchmark.asAssertion());
11235+
// JUnit XML format supports only 1 error/failure/skip
11236+
// assertion elements per test case
11237+
if (writeAssertion(assertionOrBenchmark.asAssertion())) {
11238+
break;
11239+
}
1119311240
}
1119411241
}
1119511242
}
1119611243

11197-
void JunitReporter::writeAssertion( AssertionStats const& stats ) {
11244+
bool JunitReporter::writeAssertion( AssertionStats const& stats ) {
1119811245
AssertionResult const& result = stats.assertionResult;
11199-
if ( !result.isOk() ||
11200-
result.getResultType() == ResultWas::ExplicitSkip ) {
11201-
std::string elementName;
11202-
switch( result.getResultType() ) {
11203-
case ResultWas::ThrewException:
11204-
case ResultWas::FatalErrorCondition:
11205-
elementName = "error";
11206-
break;
11207-
case ResultWas::ExplicitFailure:
11208-
case ResultWas::ExpressionFailed:
11209-
case ResultWas::DidntThrowException:
11210-
elementName = "failure";
11211-
break;
11212-
case ResultWas::ExplicitSkip:
11213-
elementName = "skipped";
11214-
break;
11215-
// We should never see these here:
11216-
case ResultWas::Info:
11217-
case ResultWas::Warning:
11218-
case ResultWas::Ok:
11219-
case ResultWas::Unknown:
11220-
case ResultWas::FailureBit:
11221-
case ResultWas::Exception:
11222-
elementName = "internalError";
11223-
break;
11224-
}
11246+
if ( result.isOk() &&
11247+
result.getResultType() != ResultWas::ExplicitSkip ) {
11248+
return false;
11249+
}
11250+
std::string elementName;
11251+
switch ( result.getResultType() ) {
11252+
case ResultWas::ThrewException:
11253+
case ResultWas::FatalErrorCondition:
11254+
elementName = "error";
11255+
break;
11256+
case ResultWas::ExplicitFailure:
11257+
case ResultWas::ExpressionFailed:
11258+
case ResultWas::DidntThrowException:
11259+
elementName = "failure";
11260+
break;
11261+
case ResultWas::ExplicitSkip:
11262+
elementName = "skipped";
11263+
break;
11264+
// We should never see these here:
11265+
case ResultWas::Info:
11266+
case ResultWas::Warning:
11267+
case ResultWas::Ok:
11268+
case ResultWas::Unknown:
11269+
case ResultWas::FailureBit:
11270+
case ResultWas::Exception:
11271+
elementName = "internalError";
11272+
break;
11273+
}
1122511274

11226-
XmlWriter::ScopedElement e = xml.scopedElement( elementName );
11275+
XmlWriter::ScopedElement e = xml.scopedElement( elementName );
1122711276

11228-
xml.writeAttribute( "message"_sr, result.getExpression() );
11229-
xml.writeAttribute( "type"_sr, result.getTestMacroName() );
11277+
xml.writeAttribute( "message"_sr, result.getExpression() );
11278+
xml.writeAttribute( "type"_sr, result.getTestMacroName() );
1123011279

11231-
ReusableStringStream rss;
11232-
if ( result.getResultType() == ResultWas::ExplicitSkip ) {
11233-
rss << "SKIPPED\n";
11234-
} else {
11235-
rss << "FAILED" << ":\n";
11236-
if (result.hasExpression()) {
11237-
rss << " ";
11238-
rss << result.getExpressionInMacro();
11239-
rss << '\n';
11240-
}
11241-
if (result.hasExpandedExpression()) {
11242-
rss << "with expansion:\n";
11243-
rss << TextFlow::Column(result.getExpandedExpression()).indent(2) << '\n';
11244-
}
11280+
ReusableStringStream rss;
11281+
if ( result.getResultType() == ResultWas::ExplicitSkip ) {
11282+
rss << "SKIPPED\n";
11283+
} else {
11284+
rss << "FAILED:\n";
11285+
if ( result.hasExpression() ) {
11286+
rss << " ";
11287+
rss << result.getExpressionInMacro();
11288+
rss << '\n';
11289+
}
11290+
if ( result.hasExpandedExpression() ) {
11291+
rss << "with expansion:\n";
11292+
rss << TextFlow::Column( result.getExpandedExpression() )
11293+
.indent( 2 )
11294+
<< '\n';
1124511295
}
11296+
}
1124611297

11247-
if( result.hasMessage() )
11248-
rss << result.getMessage() << '\n';
11249-
for( auto const& msg : stats.infoMessages )
11250-
if( msg.type == ResultWas::Info )
11251-
rss << msg.message << '\n';
11252-
11253-
rss << "at " << result.getSourceInfo();
11254-
xml.writeText( rss.str(), XmlFormatting::Newline );
11298+
if ( result.hasMessage() ) { rss << result.getMessage() << '\n'; }
11299+
for ( auto const& msg : stats.infoMessages ) {
11300+
if ( msg.type == ResultWas::Info ) { rss << msg.message << '\n'; }
1125511301
}
11302+
11303+
rss << "at " << result.getSourceInfo();
11304+
xml.writeText( rss.str(), XmlFormatting::Newline );
11305+
return true;
1125611306
}
1125711307

1125811308
} // end namespace Catch

0 commit comments

Comments
 (0)