summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAbseil Team <absl-team@google.com>2024-10-01 16:32:16 (GMT)
committerCopybara-Service <copybara-worker@google.com>2024-10-01 16:35:17 (GMT)
commita1e255a582377e1006bb88a408ac3f933ba7c916 (patch)
tree7af6560919b83947f259a5f1fa9764c1c96f9d8f
parent6dae7eb4a5c3a169f3e298392bff4680224aa94a (diff)
downloadgoogletest-a1e255a582377e1006bb88a408ac3f933ba7c916.zip
googletest-a1e255a582377e1006bb88a408ac3f933ba7c916.tar.gz
googletest-a1e255a582377e1006bb88a408ac3f933ba7c916.tar.bz2
Optional(): Add support for std::optional<>-like types lacking bool conversion.
PiperOrigin-RevId: 681053268 Change-Id: If80ba667fd4c91340e1405a9691f5ca0350fa9eb
-rw-r--r--googlemock/include/gmock/gmock-matchers.h33
-rw-r--r--googlemock/test/gmock-matchers-misc_test.cc74
2 files changed, 82 insertions, 25 deletions
diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h
index 26401d5..70e7a76 100644
--- a/googlemock/include/gmock/gmock-matchers.h
+++ b/googlemock/include/gmock/gmock-matchers.h
@@ -561,6 +561,11 @@ Matcher<T> A();
// and MUST NOT BE USED IN USER CODE!!!
namespace internal {
+// Used per go/ranked-overloads for dispatching.
+struct Rank0 {};
+struct Rank1 : Rank0 {};
+using HighestRank = Rank1;
+
// If the explanation is not empty, prints it to the ostream.
inline void PrintIfNotEmpty(const std::string& explanation,
::std::ostream* os) {
@@ -2955,10 +2960,6 @@ class EachMatcher {
const M inner_matcher_;
};
-// Use go/ranked-overloads for dispatching.
-struct Rank0 {};
-struct Rank1 : Rank0 {};
-
namespace pair_getters {
using std::get;
template <typename T>
@@ -3912,6 +3913,21 @@ GTEST_API_ std::string FormatMatcherDescription(
bool negation, const char* matcher_name,
const std::vector<const char*>& param_names, const Strings& param_values);
+// Overloads to support `OptionalMatcher` being used with a type that either
+// supports implicit conversion to bool or a `has_value()` method.
+template <typename Optional>
+auto IsOptionalEngaged(const Optional& optional,
+ Rank1) -> decltype(!!optional) {
+ // The use of double-negation here is to preserve historical behavior where
+ // the matcher used `operator!` rather than directly using `operator bool`.
+ return !static_cast<bool>(!optional);
+}
+template <typename Optional>
+auto IsOptionalEngaged(const Optional& optional,
+ Rank0) -> decltype(!optional.has_value()) {
+ return optional.has_value();
+}
+
// Implements a matcher that checks the value of a optional<> type variable.
template <typename ValueMatcher>
class OptionalMatcher {
@@ -3944,7 +3960,7 @@ class OptionalMatcher {
bool MatchAndExplain(Optional optional,
MatchResultListener* listener) const override {
- if (!optional) {
+ if (!IsOptionalEngaged(optional, HighestRank())) {
*listener << "which is not engaged";
return false;
}
@@ -5266,9 +5282,10 @@ inline InnerMatcher AllArgs(const InnerMatcher& matcher) {
}
// Returns a matcher that matches the value of an optional<> type variable.
-// The matcher implementation only uses '!arg' and requires that the optional<>
-// type has a 'value_type' member type and that '*arg' is of type 'value_type'
-// and is printable using 'PrintToString'. It is compatible with
+// The matcher implementation only uses '!arg' (or 'arg.has_value()' if '!arg`
+// isn't a valid expression) and requires that the optional<> type has a
+// 'value_type' member type and that '*arg' is of type 'value_type' and is
+// printable using 'PrintToString'. It is compatible with
// std::optional/std::experimental::optional.
// Note that to compare an optional type variable against nullopt you should
// use Eq(nullopt) and not Eq(Optional(nullopt)). The latter implies that the
diff --git a/googlemock/test/gmock-matchers-misc_test.cc b/googlemock/test/gmock-matchers-misc_test.cc
index 9ac2458..ac976dd 100644
--- a/googlemock/test/gmock-matchers-misc_test.cc
+++ b/googlemock/test/gmock-matchers-misc_test.cc
@@ -674,6 +674,8 @@ TEST_P(MatcherTupleTestP, ExplainsMatchFailure) {
// explanation.
}
+#if GTEST_HAS_TYPED_TEST
+
// Sample optional type implementation with minimal requirements for use with
// Optional matcher.
template <typename T>
@@ -691,38 +693,76 @@ class SampleOptional {
bool has_value_;
};
-TEST(OptionalTest, DescribesSelf) {
- const Matcher<SampleOptional<int>> m = Optional(Eq(1));
+// Sample optional type implementation with alternative minimal requirements for
+// use with Optional matcher. In particular, while it doesn't have a bool
+// conversion operator, it does have a has_value() method.
+template <typename T>
+class SampleOptionalWithoutBoolConversion {
+ public:
+ using value_type = T;
+ explicit SampleOptionalWithoutBoolConversion(T value)
+ : value_(std::move(value)), has_value_(true) {}
+ SampleOptionalWithoutBoolConversion() : value_(), has_value_(false) {}
+ bool has_value() const { return has_value_; }
+ const T& operator*() const { return value_; }
+
+ private:
+ T value_;
+ bool has_value_;
+};
+
+template <typename T>
+class OptionalTest : public testing::Test {};
+
+using OptionalTestTypes =
+ testing::Types<SampleOptional<int>,
+ SampleOptionalWithoutBoolConversion<int>>;
+
+TYPED_TEST_SUITE(OptionalTest, OptionalTestTypes);
+
+TYPED_TEST(OptionalTest, DescribesSelf) {
+ const Matcher<TypeParam> m = Optional(Eq(1));
EXPECT_EQ("value is equal to 1", Describe(m));
}
-TEST(OptionalTest, ExplainsSelf) {
- const Matcher<SampleOptional<int>> m = Optional(Eq(1));
- EXPECT_EQ("whose value 1 matches", Explain(m, SampleOptional<int>(1)));
- EXPECT_EQ("whose value 2 doesn't match", Explain(m, SampleOptional<int>(2)));
+TYPED_TEST(OptionalTest, ExplainsSelf) {
+ const Matcher<TypeParam> m = Optional(Eq(1));
+ EXPECT_EQ("whose value 1 matches", Explain(m, TypeParam(1)));
+ EXPECT_EQ("whose value 2 doesn't match", Explain(m, TypeParam(2)));
}
-TEST(OptionalTest, MatchesNonEmptyOptional) {
- const Matcher<SampleOptional<int>> m1 = Optional(1);
- const Matcher<SampleOptional<int>> m2 = Optional(Eq(2));
- const Matcher<SampleOptional<int>> m3 = Optional(Lt(3));
- SampleOptional<int> opt(1);
+TYPED_TEST(OptionalTest, MatchesNonEmptyOptional) {
+ const Matcher<TypeParam> m1 = Optional(1);
+ const Matcher<TypeParam> m2 = Optional(Eq(2));
+ const Matcher<TypeParam> m3 = Optional(Lt(3));
+ TypeParam opt(1);
EXPECT_TRUE(m1.Matches(opt));
EXPECT_FALSE(m2.Matches(opt));
EXPECT_TRUE(m3.Matches(opt));
}
-TEST(OptionalTest, DoesNotMatchNullopt) {
- const Matcher<SampleOptional<int>> m = Optional(1);
- SampleOptional<int> empty;
+TYPED_TEST(OptionalTest, DoesNotMatchNullopt) {
+ const Matcher<TypeParam> m = Optional(1);
+ TypeParam empty;
EXPECT_FALSE(m.Matches(empty));
}
-TEST(OptionalTest, WorksWithMoveOnly) {
- Matcher<SampleOptional<std::unique_ptr<int>>> m = Optional(Eq(nullptr));
- EXPECT_TRUE(m.Matches(SampleOptional<std::unique_ptr<int>>(nullptr)));
+template <typename T>
+class MoveOnlyOptionalTest : public testing::Test {};
+
+using MoveOnlyOptionalTestTypes =
+ testing::Types<SampleOptional<std::unique_ptr<int>>,
+ SampleOptionalWithoutBoolConversion<std::unique_ptr<int>>>;
+
+TYPED_TEST_SUITE(MoveOnlyOptionalTest, MoveOnlyOptionalTestTypes);
+
+TYPED_TEST(MoveOnlyOptionalTest, WorksWithMoveOnly) {
+ Matcher<TypeParam> m = Optional(Eq(nullptr));
+ EXPECT_TRUE(m.Matches(TypeParam(nullptr)));
}
+#endif // GTEST_HAS_TYPED_TEST
+
class SampleVariantIntString {
public:
SampleVariantIntString(int i) : i_(i), has_int_(true) {}