// 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