blob: 3ea2a7edf34ef978b39958e35238c0da85653591 [file] [log] [blame]
Peter Kastingd0546c62023-08-04 21:48:481// 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
21namespace base::test {
22
23namespace internal {
24
25// `HasVoidValueType<T>` is true iff `T` satisfies
26// `base::internal::IsExpected<T>` and `T`'s `value_type` is `void`.
27template <typename T, typename = void>
28constexpr bool HasVoidValueType = false;
29template <typename T>
30constexpr 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`.
35class 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
59 void DescribeTo(std::ostream* os) const override {
60 *os << "is an 'expected' type with a value";
61 }
62
63 void DescribeNegationTo(std::ostream* os) const override {
64 *os << "is an 'expected' type with an error";
65 }
66
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`.
79template <typename T>
80class ValueIsMatcher {
81 public:
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:
106 explicit Impl(const T& matcher)
107 : matcher_(::testing::SafeMatcherCast<const V&>(matcher)) {}
108
109 void DescribeTo(std::ostream* os) const override {
110 *os << "is an 'expected' type with a value which ";
111 matcher_.DescribeTo(os);
112 }
113
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
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`.
148template <typename T>
149class ErrorIsMatcher {
150 public:
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:
171 explicit Impl(const T& matcher)
172 : matcher_(::testing::SafeMatcherCast<const E&>(matcher)) {}
173
174 void DescribeTo(std::ostream* os) const override {
175 *os << "is an 'expected' type with an error which ";
176 matcher_.DescribeTo(os);
177 }
178
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
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.
220inline 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.
226template <typename T>
227inline 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.
234template <typename T>
235inline 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
Daniel Murphy31632c82023-11-03 15:52:26262namespace base {
263template <typename T, typename E>
264void PrintTo(const expected<T, E>& expected, ::std::ostream* os) {
265 *os << expected.ToString();
266}
267
268template <typename T>
269void PrintTo(const ok<T>& a, ::std::ostream* os) {
270 *os << a.ToString();
271}
272
273template <typename T>
274void PrintTo(const unexpected<T>& a, ::std::ostream* os) {
275 *os << a.ToString();
276}
277} // namespace base
278
Peter Kastingd0546c62023-08-04 21:48:48279#endif // BASE_TEST_GMOCK_EXPECTED_SUPPORT_H_