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