Added select test

This commit is contained in:
Thomas Veerman 2009-07-14 09:43:33 +00:00
parent ce916bcb91
commit 70d25344a2
9 changed files with 1543 additions and 4 deletions

View file

@ -7,7 +7,7 @@ OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \
test10 test12 test13 test14 test15 test16 test17 test18 test19 \
test21 test22 test23 test25 test26 test27 test28 test29 \
test30 test31 test32 test34 test35 test36 test37 test38 \
test39 t10a t11a t11b
test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f
BIGOBJ= test20 test24
ROOTOBJ= test11 test33
@ -28,7 +28,7 @@ $(ROOTOBJ):
clean:
cd select && make clean
-rm -rf *.o *.s *.bak test? test?? t10a t11a t11b DIR*
-rm -rf *.o *.s *.bak test? test?? t10a t11a t11b t40a t40b t40c t40d t40e t40f DIR*
test1: test1.c
test2: test2.c
@ -72,3 +72,10 @@ test36: test36.c
test37: test37.c
test38: test38.c
test39: test39.c
test40: test40.c
t40a: t40a.c
t40b: t40b.c
t40c: t40c.c
t40d: t40d.c
t40e: t40e.c
t40f: t40f.c

View file

@ -12,12 +12,12 @@ badones= # list of tests that failed
# Print test welcome message
clr
echo "Running POSIX compliance test suite. There are 39 tests in total."
echo "Running POSIX compliance test suite. There are 42 tests in total."
echo " "
# Run all the tests, keeping track of who failed.
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 \
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
sh1.sh sh2.sh
do total=`expr $total + 1`
if ./test$i

141
test/t40a.c Normal file
View file

@ -0,0 +1,141 @@
/* t40a.c
*
* Test FD_* macros
*
* Select works on regular files, (pseudo) terminal devices, streams-based
* files, FIFOs, pipes, and sockets. This test verifies the FD_* macros.
*
* This test is part of a bigger select test. It expects as argument which sub-
* test it is.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <errno.h>
#include <limits.h>
#ifndef OPEN_MAX
# define OPEN_MAX 1024
#endif
#define MAX_ERROR 5
int errct = 0, subtest = -1;
void e(int n, char *s) {
printf("Subtest %d, error %d, %s\n", subtest, n, s);
if (errct++ > MAX_ERROR) {
printf("Too many errors; test aborted\n");
exit(errct);
}
}
int main(int argc, char **argv) {
fd_set fds;
int i;
/* Get subtest number */
if(argc != 2) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-1);
} else if(sscanf(argv[1], "%d", &subtest) != 1) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-1);
}
/* FD_ZERO */
FD_ZERO(&fds);
for(i = 0; i < OPEN_MAX; i++) {
if(FD_ISSET(i, &fds)) {
e(1, "fd set should be completely empty");
break;
}
}
/* FD_SET */
for(i = 0; i < OPEN_MAX; i++) FD_SET(i, &fds);
for(i = 0; i < OPEN_MAX; i++) {
if(!FD_ISSET(i, &fds)) {
e(2, "fd set should be completely filled");
break;
}
}
/* Reset to empty set and verify it's really empty */
FD_ZERO(&fds);
for(i = 0; i < OPEN_MAX; i++) {
if(FD_ISSET(i, &fds)) {
e(3, "fd set should be completely empty");
break;
}
}
/* Let's try a variation on filling the set */
for(i = 0; i < OPEN_MAX; i += 2) FD_SET(i, &fds);
for(i = 0; i < OPEN_MAX - 1; i+= 2 ) {
if(!(FD_ISSET(i, &fds) && !FD_ISSET(i+1, &fds))) {
e(4, "bit pattern does not match");
break;
}
}
/* Reset to empty set and verify it's really empty */
FD_ZERO(&fds);
for(i = 0; i < OPEN_MAX; i++) {
if(FD_ISSET(i, &fds)) {
e(5,"fd set should be completely empty");
break;
}
}
/* Let's try another variation on filling the set */
for(i = 0; i < OPEN_MAX - 1; i += 2) FD_SET(i+1, &fds);
for(i = 0; i < OPEN_MAX - 1; i+= 2 ) {
if(!(FD_ISSET(i+1, &fds) && !FD_ISSET(i, &fds))) {
e(6, "bit pattern does not match");
break;
}
}
/* Reset to empty set and verify it's really empty */
FD_ZERO(&fds);
for(i = 0; i < OPEN_MAX; i++) {
if(FD_ISSET(i, &fds)) {
e(7, "fd set should be completely empty");
break;
}
}
/* FD_CLR */
for(i = 0; i < OPEN_MAX; i++) FD_SET(i, &fds); /* Set all bits */
for(i = 0; i < OPEN_MAX; i++) FD_CLR(i, &fds); /* Clear all bits */
for(i = 0; i < OPEN_MAX; i++) {
if(FD_ISSET(i, &fds)) {
e(8, "all bits in fd set should be cleared");
break;
}
}
/* Reset to empty set and verify it's really empty */
FD_ZERO(&fds);
for(i = 0; i < OPEN_MAX; i++) {
if(FD_ISSET(i, &fds)) {
e(9, "fd set should be completely empty");
break;
}
}
for(i = 0; i < OPEN_MAX; i++) FD_SET(i, &fds); /* Set all bits */
for(i = 0; i < OPEN_MAX; i += 2) FD_CLR(i, &fds); /* Clear all bits */
for(i = 0; i < OPEN_MAX; i += 2) {
if(FD_ISSET(i, &fds)) {
e(10, "all even bits in fd set should be cleared");
break;
}
}
exit(errct);
}

148
test/t40b.c Normal file
View file

