// 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 #include #include #include #include "mojo/public/cpp/bindings/lib/fixed_buffer.h" #include "mojo/public/cpp/system/message_pipe.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) { RectPtr rect(Rect::New()); rect->x = 1 * factor; rect->y = 2 * factor; rect->width = 10 * factor; rect->height = 20 * factor; return rect; } 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); } MultiVersionStructPtr MakeMultiVersionStruct() { MultiVersionStructPtr output(MultiVersionStruct::New()); output->f_int32 = 123; output->f_rect = MakeRect(5); output->f_string.emplace("hello"); output->f_array.emplace(3); (*output->f_array)[0] = 10; (*output->f_array)[1] = 9; (*output->f_array)[2] = 8; MessagePipe pipe; output->f_message_pipe = std::move(pipe.handle0); output->f_int16 = 42; return output; } template U SerializeAndDeserialize(T input) { using InputDataType = typename mojo::internal::MojomTypeTraits::Data*; using OutputDataType = typename mojo::internal::MojomTypeTraits::Data*; mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize(input, &context); mojo::internal::FixedBufferForTesting buf(size + 32); InputDataType data; mojo::internal::Serialize(input, &buf, &data, &context); // Set the subsequent area to a special value, so that we can find out if we // mistakenly access the area. void* subsequent_area = buf.Allocate(32); memset(subsequent_area, 0xAA, 32); OutputDataType output_data = reinterpret_cast(data); U output; mojo::internal::Deserialize(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()); size_t size = mojo::internal::PrepareToSerialize(rect, nullptr); EXPECT_EQ(8U + 16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::Rect_Data* data; mojo::internal::Serialize(rect, &buf, &data, nullptr); RectPtr rect2; mojo::internal::Deserialize(data, &rect2, nullptr); 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()); pair->first = MakeRect(); pair->second = MakeRect(); size_t size = mojo::internal::PrepareToSerialize(pair, nullptr); EXPECT_EQ(8U + 16U + 2 * (8U + 16U), size); mojo::internal::FixedBufferForTesting buf(size); internal::RectPair_Data* data; mojo::internal::Serialize(pair, &buf, &data, nullptr); RectPairPtr pair2; mojo::internal::Deserialize(data, &pair2, nullptr); CheckRect(*pair2->first); CheckRect(*pair2->second); } // Serialization test of a struct with an array member. TEST_F(StructTest, Serialization_ArrayPointers) { NamedRegionPtr region(NamedRegion::New()); region->name.emplace("region"); region->rects.emplace(4); for (size_t i = 0; i < region->rects->size(); ++i) (*region->rects)[i] = MakeRect(static_cast(i) + 1); size_t size = mojo::internal::PrepareToSerialize(region, nullptr); 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) size); mojo::internal::FixedBufferForTesting buf(size); internal::NamedRegion_Data* data; mojo::internal::Serialize(region, &buf, &data, nullptr); NamedRegionPtr region2; mojo::internal::Deserialize(data, ®ion2, nullptr); 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(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); size_t size = mojo::internal::PrepareToSerialize(region, nullptr); EXPECT_EQ(8U + // header 8U + // name pointer 8U, // rects pointer size); mojo::internal::FixedBufferForTesting buf(size); internal::NamedRegion_Data* data; mojo::internal::Serialize(region, &buf, &data, nullptr); NamedRegionPtr region2; mojo::internal::Deserialize(data, ®ion2, nullptr); EXPECT_FALSE(region2->name); EXPECT_FALSE(region2->rects); } // Tests deserializing structs as a newer version. TEST_F(StructTest, Versioning_OldToNew) { { MultiVersionStructV0Ptr input(MultiVersionStructV0::New()); input->f_int32 = 123; MultiVersionStructPtr expected_output(MultiVersionStruct::New()); expected_output->f_int32 = 123; MultiVersionStructPtr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } { MultiVersionStructV1Ptr input(MultiVersionStructV1::New()); input->f_int32 = 123; input->f_rect = MakeRect(5); MultiVersionStructPtr expected_output(MultiVersionStruct::New()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); MultiVersionStructPtr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } { MultiVersionStructV3Ptr input(MultiVersionStructV3::New()); input->f_int32 = 123; input->f_rect = MakeRect(5); input->f_string.emplace("hello"); MultiVersionStructPtr expected_output(MultiVersionStruct::New()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); expected_output->f_string.emplace("hello"); MultiVersionStructPtr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } { MultiVersionStructV5Ptr input(MultiVersionStructV5::New()); input->f_int32 = 123; input->f_rect = MakeRect(5); input->f_string.emplace("hello"); input->f_array.emplace(3); (*input->f_array)[0] = 10; (*input->f_array)[1] = 9; (*input->f_array)[2] = 8; MultiVersionStructPtr expected_output(MultiVersionStruct::New()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); expected_output->f_string.emplace("hello"); expected_output->f_array.emplace(3); (*expected_output->f_array)[0] = 10; (*expected_output->f_array)[1] = 9; (*expected_output->f_array)[2] = 8; MultiVersionStructPtr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } { MultiVersionStructV7Ptr input(MultiVersionStructV7::New()); input->f_int32 = 123; input->f_rect = MakeRect(5); input->f_string.emplace("hello"); input->f_array.emplace(3); (*input->f_array)[0] = 10; (*input->f_array)[1] = 9; (*input->f_array)[2] = 8; MessagePipe pipe; input->f_message_pipe = std::move(pipe.handle0); MultiVersionStructPtr expected_output(MultiVersionStruct::New()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); expected_output->f_string.emplace("hello"); expected_output->f_array.emplace(3); (*expected_output->f_array)[0] = 10; (*expected_output->f_array)[1] = 9; (*expected_output->f_array)[2] = 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(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()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); expected_output->f_string.emplace("hello"); expected_output->f_array.emplace(3); (*expected_output->f_array)[0] = 10; (*expected_output->f_array)[1] = 9; (*expected_output->f_array)[2] = 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(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()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); expected_output->f_string.emplace("hello"); expected_output->f_array.emplace(3); (*expected_output->f_array)[0] = 10; (*expected_output->f_array)[1] = 9; (*expected_output->f_array)[2] = 8; MultiVersionStructV5Ptr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } { MultiVersionStructPtr input = MakeMultiVersionStruct(); MultiVersionStructV3Ptr expected_output(MultiVersionStructV3::New()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); expected_output->f_string.emplace("hello"); MultiVersionStructV3Ptr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } { MultiVersionStructPtr input = MakeMultiVersionStruct(); MultiVersionStructV1Ptr expected_output(MultiVersionStructV1::New()); expected_output->f_int32 = 123; expected_output->f_rect = MakeRect(5); MultiVersionStructV1Ptr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } { MultiVersionStructPtr input = MakeMultiVersionStruct(); MultiVersionStructV0Ptr expected_output(MultiVersionStructV0::New()); expected_output->f_int32 = 123; MultiVersionStructV0Ptr output = SerializeAndDeserialize(std::move(input)); EXPECT_TRUE(output); EXPECT_TRUE(output->Equals(*expected_output)); } } // Serialization test for native struct. TEST_F(StructTest, Serialization_NativeStruct) { using Data = mojo::internal::NativeStruct_Data; { // Serialization of a null native struct. NativeStructPtr native; size_t size = mojo::internal::PrepareToSerialize(native, nullptr); EXPECT_EQ(0u, size); mojo::internal::FixedBufferForTesting buf(size); Data* data = nullptr; mojo::internal::Serialize(std::move(native), &buf, &data, nullptr); EXPECT_EQ(nullptr, data); NativeStructPtr output_native; mojo::internal::Deserialize(data, &output_native, nullptr); EXPECT_TRUE(output_native.is_null()); } { // Serialization of a native struct with null data. NativeStructPtr native(NativeStruct::New()); size_t size = mojo::internal::PrepareToSerialize(native, nullptr); EXPECT_EQ(0u, size); mojo::internal::FixedBufferForTesting buf(size); Data* data = nullptr; mojo::internal::Serialize(std::move(native), &buf, &data, nullptr); EXPECT_EQ(nullptr, data); NativeStructPtr output_native; mojo::internal::Deserialize(data, &output_native, nullptr); EXPECT_TRUE(output_native.is_null()); } { NativeStructPtr native(NativeStruct::New()); native->data = Array(2); native->data[0] = 'X'; native->data[1] = 'Y'; size_t size = mojo::internal::PrepareToSerialize(native, nullptr); EXPECT_EQ(16u, size); mojo::internal::FixedBufferForTesting buf(size); Data* data = nullptr; mojo::internal::Serialize(std::move(native), &buf, &data, nullptr); EXPECT_NE(nullptr, data); NativeStructPtr output_native; mojo::internal::Deserialize(data, &output_native, nullptr); EXPECT_FALSE(output_native.is_null()); EXPECT_FALSE(output_native->data.is_null()); 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; mojo::Array data = Rect::Serialize(&null_struct); EXPECT_TRUE(data.empty()); // Initialize it to non-null. RectPtr output(Rect::New()); ASSERT_TRUE(Rect::Deserialize(std::move(data), &output)); EXPECT_TRUE(output.is_null()); } { // A struct with no fields. EmptyStructPtr empty_struct(EmptyStruct::New()); mojo::Array data = EmptyStruct::Serialize(&empty_struct); EXPECT_FALSE(data.empty()); EmptyStructPtr output; ASSERT_TRUE(EmptyStruct::Deserialize(std::move(data), &output)); EXPECT_FALSE(output.is_null()); } { // A simple struct. RectPtr rect = MakeRect(); RectPtr cloned_rect = rect.Clone(); mojo::Array data = Rect::Serialize(&rect); RectPtr output; ASSERT_TRUE(Rect::Deserialize(std::move(data), &output)); EXPECT_TRUE(output.Equals(cloned_rect)); } { // A struct containing other objects. NamedRegionPtr region(NamedRegion::New()); region->name.emplace("region"); region->rects.emplace(3); for (size_t i = 0; i < region->rects->size(); ++i) (*region->rects)[i] = MakeRect(static_cast(i) + 1); NamedRegionPtr cloned_region = region.Clone(); mojo::Array data = NamedRegion::Serialize(®ion); // Make sure that the serialized result gets pointers encoded properly. mojo::Array cloned_data = data.Clone(); NamedRegionPtr output; ASSERT_TRUE(NamedRegion::Deserialize(std::move(cloned_data), &output)); EXPECT_TRUE(output.Equals(cloned_region)); } { // Deserialization failure. RectPtr rect = MakeRect(); mojo::Array data = Rect::Serialize(&rect); NamedRegionPtr output; EXPECT_FALSE(NamedRegion::Deserialize(std::move(data), &output)); } } } // namespace test } // namespace mojo