This commit is contained in:
Martin Hořeňovský 2023-12-31 15:15:04 +01:00
parent b7b71ffd3a
commit f981c9cbca
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
7 changed files with 385 additions and 286 deletions

View File

@ -33,7 +33,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif() endif()
project(Catch2 project(Catch2
VERSION 3.5.0 # CML version placeholder, don't delete VERSION 3.5.1 # CML version placeholder, don't delete
LANGUAGES CXX LANGUAGES CXX
# HOMEPAGE_URL is not supported until CMake version 3.12, which # HOMEPAGE_URL is not supported until CMake version 3.12, which
# we do not target yet. # we do not target yet.

View File

@ -2,6 +2,7 @@
# Release notes # Release notes
**Contents**<br> **Contents**<br>
[3.5.1](#351)<br>
[3.5.0](#350)<br> [3.5.0](#350)<br>
[3.4.0](#340)<br> [3.4.0](#340)<br>
[3.3.2](#332)<br> [3.3.2](#332)<br>
@ -58,6 +59,17 @@
[Even Older versions](#even-older-versions)<br> [Even Older versions](#even-older-versions)<br>
## 3.5.1
### Improvements
* Significantly improved performance of the CLI parsing.
* This includes the cost of preparing the CLI parser, so Catch2's binaries start much faster.
### Miscellaneous
* Added support for Bazel modules (#2781)
* Added CMake option to disable the build reproducibility settings (#2785)
* Added `log` library linking to the Meson build (#2784)
## 3.5.0 ## 3.5.0

View File

@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
// Catch v3.5.0 // Catch v3.5.1
// Generated: 2023-12-11 00:51:07.662625 // Generated: 2023-12-31 15:10:55.864983
// ---------------------------------------------------------- // ----------------------------------------------------------
// This file is an amalgamation of multiple different files. // This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly. // You probably shouldn't edit it directly.
@ -2273,7 +2273,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 3, 5, 0, "", 0 ); static Version version( 3, 5, 1, "", 0 );
return version; return version;
} }
@ -2415,9 +2415,7 @@ namespace Catch {
#include <algorithm>
#include <cassert> #include <cassert>
#include <iomanip>
namespace Catch { namespace Catch {
@ -2627,15 +2625,31 @@ namespace {
; ;
} }
std::string normaliseOpt( std::string const& optName ) { Catch::StringRef normaliseOpt( Catch::StringRef optName ) {
#ifdef CATCH_PLATFORM_WINDOWS if ( optName[0] == '-'
if ( optName[0] == '/' ) #if defined(CATCH_PLATFORM_WINDOWS)
return "-" + optName.substr( 1 ); || optName[0] == '/'
else
#endif #endif
) {
return optName.substr( 1, optName.size() );
}
return optName; return optName;
} }
static size_t find_first_separator(Catch::StringRef sr) {
auto is_separator = []( char c ) {
return c == ' ' || c == ':' || c == '=';
};
size_t pos = 0;
while (pos < sr.size()) {
if (is_separator(sr[pos])) { return pos; }
++pos;
}
return Catch::StringRef::npos;
}
} // namespace } // namespace
namespace Catch { namespace Catch {
@ -2651,23 +2665,23 @@ namespace Catch {
} }
if ( it != itEnd ) { if ( it != itEnd ) {
auto const& next = *it; StringRef next = *it;
if ( isOptPrefix( next[0] ) ) { if ( isOptPrefix( next[0] ) ) {
auto delimiterPos = next.find_first_of( " :=" ); auto delimiterPos = find_first_separator(next);
if ( delimiterPos != std::string::npos ) { if ( delimiterPos != StringRef::npos ) {
m_tokenBuffer.push_back( m_tokenBuffer.push_back(
{ TokenType::Option, { TokenType::Option,
next.substr( 0, delimiterPos ) } ); next.substr( 0, delimiterPos ) } );
m_tokenBuffer.push_back( m_tokenBuffer.push_back(
{ TokenType::Argument, { TokenType::Argument,
next.substr( delimiterPos + 1 ) } ); next.substr( delimiterPos + 1, next.size() ) } );
} else { } else {
if ( next[1] != '-' && next.size() > 2 ) { if ( next[1] != '-' && next.size() > 2 ) {
std::string opt = "- "; // Combined short args, e.g. "-ab" for "-a -b"
for ( size_t i = 1; i < next.size(); ++i ) { for ( size_t i = 1; i < next.size(); ++i ) {
opt[1] = next[i];
m_tokenBuffer.push_back( m_tokenBuffer.push_back(
{ TokenType::Option, opt } ); { TokenType::Option,
next.substr( i, 1 ) } );
} }
} else { } else {
m_tokenBuffer.push_back( m_tokenBuffer.push_back(
@ -2727,12 +2741,12 @@ namespace Catch {
size_t ParserBase::cardinality() const { return 1; } size_t ParserBase::cardinality() const { return 1; }
InternalParseResult ParserBase::parse( Args const& args ) const { InternalParseResult ParserBase::parse( Args const& args ) const {
return parse( args.exeName(), TokenStream( args ) ); return parse( static_cast<std::string>(args.exeName()), TokenStream( args ) );
} }
ParseState::ParseState( ParseResultType type, ParseState::ParseState( ParseResultType type,
TokenStream const& remainingTokens ): TokenStream remainingTokens ):
m_type( type ), m_remainingTokens( remainingTokens ) {} m_type( type ), m_remainingTokens( CATCH_MOVE(remainingTokens) ) {}
ParserResult BoundFlagRef::setFlag( bool flag ) { ParserResult BoundFlagRef::setFlag( bool flag ) {
m_ref = flag; m_ref = flag;
@ -2750,34 +2764,34 @@ namespace Catch {
} // namespace Detail } // namespace Detail
Detail::InternalParseResult Arg::parse(std::string const&, Detail::InternalParseResult Arg::parse(std::string const&,
Detail::TokenStream const& tokens) const { Detail::TokenStream tokens) const {
auto validationResult = validate(); auto validationResult = validate();
if (!validationResult) if (!validationResult)
return Detail::InternalParseResult(validationResult); return Detail::InternalParseResult(validationResult);
auto remainingTokens = tokens; auto token = *tokens;
auto const& token = *remainingTokens;
if (token.type != Detail::TokenType::Argument) if (token.type != Detail::TokenType::Argument)
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
ParseResultType::NoMatch, remainingTokens)); ParseResultType::NoMatch, CATCH_MOVE(tokens)));
assert(!m_ref->isFlag()); assert(!m_ref->isFlag());
auto valueRef = auto valueRef =
static_cast<Detail::BoundValueRefBase*>(m_ref.get()); static_cast<Detail::BoundValueRefBase*>(m_ref.get());
auto result = valueRef->setValue(remainingTokens->token); auto result = valueRef->setValue(static_cast<std::string>(token.token));
if ( !result ) if ( !result )
return Detail::InternalParseResult( result ); return Detail::InternalParseResult( result );
else else
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(
ParseResultType::Matched, ++remainingTokens)); Detail::ParseState( ParseResultType::Matched,
CATCH_MOVE( ++tokens ) ) );
} }
Opt::Opt(bool& ref) : Opt::Opt(bool& ref) :
ParserRefImpl(std::make_shared<Detail::BoundFlagRef>(ref)) {} ParserRefImpl(std::make_shared<Detail::BoundFlagRef>(ref)) {}
std::vector<Detail::HelpColumns> Opt::getHelpColumns() const { Detail::HelpColumns Opt::getHelpColumns() const {
std::ostringstream oss; ReusableStringStream oss;
bool first = true; bool first = true;
for (auto const& opt : m_optNames) { for (auto const& opt : m_optNames) {
if (first) if (first)
@ -2788,10 +2802,10 @@ namespace Catch {
} }
if (!m_hint.empty()) if (!m_hint.empty())
oss << " <" << m_hint << '>'; oss << " <" << m_hint << '>';
return { { oss.str(), m_description } }; return { oss.str(), m_description };
} }
bool Opt::isMatch(std::string const& optToken) const { bool Opt::isMatch(StringRef optToken) const {
auto normalisedToken = normaliseOpt(optToken); auto normalisedToken = normaliseOpt(optToken);
for (auto const& name : m_optNames) { for (auto const& name : m_optNames) {
if (normaliseOpt(name) == normalisedToken) if (normaliseOpt(name) == normalisedToken)
@ -2801,15 +2815,14 @@ namespace Catch {
} }
Detail::InternalParseResult Opt::parse(std::string const&, Detail::InternalParseResult Opt::parse(std::string const&,
Detail::TokenStream const& tokens) const { Detail::TokenStream tokens) const {
auto validationResult = validate(); auto validationResult = validate();
if (!validationResult) if (!validationResult)
return Detail::InternalParseResult(validationResult); return Detail::InternalParseResult(validationResult);
auto remainingTokens = tokens; if (tokens &&
if (remainingTokens && tokens->type == Detail::TokenType::Option) {
remainingTokens->type == Detail::TokenType::Option) { auto const& token = *tokens;
auto const& token = *remainingTokens;
if (isMatch(token.token)) { if (isMatch(token.token)) {
if (m_ref->isFlag()) { if (m_ref->isFlag()) {
auto flagRef = auto flagRef =
@ -2821,35 +2834,35 @@ namespace Catch {
if (result.value() == if (result.value() ==
ParseResultType::ShortCircuitAll) ParseResultType::ShortCircuitAll)
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
result.value(), remainingTokens)); result.value(), CATCH_MOVE(tokens)));
} else { } else {
auto valueRef = auto valueRef =
static_cast<Detail::BoundValueRefBase*>( static_cast<Detail::BoundValueRefBase*>(
m_ref.get()); m_ref.get());
++remainingTokens; ++tokens;
if (!remainingTokens) if (!tokens)
return Detail::InternalParseResult::runtimeError( return Detail::InternalParseResult::runtimeError(
"Expected argument following " + "Expected argument following " +
token.token); token.token);
auto const& argToken = *remainingTokens; auto const& argToken = *tokens;
if (argToken.type != Detail::TokenType::Argument) if (argToken.type != Detail::TokenType::Argument)
return Detail::InternalParseResult::runtimeError( return Detail::InternalParseResult::runtimeError(
"Expected argument following " + "Expected argument following " +
token.token); token.token);
const auto result = valueRef->setValue(argToken.token); const auto result = valueRef->setValue(static_cast<std::string>(argToken.token));
if (!result) if (!result)
return Detail::InternalParseResult(result); return Detail::InternalParseResult(result);
if (result.value() == if (result.value() ==
ParseResultType::ShortCircuitAll) ParseResultType::ShortCircuitAll)
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
result.value(), remainingTokens)); result.value(), CATCH_MOVE(tokens)));
} }
return Detail::InternalParseResult::ok(Detail::ParseState( return Detail::InternalParseResult::ok(Detail::ParseState(
ParseResultType::Matched, ++remainingTokens)); ParseResultType::Matched, CATCH_MOVE(++tokens)));
} }
} }
return Detail::InternalParseResult::ok( return Detail::InternalParseResult::ok(
Detail::ParseState(ParseResultType::NoMatch, remainingTokens)); Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens)));
} }
Detail::Result Opt::validate() const { Detail::Result Opt::validate() const {
@ -2881,9 +2894,9 @@ namespace Catch {
Detail::InternalParseResult Detail::InternalParseResult
ExeName::parse(std::string const&, ExeName::parse(std::string const&,
Detail::TokenStream const& tokens) const { Detail::TokenStream tokens) const {
return Detail::InternalParseResult::ok( return Detail::InternalParseResult::ok(
Detail::ParseState(ParseResultType::NoMatch, tokens)); Detail::ParseState(ParseResultType::NoMatch, CATCH_MOVE(tokens)));
} }
ParserResult ExeName::set(std::string const& newName) { ParserResult ExeName::set(std::string const& newName) {
@ -2913,9 +2926,9 @@ namespace Catch {
std::vector<Detail::HelpColumns> Parser::getHelpColumns() const { std::vector<Detail::HelpColumns> Parser::getHelpColumns() const {
std::vector<Detail::HelpColumns> cols; std::vector<Detail::HelpColumns> cols;
cols.reserve( m_options.size() );
for ( auto const& o : m_options ) { for ( auto const& o : m_options ) {
auto childCols = o.getHelpColumns(); cols.push_back(o.getHelpColumns());
cols.insert( cols.end(), childCols.begin(), childCols.end() );
} }
return cols; return cols;
} }
@ -2953,12 +2966,12 @@ namespace Catch {
optWidth = ( std::min )( optWidth, consoleWidth / 2 ); optWidth = ( std::min )( optWidth, consoleWidth / 2 );
for ( auto const& cols : rows ) { for ( auto& cols : rows ) {
auto row = TextFlow::Column( cols.left ) auto row = TextFlow::Column( CATCH_MOVE(cols.left) )
.width( optWidth ) .width( optWidth )
.indent( 2 ) + .indent( 2 ) +
TextFlow::Spacer( 4 ) + TextFlow::Spacer( 4 ) +
TextFlow::Column( cols.right ) TextFlow::Column( static_cast<std::string>(cols.descriptions) )
.width( consoleWidth - 7 - optWidth ); .width( consoleWidth - 7 - optWidth );
os << row << '\n'; os << row << '\n';
} }
@ -2980,7 +2993,7 @@ namespace Catch {
Detail::InternalParseResult Detail::InternalParseResult
Parser::parse( std::string const& exeName, Parser::parse( std::string const& exeName,
Detail::TokenStream const& tokens ) const { Detail::TokenStream tokens ) const {
struct ParserInfo { struct ParserInfo {
ParserBase const* parser = nullptr; ParserBase const* parser = nullptr;
@ -2998,7 +3011,7 @@ namespace Catch {
m_exeName.set( exeName ); m_exeName.set( exeName );
auto result = Detail::InternalParseResult::ok( auto result = Detail::InternalParseResult::ok(
Detail::ParseState( ParseResultType::NoMatch, tokens ) ); Detail::ParseState( ParseResultType::NoMatch, CATCH_MOVE(tokens) ) );
while ( result.value().remainingTokens() ) { while ( result.value().remainingTokens() ) {
bool tokenParsed = false; bool tokenParsed = false;
@ -3006,7 +3019,7 @@ namespace Catch {
if ( parseInfo.parser->cardinality() == 0 || if ( parseInfo.parser->cardinality() == 0 ||
parseInfo.count < parseInfo.parser->cardinality() ) { parseInfo.count < parseInfo.parser->cardinality() ) {
result = parseInfo.parser->parse( result = parseInfo.parser->parse(
exeName, result.value().remainingTokens() ); exeName, CATCH_MOVE(result).value().remainingTokens() );
if ( !result ) if ( !result )
return result; return result;
if ( result.value().type() != if ( result.value().type() !=
@ -3032,7 +3045,7 @@ namespace Catch {
Args::Args(int argc, char const* const* argv) : Args::Args(int argc, char const* const* argv) :
m_exeName(argv[0]), m_args(argv + 1, argv + argc) {} m_exeName(argv[0]), m_args(argv + 1, argv + argc) {}
Args::Args(std::initializer_list<std::string> args) : Args::Args(std::initializer_list<StringRef> args) :
m_exeName(*args.begin()), m_exeName(*args.begin()),
m_args(args.begin() + 1, args.end()) {} m_args(args.begin() + 1, args.end()) {}
@ -3338,8 +3351,8 @@ namespace Catch {
( "split the tests to execute into this many groups" ) ( "split the tests to execute into this many groups" )
| Opt( setShardIndex, "shard index" ) | Opt( setShardIndex, "shard index" )
["--shard-index"] ["--shard-index"]
( "index of the group of tests to execute (see --shard-count)" ) | ( "index of the group of tests to execute (see --shard-count)" )
Opt( config.allowZeroTests ) | Opt( config.allowZeroTests )
["--allow-running-no-tests"] ["--allow-running-no-tests"]
( "Treat 'No tests run' as a success" ) ( "Treat 'No tests run' as a success" )
| Arg( config.testsOrTags, "test name|pattern|tags" ) | Arg( config.testsOrTags, "test name|pattern|tags" )
@ -4641,7 +4654,6 @@ Catch::LeakDetector::~LeakDetector() {
namespace Catch { namespace Catch {
namespace { namespace {
@ -6527,7 +6539,6 @@ namespace Catch {
return sorted; return sorted;
} }
case TestRunOrder::Randomized: { case TestRunOrder::Randomized: {
seedRng(config);
using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>; using TestWithHash = std::pair<TestCaseInfoHasher::hash_t, TestCaseHandle>;
TestCaseInfoHasher h{ config.rngSeed() }; TestCaseInfoHasher h{ config.rngSeed() };
@ -7390,23 +7401,36 @@ namespace Catch {
return os; return os;
} }
Columns Column::operator+( Column const& other ) { Columns operator+(Column const& lhs, Column const& rhs) {
Columns cols; Columns cols;
cols += *this; cols += lhs;
cols += other; cols += rhs;
return cols;
}
Columns operator+(Column&& lhs, Column&& rhs) {
Columns cols;
cols += CATCH_MOVE( lhs );
cols += CATCH_MOVE( rhs );
return cols; return cols;
} }
Columns& Columns::operator+=( Column const& col ) { Columns& operator+=(Columns& lhs, Column const& rhs) {
m_columns.push_back( col ); lhs.m_columns.push_back( rhs );
return *this; return lhs;
} }
Columns& operator+=(Columns& lhs, Column&& rhs) {
Columns Columns::operator+( Column const& col ) { lhs.m_columns.push_back( CATCH_MOVE(rhs) );
Columns combined = *this; return lhs;
combined += col; }
Columns operator+( Columns const& lhs, Column const& rhs ) {
auto combined( lhs );
combined += rhs;
return combined; return combined;
} }
Columns operator+( Columns&& lhs, Column&& rhs ) {
lhs += CATCH_MOVE( rhs );
return CATCH_MOVE( lhs );
}
} // namespace TextFlow } // namespace TextFlow
} // namespace Catch } // namespace Catch
@ -8881,11 +8905,10 @@ public:
*this << RowBreak(); *this << RowBreak();
TextFlow::Columns headerCols; TextFlow::Columns headerCols;
auto spacer = TextFlow::Spacer(2);
for (auto const& info : m_columnInfos) { for (auto const& info : m_columnInfos) {
assert(info.width > 2); assert(info.width > 2);
headerCols += TextFlow::Column(info.name).width(info.width - 2); headerCols += TextFlow::Column(info.name).width(info.width - 2);
headerCols += spacer; headerCols += TextFlow::Spacer( 2 );
} }
m_os << headerCols << '\n'; m_os << headerCols << '\n';

View File

@ -6,8 +6,8 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
// Catch v3.5.0 // Catch v3.5.1
// Generated: 2023-12-11 00:51:06.770598 // Generated: 2023-12-31 15:10:53.044176
// ---------------------------------------------------------- // ----------------------------------------------------------
// This file is an amalgamation of multiple different files. // This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly. // You probably shouldn't edit it directly.
@ -690,6 +690,8 @@ namespace Catch {
using size_type = std::size_t; using size_type = std::size_t;
using const_iterator = const char*; using const_iterator = const char*;
static constexpr size_type npos{ static_cast<size_type>( -1 ) };
private: private:
static constexpr char const* const s_empty = ""; static constexpr char const* const s_empty = "";
@ -740,7 +742,7 @@ namespace Catch {
} }
// Returns a substring of [start, start + length). // Returns a substring of [start, start + length).
// If start + length > size(), then the substring is [start, start + size()). // If start + length > size(), then the substring is [start, size()).
// If start > size(), then the substring is empty. // If start > size(), then the substring is empty.
constexpr StringRef substr(size_type start, size_type length) const noexcept { constexpr StringRef substr(size_type start, size_type length) const noexcept {
if (start < m_size) { if (start < m_size) {
@ -1969,12 +1971,12 @@ namespace Catch {
template <typename Clock> template <typename Clock>
int warmup() { int warmup() {
return run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(warmup_time), warmup_seed, &resolution<Clock>) return run_for_at_least<Clock>(warmup_time, warmup_seed, &resolution<Clock>)
.iterations; .iterations;
} }
template <typename Clock> template <typename Clock>
EnvironmentEstimate estimate_clock_resolution(int iterations) { EnvironmentEstimate estimate_clock_resolution(int iterations) {
auto r = run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(clock_resolution_estimation_time), iterations, &resolution<Clock>) auto r = run_for_at_least<Clock>(clock_resolution_estimation_time, iterations, &resolution<Clock>)
.result; .result;
return { return {
FDuration(mean(r.data(), r.data() + r.size())), FDuration(mean(r.data(), r.data() + r.size())),
@ -1996,7 +1998,7 @@ namespace Catch {
}; };
time_clock(1); time_clock(1);
int iters = clock_cost_estimation_iterations; int iters = clock_cost_estimation_iterations;
auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(clock_cost_estimation_time), iters, time_clock); auto&& r = run_for_at_least<Clock>(clock_cost_estimation_time, iters, time_clock);
std::vector<double> times; std::vector<double> times;
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed)); int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
times.reserve(static_cast<size_t>(nsamples)); times.reserve(static_cast<size_t>(nsamples));
@ -3639,141 +3641,6 @@ namespace Catch {
#define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED #define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED
#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED
#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED
#include <iosfwd>
#include <cstdint>
namespace Catch {
enum class ColourMode : std::uint8_t;
class IStream;
struct Colour {
enum Code {
None = 0,
White,
Red,
Green,
Blue,
Cyan,
Yellow,
Grey,
Bright = 0x10,
BrightRed = Bright | Red,
BrightGreen = Bright | Green,
LightGrey = Bright | Grey,
BrightWhite = Bright | White,
BrightYellow = Bright | Yellow,
// By intention
FileName = LightGrey,
Warning = BrightYellow,
ResultError = BrightRed,
ResultSuccess = BrightGreen,
ResultExpectedFailure = Warning,
Error = BrightRed,
Success = Green,
Skip = LightGrey,
OriginalExpression = Cyan,
ReconstructedExpression = BrightYellow,
SecondaryText = LightGrey,
Headers = White
};
};
class ColourImpl {
protected:
//! The associated stream of this ColourImpl instance
IStream* m_stream;
public:
ColourImpl( IStream* stream ): m_stream( stream ) {}
//! RAII wrapper around writing specific colour of text using specific
//! colour impl into a stream.
class ColourGuard {
ColourImpl const* m_colourImpl;
Colour::Code m_code;
bool m_engaged = false;
public:
//! Does **not** engage the guard/start the colour
ColourGuard( Colour::Code code,
ColourImpl const* colour );
ColourGuard( ColourGuard const& rhs ) = delete;
ColourGuard& operator=( ColourGuard const& rhs ) = delete;
ColourGuard( ColourGuard&& rhs ) noexcept;
ColourGuard& operator=( ColourGuard&& rhs ) noexcept;
//! Removes colour _if_ the guard was engaged
~ColourGuard();
/**
* Explicitly engages colour for given stream.
*
* The API based on operator<< should be preferred.
*/
ColourGuard& engage( std::ostream& stream ) &;
/**
* Explicitly engages colour for given stream.
*
* The API based on operator<< should be preferred.
*/
ColourGuard&& engage( std::ostream& stream ) &&;
private:
//! Engages the guard and starts using colour
friend std::ostream& operator<<( std::ostream& lhs,
ColourGuard& guard ) {
guard.engageImpl( lhs );
return lhs;
}
//! Engages the guard and starts using colour
friend std::ostream& operator<<( std::ostream& lhs,
ColourGuard&& guard) {
guard.engageImpl( lhs );
return lhs;
}
void engageImpl( std::ostream& stream );
};
virtual ~ColourImpl(); // = default
/**
* Creates a guard object for given colour and this colour impl
*
* **Important:**
* the guard starts disengaged, and has to be engaged explicitly.
*/
ColourGuard guardColour( Colour::Code colourCode );
private:
virtual void use( Colour::Code colourCode ) const = 0;
};
//! Provides ColourImpl based on global config and target compilation platform
Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection,
IStream* stream );
//! Checks if specific colour impl has been compiled into the binary
bool isColourImplAvailable( ColourMode colourSelection );
} // end namespace Catch
#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
@ -4387,17 +4254,16 @@ namespace Catch {
enum class TokenType { Option, Argument }; enum class TokenType { Option, Argument };
struct Token { struct Token {
TokenType type; TokenType type;
std::string token; StringRef token;
}; };
// Abstracts iterators into args as a stream of tokens, with option // Abstracts iterators into args as a stream of tokens, with option
// arguments uniformly handled // arguments uniformly handled
class TokenStream { class TokenStream {
using Iterator = std::vector<std::string>::const_iterator; using Iterator = std::vector<StringRef>::const_iterator;
Iterator it; Iterator it;
Iterator itEnd; Iterator itEnd;
std::vector<Token> m_tokenBuffer; std::vector<Token> m_tokenBuffer;
void loadBuffer(); void loadBuffer();
public: public:
@ -4449,12 +4315,17 @@ namespace Catch {
ResultType m_type; ResultType m_type;
}; };
template <typename T> class ResultValueBase : public ResultBase { template <typename T>
class ResultValueBase : public ResultBase {
public: public:
auto value() const -> T const& { T const& value() const& {
enforceOk(); enforceOk();
return m_value; return m_value;
} }
T&& value() && {
enforceOk();
return CATCH_MOVE( m_value );
}
protected: protected:
ResultValueBase( ResultType type ): ResultBase( type ) {} ResultValueBase( ResultType type ): ResultBase( type ) {}
@ -4464,13 +4335,23 @@ namespace Catch {
if ( m_type == ResultType::Ok ) if ( m_type == ResultType::Ok )
new ( &m_value ) T( other.m_value ); new ( &m_value ) T( other.m_value );
} }
ResultValueBase( ResultValueBase&& other ):
ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { ResultBase( other ) {
new ( &m_value ) T( value ); if ( m_type == ResultType::Ok )
new ( &m_value ) T( CATCH_MOVE(other.m_value) );
} }
auto operator=( ResultValueBase const& other )
-> ResultValueBase& { ResultValueBase( ResultType, T const& value ):
ResultBase( ResultType::Ok ) {
new ( &m_value ) T( value );
}
ResultValueBase( ResultType, T&& value ):
ResultBase( ResultType::Ok ) {
new ( &m_value ) T( CATCH_MOVE(value) );
}
ResultValueBase& operator=( ResultValueBase const& other ) {
if ( m_type == ResultType::Ok ) if ( m_type == ResultType::Ok )
m_value.~T(); m_value.~T();
ResultBase::operator=( other ); ResultBase::operator=( other );
@ -4478,6 +4359,14 @@ namespace Catch {
new ( &m_value ) T( other.m_value ); new ( &m_value ) T( other.m_value );
return *this; return *this;
} }
ResultValueBase& operator=( ResultValueBase&& other ) {
if ( m_type == ResultType::Ok ) m_value.~T();
ResultBase::operator=( other );
if ( m_type == ResultType::Ok )
new ( &m_value ) T( CATCH_MOVE(other.m_value) );
return *this;
}
~ResultValueBase() override { ~ResultValueBase() override {
if ( m_type == ResultType::Ok ) if ( m_type == ResultType::Ok )
@ -4505,8 +4394,8 @@ namespace Catch {
} }
template <typename U> template <typename U>
static auto ok( U const& value ) -> BasicResult { static auto ok( U&& value ) -> BasicResult {
return { ResultType::Ok, value }; return { ResultType::Ok, CATCH_FORWARD(value) };
} }
static auto ok() -> BasicResult { return { ResultType::Ok }; } static auto ok() -> BasicResult { return { ResultType::Ok }; }
static auto logicError( std::string&& message ) static auto logicError( std::string&& message )
@ -4553,12 +4442,15 @@ namespace Catch {
class ParseState { class ParseState {
public: public:
ParseState( ParseResultType type, ParseState( ParseResultType type,
TokenStream const& remainingTokens ); TokenStream remainingTokens );
ParseResultType type() const { return m_type; } ParseResultType type() const { return m_type; }
TokenStream const& remainingTokens() const { TokenStream const& remainingTokens() const& {
return m_remainingTokens; return m_remainingTokens;
} }
TokenStream&& remainingTokens() && {
return CATCH_MOVE( m_remainingTokens );
}
private: private:
ParseResultType m_type; ParseResultType m_type;
@ -4571,7 +4463,7 @@ namespace Catch {
struct HelpColumns { struct HelpColumns {
std::string left; std::string left;
std::string right; StringRef descriptions;
}; };
template <typename T> template <typename T>
@ -4731,7 +4623,7 @@ namespace Catch {
virtual ~ParserBase() = default; virtual ~ParserBase() = default;
virtual auto validate() const -> Result { return Result::ok(); } virtual auto validate() const -> Result { return Result::ok(); }
virtual auto parse( std::string const& exeName, virtual auto parse( std::string const& exeName,
TokenStream const& tokens ) const TokenStream tokens ) const
-> InternalParseResult = 0; -> InternalParseResult = 0;
virtual size_t cardinality() const; virtual size_t cardinality() const;
@ -4751,8 +4643,8 @@ namespace Catch {
protected: protected:
Optionality m_optionality = Optionality::Optional; Optionality m_optionality = Optionality::Optional;
std::shared_ptr<BoundRef> m_ref; std::shared_ptr<BoundRef> m_ref;
std::string m_hint; StringRef m_hint;
std::string m_description; StringRef m_description;
explicit ParserRefImpl( std::shared_ptr<BoundRef> const& ref ): explicit ParserRefImpl( std::shared_ptr<BoundRef> const& ref ):
m_ref( ref ) {} m_ref( ref ) {}
@ -4761,28 +4653,32 @@ namespace Catch {
template <typename LambdaT> template <typename LambdaT>
ParserRefImpl( accept_many_t, ParserRefImpl( accept_many_t,
LambdaT const& ref, LambdaT const& ref,
std::string const& hint ): StringRef hint ):
m_ref( std::make_shared<BoundManyLambda<LambdaT>>( ref ) ), m_ref( std::make_shared<BoundManyLambda<LambdaT>>( ref ) ),
m_hint( hint ) {} m_hint( hint ) {}
template <typename T, template <typename T,
typename = typename std::enable_if_t< typename = typename std::enable_if_t<
!Detail::is_unary_function<T>::value>> !Detail::is_unary_function<T>::value>>
ParserRefImpl( T& ref, std::string const& hint ): ParserRefImpl( T& ref, StringRef hint ):
m_ref( std::make_shared<BoundValueRef<T>>( ref ) ), m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
m_hint( hint ) {} m_hint( hint ) {}
template <typename LambdaT, template <typename LambdaT,
typename = typename std::enable_if_t< typename = typename std::enable_if_t<
Detail::is_unary_function<LambdaT>::value>> Detail::is_unary_function<LambdaT>::value>>
ParserRefImpl( LambdaT const& ref, std::string const& hint ): ParserRefImpl( LambdaT const& ref, StringRef hint ):
m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ), m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
m_hint( hint ) {} m_hint( hint ) {}
auto operator()( std::string const& description ) -> DerivedT& { DerivedT& operator()( StringRef description ) & {
m_description = description; m_description = description;
return static_cast<DerivedT&>( *this ); return static_cast<DerivedT&>( *this );
} }
DerivedT&& operator()( StringRef description ) && {
m_description = description;
return static_cast<DerivedT&&>( *this );
}
auto optional() -> DerivedT& { auto optional() -> DerivedT& {
m_optionality = Optionality::Optional; m_optionality = Optionality::Optional;
@ -4805,7 +4701,7 @@ namespace Catch {
return 1; return 1;
} }
std::string const& hint() const { return m_hint; } StringRef hint() const { return m_hint; }
}; };
} // namespace detail } // namespace detail
@ -4819,13 +4715,13 @@ namespace Catch {
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const&, parse(std::string const&,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
}; };
// A parser for options // A parser for options
class Opt : public Detail::ParserRefImpl<Opt> { class Opt : public Detail::ParserRefImpl<Opt> {
protected: protected:
std::vector<std::string> m_optNames; std::vector<StringRef> m_optNames;
public: public:
template <typename LambdaT> template <typename LambdaT>
@ -4838,33 +4734,37 @@ namespace Catch {
template <typename LambdaT, template <typename LambdaT,
typename = typename std::enable_if_t< typename = typename std::enable_if_t<
Detail::is_unary_function<LambdaT>::value>> Detail::is_unary_function<LambdaT>::value>>
Opt( LambdaT const& ref, std::string const& hint ): Opt( LambdaT const& ref, StringRef hint ):
ParserRefImpl( ref, hint ) {} ParserRefImpl( ref, hint ) {}
template <typename LambdaT> template <typename LambdaT>
Opt( accept_many_t, LambdaT const& ref, std::string const& hint ): Opt( accept_many_t, LambdaT const& ref, StringRef hint ):
ParserRefImpl( accept_many, ref, hint ) {} ParserRefImpl( accept_many, ref, hint ) {}
template <typename T, template <typename T,
typename = typename std::enable_if_t< typename = typename std::enable_if_t<
!Detail::is_unary_function<T>::value>> !Detail::is_unary_function<T>::value>>
Opt( T& ref, std::string const& hint ): Opt( T& ref, StringRef hint ):
ParserRefImpl( ref, hint ) {} ParserRefImpl( ref, hint ) {}
auto operator[](std::string const& optName) -> Opt& { Opt& operator[]( StringRef optName ) & {
m_optNames.push_back(optName); m_optNames.push_back(optName);
return *this; return *this;
} }
Opt&& operator[]( StringRef optName ) && {
m_optNames.push_back( optName );
return CATCH_MOVE(*this);
}
std::vector<Detail::HelpColumns> getHelpColumns() const; Detail::HelpColumns getHelpColumns() const;
bool isMatch(std::string const& optToken) const; bool isMatch(StringRef optToken) const;
using ParserBase::parse; using ParserBase::parse;
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const&, parse(std::string const&,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
Detail::Result validate() const override; Detail::Result validate() const override;
}; };
@ -4887,7 +4787,7 @@ namespace Catch {
// handled specially // handled specially
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const&, parse(std::string const&,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
std::string const& name() const { return *m_name; } std::string const& name() const { return *m_name; }
Detail::ParserResult set(std::string const& newName); Detail::ParserResult set(std::string const& newName);
@ -4912,16 +4812,26 @@ namespace Catch {
return *this; return *this;
} }
auto operator|=(Opt const& opt) -> Parser& { friend Parser& operator|=( Parser& p, Opt const& opt ) {
m_options.push_back(opt); p.m_options.push_back( opt );
return *this; return p;
}
friend Parser& operator|=( Parser& p, Opt&& opt ) {
p.m_options.push_back( CATCH_MOVE(opt) );
return p;
} }
Parser& operator|=(Parser const& other); Parser& operator|=(Parser const& other);
template <typename T> template <typename T>
auto operator|(T const& other) const -> Parser { friend Parser operator|( Parser const& p, T&& rhs ) {
return Parser(*this) |= other; return Parser( p ) |= CATCH_FORWARD(rhs);
}
template <typename T>
friend Parser operator|( Parser&& p, T&& rhs ) {
p |= CATCH_FORWARD(rhs);
return CATCH_MOVE(p);
} }
std::vector<Detail::HelpColumns> getHelpColumns() const; std::vector<Detail::HelpColumns> getHelpColumns() const;
@ -4939,21 +4849,23 @@ namespace Catch {
using ParserBase::parse; using ParserBase::parse;
Detail::InternalParseResult Detail::InternalParseResult
parse(std::string const& exeName, parse(std::string const& exeName,
Detail::TokenStream const& tokens) const override; Detail::TokenStream tokens) const override;
}; };
// Transport for raw args (copied from main args, or supplied via /**
// init list for testing) * Wrapper over argc + argv, assumes that the inputs outlive it
*/
class Args { class Args {
friend Detail::TokenStream; friend Detail::TokenStream;
std::string m_exeName; StringRef m_exeName;
std::vector<std::string> m_args; std::vector<StringRef> m_args;
public: public:
Args(int argc, char const* const* argv); Args(int argc, char const* const* argv);
Args(std::initializer_list<std::string> args); // Helper constructor for testing
Args(std::initializer_list<StringRef> args);
std::string const& exeName() const { return m_exeName; } StringRef exeName() const { return m_exeName; }
}; };
@ -6997,6 +6909,7 @@ namespace Catch {
}; };
class ITestInvoker; class ITestInvoker;
struct NameAndTags;
enum class TestCaseProperties : uint8_t { enum class TestCaseProperties : uint8_t {
None = 0, None = 0,
@ -7233,7 +7146,7 @@ namespace Catch {
#define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 5 #define CATCH_VERSION_MINOR 5
#define CATCH_VERSION_PATCH 0 #define CATCH_VERSION_PATCH 1
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED #endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@ -8133,12 +8046,12 @@ class uniform_integer_distribution {
using UnsignedIntegerType = Detail::make_unsigned_t<IntegerType>; using UnsignedIntegerType = Detail::make_unsigned_t<IntegerType>;
// We store the left range bound converted to internal representation, // Only the left bound is stored, and we store it converted to its
// because it will be used in computation in the () operator. // unsigned image. This avoids having to do the conversions inside
// the operator(), at the cost of having to do the conversion in
// the a() getter. The right bound is only needed in the b() getter,
// so we recompute it there from other stored data.
UnsignedIntegerType m_a; UnsignedIntegerType m_a;
// After initialization, right bound is only used for the b() getter,
// so we keep it in the original type.
IntegerType m_b;
// How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type. // How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type.
UnsignedIntegerType m_ab_distance; UnsignedIntegerType m_ab_distance;
@ -8151,11 +8064,10 @@ class uniform_integer_distribution {
// distribution will be reused many times and this is an optimization. // distribution will be reused many times and this is an optimization.
UnsignedIntegerType m_rejection_threshold = 0; UnsignedIntegerType m_rejection_threshold = 0;
// Assumes m_b and m_a are already filled UnsignedIntegerType computeDistance(IntegerType a, IntegerType b) const {
UnsignedIntegerType computeDistance() const { // This overflows and returns 0 if a == 0 and b == TYPE_MAX.
// This overflows and returns 0 if ua == 0 and ub == TYPE_MAX.
// We handle that later when generating the number. // We handle that later when generating the number.
return transposeTo(m_b) - m_a + 1; return transposeTo(b) - transposeTo(a) + 1;
} }
static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) { static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) {
@ -8179,8 +8091,7 @@ public:
uniform_integer_distribution( IntegerType a, IntegerType b ): uniform_integer_distribution( IntegerType a, IntegerType b ):
m_a( transposeTo(a) ), m_a( transposeTo(a) ),
m_b( b ), m_ab_distance( computeDistance(a, b) ),
m_ab_distance( computeDistance() ),
m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) { m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) {
assert( a <= b ); assert( a <= b );
} }
@ -8205,7 +8116,7 @@ public:
} }
result_type a() const { return transposeBack(m_a); } result_type a() const { return transposeBack(m_a); }
result_type b() const { return m_b; } result_type b() const { return transposeBack(m_ab_distance + m_a - 1); }
}; };
} // end namespace Catch } // end namespace Catch
@ -9065,6 +8976,141 @@ namespace Catch {
#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED #endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
#ifndef CATCH_CONSOLE_COLOUR_HPP_INCLUDED
#define CATCH_CONSOLE_COLOUR_HPP_INCLUDED
#include <iosfwd>
#include <cstdint>
namespace Catch {
enum class ColourMode : std::uint8_t;
class IStream;
struct Colour {
enum Code {
None = 0,
White,
Red,
Green,
Blue,
Cyan,
Yellow,
Grey,
Bright = 0x10,
BrightRed = Bright | Red,
BrightGreen = Bright | Green,
LightGrey = Bright | Grey,
BrightWhite = Bright | White,
BrightYellow = Bright | Yellow,
// By intention
FileName = LightGrey,
Warning = BrightYellow,
ResultError = BrightRed,
ResultSuccess = BrightGreen,
ResultExpectedFailure = Warning,
Error = BrightRed,
Success = Green,
Skip = LightGrey,
OriginalExpression = Cyan,
ReconstructedExpression = BrightYellow,
SecondaryText = LightGrey,
Headers = White
};
};
class ColourImpl {
protected:
//! The associated stream of this ColourImpl instance
IStream* m_stream;
public:
ColourImpl( IStream* stream ): m_stream( stream ) {}
//! RAII wrapper around writing specific colour of text using specific
//! colour impl into a stream.
class ColourGuard {
ColourImpl const* m_colourImpl;
Colour::Code m_code;
bool m_engaged = false;
public:
//! Does **not** engage the guard/start the colour
ColourGuard( Colour::Code code,
ColourImpl const* colour );
ColourGuard( ColourGuard const& rhs ) = delete;
ColourGuard& operator=( ColourGuard const& rhs ) = delete;
ColourGuard( ColourGuard&& rhs ) noexcept;
ColourGuard& operator=( ColourGuard&& rhs ) noexcept;
//! Removes colour _if_ the guard was engaged
~ColourGuard();
/**
* Explicitly engages colour for given stream.
*
* The API based on operator<< should be preferred.
*/
ColourGuard& engage( std::ostream& stream ) &;
/**
* Explicitly engages colour for given stream.
*
* The API based on operator<< should be preferred.
*/
ColourGuard&& engage( std::ostream& stream ) &&;
private:
//! Engages the guard and starts using colour
friend std::ostream& operator<<( std::ostream& lhs,
ColourGuard& guard ) {
guard.engageImpl( lhs );
return lhs;
}
//! Engages the guard and starts using colour
friend std::ostream& operator<<( std::ostream& lhs,
ColourGuard&& guard) {
guard.engageImpl( lhs );
return lhs;
}
void engageImpl( std::ostream& stream );
};
virtual ~ColourImpl(); // = default
/**
* Creates a guard object for given colour and this colour impl
*
* **Important:**
* the guard starts disengaged, and has to be engaged explicitly.
*/
ColourGuard guardColour( Colour::Code colourCode );
private:
virtual void use( Colour::Code colourCode ) const = 0;
};
//! Provides ColourImpl based on global config and target compilation platform
Detail::unique_ptr<ColourImpl> makeColourImpl( ColourMode colourSelection,
IStream* stream );
//! Checks if specific colour impl has been compiled into the binary
bool isColourImplAvailable( ColourMode colourSelection );
} // end namespace Catch
#endif // CATCH_CONSOLE_COLOUR_HPP_INCLUDED
#ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED #ifndef CATCH_CONSOLE_WIDTH_HPP_INCLUDED
#define CATCH_CONSOLE_WIDTH_HPP_INCLUDED #define CATCH_CONSOLE_WIDTH_HPP_INCLUDED
@ -9340,7 +9386,6 @@ namespace Catch {
#ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED #ifndef CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
#define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED #define CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
#include <cassert> #include <cassert>
namespace Catch { namespace Catch {
@ -10421,7 +10466,7 @@ namespace Catch {
#ifndef CATCH_SHARDING_HPP_INCLUDED #ifndef CATCH_SHARDING_HPP_INCLUDED
#define CATCH_SHARDING_HPP_INCLUDED #define CATCH_SHARDING_HPP_INCLUDED
#include <cassert>
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
@ -10769,6 +10814,7 @@ namespace Catch {
#ifndef CATCH_TEXTFLOW_HPP_INCLUDED #ifndef CATCH_TEXTFLOW_HPP_INCLUDED
#define CATCH_TEXTFLOW_HPP_INCLUDED #define CATCH_TEXTFLOW_HPP_INCLUDED
#include <cassert> #include <cassert>
#include <string> #include <string>
#include <vector> #include <vector>
@ -10851,20 +10897,35 @@ namespace Catch {
using iterator = const_iterator; using iterator = const_iterator;
explicit Column( std::string const& text ): m_string( text ) {} explicit Column( std::string const& text ): m_string( text ) {}
explicit Column( std::string&& text ):
m_string( CATCH_MOVE(text)) {}
Column& width( size_t newWidth ) { Column& width( size_t newWidth ) & {
assert( newWidth > 0 ); assert( newWidth > 0 );
m_width = newWidth; m_width = newWidth;
return *this; return *this;
} }
Column& indent( size_t newIndent ) { Column&& width( size_t newWidth ) && {
assert( newWidth > 0 );
m_width = newWidth;
return CATCH_MOVE( *this );
}
Column& indent( size_t newIndent ) & {
m_indent = newIndent; m_indent = newIndent;
return *this; return *this;
} }
Column& initialIndent( size_t newIndent ) { Column&& indent( size_t newIndent ) && {
m_indent = newIndent;
return CATCH_MOVE( *this );
}
Column& initialIndent( size_t newIndent ) & {
m_initialIndent = newIndent; m_initialIndent = newIndent;
return *this; return *this;
} }
Column&& initialIndent( size_t newIndent ) && {
m_initialIndent = newIndent;
return CATCH_MOVE( *this );
}
size_t width() const { return m_width; } size_t width() const { return m_width; }
const_iterator begin() const { return const_iterator( *this ); } const_iterator begin() const { return const_iterator( *this ); }
@ -10873,7 +10934,8 @@ namespace Catch {
friend std::ostream& operator<<( std::ostream& os, friend std::ostream& operator<<( std::ostream& os,
Column const& col ); Column const& col );
Columns operator+( Column const& other ); friend Columns operator+( Column const& lhs, Column const& rhs );
friend Columns operator+( Column&& lhs, Column&& rhs );
}; };
//! Creates a column that serves as an empty space of specific width //! Creates a column that serves as an empty space of specific width
@ -10917,8 +10979,10 @@ namespace Catch {
iterator begin() const { return iterator( *this ); } iterator begin() const { return iterator( *this ); }
iterator end() const { return { *this, iterator::EndTag() }; } iterator end() const { return { *this, iterator::EndTag() }; }
Columns& operator+=( Column const& col ); friend Columns& operator+=( Columns& lhs, Column const& rhs );
Columns operator+( Column const& col ); friend Columns& operator+=( Columns& lhs, Column&& rhs );
friend Columns operator+( Columns const& lhs, Column const& rhs );
friend Columns operator+( Columns&& lhs, Column&& rhs );
friend std::ostream& operator<<( std::ostream& os, friend std::ostream& operator<<( std::ostream& os,
Columns const& cols ); Columns const& cols );

View File

@ -8,7 +8,7 @@
project( project(
'catch2', 'catch2',
'cpp', 'cpp',
version: '3.5.0', # CML version placeholder, don't delete version: '3.5.1', # CML version placeholder, don't delete
license: 'BSL-1.0', license: 'BSL-1.0',
meson_version: '>=0.54.1', meson_version: '>=0.54.1',
) )

View File

@ -36,7 +36,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 3, 5, 0, "", 0 ); static Version version( 3, 5, 1, "", 0 );
return version; return version;
} }

View File

@ -10,6 +10,6 @@
#define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 5 #define CATCH_VERSION_MINOR 5
#define CATCH_VERSION_PATCH 0 #define CATCH_VERSION_PATCH 1
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED #endif // CATCH_VERSION_MACROS_HPP_INCLUDED