Maintain constness in assertions & expectations to resolve operator bool() cv-qualifier inconsistency between ASSERT_FALSE/EXPECT_FALSE vs. ASSERT_TRUE/EXPECT_TRUE

Currently, these macros do not behave consistently in how they convert objects to bool, adding extra const qualifiers or using operator!(). This causes them to produce unexpected behavior on types such as the following:

```
struct example {
    operator bool() { throw std::runtime_error("constness not propagated"); }
    operator bool() const { return ...; }
};
```

This change preserves the qualifiers of the evaluated objects and makes the evaluation strategy uniform across macros.

Fixes: #4832
PiperOrigin-RevId: 953424646
Change-Id: I670a72eec11272c3b68d7eb6f739a15da227acde
This commit is contained in:
Abseil Team 2026-07-24 10:05:06 -07:00 committed by Copybara-Service
parent 44f4904703
commit 80b3670e1a
4 changed files with 87 additions and 15 deletions

View File

@ -158,13 +158,18 @@ class GTEST_API_ [[nodiscard]] AssertionResult {
// The second parameter prevents this overload from being considered if // The second parameter prevents this overload from being considered if
// the argument is implicitly convertible to AssertionResult. In that case // the argument is implicitly convertible to AssertionResult. In that case
// we want AssertionResult's copy constructor to be used. // we want AssertionResult's copy constructor to be used.
template <typename T> template <typename T,
explicit AssertionResult( std::enable_if_t<!std::is_convertible_v<T, AssertionResult> &&
const T& success, !std::is_trivially_constructible_v<bool, T>,
std::enable_if_t<!std::is_convertible_v<T, AssertionResult>>* int> = 0>
/*enabler*/ explicit AssertionResult(T&& success) : success_(std::forward<T>(success)) {}
= nullptr)
: success_(success) {} // Similar to the mutable overload, but for cases where mutability is
// unnecessary or problematic (e.g., bitfields).
template <typename T,
std::enable_if_t<!std::is_convertible_v<const T&, AssertionResult>,
int> = 0>
explicit AssertionResult(const T& success) : success_(success) {}
#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920) #if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
GTEST_DISABLE_MSC_WARNINGS_POP_() GTEST_DISABLE_MSC_WARNINGS_POP_()
@ -226,6 +231,24 @@ class GTEST_API_ [[nodiscard]] AssertionResult {
std::unique_ptr< ::std::string> message_; std::unique_ptr< ::std::string> message_;
}; };
namespace internal {
// A pair containing the result that an assertion is evaluating, and the
// expected result (true, false).
//
// Contains a conversion operator that indicates whether the two match.
struct AssertionResultExpectation {
testing::AssertionResult assertion_result;
bool expected_result;
explicit operator bool() const {
bool converted(assertion_result);
return converted == expected_result;
}
};
} // namespace internal
// Makes a successful assertion result. // Makes a successful assertion result.
GTEST_API_ AssertionResult AssertionSuccess(); GTEST_API_ AssertionResult AssertionSuccess();

View File

