530 lines
17 KiB
C++
530 lines
17 KiB
C++
// Copyright 2014 The Chromium 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 <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <utility>
|
|
|
|
#include "mojo/public/cpp/bindings/lib/fixed_buffer.h"
|
|
#include "mojo/public/cpp/system/message_pipe.h"
|
|
#include "mojo/public/interfaces/bindings/tests/test_export2.mojom.h"
|
|
#include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace mojo {
|
|
namespace test {
|
|
namespace {
|
|
|
|
RectPtr MakeRect(int32_t factor = 1) {
|
|
return Rect::New(1 * factor, 2 * factor, 10 * factor, 20 * factor);
|
|
}
|
|
|
|
void CheckRect(const Rect& rect, int32_t factor = 1) {
|
|
EXPECT_EQ(1 * factor, rect.x);
|
|
EXPECT_EQ(2 * factor, rect.y);
|
|
EXPECT_EQ(10 * factor, rect.width);
|
|
EXPECT_EQ(20 * factor, rect.height);
|
|
}
|
|
|
|
template <typename StructType>
|
|
struct SerializeStructHelperTraits {
|
|
using DataView = typename StructType::DataView;
|
|
};
|
|
|
|
template <>
|
|
struct SerializeStructHelperTraits<native::NativeStruct> {
|
|
using DataView = native::NativeStructDataView;
|
|
};
|
|
|
|
template <typename InputType, typename DataType>
|
|
size_t SerializeStruct(InputType& input,
|
|
mojo::Message* message,
|
|
mojo::internal::SerializationContext* context,
|
|
DataType** out_data) {
|
|
using StructType = typename InputType::Struct;
|
|
using DataViewType =
|
|
typename SerializeStructHelperTraits<StructType>::DataView;
|
|
*message = mojo::Message(0, 0, 0, 0, nullptr);
|
|
const size_t payload_start = message->payload_buffer()->cursor();
|
|
typename DataType::BufferWriter writer;
|
|
mojo::internal::Serialize<DataViewType>(input, message->payload_buffer(),
|
|
&writer, context);
|
|
*out_data = writer.is_null() ? nullptr : writer.data();
|
|
return message->payload_buffer()->cursor() - payload_start;
|
|
}
|
|
|
|
MultiVersionStructPtr MakeMultiVersionStruct() {
|
|
MessagePipe pipe;
|
|
return MultiVersionStruct::New(123, MakeRect(5), std::string("hello"),
|
|
std::vector<int8_t>{10, 9, 8},
|
|
std::move(pipe.handle0), false, 42);
|
|
}
|
|
|
|
template <typename U, typename T>
|
|
U SerializeAndDeserialize(T input) {
|
|
using InputMojomType = typename T::Struct::DataView;
|
|
using OutputMojomType = typename U::Struct::DataView;
|
|
|
|
using InputDataType =
|
|
typename mojo::internal::MojomTypeTraits<InputMojomType>::Data*;
|
|
using OutputDataType =
|
|
typename mojo::internal::MojomTypeTraits<OutputMojomType>::Data*;
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
InputDataType data;
|
|
SerializeStruct(input, &message, &context, &data);
|
|
|
|
// Set the subsequent area to a special value, so that we can find out if we
|
|
// mistakenly access the area.
|
|
void* subsequent_area = message.payload_buffer()->AllocateAndGet(32);
|
|
memset(subsequent_area, 0xAA, 32);
|
|
|
|
OutputDataType output_data =
|
|
reinterpret_cast<OutputDataType>(message.mutable_payload());
|
|
|
|
U output;
|
|
mojo::internal::Deserialize<OutputMojomType>(output_data, &output, &context);
|
|
return std::move(output);
|
|
}
|
|
|
|
using StructTest = testing::Test;
|
|
|
|
} // namespace
|
|
|
|
TEST_F(StructTest, Rect) {
|
|
RectPtr rect;
|
|
EXPECT_TRUE(rect.is_null());
|
|
EXPECT_TRUE(!rect);
|
|
EXPECT_FALSE(rect);
|
|
|
|
rect = nullptr;
|
|
EXPECT_TRUE(rect.is_null());
|
|
EXPECT_TRUE(!rect);
|
|
EXPECT_FALSE(rect);
|
|
|
|
rect = MakeRect();
|
|
EXPECT_FALSE(rect.is_null());
|
|
EXPECT_FALSE(!rect);
|
|
EXPECT_TRUE(rect);
|
|
|
|
RectPtr null_rect = nullptr;
|
|
EXPECT_TRUE(null_rect.is_null());
|
|
EXPECT_TRUE(!null_rect);
|
|
EXPECT_FALSE(null_rect);
|
|
|
|
CheckRect(*rect);
|
|
}
|
|
|
|
TEST_F(StructTest, Clone) {
|
|
NamedRegionPtr region;
|
|
|
|
NamedRegionPtr clone_region = region.Clone();
|
|
EXPECT_TRUE(clone_region.is_null());
|
|
|
|
region = NamedRegion::New();
|
|
clone_region = region.Clone();
|
|
EXPECT_FALSE(clone_region->name);
|
|
EXPECT_FALSE(clone_region->rects);
|
|
|
|
region->name.emplace("hello world");
|
|
clone_region = region.Clone();
|
|
EXPECT_EQ(region->name, clone_region->name);
|
|
|
|
region->rects.emplace(2);
|
|
(*region->rects)[1] = MakeRect();
|
|
clone_region = region.Clone();
|
|
EXPECT_EQ(2u, clone_region->rects->size());
|
|
EXPECT_TRUE((*clone_region->rects)[0].is_null());
|
|
CheckRect(*(*clone_region->rects)[1]);
|
|
|
|
// NoDefaultFieldValues contains handles, so Clone() is not available, but
|
|
// NoDefaultFieldValuesPtr should still compile.
|
|
NoDefaultFieldValuesPtr no_default_field_values(NoDefaultFieldValues::New());
|
|
EXPECT_FALSE(no_default_field_values->f13.is_valid());
|
|
}
|
|
|
|
// Serialization test of a struct with no pointer or handle members.
|
|
TEST_F(StructTest, Serialization_Basic) {
|
|
RectPtr rect(MakeRect());
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
internal::Rect_Data* data;
|
|
EXPECT_EQ(8U + 16U, SerializeStruct(rect, &message, &context, &data));
|
|
|
|
RectPtr rect2;
|
|
mojo::internal::Deserialize<RectDataView>(data, &rect2, &context);
|
|
|
|
CheckRect(*rect2);
|
|
}
|
|
|
|
// Construction of a struct with struct pointers from null.
|
|
TEST_F(StructTest, Construction_StructPointers) {
|
|
RectPairPtr pair;
|
|
EXPECT_TRUE(pair.is_null());
|
|
|
|
pair = RectPair::New();
|
|
EXPECT_FALSE(pair.is_null());
|
|
EXPECT_TRUE(pair->first.is_null());
|
|
EXPECT_TRUE(pair->first.is_null());
|
|
|
|
pair = nullptr;
|
|
EXPECT_TRUE(pair.is_null());
|
|
}
|
|
|
|
// Serialization test of a struct with struct pointers.
|
|
TEST_F(StructTest, Serialization_StructPointers) {
|
|
RectPairPtr pair(RectPair::New(MakeRect(), MakeRect()));
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
internal::RectPair_Data* data;
|
|
EXPECT_EQ(8U + 16U + 2 * (8U + 16U),
|
|
SerializeStruct(pair, &message, &context, &data));
|
|
|
|
RectPairPtr pair2;
|
|
mojo::internal::Deserialize<RectPairDataView>(data, &pair2, &context);
|
|
|
|
CheckRect(*pair2->first);
|
|
CheckRect(*pair2->second);
|
|
}
|
|
|
|
// Serialization test of a struct with an array member.
|
|
TEST_F(StructTest, Serialization_ArrayPointers) {
|
|
std::vector<RectPtr> rects;
|
|
for (size_t i = 0; i < 4; ++i)
|
|
rects.push_back(MakeRect(static_cast<int32_t>(i) + 1));
|
|
|
|
NamedRegionPtr region(
|
|
NamedRegion::New(std::string("region"), std::move(rects)));
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
internal::NamedRegion_Data* data;
|
|
EXPECT_EQ(8U + // header
|
|
8U + // name pointer
|
|
8U + // rects pointer
|
|
8U + // name header
|
|
8U + // name payload (rounded up)
|
|
8U + // rects header
|
|
4 * 8U + // rects payload (four pointers)
|
|
4 * (8U + // rect header
|
|
16U), // rect payload (four ints)
|
|
SerializeStruct(region, &message, &context, &data));
|
|
|
|
NamedRegionPtr region2;
|
|
mojo::internal::Deserialize<NamedRegionDataView>(data, ®ion2, &context);
|
|
|
|
EXPECT_EQ("region", *region2->name);
|
|
|
|
EXPECT_EQ(4U, region2->rects->size());
|
|
for (size_t i = 0; i < region2->rects->size(); ++i)
|
|
CheckRect(*(*region2->rects)[i], static_cast<int32_t>(i) + 1);
|
|
}
|
|
|
|
// Serialization test of a struct with null array pointers.
|
|
TEST_F(StructTest, Serialization_NullArrayPointers) {
|
|
NamedRegionPtr region(NamedRegion::New());
|
|
EXPECT_FALSE(region->name);
|
|
EXPECT_FALSE(region->rects);
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
internal::NamedRegion_Data* data;
|
|
EXPECT_EQ(8U + // header
|
|
8U + // name pointer
|
|
8U, // rects pointer
|
|
SerializeStruct(region, &message, &context, &data));
|
|
|
|
NamedRegionPtr region2;
|
|
mojo::internal::Deserialize<NamedRegionDataView>(data, ®ion2, &context);
|
|
|
|
EXPECT_FALSE(region2->name);
|
|
EXPECT_FALSE(region2->rects);
|
|
}
|
|
|
|
// Tests deserializing structs as a newer version.
|
|
TEST_F(StructTest, Versioning_OldToNew) {
|
|
{
|
|
MultiVersionStructV0Ptr input(MultiVersionStructV0::New(123));
|
|
MultiVersionStructPtr expected_output(MultiVersionStruct::New(123));
|
|
|
|
MultiVersionStructPtr output =
|
|
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MultiVersionStructV1Ptr input(MultiVersionStructV1::New(123, MakeRect(5)));
|
|
MultiVersionStructPtr expected_output(
|
|
MultiVersionStruct::New(123, MakeRect(5)));
|
|
|
|
MultiVersionStructPtr output =
|
|
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MultiVersionStructV3Ptr input(
|
|
MultiVersionStructV3::New(123, MakeRect(5), std::string("hello")));
|
|
MultiVersionStructPtr expected_output(
|
|
MultiVersionStruct::New(123, MakeRect(5), std::string("hello")));
|
|
|
|
MultiVersionStructPtr output =
|
|
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MultiVersionStructV5Ptr input(MultiVersionStructV5::New(
|
|
123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
|
|
MultiVersionStructPtr expected_output(MultiVersionStruct::New(
|
|
123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
|
|
|
|
MultiVersionStructPtr output =
|
|
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MessagePipe pipe;
|
|
MultiVersionStructV7Ptr input(MultiVersionStructV7::New(
|
|
123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8},
|
|
std::move(pipe.handle0), false));
|
|
|
|
MultiVersionStructPtr expected_output(MultiVersionStruct::New(
|
|
123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
|
|
// Save the raw handle value separately so that we can compare later.
|
|
MojoHandle expected_handle = input->f_message_pipe.get().value();
|
|
|
|
MultiVersionStructPtr output =
|
|
SerializeAndDeserialize<MultiVersionStructPtr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_EQ(expected_handle, output->f_message_pipe.get().value());
|
|
output->f_message_pipe.reset();
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
}
|
|
|
|
// Tests deserializing structs as an older version.
|
|
TEST_F(StructTest, Versioning_NewToOld) {
|
|
{
|
|
MultiVersionStructPtr input = MakeMultiVersionStruct();
|
|
MultiVersionStructV7Ptr expected_output(MultiVersionStructV7::New(
|
|
123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
|
|
// Save the raw handle value separately so that we can compare later.
|
|
MojoHandle expected_handle = input->f_message_pipe.get().value();
|
|
|
|
MultiVersionStructV7Ptr output =
|
|
SerializeAndDeserialize<MultiVersionStructV7Ptr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_EQ(expected_handle, output->f_message_pipe.get().value());
|
|
output->f_message_pipe.reset();
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MultiVersionStructPtr input = MakeMultiVersionStruct();
|
|
MultiVersionStructV5Ptr expected_output(MultiVersionStructV5::New(
|
|
123, MakeRect(5), std::string("hello"), std::vector<int8_t>{10, 9, 8}));
|
|
|
|
MultiVersionStructV5Ptr output =
|
|
SerializeAndDeserialize<MultiVersionStructV5Ptr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MultiVersionStructPtr input = MakeMultiVersionStruct();
|
|
MultiVersionStructV3Ptr expected_output(
|
|
MultiVersionStructV3::New(123, MakeRect(5), std::string("hello")));
|
|
|
|
MultiVersionStructV3Ptr output =
|
|
SerializeAndDeserialize<MultiVersionStructV3Ptr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MultiVersionStructPtr input = MakeMultiVersionStruct();
|
|
MultiVersionStructV1Ptr expected_output(
|
|
MultiVersionStructV1::New(123, MakeRect(5)));
|
|
|
|
MultiVersionStructV1Ptr output =
|
|
SerializeAndDeserialize<MultiVersionStructV1Ptr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
|
|
{
|
|
MultiVersionStructPtr input = MakeMultiVersionStruct();
|
|
MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New(123));
|
|
|
|
MultiVersionStructV0Ptr output =
|
|
SerializeAndDeserialize<MultiVersionStructV0Ptr>(std::move(input));
|
|
EXPECT_TRUE(output);
|
|
EXPECT_TRUE(output->Equals(*expected_output));
|
|
}
|
|
}
|
|
|
|
// Serialization test for native struct.
|
|
TEST_F(StructTest, Serialization_NativeStruct) {
|
|
using Data = native::internal::NativeStruct_Data;
|
|
{
|
|
// Serialization of a null native struct.
|
|
native::NativeStructPtr native;
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
Data* data = nullptr;
|
|
EXPECT_EQ(0u, SerializeStruct(native, &message, &context, &data));
|
|
EXPECT_EQ(nullptr, data);
|
|
|
|
native::NativeStructPtr output_native;
|
|
mojo::internal::Deserialize<native::NativeStructDataView>(
|
|
data, &output_native, &context);
|
|
EXPECT_TRUE(output_native.is_null());
|
|
}
|
|
|
|
{
|
|
// Serialization of a native struct with null data.
|
|
native::NativeStructPtr native(native::NativeStruct::New());
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
Data* data = nullptr;
|
|
EXPECT_EQ(32u, SerializeStruct(native, &message, &context, &data));
|
|
EXPECT_EQ(0u, data->data.Get()->size());
|
|
|
|
native::NativeStructPtr output_native;
|
|
mojo::internal::Deserialize<native::NativeStructDataView>(
|
|
data, &output_native, &context);
|
|
EXPECT_TRUE(output_native->data.empty());
|
|
}
|
|
|
|
{
|
|
native::NativeStructPtr native(native::NativeStruct::New());
|
|
native->data = std::vector<uint8_t>{'X', 'Y'};
|
|
|
|
mojo::Message message;
|
|
mojo::internal::SerializationContext context;
|
|
Data* data = nullptr;
|
|
EXPECT_EQ(40u, SerializeStruct(native, &message, &context, &data));
|
|
EXPECT_EQ(2u, data->data.Get()->size());
|
|
|
|
native::NativeStructPtr output_native;
|
|
mojo::internal::Deserialize<native::NativeStructDataView>(
|
|
data, &output_native, &context);
|
|
ASSERT_TRUE(output_native);
|
|
ASSERT_FALSE(output_native->data.empty());
|
|
EXPECT_EQ(2u, output_native->data.size());
|
|
EXPECT_EQ('X', output_native->data[0]);
|
|
EXPECT_EQ('Y', output_native->data[1]);
|
|
}
|
|
}
|
|
|
|
TEST_F(StructTest, Serialization_PublicAPI) {
|
|
{
|
|
// A null struct pointer.
|
|
RectPtr null_struct;
|
|
auto data = Rect::Serialize(&null_struct);
|
|
EXPECT_TRUE(data.empty());
|
|
|
|
// Initialize it to non-null.
|
|
RectPtr output(Rect::New());
|
|
ASSERT_TRUE(Rect::Deserialize(data, &output));
|
|
EXPECT_TRUE(output.is_null());
|
|
}
|
|
|
|
{
|
|
// A struct with no fields.
|
|
EmptyStructPtr empty_struct(EmptyStruct::New());
|
|
auto data = EmptyStruct::Serialize(&empty_struct);
|
|
EXPECT_FALSE(data.empty());
|
|
|
|
EmptyStructPtr output;
|
|
ASSERT_TRUE(EmptyStruct::Deserialize(data, &output));
|
|
EXPECT_FALSE(output.is_null());
|
|
}
|
|
|
|
{
|
|
// A simple struct.
|
|
RectPtr rect = MakeRect();
|
|
RectPtr cloned_rect = rect.Clone();
|
|
auto data = Rect::Serialize(&rect);
|
|
|
|
RectPtr output;
|
|
ASSERT_TRUE(Rect::Deserialize(data, &output));
|
|
EXPECT_TRUE(output.Equals(cloned_rect));
|
|
}
|
|
|
|
{
|
|
// A struct containing other objects.
|
|
std::vector<RectPtr> rects;
|
|
for (size_t i = 0; i < 3; ++i)
|
|
rects.push_back(MakeRect(static_cast<int32_t>(i) + 1));
|
|
NamedRegionPtr region(
|
|
NamedRegion::New(std::string("region"), std::move(rects)));
|
|
|
|
NamedRegionPtr cloned_region = region.Clone();
|
|
auto data = NamedRegion::Serialize(®ion);
|
|
|
|
// Make sure that the serialized result gets pointers encoded properly.
|
|
auto cloned_data = data;
|
|
NamedRegionPtr output;
|
|
ASSERT_TRUE(NamedRegion::Deserialize(cloned_data, &output));
|
|
EXPECT_TRUE(output.Equals(cloned_region));
|
|
}
|
|
|
|
{
|
|
// Deserialization failure.
|
|
RectPtr rect = MakeRect();
|
|
auto data = Rect::Serialize(&rect);
|
|
|
|
NamedRegionPtr output;
|
|
EXPECT_FALSE(NamedRegion::Deserialize(data, &output));
|
|
}
|
|
|
|
{
|
|
// A struct from another component.
|
|
auto pair = test_export2::StringPair::New("hello", "world");
|
|
auto data = test_export2::StringPair::Serialize(&pair);
|
|
|
|
test_export2::StringPairPtr output;
|
|
ASSERT_TRUE(test_export2::StringPair::Deserialize(data, &output));
|
|
EXPECT_TRUE(output.Equals(pair));
|
|
}
|
|
}
|
|
|
|
TEST_F(StructTest, VersionedStructConstructor) {
|
|
auto reordered = ReorderedStruct::New(123, 456, 789);
|
|
EXPECT_EQ(123, reordered->a);
|
|
EXPECT_EQ(456, reordered->b);
|
|
EXPECT_EQ(789, reordered->c);
|
|
|
|
reordered = ReorderedStruct::New(123, 456);
|
|
EXPECT_EQ(123, reordered->a);
|
|
EXPECT_EQ(6, reordered->b);
|
|
EXPECT_EQ(456, reordered->c);
|
|
|
|
reordered = ReorderedStruct::New(123);
|
|
EXPECT_EQ(3, reordered->a);
|
|
EXPECT_EQ(6, reordered->b);
|
|
EXPECT_EQ(123, reordered->c);
|
|
|
|
reordered = ReorderedStruct::New();
|
|
EXPECT_EQ(3, reordered->a);
|
|
EXPECT_EQ(6, reordered->b);
|
|
EXPECT_EQ(1, reordered->c);
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace mojo
|