/* * Copyright 2019 Google LLC. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Copyright 2020 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PRIVATE_JOIN_AND_COMPUTE_UTIL_STATUS_MATCHERS_H_ #define PRIVATE_JOIN_AND_COMPUTE_UTIL_STATUS_MATCHERS_H_ #include #include #include #include "private_join_and_compute/util/status.inc" namespace private_join_and_compute { namespace testing { #ifdef GTEST_HAS_STATUS_MATCHERS using ::testing::status::IsOk; using ::testing::status::IsOkAndHolds; using ::testing::status::StatusIs; #else // GTEST_HAS_STATUS_MATCHERS namespace internal { // This function and its overload allow the same matcher to be used for Status // and StatusOr tests. inline Status GetStatus(const Status& status) { return status; } template inline Status GetStatus(const StatusOr& statusor) { return statusor.status(); } template class StatusIsImpl : public ::testing::MatcherInterface { public: StatusIsImpl(const ::testing::Matcher& code, const ::testing::Matcher& message) : code_(code), message_(message) {} bool MatchAndExplain( StatusType status, ::testing::MatchResultListener* listener) const override { ::testing::StringMatchResultListener str_listener; Status real_status = GetStatus(status); if (!code_.MatchAndExplain(real_status.code(), &str_listener)) { *listener << str_listener.str(); return false; } if (!message_.MatchAndExplain( static_cast(real_status.message()), &str_listener)) { *listener << str_listener.str(); return false; } return true; } void DescribeTo(std::ostream* os) const override { *os << "has a status code that "; code_.DescribeTo(os); *os << " and a message that "; message_.DescribeTo(os); } void DescribeNegationTo(std::ostream* os) const override { *os << "has a status code that "; code_.DescribeNegationTo(os); *os << " and a message that "; message_.DescribeNegationTo(os); } private: ::testing::Matcher code_; ::testing::Matcher message_; }; class StatusIsPoly { public: StatusIsPoly(::testing::Matcher&& code, ::testing::Matcher&& message) : code_(code), message_(message) {} // Converts this polymorphic matcher to a monomorphic matcher. template operator ::testing::Matcher() const { return ::testing::Matcher( new StatusIsImpl(code_, message_)); } private: ::testing::Matcher code_; ::testing::Matcher message_; }; } // namespace internal // This function allows us to avoid a template parameter when writing tests, so // that we can transparently test both Status and StatusOr returns. inline internal::StatusIsPoly StatusIs( ::testing::Matcher&& code, ::testing::Matcher&& message) { return internal::StatusIsPoly( std::forward< ::testing::Matcher >(code), std::forward< ::testing::Matcher >(message)); } // Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a // reference to StatusOr. template class IsOkAndHoldsMatcherImpl : public ::testing::MatcherInterface { public: typedef typename std::remove_reference::type::value_type value_type; template explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) : inner_matcher_(::testing::SafeMatcherCast( std::forward(inner_matcher))) {} void DescribeTo(std::ostream* os) const override { *os << "is OK and has a value that "; inner_matcher_.DescribeTo(os); } void DescribeNegationTo(std::ostream* os) const override { *os << "isn't OK or has a value that "; inner_matcher_.DescribeNegationTo(os); } bool MatchAndExplain( StatusOrType actual_value, ::testing::MatchResultListener* result_listener) const override { if (!actual_value.ok()) { *result_listener << "which has status " << actual_value.status(); return false; } ::testing::StringMatchResultListener inner_listener; const bool matches = inner_matcher_.MatchAndExplain(*actual_value, &inner_listener); const std::string inner_explanation = inner_listener.str(); if (!inner_explanation.empty()) { *result_listener << "which contains value " << ::testing::PrintToString(*actual_value) << ", " << inner_explanation; } return matches; } private: const ::testing::Matcher inner_matcher_; }; // Implements IsOkAndHolds(m) as a polymorphic matcher. template class IsOkAndHoldsMatcher { public: explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher) : inner_matcher_(std::move(inner_matcher)) {} // Converts this polymorphic matcher to a monomorphic matcher of the // given type. StatusOrType can be either StatusOr or a // reference to StatusOr. template operator ::testing::Matcher() const { // NOLINT return ::testing::Matcher( new IsOkAndHoldsMatcherImpl(inner_matcher_)); } private: const InnerMatcher inner_matcher_; }; // Monomorphic implementation of matcher IsOk() for a given type T. // T can be Status, StatusOr<>, or a reference to either of them. template class MonoIsOkMatcherImpl : public ::testing::MatcherInterface { public: void DescribeTo(std::ostream* os) const override { *os << "is OK"; } void DescribeNegationTo(std::ostream* os) const override { *os << "is not OK"; } bool MatchAndExplain(T actual_value, ::testing::MatchResultListener*) const override { return GetStatus(actual_value).ok(); } }; // Implements IsOk() as a polymorphic matcher. class IsOkMatcher { public: template operator ::testing::Matcher() const { // NOLINT return ::testing::Matcher(new MonoIsOkMatcherImpl()); } }; // Returns a gMock matcher that matches a StatusOr<> whose status is // OK and whose value matches the inner matcher. template IsOkAndHoldsMatcher::type> IsOkAndHolds( InnerMatcher&& inner_matcher) { return IsOkAndHoldsMatcher::type>( std::forward(inner_matcher)); } // Returns a gMock matcher that matches a Status or StatusOr<> which is OK. inline IsOkMatcher IsOk() { return IsOkMatcher(); } #endif // GTEST_HAS_STATUS_MATCHERS } // namespace testing } // namespace private_join_and_compute #endif // PRIVATE_JOIN_AND_COMPUTE_UTIL_STATUS_MATCHERS_H_