// Copyright 2018 The gVisor 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 // // 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 GVISOR_TEST_UTIL_POSIX_ERROR_H_ #define GVISOR_TEST_UTIL_POSIX_ERROR_H_ #include <string> #include "gmock/gmock.h" #include "absl/base/attributes.h" #include "absl/strings/string_view.h" #include "absl/types/variant.h" #include "test/util/logging.h" namespace gvisor { namespace testing { class PosixErrorIsMatcherCommonImpl; template <typename T> class PosixErrorOr; class ABSL_MUST_USE_RESULT PosixError { public: PosixError() {} explicit PosixError(int errno_value) : errno_(errno_value) {} PosixError(int errno_value, std::string msg) : errno_(errno_value), msg_(std::move(msg)) {} PosixError(PosixError&& other) = default; PosixError& operator=(PosixError&& other) = default; PosixError(const PosixError&) = default; PosixError& operator=(const PosixError&) = default; bool ok() const { return errno_ == 0; } // Returns a reference to *this to make matchers compatible with // PosixErrorOr. const PosixError& error() const { return *this; } std::string error_message() const { return msg_; } // ToString produces a full string representation of this posix error // including the printable representation of the errno and the error message. std::string ToString() const; // Ignores any errors. This method does nothing except potentially suppress // complaints from any tools that are checking that errors are not dropped on // the floor. void IgnoreError() const {} private: int errno_value() const { return errno_; } int errno_ = 0; std::string msg_; friend class PosixErrorIsMatcherCommonImpl; template <typename T> friend class PosixErrorOr; }; template <typename T> class ABSL_MUST_USE_RESULT PosixErrorOr { public: // A PosixErrorOr will check fail if it is constructed with NoError(). PosixErrorOr(const PosixError& error); PosixErrorOr(const T& value); PosixErrorOr(T&& value); PosixErrorOr(PosixErrorOr&& other) = default; PosixErrorOr& operator=(PosixErrorOr&& other) = default; PosixErrorOr(const PosixErrorOr&) = default; PosixErrorOr& operator=(const PosixErrorOr&) = default; // Conversion copy/move constructor, T must be convertible from U. template <typename U> friend class PosixErrorOr; template <typename U> PosixErrorOr(PosixErrorOr<U> other); template <typename U> PosixErrorOr& operator=(PosixErrorOr<U> other); // Return a reference to the error or NoError(). PosixError error() const; // Returns this->error().error_message(); std::string error_message() const; // Returns true if this PosixErrorOr contains some T. bool ok() const; // Returns a reference to our current value, or CHECK-fails if !this->ok(). const T& ValueOrDie() const&; T& ValueOrDie() &; const T&& ValueOrDie() const&&; T&& ValueOrDie() &&; // Ignores any errors. This method does nothing except potentially suppress // complaints from any tools that are checking that errors are not dropped on // the floor. void IgnoreError() const {} private: int errno_value() const; absl::variant<T, PosixError> value_; friend class PosixErrorIsMatcherCommonImpl; }; template <typename T> PosixErrorOr<T>::PosixErrorOr(const PosixError& error) : value_(error) { TEST_CHECK_MSG( !error.ok(), "Constructing PosixErrorOr with NoError, eg. errno 0 is not allowed."); } template <typename T> PosixErrorOr<T>::PosixErrorOr(const T& value) : value_(value) {} template <typename T> PosixErrorOr<T>::PosixErrorOr(T&& value) : value_(std::move(value)) {} // Conversion copy/move constructor, T must be convertible from U. template <typename T> template <typename U> inline PosixErrorOr<T>::PosixErrorOr(PosixErrorOr<U> other) { if (absl::holds_alternative<U>(other.value_)) { // T is convertible from U. value_ = absl::get<U>(std::move(other.value_)); } else if (absl::holds_alternative<PosixError>(other.value_)) { value_ = absl::get<PosixError>(std::move(other.value_)); } else { TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); } } template <typename T> template <typename U> inline PosixErrorOr<T>& PosixErrorOr<T>::operator=(PosixErrorOr<U> other) { if (absl::holds_alternative<U>(other.value_)) { // T is convertible from U. value_ = absl::get<U>(std::move(other.value_)); } else if (absl::holds_alternative<PosixError>(other.value_)) { value_ = absl::get<PosixError>(std::move(other.value_)); } else { TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); } return *this; } template <typename T> PosixError PosixErrorOr<T>::error() const { if (!absl::holds_alternative<PosixError>(value_)) { return PosixError(); } return absl::get<PosixError>(value_); } template <typename T> int PosixErrorOr<T>::errno_value() const { return error().errno_value(); } template <typename T> std::string PosixErrorOr<T>::error_message() const { return error().error_message(); } template <typename T> bool PosixErrorOr<T>::ok() const { return absl::holds_alternative<T>(value_); } template <typename T> const T& PosixErrorOr<T>::ValueOrDie() const& { TEST_CHECK(absl::holds_alternative<T>(value_)); return absl::get<T>(value_); } template <typename T> T& PosixErrorOr<T>::ValueOrDie() & { TEST_CHECK(absl::holds_alternative<T>(value_)); return absl::get<T>(value_); } template <typename T> const T&& PosixErrorOr<T>::ValueOrDie() const&& { TEST_CHECK(absl::holds_alternative<T>(value_)); return std::move(absl::get<T>(value_)); } template <typename T> T&& PosixErrorOr<T>::ValueOrDie() && { TEST_CHECK(absl::holds_alternative<T>(value_)); return std::move(absl::get<T>(value_)); } extern ::std::ostream& operator<<(::std::ostream& os, const PosixError& e); template <typename T> ::std::ostream& operator<<(::std::ostream& os, const PosixErrorOr<T>& e) { os << e.error(); return os; } // NoError is a PosixError that represents a successful state, i.e. No Error. inline PosixError NoError() { return PosixError(); } // Monomorphic implementation of matcher IsPosixErrorOk() for a given type T. // T can be PosixError, PosixErrorOr<>, or a reference to either of them. template <typename T> class MonoPosixErrorIsOkMatcherImpl : 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 actual_value.ok(); } }; // Implements IsPosixErrorOkMatcher() as a polymorphic matcher. class IsPosixErrorOkMatcher { public: template <typename T> operator ::testing::Matcher<T>() const { // NOLINT return MakeMatcher(new MonoPosixErrorIsOkMatcherImpl<T>()); } }; // Monomorphic implementation of a matcher for a PosixErrorOr. template <typename PosixErrorOrType> class IsPosixErrorOkAndHoldsMatcherImpl : public ::testing::MatcherInterface<PosixErrorOrType> { public: using ValueType = typename std::remove_reference<decltype( std::declval<PosixErrorOrType>().ValueOrDie())>::type; template <typename InnerMatcher> explicit IsPosixErrorOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) : inner_matcher_(::testing::SafeMatcherCast<const ValueType&>( 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( PosixErrorOrType actual_value, ::testing::MatchResultListener* listener) const override { // We can't extract the value if it doesn't contain one. if (!actual_value.ok()) { return false; } ::testing::StringMatchResultListener inner_listener; const bool matches = inner_matcher_.MatchAndExplain( actual_value.ValueOrDie(), &inner_listener); const std::string inner_explanation = inner_listener.str(); *listener << "has a value " << ::testing::PrintToString(actual_value.ValueOrDie()); if (!inner_explanation.empty()) { *listener << " " << inner_explanation; } return matches; } private: const ::testing::Matcher<const ValueType&> inner_matcher_; }; // Implements IsOkAndHolds() as a polymorphic matcher. template <typename InnerMatcher> class IsPosixErrorOkAndHoldsMatcher { public: explicit IsPosixErrorOkAndHoldsMatcher(InnerMatcher inner_matcher) : inner_matcher_(std::move(inner_matcher)) {} // Converts this polymorphic matcher to a monomorphic one of the given type. // PosixErrorOrType can be either PosixErrorOr<T> or a reference to // PosixErrorOr<T>. template <typename PosixErrorOrType> operator ::testing::Matcher<PosixErrorOrType>() const { // NOLINT return ::testing::MakeMatcher( new IsPosixErrorOkAndHoldsMatcherImpl<PosixErrorOrType>( inner_matcher_)); } private: const InnerMatcher inner_matcher_; }; // PosixErrorIs() is a polymorphic matcher. This class is the common // implementation of it shared by all types T where PosixErrorIs() can be // used as a Matcher<T>. class PosixErrorIsMatcherCommonImpl { public: PosixErrorIsMatcherCommonImpl( ::testing::Matcher<int> code_matcher, ::testing::Matcher<const std::string&> 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 PosixError& error, ::testing::MatchResultListener* result_listener) const; template <typename T> bool MatchAndExplain(const PosixErrorOr<T>& error_or, ::testing::MatchResultListener* result_listener) const { if (error_or.ok()) { *result_listener << "has a value " << ::testing::PrintToString(error_or.ValueOrDie()); return false; } return MatchAndExplain(error_or.error(), result_listener); } private: const ::testing::Matcher<int> code_matcher_; const ::testing::Matcher<const std::string&> message_matcher_; }; // Monomorphic implementation of matcher PosixErrorIs() for a given type // T. T can be PosixError, PosixErrorOr<>, or a reference to either of them. template <typename T> class MonoPosixErrorIsMatcherImpl : public ::testing::MatcherInterface<T> { public: explicit MonoPosixErrorIsMatcherImpl( PosixErrorIsMatcherCommonImpl 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(actual_value, result_listener); } private: PosixErrorIsMatcherCommonImpl common_impl_; }; inline ::testing::Matcher<int> ToErrorCodeMatcher( const ::testing::Matcher<int>& m) { return m; } // Implements PosixErrorIs() as a polymorphic matcher. class PosixErrorIsMatcher { public: template <typename ErrorCodeMatcher> PosixErrorIsMatcher(ErrorCodeMatcher&& code_matcher, ::testing::Matcher<const std::string&> message_matcher) : common_impl_( ToErrorCodeMatcher(std::forward<ErrorCodeMatcher>(code_matcher)), std::move(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> operator ::testing::Matcher<T>() const { // NOLINT return MakeMatcher(new MonoPosixErrorIsMatcherImpl<T>(common_impl_)); } private: const PosixErrorIsMatcherCommonImpl common_impl_; }; // Returns a gMock matcher that matches a PosixError or PosixErrorOr<> whose // whose error code matches code_matcher, and whose error message matches // message_matcher. template <typename ErrorCodeMatcher> PosixErrorIsMatcher PosixErrorIs( ErrorCodeMatcher&& code_matcher, ::testing::Matcher<const std::string&> message_matcher) { return PosixErrorIsMatcher(std::forward<ErrorCodeMatcher>(code_matcher), std::move(message_matcher)); } // Returns a gMock matcher that matches a PosixErrorOr<> which is ok() and // value matches the inner matcher. template <typename InnerMatcher> IsPosixErrorOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type> IsPosixErrorOkAndHolds(InnerMatcher&& inner_matcher) { return IsPosixErrorOkAndHoldsMatcher<typename std::decay<InnerMatcher>::type>( std::forward<InnerMatcher>(inner_matcher)); } // Internal helper for concatenating macro values. #define POSIX_ERROR_IMPL_CONCAT_INNER_(x, y) x##y #define POSIX_ERROR_IMPL_CONCAT_(x, y) POSIX_ERROR_IMPL_CONCAT_INNER_(x, y) #define POSIX_ERROR_IMPL_ASSIGN_OR_RETURN_(posixerroror, lhs, rexpr) \ auto posixerroror = (rexpr); \ if (!posixerroror.ok()) { \ return (posixerroror.error()); \ } \ lhs = std::move(posixerroror).ValueOrDie() #define EXPECT_NO_ERRNO(expression) \ EXPECT_THAT(expression, IsPosixErrorOkMatcher()) #define ASSERT_NO_ERRNO(expression) \ ASSERT_THAT(expression, IsPosixErrorOkMatcher()) #define ASSIGN_OR_RETURN_ERRNO(lhs, rexpr) \ POSIX_ERROR_IMPL_ASSIGN_OR_RETURN_( \ POSIX_ERROR_IMPL_CONCAT_(_status_or_value, __LINE__), lhs, rexpr) #define RETURN_IF_ERRNO(s) \ do { \ if (!s.ok()) { \ return s; \ } \ } while (false); #define ASSERT_NO_ERRNO_AND_VALUE(expr) \ ({ \ auto _expr_result = (expr); \ ASSERT_NO_ERRNO(_expr_result); \ std::move(_expr_result).ValueOrDie(); \ }) } // namespace testing } // namespace gvisor #endif // GVISOR_TEST_UTIL_POSIX_ERROR_H_