@ -0,0 +1,148 @@
/* t40b.c
*
* Test regular files
*
* Select works on regular files, (pseudo) terminal devices, streams-based
* files, FIFOs, pipes, and sockets. This test verifies selecting for regular
* file descriptors. "File descriptors associated with regular files shall
* always select true for ready to read, ready to write, and error conditions"
* - Open Group. Although we set a timeout, the select should return
* immediately.
*
* This test is part of a bigger select test. It expects as argument which sub-
* test it is.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <errno.h>
#include <time.h>
#define FILE1 "/tmp/selecttest01-1"
#define FILES 2
#define TIME 3
#define MAX_ERROR 10
int errct = 0, subtest = -1;
char errorbuf[1000];
void e(int n, char *s) {
printf("Subtest %d, error %d, %s\n", subtest, n, s);
if (errct++ > MAX_ERROR) {
printf("Too many errors; test aborted\n");
exit(errct);
}
}
int main(int argc, char **argv) {
int fd1, fd2, retval;
fd_set fds_read, fds_write, fds_error;
struct timeval tv;
time_t start, end;
/* Get subtest number */
if(argc != 2) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-1);
} else if(sscanf(argv[1], "%d", &subtest) != 1) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-1);
}
/* Set timeout */
tv.tv_sec = TIME;
tv.tv_usec = 0;
/* Open a file for writing */
if((fd1 = open(FILE1, O_WRONLY|O_CREAT, 0644)) == -1) {
snprintf(errorbuf, sizeof(errorbuf), "failed to open file %s for writing",
FILE1);
e(1, errorbuf);
perror(NULL);
exit(1);
}
/* Open the same file for reading */
if((fd2 = open(FILE1, O_RDONLY)) == -1) {
snprintf(errorbuf, sizeof(errorbuf), "failed to open file %s for reading",
FILE1);
e(2, errorbuf);
perror(NULL);
exit(1);
}
/* Clear file descriptor bit masks */
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
/* Fill bit mask */
FD_SET(fd1, &fds_write);
FD_SET(fd2, &fds_read);
FD_SET(fd1, &fds_error);
FD_SET(fd2, &fds_error);
/* Do the select and time how long it takes */
start = time(NULL);
retval = select(fd2+1, &fds_read, &fds_write, &fds_error, &tv);
end = time(NULL);
/* Correct amount of ready file descriptors? 1 read + 1 write + 2 errors */
if(retval != 4) {
e(3, "four fds should be set");
}
/* Test resulting bit masks */
if(!FD_ISSET(fd1, &fds_write)) e(4, "write should be set");
if(!FD_ISSET(fd2, &fds_read)) e(5, "read should be set");
if(!FD_ISSET(fd1, &fds_error)) e(6, "error should be set");
if(!FD_ISSET(fd2, &fds_error)) e(7, "error should be set");
/* Was it instantaneous? */
if(end-start != TIME - TIME) {
snprintf(errorbuf,sizeof(errorbuf),"time spent blocking is not %d, but %ld",
TIME - TIME, (long int) (end-start));
e(8, "time spent blocking is not %d, but %ld");
}
/* Wait for read to become ready on O_WRONLY. This should fail immediately. */
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
FD_SET(fd1, &fds_read);
FD_SET(fd1, &fds_error);
FD_SET(fd2, &fds_error);
tv.tv_sec = TIME;
tv.tv_usec = 0;
retval = select(fd2+1, &fds_read, NULL, &fds_error, &tv);
/* Correct amount of ready file descriptors? 1 read + 2 error */
if(retval != 3) e(9, "incorrect amount of ready file descriptors");
if(!FD_ISSET(fd1, &fds_read)) e(10, "read should be set");
if(!FD_ISSET(fd1, &fds_error)) e(11, "error should be set");
if(!FD_ISSET(fd2, &fds_error)) e(12, "error should be set");
/* Try again as above, bit this time with O_RDONLY in the write set */
FD_ZERO(&fds_error);
FD_SET(fd2, &fds_write);
FD_SET(fd1, &fds_error);
FD_SET(fd2, &fds_error);
tv.tv_sec = TIME;
tv.tv_usec = 0;
retval = select(fd2+1, NULL, &fds_write, &fds_error, &tv);
/* Correct amount of ready file descriptors? 1 write + 2 errors */
if(retval != 3) e(13, "incorrect amount of ready file descriptors");
if(!FD_ISSET(fd2, &fds_write)) e(14, "write should be set");
if(!FD_ISSET(fd1, &fds_error)) e(15, "error should be set");
if(!FD_ISSET(fd2, &fds_error)) e(16, "error should be set");
close(fd1);
close(fd2);
unlink(FILE1);
exit(errct);
}

148
test/t40c.c Normal file
View file

