OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "mojo/public/cpp/bindings/array.h" |
| 6 #include "mojo/public/cpp/bindings/lib/array_serialization.h" |
| 7 #include "mojo/public/cpp/bindings/lib/bindings_internal.h" |
| 8 #include "mojo/public/cpp/bindings/lib/fixed_buffer.h" |
| 9 #include "mojo/public/cpp/bindings/map.h" |
| 10 #include "mojo/public/cpp/bindings/string.h" |
| 11 #include "mojo/public/cpp/bindings/struct_ptr.h" |
| 12 #include "mojo/public/cpp/environment/environment.h" |
| 13 #include "testing/gtest/include/gtest/gtest.h" |
| 14 |
| 15 namespace mojo { |
| 16 namespace test { |
| 17 |
| 18 namespace { |
| 19 |
| 20 struct StringIntData { |
| 21 const char* string_data; |
| 22 int int_data; |
| 23 } kStringIntData[] = { |
| 24 {"one", 1}, |
| 25 {"two", 2}, |
| 26 {"three", 3}, |
| 27 {"four", 4}, |
| 28 }; |
| 29 |
| 30 const size_t kStringIntDataSize = 4; |
| 31 |
| 32 class CopyableType { |
| 33 public: |
| 34 CopyableType() : copied_(false), ptr_(this) { num_instances_++; } |
| 35 CopyableType(const CopyableType& other) : copied_(true), ptr_(other.ptr()) { |
| 36 num_instances_++; |
| 37 } |
| 38 CopyableType& operator=(const CopyableType& other) { |
| 39 copied_ = true; |
| 40 ptr_ = other.ptr(); |
| 41 return *this; |
| 42 } |
| 43 ~CopyableType() { num_instances_--; } |
| 44 |
| 45 bool copied() const { return copied_; } |
| 46 static size_t num_instances() { return num_instances_; } |
| 47 CopyableType* ptr() const { return ptr_; } |
| 48 void ResetCopied() { copied_ = false; } |
| 49 |
| 50 private: |
| 51 bool copied_; |
| 52 static size_t num_instances_; |
| 53 CopyableType* ptr_; |
| 54 }; |
| 55 |
| 56 size_t CopyableType::num_instances_ = 0; |
| 57 |
| 58 class MoveOnlyType { |
| 59 MOJO_MOVE_ONLY_TYPE_FOR_CPP_03(MoveOnlyType, RValue) |
| 60 public: |
| 61 typedef MoveOnlyType Data_; |
| 62 MoveOnlyType() : moved_(false), ptr_(this) { num_instances_++; } |
| 63 MoveOnlyType(RValue other) : moved_(true), ptr_(other.object->ptr()) { |
| 64 num_instances_++; |
| 65 } |
| 66 MoveOnlyType& operator=(RValue other) { |
| 67 moved_ = true; |
| 68 ptr_ = other.object->ptr(); |
| 69 return *this; |
| 70 } |
| 71 ~MoveOnlyType() { num_instances_--; } |
| 72 |
| 73 bool moved() const { return moved_; } |
| 74 static size_t num_instances() { return num_instances_; } |
| 75 MoveOnlyType* ptr() const { return ptr_; } |
| 76 void ResetMoved() { moved_ = false; } |
| 77 |
| 78 private: |
| 79 bool moved_; |
| 80 static size_t num_instances_; |
| 81 MoveOnlyType* ptr_; |
| 82 }; |
| 83 |
| 84 size_t MoveOnlyType::num_instances_ = 0; |
| 85 |
| 86 class MapTest : public testing::Test { |
| 87 public: |
| 88 virtual ~MapTest() {} |
| 89 |
| 90 private: |
| 91 Environment env_; |
| 92 }; |
| 93 |
| 94 // Tests that basic Map operations work. |
| 95 TEST_F(MapTest, InsertWorks) { |
| 96 Map<String, int> map; |
| 97 for (size_t i = 0; i < kStringIntDataSize; ++i) |
| 98 map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data); |
| 99 |
| 100 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 101 EXPECT_EQ(kStringIntData[i].int_data, |
| 102 map.at(kStringIntData[i].string_data)); |
| 103 } |
| 104 } |
| 105 |
| 106 TEST_F(MapTest, ConstructedFromArray) { |
| 107 Array<String> keys(kStringIntDataSize); |
| 108 Array<int> values(kStringIntDataSize); |
| 109 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 110 keys[i] = kStringIntData[i].string_data; |
| 111 values[i] = kStringIntData[i].int_data; |
| 112 } |
| 113 |
| 114 Map<String, int> map(keys.Pass(), values.Pass()); |
| 115 |
| 116 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 117 EXPECT_EQ(kStringIntData[i].int_data, |
| 118 map.at(mojo::String(kStringIntData[i].string_data))); |
| 119 } |
| 120 } |
| 121 |
| 122 TEST_F(MapTest, DecomposeMapTo) { |
| 123 Array<String> keys(kStringIntDataSize); |
| 124 Array<int> values(kStringIntDataSize); |
| 125 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 126 keys[i] = kStringIntData[i].string_data; |
| 127 values[i] = kStringIntData[i].int_data; |
| 128 } |
| 129 |
| 130 Map<String, int> map(keys.Pass(), values.Pass()); |
| 131 EXPECT_EQ(kStringIntDataSize, map.size()); |
| 132 |
| 133 Array<String> keys2; |
| 134 Array<int> values2; |
| 135 map.DecomposeMapTo(&keys2, &values2); |
| 136 EXPECT_EQ(0u, map.size()); |
| 137 |
| 138 EXPECT_EQ(kStringIntDataSize, keys2.size()); |
| 139 EXPECT_EQ(kStringIntDataSize, values2.size()); |
| 140 |
| 141 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 142 // We are not guaranteed that the copies have the same sorting as the |
| 143 // originals. |
| 144 String key = kStringIntData[i].string_data; |
| 145 int value = kStringIntData[i].int_data; |
| 146 |
| 147 bool found = false; |
| 148 for (size_t j = 0; j < keys2.size(); ++j) { |
| 149 if (keys2[j] == key) { |
| 150 EXPECT_EQ(value, values2[j]); |
| 151 found = true; |
| 152 break; |
| 153 } |
| 154 } |
| 155 |
| 156 EXPECT_TRUE(found); |
| 157 } |
| 158 } |
| 159 |
| 160 TEST_F(MapTest, Insert_Copyable) { |
| 161 ASSERT_EQ(0u, CopyableType::num_instances()); |
| 162 mojo::Map<mojo::String, CopyableType> map; |
| 163 std::vector<CopyableType*> value_ptrs; |
| 164 |
| 165 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 166 const char* key = kStringIntData[i].string_data; |
| 167 CopyableType value; |
| 168 value_ptrs.push_back(value.ptr()); |
| 169 map.insert(key, value); |
| 170 ASSERT_EQ(i + 1, map.size()); |
| 171 ASSERT_EQ(i + 1, value_ptrs.size()); |
| 172 EXPECT_EQ(map.size() + 1, CopyableType::num_instances()); |
| 173 EXPECT_TRUE(map.at(key).copied()); |
| 174 EXPECT_EQ(value_ptrs[i], map.at(key).ptr()); |
| 175 map.at(key).ResetCopied(); |
| 176 EXPECT_TRUE(map); |
| 177 } |
| 178 |
| 179 // std::map doesn't have a capacity() method like std::vector so this test is |
| 180 // a lot more boring. |
| 181 |
| 182 map.reset(); |
| 183 EXPECT_EQ(0u, CopyableType::num_instances()); |
| 184 } |
| 185 |
| 186 TEST_F(MapTest, Insert_MoveOnly) { |
| 187 ASSERT_EQ(0u, MoveOnlyType::num_instances()); |
| 188 mojo::Map<mojo::String, MoveOnlyType> map; |
| 189 std::vector<MoveOnlyType*> value_ptrs; |
| 190 |
| 191 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 192 const char* key = kStringIntData[i].string_data; |
| 193 MoveOnlyType value; |
| 194 value_ptrs.push_back(value.ptr()); |
| 195 map.insert(key, value.Pass()); |
| 196 ASSERT_EQ(i + 1, map.size()); |
| 197 ASSERT_EQ(i + 1, value_ptrs.size()); |
| 198 EXPECT_EQ(map.size() + 1, MoveOnlyType::num_instances()); |
| 199 EXPECT_TRUE(map.at(key).moved()); |
| 200 EXPECT_EQ(value_ptrs[i], map.at(key).ptr()); |
| 201 map.at(key).ResetMoved(); |
| 202 EXPECT_TRUE(map); |
| 203 } |
| 204 |
| 205 // std::map doesn't have a capacity() method like std::vector so this test is |
| 206 // a lot more boring. |
| 207 |
| 208 map.reset(); |
| 209 EXPECT_EQ(0u, CopyableType::num_instances()); |
| 210 } |
| 211 |
| 212 TEST_F(MapTest, STLToMojo) { |
| 213 std::map<std::string, int> stl_data; |
| 214 for (size_t i = 0; i < kStringIntDataSize; ++i) |
| 215 stl_data[kStringIntData[i].string_data] = kStringIntData[i].int_data; |
| 216 |
| 217 Map<String, int32_t> mojo_data = Map<String, int32_t>::From(stl_data); |
| 218 |
| 219 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 220 EXPECT_EQ(kStringIntData[i].int_data, |
| 221 mojo_data.at(kStringIntData[i].string_data)); |
| 222 } |
| 223 } |
| 224 |
| 225 TEST_F(MapTest, MojoToSTL) { |
| 226 Map<String, int32_t> mojo_map; |
| 227 for (size_t i = 0; i < kStringIntDataSize; ++i) |
| 228 mojo_map.insert(kStringIntData[i].string_data, kStringIntData[i].int_data); |
| 229 |
| 230 std::map<std::string, int> stl_map = |
| 231 mojo_map.To<std::map<std::string, int>>(); |
| 232 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 233 auto it = stl_map.find(kStringIntData[i].string_data); |
| 234 ASSERT_TRUE(it != stl_map.end()); |
| 235 EXPECT_EQ(kStringIntData[i].int_data, it->second); |
| 236 } |
| 237 } |
| 238 |
| 239 TEST_F(MapTest, MapArrayClone) { |
| 240 Map<String, Array<String>> m; |
| 241 for (size_t i = 0; i < kStringIntDataSize; ++i) { |
| 242 Array<String> s; |
| 243 s.push_back(kStringIntData[i].string_data); |
| 244 m.insert(kStringIntData[i].string_data, s.Pass()); |
| 245 } |
| 246 |
| 247 Map<String, Array<String>> m2 = m.Clone(); |
| 248 |
| 249 for (auto it = m2.begin(); it != m2.end(); ++it) { |
| 250 ASSERT_EQ(1u, it.GetValue().size()); |
| 251 EXPECT_EQ(it.GetKey(), it.GetValue().at(0)); |
| 252 } |
| 253 } |
| 254 |
| 255 // Data class for an end-to-end test of serialization. Because making a more |
| 256 // limited test case tickles a clang compiler bug, we copy a minimal version of |
| 257 // what our current cpp bindings do. |
| 258 namespace internal { |
| 259 |
| 260 class ArrayOfMap_Data { |
| 261 public: |
| 262 static ArrayOfMap_Data* New(mojo::internal::Buffer* buf) { |
| 263 return new (buf->Allocate(sizeof(ArrayOfMap_Data))) ArrayOfMap_Data(); |
| 264 } |
| 265 |
| 266 mojo::internal::StructHeader header_; |
| 267 |
| 268 mojo::internal::ArrayPointer<mojo::internal::Map_Data<int32_t, int8_t>*> |
| 269 first; |
| 270 mojo::internal::ArrayPointer< |
| 271 mojo::internal::Map_Data<mojo::internal::String_Data*, |
| 272 mojo::internal::Array_Data<bool>*>*> second; |
| 273 |
| 274 private: |
| 275 ArrayOfMap_Data() { |
| 276 header_.num_bytes = sizeof(*this); |
| 277 header_.num_fields = 2; |
| 278 } |
| 279 ~ArrayOfMap_Data(); // NOT IMPLEMENTED |
| 280 }; |
| 281 static_assert(sizeof(ArrayOfMap_Data) == 24, "Bad sizeof(ArrayOfMap_Data)"); |
| 282 |
| 283 } // namespace internal |
| 284 |
| 285 class ArrayOfMapImpl; |
| 286 typedef mojo::StructPtr<ArrayOfMapImpl> ArrayOfMapImplPtr; |
| 287 |
| 288 class ArrayOfMapImpl { |
| 289 public: |
| 290 typedef internal::ArrayOfMap_Data Data_; |
| 291 static ArrayOfMapImplPtr New() { |
| 292 ArrayOfMapImplPtr rv; |
| 293 mojo::internal::StructHelper<ArrayOfMapImpl>::Initialize(&rv); |
| 294 return rv.Pass(); |
| 295 } |
| 296 |
| 297 mojo::Array<mojo::Map<int32_t, int8_t>> first; |
| 298 mojo::Array<mojo::Map<mojo::String, mojo::Array<bool>>> second; |
| 299 }; |
| 300 |
| 301 size_t GetSerializedSize_(const ArrayOfMapImplPtr& input) { |
| 302 if (!input) |
| 303 return 0; |
| 304 size_t size = sizeof(internal::ArrayOfMap_Data); |
| 305 size += GetSerializedSize_(input->first); |
| 306 size += GetSerializedSize_(input->second); |
| 307 return size; |
| 308 } |
| 309 |
| 310 void Serialize_(ArrayOfMapImplPtr input, |
| 311 mojo::internal::Buffer* buf, |
| 312 internal::ArrayOfMap_Data** output) { |
| 313 if (input) { |
| 314 internal::ArrayOfMap_Data* result = internal::ArrayOfMap_Data::New(buf); |
| 315 mojo::SerializeArray_<mojo::internal::ArrayValidateParams< |
| 316 0, |
| 317 false, |
| 318 mojo::internal:: |
| 319 ArrayValidateParams<0, false, mojo::internal::NoValidateParams>>>( |
| 320 mojo::internal::Forward(input->first), buf, &result->first.ptr); |
| 321 MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( |
| 322 !result->first.ptr, |
| 323 mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, |
| 324 "null first field in ArrayOfMapImpl struct"); |
| 325 mojo::SerializeArray_<mojo::internal::ArrayValidateParams< |
| 326 0, |
| 327 false, |
| 328 mojo::internal::ArrayValidateParams< |
| 329 0, |
| 330 false, |
| 331 mojo::internal::ArrayValidateParams< |
| 332 0, |
| 333 false, |
| 334 mojo::internal::NoValidateParams>>>>( |
| 335 mojo::internal::Forward(input->second), buf, &result->second.ptr); |
| 336 MOJO_INTERNAL_DLOG_SERIALIZATION_WARNING( |
| 337 !result->second.ptr, |
| 338 mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, |
| 339 "null second field in ArrayOfMapImpl struct"); |
| 340 *output = result; |
| 341 } else { |
| 342 *output = nullptr; |
| 343 } |
| 344 } |
| 345 |
| 346 void Deserialize_(internal::ArrayOfMap_Data* input, ArrayOfMapImplPtr* output) { |
| 347 if (input) { |
| 348 ArrayOfMapImplPtr result(ArrayOfMapImpl::New()); |
| 349 Deserialize_(input->first.ptr, &result->first); |
| 350 Deserialize_(input->second.ptr, &result->second); |
| 351 *output = result.Pass(); |
| 352 } else { |
| 353 output->reset(); |
| 354 } |
| 355 } |
| 356 |
| 357 TEST_F(MapTest, ArrayOfMap) { |
| 358 Array<Map<int32_t, int8_t>> first_array(1); |
| 359 first_array[0].insert(1, 42); |
| 360 |
| 361 Array<Map<String, Array<bool>>> second_array(1); |
| 362 Array<bool> map_value(2); |
| 363 map_value[0] = false; |
| 364 map_value[1] = true; |
| 365 second_array[0].insert("hello world", map_value.Pass()); |
| 366 |
| 367 ArrayOfMapImplPtr to_pass(ArrayOfMapImpl::New()); |
| 368 to_pass->first = first_array.Pass(); |
| 369 to_pass->second = second_array.Pass(); |
| 370 |
| 371 size_t size = GetSerializedSize_(to_pass); |
| 372 mojo::internal::FixedBuffer buf(size); |
| 373 internal::ArrayOfMap_Data* data; |
| 374 Serialize_(mojo::internal::Forward(to_pass), &buf, &data); |
| 375 |
| 376 ArrayOfMapImplPtr to_receive(ArrayOfMapImpl::New()); |
| 377 Deserialize_(data, &to_receive); |
| 378 |
| 379 ASSERT_EQ(1u, to_receive->first.size()); |
| 380 ASSERT_EQ(1u, to_receive->first[0].size()); |
| 381 ASSERT_EQ(42, to_receive->first[0].at(1)); |
| 382 |
| 383 ASSERT_EQ(1u, to_receive->second.size()); |
| 384 ASSERT_EQ(1u, to_receive->second[0].size()); |
| 385 ASSERT_FALSE(to_receive->second[0].at("hello world")[0]); |
| 386 ASSERT_TRUE(to_receive->second[0].at("hello world")[1]); |
| 387 } |
| 388 |
| 389 } // namespace |
| 390 } // namespace test |
| 391 } // namespace mojo |
OLD | NEW |