blob: a9d3ff10d821521e02f5cfeb1432a7747c44247b [file] [log] [blame]
// Copyright 2022 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/test.basic.protocol/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/fidl/cpp/wire/server.h>
#include <zircon/fidl.h>
#include <zircon/syscalls.h>
#include <thread>
#include <vector>
#include <gtest/gtest.h>
#include <sanitizer/lsan_interface.h>
#include "src/lib/testing/predicates/status.h"
namespace {
using ::test_basic_protocol::ValueEcho;
using ::test_basic_protocol::ValueEvent;
constexpr char kExpectedReply[] = "test";
TEST(Server, SyncReply) {
struct SyncServer : fidl::Server<ValueEcho> {
void Echo(EchoRequest& request, EchoCompleter::Sync& completer) override {
EXPECT_TRUE(completer.is_reply_needed());
completer.Reply(request.s());
EXPECT_FALSE(completer.is_reply_needed());
}
};
auto server = std::make_unique<SyncServer>();
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
auto endpoints = fidl::Endpoints<ValueEcho>::Create();
fidl::BindServer(loop.dispatcher(), std::move(endpoints.server), server.get());
std::thread call([&] {
fidl::WireResult result = fidl::WireCall(endpoints.client)->Echo(kExpectedReply);
EXPECT_OK(result.status());
EXPECT_EQ(result.value().s.get(), kExpectedReply);
loop.Quit();
});
EXPECT_STATUS(ZX_ERR_CANCELED, loop.Run());
call.join();
}
TEST(Server, AsyncReply) {
struct AsyncServer : fidl::Server<ValueEcho> {
void Echo(EchoRequest& request, EchoCompleter::Sync& completer) override {
worker_loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigNeverAttachToThread);
async::PostTask(worker_loop_->dispatcher(),
[request = request.s(), completer = completer.ToAsync()]() mutable {
EXPECT_TRUE(completer.is_reply_needed());
completer.Reply(request);
EXPECT_FALSE(completer.is_reply_needed());
});
EXPECT_FALSE(completer.is_reply_needed());
ASSERT_OK(worker_loop_->StartThread());
}
std::unique_ptr<async::Loop> worker_loop_;
};
auto server = std::make_unique<AsyncServer>();
async::Loop main_loop(&kAsyncLoopConfigNeverAttachToThread);
auto endpoints = fidl::Endpoints<ValueEcho>::Create();
fidl::BindServer(main_loop.dispatcher(), std::move(endpoints.server), server.get());
std::thread call([&] {
auto result = fidl::WireCall(endpoints.client)->Echo(kExpectedReply);
EXPECT_OK(result.status());
EXPECT_EQ(result.value().s.get(), kExpectedReply);
main_loop.Quit();
});
EXPECT_STATUS(ZX_ERR_CANCELED, main_loop.Run());
call.join();
}
TEST(Server, EventSendingAfterPeerClosed) {
struct Server : fidl::Server<ValueEvent> {};
Server server;
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
auto endpoints = fidl::Endpoints<ValueEvent>::Create();
fidl::ServerBinding<ValueEvent> binding{loop.dispatcher(), std::move(endpoints.server), &server,
fidl::kIgnoreBindingClosure};
// Close the peer endpoint.
endpoints.client.reset();
{
fit::result<fidl::OneWayError> result =
fidl::SendEvent(binding)->OnValueEvent({{.s = "hello"}});
EXPECT_TRUE(result.is_ok());
}
ASSERT_OK(loop.RunUntilIdle());
{
fit::result<fidl::OneWayError> result =
fidl::SendEvent(binding)->OnValueEvent({{.s = "hello"}});
EXPECT_TRUE(result.is_ok());
}
}
TEST(Server, EventSendingAfterUnrelatedError) {
struct Server : fidl::Server<ValueEvent> {};
Server server;
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
auto endpoints = fidl::Endpoints<ValueEvent>::Create();
fidl::ServerBinding<ValueEvent> binding{loop.dispatcher(), std::move(endpoints.server), &server,
fidl::kIgnoreBindingClosure};
// Send a 1 byte message which will cause the server binding to teardown.
uint8_t byte = 0;
endpoints.client.channel().write(0, &byte, sizeof(byte), nullptr, 0);
{
fit::result<fidl::OneWayError> result =
fidl::SendEvent(binding)->OnValueEvent({{.s = "hello"}});
EXPECT_TRUE(result.is_ok());
}
ASSERT_OK(loop.RunUntilIdle());
{
fit::result<fidl::OneWayError> result =
fidl::SendEvent(binding)->OnValueEvent({{.s = "hello"}});
ASSERT_FALSE(result.is_ok());
EXPECT_EQ(result.error_value().reason(), fidl::Reason::kCanceledDueToOtherError);
EXPECT_EQ(result.error_value().underlying_reason(), fidl::Reason::kUnexpectedMessage);
}
}
TEST(Server, EventSendingOnMovedFromBindingRef) {
struct Server : fidl::Server<ValueEvent> {};
Server server;
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
zx::result endpoints = fidl::CreateEndpoints<ValueEvent>();
ASSERT_OK(endpoints.status_value());
fidl::ServerBindingRef<ValueEvent> binding_ref =
fidl::BindServer(loop.dispatcher(), std::move(endpoints->server), &server);
fidl::ServerBindingRef<ValueEvent> binding_ref_new = std::move(binding_ref);
auto send_event_on_moved_from_ref = [&] {
#if __has_feature(address_sanitizer) || __has_feature(leak_sanitizer) || \
__has_feature(hwaddress_sanitizer)
// Disable LSAN for this thread while in scope. It is expected to leak by way
// of a crash.
__lsan::ScopedDisabler _;
#endif
(void)fidl::SendEvent(binding_ref)->OnValueEvent({{.s = "hello"}});
};
ASSERT_DEATH(send_event_on_moved_from_ref(), "");
}
} // namespace