mirror of
https://github.com/catchorg/Catch2.git
synced 2025-11-13 05:50:35 +01:00
Compare commits
23 Commits
v3.11.0
...
devel-cond
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1968b3114 | ||
|
|
5ed9c45e5f | ||
|
|
950ad70f4c | ||
|
|
d7c67270af | ||
|
|
c748569310 | ||
|
|
cd7e43489e | ||
|
|
f6fd079aa3 | ||
|
|
22d54b36e0 | ||
|
|
a9116c2142 | ||
|
|
2e3214709a | ||
|
|
41ed8b702a | ||
|
|
93ef2b4cb8 | ||
|
|
a1faad9315 | ||
|
|
31ee3beb0a | ||
|
|
3b853aa9fb | ||
|
|
49d79e9e9c | ||
|
|
33e6fd217a | ||
|
|
a58df2d7c5 | ||
|
|
a9223b2bb3 | ||
|
|
363ca5af18 | ||
|
|
cb6d713774 | ||
|
|
8e4ab5dd8f | ||
|
|
8219ed79f2 |
36
.github/workflows/mac-other-builds.yml
vendored
Normal file
36
.github/workflows/mac-other-builds.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: Mac Sanitizer Builds
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CXXFLAGS: -fsanitize=thread,undefined
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
# From macos-14 forward, the baseline "macos-X" image is Arm based,
|
||||||
|
# and not Intel based.
|
||||||
|
runs-on: ${{matrix.image}}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
image: [macos-13, macos-14]
|
||||||
|
build_type: [Debug, Release]
|
||||||
|
std: [14, 17]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
|
||||||
|
run: |
|
||||||
|
cmake --preset all-tests -GNinja \
|
||||||
|
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||||
|
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
|
||||||
|
-DCATCH_BUILD_EXTRA_TESTS=ON \
|
||||||
|
-DCATCH_ENABLE_WERROR=OFF
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cmake --build build
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: ctest --test-dir build -R ThreadSafetyTests --timeout 21600 --verbose
|
||||||
@@ -10,7 +10,7 @@ in-memory logs if they are not needed (the test case passed).
|
|||||||
Unlike reporters, each registered event listener is always active. Event
|
Unlike reporters, each registered event listener is always active. Event
|
||||||
listeners are always notified before reporter(s).
|
listeners are always notified before reporter(s).
|
||||||
|
|
||||||
To write your own event listener, you should derive from `Catch::TestEventListenerBase`,
|
To write your own event listener, you should derive from `Catch::EventListenerBase`,
|
||||||
as it provides empty stubs for all reporter events, allowing you to
|
as it provides empty stubs for all reporter events, allowing you to
|
||||||
only override events you care for. Afterwards you have to register it
|
only override events you care for. Afterwards you have to register it
|
||||||
with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows
|
with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ There are two ways to handle this, depending on whether you want this
|
|||||||
to be an error or not.
|
to be an error or not.
|
||||||
|
|
||||||
* If empty generator **is** an error, throw an exception in constructor.
|
* If empty generator **is** an error, throw an exception in constructor.
|
||||||
* If empty generator **is not** an error, use the [`SKIP`](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
|
* If empty generator **is not** an error, use the [`SKIP` macro](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ TEST_CASE("complex test case") {
|
|||||||
```
|
```
|
||||||
|
|
||||||
This test case will report 5 passing assertions; one for each of the three
|
This test case will report 5 passing assertions; one for each of the three
|
||||||
values in section `a1`, and then two in section `a2`, from values 2 and 4.
|
values in section `a1`, and then two in section `a2`, from values 2 and 6.
|
||||||
|
|
||||||
Note that as soon as one section is skipped, the entire test case will
|
Note that as soon as one section is skipped, the entire test case will
|
||||||
be reported as _skipped_ (unless there is a failing assertion, in which
|
be reported as _skipped_ (unless there is a failing assertion, in which
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ set(IMPL_HEADERS
|
|||||||
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
|
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
|
||||||
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
|
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
|
||||||
${SOURCES_DIR}/internal/catch_leak_detector.hpp
|
${SOURCES_DIR}/internal/catch_leak_detector.hpp
|
||||||
|
${SOURCES_DIR}/internal/catch_lifetimebound.hpp
|
||||||
${SOURCES_DIR}/internal/catch_list.hpp
|
${SOURCES_DIR}/internal/catch_list.hpp
|
||||||
${SOURCES_DIR}/internal/catch_logical_traits.hpp
|
${SOURCES_DIR}/internal/catch_logical_traits.hpp
|
||||||
${SOURCES_DIR}/internal/catch_message_info.hpp
|
${SOURCES_DIR}/internal/catch_message_info.hpp
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
#include <catch2/internal/catch_jsonwriter.hpp>
|
#include <catch2/internal/catch_jsonwriter.hpp>
|
||||||
#include <catch2/internal/catch_lazy_expr.hpp>
|
#include <catch2/internal/catch_lazy_expr.hpp>
|
||||||
#include <catch2/internal/catch_leak_detector.hpp>
|
#include <catch2/internal/catch_leak_detector.hpp>
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_list.hpp>
|
#include <catch2/internal/catch_list.hpp>
|
||||||
#include <catch2/internal/catch_logical_traits.hpp>
|
#include <catch2/internal/catch_logical_traits.hpp>
|
||||||
#include <catch2/internal/catch_message_info.hpp>
|
#include <catch2/internal/catch_message_info.hpp>
|
||||||
|
|||||||
@@ -57,6 +57,36 @@ namespace Detail {
|
|||||||
}
|
}
|
||||||
} // end unnamed namespace
|
} // end unnamed namespace
|
||||||
|
|
||||||
|
std::size_t catch_strnlen( const char* str, std::size_t n ) {
|
||||||
|
auto ret = std::char_traits<char>::find( str, n, '\0' );
|
||||||
|
if ( ret != nullptr ) { return static_cast<std::size_t>( ret - str ); }
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string formatTimeT(std::time_t time) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
std::tm timeInfo = {};
|
||||||
|
const auto err = gmtime_s( &timeInfo, &time );
|
||||||
|
if ( err ) {
|
||||||
|
return "gmtime from provided timepoint has failed. This "
|
||||||
|
"happens e.g. with pre-1970 dates using Microsoft libc";
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::tm* timeInfo = std::gmtime( &time );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" );
|
||||||
|
char timeStamp[timeStampSize];
|
||||||
|
const char* const fmt = "%Y-%m-%dT%H:%M:%SZ";
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
std::strftime( timeStamp, timeStampSize, fmt, &timeInfo );
|
||||||
|
#else
|
||||||
|
std::strftime( timeStamp, timeStampSize, fmt, timeInfo );
|
||||||
|
#endif
|
||||||
|
return std::string( timeStamp, timeStampSize - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
std::string convertIntoString(StringRef string, bool escapeInvisibles) {
|
std::string convertIntoString(StringRef string, bool escapeInvisibles) {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
// This is enough for the "don't escape invisibles" case, and a good
|
// This is enough for the "don't escape invisibles" case, and a good
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#ifndef CATCH_TOSTRING_HPP_INCLUDED
|
#ifndef CATCH_TOSTRING_HPP_INCLUDED
|
||||||
#define CATCH_TOSTRING_HPP_INCLUDED
|
#define CATCH_TOSTRING_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@@ -40,13 +40,9 @@ namespace Catch {
|
|||||||
|
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
|
|
||||||
inline std::size_t catch_strnlen(const char *str, std::size_t n) {
|
std::size_t catch_strnlen(const char *str, std::size_t n);
|
||||||
auto ret = std::char_traits<char>::find(str, n, '\0');
|
|
||||||
if (ret != nullptr) {
|
std::string formatTimeT( std::time_t time );
|
||||||
return static_cast<std::size_t>(ret - str);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr StringRef unprintableString = "{?}"_sr;
|
constexpr StringRef unprintableString = "{?}"_sr;
|
||||||
|
|
||||||
@@ -412,43 +408,37 @@ namespace Catch {
|
|||||||
// Separate std::tuple specialization
|
// Separate std::tuple specialization
|
||||||
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
|
||||||
# include <tuple>
|
# include <tuple>
|
||||||
|
# include <utility>
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace Detail {
|
namespace Detail {
|
||||||
template<
|
template <typename Tuple, std::size_t... Is>
|
||||||
typename Tuple,
|
void PrintTuple( const Tuple& tuple,
|
||||||
std::size_t N = 0,
|
std::ostream& os,
|
||||||
bool = (N < std::tuple_size<Tuple>::value)
|
std::index_sequence<Is...> ) {
|
||||||
>
|
// 1 + Account for when the tuple is empty
|
||||||
struct TupleElementPrinter {
|
char a[1 + sizeof...( Is )] = {
|
||||||
static void print(const Tuple& tuple, std::ostream& os) {
|
( ( os << ( Is ? ", " : " " )
|
||||||
os << (N ? ", " : " ")
|
<< ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
|
||||||
<< ::Catch::Detail::stringify(std::get<N>(tuple));
|
'\0' )... };
|
||||||
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
|
(void)a;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<
|
|
||||||
typename Tuple,
|
|
||||||
std::size_t N
|
|
||||||
>
|
|
||||||
struct TupleElementPrinter<Tuple, N, false> {
|
|
||||||
static void print(const Tuple&, std::ostream&) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Detail
|
||||||
|
|
||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
struct StringMaker<std::tuple<Types...>> {
|
struct StringMaker<std::tuple<Types...>> {
|
||||||
static std::string convert( const std::tuple<Types...>& tuple ) {
|
static std::string convert( const std::tuple<Types...>& tuple ) {
|
||||||
ReusableStringStream rss;
|
ReusableStringStream rss;
|
||||||
rss << '{';
|
rss << '{';
|
||||||
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
|
Detail::PrintTuple(
|
||||||
|
tuple,
|
||||||
|
rss.get(),
|
||||||
|
std::make_index_sequence<sizeof...( Types )>{} );
|
||||||
rss << " }";
|
rss << " }";
|
||||||
return rss.str();
|
return rss.str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
} // namespace Catch
|
||||||
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
|
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
|
||||||
@@ -635,28 +625,7 @@ struct ratio_string<std::milli> {
|
|||||||
const auto systemish = std::chrono::time_point_cast<
|
const auto systemish = std::chrono::time_point_cast<
|
||||||
std::chrono::system_clock::duration>( time_point );
|
std::chrono::system_clock::duration>( time_point );
|
||||||
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
|
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
|
||||||
|
return ::Catch::Detail::formatTimeT( as_time_t );
|
||||||
#ifdef _MSC_VER
|
|
||||||
std::tm timeInfo = {};
|
|
||||||
const auto err = gmtime_s( &timeInfo, &as_time_t );
|
|
||||||
if ( err ) {
|
|
||||||
return "gmtime from provided timepoint has failed. This "
|
|
||||||
"happens e.g. with pre-1970 dates using Microsoft libc";
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
std::tm* timeInfo = std::gmtime( &as_time_t );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
|
|
||||||
char timeStamp[timeStampSize];
|
|
||||||
const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
|
|
||||||
#else
|
|
||||||
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
|
|
||||||
#endif
|
|
||||||
return std::string(timeStamp, timeStampSize - 1);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ namespace Catch {
|
|||||||
( "list all listeners" )
|
( "list all listeners" )
|
||||||
| Opt( setTestOrder, "decl|lex|rand" )
|
| Opt( setTestOrder, "decl|lex|rand" )
|
||||||
["--order"]
|
["--order"]
|
||||||
( "test case order (defaults to decl)" )
|
( "test case order (defaults to rand)" )
|
||||||
| Opt( setRngSeed, "'time'|'random-device'|number" )
|
| Opt( setRngSeed, "'time'|'random-device'|number" )
|
||||||
["--rng-seed"]
|
["--rng-seed"]
|
||||||
( "set a specific seed for random numbers" )
|
( "set a specific seed for random numbers" )
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef CATCH_JSONWRITER_HPP_INCLUDED
|
#ifndef CATCH_JSONWRITER_HPP_INCLUDED
|
||||||
#define CATCH_JSONWRITER_HPP_INCLUDED
|
#define CATCH_JSONWRITER_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
|
|
||||||
@@ -27,8 +28,8 @@ namespace Catch {
|
|||||||
|
|
||||||
class JsonValueWriter {
|
class JsonValueWriter {
|
||||||
public:
|
public:
|
||||||
JsonValueWriter( std::ostream& os );
|
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||||
JsonValueWriter( std::ostream& os, std::uint64_t indent_level );
|
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||||
|
|
||||||
JsonObjectWriter writeObject() &&;
|
JsonObjectWriter writeObject() &&;
|
||||||
JsonArrayWriter writeArray() &&;
|
JsonArrayWriter writeArray() &&;
|
||||||
@@ -62,8 +63,8 @@ namespace Catch {
|
|||||||
|
|
||||||
class JsonObjectWriter {
|
class JsonObjectWriter {
|
||||||
public:
|
public:
|
||||||
JsonObjectWriter( std::ostream& os );
|
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||||
JsonObjectWriter( std::ostream& os, std::uint64_t indent_level );
|
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||||
|
|
||||||
JsonObjectWriter( JsonObjectWriter&& source ) noexcept;
|
JsonObjectWriter( JsonObjectWriter&& source ) noexcept;
|
||||||
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
|
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
|
||||||
@@ -81,8 +82,8 @@ namespace Catch {
|
|||||||
|
|
||||||
class JsonArrayWriter {
|
class JsonArrayWriter {
|
||||||
public:
|
public:
|
||||||
JsonArrayWriter( std::ostream& os );
|
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||||
JsonArrayWriter( std::ostream& os, std::uint64_t indent_level );
|
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
|
||||||
|
|
||||||
JsonArrayWriter( JsonArrayWriter&& source ) noexcept;
|
JsonArrayWriter( JsonArrayWriter&& source ) noexcept;
|
||||||
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
|
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
|
||||||
|
|||||||
24
src/catch2/internal/catch_lifetimebound.hpp
Normal file
24
src/catch2/internal/catch_lifetimebound.hpp
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
// Copyright Catch2 Authors
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE.txt or copy at
|
||||||
|
// https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
|
#ifndef CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||||
|
#define CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||||
|
|
||||||
|
#if !defined( __has_cpp_attribute )
|
||||||
|
# define CATCH_ATTR_LIFETIMEBOUND
|
||||||
|
#elif __has_cpp_attribute( msvc::lifetimebound )
|
||||||
|
# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]]
|
||||||
|
#elif __has_cpp_attribute( clang::lifetimebound )
|
||||||
|
# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
|
||||||
|
#elif __has_cpp_attribute( lifetimebound )
|
||||||
|
# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]]
|
||||||
|
#else
|
||||||
|
# define CATCH_ATTR_LIFETIMEBOUND
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED
|
||||||
@@ -129,12 +129,8 @@ namespace Catch {
|
|||||||
|
|
||||||
for ( auto const& child : m_children ) {
|
for ( auto const& child : m_children ) {
|
||||||
if ( child->isSectionTracker() &&
|
if ( child->isSectionTracker() &&
|
||||||
std::find( filters.begin(),
|
static_cast<SectionTracker const&>( *child )
|
||||||
filters.end(),
|
.trimmedName() == filters[0] ) {
|
||||||
static_cast<SectionTracker const&>(
|
|
||||||
*child )
|
|
||||||
.trimmedName() ) !=
|
|
||||||
filters.end() ) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
|
#ifndef CATCH_STRING_MANIP_HPP_INCLUDED
|
||||||
#define CATCH_STRING_MANIP_HPP_INCLUDED
|
#define CATCH_STRING_MANIP_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -28,10 +29,10 @@ namespace Catch {
|
|||||||
//! Returns a new string without whitespace at the start/end
|
//! Returns a new string without whitespace at the start/end
|
||||||
std::string trim( std::string const& str );
|
std::string trim( std::string const& str );
|
||||||
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
|
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
|
||||||
StringRef trim(StringRef ref);
|
StringRef trim( StringRef ref CATCH_ATTR_LIFETIMEBOUND );
|
||||||
|
|
||||||
// !!! Be aware, returns refs into original string - make sure original string outlives them
|
// !!! Be aware, returns refs into original string - make sure original string outlives them
|
||||||
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
|
std::vector<StringRef> splitStringRef( StringRef str CATCH_ATTR_LIFETIMEBOUND, char delimiter );
|
||||||
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
|
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +50,7 @@ namespace Catch {
|
|||||||
StringRef m_label;
|
StringRef m_label;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr pluralise(std::uint64_t count, StringRef label):
|
constexpr pluralise(std::uint64_t count, StringRef label CATCH_ATTR_LIFETIMEBOUND):
|
||||||
m_count(count),
|
m_count(count),
|
||||||
m_label(label)
|
m_label(label)
|
||||||
{}
|
{}
|
||||||
|
|||||||
@@ -8,11 +8,12 @@
|
|||||||
#ifndef CATCH_STRINGREF_HPP_INCLUDED
|
#ifndef CATCH_STRINGREF_HPP_INCLUDED
|
||||||
#define CATCH_STRINGREF_HPP_INCLUDED
|
#define CATCH_STRINGREF_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
@@ -36,14 +37,16 @@ namespace Catch {
|
|||||||
public: // construction
|
public: // construction
|
||||||
constexpr StringRef() noexcept = default;
|
constexpr StringRef() noexcept = default;
|
||||||
|
|
||||||
StringRef( char const* rawChars ) noexcept;
|
StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND ) noexcept;
|
||||||
|
|
||||||
constexpr StringRef( char const* rawChars, size_type size ) noexcept
|
constexpr StringRef( char const* rawChars CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
size_type size ) noexcept
|
||||||
: m_start( rawChars ),
|
: m_start( rawChars ),
|
||||||
m_size( size )
|
m_size( size )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
StringRef( std::string const& stdString ) noexcept
|
StringRef(
|
||||||
|
std::string const& stdString CATCH_ATTR_LIFETIMEBOUND ) noexcept
|
||||||
: m_start( stdString.c_str() ),
|
: m_start( stdString.c_str() ),
|
||||||
m_size( stdString.size() )
|
m_size( stdString.size() )
|
||||||
{}
|
{}
|
||||||
@@ -89,7 +92,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the current start pointer. May not be null-terminated.
|
// Returns the current start pointer. May not be null-terminated.
|
||||||
constexpr char const* data() const noexcept {
|
constexpr char const* data() const noexcept CATCH_ATTR_LIFETIMEBOUND {
|
||||||
return m_start;
|
return m_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ namespace TestCaseTracking {
|
|||||||
|
|
||||||
if ( m_filters.empty()
|
if ( m_filters.empty()
|
||||||
|| m_filters[0].empty()
|
|| m_filters[0].empty()
|
||||||
|| std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
|
|| m_filters[0] == m_trimmed_name ) {
|
||||||
complete = TrackerBase::isComplete();
|
complete = TrackerBase::isComplete();
|
||||||
}
|
}
|
||||||
return complete;
|
return complete;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
#ifndef CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
||||||
#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
#define CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_source_line_info.hpp>
|
#include <catch2/internal/catch_source_line_info.hpp>
|
||||||
#include <catch2/internal/catch_unique_ptr.hpp>
|
#include <catch2/internal/catch_unique_ptr.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
@@ -48,7 +49,7 @@ namespace TestCaseTracking {
|
|||||||
StringRef name;
|
StringRef name;
|
||||||
SourceLineInfo location;
|
SourceLineInfo location;
|
||||||
|
|
||||||
constexpr NameAndLocationRef( StringRef name_,
|
constexpr NameAndLocationRef( StringRef name_ CATCH_ATTR_LIFETIMEBOUND,
|
||||||
SourceLineInfo location_ ):
|
SourceLineInfo location_ ):
|
||||||
name( name_ ), location( location_ ) {}
|
name( name_ ), location( location_ ) {}
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ namespace Catch {
|
|||||||
using Mutex = std::mutex;
|
using Mutex = std::mutex;
|
||||||
using LockGuard = std::lock_guard<std::mutex>;
|
using LockGuard = std::lock_guard<std::mutex>;
|
||||||
struct AtomicCounts {
|
struct AtomicCounts {
|
||||||
std::atomic<std::uint64_t> passed = 0;
|
std::atomic<std::uint64_t> passed{ 0 };
|
||||||
std::atomic<std::uint64_t> failed = 0;
|
std::atomic<std::uint64_t> failed{ 0 };
|
||||||
std::atomic<std::uint64_t> failedButOk = 0;
|
std::atomic<std::uint64_t> failedButOk{ 0 };
|
||||||
std::atomic<std::uint64_t> skipped = 0;
|
std::atomic<std::uint64_t> skipped{ 0 };
|
||||||
};
|
};
|
||||||
#else // ^^ Use actual mutex, lock and atomics
|
#else // ^^ Use actual mutex, lock and atomics
|
||||||
// vv Dummy implementations for single-thread performance
|
// vv Dummy implementations for single-thread performance
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#ifndef CATCH_XMLWRITER_HPP_INCLUDED
|
#ifndef CATCH_XMLWRITER_HPP_INCLUDED
|
||||||
#define CATCH_XMLWRITER_HPP_INCLUDED
|
#define CATCH_XMLWRITER_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ namespace Catch {
|
|||||||
public:
|
public:
|
||||||
enum ForWhat { ForTextNodes, ForAttributes };
|
enum ForWhat { ForTextNodes, ForAttributes };
|
||||||
|
|
||||||
constexpr XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes ):
|
constexpr XmlEncode( StringRef str CATCH_ATTR_LIFETIMEBOUND, ForWhat forWhat = ForTextNodes ):
|
||||||
m_str( str ), m_forWhat( forWhat ) {}
|
m_str( str ), m_forWhat( forWhat ) {}
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ namespace Catch {
|
|||||||
|
|
||||||
class ScopedElement {
|
class ScopedElement {
|
||||||
public:
|
public:
|
||||||
ScopedElement( XmlWriter* writer, XmlFormatting fmt );
|
ScopedElement( XmlWriter* writer CATCH_ATTR_LIFETIMEBOUND, XmlFormatting fmt );
|
||||||
|
|
||||||
ScopedElement( ScopedElement&& other ) noexcept;
|
ScopedElement( ScopedElement&& other ) noexcept;
|
||||||
ScopedElement& operator=( ScopedElement&& other ) noexcept;
|
ScopedElement& operator=( ScopedElement&& other ) noexcept;
|
||||||
@@ -93,7 +94,7 @@ namespace Catch {
|
|||||||
XmlFormatting m_fmt;
|
XmlFormatting m_fmt;
|
||||||
};
|
};
|
||||||
|
|
||||||
XmlWriter( std::ostream& os );
|
XmlWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
|
||||||
~XmlWriter();
|
~XmlWriter();
|
||||||
|
|
||||||
XmlWriter( XmlWriter const& ) = delete;
|
XmlWriter( XmlWriter const& ) = delete;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <catch2/matchers/internal/catch_matchers_impl.hpp>
|
#include <catch2/matchers/internal/catch_matchers_impl.hpp>
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -79,11 +80,15 @@ namespace Matchers {
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
friend MatchAllOf operator&&( MatchAllOf&& lhs,
|
||||||
|
MatcherBase<ArgT> const& rhs
|
||||||
|
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
lhs.m_matchers.push_back(&rhs);
|
lhs.m_matchers.push_back(&rhs);
|
||||||
return CATCH_MOVE(lhs);
|
return CATCH_MOVE(lhs);
|
||||||
}
|
}
|
||||||
friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
|
friend MatchAllOf
|
||||||
|
operator&&( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatchAllOf&& rhs ) {
|
||||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||||
return CATCH_MOVE(rhs);
|
return CATCH_MOVE(rhs);
|
||||||
}
|
}
|
||||||
@@ -131,11 +136,15 @@ namespace Matchers {
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
|
friend MatchAnyOf operator||( MatchAnyOf&& lhs,
|
||||||
|
MatcherBase<ArgT> const& rhs
|
||||||
|
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
lhs.m_matchers.push_back(&rhs);
|
lhs.m_matchers.push_back(&rhs);
|
||||||
return CATCH_MOVE(lhs);
|
return CATCH_MOVE(lhs);
|
||||||
}
|
}
|
||||||
friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
|
friend MatchAnyOf
|
||||||
|
operator||( MatcherBase<ArgT> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatchAnyOf&& rhs ) {
|
||||||
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
|
||||||
return CATCH_MOVE(rhs);
|
return CATCH_MOVE(rhs);
|
||||||
}
|
}
|
||||||
@@ -155,7 +164,8 @@ namespace Matchers {
|
|||||||
MatcherBase<ArgT> const& m_underlyingMatcher;
|
MatcherBase<ArgT> const& m_underlyingMatcher;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ):
|
explicit MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher
|
||||||
|
CATCH_ATTR_LIFETIMEBOUND ):
|
||||||
m_underlyingMatcher( underlyingMatcher )
|
m_underlyingMatcher( underlyingMatcher )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -171,16 +181,22 @@ namespace Matchers {
|
|||||||
} // namespace Detail
|
} // namespace Detail
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Detail::MatchAllOf<T> operator&& (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
Detail::MatchAllOf<T>
|
||||||
|
operator&&( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return Detail::MatchAllOf<T>{} && lhs && rhs;
|
return Detail::MatchAllOf<T>{} && lhs && rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Detail::MatchAnyOf<T> operator|| (MatcherBase<T> const& lhs, MatcherBase<T> const& rhs) {
|
Detail::MatchAnyOf<T>
|
||||||
|
operator||( MatcherBase<T> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherBase<T> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return Detail::MatchAnyOf<T>{} || lhs || rhs;
|
return Detail::MatchAnyOf<T>{} || lhs || rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Detail::MatchNotOf<T> operator! (MatcherBase<T> const& matcher) {
|
Detail::MatchNotOf<T>
|
||||||
|
operator!( MatcherBase<T> const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return Detail::MatchNotOf<T>{ matcher };
|
return Detail::MatchNotOf<T>{ matcher };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <catch2/matchers/catch_matchers.hpp>
|
#include <catch2/matchers/catch_matchers.hpp>
|
||||||
#include <catch2/internal/catch_stringref.hpp>
|
#include <catch2/internal/catch_stringref.hpp>
|
||||||
#include <catch2/internal/catch_move_and_forward.hpp>
|
#include <catch2/internal/catch_move_and_forward.hpp>
|
||||||
|
#include <catch2/internal/catch_lifetimebound.hpp>
|
||||||
#include <catch2/internal/catch_logical_traits.hpp>
|
#include <catch2/internal/catch_logical_traits.hpp>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@@ -114,7 +115,8 @@ namespace Matchers {
|
|||||||
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
MatchAllOfGeneric(MatchAllOfGeneric&&) = default;
|
||||||
MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
|
MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default;
|
||||||
|
|
||||||
MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
|
||||||
|
: m_matchers{ {std::addressof(matchers)...} } {}
|
||||||
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||||
|
|
||||||
template<typename Arg>
|
template<typename Arg>
|
||||||
@@ -136,8 +138,8 @@ namespace Matchers {
|
|||||||
template<typename... MatchersRHS>
|
template<typename... MatchersRHS>
|
||||||
friend
|
friend
|
||||||
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
|
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
|
||||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
|
MatchAllOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,8 +147,8 @@ namespace Matchers {
|
|||||||
template<typename MatcherRHS>
|
template<typename MatcherRHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||||
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
|
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
|
||||||
MatchAllOfGeneric<MatcherTs...>&& lhs,
|
MatchAllOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatcherRHS const& rhs) {
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
|
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,8 +156,8 @@ namespace Matchers {
|
|||||||
template<typename MatcherLHS>
|
template<typename MatcherLHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||||
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
|
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
|
||||||
MatcherLHS const& lhs,
|
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAllOfGeneric<MatcherTs...>&& rhs) {
|
MatchAllOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -169,7 +171,8 @@ namespace Matchers {
|
|||||||
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
|
MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default;
|
||||||
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
|
MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default;
|
||||||
|
|
||||||
MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{ {std::addressof(matchers)...} } {}
|
MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND)
|
||||||
|
: m_matchers{ {std::addressof(matchers)...} } {}
|
||||||
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {}
|
||||||
|
|
||||||
template<typename Arg>
|
template<typename Arg>
|
||||||
@@ -190,8 +193,8 @@ namespace Matchers {
|
|||||||
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
|
||||||
template<typename... MatchersRHS>
|
template<typename... MatchersRHS>
|
||||||
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
|
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
|
||||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
|
MatchAnyOfGeneric<MatchersRHS...>&& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,8 +202,8 @@ namespace Matchers {
|
|||||||
template<typename MatcherRHS>
|
template<typename MatcherRHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherRHS>,
|
||||||
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
|
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
|
||||||
MatchAnyOfGeneric<MatcherTs...>&& lhs,
|
MatchAnyOfGeneric<MatcherTs...>&& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatcherRHS const& rhs) {
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
|
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,8 +211,8 @@ namespace Matchers {
|
|||||||
template<typename MatcherLHS>
|
template<typename MatcherLHS>
|
||||||
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
friend std::enable_if_t<is_matcher_v<MatcherLHS>,
|
||||||
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
|
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
|
||||||
MatcherLHS const& lhs,
|
MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
|
MatchAnyOfGeneric<MatcherTs...>&& rhs CATCH_ATTR_LIFETIMEBOUND) {
|
||||||
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -225,7 +228,8 @@ namespace Matchers {
|
|||||||
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
|
MatchNotOfGeneric(MatchNotOfGeneric&&) = default;
|
||||||
MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
|
MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default;
|
||||||
|
|
||||||
explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {}
|
explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND)
|
||||||
|
: m_matcher{matcher} {}
|
||||||
|
|
||||||
template<typename Arg>
|
template<typename Arg>
|
||||||
bool match(Arg&& arg) const {
|
bool match(Arg&& arg) const {
|
||||||
@@ -237,7 +241,9 @@ namespace Matchers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//! Negating negation can just unwrap and return underlying matcher
|
//! Negating negation can just unwrap and return underlying matcher
|
||||||
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
|
friend MatcherT const&
|
||||||
|
operator!( MatchNotOfGeneric<MatcherT> const& matcher
|
||||||
|
CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return matcher.m_matcher;
|
return matcher.m_matcher;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -247,20 +253,22 @@ namespace Matchers {
|
|||||||
// compose only generic matchers
|
// compose only generic matchers
|
||||||
template<typename MatcherLHS, typename MatcherRHS>
|
template<typename MatcherLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
|
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherRHS>>
|
||||||
operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return { lhs, rhs };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MatcherLHS, typename MatcherRHS>
|
template<typename MatcherLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
|
std::enable_if_t<Detail::are_generic_matchers_v<MatcherLHS, MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>
|
||||||
operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) {
|
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return { lhs, rhs };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Wrap provided generic matcher in generic negator
|
//! Wrap provided generic matcher in generic negator
|
||||||
template<typename MatcherT>
|
template<typename MatcherT>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
|
std::enable_if_t<Detail::is_generic_matcher_v<MatcherT>, Detail::MatchNotOfGeneric<MatcherT>>
|
||||||
operator ! (MatcherT const& matcher) {
|
operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
|
return Detail::MatchNotOfGeneric<MatcherT>{matcher};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,25 +276,29 @@ namespace Matchers {
|
|||||||
// compose mixed generic and non-generic matchers
|
// compose mixed generic and non-generic matchers
|
||||||
template<typename MatcherLHS, typename ArgRHS>
|
template<typename MatcherLHS, typename ArgRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
||||||
operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return { lhs, rhs };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ArgLHS, typename MatcherRHS>
|
template<typename ArgLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
||||||
operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
operator&&( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return { lhs, rhs };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename MatcherLHS, typename ArgRHS>
|
template<typename MatcherLHS, typename ArgRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
std::enable_if_t<Detail::is_generic_matcher_v<MatcherLHS>, Detail::MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>
|
||||||
operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) {
|
operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherBase<ArgRHS> const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return { lhs, rhs };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ArgLHS, typename MatcherRHS>
|
template<typename ArgLHS, typename MatcherRHS>
|
||||||
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
std::enable_if_t<Detail::is_generic_matcher_v<MatcherRHS>, Detail::MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>
|
||||||
operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) {
|
operator||( MatcherBase<ArgLHS> const& lhs CATCH_ATTR_LIFETIMEBOUND,
|
||||||
|
MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) {
|
||||||
return { lhs, rhs };
|
return { lhs, rhs };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ internal_headers = [
|
|||||||
'internal/catch_jsonwriter.hpp',
|
'internal/catch_jsonwriter.hpp',
|
||||||
'internal/catch_lazy_expr.hpp',
|
'internal/catch_lazy_expr.hpp',
|
||||||
'internal/catch_leak_detector.hpp',
|
'internal/catch_leak_detector.hpp',
|
||||||
|
'internal/catch_lifetimebound.hpp',
|
||||||
'internal/catch_list.hpp',
|
'internal/catch_list.hpp',
|
||||||
'internal/catch_logical_traits.hpp',
|
'internal/catch_logical_traits.hpp',
|
||||||
'internal/catch_message_info.hpp',
|
'internal/catch_message_info.hpp',
|
||||||
|
|||||||
@@ -310,38 +310,23 @@ set_tests_properties(UnmatchedOutputFilter
|
|||||||
PASS_REGULAR_EXPRESSION "No test cases matched '\\[this-tag-does-not-exist\\]'"
|
PASS_REGULAR_EXPRESSION "No test cases matched '\\[this-tag-does-not-exist\\]'"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_test(NAME FilteredSection-1 COMMAND $<TARGET_FILE:SelfTest> \#1394 -c RunSection)
|
add_test(NAME FilteredSections::SimpleExample::1 COMMAND $<TARGET_FILE:SelfTest> \#1394 -c RunSection)
|
||||||
set_tests_properties(FilteredSection-1 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
set_tests_properties(FilteredSections::SimpleExample::1 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
||||||
add_test(NAME FilteredSection-2 COMMAND $<TARGET_FILE:SelfTest> \#1394\ nested -c NestedRunSection -c s1)
|
add_test(NAME FilteredSections::SimpleExample::2 COMMAND $<TARGET_FILE:SelfTest> \#1394\ nested -c NestedRunSection -c s1)
|
||||||
set_tests_properties(FilteredSection-2 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
set_tests_properties(FilteredSections::SimpleExample::2 PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran")
|
||||||
|
|
||||||
add_test(
|
add_test(
|
||||||
NAME
|
NAME
|
||||||
FilteredSection::GeneratorsDontCauseInfiniteLoop-1
|
FilteredSections::GeneratorsDontCauseInfiniteLoop
|
||||||
COMMAND
|
COMMAND
|
||||||
$<TARGET_FILE:SelfTest> "#2025: original repro" -c "fov_0"
|
$<TARGET_FILE:SelfTest> "#2025: original repro" -c "fov_0"
|
||||||
)
|
)
|
||||||
set_tests_properties(FilteredSection::GeneratorsDontCauseInfiniteLoop-1
|
set_tests_properties(FilteredSections::GeneratorsDontCauseInfiniteLoop
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
PASS_REGULAR_EXPRESSION "inside with fov: 0" # This should happen
|
PASS_REGULAR_EXPRESSION "inside with fov: 0" # This should happen
|
||||||
FAIL_REGULAR_EXPRESSION "inside with fov: 1" # This would mean there was no filtering
|
FAIL_REGULAR_EXPRESSION "inside with fov: 1" # This would mean there was no filtering
|
||||||
)
|
)
|
||||||
|
|
||||||
# GENERATE between filtered sections (both are selected)
|
|
||||||
add_test(
|
|
||||||
NAME
|
|
||||||
FilteredSection::GeneratorsDontCauseInfiniteLoop-2
|
|
||||||
COMMAND
|
|
||||||
$<TARGET_FILE:SelfTest> "#2025: same-level sections"
|
|
||||||
-c "A"
|
|
||||||
-c "B"
|
|
||||||
--colour-mode none
|
|
||||||
)
|
|
||||||
set_tests_properties(FilteredSection::GeneratorsDontCauseInfiniteLoop-2
|
|
||||||
PROPERTIES
|
|
||||||
PASS_REGULAR_EXPRESSION "All tests passed \\(4 assertions in 1 test case\\)"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_test(NAME ApprovalTests
|
add_test(NAME ApprovalTests
|
||||||
COMMAND
|
COMMAND
|
||||||
Python3::Interpreter
|
Python3::Interpreter
|
||||||
@@ -694,6 +679,14 @@ set_tests_properties("Bazel::RngSeedEnvVar::MalformedValueIsIgnored"
|
|||||||
PASS_REGULAR_EXPRESSION "Randomness seeded to: 17171717"
|
PASS_REGULAR_EXPRESSION "Randomness seeded to: 17171717"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_test(NAME "FilteredSections::DifferentSimpleFilters"
|
||||||
|
COMMAND
|
||||||
|
Python3::Interpreter "${CMAKE_CURRENT_LIST_DIR}/TestScripts/testSectionFiltering.py" $<TARGET_FILE:SelfTest>
|
||||||
|
)
|
||||||
|
set_tests_properties("FilteredSections::DifferentSimpleFilters"
|
||||||
|
PROPERTIES
|
||||||
|
LABELS "uses-python"
|
||||||
|
)
|
||||||
|
|
||||||
list(APPEND CATCH_TEST_TARGETS SelfTest)
|
list(APPEND CATCH_TEST_TARGETS SelfTest)
|
||||||
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
|
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
|
||||||
|
|||||||
@@ -553,3 +553,17 @@ set_tests_properties(AmalgamatedFileTest
|
|||||||
PROPERTIES
|
PROPERTIES
|
||||||
PASS_REGULAR_EXPRESSION "All tests passed \\(14 assertions in 3 test cases\\)"
|
PASS_REGULAR_EXPRESSION "All tests passed \\(14 assertions in 3 test cases\\)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_executable(ThreadSafetyTests
|
||||||
|
${TESTS_DIR}/X94-ThreadSafetyTests.cpp
|
||||||
|
)
|
||||||
|
target_link_libraries(ThreadSafetyTests Catch2_buildall_interface)
|
||||||
|
target_compile_definitions(ThreadSafetyTests PUBLIC CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS)
|
||||||
|
add_test(NAME ThreadSafetyTests
|
||||||
|
COMMAND ThreadSafetyTests -r compact
|
||||||
|
)
|
||||||
|
set_tests_properties(ThreadSafetyTests
|
||||||
|
PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "assertions: 801 | 400 passed | 401 failed"
|
||||||
|
RUN_SERIAL ON
|
||||||
|
)
|
||||||
|
|||||||
44
tests/ExtraTests/X94-ThreadSafetyTests.cpp
Normal file
44
tests/ExtraTests/X94-ThreadSafetyTests.cpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
// Copyright Catch2 Authors
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE.txt or copy at
|
||||||
|
// https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
|
/**\file
|
||||||
|
* Test that assertions and messages are thread-safe.
|
||||||
|
*
|
||||||
|
* This is done by spamming assertions and messages on multiple subthreads.
|
||||||
|
* In manual, this reliably causes segfaults if the test is linked against
|
||||||
|
* a non-thread-safe version of Catch2.
|
||||||
|
*
|
||||||
|
* The CTest test definition should also verify that the final assertion
|
||||||
|
* count is correct.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
TEST_CASE( "Failed REQUIRE in the main thread is fine" ) {
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
for ( size_t t = 0; t < 4; ++t) {
|
||||||
|
threads.emplace_back( [t]() {
|
||||||
|
CAPTURE(t);
|
||||||
|
for (size_t i = 0; i < 100; ++i) {
|
||||||
|
CAPTURE(i);
|
||||||
|
CHECK( false );
|
||||||
|
CHECK( true );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& t : threads) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE( false );
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
// SPDX-License-Identifier: BSL-1.0
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/generators/catch_generators.hpp>
|
||||||
#include <catch2/internal/catch_enforce.hpp>
|
#include <catch2/internal/catch_enforce.hpp>
|
||||||
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
|
||||||
#include <catch2/internal/catch_optional.hpp>
|
#include <catch2/internal/catch_optional.hpp>
|
||||||
@@ -170,3 +171,64 @@ TEST_CASE( "Decomposer checks that the argument is 0 when handling "
|
|||||||
|
|
||||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE( "foo", "[approvals]" ) {
|
||||||
|
SECTION( "A" ) {
|
||||||
|
SECTION( "B1" ) { REQUIRE( true ); }
|
||||||
|
SECTION( "B2" ) { REQUIRE( true ); }
|
||||||
|
SECTION( "B3" ) { REQUIRE( true ); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "bar", "[approvals]" ) {
|
||||||
|
REQUIRE( true );
|
||||||
|
SECTION( "A" ) {
|
||||||
|
SECTION( "B1" ) { REQUIRE( true ); }
|
||||||
|
SECTION( "B2" ) { REQUIRE( true ); }
|
||||||
|
SECTION( "B3" ) { REQUIRE( true ); }
|
||||||
|
}
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "baz", "[approvals]" ) {
|
||||||
|
SECTION( "A" ) { REQUIRE( true ); }
|
||||||
|
auto _ = GENERATE( 1, 2, 3 );
|
||||||
|
(void)_;
|
||||||
|
SECTION( "B" ) { REQUIRE( true ); }
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "qux", "[approvals]" ) {
|
||||||
|
REQUIRE( true );
|
||||||
|
SECTION( "A" ) { REQUIRE( true ); }
|
||||||
|
auto _ = GENERATE( 1, 2, 3 );
|
||||||
|
(void)_;
|
||||||
|
SECTION( "B" ) { REQUIRE( true ); }
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE( "corge", "[approvals]" ) {
|
||||||
|
REQUIRE( true );
|
||||||
|
SECTION( "A" ) {
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
auto i = GENERATE( 1, 2, 3 );
|
||||||
|
DYNAMIC_SECTION( "i=" << i ) {
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("grault", "[approvals]") {
|
||||||
|
REQUIRE( true );
|
||||||
|
SECTION( "A" ) {
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
SECTION("B") {
|
||||||
|
auto i = GENERATE( 1, 2, 3 );
|
||||||
|
DYNAMIC_SECTION( "i=" << i ) {
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REQUIRE( true );
|
||||||
|
}
|
||||||
|
|||||||
@@ -354,27 +354,9 @@ TEST_CASE("#1514: stderr/stdout is not captured in tests aborted by an exception
|
|||||||
FAIL("1514");
|
FAIL("1514");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE( "#2025: -c shouldn't cause infinite loop", "[sections][generators][regression][.approvals]" ) {
|
|
||||||
SECTION( "Check cursor from buffer offset" ) {
|
|
||||||
auto bufPos = GENERATE_REF( range( 0, 44 ) );
|
|
||||||
WHEN( "Buffer position is " << bufPos ) { REQUIRE( 1 == 1 ); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("#2025: original repro", "[sections][generators][regression][.approvals]") {
|
TEST_CASE("#2025: original repro", "[sections][generators][regression][.approvals]") {
|
||||||
auto fov = GENERATE(true, false);
|
auto fov = GENERATE(true, false);
|
||||||
DYNAMIC_SECTION("fov_" << fov) {
|
DYNAMIC_SECTION("fov_" << fov) {
|
||||||
std::cout << "inside with fov: " << fov << '\n';
|
std::cout << "inside with fov: " << fov << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("#2025: same-level sections", "[sections][generators][regression][.approvals]") {
|
|
||||||
SECTION("A") {
|
|
||||||
SUCCEED();
|
|
||||||
}
|
|
||||||
auto i = GENERATE(1, 2, 3);
|
|
||||||
SECTION("B") {
|
|
||||||
REQUIRE(i < 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
128
tests/TestScripts/testSectionFiltering.py
Executable file
128
tests/TestScripts/testSectionFiltering.py
Executable file
@@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Copyright Catch2 Authors
|
||||||
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
|
# (See accompanying file LICENSE.txt or copy at
|
||||||
|
# https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
|
"""
|
||||||
|
This test script verifies the behaviour of the legacy section filtering
|
||||||
|
using `-c`, `--section` CLI parameters.
|
||||||
|
|
||||||
|
This is done by having a hardcoded set of test filter + section filter
|
||||||
|
combinations, together with the expected number of assertions that will
|
||||||
|
be run inside the test for given filter combo.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import Tuple, List
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
def make_cli_filter(section_names: Tuple[str, ...]) -> List[str]:
|
||||||
|
final = []
|
||||||
|
for name in section_names:
|
||||||
|
final.append('--section')
|
||||||
|
final.append(name)
|
||||||
|
return final
|
||||||
|
|
||||||
|
def run_one_test(binary_path: str, test_name: str, section_names: Tuple[str, ...], expected_assertions: int):
|
||||||
|
cmd = [
|
||||||
|
binary_path,
|
||||||
|
'--reporter', 'xml',
|
||||||
|
test_name
|
||||||
|
]
|
||||||
|
cmd.extend(make_cli_filter(section_names))
|
||||||
|
try:
|
||||||
|
ret = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
check=True,
|
||||||
|
universal_newlines=True,
|
||||||
|
)
|
||||||
|
stdout = ret.stdout
|
||||||
|
except subprocess.SubprocessError as ex:
|
||||||
|
print('Could not run "{}"'.format(cmd))
|
||||||
|
print("Return code: {}".format(ex.returncode))
|
||||||
|
print("stdout: {}".format(ex.stdout))
|
||||||
|
print("stderr: {}".format(ex.stderr))
|
||||||
|
raise
|
||||||
|
|
||||||
|
try:
|
||||||
|
tree = ET.fromstring(stdout)
|
||||||
|
except ET.ParseError as ex:
|
||||||
|
print("Invalid XML: '{}'".format(ex))
|
||||||
|
raise
|
||||||
|
|
||||||
|
# Validate that we ran exactly 1 test case, and it passed
|
||||||
|
test_case_stats = tree.find('OverallResultsCases')
|
||||||
|
expected_testcases = {'successes' : '1', 'failures' : '0', 'expectedFailures': '0', 'skips': '0'}
|
||||||
|
assert test_case_stats.attrib == expected_testcases, f'We did not run single passing test case as expected. {test_name}: {test_case_stats.attrib}'
|
||||||
|
|
||||||
|
# Validate that we got exactly the expected number of passing assertions
|
||||||
|
expected_assertions = {'successes' : str(expected_assertions), 'failures' : '0', 'expectedFailures': '0', 'skips': '0'}
|
||||||
|
assertion_stats = tree.find('OverallResults')
|
||||||
|
assert assertion_stats.attrib == expected_assertions, f'"{test_name}": {assertion_stats.attrib} vs {expected_assertions}'
|
||||||
|
|
||||||
|
|
||||||
|
# Inputs taken from issue #3038
|
||||||
|
tests = {
|
||||||
|
'foo': (
|
||||||
|
((), 3),
|
||||||
|
(('A',), 3),
|
||||||
|
(('A', 'B'), 0),
|
||||||
|
(('A', 'B1'), 1),
|
||||||
|
(('A', 'B2'), 1),
|
||||||
|
(('A', 'B1', 'B2'), 1),
|
||||||
|
(('A', 'B2', 'XXXX'), 1),
|
||||||
|
),
|
||||||
|
'bar': (
|
||||||
|
((), 9),
|
||||||
|
(('A',), 9),
|
||||||
|
(('A', 'B1'), 3),
|
||||||
|
(('XXXX',), 2),
|
||||||
|
(('B1',), 2),
|
||||||
|
(('A', 'B1', 'B2'), 3),
|
||||||
|
),
|
||||||
|
'baz': (
|
||||||
|
((), 4),
|
||||||
|
(('A',), 1),
|
||||||
|
(('A', 'B'), 1),
|
||||||
|
(('A', 'XXXX'), 1),
|
||||||
|
(('B',), 3),
|
||||||
|
(('XXXX',), 0),
|
||||||
|
),
|
||||||
|
'qux': (
|
||||||
|
((), 12),
|
||||||
|
(('A',), 7),
|
||||||
|
(('B',), 9),
|
||||||
|
(('B', 'XXXX'), 9),
|
||||||
|
(('XXXX',), 6),
|
||||||
|
),
|
||||||
|
'corge': (
|
||||||
|
((), 12),
|
||||||
|
(('i=2',), 7),
|
||||||
|
(('i=3',), 7),
|
||||||
|
),
|
||||||
|
'grault': (
|
||||||
|
((), 12),
|
||||||
|
(('A',), 3),
|
||||||
|
(('B',), 9),
|
||||||
|
(('B', 'i=1'), 7),
|
||||||
|
(('B', 'XXXX'), 6),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sys.argv) != 2:
|
||||||
|
print("Wrong number of arguments, expected just the path to Catch2 SelfTest binary")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
bin_path = os.path.abspath(sys.argv[1])
|
||||||
|
|
||||||
|
for test_filter, specs in tests.items():
|
||||||
|
for section_path, expected_assertions in specs:
|
||||||
|
run_one_test(bin_path, test_filter, section_path, expected_assertions)
|
||||||
Reference in New Issue
Block a user