@ -1818,14 +1818,13 @@ class [[nodiscard]] TestWithParam : public Test,
#define GTEST_EXPECT_TRUE(condition) \ #define GTEST_EXPECT_TRUE(condition) \
GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
GTEST_NONFATAL_FAILURE_) GTEST_NONFATAL_FAILURE_)
#define GTEST_EXPECT_FALSE(condition) \ #define GTEST_EXPECT_FALSE(condition) \
GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ GTEST_TEST_BOOLEAN_(condition, #condition, true, false, \
GTEST_NONFATAL_FAILURE_) GTEST_NONFATAL_FAILURE_)
#define GTEST_ASSERT_TRUE(condition) \ #define GTEST_ASSERT_TRUE(condition) \
GTEST_TEST_BOOLEAN_(condition, #condition, false, true, GTEST_FATAL_FAILURE_) GTEST_TEST_BOOLEAN_(condition, #condition, false, true, GTEST_FATAL_FAILURE_)
#define GTEST_ASSERT_FALSE(condition) \ #define GTEST_ASSERT_FALSE(condition) \
GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ GTEST_TEST_BOOLEAN_(condition, #condition, true, false, GTEST_FATAL_FAILURE_)
GTEST_FATAL_FAILURE_)
// Define these macros to 1 to omit the definition of the corresponding // Define these macros to 1 to omit the definition of the corresponding
// EXPECT or ASSERT, which clashes with some users' own code. // EXPECT or ASSERT, which clashes with some users' own code.

View File

@ -1453,12 +1453,12 @@ class [[nodiscard]] NeverThrown {
// representation of expression as it was passed into the EXPECT_TRUE. // representation of expression as it was passed into the EXPECT_TRUE.
#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ #define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (const ::testing::AssertionResult gtest_ar_ = \ if (::testing::internal::AssertionResultExpectation gtest_are_ = { \
::testing::AssertionResult(expression)) \ ::testing::AssertionResult(expression), expected}) \
; \ ; \
else \ else \
fail(::testing::internal::GetBoolAssertionFailureMessage( \ fail(::testing::internal::GetBoolAssertionFailureMessage( \
gtest_ar_, text, #actual, #expected)) gtest_are_.assertion_result, text, #actual, #expected))
#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ #define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ GTEST_AMBIGUOUS_ELSE_BLOCKER_ \

View File

@ -88,6 +88,20 @@ static_assert(sizeof(decltype(std::declval<ConvertibleGlobalType&>()
<< 1)(*)()) > 0, << 1)(*)()) > 0,
"error in operator<< overload resolution"); "error in operator<< overload resolution");
namespace {
template <bool MutableResult, bool ConstResult>
struct ConvertibleToBool {
explicit operator bool() { return MutableResult; }
explicit operator bool() const { return ConstResult; }
};
struct Bitfield {
bool bit : 1;
};
} // namespace
namespace testing { namespace testing {
namespace internal { namespace internal {
@ -3699,6 +3713,15 @@ TEST(AssertionTest, AppendUserMessage) {
TEST(AssertionTest, ASSERT_TRUE) { TEST(AssertionTest, ASSERT_TRUE) {
ASSERT_TRUE(2 > 1); // NOLINT ASSERT_TRUE(2 > 1); // NOLINT
EXPECT_FATAL_FAILURE(ASSERT_TRUE(2 < 1), "2 < 1"); EXPECT_FATAL_FAILURE(ASSERT_TRUE(2 < 1), "2 < 1");
ASSERT_TRUE((ConvertibleToBool<true, false>()));
ASSERT_TRUE((std::add_const_t<ConvertibleToBool<false, true>>()));
Bitfield bf = {true};
ASSERT_TRUE(bf.bit); // &
ASSERT_TRUE(Bitfield{true}.bit); // &&
ASSERT_TRUE(static_cast<const Bitfield&>(Bitfield{true}).bit); // const&
ASSERT_TRUE(static_cast<const Bitfield&&>(Bitfield{true}).bit); // const&&
} }
// Tests ASSERT_TRUE(predicate) for predicates returning AssertionResult. // Tests ASSERT_TRUE(predicate) for predicates returning AssertionResult.
@ -3725,6 +3748,15 @@ TEST(AssertionTest, ASSERT_FALSE) {
"Value of: 2 > 1\n" "Value of: 2 > 1\n"
" Actual: true\n" " Actual: true\n"
"Expected: false"); "Expected: false");
ASSERT_FALSE((ConvertibleToBool<false, true>()));
ASSERT_FALSE((std::add_const_t<ConvertibleToBool<true, false>>()));
Bitfield bf = {false};
ASSERT_FALSE(bf.bit); // &
ASSERT_FALSE(Bitfield{false}.bit); // &&
ASSERT_FALSE(static_cast<const Bitfield&>(Bitfield{false}).bit); // const&
ASSERT_FALSE(static_cast<const Bitfield&&>(Bitfield{false}).bit); // const&&
} }
// Tests ASSERT_FALSE(predicate) for predicates returning AssertionResult. // Tests ASSERT_FALSE(predicate) for predicates returning AssertionResult.
@ -4426,6 +4458,15 @@ TEST(ExpectTest, EXPECT_TRUE) {
" Actual: false\n" " Actual: false\n"
"Expected: true"); "Expected: true");
EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(2 > 3), "2 > 3"); EXPECT_NONFATAL_FAILURE(EXPECT_TRUE(2 > 3), "2 > 3");
EXPECT_TRUE((ConvertibleToBool<true, false>()));
EXPECT_TRUE((std::add_const_t<ConvertibleToBool<false, true>>()));
Bitfield bf = {true};
EXPECT_TRUE(bf.bit); // &
EXPECT_TRUE(Bitfield{true}.bit); // &&
EXPECT_TRUE(static_cast<const Bitfield&>(Bitfield{true}).bit); // const&
EXPECT_TRUE(static_cast<const Bitfield&&>(Bitfield{true}).bit); // const&&
} }
// Tests EXPECT_TRUE(predicate) for predicates returning AssertionResult. // Tests EXPECT_TRUE(predicate) for predicates returning AssertionResult.
@ -4455,6 +4496,15 @@ TEST(ExpectTest, EXPECT_FALSE) {
" Actual: true\n" " Actual: true\n"
"Expected: false"); "Expected: false");
EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(2 < 3), "2 < 3"); EXPECT_NONFATAL_FAILURE(EXPECT_FALSE(2 < 3), "2 < 3");
EXPECT_FALSE((ConvertibleToBool<false, true>()));
EXPECT_FALSE((std::add_const_t<ConvertibleToBool<true, false>>()));
Bitfield bf = {false};
EXPECT_FALSE(bf.bit); // &
EXPECT_FALSE(Bitfield{false}.bit); // &&
EXPECT_FALSE(static_cast<const Bitfield&>(Bitfield{false}).bit); // const&
EXPECT_FALSE(static_cast<const Bitfield&&>(Bitfield{false}).bit); // const&&
} }
// Tests EXPECT_FALSE(predicate) for predicates returning AssertionResult. // Tests EXPECT_FALSE(predicate) for predicates returning AssertionResult.