247 lines
8.1 KiB
C++
247 lines
8.1 KiB
C++
// 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 <ostream> // NOLINT
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#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 <typename T>
|
|
inline const absl::Status& GetStatus(const absl::StatusOr<T>& status) {
|
|
return status.status();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Implementation of IsOkAndHolds().
|
|
|
|
// Monomorphic implementation of matcher IsOkAndHolds(m). StatusOrType is a
|
|
// reference to StatusOr<T>.
|
|
template <typename StatusOrType>
|
|
class IsOkAndHoldsMatcherImpl
|
|
: public ::testing::MatcherInterface<StatusOrType> {
|
|
public:
|
|
typedef
|
|
typename std::remove_reference<StatusOrType>::type::value_type value_type;
|
|
|
|
template <typename InnerMatcher>
|
|
explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher)
|
|
: inner_matcher_(::testing::SafeMatcherCast<const value_type&>(
|
|
std::forward<InnerMatcher>(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 (!GetStatus(actual_value).ok()) {
|
|
*result_listener << "which has status " << GetStatus(actual_value);
|
|
return false;
|
|
}
|
|
|
|
// Call through to the inner matcher.
|
|
return inner_matcher_.MatchAndExplain(*actual_value, result_listener);
|
|
}
|
|
|
|
private:
|
|
const ::testing::Matcher<const value_type&> inner_matcher_;
|
|
};
|
|
|
|
// Implements IsOkAndHolds(m) as a polymorphic matcher.
|
|
template <typename InnerMatcher>
|
|
class IsOkAndHoldsMatcher {
|
|
public:
|
|
explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher)
|
|
: inner_matcher_(std::forward<InnerMatcher>(inner_matcher)) {}
|
|
|
|
// Converts this polymorphic matcher to a monomorphic matcher of the
|
|
// given type. StatusOrType can be either StatusOr<T> or a
|
|
// reference to StatusOr<T>.
|
|
template <typename StatusOrType>
|
|
operator ::testing::Matcher<StatusOrType>() const { // NOLINT
|
|
return ::testing::Matcher<StatusOrType>(
|
|
new IsOkAndHoldsMatcherImpl<const StatusOrType&>(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<StatusCode>`.
|
|
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<int>(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<int>(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<int>(lhs) == static_cast<int>(rhs);
|
|
}
|
|
inline bool operator!=(const StatusCode& lhs, const StatusCode& rhs) {
|
|
return static_cast<int>(lhs) != static_cast<int>(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<T>.
|
|
class StatusIsMatcherCommonImpl {
|
|
public:
|
|
StatusIsMatcherCommonImpl(
|
|
::testing::Matcher<StatusCode> code_matcher,
|
|
::testing::Matcher<absl::string_view> 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<StatusCode> code_matcher_;
|
|
const ::testing::Matcher<absl::string_view> 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 <typename T>
|
|
class MonoStatusIsMatcherImpl : public ::testing::MatcherInterface<T> {
|
|
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 <typename StatusCodeMatcher, typename StatusMessageMatcher>
|
|
StatusIsMatcher(StatusCodeMatcher&& code_matcher,
|
|
StatusMessageMatcher&& message_matcher)
|
|
: common_impl_(::testing::MatcherCast<StatusCode>(
|
|
std::forward<StatusCodeMatcher>(code_matcher)),
|
|
::testing::MatcherCast<absl::string_view>(
|
|
std::forward<StatusMessageMatcher>(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 <typename T>
|
|
/*implicit*/ operator ::testing::Matcher<T>() const { // NOLINT
|
|
return ::testing::Matcher<T>(
|
|
new MonoStatusIsMatcherImpl<const T&>(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 <typename T>
|
|
class MonoIsOkMatcherImpl : public ::testing::MatcherInterface<T> {
|
|
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 <typename T>
|
|
/*implicit*/ operator ::testing::Matcher<T>() const { // NOLINT
|
|
return ::testing::Matcher<T>(new MonoIsOkMatcherImpl<const T&>());
|
|
}
|
|
};
|
|
|
|
} // namespace status_internal
|
|
ABSL_NAMESPACE_END
|
|
} // namespace absl_testing
|
|
|
|
#endif // ABSL_STATUS_INTERNAL_STATUS_MATCHERS_H_
|