|  | // 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 "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/environment/environment.h" | 
|  | #include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace mojo { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | using mojo::internal::Array_Data; | 
|  | using mojo::internal::ArrayValidateParams; | 
|  | using mojo::internal::FixedBuffer; | 
|  | using mojo::internal::NoValidateParams; | 
|  | using mojo::internal::String_Data; | 
|  |  | 
|  | class ArrayTest : public testing::Test { | 
|  | public: | 
|  | ~ArrayTest() override {} | 
|  |  | 
|  | private: | 
|  | Environment env_; | 
|  | }; | 
|  |  | 
|  | // Tests that basic Array operations work. | 
|  | TEST_F(ArrayTest, Basic) { | 
|  | Array<char> array(8); | 
|  | for (size_t i = 0; i < array.size(); ++i) { | 
|  | char val = static_cast<char>(i * 2); | 
|  | array[i] = val; | 
|  | EXPECT_EQ(val, array.at(i)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tests that basic Array<bool> operations work. | 
|  | TEST_F(ArrayTest, Bool) { | 
|  | Array<bool> array(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_F(ArrayTest, Handle) { | 
|  | MessagePipe pipe; | 
|  | Array<ScopedMessagePipeHandle> handles(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_F(ArrayTest, HandlesAreClosed) { | 
|  | MessagePipe pipe; | 
|  | MojoHandle pipe0_value = pipe.handle0.get().value(); | 
|  | MojoHandle pipe1_value = pipe.handle0.get().value(); | 
|  |  | 
|  | { | 
|  | Array<ScopedMessagePipeHandle> handles(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_F(ArrayTest, Clone) { | 
|  | { | 
|  | // Test POD. | 
|  | Array<int32_t> array(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. | 
|  | Array<String> array(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. | 
|  | Array<RectPtr> array(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. | 
|  | Array<Array<int8_t>> array(2); | 
|  | array[1] = Array<int8_t>(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. | 
|  | Array<ScopedMessagePipeHandle> array(10); | 
|  | EXPECT_FALSE(array[0].is_valid()); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(ArrayTest, Serialization_ArrayOfPOD) { | 
|  | Array<int32_t> array(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); | 
|  |  | 
|  | FixedBuffer buf(size); | 
|  | Array_Data<int32_t>* data; | 
|  | SerializeArray_<ArrayValidateParams<0, false, NoValidateParams>>( | 
|  | array.Pass(), &buf, &data); | 
|  |  | 
|  | 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_F(ArrayTest, Serialization_EmptyArrayOfPOD) { | 
|  | Array<int32_t> array(0); | 
|  | size_t size = GetSerializedSize_(array); | 
|  | EXPECT_EQ(8U, size); | 
|  |  | 
|  | FixedBuffer buf(size); | 
|  | Array_Data<int32_t>* data; | 
|  | SerializeArray_<ArrayValidateParams<0, false, NoValidateParams>>( | 
|  | array.Pass(), &buf, &data); | 
|  |  | 
|  | Array<int32_t> array2; | 
|  | Deserialize_(data, &array2); | 
|  | EXPECT_EQ(0U, array2.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(ArrayTest, Serialization_ArrayOfArrayOfPOD) { | 
|  | Array<Array<int32_t>> array(2); | 
|  | for (size_t j = 0; j < array.size(); ++j) { | 
|  | Array<int32_t> inner(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); | 
|  |  | 
|  | FixedBuffer buf(size); | 
|  | Array_Data<Array_Data<int32_t>*>* data; | 
|  | SerializeArray_< | 
|  | ArrayValidateParams<0, | 
|  | false, | 
|  | ArrayValidateParams<0, false, NoValidateParams>>>( | 
|  | array.Pass(), &buf, &data); | 
|  |  | 
|  | 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_F(ArrayTest, Serialization_ArrayOfBool) { | 
|  | Array<bool> array(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); | 
|  |  | 
|  | FixedBuffer buf(size); | 
|  | Array_Data<bool>* data; | 
|  | SerializeArray_<ArrayValidateParams<0, false, NoValidateParams>>( | 
|  | array.Pass(), &buf, &data); | 
|  |  | 
|  | 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_F(ArrayTest, Serialization_ArrayOfString) { | 
|  | Array<String> array(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); | 
|  |  | 
|  | FixedBuffer buf(size); | 
|  | Array_Data<String_Data*>* data; | 
|  | SerializeArray_< | 
|  | ArrayValidateParams<0, | 
|  | false, | 
|  | ArrayValidateParams<0, false, NoValidateParams>>>( | 
|  | array.Pass(), &buf, &data); | 
|  |  | 
|  | 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]); | 
|  | } | 
|  | } | 
|  |  | 
|  | TEST_F(ArrayTest, Resize_Copyable) { | 
|  | ASSERT_EQ(0u, CopyableType::num_instances()); | 
|  | mojo::Array<CopyableType> array(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_F(ArrayTest, Resize_MoveOnly) { | 
|  | ASSERT_EQ(0u, MoveOnlyType::num_instances()); | 
|  | mojo::Array<MoveOnlyType> array(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_F(ArrayTest, PushBack_Copyable) { | 
|  | ASSERT_EQ(0u, CopyableType::num_instances()); | 
|  | mojo::Array<CopyableType> array(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_F(ArrayTest, PushBack_MoveOnly) { | 
|  | ASSERT_EQ(0u, MoveOnlyType::num_instances()); | 
|  | mojo::Array<MoveOnlyType> array(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()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace test | 
|  | }  // namespace mojo |