blob: 482e81ff8e5362e485a0845a5dd44ea73f5d4e19 [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 <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/epitaph.h>
#include <lib/fidl/llcpp/server.h>
#include <lib/sync/completion.h>
#include <lib/zx/channel.h>
#include <string.h>
#include <zircon/types.h>
#include <fidl/test/coding/fuchsia/llcpp/fidl.h>
#include <fidl/test/coding/llcpp/fidl.h>
#include <zxtest/zxtest.h>
namespace {
using ::llcpp::fidl::test::coding::fuchsia::Example;
class Server : public Example::Interface {
public:
explicit Server(const char* data, size_t size) : data_(data), size_(size) {}
void TwoWay(fidl::StringView in, TwoWayCompleter::Sync& completer) override {
ASSERT_EQ(size_, in.size());
EXPECT_EQ(0, strncmp(data_, in.data(), size_));
completer.Reply(std::move(in));
}
void OneWay(fidl::StringView, OneWayCompleter::Sync&) override {}
private:
const char* data_;
size_t size_;
};
TEST(GenAPITestCase, TwoWayAsyncManaged) {
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(loop.StartThread());
fidl::Client<Example> client(std::move(local), loop.dispatcher());
static constexpr char data[] = "TwoWay() sync managed";
auto server_binding = fidl::BindServer(loop.dispatcher(), std::move(remote),
std::make_unique<Server>(data, sizeof(data)));
ASSERT_TRUE(server_binding.is_ok());
sync_completion_t done;
auto result = client->TwoWay(fidl::StringView(data, sizeof(data)),
[&done](Example::TwoWayResponse* response) {
ASSERT_EQ(sizeof(data), response->out.size());
EXPECT_EQ(0, strncmp(response->out.data(), data, sizeof(data)));
sync_completion_signal(&done);
});
ASSERT_TRUE(result.ok());
ASSERT_OK(sync_completion_wait(&done, ZX_TIME_INFINITE));
server_binding.value().Unbind();
}
TEST(GenAPITestCase, TwoWayAsyncCallerAllocated) {
class ResponseContext final : public Example::TwoWayResponseContext {
public:
ResponseContext(sync_completion_t* done, const char* data, size_t size)
: done_(done), data_(data), size_(size) {}
void OnError() override {
sync_completion_signal(done_);
FAIL();
}
void OnReply(Example::TwoWayResponse* message) override {
auto& out = message->out;
ASSERT_EQ(size_, out.size());
EXPECT_EQ(0, strncmp(out.data(), data_, size_));
sync_completion_signal(done_);
}
private:
sync_completion_t* done_;
const char* data_;
size_t size_;
};
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(loop.StartThread());
fidl::Client<Example> client(std::move(local), loop.dispatcher());
static constexpr char data[] = "TwoWay() sync caller-allocated";
auto server_binding = fidl::BindServer(loop.dispatcher(), std::move(remote),
std::make_unique<Server>(data, sizeof(data)));
ASSERT_TRUE(server_binding.is_ok());
sync_completion_t done;
fidl::Buffer<Example::TwoWayRequest> buffer;
ResponseContext context(&done, data, sizeof(data));
auto result = client->TwoWay(buffer.view(), fidl::StringView(data, sizeof(data)), &context);
ASSERT_TRUE(result.ok());
ASSERT_OK(sync_completion_wait(&done, ZX_TIME_INFINITE));
server_binding.value().Unbind();
}
TEST(GenAPITestCase, EventManaged) {
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(loop.StartThread());
static constexpr char data[] = "OnEvent() managed";
class EventHandler : public Example::AsyncEventHandler {
public:
EventHandler() = default;
sync_completion_t& done() { return done_; }
void OnEvent(Example::OnEventResponse* event) {
ASSERT_EQ(sizeof(data), event->out.size());
EXPECT_EQ(0, strncmp(event->out.data(), data, sizeof(data)));
sync_completion_signal(&done_);
}
private:
sync_completion_t done_;
};
auto event_handler = std::make_shared<EventHandler>();
fidl::Client<Example> client(std::move(local), loop.dispatcher(), event_handler);
auto server_binding = fidl::BindServer(loop.dispatcher(), std::move(remote),
std::make_unique<Server>(data, sizeof(data)));
ASSERT_TRUE(server_binding.is_ok());
// Wait for the event from the server.
ASSERT_OK(server_binding.value()->OnEvent(fidl::StringView(data, sizeof(data))));
ASSERT_OK(sync_completion_wait(&event_handler->done(), ZX_TIME_INFINITE));
server_binding.value().Unbind();
}
// This is test is almost identical to ClientBindingTestCase.Epitaph in llcpp_client_test.cc but
// validates the part of the flow that's handled in the generated code.
TEST(GenAPITestCase, Epitaph) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(loop.StartThread());
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
sync_completion_t unbound;
class EventHandler : public Example::AsyncEventHandler {
public:
explicit EventHandler(sync_completion_t& unbound) : unbound_(unbound) {}
void Unbound(fidl::UnbindInfo info) override {
EXPECT_EQ(fidl::UnbindInfo::kPeerClosed, info.reason);
EXPECT_EQ(ZX_ERR_BAD_STATE, info.status);
sync_completion_signal(&unbound_);
};
private:
sync_completion_t& unbound_;
};
fidl::Client<Example> client(std::move(local), loop.dispatcher(),
std::make_shared<EventHandler>(unbound));
// Send an epitaph and wait for on_unbound to run.
ASSERT_OK(fidl_epitaph_write(remote.get(), ZX_ERR_BAD_STATE));
EXPECT_OK(sync_completion_wait(&unbound, ZX_TIME_INFINITE));
}
TEST(GenAPITestCase, UnbindInfoEncodeError) {
class ErrorServer : public Example::Interface {
public:
explicit ErrorServer() {}
void TwoWay(fidl::StringView in, TwoWayCompleter::Sync& completer) override {
// Fail to send the reply due to an encoding error (the buffer is too
// small).
fidl::BufferSpan empty;
EXPECT_EQ(ZX_ERR_BUFFER_TOO_SMALL, completer.Reply(empty, std::move(in)).status());
completer.Close(ZX_OK); // This should not panic.
}
void OneWay(fidl::StringView, OneWayCompleter::Sync&) override {}
};
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(loop.StartThread());
fidl::Client<Example> client(std::move(local), loop.dispatcher());
sync_completion_t done;
fidl::OnUnboundFn<ErrorServer> on_unbound = [&done](ErrorServer*, fidl::UnbindInfo info,
zx::channel) {
EXPECT_EQ(fidl::UnbindInfo::kEncodeError, info.reason);
EXPECT_EQ(ZX_ERR_BUFFER_TOO_SMALL, info.status);
sync_completion_signal(&done);
};
auto server = std::make_unique<ErrorServer>();
auto server_binding =
fidl::BindServer(loop.dispatcher(), std::move(remote), server.get(), std::move(on_unbound));
ASSERT_TRUE(server_binding.is_ok());
// Make a synchronous call which should fail as a result of the server end closing.
auto result = client->TwoWay_Sync(fidl::StringView("", 0));
ASSERT_FALSE(result.ok());
EXPECT_EQ(ZX_ERR_PEER_CLOSED, result.status());
// Wait for the unbound handler to run.
ASSERT_OK(sync_completion_wait(&done, ZX_TIME_INFINITE));
}
TEST(GenAPITestCase, UnbindInfoDecodeError) {
zx::channel local, remote;
ASSERT_OK(zx::channel::create(0, &local, &remote));
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(loop.StartThread());
sync_completion_t done;
class EventHandler : public Example::AsyncEventHandler {
public:
EventHandler(sync_completion_t& done) : done_(done) {}
void OnEvent(Example::OnEventResponse* event) override {
FAIL();
sync_completion_signal(&done_);
}
void Unbound(fidl::UnbindInfo info) override {
EXPECT_EQ(fidl::UnbindInfo::kDecodeError, info.reason);
sync_completion_signal(&done_);
};
private:
sync_completion_t& done_;
};
fidl::Client<Example> client(std::move(local), loop.dispatcher(),
std::make_shared<EventHandler>(done));
// Set up an Example.OnEvent() message but send it without the payload. This should trigger a
// decoding error.
Example::OnEventResponse resp{fidl::StringView("", 0)};
fidl::OwnedEncodedMessage<Example::OnEventResponse> encoded(&resp);
ASSERT_TRUE(encoded.ok());
ASSERT_OK(remote.write(0, encoded.GetOutgoingMessage().bytes(), sizeof(fidl_message_header_t),
nullptr, 0));
ASSERT_OK(sync_completion_wait(&done, ZX_TIME_INFINITE));
}
} // namespace