diff options
Diffstat (limited to 'tests')
31 files changed, 1772 insertions, 118 deletions
diff --git a/tests/socket-client.h b/tests/socket-client.h new file mode 100644 index 0000000000..ac82463ce1 --- /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 <http://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2011. */ + +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +/* 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 0000000000..283ef2fc2a --- /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 <http://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2011. */ + +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +/* 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-chown.h b/tests/test-chown.h index 7630de48da..85cbb5afc9 100644 --- a/tests/test-chown.h +++ b/tests/test-chown.h @@ -38,7 +38,7 @@ test_chown (int (*func) (char const *, uid_t, gid_t), bool print) int result; /* Solaris 8 is interesting - if the current process belongs to - multiple groups, the current directory is owned by a a group that + multiple groups, the current directory is owned by a group that the current process belongs to but different than getegid(), and the current directory does not have the S_ISGID bit, then regular files created in the directory belong to the directory's group, @@ -71,7 +71,7 @@ test_chown (int (*func) (char const *, uid_t, gid_t), bool print) ASSERT (close (creat (BASE "dir/file", 0600)) == 0); ASSERT (stat (BASE "dir/file", &st1) == 0); ASSERT (st1.st_uid != (uid_t) -1); - ASSERT (st1.st_gid != (uid_t) -1); + ASSERT (st1.st_gid != (gid_t) -1); ASSERT (st1.st_gid == getegid ()); /* Sanity check of error cases. */ diff --git a/tests/test-fclose.c b/tests/test-fclose.c new file mode 100644 index 0000000000..d9b940619b --- /dev/null +++ b/tests/test-fclose.c @@ -0,0 +1,93 @@ +/* Test of fclose module. + 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, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Eric Blake. */ + +#include <config.h> + +#include <stdio.h> + +#include "signature.h" +SIGNATURE_CHECK (fclose, int, (FILE *)); + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "macros.h" + +#define BASE "test-fclose.t" + +int +main (int argc, char **argv) +{ + const char buf[] = "hello world"; + int fd; + int fd2; + FILE *f; + + /* Prepare a seekable file. */ + fd = open (BASE, O_RDWR | O_CREAT | O_TRUNC, 0600); + ASSERT (0 <= fd); + ASSERT (write (fd, buf, sizeof buf) == sizeof buf); + ASSERT (lseek (fd, 1, SEEK_SET) == 1); + + /* Create an output stream visiting the file; when it is closed, all + other file descriptors visiting the file must see the new file + position. */ + fd2 = dup (fd); + ASSERT (0 <= fd2); + f = fdopen (fd2, "w"); + ASSERT (f); + ASSERT (fputc (buf[1], f) == buf[1]); + ASSERT (fclose (f) == 0); + errno = 0; + ASSERT (lseek (fd2, 0, SEEK_CUR) == -1); + ASSERT (errno == EBADF); + ASSERT (lseek (fd, 0, SEEK_CUR) == 2); + +#if GNULIB_FFLUSH + /* Likewise for an input stream, but only when we know fflush works + on input streams. */ + fd2 = dup (fd); + ASSERT (0 <= fd2); + f = fdopen (fd2, "r"); + ASSERT (f); + ASSERT (fgetc (f) == buf[2]); + ASSERT (fclose (f) == 0); + errno = 0; + ASSERT (lseek (fd2, 0, SEEK_CUR) == -1); + ASSERT (errno == EBADF); + ASSERT (lseek (fd, 0, SEEK_CUR) == 3); +#endif + + /* Test that fclose() sets errno if someone else closes the stream + fd behind the back of stdio. */ + f = fdopen (fd, "w+"); + ASSERT (f); + ASSERT (close (fd) == 0); + errno = 0; + ASSERT (fclose (f) == EOF); + ASSERT (errno == EBADF); + + /* Clean up. */ + ASSERT (remove (BASE) == 0); + + return 0; +} diff --git a/tests/test-fcntl-h.c b/tests/test-fcntl-h.c index dd20fbb406..648701ef4b 100644 --- a/tests/test-fcntl-h.c +++ b/tests/test-fcntl-h.c @@ -29,10 +29,88 @@ int o = O_DIRECT | O_DIRECTORY | O_DSYNC | O_NDELAY | O_NOATIME | O_NONBLOCK int sk[] = { SEEK_CUR, SEEK_END, SEEK_SET }; /* Check that the FD_* macros are defined. */ -int fd = FD_CLOEXEC; +int i = FD_CLOEXEC; int main (void) { - return 0; + /* Ensure no overlap in SEEK_*. */ + switch (0) + { + case SEEK_CUR: + case SEEK_END: + case SEEK_SET: + ; + } + + /* Ensure no dangerous overlap in non-zero gnulib-defined replacements. */ + switch (O_RDONLY) + { + /* Access modes */ + case O_RDONLY: + case O_WRONLY: + case O_RDWR: +#if O_EXEC && O_EXEC != O_RDONLY + case O_EXEC: +#endif +#if O_SEARCH && O_EXEC != O_SEARCH && O_SEARCH != O_RDONLY + case O_SEARCH: +#endif + i = O_ACCMODE == (O_RDONLY | O_WRONLY | O_RDWR | O_EXEC | O_SEARCH); + break; + + /* Everyone should have these */ + case O_CREAT: + case O_EXCL: + case O_TRUNC: + case O_APPEND: + break; + + /* These might be 0 or O_RDONLY, only test non-zero versions. */ +#if O_CLOEXEC + case O_CLOEXEC: +#endif +#if O_DIRECT + case O_DIRECT: +#endif +#if O_DIRECTORY + case O_DIRECTORY: +#endif +#if O_DSYNC + case O_DSYNC: +#endif +#if O_NOATIME + case O_NOATIME: +#endif +#if O_NONBLOCK + case O_NONBLOCK: +#endif +#if O_NOCTTY + case O_NOCTTY: +#endif +#if O_NOFOLLOW + case O_NOFOLLOW: +#endif +#if O_NOLINKS + case O_NOLINKS: +#endif +#if O_RSYNC && O_RSYNC != O_DSYNC + case O_RSYNC: +#endif +#if O_SYNC && O_SYNC != O_RSYNC + case O_SYNC: +#endif +#if O_TTY_INIT + case O_TTY_INIT: +#endif +#if O_BINARY + case O_BINARY: +#endif +#if O_TEXT + case O_TEXT: +#endif + ; + } + + return !i; } diff --git a/tests/test-getaddrinfo.c b/tests/test-getaddrinfo.c index 482698937a..d2984e226b 100644 --- a/tests/test-getaddrinfo.c +++ b/tests/test-getaddrinfo.c @@ -107,7 +107,7 @@ simple (char const *host, char const *service) #endif /* Provide details if errno was set. */ if (res == EAI_SYSTEM) - dbgprintf ("system error: %s\n", strerror (err)); + fprintf (stderr, "system error: %s\n", strerror (err)); return 1; } diff --git a/tests/test-getcwd-lgpl.c b/tests/test-getcwd-lgpl.c new file mode 100644 index 0000000000..67ee66d539 --- /dev/null +++ b/tests/test-getcwd-lgpl.c @@ -0,0 +1,87 @@ +/* Test of getcwd() function. + Copyright (C) 2009-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 <unistd.h> + +#include "signature.h" +SIGNATURE_CHECK (getcwd, char *, (char *, size_t)); + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "macros.h" + +int +main (int argc, char **argv) +{ + char *pwd1; + char *pwd2; + /* If the user provides an argument, attempt to chdir there first. */ + if (1 < argc) + { + if (chdir (argv[1]) == 0) + printf ("changed to directory %s\n", argv[1]); + } + + pwd1 = getcwd (NULL, 0); + ASSERT (pwd1 && *pwd1); + if (1 < argc) + printf ("cwd=%s\n", pwd1); + + /* Make sure the result is usable. */ + ASSERT (chdir (pwd1) == 0); + ASSERT (chdir (".//./.") == 0); + + /* Make sure that result is normalized. */ + pwd2 = getcwd (NULL, 0); + ASSERT (pwd2); + ASSERT (strcmp (pwd1, pwd2) == 0); + free (pwd2); + { + size_t len = strlen (pwd1); + ssize_t i = len - 10; + if (i < 1) + i = 1; + pwd2 = getcwd (NULL, len + 1); + ASSERT (pwd2); + free (pwd2); + pwd2 = malloc (len + 2); + for ( ; i <= len; i++) + { + errno = 0; + ASSERT (getcwd (pwd2, i) == NULL); + ASSERT (errno == ERANGE); + errno = 0; + ASSERT (getcwd (NULL, i) == NULL); + ASSERT (errno == ERANGE); + } + ASSERT (getcwd (pwd2, len + 1) == pwd2); + pwd2[len] = '/'; + pwd2[len + 1] = '\0'; + } + ASSERT (strstr (pwd2, "/./") == NULL); + ASSERT (strstr (pwd2, "/../") == NULL); + ASSERT (strstr (pwd2 + 1 + (pwd2[1] == '/'), "//") == NULL); + + free (pwd1); + free (pwd2); + + return 0; +} diff --git a/tests/test-getcwd.c b/tests/test-getcwd.c index 18fc74f94b..14a526f65e 100644 --- a/tests/test-getcwd.c +++ b/tests/test-getcwd.c @@ -18,59 +18,206 @@ #include <unistd.h> -#include "signature.h" -SIGNATURE_CHECK (getcwd, char *, (char *, size_t)); - +#include <errno.h> +#include <fcntl.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include "macros.h" -int -main (int argc, char **argv) +#if ! HAVE_GETPAGESIZE +# define getpagesize() 0 +#endif + +/* This size is chosen to be larger than PATH_MAX (4k), yet smaller than + the 16kB pagesize on ia64 linux. Those conditions make the code below + trigger a bug in glibc's getcwd implementation before 2.4.90-10. */ +#define TARGET_LEN (5 * 1024) + +/* Keep this test in sync with m4/getcwd-abort-bug.m4. */ +static int +test_abort_bug (void) { - char *pwd1; - char *pwd2; - /* If the user provides an argument, attempt to chdir there first. */ - if (1 < argc) + char const *dir_name = "confdir-14B---"; + char *cwd; + size_t initial_cwd_len; + int fail = 0; + size_t desired_depth; + size_t d; + + /* The bug is triggered when PATH_MAX < getpagesize (), so skip + this relatively expensive and invasive test if that's not true. */ + if (getpagesize () <= PATH_MAX) + return 0; + + cwd = getcwd (NULL, 0); + if (cwd == NULL) + return 2; + + initial_cwd_len = strlen (cwd); + free (cwd); + desired_depth = ((TARGET_LEN - 1 - initial_cwd_len) + / (1 + strlen (dir_name))); + for (d = 0; d < desired_depth; d++) { - if (chdir (argv[1]) == 0) - printf ("changed to directory %s\n", argv[1]); + if (mkdir (dir_name, S_IRWXU) < 0 || chdir (dir_name) < 0) + { + fail = 3; /* Unable to construct deep hierarchy. */ + break; + } } - pwd1 = getcwd (NULL, 0); - ASSERT (pwd1 && *pwd1); - if (1 < argc) - printf ("cwd=%s\n", pwd1); + /* If libc has the bug in question, this invocation of getcwd + results in a failed assertion. */ + cwd = getcwd (NULL, 0); + if (cwd == NULL) + fail = 4; /* getcwd failed. This is ok, and expected. */ + free (cwd); + + /* Call rmdir first, in case the above chdir failed. */ + rmdir (dir_name); + while (0 < d--) + { + if (chdir ("..") < 0) + { + fail = 5; + break; + } + rmdir (dir_name); + } + + return fail; +} + +/* The length of this name must be 8. */ +#define DIR_NAME "confdir3" +#define DIR_NAME_LEN 8 +#define DIR_NAME_SIZE (DIR_NAME_LEN + 1) + +/* The length of "../". */ +#define DOTDOTSLASH_LEN 3 + +/* Leftover bytes in the buffer, to work around library or OS bugs. */ +#define BUF_SLOP 20 + +/* Keep this test in sync with m4/getcwd-path-max.m4. */ +static int +test_long_name (void) +{ +#ifndef PATH_MAX + /* The Hurd doesn't define this, so getcwd can't exhibit the bug -- + at least not on a local file system. And if we were to start worrying + about remote file systems, we'd have to enable the wrapper function + all of the time, just to be safe. That's not worth the cost. */ + return 0; +#elif ((INT_MAX / (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) \ + - DIR_NAME_SIZE - BUF_SLOP) \ + <= PATH_MAX) + /* FIXME: Assuming there's a system for which this is true, + this should be done in a compile test. */ + return 0; +#else + char buf[PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN + 1) + + DIR_NAME_SIZE + BUF_SLOP]; + char *cwd = getcwd (buf, PATH_MAX); + size_t initial_cwd_len; + size_t cwd_len; + int fail = 0; + size_t n_chdirs = 0; + + if (cwd == NULL) + return 10; + + cwd_len = initial_cwd_len = strlen (cwd); + + while (1) + { + size_t dotdot_max = PATH_MAX * (DIR_NAME_SIZE / DOTDOTSLASH_LEN); + char *c = NULL; + + cwd_len += DIR_NAME_SIZE; + /* If mkdir or chdir fails, it could be that this system cannot create + any file with an absolute name longer than PATH_MAX, such as cygwin. + If so, leave fail as 0, because the current working directory can't + be too long for getcwd if it can't even be created. For other + errors, be pessimistic and consider that as a failure, too. */ + if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0) + { + if (! (errno == ERANGE || errno == ENAMETOOLONG)) + fail = 20; + break; + } + + if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE) + { + c = getcwd (buf, PATH_MAX); + if (!c && errno == ENOENT) + { + fail = 11; + break; + } + if (c || ! (errno == ERANGE || errno == ENAMETOOLONG)) + { + fail = 21; + break; + } + } - /* Make sure the result is usable. */ - ASSERT (chdir (pwd1) == 0); - ASSERT (chdir ("././.") == 0); + if (dotdot_max <= cwd_len - initial_cwd_len) + { + if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len) + break; + c = getcwd (buf, cwd_len + 1); + if (!c) + { + if (! (errno == ERANGE || errno == ENOENT + || errno == ENAMETOOLONG)) + { + fail = 22; + break; + } + if (AT_FDCWD || errno == ERANGE || errno == ENOENT) + { + fail = 12; + break; + } + } + } - /* Make sure that result is normalized. */ - pwd2 = getcwd (NULL, 0); - ASSERT (pwd2); - ASSERT (strcmp (pwd1, pwd2) == 0); - free (pwd2); + if (c && strlen (c) != cwd_len) + { + fail = 23; + break; + } + ++n_chdirs; + } + + /* Leaving behind such a deep directory is not polite. + So clean up here, right away, even though the driving + shell script would also clean up. */ { - size_t len = strlen (pwd1); - ssize_t i = len - 10; - if (i < 0) - i = 0; - pwd2 = malloc (len + 2); - for ( ; i < len; i++) - ASSERT (getcwd (pwd2, i) == NULL); - pwd2 = getcwd (pwd2, len + 1); - ASSERT (pwd2); - pwd2[len] = '/'; - pwd2[len + 1] = '\0'; + size_t i; + + /* Try rmdir first, in case the chdir failed. */ + rmdir (DIR_NAME); + for (i = 0; i <= n_chdirs; i++) + { + if (chdir ("..") < 0) + break; + if (rmdir (DIR_NAME) != 0) + break; + } } - ASSERT (strstr (pwd2, "/./") == NULL); - ASSERT (strstr (pwd2, "/../") == NULL); - free (pwd1); - free (pwd2); + return fail; +#endif +} - return 0; +int +main (int argc, char **argv) +{ + return test_abort_bug () + test_long_name (); } diff --git a/tests/test-hash.c b/tests/test-hash.c index 108daefdf4..4a8a3bef37 100644 --- a/tests/test-hash.c +++ b/tests/test-hash.c @@ -20,7 +20,6 @@ #include "hash.h" #include "hash-pjw.h" #include "inttostr.h" -#include "xalloc.h" #include <stdio.h> #include <stdlib.h> @@ -114,8 +113,10 @@ main (int argc, char **argv) ASSERT (ht); insert_new (ht, "a"); { - char *str1 = xstrdup ("a"); - char *str2 = hash_insert (ht, str1); + char *str1 = strdup ("a"); + char *str2; + ASSERT (str1); + str2 = hash_insert (ht, str1); ASSERT (str1 != str2); ASSERT (STREQ (str1, str2)); free (str1); @@ -161,7 +162,8 @@ main (int argc, char **argv) ht = hash_initialize (sz, NULL, NULL, NULL, NULL); ASSERT (ht); { - char *str = xstrdup ("a"); + char *str = strdup ("a"); + ASSERT (str); insert_new (ht, "a"); insert_new (ht, str); ASSERT (hash_lookup (ht, str) == str); @@ -206,7 +208,9 @@ main (int argc, char **argv) { char buf[50]; char const *p = uinttostr (i, buf); - insert_new (ht, xstrdup (p)); + char *p_dup = strdup (p); + ASSERT (p_dup); + insert_new (ht, p_dup); } break; diff --git a/tests/test-lchown.h b/tests/test-lchown.h index aa10674187..229ed6b01b 100644 --- a/tests/test-lchown.h +++ b/tests/test-lchown.h @@ -46,7 +46,7 @@ test_lchown (int (*func) (char const *, uid_t, gid_t), bool print) int result; /* Solaris 8 is interesting - if the current process belongs to - multiple groups, the current directory is owned by a a group that + multiple groups, the current directory is owned by a group that the current process belongs to but different than getegid(), and the current directory does not have the S_ISGID bit, then regular files created in the directory belong to the directory's group, diff --git a/tests/test-linkat.c b/tests/test-linkat.c index 8d179e2faf..2bf1790524 100644 --- a/tests/test-linkat.c +++ b/tests/test-linkat.c @@ -34,7 +34,6 @@ SIGNATURE_CHECK (linkat, int, (int, char const *, int, char const *, int)); #include "areadlink.h" #include "filenamecat.h" #include "same-inode.h" -#include "xgetcwd.h" #include "ignore-value.h" #include "macros.h" @@ -119,7 +118,8 @@ main (void) ASSERT (mkdir (BASE "sub1", 0700) == 0); ASSERT (mkdir (BASE "sub2", 0700) == 0); ASSERT (close (creat (BASE "00", 0600)) == 0); - cwd = xgetcwd (); + cwd = getcwd (NULL, 0); + ASSERT (cwd); dfd = open (BASE "sub1", O_RDONLY); ASSERT (0 <= dfd); @@ -140,9 +140,11 @@ main (void) for (i = 0; i < 32; i++) { int fd1 = (i & 8) ? dfd : AT_FDCWD; - char *file1 = file_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL); + char *file1 = mfile_name_concat ((i & 4) ? ".." : cwd, BASE "xx", NULL); int fd2 = (i & 2) ? dfd : AT_FDCWD; - char *file2 = file_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL); + char *file2 = mfile_name_concat ((i & 1) ? ".." : cwd, BASE "xx", NULL); + ASSERT (file1); + ASSERT (file2); flag = (i & 0x10 ? AT_SYMLINK_FOLLOW : 0); ASSERT (sprintf (strchr (file1, '\0') - 2, "%02d", i) == 2); diff --git a/tests/test-netdb-c++.cc b/tests/test-netdb-c++.cc new file mode 100644 index 0000000000..0dc47b9ae7 --- /dev/null +++ b/tests/test-netdb-c++.cc @@ -0,0 +1,42 @@ +/* Test of <netdb.h> substitute in C++ mode. + 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/>. */ + +/* Written by Bruno Haible <bruno@clisp.org>, 2011. */ + +#define GNULIB_NAMESPACE gnulib +#include <config.h> + +#include <netdb.h> + +#include "signature.h" + + +#if GNULIB_TEST_GETADDRINFO +SIGNATURE_CHECK (GNULIB_NAMESPACE::getaddrinfo, int, + (const char *, const char *, const struct addrinfo *, + struct addrinfo **)); +SIGNATURE_CHECK (GNULIB_NAMESPACE::freeaddrinfo, void, (struct addrinfo *)); +SIGNATURE_CHECK (GNULIB_NAMESPACE::gai_strerror, const char *, (int)); +SIGNATURE_CHECK (GNULIB_NAMESPACE::getnameinfo, int, + (const struct sockaddr *, socklen_t, char *, socklen_t, + char *, socklen_t, int)); +#endif + + +int +main () +{ +} diff --git a/tests/test-nonblocking-misc.h b/tests/test-nonblocking-misc.h new file mode 100644 index 0000000000..66a13e47e4 --- /dev/null +++ b/tests/test-nonblocking-misc.h @@ -0,0 +1,108 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +/* Whether to print debugging messages. */ +#define ENABLE_DEBUGGING 0 + +/* Delay (in microseconds) to sleep when write() or read() returned -1 with + errno = EAGAIN. */ +#define SMALL_DELAY 10000 + +/* Return a memory area, filled with the data to be transferred. */ +static unsigned char * +init_data (size_t data_block_size) +{ + unsigned char *data; + unsigned int i; + + data = (unsigned char *) malloc (2 * data_block_size); + ASSERT (data != NULL); + + for (i = 0; i < 2 * data_block_size; i++) + data[i] = (unsigned char) (i * i + (7 * i) % 61 + 4); + + return data; +} + +#if ENABLE_DEBUGGING +# include <stdarg.h> +static int dbgfprintf (FILE *fp, const char *format, ...) + _GL_ATTRIBUTE_FORMAT_PRINTF (2, 3); +static int +dbgfprintf (FILE *fp, const char *format, ...) +{ + /* Accumulate the entire line in a buffer, so that the output on fp + is done atomically. */ + char line[1024]; + size_t line_len; + struct timeval current_time; + va_list args; + int ret; + + line_len = 0; + gettimeofday (¤t_time, NULL); + ret = snprintf (line, sizeof (line), "%.6f ", + current_time.tv_sec + (double) current_time.tv_usec * 1e-6); + if (ret < 0) + return -1; + line_len = strlen (line); + + va_start (args, format); + ret = vsnprintf (line + line_len, sizeof (line) - line_len, format, args); + va_end (args); + if (ret < 0) + return -1; + line_len += strlen (line + line_len); + + ret = fwrite (line, 1, line_len, fp); + + /* Make sure the debugging information is output, so that the order of the + messages reflects the timeline of events, and so that the output is not + lost if the program crashes afterwards (relevant on mingw). */ + fflush (fp); + return ret; +} +#else +# define dbgfprintf if (1) ; else fprintf +#endif + +/* Return a textual description of the error code ERR, if FAILED is true. + Return an empty string if FAILED is false. */ +static const char * +dbgstrerror (bool failed, int err) +{ + static char buf[256]; + if (failed) + { + sprintf (buf, " %d %s", err, strerror (err)); + return buf; + } + else + return ""; +} + +#define TIMING_DECLS \ + struct timeval before_time; \ + struct timeval after_time; \ + double spent_time; +#define START_TIMING \ + gettimeofday (&before_time, NULL); +#define END_TIMING \ + gettimeofday (&after_time, NULL); \ + spent_time = \ + (after_time.tv_sec - before_time.tv_sec) \ + + ((double) after_time.tv_usec - (double) before_time.tv_usec) * 1e-6; diff --git a/tests/test-nonblocking-pipe-child.c b/tests/test-nonblocking-pipe-child.c new file mode 100644 index 0000000000..d12a6e0ce5 --- /dev/null +++ b/tests/test-nonblocking-pipe-child.c @@ -0,0 +1,50 @@ +/* Child program invoked by test-nonblocking-pipe-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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> + +#include "binary-io.h" + +#include "macros.h" +#include "test-nonblocking-pipe.h" +#define PROG_ROLE "child" +#include "test-nonblocking-reader.h" + +int +main (int argc, char *argv[]) +{ + int test = atoi (argv[1]); + + /* Close unused file descriptors. */ + close (STDOUT_FILENO); + + /* STDIN_FILENO was created as binary in the parent process. But since an + fd's mode is stored in the process, not in the kernel, on native Windows + we need to set it as binary in the child process again. */ + SET_BINARY (STDIN_FILENO); + + main_reader_loop (test, PIPE_DATA_BLOCK_SIZE, STDIN_FILENO); + + return 0; +} diff --git a/tests/test-nonblocking-pipe-main.c b/tests/test-nonblocking-pipe-main.c new file mode 100644 index 0000000000..e61269ce5d --- /dev/null +++ b/tests/test-nonblocking-pipe-main.c @@ -0,0 +1,110 @@ +/* Test for nonblocking read and write on pipes. + + 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 <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# include <process.h> +#else +# include <spawn.h> +#endif + +#include "nonblocking.h" +#include "wait-process.h" + +#include "macros.h" +#include "test-nonblocking-pipe.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 fd[2]; + int child; + int exitcode; + + /* Create a pipe. */ + ASSERT (pipe (fd) >= 0); + + /* Map fd[0] to STDIN_FILENO and fd[1] to STDOUT_FILENO, because on Windows, + the only three file descriptors that are inherited by child processes are + STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO. */ + if (fd[0] != STDIN_FILENO) + { + ASSERT (dup2 (fd[0], STDIN_FILENO) >= 0); + close (fd[0]); + } + if (fd[1] != STDOUT_FILENO) + { + ASSERT (dup2 (fd[1], STDOUT_FILENO) >= 0); + close (fd[1]); + } + + /* Prepare the file descriptors. */ + if (test & 1) + ASSERT (set_nonblocking_flag (STDOUT_FILENO, true) >= 0); + if (test & 2) + ASSERT (set_nonblocking_flag (STDIN_FILENO, true) >= 0); + + /* Spawn the child process. */ + { + const char *child_argv[3]; + + child_argv[0] = child_path; + child_argv[1] = argv[2]; + child_argv[2] = 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 + } + + /* Close unused file descriptors. */ + close (STDIN_FILENO); + + exitcode = + main_writer_loop (test, PIPE_DATA_BLOCK_SIZE, STDOUT_FILENO, false); + + { + int err = + wait_subprocess (child, child_path, false, false, false, false, NULL); + ASSERT (err == 0); + } + + return exitcode; +} diff --git a/tests/test-nonblocking-pipe.h b/tests/test-nonblocking-pipe.h new file mode 100644 index 0000000000..c4e65616a4 --- /dev/null +++ b/tests/test-nonblocking-pipe.h @@ -0,0 +1,38 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +/* A data block ought to be larger than the size of the in-kernel buffer. + Working values of PIPE_DATA_BLOCK_SIZE, depending on kernel: + + Platform PIPE_DATA_BLOCK_SIZE + + Linux >= 63489 + FreeBSD, OpenBSD, MacOS X >= 65537 + AIX >= 32769 + HP-UX >= 8193 + IRIX >= 10241 + OSF/1 >= 262145 + Solaris <= 7 >= 10241 + Solaris >= 8 >= 20481 + Cygwin >= 65537 + native Win32 >= 4097 (depends on the _pipe argument) + */ +#if defined __osf__ +# define PIPE_DATA_BLOCK_SIZE 270000 +#else +# define PIPE_DATA_BLOCK_SIZE 70000 +#endif diff --git a/tests/test-nonblocking-pipe.sh b/tests/test-nonblocking-pipe.sh new file mode 100755 index 0000000000..dd692be7ee --- /dev/null +++ b/tests/test-nonblocking-pipe.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# Note: This test fails on Cygwin 1.5.x, because the non-blocking flag has +# apparently no effect on STDOUT_FILENO. It is fixed in Cygwin 1.7. + +# Test blocking write() with blocking read(). +# Commented out because this test succeeds on all platforms anyway. +#./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 0 || exit 1 + +# Test non-blocking write() with blocking read(). +./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 1 || exit 1 + +# Test blocking write() with non-blocking read(). +./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 2 || exit 1 + +# Test non-blocking write() with non-blocking read(). +./test-nonblocking-pipe-main${EXEEXT} ./test-nonblocking-pipe-child${EXEEXT} 3 || exit 1 diff --git a/tests/test-nonblocking-reader.h b/tests/test-nonblocking-reader.h new file mode 100644 index 0000000000..220862ff2d --- /dev/null +++ b/tests/test-nonblocking-reader.h @@ -0,0 +1,200 @@ +/* The reader part of a test program for non-blocking communication. + + 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/>. */ + +/* This program implements 4 tests: + + test == 0: + Test blocking write() with blocking read(). + + Timeline Main process Child process + 0 s Start Start, read(10000) + 1 s write(20000) Return from read(10000) + 2 s Next read(10000) + 2 s Return from write(20000) Return from read(10000) + + test == 1: + Test non-blocking write() with blocking read(). + + Timeline Main process Child process + 0 s Start Start, read(10000) + 1 s write(20000) Return from read(10000) + Return with at least 10000, + Repeatedly continue + write() of the rest + 2 s Next read(10000) + 2 s Return from write(10000) Return from read(10000) + + test == 2: + Test blocking write() with non-blocking read(). + + Timeline Main process Child process + 0 s Start Start, read(10000) + repeatedly polling + 1 s write(20000) Return from read(10000) + 2 s Next read(10000) + 2 s Return from write(20000) Return from read(10000) + + test == 3: + Test non-blocking write() with non-blocking read(). + */ + +#include "test-nonblocking-misc.h" + +static ssize_t +full_read (size_t fd, void *buf, size_t count) +{ + size_t bytes_read; + + bytes_read = 0; + while (bytes_read < count) + { + TIMING_DECLS + ssize_t ret; + int saved_errno; + + dbgfprintf (stderr, "%s: >> read (%lu)\n", PROG_ROLE, + (unsigned long) (count - bytes_read)); + START_TIMING + ret = read (fd, (char *) buf + bytes_read, count - bytes_read); + saved_errno = errno; + END_TIMING + dbgfprintf (stderr, "%s: << read -> %ld%s\n", PROG_ROLE, + (long) ret, dbgstrerror (ret < 0, saved_errno)); + if (ret < 0) + return -1; + else + { + ASSERT (ret > 0); + bytes_read += ret; + } + } + return bytes_read; +} + +static ssize_t +full_read_from_nonblocking_fd (size_t fd, void *buf, size_t count) +{ + size_t bytes_read; + + bytes_read = 0; + while (bytes_read < count) + { + TIMING_DECLS + ssize_t ret; + int saved_errno; + + dbgfprintf (stderr, "%s: >> read (%lu)\n", PROG_ROLE, + (unsigned long) (count - bytes_read)); + START_TIMING + ret = read (fd, (char *) buf + bytes_read, count - bytes_read); + saved_errno = errno; + END_TIMING + dbgfprintf (stderr, "%s: << read -> %ld%s\n", PROG_ROLE, + (long) ret, dbgstrerror (ret < 0, saved_errno)); + /* This assertion fails if the non-blocking flag is effectively not set + on fd. */ + ASSERT (spent_time < 0.5); + if (ret < 0) + { + ASSERT (saved_errno == EAGAIN); + usleep (SMALL_DELAY); + } + else + { + ASSERT (ret > 0); + bytes_read += ret; + } + } + return bytes_read; +} + +/* Execute the reader loop. */ +static void +main_reader_loop (int test, size_t data_block_size, int fd) +{ + unsigned char *expected; + unsigned char *data; + + /* Set up the expected data. */ + expected = init_data (data_block_size); + + data = (unsigned char *) malloc (2 * data_block_size); + ASSERT (data != NULL); + + switch (test) + { + TIMING_DECLS + ssize_t ret; + + case 0: /* Test blocking write() with blocking read(). */ + case 1: /* Test non-blocking write() with blocking read(). */ + START_TIMING + ret = full_read (fd, data, data_block_size); + END_TIMING + ASSERT (ret == data_block_size); + ASSERT (memcmp (data, expected, data_block_size) == 0); + ASSERT (spent_time > 0.5); + /* This assertion fails if data_block_size is very large and + ENABLE_DEBUGGING is 1. */ + ASSERT (spent_time < 1.5); + + usleep (1000000); + + START_TIMING + ret = full_read (fd, data, data_block_size); + END_TIMING + ASSERT (ret == data_block_size); + ASSERT (memcmp (data, expected + data_block_size, data_block_size) == 0); + /* This assertion fails if data_block_size is much larger than needed + and SMALL_DELAY is too large. */ + ASSERT (spent_time < 0.5); + + break; + + case 2: /* Test blocking write() with non-blocking read(). */ + case 3: /* Test non-blocking write() with non-blocking read(). */ + START_TIMING + ret = full_read_from_nonblocking_fd (fd, data, data_block_size); + END_TIMING + ASSERT (ret == data_block_size); + ASSERT (memcmp (data, expected, data_block_size) == 0); + ASSERT (spent_time > 0.5); + /* This assertion fails if data_block_size is much larger than needed + and SMALL_DELAY is too large, or if data_block_size is very large and + ENABLE_DEBUGGING is 1. */ + ASSERT (spent_time < 1.5); + + usleep (1000000); + + START_TIMING + ret = full_read_from_nonblocking_fd (fd, data, data_block_size); + END_TIMING + ASSERT (ret == data_block_size); + ASSERT (memcmp (data, expected + data_block_size, data_block_size) == 0); + /* This assertion fails if data_block_size is much larger than needed + and SMALL_DELAY is too large. */ + ASSERT (spent_time < 0.5); + + break; + + default: + abort (); + } + + free (data); + free (expected); +} diff --git a/tests/test-nonblocking-socket-child.c b/tests/test-nonblocking-socket-child.c new file mode 100644 index 0000000000..17545cf644 --- /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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> + +#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 0000000000..034873d5d2 --- /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 <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/socket.h> + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +# include <process.h> +#else +# include <spawn.h> +#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 0000000000..5f2268d9c3 --- /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 <http://www.gnu.org/licenses/>. */ + +/* 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 0000000000..3818c93d3a --- /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 diff --git a/tests/test-nonblocking-writer.h b/tests/test-nonblocking-writer.h new file mode 100644 index 0000000000..6db53f6dab --- /dev/null +++ b/tests/test-nonblocking-writer.h @@ -0,0 +1,186 @@ +/* The writer part of a test program for non-blocking communication. + + 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/>. */ + +/* This program implements 4 tests: + + test == 0: + Test blocking write() with blocking read(). + + Timeline Main process Child process + 0 s Start Start, read(10000) + 1 s write(20000) Return from read(10000) + 2 s Next read(10000) + 2 s Return from write(20000) Return from read(10000) + + test == 1: + Test non-blocking write() with blocking read(). + + Timeline Main process Child process + 0 s Start Start, read(10000) + 1 s write(20000) Return from read(10000) + Return with at least 10000, + Repeatedly continue + write() of the rest + 2 s Next read(10000) + 2 s Return from write(10000) Return from read(10000) + + test == 2: + Test blocking write() with non-blocking read(). + + Timeline Main process Child process + 0 s Start Start, read(10000) + repeatedly polling + 1 s write(20000) Return from read(10000) + 2 s Next read(10000) + 2 s Return from write(20000) Return from read(10000) + + test == 3: + Test non-blocking write() with non-blocking read(). + */ + +#include "test-nonblocking-misc.h" + +/* Execute the writer loop. + Returns 0 if successful, 1 if data_block_size is too small. */ +static int +main_writer_loop (int test, size_t data_block_size, int fd, + bool has_large_buffer) +{ + int too_small = 0; + unsigned char *data; + + /* Set up the data to transfer. */ + data = init_data (data_block_size); + + switch (test) + { + TIMING_DECLS + ssize_t ret; + + case 0: /* Test blocking write() with blocking read(). */ + case 2: /* Test blocking write() with non-blocking read(). */ + { + int saved_errno; + + usleep (1000000); + + dbgfprintf (stderr, "%s:1: >> write (%lu)\n", PROG_ROLE, + (unsigned long) 2 * data_block_size); + START_TIMING + ret = write (fd, data, 2 * data_block_size); + saved_errno = errno; + END_TIMING + dbgfprintf (stderr, "%s:1: << write -> %ld%s\n", PROG_ROLE, + (long) ret, dbgstrerror (ret < 0, saved_errno)); + ASSERT (ret == 2 * data_block_size); + if (!has_large_buffer) + { + /* This assertion fails if data_block_size is too small. */ + if (!(spent_time > 0.5)) + { + fprintf (stderr, + "%s:1: spent_time = %g, data_block_size too small\n", + PROG_ROLE, spent_time); + too_small = 1; + } + } + ASSERT (spent_time < 1.5); + } + break; + + case 1: /* Test non-blocking write() with blocking read(). */ + case 3: /* Test non-blocking write() with non-blocking read(). */ + { + size_t bytes_written; + int saved_errno; + + usleep (1000000); + + bytes_written = 0; + while (bytes_written < 2 * data_block_size) + { + dbgfprintf (stderr, "%s:2: >> write (%lu)\n", PROG_ROLE, + (unsigned long) (2 * data_block_size - bytes_written)); + START_TIMING + ret = write (fd, data + bytes_written, + 2 * data_block_size - bytes_written); + saved_errno = errno; + END_TIMING + dbgfprintf (stderr, "%s:2: << write -> %ld%s\n", PROG_ROLE, + (long) ret, dbgstrerror (ret < 0, saved_errno)); + if (ret < 0 && bytes_written >= data_block_size) + { + ASSERT (saved_errno == EAGAIN); + ASSERT (spent_time < 0.5); + break; + } + /* This assertion fails if the non-blocking flag is effectively not + set on fd. */ + ASSERT (spent_time < 0.5); + if (ret < 0) + { + ASSERT (saved_errno == EAGAIN); + usleep (SMALL_DELAY); + } + else + { + /* This assertion fails if data_block_size is too small. */ + if (!(ret > 0)) + { + fprintf (stderr, + "%s:1: spent_time = %g, data_block_size too small\n", + PROG_ROLE, spent_time); + too_small = 1; + } + bytes_written += ret; + } + } + ASSERT (bytes_written >= data_block_size); + + while (bytes_written < 2 * data_block_size) + { + dbgfprintf (stderr, "%s:3: >> write (%lu)\n", PROG_ROLE, + (unsigned long) (2 * data_block_size - bytes_written)); + START_TIMING + ret = write (fd, data + bytes_written, + 2 * data_block_size - bytes_written); + saved_errno = errno; + END_TIMING + dbgfprintf (stderr, "%s:3: << write -> %ld%s\n", PROG_ROLE, + (long) ret, dbgstrerror (ret < 0, saved_errno)); + ASSERT (spent_time < 0.5); + if (ret < 0) + { + ASSERT (saved_errno == EAGAIN); + usleep (SMALL_DELAY); + } + else + { + ASSERT (ret > 0); + bytes_written += ret; + } + } + } + break; + + default: + abort (); + } + + free (data); + return too_small; +} diff --git a/tests/test-nonblocking.c b/tests/test-nonblocking.c index f1b7610543..f3f1f1355b 100644 --- a/tests/test-nonblocking.c +++ b/tests/test-nonblocking.c @@ -33,13 +33,6 @@ main (void) const char *file = "test-nonblock.tmp"; int fd_file; int fd_pipe[2]; - int fd_sock; - bool sock_works = true; - -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ - /* For now, we can't get nonblocking status of windows sockets. */ - sock_works = false; -#endif fd_file = creat (file, 0600); @@ -79,28 +72,39 @@ main (void) ASSERT (close (fd_pipe[1]) == 0); #if GNULIB_TEST_PIPE2 - /* mingw still lacks O_NONBLOCK replacement. */ ASSERT (pipe2 (fd_pipe, O_NONBLOCK) == 0); - ASSERT (get_nonblocking_flag (fd_pipe[0]) == !!O_NONBLOCK); - ASSERT (get_nonblocking_flag (fd_pipe[1]) == !!O_NONBLOCK); + ASSERT (get_nonblocking_flag (fd_pipe[0]) == 1); + ASSERT (get_nonblocking_flag (fd_pipe[1]) == 1); ASSERT (close (fd_pipe[0]) == 0); ASSERT (close (fd_pipe[1]) == 0); #endif /* GNULIB_TEST_PIPE2 */ - /* Test sockets. */ - fd_sock = socket (AF_INET, SOCK_STREAM, 0); - ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 0 : -1)); - ASSERT (set_nonblocking_flag (fd_sock, true) == 0); - ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 1 : -1)); - ASSERT (set_nonblocking_flag (fd_sock, false) == 0); - ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 0 : -1)); - ASSERT (close (fd_sock) == 0); - -#if SOCK_NONBLOCK - fd_sock = socket (AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); - ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 1 : -1)); - ASSERT (close (fd_sock) == 0); -#endif /* SOCK_NONBLOCK */ +#if GNULIB_TEST_SOCKET + { + /* Test sockets. */ + bool sock_works = true; + int fd_sock; + +# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ + /* For now, we can't get nonblocking status of windows sockets. */ + sock_works = false; +# endif + + fd_sock = socket (AF_INET, SOCK_STREAM, 0); + ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 0 : -1)); + ASSERT (set_nonblocking_flag (fd_sock, true) == 0); + ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 1 : -1)); + ASSERT (set_nonblocking_flag (fd_sock, false) == 0); + ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 0 : -1)); + ASSERT (close (fd_sock) == 0); + +# if SOCK_NONBLOCK + fd_sock = socket (AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + ASSERT (get_nonblocking_flag (fd_sock) == (sock_works ? 1 : -1)); + ASSERT (close (fd_sock) == 0); +# endif /* SOCK_NONBLOCK */ + } +#endif /* GNULIB_TEST_SOCKET */ /* Test error handling. */ { diff --git a/tests/test-open.h b/tests/test-open.h index 3e5c5e1726..2ba5d137bf 100644 --- a/tests/test-open.h +++ b/tests/test-open.h @@ -63,6 +63,12 @@ test_open (int (*func) (char const *, int, ...), bool print) ASSERT (write (fd, "c", 1) == 1); ASSERT (close (fd) == 0); + /* Although O_NONBLOCK on regular files can be ignored, it must not + cause a failure. */ + fd = func (BASE "file", O_NONBLOCK | O_RDONLY); + ASSERT (0 <= fd); + ASSERT (close (fd) == 0); + /* Symlink handling, where supported. */ if (symlink (BASE "file", BASE "link") != 0) { diff --git a/tests/test-passfd.c b/tests/test-passfd.c index d657ad9d66..315e6c21ba 100644 --- a/tests/test-passfd.c +++ b/tests/test-passfd.c @@ -18,6 +18,7 @@ #include "passfd.h" +#include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdlib.h> @@ -33,6 +34,7 @@ int main () { +#if HAVE_SOCKETPAIR int pair[2]; int ret; pid_t pid; @@ -41,11 +43,11 @@ main () int fd; struct stat st; -#if HAVE_DECL_ALARM +# if HAVE_DECL_ALARM /* Avoid hanging on failure. */ signal (SIGALRM, SIG_DFL); alarm (5); -#endif +# endif fdnull = open ("/dev/null", O_RDWR); if (fdnull < 0) @@ -115,4 +117,17 @@ main () } return 0; } +#else + errno = 0; + ASSERT(sendfd (0, 0) == -1); + ASSERT(errno == ENOSYS); + + errno = 0; + ASSERT(recvfd (0, 0) == -1); + ASSERT(errno == ENOSYS); + + fputs ("skipping test: socketpair not supported on this system\n", + stderr); + return 77; +#endif } diff --git a/tests/test-pipe2.c b/tests/test-pipe2.c index ddfb819abb..8ca8e01363 100644 --- a/tests/test-pipe2.c +++ b/tests/test-pipe2.c @@ -33,6 +33,7 @@ SIGNATURE_CHECK (pipe2, int, (int[2], int)); #include "binary-io.h" #include "macros.h" +#include "nonblocking.h" /* Return true if FD is open. */ static bool @@ -67,49 +68,23 @@ is_cloexec (int fd) #endif } -/* Return true if FD is in non-blocking mode. */ -static bool -is_nonblocking (int fd) -{ -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ - /* We don't use the non-blocking mode for sockets here. */ - return 0; -#else - int flags; - ASSERT ((flags = fcntl (fd, F_GETFL)) >= 0); - return (flags & O_NONBLOCK) != 0; -#endif -} - int main () { int use_nonblocking; int use_cloexec; -#if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) for (use_nonblocking = 0; use_nonblocking <= 1; use_nonblocking++) -#else - use_nonblocking = 0; -#endif -#if O_CLOEXEC - for (use_cloexec = 0; use_cloexec <= 1; use_cloexec++) -#else - use_cloexec = 0; -#endif + for (use_cloexec = 0; use_cloexec <= !!O_CLOEXEC; use_cloexec++) { int o_flags; int fd[2]; o_flags = 0; -#if !((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__) if (use_nonblocking) o_flags |= O_NONBLOCK; -#endif -#if O_CLOEXEC if (use_cloexec) o_flags |= O_CLOEXEC; -#endif fd[0] = -1; fd[1] = -1; @@ -131,14 +106,17 @@ main () } if (use_nonblocking) { - ASSERT (is_nonblocking (fd[0])); - ASSERT (is_nonblocking (fd[1])); + ASSERT (get_nonblocking_flag (fd[0]) == 1); + ASSERT (get_nonblocking_flag (fd[1]) == 1); } else { - ASSERT (!is_nonblocking (fd[0])); - ASSERT (!is_nonblocking (fd[1])); + ASSERT (get_nonblocking_flag (fd[0]) == 0); + ASSERT (get_nonblocking_flag (fd[1]) == 0); } + + ASSERT (close (fd[0]) == 0); + ASSERT (close (fd[1]) == 0); } return 0; diff --git a/tests/test-renameat.c b/tests/test-renameat.c index 1849a24413..96fe3a7555 100644 --- a/tests/test-renameat.c +++ b/tests/test-renameat.c @@ -33,7 +33,6 @@ SIGNATURE_CHECK (renameat, int, (int, char const *, int, char const *)); #include <sys/stat.h> #include "filenamecat.h" -#include "xgetcwd.h" #include "ignore-value.h" #include "macros.h" @@ -79,7 +78,8 @@ main (void) dfd = creat (BASE "00", 0600); ASSERT (0 <= dfd); ASSERT (close (dfd) == 0); - cwd = xgetcwd (); + cwd = getcwd (NULL, 0); + ASSERT (cwd); dfd = open (BASE "sub1", O_RDONLY); ASSERT (0 <= dfd); diff --git a/tests/test-stdio-c++.cc b/tests/test-stdio-c++.cc index a9d00a39f8..07faf0fa02 100644 --- a/tests/test-stdio-c++.cc +++ b/tests/test-stdio-c++.cc @@ -36,6 +36,14 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::fclose, int, (FILE *)); SIGNATURE_CHECK (GNULIB_NAMESPACE::fflush, int, (FILE *)); #endif +#if GNULIB_TEST_FGETC +SIGNATURE_CHECK (GNULIB_NAMESPACE::fgetc, int, (FILE *)); +#endif + +#if GNULIB_TEST_FGETS +SIGNATURE_CHECK (GNULIB_NAMESPACE::fgets, char *, (char *, int, FILE *)); +#endif + #if GNULIB_TEST_FOPEN SIGNATURE_CHECK (GNULIB_NAMESPACE::fopen, FILE *, (const char *, const char *)); @@ -57,11 +65,20 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::fputc, int, (int, FILE *)); SIGNATURE_CHECK (GNULIB_NAMESPACE::fputs, int, (const char *, FILE *)); #endif +#if GNULIB_TEST_FREAD +SIGNATURE_CHECK (GNULIB_NAMESPACE::fread, size_t, + (void *, size_t, size_t, FILE *)); +#endif + #if GNULIB_TEST_FREOPEN SIGNATURE_CHECK (GNULIB_NAMESPACE::freopen, FILE *, (const char *, const char *, FILE *)); #endif +#if GNULIB_TEST_FSCANF +SIGNATURE_CHECK (GNULIB_NAMESPACE::fscanf, int, (FILE *, const char *, ...)); +#endif + #if GNULIB_TEST_FSEEK SIGNATURE_CHECK (GNULIB_NAMESPACE::fseek, int, (FILE *, long, int)); #endif @@ -83,6 +100,14 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::fwrite, size_t, (const void *, size_t, size_t, FILE *)); #endif +#if GNULIB_TEST_GETC +SIGNATURE_CHECK (GNULIB_NAMESPACE::getc, int, (FILE *)); +#endif + +#if GNULIB_TEST_GETCHAR +SIGNATURE_CHECK (GNULIB_NAMESPACE::getchar, int, (void)); +#endif + #if GNULIB_TEST_GETDELIM SIGNATURE_CHECK (GNULIB_NAMESPACE::getdelim, ssize_t, (char **, size_t *, int, FILE *)); @@ -93,6 +118,10 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::getline, ssize_t, (char **, size_t *, FILE *)); #endif +#if GNULIB_TEST_GETS +SIGNATURE_CHECK (GNULIB_NAMESPACE::gets, char *, (char *)); +#endif + #if GNULIB_TEST_OBSTACK_PRINTF || GNULIB_TEST_OBSTACK_PRINTF_POSIX SIGNATURE_CHECK (GNULIB_NAMESPACE::obstack_printf, int, (struct obstack *, const char *, ...)); @@ -140,6 +169,10 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::renameat, int, (int, char const *, int, char const *)); #endif +#if GNULIB_TEST_SCANF +SIGNATURE_CHECK (GNULIB_NAMESPACE::scanf, int, (const char *, ...)); +#endif + #if GNULIB_TEST_SNPRINTF SIGNATURE_CHECK (GNULIB_NAMESPACE::snprintf, int, (char *, size_t, const char *, ...)); @@ -170,10 +203,19 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::vfprintf, int, (FILE *, const char *, va_list)); #endif +#if GNULIB_TEST_VFSCANF +SIGNATURE_CHECK (GNULIB_NAMESPACE::vfscanf, int, + (FILE *, const char *, va_list)); +#endif + #if GNULIB_TEST_VPRINTF_POSIX || GNULIB_TEST_VPRINTF SIGNATURE_CHECK (GNULIB_NAMESPACE::vprintf, int, (const char *, va_list)); #endif +#if GNULIB_TEST_VSCANF +SIGNATURE_CHECK (GNULIB_NAMESPACE::vscanf, int, (const char *, va_list)); +#endif + #if GNULIB_TEST_VSNPRINTF SIGNATURE_CHECK (GNULIB_NAMESPACE::vsnprintf, int, (char *, size_t, const char *, va_list)); diff --git a/tests/test-sys_socket.c b/tests/test-sys_socket.c index 8f323ca1d9..a6e99d6008 100644 --- a/tests/test-sys_socket.c +++ b/tests/test-sys_socket.c @@ -30,6 +30,12 @@ int a[] = { SHUT_RD, SHUT_WR, SHUT_RDWR }; /* Check that the 'socklen_t' type is defined. */ socklen_t t1; +/* Check that 'struct iovec' is defined. */ +struct iovec io; + +/* Check that a minimal set of 'struct msghdr' is defined. */ +struct msghdr msg; + int main (void) { @@ -51,10 +57,8 @@ main (void) x.ss_family = 42; i = 42; + msg.msg_iov = &io; - /* Tell the compiler that these variables are used. */ - (void) x; - (void) i; - - return 0; + return (x.ss_family - i + msg.msg_namelen + msg.msg_iov->iov_len + + msg.msg_iovlen); } diff --git a/tests/test-sys_uio.c b/tests/test-sys_uio.c new file mode 100644 index 0000000000..7855a6bc43 --- /dev/null +++ b/tests/test-sys_uio.c @@ -0,0 +1,32 @@ +/* Test of <sys/uio.h> 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/>. */ + +/* Written by Eric Blake <eblake@redhat.com>, 2011. */ + +#include <config.h> + +#include <sys/uio.h> + +/* Check that necessary types are defined. */ +size_t a; +ssize_t b; +struct iovec c; + +int +main (void) +{ + return a + b + !!c.iov_base + c.iov_len; +} |