xref: /aosp_15_r20/external/abseil-cpp/absl/status/internal/status_matchers.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
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