blob: 30c842527a6183520b989f0c5b753e3450e6a6c3 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <threads.h>
#include <unistd.h>
#include <zircon/syscalls.h>
#include <lib/fdio/io.h>
#include <lib/fdio/util.h>
#include <unittest/unittest.h>
bool close_test(void) {
BEGIN_TEST;
zx_handle_t h = ZX_HANDLE_INVALID;
ASSERT_EQ(ZX_OK, zx_event_create(0u, &h), "zx_event_create() failed");
ASSERT_NE(h, ZX_HANDLE_INVALID, "");
// fdio_handle_fd() with shared_handle = true
int fd = fdio_handle_fd(h, ZX_USER_SIGNAL_0, ZX_USER_SIGNAL_1, true);
ASSERT_GT(fd, 0, "fdio_handle_fd() failed");
close(fd);
// close(fd) has not closed the wrapped handle
EXPECT_EQ(ZX_OK, zx_object_signal(h, 0, ZX_USER_SIGNAL_0),
"zx_object_signal() should succeed");
// fdio_handle_fd() with shared_handle = false
fd = fdio_handle_fd(h, ZX_USER_SIGNAL_0, ZX_USER_SIGNAL_1, false);
ASSERT_GT(fd, 0, "fdio_handle_fd() failed");
close(fd);
// close(fd) has closed the wrapped handle
EXPECT_EQ(ZX_ERR_BAD_HANDLE, zx_object_signal(h, 0, ZX_USER_SIGNAL_0),
"zx_object_signal() should fail");
END_TEST;
}
bool pipe_test(void) {
BEGIN_TEST;
int fds[2];
int status = pipe(fds);
ASSERT_EQ(status, 0, "pipe() failed");
status = fcntl(fds[0], F_GETFL);
ASSERT_EQ(status, 0, "fcntl(F_GETFL) failed");
status |= O_NONBLOCK;
status = fcntl(fds[0], F_SETFL, status);
ASSERT_EQ(status, 0, "fcntl(FSETFL, O_NONBLOCK) failed");
status = fcntl(fds[0], F_GETFL);
ASSERT_EQ(status, O_NONBLOCK, "fcntl(F_GETFL) failed");
int message[2] = {-6, 1};
ssize_t written = write(fds[1], message, sizeof(message));
ASSERT_GE(written, 0, "write() failed");
ASSERT_EQ((uint32_t)written, sizeof(message),
"write() should have written the whole message.");
int available = 0;
status = ioctl(fds[0], FIONREAD, &available);
ASSERT_GE(status, 0, "ioctl(FIONREAD) failed");
EXPECT_EQ((uint32_t)available, sizeof(message),
"ioctl(FIONREAD) queried wrong number of bytes");
int read_message[2];
ssize_t bytes_read = read(fds[0], read_message, sizeof(read_message));
ASSERT_GE(bytes_read, 0, "read() failed");
ASSERT_EQ((uint32_t)bytes_read, sizeof(read_message),
"read() read wrong number of bytes");
EXPECT_EQ(read_message[0], message[0], "read() read wrong value");
EXPECT_EQ(read_message[1], message[1], "read() read wrong value");
END_TEST;
}
int write_thread(void* arg) {
// Sleep to try to ensure the write happens after the poll.
zx_nanosleep(ZX_MSEC(5));
int message[2] = {-6, 1};
ssize_t written = write(*(int*)arg, message, sizeof(message));
ASSERT_GE(written, 0, "write() failed");
ASSERT_EQ((uint32_t)written, sizeof(message),
"write() should have written the whole message.");
return 0;
}
bool ppoll_test_handler(struct timespec* timeout) {
BEGIN_TEST;
int fds[2];
int status = pipe(fds);
ASSERT_EQ(status, 0, "pipe() failed");
thrd_t t;
int thrd_create_result = thrd_create(&t, write_thread, &fds[1]);
ASSERT_EQ(thrd_create_result, thrd_success, "create blocking send thread");
struct pollfd poll_fds[1] = {{fds[0], POLLIN, 0}};
int ppoll_result = ppoll(poll_fds, 1, timeout, NULL);
EXPECT_EQ(1, ppoll_result, "didn't read anything");
ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking send thread");
END_TEST;
}
bool ppoll_negative_test(void) {
struct timespec timeout_ts = {-1, -1};
return ppoll_test_handler(&timeout_ts);
}
bool ppoll_null_test(void) {
return ppoll_test_handler(NULL);
}
bool ppoll_overflow_test(void) {
unsigned int nanoseconds_in_seconds = 1000000000;
struct timespec timeout_ts = {UINT64_MAX / nanoseconds_in_seconds, UINT64_MAX % nanoseconds_in_seconds};
return ppoll_test_handler(&timeout_ts);
}
bool ppoll_immediate_timeout_test(void) {
BEGIN_TEST;
int fds[2];
int status = pipe(fds);
ASSERT_EQ(status, 0, "pipe() failed");
struct timespec timeout = {0, 0};
struct pollfd poll_fds[1] = {{fds[0], POLLIN, 0}};
int ppoll_result = ppoll(poll_fds, 1, &timeout, NULL);
EXPECT_EQ(0, ppoll_result, "no fds should be readable");
END_TEST;
}
bool transfer_fd_test(void) {
BEGIN_TEST;
int fds[2];
int status = pipe(fds);
ASSERT_EQ(status, 0, "pipe() failed");
// Make pipe nonblocking, write message
status |= O_NONBLOCK;
status = fcntl(fds[0], F_SETFL, status);
ASSERT_EQ(status, 0, "fcntl(FSETFL, O_NONBLOCK) failed");
int message[2] = {-6, 1};
ssize_t written = write(fds[1], message, sizeof(message));
ASSERT_GE(written, 0, "write() failed");
ASSERT_EQ((uint32_t)written, sizeof(message),
"write() should have written the whole message.");
// fd --> handles
zx_handle_t handles[FDIO_MAX_HANDLES];
uint32_t types[FDIO_MAX_HANDLES];
zx_status_t r = fdio_transfer_fd(fds[0], 0, handles, types);
ASSERT_GT(r, 0, "failed to transfer fds to handles");
// handles --> fd
ASSERT_EQ(fdio_create_fd(handles, types, r, &fds[0]), ZX_OK,
"failed to transfer handles to fds");
// Read message
int read_message[2];
ssize_t bytes_read = read(fds[0], read_message, sizeof(read_message));
ASSERT_GE(bytes_read, 0, "read() failed");
ASSERT_EQ((uint32_t)bytes_read, sizeof(read_message),
"read() read wrong number of bytes");
EXPECT_EQ(read_message[0], message[0], "read() read wrong value");
EXPECT_EQ(read_message[1], message[1], "read() read wrong value");
END_TEST;
}
bool transfer_device_test(void) {
BEGIN_TEST;
int fd = open("/dev/zero", O_RDONLY);
ASSERT_GE(fd, 0, "Failed to open /dev/zero");
// fd --> handles
zx_handle_t handles[FDIO_MAX_HANDLES];
uint32_t types[FDIO_MAX_HANDLES];
zx_status_t r = fdio_transfer_fd(fd, 0, handles, types);
ASSERT_GT(r, 0, "failed to transfer fds to handles");
// handles --> fd
ASSERT_EQ(fdio_create_fd(handles, types, r, &fd), ZX_OK,
"failed to transfer handles to fds");
ASSERT_EQ(close(fd), 0, "Failed to close fd");
END_TEST;
}
BEGIN_TEST_CASE(fdio_handle_fd_test)
RUN_TEST(close_test);
RUN_TEST(pipe_test);
RUN_TEST(ppoll_negative_test);
RUN_TEST(ppoll_null_test);
RUN_TEST(ppoll_overflow_test);
RUN_TEST(ppoll_immediate_timeout_test);
RUN_TEST(transfer_fd_test);
RUN_TEST(transfer_device_test);
END_TEST_CASE(fdio_handle_fd_test)