From bce9d2f253449c62e05c030307e4d298ef914df9 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 17 Apr 2011 19:50:10 +0200 Subject: [PATCH] nonblocking: Add tests for sockets. * tests/test-nonblocking-socket.sh: New file. * tests/test-nonblocking-socket-main.c: New file. * tests/test-nonblocking-socket-child.c: New file. * tests/test-nonblocking-socket.h: New file. * tests/socket-server.h: New file. * tests/socket-client.h: New file. * modules/nonblocking-socket-tests: New file. * modules/nonblocking-tests (Depends-on): Add nonblocking-socket-tests. --- ChangeLog | 12 ++++ modules/nonblocking-socket-tests | 46 +++++++++++++ modules/nonblocking-tests | 1 + tests/socket-client.h | 57 ++++++++++++++++ tests/socket-server.h | 117 ++++++++++++++++++++++++++++++++ tests/test-nonblocking-socket-child.c | 52 +++++++++++++++ tests/test-nonblocking-socket-main.c | 121 ++++++++++++++++++++++++++++++++++ tests/test-nonblocking-socket.h | 51 ++++++++++++++ tests/test-nonblocking-socket.sh | 13 ++++ 9 files changed, 470 insertions(+) create mode 100644 modules/nonblocking-socket-tests create mode 100644 tests/socket-client.h create mode 100644 tests/socket-server.h create mode 100644 tests/test-nonblocking-socket-child.c create mode 100644 tests/test-nonblocking-socket-main.c create mode 100644 tests/test-nonblocking-socket.h create mode 100755 tests/test-nonblocking-socket.sh diff --git a/ChangeLog b/ChangeLog index d86f4c9d2..d0923da0e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ 2011-04-17 Bruno Haible + nonblocking: Add tests for sockets. + * tests/test-nonblocking-socket.sh: New file. + * tests/test-nonblocking-socket-main.c: New file. + * tests/test-nonblocking-socket-child.c: New file. + * tests/test-nonblocking-socket.h: New file. + * tests/socket-server.h: New file. + * tests/socket-client.h: New file. + * modules/nonblocking-socket-tests: New file. + * modules/nonblocking-tests (Depends-on): Add nonblocking-socket-tests. + +2011-04-17 Bruno Haible + nonblocking: Add tests for pipes. * tests/test-nonblocking-pipe.sh: New file. * tests/test-nonblocking-pipe-main.c: New file. diff --git a/modules/nonblocking-socket-tests b/modules/nonblocking-socket-tests new file mode 100644 index 000000000..368bed882 --- /dev/null +++ b/modules/nonblocking-socket-tests @@ -0,0 +1,46 @@ +Files: +tests/test-nonblocking-socket.sh +tests/test-nonblocking-socket-main.c +tests/test-nonblocking-socket-child.c +tests/test-nonblocking-socket.h +tests/test-nonblocking-writer.h +tests/test-nonblocking-reader.h +tests/test-nonblocking-misc.h +tests/socket-server.h +tests/socket-client.h +tests/macros.h + +Depends-on: +stdbool +unistd +sys_socket +nonblocking +wait-process +environ +posix_spawnp +netinet_in +arpa_inet +socket +setsockopt +bind +getsockname +listen +accept +getsockopt +connect +gettimeofday +snprintf +vsnprintf +strerror +ssize_t +usleep +read +write + +configure.ac: + +Makefile.am: +TESTS += test-nonblocking-socket.sh +check_PROGRAMS += test-nonblocking-socket-main test-nonblocking-socket-child +test_nonblocking_socket_main_LDADD = $(LDADD) $(LIBSOCKET) +test_nonblocking_socket_child_LDADD = $(LDADD) $(LIBSOCKET) diff --git a/modules/nonblocking-tests b/modules/nonblocking-tests index bd6cc134b..b84a32713 100644 --- a/modules/nonblocking-tests +++ b/modules/nonblocking-tests @@ -6,6 +6,7 @@ Depends-on: close pipe-posix nonblocking-pipe-tests +nonblocking-socket-tests configure.ac: diff --git a/tests/socket-client.h b/tests/socket-client.h new file mode 100644 index 000000000..ac82463ce --- /dev/null +++ b/tests/socket-client.h @@ -0,0 +1,57 @@ +/* Create sockets for use in tests (client side). + 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 . */ + +/* Written by Bruno Haible , 2011. */ + +#include +#include +#include +#include + +/* Creates a client socket, by connecting to a server on the given port. */ +static int +create_client_socket (int port) +{ + int client_socket; + + /* Create a client socket. */ + client_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + ASSERT (client_socket >= 0); + /* Connect to the server process at the specified port. */ + { + struct sockaddr_in addr; + + memset (&addr, 0, sizeof (addr)); /* needed on AIX and OSF/1 */ + addr.sin_family = AF_INET; + #if 0 /* Unoptimized */ + inet_pton (AF_INET, "127.0.0.1", &addr.sin_addr); + #elif 0 /* Nearly optimized */ + addr.sin_addr.s_addr = htonl (0x7F000001); /* 127.0.0.1 */ + #else /* Fully optimized */ + { + unsigned char dotted[4] = { 127, 0, 0, 1 }; /* 127.0.0.1 */ + memcpy (&addr.sin_addr.s_addr, dotted, 4); + } + #endif + addr.sin_port = htons (port); + + ASSERT (connect (client_socket, + (const struct sockaddr *) &addr, sizeof (addr)) + == 0); + } + + return client_socket; +} diff --git a/tests/socket-server.h b/tests/socket-server.h new file mode 100644 index 000000000..283ef2fc2 --- /dev/null +++ b/tests/socket-server.h @@ -0,0 +1,117 @@ +/* Create sockets for use in tests (server side). + 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 . */ + +/* Written by Bruno Haible , 2011. */ + +#include +#include +#include +#include + +/* Creates a server that can be used to listen on incoming + connections. It uses the IPv4 protocol. + If PORT is 0, a port is assigned by the kernel. + Returns the server. Returns the chosen port in *PPORT. */ +static int +create_server (int port, unsigned int max_backlog, int *pport) +{ + int server; + + /* Create a server socket. */ + server = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (server < 0) + { + fputs ("Skipping test: cannot create server socket: socket() failed\n", + stderr); + exit (77); + } + /* Bind it to a local IPv4 address. */ + if (port != 0) + { + /* Set an option for the next bind() call: Avoid an EADDRINUSE error + in case there are TIME_WAIT or CLOSE_WAIT sockets hanging around on + the port. (Sockets in LISTEN or ESTABLISHED state on the same port + will still yield an error.) */ + unsigned int flag = 1; + if (setsockopt (server, SOL_SOCKET, SO_REUSEADDR, &flag, + sizeof (flag)) + < 0) + { + fputs ("Skipping test: cannot create server socket: setsockopt() failed\n", + stderr); + exit (77); + } + } + { + struct sockaddr_in addr; + + memset (&addr, 0, sizeof (addr)); /* needed on AIX and OSF/1 */ + addr.sin_family = AF_INET; + #if 0 /* Unoptimized */ + inet_pton (AF_INET, "127.0.0.1", &addr.sin_addr); + #elif 0 /* Nearly optimized */ + addr.sin_addr.s_addr = htonl (0x7F000001); /* 127.0.0.1 */ + #else /* Fully optimized */ + { + unsigned char dotted[4] = { 127, 0, 0, 1 }; /* 127.0.0.1 */ + memcpy (&addr.sin_addr.s_addr, dotted, 4); + } + #endif + addr.sin_port = htons (port); + + if (bind (server, (const struct sockaddr *) &addr, sizeof (addr)) < 0) + { + fputs ("Skipping test: cannot create server socket: bind() failed\n", + stderr); + exit (77); + } + } + if (port == 0) + { + /* Get the port that was assigned by bind(). */ + struct sockaddr_in addr; + socklen_t addrlen = sizeof (addr); + + if (getsockname (server, (struct sockaddr *) &addr, &addrlen) < 0) + { + fputs ("Skipping test: cannot create server socket: getsockname() failed\n", + stderr); + exit (77); + } + port = ntohs (addr.sin_port); + } + /* Start listening for a connection from the child process. */ + if (listen (server, max_backlog) < 0) + { + fputs ("Skipping test: cannot create server socket: listen() failed\n", + stderr); + exit (77); + } + + *pport = port; + return server; +} + +/* Creates a server socket, by accepting a connection to a server. */ +static int +create_server_socket (int server) +{ + struct sockaddr_storage addr; + socklen_t addrlen = sizeof (addr); + int connected_socket = accept (server, (struct sockaddr *) &addr, &addrlen); + ASSERT (connected_socket >= 0); + return connected_socket; +} diff --git a/tests/test-nonblocking-socket-child.c b/tests/test-nonblocking-socket-child.c new file mode 100644 index 000000000..17545cf64 --- /dev/null +++ b/tests/test-nonblocking-socket-child.c @@ -0,0 +1,52 @@ +/* Child program invoked by test-nonblocking-socket-main. + + 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 . */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "nonblocking.h" + +#include "macros.h" +#include "socket-client.h" +#include "test-nonblocking-socket.h" +#define PROG_ROLE "child" +#include "test-nonblocking-reader.h" + +int +main (int argc, char *argv[]) +{ + int test = atoi (argv[1]); + int port = atoi (argv[2]); + int client_socket; + + /* Create a client socket. */ + client_socket = create_client_socket (port); + + /* Prepare the file descriptor. */ + if (test & 2) + ASSERT (set_nonblocking_flag (client_socket, true) >= 0); + + main_reader_loop (test, SOCKET_DATA_BLOCK_SIZE, client_socket); + + return 0; +} diff --git a/tests/test-nonblocking-socket-main.c b/tests/test-nonblocking-socket-main.c new file mode 100644 index 000000000..034873d5d --- /dev/null +++ b/tests/test-nonblocking-socket-main.c @@ -0,0 +1,121 @@ +/* Test for nonblocking read and write on sockets. + + 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 . */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# include +#else +# include +#endif + +#include "nonblocking.h" +#include "wait-process.h" + +#include "macros.h" +#include "socket-server.h" +#include "test-nonblocking-socket.h" +#define PROG_ROLE "main" +#include "test-nonblocking-writer.h" + +int +main (int argc, char *argv[]) +{ + const char *child_path = argv[1]; + int test = atoi (argv[2]); + int server; + int port; + int child; + int server_socket; + int exitcode; + + /* Create a server socket. */ + server = create_server (0, 1, &port); + + /* Spawn the child process. */ + { + char port_arg[10+1]; + const char *child_argv[4]; + + sprintf (port_arg, "%u", port); + child_argv[0] = child_path; + child_argv[1] = argv[2]; + child_argv[2] = port_arg; + child_argv[3] = NULL; + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + child = spawnvpe (P_NOWAIT, child_path, child_argv, + (const char **) environ); + ASSERT (child >= 0); +#else + { + pid_t child_pid; + int err = + posix_spawnp (&child_pid, child_path, NULL, NULL, (char **) child_argv, + environ); + ASSERT (err == 0); + child = child_pid; + } +#endif + } + + /* Accept a connection from the child process. */ + server_socket = create_server_socket (server); + + /* Prepare the file descriptor. */ + if (test & 1) + ASSERT (set_nonblocking_flag (server_socket, true) >= 0); + +#if ENABLE_DEBUGGING +# ifdef SO_SNDBUF + { + int value; + socklen_t value_len = sizeof (value); + if (getsockopt (server_socket, SOL_SOCKET, SO_SNDBUF, &value, &value_len) >= 0) + fprintf (stderr, "SO_SNDBUF = %d\n", value); + } +# endif +# ifdef SO_RCVBUF + { + int value; + socklen_t value_len = sizeof (value); + if (getsockopt (server_socket, SOL_SOCKET, SO_RCVBUF, &value, &value_len) >= 0) + fprintf (stderr, "SO_RCVBUF = %d\n", value); + } +# endif +#endif + + exitcode = + main_writer_loop (test, SOCKET_DATA_BLOCK_SIZE, server_socket, + SOCKET_HAS_LARGE_BUFFER); + + { + int err = + wait_subprocess (child, child_path, false, false, false, false, NULL); + ASSERT (err == 0); + } + + return exitcode; +} diff --git a/tests/test-nonblocking-socket.h b/tests/test-nonblocking-socket.h new file mode 100644 index 000000000..5f2268d9c --- /dev/null +++ b/tests/test-nonblocking-socket.h @@ -0,0 +1,51 @@ +/* Test for nonblocking read and write. + + 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 . */ + +/* A data block ought to be larger than the size of the in-kernel buffer. + Working values of SOCKET_DATA_BLOCK_SIZE, depending on kernel: + + Platform SOCKET_DATA_BLOCK_SIZE + + Linux >= 7350000 (depends on circumstances) + FreeBSD >= 107521 + OpenBSD >= 28673 + MacOS X >= 680000 (depends on circumstances) + AIX 5.1 >= 125713 + AIX 7.1 >= 200000 (depends on circumstances) + HP-UX >= 114689 + IRIX >= 61089 + OSF/1 >= 122881 + Solaris 7 >= 63000 (depends on circumstances) + Solaris 8 >= 49153 + Solaris 9 >= 73729 + Solaris 10 >= 98305 + Solaris 11 2010-11 >= 73729 + Cygwin 1.5.x >= 66294401 but then write() fails with ENOBUFS + Cygwin 1.7.x >= 163838 (depends on circumstances) + native Win32 >= 66294401 + */ +#define SOCKET_DATA_BLOCK_SIZE 1000000 + +/* On Linux, MacOS X, Cygwin 1.5.x, native Win32, + sockets have very large buffers in the kernel, so that write() calls + succeed before the reader has started reading, even if fd is blocking + and the amount of data is larger than 1 MB. */ +#if defined __linux__ || (defined __APPLE__ && defined __MACH__) || (defined _WIN32 || defined __WIN32__) || defined __CYGWIN__ +# define SOCKET_HAS_LARGE_BUFFER 1 +#else +# define SOCKET_HAS_LARGE_BUFFER 0 +#endif diff --git a/tests/test-nonblocking-socket.sh b/tests/test-nonblocking-socket.sh new file mode 100755 index 000000000..3818c93d3 --- /dev/null +++ b/tests/test-nonblocking-socket.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# Test blocking write() with blocking read(). +./test-nonblocking-socket-main${EXEEXT} ./test-nonblocking-socket-child${EXEEXT} 0 || exit 1 + +# Test non-blocking write() with blocking read(). +./test-nonblocking-socket-main${EXEEXT} ./test-nonblocking-socket-child${EXEEXT} 1 || exit 1 + +# Test blocking write() with non-blocking read(). +./test-nonblocking-socket-main${EXEEXT} ./test-nonblocking-socket-child${EXEEXT} 2 || exit 1 + +# Test non-blocking write() with non-blocking read(). +./test-nonblocking-socket-main${EXEEXT} ./test-nonblocking-socket-child${EXEEXT} 3 || exit 1 -- 2.11.0