Chromium Code Reviews| Index: mojo/public/cpp/bindings/tests/map_unittest.cc |
| diff --git a/mojo/public/cpp/bindings/tests/map_unittest.cc b/mojo/public/cpp/bindings/tests/map_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..683a5e60085d6f6bd00ca2fe622e50ab42c0523f |
| --- /dev/null |
| +++ b/mojo/public/cpp/bindings/tests/map_unittest.cc |
| @@ -0,0 +1,391 @@ |
| +// 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_serialization.h" |
| +#include "mojo/public/cpp/bindings/lib/bindings_internal.h" |
| +#include "mojo/public/cpp/bindings/lib/fixed_buffer.h" |
| +#include "mojo/public/cpp/bindings/map.h" |
| +#include "mojo/public/cpp/bindings/string.h" |
| +#include "mojo/public/cpp/bindings/struct_ptr.h" |
| +#include "mojo/public/cpp/environment/environment.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace mojo { |
| +namespace test { |
| + |
| +namespace { |
| + |
| +struct StringIntData { |
| + const char* string_data; |
| + int int_data; |
| +} kStringIntData[] = { |
| + {"one", 1}, |
| + {"two", 2}, |
| + {"three", 3}, |
| + {"four", 4}, |
| +}; |
| + |
| +const size_t kStringIntDataSize = 4; |
| + |
| +class CopyableType { |
|
yzshen1
2014/10/09 22:24:41
Shall we move these classes which are shared with
|
| + public: |
| + CopyableType() : copied_(false), ptr_(this) { num_instances_++; } |
| + CopyableType(const CopyableType& other) : copied_(true), ptr_(other.ptr()) { |
| + num_instances_++; |
| + } |
| + CopyableType& operator=(const CopyableType& other) { |
| + copied_ = true; |
| + ptr_ = other.ptr(); |
| + return *this; |
| + } |
| + ~CopyableType() { num_instances_--; } |
| + |
| + bool copied() const { return copied_; } |
| + static size_t num_instances() { return num_instances_; } |
| + CopyableType* ptr() const { return ptr_; } |
| + void ResetCopied() { copied_ = false; } |
| + |
| + private: |
| + bool copied_; |
| + static size_t num_instances_; |
| + CopyableType* ptr_; |
| +}; |
| + |
| +size_t CopyableType::num_instances_ = 0; |
| + |
| +class MoveOnlyType { |
| + MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(MoveOnlyType, RValue) |
| + public: |
| + typedef MoveOnlyType Data_; |
| + MoveOnlyType() : moved_(false), ptr_(this) { num_instances_++; } |
| + MoveOnlyType(RValue other) : moved_(true), ptr_(other.object->ptr()) { |
| + num_instances_++; |
| + } |
| + MoveOnlyType& operator=(RValue other) { |
| + moved_ = true; |
| + ptr_ = other.object->ptr(); |
| + return *this; |
| + } |
| + ~MoveOnlyType() { num_instances_--; } |
| + |
| + bool moved() const { return moved_; } |
| + static size_t num_instances() { return num_instances_; } |
| + MoveOnlyType* ptr() const { return ptr_; } |
| + void ResetMoved() { moved_ = false; } |
| + |
| + private: |
| + bool moved_; |
| + static size_t num_instances_; |
| + MoveOnlyType* ptr_; |
| +}; |
| + |
| +size_t MoveOnlyType::num_instances_ = 0; |
| + |
| +class MapTest : public testing::Test { |
| + public: |
| + virtual ~MapTest() {} |
| + |
| + private: |
| + Environment env_; |
| +}; |
| + |
| +// Tests that basic Map operations work. |
| +TEST_F(MapTest, InsertWorks) { |
| + Map<String, int> map; |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) |
| + map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data); |
| + |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + EXPECT_EQ(kStringIntData[i].int_data, |
| + map.at(kStringIntData[i].string_data)); |
| + } |
| +} |
| + |
| +TEST_F(MapTest, ConstructedFromArray) { |
| + Array<String> keys(kStringIntDataSize); |
| + Array<int> values(kStringIntDataSize); |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + keys[i] = kStringIntData[i].string_data; |
| + values[i] = kStringIntData[i].int_data; |
| + } |
| + |
| + Map<String, int> map(keys.Pass(), values.Pass()); |
| + |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + EXPECT_EQ(kStringIntData[i].int_data, |
| + map.at(mojo::String(kStringIntData[i].string_data))); |
| + } |
| +} |
| + |
| +TEST_F(MapTest, DecomposeMapTo) { |
| + Array<String> keys(kStringIntDataSize); |
| + Array<int> values(kStringIntDataSize); |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + keys[i] = kStringIntData[i].string_data; |
| + values[i] = kStringIntData[i].int_data; |
| + } |
| + |
| + Map<String, int> map(keys.Pass(), values.Pass()); |
| + EXPECT_EQ(kStringIntDataSize, map.size()); |
| + |
| + Array<String> keys2; |
| + Array<int> values2; |
| + map.DecomposeMapTo(&keys2, &values2); |
| + EXPECT_EQ(0u, map.size()); |
| + |
| + EXPECT_EQ(kStringIntDataSize, keys2.size()); |
| + EXPECT_EQ(kStringIntDataSize, values2.size()); |
| + |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + // We are not guaranteed that the copies have the same sorting as the |
| + // originals. |
| + String key = kStringIntData[i].string_data; |
| + int value = kStringIntData[i].int_data; |
| + |
| + bool found = false; |
| + for (size_t j = 0; j < keys2.size(); ++j) { |
| + if (keys2[j] == key) { |
| + EXPECT_EQ(value, values2[j]); |
| + found = true; |
| + break; |
| + } |
| + } |
| + |
| + EXPECT_TRUE(found); |
| + } |
| +} |
| + |
| +TEST_F(MapTest, Insert_Copyable) { |
| + ASSERT_EQ(0u, CopyableType::num_instances()); |
| + mojo::Map<mojo::String, CopyableType> map; |
| + std::vector<CopyableType*> value_ptrs; |
| + |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + const char* key = kStringIntData[i].string_data; |
| + CopyableType value; |
| + value_ptrs.push_back(value.ptr()); |
| + map.insert(key, value); |
| + ASSERT_EQ(i + 1, map.size()); |
| + ASSERT_EQ(i + 1, value_ptrs.size()); |
| + EXPECT_EQ(map.size() + 1, CopyableType::num_instances()); |
| + EXPECT_TRUE(map.at(key).copied()); |
| + EXPECT_EQ(value_ptrs[i], map.at(key).ptr()); |
| + map.at(key).ResetCopied(); |
| + EXPECT_TRUE(map); |
| + } |
| + |
| + // std::map doesn't have a capacity() method like std::vector so this test is |
| + // a lot more boring. |
| + |
| + map.reset(); |
| + EXPECT_EQ(0u, CopyableType::num_instances()); |
| +} |
| + |
| +TEST_F(MapTest, Insert_MoveOnly) { |
| + ASSERT_EQ(0u, MoveOnlyType::num_instances()); |
| + mojo::Map<mojo::String, MoveOnlyType> map; |
| + std::vector<MoveOnlyType*> value_ptrs; |
| + |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + const char* key = kStringIntData[i].string_data; |
| + MoveOnlyType value; |
| + value_ptrs.push_back(value.ptr()); |
| + map.insert(key, value.Pass()); |
| + ASSERT_EQ(i + 1, map.size()); |
| + ASSERT_EQ(i + 1, value_ptrs.size()); |
| + EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances()); |
| + EXPECT_TRUE(map.at(key).moved()); |
| + EXPECT_EQ(value_ptrs[i], map.at(key).ptr()); |
| + map.at(key).ResetMoved(); |
| + EXPECT_TRUE(map); |
| + } |
| + |
| + // std::map doesn't have a capacity() method like std::vector so this test is |
| + // a lot more boring. |
| + |
| + map.reset(); |
| + EXPECT_EQ(0u, CopyableType::num_instances()); |
| +} |
| + |
| +TEST_F(MapTest, STLToMojo) { |
| + std::map<std::string, int> stl_data; |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) |
| + stl_data[kStringIntData[i].string_data] = kStringIntData[i].int_data; |
| + |
| + Map<String, int32_t> mojo_data = Map<String, int32_t>::From(stl_data); |
| + |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + EXPECT_EQ(kStringIntData[i].int_data, |
| + mojo_data.at(kStringIntData[i].string_data)); |
| + } |
| +} |
| + |
| +TEST_F(MapTest, MojoToSTL) { |
| + Map<String, int32_t> mojo_map; |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) |
| + mojo_map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data); |
| + |
| + std::map<std::string, int> stl_map = |
| + mojo_map.To<std::map<std::string, int>>(); |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + auto it = stl_map.find(kStringIntData[i].string_data); |
| + ASSERT_TRUE(it != stl_map.end()); |
| + EXPECT_EQ(kStringIntData[i].int_data, it->second); |
| + } |
| +} |
| + |
| +TEST_F(MapTest, MapArrayClone) { |
| + Map<String, Array<String>> m; |
| + for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| + Array<String> s; |
| + s.push_back(kStringIntData[i].string_data); |
| + m.insert(kStringIntData[i].string_data, s.Pass()); |
| + } |
| + |
| + Map<String, Array<String>> m2 = m.Clone(); |
| + |
| + for (auto it = m2.begin(); it != m2.end(); ++it) { |
| + ASSERT_EQ(1u, it.GetValue().size()); |
| + EXPECT_EQ(it.GetKey(), it.GetValue().at(0)); |
| + } |
| +} |
| + |
| +// Data class for an end-to-end test of serialization. Because making a more |
| +// limited test case tickles a clang compiler bug, we copy a minimal version of |
| +// what our current cpp bindings do. |
| +namespace internal { |
| + |
| +class ArrayOfMap_Data { |
| + public: |
| + static ArrayOfMap_Data* New(mojo::internal::Buffer* buf) { |
| + return new (buf->Allocate(sizeof(ArrayOfMap_Data))) ArrayOfMap_Data(); |
| + } |
| + |
| + mojo::internal::StructHeader header_; |
| + |
| + mojo::internal::ArrayPointer<mojo::internal::Map_Data<int32_t, int8_t>*> |
| + first; |
| + mojo::internal::ArrayPointer< |
| + mojo::internal::Map_Data<mojo::internal::String_Data*, |
| + mojo::internal::Array_Data<bool>*>*> second; |
| + |
| + private: |
| + ArrayOfMap_Data() { |
| + header_.num_bytes = sizeof(*this); |
| + header_.num_fields = 2; |
| + } |
| + ~ArrayOfMap_Data(); // NOT IMPLEMENTED |
| +}; |
| +static_assert(sizeof(ArrayOfMap_Data) == 24, "Bad sizeof(ArrayOfMap_Data)"); |
| + |
| +} // namespace internal |
| + |
| +class ArrayOfMapImpl; |
| +typedef mojo::StructPtr<ArrayOfMapImpl> ArrayOfMapImplPtr; |
| + |
| +class ArrayOfMapImpl { |
| + public: |
| + typedef internal::ArrayOfMap_Data Data_; |
| + static ArrayOfMapImplPtr New() { |
| + ArrayOfMapImplPtr rv; |
| + mojo::internal::StructHelper<ArrayOfMapImpl>::Initialize(&rv); |
| + return rv.Pass(); |
| + } |
| + |
| + mojo::Array<mojo::Map<int32_t, int8_t>> first; |
| + mojo::Array<mojo::Map<mojo::String, mojo::Array<bool>>> second; |
| +}; |
| + |
| +size_t GetSerializedSize_(const ArrayOfMapImplPtr& input) { |
| + if (!input) |
| + return 0; |
| + size_t size = sizeof(internal::ArrayOfMap_Data); |
| + size += GetSerializedSize_(input->first); |
| + size += GetSerializedSize_(input->second); |
| + return size; |
| +} |
| + |
| +void Serialize_(ArrayOfMapImplPtr input, |
| + mojo::internal::Buffer* buf, |
| + internal::ArrayOfMap_Data** output) { |
| + if (input) { |
| + internal::ArrayOfMap_Data* result = internal::ArrayOfMap_Data::New(buf); |
| + mojo::SerializeArray_<mojo::internal::ArrayValidateParams< |
| + 0, |
| + false, |
| + mojo::internal:: |
| + ArrayValidateParams<0, false, mojo::internal::NoValidateParams>>>( |
| + mojo::internal::Forward(input->first), buf, &result->first.ptr); |
| + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( |
| + !result->first.ptr, |
| + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, |
| + "null first field in ArrayOfMapImpl struct"); |
| + mojo::SerializeArray_<mojo::internal::ArrayValidateParams< |
| + 0, |
| + false, |
| + mojo::internal::ArrayValidateParams< |
| + 0, |
| + false, |
| + mojo::internal::ArrayValidateParams< |
| + 0, |
| + false, |
| + mojo::internal::NoValidateParams>>>>( |
| + mojo::internal::Forward(input->second), buf, &result->second.ptr); |
| + MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( |
| + !result->second.ptr, |
| + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, |
| + "null second field in ArrayOfMapImpl struct"); |
| + *output = result; |
| + } else { |
| + *output = nullptr; |
| + } |
| +} |
| + |
| +void Deserialize_(internal::ArrayOfMap_Data* input, ArrayOfMapImplPtr* output) { |
| + if (input) { |
| + ArrayOfMapImplPtr result(ArrayOfMapImpl::New()); |
| + Deserialize_(input->first.ptr, &result->first); |
| + Deserialize_(input->second.ptr, &result->second); |
| + *output = result.Pass(); |
| + } else { |
| + output->reset(); |
| + } |
| +} |
| + |
| +TEST_F(MapTest, ArrayOfMap) { |
| + Array<Map<int32_t, int8_t>> first_array(1); |
| + first_array[0].insert(1, 42); |
| + |
| + Array<Map<String, Array<bool>>> second_array(1); |
| + Array<bool> map_value(2); |
| + map_value[0] = false; |
| + map_value[1] = true; |
| + second_array[0].insert("hello world", map_value.Pass()); |
| + |
| + ArrayOfMapImplPtr to_pass(ArrayOfMapImpl::New()); |
| + to_pass->first = first_array.Pass(); |
| + to_pass->second = second_array.Pass(); |
| + |
| + size_t size = GetSerializedSize_(to_pass); |
| + mojo::internal::FixedBuffer buf(size); |
| + internal::ArrayOfMap_Data* data; |
| + Serialize_(mojo::internal::Forward(to_pass), &buf, &data); |
| + |
| + ArrayOfMapImplPtr to_receive(ArrayOfMapImpl::New()); |
| + Deserialize_(data, &to_receive); |
| + |
| + ASSERT_EQ(1u, to_receive->first.size()); |
| + ASSERT_EQ(1u, to_receive->first[0].size()); |
| + ASSERT_EQ(42, to_receive->first[0].at(1)); |
| + |
| + ASSERT_EQ(1u, to_receive->second.size()); |
| + ASSERT_EQ(1u, to_receive->second[0].size()); |
| + ASSERT_FALSE(to_receive->second[0].at("hello world")[0]); |
| + ASSERT_TRUE(to_receive->second[0].at("hello world")[1]); |
| +} |
| + |
| +} // namespace |
| +} // namespace test |
| +} // namespace mojo |