// 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 #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 { // PosixError must be async-signal-safe. class ABSL_MUST_USE_RESULT PosixError { public: PosixError() {} explicit PosixError(int errno_value) : errno_(errno_value) {} PosixError(int errno_value, std::string_view msg) : errno_(errno_value) { // Check that `msg` will fit, leaving room for '\0' at the end. TEST_CHECK(msg.size() < sizeof(msg_)); msg.copy(msg_, msg.size()); } 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; } int errno_value() const { return errno_; } const char* 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_ = 0; char msg_[1024] = {}; }; template 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 friend class PosixErrorOr; template PosixErrorOr(PosixErrorOr other); template PosixErrorOr& operator=(PosixErrorOr other); // Returns true if this PosixErrorOr contains some T. bool ok() const; // Return a copy of the contained PosixError or NoError(). PosixError error() 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: absl::variant value_; friend class PosixErrorIsMatcherCommonImpl; }; template PosixErrorOr::PosixErrorOr(const PosixError& error) : value_(error) { TEST_CHECK_MSG( !error.ok(), "Constructing PosixErrorOr with NoError, eg. errno 0 is not allowed."); } 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 PosixError PosixErrorOr::error() const { if (!absl::holds_alternative(value_)) { return PosixError(); } return absl::get(value_); } template bool PosixErrorOr::ok() const { return absl::holds_alternative(value_); } 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 { // 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 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; template bool MatchAndExplain(const PosixErrorOr& 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 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, 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_