blob: 477cc97a4fe703be21e719dd32bbd3379120e8cf [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/coding.h>
#include <lib/fidl/llcpp/string_view.h>
#include <lib/fidl/llcpp/vector_view.h>
#include <limits.h>
#include <zircon/syscalls.h>
#include <cstddef>
#include <memory>
#include <vector>
#include <unittest/unittest.h>
#include "extra_messages.h"
#include "fidl_coded_types.h"
#include "fidl_structs.h"
namespace fidl {
namespace {
bool linearize_present_nonnullable_string() {
BEGIN_TEST;
unbounded_nonnullable_string_inline_data message = {};
constexpr const char* kStr = "hello!";
constexpr size_t kLength = 6;
char some_other_string[kLength] = {0};
message.string = fidl_string_t{kLength, some_other_string};
memcpy(some_other_string, kStr, kLength);
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
// Manually compute linearized size
constexpr uint32_t buf_size = 32u + FIDL_ALIGN(kLength);
// Allocate a buffer of the specified size
std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size]);
status = fidl_linearize(&unbounded_nonnullable_string_message_type, &message, buf.get(), buf_size,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
EXPECT_NULL(error, error);
EXPECT_EQ(actual_num_bytes, buf_size);
// Verify pointers and object packing
auto inline_data = reinterpret_cast<unbounded_nonnullable_string_inline_data*>(buf.get());
EXPECT_EQ(static_cast<void*>(inline_data->string.data),
static_cast<void*>(&buf[FIDL_ALIGN(sizeof(message))]));
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>("hello!"),
reinterpret_cast<const uint8_t*>(inline_data->string.data), kLength,
"Secondary object string must be hello!");
END_TEST;
}
bool linearize_present_nonnullable_string_unaligned_error() {
BEGIN_TEST;
unbounded_nonnullable_string_inline_data message = {};
constexpr const char* kStr = "hello!";
constexpr size_t kLength = 6;
char some_other_string[kLength] = {0};
message.string = fidl_string_t{kLength, some_other_string};
memcpy(some_other_string, kStr, kLength);
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
// Pass in an unaligned storage
constexpr uint32_t buf_size = 32u + FIDL_ALIGN(kLength);
std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size * 2]);
uint8_t* unaligned_ptr = buf.get() + 1;
status = fidl_linearize(&unbounded_nonnullable_string_message_type, &message, unaligned_ptr,
buf_size, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
EXPECT_NONNULL(error);
ASSERT_STR_STR(error, "must be aligned to FIDL_ALIGNMENT");
END_TEST;
}
bool linearize_present_nonnullable_longer_string() {
BEGIN_TEST;
unbounded_nonnullable_string_inline_data message = {};
constexpr const char* kStr = "hello world!";
constexpr size_t kLength = 12;
char some_other_string[kLength] = {0};
message.string = fidl_string_t{kLength, some_other_string};
memcpy(some_other_string, kStr, kLength);
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
// Manually compute linearized size
constexpr uint32_t buf_size = 32u + FIDL_ALIGN(kLength);
// For non-handle-containing structures, linearizing should not change anything
const unbounded_nonnullable_string_inline_data message_shallow_copy = message;
// Allocate a buffer of the specified size
std::unique_ptr<uint8_t[]> buf(new uint8_t[buf_size]);
status = fidl_linearize(&unbounded_nonnullable_string_message_type, &message, buf.get(), buf_size,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
EXPECT_NULL(error, error);
EXPECT_EQ(actual_num_bytes, buf_size);
// Verify pointers and object packing
auto inline_data = reinterpret_cast<unbounded_nonnullable_string_inline_data*>(buf.get());
EXPECT_EQ(static_cast<void*>(inline_data->string.data),
static_cast<void*>(&buf[FIDL_ALIGN(sizeof(message))]));
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>("hello world!"),
reinterpret_cast<const uint8_t*>(inline_data->string.data), kLength,
"Secondary object string must be hello world!");
// Verify that message is not destroyed
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(&message_shallow_copy),
reinterpret_cast<uint8_t*>(&message), sizeof(message),
"Input object should not change");
// Linearizing with a buffer size smaller than allowed should error out
status = fidl_linearize(&unbounded_nonnullable_string_message_type, &message, buf.get(),
buf_size - 1, nullptr, &error);
EXPECT_EQ(status, ZX_ERR_BUFFER_TOO_SMALL);
EXPECT_NONNULL(error, "Should report error when buffer too small");
END_TEST;
}
bool linearize_vector_of_uint32() {
BEGIN_TEST;
// Linearizing this array...
constexpr uint32_t array_len = 40;
std::unique_ptr<uint32_t[]> numbers(new uint32_t[array_len]);
for (uint32_t i = 0; i < array_len; i++) {
numbers[i] = i;
}
// into this buffer, which is big enough for the entire message
constexpr uint32_t buf_size = 512;
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buf_size]);
unbounded_nonnullable_vector_of_uint32_inline_data message = {};
message.header.ordinal = 456;
message.header.txid = 789;
message.vector = (fidl_vector_t){
.count = array_len,
.data = numbers.get(),
};
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&unbounded_nonnullable_vector_of_uint32_message_type, &message,
buffer.get(), buf_size, &actual_num_bytes, &error);
ASSERT_EQ(status, ZX_OK);
ASSERT_NULL(error, error);
ASSERT_GT(actual_num_bytes, sizeof(message));
// Verify that vector contents have been copied copied correctly
auto linearized_message =
reinterpret_cast<unbounded_nonnullable_vector_of_uint32_inline_data*>(buffer.get());
EXPECT_NONNULL(linearized_message->vector.data);
EXPECT_NE(linearized_message->vector.data, message.vector.data);
auto copied_numbers = reinterpret_cast<uint32_t*>(linearized_message->vector.data);
for (uint32_t i = 0; i < array_len; i++) {
EXPECT_EQ(copied_numbers[i], i);
}
EXPECT_EQ(memcmp(&message.header, &linearized_message->header, sizeof(message.header)), 0);
// Verify that linearizing with less number of bytes does fail
status = fidl_linearize(&unbounded_nonnullable_vector_of_uint32_message_type, &message,
buffer.get(), actual_num_bytes - 1, nullptr, &error);
EXPECT_EQ(status, ZX_ERR_BUFFER_TOO_SMALL);
EXPECT_NONNULL(error);
END_TEST;
}
bool linearize_vector_of_nonnullable_uint32_coerce_null_to_empty() {
BEGIN_TEST;
std::vector<uint8_t> buffer(512);
unbounded_nonnullable_vector_of_uint32_inline_data message = {};
message.header.ordinal = 456;
message.header.txid = 789;
// Null data pointer and zero count should be treated as an empty vector
// by the linearizer.
message.vector = (fidl_vector_t){
.count = 0,
.data = nullptr,
};
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status =
fidl_linearize(&unbounded_nonnullable_vector_of_uint32_message_type, &message, &buffer[0],
static_cast<uint32_t>(buffer.size()), &actual_num_bytes, &error);
ASSERT_EQ(status, ZX_OK);
ASSERT_NULL(error, error);
ASSERT_EQ(actual_num_bytes, sizeof(message));
auto linearized_message =
reinterpret_cast<unbounded_nonnullable_vector_of_uint32_inline_data*>(&buffer[0]);
EXPECT_EQ(memcmp(&message.header, &linearized_message->header, sizeof(message.header)), 0);
EXPECT_NONNULL(linearized_message->vector.data);
// Verify that the data pointer in the linearized message points to the next
// out-of-line location.
EXPECT_EQ(linearized_message->vector.data,
reinterpret_cast<uint8_t*>(linearized_message) + sizeof(message));
// Verify that linearizing with less number of bytes does fail
status = fidl_linearize(&unbounded_nonnullable_vector_of_uint32_message_type, &message,
&buffer[0], actual_num_bytes - 1, nullptr, &error);
EXPECT_EQ(status, ZX_ERR_BUFFER_TOO_SMALL);
EXPECT_NONNULL(error);
END_TEST;
}
bool linearize_vector_of_string() {
BEGIN_TEST;
// Define the memory-layout of the inline object
struct VectorOfStringRequest {
alignas(FIDL_ALIGNMENT) fidl_message_header_t header;
fidl::VectorView<fidl::StringView> vector;
};
// Serialize these strings...
const char str1[] = "Open connection,";
const char str2[] = "Send the wrong FIDL message,";
const char str3[] = "Get an epitaph.";
// into this buffer, which is big enough for the entire message
constexpr uint32_t buf_size = 512;
std::unique_ptr<uint8_t[]> buffer(new uint8_t[buf_size]);
fidl::StringView strings[3] = {};
strings[0].set_data(fidl::unowned_ptr_t<const char>(str1));
strings[0].set_size(sizeof(str1));
strings[1].set_data(fidl::unowned_ptr_t<const char>(str2));
strings[1].set_size(sizeof(str2));
strings[2].set_data(fidl::unowned_ptr_t<const char>(str3));
strings[2].set_size(sizeof(str3));
VectorOfStringRequest message;
message.vector.set_data(fidl::unowned_ptr(strings));
message.vector.set_count(3);
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&fidl_test_coding_LinearizerTestVectorOfStringRequestTable, &message,
buffer.get(), buf_size, &actual_num_bytes, &error);
ASSERT_EQ(status, ZX_OK);
ASSERT_NULL(error, error);
ASSERT_GT(actual_num_bytes, sizeof(message));
// Verify that vector contents have been copied copied correctly
auto linearized_message = reinterpret_cast<VectorOfStringRequest*>(buffer.get());
EXPECT_NONNULL(linearized_message->vector.data());
EXPECT_EQ(linearized_message->vector.count(), 3);
EXPECT_NE(linearized_message->vector[0].data(), str1);
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(linearized_message->vector[0].data()),
reinterpret_cast<const uint8_t*>(str1), sizeof(str1), str1);
EXPECT_NE(linearized_message->vector[1].data(), str2);
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(linearized_message->vector[1].data()),
reinterpret_cast<const uint8_t*>(str2), sizeof(str2), str2);
EXPECT_NE(linearized_message->vector[2].data(), str3);
EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(linearized_message->vector[2].data()),
reinterpret_cast<const uint8_t*>(str3), sizeof(str3), str3);
END_TEST;
}
bool linearize_struct_with_handle() {
BEGIN_TEST;
constexpr zx_handle_t dummy_handle = static_cast<zx_handle_t>(42);
// Define the memory-layout of the inline object
struct StructWithHandle {
alignas(FIDL_ALIGNMENT) zx_handle_t h;
int32_t foo;
};
// Since there are no out-of-line objects, the size is known
constexpr uint32_t buf_size = sizeof(StructWithHandle);
StructWithHandle message = {
.h = dummy_handle,
.foo = 0,
};
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&fidl_test_coding_StructWithHandleTable, &message, buffer, buf_size,
&actual_num_bytes, &error);
ASSERT_EQ(status, ZX_OK);
ASSERT_EQ(actual_num_bytes, sizeof(StructWithHandle));
ASSERT_NULL(error, error);
// Handles in the original object are moved
auto linearized_message = reinterpret_cast<StructWithHandle*>(buffer);
EXPECT_EQ(message.h, ZX_HANDLE_INVALID);
EXPECT_EQ(linearized_message->h, dummy_handle);
END_TEST;
}
bool linearize_struct_with_many_handles() {
BEGIN_TEST;
zx_handle_t dummy_handles[4] = {};
auto handle_value_at = [](int i) -> zx_handle_t { return static_cast<zx_handle_t>(100 + i); };
for (int i = 0; i < 4; i++) {
dummy_handles[i] = handle_value_at(i);
}
// Define the memory-layout of the inline object
struct StructWithManyHandles {
alignas(FIDL_ALIGNMENT) zx_handle_t h1;
zx_handle_t h2;
fidl::VectorView<zx_handle_t> hs;
};
fidl::VectorView<zx_handle_t> hs;
hs.set_count(2);
hs.set_data(fidl::unowned_ptr(&dummy_handles[2]));
constexpr uint32_t buf_size = 512;
StructWithManyHandles message = {
.h1 = dummy_handles[0],
.h2 = dummy_handles[1],
.hs = std::move(hs),
};
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&fidl_test_coding_StructWithManyHandlesTable, &message, buffer, buf_size,
&actual_num_bytes, &error);
ASSERT_EQ(status, ZX_OK);
ASSERT_GT(actual_num_bytes, sizeof(StructWithManyHandles));
ASSERT_NULL(error, error);
// Handles in the original object are moved
auto linearized_message = reinterpret_cast<StructWithManyHandles*>(buffer);
EXPECT_EQ(message.h1, ZX_HANDLE_INVALID);
EXPECT_EQ(message.h2, ZX_HANDLE_INVALID);
EXPECT_EQ(message.hs[0], ZX_HANDLE_INVALID);
EXPECT_EQ(message.hs[1], ZX_HANDLE_INVALID);
EXPECT_EQ(linearized_message->h1, handle_value_at(0));
EXPECT_EQ(linearized_message->h2, handle_value_at(1));
EXPECT_EQ(linearized_message->hs[0], handle_value_at(2));
EXPECT_EQ(linearized_message->hs[1], handle_value_at(3));
END_TEST;
}
bool linearize_simple_table() {
BEGIN_TEST;
SimpleTableEnvelopes envelopes = {};
SimpleTable simple_table;
simple_table.set_count(5);
simple_table.set_data(fidl::unowned_ptr(&envelopes.x));
IntStruct x = {10};
IntStruct y = {20};
envelopes.x.data = &x;
envelopes.y.data = &y;
// Attempt to linearize with different table schemas to verify evolution-compatibility
for (auto coding_table :
{&fidl_test_coding_SimpleTableTable, &fidl_test_coding_NewerSimpleTableTable}) {
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status =
fidl_linearize(coding_table, &simple_table, buffer, buf_size, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
EXPECT_GT(actual_num_bytes, sizeof(SimpleTable));
EXPECT_NULL(error, error);
// Verify object placement
const auto& linearized = *reinterpret_cast<SimpleTable*>(buffer);
EXPECT_EQ(reinterpret_cast<IntStruct*>(linearized[0].data)->v, 10);
EXPECT_EQ(reinterpret_cast<IntStruct*>(linearized[4].data)->v, 20);
// Verify auto-filling envelope header
for (int i = 0; i < 5; i++) {
if (i == 0 || i == 4) {
EXPECT_EQ(linearized[i].num_bytes, 8);
EXPECT_EQ(linearized[i].num_handles, 0);
} else {
EXPECT_EQ(linearized[i].num_bytes, 0);
EXPECT_EQ(linearized[i].num_handles, 0);
}
}
}
// Alternative version with only x set, such that we can use OlderSimpleTable
envelopes.y.data = nullptr;
// Attempt to linearize with different table schemas to verify evolution-compatibility
for (auto coding_table :
{&fidl_test_coding_OlderSimpleTableTable, &fidl_test_coding_SimpleTableTable,
&fidl_test_coding_NewerSimpleTableTable}) {
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status =
fidl_linearize(coding_table, &simple_table, buffer, buf_size, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
EXPECT_GT(actual_num_bytes, sizeof(SimpleTable));
EXPECT_NULL(error, error);
// Verify object placement
const auto& linearized = *reinterpret_cast<SimpleTable*>(buffer);
EXPECT_EQ(reinterpret_cast<IntStruct*>(linearized[0].data)->v, 10);
// Verify auto-filling envelope header
EXPECT_EQ(linearized[0].num_bytes, 8);
EXPECT_EQ(linearized[0].num_handles, 0);
for (int i = 1; i < 5; i++) {
EXPECT_EQ(linearized[i].num_bytes, 0);
EXPECT_EQ(linearized[i].num_handles, 0);
}
}
// If y is set,
envelopes.y.data = &y;
// but OlderSimpleTable is used, it should error as the walker does not know how to process y.
{
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&fidl_test_coding_OlderSimpleTableTable, &simple_table, buffer,
buf_size, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
EXPECT_EQ(actual_num_bytes, 0);
EXPECT_NONNULL(error);
}
END_TEST;
}
namespace {
bool linearize_table_field_1() {
BEGIN_TEST;
TableOfStructEnvelopes envelopes = {};
TableOfStruct table;
table.set_count(1);
table.set_data(fidl::unowned_ptr(&envelopes.a));
constexpr zx_handle_t dummy_handle = static_cast<zx_handle_t>(42);
OrdinalOneStructWithHandle ordinal1 = {.h = dummy_handle, .foo = 0};
envelopes.a.data = &ordinal1;
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&fidl_test_coding_TableOfStructWithHandleTable, &table, buffer, buf_size,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
EXPECT_GT(actual_num_bytes, sizeof(TableOfStruct));
EXPECT_NULL(error, error);
// Verify that handles have been moved
const auto& linearized = *reinterpret_cast<TableOfStruct*>(buffer);
EXPECT_EQ(reinterpret_cast<OrdinalOneStructWithHandle*>(linearized[0].data)->h, dummy_handle);
EXPECT_EQ(ordinal1.h, ZX_HANDLE_INVALID);
// Verify auto-filling envelope header
EXPECT_EQ(linearized[0].num_bytes, sizeof(ordinal1));
EXPECT_EQ(linearized[0].num_handles, 1);
END_TEST;
}
bool linearize_table_field_2() {
BEGIN_TEST;
TableOfStructEnvelopes envelopes = {};
TableOfStruct table;
table.set_count(2);
table.set_data(fidl::unowned_ptr(&envelopes.a));
zx_handle_t dummy_handles[4] = {};
auto handle_value_at = [](int i) -> zx_handle_t { return static_cast<zx_handle_t>(100 + i); };
for (int i = 0; i < 4; i++) {
dummy_handles[i] = handle_value_at(i);
}
fidl::VectorView<zx_handle_t> hs;
hs.set_count(2);
hs.set_data(fidl::unowned_ptr(&dummy_handles[2]));
OrdinalTwoStructWithManyHandles ordinal2 = {
.h1 = dummy_handles[0],
.h2 = dummy_handles[1],
.hs = std::move(hs),
};
envelopes.b.data = &ordinal2;
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&fidl_test_coding_TableOfStructWithHandleTable, &table, buffer, buf_size,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
EXPECT_GT(actual_num_bytes, sizeof(TableOfStruct));
EXPECT_NULL(error, error);
// Verify that handles have been moved
const auto& linearized = *reinterpret_cast<TableOfStruct*>(buffer);
auto linearized_field = reinterpret_cast<OrdinalTwoStructWithManyHandles*>(linearized[1].data);
ASSERT_NONNULL(linearized_field);
EXPECT_EQ(linearized_field->h1, handle_value_at(0));
EXPECT_EQ(ordinal2.h1, ZX_HANDLE_INVALID);
EXPECT_EQ(linearized_field->h2, handle_value_at(1));
EXPECT_EQ(ordinal2.h2, ZX_HANDLE_INVALID);
EXPECT_EQ(linearized_field->hs[0], handle_value_at(2));
EXPECT_EQ(ordinal2.hs[0], ZX_HANDLE_INVALID);
EXPECT_EQ(linearized_field->hs[1], handle_value_at(3));
EXPECT_EQ(ordinal2.hs[1], ZX_HANDLE_INVALID);
// Verify auto-filling envelope header
EXPECT_EQ(linearized[0].num_bytes, 0);
EXPECT_EQ(linearized[0].num_handles, 0);
EXPECT_EQ(2, ordinal2.hs.count());
static_assert(sizeof(ordinal2) == 24);
EXPECT_EQ(32, sizeof(ordinal2) + sizeof(zx_handle_t) * ordinal2.hs.count());
EXPECT_EQ(32, FIDL_ALIGN(sizeof(ordinal2)) + sizeof(zx_handle_t) * ordinal2.hs.count());
EXPECT_EQ(linearized[1].num_bytes,
FIDL_ALIGN(sizeof(ordinal2)) + sizeof(zx_handle_t) * ordinal2.hs.count());
EXPECT_EQ(linearized[1].num_handles, 4);
END_TEST;
}
} // namespace
bool linearize_xunion_empty_invariant_empty() {
BEGIN_TEST;
// Non-zero ordinal with empty envelope is an error
SampleNullableXUnionStruct xunion = {};
xunion.opt_xu.header = (fidl_xunion_t){.tag = kSampleXUnionIntStructOrdinal, .envelope = {}};
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&v1_fidl_test_coding_SampleNullableXUnionStructTable, &xunion, buffer,
buf_size, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
EXPECT_NONNULL(error);
EXPECT_STR_EQ(error, "empty xunion must have zero as ordinal");
END_TEST;
}
bool linearize_xunion_empty_invariant_zero_ordinal() {
BEGIN_TEST;
// Zero ordinal with non-empty envelope is an error
IntStruct int_struct = {.v = 100};
SampleNullableXUnionStruct xunion = {};
xunion.opt_xu.header = (fidl_xunion_t){
.tag = 0,
.envelope = (fidl_envelope_t){.num_bytes = 8, .num_handles = 0, .data = &int_struct}};
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&v1_fidl_test_coding_SampleNullableXUnionStructTable, &xunion, buffer,
buf_size, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
EXPECT_NONNULL(error);
EXPECT_STR_EQ(error, "xunion with zero as ordinal must be empty");
END_TEST;
}
bool linearize_xunion_primitive_field() {
BEGIN_TEST;
int32_t raw_int = 42;
SampleXUnionStruct xunion = {};
xunion.xu.header = (fidl_xunion_t){
.tag = kSampleXUnionRawIntOrdinal,
.envelope = (fidl_envelope_t){.num_bytes = 0, .num_handles = 0, .data = &raw_int}};
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size];
const char* error = nullptr;
zx_status_t status;
uint32_t actual_num_bytes = 0;
status = fidl_linearize(&v1_fidl_test_coding_SampleXUnionStructTable, &xunion, buffer, buf_size,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
EXPECT_NULL(error, error);
uint8_t golden_linearized_prefix[] = {
0x03, 0x00, 0x00, 0x00, // The ordinal value is 3
0x00, 0x00, 0x00, 0x00, // xunion padding
0x08, 0x00, 0x00, 0x00, // num_bytes of envelope
0x00, 0x00, 0x00, 0x00, // num_handles of envelope
// The out-of-line address of the payload would follow.
};
constexpr uint32_t kEnvelopeDataPointerSize = sizeof(uintptr_t);
constexpr uint32_t kEnvelopePayloadSize = FidlAlign(sizeof(int32_t));
ASSERT_EQ(actual_num_bytes,
sizeof(golden_linearized_prefix) + kEnvelopeDataPointerSize + kEnvelopePayloadSize);
ASSERT_BYTES_EQ(buffer, golden_linearized_prefix, sizeof(golden_linearized_prefix),
"linearized result is different from goldens");
SampleXUnionStruct* linearized = reinterpret_cast<SampleXUnionStruct*>(&buffer[0]);
int32_t* payload_addr = reinterpret_cast<int32_t*>(linearized->xu.header.envelope.data);
std::ptrdiff_t distance = reinterpret_cast<uint8_t*>(payload_addr) - &buffer[0];
ASSERT_EQ(distance, sizeof(golden_linearized_prefix) + kEnvelopeDataPointerSize);
ASSERT_EQ(*payload_addr, raw_int);
END_TEST;
}
bool linearize_union_tracking_ptr_unowned() {
BEGIN_TEST;
int32_t int_val = 2;
LLCPPStyleUnionStruct str;
str.u.set_Primitive(fidl::unowned_ptr(&int_val));
constexpr uint32_t kBufSize = 512;
uint8_t buffer[kBufSize];
const char* error = nullptr;
uint32_t actual_num_bytes = 0;
auto status = fidl_linearize(&v1_fidl_test_coding_LLCPPStyleUnionStructTable, &str, buffer,
kBufSize, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
fidl_xunion_t* written_xunion = reinterpret_cast<fidl_xunion_t*>(buffer);
EXPECT_EQ(written_xunion->tag, 1);
EXPECT_EQ(written_xunion->envelope.num_handles, 0);
EXPECT_EQ(written_xunion->envelope.num_bytes, 8);
EXPECT_EQ(*reinterpret_cast<int32_t*>(written_xunion->envelope.data), int_val);
END_TEST;
}
bool linearize_union_tracking_ptr_heap_allocate() {
BEGIN_TEST;
constexpr int32_t int_val = 2;
LLCPPStyleUnionStruct str;
str.u.set_Primitive(std::make_unique<int32_t>(int_val));
constexpr uint32_t kBufSize = 512;
uint8_t buffer[kBufSize];
const char* error = nullptr;
uint32_t actual_num_bytes = 0;
auto status = fidl_linearize(&v1_fidl_test_coding_LLCPPStyleUnionStructTable, &str, buffer,
kBufSize, &actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
fidl_xunion_t* written_xunion = reinterpret_cast<fidl_xunion_t*>(buffer);
EXPECT_EQ(written_xunion->tag, 1);
EXPECT_EQ(written_xunion->envelope.num_handles, 0);
EXPECT_EQ(written_xunion->envelope.num_bytes, 8);
EXPECT_EQ(*reinterpret_cast<int32_t*>(written_xunion->envelope.data), int_val);
END_TEST;
}
bool linearize_vector_view_tracking_ptr_unowned() {
BEGIN_TEST;
constexpr uint32_t kSize = 16;
uint32_t arr[kSize];
for (uint32_t i = 0; i < kSize; i++)
arr[i] = i;
Uint32VectorStruct str;
str.vec.set_data(fidl::unowned_ptr(arr));
str.vec.set_count(kSize);
constexpr uint32_t kBufSize = 512;
uint8_t buffer[kBufSize];
const char* error = nullptr;
uint32_t actual_num_bytes = 0;
auto status = fidl_linearize(&v1_fidl_test_coding_Uint32VectorStructTable, &str, buffer, kBufSize,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
fidl_vector_t* written_vector = reinterpret_cast<fidl_vector_t*>(buffer);
EXPECT_EQ(written_vector->count, 16);
EXPECT_NE(written_vector->data, nullptr);
uint32_t* written_arr = reinterpret_cast<uint32_t*>(written_vector->data);
for (uint32_t i = 0; i < kSize; i++)
EXPECT_EQ(written_arr[i], i);
END_TEST;
}
bool linearize_vector_view_tracking_ptr_heap_allocate() {
BEGIN_TEST;
constexpr uint32_t kSize = 16;
auto uptr = std::make_unique<uint32_t[]>(kSize);
for (uint32_t i = 0; i < kSize; i++)
uptr[i] = i;
Uint32VectorStruct str;
str.vec.set_data(std::move(uptr));
str.vec.set_count(kSize);
constexpr uint32_t kBufSize = 512;
uint8_t buffer[kBufSize];
const char* error = nullptr;
uint32_t actual_num_bytes = 0;
auto status = fidl_linearize(&v1_fidl_test_coding_Uint32VectorStructTable, &str, buffer, kBufSize,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
fidl_vector_t* written_vector = reinterpret_cast<fidl_vector_t*>(buffer);
EXPECT_EQ(written_vector->count, 16);
EXPECT_NE(written_vector->data, nullptr);
uint32_t* written_arr = reinterpret_cast<uint32_t*>(written_vector->data);
for (uint32_t i = 0; i < kSize; i++)
EXPECT_EQ(written_arr[i], i);
END_TEST;
}
bool linearize_string_view_tracking_ptr_unowned() {
BEGIN_TEST;
const char input[] = "abcd";
StringStruct str = {.str = fidl::unowned_str(input, strlen(input))};
constexpr uint32_t kBufSize = 512;
uint8_t buffer[kBufSize];
const char* error = nullptr;
uint32_t actual_num_bytes = 0;
auto status = fidl_linearize(&v1_fidl_test_coding_StringStructTable, &str, buffer, kBufSize,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
fidl_string_t* written_string = reinterpret_cast<fidl_string_t*>(buffer);
EXPECT_EQ(written_string->size, strlen(input));
EXPECT_NE(written_string->data, nullptr);
const char* written_data = reinterpret_cast<const char*>(written_string->data);
for (size_t i = 0; i < strlen(input); i++)
EXPECT_EQ(written_data[i], input[i]);
END_TEST;
}
bool linearize_string_view_tracking_ptr_heap_allocate() {
BEGIN_TEST;
const char input[] = "abcd";
StringStruct str = {.str = fidl::heap_copy_str(input, strlen(input))};
constexpr uint32_t kBufSize = 512;
uint8_t buffer[kBufSize];
const char* error = nullptr;
uint32_t actual_num_bytes = 0;
auto status = fidl_linearize(&v1_fidl_test_coding_StringStructTable, &str, buffer, kBufSize,
&actual_num_bytes, &error);
EXPECT_EQ(status, ZX_OK);
fidl_string_t* written_string = reinterpret_cast<fidl_string_t*>(buffer);
EXPECT_EQ(written_string->size, strlen(input));
EXPECT_NE(written_string->data, nullptr);
const char* written_data = reinterpret_cast<const char*>(written_string->data);
for (size_t i = 0; i < strlen(input); i++)
EXPECT_EQ(written_data[i], input[i]);
END_TEST;
}
BEGIN_TEST_CASE(strings)
RUN_TEST(linearize_present_nonnullable_string)
RUN_TEST(linearize_present_nonnullable_longer_string)
END_TEST_CASE(strings)
BEGIN_TEST_CASE(unaligned)
RUN_TEST(linearize_present_nonnullable_string_unaligned_error)
END_TEST_CASE(unaligned)
BEGIN_TEST_CASE(vectors)
RUN_TEST(linearize_vector_of_uint32)
RUN_TEST(linearize_vector_of_nonnullable_uint32_coerce_null_to_empty)
RUN_TEST(linearize_vector_of_string)
END_TEST_CASE(vectors)
BEGIN_TEST_CASE(handles)
RUN_TEST(linearize_struct_with_handle)
RUN_TEST(linearize_struct_with_many_handles)
END_TEST_CASE(handles)
BEGIN_TEST_CASE(tables)
RUN_TEST(linearize_simple_table)
RUN_TEST(linearize_table_field_1)
RUN_TEST(linearize_table_field_2)
END_TEST_CASE(tables)
BEGIN_TEST_CASE(xunions)
RUN_TEST(linearize_xunion_empty_invariant_empty)
RUN_TEST(linearize_xunion_empty_invariant_zero_ordinal)
RUN_TEST(linearize_xunion_primitive_field)
END_TEST_CASE(xunions)
BEGIN_TEST_CASE(tracking_ptr)
RUN_TEST(linearize_union_tracking_ptr_unowned)
RUN_TEST(linearize_union_tracking_ptr_heap_allocate)
RUN_TEST(linearize_vector_view_tracking_ptr_unowned)
RUN_TEST(linearize_vector_view_tracking_ptr_heap_allocate)
RUN_TEST(linearize_string_view_tracking_ptr_unowned)
RUN_TEST(linearize_string_view_tracking_ptr_heap_allocate)
END_TEST_CASE(tracking_ptr)
} // namespace
} // namespace fidl