xref: /aosp_15_r20/external/cronet/base/test/gmock_expected_support.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_TEST_GMOCK_EXPECTED_SUPPORT_H_
6 #define BASE_TEST_GMOCK_EXPECTED_SUPPORT_H_
7 
8 #include <ostream>
9 #include <string>
10 #include <type_traits>
11 #include <utility>
12 
13 #include "base/strings/strcat.h"
14 #include "base/strings/to_string.h"
15 #include "base/types/expected.h"
16 #include "base/types/expected_internal.h"
17 #include "base/types/expected_macros.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 namespace base::test {
22 
23 namespace internal {
24 
25 // `HasVoidValueType<T>` is true iff `T` satisfies
26 // `base::internal::IsExpected<T>` and `T`'s `value_type` is `void`.
27 template <typename T, typename = void>
28 constexpr bool HasVoidValueType = false;
29 template <typename T>
30 constexpr bool
31     HasVoidValueType<T, std::enable_if_t<base::internal::IsExpected<T>>> =
32         std::is_void_v<typename std::remove_cvref_t<T>::value_type>;
33 
34 // Implementation for matcher `HasValue`.
35 class HasValueMatcher {
36  public:
37   HasValueMatcher() = default;
38 
39   template <typename T>
40   operator ::testing::Matcher<T>() const {  // NOLINT
41     return ::testing::Matcher<T>(new Impl<const T&>());
42   }
43 
44  private:
45   // Reject instantiation with types that do not satisfy
46   // `base::internal::IsExpected<T>`.
47   template <typename T, typename = void>
48   class Impl {
49     static_assert(base::internal::IsExpected<T>,
50                   "Must be used with base::expected<T, E>");
51   };
52 
53   template <typename T>
54   class Impl<T, std::enable_if_t<base::internal::IsExpected<T>>>
55       : public ::testing::MatcherInterface<T> {
56    public:
57     Impl() = default;
58 
DescribeTo(std::ostream * os)59     void DescribeTo(std::ostream* os) const override {
60       *os << "is an 'expected' type with a value";
61     }
62 
DescribeNegationTo(std::ostream * os)63     void DescribeNegationTo(std::ostream* os) const override {
64       *os << "is an 'expected' type with an error";
65     }
66 
MatchAndExplain(T actual_value,::testing::MatchResultListener * listener)67     bool MatchAndExplain(
68         T actual_value,
69         ::testing::MatchResultListener* listener) const override {
70       if (!actual_value.has_value()) {
71         *listener << "which has the error " << ToString(actual_value.error());
72       }
73       return actual_value.has_value();
74     }
75   };
76 };
77 
78 // Implementation for matcher `ValueIs`.
79 template <typename T>
80 class ValueIsMatcher {
81  public:
ValueIsMatcher(T matcher)82   explicit ValueIsMatcher(T matcher) : matcher_(std::move(matcher)) {}
83 
84   template <typename U>
85   operator ::testing::Matcher<U>() const {  // NOLINT
86     return ::testing::Matcher<U>(new Impl<const U&>(matcher_));
87   }
88 
89  private:
90   // Reject instantiation with types that do not satisfy
91   // `base::internal::IsExpected<U> && !HasVoidValueType<U>`.
92   template <typename U, typename = void>
93   class Impl {
94     static_assert(base::internal::IsExpected<U>,
95                   "Must be used with base::expected<T, E>");
96     static_assert(!HasVoidValueType<U>,
97                   "expected object must have non-void value type");
98   };
99 
100   template <typename U>
101   class Impl<
102       U,
103       std::enable_if_t<base::internal::IsExpected<U> && !HasVoidValueType<U>>>
104       : public ::testing::MatcherInterface<U> {
105    public:
Impl(const T & matcher)106     explicit Impl(const T& matcher)
107         : matcher_(::testing::SafeMatcherCast<const V&>(matcher)) {}
108 
DescribeTo(std::ostream * os)109     void DescribeTo(std::ostream* os) const override {
110       *os << "is an 'expected' type with a value which ";
111       matcher_.DescribeTo(os);
112     }
113 
DescribeNegationTo(std::ostream * os)114     void DescribeNegationTo(std::ostream* os) const override {
115       *os << "is an 'expected' type with an error or a value which ";
116       matcher_.DescribeNegationTo(os);
117     }
118 
MatchAndExplain(U actual_value,::testing::MatchResultListener * listener)119     bool MatchAndExplain(
120         U actual_value,
121         ::testing::MatchResultListener* listener) const override {
122       if (!actual_value.has_value()) {
123         *listener << "which has the error " << ToString(actual_value.error());
124         return false;
125       }
126 
127       ::testing::StringMatchResultListener inner_listener;
128       const bool match =
129           matcher_.MatchAndExplain(actual_value.value(), &inner_listener);
130       const std::string explanation = inner_listener.str();
131       if (!explanation.empty()) {
132         *listener << "which has the value " << ToString(actual_value.value())
133                   << ", " << explanation;
134       }
135       return match;
136     }
137 
138    private:
139     using V = typename std::remove_cvref_t<U>::value_type;
140 
141     const ::testing::Matcher<const V&> matcher_;
142   };
143 
144   const T matcher_;
145 };
146 
147 // Implementation for matcher `ErrorIs`.
148 template <typename T>
149 class ErrorIsMatcher {
150  public:
ErrorIsMatcher(T matcher)151   explicit ErrorIsMatcher(T matcher) : matcher_(std::move(matcher)) {}
152 
153   template <typename U>
154   operator ::testing::Matcher<U>() const {  // NOLINT
155     return ::testing::Matcher<U>(new Impl<const U&>(matcher_));
156   }
157 
158  private:
159   // Reject instantiation with types that do not satisfy
160   // `base::internal::IsExpected<U>`.
161   template <typename U, typename = void>
162   class Impl {
163     static_assert(base::internal::IsExpected<U>,
164                   "Must be used with base::expected<T, E>");
165   };
166 
167   template <typename U>
168   class Impl<U, std::enable_if_t<base::internal::IsExpected<U>>>
169       : public ::testing::MatcherInterface<U> {
170    public:
Impl(const T & matcher)171     explicit Impl(const T& matcher)
172         : matcher_(::testing::SafeMatcherCast<const E&>(matcher)) {}
173 
DescribeTo(std::ostream * os)174     void DescribeTo(std::ostream* os) const override {
175       *os << "is an 'expected' type with an error which ";
176       matcher_.DescribeTo(os);
177     }
178 
DescribeNegationTo(std::ostream * os)179     void DescribeNegationTo(std::ostream* os) const override {
180       *os << "is an 'expected' type with a value or an error which ";
181       matcher_.DescribeNegationTo(os);
182     }
183 
MatchAndExplain(U actual_value,::testing::MatchResultListener * listener)184     bool MatchAndExplain(
185         U actual_value,
186         ::testing::MatchResultListener* listener) const override {
187       if (actual_value.has_value()) {
188         if constexpr (HasVoidValueType<U>) {
189           *listener << "which has a value";
190         } else {
191           *listener << "which has the value " << ToString(actual_value.value());
192         }
193         return false;
194       }
195 
196       ::testing::StringMatchResultListener inner_listener;
197       const bool match =
198           matcher_.MatchAndExplain(actual_value.error(), &inner_listener);
199       const std::string explanation = inner_listener.str();
200       if (!explanation.empty()) {
201         *listener << "which has the error " << ToString(actual_value.error())
202                   << ", " << explanation;
203       }
204       return match;
205     }
206 
207    private:
208     using E = typename std::remove_cvref_t<U>::error_type;
209 
210     const ::testing::Matcher<const E&> matcher_;
211   };
212 
213  private:
214   const T matcher_;
215 };
216 
217 }  // namespace internal
218 
219 // Returns a gMock matcher that matches an `expected<T, E>` which has a value.
HasValue()220 inline internal::HasValueMatcher HasValue() {
221   return internal::HasValueMatcher();
222 }
223 
224 // Returns a gMock matcher that matches an `expected<T, E>` which has a non-void
225 // value which matches the inner matcher.
226 template <typename T>
ValueIs(T && matcher)227 inline internal::ValueIsMatcher<typename std::decay_t<T>> ValueIs(T&& matcher) {
228   return internal::ValueIsMatcher<typename std::decay_t<T>>(
229       std::forward<T>(matcher));
230 }
231 
232 // Returns a gMock matcher that matches an `expected<T, E>` which has an error
233 // which matches the inner matcher.
234 template <typename T>
ErrorIs(T && matcher)235 inline internal::ErrorIsMatcher<typename std::decay_t<T>> ErrorIs(T&& matcher) {
236   return internal::ErrorIsMatcher<typename std::decay_t<T>>(
237       std::forward<T>(matcher));
238 }
239 
240 }  // namespace base::test
241 
242 // Executes an expression that returns an `expected<T, E>` or some subclass
243 // thereof, and assigns the contained `T` to `lhs` if the result is a value. If
244 // the result is an error, generates a test failure and returns from the current
245 // function, which must have a `void` return type. For more usage examples and
246 // caveats, see the documentation for `ASSIGN_OR_RETURN`.
247 //
248 // Example: Declaring and initializing a new value:
249 //   ASSERT_OK_AND_ASSIGN(ValueType value, MaybeGetValue(arg));
250 //
251 // Example: Assigning to an existing value:
252 //   ValueType value;
253 //   ASSERT_OK_AND_ASSIGN(value, MaybeGetValue(arg));
254 #define ASSERT_OK_AND_ASSIGN(lhs, rexpr)                               \
255   ASSIGN_OR_RETURN(lhs, rexpr, [](const auto& e) {                     \
256     return GTEST_MESSAGE_(                                             \
257         base::StrCat({#rexpr, " returned error: ", base::ToString(e)}) \
258             .c_str(),                                                  \
259         ::testing::TestPartResult::kFatalFailure);                     \
260   })
261 
262 namespace base {
263 template <typename T, typename E>
PrintTo(const expected<T,E> & expected,::std::ostream * os)264 void PrintTo(const expected<T, E>& expected, ::std::ostream* os) {
265   *os << expected.ToString();
266 }
267 
268 template <typename T>
PrintTo(const ok<T> & a,::std::ostream * os)269 void PrintTo(const ok<T>& a, ::std::ostream* os) {
270   *os << a.ToString();
271 }
272 
273 template <typename T>
PrintTo(const unexpected<T> & a,::std::ostream * os)274 void PrintTo(const unexpected<T>& a, ::std::ostream* os) {
275   *os << a.ToString();
276 }
277 }  // namespace base
278 
279 #endif  // BASE_TEST_GMOCK_EXPECTED_SUPPORT_H_
280