@ -0,0 +1,148 @@
/* t40c.c
*
* Test (pseudo) terminal devices
*
* Select works on regular files, (pseudo) terminal devices, streams-based
* files, FIFOs, pipes, and sockets. This test verifies selecting for (pseudo)
* terminal devices.
*
* This test is part of a bigger select test. It expects as argument which sub-
* test it is.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#define TERMINALW "/dev/ttyp0"
#define TERMINALR "/dev/ptyp0"
#define SENDSTRING "minixrocks"
#define MAX_ERROR 5
int errct = 0, subtest = -1;
void e(int n, char *s) {
printf("Subtest %d, error %d, %s\n", subtest, n, s);
if (errct++ > MAX_ERROR) {
printf("Too many errors; test aborted\n");
exit(errct);
}
}
int do_child(void) {
int fd, retval;
struct timeval tv;
/* Opening master terminal for writing */
if((fd = open(TERMINALW, O_WRONLY)) == -1) {
printf("Error opening %s for writing, signalling parent to quit\n",
TERMINALW);
perror(NULL);
printf("Please make sure that %s is not in use while running this test\n",
TERMINALW);
exit(-1);
}
/* Going to sleep for two seconds to allow the parent proc to get ready */
tv.tv_sec = 2;
tv.tv_usec = 0;
select(0, NULL, NULL, NULL, &tv);
/* Try to write. Doesn't matter how many bytes we actually send. */
retval = write(fd, SENDSTRING, strlen(SENDSTRING));
close(fd);
/* Wait for another second to allow the parent to process incoming data */
tv.tv_usec = 1000000;
retval = select(0,NULL, NULL, NULL, &tv);
exit(0);
}
int do_parent(int child) {
int fd;
fd_set fds_read, fds_write, fds_error;
int retval;
/* Open slave terminal for reading */
if((fd = open(TERMINALR, O_RDONLY)) == -1) {
printf("Error opening %s for reading\n", TERMINALR);
perror(NULL);
printf("Please make sure that %s is not in use while running this test.\n",
TERMINALR);
waitpid(child, &retval, 0);
exit(-1);
}
/* Clear bit masks */
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
/* Set read bits */
FD_SET(fd, &fds_read);
FD_SET(fd, &fds_write);
/* Test if we can read or write from/to fd. As fd is opened read only we
* cannot actually write, so the select should return immediately with fd
* set in fds_write, but not in fds_read. Note that the child waits two
* seconds before sending data. This gives us the opportunity run this
* sub-test as reading from fd is blocking at this point. */
retval = select(fd+1, &fds_read, &fds_write, &fds_error, NULL);
if(retval != 1) e(1, "incorrect amount of ready file descriptors");
if(FD_ISSET(fd, &fds_read)) e(2, "read should NOT be set");
if(!FD_ISSET(fd, &fds_write)) e(3, "write should be set");
if(FD_ISSET(fd, &fds_error)) e(4, "error should NOT be set");
/* Block until ready; until child wrote stuff */
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
FD_SET(fd, &fds_read);
retval = select(fd+1, &fds_read, NULL, &fds_error, NULL);
if(retval != 1) e(5, "incorrect amount of ready file descriptors");
if(!FD_ISSET(fd, &fds_read)) e(6, "read should be set");
if(FD_ISSET(fd, &fds_error)) e(7, "error should not be set");
FD_ZERO(&fds_read); FD_ZERO(&fds_error);
FD_SET(fd,&fds_write);
retval = select(fd+1, NULL, &fds_write, NULL, NULL);
/* As it is impossible to write to a read only fd, this select should return
* immediately with fd set in fds_write. */
if(retval != 1) e(8, "incorrect amount or ready file descriptors");
close(fd);
waitpid(child, &retval, 0);
exit(errct);
}
int main(int argc, char **argv) {
int forkres;
/* Get subtest number */
if(argc != 2) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-1);
} else if(sscanf(argv[1], "%d", &subtest) != 1) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-1);
}
forkres = fork();
if(forkres == 0) do_child();
else if(forkres > 0) do_parent(forkres);
else { /* Fork failed */
perror("Unable to fork");
exit(-1);
}
exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
}

428
test/t40d.c Normal file
View file

