[PATCH 1/6] selftests/filesystems: add initial select and poll selftest

From: Willem de Bruijn
Date: Mon Jan 11 2021 - 19:33:19 EST


From: Willem de Bruijn <willemb@xxxxxxxxxx>

Add initial code coverage for select, pselect, poll and ppoll.

Open a socketpair and wait for a read event.
1. run with data waiting
2. run to timeout, if a (short) timeout is specified.

Also optionally pass sigset to pselect and ppoll, to exercise
all datapaths. Build with -m32, -mx32 and -m64 to cover all the
various compat and 32/64-bit time syscall implementations.

Signed-off-by: Willem de Bruijn <willemb@xxxxxxxxxx>
---
.../testing/selftests/filesystems/.gitignore | 1 +
tools/testing/selftests/filesystems/Makefile | 2 +-
.../selftests/filesystems/selectpoll.c | 207 ++++++++++++++++++
3 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/filesystems/selectpoll.c

diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore
index f0c0ff20d6cf..d4a2e50475ea 100644
--- a/tools/testing/selftests/filesystems/.gitignore
+++ b/tools/testing/selftests/filesystems/.gitignore
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
dnotify_test
devpts_pts
+selectpoll
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile
index 129880fb42d3..8de184865fa4 100644
--- a/tools/testing/selftests/filesystems/Makefile
+++ b/tools/testing/selftests/filesystems/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0

CFLAGS += -I../../../../usr/include/
-TEST_GEN_PROGS := devpts_pts
+TEST_GEN_PROGS := devpts_pts selectpoll
TEST_GEN_PROGS_EXTENDED := dnotify_test

