From 1a4f16d679642c5b58ddb11e997f60731ddc1974 Mon Sep 17 00:00:00 2001 From: Dustin Gooding Date: Fri, 21 Mar 2025 15:41:11 -0500 Subject: [PATCH] :bug: Fix printing variables when expect or assert fails Problem: - EXPECT and ASSERT macros only print whether the expression is true or false when the test expectations fail - GUnit stopped printing out the values of the variables used in the expressiong passed to the EXPECT or ASSERT macros - Fix for logical expression broke variable printing Solution: - Remove extra parenthesis in macros that resulted in full evaluation of the expression before the op and cmp objects could be initialized. - Add CmpHelperAnd/Or/Xor with corresponding and/or/xor overloads to allow evaluating logical expressions. - Added test to verify tested expressions are only evaluated once. --- include/GUnit/GAssert.h | 57 ++++++++++++++++++++++++++++++++++++++--- test/GAssert.cpp | 18 +++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/include/GUnit/GAssert.h b/include/GUnit/GAssert.h index 6f0743a..f187ca0 100644 --- a/include/GUnit/GAssert.h +++ b/include/GUnit/GAssert.h @@ -17,6 +17,36 @@ namespace testing { inline namespace v1 { namespace detail { +template +AssertionResult CmpHelperAnd(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, const T2& rhs) { + if (lhs and rhs) { + return AssertionSuccess(); + } + return internal::CmpHelperOpFailure(lhs_expression, rhs_expression, lhs, rhs, "and"); +}; + +template +AssertionResult CmpHelperOr(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, const T2& rhs) { + if (lhs or rhs) { + return AssertionSuccess(); + } + return internal::CmpHelperOpFailure(lhs_expression, rhs_expression, lhs, rhs, "or"); +}; + +template +AssertionResult CmpHelperXor(const char* lhs_expression, + const char* rhs_expression, + const T1& lhs, const T2& rhs) { + if (lhs xor rhs) { + return AssertionSuccess(); + } + return internal::CmpHelperOpFailure(lhs_expression, rhs_expression, lhs, rhs, "xor"); +}; + struct info { const char* file{}; unsigned long line{}; @@ -127,6 +157,27 @@ class op { info_, "<", lhs_, rhs}; } + template + auto operator&&(const TRhs& rhs) const { + followed_ = true; + return msg{ + info_, "and", lhs_, rhs}; + } + + template + auto operator||(const TRhs& rhs) const { + followed_ = true; + return msg{ + info_, "or", lhs_, rhs}; + } + + template + auto operator^(const TRhs& rhs) const { + followed_ = true; + return msg{ + info_, "xor", lhs_, rhs}; + } + operator bool() const { return result_; } private: @@ -213,7 +264,7 @@ void prevent_commas(T&&) {} (::testing::detail::op{ \ ::testing::detail::info{__FILE__, __LINE__, #__VA_ARGS__, \ ::testing::TestPartResult::kNonFatalFailure}} \ - << (__VA_ARGS__)) + << __VA_ARGS__) #define EXPECT(...) \ GUNIT_PREVENT_COMMAS(__VA_ARGS__); \ @@ -223,14 +274,14 @@ void prevent_commas(T&&) {} if (::testing::detail::op{ \ ::testing::detail::info{__FILE__, __LINE__, #__VA_ARGS__, \ ::testing::TestPartResult::kFatalFailure}} \ - << (__VA_ARGS__)) \ + << __VA_ARGS__) \ void(::testing::detail::drop{}); \ else \ return ::testing::detail::ret_void{} == \ (::testing::detail::op{::testing::detail::info{ \ __FILE__, __LINE__, #__VA_ARGS__, \ ::testing::TestPartResult::kFatalFailure}} \ - << (__VA_ARGS__)) + << __VA_ARGS__) #define ASSERT(...) \ GUNIT_PREVENT_COMMAS(__VA_ARGS__); \ diff --git a/test/GAssert.cpp b/test/GAssert.cpp index 2cdff3c..9f85b9e 100644 --- a/test/GAssert.cpp +++ b/test/GAssert.cpp @@ -8,6 +8,20 @@ #include #include "GUnit/GAssert.h" +struct call_only_once { + int a{42}; + bool called{false}; + + auto value() -> int { + if (called) { + throw std::runtime_error("Called twice, should only evaluate once."); + } + + called = true; + return a; + } +}; + TEST(GAssert, ShouldSupportExpect) { auto i = 42; const auto b = true; @@ -16,6 +30,10 @@ TEST(GAssert, ShouldSupportExpect) { EXPECT(!false) << "message"; EXPECT(b); + call_only_once once{}; + EXPECT(once.value() == 42); + EXPECT_THROW(once.value(), std::runtime_error); + EXPECT(i == 42); EXPECT(42 == i); EXPECT(42 == i);