// Copyright 2024 The Abseil Authors // // 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. #ifndef ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_ #define ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_ #include // NOLINT #include #include #include #include "gmock/gmock.h" // gmock_for_status_matchers.h #include "absl/base/config.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" namespace absl_testing { ABSL_NAMESPACE_BEGIN namespace status_internal { inline const absl::Status& GetStatus(const absl::Status& status) { return status; } template inline const absl::Status& GetStatus(const absl::StatusOr& status) { return status.status(); } //////////////////////////////////////////////////////////// // Implementation of IsOkAndHolds(). // 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; } // Call through to the inner matcher. return inner_matcher_.MatchAndExplain(*actual_value, result_listener); } 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::forward(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_; }; //////////////////////////////////////////////////////////// // Implementation of StatusIs(). // `StatusCode` is implicitly convertible from `int`, `absl::StatusCode`, and // is explicitly convertible to these types as well. // // We need this class because `absl::StatusCode` (as a scoped enum) is not // implicitly convertible to `int`. In order to handle use cases like // ``` // StatusIs(Anyof(absl::StatusCode::kUnknown, absl::StatusCode::kCancelled)) // ``` // which uses polymorphic matchers, we need to unify the interfaces into // `Matcher`. class StatusCode { public: /*implicit*/ StatusCode(int code) // NOLINT : code_(static_cast<::absl::StatusCode>(code)) {} /*implicit*/ StatusCode(::absl::StatusCode code) : code_(code) {} // NOLINT explicit operator int() const { return static_cast(code_); } friend inline void PrintTo(const StatusCode& code, std::ostream* os) { // TODO(b/321095377): Change this to print the status code as a string. *os << static_cast(code); } private: ::absl::StatusCode code_; }; // Relational operators to handle matchers like Eq, Lt, etc.. inline bool operator==(const StatusCode& lhs, const StatusCode& rhs) { return static_cast(lhs) == static_cast(rhs); } inline bool operator!=(const StatusCode& lhs, const StatusCode& rhs) { return static_cast(lhs) != static_cast(rhs); } // StatusIs() is a polymorphic matcher. This class is the common // implementation of it shared by all types T where StatusIs() can be // used as a Matcher. class StatusIsMatcherCommonImpl { public: StatusIsMatcherCommonImpl( ::testing::Matcher code_matcher, ::testing::Matcher message_matcher) : code_matcher_(std::move(code_matcher)), message_matcher_(std::move(message_matcher)) {} void DescribeTo(std::ostream* os) const; void DescribeNegationTo(std::ostream* os) const; bool MatchAndExplain(const absl::Status& status, ::testing::MatchResultListener* result_listener) const; private: const ::testing::Matcher code_matcher_; const ::testing::Matcher message_matcher_; }; // Monomorphic implementation of matcher StatusIs() for a given type // T. T can be Status, StatusOr<>, or a reference to either of them. template class MonoStatusIsMatcherImpl : public ::testing::MatcherInterface { public: explicit MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl) : common_impl_(std::move(common_impl)) {} void DescribeTo(std::ostream* os) const override { common_impl_.DescribeTo(os); } void DescribeNegationTo(std::ostream* os) const override { common_impl_.DescribeNegationTo(os); } bool MatchAndExplain( T actual_value, ::testing::MatchResultListener* result_listener) const override { return common_impl_.MatchAndExplain(GetStatus(actual_value), result_listener); } private: StatusIsMatcherCommonImpl common_impl_; }; // Implements StatusIs() as a polymorphic matcher. class StatusIsMatcher { public: template StatusIsMatcher(StatusCodeMatcher&& code_matcher, StatusMessageMatcher&& message_matcher) : common_impl_(::testing::MatcherCast( std::forward(code_matcher)), ::testing::MatcherCast( std::forward(message_matcher))) { } // Converts this polymorphic matcher to a monomorphic matcher of the // given type. T can be StatusOr<>, Status, or a reference to // either of them. template /*implicit*/ operator ::testing::Matcher() const { // NOLINT return ::testing::Matcher( new MonoStatusIsMatcherImpl(common_impl_)); } private: const StatusIsMatcherCommonImpl common_impl_; }; // 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 /*implicit*/ operator ::testing::Matcher() const { // NOLINT return ::testing::Matcher(new MonoIsOkMatcherImpl()); } }; } // namespace status_internal ABSL_NAMESPACE_END } // namespace absl_testing #endif // ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_