blob: 34416fe6900f0eb78f84077445520af706490a04 [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 <lib/async-loop/cpp/loop.h>
#include <lib/fidl/cpp/internal/natural_client_messenger.h>
#include <lib/fidl/cpp/message.h>
#include <lib/fidl/llcpp/client_base.h>
#include <lib/fidl/llcpp/connect_service.h>
#include <lib/fidl/llcpp/message.h>
#include <array>
#include <zxtest/zxtest.h>
#include "test_messages.h"
namespace {
class TestProtocol {
TestProtocol() = delete;
};
// A fake client that supports capturing the messages sent to the server.
class FakeClientImpl : public fidl::internal::ClientBase {
public:
FakeClientImpl() {
zx::status endpoints = fidl::CreateEndpoints<TestProtocol>();
ZX_ASSERT(endpoints.is_ok());
endpoints_ = std::move(endpoints.value());
}
size_t GetTransactionCount() { return fidl::internal::ClientBase::GetTransactionCount(); }
fidl::Endpoints<TestProtocol>& endpoints() { return endpoints_; }
std::optional<fidl::UnbindInfo> DispatchEvent(
fidl::IncomingMessage& msg, fidl::internal::AsyncEventHandler* maybe_event_handler) override {
ZX_PANIC("Never used in this test");
}
fidl::IncomingMessage ReadFromServer() {
return fidl::ChannelReadEtc(endpoints_.server.channel().get(), 0,
fidl::BufferSpan(read_buffer_.data(), read_buffer_.size()),
cpp20::span<zx_handle_info_t>{});
}
private:
fidl::Endpoints<TestProtocol> endpoints_;
FIDL_ALIGNDECL std::array<uint8_t, ZX_CHANNEL_MAX_MSG_BYTES> read_buffer_;
};
constexpr uint64_t kTestOrdinal = 0x1234567812345678;
// A response context for recording errors and cancellation.
class MockResponseContext : public fidl::internal::ResponseContext {
public:
MockResponseContext() : fidl::internal::ResponseContext(kTestOrdinal) {}
cpp17::optional<fidl::UnbindInfo> OnRawResult(::fidl::IncomingMessage&& msg) override {
if (msg.ok()) {
// We never get a response from the server in this test.
ZX_PANIC("Never used in this test");
}
if (msg.reason() == fidl::Reason::kUnbind) {
canceled_ = true;
return cpp17::nullopt;
}
num_errors_ += 1;
last_error_ = msg.error();
return cpp17::nullopt;
}
bool canceled() const { return canceled_; }
int num_errors() const { return num_errors_; }
cpp17::optional<fidl::Result> last_error() const { return last_error_; }
private:
bool canceled_ = false;
int num_errors_ = 0;
cpp17::optional<fidl::Result> last_error_ = cpp17::nullopt;
};
class NaturalClientMessengerTest : public zxtest::Test {
public:
NaturalClientMessengerTest()
: loop_(&kAsyncLoopConfigNeverAttachToThread),
controller_(Create(loop_.dispatcher())),
impl_(static_cast<FakeClientImpl*>(controller_.get())),
messenger_(impl_) {}
protected:
async::Loop& loop() { return loop_; }
FakeClientImpl* impl() { return impl_; }
fidl::internal::ClientController& controller() { return controller_; }
fidl::internal::NaturalClientMessenger& messenger() { return messenger_; }
MockResponseContext& context() { return context_; }
private:
static fidl::internal::ClientController Create(async_dispatcher_t* dispatcher) {
std::shared_ptr impl = std::make_shared<FakeClientImpl>();
fidl::internal::ClientController controller;
controller.Bind(impl, impl->endpoints().client.TakeChannel(), dispatcher,
/* event_handler */ nullptr, fidl::AnyTeardownObserver::Noop(),
fidl::internal::ThreadingPolicy::kCreateAndTeardownFromDispatcherThread);
return controller;
}
async::Loop loop_;
fidl::internal::ClientController controller_;
FakeClientImpl* impl_;
fidl::internal::NaturalClientMessenger messenger_;
MockResponseContext context_;
};
TEST_F(NaturalClientMessengerTest, TwoWay) {
GoodMessage good;
EXPECT_EQ(0, impl()->GetTransactionCount());
EXPECT_EQ(0, context().num_errors());
messenger().TwoWay(good.type(), good.message(), &context());
loop().RunUntilIdle();
EXPECT_EQ(1, impl()->GetTransactionCount());
EXPECT_FALSE(context().canceled());
EXPECT_EQ(0, context().num_errors());
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_OK(incoming.status());
EXPECT_EQ(kTestOrdinal, incoming.header()->ordinal);
EXPECT_NE(0, incoming.header()->txid);
impl()->ForgetAsyncTxn(&context());
}
TEST_F(NaturalClientMessengerTest, TwoWayInvalidMessage) {
BadMessage too_large;
EXPECT_EQ(0, impl()->GetTransactionCount());
EXPECT_EQ(0, context().num_errors());
messenger().TwoWay(too_large.type(), too_large.message(), &context());
{
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_STATUS(ZX_ERR_SHOULD_WAIT, incoming.status());
}
loop().RunUntilIdle();
EXPECT_EQ(0, impl()->GetTransactionCount());
EXPECT_FALSE(context().canceled());
EXPECT_EQ(1, context().num_errors());
EXPECT_TRUE(context().last_error().has_value());
EXPECT_EQ(fidl::Reason::kEncodeError, context().last_error()->reason());
EXPECT_STATUS(ZX_ERR_INVALID_ARGS, context().last_error()->status());
{
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_STATUS(ZX_ERR_PEER_CLOSED, incoming.status());
}
}
TEST_F(NaturalClientMessengerTest, TwoWayUnbound) {
GoodMessage good;
controller().Unbind();
ASSERT_OK(loop().RunUntilIdle());
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_STATUS(ZX_ERR_PEER_CLOSED, incoming.status());
EXPECT_EQ(0, impl()->GetTransactionCount());
EXPECT_FALSE(context().canceled());
EXPECT_EQ(0, context().num_errors());
messenger().TwoWay(good.type(), good.message(), &context());
loop().RunUntilIdle();
EXPECT_EQ(0, impl()->GetTransactionCount());
EXPECT_TRUE(context().canceled());
EXPECT_EQ(0, context().num_errors());
EXPECT_FALSE(context().last_error().has_value());
}
TEST_F(NaturalClientMessengerTest, OneWay) {
GoodMessage good;
EXPECT_EQ(0, impl()->GetTransactionCount());
fidl::Result result = messenger().OneWay(good.type(), good.message());
loop().RunUntilIdle();
EXPECT_OK(result.status());
EXPECT_EQ(0, impl()->GetTransactionCount());
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_OK(incoming.status());
EXPECT_EQ(kTestOrdinal, incoming.header()->ordinal);
EXPECT_EQ(0, incoming.header()->txid);
}
TEST_F(NaturalClientMessengerTest, OneWayInvalidMessage) {
BadMessage too_large;
EXPECT_EQ(0, impl()->GetTransactionCount());
fidl::Result result = messenger().OneWay(too_large.type(), too_large.message());
{
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_STATUS(ZX_ERR_SHOULD_WAIT, incoming.status());
}
loop().RunUntilIdle();
EXPECT_STATUS(ZX_ERR_INVALID_ARGS, result.status());
EXPECT_EQ(0, impl()->GetTransactionCount());
{
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_STATUS(ZX_ERR_PEER_CLOSED, incoming.status());
}
}
TEST_F(NaturalClientMessengerTest, OneWayUnbound) {
GoodMessage good;
controller().Unbind();
ASSERT_OK(loop().RunUntilIdle());
fidl::IncomingMessage incoming = impl()->ReadFromServer();
EXPECT_STATUS(ZX_ERR_PEER_CLOSED, incoming.status());
EXPECT_EQ(0, impl()->GetTransactionCount());
fidl::Result result = messenger().OneWay(good.type(), good.message());
loop().RunUntilIdle();
EXPECT_EQ(ZX_ERR_CANCELED, result.status());
EXPECT_EQ(0, impl()->GetTransactionCount());
}
} // namespace