• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>
28 concept HasVoidValueType =
29     base::internal::IsExpected<T> &&
30     std::is_void_v<typename std::remove_cvref_t<T>::value_type>;
31 
32 // Implementation for matcher `HasValue`.
33 class HasValueMatcher {
34  public:
35   HasValueMatcher() = default;
36 
37   template <typename T>
38   operator ::testing::Matcher<T>() const {  // NOLINT
39     return ::testing::Matcher<T>(new Impl<const T&>());
40   }
41 
42  private:
43   template <typename T>
requires(base::internal::IsExpected<T>)44     requires(base::internal::IsExpected<T>)
45   class Impl : public ::testing::MatcherInterface<T> {
46    public:
47     Impl() = default;
48 
49     void DescribeTo(std::ostream* os) const override {
50       *os << "is an 'expected' type with a value";
51     }
52 
53     void DescribeNegationTo(std::ostream* os) const override {
54       *os << "is an 'expected' type with an error";
55     }
56 
57     bool MatchAndExplain(
58         T actual_value,
59         ::testing::MatchResultListener* listener) const override {
60       if (!actual_value.has_value()) {
61         *listener << "which has the error " << ToString(actual_value.error());
62       }
63       return actual_value.has_value();
64     }
65   };
66 };
67 
68 // Implementation for matcher `ValueIs`.
69 template <typename T>
70 class ValueIsMatcher {
71  public:
ValueIsMatcher(T matcher)72   explicit ValueIsMatcher(T matcher) : matcher_(std::move(matcher)) {}
73 
74   template <typename U>
75   operator ::testing::Matcher<U>() const {  // NOLINT
76     return ::testing::Matcher<U>(new Impl<const U&>(matcher_));
77   }
78 
79  private:
80   template <typename U>
81     requires(base::internal::IsExpected<U> && !HasVoidValueType<U>)
82   class Impl : public ::testing::MatcherInterface<U> {
83    public:
Impl(const T & matcher)84     explicit Impl(const T& matcher)
85         : matcher_(::testing::SafeMatcherCast<const V&>(matcher)) {}
86 
DescribeTo(std::ostream * os)87     void DescribeTo(std::ostream* os) const override {
88       *os << "is an 'expected' type with a value which ";
89       matcher_.DescribeTo(os);
90     }
91 
DescribeNegationTo(std::ostream * os)92     void DescribeNegationTo(std::ostream* os) const override {
93       *os << "is an 'expected' type with an error or a value which ";
94       matcher_.DescribeNegationTo(os);
95     }
96 
MatchAndExplain(U actual_value,::testing::MatchResultListener * listener)97     bool MatchAndExplain(
98         U actual_value,
99         ::testing::MatchResultListener* listener) const override {
100       if (!actual_value.has_value()) {
101         *listener << "which has the error " << ToString(actual_value.error());
102         return false;
103       }
104 
105       ::testing::StringMatchResultListener inner_listener;
106       const bool match =
107           matcher_.MatchAndExplain(actual_value.value(), &inner_listener);
108       const std::string explanation = inner_listener.str();
109       if (!explanation.empty()) {
110         *listener << "which has the value " << ToString(actual_value.value())
111                   << ", " << explanation;
112       }
113       return match;
114     }
115 
116    private:
117     using V = typename std::remove_cvref_t<U>::value_type;
118 
119     const ::testing::Matcher<const V&> matcher_;
120   };
121 
122   const T matcher_;
123 };
124 
125 // Implementation for matcher `ErrorIs`.
126 template <typename T>
127 class ErrorIsMatcher {
128  public:
ErrorIsMatcher(T matcher)129   explicit ErrorIsMatcher(T matcher) : matcher_(std::move(matcher)) {}
130 
131   template <typename U>
132   operator ::testing::Matcher<U>() const {  // NOLINT
133     return ::testing::Matcher<U>(new Impl<const U&>(matcher_));
134   }
135 
136  private:
137   template <typename U>
requires(base::internal::IsExpected<U>)138     requires(base::internal::IsExpected<U>)
139   class Impl : public ::testing::MatcherInterface<U> {
140    public:
141     explicit Impl(const T& matcher)
142         : matcher_(::testing::SafeMatcherCast<const E&>(matcher)) {}
143 
144     void DescribeTo(std::ostream* os) const override {
145       *os << "is an 'expected' type with an error which ";
146       matcher_.DescribeTo(os);
147     }
148 
149     void DescribeNegationTo(std::ostream* os) const override {
150       *os << "is an 'expected' type with a value or an error which ";
151       matcher_.DescribeNegationTo(os);
152     }
153 
154     bool MatchAndExplain(
155         U actual_value,
156         ::testing::MatchResultListener* listener) const override {
157       if (actual_value.has_value()) {
158         if constexpr (HasVoidValueType<U>) {
159           *listener << "which has a value";
160         } else {
161           *listener << "which has the value " << ToString(actual_value.value());
162         }
163         return false;
164       }
165 
166       ::testing::StringMatchResultListener inner_listener;
167       const bool match =
168           matcher_.MatchAndExplain(actual_value.error(), &inner_listener);
169       const std::string explanation = inner_listener.str();
170       if (!explanation.empty()) {
171         *listener << "which has the error " << ToString(actual_value.error())
172                   << ", " << explanation;
173       }
174       return match;
175     }
176 
177    private:
178     using E = typename std::remove_cvref_t<U>::error_type;
179 
180     const ::testing::Matcher<const E&> matcher_;
181   };
182 
183  private:
184   const T matcher_;
185 };
186 
187 }  // namespace internal
188 
189 // Returns a gMock matcher that matches an `expected<T, E>` which has a value.
HasValue()190 inline internal::HasValueMatcher HasValue() {
191   return internal::HasValueMatcher();
192 }
193 
194 // Returns a gMock matcher that matches an `expected<T, E>` which has a non-void
195 // value which matches the inner matcher.
196 template <typename T>
ValueIs(T && matcher)197 inline internal::ValueIsMatcher<typename std::decay_t<T>> ValueIs(T&& matcher) {
198   return internal::ValueIsMatcher<typename std::decay_t<T>>(
199       std::forward<T>(matcher));
200 }
201 
202 // Returns a gMock matcher that matches an `expected<T, E>` which has an error
203 // which matches the inner matcher.
204 template <typename T>
ErrorIs(T && matcher)205 inline internal::ErrorIsMatcher<typename std::decay_t<T>> ErrorIs(T&& matcher) {
206   return internal::ErrorIsMatcher<typename std::decay_t<T>>(
207       std::forward<T>(matcher));
208 }
209 
210 }  // namespace base::test
211 
212 // Executes an expression that returns an `expected<T, E>` or some subclass
213 // thereof, and assigns the contained `T` to `lhs` if the result is a value. If
214 // the result is an error, generates a test failure and returns from the current
215 // function, which must have a `void` return type. For more usage examples and
216 // caveats, see the documentation for `ASSIGN_OR_RETURN`.
217 //
218 // Example: Declaring and initializing a new value:
219 //   ASSERT_OK_AND_ASSIGN(ValueType value, MaybeGetValue(arg));
220 //
221 // Example: Assigning to an existing value:
222 //   ValueType value;
223 //   ASSERT_OK_AND_ASSIGN(value, MaybeGetValue(arg));
224 #define ASSERT_OK_AND_ASSIGN(lhs, rexpr)                               \
225   ASSIGN_OR_RETURN(lhs, rexpr, [](const auto& e) {                     \
226     return GTEST_MESSAGE_(                                             \
227         base::StrCat({#rexpr, " returned error: ", base::ToString(e)}) \
228             .c_str(),                                                  \
229         ::testing::TestPartResult::kFatalFailure);                     \
230   })
231 
232 namespace base {
233 template <typename T, typename E>
PrintTo(const expected<T,E> & expected,::std::ostream * os)234 void PrintTo(const expected<T, E>& expected, ::std::ostream* os) {
235   *os << expected.ToString();
236 }
237 
238 template <typename T>
PrintTo(const ok<T> & a,::std::ostream * os)239 void PrintTo(const ok<T>& a, ::std::ostream* os) {
240   *os << a.ToString();
241 }
242 
243 template <typename T>
PrintTo(const unexpected<T> & a,::std::ostream * os)244 void PrintTo(const unexpected<T>& a, ::std::ostream* os) {
245   *os << a.ToString();
246 }
247 }  // namespace base
248 
249 #endif  // BASE_TEST_GMOCK_EXPECTED_SUPPORT_H_
250