@ -0,0 +1,428 @@
/* t40d.c
*
* Test FIFOs and pipes
*
* Select works on regular files, (pseudo) terminal devices, streams-based
* files, FIFOs, pipes, and sockets. This test verifies selecting for FIFOs
* (named pipes) and pipes (anonymous pipes). This test will not verify most
* error file descriptors, as the setting of this fdset in the face of an error
* condition is implementation-specific (except for regular files (alway set)
* and sockets (protocol-specific or OOB data received), but those file types
* are not being tested in this specific test).
*
* This test is part of a bigger select test. It expects as argument which sub-
* test it is.
*
* [1] If a socket has a pending error, it shall be considered to have an
* exceptional condition pending. Otherwise, what constitutes an exceptional
* condition is file type-specific. For a file descriptor for use with a
* socket, it is protocol-specific except as noted below. For other file types
* it is implementation-defined. If the operation is meaningless for a
* particular file type, pselect() or select() shall indicate that the
* descriptor is ready for read or write operations, and shall indicate that
* the descriptor has no exceptional condition pending.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#define NAMEDPIPE1 "/tmp/selecttest03-1"
#define NAMEDPIPE2 "/tmp/selecttest03-2"
#define SENDSTRING "minixrocks"
#define DO_HANDLEDATA 1
#define DO_PAUSE 3
#define DO_TIMEOUT 7
#define MAX_ERROR 5
int errct = 0, subtest = -1;
char errbuf[1000];
int fd_ap[2]; /* Anonymous pipe; read from fd_ap[0], write to fd_ap[1] */
int fd_np1; /* Named pipe */
int fd_np2; /* Named pipe */
void e(int n, char *s) {
printf("Subtest %d, error %d, %s\n", subtest, n, s);
if (errct++ > MAX_ERROR) {
printf("Too many errors; test aborted\n");
exit(errct);
}
}
void do_child(void) {
struct timeval tv;
int retval;
/* Open named pipe for writing. This will block until a reader arrives. */
if((fd_np1 = open(NAMEDPIPE1, O_WRONLY)) == -1) {
printf("Error opening %s for writing, signalling parent to quit\n",
NAMEDPIPE1);
perror(NULL);
printf("Please make sure that %s is not in use while running this test\n",
NAMEDPIPE1);
exit(-1);
}
/* Going to sleep for three seconds to allow the parent proc to get ready */
tv.tv_sec = DO_HANDLEDATA;
tv.tv_usec = 0;
select(0, NULL, NULL, NULL, &tv);
/* Try to write. Doesn't matter how many bytes we actually send. */
retval = write(fd_np1, SENDSTRING, strlen(SENDSTRING));
/* Wait for another second to allow the parent to process incoming data */
tv.tv_sec = DO_HANDLEDATA;
tv.tv_usec = 0;
retval = select(0,NULL, NULL, NULL, &tv);
close(fd_np1);
/* Wait for another second to allow the parent to process incoming data */
tv.tv_sec = DO_HANDLEDATA;
tv.tv_usec = 0;
retval = select(0,NULL, NULL, NULL, &tv);
/* Open named pipe for reading. This will block until a writer arrives. */
if((fd_np2 = open(NAMEDPIPE2, O_RDONLY)) == -1) {
printf("Error opening %s for reading, signalling parent to quit\n",
NAMEDPIPE2);
perror(NULL);
printf("Please make sure that %s is not in use while running this test\n",
NAMEDPIPE2);
exit(-1);
}
/* Wait for another second to allow the parent to run some tests. */
tv.tv_sec = DO_HANDLEDATA;
tv.tv_usec = 0;
retval = select(0, NULL, NULL, NULL, &tv);
close(fd_np2);
/* Anonymous pipe */
/* Let the parent do initial read and write tests from and to the pipe. */
tv.tv_sec = DO_PAUSE;
tv.tv_usec = 0;
retval = select(0, NULL, NULL, NULL, &tv);
/* Unblock blocking read select by writing data */
if(write(fd_ap[1], SENDSTRING, strlen(SENDSTRING)) < 0) {
perror("Could not write to anonymous pipe");
exit(-1);
}
exit(0);
}
int count_fds(int nfds, fd_set *fds) {
/* Return number of bits set in fds */
int i, result = 0;
assert(fds != NULL && nfds > 0);
for(i = 0; i < nfds; i++) {
if(FD_ISSET(i, fds)) result++;
}
return result;
}
int empty_fds(int nfds, fd_set *fds) {
/* Returns nonzero if the first bits up to nfds in fds are not set */
int i;
assert(fds != NULL && nfds > 0);
for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
return 1;
}
int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
/* Returns nonzero if lh equals rh up to nfds bits */
int i;
assert(lh != NULL && rh != NULL && nfds > 0);
for(i = 0; i < nfds; i++) {
if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
(!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
return 0;
}
}
return 1;
}
void dump_fds(int nfds, fd_set *fds) {
/* Print a graphical representation of bits in fds */
int i;
if(fds != NULL && nfds > 0) {
for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
printf("\n");
}
}
void do_parent(int child) {
fd_set fds_read, fds_write, fds_error;
fd_set fds_compare_read, fds_compare_write;
struct timeval tv;
time_t start, end;
int retval;
char buf[20];
/* Open named pipe for reading. This will block until a writer arrives. */
if((fd_np1 = open(NAMEDPIPE1, O_RDONLY)) == -1) {
printf("Error opening %s for reading\n", NAMEDPIPE1);
perror(NULL);
printf("Please make sure that %s is not in use while running this test.\n",
NAMEDPIPE1);
waitpid(child, &retval, 0);
exit(-1);
}
/* Clear bit masks */
FD_ZERO(&fds_read); FD_ZERO(&fds_write);
/* Set read and write bits */
FD_SET(fd_np1, &fds_read);
FD_SET(fd_np1, &fds_write);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
/* Test if we can read or write from/to fd_np1. As fd_np1 is opened read only
* we cannot actually write, so the select should return immediately [1] and
* the offending bit set in the fd set. We read from a pipe that is opened
* with O_NONBLOCKING cleared, so it is guaranteed we can read.
* However, at this moment the writer is sleeping, so the pipe is empty and
* read is supposed to block. Therefore, only 1 file descriptor should be
* ready. A timeout value is still set in case an error occurs in a faulty
* implementation. */
retval = select(fd_np1+1, &fds_read, &fds_write, NULL, &tv);
/* Did we receive an error? */
if(retval <= 0) {
snprintf(errbuf, sizeof(errbuf),
"one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
e(1, errbuf);
}
if(!empty_fds(fd_np1+1,&fds_read)) e(2, "no read bits should be set");
/* Make sure the write bit is set (and just 1 bit) */
FD_ZERO(&fds_compare_write); FD_SET(fd_np1, &fds_compare_write);
if(!compare_fds(fd_np1+1, &fds_compare_write, &fds_write))
e(3, "write should be set");
/* Clear sets and set up new bit masks */
FD_ZERO(&fds_read); FD_ZERO(&fds_write);
FD_SET(fd_np1, &fds_read);
tv.tv_sec = DO_TIMEOUT; /* To make sure we get to see some error messages
instead of blocking forever when the
implementation is faulty. A timeout causes retval
to be 0. */
tv.tv_usec = 0;
/* The sleeping writer is about to wake up and write data to the pipe. */
retval = select(fd_np1+1, &fds_read, &fds_write, NULL, &tv);
/* Correct amount of ready file descriptors? Just 1 read */
if(retval != 1) {
snprintf(errbuf, sizeof(errbuf),
"one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
e(4, errbuf);
}
if(!FD_ISSET(fd_np1, &fds_read)) e(5, "read should be set");
/* Note that we left the write set empty. This should be equivalent to
* setting this parameter to NULL. */
if(!empty_fds(fd_np1+1, &fds_write)) e(6, "write should NOT be set");
/* In case something went wrong above, we might end up with a child process
* blocking on a write call we close the file descriptor now. Synchronize on
* a read. */
if(read(fd_np1, buf, sizeof(SENDSTRING)) < 0) perror("Read error");
/* Close file descriptor. We're going to reverse the test */
close(fd_np1);
/* Wait for a second to allow the child to close the pipe as well */
tv.tv_sec = DO_HANDLEDATA;
tv.tv_usec = 0;
retval = select(0,NULL, NULL, NULL, &tv);
/* Open named pipe for writing. This call blocks until a reader arrives. */
if((fd_np2 = open(NAMEDPIPE2, O_WRONLY)) == -1) {
printf("Error opening %s for writing\n",
NAMEDPIPE2);
perror(NULL);
printf("Please make sure that %s is not in use while running this test\n",
NAMEDPIPE2);
exit(-1);
}
/* At this moment the child process has opened the named pipe for reading and
* we have opened it for writing. We're now going to reverse some of the
* tests we've done earlier. */
/* Clear sets and set up bit masks */
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
FD_SET(fd_np2, &fds_read);
FD_SET(fd_np2, &fds_write);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
/* Select for reading from an fd opened O_WRONLY. This should return
* immediately as it is not a meaningful operation [1] and is therefore not
* blocking. The select should return two file descriptors are ready (the
* failing read and valid write). */
retval = select(fd_np2+1, &fds_read, &fds_write, &fds_error, &tv);
/* Did we receive an error? */
if(retval <= 0) {
snprintf(errbuf, sizeof(errbuf),
"two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
e(7, errbuf);
}
/* Make sure read bit is set (and just 1 bit) */
FD_ZERO(&fds_compare_read); FD_SET(fd_np2, &fds_compare_read);
if(!compare_fds(fd_np2+1, &fds_compare_read, &fds_read))
e(8, "read should be set");
/* Write bit should be set (and just 1 bit) */
FD_ZERO(&fds_compare_write); FD_SET(fd_np2, &fds_compare_write);
if(!compare_fds(fd_np2+1, &fds_compare_write, &fds_write))
e(9, "write should be set");
if(!empty_fds(fd_np2+1, &fds_error))
e(10, "Error should NOT be set");
FD_ZERO(&fds_read); FD_ZERO(&fds_write);
FD_SET(fd_np2, &fds_write);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
retval = select(fd_np2+1, &fds_read, &fds_write, NULL, &tv);
/* Correct amount of ready file descriptors? Just 1 write */
if(retval != 1) {
snprintf(errbuf, sizeof(errbuf),
"one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
e(11, errbuf);
}
if(!empty_fds(fd_np2+1, &fds_read)) e(12, "read should NOT be set");
/* Anonymous pipe */
/* Check if we can write to the pipe */
FD_ZERO(&fds_read); FD_ZERO(&fds_write);
FD_SET(fd_ap[1], &fds_write);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
retval = select(fd_ap[1]+1, NULL, &fds_write, NULL, &tv);
/* Correct amount of ready file descriptors? Just 1 write */
if(retval != 1) {
snprintf(errbuf, sizeof(errbuf),
"one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
e(13, errbuf);
}
/* Make sure write bit is set (and just 1 bit) */
FD_ZERO(&fds_compare_write); FD_SET(fd_ap[1], &fds_compare_write);
if(!compare_fds(fd_ap[1]+1, &fds_compare_write, &fds_write))
e(14, "write should be set");
/* Intentionally test reading from pipe and letting it time out. */
FD_SET(fd_ap[0], &fds_read);
tv.tv_sec = 1;
tv.tv_usec = 0;
start = time(NULL);
retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
end = time(NULL);
/* Did we time out? */
if(retval != 0) e(15, "we should have timed out");
/* Did it take us approximately 1 second? */
if((int) (end - start) != 1) {
snprintf(errbuf, sizeof(errbuf),
"time out is not 1 second (instead, it is %ld)",
(long int) (end - start));
e(16, errbuf);
}
/* Do another read, but this time we expect incoming data from child. */
FD_ZERO(&fds_read);
FD_SET(fd_ap[0], &fds_read);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
/* Correct amount of ready file descriptors? Just 1 read. */
if(retval != 1) e(17, "one fd should be set");
/* Is the read bit set? And just 1 bit. */
FD_ZERO(&fds_compare_read); FD_SET(fd_ap[0], &fds_compare_read);
if(!compare_fds(fd_ap[0]+1, &fds_compare_read, &fds_read))
e(18, "read should be set.");
/* By convention fd_ap[0] is meant to be used for reading from the pipe and
* fd_ap[1] is meant for writing, where fd_ap is a an anonymous pipe.
* However, it is unspecified what happens when fd_ap[0] is used for writing
* and fd_ap[1] for reading. (It is unsupported on Minix.) As such, it is not
* necessary to make test cases for wrong pipe file descriptors using select.
*/
waitpid(child, &retval, 0);
unlink(NAMEDPIPE2);
unlink(NAMEDPIPE1);
exit(errct);
}
int main(int argc, char **argv) {
int forkres;
/* Get subtest number */
if(argc != 2) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-2);
} else if(sscanf(argv[1], "%d", &subtest) != 1) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-2);
}
/* Set up anonymous pipe */
if(pipe(fd_ap) < 0) {
perror("Could not create anonymous pipe");
exit(-1);
}
/* Create named pipe2. It is unlinked by do_parent. */
if(mkfifo(NAMEDPIPE1, 0600) < 0) {
printf("Could not create named pipe %s", NAMEDPIPE1);
perror(NULL);
exit(-1);
}
if(mkfifo(NAMEDPIPE2, 0600) < 0) {
printf("Could not create named pipe %s", NAMEDPIPE2);
perror(NULL);
exit(-1);
}
forkres = fork();
if(forkres == 0) do_child();
else if(forkres > 0) do_parent(forkres);
else { /* Fork failed */
perror("Unable to fork");
exit(-1);
}
exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
}

429
test/t40e.c Normal file
View file

@ -0,0 +1,429 @@
/* t40e.c
*
* Test sockets
*
* Select works on regular files, (pseudo) terminal devices, streams-based
* files, FIFOs, pipes, and sockets. This test verifies selecting for sockets.
*
* This test is part of a bigger select test. It expects as argument which sub-
* test it is.
*
* Specific rules for sockets:
* If a socket has a pending error, it shall be considered to have an
* exceptional condition pending. Otherwise, what constitutes an exceptional
* condition is file type-specific. For a file descriptor for use with a
* socket, it is protocol-specific except as noted below. For other file types
* it is implementation-defined. If the operation is meaningless for a
* particular file type, pselect() or select() shall indicate that the
* descriptor is ready for read or write operations, and shall indicate that
* the descriptor has no exceptional condition pending.
*
* [1] If a descriptor refers to a socket, the implied input function is the
* recvmsg()function with parameters requesting normal and ancillary data, such
* that the presence of either type shall cause the socket to be marked as
* readable. The presence of out-of-band data shall be checked if the socket
* option SO_OOBINLINE has been enabled, as out-of-band data is enqueued with
* normal data. If the socket is currently listening, then it shall be marked
* as readable if an incoming connection request has been received, and a call
* to the accept() function shall complete without blocking.
*
* [2] If a descriptor refers to a socket, the implied output function is the
* sendmsg() function supplying an amount of normal data equal to the current
* value of the SO_SNDLOWAT option for the socket. If a non-blocking call to
* the connect() function has been made for a socket, and the connection
* attempt has either succeeded or failed leaving a pending error, the socket
* shall be marked as writable.
*
* [3] A socket shall be considered to have an exceptional condition pending if
* a receive operation with O_NONBLOCK clear for the open file description and
* with the MSG_OOB flag set would return out-of-band data without blocking.
* (It is protocol-specific whether the MSG_OOB flag would be used to read
* out-of-band data.) A socket shall also be considered to have an exceptional
* condition pending if an out-of-band data mark is present in the receive
* queue. Other circumstances under which a socket may be considered to have an
* exceptional condition pending are protocol-specific and
* implementation-defined.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <netdb.h>
#define DO_HANDLEDATA 1
#define DO_PAUSE 3
#define DO_TIMEOUT 7
#define MYPORT 3490
#define NUMCHILDREN 5
#define MAX_ERROR 10
int errct = 0, subtest = -1;
char errbuf[1000];
void e(int n, char *s) {
printf("Subtest %d, error %d, %s\n", subtest, n, s);
if (errct++ > MAX_ERROR) {
printf("Too many errors; test aborted\n");
exit(errct);
}
}
/* All *_fds routines are helping routines. They intentionally use FD_* macros
in order to prevent making assumptions on how the macros are implemented.*/
int count_fds(int nfds, fd_set *fds) {
/* Return number of bits set in fds */
int i, result = 0;
assert(fds != NULL && nfds > 0);
for(i = 0; i < nfds; i++) {
if(FD_ISSET(i, fds)) result++;
}
return result;
}
int empty_fds(int nfds, fd_set *fds) {
/* Returns nonzero if the first bits up to nfds in fds are not set */
int i;
assert(fds != NULL && nfds > 0);
for(i = 0; i < nfds; i++) if(FD_ISSET(i, fds)) return 0;
return 1;
}
int compare_fds(int nfds, fd_set *lh, fd_set *rh) {
/* Returns nonzero if lh equals rh up to nfds bits */
int i;
assert(lh != NULL && rh != NULL && nfds > 0);
for(i = 0; i < nfds; i++) {
if((FD_ISSET(i, lh) && !FD_ISSET(i, rh)) ||
(!FD_ISSET(i, lh) && FD_ISSET(i, rh))) {
return 0;
}
}
return 1;
}
void dump_fds(int nfds, fd_set *fds) {
/* Print a graphical representation of bits in fds */
int i;
if(fds != NULL && nfds > 0) {
for(i = 0; i < nfds; i++) printf("%d ", (FD_ISSET(i, fds) ? 1 : 0));
printf("\n");
}
}
void do_child(int childno) {
int fd_sock, port;
int retval;
fd_set fds_read, fds_write, fds_error;
fd_set fds_compare_write;
struct hostent *he;
struct sockaddr_in server;
struct timeval tv;
if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("Error getting socket\n");
exit(-1);
}
if((he = gethostbyname("127.0.0.1")) == NULL){/*"localhost" might be unknown*/
perror("Error resolving");
exit(-1);
}
/* Child 4 connects to the wrong port. See Actual testing description below.*/
port = (childno == 3 ? MYPORT + 1 : MYPORT);
memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
server.sin_family = AF_INET;
server.sin_port = htons(port);
#if 0
printf("Going to connect to: %s:%d\n", inet_ntoa(server.sin_addr),
ntohs(server.sin_port));
#endif
/* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
#ifndef _MINIX
memset(server.sin_zero, '\0', sizeof server.sin_zero);
#endif
/* Wait for parent to set up connection */
tv.tv_sec = (childno <= 1 ? DO_PAUSE : DO_TIMEOUT);
tv.tv_usec = 0;
retval = select(0, NULL, NULL, NULL, &tv);
/* All set, let's do some testing */
/* Children 3 and 4 do a non-blocking connect */
if(childno == 2 || childno == 3)
fcntl(fd_sock, F_SETFL, fcntl(fd_sock, F_GETFL, 0) | O_NONBLOCK);
if(connect(fd_sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
/* Well, we don't actually care. The connect is non-blocking and is
supposed to "in progress" at this point. */
}
if(childno == 2 || childno == 3) { /* Children 3 and 4 */
/* Open Group: "If a non-blocking call to the connect() function has been
made for a socket, and the connection attempt has either succeeded or
failed leaving a pending error, the socket shall be marked as writable.
...
A socket shall be considered to have an exceptional condition pending if
a receive operation with O_NONBLOCK clear for the open file description
and with the MSG_OOB flag set would return out-of-band data without
blocking. (It is protocol-specific whether the MSG_OOB flag would be used
to read out-of-band data.) A socket shall also be considered to have an
exceptional condition pending if an out-of-band data mark is present in
the receive queue. Other circumstances under which a socket may be
considered to have an exceptional condition pending are protocol-specific
and implementation-defined."
In other words, it only makes sense for us to check the write set as the
read set is not expected to be set, but is allowed to be set (i.e.,
unspecified) and whether the error set is set is implementation-defined.
*/
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
FD_SET(fd_sock, &fds_write);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
retval = select(fd_sock+1, NULL, &fds_write, NULL, &tv);
if(retval <= 0) e(6, "expected one fd to be ready");
FD_ZERO(&fds_compare_write); FD_SET(fd_sock, &fds_compare_write);
if(!compare_fds(fd_sock+1, &fds_compare_write, &fds_compare_write))
e(7, "write should be set");
}
if(close(fd_sock) < 0) {
perror("Error disconnecting");
exit(-1);
}
exit(errct);
}
void do_parent(void) {
int fd_sock, fd_new, yes = 1, exitstatus;
int sockets[NUMCHILDREN], i;
fd_set fds_read, fds_write, fds_error;
fd_set fds_compare_read, fds_compare_write;
struct timeval tv;
int retval, childresults = 0;
struct sockaddr_in my_addr;
struct sockaddr_in other_addr;
socklen_t other_size;
if((fd_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("Error getting socket\n");
exit(-1);
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT); /* Short, network byte order */
my_addr.sin_addr.s_addr = INADDR_ANY;
/* Normally we'd zerofill sin_zero, but there is no such thing on Minix */
#ifndef _MINIX
memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
#endif
/* Reuse port number. Not implemented in Minix. */
#ifndef _MINIX
if(setsockopt(fd_sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
perror("Error setting port reuse option");
exit(-1);
}
#endif
/* Bind to port */
if(bind(fd_sock, (struct sockaddr *) &my_addr, sizeof my_addr) < 0) {
perror("Error binding to port");
exit(-1);
}
/* Mark socket to be used for incoming connections */
if(listen(fd_sock, 20) < 0) {
perror("Listen");
exit(-1);
}
/* Actual testing */
/* While sockets resemble file descriptors, they are not the same at all.
We can read/write from/to and close file descriptors, but we cannot open
them O_RDONLY or O_WRONLY; they are always O_RDWR (other flags do not make
sense regarding sockets). As such, we cannot provide wrong file descriptors
to select, except for descriptors that are not in use.
We will test standard behavior and what is described in [2]. [1] and [3]
are not possible to test on Minix, as Minix does not support OOB data. That
is, the TCP layer can handle it, but there is no socket interface for it.
Our test consists of waiting for input from the first two children and
waiting to write output [standard usage]. Then the first child closes its
connection we select for reading. This should fail with error set. Then we
close child number two on our side and select for reading. This should fail
with EBADF. Child number three shall then do a non-blocking connect (after
waiting for DO_PAUSE seconds) and do a select, resulting in being marked
ready for writing. Subsequently child number four also does a non-blocking
connect to loclhost on MYPORT+1 (causing the connect to fail) and then does
a select. This should result in write and error being set (error because of
pending error).
*/
/* Accept and store connections from the first two children */
other_size = sizeof(other_addr);
for(i = 0; i < 2; i++) {
fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
if(fd_new < 0) break;
sockets[i] = fd_new;
}
/* If we break out of the for loop, we ran across an error and want to exit.
Check whether we broke out. */
if(fd_new < 0) {
perror("Error accepting connection");
exit(-1);
}
/* Select error condition checking */
for(childresults = 0; childresults < 2; childresults++) {
FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error);
FD_SET(sockets[childresults], &fds_read);
FD_SET(sockets[childresults], &fds_write);
FD_SET(sockets[childresults], &fds_error);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
retval = select(sockets[childresults]+1, &fds_read, &fds_write, &fds_error,
&tv);
if(retval <= 0) {
snprintf(errbuf, sizeof(errbuf),
"two fds should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
e(1, errbuf);
}
FD_ZERO(&fds_compare_read); FD_ZERO(&fds_compare_write);
FD_SET(sockets[childresults], &fds_compare_write);
/* We can't say much about being ready for reading at this point or not. It
is not specified and the other side might have data ready for us to read
*/
if(!compare_fds(sockets[childresults]+1, &fds_compare_write, &fds_write))
e(2, "write should be set");
if(!empty_fds(sockets[childresults]+1, &fds_error))
e(3, "no error should be set");
}
/* We continue by accepting a connection of child 3 */
fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
if(fd_new < 0) {
perror("Error accepting connection\n");
exit(-1);
}
sockets[2] = fd_new;
/* Child 4 will never connect */
/* Child 5 is still pending to be accepted. Open Group: "If the socket is
currently listening, then it shall be marked as readable if an incoming
connection request has been received, and a call to the accept() function
shall complete without blocking."*/
FD_ZERO(&fds_read);
FD_SET(fd_sock, &fds_read);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
retval = select(fd_sock+1, &fds_read, NULL, NULL, &tv);
if(retval <= 0) {
snprintf(errbuf, sizeof(errbuf),
"one fd should be set%s", (retval == 0 ? " (TIMEOUT)" : ""));
e(4, errbuf);
}
/* Check read bit is set */
FD_ZERO(&fds_compare_read); FD_SET(fd_sock, &fds_compare_read);
if(!compare_fds(fd_sock+1, &fds_compare_read, &fds_read))
e(5, "read should be set");
/* Accept incoming connection to unblock child 5 */
fd_new = accept(fd_sock, (struct sockaddr *) &other_addr, &other_size);
if(fd_new < 0) {
perror("Error accepting connection\n");
exit(-1);
}
sockets[4] = fd_new;
/* We're done, let's wait a second to synchronize children and parent. */
tv.tv_sec = DO_HANDLEDATA;
tv.tv_usec = 0;
select(0, NULL, NULL, NULL, &tv);
/* Close connection with children. */
for(i = 0; i < NUMCHILDREN; i++) {
if(i == 3) /* No need to disconnect child 4 that failed to connect. */
continue;
if(close(sockets[i]) < 0) {
perror(NULL);
}
}
/* Close listening socket */
if(close(fd_sock) < 0) {
perror("Closing listening socket");
errct++;
}
for(i = 0; i < NUMCHILDREN; i++) {
wait(&exitstatus); /* Wait for children */
if(exitstatus > 0)
errct += WEXITSTATUS(exitstatus); /* and count their errors, too. */
}
exit(errct);
}
int main(int argc, char **argv) {
int forkres, i;
/* Get subtest number */
if(argc != 2) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-2);
} else if(sscanf(argv[1], "%d", &subtest) != 1) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-2);
}
/* Fork off a bunch of children */
for(i = 0; i < NUMCHILDREN; i++) {
forkres = fork();
if(forkres == 0) do_child(i);
else if(forkres < 0) {
perror("Unable to fork");
exit(-1);
}
}
/* do_child always calls exit(), so when we end up here, we're the parent. */
do_parent();
exit(-2); /* We're not supposed to get here. Both do_* routines should exit.*/
}

