Paul Eggert wrote on 2011-07-05: > --- /dev/null > +++ b/tests/test-pselect.c > @@ -0,0 +1,2 @@ > +#define TEST_PSELECT > +#include "test-select.c" > diff --git a/tests/test-select.c b/tests/test-select.c > index 5c15320..9e927f8 100644
> --- a/tests/test-select.c > +++ b/tests/test-select.c > @@ -21,8 +21,16 @@ > #include <sys/select.h> > > #include "signature.h" > + > +#ifdef TEST_PSELECT > +SIGNATURE_CHECK (pselect, int, > + (int, fd_set *restrict, fd_set *restrict, fd_set *restrict, > + struct timespec const *restrict, const sigset_t > *restrict)); > +#else > SIGNATURE_CHECK (select, int, (int, fd_set *, fd_set *, fd_set *, > struct timeval *)); > +#endif > + > /* The following may be macros without underlying functions, so only > check signature if they are not macros. */ > #ifndef FD_CLR > @@ -190,7 +198,20 @@ do_select (int fd, int ev, struct timeval *timeout) > FD_SET (fd, &wfds); > if (ev & SEL_EXC) > FD_SET (fd, &xfds); > +#ifdef TEST_PSELECT > + { This is quite a bit of #ifdefology, for my taste. Here's a proposed refactoring that moves the common code to a .h file. So that it is clear and easy to add new code to the tests: - new code for select() goes in tests-select.c. - new code for pselect() goes in tests-pselect.c. - new code for both goes in tests-select.h. Objections to this change? 2011-07-22 Bruno Haible <br...@clisp.org> select tests, pselect tests: Refactor. * tests/test-select.h: New file, extracted from tests/test-select.c. (select_fn): New type. (test, do_select, do_select_nowait, do_select_wait, test_tty, test_connect_first, test_accept_first, test_pair, test_socket_pair, test_pipe): Add my_select argument. (test_function): Renamed from main. Add my_select argument. * modules/select-tests (Files): Add tests/test-select.h. * tests/test-pselect.c: Include test-select.h instead of test-select.c. (my_select, main): New functions. * modules/pselect-tests (Files): Add tests/test-select.h, tests/macros.h, tests/signature.h. (Depends-on): Remove select-tests. Add dependencies of test-select.h. (configure.ac): Check for <sys/wait.h>. ============================= tests/test-select.h ============================= /* Test of select() substitute. Copyright (C) 2008-2011 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* Written by Paolo Bonzini, 2008. */ #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <stdbool.h> #include <sys/ioctl.h> #include <errno.h> #include "macros.h" #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ # define WIN32_NATIVE #endif #ifdef HAVE_SYS_WAIT_H # include <sys/wait.h> #endif #ifndef SO_REUSEPORT # define SO_REUSEPORT SO_REUSEADDR #endif #define TEST_PORT 12345 typedef int (*select_fn) (int, fd_set *, fd_set *, fd_set *, struct timeval *); /* Minimal testing infrastructure. */ static int failures; static void failed (const char *reason) { if (++failures > 1) printf (" "); printf ("failed (%s)\n", reason); } static int test (void (*fn) (select_fn), select_fn my_select, const char *msg) { failures = 0; printf ("%s... ", msg); fflush (stdout); fn (my_select); if (!failures) printf ("passed\n"); return failures; } /* Funny socket code. */ static int open_server_socket (void) { int s, x; struct sockaddr_in ia; s = socket (AF_INET, SOCK_STREAM, 0); memset (&ia, 0, sizeof (ia)); ia.sin_family = AF_INET; inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); ia.sin_port = htons (TEST_PORT); if (bind (s, (struct sockaddr *) &ia, sizeof (ia)) < 0) { perror ("bind"); exit (77); } x = 1; setsockopt (s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x)); if (listen (s, 1) < 0) { perror ("listen"); exit (77); } return s; } static int connect_to_socket (bool blocking) { int s; struct sockaddr_in ia; s = socket (AF_INET, SOCK_STREAM, 0); memset (&ia, 0, sizeof (ia)); ia.sin_family = AF_INET; inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); ia.sin_port = htons (TEST_PORT); if (!blocking) { #ifdef WIN32_NATIVE unsigned long iMode = 1; ioctl (s, FIONBIO, (char *) &iMode); #elif defined F_GETFL int oldflags = fcntl (s, F_GETFL, NULL); if (!(oldflags & O_NONBLOCK)) fcntl (s, F_SETFL, oldflags | O_NONBLOCK); #endif } if (connect (s, (struct sockaddr *) &ia, sizeof (ia)) < 0 && (blocking || errno != EINPROGRESS)) { perror ("connect"); exit (77); } return s; } /* A slightly more convenient interface to select(2). Waits until a specific event occurs on a file descriptor FD. EV is a bit mask of events to look for: SEL_IN - input can be polled without blocking, SEL_OUT - output can be provided without blocking, SEL_EXC - an exception occurred, A maximum wait time is specified by TIMEOUT. *TIMEOUT = { 0, 0 } means to return immediately, TIMEOUT = NULL means to wait indefinitely. */ enum { SEL_IN = 1, SEL_OUT = 2, SEL_EXC = 4 }; static int do_select (int fd, int ev, struct timeval *timeout, select_fn my_select) { fd_set rfds, wfds, xfds; int r, rev; FD_ZERO (&rfds); FD_ZERO (&wfds); FD_ZERO (&xfds); if (ev & SEL_IN) FD_SET (fd, &rfds); if (ev & SEL_OUT) FD_SET (fd, &wfds); if (ev & SEL_EXC) FD_SET (fd, &xfds); r = my_select (fd + 1, &rfds, &wfds, &xfds, timeout); if (r < 0) return r; rev = 0; if (FD_ISSET (fd, &rfds)) rev |= SEL_IN; if (FD_ISSET (fd, &wfds)) rev |= SEL_OUT; if (FD_ISSET (fd, &xfds)) rev |= SEL_EXC; if (rev && r == 0) failed ("select returned 0"); if (rev & ~ev) failed ("select returned unrequested events"); return rev; } static int do_select_nowait (int fd, int ev, select_fn my_select) { struct timeval tv0; tv0.tv_sec = 0; tv0.tv_usec = 0; return do_select (fd, ev, &tv0, my_select); } static int do_select_wait (int fd, int ev, select_fn my_select) { return do_select (fd, ev, NULL, my_select); } /* Test select(2) for TTYs. */ #ifdef INTERACTIVE static void test_tty (select_fn my_select) { if (do_select_nowait (0, SEL_IN, my_select) != 0) failed ("can read"); if (do_select_nowait (0, SEL_OUT, my_select) == 0) failed ("cannot write"); if (do_select_wait (0, SEL_IN, my_select) == 0) failed ("return with infinite timeout"); getchar (); if (do_select_nowait (0, SEL_IN, my_select) != 0) failed ("can read after getc"); } #endif /* Test select(2) for unconnected nonblocking sockets. */ static void test_connect_first (select_fn my_select) { int s = open_server_socket (); struct sockaddr_in ia; socklen_t addrlen; int c1, c2; if (do_select_nowait (s, SEL_IN | SEL_EXC, my_select) != 0) failed ("can read, socket not connected"); c1 = connect_to_socket (false); if (do_select_wait (s, SEL_IN | SEL_EXC, my_select) != SEL_IN) failed ("expecting readability on passive socket"); if (do_select_nowait (s, SEL_IN | SEL_EXC, my_select) != SEL_IN) failed ("expecting readability on passive socket"); addrlen = sizeof (ia); c2 = accept (s, (struct sockaddr *) &ia, &addrlen); ASSERT (close (s) == 0); ASSERT (close (c1) == 0); ASSERT (close (c2) == 0); } /* Test select(2) for unconnected blocking sockets. */ static void test_accept_first (select_fn my_select) { #ifndef WIN32_NATIVE int s = open_server_socket (); struct sockaddr_in ia; socklen_t addrlen; char buf[3]; int c, pid; pid = fork (); if (pid < 0) return; if (pid == 0) { addrlen = sizeof (ia); c = accept (s, (struct sockaddr *) &ia, &addrlen); ASSERT (close (s) == 0); ASSERT (write (c, "foo", 3) == 3); ASSERT (read (c, buf, 3) == 3); shutdown (c, SHUT_RD); ASSERT (close (c) == 0); exit (0); } else { ASSERT (close (s) == 0); c = connect_to_socket (true); if (do_select_nowait (c, SEL_OUT, my_select) != SEL_OUT) failed ("cannot write after blocking connect"); ASSERT (write (c, "foo", 3) == 3); wait (&pid); if (do_select_wait (c, SEL_IN, my_select) != SEL_IN) failed ("cannot read data left in the socket by closed process"); ASSERT (read (c, buf, 3) == 3); ASSERT (write (c, "foo", 3) == 3); (void) close (c); /* may fail with errno = ECONNRESET */ } #endif } /* Common code for pipes and connected sockets. */ static void test_pair (int rd, int wd, select_fn my_select) { char buf[3]; if (do_select_wait (wd, SEL_IN | SEL_OUT | SEL_EXC, my_select) != SEL_OUT) failed ("expecting writability before writing"); if (do_select_nowait (wd, SEL_IN | SEL_OUT | SEL_EXC, my_select) != SEL_OUT) failed ("expecting writability before writing"); ASSERT (write (wd, "foo", 3) == 3); if (do_select_wait (rd, SEL_IN, my_select) != SEL_IN) failed ("expecting readability after writing"); if (do_select_nowait (rd, SEL_IN, my_select) != SEL_IN) failed ("expecting readability after writing"); ASSERT (read (rd, buf, 3) == 3); } /* Test select(2) on connected sockets. */ static void test_socket_pair (select_fn my_select) { struct sockaddr_in ia; socklen_t addrlen = sizeof (ia); int s = open_server_socket (); int c1 = connect_to_socket (false); int c2 = accept (s, (struct sockaddr *) &ia, &addrlen); ASSERT (close (s) == 0); test_pair (c1, c2, my_select); ASSERT (close (c1) == 0); ASSERT (write (c2, "foo", 3) == 3); (void) close (c2); /* may fail with errno = ECONNRESET */ } /* Test select(2) on pipes. */ static void test_pipe (select_fn my_select) { int fd[2]; ASSERT (pipe (fd) == 0); test_pair (fd[0], fd[1], my_select); ASSERT (close (fd[0]) == 0); ASSERT (close (fd[1]) == 0); } /* Do them all. */ int test_function (select_fn my_select) { int result; #ifdef INTERACTIVE printf ("Please press Enter\n"); test (test_tty, "TTY", my_select); #endif result = test (test_connect_first, my_select, "Unconnected socket test"); result += test (test_socket_pair, my_select, "Connected sockets test"); result += test (test_accept_first, my_select, "General socket test with fork"); result += test (test_pipe, my_select, "Pipe test"); return result; } =============================================================================== --- modules/pselect-tests.orig Sat Jul 23 04:00:38 2011 +++ modules/pselect-tests Sat Jul 23 03:50:36 2011 @@ -1,10 +1,31 @@ Files: tests/test-pselect.c +tests/test-select.h +tests/macros.h +tests/signature.h Depends-on: -select-tests +stdbool +netinet_in +arpa_inet +unistd +sys_ioctl +extensions +inet_pton +errno +perror +pipe-posix +socket +bind +setsockopt +listen +connect +accept +ioctl +close configure.ac: +AC_CHECK_HEADERS_ONCE([sys/wait.h]) Makefile.am: TESTS += test-pselect --- modules/select-tests.orig Sat Jul 23 04:00:38 2011 +++ modules/select-tests Sat Jul 23 03:26:58 2011 @@ -2,6 +2,7 @@ tests/macros.h tests/signature.h tests/test-select.c +tests/test-select.h tests/test-select-fd.c tests/test-select-in.sh tests/test-select-out.sh --- tests/test-pselect.c.orig Sat Jul 23 04:00:38 2011 +++ tests/test-pselect.c Sat Jul 23 03:49:56 2011 @@ -1,2 +1,48 @@ -#define TEST_PSELECT -#include "test-select.c" +/* Test of pselect() substitute. + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <sys/select.h> + +#include "signature.h" + +SIGNATURE_CHECK (pselect, int, + (int, fd_set *restrict, fd_set *restrict, fd_set *restrict, + struct timespec const *restrict, const sigset_t *restrict)); + +#include "test-select.h" + +static int +my_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) +{ + struct timespec ts; + struct timespec *pts = NULL; + if (timeout) + { + ts.tv_sec = timeout->tv_sec; + ts.tv_nsec = timeout->tv_usec * 1000; + pts = &ts; + } + return pselect (nfds, readfds, writefds, exceptfds, pts, NULL); +} + +int +main (void) +{ + return test_function (my_select); +} --- tests/test-select.c.orig Sat Jul 23 04:00:38 2011 +++ tests/test-select.c Sat Jul 23 03:39:11 2011 @@ -22,382 +22,13 @@ #include "signature.h" -#ifdef TEST_PSELECT -SIGNATURE_CHECK (pselect, int, - (int, fd_set *restrict, fd_set *restrict, fd_set *restrict, - struct timespec const *restrict, const sigset_t *restrict)); -#else SIGNATURE_CHECK (select, int, (int, fd_set *, fd_set *, fd_set *, struct timeval *)); -#endif -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <unistd.h> -#include <fcntl.h> -#include <stdlib.h> -#include <stdbool.h> -#include <sys/ioctl.h> -#include <errno.h> - -#include "macros.h" - -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ -# define WIN32_NATIVE -#endif - -#ifdef HAVE_SYS_WAIT_H -# include <sys/wait.h> -#endif - -#ifndef SO_REUSEPORT -# define SO_REUSEPORT SO_REUSEADDR -#endif - -#define TEST_PORT 12345 - - -/* Minimal testing infrastructure. */ - -static int failures; - -static void -failed (const char *reason) -{ - if (++failures > 1) - printf (" "); - printf ("failed (%s)\n", reason); -} - -static int -test (void (*fn) (void), const char *msg) -{ - failures = 0; - printf ("%s... ", msg); - fflush (stdout); - fn (); - - if (!failures) - printf ("passed\n"); - - return failures; -} - - -/* Funny socket code. */ - -static int -open_server_socket (void) -{ - int s, x; - struct sockaddr_in ia; - - s = socket (AF_INET, SOCK_STREAM, 0); - - memset (&ia, 0, sizeof (ia)); - ia.sin_family = AF_INET; - inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); - ia.sin_port = htons (TEST_PORT); - if (bind (s, (struct sockaddr *) &ia, sizeof (ia)) < 0) - { - perror ("bind"); - exit (77); - } - - x = 1; - setsockopt (s, SOL_SOCKET, SO_REUSEPORT, &x, sizeof (x)); - - if (listen (s, 1) < 0) - { - perror ("listen"); - exit (77); - } - - return s; -} - -static int -connect_to_socket (bool blocking) -{ - int s; - struct sockaddr_in ia; - - s = socket (AF_INET, SOCK_STREAM, 0); - - memset (&ia, 0, sizeof (ia)); - ia.sin_family = AF_INET; - inet_pton (AF_INET, "127.0.0.1", &ia.sin_addr); - ia.sin_port = htons (TEST_PORT); - - if (!blocking) - { -#ifdef WIN32_NATIVE - unsigned long iMode = 1; - ioctl (s, FIONBIO, (char *) &iMode); - -#elif defined F_GETFL - int oldflags = fcntl (s, F_GETFL, NULL); - - if (!(oldflags & O_NONBLOCK)) - fcntl (s, F_SETFL, oldflags | O_NONBLOCK); -#endif - } - - if (connect (s, (struct sockaddr *) &ia, sizeof (ia)) < 0 - && (blocking || errno != EINPROGRESS)) - { - perror ("connect"); - exit (77); - } - - return s; -} - - -/* A slightly more convenient interface to select(2). - Waits until a specific event occurs on a file descriptor FD. - EV is a bit mask of events to look for: - SEL_IN - input can be polled without blocking, - SEL_OUT - output can be provided without blocking, - SEL_EXC - an exception occurred, - A maximum wait time is specified by TIMEOUT. - *TIMEOUT = { 0, 0 } means to return immediately, - TIMEOUT = NULL means to wait indefinitely. */ - -enum { SEL_IN = 1, SEL_OUT = 2, SEL_EXC = 4 }; - -static int -do_select (int fd, int ev, struct timeval *timeout) -{ - fd_set rfds, wfds, xfds; - int r, rev; - - FD_ZERO (&rfds); - FD_ZERO (&wfds); - FD_ZERO (&xfds); - if (ev & SEL_IN) - FD_SET (fd, &rfds); - if (ev & SEL_OUT) - FD_SET (fd, &wfds); - if (ev & SEL_EXC) - FD_SET (fd, &xfds); -#ifdef TEST_PSELECT - { - struct timespec ts, *pts = NULL; - if (timeout) - { - ts.tv_sec = timeout->tv_sec; - ts.tv_nsec = timeout->tv_usec * 1000; - pts = &ts; - } - r = pselect (fd + 1, &rfds, &wfds, &xfds, pts, NULL); - } -#else - r = select (fd + 1, &rfds, &wfds, &xfds, timeout); -#endif - if (r < 0) - return r; - - rev = 0; - if (FD_ISSET (fd, &rfds)) - rev |= SEL_IN; - if (FD_ISSET (fd, &wfds)) - rev |= SEL_OUT; - if (FD_ISSET (fd, &xfds)) - rev |= SEL_EXC; - if (rev && r == 0) - failed ("select returned 0"); - if (rev & ~ev) - failed ("select returned unrequested events"); - - return rev; -} - -static int -do_select_nowait (int fd, int ev) -{ - struct timeval tv0; - tv0.tv_sec = 0; - tv0.tv_usec = 0; - return do_select (fd, ev, &tv0); -} - -static int -do_select_wait (int fd, int ev) -{ - return do_select (fd, ev, NULL); -} - - -/* Test select(2) for TTYs. */ - -#ifdef INTERACTIVE -static void -test_tty (void) -{ - if (do_select_nowait (0, SEL_IN) != 0) - failed ("can read"); - if (do_select_nowait (0, SEL_OUT) == 0) - failed ("cannot write"); - - if (do_select_wait (0, SEL_IN) == 0) - failed ("return with infinite timeout"); - - getchar (); - if (do_select_nowait (0, SEL_IN) != 0) - failed ("can read after getc"); -} -#endif - - -/* Test select(2) for unconnected nonblocking sockets. */ - -static void -test_connect_first (void) -{ - int s = open_server_socket (); - struct sockaddr_in ia; - socklen_t addrlen; - - int c1, c2; - - if (do_select_nowait (s, SEL_IN | SEL_EXC) != 0) - failed ("can read, socket not connected"); - - c1 = connect_to_socket (false); - - if (do_select_wait (s, SEL_IN | SEL_EXC) != SEL_IN) - failed ("expecting readability on passive socket"); - if (do_select_nowait (s, SEL_IN | SEL_EXC) != SEL_IN) - failed ("expecting readability on passive socket"); - - addrlen = sizeof (ia); - c2 = accept (s, (struct sockaddr *) &ia, &addrlen); - ASSERT (close (s) == 0); - ASSERT (close (c1) == 0); - ASSERT (close (c2) == 0); -} - - -/* Test select(2) for unconnected blocking sockets. */ - -static void -test_accept_first (void) -{ -#ifndef WIN32_NATIVE - int s = open_server_socket (); - struct sockaddr_in ia; - socklen_t addrlen; - char buf[3]; - int c, pid; - - pid = fork (); - if (pid < 0) - return; - - if (pid == 0) - { - addrlen = sizeof (ia); - c = accept (s, (struct sockaddr *) &ia, &addrlen); - ASSERT (close (s) == 0); - ASSERT (write (c, "foo", 3) == 3); - ASSERT (read (c, buf, 3) == 3); - shutdown (c, SHUT_RD); - ASSERT (close (c) == 0); - exit (0); - } - else - { - ASSERT (close (s) == 0); - c = connect_to_socket (true); - if (do_select_nowait (c, SEL_OUT) != SEL_OUT) - failed ("cannot write after blocking connect"); - ASSERT (write (c, "foo", 3) == 3); - wait (&pid); - if (do_select_wait (c, SEL_IN) != SEL_IN) - failed ("cannot read data left in the socket by closed process"); - ASSERT (read (c, buf, 3) == 3); - ASSERT (write (c, "foo", 3) == 3); - (void) close (c); /* may fail with errno = ECONNRESET */ - } -#endif -} - - -/* Common code for pipes and connected sockets. */ - -static void -test_pair (int rd, int wd) -{ - char buf[3]; - if (do_select_wait (wd, SEL_IN | SEL_OUT | SEL_EXC) != SEL_OUT) - failed ("expecting writability before writing"); - if (do_select_nowait (wd, SEL_IN | SEL_OUT | SEL_EXC) != SEL_OUT) - failed ("expecting writability before writing"); - - ASSERT (write (wd, "foo", 3) == 3); - if (do_select_wait (rd, SEL_IN) != SEL_IN) - failed ("expecting readability after writing"); - if (do_select_nowait (rd, SEL_IN) != SEL_IN) - failed ("expecting readability after writing"); - - ASSERT (read (rd, buf, 3) == 3); -} - - -/* Test select(2) on connected sockets. */ - -static void -test_socket_pair (void) -{ - struct sockaddr_in ia; - - socklen_t addrlen = sizeof (ia); - int s = open_server_socket (); - int c1 = connect_to_socket (false); - int c2 = accept (s, (struct sockaddr *) &ia, &addrlen); - - ASSERT (close (s) == 0); - - test_pair (c1, c2); - ASSERT (close (c1) == 0); - ASSERT (write (c2, "foo", 3) == 3); - (void) close (c2); /* may fail with errno = ECONNRESET */ -} - - -/* Test select(2) on pipes. */ - -static void -test_pipe (void) -{ - int fd[2]; - - ASSERT (pipe (fd) == 0); - test_pair (fd[0], fd[1]); - ASSERT (close (fd[0]) == 0); - ASSERT (close (fd[1]) == 0); -} - - -/* Do them all. */ +#include "test-select.h" int main (void) { - int result; - -#ifdef INTERACTIVE - printf ("Please press Enter\n"); - test (test_tty, "TTY"); -#endif - - result = test (test_connect_first, "Unconnected socket test"); - result += test (test_socket_pair, "Connected sockets test"); - result += test (test_accept_first, "General socket test with fork"); - result += test (test_pipe, "Pipe test"); - - exit (result); + return test_function (select); } -- In memoriam Dmitry Pavlov <http://en.wikipedia.org/wiki/Dmitry_Pavlov_(general)>