status_matchers.h (8331B)
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 33 inline const absl::Status& GetStatus(const absl::Status& status) { 34 return status; 35 } 36 37 template <typename T> 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> 55 explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) 56 : inner_matcher_(::testing::SafeMatcherCast<const value_type&>( 57 std::forward<InnerMatcher>(inner_matcher))) {} 58 59 void DescribeTo(std::ostream* os) const override { 60 *os << "is OK and has a value that "; 61 inner_matcher_.DescribeTo(os); 62 } 63 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 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: 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: 120 /*implicit*/ StatusCode(int code) // NOLINT 121 : code_(static_cast<::absl::StatusCode>(code)) {} 122 /*implicit*/ StatusCode(::absl::StatusCode code) : code_(code) {} // NOLINT 123 124 explicit operator int() const { return static_cast<int>(code_); } 125 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: 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: 171 explicit MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl) 172 : common_impl_(std::move(common_impl)) {} 173 174 void DescribeTo(std::ostream* os) const override { 175 common_impl_.DescribeTo(os); 176 } 177 178 void DescribeNegationTo(std::ostream* os) const override { 179 common_impl_.DescribeNegationTo(os); 180 } 181 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> 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: 223 void DescribeTo(std::ostream* os) const override { *os << "is OK"; } 224 void DescribeNegationTo(std::ostream* os) const override { 225 *os << "is not OK"; 226 } 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_