// Copyright 2018 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 GVISOR_TEST_UTIL_POSIX_ERROR_H_ #define GVISOR_TEST_UTIL_POSIX_ERROR_H_ #include #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 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 std::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 friend class PosixErrorOr; }; template class ABSL_MUST_USE_RESULT PosixErrorOr { public: 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 friend class PosixErrorOr; template PosixErrorOr(PosixErrorOr other); template PosixErrorOr& operator=(PosixErrorOr other); // Return a reference to the error or NoError(). const PosixError error() const; // Returns this->error().error_message(); const std::string error_message() const; // Returns this->error().ok() 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: const int errno_value() const; absl::variant value_; friend class PosixErrorIsMatcherCommonImpl; }; template PosixErrorOr::PosixErrorOr(const PosixError& error) : value_(error) {} template PosixErrorOr::PosixErrorOr(const T& value) : value_(value) {} template PosixErrorOr::PosixErrorOr(T&& value) : value_(std::move(value)) {} // Conversion copy/move constructor, T must be convertible from U. template template inline PosixErrorOr::PosixErrorOr(PosixErrorOr other) { if (absl::holds_alternative(other.value_)) { // T is convertible from U. value_ = absl::get(std::move(other.value_)); } else if (absl::holds_alternative(other.value_)) { value_ = absl::get(std::move(other.value_)); } else { TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); } } template template inline PosixErrorOr& PosixErrorOr::operator=(PosixErrorOr other) { if (absl::holds_alternative(other.value_)) { // T is convertible from U. value_ = absl::get(std::move(other.value_)); } else if (absl::holds_alternative(other.value_)) { value_ = absl::get(std::move(other.value_)); } else { TEST_CHECK_MSG(false, "PosixErrorOr does not contain PosixError or value"); } return *this; } template const PosixError PosixErrorOr::error() const { if (!absl::holds_alternative(value_)) { return PosixError(); } return absl::get(value_); } template const int PosixErrorOr::errno_value() const { return error().errno_value(); } template const std::string PosixErrorOr::error_message() const { return error().error_message(); } template bool PosixErrorOr::ok() const { return error().ok(); } template const T& PosixErrorOr::ValueOrDie() const& { TEST_CHECK(absl::holds_alternative(value_)); return absl::get(value_); } template T& PosixErrorOr::ValueOrDie() & { TEST_CHECK(absl::holds_alternative(value_)); return absl::get(value_); } template const T&& PosixErrorOr::ValueOrDie() const&& { TEST_CHECK(absl::holds_alternative(value_)); return std::move(absl::get(value_)); } template T&& PosixErrorOr::ValueOrDie() && { TEST_CHECK(absl::holds_alternative(value_)); return std::move(absl::get(value_)); } extern ::std::ostream& operator<<(::std::ostream& os, const PosixError& e); template ::std::ostream& operator<<(::std::ostream& os, const PosixErrorOr& 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 class MonoPosixErrorIsOkMatcherImpl : 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 actual_value.ok(); } }; // Implements IsPosixErrorOkMatcher() as a polymorphic matcher. class IsPosixErrorOkMatcher { public: template operator ::testing::Matcher() const { // NOLINT return MakeMatcher(new MonoPosixErrorIsOkMatcherImpl()); } }; // Monomorphic implementation of a matcher for a PosixErrorOr. template class IsPosixErrorOkAndHoldsMatcherImpl : public ::testing::MatcherInterface { public: using ValueType = typename std::remove_reference().ValueOrDie())>::type; template explicit IsPosixErrorOkAndHoldsMatcherImpl(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( PosixErrorOrType actual_value, ::testing::MatchResultListener* listener) const override { if (!actual_value.ok()) { *listener << "which has error value " << actual_value.error(); 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(); if (!inner_explanation.empty()) { *listener << "which contains value " << ::testing::PrintToString(actual_value.ValueOrDie()) << ", " << inner_explanation; } return matches; } private: const ::testing::Matcher inner_matcher_; }; // Implements IsOkAndHolds() as a polymorphic matcher. template 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 or a reference to // PosixErrorOr. template operator ::testing::Matcher() const { // NOLINT return ::testing::MakeMatcher( new IsPosixErrorOkAndHoldsMatcherImpl( 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. class PosixErrorIsMatcherCommonImpl { public: PosixErrorIsMatcherCommonImpl( ::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 PosixError& error, ::testing::MatchResultListener* result_listener) const; private: const ::testing::Matcher code_matcher_; const ::testing::Matcher 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 class MonoPosixErrorIsMatcherImpl : public ::testing::MatcherInterface { 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.error(), result_listener); } private: PosixErrorIsMatcherCommonImpl common_impl_; }; inline ::testing::Matcher ToErrorCodeMatcher( const ::testing::Matcher& m) { return m; } // Implements PosixErrorIs() as a polymorphic matcher. class PosixErrorIsMatcher { public: template PosixErrorIsMatcher(ErrorCodeMatcher&& code_matcher, ::testing::Matcher message_matcher) : common_impl_( ToErrorCodeMatcher(std::forward(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 operator ::testing::Matcher() const { // NOLINT return MakeMatcher(new MonoPosixErrorIsMatcherImpl(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 PosixErrorIsMatcher PosixErrorIs( ErrorCodeMatcher&& code_matcher, ::testing::Matcher message_matcher) { return PosixErrorIsMatcher(std::forward(code_matcher), std::move(message_matcher)); } // Returns a gMock matcher that matches a PosixErrorOr<> which is ok() and // value matches the inner matcher. template IsPosixErrorOkAndHoldsMatcher::type> IsPosixErrorOkAndHolds(InnerMatcher&& inner_matcher) { return IsPosixErrorOkAndHoldsMatcher::type>( std::forward(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_