| // Copyright 2016 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. |
| |
| // TODO(vardhan): Needs a lot more testing. |
| |
| #include <mojo/bindings/struct.h> |
| |
| #include <mojo/bindings/array.h> |
| #include <mojo/bindings/internal/util.h> |
| #include <string.h> |
| |
| #include "gtest/gtest.h" |
| #include "mojo/public/c/tests/bindings/testing_util.h" |
| #include "mojo/public/cpp/system/macros.h" |
| #include "mojo/public/interfaces/bindings/tests/rect.mojom-c.h" |
| #include "mojo/public/interfaces/bindings/tests/test_structs.mojom-c.h" |
| #include "mojo/public/interfaces/bindings/tests/test_unions.mojom-c.h" |
| |
| namespace { |
| |
| #define BYTES_LEFT_AFTER_FIELD(type, field) \ |
| (sizeof(type) - offsetof(type, field)) |
| |
| struct mojo_test_Rect* MakeRect(struct MojomBuffer* buf) { |
| struct mojo_test_Rect* r = static_cast<struct mojo_test_Rect*>( |
| MojomBuffer_Allocate(buf, sizeof(struct mojo_test_Rect))); |
| *r = mojo_test_Rect{// header |
| { |
| sizeof(struct mojo_test_Rect), 0, |
| }, |
| 0u, |
| 0u, |
| 0u, |
| 0u}; |
| return r; |
| } |
| |
| // TODO(vardhan): Move this into string.h/c if it proves useful again. |
| struct MojomStringHeader* MakeMojomStringFromCString(MojomBuffer* buf, |
| const char* chars, |
| size_t num_chars) { |
| struct MojomArrayHeader* arr = MojomArray_New(buf, num_chars, 1); |
| assert(NULL != arr); |
| |
| memcpy((char*)arr + sizeof(MojomStringHeader), chars, num_chars); |
| return (struct MojomStringHeader*)arr; |
| } |
| |
| TEST(StructSerializedSizeTest, Basic) { |
| struct mojo_test_Rect rect = {{ |
| sizeof(struct mojo_test_Rect), 0, |
| }, |
| 0u, |
| 0u, |
| 0u, |
| 0u}; |
| size_t size = mojo_test_Rect_ComputeSerializedSize(&rect); |
| EXPECT_EQ(8U + 16U, size); |
| |
| char buffer_bytes[1000]; |
| struct MojomBuffer buf = {buffer_bytes, sizeof(buffer_bytes), 0}; |
| CopyAndCompare(&buf, &rect, sizeof(rect), mojo_test_Rect_DeepCopy, |
| mojo_test_Rect_EncodePointersAndHandles, |
| mojo_test_Rect_DecodePointersAndHandles); |
| } |
| |
| TEST(StructSerializationTest, StructOfStructs) { |
| char buffer_bytes[1000] = {0}; |
| struct MojomBuffer buf = {buffer_bytes, sizeof(buffer_bytes), 0}; |
| |
| struct mojo_test_RectPair* pair = static_cast<struct mojo_test_RectPair*>( |
| MojomBuffer_Allocate(&buf, sizeof(struct mojo_test_RectPair))); |
| *pair = mojo_test_RectPair{ |
| {sizeof(struct mojo_test_RectPair), 0}, |
| {MakeRect(&buf)}, // first |
| {MakeRect(&buf)}, // second |
| }; |
| |
| EXPECT_EQ(8U + 16U + 2 * (8U + 16U), |
| mojo_test_RectPair_ComputeSerializedSize(pair)); |
| |
| // We save the underlying (unencoded) buffer. We can compare the two after |
| // deserialization to make sure deserialization is correct. |
| char buffer_bytes_copy[sizeof(buffer_bytes)]; |
| memcpy(buffer_bytes_copy, buffer_bytes, sizeof(buffer_bytes_copy)); |
| |
| mojo_test_RectPair_EncodePointersAndHandles(pair, buf.num_bytes_used, NULL); |
| |
| EXPECT_EQ(BYTES_LEFT_AFTER_FIELD(struct mojo_test_RectPair, first), |
| pair->first.offset); |
| EXPECT_EQ(BYTES_LEFT_AFTER_FIELD(struct mojo_test_RectPair, second) + |
| sizeof(struct mojo_test_Rect), |
| pair->second.offset); |
| |
| mojo_test_RectPair_DecodePointersAndHandles( |
| reinterpret_cast<struct mojo_test_RectPair*>(buf.buf), buf.num_bytes_used, |
| NULL, 0); |
| EXPECT_EQ(0, memcmp(buf.buf, buffer_bytes_copy, buf.num_bytes_used)); |
| |
| { |
| char buffer_bytes2[1000] = {0}; |
| struct MojomBuffer buf2 = {buffer_bytes2, sizeof(buffer_bytes2), 0}; |
| CopyAndCompare(&buf2, pair, buf.num_bytes_used, mojo_test_RectPair_DeepCopy, |
| mojo_test_RectPair_EncodePointersAndHandles, |
| mojo_test_RectPair_DecodePointersAndHandles); |
| } |
| } |
| |
| // Tests a struct that has: |
| // - nullable string which isn't null. |
| // - nullable array of rects, which isn't null. |
| TEST(StructSerializationTest, StructOfArrays) { |
| char buffer_bytes[1000]; |
| MojomBuffer buf = {buffer_bytes, sizeof(buffer_bytes), 0}; |
| |
| const char* kRegionName = "region"; |
| |
| struct mojo_test_NamedRegion* named_region = |
| static_cast<struct mojo_test_NamedRegion*>( |
| MojomBuffer_Allocate(&buf, sizeof(struct mojo_test_NamedRegion))); |
| *named_region = mojo_test_NamedRegion{ |
| // header |
| { |
| sizeof(struct mojo_test_NamedRegion), 0, |
| }, |
| {NULL}, |
| {NULL}, |
| }; |
| named_region->name.ptr = |
| MakeMojomStringFromCString(&buf, kRegionName, strlen(kRegionName)); |
| // We save a pointer to |rects| so we can use it after it has been encoded |
| // within |named_region|. |
| auto rects = MojomArray_New(&buf, 4, sizeof(union mojo_test_RectPtr)); |
| named_region->rects.ptr = rects; |
| ASSERT_TRUE(named_region->rects.ptr != NULL); |
| |
| MOJOM_ARRAY_INDEX(named_region->rects.ptr, union mojo_test_RectPtr, 0) |
| ->ptr = MakeRect(&buf); |
| MOJOM_ARRAY_INDEX(named_region->rects.ptr, union mojo_test_RectPtr, 1) |
| ->ptr = MakeRect(&buf); |
| MOJOM_ARRAY_INDEX(named_region->rects.ptr, union mojo_test_RectPtr, 2) |
| ->ptr = MakeRect(&buf); |
| MOJOM_ARRAY_INDEX(named_region->rects.ptr, union mojo_test_RectPtr, 3) |
| ->ptr = MakeRect(&buf); |
| |
| size_t size = mojo_test_NamedRegion_ComputeSerializedSize(named_region); |
| 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); |
| |
| char buffer_bytes_copy[sizeof(buffer_bytes)] = {0}; |
| memcpy(buffer_bytes_copy, buffer_bytes, sizeof(buffer_bytes_copy)); |
| |
| mojo_test_NamedRegion_EncodePointersAndHandles(named_region, |
| buf.num_bytes_used, NULL); |
| |
| EXPECT_EQ(BYTES_LEFT_AFTER_FIELD(struct mojo_test_NamedRegion, name), |
| named_region->name.offset); |
| EXPECT_EQ(BYTES_LEFT_AFTER_FIELD(struct mojo_test_NamedRegion, rects) + |
| MOJOM_INTERNAL_ROUND_TO_8(sizeof(struct MojomStringHeader) + |
| strlen(kRegionName)), |
| named_region->rects.offset); |
| |
| // Test the offsets encoded inside the rect array. |
| EXPECT_EQ(sizeof(union mojo_test_RectPtr) * 4, |
| MOJOM_ARRAY_INDEX(rects, union mojo_test_RectPtr, 0)->offset); |
| EXPECT_EQ(sizeof(union mojo_test_RectPtr) * 3 + sizeof(struct mojo_test_Rect), |
| MOJOM_ARRAY_INDEX(rects, union mojo_test_RectPtr, 1)->offset); |
| EXPECT_EQ( |
| sizeof(union mojo_test_RectPtr) * 2 + sizeof(struct mojo_test_Rect) * 2, |
| MOJOM_ARRAY_INDEX(rects, union mojo_test_RectPtr, 2)->offset); |
| EXPECT_EQ(sizeof(union mojo_test_RectPtr) + sizeof(struct mojo_test_Rect) * 3, |
| MOJOM_ARRAY_INDEX(rects, union mojo_test_RectPtr, 3)->offset); |
| |
| mojo_test_NamedRegion_DecodePointersAndHandles( |
| reinterpret_cast<struct mojo_test_NamedRegion*>(buf.buf), |
| buf.num_bytes_used, NULL, 0); |
| EXPECT_EQ(0, memcmp(buf.buf, buffer_bytes_copy, buf.num_bytes_used)); |
| |
| { |
| char buffer_bytes2[sizeof(buffer_bytes)] = {0}; |
| struct MojomBuffer buf2 = {buffer_bytes2, sizeof(buffer_bytes2), 0}; |
| CopyAndCompare(&buf2, named_region, buf.num_bytes_used, |
| mojo_test_NamedRegion_DeepCopy, |
| mojo_test_NamedRegion_EncodePointersAndHandles, |
| mojo_test_NamedRegion_DecodePointersAndHandles); |
| } |
| } |
| |
| // Tests a struct that has: |
| // - nullable string which is null. |
| // - nullable array of rects, which is null. |
| TEST(StructSerializationTest, StructOfNullArrays) { |
| struct mojo_test_NamedRegion named_region = { |
| // header |
| { |
| sizeof(struct mojo_test_NamedRegion), 0, |
| }, |
| {NULL}, |
| {NULL}, |
| }; |
| struct mojo_test_NamedRegion named_region_copy = named_region; |
| |
| size_t size = mojo_test_NamedRegion_ComputeSerializedSize(&named_region); |
| EXPECT_EQ(8U + // header |
| 8U + // name pointer |
| 8U, // rects pointer |
| size); |
| |
| mojo_test_NamedRegion_EncodePointersAndHandles( |
| &named_region, sizeof(struct mojo_test_NamedRegion), NULL); |
| EXPECT_EQ(0u, named_region.name.offset); |
| EXPECT_EQ(0u, named_region.rects.offset); |
| |
| mojo_test_NamedRegion_DecodePointersAndHandles( |
| &named_region, sizeof(struct mojo_test_NamedRegion), NULL, 0); |
| EXPECT_EQ(0, memcmp(&named_region, &named_region_copy, |
| sizeof(struct mojo_test_NamedRegion))); |
| |
| { |
| char buffer_bytes2[sizeof(named_region)] = {0}; |
| struct MojomBuffer buf2 = {buffer_bytes2, sizeof(buffer_bytes2), 0}; |
| CopyAndCompare(&buf2, &named_region, sizeof(named_region), |
| mojo_test_NamedRegion_DeepCopy, |
| mojo_test_NamedRegion_EncodePointersAndHandles, |
| mojo_test_NamedRegion_DecodePointersAndHandles); |
| } |
| } |
| |
| TEST(StructSerializationTest, StructOfUnion) { |
| struct mojo_test_SmallStructNonNullableUnion u = { |
| // header |
| { |
| sizeof(struct mojo_test_SmallStructNonNullableUnion), |
| 0, // version |
| }, |
| // PodUnion |
| { |
| 16ul, // size |
| mojo_test_PodUnion_Tag_f_int8, // tag, |
| {0}, // data |
| }, |
| }; |
| |
| struct mojo_test_SmallStructNonNullableUnion u_null = { |
| // header |
| { |
| sizeof(struct mojo_test_SmallStructNonNullableUnion), |
| 0, // version |
| }, |
| // PodUnion |
| { |
| 0ul, // size |
| mojo_test_PodUnion_Tag__UNKNOWN__, // tag, |
| {0}, // data |
| }, |
| }; |
| |
| EXPECT_EQ(8U + // header |
| 16U, // union |
| mojo_test_SmallStructNonNullableUnion_ComputeSerializedSize(&u)); |
| EXPECT_EQ( |
| 8U + // header |
| 16U, // union |
| mojo_test_SmallStructNonNullableUnion_ComputeSerializedSize(&u_null)); |
| |
| // Encoding shouldn't have done anything to these structs (they have no |
| // pointers or handles): |
| struct mojo_test_SmallStructNonNullableUnion u_copy = u; |
| mojo_test_SmallStructNonNullableUnion_EncodePointersAndHandles( |
| &u, sizeof(struct mojo_test_SmallStructNonNullableUnion), NULL); |
| EXPECT_EQ(0, memcmp(&u, &u_copy, sizeof(u))); |
| |
| // Similarly, decoding shouldn't change the struct at all: |
| mojo_test_SmallStructNonNullableUnion_DecodePointersAndHandles( |
| &u, sizeof(struct mojo_test_SmallStructNonNullableUnion), NULL, 0); |
| EXPECT_EQ(0, memcmp(&u, &u_copy, sizeof(u))); |
| |
| struct mojo_test_SmallStructNonNullableUnion u_null_copy = u_null; |
| mojo_test_SmallStructNonNullableUnion_EncodePointersAndHandles( |
| &u_null, sizeof(struct mojo_test_SmallStructNonNullableUnion), NULL); |
| EXPECT_EQ(0, memcmp(&u_null, &u_null_copy, sizeof(u_null))); |
| |
| mojo_test_SmallStructNonNullableUnion_DecodePointersAndHandles( |
| &u_null, sizeof(struct mojo_test_SmallStructNonNullableUnion), NULL, 0); |
| EXPECT_EQ(0, memcmp(&u_null, &u_null_copy, sizeof(u_null))); |
| |
| { |
| char buffer_bytes[sizeof(u)] = {0}; |
| struct MojomBuffer buf = {buffer_bytes, sizeof(buffer_bytes), 0}; |
| CopyAndCompare( |
| &buf, &u, sizeof(u), mojo_test_SmallStructNonNullableUnion_DeepCopy, |
| mojo_test_SmallStructNonNullableUnion_EncodePointersAndHandles, |
| mojo_test_SmallStructNonNullableUnion_DecodePointersAndHandles); |
| } |
| } |
| |
| TEST(StructSerializationTest, StructWithHandle) { |
| struct mojo_test_NullableHandleStruct handle_struct = |
| mojo_test_NullableHandleStruct{ |
| // header |
| { |
| sizeof(struct mojo_test_NullableHandleStruct), 0, |
| }, |
| MOJO_HANDLE_INVALID, |
| 13, |
| }; |
| |
| EXPECT_EQ( |
| sizeof(struct mojo_test_NullableHandleStruct), |
| mojo_test_NullableHandleStruct_ComputeSerializedSize(&handle_struct)); |
| |
| MojoHandle handles[1]; |
| struct MojomHandleBuffer handle_buf = {handles, MOJO_ARRAYSIZE(handles), 0u}; |
| mojo_test_NullableHandleStruct_EncodePointersAndHandles( |
| &handle_struct, sizeof(struct mojo_test_NullableHandleStruct), |
| &handle_buf); |
| EXPECT_EQ(0u, handle_buf.num_handles_used); |
| EXPECT_EQ(static_cast<MojoHandle>(-1), handle_struct.h); |
| |
| mojo_test_NullableHandleStruct_DecodePointersAndHandles( |
| &handle_struct, sizeof(struct mojo_test_NullableHandleStruct), handles, |
| MOJO_ARRAYSIZE(handles)); |
| EXPECT_EQ(MOJO_HANDLE_INVALID, handle_struct.h); |
| |
| handle_struct.h = 123; |
| mojo_test_NullableHandleStruct_EncodePointersAndHandles( |
| &handle_struct, sizeof(struct mojo_test_NullableHandleStruct), |
| &handle_buf); |
| EXPECT_EQ(1u, handle_buf.num_handles_used); |
| EXPECT_EQ(static_cast<MojoHandle>(0), handle_struct.h); |
| EXPECT_EQ(static_cast<MojoHandle>(123), handles[0]); |
| |
| mojo_test_NullableHandleStruct_DecodePointersAndHandles( |
| &handle_struct, sizeof(struct mojo_test_NullableHandleStruct), handles, |
| MOJO_ARRAYSIZE(handles)); |
| EXPECT_EQ(static_cast<MojoHandle>(123), handle_struct.h); |
| EXPECT_EQ(MOJO_HANDLE_INVALID, handles[0]); |
| |
| { |
| char buffer_bytes[1000] = {0}; |
| struct MojomBuffer buf = {buffer_bytes, 4, 0}; |
| auto* copied_struct = |
| mojo_test_NullableHandleStruct_DeepCopy(&buf, &handle_struct); |
| // Not enough space: |
| ASSERT_FALSE(copied_struct); |
| buf.buf_size = sizeof(buffer_bytes); |
| copied_struct = |
| mojo_test_NullableHandleStruct_DeepCopy(&buf, &handle_struct); |
| ASSERT_TRUE(copied_struct); |
| // The old and the new copy should both have the handles. |
| EXPECT_EQ(static_cast<MojoHandle>(123), handle_struct.h); |
| EXPECT_EQ(static_cast<MojoHandle>(123), copied_struct->h); |
| } |
| } |
| |
| } // namespace |