blob: fa73b05d388b23b59b9909f8396f80f4aeb5b5c6 [file] [log] [blame]
// Copyright 2017 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/fidl/cpp/builder.h>
#include <lib/fidl/cpp/message.h>
#include <lib/fidl/cpp/message_builder.h>
#include <lib/fidl/llcpp/memory.h>
#include <lib/fidl/llcpp/string_view.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <zxtest/zxtest.h>
#include "fidl_coded_types.h"
namespace {
TEST(Message, BasicTests) {
uint8_t byte_buffer[ZX_CHANNEL_MAX_MSG_BYTES];
zx_handle_t handle_buffer[ZX_CHANNEL_MAX_MSG_HANDLES];
fidl::Builder builder(byte_buffer, ZX_CHANNEL_MAX_MSG_BYTES);
fidl_message_header_t* header = builder.New<fidl_message_header_t>();
header->txid = 5u;
header->ordinal = 42u;
fidl::StringView* view = builder.New<fidl::StringView>();
char* data = builder.NewArray<char>(4);
view->set_data(fidl::unowned_ptr(data));
view->set_size(4);
data[0] = 'a';
data[1] = 'b';
data[2] = 'c';
fidl::Message message(builder.Finalize(),
fidl::HandlePart(handle_buffer, ZX_CHANNEL_MAX_MSG_HANDLES));
EXPECT_EQ(message.txid(), 5u);
EXPECT_EQ(message.ordinal(), 42u);
fidl::BytePart payload = message.payload();
EXPECT_EQ(reinterpret_cast<fidl::StringView*>(payload.data()), view);
zx::channel h1, h2;
EXPECT_EQ(zx::channel::create(0, &h1, &h2), ZX_OK);
EXPECT_EQ(ZX_OK, message.Write(h1.get(), 0u));
memset(byte_buffer, 0, ZX_CHANNEL_MAX_MSG_BYTES);
EXPECT_EQ(message.txid(), 0u);
EXPECT_EQ(message.ordinal(), 0u);
EXPECT_EQ(ZX_OK, message.Read(h2.get(), 0u));
EXPECT_EQ(message.txid(), 5u);
EXPECT_EQ(message.ordinal(), 42u);
}
TEST(Message, ReadErrorCodes) {
// Create a Message buffer.
constexpr size_t kBufferSize = 100;
uint8_t byte_buffer[kBufferSize];
fidl::Message message(fidl::BytePart::WrapEmpty(byte_buffer), fidl::HandlePart());
// Create a channel.
zx::channel client, server;
EXPECT_OK(zx::channel::create(0, &client, &server));
// Read from an empty channel.
EXPECT_EQ(message.Read(client.get(), /*flags=*/0), ZX_ERR_SHOULD_WAIT);
// Read with invalid flags.
EXPECT_EQ(message.Read(client.get(), /*flags=*/~0), ZX_ERR_NOT_SUPPORTED);
// Read a message smaller than the FIDL header size.
{
uint8_t write_buffer[1] = {0};
EXPECT_OK(
server.write(/*flags=*/0, &write_buffer, sizeof(write_buffer), /*handles=*/nullptr, 0));
EXPECT_EQ(message.Read(client.get(), /*flags=*/0), ZX_ERR_INVALID_ARGS);
}
// Read a message larger than our receive buffer.
{
uint8_t write_buffer[kBufferSize + 1];
memset(write_buffer, 0xff, sizeof(write_buffer));
EXPECT_OK(
server.write(/*flags=*/0, &write_buffer, sizeof(write_buffer), /*handles=*/nullptr, 0));
EXPECT_EQ(message.Read(client.get(), /*flags=*/ZX_CHANNEL_READ_MAY_DISCARD),
ZX_ERR_BUFFER_TOO_SMALL);
}
// Read from closed channel.
server.reset();
EXPECT_EQ(message.Read(client.get(), /*flags=*/0), ZX_ERR_PEER_CLOSED);
}
TEST(MessageBuilder, BasicTests) {
zx::event e;
EXPECT_EQ(zx::event::create(0, &e), ZX_OK);
EXPECT_NE(e.get(), ZX_HANDLE_INVALID);
fidl::MessageBuilder builder(&nonnullable_handle_message_type);
builder.header()->txid = 5u;
builder.header()->ordinal = 42u;
zx_handle_t* handle_ptr = builder.New<zx_handle_t>();
zx_handle_t handle_value = e.release();
*handle_ptr = handle_value;
fidl::Message message;
const char* error_msg;
EXPECT_EQ(builder.Encode(&message, &error_msg), ZX_OK);
EXPECT_EQ(message.txid(), 5u);
EXPECT_EQ(message.ordinal(), 42u);
EXPECT_EQ(message.handles().actual(), 1u);
EXPECT_EQ(message.handles().size(), 1u);
EXPECT_EQ(message.handles().data()[0], handle_value);
}
TEST(MessagePart, IsStlContainerTest) {
EXPECT_EQ(sizeof(uint8_t), sizeof(fidl::BytePart::value_type));
EXPECT_EQ(sizeof(zx_handle_t), sizeof(fidl::HandlePart::value_type));
EXPECT_EQ(sizeof(const uint8_t*), sizeof(fidl::BytePart::const_iterator));
EXPECT_EQ(sizeof(const zx_handle_t*), sizeof(fidl::HandlePart::const_iterator));
}
TEST(MessagePart, Size) {
fidl::Message message;
EXPECT_EQ(message.bytes().size(), 0u);
uint8_t dummy_msg[42];
fidl::MessagePart msg(dummy_msg, 42, 10);
EXPECT_EQ(msg.size(), 10u);
fidl::MessagePart new_msg = std::move(msg);
EXPECT_EQ(new_msg.size(), 10u);
EXPECT_EQ(msg.size(), 0u);
}
TEST(MessagePart, WrapArray) {
uint8_t dummy[42];
auto full = fidl::MessagePart<uint8_t>::WrapFull(dummy);
EXPECT_EQ(full.data(), dummy);
EXPECT_EQ(full.actual(), 42);
EXPECT_EQ(full.capacity(), 42);
auto empty = fidl::MessagePart<uint8_t>::WrapEmpty(dummy);
EXPECT_EQ(empty.data(), dummy);
EXPECT_EQ(empty.actual(), 0);
EXPECT_EQ(empty.capacity(), 42);
}
extern "C" {
// Defined in generated/transformer_tables.test.h.
extern const fidl_type_t example_Sandwich1Table;
extern const fidl_type_t example_SimpleTableArrayStructTable;
// Defined in transformer_tests.c.
extern const uint8_t simpletablearraystruct_v1_and_old[0x50];
extern const uint8_t sandwich1_case1_v1[0x30];
extern const uint8_t sandwich1_case1_old[0x10];
}
TEST(Message, TransformWithCallbackNoop) {
const auto& src_bytes = simpletablearraystruct_v1_and_old;
uint32_t src_num_bytes = sizeof(simpletablearraystruct_v1_and_old);
uint32_t num_called = 0;
auto callback = [&](const uint8_t* dst_bytes, uint32_t dst_num_bytes) -> zx_status_t {
num_called++;
// since this struct does not contain any unions, dst_bytes should be
// the same as the input
if (dst_bytes == src_bytes) {
return ZX_OK;
}
// return a special value to distinguish from transformer errors
return 1;
};
ASSERT_EQ(ZX_OK, fidl::FidlTransformWithCallback(FIDL_TRANSFORMATION_OLD_TO_V1,
&example_SimpleTableArrayStructTable, src_bytes,
src_num_bytes, nullptr, callback));
ASSERT_EQ(num_called, 1);
}
TEST(Message, TransformWithCallback) {
uint32_t num_called = 0;
auto callback = [&](const uint8_t* dst_bytes, uint32_t dst_num_bytes) -> zx_status_t {
num_called++;
if (memcmp(dst_bytes, sandwich1_case1_v1, dst_num_bytes) == 0) {
return ZX_OK;
}
return 1;
};
ASSERT_EQ(ZX_OK, fidl::FidlTransformWithCallback(FIDL_TRANSFORMATION_OLD_TO_V1,
&example_Sandwich1Table, sandwich1_case1_old,
sizeof(sandwich1_case1_old), nullptr, callback));
ASSERT_EQ(num_called, 1);
}
} // namespace