summaryrefslogtreecommitdiffhomepage
path: root/test/util/test_util.h
diff options
context:
space:
mode:
Diffstat (limited to 'test/util/test_util.h')
-rw-r--r--test/util/test_util.h794
1 files changed, 794 insertions, 0 deletions
diff --git a/test/util/test_util.h b/test/util/test_util.h
new file mode 100644
index 000000000..2a7609e5c
--- /dev/null
+++ b/test/util/test_util.h
@@ -0,0 +1,794 @@
+// 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.
+
+// Utilities for syscall testing.
+//
+// Initialization
+// ==============
+//
+// Prior to calling RUN_ALL_TESTS, all tests must use TestInit(&argc, &argv).
+// See the TestInit function for exact side-effects and semantics.
+//
+// Configuration
+// =============
+//
+// IsRunningOnGvisor returns true if the test is known to be running on gVisor.
+// GvisorPlatform can be used to get more detail:
+//
+// switch (GvisorPlatform()) {
+// case Platform::kNative:
+// case Platform::kGvisor:
+// EXPECT_THAT(mmap(...), SyscallSucceeds());
+// break;
+// case Platform::kPtrace:
+// EXPECT_THAT(mmap(...), SyscallFailsWithErrno(ENOSYS));
+// break;
+// }
+//
+// Matchers
+// ========
+//
+// ElementOf(xs) matches if the matched value is equal to an element of the
+// container xs. Example:
+//
+// // PASS
+// EXPECT_THAT(1, ElementOf({0, 1, 2}));
+//
+// // FAIL
+// // Value of: 3
+// // Expected: one of {0, 1, 2}
+// // Actual: 3
+// EXPECT_THAT(3, ElementOf({0, 1, 2}));
+//
+// SyscallSucceeds() matches if the syscall is successful. A successful syscall
+// is defined by either a return value not equal to -1, or a return value of -1
+// with an errno of 0 (which is a possible successful return for e.g.
+// PTRACE_PEEK). Example:
+//
+// // PASS
+// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallSucceeds());
+//
+// // FAIL
+// // Value of: open("/", O_RDWR)
+// // Expected: not -1 (success)
+// // Actual: -1 (of type int), with errno 21 (Is a directory)
+// EXPECT_THAT(open("/", O_RDWR), SyscallSucceeds());
+//
+// SyscallSucceedsWithValue(m) matches if the syscall is successful, and the
+// value also matches m. Example:
+//
+// // PASS
+// EXPECT_THAT(read(4, buf, 8192), SyscallSucceedsWithValue(8192));
+//
+// // FAIL
+// // Value of: read(-1, buf, 8192)
+// // Expected: is equal to 8192
+// // Actual: -1 (of type long), with errno 9 (Bad file number)
+// EXPECT_THAT(read(-1, buf, 8192), SyscallSucceedsWithValue(8192));
+//
+// // FAIL
+// // Value of: read(4, buf, 1)
+// // Expected: is > 4096
+// // Actual: 1 (of type long)
+// EXPECT_THAT(read(4, buf, 1), SyscallSucceedsWithValue(Gt(4096)));
+//
+// SyscallFails() matches if the syscall is unsuccessful. An unsuccessful
+// syscall is defined by a return value of -1 with a non-zero errno. Example:
+//
+// // PASS
+// EXPECT_THAT(open("/", O_RDWR), SyscallFails());
+//
+// // FAIL
+// // Value of: open("/dev/null", O_RDONLY)
+// // Expected: -1 (failure)
+// // Actual: 0 (of type int)
+// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallFails());
+//
+// SyscallFailsWithErrno(m) matches if the syscall is unsuccessful, and errno
+// matches m. Example:
+//
+// // PASS
+// EXPECT_THAT(open("/", O_RDWR), SyscallFailsWithErrno(EISDIR));
+//
+// // PASS
+// EXPECT_THAT(open("/etc/passwd", O_RDWR | O_DIRECTORY),
+// SyscallFailsWithErrno(AnyOf(EACCES, ENOTDIR)));
+//
+// // FAIL
+// // Value of: open("/dev/null", O_RDONLY)
+// // Expected: -1 (failure) with errno 21 (Is a directory)
+// // Actual: 0 (of type int)
+// EXPECT_THAT(open("/dev/null", O_RDONLY), SyscallFailsWithErrno(EISDIR));
+//
+// // FAIL
+// // Value of: open("/", O_RDWR)
+// // Expected: -1 (failure) with errno 22 (Invalid argument)
+// // Actual: -1 (of type int), failure, but with errno 21 (Is a directory)
+// EXPECT_THAT(open("/", O_RDWR), SyscallFailsWithErrno(EINVAL));
+//
+// Because the syscall matchers encode save/restore functionality, their meaning
+// should not be inverted via Not. That is, AnyOf(SyscallSucceedsWithValue(1),
+// SyscallSucceedsWithValue(2)) is permitted, but not
+// Not(SyscallFailsWithErrno(EPERM)).
+//
+// Syscalls
+// ========
+//
+// RetryEINTR wraps a function that returns -1 and sets errno on failure
+// to be automatically retried when EINTR occurs. Example:
+//
+// auto rv = RetryEINTR(waitpid)(pid, &status, 0);
+//
+// ReadFd/WriteFd/PreadFd/PwriteFd are interface-compatible wrappers around the
+// read/write/pread/pwrite syscalls to handle both EINTR and partial
+// reads/writes. Example:
+//
+// EXPECT_THAT(ReadFd(fd, &buf, size), SyscallSucceedsWithValue(size));
+//
+// General Utilities
+// =================
+//
+// ApplyVec(f, xs) returns a vector containing the result of applying function
+// `f` to each value in `xs`.
+//
+// AllBitwiseCombinations takes a variadic number of ranges containing integers
+// and returns a vector containing every integer that can be formed by ORing
+// together exactly one integer from each list. List<T> is an alias for
+// std::initializer_list<T> that makes AllBitwiseCombinations more ergonomic to
+// use with list literals (initializer lists do not otherwise participate in
+// template argument deduction). Example:
+//
+// EXPECT_THAT(
+// AllBitwiseCombinations<int>(
+// List<int>{SOCK_DGRAM, SOCK_STREAM},
+// List<int>{0, SOCK_NONBLOCK}),
+// Contains({SOCK_DGRAM, SOCK_STREAM, SOCK_DGRAM | SOCK_NONBLOCK,
+// SOCK_STREAM | SOCK_NONBLOCK}));
+//
+// VecCat takes a variadic number of containers and returns a vector containing
+// the concatenated contents.
+//
+// VecAppend takes an initial container and a variadic number of containers and
+// appends each to the initial container.
+//
+// RandomizeBuffer will use MTRandom to fill the given buffer with random bytes.
+//
+// GenerateIovecs will return the smallest number of iovec arrays for writing a
+// given total number of bytes to a file, each iovec array size up to IOV_MAX,
+// each iovec in each array pointing to the same buffer.
+
+#ifndef GVISOR_TEST_UTIL_TEST_UTIL_H_
+#define GVISOR_TEST_UTIL_TEST_UTIL_H_
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <cerrno>
+#include <initializer_list>
+#include <iterator>
+#include <string>
+#include <thread> // NOLINT: using std::thread::hardware_concurrency().
+#include <utility>
+#include <vector>
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+#include "gmock/gmock.h"
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "absl/time/time.h"
+#include "test/util/fs_util.h"
+#include "test/util/logging.h"
+#include "test/util/posix_error.h"
+#include "test/util/save_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// TestInit must be called prior to RUN_ALL_TESTS.
+//
+// This parses all arguments and adjusts argc and argv appropriately.
+//
+// TestInit may create background threads.
+void TestInit(int* argc, char*** argv);
+
+// SKIP_IF may be used to skip a test case.
+//
+// These cases are still emitted, but a SKIPPED line will appear.
+#define SKIP_IF(expr) \
+ do { \
+ if (expr) { \
+ std::cout << "\033[0;33m[ SKIPPED ]\033[m => " << #expr << std::endl; \
+ return; \
+ } \
+ } while (0)
+
+#define SKIP_BEFORE_KERNEL(maj, min) \
+ do { \
+ auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); \
+ SKIP_IF(version.major < (maj) || \
+ (version.major == (maj) && version.minor < (min))); \
+ } while (0)
+
+enum class Platform {
+ kNative,
+ kKVM,
+ kPtrace,
+};
+bool IsRunningOnGvisor();
+Platform GvisorPlatform();
+
+void SetupGvisorDeathTest();
+
+struct KernelVersion {
+ int major;
+ int minor;
+ int micro;
+};
+
+bool operator==(const KernelVersion& first, const KernelVersion& second);
+
+PosixErrorOr<KernelVersion> ParseKernelVersion(absl::string_view vers_string);
+PosixErrorOr<KernelVersion> GetKernelVersion();
+
+static const size_t kPageSize = sysconf(_SC_PAGESIZE);
+
+enum class CPUVendor { kIntel, kAMD, kUnknownVendor };
+
+CPUVendor GetCPUVendor();
+
+inline int NumCPUs() { return std::thread::hardware_concurrency(); }
+
+// Converts cpu_set_t to a std::string for easy examination.
+std::string CPUSetToString(const cpu_set_t& set, size_t cpus = CPU_SETSIZE);
+
+struct OpenFd {
+ // fd is the open file descriptor number.
+ int fd = -1;
+
+ // link is the resolution of the symbolic link.
+ std::string link;
+};
+
+// Make it easier to log OpenFds to error streams.
+std::ostream& operator<<(std::ostream& out, std::vector<OpenFd> const& v);
+std::ostream& operator<<(std::ostream& out, OpenFd const& ofd);
+
+// Gets a detailed list of open fds for this process.
+PosixErrorOr<std::vector<OpenFd>> GetOpenFDs();
+
+// Returns the number of hard links to a path.
+PosixErrorOr<uint64_t> Links(const std::string& path);
+
+namespace internal {
+
+inline std::string ErrnoWithMessage(int const errnum) {
+ char buf[1024] = {};
+ const char* str = strerror_r(errnum, buf, sizeof(buf));
+ if (str == nullptr || str[0] == '\0') {
+ snprintf(buf, sizeof(buf), "Unknown error %d", errnum);
+ str = buf;
+ }
+ return absl::StrCat(errnum, " (", str, ")");
+}
+
+template <typename Container>
+class ElementOfMatcher {
+ public:
+ explicit ElementOfMatcher(Container container)
+ : container_(::std::move(container)) {}
+
+ template <typename T>
+ bool MatchAndExplain(T const& rv,
+ ::testing::MatchResultListener* const listener) const {
+ using std::count;
+ return count(container_.begin(), container_.end(), rv) != 0;
+ }
+
+ void DescribeTo(::std::ostream* const os) const {
+ *os << "one of {";
+ char const* sep = "";
+ for (auto const& elem : container_) {
+ *os << sep << elem;
+ sep = ", ";
+ }
+ *os << "}";
+ }
+
+ void DescribeNegationTo(::std::ostream* const os) const {
+ *os << "none of {";
+ char const* sep = "";
+ for (auto const& elem : container_) {
+ *os << sep << elem;
+ sep = ", ";
+ }
+ *os << "}";
+ }
+
+ private:
+ Container const container_;
+};
+
+template <typename E>
+class SyscallSuccessMatcher {
+ public:
+ explicit SyscallSuccessMatcher(E expected)
+ : expected_(::std::move(expected)) {}
+
+ template <typename T>
+ operator ::testing::Matcher<T>() const {
+ // E is one of three things:
+ // - T, or a type losslessly and implicitly convertible to T.
+ // - A monomorphic Matcher<T>.
+ // - A polymorphic matcher.
+ // SafeMatcherCast handles any of the above correctly.
+ //
+ // Similarly, gMock will invoke this conversion operator to obtain a
+ // monomorphic matcher (this is how polymorphic matchers are implemented).
+ return ::testing::MakeMatcher(
+ new Impl<T>(::testing::SafeMatcherCast<T>(expected_)));
+ }
+
+ private:
+ template <typename T>
+ class Impl : public ::testing::MatcherInterface<T> {
+ public:
+ explicit Impl(::testing::Matcher<T> matcher)
+ : matcher_(::std::move(matcher)) {}
+
+ bool MatchAndExplain(
+ T const& rv,
+ ::testing::MatchResultListener* const listener) const override {
+ if (rv == static_cast<decltype(rv)>(-1) && errno != 0) {
+ *listener << "with errno " << ErrnoWithMessage(errno);
+ return false;
+ }
+ bool match = matcher_.MatchAndExplain(rv, listener);
+ if (match) {
+ MaybeSave();
+ }
+ return match;
+ }
+
+ void DescribeTo(::std::ostream* const os) const override {
+ matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(::std::ostream* const os) const override {
+ matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ ::testing::Matcher<T> matcher_;
+ };
+
+ private:
+ E expected_;
+};
+
+// A polymorphic matcher equivalent to ::testing::internal::AnyMatcher, except
+// not in namespace ::testing::internal, and describing SyscallSucceeds()'s
+// match constraints (which are enforced by SyscallSuccessMatcher::Impl).
+class AnySuccessValueMatcher {
+ public:
+ template <typename T>
+ operator ::testing::Matcher<T>() const {
+ return ::testing::MakeMatcher(new Impl<T>());
+ }
+
+ private:
+ template <typename T>
+ class Impl : public ::testing::MatcherInterface<T> {
+ public:
+ bool MatchAndExplain(
+ T const& rv,
+ ::testing::MatchResultListener* const listener) const override {
+ return true;
+ }
+
+ void DescribeTo(::std::ostream* const os) const override {
+ *os << "not -1 (success)";
+ }
+
+ void DescribeNegationTo(::std::ostream* const os) const override {
+ *os << "-1 (failure)";
+ }
+ };
+};
+
+class SyscallFailureMatcher {
+ public:
+ explicit SyscallFailureMatcher(::testing::Matcher<int> errno_matcher)
+ : errno_matcher_(std::move(errno_matcher)) {}
+
+ template <typename T>
+ bool MatchAndExplain(T const& rv,
+ ::testing::MatchResultListener* const listener) const {
+ if (rv != static_cast<decltype(rv)>(-1)) {
+ return false;
+ }
+ int actual_errno = errno;
+ *listener << "with errno " << ErrnoWithMessage(actual_errno);
+ bool match = errno_matcher_.MatchAndExplain(actual_errno, listener);
+ if (match) {
+ MaybeSave();
+ }
+ return match;
+ }
+
+ void DescribeTo(::std::ostream* const os) const {
+ *os << "-1 (failure), with errno ";
+ errno_matcher_.DescribeTo(os);
+ }
+
+ void DescribeNegationTo(::std::ostream* const os) const {
+ *os << "not -1 (success), with errno ";
+ errno_matcher_.DescribeNegationTo(os);
+ }
+
+ private:
+ ::testing::Matcher<int> errno_matcher_;
+};
+
+class SpecificErrnoMatcher : public ::testing::MatcherInterface<int> {
+ public:
+ explicit SpecificErrnoMatcher(int const expected) : expected_(expected) {}
+
+ bool MatchAndExplain(
+ int const actual_errno,
+ ::testing::MatchResultListener* const listener) const override {
+ return actual_errno == expected_;
+ }
+
+ void DescribeTo(::std::ostream* const os) const override {
+ *os << ErrnoWithMessage(expected_);
+ }
+
+ void DescribeNegationTo(::std::ostream* const os) const override {
+ *os << "not " << ErrnoWithMessage(expected_);
+ }
+
+ private:
+ int const expected_;
+};
+
+inline ::testing::Matcher<int> SpecificErrno(int const expected) {
+ return ::testing::MakeMatcher(new SpecificErrnoMatcher(expected));
+}
+
+} // namespace internal
+
+template <typename Container>
+inline ::testing::PolymorphicMatcher<internal::ElementOfMatcher<Container>>
+ElementOf(Container container) {
+ return ::testing::MakePolymorphicMatcher(
+ internal::ElementOfMatcher<Container>(::std::move(container)));
+}
+
+template <typename T>
+inline ::testing::PolymorphicMatcher<
+ internal::ElementOfMatcher<::std::vector<T>>>
+ElementOf(::std::initializer_list<T> elems) {
+ return ::testing::MakePolymorphicMatcher(
+ internal::ElementOfMatcher<::std::vector<T>>(::std::vector<T>(elems)));
+}
+
+template <typename E>
+inline internal::SyscallSuccessMatcher<E> SyscallSucceedsWithValue(E expected) {
+ return internal::SyscallSuccessMatcher<E>(::std::move(expected));
+}
+
+inline internal::SyscallSuccessMatcher<internal::AnySuccessValueMatcher>
+SyscallSucceeds() {
+ return SyscallSucceedsWithValue(
+ ::gvisor::testing::internal::AnySuccessValueMatcher());
+}
+
+inline ::testing::PolymorphicMatcher<internal::SyscallFailureMatcher>
+SyscallFailsWithErrno(::testing::Matcher<int> expected) {
+ return ::testing::MakePolymorphicMatcher(
+ internal::SyscallFailureMatcher(::std::move(expected)));
+}
+
+// Overload taking an int so that SyscallFailsWithErrno(<specific errno>) uses
+// internal::SpecificErrno (which stringifies the errno) rather than
+// ::testing::Eq (which doesn't).
+inline ::testing::PolymorphicMatcher<internal::SyscallFailureMatcher>
+SyscallFailsWithErrno(int const expected) {
+ return SyscallFailsWithErrno(internal::SpecificErrno(expected));
+}
+
+inline ::testing::PolymorphicMatcher<internal::SyscallFailureMatcher>
+SyscallFails() {
+ return SyscallFailsWithErrno(::testing::Gt(0));
+}
+
+// As of GCC 7.2, -Wall => -Wc++17-compat => -Wnoexcept-type generates an
+// irrelevant, non-actionable warning about ABI compatibility when
+// RetryEINTRImpl is constructed with a noexcept function, such as glibc's
+// syscall(). See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80985.
+#if defined(__GNUC__) && !defined(__clang__) && \
+ (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnoexcept-type"
+#endif
+
+namespace internal {
+
+template <typename F>
+struct RetryEINTRImpl {
+ F const f;
+
+ explicit constexpr RetryEINTRImpl(F f) : f(std::move(f)) {}
+
+ template <typename... Args>
+ auto operator()(Args&&... args) const
+ -> decltype(f(std::forward<Args>(args)...)) {
+ while (true) {
+ errno = 0;
+ auto const ret = f(std::forward<Args>(args)...);
+ if (ret != -1 || errno != EINTR) {
+ return ret;
+ }
+ }
+ }
+};
+
+} // namespace internal
+
+template <typename F>
+constexpr internal::RetryEINTRImpl<F> RetryEINTR(F&& f) {
+ return internal::RetryEINTRImpl<F>(std::forward<F>(f));
+}
+
+#if defined(__GNUC__) && !defined(__clang__) && \
+ (__GNUC__ > 7 || (__GNUC__ == 7 && __GNUC_MINOR__ >= 2))
+#pragma GCC diagnostic pop
+#endif
+
+namespace internal {
+
+template <typename F>
+ssize_t ApplyFileIoSyscall(F const& f, size_t const count) {
+ size_t completed = 0;
+ // `do ... while` because some callers actually want to make a syscall with a
+ // count of 0.
+ do {
+ auto const cur = RetryEINTR(f)(completed);
+ if (cur < 0) {
+ return cur;
+ } else if (cur == 0) {
+ break;
+ }
+ completed += cur;
+ } while (completed < count);
+ return completed;
+}
+
+} // namespace internal
+
+inline ssize_t ReadFd(int fd, void* buf, size_t count) {
+ return internal::ApplyFileIoSyscall(
+ [&](size_t completed) {
+ return read(fd, static_cast<char*>(buf) + completed, count - completed);
+ },
+ count);
+}
+
+inline ssize_t WriteFd(int fd, void const* buf, size_t count) {
+ return internal::ApplyFileIoSyscall(
+ [&](size_t completed) {
+ return write(fd, static_cast<char const*>(buf) + completed,
+ count - completed);
+ },
+ count);
+}
+
+inline ssize_t PreadFd(int fd, void* buf, size_t count, off_t offset) {
+ return internal::ApplyFileIoSyscall(
+ [&](size_t completed) {
+ return pread(fd, static_cast<char*>(buf) + completed, count - completed,
+ offset + completed);
+ },
+ count);
+}
+
+inline ssize_t PwriteFd(int fd, void const* buf, size_t count, off_t offset) {
+ return internal::ApplyFileIoSyscall(
+ [&](size_t completed) {
+ return pwrite(fd, static_cast<char const*>(buf) + completed,
+ count - completed, offset + completed);
+ },
+ count);
+}
+
+template <typename T>
+using List = std::initializer_list<T>;
+
+namespace internal {
+
+template <typename T>
+void AppendAllBitwiseCombinations(std::vector<T>* combinations, T current) {
+ combinations->push_back(current);
+}
+
+template <typename T, typename Arg, typename... Args>
+void AppendAllBitwiseCombinations(std::vector<T>* combinations, T current,
+ Arg&& next, Args&&... rest) {
+ for (auto const option : next) {
+ AppendAllBitwiseCombinations(combinations, current | option, rest...);
+ }
+}
+
+inline size_t CombinedSize(size_t accum) { return accum; }
+
+template <typename T, typename... Args>
+size_t CombinedSize(size_t accum, T const& x, Args&&... xs) {
+ return CombinedSize(accum + x.size(), std::forward<Args>(xs)...);
+}
+
+// Base case: no more containers, so do nothing.
+template <typename T>
+void DoMoveExtendContainer(T* c) {}
+
+// Append each container next to c.
+template <typename T, typename U, typename... Args>
+void DoMoveExtendContainer(T* c, U&& next, Args&&... rest) {
+ std::move(std::begin(next), std::end(next), std::back_inserter(*c));
+ DoMoveExtendContainer(c, std::forward<Args>(rest)...);
+}
+
+} // namespace internal
+
+template <typename T = int>
+std::vector<T> AllBitwiseCombinations() {
+ return std::vector<T>();
+}
+
+template <typename T = int, typename... Args>
+std::vector<T> AllBitwiseCombinations(Args&&... args) {
+ std::vector<T> combinations;
+ internal::AppendAllBitwiseCombinations(&combinations, 0, args...);
+ return combinations;
+}
+
+template <typename T, typename U, typename F>
+std::vector<T> ApplyVec(F const& f, std::vector<U> const& us) {
+ std::vector<T> vec;
+ vec.reserve(us.size());
+ for (auto const& u : us) {
+ vec.push_back(f(u));
+ }
+ return vec;
+}
+
+template <typename T, typename U>
+std::vector<T> ApplyVecToVec(std::vector<std::function<T(U)>> const& fs,
+ std::vector<U> const& us) {
+ std::vector<T> vec;
+ vec.reserve(us.size() * fs.size());
+ for (auto const& f : fs) {
+ for (auto const& u : us) {
+ vec.push_back(f(u));
+ }
+ }
+ return vec;
+}
+
+// Moves all elements from the containers `args` to the end of `c`.
+template <typename T, typename... Args>
+void VecAppend(T* c, Args&&... args) {
+ c->reserve(internal::CombinedSize(c->size(), args...));
+ internal::DoMoveExtendContainer(c, std::forward<Args>(args)...);
+}
+
+// Returns a vector containing the concatenated contents of the containers
+// `args`.
+template <typename T, typename... Args>
+std::vector<T> VecCat(Args&&... args) {
+ std::vector<T> combined;
+ VecAppend(&combined, std::forward<Args>(args)...);
+ return combined;
+}
+
+#define RETURN_ERROR_IF_SYSCALL_FAIL(syscall) \
+ do { \
+ if ((syscall) < 0 && errno != 0) { \
+ return PosixError(errno, #syscall); \
+ } \
+ } while (false)
+
+// Fill the given buffer with random bytes.
+void RandomizeBuffer(void* buffer, size_t len);
+
+template <typename T>
+inline PosixErrorOr<T> Atoi(absl::string_view str) {
+ T ret;
+ if (!absl::SimpleAtoi<T>(str, &ret)) {
+ return PosixError(EINVAL, "String not a number.");
+ }
+ return ret;
+}
+
+inline PosixErrorOr<uint64_t> AtoiBase(absl::string_view str, int base) {
+ if (base > 255 || base < 2) {
+ return PosixError(EINVAL, "Invalid Base");
+ }
+
+ uint64_t ret = 0;
+ if (!absl::numbers_internal::safe_strtou64_base(str, &ret, base)) {
+ return PosixError(EINVAL, "String not a number.");
+ }
+
+ return ret;
+}
+
+inline PosixErrorOr<double> Atod(absl::string_view str) {
+ double ret;
+ if (!absl::SimpleAtod(str, &ret)) {
+ return PosixError(EINVAL, "String not a double type.");
+ }
+ return ret;
+}
+
+inline PosixErrorOr<float> Atof(absl::string_view str) {
+ float ret;
+ if (!absl::SimpleAtof(str, &ret)) {
+ return PosixError(EINVAL, "String not a float type.");
+ }
+ return ret;
+}
+
+// Return the smallest number of iovec arrays that can be used to write
+// "total_bytes" number of bytes, each iovec writing one "buf".
+std::vector<std::vector<struct iovec>> GenerateIovecs(uint64_t total_size,
+ void* buf, size_t buflen);
+
+// Sleep for at least the specified duration. Avoids glibc.
+void SleepSafe(absl::Duration duration);
+
+// Returns bytes in 'n' megabytes. Used for readability.
+uint64_t Megabytes(uint64_t n);
+
+// Predicate for checking that a value is within some tolerance of another
+// value. Returns true iff current is in the range [target * (1 - tolerance),
+// target * (1 + tolerance)].
+bool Equivalent(uint64_t current, uint64_t target, double tolerance);
+
+// Matcher wrapping the Equivalent predicate.
+MATCHER_P2(EquivalentWithin, target, tolerance,
+ std::string(negation ? "Isn't" : "Is") +
+ ::absl::StrFormat(" within %.2f%% of the target of %zd bytes",
+ tolerance * 100, target)) {
+ if (target == 0) {
+ *result_listener << ::absl::StreamFormat("difference of infinity%%");
+ } else {
+ int64_t delta = static_cast<int64_t>(arg) - static_cast<int64_t>(target);
+ double delta_percent =
+ static_cast<double>(delta) / static_cast<double>(target) * 100;
+ *result_listener << ::absl::StreamFormat("difference of %.2f%%",
+ delta_percent);
+ }
+ return Equivalent(arg, target, tolerance);
+}
+
+void TestInit(int* argc, char*** argv);
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_UTIL_TEST_UTIL_H_