blob: 524e1d75af8d8721f163e4c2831873c9ebe452f2 [file] [log] [blame]
// Copyright 2020 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.posix.socket/cpp/wire_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/fd.h>
#include <fbl/unique_fd.h>
#include <zxtest/zxtest.h>
namespace {
template <typename Protocol>
class Server final : public fidl::testing::WireTestBase<Protocol> {
public:
using DescribeResponse = typename fidl::WireResponse<typename Protocol::Describe>;
Server(const char* protocol, DescribeResponse response)
: protocol_(protocol), describe_response_(std::move(response)) {}
private:
using Super = fidl::testing::WireTestBase<Protocol>;
void NotImplemented_(const std::string& name, fidl::CompleterBase& completer) final {
ADD_FAILURE("%s should not be called", name.c_str());
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void Close(typename Super::CloseCompleter::Sync& completer) final {
completer.ReplySuccess();
EXPECT_OK(completer.result_of_reply().status());
// FDIO expects the channel to be closed after replying.
completer.Close(ZX_OK);
}
void Query(typename Super::QueryCompleter::Sync& completer) final {
uint8_t* data = reinterpret_cast<uint8_t*>(const_cast<char*>(protocol_.data()));
completer.Reply(fidl::VectorView<uint8_t>::FromExternal(data, protocol_.size()));
}
void Describe(typename Super::DescribeCompleter::Sync& completer) final {
ASSERT_TRUE(describe_response_.has_value(), "Describe called more than once");
completer.Reply(std::exchange(describe_response_, std::nullopt).value());
EXPECT_OK(completer.result_of_reply().status());
}
std::string_view protocol_;
std::optional<DescribeResponse> describe_response_;
};
// Serves |node_info| over |endpoints| using a |Server| instance by
// creating a file descriptor from |client_channel| and immediately closing it.
template <typename Protocol>
void ServeAndExerciseFileDescriptionTeardown(
const char* protocol, typename Server<Protocol>::DescribeResponse describe_response,
fidl::Endpoints<Protocol> endpoints) {
Server<Protocol> server(protocol, describe_response);
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
fidl::BindServer(loop.dispatcher(), std::move(endpoints.server), &server);
ASSERT_OK(loop.StartThread("fake-socket-server"));
{
fbl::unique_fd fd;
ASSERT_OK(fdio_fd_create(endpoints.client.channel().release(), fd.reset_and_get_address()));
}
}
TEST(SocketCleanup, SynchronousDatagram) {
auto endpoints = fidl::CreateEndpoints<fuchsia_posix_socket::SynchronousDatagramSocket>();
ASSERT_OK(endpoints.status_value());
zx::eventpair client_event, server_event;
ASSERT_OK(zx::eventpair::create(0, &client_event, &server_event));
fidl::Arena alloc;
ASSERT_NO_FATAL_FAILURE(ServeAndExerciseFileDescriptionTeardown(
fuchsia_posix_socket::wire::kSynchronousDatagramSocketProtocolName,
fidl::WireResponse<fuchsia_posix_socket::SynchronousDatagramSocket::Describe>{
fuchsia_posix_socket::wire::SynchronousDatagramSocketDescribeResponse::Builder(alloc)
.event(std::move(client_event))
.Build()},
std::move(endpoints.value())));
// Client must have disposed of its channel and eventpair handle on close.
EXPECT_STATUS(endpoints->client.channel().wait_one(ZX_CHANNEL_PEER_CLOSED,
zx::time::infinite_past(), nullptr),
ZX_ERR_BAD_HANDLE);
EXPECT_OK(server_event.wait_one(ZX_EVENTPAIR_PEER_CLOSED, zx::time::infinite_past(), nullptr));
}
TEST(SocketCleanup, Stream) {
auto endpoints = fidl::CreateEndpoints<fuchsia_posix_socket::StreamSocket>();
ASSERT_OK(endpoints.status_value());
zx::socket client_socket, server_socket;
ASSERT_OK(zx::socket::create(0, &client_socket, &server_socket));
fidl::Arena alloc;
ASSERT_NO_FATAL_FAILURE(ServeAndExerciseFileDescriptionTeardown(
fuchsia_posix_socket::wire::kStreamSocketProtocolName,
fidl::WireResponse<fuchsia_posix_socket::StreamSocket::Describe>{
fuchsia_posix_socket::wire::StreamSocketDescribeResponse::Builder(alloc)
.socket(std::move(client_socket))
.Build()},
std::move(endpoints.value())));
// Client must have disposed of its channel and socket handles on close.
EXPECT_STATUS(endpoints->client.channel().wait_one(ZX_CHANNEL_PEER_CLOSED,
zx::time::infinite_past(), nullptr),
ZX_ERR_BAD_HANDLE);
EXPECT_OK(server_socket.wait_one(ZX_SOCKET_PEER_CLOSED, zx::time::infinite_past(), nullptr));
}
TEST(SocketCleanup, Datagram) {
auto endpoints = fidl::CreateEndpoints<fuchsia_posix_socket::DatagramSocket>();
ASSERT_OK(endpoints.status_value());
zx::socket client_socket, server_socket;
ASSERT_OK(zx::socket::create(0, &client_socket, &server_socket));
fidl::Arena alloc;
ASSERT_NO_FATAL_FAILURE(ServeAndExerciseFileDescriptionTeardown(
fuchsia_posix_socket::wire::kDatagramSocketProtocolName,
fidl::WireResponse<fuchsia_posix_socket::DatagramSocket::Describe>{
fuchsia_posix_socket::wire::DatagramSocketDescribeResponse::Builder(alloc)
.socket(std::move(client_socket))
.tx_meta_buf_size(0)
.rx_meta_buf_size(0)
.metadata_encoding_protocol_version(
fuchsia_posix_socket::wire::UdpMetadataEncodingProtocolVersion::kZero)
.Build()},
std::move(endpoints.value())));
// Client must have disposed of its channel and socket handles on close.
EXPECT_STATUS(endpoints->client.channel().wait_one(ZX_CHANNEL_PEER_CLOSED,
zx::time::infinite_past(), nullptr),
ZX_ERR_BAD_HANDLE);
EXPECT_OK(server_socket.wait_one(ZX_SOCKET_PEER_CLOSED, zx::time::infinite_past(), nullptr));
}
} // namespace