feat: Allow Property matcher with default arguments (closes #4747)

This commit is contained in:
GitaekLee 2025-05-01 14:59:13 -07:00 committed by LeeGiTaek
parent 00b2154e8e
commit ab97af0f05
2 changed files with 52 additions and 9 deletions

View File

@ -2217,7 +2217,8 @@ class PropertyMatcher {
*listener << whose_property_ << "is "; *listener << whose_property_ << "is ";
// Cannot pass the return value (for example, int) to MatchPrintAndExplain, // Cannot pass the return value (for example, int) to MatchPrintAndExplain,
// which takes a non-const reference as argument. // which takes a non-const reference as argument.
RefToConstProperty result = (obj.*property_)(); // Use std::invoke to handle potential default arguments.
RefToConstProperty result = std::invoke(property_, obj);
return MatchPrintAndExplain(result, matcher_, listener); return MatchPrintAndExplain(result, matcher_, listener);
} }
@ -2225,11 +2226,10 @@ class PropertyMatcher {
MatchResultListener* listener) const { MatchResultListener* listener) const {
if (p == nullptr) return false; if (p == nullptr) return false;
*listener << "which points to an object "; *listener << "which points to an object " << whose_property_ << "is ";
// Since *p has a property method, it must be a class/struct/union // Use std::invoke to handle potential default arguments with pointers.
// type and thus cannot be a pointer. Therefore we pass RefToConstProperty result = std::invoke(property_, p);
// false_type() as the first argument. return MatchPrintAndExplain(result, matcher_, listener);
return MatchAndExplainImpl(std::false_type(), *p, listener);
} }
Property property_; Property property_;

View File

@ -45,8 +45,8 @@
#include <vector> #include <vector>
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "test/gmock-matchers_test.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "test/gmock-matchers_test.h"
// Silence warning C4244: 'initializing': conversion from 'int' to 'short', // Silence warning C4244: 'initializing': conversion from 'int' to 'short',
// possible loss of data and C4100, unreferenced local parameter // possible loss of data and C4100, unreferenced local parameter
@ -3439,8 +3439,51 @@ TEST(ContainsTest, WorksForTwoDimensionalNativeArray) {
EXPECT_THAT(a, Contains(Not(Contains(5)))); EXPECT_THAT(a, Contains(Not(Contains(5))));
} }
#if defined(__cpp_lib_source_location) && __cpp_lib_source_location >= 201907L
#include <source_location>
class SourceLocationClass {
public:
const std::source_location& GetLocation(
std::source_location sl = std::source_location::current()) const {
last_location_ = sl; // Store it to check later
return last_location_;
}
private:
mutable std::source_location last_location_;
};
MATCHER_P(LocationFunctionName, expected_function_name, "") {
return std::string(arg.function_name()) == expected_function_name;
}
TEST(PropertyTest, WorksWithSourceLocationDefaultArgument) {
SourceLocationClass obj;
const char* expected_func = "";
#if defined(__GNUC__) || defined(__clang__)
expected_func =
"testing::gmock_matchers_containers_test::"
"PropertyTest_WorksWithSourceLocationDefaultArgument_Test::TestBody()";
#elif defined(_MSC_VER)
expected_func =
"testing::gmock_matchers_containers_test::"
"PropertyTest_WorksWithSourceLocationDefaultArgument_Test::TestBody"; // No parentheses usually
#else
expected_func = "TestBody";
#endif
Matcher<const SourceLocationClass&> m = Property(
&SourceLocationClass::GetLocation, LocationFunctionName(expected_func));
EXPECT_TRUE(m.Matches(obj));
const auto& loc = obj.GetLocation();
EXPECT_STREQ(expected_func, loc.function_name());
}
#endif // __cpp_lib_source_location
} // namespace } // namespace
} // namespace gmock_matchers_test } // namespace gmock_matchers_test
} // namespace testing } // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4244 4100