summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux')
-rw-r--r--test/syscalls/linux/clock_gettime.cc7
-rw-r--r--test/syscalls/linux/ip_socket_test_util.cc63
-rw-r--r--test/syscalls/linux/ip_socket_test_util.h34
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound.cc2
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc161
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h20
-rw-r--r--test/syscalls/linux/timerfd.cc76
-rw-r--r--test/syscalls/linux/vdso_clock_gettime.cc5
8 files changed, 331 insertions, 37 deletions
diff --git a/test/syscalls/linux/clock_gettime.cc b/test/syscalls/linux/clock_gettime.cc
index 335a38d41..c9e3ed6b2 100644
--- a/test/syscalls/linux/clock_gettime.cc
+++ b/test/syscalls/linux/clock_gettime.cc
@@ -132,6 +132,9 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
return "CLOCK_MONOTONIC_COARSE";
case CLOCK_MONOTONIC_RAW:
return "CLOCK_MONOTONIC_RAW";
+ case CLOCK_BOOTTIME:
+ // CLOCK_BOOTTIME is a monotonic clock.
+ return "CLOCK_BOOTTIME";
default:
return absl::StrCat(info.param);
}
@@ -140,15 +143,13 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
INSTANTIATE_TEST_SUITE_P(ClockGettime, MonotonicClockTest,
::testing::Values(CLOCK_MONOTONIC,
CLOCK_MONOTONIC_COARSE,
- CLOCK_MONOTONIC_RAW),
+ CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIME),
PrintClockId);
TEST(ClockGettime, UnimplementedReturnsEINVAL) {
SKIP_IF(!IsRunningOnGvisor());
struct timespec tp;
- EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME, &tp),
- SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(clock_gettime(CLOCK_REALTIME_ALARM, &tp),
SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME_ALARM, &tp),
diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc
index 5fc4e9115..c73262e72 100644
--- a/test/syscalls/linux/ip_socket_test_util.cc
+++ b/test/syscalls/linux/ip_socket_test_util.cc
@@ -120,5 +120,68 @@ SocketKind IPv4TCPUnboundSocket(int type) {
UnboundSocketCreator(AF_INET, type | SOCK_STREAM, IPPROTO_TCP)};
}
+PosixError IfAddrHelper::Load() {
+ Release();
+ RETURN_ERROR_IF_SYSCALL_FAIL(getifaddrs(&ifaddr_));
+ return PosixError(0);
+}
+
+void IfAddrHelper::Release() {
+ if (ifaddr_) {
+ freeifaddrs(ifaddr_);
+ }
+ ifaddr_ = nullptr;
+}
+
+std::vector<std::string> IfAddrHelper::InterfaceList(int family) {
+ std::vector<std::string> names;
+ for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) {
+ continue;
+ }
+ names.emplace(names.end(), ifa->ifa_name);
+ }
+ return names;
+}
+
+sockaddr* IfAddrHelper::GetAddr(int family, std::string name) {
+ for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) {
+ continue;
+ }
+ if (name == ifa->ifa_name) {
+ return ifa->ifa_addr;
+ }
+ }
+ return nullptr;
+}
+
+PosixErrorOr<int> IfAddrHelper::GetIndex(std::string name) {
+ return InterfaceIndex(name);
+}
+
+std::string GetAddr4Str(in_addr* a) {
+ char str[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, a, str, sizeof(str));
+ return std::string(str);
+}
+
+std::string GetAddr6Str(in6_addr* a) {
+ char str[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, a, str, sizeof(str));
+ return std::string(str);
+}
+
+std::string GetAddrStr(sockaddr* a) {
+ if (a->sa_family == AF_INET) {
+ auto src = &(reinterpret_cast<sockaddr_in*>(a)->sin_addr);
+ return GetAddr4Str(src);
+ } else if (a->sa_family == AF_INET6) {
+ auto src = &(reinterpret_cast<sockaddr_in6*>(a)->sin6_addr);
+ return GetAddr6Str(src);
+ }
+ return std::string("<invalid>");
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h
index 6898effb8..b498a053d 100644
--- a/test/syscalls/linux/ip_socket_test_util.h
+++ b/test/syscalls/linux/ip_socket_test_util.h
@@ -15,7 +15,12 @@
#ifndef GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_
#define GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <sys/types.h>
+
#include <string>
+
#include "test/syscalls/linux/socket_test_util.h"
namespace gvisor {
@@ -66,6 +71,35 @@ SocketKind IPv4UDPUnboundSocket(int type);
// a SimpleSocket created with AF_INET, SOCK_STREAM and the given type.
SocketKind IPv4TCPUnboundSocket(int type);
+// IfAddrHelper is a helper class that determines the local interfaces present
+// and provides functions to obtain their names, index numbers, and IP address.
+class IfAddrHelper {
+ public:
+ IfAddrHelper() : ifaddr_(nullptr) {}
+ ~IfAddrHelper() { Release(); }
+
+ PosixError Load();
+ void Release();
+
+ std::vector<std::string> InterfaceList(int family);
+
+ struct sockaddr* GetAddr(int family, std::string name);
+ PosixErrorOr<int> GetIndex(std::string name);
+
+ private:
+ struct ifaddrs* ifaddr_;
+};
+
+// GetAddr4Str returns the given IPv4 network address structure as a string.
+std::string GetAddr4Str(in_addr* a);
+
+// GetAddr6Str returns the given IPv6 network address structure as a string.
+std::string GetAddr6Str(in6_addr* a);
+
+// GetAddrStr returns the given IPv4 or IPv6 network address structure as a
+// string.
+std::string GetAddrStr(sockaddr* a);
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
index 0ec828d8d..d9aa7ff3f 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
@@ -40,7 +40,7 @@ TestAddress V4Multicast() {
return t;
}
-// Check that packets are not received without a group memebership. Default send
+// Check that packets are not received without a group membership. Default send
// interface configured by bind.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNoGroup) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
index 6b92e05aa..c85ae30dc 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
@@ -15,10 +15,13 @@
#include "test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h"
#include <arpa/inet.h>
+#include <ifaddrs.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
+
+#include <cstdint>
#include <cstdio>
#include <cstring>
@@ -32,6 +35,52 @@
namespace gvisor {
namespace testing {
+TestAddress V4EmptyAddress() {
+ TestAddress t("V4Empty");
+ t.addr.ss_family = AF_INET;
+ t.addr_len = sizeof(sockaddr_in);
+ return t;
+}
+
+void IPv4UDPUnboundExternalNetworkingSocketTest::SetUp() {
+ got_if_infos_ = false;
+
+ // Get interface list.
+ std::vector<std::string> if_names;
+ ASSERT_NO_ERRNO(if_helper_.Load());
+ if_names = if_helper_.InterfaceList(AF_INET);
+ if (if_names.size() != 2) {
+ return;
+ }
+
+ // Figure out which interface is where.
+ int lo = 0, eth = 1;
+ if (if_names[lo] != "lo") {
+ lo = 1;
+ eth = 0;
+ }
+
+ if (if_names[lo] != "lo") {
+ return;
+ }
+
+ lo_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(if_names[lo]));
+ lo_if_addr_ = if_helper_.GetAddr(AF_INET, if_names[lo]);
+ if (lo_if_addr_ == nullptr) {
+ return;
+ }
+ lo_if_sin_addr_ = reinterpret_cast<sockaddr_in*>(lo_if_addr_)->sin_addr;
+
+ eth_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(if_names[eth]));
+ eth_if_addr_ = if_helper_.GetAddr(AF_INET, if_names[eth]);
+ if (eth_if_addr_ == nullptr) {
+ return;
+ }
+ eth_if_sin_addr_ = reinterpret_cast<sockaddr_in*>(eth_if_addr_)->sin_addr;
+
+ got_if_infos_ = true;
+}
+
// Verifies that a newly instantiated UDP socket does not have the
// broadcast socket option enabled.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, UDPBroadcastDefault) {
@@ -658,7 +707,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
sender->get(), reinterpret_cast<sockaddr*>(&sendto_addr.addr),
sendto_addr.addr_len),
SyscallSucceeds());
- TestAddress sender_addr("");
+ auto sender_addr = V4EmptyAddress();
ASSERT_THAT(
getsockname(sender->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr),
&sender_addr.addr_len),
@@ -674,7 +723,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Receive a multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- TestAddress src_addr("");
+ auto src_addr = V4EmptyAddress();
ASSERT_THAT(
RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0,
reinterpret_cast<sockaddr*>(&src_addr.addr),
@@ -688,5 +737,113 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
EXPECT_EQ(sender_addr_in->sin_addr.s_addr, src_addr_in->sin_addr.s_addr);
}
+// Check that when setting the IP_MULTICAST_IF option to both an index pointing
+// to the loopback interface and an address pointing to the non-loopback
+// interface, a multicast packet sent out uses the latter as its source address.
+TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
+ IpMulticastLoopbackIfNicAndAddr) {
+ // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
+ // IPv4 address on eth0.
+ SKIP_IF(!got_if_infos_);
+
+ // Create receiver, bind to ANY and join the multicast group.
+ auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto receiver_addr = V4Any();
+ ASSERT_THAT(
+ bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ receiver_addr.addr_len),
+ SyscallSucceeds());
+ socklen_t receiver_addr_len = receiver_addr.addr_len;
+ ASSERT_THAT(getsockname(receiver->get(),
+ reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ &receiver_addr_len),
+ SyscallSucceeds());
+ EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
+ int receiver_port =
+ reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
+ ip_mreqn group = {};
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ group.imr_ifindex = lo_if_idx_;
+ ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
+ sizeof(group)),
+ SyscallSucceeds());
+
+ // Set outgoing multicast interface config, with NIC and addr pointing to
+ // different interfaces.
+ auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ ip_mreqn iface = {};
+ iface.imr_ifindex = lo_if_idx_;
+ iface.imr_address = eth_if_sin_addr_;
+ ASSERT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallSucceeds());
+
+ // Send a multicast packet.
+ auto sendto_addr = V4Multicast();
+ reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = receiver_port;
+ char send_buf[4] = {};
+ ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0,
+ reinterpret_cast<sockaddr*>(&sendto_addr.addr),
+ sendto_addr.addr_len),
+ SyscallSucceedsWithValue(sizeof(send_buf)));
+
+ // Receive a multicast packet.
+ char recv_buf[sizeof(send_buf)] = {};
+ auto src_addr = V4EmptyAddress();
+ ASSERT_THAT(
+ RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0,
+ reinterpret_cast<sockaddr*>(&src_addr.addr),
+ &src_addr.addr_len),
+ SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_EQ(sizeof(struct sockaddr_in), src_addr.addr_len);
+ sockaddr_in* src_addr_in = reinterpret_cast<sockaddr_in*>(&src_addr.addr);
+
+ // FIXME (b/137781162): When sending a multicast packet use the proper logic
+ // to determine the packet's src-IP.
+ SKIP_IF(IsRunningOnGvisor());
+
+ // Verify the received source address.
+ EXPECT_EQ(eth_if_sin_addr_.s_addr, src_addr_in->sin_addr.s_addr);
+}
+
+// Check that when we are bound to one interface we can set IP_MULTICAST_IF to
+// another interface.
+TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
+ IpMulticastLoopbackBindToOneIfSetMcastIfToAnother) {
+ // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
+ // IPv4 address on eth0.
+ SKIP_IF(!got_if_infos_);
+
+ // FIXME (b/137790511): When bound to one interface it is not possible to set
+ // IP_MULTICAST_IF to a different interface.
+ SKIP_IF(IsRunningOnGvisor());
+
+ // Create sender and bind to eth interface.
+ auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ ASSERT_THAT(bind(sender->get(), eth_if_addr_, sizeof(sockaddr_in)),
+ SyscallSucceeds());
+
+ // Run through all possible combinations of index and address for
+ // IP_MULTICAST_IF that selects the loopback interface.
+ struct {
+ int imr_ifindex;
+ struct in_addr imr_address;
+ } test_data[] = {
+ {lo_if_idx_, {}},
+ {0, lo_if_sin_addr_},
+ {lo_if_idx_, lo_if_sin_addr_},
+ {lo_if_idx_, eth_if_sin_addr_},
+ };
+ for (auto t : test_data) {
+ ip_mreqn iface = {};
+ iface.imr_ifindex = t.imr_ifindex;
+ iface.imr_address = t.imr_address;
+ EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallSucceeds())
+ << "imr_index=" << iface.imr_ifindex
+ << " imr_address=" << GetAddr4Str(&iface.imr_address);
+ }
+}
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
index 45e1d37ea..bec2e96ee 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
@@ -15,6 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_test_util.h"
namespace gvisor {
@@ -22,7 +23,24 @@ namespace testing {
// Test fixture for tests that apply to unbound IPv4 UDP sockets in a sandbox
// with external networking support.
-using IPv4UDPUnboundExternalNetworkingSocketTest = SimpleSocketTest;
+class IPv4UDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest {
+ protected:
+ void SetUp();
+
+ IfAddrHelper if_helper_;
+
+ // got_if_infos_ is set to false if SetUp() could not obtain all interface
+ // infos that we need.
+ bool got_if_infos_;
+
+ // Interface infos.
+ int lo_if_idx_;
+ int eth_if_idx_;
+ sockaddr* lo_if_addr_;
+ sockaddr* eth_if_addr_;
+ in_addr lo_if_sin_addr_;
+ in_addr eth_if_sin_addr_;
+};
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/timerfd.cc b/test/syscalls/linux/timerfd.cc
index 9df53612f..86ed87b7c 100644
--- a/test/syscalls/linux/timerfd.cc
+++ b/test/syscalls/linux/timerfd.cc
@@ -44,21 +44,24 @@ PosixErrorOr<FileDescriptor> TimerfdCreate(int clockid, int flags) {
//
// - Because clock_gettime(CLOCK_MONOTONIC) is implemented through the VDSO,
// it technically uses a closely-related, but distinct, time domain from the
-// CLOCK_MONOTONIC used to trigger timerfd expirations.
+// CLOCK_MONOTONIC used to trigger timerfd expirations. The same applies to
+// CLOCK_BOOTTIME which is an alias for CLOCK_MONOTONIC.
absl::Duration TimerSlack() { return absl::Milliseconds(500); }
-TEST(TimerfdTest, IsInitiallyStopped) {
- auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
+class TimerfdTest : public ::testing::TestWithParam<int> {};
+
+TEST_P(TimerfdTest, IsInitiallyStopped) {
+ auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
struct itimerspec its = {};
ASSERT_THAT(timerfd_gettime(tfd.get(), &its), SyscallSucceeds());
EXPECT_EQ(0, its.it_value.tv_sec);
EXPECT_EQ(0, its.it_value.tv_nsec);
}
-TEST(TimerfdTest, SingleShot) {
+TEST_P(TimerfdTest, SingleShot) {
constexpr absl::Duration kDelay = absl::Seconds(1);
- auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
+ auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
struct itimerspec its = {};
its.it_value = absl::ToTimespec(kDelay);
ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
@@ -72,11 +75,11 @@ TEST(TimerfdTest, SingleShot) {
EXPECT_EQ(1, val);
}
-TEST(TimerfdTest, Periodic) {
+TEST_P(TimerfdTest, Periodic) {
constexpr absl::Duration kDelay = absl::Seconds(1);
constexpr int kPeriods = 3;
- auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
+ auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
struct itimerspec its = {};
its.it_value = absl::ToTimespec(kDelay);
its.it_interval = absl::ToTimespec(kDelay);
@@ -92,10 +95,10 @@ TEST(TimerfdTest, Periodic) {
EXPECT_GE(val, kPeriods);
}
-TEST(TimerfdTest, BlockingRead) {
+TEST_P(TimerfdTest, BlockingRead) {
constexpr absl::Duration kDelay = absl::Seconds(3);
- auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
+ auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
struct itimerspec its = {};
its.it_value.tv_sec = absl::ToInt64Seconds(kDelay);
auto const start_time = absl::Now();
@@ -111,11 +114,11 @@ TEST(TimerfdTest, BlockingRead) {
EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay);
}
-TEST(TimerfdTest, NonblockingRead_NoRandomSave) {
+TEST_P(TimerfdTest, NonblockingRead_NoRandomSave) {
constexpr absl::Duration kDelay = absl::Seconds(5);
auto const tfd =
- ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
+ ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
// Since the timer is initially disabled and has never fired, read should
// return EAGAIN.
@@ -148,11 +151,11 @@ TEST(TimerfdTest, NonblockingRead_NoRandomSave) {
SyscallFailsWithErrno(EAGAIN));
}
-TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) {
+TEST_P(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) {
constexpr absl::Duration kDelay = absl::Seconds(3);
auto const tfd =
- ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
+ ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
struct itimerspec its = {};
its.it_value.tv_sec = absl::ToInt64Seconds(kDelay);
auto const start_time = absl::Now();
@@ -181,15 +184,15 @@ TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) {
SyscallFailsWithErrno(EAGAIN));
}
-TEST(TimerfdTest, SetAbsoluteTime) {
+TEST_P(TimerfdTest, SetAbsoluteTime) {
constexpr absl::Duration kDelay = absl::Seconds(3);
// Use a non-blocking timerfd so that if TFD_TIMER_ABSTIME is incorrectly
// non-functional, we get EAGAIN rather than a test timeout.
auto const tfd =
- ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
+ ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
struct itimerspec its = {};
- ASSERT_THAT(clock_gettime(CLOCK_MONOTONIC, &its.it_value), SyscallSucceeds());
+ ASSERT_THAT(clock_gettime(GetParam(), &its.it_value), SyscallSucceeds());
its.it_value.tv_sec += absl::ToInt64Seconds(kDelay);
ASSERT_THAT(timerfd_settime(tfd.get(), TFD_TIMER_ABSTIME, &its, nullptr),
SyscallSucceeds());
@@ -201,7 +204,34 @@ TEST(TimerfdTest, SetAbsoluteTime) {
EXPECT_EQ(1, val);
}
-TEST(TimerfdTest, ClockRealtime) {
+TEST_P(TimerfdTest, IllegalReadWrite) {
+ auto const tfd =
+ ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
+ uint64_t val = 0;
+ EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0),
+ SyscallFailsWithErrno(ESPIPE));
+ EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)),
+ SyscallFailsWithErrno(EINVAL));
+ EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0),
+ SyscallFailsWithErrno(ESPIPE));
+}
+
+std::string PrintClockId(::testing::TestParamInfo<int> info) {
+ switch (info.param) {
+ case CLOCK_MONOTONIC:
+ return "CLOCK_MONOTONIC";
+ case CLOCK_BOOTTIME:
+ return "CLOCK_BOOTTIME";
+ default:
+ return absl::StrCat(info.param);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(AllTimerTypes, TimerfdTest,
+ ::testing::Values(CLOCK_MONOTONIC, CLOCK_BOOTTIME),
+ PrintClockId);
+
+TEST(TimerfdClockRealtimeTest, ClockRealtime) {
// Since CLOCK_REALTIME can, by definition, change, we can't make any
// non-flaky assertions about the amount of time it takes for a
// CLOCK_REALTIME-based timer to expire. Just check that it expires at all,
@@ -220,18 +250,6 @@ TEST(TimerfdTest, ClockRealtime) {
EXPECT_EQ(1, val);
}
-TEST(TimerfdTest, IllegalReadWrite) {
- auto const tfd =
- ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
- uint64_t val = 0;
- EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0),
- SyscallFailsWithErrno(ESPIPE));
- EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)),
- SyscallFailsWithErrno(EINVAL));
- EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0),
- SyscallFailsWithErrno(ESPIPE));
-}
-
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/vdso_clock_gettime.cc b/test/syscalls/linux/vdso_clock_gettime.cc
index 759a50569..40c0014b9 100644
--- a/test/syscalls/linux/vdso_clock_gettime.cc
+++ b/test/syscalls/linux/vdso_clock_gettime.cc
@@ -39,6 +39,8 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
return "CLOCK_MONOTONIC";
case CLOCK_REALTIME:
return "CLOCK_REALTIME";
+ case CLOCK_BOOTTIME:
+ return "CLOCK_BOOTTIME";
default:
return absl::StrCat(info.param);
}
@@ -95,7 +97,8 @@ TEST_P(CorrectVDSOClockTest, IsCorrect) {
}
INSTANTIATE_TEST_SUITE_P(ClockGettime, CorrectVDSOClockTest,
- ::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME),
+ ::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME,
+ CLOCK_BOOTTIME),
PrintClockId);
} // namespace