Files
nt-lite/code/user/tests/posix_pipe_smoke/posix_pipe_smoke.c
T

127 lines
4.2 KiB
C

// User-mode smoke test for POSIX pipe, nonblocking, and poll semantics.
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <sys/stat.h>
static void print(const char* text) {
(void)write(1, text, strlen(text));
}
// Terminates the smoke test with one diagnostic.
static void fail(const char* message) {
print("posix_pipe_smoke: ");
print(message);
print("\n");
exit(1);
}
// Requires one descriptor to expose the expected immediate poll bits.
static short poll_now(int fd, short events) {
struct pollfd descriptor;
descriptor.fd = fd;
descriptor.events = events;
descriptor.revents = 0;
int result = poll(&descriptor, 1, 0);
if (result < 0)
fail("poll failed");
return descriptor.revents;
}
// Runs pipe lifetime, readiness, EOF, broken-pipe, and atomic-write checks.
int main(void) {
int descriptors[2];
if (pipe(descriptors) != 0)
fail("pipe failed");
struct stat st;
if (fstat(descriptors[0], &st) != 0 || !S_ISFIFO(st.st_mode))
fail("read endpoint is not FIFO");
if (fstat(descriptors[1], &st) != 0 || !S_ISFIFO(st.st_mode))
fail("write endpoint is not FIFO");
if (poll_now(descriptors[0], POLLIN) & POLLIN)
fail("empty pipe reported readable");
if (!(poll_now(descriptors[1], POLLOUT) & POLLOUT))
fail("empty pipe write endpoint not writable");
if (write(descriptors[1], "abc", 3) != 3)
fail("basic write failed");
if (!(poll_now(descriptors[0], POLLIN) & POLLIN))
fail("pipe data did not become readable");
char buffer[4] = {0, 0, 0, 0};
if (read(descriptors[0], buffer, 3) != 3 || strcmp(buffer, "abc") != 0)
fail("basic read failed");
if (fcntl(descriptors[0], F_SETFL, O_NONBLOCK) != 0)
fail("read F_SETFL O_NONBLOCK failed");
errno = 0;
if (read(descriptors[0], buffer, 1) != -1 || errno != EAGAIN)
fail("empty nonblocking read did not return EAGAIN");
int writerAlias = dup(descriptors[1]);
if (writerAlias < 0)
fail("writer dup failed");
if (close(descriptors[1]) != 0)
fail("close original writer failed");
if (poll_now(descriptors[0], POLLIN | POLLHUP) & POLLHUP)
fail("HUP published before final writer closed");
if (close(writerAlias) != 0)
fail("close final writer failed");
short eofEvents = poll_now(descriptors[0], POLLIN | POLLHUP);
if ((eofEvents & (POLLIN | POLLHUP)) != (POLLIN | POLLHUP))
fail("EOF readiness transition missing");
if (read(descriptors[0], buffer, 1) != 0)
fail("final writer close did not produce EOF");
if (close(descriptors[0]) != 0)
fail("close read endpoint failed");
if (pipe(descriptors) != 0)
fail("second pipe failed");
if (close(descriptors[0]) != 0)
fail("close second read endpoint failed");
sighandler_t previousPipe = signal(SIGPIPE, SIG_IGN);
if (previousPipe == SIG_ERR)
fail("ignore SIGPIPE failed");
errno = 0;
if (write(descriptors[1], "x", 1) != -1 || errno != EPIPE)
fail("write without readers did not return EPIPE");
if (signal(SIGPIPE, previousPipe) == SIG_ERR)
fail("restore SIGPIPE failed");
if (close(descriptors[1]) != 0)
fail("close broken writer failed");
if (pipe(descriptors) != 0)
fail("atomic pipe failed");
if (fcntl(descriptors[1], F_SETFL, O_NONBLOCK) != 0)
fail("write F_SETFL O_NONBLOCK failed");
char atomic[PIPE_BUF];
memset(atomic, 'A', sizeof(atomic));
for (;;) {
errno = 0;
ssize_t written = write(descriptors[1], atomic, sizeof(atomic));
if (written == (ssize_t)sizeof(atomic))
continue;
if (written == -1 && errno == EAGAIN)
break;
fail("PIPE_BUF write was partially committed");
}
if (poll_now(descriptors[1], POLLOUT) & POLLOUT)
fail("full pipe still reported writable");
if (!(poll_now(descriptors[0], POLLIN) & POLLIN))
fail("full pipe read endpoint not readable");
if (close(descriptors[0]) != 0 || close(descriptors[1]) != 0)
fail("atomic pipe close failed");
print("posix_pipe_smoke: PASS\n");
return 0;
}