summaryrefslogtreecommitdiffhomepage
path: root/test/util/posix_error.h
diff options
context:
space:
mode:
Diffstat (limited to 'test/util/posix_error.h')
-rw-r--r--test/util/posix_error.h428
1 files changed, 428 insertions, 0 deletions
diff --git a/test/util/posix_error.h b/test/util/posix_error.h
new file mode 100644
index 000000000..8450be9b9
--- /dev/null
+++ b/test/util/posix_error.h
@@ -0,0 +1,428 @@
+// 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 <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 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 <typename T>
+ friend class PosixErrorOr;
+};
+
+template <typename T>
+class ABSL_MUST_USE_RESULT PosixErrorOr {
+ public:
+ PosixErrorOr(const PosixError& error); // NOLINT
+ explicit PosixErrorOr(const T& value);
+ PosixErrorOr(T&& value); // NOLINT
+
+ 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().
+ 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();
+
+ // 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<T, PosixError> value_;
+
+ friend class PosixErrorIsMatcherCommonImpl;
+};
+
+template <typename T>
+PosixErrorOr<T>::PosixErrorOr(const PosixError& error) : value_(error) {}
+
+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>
+const PosixError PosixErrorOr<T>::error() const {
+ if (!absl::holds_alternative<PosixError>(value_)) {
+ return PosixError();
+ }
+ return absl::get<PosixError>(value_);
+}
+
+template <typename T>
+const int PosixErrorOr<T>::errno_value() const {
+ return error().errno_value();
+}
+
+template <typename T>
+const std::string PosixErrorOr<T>::error_message() const {
+ return error().error_message();
+}
+
+template <typename T>
+bool PosixErrorOr<T>::ok() const {
+ return error().ok();
+}
+
+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_);
+}
+
+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 {
+ 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<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;
+
+ 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.error(), 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_