781 lines
23 KiB
C++
781 lines
23 KiB
C++
// 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.
|
|
|
|
// 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:
|
|
//
|
|
// if (GvisorPlatform() == Platform::kPtrace) {
|
|
// ...
|
|
// }
|
|
//
|
|
// SetupGvisorDeathTest ensures that signal handling does not interfere with
|
|
/// tests that rely on fatal signals.
|
|
//
|
|
// 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 "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) GTEST_SKIP() << #expr; \
|
|
} while (0)
|
|
|
|
// Platform contains platform names.
|
|
namespace Platform {
|
|
constexpr char kNative[] = "native";
|
|
constexpr char kPtrace[] = "ptrace";
|
|
constexpr char kKVM[] = "kvm";
|
|
} // namespace Platform
|
|
|
|
bool IsRunningOnGvisor();
|
|
const std::string GvisorPlatform();
|
|
bool IsRunningWithHostinet();
|
|
bool IsRunningWithVFS1();
|
|
|
|
#ifdef __linux__
|
|
void SetupGvisorDeathTest();
|
|
#endif
|
|
|
|
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 {
|
|
|
|
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 " << PosixError(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 " << PosixError(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 << PosixError(expected_);
|
|
}
|
|
|
|
void DescribeNegationTo(::std::ostream* const os) const override {
|
|
*os << "not " << PosixError(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);
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Returns the absolute path to the a data dependency. 'path' is the runfile
|
|
// location relative to workspace root.
|
|
#ifdef __linux__
|
|
std::string RunfilePath(std::string path);
|
|
#endif
|
|
|
|
void TestInit(int* argc, char*** argv);
|
|
int RunAllTests(void);
|
|
|
|
} // namespace testing
|
|
} // namespace gvisor
|
|
|
|
#endif // GVISOR_TEST_UTIL_TEST_UTIL_H_
|