blob: bfabd9303939051f61706112114a2fe1937599c2 [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_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <lib/zxio/cpp/create_with_type.h>
#include <lib/zxio/types.h>
#include <lib/zxio/zxio.h>
#include <zircon/syscalls/types.h>
#include <zircon/types.h>
#include <zxtest/zxtest.h>
#include "sdk/lib/zxio/tests/test_file_server_base.h"
#include "sdk/lib/zxio/tests/test_node_server.h"
TEST(Create, InvalidArgs) {
ASSERT_EQ(zxio_create(ZX_HANDLE_INVALID, nullptr), ZX_ERR_INVALID_ARGS);
zxio_storage_t storage;
ASSERT_EQ(zxio_create(ZX_HANDLE_INVALID, &storage), ZX_ERR_INVALID_ARGS);
zx::channel channel0, channel1;
ASSERT_OK(zx::channel::create(0u, &channel0, &channel1));
ASSERT_EQ(zxio_create(channel0.release(), nullptr), ZX_ERR_INVALID_ARGS);
// Make sure that the handle is closed.
zx_signals_t pending = 0;
ASSERT_EQ(channel1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_CHANNEL_PEER_CLOSED, ZX_CHANNEL_PEER_CLOSED);
}
TEST(Create, NotSupported) {
zx::event event;
ASSERT_OK(zx::event::create(0u, &event));
zxio_storage_t storage;
ASSERT_EQ(zxio_create(event.release(), &storage), ZX_ERR_NOT_SUPPORTED);
zxio_t* io = &storage.io;
zx::handle handle;
ASSERT_OK(zxio_release(io, handle.reset_and_get_address()));
ASSERT_OK(zxio_close(io));
zx_info_handle_basic_t info = {};
ASSERT_OK(handle.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr));
EXPECT_EQ(info.type, ZX_OBJ_TYPE_EVENT);
}
// Tests that calling zxio_create_with_type() with an invalid storage pointer
// still closes all the handles known for the type.
TEST(CreateWithTypeInvalidStorageClosesHandles, DatagramSocket) {
zx::eventpair event0, event1;
ASSERT_OK(zx::eventpair::create(0, &event0, &event1));
zx::channel channel0, channel1;
ASSERT_OK(zx::channel::create(0, &channel0, &channel1));
ASSERT_EQ(zxio_create_with_type(nullptr, ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET, event0.release(),
channel0.release()),
ZX_ERR_INVALID_ARGS);
zx_signals_t pending = 0;
ASSERT_EQ(event1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_EVENTPAIR_PEER_CLOSED, ZX_EVENTPAIR_PEER_CLOSED);
ASSERT_EQ(channel1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_CHANNEL_PEER_CLOSED, ZX_CHANNEL_PEER_CLOSED);
}
TEST(CreateWithTypeInvalidStorageClosesHandles, Directory) {
zx::channel channel0, channel1;
ASSERT_OK(zx::channel::create(0, &channel0, &channel1));
ASSERT_EQ(zxio_create_with_type(nullptr, ZXIO_OBJECT_TYPE_DIR, channel0.release()),
ZX_ERR_INVALID_ARGS);
zx_signals_t pending = 0;
ASSERT_EQ(channel1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_CHANNEL_PEER_CLOSED, ZX_CHANNEL_PEER_CLOSED);
}
TEST(CreateWithTypeInvalidStorageClosesHandles, Node) {
zx::channel channel0, channel1;
ASSERT_OK(zx::channel::create(0, &channel0, &channel1));
ASSERT_EQ(zxio_create_with_type(nullptr, ZXIO_OBJECT_TYPE_NODE, channel0.release()),
ZX_ERR_INVALID_ARGS);
zx_signals_t pending = 0;
ASSERT_EQ(channel1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_CHANNEL_PEER_CLOSED, ZX_CHANNEL_PEER_CLOSED);
}
TEST(CreateWithTypeInvalidStorageClosesHandles, StreamSocket) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &socket0, &socket1));
zx::channel channel0, channel1;
ASSERT_OK(zx::channel::create(0, &channel0, &channel1));
ASSERT_EQ(zxio_create_with_type(nullptr, ZXIO_OBJECT_TYPE_STREAM_SOCKET, socket0.release(),
channel0.release(), nullptr),
ZX_ERR_INVALID_ARGS);
zx_signals_t pending = 0;
ASSERT_EQ(socket1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_SOCKET_PEER_CLOSED, ZX_SOCKET_PEER_CLOSED);
ASSERT_EQ(channel1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_CHANNEL_PEER_CLOSED, ZX_CHANNEL_PEER_CLOSED);
}
TEST(CreateWithTypeInvalidStorageClosesHandles, Pipe) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &socket0, &socket1));
ASSERT_EQ(zxio_create_with_type(nullptr, ZXIO_OBJECT_TYPE_PIPE, socket0.release()),
ZX_ERR_INVALID_ARGS);
// Make sure that the handle is closed.
zx_signals_t pending = 0;
ASSERT_EQ(socket1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_SOCKET_PEER_CLOSED, ZX_SOCKET_PEER_CLOSED);
}
TEST(CreateWithTypeInvalidStorageClosesHandles, RawSocket) {
zx::eventpair event0, event1;
ASSERT_OK(zx::eventpair::create(0, &event0, &event1));
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(ZX_SOCKET_STREAM, &socket0, &socket1));
ASSERT_EQ(zxio_create_with_type(nullptr, ZXIO_OBJECT_TYPE_RAW_SOCKET, event0.release(),
socket0.release()),
ZX_ERR_INVALID_ARGS);
// Make sure that the handle is closed.
zx_signals_t pending = 0;
ASSERT_EQ(event1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_EVENTPAIR_PEER_CLOSED, ZX_EVENTPAIR_PEER_CLOSED);
ASSERT_EQ(socket1.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_SOCKET_PEER_CLOSED, ZX_SOCKET_PEER_CLOSED);
}
TEST(CreateWithTypeInvalidStorageClosesHandles, Vmo) {
zx::vmo parent, child;
ASSERT_OK(zx::vmo::create(4096, 0, &parent));
ASSERT_OK(parent.create_child(ZX_VMO_CHILD_SLICE, 0, 4096, &child));
zx::stream stream;
ASSERT_OK(zx::stream::create(0, child, 0, &stream));
ASSERT_EQ(zxio_create_with_type(nullptr, ZXIO_OBJECT_TYPE_VMO, child.release(), stream.release()),
ZX_ERR_INVALID_ARGS);
// Make sure that the handle is closed.
zx_signals_t pending = 0;
ASSERT_EQ(parent.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_VMO_ZERO_CHILDREN, ZX_VMO_ZERO_CHILDREN);
}
template <typename NodeServer>
class CreateTestBase : public zxtest::Test {
protected:
CreateTestBase() : control_loop_(&kAsyncLoopConfigNoAttachToCurrentThread) {}
using ProtocolType = typename NodeServer::_EnclosingProtocol;
void SetUp() final {
zx::status node_ends = fidl::CreateEndpoints<ProtocolType>();
ASSERT_OK(node_ends.status_value());
node_client_end_ = std::move(node_ends->client);
node_server_end_ = std::move(node_ends->server);
}
void TearDown() final { control_loop_.Shutdown(); }
zx::channel TakeClientChannel() { return node_client_end_.TakeChannel(); }
void StartServerThread() {
fidl::BindServer(control_loop_.dispatcher(), std::move(node_server_end_), &node_server_);
control_loop_.StartThread("control");
}
zxio_t* zxio() { return &storage_.io; }
zxio_storage_t* storage() { return &storage_; }
void SendOnOpenEvent(fuchsia_io::wire::NodeInfo node_info) {
ASSERT_OK(fidl::WireSendEvent(node_server_end_)->OnOpen(ZX_OK, std::move(node_info)));
}
NodeServer& node_server() { return node_server_; }
private:
zxio_storage_t storage_;
fidl::ClientEnd<ProtocolType> node_client_end_;
fidl::ServerEnd<ProtocolType> node_server_end_;
NodeServer node_server_;
async::Loop control_loop_;
};
using CreateTest = CreateTestBase<zxio_tests::DescribeNodeServer>;
using CreateWithOnOpenTest = CreateTestBase<zxio_tests::CloseOnlyNodeServer>;
using DescribeRequestView = fidl::WireServer<::fuchsia_io::Node>::DescribeRequestView;
using DescribeCompleter = fidl::WireServer<::fuchsia_io::Node>::DescribeCompleter;
TEST_F(CreateTest, Device) {
node_server().set_describe_function(
[](DescribeRequestView request, DescribeCompleter::Sync& completer) mutable {
completer.Reply(fuchsia_io::wire::NodeInfo::WithDevice({}));
});
StartServerThread();
ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
}
TEST_F(CreateWithOnOpenTest, Device) {
SendOnOpenEvent(fuchsia_io::wire::NodeInfo::WithDevice({}));
ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
StartServerThread();
ASSERT_OK(zxio_close(zxio()));
}
class SyncNodeServer : public zxio_tests::DescribeNodeServer {
public:
void Sync(SyncRequestView request, SyncCompleter::Sync& completer) final {
completer.ReplySuccess();
}
};
using CreateDirectoryTest = CreateTestBase<SyncNodeServer>;
TEST_F(CreateDirectoryTest, Directory) {
node_server().set_describe_function(
[](DescribeRequestView request, DescribeCompleter::Sync& completer) mutable {
completer.Reply(fuchsia_io::wire::NodeInfo::WithDirectory({}));
});
StartServerThread();
ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
EXPECT_OK(zxio_sync(zxio()));
ASSERT_OK(zxio_close(zxio()));
}
TEST_F(CreateDirectoryTest, DirectoryWithType) {
StartServerThread();
ASSERT_OK(zxio_create_with_type(storage(), ZXIO_OBJECT_TYPE_DIR, TakeClientChannel().release()));
EXPECT_OK(zxio_sync(zxio()));
ASSERT_OK(zxio_close(zxio()));
}
TEST_F(CreateDirectoryTest, DirectoryWithTypeWrapper) {
StartServerThread();
ASSERT_OK(zxio::CreateDirectory(storage(),
fidl::ClientEnd<fuchsia_io::Directory>(TakeClientChannel())));
EXPECT_OK(zxio_sync(zxio()));
ASSERT_OK(zxio_close(zxio()));
}
using CreateDirectoryWithOnOpenTest = CreateTestBase<SyncNodeServer>;
TEST_F(CreateDirectoryWithOnOpenTest, Directory) {
SendOnOpenEvent(fuchsia_io::wire::NodeInfo::WithDirectory({}));
ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
StartServerThread();
EXPECT_OK(zxio_sync(zxio()));
ASSERT_OK(zxio_close(zxio()));
}
class TestFileServerWithDescribe : public zxio_tests::TestReadFileServer {
public:
void set_file(fuchsia_io::wire::FileObject file) { file_ = std::move(file); }
protected:
void Describe(DescribeRequestView request, DescribeCompleter::Sync& completer) final {
completer.Reply(fuchsia_io::wire::NodeInfo::WithFile(
fidl::ObjectView<decltype(file_)>::FromExternal(&file_)));
}
private:
fuchsia_io::wire::FileObject file_;
};
using CreateFileTest = CreateTestBase<TestFileServerWithDescribe>;
TEST_F(CreateFileTest, File) {
zx::event file_event;
ASSERT_OK(zx::event::create(0u, &file_event));
zx::stream stream;
node_server().set_file({
.event = std::move(file_event),
.stream = std::move(stream),
});
StartServerThread();
ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
// Sanity check the zxio by reading some test data from the server.
char buffer[sizeof(zxio_tests::TestReadFileServer::kTestData)];
size_t actual = 0u;
ASSERT_OK(zxio_read(zxio(), buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(sizeof(buffer), actual);
EXPECT_BYTES_EQ(buffer, zxio_tests::TestReadFileServer::kTestData, sizeof(buffer));
}
using CreateFileWithOnOpenTest = CreateTestBase<zxio_tests::TestReadFileServer>;
TEST_F(CreateFileWithOnOpenTest, File) {
zx::event file_event;
ASSERT_OK(zx::event::create(0u, &file_event));
zx::stream stream;
fuchsia_io::wire::FileObject file = {
.event = std::move(file_event),
.stream = std::move(stream),
};
SendOnOpenEvent(
fuchsia_io::wire::NodeInfo::WithFile(fidl::ObjectView<decltype(file)>::FromExternal(&file)));
ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
StartServerThread();
// Sanity check the zxio by reading some test data from the server.
char buffer[sizeof(zxio_tests::TestReadFileServer::kTestData)];
size_t actual = 0u;
ASSERT_OK(zxio_read(zxio(), buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(sizeof(buffer), actual);
EXPECT_BYTES_EQ(buffer, zxio_tests::TestReadFileServer::kTestData, sizeof(buffer));
ASSERT_OK(zxio_close(zxio()));
}
TEST_F(CreateTest, Pipe) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
node_server().set_describe_function(
[socket0 = std::move(socket0)](DescribeRequestView request,
DescribeCompleter::Sync& completer) mutable {
completer.Reply(fuchsia_io::wire::NodeInfo::WithPipe({
.socket = std::move(socket0),
}));
});
StartServerThread();
ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
// Send some data through the kernel socket object and read it through zxio to
// sanity check that the pipe is functional.
int32_t data = 0x1a2a3a4a;
size_t actual = 0u;
ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, sizeof(data));
int32_t buffer = 0;
ASSERT_OK(zxio_read(zxio(), &buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(actual, sizeof(buffer));
EXPECT_EQ(buffer, data);
ASSERT_OK(zxio_close(zxio()));
}
TEST(CreateWithTypeTest, Pipe) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx_info_socket_t info = {};
ASSERT_OK(socket0.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr));
zxio_storage_t storage;
ASSERT_OK(zxio_create_with_type(&storage, ZXIO_OBJECT_TYPE_PIPE, socket0.release(), &info));
zxio_t* zxio = &storage.io;
// Send some data through the kernel socket object and read it through zxio to
// sanity check that the pipe is functional.
int32_t data = 0x1a2a3a4a;
size_t actual = 0u;
ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, sizeof(data));
int32_t buffer = 0;
ASSERT_OK(zxio_read(zxio, &buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(actual, sizeof(buffer));
EXPECT_EQ(buffer, data);
ASSERT_OK(zxio_close(zxio));
}
TEST(CreateWithTypeWrapperTest, Pipe) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
zx_info_socket_t info = {};
ASSERT_OK(socket0.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr));
zxio_storage_t storage;
ASSERT_OK(zxio::CreatePipe(&storage, std::move(socket0), info));
zxio_t* zxio = &storage.io;
// Send some data through the kernel socket object and read it through zxio to
// sanity check that the pipe is functional.
int32_t data = 0x1a2a3a4a;
size_t actual = 0u;
ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, sizeof(data));
int32_t buffer = 0;
ASSERT_OK(zxio_read(zxio, &buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(actual, sizeof(buffer));
EXPECT_EQ(buffer, data);
ASSERT_OK(zxio_close(zxio));
}
TEST_F(CreateWithOnOpenTest, Pipe) {
zx::socket socket0, socket1;
ASSERT_OK(zx::socket::create(0u, &socket0, &socket1));
SendOnOpenEvent(fuchsia_io::wire::NodeInfo::WithPipe({
.socket = std::move(socket0),
}));
ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
StartServerThread();
// Send some data through the kernel socket object and read it through zxio to
// sanity check that the pipe is functional.
int32_t data = 0x1a2a3a4a;
size_t actual = 0u;
ASSERT_OK(socket1.write(0u, &data, sizeof(data), &actual));
EXPECT_EQ(actual, sizeof(data));
int32_t buffer = 0;
ASSERT_OK(zxio_read(zxio(), &buffer, sizeof(buffer), 0u, &actual));
EXPECT_EQ(actual, sizeof(buffer));
EXPECT_EQ(buffer, data);
ASSERT_OK(zxio_close(zxio()));
}
TEST_F(CreateTest, Service) {
node_server().set_describe_function(
[](DescribeRequestView request, DescribeCompleter::Sync& completer) mutable {
completer.Reply(fuchsia_io::wire::NodeInfo::WithService({}));
});
StartServerThread();
ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
ASSERT_OK(zxio_close(zxio()));
}
TEST_F(CreateWithOnOpenTest, Service) {
SendOnOpenEvent(fuchsia_io::wire::NodeInfo::WithService({}));
ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
StartServerThread();
ASSERT_OK(zxio_close(zxio()));
}
TEST_F(CreateTest, Tty) {
zx::eventpair event0, event1;
ASSERT_OK(zx::eventpair::create(0, &event0, &event1));
node_server().set_describe_function(
[event1 = std::move(event1)](DescribeRequestView request,
DescribeCompleter::Sync& completer) mutable {
completer.Reply(fuchsia_io::wire::NodeInfo::WithTty({
.event = std::move(event1),
}));
});
StartServerThread();
ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
// Closing the zxio object should close our eventpair's peer event.
zx_signals_t pending = 0;
ASSERT_EQ(event0.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_NE(pending & ZX_EVENTPAIR_PEER_CLOSED, ZX_EVENTPAIR_PEER_CLOSED, "pending is %u", pending);
ASSERT_OK(zxio_close(zxio()));
ASSERT_EQ(event0.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_EVENTPAIR_PEER_CLOSED, ZX_EVENTPAIR_PEER_CLOSED, "pending is %u", pending);
}
TEST_F(CreateWithOnOpenTest, Tty) {
zx::eventpair event0, event1;
ASSERT_OK(zx::eventpair::create(0, &event0, &event1));
SendOnOpenEvent(fuchsia_io::wire::NodeInfo::WithTty({
.event = std::move(event1),
}));
ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
// Closing the zxio object should close our eventpair's peer event.
zx_signals_t pending = 0;
ASSERT_EQ(event0.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_NE(pending & ZX_EVENTPAIR_PEER_CLOSED, ZX_EVENTPAIR_PEER_CLOSED, "pending is %u", pending);
StartServerThread();
ASSERT_OK(zxio_close(zxio()));
ASSERT_EQ(event0.wait_one(0u, zx::time::infinite_past(), &pending), ZX_ERR_TIMED_OUT);
EXPECT_EQ(pending & ZX_EVENTPAIR_PEER_CLOSED, ZX_EVENTPAIR_PEER_CLOSED, "pending is %u", pending);
}
class TestVmofileServerWithDescribe : public zxio_tests::TestVmofileServer {
public:
void set_vmofile(fuchsia_io::wire::Vmofile vmofile) { vmofile_ = std::move(vmofile); }
protected:
void Describe(DescribeRequestView request, DescribeCompleter::Sync& completer) final {
completer.Reply(fuchsia_io::wire::NodeInfo::WithVmofile(
fidl::ObjectView<decltype(vmofile_)>::FromExternal(&vmofile_)));
}
private:
fuchsia_io::wire::Vmofile vmofile_;
};
using CreateVmofileTest = CreateTestBase<TestVmofileServerWithDescribe>;
TEST_F(CreateVmofileTest, File) {
const uint64_t vmo_size = 5678;
const uint64_t file_start_offset = 1234;
const uint64_t file_length = 345;
const uint64_t offset_within_file = 234;
node_server().set_seek_offset(offset_within_file);
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(vmo_size, 0u, &vmo));
node_server().set_vmofile({
.vmo = std::move(vmo),
.offset = file_start_offset,
.length = file_length,
});
StartServerThread();
ASSERT_OK(zxio_create(TakeClientChannel().release(), storage()));
ASSERT_OK(zxio_close(zxio()));
}
TEST(CreateVmofileWithTypeTest, File) {
const uint64_t vmo_size = 5678;
const uint64_t file_start_offset = 1234;
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(vmo_size, 0u, &vmo));
zx::stream stream;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo, file_start_offset, &stream));
zxio_storage_t storage;
ASSERT_OK(zxio_create_with_type(&storage, ZXIO_OBJECT_TYPE_VMO, vmo.release(), stream.release()));
zxio_t* zxio = &storage.io;
zxio_node_attributes_t attr;
EXPECT_OK(zxio_attr_get(zxio, &attr));
EXPECT_TRUE(attr.has.content_size);
EXPECT_EQ(attr.content_size, vmo_size);
size_t seek_current_offset = 0u;
EXPECT_OK(zxio_seek(zxio, ZXIO_SEEK_ORIGIN_CURRENT, 0, &seek_current_offset));
EXPECT_EQ(static_cast<size_t>(file_start_offset), seek_current_offset);
ASSERT_OK(zxio_close(zxio));
}
TEST(CreateVmofileWithTypeWrapperTest, File) {
const uint64_t vmo_size = 5678;
const uint64_t file_start_offset = 1234;
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(vmo_size, 0u, &vmo));
zx::stream stream;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo, file_start_offset, &stream));
zxio_storage_t storage;
ASSERT_OK(zxio::CreateVmo(&storage, std::move(vmo), std::move(stream)));
zxio_t* zxio = &storage.io;
zxio_node_attributes_t attr;
EXPECT_OK(zxio_attr_get(zxio, &attr));
EXPECT_TRUE(attr.has.content_size);
EXPECT_EQ(attr.content_size, vmo_size);
size_t seek_current_offset = 0u;
EXPECT_OK(zxio_seek(zxio, ZXIO_SEEK_ORIGIN_CURRENT, 0, &seek_current_offset));
EXPECT_EQ(static_cast<size_t>(file_start_offset), seek_current_offset);
ASSERT_OK(zxio_close(zxio));
}
using CreateVmofileWithOnOpenTest = CreateTestBase<zxio_tests::TestVmofileServer>;
TEST_F(CreateVmofileWithOnOpenTest, File) {
const uint64_t vmo_size = 5678;
const uint64_t file_start_offset = 1234;
const uint64_t file_length = 345;
const uint64_t offset_within_file = 234;
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(vmo_size, 0u, &vmo));
fuchsia_io::wire::Vmofile vmofile = {
.vmo = std::move(vmo),
.offset = file_start_offset,
.length = file_length,
};
SendOnOpenEvent(fuchsia_io::wire::NodeInfo::WithVmofile(
fidl::ObjectView<decltype(vmofile)>::FromExternal(&vmofile)));
node_server().set_seek_offset(offset_within_file);
StartServerThread();
ASSERT_OK(zxio_create_with_on_open(TakeClientChannel().release(), storage()));
// Sanity check the zxio object.
zxio_node_attributes_t attr;
EXPECT_OK(zxio_attr_get(zxio(), &attr));
EXPECT_TRUE(attr.has.content_size);
EXPECT_EQ(attr.content_size, file_length);
size_t seek_current_offset = 0u;
EXPECT_OK(zxio_seek(zxio(), ZXIO_SEEK_ORIGIN_CURRENT, 0, &seek_current_offset));
EXPECT_EQ(static_cast<size_t>(offset_within_file), seek_current_offset);
size_t seek_start_offset = 0u;
EXPECT_OK(zxio_seek(zxio(), ZXIO_SEEK_ORIGIN_START, 0, &seek_start_offset));
EXPECT_EQ(0, seek_start_offset);
size_t seek_end_offset = 0u;
EXPECT_OK(zxio_seek(zxio(), ZXIO_SEEK_ORIGIN_END, 0, &seek_end_offset));
EXPECT_EQ(static_cast<size_t>(file_length), seek_end_offset);
ASSERT_OK(zxio_close(zxio()));
}