1 /* 2 * Copyright 2019 Google LLC. 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 16 /* 17 * Copyright 2020 Google LLC 18 * 19 * Licensed under the Apache License, Version 2.0 (the "License"); 20 * you may not use this file except in compliance with the License. 21 * You may obtain a copy of the License at 22 * 23 * http://www.apache.org/licenses/LICENSE-2.0 24 * 25 * Unless required by applicable law or agreed to in writing, software 26 * distributed under the License is distributed on an "AS IS" BASIS, 27 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 * See the License for the specific language governing permissions and 29 * limitations under the License. 30 */ 31 32 #ifndef PRIVATE_JOIN_AND_COMPUTE_UTIL_STATUS_MATCHERS_H_ 33 #define PRIVATE_JOIN_AND_COMPUTE_UTIL_STATUS_MATCHERS_H_ 34 35 #include <gmock/gmock.h> 36 37 #include <ostream> 38 #include <string> 39 40 #include "private_join_and_compute/util/status.inc" 41 42 namespace private_join_and_compute { 43 namespace testing { 44 45 #ifdef GTEST_HAS_STATUS_MATCHERS 46 47 using ::testing::status::IsOk; 48 using ::testing::status::IsOkAndHolds; 49 using ::testing::status::StatusIs; 50 51 #else // GTEST_HAS_STATUS_MATCHERS 52 53 namespace internal { 54 55 // This function and its overload allow the same matcher to be used for Status 56 // and StatusOr tests. 57 inline Status GetStatus(const Status& status) { return status; } 58 59 template <typename T> 60 inline Status GetStatus(const StatusOr<T>& statusor) { 61 return statusor.status(); 62 } 63 64 template <typename StatusType> 65 class StatusIsImpl : public ::testing::MatcherInterface<StatusType> { 66 public: 67 StatusIsImpl(const ::testing::Matcher<StatusCode>& code, 68 const ::testing::Matcher<const std::string&>& message) 69 : code_(code), message_(message) {} 70 71 bool MatchAndExplain( 72 StatusType status, 73 ::testing::MatchResultListener* listener) const override { 74 ::testing::StringMatchResultListener str_listener; 75 Status real_status = GetStatus(status); 76 if (!code_.MatchAndExplain(real_status.code(), &str_listener)) { 77 *listener << str_listener.str(); 78 return false; 79 } 80 if (!message_.MatchAndExplain( 81 static_cast<std::string>(real_status.message()), &str_listener)) { 82 *listener << str_listener.str(); 83 return false; 84 } 85 return true; 86 } 87 88 void DescribeTo(std::ostream* os) const override { 89 *os << "has a status code that "; 90 code_.DescribeTo(os); 91 *os << " and a message that "; 92 message_.DescribeTo(os); 93 } 94 95 void DescribeNegationTo(std::ostream* os) const override { 96 *os << "has a status code that "; 97 code_.DescribeNegationTo(os); 98 *os << " and a message that "; 99 message_.DescribeNegationTo(os); 100 } 101 102 private: 103 ::testing::Matcher<StatusCode> code_; 104 ::testing::Matcher<const std::string&> message_; 105 }; 106 107 class StatusIsPoly { 108 public: 109 StatusIsPoly(::testing::Matcher<StatusCode>&& code, 110 ::testing::Matcher<const std::string&>&& message) 111 : code_(code), message_(message) {} 112 113 // Converts this polymorphic matcher to a monomorphic matcher. 114 template <typename StatusType> 115 operator ::testing::Matcher<StatusType>() const { 116 return ::testing::Matcher<StatusType>( 117 new StatusIsImpl<StatusType>(code_, message_)); 118 } 119 120 private: 121 ::testing::Matcher<StatusCode> code_; 122 ::testing::Matcher<const std::string&> message_; 123 }; 124 125 } // namespace internal 126 127 // This function allows us to avoid a template parameter when writing tests, so 128 // that we can transparently test both Status and StatusOr returns. 129 inline internal::StatusIsPoly StatusIs( 130 ::testing::Matcher<StatusCode>&& code, 131 ::testing::Matcher<const std::string&>&& message) { 132 return internal::StatusIsPoly( 133 std::forward< ::testing::Matcher<StatusCode> >(code), 134 std::forward< ::testing::Matcher<const std::string&> >(message)); 135 } 136 137 // Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a 138 // reference to StatusOr<T>. 139 template <typename StatusOrType> 140 class IsOkAndHoldsMatcherImpl 141 : public ::testing::MatcherInterface<StatusOrType> { 142 public: 143 typedef 144 typename std::remove_reference<StatusOrType>::type::value_type value_type; 145 146 template <typename InnerMatcher> 147 explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) 148 : inner_matcher_(::testing::SafeMatcherCast<const value_type&>( 149 std::forward<InnerMatcher>(inner_matcher))) {} 150 151 void DescribeTo(std::ostream* os) const override { 152 *os << "is OK and has a value that "; 153 inner_matcher_.DescribeTo(os); 154 } 155 156 void DescribeNegationTo(std::ostream* os) const override { 157 *os << "isn't OK or has a value that "; 158 inner_matcher_.DescribeNegationTo(os); 159 } 160 161 bool MatchAndExplain( 162 StatusOrType actual_value, 163 ::testing::MatchResultListener* result_listener) const override { 164 if (!actual_value.ok()) { 165 *result_listener << "which has status " << actual_value.status(); 166 return false; 167 } 168 169 ::testing::StringMatchResultListener inner_listener; 170 const bool matches = 171 inner_matcher_.MatchAndExplain(*actual_value, &inner_listener); 172 const std::string inner_explanation = inner_listener.str(); 173 if (!inner_explanation.empty()) { 174 *result_listener << "which contains value " 175 << ::testing::PrintToString(*actual_value) << ", " 176 << inner_explanation; 177 } 178 return matches; 179 } 180 181 private: 182 const ::testing::Matcher<const value_type&> inner_matcher_; 183 }; 184 185 // Implements IsOkAndHolds(m) as a polymorphic matcher. 186 template <typename InnerMatcher> 187 class IsOkAndHoldsMatcher { 188 public: 189 explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher) 190 : inner_matcher_(std::move(inner_matcher)) {} 191 192 // Converts this polymorphic matcher to a monomorphic matcher of the 193 // given type. StatusOrType can be either StatusOr<T> or a 194 // reference to StatusOr<T>. 195 template <typename StatusOrType> 196 operator ::testing::Matcher<StatusOrType>() const { // NOLINT 197 return ::testing::Matcher<StatusOrType>( 198 new IsOkAndHoldsMatcherImpl<const StatusOrType&>(inner_matcher_)); 199 } 200 201 private: 202 const InnerMatcher inner_matcher_; 203 }; 204 205 // Monomorphic implementation of matcher IsOk() for a given type T. 206 // T can be Status, StatusOr<>, or a reference to either of them. 207 template <typename T> 208 class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> { 209 public: 210 void DescribeTo(std::ostream* os) const override { *os << "is OK"; } 211 void DescribeNegationTo(std::ostream* os) const override { 212 *os << "is not OK"; 213 } 214 bool MatchAndExplain(T actual_value, 215 ::testing::MatchResultListener*) const override { 216 return GetStatus(actual_value).ok(); 217 } 218 }; 219 220 // Implements IsOk() as a polymorphic matcher. 221 class IsOkMatcher { 222 public: 223 template <typename T> 224 operator ::testing::Matcher<T>() const { // NOLINT 225 return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<T>()); 226 } 227 }; 228 229 // Returns a gMock matcher that matches a StatusOr<> whose status is 230 // OK and whose value matches the inner matcher. 231 template <typename InnerMatcher> 232 IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> IsOkAndHolds( 233 InnerMatcher&& inner_matcher) { 234 return IsOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>( 235 std::forward<InnerMatcher>(inner_matcher)); 236 } 237 238 // Returns a gMock matcher that matches a Status or StatusOr<> which is OK. 239 inline IsOkMatcher IsOk() { return IsOkMatcher(); } 240 241 #endif // GTEST_HAS_STATUS_MATCHERS 242 243 } // namespace testing 244 } // namespace private_join_and_compute 245 246 #endif // PRIVATE_JOIN_AND_COMPUTE_UTIL_STATUS_MATCHERS_H_ 247