From bc524d70253a4ab2fe40c3ca3e5666e267c0a4d1 Mon Sep 17 00:00:00 2001 From: Lexi Winter Date: Sun, 29 Jun 2025 19:25:29 +0100 Subject: import catch2 3.8.1 --- tests/SelfTest/UsageTests/Compilation.tests.cpp | 525 ++++++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 tests/SelfTest/UsageTests/Compilation.tests.cpp (limited to 'tests/SelfTest/UsageTests/Compilation.tests.cpp') diff --git a/tests/SelfTest/UsageTests/Compilation.tests.cpp b/tests/SelfTest/UsageTests/Compilation.tests.cpp new file mode 100644 index 0000000..7a6a1f2 --- /dev/null +++ b/tests/SelfTest/UsageTests/Compilation.tests.cpp @@ -0,0 +1,525 @@ + +// 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 + +#include + +#include +#include + +// Setup for #1403 -- look for global overloads of operator << for classes +// in a different namespace. +#include + +namespace foo { + struct helper_1403 { + bool operator==(helper_1403) const { return true; } + }; +} + +namespace bar { + template + struct TypeList {}; +} + +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#endif +static std::ostream& operator<<(std::ostream& out, foo::helper_1403 const&) { + return out << "[1403 helper]"; +} +/////////////////////////////// + +#include +#include +#include + +#include + +// Comparison operators can return non-booleans. +// This is unusual, but should be supported. +struct logic_t { + logic_t operator< (logic_t) const { return {}; } + logic_t operator<=(logic_t) const { return {}; } + logic_t operator> (logic_t) const { return {}; } + logic_t operator>=(logic_t) const { return {}; } + logic_t operator==(logic_t) const { return {}; } + logic_t operator!=(logic_t) const { return {}; } + explicit operator bool() const { return true; } +}; + + +static void throws_int(bool b) { + if (b) { + throw 1; + } +} + +template +bool templated_tests(T t) { + int a = 3; + REQUIRE(a == t); + CHECK(a == t); + REQUIRE_THROWS(throws_int(true)); + CHECK_THROWS_AS(throws_int(true), int); + REQUIRE_NOTHROW(throws_int(false)); + REQUIRE_THAT("aaa", Catch::Matchers::EndsWith("aaa")); + return true; +} + +struct A {}; + +static std::ostream &operator<<(std::ostream &o, const A &) { return o << 0; } + +struct B : private A { + bool operator==(int) const { return true; } +}; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +#endif +#ifdef __GNUC__ +// Note that because -~GCC~-, this warning cannot be silenced temporarily, by pushing diagnostic stack... +// Luckily it is firing in test files and thus can be silenced for the whole file, without losing much. +#pragma GCC diagnostic ignored "-Wunused-function" +#endif + +B f(); + +std::ostream g(); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +template +struct Fixture_1245 {}; + +// This is a minimal example for an issue we have found in 1.7.0 +struct dummy_809 { + int i; +}; + +template +bool operator==(const T& val, dummy_809 f) { + return val == f.i; +} + +TEST_CASE("#809") { + dummy_809 f; + f.i = 42; + REQUIRE(42 == f); +} + + +// ------------------------------------------------------------------ +// Changes to REQUIRE_THROWS_AS made it stop working in a template in +// an unfixable way (as long as C++03 compatibility is being kept). +// To prevent these from happening in the future, this needs to compile + + TEST_CASE("#833") { + REQUIRE(templated_tests(3)); + } + + +// Test containing example where original stream insertable check breaks compilation +TEST_CASE("#872") { + A dummy; + CAPTURE(dummy); + B x; + REQUIRE (x == 4); +} + +TEST_CASE("#1027: Bitfields can be captured") { + struct Y { + uint32_t v : 1; + }; + Y y{ 0 }; + REQUIRE(y.v == 0); + REQUIRE(0 == y.v); +} + +// Comparison operators can return non-booleans. +// This is unusual, but should be supported. +TEST_CASE("#1147") { + logic_t t1, t2; + REQUIRE(t1 == t2); + REQUIRE(t1 != t2); + REQUIRE(t1 < t2); + REQUIRE(t1 > t2); + REQUIRE(t1 <= t2); + REQUIRE(t1 >= t2); +} + +// unsigned array +TEST_CASE("#1238") { + unsigned char uarr[] = "123"; + CAPTURE(uarr); + signed char sarr[] = "456"; + CAPTURE(sarr); + + REQUIRE(std::memcmp(uarr, "123", sizeof(uarr)) == 0); + REQUIRE(std::memcmp(sarr, "456", sizeof(sarr)) == 0); +} + +TEST_CASE_METHOD((Fixture_1245), "#1245", "[compilation]") { + SUCCEED(); +} + +TEST_CASE("#1403", "[compilation]") { + ::foo::helper_1403 h1, h2; + REQUIRE(h1 == h2); +} + +TEST_CASE("Optionally static assertions", "[compilation]") { + STATIC_REQUIRE( std::is_void::value ); + STATIC_REQUIRE_FALSE( std::is_void::value ); + STATIC_CHECK( std::is_void::value ); + STATIC_CHECK_FALSE( std::is_void::value ); +} + +TEST_CASE("#1548", "[compilation]") { + using namespace bar; + REQUIRE(std::is_same, TypeList>::value); +} + + // #925 + using signal_t = void (*) (void*); + + struct TestClass { + signal_t testMethod_uponComplete_arg = nullptr; + }; + + namespace utility { + inline static void synchronizing_callback( void * ) { } + } + +#if defined (_MSC_VER) +#pragma warning(push) +// The function pointer comparison below triggers warning because of +// calling conventions +#pragma warning(disable:4244) +#endif + TEST_CASE("#925: comparing function pointer to function address failed to compile", "[!nonportable]" ) { + TestClass test; + REQUIRE(utility::synchronizing_callback != test.testMethod_uponComplete_arg); + } +#if defined (_MSC_VER) +#pragma warning(pop) +#endif + +TEST_CASE( "#1319: Sections can have description (even if it is not saved", + "[compilation]" ) { + SECTION( "SectionName", "This is a long form section description" ) { + SUCCEED(); + } +} + +TEST_CASE("Lambdas in assertions") { + REQUIRE([]() { return true; }()); +} + +namespace { + struct HasBitOperators { + int value; + + friend HasBitOperators operator| (HasBitOperators lhs, HasBitOperators rhs) { + return { lhs.value | rhs.value }; + } + friend HasBitOperators operator& (HasBitOperators lhs, HasBitOperators rhs) { + return { lhs.value & rhs.value }; + } + friend HasBitOperators operator^ (HasBitOperators lhs, HasBitOperators rhs) { + return { lhs.value ^ rhs.value }; + } + explicit operator bool() const { + return !!value; + } + + friend std::ostream& operator<<(std::ostream& out, HasBitOperators val) { + out << "Val: " << val.value; + return out; + } + }; +} + +TEST_CASE("Assertion macros support bit operators and bool conversions", "[compilation][bitops]") { + HasBitOperators lhs{ 1 }, rhs{ 2 }; + REQUIRE(lhs | rhs); + REQUIRE_FALSE(lhs & rhs); + REQUIRE(HasBitOperators{ 1 } & HasBitOperators{ 1 }); + REQUIRE(lhs ^ rhs); + REQUIRE_FALSE(lhs ^ lhs); +} + +namespace { + struct ImmovableType { + ImmovableType() = default; + + ImmovableType(ImmovableType const&) = delete; + ImmovableType& operator=(ImmovableType const&) = delete; + ImmovableType(ImmovableType&&) = delete; + ImmovableType& operator=(ImmovableType&&) = delete; + + friend bool operator==(ImmovableType const&, ImmovableType const&) { + return true; + } + }; +} + +TEST_CASE("Immovable types are supported in basic assertions", "[compilation][.approvals]") { + REQUIRE(ImmovableType{} == ImmovableType{}); +} + +namespace adl { + +struct always_true { + explicit operator bool() const { return true; } +}; + +#define COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(op) \ +template \ +auto operator op (T&&, U&&) { \ + return always_true{}; \ +} + +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(==) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(!=) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(<) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(>) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(<=) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(>=) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(|) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(&) +COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(^) + +#undef COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR + +} + +TEST_CASE("ADL universal operators don't hijack expression deconstruction", "[compilation][.approvals]") { + REQUIRE(adl::always_true{}); + REQUIRE(0 == adl::always_true{}); + REQUIRE(0 != adl::always_true{}); + REQUIRE(0 < adl::always_true{}); + REQUIRE(0 > adl::always_true{}); + REQUIRE(0 <= adl::always_true{}); + REQUIRE(0 >= adl::always_true{}); + REQUIRE(0 | adl::always_true{}); + REQUIRE(0 & adl::always_true{}); + REQUIRE(0 ^ adl::always_true{}); +} + +TEST_CASE( "#2555 - types that can only be compared with 0 literal implemented as pointer conversion are supported", + "[compilation][approvals]" ) { + REQUIRE( TypeWithLit0Comparisons{} < 0 ); + REQUIRE_FALSE( 0 < TypeWithLit0Comparisons{} ); + REQUIRE( TypeWithLit0Comparisons{} <= 0 ); + REQUIRE_FALSE( 0 <= TypeWithLit0Comparisons{} ); + + REQUIRE( TypeWithLit0Comparisons{} > 0 ); + REQUIRE_FALSE( 0 > TypeWithLit0Comparisons{} ); + REQUIRE( TypeWithLit0Comparisons{} >= 0 ); + REQUIRE_FALSE( 0 >= TypeWithLit0Comparisons{} ); + + REQUIRE( TypeWithLit0Comparisons{} == 0 ); + REQUIRE_FALSE( 0 == TypeWithLit0Comparisons{} ); + REQUIRE( TypeWithLit0Comparisons{} != 0 ); + REQUIRE_FALSE( 0 != TypeWithLit0Comparisons{} ); +} + +// These tests require `consteval` to propagate through `constexpr` calls +// which is a late DR against C++20. +#if defined( CATCH_CPP20_OR_GREATER ) && defined( __cpp_consteval ) && \ + __cpp_consteval >= 202211L +// Can't have internal linkage to avoid warnings +void ZeroLiteralErrorFunc(); +namespace { + struct ZeroLiteralConsteval { + template , int> = 0> + consteval ZeroLiteralConsteval( T zero ) noexcept { + if ( zero != 0 ) { ZeroLiteralErrorFunc(); } + } + }; + + // Should only be constructible from literal 0. Uses the propagating + // consteval constructor trick (currently used by MSVC, might be used + // by libc++ in the future as well). + struct TypeWithConstevalLit0Comparison { +# define DEFINE_COMP_OP( op ) \ + constexpr friend bool operator op( TypeWithConstevalLit0Comparison, \ + ZeroLiteralConsteval ) { \ + return true; \ + } \ + constexpr friend bool operator op( ZeroLiteralConsteval, \ + TypeWithConstevalLit0Comparison ) { \ + return false; \ + } \ + /* std::orderings only have these for ==, but we add them for all \ + operators so we can test all overloads for decomposer */ \ + constexpr friend bool operator op( TypeWithConstevalLit0Comparison, \ + TypeWithConstevalLit0Comparison ) { \ + return true; \ + } + + DEFINE_COMP_OP( < ) + DEFINE_COMP_OP( <= ) + DEFINE_COMP_OP( > ) + DEFINE_COMP_OP( >= ) + DEFINE_COMP_OP( == ) + DEFINE_COMP_OP( != ) + +#undef DEFINE_COMP_OP + }; + +} // namespace + +namespace Catch { + template <> + struct capture_by_value : std::true_type {}; +} + +TEST_CASE( "#2555 - types that can only be compared with 0 literal implemented as consteval check are supported", + "[compilation][approvals]" ) { + REQUIRE( TypeWithConstevalLit0Comparison{} < 0 ); + REQUIRE_FALSE( 0 < TypeWithConstevalLit0Comparison{} ); + REQUIRE( TypeWithConstevalLit0Comparison{} <= 0 ); + REQUIRE_FALSE( 0 <= TypeWithConstevalLit0Comparison{} ); + + REQUIRE( TypeWithConstevalLit0Comparison{} > 0 ); + REQUIRE_FALSE( 0 > TypeWithConstevalLit0Comparison{} ); + REQUIRE( TypeWithConstevalLit0Comparison{} >= 0 ); + REQUIRE_FALSE( 0 >= TypeWithConstevalLit0Comparison{} ); + + REQUIRE( TypeWithConstevalLit0Comparison{} == 0 ); + REQUIRE_FALSE( 0 == TypeWithConstevalLit0Comparison{} ); + REQUIRE( TypeWithConstevalLit0Comparison{} != 0 ); + REQUIRE_FALSE( 0 != TypeWithConstevalLit0Comparison{} ); +} + +// We check all comparison ops to test, even though orderings, the primary +// motivation for this functionality, only have self-comparison (and thus +// have the ambiguity issue) for `==` and `!=`. +TEST_CASE( "Comparing const instances of type registered with capture_by_value", + "[regression][approvals][compilation]" ) { + SECTION("Type with consteval-int constructor") { + auto const const_Lit0Type_1 = TypeWithConstevalLit0Comparison{}; + auto const const_Lit0Type_2 = TypeWithConstevalLit0Comparison{}; + REQUIRE( const_Lit0Type_1 == const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 <= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 < const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 >= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 > const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 != const_Lit0Type_2 ); + } + SECTION("Type with constexpr-int constructor") { + auto const const_Lit0Type_1 = TypeWithLit0Comparisons{}; + auto const const_Lit0Type_2 = TypeWithLit0Comparisons{}; + REQUIRE( const_Lit0Type_1 == const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 <= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 < const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 >= const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 > const_Lit0Type_2 ); + REQUIRE( const_Lit0Type_1 != const_Lit0Type_2 ); + } +} + +#endif // C++20 consteval + + +namespace { + struct MultipleImplicitConstructors { + MultipleImplicitConstructors( double ) {} + MultipleImplicitConstructors( int64_t ) {} + bool operator==( MultipleImplicitConstructors ) const { return true; } + bool operator!=( MultipleImplicitConstructors ) const { return true; } + bool operator<( MultipleImplicitConstructors ) const { return true; } + bool operator<=( MultipleImplicitConstructors ) const { return true; } + bool operator>( MultipleImplicitConstructors ) const { return true; } + bool operator>=( MultipleImplicitConstructors ) const { return true; } + }; +} +TEST_CASE("#2571 - tests compile types that have multiple implicit constructors from lit 0", + "[compilation][approvals]") { + MultipleImplicitConstructors mic1( 0.0 ); + MultipleImplicitConstructors mic2( 0.0 ); + REQUIRE( mic1 == mic2 ); + REQUIRE( mic1 != mic2 ); + REQUIRE( mic1 < mic2 ); + REQUIRE( mic1 <= mic2 ); + REQUIRE( mic1 > mic2 ); + REQUIRE( mic1 >= mic2 ); +} + +#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) +// This test does not test all the related codepaths, but it is the original +// reproducer +TEST_CASE( "Comparing const std::weak_ordering instances must compile", + "[compilation][approvals][regression]" ) { + auto const const_ordering_1 = std::weak_ordering::less; + auto const const_ordering_2 = std::weak_ordering::less; + auto plain_ordering_1 = std::weak_ordering::less; + REQUIRE( const_ordering_1 == plain_ordering_1 ); + REQUIRE( const_ordering_1 == const_ordering_2 ); + REQUIRE( plain_ordering_1 == const_ordering_1 ); +} +#endif + +// Reproduce issue with yaml-cpp iterators, where the `const_iterator` +// for Node type has `const T` as the value_type. This is wrong for +// multitude of reasons, but there might be other libraries in the wild +// that share this issue, and the workaround needed to support +// `from_range(iter, iter)` helper with those libraries is easy enough. +class HasBadIterator { + std::array m_arr{}; + +public: + class iterator { + const int* m_ptr = nullptr; + + public: + iterator( const int* ptr ): m_ptr( ptr ) {} + + using difference_type = std::ptrdiff_t; + using value_type = const int; + using pointer = const int*; + using reference = const int&; + using iterator_category = std::input_iterator_tag; + + iterator& operator++() { + ++m_ptr; + return *this; + } + + iterator operator++( int ) { + auto ret( *this ); + ++( *this ); + return ret; + } + + friend bool operator==( iterator lhs, iterator rhs ) { + return lhs.m_ptr == rhs.m_ptr; + } + friend bool operator!=( iterator lhs, iterator rhs ) { + return !( lhs == rhs ); + } + + int operator*() const { return *m_ptr; } + }; + + iterator cbegin() const { return { m_arr.data() }; } + iterator cend() const { return { m_arr.data() + m_arr.size() }; } +}; + +TEST_CASE("from_range(iter, iter) supports const_iterators", "[generators][from-range][approvals]") { + using namespace Catch::Generators; + + HasBadIterator data; + auto gen = from_range(data.cbegin(), data.cend()); + (void)gen; +} -- cgit v1.2.3