blob: 5d4308efd4d1f2b7d09febc1a21fab61d3649eb2 [file] [log] [blame]
// Copyright 2018 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/internal.h>
#include <lib/fidl/llcpp/array.h>
#include <lib/fidl/llcpp/coding.h>
#include <lib/fidl/llcpp/message.h>
#include <lib/fidl/llcpp/sync_call.h>
#include <lib/zx/channel.h>
#include <zircon/fidl.h>
#include <memory>
#include <optional>
#include <utility>
#include <fidl/test/coding/fuchsia/llcpp/fidl.h>
#include <zxtest/zxtest.h>
using ::llcpp::fidl::test::coding::fuchsia::TypesTest;
namespace {
// Because the EncodedMessage/DecodedMessage classes close handles using the corresponding
// Zircon system call instead of calling a destructor, we indirectly test for handle closure
// via the ZX_ERR_PEER_CLOSED error message.
void HelperExpectPeerValid(zx::channel& channel) {
const char* foo = "A";
EXPECT_EQ(channel.write(0, foo, 1, nullptr, 0), ZX_OK);
}
void HelperExpectPeerInvalid(zx::channel& channel) {
const char* foo = "A";
EXPECT_EQ(channel.write(0, foo, 1, nullptr, 0), ZX_ERR_PEER_CLOSED);
}
TEST(LlcppTypesTests, EncodedMessageTest) {
TypesTest::NonNullableChannelRequest msg(0);
// Capture the extra handle here; it will not be cleaned by encoded_message
zx::channel channel_1;
EXPECT_EQ(zx::channel::create(0, &msg.channel, &channel_1), ZX_OK);
{
fidl::OwnedEncodedMessage<TypesTest::NonNullableChannelRequest> encoded(&msg);
HelperExpectPeerValid(channel_1);
}
HelperExpectPeerInvalid(channel_1);
}
TEST(LlcppTypesTests, DecodedMessageTest) {
TypesTest::NonNullableChannelRequest msg(0);
// Capture the extra handle here; it will not be cleaned by encoded.
zx::channel channel_1;
EXPECT_EQ(zx::channel::create(0, &msg.channel, &channel_1), ZX_OK);
fidl::OwnedEncodedMessage<TypesTest::NonNullableChannelRequest> encoded(&msg);
{
auto decoded =
fidl::DecodedMessage<TypesTest::NonNullableChannelRequest>::FromOutgoingWithRawHandleCopy(
&encoded);
HelperExpectPeerValid(channel_1);
}
HelperExpectPeerInvalid(channel_1);
}
// Start with a message, then encode, decode and encode again.
TEST(LlcppTypesTests, RoundTripTest) {
TypesTest::NonNullableChannelRequest msg(10);
// Capture the extra handle here; it will not be cleaned by encoded_message
zx::channel channel_1;
EXPECT_EQ(zx::channel::create(0, &msg.channel, &channel_1), ZX_OK);
zx_handle_t unsafe_handle_backup(msg.channel.get());
// We need to define our own storage because it is used after encoded is deleted.
FIDL_ALIGNDECL uint8_t storage[sizeof(TypesTest::NonNullableChannelRequest)];
auto encoded = new fidl::UnownedEncodedMessage<TypesTest::NonNullableChannelRequest>(
storage, sizeof(storage), &msg);
EXPECT_EQ(encoded->GetOutgoingMessage().byte_actual(),
sizeof(TypesTest::NonNullableChannelRequest));
uint8_t golden_encoded[] = {0x0a, 0x00, 0x00, 0x00, // txid
0x00, 0x00, 0x00, 0x01, // flags and version
0xa1, 0xd4, 0x9b, 0x76, // low bytes of ordinal
0x82, 0x41, 0x13, 0x06, // high bytes of ordinal
0xff, 0xff, 0xff, 0xff, // handle present
0x00, 0x00, 0x00, 0x00}; // Padding
// Byte-accurate comparison
EXPECT_EQ(memcmp(golden_encoded, encoded->GetOutgoingMessage().bytes(),
encoded->GetOutgoingMessage().byte_actual()),
0);
HelperExpectPeerValid(channel_1);
// Decode
auto decoded =
fidl::DecodedMessage<TypesTest::NonNullableChannelRequest>::FromOutgoingWithRawHandleCopy(
encoded);
EXPECT_TRUE(decoded.ok());
EXPECT_NULL(decoded.error(), "%s", decoded.error());
EXPECT_EQ(decoded.PrimaryObject()->_hdr.txid, 10);
EXPECT_EQ(decoded.PrimaryObject()->_hdr.ordinal, 0x6134182769bd4a1lu);
EXPECT_EQ(decoded.PrimaryObject()->channel.get(), unsafe_handle_backup);
// encoded_message should be consumed
EXPECT_EQ(encoded->GetOutgoingMessage().handle_actual(), 0);
delete encoded;
// At this point, encoded is destroyed but not decoded, it should not accidentally close the
// channel.
HelperExpectPeerValid(channel_1);
// Encode
{
fidl::OwnedEncodedMessage<TypesTest::NonNullableChannelRequest> encoded2(
decoded.PrimaryObject());
EXPECT_TRUE(encoded2.ok());
EXPECT_NULL(encoded2.error(), "%s", encoded2.error());
// Byte-level comparison
EXPECT_EQ(encoded2.GetOutgoingMessage().byte_actual(),
sizeof(TypesTest::NonNullableChannelRequest));
EXPECT_EQ(memcmp(golden_encoded, encoded2.GetOutgoingMessage().bytes(),
encoded2.GetOutgoingMessage().byte_actual()),
0);
EXPECT_EQ(encoded2.GetOutgoingMessage().handle_actual(), 1);
EXPECT_EQ(encoded2.GetOutgoingMessage().handles()[0].handle, unsafe_handle_backup);
HelperExpectPeerValid(channel_1);
}
// Encoded message was destroyed, bringing down the handle with it
HelperExpectPeerInvalid(channel_1);
}
TEST(LlcppTypesTests, ArrayLayoutTest) {
static_assert(sizeof(fidl::Array<uint8_t, 3>) == sizeof(uint8_t[3]));
static_assert(sizeof(fidl::Array<fidl::Array<uint8_t, 7>, 3>) == sizeof(uint8_t[3][7]));
constexpr fidl::Array<uint8_t, 3> a = {1, 2, 3};
constexpr uint8_t b[3] = {1, 2, 3};
EXPECT_EQ((&a[2] - &a[0]), (&b[2] - &b[0]));
}
TEST(LlcppTypesTests, UninitializedBufferStackAllocationAlignmentTest) {
fidl::internal::AlignedBuffer<1> array_of_1;
ASSERT_EQ(sizeof(array_of_1), 8);
ASSERT_TRUE(reinterpret_cast<uintptr_t>(&array_of_1) % 8 == 0);
fidl::internal::AlignedBuffer<5> array_of_5;
ASSERT_EQ(sizeof(array_of_5), 8);
ASSERT_TRUE(reinterpret_cast<uintptr_t>(&array_of_5) % 8 == 0);
fidl::internal::AlignedBuffer<25> array_of_25;
ASSERT_EQ(sizeof(array_of_25), 32);
ASSERT_TRUE(reinterpret_cast<uintptr_t>(&array_of_25) % 8 == 0);
fidl::internal::AlignedBuffer<100> array_of_100;
ASSERT_EQ(sizeof(array_of_100), 104);
ASSERT_TRUE(reinterpret_cast<uintptr_t>(&array_of_100) % 8 == 0);
}
TEST(LlcppTypesTests, UninitializedBufferHeapAllocationAlignmentTest) {
std::unique_ptr array_of_1 = std::make_unique<fidl::internal::AlignedBuffer<1>>();
ASSERT_TRUE(reinterpret_cast<uintptr_t>(array_of_1.get()) % 8 == 0);
std::unique_ptr array_of_5 = std::make_unique<fidl::internal::AlignedBuffer<5>>();
ASSERT_TRUE(reinterpret_cast<uintptr_t>(array_of_5.get()) % 8 == 0);
std::unique_ptr array_of_25 = std::make_unique<fidl::internal::AlignedBuffer<25>>();
ASSERT_TRUE(reinterpret_cast<uintptr_t>(array_of_25.get()) % 8 == 0);
std::unique_ptr array_of_100 = std::make_unique<fidl::internal::AlignedBuffer<100>>();
ASSERT_TRUE(reinterpret_cast<uintptr_t>(array_of_100.get()) % 8 == 0);
}
TEST(LlcppTypesTests, ResponseStorageAllocationStrategyTest) {
// The stack allocation limit of 512 bytes is defined in
// zircon/system/ulib/fidl/include/lib/fidl/llcpp/sync_call.h
static_assert(sizeof(TypesTest::RequestOf512BytesRequest) == 512);
// Buffers for messages no bigger than 512 bytes are embedded, for this request,
// OwnedEncodedMessage size is bigger than 512 bytes.
static_assert(sizeof(fidl::OwnedEncodedMessage<TypesTest::RequestOf512BytesRequest>) > 512);
static_assert(sizeof(TypesTest::RequestOf513BytesRequest) == 520);
// Buffers for messages bigger than 512 bytes are store on the heap, for this request,
// OwnedEncodedMessage size is smaller than 512 bytes.
static_assert(sizeof(fidl::OwnedEncodedMessage<TypesTest::RequestOf513BytesRequest>) < 512);
}
} // namespace