| // 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 "gtest/gtest.h" |
| #include "mojo/public/cpp/bindings/array.h" |
| #include "mojo/public/cpp/bindings/lib/array_internal.h" |
| #include "mojo/public/cpp/bindings/lib/array_serialization.h" |
| #include "mojo/public/cpp/bindings/lib/fixed_buffer.h" |
| #include "mojo/public/cpp/bindings/tests/container_test_util.h" |
| #include "mojo/public/cpp/bindings/tests/iterator_test_util.h" |
| #include "mojo/public/interfaces/bindings/tests/test_arrays.mojom.h" |
| #include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" |
| |
| namespace mojo { |
| namespace test { |
| namespace { |
| |
| using mojo::internal::Array_Data; |
| using mojo::internal::ArrayValidateParams; |
| using mojo::internal::FixedBufferForTesting; |
| using mojo::internal::String_Data; |
| |
| // Tests that basic Array operations work. |
| TEST(ArrayTest, Basic) { |
| auto array = Array<uint8_t>::New(8); |
| for (size_t i = 0u; i < array.size(); ++i) { |
| uint8_t val = static_cast<uint8_t>(i * 2); |
| array[i] = val; |
| EXPECT_EQ(val, array.at(i)); |
| } |
| |
| EXPECT_EQ(0u, *array.data()); |
| EXPECT_EQ(2u, *(array.data() + 1)); |
| EXPECT_EQ(4u, *(array.data() + 2)); |
| } |
| |
| TEST(ArrayTest, Testability) { |
| Array<int32_t> array; |
| EXPECT_FALSE(array); |
| EXPECT_TRUE(array.is_null()); |
| |
| array.push_back(123); |
| EXPECT_TRUE(array); |
| EXPECT_FALSE(array.is_null()); |
| } |
| |
| void NullptrConstructorTestHelper(Array<int32_t> array) { |
| EXPECT_FALSE(array); |
| EXPECT_TRUE(array.is_null()); |
| EXPECT_EQ(0u, array.size()); |
| } |
| |
| TEST(ArrayTest, NullptrConstructor) { |
| Array<int32_t> array(nullptr); |
| EXPECT_FALSE(array); |
| EXPECT_TRUE(array.is_null()); |
| EXPECT_EQ(0u, array.size()); |
| |
| array.push_back(123); |
| EXPECT_TRUE(array); |
| EXPECT_FALSE(array.is_null()); |
| EXPECT_EQ(1u, array.size()); |
| |
| // Test some implicit constructions of |Array<int32_t>| from a |nullptr|. |
| array = nullptr; |
| NullptrConstructorTestHelper(nullptr); |
| } |
| |
| // Tests that basic Array<bool> operations work. |
| TEST(ArrayTest, Bool) { |
| auto array = Array<bool>::New(64); |
| for (size_t i = 0; i < array.size(); ++i) { |
| bool val = i % 3 == 0; |
| array[i] = val; |
| EXPECT_EQ(val, array.at(i)); |
| } |
| } |
| |
| // Tests that Array<ScopedMessagePipeHandle> supports transferring handles. |
| TEST(ArrayTest, Handle) { |
| MessagePipe pipe; |
| auto handles = Array<ScopedMessagePipeHandle>::New(2); |
| handles[0] = pipe.handle0.Pass(); |
| handles[1].reset(pipe.handle1.release()); |
| |
| EXPECT_FALSE(pipe.handle0.is_valid()); |
| EXPECT_FALSE(pipe.handle1.is_valid()); |
| |
| Array<ScopedMessagePipeHandle> handles2 = handles.Pass(); |
| EXPECT_TRUE(handles2[0].is_valid()); |
| EXPECT_TRUE(handles2[1].is_valid()); |
| |
| ScopedMessagePipeHandle pipe_handle = handles2[0].Pass(); |
| EXPECT_TRUE(pipe_handle.is_valid()); |
| EXPECT_FALSE(handles2[0].is_valid()); |
| } |
| |
| // Tests that Array<ScopedMessagePipeHandle> supports closing handles. |
| TEST(ArrayTest, HandlesAreClosed) { |
| MessagePipe pipe; |
| MojoHandle pipe0_value = pipe.handle0.get().value(); |
| MojoHandle pipe1_value = pipe.handle0.get().value(); |
| |
| { |
| auto handles = Array<ScopedMessagePipeHandle>::New(2); |
| handles[0] = pipe.handle0.Pass(); |
| handles[1].reset(pipe.handle0.release()); |
| } |
| |
| // We expect the pipes to have been closed. |
| EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe0_value)); |
| EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(pipe1_value)); |
| } |
| |
| TEST(ArrayTest, Clone) { |
| { |
| // Test POD. |
| auto array = Array<int32_t>::New(3); |
| for (size_t i = 0; i < array.size(); ++i) |
| array[i] = static_cast<int32_t>(i); |
| |
| Array<int32_t> clone_array = array.Clone(); |
| EXPECT_EQ(array.size(), clone_array.size()); |
| for (size_t i = 0; i < array.size(); ++i) |
| EXPECT_EQ(array[i], clone_array[i]); |
| } |
| |
| { |
| // Test copyable object. |
| auto array = Array<String>::New(2); |
| array[0] = "hello"; |
| array[1] = "world"; |
| |
| Array<String> clone_array = array.Clone(); |
| EXPECT_EQ(array.size(), clone_array.size()); |
| for (size_t i = 0; i < array.size(); ++i) |
| EXPECT_EQ(array[i], clone_array[i]); |
| } |
| |
| { |
| // Test struct. |
| auto array = Array<RectPtr>::New(2); |
| array[1] = Rect::New(); |
| array[1]->x = 1; |
| array[1]->y = 2; |
| array[1]->width = 3; |
| array[1]->height = 4; |
| |
| Array<RectPtr> clone_array = array.Clone(); |
| EXPECT_EQ(array.size(), clone_array.size()); |
| EXPECT_TRUE(clone_array[0].is_null()); |
| EXPECT_EQ(array[1]->x, clone_array[1]->x); |
| EXPECT_EQ(array[1]->y, clone_array[1]->y); |
| EXPECT_EQ(array[1]->width, clone_array[1]->width); |
| EXPECT_EQ(array[1]->height, clone_array[1]->height); |
| } |
| |
| { |
| // Test array of array. |
| auto array = Array<Array<int8_t>>::New(2); |
| array[1] = Array<int8_t>::New(2); |
| array[1][0] = 0; |
| array[1][1] = 1; |
| |
| Array<Array<int8_t>> clone_array = array.Clone(); |
| EXPECT_EQ(array.size(), clone_array.size()); |
| EXPECT_TRUE(clone_array[0].is_null()); |
| EXPECT_EQ(array[1].size(), clone_array[1].size()); |
| EXPECT_EQ(array[1][0], clone_array[1][0]); |
| EXPECT_EQ(array[1][1], clone_array[1][1]); |
| } |
| |
| { |
| // Test that array of handles still works although Clone() is not available. |
| auto array = Array<ScopedMessagePipeHandle>::New(10); |
| EXPECT_FALSE(array[0].is_valid()); |
| } |
| } |
| |
| TEST(ArrayTest, Serialization_ArrayOfPOD) { |
| auto array = Array<int32_t>::New(4); |
| for (size_t i = 0; i < array.size(); ++i) |
| array[i] = static_cast<int32_t>(i); |
| |
| size_t size = GetSerializedSize_(array); |
| EXPECT_EQ(8U + 4 * 4U, size); |
| |
| FixedBufferForTesting buf(size); |
| Array_Data<int32_t>* data = nullptr; |
| ArrayValidateParams validate_params(0, false, nullptr); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf, &data, &validate_params)); |
| |
| Array<int32_t> array2; |
| Deserialize_(data, &array2); |
| |
| EXPECT_EQ(4U, array2.size()); |
| for (size_t i = 0; i < array2.size(); ++i) |
| EXPECT_EQ(static_cast<int32_t>(i), array2[i]); |
| } |
| |
| TEST(ArrayTest, Serialization_EmptyArrayOfPOD) { |
| auto array = Array<int32_t>::New(0); |
| size_t size = GetSerializedSize_(array); |
| EXPECT_EQ(8U, size); |
| |
| FixedBufferForTesting buf(size); |
| Array_Data<int32_t>* data = nullptr; |
| ArrayValidateParams validate_params(0, false, nullptr); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf, &data, &validate_params)); |
| |
| Array<int32_t> array2; |
| Deserialize_(data, &array2); |
| EXPECT_EQ(0U, array2.size()); |
| } |
| |
| TEST(ArrayTest, Serialization_ArrayOfArrayOfPOD) { |
| auto array = Array<Array<int32_t>>::New(2); |
| for (size_t j = 0; j < array.size(); ++j) { |
| auto inner = Array<int32_t>::New(4); |
| for (size_t i = 0; i < inner.size(); ++i) |
| inner[i] = static_cast<int32_t>(i + (j * 10)); |
| array[j] = inner.Pass(); |
| } |
| |
| size_t size = GetSerializedSize_(array); |
| EXPECT_EQ(8U + 2 * 8U + 2 * (8U + 4 * 4U), size); |
| |
| FixedBufferForTesting buf(size); |
| Array_Data<Array_Data<int32_t>*>* data = nullptr; |
| ArrayValidateParams validate_params( |
| 0, false, new ArrayValidateParams(0, false, nullptr)); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf, &data, &validate_params)); |
| |
| Array<Array<int32_t>> array2; |
| Deserialize_(data, &array2); |
| |
| EXPECT_EQ(2U, array2.size()); |
| for (size_t j = 0; j < array2.size(); ++j) { |
| const Array<int32_t>& inner = array2[j]; |
| EXPECT_EQ(4U, inner.size()); |
| for (size_t i = 0; i < inner.size(); ++i) |
| EXPECT_EQ(static_cast<int32_t>(i + (j * 10)), inner[i]); |
| } |
| } |
| |
| TEST(ArrayTest, Serialization_ArrayOfScopedEnum) { |
| enum class TestEnum : int32_t { |
| E0, |
| E1, |
| E2, |
| E3, |
| }; |
| static const TestEnum TEST_VALS[] = { |
| TestEnum::E0, TestEnum::E2, TestEnum::E1, TestEnum::E3, |
| TestEnum::E2, TestEnum::E2, TestEnum::E2, TestEnum::E0, |
| }; |
| |
| auto array = Array<TestEnum>::New(MOJO_ARRAYSIZE(TEST_VALS)); |
| for (size_t i = 0; i < array.size(); ++i) |
| array[i] = TEST_VALS[i]; |
| |
| size_t size = GetSerializedSize_(array); |
| EXPECT_EQ(8U + (MOJO_ARRAYSIZE(TEST_VALS) * sizeof(int32_t)), size); |
| |
| FixedBufferForTesting buf(size); |
| Array_Data<int32_t>* data = nullptr; |
| ArrayValidateParams validate_params(0, false, nullptr); |
| SerializeArray_(&array, &buf, &data, &validate_params); |
| |
| Array<TestEnum> array2; |
| Deserialize_(data, &array2); |
| |
| EXPECT_EQ(MOJO_ARRAYSIZE(TEST_VALS), array2.size()); |
| for (size_t i = 0; i < array2.size(); ++i) |
| EXPECT_EQ(TEST_VALS[i], array2[i]); |
| } |
| |
| TEST(ArrayTest, Serialization_ArrayOfBool) { |
| auto array = Array<bool>::New(10); |
| for (size_t i = 0; i < array.size(); ++i) |
| array[i] = i % 2 ? true : false; |
| |
| size_t size = GetSerializedSize_(array); |
| EXPECT_EQ(8U + 8U, size); |
| |
| FixedBufferForTesting buf(size); |
| Array_Data<bool>* data = nullptr; |
| ArrayValidateParams validate_params(0, false, nullptr); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf, &data, &validate_params)); |
| |
| Array<bool> array2; |
| Deserialize_(data, &array2); |
| |
| EXPECT_EQ(10U, array2.size()); |
| for (size_t i = 0; i < array2.size(); ++i) |
| EXPECT_EQ(i % 2 ? true : false, array2[i]); |
| } |
| |
| TEST(ArrayTest, Serialization_ArrayOfString) { |
| auto array = Array<String>::New(10); |
| for (size_t i = 0; i < array.size(); ++i) { |
| char c = 'A' + static_cast<char>(i); |
| array[i] = String(&c, 1); |
| } |
| |
| size_t size = GetSerializedSize_(array); |
| EXPECT_EQ(8U + // array header |
| 10 * 8U + // array payload (10 pointers) |
| 10 * (8U + // string header |
| 8U), // string length of 1 padded to 8 |
| size); |
| |
| FixedBufferForTesting buf(size); |
| Array_Data<String_Data*>* data = nullptr; |
| ArrayValidateParams validate_params( |
| 0, false, new ArrayValidateParams(0, false, nullptr)); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf, &data, &validate_params)); |
| |
| Array<String> array2; |
| Deserialize_(data, &array2); |
| |
| EXPECT_EQ(10U, array2.size()); |
| for (size_t i = 0; i < array2.size(); ++i) { |
| char c = 'A' + static_cast<char>(i); |
| EXPECT_EQ(String(&c, 1), array2[i]); |
| } |
| } |
| |
| // Tests serializing and deserializing an Array<Handle>. |
| TEST(ArrayTest, Serialization_ArrayOfHandle) { |
| auto array = Array<ScopedHandleBase<MessagePipeHandle>>::New(4); |
| MessagePipe p0; |
| MessagePipe p1; |
| // array[0] is left invalid. |
| array[1] = p0.handle1.Pass(); |
| array[2] = p1.handle0.Pass(); |
| array[3] = p1.handle1.Pass(); |
| |
| size_t size = GetSerializedSize_(array); |
| EXPECT_EQ(8U // array header |
| + (4U * 4), // 4 handles |
| size); |
| |
| // We're going to reuse this buffer.. twice. |
| FixedBufferForTesting buf(size * 3); |
| Array_Data<MessagePipeHandle>* data = nullptr; |
| |
| // 1. Serialization should fail on non-nullable invalid Handle. |
| ArrayValidateParams validate_params(4, false, nullptr); |
| EXPECT_EQ(mojo::internal::ValidationError::UNEXPECTED_INVALID_HANDLE, |
| SerializeArray_(&array, &buf, &data, &validate_params)); |
| |
| // We failed trying to transfer the first handle, so the rest are left valid. |
| EXPECT_FALSE(array[0].is_valid()); |
| EXPECT_TRUE(array[1].is_valid()); |
| EXPECT_TRUE(array[2].is_valid()); |
| EXPECT_TRUE(array[3].is_valid()); |
| |
| // 2. Serialization should pass on nullable invalid Handle. |
| ArrayValidateParams validate_params_nullable(4, true, nullptr); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf, &data, &validate_params_nullable)); |
| |
| EXPECT_FALSE(array[0].is_valid()); |
| EXPECT_FALSE(array[1].is_valid()); |
| EXPECT_FALSE(array[2].is_valid()); |
| EXPECT_FALSE(array[3].is_valid()); |
| |
| Deserialize_(data, &array); |
| EXPECT_FALSE(array[0].is_valid()); |
| EXPECT_TRUE(array[1].is_valid()); |
| EXPECT_TRUE(array[2].is_valid()); |
| EXPECT_TRUE(array[3].is_valid()); |
| } |
| |
| TEST(ArrayTest, Serialization_StructWithArraysOfHandles) { |
| StructWithHandles handles_struct; |
| MessagePipe handle_pair_0; |
| } |
| |
| // Test serializing and deserializing an Array<InterfacePtr>. |
| TEST(ArrayTest, Serialization_ArrayOfInterfacePtr) { |
| auto iface_array = Array<mojo::InterfaceHandle<TestInterface>>::New(1); |
| size_t size = GetSerializedSize_(iface_array); |
| EXPECT_EQ(8U // array header |
| + (8U * 1), // Interface_Data * number of elements |
| size); |
| |
| FixedBufferForTesting buf(size * 3); |
| Array_Data<mojo::internal::Interface_Data>* output = nullptr; |
| |
| // 1. Invalid InterfacePtr should fail serialization. |
| ArrayValidateParams validate_non_nullable(1, false, nullptr); |
| EXPECT_EQ( |
| mojo::internal::ValidationError::UNEXPECTED_INVALID_HANDLE, |
| SerializeArray_(&iface_array, &buf, &output, &validate_non_nullable)); |
| EXPECT_FALSE(iface_array[0].is_valid()); |
| |
| // 2. Invalid InterfacePtr should pass if array elements are nullable. |
| ArrayValidateParams validate_nullable(1, true, nullptr); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&iface_array, &buf, &output, &validate_nullable)); |
| EXPECT_FALSE(iface_array[0].is_valid()); |
| |
| // 3. Should serialize successfully if InterfacePtr is valid. |
| TestInterfacePtr iface_ptr; |
| auto iface_req = GetProxy(&iface_ptr); |
| |
| iface_array[0] = iface_ptr.Pass(); |
| EXPECT_TRUE(iface_array[0].is_valid()); |
| |
| EXPECT_EQ( |
| mojo::internal::ValidationError::NONE, |
| SerializeArray_(&iface_array, &buf, &output, &validate_non_nullable)); |
| EXPECT_FALSE(iface_array[0].is_valid()); |
| |
| Deserialize_(output, &iface_array); |
| EXPECT_TRUE(iface_array[0].is_valid()); |
| } |
| |
| // Test serializing and deserializing a struct with an Array<> of another struct |
| // which has an InterfacePtr. |
| TEST(ArrayTest, Serialization_StructWithArrayOfInterfacePtr) { |
| StructWithInterfaceArray struct_arr_iface; |
| struct_arr_iface.structs_array = Array<StructWithInterfacePtr>::New(1); |
| struct_arr_iface.nullable_structs_array = |
| Array<StructWithInterfacePtr>::New(1); |
| |
| size_t size = GetSerializedSize_(struct_arr_iface); |
| EXPECT_EQ(8U // struct header |
| + 8U // offset to |structs_array| |
| + (8U // array header |
| + 8U) // offset to StructWithInterface (nullptr) |
| + 8U // offset to |structs_nullable_array| |
| + 8U // offset to |nullable_structs_array| |
| + (8U // array header |
| + 8U) // offset to StructWithinInterface (nullptr) |
| + 8U, // offset to |nullable_structs_nullable_array| |
| size); |
| |
| FixedBufferForTesting buf(size * 2); |
| StructWithInterfaceArray::Data_* struct_arr_iface_data = nullptr; |
| // 1. This should fail because |structs_array| has an invalid InterfacePtr<> |
| // and it is not nullable. |
| EXPECT_EQ(mojo::internal::ValidationError::UNEXPECTED_NULL_POINTER, |
| Serialize_(&struct_arr_iface, &buf, &struct_arr_iface_data)); |
| |
| // 2. Adding in a struct with a valid InterfacePtr<> will let it serialize. |
| TestInterfacePtr iface_ptr; |
| auto iface_req = GetProxy(&iface_ptr); |
| |
| StructWithInterfacePtr iface_struct(StructWithInterface::New()); |
| iface_struct->iptr = iface_ptr.Pass(); |
| |
| struct_arr_iface.structs_array[0] = iface_struct.Pass(); |
| ASSERT_TRUE(struct_arr_iface.structs_array[0]->iptr.is_valid()); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| Serialize_(&struct_arr_iface, &buf, &struct_arr_iface_data)); |
| |
| EXPECT_FALSE(struct_arr_iface.structs_array[0]->iptr.is_valid()); |
| |
| Deserialize_(struct_arr_iface_data, &struct_arr_iface); |
| EXPECT_TRUE(struct_arr_iface.structs_array[0]->iptr.is_valid()); |
| } |
| |
| // Test serializing and deserializing a struct with an Array<> of interface |
| // requests. |
| TEST(ArrayTest, Serialization_StructWithArrayOfIntefaceRequest) { |
| StructWithInterfaceRequests struct_arr_iface_req; |
| struct_arr_iface_req.req_array = |
| Array<InterfaceRequest<TestInterface>>::New(1); |
| struct_arr_iface_req.nullable_req_array = |
| Array<InterfaceRequest<TestInterface>>::New(1); |
| |
| size_t size = GetSerializedSize_(struct_arr_iface_req); |
| EXPECT_EQ(8U // struct header |
| + 8U // offset to |req_array| |
| + (8U // array header for |req_array| |
| + 4U // InterfaceRequest |
| + 4U) // alignment padding |
| + 8U // offset to |req_nullable_array| |
| + 8U // offset to |nullable_req_array| |
| + (8U // array header for |nullable_req_array| |
| + 4U // InterfaceRequest |
| + 4U) // alignment padding |
| + 8U, // offset to |nullable_req_nullable_array| |
| size); |
| |
| FixedBufferForTesting buf(size * 2); |
| StructWithInterfaceRequests::Data_* struct_arr_iface_req_data; |
| // 1. This should fail because |req_array| has an invalid InterfaceRequest<> |
| // and it is not nullable. |
| EXPECT_EQ( |
| mojo::internal::ValidationError::UNEXPECTED_INVALID_HANDLE, |
| Serialize_(&struct_arr_iface_req, &buf, &struct_arr_iface_req_data)); |
| |
| // 2. Adding in a valid InterfacePtr<> will let it serialize. |
| TestInterfacePtr iface_ptr; |
| struct_arr_iface_req.req_array[0] = GetProxy(&iface_ptr); |
| EXPECT_TRUE(struct_arr_iface_req.req_array[0].is_pending()); |
| |
| EXPECT_EQ( |
| mojo::internal::ValidationError::NONE, |
| Serialize_(&struct_arr_iface_req, &buf, &struct_arr_iface_req_data)); |
| |
| EXPECT_FALSE(struct_arr_iface_req.req_array[0].is_pending()); |
| |
| Deserialize_(struct_arr_iface_req_data, &struct_arr_iface_req); |
| EXPECT_TRUE(struct_arr_iface_req.req_array[0].is_pending()); |
| } |
| |
| TEST(ArrayTest, Resize_Copyable) { |
| ASSERT_EQ(0u, CopyableType::num_instances()); |
| auto array = mojo::Array<CopyableType>::New(3); |
| std::vector<CopyableType*> value_ptrs; |
| value_ptrs.push_back(array[0].ptr()); |
| value_ptrs.push_back(array[1].ptr()); |
| |
| for (size_t i = 0; i < array.size(); i++) |
| array[i].ResetCopied(); |
| |
| array.resize(2); |
| ASSERT_EQ(2u, array.size()); |
| EXPECT_EQ(array.size(), CopyableType::num_instances()); |
| for (size_t i = 0; i < array.size(); i++) { |
| EXPECT_FALSE(array[i].copied()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| } |
| |
| array.resize(3); |
| array[2].ResetCopied(); |
| ASSERT_EQ(3u, array.size()); |
| EXPECT_EQ(array.size(), CopyableType::num_instances()); |
| for (size_t i = 0; i < array.size(); i++) |
| EXPECT_FALSE(array[i].copied()); |
| value_ptrs.push_back(array[2].ptr()); |
| |
| size_t capacity = array.storage().capacity(); |
| array.resize(capacity); |
| ASSERT_EQ(capacity, array.size()); |
| EXPECT_EQ(array.size(), CopyableType::num_instances()); |
| for (size_t i = 0; i < 3; i++) |
| EXPECT_FALSE(array[i].copied()); |
| for (size_t i = 3; i < array.size(); i++) { |
| array[i].ResetCopied(); |
| value_ptrs.push_back(array[i].ptr()); |
| } |
| |
| array.resize(capacity + 2); |
| ASSERT_EQ(capacity + 2, array.size()); |
| EXPECT_EQ(array.size(), CopyableType::num_instances()); |
| for (size_t i = 0; i < capacity; i++) { |
| EXPECT_TRUE(array[i].copied()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| } |
| array.reset(); |
| EXPECT_EQ(0u, CopyableType::num_instances()); |
| EXPECT_FALSE(array); |
| array.resize(0); |
| EXPECT_EQ(0u, CopyableType::num_instances()); |
| EXPECT_TRUE(array); |
| } |
| |
| TEST(ArrayTest, Resize_MoveOnly) { |
| ASSERT_EQ(0u, MoveOnlyType::num_instances()); |
| auto array = mojo::Array<MoveOnlyType>::New(3); |
| std::vector<MoveOnlyType*> value_ptrs; |
| value_ptrs.push_back(array[0].ptr()); |
| value_ptrs.push_back(array[1].ptr()); |
| |
| for (size_t i = 0; i < array.size(); i++) |
| EXPECT_FALSE(array[i].moved()); |
| |
| array.resize(2); |
| ASSERT_EQ(2u, array.size()); |
| EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); |
| for (size_t i = 0; i < array.size(); i++) { |
| EXPECT_FALSE(array[i].moved()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| } |
| |
| array.resize(3); |
| ASSERT_EQ(3u, array.size()); |
| EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); |
| for (size_t i = 0; i < array.size(); i++) |
| EXPECT_FALSE(array[i].moved()); |
| value_ptrs.push_back(array[2].ptr()); |
| |
| size_t capacity = array.storage().capacity(); |
| array.resize(capacity); |
| ASSERT_EQ(capacity, array.size()); |
| EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); |
| for (size_t i = 0; i < array.size(); i++) |
| EXPECT_FALSE(array[i].moved()); |
| for (size_t i = 3; i < array.size(); i++) |
| value_ptrs.push_back(array[i].ptr()); |
| |
| array.resize(capacity + 2); |
| ASSERT_EQ(capacity + 2, array.size()); |
| EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); |
| for (size_t i = 0; i < capacity; i++) { |
| EXPECT_TRUE(array[i].moved()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| } |
| for (size_t i = capacity; i < array.size(); i++) |
| EXPECT_FALSE(array[i].moved()); |
| |
| array.reset(); |
| EXPECT_EQ(0u, MoveOnlyType::num_instances()); |
| EXPECT_FALSE(array); |
| array.resize(0); |
| EXPECT_EQ(0u, MoveOnlyType::num_instances()); |
| EXPECT_TRUE(array); |
| } |
| |
| TEST(ArrayTest, PushBack_Copyable) { |
| ASSERT_EQ(0u, CopyableType::num_instances()); |
| auto array = mojo::Array<CopyableType>::New(2); |
| array.reset(); |
| std::vector<CopyableType*> value_ptrs; |
| size_t capacity = array.storage().capacity(); |
| for (size_t i = 0; i < capacity; i++) { |
| CopyableType value; |
| value_ptrs.push_back(value.ptr()); |
| array.push_back(value); |
| ASSERT_EQ(i + 1, array.size()); |
| ASSERT_EQ(i + 1, value_ptrs.size()); |
| EXPECT_EQ(array.size() + 1, CopyableType::num_instances()); |
| EXPECT_TRUE(array[i].copied()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| array[i].ResetCopied(); |
| EXPECT_TRUE(array); |
| } |
| { |
| CopyableType value; |
| value_ptrs.push_back(value.ptr()); |
| array.push_back(value); |
| EXPECT_EQ(array.size() + 1, CopyableType::num_instances()); |
| } |
| ASSERT_EQ(capacity + 1, array.size()); |
| EXPECT_EQ(array.size(), CopyableType::num_instances()); |
| |
| for (size_t i = 0; i < array.size(); i++) { |
| EXPECT_TRUE(array[i].copied()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| } |
| array.reset(); |
| EXPECT_EQ(0u, CopyableType::num_instances()); |
| } |
| |
| TEST(ArrayTest, PushBack_MoveOnly) { |
| ASSERT_EQ(0u, MoveOnlyType::num_instances()); |
| auto array = mojo::Array<MoveOnlyType>::New(2); |
| array.reset(); |
| std::vector<MoveOnlyType*> value_ptrs; |
| size_t capacity = array.storage().capacity(); |
| for (size_t i = 0; i < capacity; i++) { |
| MoveOnlyType value; |
| value_ptrs.push_back(value.ptr()); |
| array.push_back(value.Pass()); |
| ASSERT_EQ(i + 1, array.size()); |
| ASSERT_EQ(i + 1, value_ptrs.size()); |
| EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances()); |
| EXPECT_TRUE(array[i].moved()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| array[i].ResetMoved(); |
| EXPECT_TRUE(array); |
| } |
| { |
| MoveOnlyType value; |
| value_ptrs.push_back(value.ptr()); |
| array.push_back(value.Pass()); |
| EXPECT_EQ(array.size() + 1, MoveOnlyType::num_instances()); |
| } |
| ASSERT_EQ(capacity + 1, array.size()); |
| EXPECT_EQ(array.size(), MoveOnlyType::num_instances()); |
| |
| for (size_t i = 0; i < array.size(); i++) { |
| EXPECT_TRUE(array[i].moved()); |
| EXPECT_EQ(value_ptrs[i], array[i].ptr()); |
| } |
| array.reset(); |
| EXPECT_EQ(0u, MoveOnlyType::num_instances()); |
| } |
| |
| TEST(ArrayTest, Iterator) { |
| std::vector<int> values; |
| values.push_back(0); |
| values.push_back(1); |
| values.push_back(2); |
| values.push_back(3); |
| Array<int> arr = Array<int>::From(values); |
| |
| // Test RandomAcessIterator traits. |
| { |
| // Test +,-,+=,-=. |
| auto i1 = arr.begin(); |
| i1 += 2; |
| EXPECT_EQ(*i1, values[2]); |
| i1 -= 2; |
| EXPECT_EQ(*i1, values[0]); |
| EXPECT_EQ((i1 + 2)[1], values[3]); |
| |
| auto i2 = arr.begin() + 3; |
| EXPECT_EQ(*i2, values[3]); |
| EXPECT_EQ(*(i2 - 2), values[1]); |
| EXPECT_EQ(i2 - i1, 3); |
| |
| { |
| auto j1 = arr.begin(); |
| auto j1_cp = arr.begin(); |
| j1 += 1; |
| j1_cp++; |
| EXPECT_EQ(j1, j1_cp); |
| |
| j1 -= 1; |
| j1_cp--; |
| EXPECT_EQ(j1, j1_cp); |
| } |
| |
| // Test >, <, >=, <=. |
| EXPECT_GT(i2, i1); |
| EXPECT_LT(i1, i2); |
| EXPECT_GE(i2, i1); |
| EXPECT_LE(i1, i2); |
| } |
| |
| { |
| SCOPED_TRACE("Array iterator bidirectionality test."); |
| ExpectBidiIteratorConcept(arr.begin(), arr.end(), values); |
| ExpectBidiMutableIteratorConcept(arr.begin(), arr.end(), values); |
| } |
| } |
| |
| // Test serializing and deserializing of an array with null elements. |
| TEST(ArrayTest, Serialization_ArrayOfStructPtr) { |
| ArrayValidateParams validate_nullable(2, true, nullptr); |
| ArrayValidateParams validate_non_nullable(2, false, nullptr); |
| |
| Array<RectPtr> array = Array<RectPtr>::New(2); |
| array[1] = Rect::New(); |
| array[1]->x = 1; |
| array[1]->y = 2; |
| array[1]->width = 3; |
| array[1]->height = 4; |
| |
| size_t size_with_null = GetSerializedSize_(array); |
| EXPECT_EQ(8U + // array header |
| 2 * 8U + // array payload (2 pointers) |
| 8U + 4 * 4U, // struct header + contents (4 int32) |
| size_with_null); |
| Array_Data<Rect::Data_*>* output_with_null = nullptr; |
| |
| // 1. Array with non-nullable structs should fail serialization due to |
| // the null first element. |
| { |
| FixedBufferForTesting buf_with_null(size_with_null); |
| EXPECT_EQ(mojo::internal::ValidationError::UNEXPECTED_NULL_POINTER, |
| SerializeArray_(&array, &buf_with_null, &output_with_null, |
| &validate_non_nullable)); |
| } |
| |
| // 2. Array with nullable structs should succeed. |
| { |
| FixedBufferForTesting buf_with_null(size_with_null); |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf_with_null, &output_with_null, |
| &validate_nullable)); |
| |
| Array<RectPtr> array2; |
| Deserialize_(output_with_null, &array2); |
| EXPECT_TRUE(array2[0].is_null()); |
| EXPECT_FALSE(array2[1].is_null()); |
| EXPECT_EQ(1, array2[1]->x); |
| EXPECT_EQ(2, array2[1]->y); |
| EXPECT_EQ(3, array2[1]->width); |
| EXPECT_EQ(4, array2[1]->height); |
| } |
| |
| // 3. Array with non-nullable structs should succeed after we fill in |
| // the missing first element. |
| { |
| array[0] = Rect::New(); |
| array[0]->x = -1; |
| array[0]->y = -2; |
| array[0]->width = -3; |
| array[0]->height = -4; |
| |
| size_t size_without_null = GetSerializedSize_(array); |
| EXPECT_EQ(8U + // array header |
| 2 * 8U + // array payload (2 pointers) |
| 2 * (8U + 4 * 4U), // struct header + contents (4 int32) |
| size_without_null); |
| |
| FixedBufferForTesting buf_without_null(size_without_null); |
| Array_Data<Rect::Data_*>* output_without_null = nullptr; |
| EXPECT_EQ(mojo::internal::ValidationError::NONE, |
| SerializeArray_(&array, &buf_without_null, &output_without_null, |
| &validate_non_nullable)); |
| |
| Array<RectPtr> array3; |
| Deserialize_(output_without_null, &array3); |
| EXPECT_FALSE(array3[0].is_null()); |
| EXPECT_EQ(-1, array3[0]->x); |
| EXPECT_EQ(-2, array3[0]->y); |
| EXPECT_EQ(-3, array3[0]->width); |
| EXPECT_EQ(-4, array3[0]->height); |
| EXPECT_FALSE(array3[1].is_null()); |
| EXPECT_EQ(1, array3[1]->x); |
| EXPECT_EQ(2, array3[1]->y); |
| EXPECT_EQ(3, array3[1]->width); |
| EXPECT_EQ(4, array3[1]->height); |
| } |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace mojo |