| /* |
| * Copyright 2017 The Emscripten Authors. All rights reserved. |
| * Emscripten is available under two separate licenses, the MIT license and the |
| * University of Illinois/NCSA Open Source License. Both these licenses can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <unistd.h> |
| #include <stdbool.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <poll.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <string.h> |
| #include <stdio.h> |
| |
| unsigned char buf[1 << 16]; |
| |
| #define MAX(a, b) (((a) >= (b)) ? (a) : (b)) |
| |
| |
| // This test program relies on the simplest read/write behavior when |
| // all the data can be read/written in one call. |
| |
| void test_write(int fd1, unsigned char *ch, int size) { |
| memset(buf, 0, sizeof buf); |
| for (int i = 0; i < size; ++i) { |
| buf[i] = (*ch)++; |
| } |
| assert(write(fd1, buf, size) == size); |
| } |
| |
| void test_read(int fd0, unsigned char *ch, int size) { |
| memset(buf, 0, sizeof buf); |
| assert(read(fd0, buf, size) == size); |
| for (int i = 0; i < sizeof buf; ++i) { |
| unsigned char correct_ch = (i < size) ? (*ch)++ : 0; |
| assert(buf[i] == correct_ch); |
| } |
| } |
| |
| // test_select and test_poll perform the exact same actions/assertions but |
| // with two different system calls. They should always give the same |
| // result. |
| |
| void test_select(int *fd, bool data_available) { |
| fd_set rfds; |
| FD_ZERO(&rfds); |
| FD_SET(fd[0], &rfds); |
| FD_SET(fd[1], &rfds); |
| |
| fd_set wfds; |
| FD_ZERO(&wfds); |
| FD_SET(fd[0], &wfds); |
| FD_SET(fd[1], &wfds); |
| |
| // Don't block at all |
| struct timeval tv = { 0, 0}; |
| int maxfd = MAX(fd[0], fd[1]) + 1; |
| int ret = select(maxfd, &rfds, &wfds, NULL, &tv); |
| |
| if (data_available) { |
| assert(ret == 2); |
| assert(FD_ISSET(fd[0], &rfds)); |
| } else { |
| assert(ret == 1); |
| assert(!FD_ISSET(fd[0], &rfds)); |
| } |
| |
| assert(FD_ISSET(fd[1], &wfds)); |
| } |
| |
| void test_poll(int *fd, int data_available) { |
| struct pollfd pfds[2] = {0}; |
| pfds[0].fd = fd[0]; |
| pfds[0].events = POLLIN | POLLOUT; |
| pfds[1].fd = fd[1]; |
| pfds[1].events = POLLIN | POLLOUT; |
| |
| int ret = poll(pfds, 2, 0); |
| if (data_available) { |
| assert(ret == 2); |
| assert(pfds[0].revents == POLLIN); |
| } else { |
| assert(ret == 1); |
| assert(pfds[0].revents == 0); |
| } |
| assert(pfds[1].revents == POLLOUT); |
| |
| // select should report the exact same status. |
| test_select(fd, data_available); |
| } |
| |
| void test_most() { |
| int fd[2]; |
| unsigned char wchar = 0; |
| unsigned char rchar = 0; |
| |
| assert(pipe(fd) == 0); |
| |
| // Test that pipe is statable |
| struct stat st; |
| assert(fstat(fd[0], &st) == 0); |
| |
| // Test that pipe is not seekable |
| |
| memset(buf, 0, sizeof buf); |
| assert(write(fd[1], buf, 128) == 128); |
| assert(lseek(fd[0], 0, SEEK_CUR) == -1); |
| assert(errno == ESPIPE); |
| assert(lseek(fd[1], 0, SEEK_CUR) == -1); |
| assert(errno == ESPIPE); |
| assert(read(fd[0], buf, sizeof buf) == 128); |
| |
| // Now pipe is empty |
| // Test interleaved writing and reading of different buffer sizes |
| |
| // write about 40 Kb of data |
| for (int i = 1; i < 200; ++i) { |
| test_write(fd[1], &wchar, i + 2); |
| test_poll(fd, true); |
| test_read (fd[0], &rchar, i); |
| test_poll(fd, true); |
| test_write(fd[1], &wchar, i + 1); |
| test_poll(fd, true); |
| test_read (fd[0], &rchar, i + 3); |
| test_poll(fd, false); |
| } |
| |
| // Test reading when there is less data available than the read buffer size |
| |
| assert(write(fd[1], buf, 10) == 10); |
| assert(read(fd[0], buf, sizeof buf) == 10); |
| |
| // Write total of 1 Mb of data in small chunks |
| // The pipe should not overflow |
| |
| int bytes_to_write = 1 << 20; |
| while (bytes_to_write > 0) { |
| test_write(fd[1], &wchar, sizeof buf); |
| test_read (fd[0], &rchar, sizeof buf); |
| bytes_to_write -= sizeof buf; |
| } |
| |
| // Write large chunks of data (supposed to be larger than one internal buffer) |
| test_write(fd[1], &wchar, 123); |
| test_write(fd[1], &wchar, (1 << 15) + 321); |
| test_write(fd[1], &wchar, 456); |
| test_read(fd[0], &rchar, 456); |
| test_read(fd[0], &rchar, (1 << 15) + 123); |
| test_read(fd[0], &rchar, 321); |
| |
| #ifndef WASMFS // TODO: fcntl in WASMFS |
| // Test non-blocking read from empty pipe |
| assert(fcntl(fd[0], F_SETFL, O_NONBLOCK) == 0); |
| assert(read(fd[0], buf, sizeof buf) == -1); |
| assert(errno == EAGAIN); |
| #endif |
| |
| // Normal operations still work in non-blocking mode |
| test_poll(fd, false); |
| test_write(fd[1], &wchar, 10); |
| test_poll(fd, true); |
| test_read (fd[0], &rchar, 10); |
| test_poll(fd, false); |
| |
| // Clear buffer |
| memset(buf, 0, sizeof(buf)); |
| // Test closing pipes. |
| // Write to pipe |
| assert(write(fd[1], "XXXX", 4) == 4); |
| // Close write end |
| assert(close(fd[1]) == 0); |
| // This write should fail |
| assert(write(fd[1], "YYYY", 4) == -1); |
| // The error number is EBADF |
| assert(errno == EBADF); |
| |
| // read from the other end of the pipe |
| assert(read(fd[0], buf, 5) == 4); |
| // We should have read what we wrote to the other end |
| assert(memcmp(buf, "XXXX", 4) == 0); |
| |
| // Close the read end |
| assert(close(fd[0]) == 0); |
| // Now reading should return an error |
| assert(read(fd[0], buf, 5) == -1); |
| // The error number is EBADF |
| assert(errno == EBADF); |
| |
| puts("done"); |
| } |
| |
| void test_redirect_stderr_to_pipe() { |
| int stderrfd = fileno(stderr); |
| int pipefd[2]; |
| int original_fd = dup(stderrfd); // duplicate stderr to original_fd, and original_fd is used to restore stderr later |
| assert(original_fd >= 0); |
| assert(pipe(pipefd) == 0); |
| |
| int read_end_fd = pipefd[0]; |
| int write_end_fd = pipefd[1]; |
| |
| assert(dup2(write_end_fd, stderrfd) == stderrfd); // now things write to fd(stderr) is redirected to write_end_fd |
| assert(close(write_end_fd) == 0); // close the write end of the pipe after duplicating |
| |
| assert(write(stderrfd, "xyz", 3) == 3); // write to the stderr, expected to be read from pipe |
| |
| assert(dup2(original_fd, stderrfd) == stderrfd); // restore fd (stderr) to its original state |
| assert(close(original_fd) == 0); |
| |
| char buffer[10] = {0}; |
| assert(read(read_end_fd, buffer, 10) == 3); |
| assert(strcmp(buffer, "xyz") == 0); |
| assert(close(read_end_fd) == 0); // Close the read end of the pipe |
| puts("done"); |
| } |
| |
| int main() { |
| test_most(); |
| test_redirect_stderr_to_pipe(); |
| return 0; |
| } |