blob: 444853f40bc9bdc0c767048f8854298dd2d91015 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <sys/select.h>
#include <fbl/unique_fd.h>
#include <zxtest/zxtest.h>
#include "predicates.h"
namespace {
constexpr std::chrono::duration minimum_duration = std::chrono::milliseconds(1);
// Like with poll and ppoll, Fuchsia guarantees that selecting on 0
// fds in the fd_sets is equivalent to sleeping until the timeout.
//
// This is extremely similar to the tests of poll and ppoll in
// fdio_poll.cc.
TEST(Select, SelectZeroFds) {
// NB: We pass non-zero here to test deferred cleanup when no work is done by select.
for (int i = 0; i < 5; ++i) {
struct timeval timeout = {
.tv_usec = std::chrono::microseconds(minimum_duration).count(),
};
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
const auto begin = std::chrono::steady_clock::now();
EXPECT_SUCCESS(select(i, &readfds, &writefds, &exceptfds, &timeout));
EXPECT_GE(std::chrono::steady_clock::now() - begin, minimum_duration);
// All bits in all the fd sets should be 0.
for (int fd = 0; fd < FD_SETSIZE; ++fd) {
EXPECT_FALSE(FD_ISSET(fd, &readfds));
EXPECT_FALSE(FD_ISSET(fd, &writefds));
EXPECT_FALSE(FD_ISSET(fd, &exceptfds));
}
}
}
TEST(Select, Pipe) {
std::array<fbl::unique_fd, 2> fds;
int int_fds[fds.size()];
ASSERT_SUCCESS(pipe(int_fds));
fds[0].reset(int_fds[0]);
fds[1].reset(int_fds[1]);
{
struct timeval timeout = {
.tv_usec = std::chrono::microseconds(minimum_duration).count(),
};
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fds[0].get(), &readfds);
EXPECT_EQ(select(fds[0].get() + 1, &readfds, nullptr, nullptr, &timeout), 0, "%s",
strerror(errno));
EXPECT_FALSE(FD_ISSET(fds[0].get(), &readfds));
}
{
struct timeval timeout = {
.tv_usec = std::chrono::microseconds(minimum_duration).count(),
};
char c;
ASSERT_EQ(write(fds[1].get(), &c, sizeof(c)), ssize_t(sizeof(c)), "%s", strerror(errno));
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fds[0].get(), &readfds);
EXPECT_EQ(select(fds[0].get() + 1, &readfds, nullptr, nullptr, &timeout), 1, "%s",
strerror(errno));
EXPECT_TRUE(FD_ISSET(fds[0].get(), &readfds));
}
{
struct timeval timeout = {
.tv_usec = std::chrono::microseconds(minimum_duration).count(),
};
ASSERT_SUCCESS(close(fds[1].get()));
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fds[0].get(), &readfds);
EXPECT_EQ(select(fds[0].get() + 1, &readfds, nullptr, nullptr, &timeout), 1, "%s",
strerror(errno));
EXPECT_TRUE(FD_ISSET(fds[0].get(), &readfds));
}
}
TEST(Select, SelectNegative) {
fd_set readfds;
fd_set writefds;
fd_set exceptfds;
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
{
struct timeval timeout = {
.tv_sec = -1,
};
EXPECT_EQ(select(0, &readfds, &writefds, &exceptfds, &timeout), -1);
EXPECT_ERRNO(EINVAL);
}
{
struct timeval timeout = {
.tv_usec = -1,
};
EXPECT_EQ(select(0, &readfds, &writefds, &exceptfds, &timeout), -1);
EXPECT_ERRNO(EINVAL);
}
}
} // namespace