185
test/t40f.c Normal file
View file

@ -0,0 +1,185 @@
/* t40f.c
*
* Test timing
*
* Select works on regular files, (pseudo) terminal devices, streams-based
* files, FIFOs, pipes, and sockets. This test verifies selecting with a time
* out set.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define DO_HANDLEDATA 1
#define DO_PAUSE 3
#define DO_TIMEOUT 7
#define DO_DELTA 0.5
#define MAX_ERROR 5
#define DELTA(x,y) (x.tv_sec - y.tv_sec) * CLOCKS_PER_SEC \
+ (x.tv_usec - y.tv_usec) * CLOCKS_PER_SEC / 1000000
int errct = 0, subtest = -1, got_signal = 0;
int fd_ap[2];
void catch_signal(int sig_no) {
got_signal = 1;
}
void e(int n, char *s) {
printf("Subtest %d, error %d, %s\n", subtest, n, s);
if (errct++ > MAX_ERROR) {
printf("Too many errors; test aborted\n");
exit(errct);
}
}
float compute_diff(struct timeval start, struct timeval end, float compare) {
/* Compute time difference. It is assumed that the value of start <= end. */
clock_t delta;
int seconds, hundreths;
float diff;
delta = DELTA(end, start); /* delta is in ticks */
seconds = (int) (delta / CLOCKS_PER_SEC);
hundreths = (int) (delta * 100 / CLOCKS_PER_SEC) - (seconds * 100);
diff = seconds + (hundreths / 100.0);
diff -= compare;
if(diff < 0) diff *= -1; /* Make diff a positive value */
return diff;
}
void do_child(void) {
struct timeval tv;
int retval;
/* Let the parent do initial read and write tests from and to the pipe. */
tv.tv_sec = DO_PAUSE + DO_PAUSE + DO_PAUSE + 1;
tv.tv_usec = 0;
retval = select(0, NULL, NULL, NULL, &tv);
/* At this point the parent has a pending select with a DO_TIMEOUT timeout.
We're going to interrupt by sending a signal */
if(kill(getppid(), SIGUSR1) < 0) perror("Failed to send signal");
exit(0);
}
void do_parent(int child) {
fd_set fds_read;
struct timeval tv, start_time, end_time;
int retval;
/* Install signal handler for SIGUSR1 */
signal(SIGUSR1, catch_signal);
/* Parent and child share an anonymous pipe. Select for read and wait for the
timeout to occur. We wait for DO_PAUSE seconds. Let's see if that's
approximately right.*/
FD_ZERO(&fds_read);
FD_SET(fd_ap[0], &fds_read);
tv.tv_sec = DO_PAUSE;
tv.tv_usec = 0;
(void) gettimeofday(&start_time, NULL); /* Record starting time */
retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
(void) gettimeofday(&end_time, NULL); /* Record ending time */
/* Did we time out? */
if(retval != 0) e(1, "Should have timed out");
/* Approximately right? The standard does not specify how precise the timeout
should be. Instead, the granularity is implementation-defined. In this
test we assume that the difference should be no more than half a second.*/
if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
e(2, "Time difference too large");
/* Let's wait for another DO_PAUSE seconds, expressed as microseconds */
FD_ZERO(&fds_read);
FD_SET(fd_ap[0], &fds_read);
tv.tv_sec = 0;
tv.tv_usec = DO_PAUSE * 1000000L;
(void) gettimeofday(&start_time, NULL); /* Record starting time */
retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
(void) gettimeofday(&end_time, NULL); /* Record ending time */
if(retval != 0) e(3, "Should have timed out");
if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
e(4, "Time difference too large");
/* Let's wait for another DO_PAUSE seconds, expressed in seconds and micro
seconds. */
FD_ZERO(&fds_read);
FD_SET(fd_ap[0], &fds_read);
tv.tv_sec = DO_PAUSE - 1;
tv.tv_usec = (DO_PAUSE - tv.tv_sec) * 1000000L;
(void) gettimeofday(&start_time, NULL); /* Record starting time */
retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
(void) gettimeofday(&end_time, NULL); /* Record ending time */
if(retval != 0) e(5, "Should have timed out");
if(compute_diff(start_time, end_time, DO_PAUSE) > DO_DELTA)
e(6, "Time difference too large");
/* Finally, we test if our timeout is interrupted by a signal */
FD_ZERO(&fds_read);
FD_SET(fd_ap[0], &fds_read);
tv.tv_sec = DO_TIMEOUT;
tv.tv_usec = 0;
(void) gettimeofday(&start_time, NULL); /* Record starting time */
retval = select(fd_ap[0]+1, &fds_read, NULL, NULL, &tv);
(void) gettimeofday(&end_time, NULL); /* Record ending time */
if(retval != -1) e(7, "Should have been interrupted");
if(compute_diff(start_time, end_time, DO_TIMEOUT) < DO_DELTA)
e(8, "Failed to get interrupted by a signal");
if(!got_signal) e(9, "Failed to get interrupted by a signal");
waitpid(child, &retval, 0);
exit(errct);
}
int main(int argc, char **argv) {
int forkres;
/* Get subtest number */
if(argc != 2) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-2);
} else if(sscanf(argv[1], "%d", &subtest) != 1) {
printf("Usage: %s subtest_no\n", argv[0]);
exit(-2);
}
/* Set up anonymous pipe */
if(pipe(fd_ap) < 0) {
perror("Could not create anonymous pipe");
exit(-1);
}
forkres = fork();
if(forkres == 0) do_child();
else if(forkres > 0) do_parent(forkres);
else { /* Fork failed */
perror("Unable to fork");
exit(-1);
}
exit(-2); /* We're not supposed to get here. Both do_* routines should exit*/
}

53
test/test40.c Normal file
View file

@ -0,0 +1,53 @@
/* Test40.c
*
* Test select(...) system call
*
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
int main(int argc, char **argv) {
char *tests[] = {"t40a", "t40b", "t40c", "t40d", "t40e", "t40f"};
int no_tests, i, forkres, status = 0, errorct = 0;
no_tests = sizeof(tests) / sizeof(char *);
printf("Test 40 ");
fflush(stdout);
for(i = 0; i < no_tests; i++) {
char subtest[2];
sprintf(subtest, "%d", i+1);
forkres = fork();
if(forkres == 0) { /* Child */
execl(tests[i], tests[i], subtest, (char *) 0);
printf("Failed to execute subtest %s\n", tests[i]);
exit(-2);
} else if(forkres > 0) { /* Parent */
if(waitpid(forkres, &status, 0) > 0 && WEXITSTATUS(status) < 20) {
errorct += WEXITSTATUS(status); /* Count errors */
}
status = 0; /* Reset */
} else {
printf("Failed to fork\n");
exit(-2);
}
}
if(errorct == 0) {
printf("Ok\n");
exit(0);
} else {
printf("%d error(s)\n", errorct);
exit(1);
}
return (-1); /* Impossible */
}