From f3e0d4df60935583d7d3f8ec31c5b0bdeaf351e4 Mon Sep 17 00:00:00 2001
From: Bruno Haible
Date: Mon, 11 Apr 2011 03:57:36 +0200
Subject: [PATCH] Add more tests for module 'nonblocking'.
---
modules/nonblocking-tests | 19 +++-
tests/test-nonblocking-data.h | 29 +++++
tests/test-nonblocking-pipe-child.c | 143 ++++++++++++++++++++++
tests/test-nonblocking-pipe-main.c | 224 +++++++++++++++++++++++++++++++++++
tests/test-nonblocking-pipe.sh | 13 ++
5 files changed, 426 insertions(+), 2 deletions(-)
create mode 100644 tests/test-nonblocking-data.h
create mode 100644 tests/test-nonblocking-pipe-child.c
create mode 100644 tests/test-nonblocking-pipe-main.c
create mode 100755 tests/test-nonblocking-pipe.sh
diff --git a/modules/nonblocking-tests b/modules/nonblocking-tests
index a1e5e7c..cd4e2d0 100644
--- a/modules/nonblocking-tests
+++ b/modules/nonblocking-tests
@@ -1,14 +1,29 @@
Files:
tests/test-nonblocking.c
+tests/test-nonblocking-pipe.sh
+tests/test-nonblocking-pipe-main.c
+tests/test-nonblocking-pipe-child.c
+tests/test-nonblocking-data.h
tests/macros.h
Depends-on:
close
+dup2
+environ
+full-read
+gettimeofday
pipe-posix
+posix_spawnp
+ssize_t
+unistd
+usleep
+wait-process
configure.ac:
Makefile.am:
-TESTS += test-nonblocking
-check_PROGRAMS += test-nonblocking
+TESTS += test-nonblocking test-nonblocking-pipe.sh
+check_PROGRAMS += \
+ test-nonblocking \
+ test-nonblocking-pipe-main test-nonblocking-pipe-child
test_nonblocking_LDADD = $(LDADD) $(LIBSOCKET)
diff --git a/tests/test-nonblocking-data.h b/tests/test-nonblocking-data.h
new file mode 100644
index 0000000..8928985
--- /dev/null
+++ b/tests/test-nonblocking-data.h
@@ -0,0 +1,29 @@
+/* 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.
+ On Linux, it needs to be >= 63489. */
+#define DATA_BLOCK_SIZE 100000
+
+static void
+init_data (unsigned char data[2 * DATA_BLOCK_SIZE])
+{
+ unsigned int i;
+
+ for (i = 0; i < 2 * DATA_BLOCK_SIZE; i++)
+ data[i] = (unsigned char) (i * i + (7 * i) % 61 + 4);
+}
diff --git a/tests/test-nonblocking-pipe-child.c b/tests/test-nonblocking-pipe-child.c
new file mode 100644
index 0000000..da1d8ee
--- /dev/null
+++ b/tests/test-nonblocking-pipe-child.c
@@ -0,0 +1,143 @@
+/* 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 . */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "full-read.h"
+
+#include "test-nonblocking-data.h"
+#include "macros.h"
+
+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)
+ {
+ 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;
+ ssize_t ret;
+ int saved_errno;
+
+ START_TIMING
+ ret = read (fd, (char *) buf + bytes_read, count - bytes_read);
+ saved_errno = errno;
+ END_TIMING
+ ASSERT (spent_time < 0.5);
+ if (ret < 0)
+ {
+ ASSERT (saved_errno == EAGAIN);
+ usleep (100000);
+ }
+ else
+ {
+ ASSERT (ret > 0);
+ bytes_read += ret;
+ }
+ }
+ return bytes_read;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int test = atoi (argv[1]);
+ unsigned char expected[2 * DATA_BLOCK_SIZE];
+ unsigned char data[2 * DATA_BLOCK_SIZE];
+
+ /* Close unused file descriptors. */
+ close (STDOUT_FILENO);
+
+ /* Set up the expected data. */
+ init_data (expected);
+
+ switch (test)
+ {
+ 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;
+ 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 (STDIN_FILENO, data, DATA_BLOCK_SIZE);
+ END_TIMING
+ ASSERT (ret == DATA_BLOCK_SIZE);
+ ASSERT (memcmp (data, expected, DATA_BLOCK_SIZE) == 0);
+ ASSERT (spent_time > 0.5 && spent_time < 1.5);
+
+ usleep (1000000);
+
+ START_TIMING
+ ret = full_read (STDIN_FILENO, data, DATA_BLOCK_SIZE);
+ END_TIMING
+ ASSERT (ret == DATA_BLOCK_SIZE);
+ ASSERT (memcmp (data, expected + DATA_BLOCK_SIZE, DATA_BLOCK_SIZE) == 0);
+ 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 (STDIN_FILENO, data, DATA_BLOCK_SIZE);
+ END_TIMING
+ ASSERT (ret == DATA_BLOCK_SIZE);
+ ASSERT (memcmp (data, expected, DATA_BLOCK_SIZE) == 0);
+ ASSERT (spent_time > 0.5 && spent_time < 1.5);
+
+ usleep (1000000);
+
+ START_TIMING
+ ret = full_read_from_nonblocking_fd (STDIN_FILENO, data, DATA_BLOCK_SIZE);
+ END_TIMING
+ ASSERT (ret == DATA_BLOCK_SIZE);
+ ASSERT (memcmp (data, expected + DATA_BLOCK_SIZE, DATA_BLOCK_SIZE) == 0);
+ ASSERT (spent_time < 0.5);
+
+ break;
+
+ default:
+ abort ();
+ }
+
+ return 0;
+}
diff --git a/tests/test-nonblocking-pipe-main.c b/tests/test-nonblocking-pipe-main.c
new file mode 100644
index 0000000..5fd335d
--- /dev/null
+++ b/tests/test-nonblocking-pipe-main.c
@@ -0,0 +1,224 @@
+/* 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 . */
+
+#include
+
+#include
+#include
+#include
+#include
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# include
+#else
+# include
+#endif
+
+#include "nonblocking.h"
+#include "wait-process.h"
+
+#include "test-nonblocking-data.h"
+#include "macros.h"
+
+/* 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().
+ */
+
+int
+main (int argc, char *argv[])
+{
+ const char *child_path = argv[1];
+ int test = atoi (argv[2]);
+ int fd[2];
+ int child;
+ unsigned char data[2 * DATA_BLOCK_SIZE];
+
+ /* 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);
+
+ /* Set up the data to transfer. */
+ init_data (data);
+
+ switch (test)
+ {
+ 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;
+ ssize_t ret;
+
+ case 0: /* Test blocking write() with blocking read(). */
+ case 2: /* Test blocking write() with non-blocking read(). */
+ usleep (1000000);
+
+ START_TIMING
+ ret = write (STDOUT_FILENO, data, 2 * DATA_BLOCK_SIZE);
+ END_TIMING
+ ASSERT (ret == 2 * DATA_BLOCK_SIZE);
+ ASSERT (spent_time > 0.5 && 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;
+
+ usleep (1000000);
+
+ bytes_written = 0;
+ for (;;)
+ {
+ START_TIMING
+ ret = write (STDOUT_FILENO, data + bytes_written,
+ 2 * DATA_BLOCK_SIZE - bytes_written);
+ if (ret < 0 && bytes_written >= DATA_BLOCK_SIZE)
+ break;
+ END_TIMING
+ ASSERT (spent_time < 0.5);
+ if (ret >= 0)
+ {
+ ASSERT (ret > 0);
+ bytes_written += ret;
+ }
+ }
+ ASSERT (errno == EAGAIN);
+ END_TIMING
+ ASSERT (spent_time < 0.5);
+ ASSERT (bytes_written >= DATA_BLOCK_SIZE);
+
+ while (bytes_written < 2 * DATA_BLOCK_SIZE)
+ {
+ START_TIMING
+ ret = write (STDOUT_FILENO, data + bytes_written,
+ 2 * DATA_BLOCK_SIZE - bytes_written);
+ if (ret < 0)
+ {
+ ASSERT (errno == EAGAIN);
+ END_TIMING
+ ASSERT (spent_time < 0.5);
+ usleep (100000);
+ }
+ else
+ {
+ END_TIMING
+ ASSERT (spent_time < 0.5);
+ ASSERT (ret > 0);
+ bytes_written += ret;
+ }
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ {
+ int err =
+ wait_subprocess (child, child_path, false, false, false, false, NULL);
+ ASSERT (err == 0);
+ }
+
+ return 0;
+}
diff --git a/tests/test-nonblocking-pipe.sh b/tests/test-nonblocking-pipe.sh
new file mode 100755
index 0000000..c6e1e0b
--- /dev/null
+++ b/tests/test-nonblocking-pipe.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# Test blocking write() with blocking read().
+./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
--
1.6.3.2