include ../lib.mk
diff --git a/tools/testing/selftests/filesystems/selectpoll.c b/tools/testing/selftests/filesystems/selectpoll.c
new file mode 100644
index 000000000000..315da0786a6c
--- /dev/null
+++ b/tools/testing/selftests/filesystems/selectpoll.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+#include <asm/unistd.h>
+#include <poll.h>
+#include <unistd.h>
+#include <assert.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include "../kselftest_harness.h"
+
+const unsigned long timeout_us = 5UL * 1000;
+const unsigned long timeout_ns = timeout_us * 1000;
+
+/* (p)select: basic invocation, optionally with data waiting */
+
+FIXTURE(select_basic)
+{
+ fd_set readfds;
+ int sfd[2];
+};
+
+FIXTURE_SETUP(select_basic)
+{
+ ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, self->sfd), 0);
+
+ FD_ZERO(&self->readfds);
+ FD_SET(self->sfd[0], &self->readfds);
+ FD_SET(self->sfd[1], &self->readfds);
+}
+
+FIXTURE_TEARDOWN(select_basic)
+{
+ /* FD_ISSET(self->sfd[0] tested in TEST_F: depends on timeout */
+ ASSERT_EQ(FD_ISSET(self->sfd[1], &self->readfds), 0);
+
+ EXPECT_EQ(close(self->sfd[0]), 0);
+ EXPECT_EQ(close(self->sfd[1]), 0);
+}
+
+TEST_F(select_basic, select)
+{
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ ASSERT_EQ(select(self->sfd[1] + 1, &self->readfds,
+ NULL, NULL, NULL), 1);
+ ASSERT_NE(FD_ISSET(self->sfd[0], &self->readfds), 0);
+}
+
+TEST_F(select_basic, select_with_timeout)
+{
+ struct timeval tv = { .tv_usec = timeout_us };
+
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ ASSERT_EQ(select(self->sfd[1] + 1, &self->readfds,
+ NULL, NULL, &tv), 1);
+ ASSERT_GE(tv.tv_usec, 1000);
+ ASSERT_NE(FD_ISSET(self->sfd[0], &self->readfds), 0);
+}
+
+TEST_F(select_basic, select_timeout)
+{
+ struct timeval tv = { .tv_usec = timeout_us };
+
+ ASSERT_EQ(select(self->sfd[1] + 1, &self->readfds,
+ NULL, NULL, &tv), 0);
+ ASSERT_EQ(FD_ISSET(self->sfd[0], &self->readfds), 0);
+}
+
+TEST_F(select_basic, pselect)
+{
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ ASSERT_EQ(pselect(self->sfd[1] + 1, &self->readfds,
+ NULL, NULL, NULL, NULL), 1);
+ ASSERT_NE(FD_ISSET(self->sfd[0], &self->readfds), 0);
+}
+
+TEST_F(select_basic, pselect_with_timeout)
+{
+ struct timespec ts = { .tv_nsec = timeout_ns };
+
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ ASSERT_EQ(pselect(self->sfd[1] + 1, &self->readfds,
+ NULL, NULL, &ts, NULL), 1);
+ ASSERT_GE(ts.tv_nsec, 1000);
+ ASSERT_NE(FD_ISSET(self->sfd[0], &self->readfds), 0);
+}
+
+TEST_F(select_basic, pselect_timeout)
+{
+ struct timespec ts = { .tv_nsec = timeout_ns };
+
+ ASSERT_EQ(pselect(self->sfd[1] + 1, &self->readfds,
+ NULL, NULL, &ts, NULL), 0);
+ ASSERT_EQ(FD_ISSET(self->sfd[0], &self->readfds), 0);
+}
+
+TEST_F(select_basic, pselect_sigset_with_timeout)
+{
+ struct timespec ts = { .tv_nsec = timeout_ns };
+ sigset_t sigmask;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR1);
+ sigprocmask(SIG_SETMASK, &sigmask, NULL);
+ sigemptyset(&sigmask);
+
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ ASSERT_EQ(pselect(self->sfd[1] + 1, &self->readfds,
+ NULL, NULL, &ts, &sigmask), 1);
+ ASSERT_GE(ts.tv_nsec, 1000);
+ ASSERT_NE(FD_ISSET(self->sfd[0], &self->readfds), 0);
+}
+
+/* (p)poll: basic invocation with data waiting */
+
+FIXTURE(poll_basic)
+{
+ struct pollfd pfds[2];
+ int sfd[2];
+};
+
+FIXTURE_SETUP(poll_basic)
+{
+ ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, self->sfd), 0);
+
+ self->pfds[0].events = POLLIN;
+ self->pfds[0].revents = 0;
+ self->pfds[0].fd = self->sfd[0];
+
+ self->pfds[1].events = POLLIN;
+ self->pfds[1].revents = 0;
+ self->pfds[1].fd = self->sfd[1];
+}
+
+FIXTURE_TEARDOWN(poll_basic)
+{
+ /* FD_ISSET(self->pfds[0] tested in TEST_F: depends on timeout */
+ EXPECT_EQ(self->pfds[1].revents & POLLIN, 0);
+
+ EXPECT_EQ(close(self->sfd[0]), 0);
+ EXPECT_EQ(close(self->sfd[1]), 0);
+}
+
+TEST_F(poll_basic, poll)
+{
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ EXPECT_EQ(poll(self->pfds, ARRAY_SIZE(self->pfds), 0), 1);
+ EXPECT_EQ(self->pfds[0].revents & POLLIN, POLLIN);
+}
+
+TEST_F(poll_basic, poll_with_timeout)
+{
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ EXPECT_EQ(poll(self->pfds, ARRAY_SIZE(self->pfds), 1001), 1);
+ EXPECT_EQ(self->pfds[0].revents & POLLIN, POLLIN);
+}
+
+TEST_F(poll_basic, poll_timeout)
+{
+ EXPECT_EQ(poll(self->pfds, ARRAY_SIZE(self->pfds), 1001), 0);
+ EXPECT_EQ(self->pfds[0].revents & POLLIN, 0);
+}
+
+TEST_F(poll_basic, ppoll)
+{
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ EXPECT_EQ(ppoll(self->pfds, ARRAY_SIZE(self->pfds), NULL, NULL), 1);
+ EXPECT_EQ(self->pfds[0].revents & POLLIN, POLLIN);
+}
+
+TEST_F(poll_basic, ppoll_with_timeout)
+{
+ struct timespec ts = { .tv_nsec = timeout_ns };
+
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ EXPECT_EQ(ppoll(self->pfds, ARRAY_SIZE(self->pfds), &ts, NULL), 1);
+ ASSERT_GE(ts.tv_nsec, 1000);
+ EXPECT_EQ(self->pfds[0].revents & POLLIN, POLLIN);
+}
+
+TEST_F(poll_basic, ppoll_timeout)
+{
+ struct timespec ts = { .tv_nsec = timeout_ns };
+
+ EXPECT_EQ(ppoll(self->pfds, ARRAY_SIZE(self->pfds), &ts, NULL), 0);
+ EXPECT_EQ(self->pfds[0].revents & POLLIN, 0);
+}
+
+TEST_F(poll_basic, ppoll_sigset_with_timeout)
+{
+ struct timespec ts = { .tv_nsec = timeout_ns };
+ sigset_t sigmask;
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGUSR1);
+ sigprocmask(SIG_SETMASK, &sigmask, NULL);
+ sigemptyset(&sigmask);
+
+ ASSERT_EQ(write(self->sfd[1], "w", 1), 1);
+ EXPECT_EQ(ppoll(self->pfds, ARRAY_SIZE(self->pfds), &ts, &sigmask), 1);
+ ASSERT_GE(ts.tv_nsec, 1000);
+ EXPECT_EQ(self->pfds[0].revents & POLLIN, POLLIN);
+}
+
+TEST_HARNESS_MAIN
--
2.30.0.284.gd98b1dd5eaa7-goog