diff options
Diffstat (limited to 'test/util')
42 files changed, 0 insertions, 5567 deletions
diff --git a/test/util/BUILD b/test/util/BUILD deleted file mode 100644 index 52f8b9e1f..000000000 --- a/test/util/BUILD +++ /dev/null @@ -1,315 +0,0 @@ -load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test") -load("//test/syscalls:build_defs.bzl", "select_for_linux") - -package( - default_visibility = ["//:sandbox"], - licenses = ["notice"], -) - -cc_library( - name = "capability_util", - testonly = 1, - srcs = ["capability_util.cc"], - hdrs = ["capability_util.h"], - deps = [ - ":cleanup", - ":memory_util", - ":posix_error", - ":save_util", - ":test_util", - "@com_google_absl//absl/strings", - ], -) - -cc_library( - name = "eventfd_util", - testonly = 1, - hdrs = ["eventfd_util.h"], - deps = [ - ":file_descriptor", - ":posix_error", - ":save_util", - ], -) - -cc_library( - name = "file_descriptor", - testonly = 1, - hdrs = ["file_descriptor.h"], - deps = [ - ":logging", - ":posix_error", - ":save_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "proc_util", - testonly = 1, - srcs = ["proc_util.cc"], - hdrs = ["proc_util.h"], - deps = [ - ":fs_util", - ":posix_error", - ":test_util", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest", - ], -) - -cc_test( - name = "proc_util_test", - size = "small", - srcs = ["proc_util_test.cc"], - deps = [ - ":proc_util", - ":test_main", - ":test_util", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "cleanup", - testonly = 1, - hdrs = ["cleanup.h"], -) - -cc_library( - name = "fs_util", - testonly = 1, - srcs = ["fs_util.cc"], - hdrs = ["fs_util.h"], - deps = [ - ":cleanup", - ":file_descriptor", - ":posix_error", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest", - ], -) - -cc_test( - name = "fs_util_test", - size = "small", - srcs = ["fs_util_test.cc"], - deps = [ - ":fs_util", - ":posix_error", - ":temp_path", - ":test_main", - ":test_util", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "logging", - testonly = 1, - srcs = ["logging.cc"], - hdrs = ["logging.h"], -) - -cc_library( - name = "memory_util", - testonly = 1, - hdrs = ["memory_util.h"], - deps = [ - ":logging", - ":posix_error", - ":save_util", - ":test_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - ], -) - -cc_library( - name = "mount_util", - testonly = 1, - hdrs = ["mount_util.h"], - deps = [ - ":cleanup", - ":posix_error", - ":test_util", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "save_util", - testonly = 1, - srcs = ["save_util.cc"] + - select_for_linux( - ["save_util_linux.cc"], - ["save_util_other.cc"], - ), - hdrs = ["save_util.h"], -) - -cc_library( - name = "multiprocess_util", - testonly = 1, - srcs = ["multiprocess_util.cc"], - hdrs = ["multiprocess_util.h"], - deps = [ - ":cleanup", - ":file_descriptor", - ":posix_error", - ":save_util", - ":test_util", - "@com_google_absl//absl/strings", - ], -) - -cc_library( - name = "posix_error", - testonly = 1, - srcs = ["posix_error.cc"], - hdrs = ["posix_error.h"], - deps = [ - ":logging", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:variant", - "@com_google_googletest//:gtest", - ], -) - -cc_test( - name = "posix_error_test", - size = "small", - srcs = ["posix_error_test.cc"], - deps = [ - ":posix_error", - ":test_main", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "signal_util", - testonly = 1, - srcs = ["signal_util.cc"], - hdrs = ["signal_util.h"], - deps = [ - ":cleanup", - ":posix_error", - ":test_util", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "temp_path", - testonly = 1, - srcs = ["temp_path.cc"], - hdrs = ["temp_path.h"], - deps = [ - ":fs_util", - ":posix_error", - ":test_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "test_util", - testonly = 1, - srcs = ["test_util.cc"], - hdrs = ["test_util.h"], - deps = [ - ":fs_util", - ":logging", - ":posix_error", - ":save_util", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/flags:parse", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "thread_util", - testonly = 1, - hdrs = ["thread_util.h"], - deps = [":logging"], -) - -cc_library( - name = "time_util", - testonly = 1, - srcs = ["time_util.cc"], - hdrs = ["time_util.h"], - deps = [ - "@com_google_absl//absl/time", - ], -) - -cc_library( - name = "timer_util", - testonly = 1, - srcs = ["timer_util.cc"], - hdrs = ["timer_util.h"], - deps = [ - ":cleanup", - ":logging", - ":posix_error", - ":test_util", - "@com_google_absl//absl/time", - "@com_google_googletest//:gtest", - ], -) - -cc_test( - name = "test_util_test", - size = "small", - srcs = ["test_util_test.cc"], - deps = [ - ":test_main", - ":test_util", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "test_main", - testonly = 1, - srcs = ["test_main.cc"], - deps = [":test_util"], -) - -cc_library( - name = "epoll_util", - testonly = 1, - srcs = ["epoll_util.cc"], - hdrs = ["epoll_util.h"], - deps = [ - ":file_descriptor", - ":posix_error", - ":save_util", - "@com_google_googletest//:gtest", - ], -) - -cc_library( - name = "rlimit_util", - testonly = 1, - srcs = ["rlimit_util.cc"], - hdrs = ["rlimit_util.h"], - deps = [ - ":cleanup", - ":logging", - ":posix_error", - ":test_util", - ], -) diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc deleted file mode 100644 index 5d733887b..000000000 --- a/test/util/capability_util.cc +++ /dev/null @@ -1,81 +0,0 @@ -// 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. - -#include "test/util/capability_util.h" - -#include <linux/capability.h> -#include <sched.h> -#include <sys/mman.h> -#include <sys/wait.h> - -#include <iostream> - -#include "absl/strings/str_cat.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<bool> CanCreateUserNamespace() { - // The most reliable way to determine if userns creation is possible is by - // trying to create one; see below. - ASSIGN_OR_RETURN_ERRNO( - auto child_stack, - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - int const child_pid = - clone(+[](void*) { return 0; }, - reinterpret_cast<void*>(child_stack.addr() + kPageSize), - CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr); - if (child_pid > 0) { - int status; - int const ret = waitpid(child_pid, &status, /* options = */ 0); - MaybeSave(); - if (ret < 0) { - return PosixError(errno, "waitpid"); - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - return PosixError( - ESRCH, absl::StrCat("child process exited with status ", status)); - } - return true; - } else if (errno == EPERM) { - // Per clone(2), EPERM can be returned if: - // - // - "CLONE_NEWUSER was specified in flags, but either the effective user ID - // or the effective group ID of the caller does not have a mapping in the - // parent namespace (see user_namespaces(7))." - // - // - "(since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller - // is in a chroot environment (i.e., the caller's root directory does - // not match the root directory of the mount namespace in which it - // resides)." - std::cerr << "clone(CLONE_NEWUSER) failed with EPERM"; - return false; - } else if (errno == EUSERS) { - // "(since Linux 3.11) CLONE_NEWUSER was specified in flags, and the call - // would cause the limit on the number of nested user namespaces to be - // exceeded. See user_namespaces(7)." - std::cerr << "clone(CLONE_NEWUSER) failed with EUSERS"; - return false; - } else { - // Unexpected error code; indicate an actual error. - return PosixError(errno, "clone(CLONE_NEWUSER)"); - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/capability_util.h b/test/util/capability_util.h deleted file mode 100644 index bb9ea1fe5..000000000 --- a/test/util/capability_util.h +++ /dev/null @@ -1,101 +0,0 @@ -// 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 testing capabilities. - -#ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ -#define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ - -#include <errno.h> -#include <linux/capability.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -#ifndef _LINUX_CAPABILITY_VERSION_3 -#error Expecting _LINUX_CAPABILITY_VERSION_3 support -#endif - -namespace gvisor { -namespace testing { - -// HaveCapability returns true if the process has the specified EFFECTIVE -// capability. -inline PosixErrorOr<bool> HaveCapability(int cap) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0; -} - -// SetCapability sets the specified EFFECTIVE capability. -inline PosixError SetCapability(int cap, bool set) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - if (set) { - caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap); - } else { - caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); - } - header = {_LINUX_CAPABILITY_VERSION_3, 0}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); - MaybeSave(); - - return NoError(); -} - -// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE -// capabilities must be a subset of PERMITTED, so those are dropped as well. -inline PosixError DropPermittedCapability(int cap) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); - caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap); - - header = {_LINUX_CAPABILITY_VERSION_3, 0}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); - MaybeSave(); - - return NoError(); -} - -PosixErrorOr<bool> CanCreateUserNamespace(); - -} // namespace testing -} // namespace gvisor -#endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ diff --git a/test/util/cleanup.h b/test/util/cleanup.h deleted file mode 100644 index c76482ef4..000000000 --- a/test/util/cleanup.h +++ /dev/null @@ -1,61 +0,0 @@ -// 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_CLEANUP_H_ -#define GVISOR_TEST_UTIL_CLEANUP_H_ - -#include <functional> -#include <utility> - -namespace gvisor { -namespace testing { - -class Cleanup { - public: - Cleanup() : released_(true) {} - explicit Cleanup(std::function<void()>&& callback) : cb_(callback) {} - - Cleanup(Cleanup&& other) { - released_ = other.released_; - cb_ = other.Release(); - } - - Cleanup& operator=(Cleanup&& other) { - released_ = other.released_; - cb_ = other.Release(); - return *this; - } - - ~Cleanup() { - if (!released_) { - cb_(); - } - } - - std::function<void()>&& Release() { - released_ = true; - return std::move(cb_); - } - - private: - Cleanup(Cleanup const& other) = delete; - - bool released_ = false; - std::function<void(void)> cb_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_CLEANUP_H_ diff --git a/test/util/epoll_util.cc b/test/util/epoll_util.cc deleted file mode 100644 index 2e5051468..000000000 --- a/test/util/epoll_util.cc +++ /dev/null @@ -1,52 +0,0 @@ -// 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. - -#include "test/util/epoll_util.h" - -#include <sys/epoll.h> - -#include "gmock/gmock.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<FileDescriptor> NewEpollFD(int size) { - // "Since Linux 2.6.8, the size argument is ignored, but must be greater than - // zero." - epoll_create(2) - int fd = epoll_create(size); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, "epoll_create"); - } - return FileDescriptor(fd); -} - -PosixError RegisterEpollFD(int epoll_fd, int target_fd, int events, - uint64_t data) { - struct epoll_event event; - event.events = events; - event.data.u64 = data; - int rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, target_fd, &event); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "epoll_ctl"); - } - return NoError(); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/epoll_util.h b/test/util/epoll_util.h deleted file mode 100644 index f233b37d5..000000000 --- a/test/util/epoll_util.h +++ /dev/null @@ -1,36 +0,0 @@ -// 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_EPOLL_UTIL_H_ -#define GVISOR_TEST_UTIL_EPOLL_UTIL_H_ - -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Returns a new epoll file descriptor. -PosixErrorOr<FileDescriptor> NewEpollFD(int size = 1); - -// Registers `target_fd` with the epoll instance represented by `epoll_fd` for -// the epoll events `events`. Events on `target_fd` will be indicated by setting -// data.u64 to `data` in the returned epoll_event. -PosixError RegisterEpollFD(int epoll_fd, int target_fd, int events, - uint64_t data); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_EPOLL_UTIL_H_ diff --git a/test/util/eventfd_util.h b/test/util/eventfd_util.h deleted file mode 100644 index cb9ce829c..000000000 --- a/test/util/eventfd_util.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 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_EVENTFD_UTIL_H_ -#define GVISOR_TEST_UTIL_EVENTFD_UTIL_H_ - -#include <sys/eventfd.h> - -#include <cerrno> - -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -// Returns a new eventfd with the given initial value and flags. -inline PosixErrorOr<FileDescriptor> NewEventFD(unsigned int initval = 0, - int flags = 0) { - int fd = eventfd(initval, flags); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, "eventfd"); - } - return FileDescriptor(fd); -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_EVENTFD_UTIL_H_ diff --git a/test/util/file_descriptor.h b/test/util/file_descriptor.h deleted file mode 100644 index fc5caa55b..000000000 --- a/test/util/file_descriptor.h +++ /dev/null @@ -1,134 +0,0 @@ -// 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_FILE_DESCRIPTOR_H_ -#define GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <string> - -#include "gmock/gmock.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -// FileDescriptor is an RAII type class which takes ownership of a file -// descriptor. It will close the FD when this object goes out of scope. -class FileDescriptor { - public: - // Constructs an empty FileDescriptor (one that does not own a file - // descriptor). - FileDescriptor() = default; - - // Constructs a FileDescriptor that owns fd. If fd is negative, constructs an - // empty FileDescriptor. - explicit FileDescriptor(int fd) { set_fd(fd); } - - FileDescriptor(FileDescriptor&& orig) : fd_(orig.release()) {} - - FileDescriptor& operator=(FileDescriptor&& orig) { - reset(orig.release()); - return *this; - } - - PosixErrorOr<FileDescriptor> Dup() const { - if (fd_ < 0) { - return PosixError(EINVAL, "Attempting to Dup unset fd"); - } - - int fd = dup(fd_); - if (fd < 0) { - return PosixError(errno, absl::StrCat("dup ", fd_)); - } - MaybeSave(); - return FileDescriptor(fd); - } - - FileDescriptor(FileDescriptor const& other) = delete; - FileDescriptor& operator=(FileDescriptor const& other) = delete; - - ~FileDescriptor() { reset(); } - - // If this object is non-empty, returns the owned file descriptor. (Ownership - // is retained by the FileDescriptor.) Otherwise returns -1. - int get() const { return fd_; } - - // If this object is non-empty, transfers ownership of the file descriptor to - // the caller and returns it. Otherwise returns -1. - int release() { - int const fd = fd_; - fd_ = -1; - return fd; - } - - // If this object is non-empty, closes the owned file descriptor (recording a - // test failure if the close fails). - void reset() { reset(-1); } - - // Like no-arg reset(), but the FileDescriptor takes ownership of fd after - // closing its existing file descriptor. - void reset(int fd) { - if (fd_ >= 0) { - TEST_PCHECK(close(fd_) == 0); - MaybeSave(); - } - set_fd(fd); - } - - private: - // Wrapper that coerces negative fd values other than -1 to -1 so that get() - // etc. return -1. - void set_fd(int fd) { fd_ = std::max(fd, -1); } - - int fd_ = -1; -}; - -// Wrapper around open(2) that returns a FileDescriptor. -inline PosixErrorOr<FileDescriptor> Open(std::string const& path, int flags, - mode_t mode = 0) { - int fd = open(path.c_str(), flags, mode); - if (fd < 0) { - return PosixError(errno, absl::StrFormat("open(%s, %#x, %#o)", path.c_str(), - flags, mode)); - } - MaybeSave(); - return FileDescriptor(fd); -} - -// Wrapper around openat(2) that returns a FileDescriptor. -inline PosixErrorOr<FileDescriptor> OpenAt(int dirfd, std::string const& path, - int flags, mode_t mode = 0) { - int fd = openat(dirfd, path.c_str(), flags, mode); - if (fd < 0) { - return PosixError(errno, absl::StrFormat("openat(%d, %s, %#x, %#o)", dirfd, - path, flags, mode)); - } - MaybeSave(); - return FileDescriptor(fd); -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_FILE_DESCRIPTOR_H_ diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc deleted file mode 100644 index f7d231b14..000000000 --- a/test/util/fs_util.cc +++ /dev/null @@ -1,604 +0,0 @@ -// 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. - -#include "test/util/fs_util.h" - -#include <dirent.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "absl/strings/match.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -namespace { -PosixError WriteContentsToFD(int fd, absl::string_view contents) { - int written = 0; - while (static_cast<absl::string_view::size_type>(written) < contents.size()) { - int wrote = write(fd, contents.data() + written, contents.size() - written); - if (wrote < 0) { - if (errno == EINTR) { - continue; - } - return PosixError( - errno, absl::StrCat("WriteContentsToFD fd: ", fd, " write failure.")); - } - written += wrote; - } - return NoError(); -} -} // namespace - -namespace internal { - -// Given a collection of file paths, append them all together, -// ensuring that the proper path separators are inserted between them. -std::string JoinPathImpl(std::initializer_list<absl::string_view> paths) { - std::string result; - - if (paths.size() != 0) { - // This size calculation is worst-case: it assumes one extra "/" for every - // path other than the first. - size_t total_size = paths.size() - 1; - for (const absl::string_view path : paths) total_size += path.size(); - result.resize(total_size); - - auto begin = result.begin(); - auto out = begin; - bool trailing_slash = false; - for (absl::string_view path : paths) { - if (path.empty()) continue; - if (path.front() == '/') { - if (trailing_slash) { - path.remove_prefix(1); - } - } else { - if (!trailing_slash && out != begin) *out++ = '/'; - } - const size_t this_size = path.size(); - memcpy(&*out, path.data(), this_size); - out += this_size; - trailing_slash = out[-1] == '/'; - } - result.erase(out - begin); - } - return result; -} -} // namespace internal - -// Returns a status or the current working directory. -PosixErrorOr<std::string> GetCWD() { - char buffer[PATH_MAX + 1] = {}; - if (getcwd(buffer, PATH_MAX) == nullptr) { - return PosixError(errno, "GetCWD() failed"); - } - - return std::string(buffer); -} - -PosixErrorOr<struct stat> Stat(absl::string_view path) { - struct stat stat_buf; - int res = stat(std::string(path).c_str(), &stat_buf); - if (res < 0) { - return PosixError(errno, absl::StrCat("stat ", path)); - } - return stat_buf; -} - -PosixErrorOr<struct stat> Fstat(int fd) { - struct stat stat_buf; - int res = fstat(fd, &stat_buf); - if (res < 0) { - return PosixError(errno, absl::StrCat("fstat ", fd)); - } - return stat_buf; -} - -PosixErrorOr<bool> Exists(absl::string_view path) { - struct stat stat_buf; - int res = stat(std::string(path).c_str(), &stat_buf); - if (res < 0) { - if (errno == ENOENT) { - return false; - } - return PosixError(errno, absl::StrCat("stat ", path)); - } - return true; -} - -PosixErrorOr<bool> IsDirectory(absl::string_view path) { - ASSIGN_OR_RETURN_ERRNO(struct stat stat_buf, Stat(path)); - if (S_ISDIR(stat_buf.st_mode)) { - return true; - } - - return false; -} - -PosixError Delete(absl::string_view path) { - int res = unlink(std::string(path).c_str()); - if (res < 0) { - return PosixError(errno, absl::StrCat("unlink ", path)); - } - - return NoError(); -} - -PosixError Truncate(absl::string_view path, int length) { - int res = truncate(std::string(path).c_str(), length); - if (res < 0) { - return PosixError(errno, - absl::StrCat("truncate ", path, " to length ", length)); - } - - return NoError(); -} - -PosixError Chmod(absl::string_view path, int mode) { - int res = chmod(std::string(path).c_str(), mode); - if (res < 0) { - return PosixError(errno, absl::StrCat("chmod ", path)); - } - - return NoError(); -} - -PosixError Mkdir(absl::string_view path, int mode) { - int res = mkdir(std::string(path).c_str(), mode); - if (res < 0) { - return PosixError(errno, absl::StrCat("mkdir ", path, " mode ", mode)); - } - - return NoError(); -} - -PosixError Rmdir(absl::string_view path) { - int res = rmdir(std::string(path).c_str()); - if (res < 0) { - return PosixError(errno, absl::StrCat("rmdir ", path)); - } - - return NoError(); -} - -PosixError SetContents(absl::string_view path, absl::string_view contents) { - ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); - if (!exists) { - return PosixError( - ENOENT, absl::StrCat("SetContents file ", path, " doesn't exist.")); - } - - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_WRONLY | O_TRUNC)); - return WriteContentsToFD(fd.get(), contents); -} - -// Create a file with the given contents (if it does not already exist with the -// given mode) and then set the contents. -PosixError CreateWithContents(absl::string_view path, - absl::string_view contents, int mode) { - ASSIGN_OR_RETURN_ERRNO( - auto fd, Open(std::string(path), O_WRONLY | O_CREAT | O_TRUNC, mode)); - return WriteContentsToFD(fd.get(), contents); -} - -PosixError GetContents(absl::string_view path, std::string* output) { - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(std::string(path), O_RDONLY)); - output->clear(); - - // Keep reading until we hit an EOF or an error. - return GetContentsFD(fd.get(), output); -} - -PosixErrorOr<std::string> GetContents(absl::string_view path) { - std::string ret; - RETURN_IF_ERRNO(GetContents(path, &ret)); - return ret; -} - -PosixErrorOr<std::string> GetContentsFD(int fd) { - std::string ret; - RETURN_IF_ERRNO(GetContentsFD(fd, &ret)); - return ret; -} - -PosixError GetContentsFD(int fd, std::string* output) { - // Keep reading until we hit an EOF or an error. - while (true) { - char buf[16 * 1024] = {}; // Read in 16KB chunks. - int bytes_read = read(fd, buf, sizeof(buf)); - if (bytes_read < 0) { - if (errno == EINTR) { - continue; - } - return PosixError(errno, "GetContentsFD read failure."); - } - - if (bytes_read == 0) { - break; // EOF. - } - - output->append(buf, bytes_read); - } - return NoError(); -} - -PosixErrorOr<std::string> ReadLink(absl::string_view path) { - char buf[PATH_MAX + 1] = {}; - int ret = readlink(std::string(path).c_str(), buf, PATH_MAX); - if (ret < 0) { - return PosixError(errno, absl::StrCat("readlink ", path)); - } - - return std::string(buf, ret); -} - -PosixError WalkTree( - absl::string_view path, bool recursive, - const std::function<void(absl::string_view, const struct stat&)>& cb) { - DIR* dir = opendir(std::string(path).c_str()); - if (dir == nullptr) { - return PosixError(errno, absl::StrCat("opendir ", path)); - } - auto dir_closer = Cleanup([&dir]() { closedir(dir); }); - while (true) { - // Readdir(3): If the end of the directory stream is reached, NULL is - // returned and errno is not changed. If an error occurs, NULL is returned - // and errno is set appropriately. To distinguish end of stream and from an - // error, set errno to zero before calling readdir() and then check the - // value of errno if NULL is returned. - errno = 0; - struct dirent* dp = readdir(dir); - if (dp == nullptr) { - if (errno != 0) { - return PosixError(errno, absl::StrCat("readdir ", path)); - } - break; // We're done. - } - - if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { - // Skip dots. - continue; - } - - auto full_path = JoinPath(path, dp->d_name); - ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(full_path)); - if (S_ISDIR(s.st_mode) && recursive) { - RETURN_IF_ERRNO(WalkTree(full_path, recursive, cb)); - } else { - cb(full_path, s); - } - } - // We're done walking so let's invoke our cleanup callback now. - dir_closer.Release()(); - - // And we have to dispatch the callback on the base directory. - ASSIGN_OR_RETURN_ERRNO(struct stat s, Stat(path)); - cb(path, s); - - return NoError(); -} - -PosixErrorOr<std::vector<std::string>> ListDir(absl::string_view abspath, - bool skipdots) { - std::vector<std::string> files; - - DIR* dir = opendir(std::string(abspath).c_str()); - if (dir == nullptr) { - return PosixError(errno, absl::StrCat("opendir ", abspath)); - } - auto dir_closer = Cleanup([&dir]() { closedir(dir); }); - while (true) { - // Readdir(3): If the end of the directory stream is reached, NULL is - // returned and errno is not changed. If an error occurs, NULL is returned - // and errno is set appropriately. To distinguish end of stream and from an - // error, set errno to zero before calling readdir() and then check the - // value of errno if NULL is returned. - errno = 0; - struct dirent* dp = readdir(dir); - if (dp == nullptr) { - if (errno != 0) { - return PosixError(errno, absl::StrCat("readdir ", abspath)); - } - break; // We're done. - } - - if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) { - if (skipdots) { - continue; - } - } - files.push_back(std::string(dp->d_name)); - } - - return files; -} - -PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs, - int* undeleted_files) { - ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); - if (!exists) { - return PosixError(ENOENT, absl::StrCat(path, " does not exist")); - } - - ASSIGN_OR_RETURN_ERRNO(bool dir, IsDirectory(path)); - if (!dir) { - // Nothing recursive needs to happen we can just call Delete. - auto status = Delete(path); - if (!status.ok() && undeleted_files) { - (*undeleted_files)++; - } - return status; - } - - return WalkTree(path, /*recursive=*/true, - [&](absl::string_view absolute_path, const struct stat& s) { - if (S_ISDIR(s.st_mode)) { - auto rm_status = Rmdir(absolute_path); - if (!rm_status.ok() && undeleted_dirs) { - (*undeleted_dirs)++; - } - } else { - auto delete_status = Delete(absolute_path); - if (!delete_status.ok() && undeleted_files) { - (*undeleted_files)++; - } - } - }); -} - -PosixError RecursivelyCreateDir(absl::string_view path) { - if (path.empty() || path == "/") { - return PosixError(EINVAL, "Cannot create root!"); - } - - // Does it already exist, if so we're done. - ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(path)); - if (exists) { - return NoError(); - } - - // Do we need to create directories under us? - auto dirname = Dirname(path); - ASSIGN_OR_RETURN_ERRNO(exists, Exists(dirname)); - if (!exists) { - RETURN_IF_ERRNO(RecursivelyCreateDir(dirname)); - } - - return Mkdir(path); -} - -// Makes a path absolute with respect to an optional base. If no base is -// provided it will use the current working directory. -PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename, - absl::string_view base) { - if (filename.empty()) { - return PosixError(EINVAL, "filename cannot be empty."); - } - - if (filename[0] == '/') { - // This path is already absolute. - return std::string(filename); - } - - std::string actual_base; - if (!base.empty()) { - actual_base = std::string(base); - } else { - auto cwd_or = GetCWD(); - RETURN_IF_ERRNO(cwd_or.error()); - actual_base = cwd_or.ValueOrDie(); - } - - // Reverse iterate removing trailing slashes, effectively right trim '/'. - for (int i = actual_base.size() - 1; i >= 0 && actual_base[i] == '/'; --i) { - actual_base.erase(i, 1); - } - - if (filename == ".") { - return actual_base.empty() ? "/" : actual_base; - } - - return absl::StrCat(actual_base, "/", filename); -} - -std::string CleanPath(const absl::string_view unclean_path) { - std::string path = std::string(unclean_path); - const char *src = path.c_str(); - std::string::iterator dst = path.begin(); - - // Check for absolute path and determine initial backtrack limit. - const bool is_absolute_path = *src == '/'; - if (is_absolute_path) { - *dst++ = *src++; - while (*src == '/') ++src; - } - std::string::const_iterator backtrack_limit = dst; - - // Process all parts - while (*src) { - bool parsed = false; - - if (src[0] == '.') { - // 1dot ".<whateverisnext>", check for END or SEP. - if (src[1] == '/' || !src[1]) { - if (*++src) { - ++src; - } - parsed = true; - } else if (src[1] == '.' && (src[2] == '/' || !src[2])) { - // 2dot END or SEP (".." | "../<whateverisnext>"). - src += 2; - if (dst != backtrack_limit) { - // We can backtrack the previous part - for (--dst; dst != backtrack_limit && dst[-1] != '/'; --dst) { - // Empty. - } - } else if (!is_absolute_path) { - // Failed to backtrack and we can't skip it either. Rewind and copy. - src -= 2; - *dst++ = *src++; - *dst++ = *src++; - if (*src) { - *dst++ = *src; - } - // We can never backtrack over a copied "../" part so set new limit. - backtrack_limit = dst; - } - if (*src) { - ++src; - } - parsed = true; - } - } - - // If not parsed, copy entire part until the next SEP or EOS. - if (!parsed) { - while (*src && *src != '/') { - *dst++ = *src++; - } - if (*src) { - *dst++ = *src++; - } - } - - // Skip consecutive SEP occurrences - while (*src == '/') { - ++src; - } - } - - // Calculate and check the length of the cleaned path. - int path_length = dst - path.begin(); - if (path_length != 0) { - // Remove trailing '/' except if it is root path ("/" ==> path_length := 1) - if (path_length > 1 && path[path_length - 1] == '/') { - --path_length; - } - path.resize(path_length); - } else { - // The cleaned path is empty; assign "." as per the spec. - path.assign(1, '.'); - } - return path; -} - -PosixErrorOr<std::string> GetRelativePath(absl::string_view source, - absl::string_view dest) { - if (!absl::StartsWith(source, "/") || !absl::StartsWith(dest, "/")) { - // At least one of the inputs is not an absolute path. - return PosixError( - EINVAL, - "GetRelativePath: At least one of the inputs is not an absolute path."); - } - const std::string clean_source = CleanPath(source); - const std::string clean_dest = CleanPath(dest); - auto source_parts = absl::StrSplit(clean_source, '/', absl::SkipEmpty()); - auto dest_parts = absl::StrSplit(clean_dest, '/', absl::SkipEmpty()); - auto source_iter = source_parts.begin(); - auto dest_iter = dest_parts.begin(); - - // Advance past common prefix. - while (source_iter != source_parts.end() && dest_iter != dest_parts.end() && - *source_iter == *dest_iter) { - ++source_iter; - ++dest_iter; - } - - // Build result backtracking. - std::string result = ""; - while (source_iter != source_parts.end()) { - absl::StrAppend(&result, "../"); - ++source_iter; - } - - // Add remaining path to dest. - while (dest_iter != dest_parts.end()) { - absl::StrAppend(&result, *dest_iter, "/"); - ++dest_iter; - } - - if (result.empty()) { - return std::string("."); - } - - // Remove trailing slash. - result.erase(result.size() - 1); - return result; -} - -absl::string_view Dirname(absl::string_view path) { - return SplitPath(path).first; -} - -absl::string_view Basename(absl::string_view path) { - return SplitPath(path).second; -} - -std::pair<absl::string_view, absl::string_view> SplitPath( - absl::string_view path) { - std::string::size_type pos = path.find_last_of('/'); - - // Handle the case with no '/' in 'path'. - if (pos == absl::string_view::npos) { - return std::make_pair(path.substr(0, 0), path); - } - - // Handle the case with a single leading '/' in 'path'. - if (pos == 0) { - return std::make_pair(path.substr(0, 1), absl::ClippedSubstr(path, 1)); - } - - return std::make_pair(path.substr(0, pos), - absl::ClippedSubstr(path, pos + 1)); -} - -std::string JoinPath(absl::string_view path1, absl::string_view path2) { - if (path1.empty()) { - return std::string(path2); - } - if (path2.empty()) { - return std::string(path1); - } - - if (path1.back() == '/') { - if (path2.front() == '/') { - return absl::StrCat(path1, absl::ClippedSubstr(path2, 1)); - } - } else { - if (path2.front() != '/') { - return absl::StrCat(path1, "/", path2); - } - } - return absl::StrCat(path1, path2); -} - -PosixErrorOr<std::string> ProcessExePath(int pid) { - if (pid <= 0) { - return PosixError(EINVAL, "Invalid pid specified"); - } - - return ReadLink(absl::StrCat("/proc/", pid, "/exe")); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/fs_util.h b/test/util/fs_util.h deleted file mode 100644 index e5b555891..000000000 --- a/test/util/fs_util.h +++ /dev/null @@ -1,185 +0,0 @@ -// 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_FS_UTIL_H_ -#define GVISOR_TEST_UTIL_FS_UTIL_H_ - -#include <dirent.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "absl/strings/string_view.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { -// Returns a status or the current working directory. -PosixErrorOr<std::string> GetCWD(); - -// Returns true/false depending on whether or not path exists, or an error if it -// can't be determined. -PosixErrorOr<bool> Exists(absl::string_view path); - -// Returns a stat structure for the given path or an error. -PosixErrorOr<struct stat> Stat(absl::string_view path); - -// Returns a stat struct for the given fd. -PosixErrorOr<struct stat> Fstat(int fd); - -// Deletes the file or directory at path or returns an error. -PosixError Delete(absl::string_view path); - -// Changes the mode of a file or returns an error. -PosixError Chmod(absl::string_view path, int mode); - -// Truncates a file to the given length or returns an error. -PosixError Truncate(absl::string_view path, int length); - -// Returns true/false depending on whether or not the path is a directory or -// returns an error. -PosixErrorOr<bool> IsDirectory(absl::string_view path); - -// Makes a directory or returns an error. -PosixError Mkdir(absl::string_view path, int mode = 0755); - -// Removes a directory or returns an error. -PosixError Rmdir(absl::string_view path); - -// Attempts to set the contents of a file or returns an error. -PosixError SetContents(absl::string_view path, absl::string_view contents); - -// Creates a file with the given contents and mode or returns an error. -PosixError CreateWithContents(absl::string_view path, - absl::string_view contents, int mode = 0666); - -// Attempts to read the entire contents of the file into the provided string -// buffer or returns an error. -PosixError GetContents(absl::string_view path, std::string* output); - -// Attempts to read the entire contents of the file or returns an error. -PosixErrorOr<std::string> GetContents(absl::string_view path); - -// Attempts to read the entire contents of the provided fd into the provided -// string or returns an error. -PosixError GetContentsFD(int fd, std::string* output); - -// Attempts to read the entire contents of the provided fd or returns an error. -PosixErrorOr<std::string> GetContentsFD(int fd); - -// Executes the readlink(2) system call or returns an error. -PosixErrorOr<std::string> ReadLink(absl::string_view path); - -// WalkTree will walk a directory tree in a depth first search manner (if -// recursive). It will invoke a provided callback for each file and directory, -// the parent will always be invoked last making this appropriate for things -// such as deleting an entire directory tree. -// -// This method will return an error when it's unable to access the provided -// path, or when the path is not a directory. -PosixError WalkTree( - absl::string_view path, bool recursive, - const std::function<void(absl::string_view, const struct stat&)>& cb); - -// Returns the base filenames for all files under a given absolute path. If -// skipdots is true the returned vector will not contain "." or "..". This -// method does not walk the tree recursively it only returns the elements -// in that directory. -PosixErrorOr<std::vector<std::string>> ListDir(absl::string_view abspath, - bool skipdots); - -// Attempt to recursively delete a directory or file. Returns an error and -// the number of undeleted directories and files. If either -// undeleted_dirs or undeleted_files is nullptr then it will not be used. -PosixError RecursivelyDelete(absl::string_view path, int* undeleted_dirs, - int* undeleted_files); - -// Recursively create the directory provided or return an error. -PosixError RecursivelyCreateDir(absl::string_view path); - -// Makes a path absolute with respect to an optional base. If no base is -// provided it will use the current working directory. -PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename, - absl::string_view base); - -// Generates a relative path from the source directory to the destination -// (dest) file or directory. This uses ../ when necessary for destinations -// which are not nested within the source. Both source and dest are required -// to be absolute paths, and an empty string will be returned if they are not. -PosixErrorOr<std::string> GetRelativePath(absl::string_view source, - absl::string_view dest); - -// Returns the part of the path before the final "/", EXCEPT: -// * If there is a single leading "/" in the path, the result will be the -// leading "/". -// * If there is no "/" in the path, the result is the empty prefix of the -// input string. -absl::string_view Dirname(absl::string_view path); - -// Return the parts of the path, split on the final "/". If there is no -// "/" in the path, the first part of the output is empty and the second -// is the input. If the only "/" in the path is the first character, it is -// the first part of the output. -std::pair<absl::string_view, absl::string_view> SplitPath( - absl::string_view path); - -// Returns the part of the path after the final "/". If there is no -// "/" in the path, the result is the same as the input. -// Note that this function's behavior differs from the Unix basename -// command if path ends with "/". For such paths, this function returns the -// empty string. -absl::string_view Basename(absl::string_view path); - -// Collapse duplicate "/"s, resolve ".." and "." path elements, remove -// trailing "/". -// -// NOTE: This respects relative vs. absolute paths, but does not -// invoke any system calls (getcwd(2)) in order to resolve relative -// paths wrt actual working directory. That is, this is purely a -// string manipulation, completely independent of process state. -std::string CleanPath(absl::string_view path); - -// Returns the full path to the executable of the given pid or a PosixError. -PosixErrorOr<std::string> ProcessExePath(int pid); - -namespace internal { -// Not part of the public API. -std::string JoinPathImpl(std::initializer_list<absl::string_view> paths); -} // namespace internal - -// Join multiple paths together. -// All paths will be treated as relative paths, regardless of whether or not -// they start with a leading '/'. That is, all paths will be concatenated -// together, with the appropriate path separator inserted in between. -// Arguments must be convertible to absl::string_view. -// -// Usage: -// std::string path = JoinPath("/foo", dirname, filename); -// std::string path = JoinPath(FLAGS_test_srcdir, filename); -// -// 0, 1, 2-path specializations exist to optimize common cases. -inline std::string JoinPath() { return std::string(); } -inline std::string JoinPath(absl::string_view path) { - return std::string(path.data(), path.size()); -} - -std::string JoinPath(absl::string_view path1, absl::string_view path2); -template <typename... T> -inline std::string JoinPath(absl::string_view path1, absl::string_view path2, - absl::string_view path3, const T&... args) { - return internal::JoinPathImpl({path1, path2, path3, args...}); -} -} // namespace testing -} // namespace gvisor -#endif // GVISOR_TEST_UTIL_FS_UTIL_H_ diff --git a/test/util/fs_util_test.cc b/test/util/fs_util_test.cc deleted file mode 100644 index 2a200320a..000000000 --- a/test/util/fs_util_test.cc +++ /dev/null @@ -1,103 +0,0 @@ -// 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. - -#include <errno.h> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(FsUtilTest, RecursivelyCreateDirManualDelete) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string base_path = - JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); - - ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); - ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); - - // Delete everything until we hit root and then stop, we want to try this - // without using RecursivelyDelete. - std::string cur_path = base_path; - while (cur_path != root.path()) { - ASSERT_THAT(Exists(cur_path), IsPosixErrorOkAndHolds(true)); - ASSERT_NO_ERRNO(Rmdir(cur_path)); - ASSERT_THAT(Exists(cur_path), IsPosixErrorOkAndHolds(false)); - auto dir = Dirname(cur_path); - cur_path = std::string(dir); - } -} - -TEST(FsUtilTest, RecursivelyCreateAndDeleteDir) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string base_path = - JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); - - ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); - ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); - - const std::string sub_path = JoinPath(root.path(), "a"); - ASSERT_NO_ERRNO(RecursivelyDelete(sub_path, nullptr, nullptr)); - ASSERT_THAT(Exists(sub_path), IsPosixErrorOkAndHolds(false)); -} - -TEST(FsUtilTest, RecursivelyCreateAndDeletePartial) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string base_path = - JoinPath(root.path(), "/a/b/c/d/e/f/g/h/i/j/k/l/m"); - - ASSERT_THAT(Exists(base_path), IsPosixErrorOkAndHolds(false)); - ASSERT_NO_ERRNO(RecursivelyCreateDir(base_path)); - - const std::string a = JoinPath(root.path(), "a"); - auto listing = ASSERT_NO_ERRNO_AND_VALUE(ListDir(a, true)); - ASSERT_THAT(listing, ::testing::Contains("b")); - ASSERT_EQ(listing.size(), 1); - - listing = ASSERT_NO_ERRNO_AND_VALUE(ListDir(a, false)); - ASSERT_THAT(listing, ::testing::Contains(".")); - ASSERT_THAT(listing, ::testing::Contains("..")); - ASSERT_THAT(listing, ::testing::Contains("b")); - ASSERT_EQ(listing.size(), 3); - - const std::string sub_path = JoinPath(root.path(), "/a/b/c/d/e/f"); - - ASSERT_NO_ERRNO( - CreateWithContents(JoinPath(Dirname(sub_path), "file"), "Hello World")); - std::string contents = ""; - ASSERT_NO_ERRNO(GetContents(JoinPath(Dirname(sub_path), "file"), &contents)); - ASSERT_EQ(contents, "Hello World"); - - ASSERT_NO_ERRNO(RecursivelyDelete(sub_path, nullptr, nullptr)); - ASSERT_THAT(Exists(sub_path), IsPosixErrorOkAndHolds(false)); - - // The parent of the subpath (directory e) should still exist. - ASSERT_THAT(Exists(Dirname(sub_path)), IsPosixErrorOkAndHolds(true)); - - // The file we created along side f should also still exist. - ASSERT_THAT(Exists(JoinPath(Dirname(sub_path), "file")), - IsPosixErrorOkAndHolds(true)); -} -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/logging.cc b/test/util/logging.cc deleted file mode 100644 index 5d5e76c46..000000000 --- a/test/util/logging.cc +++ /dev/null @@ -1,97 +0,0 @@ -// 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. - -#include "test/util/logging.h" - -#include <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <unistd.h> - -namespace gvisor { -namespace testing { - -namespace { - -// We implement this here instead of using test_util to avoid cyclic -// dependencies. -int Write(int fd, const char* buf, size_t size) { - size_t written = 0; - while (written < size) { - int res = write(fd, buf + written, size - written); - if (res < 0 && errno == EINTR) { - continue; - } else if (res <= 0) { - break; - } - - written += res; - } - return static_cast<int>(written); -} - -// Write 32-bit decimal number to fd. -int WriteNumber(int fd, uint32_t val) { - constexpr char kDigits[] = "0123456789"; - constexpr int kBase = 10; - - // 10 chars for 32-bit number in decimal, 1 char for the NUL-terminator. - constexpr int kBufferSize = 11; - char buf[kBufferSize]; - - // Convert the number to string. - char* s = buf + sizeof(buf) - 1; - size_t size = 0; - - *s = '\0'; - do { - s--; - size++; - - *s = kDigits[val % kBase]; - val /= kBase; - } while (val); - - return Write(fd, s, size); -} - -} // namespace - -void CheckFailure(const char* cond, size_t cond_size, const char* msg, - size_t msg_size, bool include_errno) { - int saved_errno = errno; - - constexpr char kCheckFailure[] = "Check failed: "; - Write(2, kCheckFailure, sizeof(kCheckFailure) - 1); - Write(2, cond, cond_size); - - if (msg != nullptr) { - Write(2, ": ", 2); - Write(2, msg, msg_size); - } - - if (include_errno) { - constexpr char kErrnoMessage[] = " (errno "; - Write(2, kErrnoMessage, sizeof(kErrnoMessage) - 1); - WriteNumber(2, saved_errno); - Write(2, ")", 1); - } - - Write(2, "\n", 1); - - abort(); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/logging.h b/test/util/logging.h deleted file mode 100644 index 589166fab..000000000 --- a/test/util/logging.h +++ /dev/null @@ -1,73 +0,0 @@ -// 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_LOGGING_H_ -#define GVISOR_TEST_UTIL_LOGGING_H_ - -#include <stddef.h> - -namespace gvisor { -namespace testing { - -void CheckFailure(const char* cond, size_t cond_size, const char* msg, - size_t msg_size, bool include_errno); - -// If cond is false, aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_CHECK(cond) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, nullptr, \ - 0, false); \ - } \ - } while (0) - -// If cond is false, logs msg then aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_CHECK_MSG(cond, msg) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, msg, \ - sizeof(msg) - 1, false); \ - } \ - } while (0) - -// If cond is false, logs errno, then aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_PCHECK(cond) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, nullptr, \ - 0, true); \ - } \ - } while (0) - -// If cond is false, logs msg and errno, then aborts the current process. -// -// This macro is async-signal-safe. -#define TEST_PCHECK_MSG(cond, msg) \ - do { \ - if (!(cond)) { \ - ::gvisor::testing::CheckFailure(#cond, sizeof(#cond) - 1, msg, \ - sizeof(msg) - 1, true); \ - } \ - } while (0) - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_LOGGING_H_ diff --git a/test/util/memory_util.h b/test/util/memory_util.h deleted file mode 100644 index e189b73e8..000000000 --- a/test/util/memory_util.h +++ /dev/null @@ -1,147 +0,0 @@ -// 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_MEMORY_UTIL_H_ -#define GVISOR_TEST_UTIL_MEMORY_UTIL_H_ - -#include <errno.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/mman.h> - -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// RAII type for mmap'ed memory. Only usable in tests due to use of a test-only -// macro that can't be named without invoking the presubmit's wrath. -class Mapping { - public: - // Constructs a mapping that owns nothing. - Mapping() = default; - - // Constructs a mapping that owns the mmapped memory [ptr, ptr+len). Most - // users should use Mmap or MmapAnon instead. - Mapping(void* ptr, size_t len) : ptr_(ptr), len_(len) {} - - Mapping(Mapping&& orig) : ptr_(orig.ptr_), len_(orig.len_) { orig.release(); } - - Mapping& operator=(Mapping&& orig) { - ptr_ = orig.ptr_; - len_ = orig.len_; - orig.release(); - return *this; - } - - Mapping(Mapping const&) = delete; - Mapping& operator=(Mapping const&) = delete; - - ~Mapping() { reset(); } - - void* ptr() const { return ptr_; } - size_t len() const { return len_; } - - // Returns a pointer to the end of the mapping. Useful for when the mapping - // is used as a thread stack. - void* endptr() const { return reinterpret_cast<void*>(addr() + len_); } - - // Returns the start of this mapping cast to uintptr_t for ease of pointer - // arithmetic. - uintptr_t addr() const { return reinterpret_cast<uintptr_t>(ptr_); } - - // Returns the end of this mapping cast to uintptr_t for ease of pointer - // arithmetic. - uintptr_t endaddr() const { return reinterpret_cast<uintptr_t>(endptr()); } - - // Returns this mapping as a StringPiece for ease of comparison. - // - // This function is named view in anticipation of the eventual replacement of - // StringPiece with std::string_view. - absl::string_view view() const { - return absl::string_view(static_cast<char const*>(ptr_), len_); - } - - // These are both named reset for consistency with standard smart pointers. - - void reset(void* ptr, size_t len) { - if (len_) { - TEST_PCHECK(munmap(ptr_, len_) == 0); - } - ptr_ = ptr; - len_ = len; - } - - void reset() { reset(nullptr, 0); } - - void release() { - ptr_ = nullptr; - len_ = 0; - } - - private: - void* ptr_ = nullptr; - size_t len_ = 0; -}; - -// Wrapper around mmap(2) that returns a Mapping. -inline PosixErrorOr<Mapping> Mmap(void* addr, size_t length, int prot, - int flags, int fd, off_t offset) { - void* ptr = mmap(addr, length, prot, flags, fd, offset); - if (ptr == MAP_FAILED) { - return PosixError( - errno, absl::StrFormat("mmap(%p, %d, %x, %x, %d, %d)", addr, length, - prot, flags, fd, offset)); - } - MaybeSave(); - return Mapping(ptr, length); -} - -// Convenience wrapper around Mmap for anonymous mappings. -inline PosixErrorOr<Mapping> MmapAnon(size_t length, int prot, int flags) { - return Mmap(nullptr, length, prot, flags | MAP_ANONYMOUS, -1, 0); -} - -// Wrapper for mremap that returns a PosixErrorOr<>, since the return type of -// void* isn't directly compatible with SyscallSucceeds. -inline PosixErrorOr<void*> Mremap(void* old_address, size_t old_size, - size_t new_size, int flags, - void* new_address) { - void* rv = mremap(old_address, old_size, new_size, flags, new_address); - if (rv == MAP_FAILED) { - return PosixError(errno, "mremap failed"); - } - return rv; -} - -// Returns true if the page containing addr is mapped. -inline bool IsMapped(uintptr_t addr) { - int const rv = msync(reinterpret_cast<void*>(addr & ~(kPageSize - 1)), - kPageSize, MS_ASYNC); - if (rv == 0) { - return true; - } - TEST_PCHECK_MSG(errno == ENOMEM, "msync failed with unexpected errno"); - return false; -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_MEMORY_UTIL_H_ diff --git a/test/util/mount_util.h b/test/util/mount_util.h deleted file mode 100644 index 38ec6c8a1..000000000 --- a/test/util/mount_util.h +++ /dev/null @@ -1,50 +0,0 @@ -// 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_MOUNT_UTIL_H_ -#define GVISOR_TEST_UTIL_MOUNT_UTIL_H_ - -#include <errno.h> -#include <sys/mount.h> -#include <functional> -#include <string> - -#include "gmock/gmock.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Mount mounts the filesystem, and unmounts when the returned reference is -// destroyed. -inline PosixErrorOr<Cleanup> Mount(const std::string &source, - const std::string &target, - const std::string &fstype, uint64_t mountflags, - const std::string &data, - uint64_t umountflags) { - if (mount(source.c_str(), target.c_str(), fstype.c_str(), mountflags, - data.c_str()) == -1) { - return PosixError(errno, "mount failed"); - } - return Cleanup([target, umountflags]() { - EXPECT_THAT(umount2(target.c_str(), umountflags), SyscallSucceeds()); - }); -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_MOUNT_UTIL_H_ diff --git a/test/util/multiprocess_util.cc b/test/util/multiprocess_util.cc deleted file mode 100644 index 95f5f3b4f..000000000 --- a/test/util/multiprocess_util.cc +++ /dev/null @@ -1,139 +0,0 @@ -// 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. - -#include "test/util/multiprocess_util.h" - -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <sys/prctl.h> -#include <unistd.h> - -#include "absl/strings/str_cat.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, - const ExecveArray& argv, - const ExecveArray& envv, - const std::function<void()>& fn, pid_t* child, - int* execve_errno) { - int pfds[2]; - int ret = pipe2(pfds, O_CLOEXEC); - if (ret < 0) { - return PosixError(errno, "pipe failed"); - } - FileDescriptor rfd(pfds[0]); - FileDescriptor wfd(pfds[1]); - - int parent_stdout = dup(STDOUT_FILENO); - if (parent_stdout < 0) { - return PosixError(errno, "dup stdout"); - } - int parent_stderr = dup(STDERR_FILENO); - if (parent_stdout < 0) { - return PosixError(errno, "dup stderr"); - } - - pid_t pid = fork(); - if (pid < 0) { - return PosixError(errno, "fork failed"); - } else if (pid == 0) { - // Child. - rfd.reset(); - if (dup2(parent_stdout, STDOUT_FILENO) < 0) { - _exit(3); - } - if (dup2(parent_stderr, STDERR_FILENO) < 0) { - _exit(4); - } - close(parent_stdout); - close(parent_stderr); - - // Clean ourself up in case the parent doesn't. - if (prctl(PR_SET_PDEATHSIG, SIGKILL)) { - _exit(3); - } - - if (fn) { - fn(); - } - - execve(filename.c_str(), argv.get(), envv.get()); - int error = errno; - if (WriteFd(pfds[1], &error, sizeof(error)) != sizeof(error)) { - // We can't do much if the write fails, but we can at least exit with a - // different code. - _exit(2); - } - _exit(1); - } - - // Parent. - if (child) { - *child = pid; - } - - auto cleanup = Cleanup([pid] { - kill(pid, SIGKILL); - RetryEINTR(waitpid)(pid, nullptr, 0); - }); - - wfd.reset(); - - int read_errno; - ret = ReadFd(rfd.get(), &read_errno, sizeof(read_errno)); - if (ret == 0) { - // Other end of the pipe closed, execve must have succeeded. - read_errno = 0; - } else if (ret < 0) { - return PosixError(errno, "read pipe failed"); - } else if (ret != sizeof(read_errno)) { - return PosixError(EPIPE, absl::StrCat("pipe read wrong size ", ret)); - } - - if (execve_errno) { - *execve_errno = read_errno; - } - - return std::move(cleanup); -} - -PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn) { - pid_t pid = fork(); - if (pid == 0) { - fn(); - _exit(0); - } - MaybeSave(); - if (pid < 0) { - return PosixError(errno, "fork failed"); - } - - int status; - if (waitpid(pid, &status, 0) < 0) { - return PosixError(errno, "waitpid failed"); - } - - return status; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/multiprocess_util.h b/test/util/multiprocess_util.h deleted file mode 100644 index 0aecd3439..000000000 --- a/test/util/multiprocess_util.h +++ /dev/null @@ -1,114 +0,0 @@ -// 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_MULTIPROCESS_UTIL_H_ -#define GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ - -#include <unistd.h> - -#include <algorithm> -#include <string> -#include <utility> -#include <vector> - -#include "absl/strings/string_view.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Immutable holder for a dynamically-sized array of pointers to mutable char, -// terminated by a null pointer, as required for the argv and envp arguments to -// execve(2). -class ExecveArray { - public: - // Constructs an empty ExecveArray. - ExecveArray() = default; - - // Constructs an ExecveArray by copying strings from the given range. T must - // be a range over ranges of char. - template <typename T> - explicit ExecveArray(T const& strs) : ExecveArray(strs.begin(), strs.end()) {} - - // Constructs an ExecveArray by copying strings from [first, last). InputIt - // must be an input iterator over a range over char. - template <typename InputIt> - ExecveArray(InputIt first, InputIt last) { - std::vector<size_t> offsets; - auto output_it = std::back_inserter(str_); - for (InputIt it = first; it != last; ++it) { - offsets.push_back(str_.size()); - auto const& s = *it; - std::copy(s.begin(), s.end(), output_it); - str_.push_back('\0'); - } - ptrs_.reserve(offsets.size() + 1); - for (auto offset : offsets) { - ptrs_.push_back(str_.data() + offset); - } - ptrs_.push_back(nullptr); - } - - // Constructs an ExecveArray by copying strings from list. This overload must - // exist independently of the single-argument template constructor because - // std::initializer_list does not participate in template argument deduction - // (i.e. cannot be type-inferred in an invocation of the templated - // constructor). - /* implicit */ ExecveArray(std::initializer_list<absl::string_view> list) - : ExecveArray(list.begin(), list.end()) {} - - // Disable move construction and assignment since ptrs_ points into str_. - ExecveArray(ExecveArray&&) = delete; - ExecveArray& operator=(ExecveArray&&) = delete; - - char* const* get() const { return ptrs_.data(); } - size_t get_size() { return str_.size(); } - - private: - std::vector<char> str_; - std::vector<char*> ptrs_; -}; - -// Simplified version of SubProcess. Returns OK and a cleanup function to kill -// the child if it made it to execve. -// -// fn is run between fork and exec. If it needs to fail, it should exit the -// process. -// -// The child pid is returned via child, if provided. -// execve's error code is returned via execve_errno, if provided. -PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, - const ExecveArray& argv, - const ExecveArray& envv, - const std::function<void()>& fn, pid_t* child, - int* execve_errno); - -inline PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename, - const ExecveArray& argv, - const ExecveArray& envv, pid_t* child, - int* execve_errno) { - return ForkAndExec(filename, argv, envv, [] {}, child, execve_errno); -} - -// Calls fn in a forked subprocess and returns the exit status of the -// subprocess. -// -// fn must be async-signal-safe. -PosixErrorOr<int> InForkedProcess(const std::function<void()>& fn); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_MULTIPROCESS_UTIL_H_ diff --git a/test/util/posix_error.cc b/test/util/posix_error.cc deleted file mode 100644 index cebf7e0ac..000000000 --- a/test/util/posix_error.cc +++ /dev/null @@ -1,98 +0,0 @@ -// 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. - -#include "test/util/posix_error.h" - -#include <cassert> -#include <cerrno> -#include <cstring> -#include <string> - -#include "absl/strings/str_cat.h" - -namespace gvisor { -namespace testing { - -std::string PosixError::ToString() const { - if (ok()) { - return "No Error"; - } - - std::string ret; - - char strerrno_buf[1024] = {}; - - auto res = strerror_r(errno_, strerrno_buf, sizeof(strerrno_buf)); - -// The GNU version of strerror_r always returns a non-null char* pointing to a -// buffer containing the stringified errno; the XSI version returns a positive -// errno which indicates the result of writing the stringified errno into the -// supplied buffer. The gymnastics below are needed to support both. -#ifndef _GNU_SOURCE - if (res != 0) { - ret = absl::StrCat("PosixError(errno=", errno_, " strerror_r FAILED(", ret, - "))"); - } else { - ret = absl::StrCat("PosixError(errno=", errno_, " ", strerrno_buf, ")"); - } -#else - ret = absl::StrCat("PosixError(errno=", errno_, " ", res, ")"); -#endif - - if (!msg_.empty()) { - ret.append(" "); - ret.append(msg_); - } - - return ret; -} - -::std::ostream& operator<<(::std::ostream& os, const PosixError& e) { - os << e.ToString(); - return os; -} - -void PosixErrorIsMatcherCommonImpl::DescribeTo(std::ostream* os) const { - *os << "has an errno value that "; - code_matcher_.DescribeTo(os); - *os << ", and has an error message that "; - message_matcher_.DescribeTo(os); -} - -void PosixErrorIsMatcherCommonImpl::DescribeNegationTo(std::ostream* os) const { - *os << "has an errno value that "; - code_matcher_.DescribeNegationTo(os); - *os << ", or has an error message that "; - message_matcher_.DescribeNegationTo(os); -} - -bool PosixErrorIsMatcherCommonImpl::MatchAndExplain( - const PosixError& error, - ::testing::MatchResultListener* result_listener) const { - ::testing::StringMatchResultListener inner_listener; - - inner_listener.Clear(); - if (!code_matcher_.MatchAndExplain(error.errno_value(), &inner_listener)) { - return false; - } - - if (!message_matcher_.Matches(error.error_message())) { - return false; - } - - return true; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/posix_error.h b/test/util/posix_error.h deleted file mode 100644 index ad666bce0..000000000 --- a/test/util/posix_error.h +++ /dev/null @@ -1,462 +0,0 @@ -// 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 <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 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: - // 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 <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(). - PosixError error() const; - - // Returns this->error().error_message(); - std::string error_message() const; - - // Returns true if this PosixErrorOr contains some T. - 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: - int errno_value() const; - absl::variant<T, PosixError> value_; - - friend class PosixErrorIsMatcherCommonImpl; -}; - -template <typename T> -PosixErrorOr<T>::PosixErrorOr(const PosixError& error) : value_(error) { - TEST_CHECK_MSG( - !error.ok(), - "Constructing PosixErrorOr with NoError, eg. errno 0 is not allowed."); -} - -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> -PosixError PosixErrorOr<T>::error() const { - if (!absl::holds_alternative<PosixError>(value_)) { - return PosixError(); - } - return absl::get<PosixError>(value_); -} - -template <typename T> -int PosixErrorOr<T>::errno_value() const { - return error().errno_value(); -} - -template <typename T> -std::string PosixErrorOr<T>::error_message() const { - return error().error_message(); -} - -template <typename T> -bool PosixErrorOr<T>::ok() const { - return absl::holds_alternative<T>(value_); -} - -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_); -} - -template <typename T> -const T&& PosixErrorOr<T>::ValueOrDie() const&& { - TEST_CHECK(absl::holds_alternative<T>(value_)); - return std::move(absl::get<T>(value_)); -} - -template <typename T> -T&& PosixErrorOr<T>::ValueOrDie() && { - TEST_CHECK(absl::holds_alternative<T>(value_)); - return std::move(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 { - // 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<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; - - template <typename T> - bool MatchAndExplain(const PosixErrorOr<T>& 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<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, 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_ diff --git a/test/util/posix_error_test.cc b/test/util/posix_error_test.cc deleted file mode 100644 index d67270842..000000000 --- a/test/util/posix_error_test.cc +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - -#include "test/util/posix_error.h" - -#include <errno.h> -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(PosixErrorTest, PosixError) { - auto err = PosixError(EAGAIN); - EXPECT_THAT(err, PosixErrorIs(EAGAIN, "")); -} - -TEST(PosixErrorTest, PosixErrorOrPosixError) { - auto err = PosixErrorOr<std::nullptr_t>(PosixError(EAGAIN)); - EXPECT_THAT(err, PosixErrorIs(EAGAIN, "")); -} - -TEST(PosixErrorTest, PosixErrorOrNullptr) { - auto err = PosixErrorOr<std::nullptr_t>(nullptr); - EXPECT_TRUE(err.ok()); - EXPECT_NO_ERRNO(err); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/proc_util.cc b/test/util/proc_util.cc deleted file mode 100644 index 75b24da37..000000000 --- a/test/util/proc_util.cc +++ /dev/null @@ -1,107 +0,0 @@ -// 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. - -#include "test/util/proc_util.h" - -#include <algorithm> -#include <iostream> -#include <vector> - -#include "absl/strings/ascii.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "test/util/fs_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Parses a single line from /proc/<xxx>/maps. -PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line) { - ProcMapsEntry map_entry = {}; - - // Limit splitting to 6 parts so that if there is a file path and it contains - // spaces, the file path is not split. - std::vector<std::string> parts = - absl::StrSplit(line, absl::MaxSplits(' ', 5), absl::SkipEmpty()); - - // parts.size() should be 6 if there is a file name specified, and 5 - // otherwise. - if (parts.size() < 5) { - return PosixError(EINVAL, absl::StrCat("Invalid line: ", line)); - } - - // Address range in the form X-X where X are hex values without leading 0x. - std::vector<std::string> addresses = absl::StrSplit(parts[0], '-'); - if (addresses.size() != 2) { - return PosixError(EINVAL, - absl::StrCat("Invalid address range: ", parts[0])); - } - ASSIGN_OR_RETURN_ERRNO(map_entry.start, AtoiBase(addresses[0], 16)); - ASSIGN_OR_RETURN_ERRNO(map_entry.end, AtoiBase(addresses[1], 16)); - - // Permissions are four bytes of the form rwxp or - if permission not set. - if (parts[1].size() != 4) { - return PosixError(EINVAL, - absl::StrCat("Invalid permission field: ", parts[1])); - } - - map_entry.readable = parts[1][0] == 'r'; - map_entry.writable = parts[1][1] == 'w'; - map_entry.executable = parts[1][2] == 'x'; - map_entry.priv = parts[1][3] == 'p'; - - ASSIGN_OR_RETURN_ERRNO(map_entry.offset, AtoiBase(parts[2], 16)); - - std::vector<std::string> device = absl::StrSplit(parts[3], ':'); - if (device.size() != 2) { - return PosixError(EINVAL, absl::StrCat("Invalid device: ", parts[3])); - } - ASSIGN_OR_RETURN_ERRNO(map_entry.major, AtoiBase(device[0], 16)); - ASSIGN_OR_RETURN_ERRNO(map_entry.minor, AtoiBase(device[1], 16)); - - ASSIGN_OR_RETURN_ERRNO(map_entry.inode, Atoi<int64_t>(parts[4])); - if (parts.size() == 6) { - // A filename is present. However, absl::StrSplit retained the whitespace - // between the inode number and the filename. - map_entry.filename = - std::string(absl::StripLeadingAsciiWhitespace(parts[5])); - } - - return map_entry; -} - -PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps( - absl::string_view contents) { - std::vector<ProcMapsEntry> entries; - auto lines = absl::StrSplit(contents, '\n', absl::SkipEmpty()); - for (const auto& l : lines) { - std::cout << "line: " << l; - ASSIGN_OR_RETURN_ERRNO(auto entry, ParseProcMapsLine(l)); - entries.push_back(entry); - } - return entries; -} - -PosixErrorOr<bool> IsVsyscallEnabled() { - ASSIGN_OR_RETURN_ERRNO(auto contents, GetContents("/proc/self/maps")); - ASSIGN_OR_RETURN_ERRNO(auto maps, ParseProcMaps(contents)); - return std::any_of(maps.begin(), maps.end(), [](const ProcMapsEntry& e) { - return e.filename == "[vsyscall]"; - }); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/proc_util.h b/test/util/proc_util.h deleted file mode 100644 index af209a51e..000000000 --- a/test/util/proc_util.h +++ /dev/null @@ -1,150 +0,0 @@ -// 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_PROC_UTIL_H_ -#define GVISOR_TEST_UTIL_PROC_UTIL_H_ - -#include <ostream> -#include <string> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// ProcMapsEntry contains the data from a single line in /proc/<xxx>/maps. -struct ProcMapsEntry { - uint64_t start; - uint64_t end; - bool readable; - bool writable; - bool executable; - bool priv; - uint64_t offset; - int major; - int minor; - int64_t inode; - std::string filename; -}; - -// Parses a ProcMaps line or returns an error. -PosixErrorOr<ProcMapsEntry> ParseProcMapsLine(absl::string_view line); -PosixErrorOr<std::vector<ProcMapsEntry>> ParseProcMaps( - absl::string_view contents); - -// Returns true if vsyscall (emmulation or not) is enabled. -PosixErrorOr<bool> IsVsyscallEnabled(); - -// Printer for ProcMapsEntry. -inline std::ostream& operator<<(std::ostream& os, const ProcMapsEntry& entry) { - std::string str = - absl::StrCat(absl::Hex(entry.start, absl::PadSpec::kZeroPad8), "-", - absl::Hex(entry.end, absl::PadSpec::kZeroPad8), " "); - - absl::StrAppend(&str, entry.readable ? "r" : "-"); - absl::StrAppend(&str, entry.writable ? "w" : "-"); - absl::StrAppend(&str, entry.executable ? "x" : "-"); - absl::StrAppend(&str, entry.priv ? "p" : "s"); - - absl::StrAppend(&str, " ", absl::Hex(entry.offset, absl::PadSpec::kZeroPad8), - " ", absl::Hex(entry.major, absl::PadSpec::kZeroPad2), ":", - absl::Hex(entry.minor, absl::PadSpec::kZeroPad2), " ", - entry.inode); - if (absl::string_view(entry.filename) != "") { - // Pad to column 74 - int pad = 73 - str.length(); - if (pad > 0) { - absl::StrAppend(&str, std::string(pad, ' ')); - } - absl::StrAppend(&str, entry.filename); - } - os << str; - return os; -} - -// Printer for std::vector<ProcMapsEntry>. -inline std::ostream& operator<<(std::ostream& os, - const std::vector<ProcMapsEntry>& vec) { - for (unsigned int i = 0; i < vec.size(); i++) { - os << vec[i]; - if (i != vec.size() - 1) { - os << "\n"; - } - } - return os; -} - -// GMock printer for std::vector<ProcMapsEntry>. -inline void PrintTo(const std::vector<ProcMapsEntry>& vec, std::ostream* os) { - *os << vec; -} - -// Checks that /proc/pid/maps contains all of the passed mappings. -// -// The major, minor, and inode fields are ignored. -MATCHER_P(ContainsMappings, mappings, - "contains mappings:\n" + ::testing::PrintToString(mappings)) { - auto contents_or = GetContents(absl::StrCat("/proc/", arg, "/maps")); - if (!contents_or.ok()) { - *result_listener << "Unable to read mappings: " - << contents_or.error().ToString(); - return false; - } - - auto maps_or = ParseProcMaps(contents_or.ValueOrDie()); - if (!maps_or.ok()) { - *result_listener << "Unable to parse mappings: " - << maps_or.error().ToString(); - return false; - } - - auto maps = std::move(maps_or).ValueOrDie(); - - // Does maps contain all elements in mappings? The comparator ignores - // the major, minor, and inode fields. - bool all_present = true; - std::for_each(mappings.begin(), mappings.end(), [&](const ProcMapsEntry& e1) { - auto it = - std::find_if(maps.begin(), maps.end(), [&e1](const ProcMapsEntry& e2) { - return e1.start == e2.start && e1.end == e2.end && - e1.readable == e2.readable && e1.writable == e2.writable && - e1.executable == e2.executable && e1.priv == e2.priv && - e1.offset == e2.offset && e1.filename == e2.filename; - }); - if (it == maps.end()) { - // It wasn't found. - if (all_present) { - // We will output the message once and then a line for each mapping - // that wasn't found. - all_present = false; - *result_listener << "Got mappings:\n" - << maps << "\nThat were missing:\n"; - } - *result_listener << e1 << "\n"; - } - }); - - return all_present; -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_PROC_UTIL_H_ diff --git a/test/util/proc_util_test.cc b/test/util/proc_util_test.cc deleted file mode 100644 index 71dd2355e..000000000 --- a/test/util/proc_util_test.cc +++ /dev/null @@ -1,81 +0,0 @@ -// 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. - -#include "test/util/proc_util.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -using ::testing::IsEmpty; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ParseProcMapsLineTest, WithoutFilename) { - auto entry = ASSERT_NO_ERRNO_AND_VALUE( - ParseProcMapsLine("2ab4f00b7000-2ab4f00b9000 r-xp 00000000 00:00 0 ")); - EXPECT_EQ(entry.start, 0x2ab4f00b7000); - EXPECT_EQ(entry.end, 0x2ab4f00b9000); - EXPECT_TRUE(entry.readable); - EXPECT_FALSE(entry.writable); - EXPECT_TRUE(entry.executable); - EXPECT_TRUE(entry.priv); - EXPECT_EQ(entry.offset, 0); - EXPECT_EQ(entry.major, 0); - EXPECT_EQ(entry.minor, 0); - EXPECT_EQ(entry.inode, 0); - EXPECT_THAT(entry.filename, IsEmpty()); -} - -TEST(ParseProcMapsLineTest, WithFilename) { - auto entry = ASSERT_NO_ERRNO_AND_VALUE( - ParseProcMapsLine("00407000-00408000 rw-p 00006000 00:0e 10 " - " /bin/cat")); - EXPECT_EQ(entry.start, 0x407000); - EXPECT_EQ(entry.end, 0x408000); - EXPECT_TRUE(entry.readable); - EXPECT_TRUE(entry.writable); - EXPECT_FALSE(entry.executable); - EXPECT_TRUE(entry.priv); - EXPECT_EQ(entry.offset, 0x6000); - EXPECT_EQ(entry.major, 0); - EXPECT_EQ(entry.minor, 0x0e); - EXPECT_EQ(entry.inode, 10); - EXPECT_EQ(entry.filename, "/bin/cat"); -} - -TEST(ParseProcMapsLineTest, WithFilenameContainingSpaces) { - auto entry = ASSERT_NO_ERRNO_AND_VALUE( - ParseProcMapsLine("7f26b3b12000-7f26b3b13000 rw-s 00000000 00:05 1432484 " - " /dev/zero (deleted)")); - EXPECT_EQ(entry.start, 0x7f26b3b12000); - EXPECT_EQ(entry.end, 0x7f26b3b13000); - EXPECT_TRUE(entry.readable); - EXPECT_TRUE(entry.writable); - EXPECT_FALSE(entry.executable); - EXPECT_FALSE(entry.priv); - EXPECT_EQ(entry.offset, 0); - EXPECT_EQ(entry.major, 0); - EXPECT_EQ(entry.minor, 0x05); - EXPECT_EQ(entry.inode, 1432484); - EXPECT_EQ(entry.filename, "/dev/zero (deleted)"); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/rlimit_util.cc b/test/util/rlimit_util.cc deleted file mode 100644 index 684253f78..000000000 --- a/test/util/rlimit_util.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019 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. - -#include "test/util/rlimit_util.h" - -#include <sys/resource.h> -#include <cerrno> - -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<Cleanup> ScopedSetSoftRlimit(int resource, rlim_t newval) { - struct rlimit old_rlim; - if (getrlimit(resource, &old_rlim) != 0) { - return PosixError(errno, "getrlimit failed"); - } - struct rlimit new_rlim = old_rlim; - new_rlim.rlim_cur = newval; - if (setrlimit(resource, &new_rlim) != 0) { - return PosixError(errno, "setrlimit failed"); - } - return Cleanup([resource, old_rlim] { - TEST_PCHECK(setrlimit(resource, &old_rlim) == 0); - }); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/rlimit_util.h b/test/util/rlimit_util.h deleted file mode 100644 index 873252a32..000000000 --- a/test/util/rlimit_util.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019 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_RLIMIT_UTIL_H_ -#define GVISOR_TEST_UTIL_RLIMIT_UTIL_H_ - -#include <sys/resource.h> -#include <sys/time.h> - -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<Cleanup> ScopedSetSoftRlimit(int resource, rlim_t newval); - -} // namespace testing -} // namespace gvisor -#endif // GVISOR_TEST_UTIL_RLIMIT_UTIL_H_ diff --git a/test/util/save_util.cc b/test/util/save_util.cc deleted file mode 100644 index 384d626f0..000000000 --- a/test/util/save_util.cc +++ /dev/null @@ -1,71 +0,0 @@ -// 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. - -#include "test/util/save_util.h" - -#include <stddef.h> -#include <stdlib.h> -#include <unistd.h> - -#include <atomic> -#include <cerrno> - -#define GVISOR_COOPERATIVE_SAVE_TEST "GVISOR_COOPERATIVE_SAVE_TEST" - -namespace gvisor { -namespace testing { -namespace { - -enum class CooperativeSaveMode { - kUnknown = 0, // cooperative_save_mode is statically-initialized to 0 - kAvailable, - kNotAvailable, -}; - -std::atomic<CooperativeSaveMode> cooperative_save_mode; - -bool CooperativeSaveEnabled() { - auto mode = cooperative_save_mode.load(); - if (mode == CooperativeSaveMode::kUnknown) { - mode = (getenv(GVISOR_COOPERATIVE_SAVE_TEST) != nullptr) - ? CooperativeSaveMode::kAvailable - : CooperativeSaveMode::kNotAvailable; - cooperative_save_mode.store(mode); - } - return mode == CooperativeSaveMode::kAvailable; -} - -std::atomic<int> save_disable; - -} // namespace - -DisableSave::DisableSave() { save_disable++; } - -DisableSave::~DisableSave() { reset(); } - -void DisableSave::reset() { - if (!reset_) { - reset_ = true; - save_disable--; - } -} - -namespace internal { -bool ShouldSave() { - return CooperativeSaveEnabled() && (save_disable.load() == 0); -} -} // namespace internal - -} // namespace testing -} // namespace gvisor diff --git a/test/util/save_util.h b/test/util/save_util.h deleted file mode 100644 index bddad6120..000000000 --- a/test/util/save_util.h +++ /dev/null @@ -1,52 +0,0 @@ -// 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_SAVE_UTIL_H_ -#define GVISOR_TEST_UTIL_SAVE_UTIL_H_ - -namespace gvisor { -namespace testing { -// Disable save prevents saving while the given function executes. -// -// This lasts the duration of the object, unless reset is called. -class DisableSave { - public: - DisableSave(); - ~DisableSave(); - DisableSave(DisableSave const&) = delete; - DisableSave(DisableSave&&) = delete; - DisableSave& operator=(DisableSave const&) = delete; - DisableSave& operator=(DisableSave&&) = delete; - - // reset allows saves to continue, and is called implicitly by the destructor. - // It may be called multiple times safely, but is not thread-safe. - void reset(); - - private: - bool reset_ = false; -}; - -// May perform a co-operative save cycle. -// -// errno is guaranteed to be preserved. -void MaybeSave(); - -namespace internal { -bool ShouldSave(); -} // namespace internal - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_SAVE_UTIL_H_ diff --git a/test/util/save_util_linux.cc b/test/util/save_util_linux.cc deleted file mode 100644 index 7a0f14342..000000000 --- a/test/util/save_util_linux.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 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. - -#include <errno.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "test/util/save_util.h" - -namespace gvisor { -namespace testing { - -void MaybeSave() { - if (internal::ShouldSave()) { - int orig_errno = errno; - syscall(SYS_create_module, nullptr, 0); - errno = orig_errno; - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/save_util_other.cc b/test/util/save_util_other.cc deleted file mode 100644 index 1aca663b7..000000000 --- a/test/util/save_util_other.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 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. - -namespace gvisor { -namespace testing { - -void MaybeSave() { - // Saving is never available in a non-linux environment. -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/signal_util.cc b/test/util/signal_util.cc deleted file mode 100644 index 26738864f..000000000 --- a/test/util/signal_util.cc +++ /dev/null @@ -1,103 +0,0 @@ -// 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. - -#include "test/util/signal_util.h" - -#include <signal.h> -#include <ostream> - -#include "gtest/gtest.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace { - -struct Range { - int start; - int end; -}; - -// Format a Range as "start-end" or "start" for single value Ranges. -static ::std::ostream& operator<<(::std::ostream& os, const Range& range) { - if (range.end > range.start) { - return os << range.start << '-' << range.end; - } - - return os << range.start; -} - -} // namespace - -// Format a sigset_t as a comma separated list of numeric ranges. -// Empty sigset: [] -// Full sigset: [1-31,34-64] -::std::ostream& operator<<(::std::ostream& os, const sigset_t& sigset) { - const char* delim = ""; - Range range = {0, 0}; - - os << '['; - - for (int sig = 1; sig <= gvisor::testing::kMaxSignal; ++sig) { - if (sigismember(&sigset, sig)) { - if (range.start) { - range.end = sig; - } else { - range.start = sig; - range.end = sig; - } - } else if (range.start) { - os << delim << range; - delim = ","; - range.start = 0; - range.end = 0; - } - } - - if (range.start) { - os << delim << range; - } - - return os << ']'; -} - -namespace gvisor { -namespace testing { - -PosixErrorOr<Cleanup> ScopedSigaction(int sig, struct sigaction const& sa) { - struct sigaction old_sa; - int rc = sigaction(sig, &sa, &old_sa); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "sigaction failed"); - } - return Cleanup([sig, old_sa] { - EXPECT_THAT(sigaction(sig, &old_sa, nullptr), SyscallSucceeds()); - }); -} - -PosixErrorOr<Cleanup> ScopedSignalMask(int how, sigset_t const& set) { - sigset_t old; - int rc = sigprocmask(how, &set, &old); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "sigprocmask failed"); - } - return Cleanup([old] { - EXPECT_THAT(sigprocmask(SIG_SETMASK, &old, nullptr), SyscallSucceeds()); - }); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/signal_util.h b/test/util/signal_util.h deleted file mode 100644 index 7fd2af015..000000000 --- a/test/util/signal_util.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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_SIGNAL_UTIL_H_ -#define GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ - -#include <signal.h> -#include <sys/syscall.h> -#include <unistd.h> -#include <ostream> - -#include "gmock/gmock.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" - -// Format a sigset_t as a comma separated list of numeric ranges. -::std::ostream& operator<<(::std::ostream& os, const sigset_t& sigset); - -namespace gvisor { -namespace testing { - -// The maximum signal number. -static constexpr int kMaxSignal = 64; - -// Wrapper for the tgkill(2) syscall, which glibc does not provide. -inline int tgkill(pid_t tgid, pid_t tid, int sig) { - return syscall(__NR_tgkill, tgid, tid, sig); -} - -// Installs the passed sigaction and returns a cleanup function to restore the -// previous handler when it goes out of scope. -PosixErrorOr<Cleanup> ScopedSigaction(int sig, struct sigaction const& sa); - -// Updates the signal mask as per sigprocmask(2) and returns a cleanup function -// to restore the previous signal mask when it goes out of scope. -PosixErrorOr<Cleanup> ScopedSignalMask(int how, sigset_t const& set); - -// ScopedSignalMask variant that creates a mask of the single signal 'sig'. -inline PosixErrorOr<Cleanup> ScopedSignalMask(int how, int sig) { - sigset_t set; - sigemptyset(&set); - sigaddset(&set, sig); - return ScopedSignalMask(how, set); -} - -// Asserts equality of two sigset_t values. -MATCHER_P(EqualsSigset, value, "equals " + ::testing::PrintToString(value)) { - for (int sig = 1; sig <= kMaxSignal; ++sig) { - if (sigismember(&arg, sig) != sigismember(&value, sig)) { - return false; - } - } - return true; -} - -#ifdef __x86_64__ -// Fault can be used to generate a synchronous SIGSEGV. -// -// This fault can be fixed up in a handler via fixup, below. -inline void Fault() { - // Zero and dereference %ax. - asm("movabs $0, %%rax\r\n" - "mov 0(%%rax), %%rax\r\n" - : - : - : "ax"); -} - -// FixupFault fixes up a fault generated by fault, above. -inline void FixupFault(ucontext_t* ctx) { - // Skip the bad instruction above. - // - // The encoding is 0x48 0xab 0x00. - ctx->uc_mcontext.gregs[REG_RIP] += 3; -} -#endif - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_SIGNAL_UTIL_H_ diff --git a/test/util/temp_path.cc b/test/util/temp_path.cc deleted file mode 100644 index 35aacb172..000000000 --- a/test/util/temp_path.cc +++ /dev/null @@ -1,163 +0,0 @@ -// 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. - -#include "test/util/temp_path.h" - -#include <unistd.h> - -#include <atomic> -#include <cstdlib> -#include <iostream> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -std::atomic<uint64_t> global_temp_file_number = ATOMIC_VAR_INIT(1); - -// Return a new temp filename, intended to be unique system-wide. -// -// The global file number helps maintain file naming consistency across -// different runs of a test. -// -// The timestamp is necessary because the test infrastructure invokes each -// test case in a separate process (resetting global_temp_file_number) and -// potentially in parallel, which allows for races between selecting and using a -// name. -std::string NextTempBasename() { - return absl::StrCat("gvisor_test_temp_", global_temp_file_number++, "_", - absl::ToUnixNanos(absl::Now())); -} - -void TryDeleteRecursively(std::string const& path) { - if (!path.empty()) { - int undeleted_dirs = 0; - int undeleted_files = 0; - auto status = RecursivelyDelete(path, &undeleted_dirs, &undeleted_files); - if (undeleted_dirs || undeleted_files || !status.ok()) { - std::cerr << path << ": failed to delete " << undeleted_dirs - << " directories and " << undeleted_files - << " files: " << status; - } - } -} - -} // namespace - -constexpr mode_t TempPath::kDefaultFileMode; -constexpr mode_t TempPath::kDefaultDirMode; - -std::string NewTempAbsPathInDir(absl::string_view const dir) { - return JoinPath(dir, NextTempBasename()); -} - -std::string NewTempAbsPath() { - return NewTempAbsPathInDir(GetAbsoluteTestTmpdir()); -} - -std::string NewTempRelPath() { return NextTempBasename(); } - -std::string GetAbsoluteTestTmpdir() { - char* env_tmpdir = getenv("TEST_TMPDIR"); - std::string tmp_dir = - env_tmpdir != nullptr ? std::string(env_tmpdir) : "/tmp"; - - return MakeAbsolute(tmp_dir, "").ValueOrDie(); -} - -PosixErrorOr<TempPath> TempPath::CreateFileWith(absl::string_view const parent, - absl::string_view const content, - mode_t const mode) { - return CreateIn(parent, [=](absl::string_view path) -> PosixError { - // CreateWithContents will call open(O_WRONLY) with the given mode. If the - // mode is not user-writable, save/restore cannot preserve the fd. Hence - // the little permission dance that's done here. - auto res = CreateWithContents(path, content, mode | 0200); - RETURN_IF_ERRNO(res); - - return Chmod(path, mode); - }); -} - -PosixErrorOr<TempPath> TempPath::CreateDirWith(absl::string_view const parent, - mode_t const mode) { - return CreateIn(parent, - [=](absl::string_view path) { return Mkdir(path, mode); }); -} - -PosixErrorOr<TempPath> TempPath::CreateSymlinkTo(absl::string_view const parent, - std::string const& dest) { - return CreateIn(parent, [=](absl::string_view path) { - int ret = symlink(dest.c_str(), std::string(path).c_str()); - if (ret != 0) { - return PosixError(errno, "symlink failed"); - } - return NoError(); - }); -} - -PosixErrorOr<TempPath> TempPath::CreateFileIn(absl::string_view const parent) { - return TempPath::CreateFileWith(parent, absl::string_view(), - kDefaultFileMode); -} - -PosixErrorOr<TempPath> TempPath::CreateDirIn(absl::string_view const parent) { - return TempPath::CreateDirWith(parent, kDefaultDirMode); -} - -PosixErrorOr<TempPath> TempPath::CreateFileMode(mode_t mode) { - return TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), absl::string_view(), - mode); -} - -PosixErrorOr<TempPath> TempPath::CreateFile() { - return TempPath::CreateFileIn(GetAbsoluteTestTmpdir()); -} - -PosixErrorOr<TempPath> TempPath::CreateDir() { - return TempPath::CreateDirIn(GetAbsoluteTestTmpdir()); -} - -TempPath::~TempPath() { TryDeleteRecursively(path_); } - -TempPath::TempPath(TempPath&& orig) { reset(orig.release()); } - -TempPath& TempPath::operator=(TempPath&& orig) { - reset(orig.release()); - return *this; -} - -std::string TempPath::reset(std::string newpath) { - std::string path = path_; - TryDeleteRecursively(path_); - path_ = std::move(newpath); - return path; -} - -std::string TempPath::release() { - std::string path = path_; - path_ = std::string(); - return path; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/temp_path.h b/test/util/temp_path.h deleted file mode 100644 index 92d669503..000000000 --- a/test/util/temp_path.h +++ /dev/null @@ -1,134 +0,0 @@ -// 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_TEMP_PATH_H_ -#define GVISOR_TEST_UTIL_TEMP_PATH_H_ - -#include <sys/stat.h> -#include <string> -#include <utility> - -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Returns an absolute path for a file in `dir` that does not yet exist. -// Distinct calls to NewTempAbsPathInDir from the same process, even from -// multiple threads, are guaranteed to return different paths. Distinct calls to -// NewTempAbsPathInDir from different processes are not synchronized. -std::string NewTempAbsPathInDir(absl::string_view const dir); - -// Like NewTempAbsPathInDir, but the returned path is in the test's temporary -// directory, as provided by the testing framework. -std::string NewTempAbsPath(); - -// Like NewTempAbsPathInDir, but the returned path is relative (to the current -// working directory). -std::string NewTempRelPath(); - -// Returns the absolute path for the test temp dir. -std::string GetAbsoluteTestTmpdir(); - -// Represents a temporary file or directory. -class TempPath { - public: - // Default creation mode for files. - static constexpr mode_t kDefaultFileMode = 0644; - - // Default creation mode for directories. - static constexpr mode_t kDefaultDirMode = 0755; - - // Creates a temporary file in directory `parent` with mode `mode` and - // contents `content`. - static PosixErrorOr<TempPath> CreateFileWith(absl::string_view parent, - absl::string_view content, - mode_t mode); - - // Creates an empty temporary subdirectory in directory `parent` with mode - // `mode`. - static PosixErrorOr<TempPath> CreateDirWith(absl::string_view parent, - mode_t mode); - - // Creates a temporary symlink in directory `parent` to destination `dest`. - static PosixErrorOr<TempPath> CreateSymlinkTo(absl::string_view parent, - std::string const& dest); - - // Creates an empty temporary file in directory `parent` with mode - // kDefaultFileMode. - static PosixErrorOr<TempPath> CreateFileIn(absl::string_view parent); - - // Creates an empty temporary subdirectory in directory `parent` with mode - // kDefaultDirMode. - static PosixErrorOr<TempPath> CreateDirIn(absl::string_view parent); - - // Creates an empty temporary file in the test's temporary directory with mode - // `mode`. - static PosixErrorOr<TempPath> CreateFileMode(mode_t mode); - - // Creates an empty temporary file in the test's temporary directory with - // mode kDefaultFileMode. - static PosixErrorOr<TempPath> CreateFile(); - - // Creates an empty temporary subdirectory in the test's temporary directory - // with mode kDefaultDirMode. - static PosixErrorOr<TempPath> CreateDir(); - - // Constructs a TempPath that represents nothing. - TempPath() = default; - - // Constructs a TempPath that represents the given path, which will be deleted - // when the TempPath is destroyed. - explicit TempPath(std::string path) : path_(std::move(path)) {} - - // Attempts to delete the represented temporary file or directory (in the - // latter case, also attempts to delete its contents). - ~TempPath(); - - // Attempts to delete the represented temporary file or directory, then - // transfers ownership of the path represented by orig to this TempPath. - TempPath(TempPath&& orig); - TempPath& operator=(TempPath&& orig); - - // Changes the path this TempPath represents. If the TempPath already - // represented a path, deletes and returns that path. Otherwise returns the - // empty string. - std::string reset(std::string newpath); - std::string reset() { return reset(""); } - - // Forgets and returns the path this TempPath represents. The path is not - // deleted. - std::string release(); - - // Returns the path this TempPath represents. - std::string path() const { return path_; } - - private: - template <typename F> - static PosixErrorOr<TempPath> CreateIn(absl::string_view const parent, - F const& f) { - std::string path = NewTempAbsPathInDir(parent); - RETURN_IF_ERRNO(f(path)); - return TempPath(std::move(path)); - } - - std::string path_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TEMP_PATH_H_ diff --git a/test/util/test_main.cc b/test/util/test_main.cc deleted file mode 100644 index 5c7ee0064..000000000 --- a/test/util/test_main.cc +++ /dev/null @@ -1,20 +0,0 @@ -// 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. - -#include "test/util/test_util.h" - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - return RUN_ALL_TESTS(); -} diff --git a/test/util/test_util.cc b/test/util/test_util.cc deleted file mode 100644 index ba0dcf7d0..000000000 --- a/test/util/test_util.cc +++ /dev/null @@ -1,238 +0,0 @@ -// 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. - -#include "test/util/test_util.h" - -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include <ctime> -#include <iostream> -#include <vector> - -#include "absl/base/attributes.h" -#include "absl/flags/flag.h" // IWYU pragma: keep -#include "absl/flags/parse.h" // IWYU pragma: keep -#include "absl/strings/numbers.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/time/time.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -#define TEST_ON_GVISOR "TEST_ON_GVISOR" - -bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; } - -Platform GvisorPlatform() { - // Set by runner.go. - char* env = getenv(TEST_ON_GVISOR); - if (!env) { - return Platform::kNative; - } - if (strcmp(env, "ptrace") == 0) { - return Platform::kPtrace; - } - if (strcmp(env, "kvm") == 0) { - return Platform::kKVM; - } - std::cerr << "unknown platform " << env; - abort(); -} - -// Inline cpuid instruction. Preserve %ebx/%rbx register. In PIC compilations -// %ebx contains the address of the global offset table. %rbx is occasionally -// used to address stack variables in presence of dynamic allocas. -#if defined(__x86_64__) -#define GETCPUID(a, b, c, d, a_inp, c_inp) \ - asm("mov %%rbx, %%rdi\n" \ - "cpuid\n" \ - "xchg %%rdi, %%rbx\n" \ - : "=a"(a), "=D"(b), "=c"(c), "=d"(d) \ - : "a"(a_inp), "2"(c_inp)) -#endif // defined(__x86_64__) - -CPUVendor GetCPUVendor() { - uint32_t eax, ebx, ecx, edx; - std::string vendor_str; - // Get vendor string (issue CPUID with eax = 0) - GETCPUID(eax, ebx, ecx, edx, 0, 0); - vendor_str.append(reinterpret_cast<char*>(&ebx), 4); - vendor_str.append(reinterpret_cast<char*>(&edx), 4); - vendor_str.append(reinterpret_cast<char*>(&ecx), 4); - if (vendor_str == "GenuineIntel") { - return CPUVendor::kIntel; - } else if (vendor_str == "AuthenticAMD") { - return CPUVendor::kAMD; - } - return CPUVendor::kUnknownVendor; -} - -bool operator==(const KernelVersion& first, const KernelVersion& second) { - return first.major == second.major && first.minor == second.minor && - first.micro == second.micro; -} - -PosixErrorOr<KernelVersion> ParseKernelVersion(absl::string_view vers_str) { - KernelVersion version = {}; - std::vector<std::string> values = - absl::StrSplit(vers_str, absl::ByAnyChar(".-")); - if (values.size() == 2) { - ASSIGN_OR_RETURN_ERRNO(version.major, Atoi<int>(values[0])); - ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi<int>(values[1])); - return version; - } else if (values.size() >= 3) { - ASSIGN_OR_RETURN_ERRNO(version.major, Atoi<int>(values[0])); - ASSIGN_OR_RETURN_ERRNO(version.minor, Atoi<int>(values[1])); - ASSIGN_OR_RETURN_ERRNO(version.micro, Atoi<int>(values[2])); - return version; - } - return PosixError(EINVAL, absl::StrCat("Unknown kernel release: ", vers_str)); -} - -PosixErrorOr<KernelVersion> GetKernelVersion() { - utsname buf; - RETURN_ERROR_IF_SYSCALL_FAIL(uname(&buf)); - return ParseKernelVersion(buf.release); -} - -void SetupGvisorDeathTest() { -} - -std::string CPUSetToString(const cpu_set_t& set, size_t cpus) { - std::string str = "cpuset["; - for (unsigned int n = 0; n < cpus; n++) { - if (CPU_ISSET(n, &set)) { - if (n != 0) { - absl::StrAppend(&str, " "); - } - absl::StrAppend(&str, n); - } - } - absl::StrAppend(&str, "]"); - return str; -} - -// An overloaded operator<< makes it easy to dump the value of an OpenFd. -std::ostream& operator<<(std::ostream& out, OpenFd const& ofd) { - out << ofd.fd << " -> " << ofd.link; - return out; -} - -// An overloaded operator<< makes it easy to dump a vector of OpenFDs. -std::ostream& operator<<(std::ostream& out, std::vector<OpenFd> const& v) { - for (const auto& ofd : v) { - out << ofd << std::endl; - } - return out; -} - -PosixErrorOr<std::vector<OpenFd>> GetOpenFDs() { - // Get the results from /proc/self/fd. - ASSIGN_OR_RETURN_ERRNO(auto dir_list, - ListDir("/proc/self/fd", /*skipdots=*/true)); - - std::vector<OpenFd> ret_fds; - for (const auto& str_fd : dir_list) { - OpenFd open_fd = {}; - ASSIGN_OR_RETURN_ERRNO(open_fd.fd, Atoi<int>(str_fd)); - std::string path = absl::StrCat("/proc/self/fd/", open_fd.fd); - - // Resolve the link. - char buf[PATH_MAX] = {}; - int ret = readlink(path.c_str(), buf, sizeof(buf)); - if (ret < 0) { - if (errno == ENOENT) { - // The FD may have been closed, let's be resilient. - continue; - } - - return PosixError( - errno, absl::StrCat("readlink of ", path, " returned errno ", errno)); - } - open_fd.link = std::string(buf, ret); - ret_fds.emplace_back(std::move(open_fd)); - } - return ret_fds; -} - -PosixErrorOr<uint64_t> Links(const std::string& path) { - struct stat st; - if (stat(path.c_str(), &st)) { - return PosixError(errno, absl::StrCat("Failed to stat ", path)); - } - return static_cast<uint64_t>(st.st_nlink); -} - -void RandomizeBuffer(void* buffer, size_t len) { - struct timespec ts = {}; - clock_gettime(CLOCK_MONOTONIC, &ts); - uint32_t seed = static_cast<uint32_t>(ts.tv_nsec); - char* const buf = static_cast<char*>(buffer); - for (size_t i = 0; i < len; i++) { - buf[i] = rand_r(&seed) % 255; - } -} - -std::vector<std::vector<struct iovec>> GenerateIovecs(uint64_t total_size, - void* buf, - size_t buflen) { - std::vector<std::vector<struct iovec>> result; - for (uint64_t offset = 0; offset < total_size;) { - auto& iovec_array = *result.emplace(result.end()); - - for (; offset < total_size && iovec_array.size() < IOV_MAX; - offset += buflen) { - struct iovec iov = {}; - iov.iov_base = buf; - iov.iov_len = std::min<uint64_t>(total_size - offset, buflen); - iovec_array.push_back(iov); - } - } - - return result; -} - -uint64_t Megabytes(uint64_t n) { - // Overflow check, upper 20 bits in n shouldn't be set. - TEST_CHECK(!(0xfffff00000000000 & n)); - return n << 20; -} - -bool Equivalent(uint64_t current, uint64_t target, double tolerance) { - auto abs_diff = target > current ? target - current : current - target; - return abs_diff <= static_cast<uint64_t>(tolerance * target); -} - -void TestInit(int* argc, char*** argv) { - ::testing::InitGoogleTest(argc, *argv); - ::absl::ParseCommandLine(*argc, *argv); - - // Always mask SIGPIPE as it's common and tests aren't expected to handle it. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - TEST_CHECK(sigaction(SIGPIPE, &sa, nullptr) == 0); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/test_util.h b/test/util/test_util.h deleted file mode 100644 index b9d2dc2ba..000000000 --- a/test/util/test_util.h +++ /dev/null @@ -1,770 +0,0 @@ -// 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: -// -// 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 "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) - -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 { - -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); -} - -void TestInit(int* argc, char*** argv); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TEST_UTIL_H_ diff --git a/test/util/test_util_test.cc b/test/util/test_util_test.cc deleted file mode 100644 index b7300d9e5..000000000 --- a/test/util/test_util_test.cc +++ /dev/null @@ -1,250 +0,0 @@ -// 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. - -#include "test/util/test_util.h" - -#include <errno.h> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -using ::testing::AnyOf; -using ::testing::Gt; -using ::testing::IsEmpty; -using ::testing::Lt; -using ::testing::Not; -using ::testing::TypedEq; -using ::testing::UnorderedElementsAre; -using ::testing::UnorderedElementsAreArray; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(KernelVersionParsing, ValidateParsing) { - KernelVersion v = ASSERT_NO_ERRNO_AND_VALUE( - ParseKernelVersion("4.18.10-1foo2-amd64 baz blah")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-1foo2-amd64")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-14-amd64")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10-amd64")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.18.10")); - ASSERT_TRUE(v == KernelVersion({4, 18, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.0.10")); - ASSERT_TRUE(v == KernelVersion({4, 0, 10})); - - v = ASSERT_NO_ERRNO_AND_VALUE(ParseKernelVersion("4.0")); - ASSERT_TRUE(v == KernelVersion({4, 0, 0})); - - ASSERT_THAT(ParseKernelVersion("4.a"), PosixErrorIs(EINVAL, ::testing::_)); - ASSERT_THAT(ParseKernelVersion("3"), PosixErrorIs(EINVAL, ::testing::_)); - ASSERT_THAT(ParseKernelVersion(""), PosixErrorIs(EINVAL, ::testing::_)); - ASSERT_THAT(ParseKernelVersion("version 3.3.10"), - PosixErrorIs(EINVAL, ::testing::_)); -} - -TEST(MatchersTest, SyscallSucceeds) { - EXPECT_THAT(0, SyscallSucceeds()); - EXPECT_THAT(0L, SyscallSucceeds()); - - errno = 0; - EXPECT_THAT(-1, SyscallSucceeds()); - EXPECT_THAT(-1L, SyscallSucceeds()); - - errno = ENOMEM; - EXPECT_THAT(-1, Not(SyscallSucceeds())); - EXPECT_THAT(-1L, Not(SyscallSucceeds())); -} - -TEST(MatchersTest, SyscallSucceedsWithValue) { - EXPECT_THAT(0, SyscallSucceedsWithValue(0)); - EXPECT_THAT(1, SyscallSucceedsWithValue(Lt(3))); - EXPECT_THAT(-1, Not(SyscallSucceedsWithValue(Lt(3)))); - EXPECT_THAT(4, Not(SyscallSucceedsWithValue(Lt(3)))); - - // Non-int -1 - EXPECT_THAT(-1L, Not(SyscallSucceedsWithValue(0))); - - // Non-int, truncates to -1 if converted to int, with expected value - EXPECT_THAT(0xffffffffL, SyscallSucceedsWithValue(0xffffffffL)); - - // Non-int, truncates to -1 if converted to int, with monomorphic matcher - EXPECT_THAT(0xffffffffL, - SyscallSucceedsWithValue(TypedEq<long>(0xffffffffL))); - - // Non-int, truncates to -1 if converted to int, with polymorphic matcher - EXPECT_THAT(0xffffffffL, SyscallSucceedsWithValue(Gt(1))); -} - -TEST(MatchersTest, SyscallFails) { - EXPECT_THAT(0, Not(SyscallFails())); - EXPECT_THAT(0L, Not(SyscallFails())); - - errno = 0; - EXPECT_THAT(-1, Not(SyscallFails())); - EXPECT_THAT(-1L, Not(SyscallFails())); - - errno = ENOMEM; - EXPECT_THAT(-1, SyscallFails()); - EXPECT_THAT(-1L, SyscallFails()); -} - -TEST(MatchersTest, SyscallFailsWithErrno) { - EXPECT_THAT(0, Not(SyscallFailsWithErrno(EINVAL))); - EXPECT_THAT(0L, Not(SyscallFailsWithErrno(EINVAL))); - - errno = ENOMEM; - EXPECT_THAT(-1, Not(SyscallFailsWithErrno(EINVAL))); - EXPECT_THAT(-1L, Not(SyscallFailsWithErrno(EINVAL))); - - errno = EINVAL; - EXPECT_THAT(-1, SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(-1L, SyscallFailsWithErrno(EINVAL)); - - EXPECT_THAT(-1, SyscallFailsWithErrno(AnyOf(EINVAL, ENOMEM))); - EXPECT_THAT(-1L, SyscallFailsWithErrno(AnyOf(EINVAL, ENOMEM))); - - std::vector<int> expected_errnos({EINVAL, ENOMEM}); - errno = ENOMEM; - EXPECT_THAT(-1, SyscallFailsWithErrno(ElementOf(expected_errnos))); - EXPECT_THAT(-1L, SyscallFailsWithErrno(ElementOf(expected_errnos))); -} - -TEST(AllBitwiseCombinationsTest, NoArguments) { - EXPECT_THAT(AllBitwiseCombinations(), IsEmpty()); -} - -TEST(AllBitwiseCombinationsTest, EmptyList) { - EXPECT_THAT(AllBitwiseCombinations(List<int>{}), IsEmpty()); -} - -TEST(AllBitwiseCombinationsTest, SingleElementList) { - EXPECT_THAT(AllBitwiseCombinations(List<int>{5}), UnorderedElementsAre(5)); -} - -TEST(AllBitwiseCombinationsTest, SingleList) { - EXPECT_THAT(AllBitwiseCombinations(List<int>{0, 1, 2, 4}), - UnorderedElementsAre(0, 1, 2, 4)); -} - -TEST(AllBitwiseCombinationsTest, MultipleLists) { - EXPECT_THAT( - AllBitwiseCombinations(List<int>{0, 1, 2, 3}, List<int>{0, 4, 8, 12}), - UnorderedElementsAreArray( - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})); -} - -TEST(RandomizeBuffer, Works) { - const std::vector<char> original(4096); - std::vector<char> buffer = original; - RandomizeBuffer(buffer.data(), buffer.size()); - EXPECT_NE(buffer, original); -} - -// Enable comparison of vectors of iovec arrays for the following test. -MATCHER_P(IovecsListEq, expected, "") { - if (arg.size() != expected.size()) { - *result_listener << "sizes are different (actual: " << arg.size() - << ", expected: " << expected.size() << ")"; - return false; - } - - for (uint64_t i = 0; i < expected.size(); ++i) { - const std::vector<struct iovec>& actual_iovecs = arg[i]; - const std::vector<struct iovec>& expected_iovecs = expected[i]; - if (actual_iovecs.size() != expected_iovecs.size()) { - *result_listener << "iovec array size at position " << i - << " is different (actual: " << actual_iovecs.size() - << ", expected: " << expected_iovecs.size() << ")"; - return false; - } - - for (uint64_t j = 0; j < expected_iovecs.size(); ++j) { - const struct iovec& actual_iov = actual_iovecs[j]; - const struct iovec& expected_iov = expected_iovecs[j]; - if (actual_iov.iov_base != expected_iov.iov_base) { - *result_listener << "iovecs in array " << i << " at position " << j - << " are different (expected iov_base: " - << expected_iov.iov_base - << ", got: " << actual_iov.iov_base << ")"; - return false; - } - if (actual_iov.iov_len != expected_iov.iov_len) { - *result_listener << "iovecs in array " << i << " at position " << j - << " are different (expected iov_len: " - << expected_iov.iov_len - << ", got: " << actual_iov.iov_len << ")"; - return false; - } - } - } - - return true; -} - -// Verify empty iovec list generation. -TEST(GenerateIovecs, EmptyList) { - std::vector<char> buffer = {'a', 'b', 'c'}; - - EXPECT_THAT(GenerateIovecs(0, buffer.data(), buffer.size()), - IovecsListEq(std::vector<std::vector<struct iovec>>())); -} - -// Verify generating a single array of only one, partial, iovec. -TEST(GenerateIovecs, OneArray) { - std::vector<char> buffer = {'a', 'b', 'c'}; - - std::vector<std::vector<struct iovec>> expected; - struct iovec iov = {}; - iov.iov_base = buffer.data(); - iov.iov_len = 2; - expected.push_back(std::vector<struct iovec>({iov})); - EXPECT_THAT(GenerateIovecs(2, buffer.data(), buffer.size()), - IovecsListEq(expected)); -} - -// Verify that it wraps around after IOV_MAX iovecs. -TEST(GenerateIovecs, WrapsAtIovMax) { - std::vector<char> buffer = {'a', 'b', 'c'}; - - std::vector<std::vector<struct iovec>> expected; - struct iovec iov = {}; - iov.iov_base = buffer.data(); - iov.iov_len = buffer.size(); - expected.emplace_back(); - for (int i = 0; i < IOV_MAX; ++i) { - expected[0].push_back(iov); - } - iov.iov_len = 1; - expected.push_back(std::vector<struct iovec>({iov})); - - EXPECT_THAT( - GenerateIovecs(IOV_MAX * buffer.size() + 1, buffer.data(), buffer.size()), - IovecsListEq(expected)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/util/thread_util.h b/test/util/thread_util.h deleted file mode 100644 index 923c4fe10..000000000 --- a/test/util/thread_util.h +++ /dev/null @@ -1,93 +0,0 @@ -// 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_THREAD_UTIL_H_ -#define GVISOR_TEST_UTIL_THREAD_UTIL_H_ - -#include <pthread.h> -#ifdef __linux__ -#include <sys/syscall.h> -#endif -#include <unistd.h> - -#include <functional> -#include <utility> - -#include "test/util/logging.h" - -namespace gvisor { -namespace testing { - -// ScopedThread is a minimal wrapper around pthreads. -// -// This is used in lieu of more complex mechanisms because it provides very -// predictable behavior (no messing with timers, etc.) The thread will -// automatically joined when it is destructed (goes out of scope), but can be -// joined manually as well. -class ScopedThread { - public: - // Constructs a thread that executes f exactly once. - explicit ScopedThread(std::function<void*()> f) : f_(std::move(f)) { - CreateThread(); - } - - explicit ScopedThread(const std::function<void()>& f) { - f_ = [=] { - f(); - return nullptr; - }; - CreateThread(); - } - - ScopedThread(const ScopedThread& other) = delete; - ScopedThread& operator=(const ScopedThread& other) = delete; - - // Joins the thread. - ~ScopedThread() { Join(); } - - // Waits until this thread has finished executing. Join is idempotent and may - // be called multiple times, however Join itself is not thread-safe. - void* Join() { - if (!joined_) { - TEST_PCHECK(pthread_join(pt_, &retval_) == 0); - joined_ = true; - } - return retval_; - } - - private: - void CreateThread() { - TEST_PCHECK_MSG(pthread_create( - &pt_, /* attr = */ nullptr, - +[](void* arg) -> void* { - return static_cast<ScopedThread*>(arg)->f_(); - }, - this) == 0, - "thread creation failed"); - } - - std::function<void*()> f_; - pthread_t pt_; - bool joined_ = false; - void* retval_ = nullptr; -}; - -#ifdef __linux__ -inline pid_t gettid() { return syscall(SYS_gettid); } -#endif - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_THREAD_UTIL_H_ diff --git a/test/util/time_util.cc b/test/util/time_util.cc deleted file mode 100644 index 1ddfbfc9c..000000000 --- a/test/util/time_util.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 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. - -#include "test/util/time_util.h" - -#include <sys/syscall.h> -#include <unistd.h> - -#include "absl/time/time.h" - -namespace gvisor { -namespace testing { - -void SleepSafe(absl::Duration duration) { - if (duration == absl::ZeroDuration()) { - return; - } - - struct timespec ts = absl::ToTimespec(duration); - int ret; - while (1) { - ret = syscall(__NR_nanosleep, &ts, &ts); - if (ret == 0 || (ret <= 0 && errno != EINTR)) { - break; - } - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/time_util.h b/test/util/time_util.h deleted file mode 100644 index f3ddc9fde..000000000 --- a/test/util/time_util.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 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_TIME_UTIL_H_ -#define GVISOR_TEST_UTIL_TIME_UTIL_H_ - -#include "absl/time/time.h" - -namespace gvisor { -namespace testing { - -// Sleep for at least the specified duration. Avoids glibc. -void SleepSafe(absl::Duration duration); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TIME_UTIL_H_ diff --git a/test/util/timer_util.cc b/test/util/timer_util.cc deleted file mode 100644 index 43a26b0d3..000000000 --- a/test/util/timer_util.cc +++ /dev/null @@ -1,27 +0,0 @@ -// 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. - -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -absl::Time Now(clockid_t id) { - struct timespec now; - TEST_PCHECK(clock_gettime(id, &now) == 0); - return absl::TimeFromTimespec(now); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/util/timer_util.h b/test/util/timer_util.h deleted file mode 100644 index 31aea4fc6..000000000 --- a/test/util/timer_util.h +++ /dev/null @@ -1,74 +0,0 @@ -// 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_TIMER_UTIL_H_ -#define GVISOR_TEST_UTIL_TIMER_UTIL_H_ - -#include <errno.h> -#include <sys/time.h> - -#include <functional> - -#include "gmock/gmock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// MonotonicTimer is a simple timer that uses a monotonic clock. -class MonotonicTimer { - public: - MonotonicTimer() {} - absl::Duration Duration() { - struct timespec ts; - TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - return absl::TimeFromTimespec(ts) - start_; - } - - void Start() { - struct timespec ts; - TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - start_ = absl::TimeFromTimespec(ts); - } - - protected: - absl::Time start_; -}; - -// Sets the given itimer and returns a cleanup function that restores the -// previous itimer when it goes out of scope. -inline PosixErrorOr<Cleanup> ScopedItimer(int which, - struct itimerval const& new_value) { - struct itimerval old_value; - int rc = setitimer(which, &new_value, &old_value); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "setitimer failed"); - } - return Cleanup(std::function<void(void)>([which, old_value] { - EXPECT_THAT(setitimer(which, &old_value, nullptr), SyscallSucceeds()); - })); -} - -// Returns the current time. -absl::Time Now(clockid_t id); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_UTIL_TIMER_UTIL_H_ |