blob: 60f120a42891c3ff56a987eefa92cd5a411d7559 [file] [log] [blame]
// Copyright 2021 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 <fidl/fuchsia.io/cpp/wire_types.h>
#include <lib/zx/socket.h>
#include <lib/zxio/cpp/inception.h>
#include <lib/zxio/zxio.h>
#include <zircon/rights.h>
#include <memory>
#include <zxtest/zxtest.h>
namespace {
TEST(Pipe, Create) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
zxio_node_attributes_t attr = {.has = {.object_type = true}};
ASSERT_OK(zxio_attr_get(io, &attr));
EXPECT_EQ(ZXIO_OBJECT_TYPE_PIPE, attr.object_type);
ASSERT_STATUS(ZX_ERR_NOT_SUPPORTED, zxio_attr_set(io, &attr));
zxio_destroy(io);
}
TEST(Pipe, CreateWithAllocator) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
auto zxio_allocator = [](zxio_object_type_t type, zxio_storage_t** out_storage,
void** out_context) {
EXPECT_EQ(type, ZXIO_OBJECT_TYPE_PIPE);
*out_storage = new zxio_storage_t;
*out_context = *out_storage;
return ZX_OK;
};
void* context = nullptr;
ASSERT_OK(zxio_create_with_allocator(std::move(socket0), zxio_allocator, &context));
std::unique_ptr<zxio_storage_t> storage(static_cast<zxio_storage_t*>(context));
zxio_t* io = &storage->io;
zxio_destroy(io);
}
TEST(Pipe, FlagsGetDefault) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
// By default, socket supports IO (Read + Write).
uint64_t raw_flags{};
ASSERT_OK(zxio_flags_get(io, &raw_flags));
fuchsia_io::wire::Flags flags{raw_flags};
EXPECT_TRUE(flags & fuchsia_io::wire::Flags::kPermReadBytes);
EXPECT_TRUE(flags & fuchsia_io::wire::Flags::kPermWriteBytes);
}
TEST(Pipe, FlagsGetReadOnly) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx::socket duplicate_readonly_socket;
ASSERT_OK(socket0.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ, &duplicate_readonly_socket));
zxio_storage_t storage;
ASSERT_OK(zxio_create(duplicate_readonly_socket.release(), &storage));
zxio_t* io = &storage.io;
uint64_t raw_flags{};
ASSERT_OK(zxio_flags_get(io, &raw_flags));
fuchsia_io::wire::Flags flags{raw_flags};
EXPECT_TRUE(flags & fuchsia_io::wire::Flags::kPermReadBytes);
EXPECT_FALSE(flags & fuchsia_io::wire::Flags::kPermWriteBytes);
}
TEST(Pipe, FlagsGetNoIO) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx::socket duplicate_socket;
ASSERT_OK(socket0.duplicate(ZX_RIGHTS_BASIC, &duplicate_socket));
zxio_storage_t storage;
ASSERT_OK(zxio_create(duplicate_socket.release(), &storage));
zxio_t* io = &storage.io;
uint64_t raw_flags{};
ASSERT_OK(zxio_flags_get(io, &raw_flags));
fuchsia_io::wire::Flags flags{raw_flags};
EXPECT_FALSE(flags & fuchsia_io::wire::Flags::kPermReadBytes);
EXPECT_FALSE(flags & fuchsia_io::wire::Flags::kPermWriteBytes);
}
TEST(Pipe, FlagsSetWithValidInputFlags) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
fuchsia_io::wire::Flags flags =
fuchsia_io::wire::Flags::kPermReadBytes | fuchsia_io::wire::Flags::kPermWriteBytes;
ASSERT_OK(zxio_flags_set(io, uint64_t{flags}));
}
TEST(Pipe, FlagsSetWithInvalidInputFlagsIsError) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx::socket duplicate_socket;
ASSERT_OK(socket0.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_WRITE, &duplicate_socket));
zxio_storage_t storage;
ASSERT_OK(zxio_create(duplicate_socket.release(), &storage));
zxio_t* io = &storage.io;
fuchsia_io::wire::Flags flags =
fuchsia_io::wire::Flags::kPermReadBytes | fuchsia_io::wire::Flags::kPermWriteBytes;
EXPECT_STATUS(zxio_flags_set(io, uint64_t{flags}), ZX_ERR_NOT_SUPPORTED);
}
TEST(Pipe, DeprecatedFlagsGetDefault) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
// By default, socket supports IO (Read + Write).
uint32_t raw_flags{};
ASSERT_OK(zxio_deprecated_flags_get(io, &raw_flags));
fuchsia_io::wire::OpenFlags flags{raw_flags};
EXPECT_TRUE(flags & fuchsia_io::wire::OpenFlags::kRightReadable);
EXPECT_TRUE(flags & fuchsia_io::wire::OpenFlags::kRightWritable);
}
TEST(Pipe, DeprecatedFlagsGetReadOnly) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx::socket duplicate_readonly_socket;
ASSERT_OK(socket0.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ, &duplicate_readonly_socket));
zxio_storage_t storage;
ASSERT_OK(zxio_create(duplicate_readonly_socket.release(), &storage));
zxio_t* io = &storage.io;
uint32_t raw_flags{};
ASSERT_OK(zxio_deprecated_flags_get(io, &raw_flags));
fuchsia_io::wire::OpenFlags flags{raw_flags};
EXPECT_TRUE(flags & fuchsia_io::wire::OpenFlags::kRightReadable);
EXPECT_FALSE(flags & fuchsia_io::wire::OpenFlags::kRightWritable);
}
TEST(Pipe, DeprecatedFlagsGetNoIO) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx::socket duplicate_socket;
ASSERT_OK(socket0.duplicate(ZX_RIGHTS_BASIC, &duplicate_socket));
zxio_storage_t storage;
ASSERT_OK(zxio_create(duplicate_socket.release(), &storage));
zxio_t* io = &storage.io;
uint32_t raw_flags{};
ASSERT_OK(zxio_deprecated_flags_get(io, &raw_flags));
fuchsia_io::wire::OpenFlags flags{raw_flags};
EXPECT_FALSE(flags & fuchsia_io::wire::OpenFlags::kRightReadable);
EXPECT_FALSE(flags & fuchsia_io::wire::OpenFlags::kRightWritable);
}
TEST(Pipe, DeprecatedFlagsSetWithValidInputFlags) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
fuchsia_io::wire::OpenFlags flags =
fuchsia_io::wire::OpenFlags::kRightReadable | fuchsia_io::wire::OpenFlags::kRightWritable;
ASSERT_OK(zxio_deprecated_flags_set(io, static_cast<uint32_t>(flags)));
}
TEST(Pipe, DeprecatedFlagsSetWithInvalidInputFlagsIsError) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx::socket duplicate_socket;
ASSERT_OK(socket0.duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_WRITE, &duplicate_socket));
zxio_storage_t storage;
ASSERT_OK(zxio_create(duplicate_socket.release(), &storage));
zxio_t* io = &storage.io;
fuchsia_io::wire::OpenFlags flags =
fuchsia_io::wire::OpenFlags::kRightReadable | fuchsia_io::wire::OpenFlags::kRightWritable;
EXPECT_STATUS(zxio_deprecated_flags_set(io, static_cast<uint32_t>(flags)), ZX_ERR_NOT_SUPPORTED);
}
TEST(Pipe, Basic) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
const uint32_t data = 0x41424344;
size_t actual = 0u;
ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, sizeof(data));
uint32_t buffer = 0u;
ASSERT_OK(zxio_read(io, &buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(actual, sizeof(buffer));
EXPECT_EQ(buffer, data);
zxio_destroy(io);
}
TEST(Pipe, GetReadBufferAvailable) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
size_t available = 0;
ASSERT_OK(zxio_get_read_buffer_available(io, &available));
EXPECT_EQ(0u, available);
const uint32_t data = 0x41424344;
size_t actual = 0u;
ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, sizeof(data));
ASSERT_OK(zxio_get_read_buffer_available(io, &available));
EXPECT_EQ(sizeof(data), available);
uint32_t buffer = 0u;
ASSERT_OK(zxio_read(io, &buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(actual, sizeof(buffer));
ASSERT_OK(zxio_get_read_buffer_available(io, &available));
EXPECT_EQ(0u, available);
zxio_destroy(io);
}
// Test that after shutting a pipe endpoint down for reading that reading from
// that endpoint and writing to the peer endpoint fail.
TEST(Pipe, ShutdownRead) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
const uint32_t data = 0x41424344;
// Write some data before shutting down reading on the peer. Should succeed.
size_t actual = 0u;
EXPECT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, 4u);
actual = 0u;
int16_t out_code;
EXPECT_OK(zxio_shutdown(io, ZXIO_SHUTDOWN_OPTIONS_READ, &out_code));
EXPECT_EQ(out_code, 0);
// We shouldn't be able to write any more data into the peer.
EXPECT_STATUS(socket1.write(0u, &data, sizeof(data), &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
actual = 0u;
char buf[4] = {};
// We should be able to read data written into the pipe before reading was
// disabled.
EXPECT_OK(zxio_read(io, buf, sizeof(buf), 0u, &actual));
EXPECT_EQ(actual, 4u);
actual = 0u;
EXPECT_STATUS(zxio_read(io, buf, sizeof(buf), 0u, &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
actual = 0u;
zxio_destroy(io);
}
// Test that after shutting a pipe endpoint down for writing that writing to
// that endpoint and reading from the peer endpoint fail.
TEST(Pipe, ShutdownWrite) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
int16_t out_code;
EXPECT_OK(zxio_shutdown(io, ZXIO_SHUTDOWN_OPTIONS_WRITE, &out_code));
EXPECT_EQ(out_code, 0);
size_t actual = 0u;
char buf[4] = {};
EXPECT_STATUS(socket1.read(0u, &buf, sizeof(buf), &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
actual = 0u;
const uint32_t data = 0x41424344;
EXPECT_STATUS(zxio_write(io, &data, sizeof(data), 0u, &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
zxio_destroy(io);
}
// Test that after shutting a pipe endpoint down for reading and writing that
// reading or writing to either endpoint fails.
TEST(Pipe, ShutdownReadWrite) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zxio_storage_t storage;
ASSERT_OK(zxio_create(socket0.release(), &storage));
zxio_t* io = &storage.io;
const uint32_t data = 0x41424344;
// Write some data before shutting down the peer. Should succeed.
size_t actual = 0u;
EXPECT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, 4u);
actual = 0u;
int16_t out_code;
EXPECT_OK(zxio_shutdown(io, ZXIO_SHUTDOWN_OPTIONS_READ | ZXIO_SHUTDOWN_OPTIONS_WRITE, &out_code));
EXPECT_EQ(out_code, 0);
char buf[4] = {};
EXPECT_STATUS(socket1.read(0u, &buf, sizeof(buf), &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
actual = 0u;
EXPECT_STATUS(socket1.write(0u, &data, sizeof(data), &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
actual = 0u;
// We should be able to read data written into the pipe before reading was
// disabled.
EXPECT_OK(zxio_read(io, buf, sizeof(buf), 0u, &actual));
EXPECT_EQ(actual, 4u);
actual = 0u;
EXPECT_STATUS(zxio_read(io, buf, sizeof(buf), 0u, &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
actual = 0u;
EXPECT_STATUS(zxio_write(io, &data, sizeof(data), 0u, &actual), ZX_ERR_BAD_STATE);
EXPECT_EQ(actual, 0u);
zxio_destroy(io);
}
} // namespace