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 { | |
yzshen1
2014/10/09 22:24:41
Shall we move these classes which are shared with
| |
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 |