Skip to content

Commit a1e255a

Browse files
Abseil Teamcopybara-github
authored andcommitted
Optional(): Add support for std::optional<>-like types lacking bool conversion.
PiperOrigin-RevId: 681053268 Change-Id: If80ba667fd4c91340e1405a9691f5ca0350fa9eb
1 parent 6dae7eb commit a1e255a

File tree

2 files changed

+82
-25
lines changed

2 files changed

+82
-25
lines changed

googlemock/include/gmock/gmock-matchers.h

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,11 @@ Matcher<T> A();
561561
// and MUST NOT BE USED IN USER CODE!!!
562562
namespace internal {
563563

564+
// Used per go/ranked-overloads for dispatching.
565+
struct Rank0 {};
566+
struct Rank1 : Rank0 {};
567+
using HighestRank = Rank1;
568+
564569
// If the explanation is not empty, prints it to the ostream.
565570
inline void PrintIfNotEmpty(const std::string& explanation,
566571
::std::ostream* os) {
@@ -2955,10 +2960,6 @@ class EachMatcher {
29552960
const M inner_matcher_;
29562961
};
29572962

2958-
// Use go/ranked-overloads for dispatching.
2959-
struct Rank0 {};
2960-
struct Rank1 : Rank0 {};
2961-
29622963
namespace pair_getters {
29632964
using std::get;
29642965
template <typename T>
@@ -3912,6 +3913,21 @@ GTEST_API_ std::string FormatMatcherDescription(
39123913
bool negation, const char* matcher_name,
39133914
const std::vector<const char*>& param_names, const Strings& param_values);
39143915

3916+
// Overloads to support `OptionalMatcher` being used with a type that either
3917+
// supports implicit conversion to bool or a `has_value()` method.
3918+
template <typename Optional>
3919+
auto IsOptionalEngaged(const Optional& optional,
3920+
Rank1) -> decltype(!!optional) {
3921+
// The use of double-negation here is to preserve historical behavior where
3922+
// the matcher used `operator!` rather than directly using `operator bool`.
3923+
return !static_cast<bool>(!optional);
3924+
}
3925+
template <typename Optional>
3926+
auto IsOptionalEngaged(const Optional& optional,
3927+
Rank0) -> decltype(!optional.has_value()) {
3928+
return optional.has_value();
3929+
}
3930+
39153931
// Implements a matcher that checks the value of a optional<> type variable.
39163932
template <typename ValueMatcher>
39173933
class OptionalMatcher {
@@ -3944,7 +3960,7 @@ class OptionalMatcher {
39443960

39453961
bool MatchAndExplain(Optional optional,
39463962
MatchResultListener* listener) const override {
3947-
if (!optional) {
3963+
if (!IsOptionalEngaged(optional, HighestRank())) {
39483964
*listener << "which is not engaged";
39493965
return false;
39503966
}
@@ -5266,9 +5282,10 @@ inline InnerMatcher AllArgs(const InnerMatcher& matcher) {
52665282
}
52675283

52685284
// Returns a matcher that matches the value of an optional<> type variable.
5269-
// The matcher implementation only uses '!arg' and requires that the optional<>
5270-
// type has a 'value_type' member type and that '*arg' is of type 'value_type'
5271-
// and is printable using 'PrintToString'. It is compatible with
5285+
// The matcher implementation only uses '!arg' (or 'arg.has_value()' if '!arg`
5286+
// isn't a valid expression) and requires that the optional<> type has a
5287+
// 'value_type' member type and that '*arg' is of type 'value_type' and is
5288+
// printable using 'PrintToString'. It is compatible with
52725289
// std::optional/std::experimental::optional.
52735290
// Note that to compare an optional type variable against nullopt you should
52745291
// use Eq(nullopt) and not Eq(Optional(nullopt)). The latter implies that the

googlemock/test/gmock-matchers-misc_test.cc

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,8 @@ TEST_P(MatcherTupleTestP, ExplainsMatchFailure) {
674674
// explanation.
675675
}
676676

677+
#if GTEST_HAS_TYPED_TEST
678+
677679
// Sample optional type implementation with minimal requirements for use with
678680
// Optional matcher.
679681
template <typename T>
@@ -691,38 +693,76 @@ class SampleOptional {
691693
bool has_value_;
692694
};
693695

694-
TEST(OptionalTest, DescribesSelf) {
695-
const Matcher<SampleOptional<int>> m = Optional(Eq(1));
696+
// Sample optional type implementation with alternative minimal requirements for
697+
// use with Optional matcher. In particular, while it doesn't have a bool
698+
// conversion operator, it does have a has_value() method.
699+
template <typename T>
700+
class SampleOptionalWithoutBoolConversion {
701+
public:
702+
using value_type = T;
703+
explicit SampleOptionalWithoutBoolConversion(T value)
704+
: value_(std::move(value)), has_value_(true) {}
705+
SampleOptionalWithoutBoolConversion() : value_(), has_value_(false) {}
706+
bool has_value() const { return has_value_; }
707+
const T& operator*() const { return value_; }
708+
709+
private:
710+
T value_;
711+
bool has_value_;
712+
};
713+
714+
template <typename T>
715+
class OptionalTest : public testing::Test {};
716+
717+
using OptionalTestTypes =
718+
testing::Types<SampleOptional<int>,
719+
SampleOptionalWithoutBoolConversion<int>>;
720+
721+
TYPED_TEST_SUITE(OptionalTest, OptionalTestTypes);
722+
723+
TYPED_TEST(OptionalTest, DescribesSelf) {
724+
const Matcher<TypeParam> m = Optional(Eq(1));
696725
EXPECT_EQ("value is equal to 1", Describe(m));
697726
}
698727

699-
TEST(OptionalTest, ExplainsSelf) {
700-
const Matcher<SampleOptional<int>> m = Optional(Eq(1));
701-
EXPECT_EQ("whose value 1 matches", Explain(m, SampleOptional<int>(1)));
702-
EXPECT_EQ("whose value 2 doesn't match", Explain(m, SampleOptional<int>(2)));
728+
TYPED_TEST(OptionalTest, ExplainsSelf) {
729+
const Matcher<TypeParam> m = Optional(Eq(1));
730+
EXPECT_EQ("whose value 1 matches", Explain(m, TypeParam(1)));
731+
EXPECT_EQ("whose value 2 doesn't match", Explain(m, TypeParam(2)));
703732
}
704733

705-
TEST(OptionalTest, MatchesNonEmptyOptional) {
706-
const Matcher<SampleOptional<int>> m1 = Optional(1);
707-
const Matcher<SampleOptional<int>> m2 = Optional(Eq(2));
708-
const Matcher<SampleOptional<int>> m3 = Optional(Lt(3));
709-
SampleOptional<int> opt(1);
734+
TYPED_TEST(OptionalTest, MatchesNonEmptyOptional) {
735+
const Matcher<TypeParam> m1 = Optional(1);
736+
const Matcher<TypeParam> m2 = Optional(Eq(2));
737+
const Matcher<TypeParam> m3 = Optional(Lt(3));
738+
TypeParam opt(1);
710739
EXPECT_TRUE(m1.Matches(opt));
711740
EXPECT_FALSE(m2.Matches(opt));
712741
EXPECT_TRUE(m3.Matches(opt));
713742
}
714743

715-
TEST(OptionalTest, DoesNotMatchNullopt) {
716-
const Matcher<SampleOptional<int>> m = Optional(1);
717-
SampleOptional<int> empty;
744+
TYPED_TEST(OptionalTest, DoesNotMatchNullopt) {
745+
const Matcher<TypeParam> m = Optional(1);
746+
TypeParam empty;
718747
EXPECT_FALSE(m.Matches(empty));
719748
}
720749

721-
TEST(OptionalTest, WorksWithMoveOnly) {
722-
Matcher<SampleOptional<std::unique_ptr<int>>> m = Optional(Eq(nullptr));
723-
EXPECT_TRUE(m.Matches(SampleOptional<std::unique_ptr<int>>(nullptr)));
750+
template <typename T>
751+
class MoveOnlyOptionalTest : public testing::Test {};
752+
753+
using MoveOnlyOptionalTestTypes =
754+
testing::Types<SampleOptional<std::unique_ptr<int>>,
755+
SampleOptionalWithoutBoolConversion<std::unique_ptr<int>>>;
756+
757+
TYPED_TEST_SUITE(MoveOnlyOptionalTest, MoveOnlyOptionalTestTypes);
758+
759+
TYPED_TEST(MoveOnlyOptionalTest, WorksWithMoveOnly) {
760+
Matcher<TypeParam> m = Optional(Eq(nullptr));
761+
EXPECT_TRUE(m.Matches(TypeParam(nullptr)));
724762
}
725763

764+
#endif // GTEST_HAS_TYPED_TEST
765+
726766
class SampleVariantIntString {
727767
public:
728768
SampleVariantIntString(int i) : i_(i), has_int_(true) {}

0 commit comments

Comments
 (0)