1 // Copyright 2024 The Abseil Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_
16 #define ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_
17
18 #include <ostream> // NOLINT
19 #include <string>
20 #include <type_traits>
21 #include <utility>
22
23 #include "gmock/gmock.h" // gmock_for_status_matchers.h
24 #include "absl/base/config.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/string_view.h"
28
29 namespace absl_testing {
30 ABSL_NAMESPACE_BEGIN
31 namespace status_internal {
32
GetStatus(const absl::Status & status)33 inline const absl::Status& GetStatus(const absl::Status& status) {
34 return status;
35 }
36
37 template <typename T>
GetStatus(const absl::StatusOr<T> & status)38 inline const absl::Status& GetStatus(const absl::StatusOr<T>& status) {
39 return status.status();
40 }
41
42 ////////////////////////////////////////////////////////////
43 // Implementation of IsOkAndHolds().
44
45 // Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a
46 // reference to StatusOr<T>.
47 template <typename StatusOrType>
48 class IsOkAndHoldsMatcherImpl
49 : public ::testing::MatcherInterface<StatusOrType> {
50 public:
51 typedef
52 typename std::remove_reference<StatusOrType>::type::value_type value_type;
53
54 template <typename InnerMatcher>
IsOkAndHoldsMatcherImpl(InnerMatcher && inner_matcher)55 explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
56 : inner_matcher_(::testing::SafeMatcherCast<const value_type&>(
57 std::forward<InnerMatcher>(inner_matcher))) {}
58
DescribeTo(std::ostream * os)59 void DescribeTo(std::ostream* os) const override {
60 *os << "is OK and has a value that ";
61 inner_matcher_.DescribeTo(os);
62 }
63
DescribeNegationTo(std::ostream * os)64 void DescribeNegationTo(std::ostream* os) const override {
65 *os << "isn't OK or has a value that ";
66 inner_matcher_.DescribeNegationTo(os);
67 }
68
MatchAndExplain(StatusOrType actual_value,::testing::MatchResultListener * result_listener)69 bool MatchAndExplain(
70 StatusOrType actual_value,
71 ::testing::MatchResultListener* result_listener) const override {
72 if (!GetStatus(actual_value).ok()) {
73 *result_listener << "which has status " << GetStatus(actual_value);
74 return false;
75 }
76
77 // Call through to the inner matcher.
78 return inner_matcher_.MatchAndExplain(*actual_value, result_listener);
79 }
80
81 private:
82 const ::testing::Matcher<const value_type&> inner_matcher_;
83 };
84
85 // Implements IsOkAndHolds(m) as a polymorphic matcher.
86 template <typename InnerMatcher>
87 class IsOkAndHoldsMatcher {
88 public:
IsOkAndHoldsMatcher(InnerMatcher inner_matcher)89 explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
90 : inner_matcher_(std::forward<InnerMatcher>(inner_matcher)) {}
91
92 // Converts this polymorphic matcher to a monomorphic matcher of the
93 // given type. StatusOrType can be either StatusOr<T> or a
94 // reference to StatusOr<T>.
95 template <typename StatusOrType>
96 operator ::testing::Matcher<StatusOrType>() const { // NOLINT
97 return ::testing::Matcher<StatusOrType>(
98 new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_));
99 }
100
101 private:
102 const InnerMatcher inner_matcher_;
103 };
104
105 ////////////////////////////////////////////////////////////
106 // Implementation of StatusIs().
107
108 // `StatusCode` is implicitly convertible from `int`, `absl::StatusCode`, and
109 // is explicitly convertible to these types as well.
110 //
111 // We need this class because `absl::StatusCode` (as a scoped enum) is not
112 // implicitly convertible to `int`. In order to handle use cases like
113 // ```
114 // StatusIs(Anyof(absl::StatusCode::kUnknown, absl::StatusCode::kCancelled))
115 // ```
116 // which uses polymorphic matchers, we need to unify the interfaces into
117 // `Matcher<StatusCode>`.
118 class StatusCode {
119 public:
StatusCode(int code)120 /*implicit*/ StatusCode(int code) // NOLINT
121 : code_(static_cast<::absl::StatusCode>(code)) {}
StatusCode(::absl::StatusCode code)122 /*implicit*/ StatusCode(::absl::StatusCode code) : code_(code) {} // NOLINT
123
124 explicit operator int() const { return static_cast<int>(code_); }
125
PrintTo(const StatusCode & code,std::ostream * os)126 friend inline void PrintTo(const StatusCode& code, std::ostream* os) {
127 // TODO(b/321095377): Change this to print the status code as a string.
128 *os << static_cast<int>(code);
129 }
130
131 private:
132 ::absl::StatusCode code_;
133 };
134
135 // Relational operators to handle matchers like Eq, Lt, etc..
136 inline bool operator==(const StatusCode& lhs, const StatusCode& rhs) {
137 return static_cast<int>(lhs) == static_cast<int>(rhs);
138 }
139 inline bool operator!=(const StatusCode& lhs, const StatusCode& rhs) {
140 return static_cast<int>(lhs) != static_cast<int>(rhs);
141 }
142
143 // StatusIs() is a polymorphic matcher. This class is the common
144 // implementation of it shared by all types T where StatusIs() can be
145 // used as a Matcher<T>.
146 class StatusIsMatcherCommonImpl {
147 public:
StatusIsMatcherCommonImpl(::testing::Matcher<StatusCode> code_matcher,::testing::Matcher<absl::string_view> message_matcher)148 StatusIsMatcherCommonImpl(
149 ::testing::Matcher<StatusCode> code_matcher,
150 ::testing::Matcher<absl::string_view> message_matcher)
151 : code_matcher_(std::move(code_matcher)),
152 message_matcher_(std::move(message_matcher)) {}
153
154 void DescribeTo(std::ostream* os) const;
155
156 void DescribeNegationTo(std::ostream* os) const;
157
158 bool MatchAndExplain(const absl::Status& status,
159 ::testing::MatchResultListener* result_listener) const;
160
161 private:
162 const ::testing::Matcher<StatusCode> code_matcher_;
163 const ::testing::Matcher<absl::string_view> message_matcher_;
164 };
165
166 // Monomorphic implementation of matcher StatusIs() for a given type
167 // T. T can be Status, StatusOr<>, or a reference to either of them.
168 template <typename T>
169 class MonoStatusIsMatcherImpl : public ::testing::MatcherInterface<T> {
170 public:
MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl)171 explicit MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl)
172 : common_impl_(std::move(common_impl)) {}
173
DescribeTo(std::ostream * os)174 void DescribeTo(std::ostream* os) const override {
175 common_impl_.DescribeTo(os);
176 }
177
DescribeNegationTo(std::ostream * os)178 void DescribeNegationTo(std::ostream* os) const override {
179 common_impl_.DescribeNegationTo(os);
180 }
181
MatchAndExplain(T actual_value,::testing::MatchResultListener * result_listener)182 bool MatchAndExplain(
183 T actual_value,
184 ::testing::MatchResultListener* result_listener) const override {
185 return common_impl_.MatchAndExplain(GetStatus(actual_value),
186 result_listener);
187 }
188
189 private:
190 StatusIsMatcherCommonImpl common_impl_;
191 };
192
193 // Implements StatusIs() as a polymorphic matcher.
194 class StatusIsMatcher {
195 public:
196 template <typename StatusCodeMatcher, typename StatusMessageMatcher>
StatusIsMatcher(StatusCodeMatcher && code_matcher,StatusMessageMatcher && message_matcher)197 StatusIsMatcher(StatusCodeMatcher&& code_matcher,
198 StatusMessageMatcher&& message_matcher)
199 : common_impl_(::testing::MatcherCast<StatusCode>(
200 std::forward<StatusCodeMatcher>(code_matcher)),
201 ::testing::MatcherCast<absl::string_view>(
202 std::forward<StatusMessageMatcher>(message_matcher))) {
203 }
204
205 // Converts this polymorphic matcher to a monomorphic matcher of the
206 // given type. T can be StatusOr<>, Status, or a reference to
207 // either of them.
208 template <typename T>
209 /*implicit*/ operator ::testing::Matcher<T>() const { // NOLINT
210 return ::testing::Matcher<T>(
211 new MonoStatusIsMatcherImpl<const T&>(common_impl_));
212 }
213
214 private:
215 const StatusIsMatcherCommonImpl common_impl_;
216 };
217
218 // Monomorphic implementation of matcher IsOk() for a given type T.
219 // T can be Status, StatusOr<>, or a reference to either of them.
220 template <typename T>
221 class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> {
222 public:
DescribeTo(std::ostream * os)223 void DescribeTo(std::ostream* os) const override { *os << "is OK"; }
DescribeNegationTo(std::ostream * os)224 void DescribeNegationTo(std::ostream* os) const override {
225 *os << "is not OK";
226 }
MatchAndExplain(T actual_value,::testing::MatchResultListener *)227 bool MatchAndExplain(T actual_value,
228 ::testing::MatchResultListener*) const override {
229 return GetStatus(actual_value).ok();
230 }
231 };
232
233 // Implements IsOk() as a polymorphic matcher.
234 class IsOkMatcher {
235 public:
236 template <typename T>
237 /*implicit*/ operator ::testing::Matcher<T>() const { // NOLINT
238 return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<const T&>());
239 }
240 };
241
242 } // namespace status_internal
243 ABSL_NAMESPACE_END
244 } // namespace absl_testing
245
246 #endif // ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_
247