1 | /* |
2 | * Copyright 2011-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/dynamic.h> |
18 | |
19 | #include <folly/Range.h> |
20 | #include <folly/json.h> |
21 | #include <folly/portability/GTest.h> |
22 | |
23 | #include <iterator> |
24 | |
25 | using folly::dynamic; |
26 | using folly::StringPiece; |
27 | |
28 | TEST(Dynamic, Default) { |
29 | dynamic obj; |
30 | EXPECT_TRUE(obj.isNull()); |
31 | } |
32 | |
33 | TEST(Dynamic, ObjectBasics) { |
34 | dynamic obj = dynamic::object("a" , false); |
35 | EXPECT_EQ(obj.at("a" ), false); |
36 | EXPECT_EQ(obj.size(), 1); |
37 | obj.insert("a" , true); |
38 | |
39 | dynamic key{"a" }; |
40 | folly::StringPiece sp{"a" }; |
41 | std::string s{"a" }; |
42 | |
43 | EXPECT_EQ(obj.size(), 1); |
44 | EXPECT_EQ(obj.at("a" ), true); |
45 | EXPECT_EQ(obj.at(sp), true); |
46 | EXPECT_EQ(obj.at(key), true); |
47 | |
48 | obj.at(sp) = nullptr; |
49 | EXPECT_EQ(obj.size(), 1); |
50 | EXPECT_TRUE(obj.at(s) == nullptr); |
51 | |
52 | obj["a" ] = 12; |
53 | EXPECT_EQ(obj[sp], 12); |
54 | obj[key] = "foo" ; |
55 | EXPECT_EQ(obj["a" ], "foo" ); |
56 | (void)obj["b" ]; |
57 | EXPECT_EQ(obj.size(), 2); |
58 | |
59 | obj.erase("a" ); |
60 | EXPECT_TRUE(obj.find(sp) == obj.items().end()); |
61 | obj.erase("b" ); |
62 | EXPECT_EQ(obj.size(), 0); |
63 | |
64 | dynamic newObject = dynamic::object; |
65 | |
66 | newObject["z" ] = 12; |
67 | EXPECT_EQ(newObject.size(), 1); |
68 | newObject["a" ] = true; |
69 | EXPECT_EQ(newObject.size(), 2); |
70 | |
71 | EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first); |
72 | EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second); |
73 | std::vector<std::pair<std::string, dynamic>> found; |
74 | found.emplace_back( |
75 | newObject.keys().begin()->asString(), *newObject.values().begin()); |
76 | |
77 | EXPECT_EQ( |
78 | *std::next(newObject.keys().begin()), |
79 | std::next(newObject.items().begin())->first); |
80 | EXPECT_EQ( |
81 | *std::next(newObject.values().begin()), |
82 | std::next(newObject.items().begin())->second); |
83 | found.emplace_back( |
84 | std::next(newObject.keys().begin())->asString(), |
85 | *std::next(newObject.values().begin())); |
86 | |
87 | std::sort(found.begin(), found.end()); |
88 | |
89 | EXPECT_EQ("a" , found[0].first); |
90 | EXPECT_TRUE(found[0].second.asBool()); |
91 | |
92 | EXPECT_EQ("z" , found[1].first); |
93 | EXPECT_EQ(12, found[1].second.asInt()); |
94 | |
95 | dynamic obj2 = dynamic::object; |
96 | EXPECT_TRUE(obj2.isObject()); |
97 | |
98 | dynamic d3 = nullptr; |
99 | EXPECT_TRUE(d3 == nullptr); |
100 | d3 = dynamic::object; |
101 | EXPECT_TRUE(d3.isObject()); |
102 | d3["foo" ] = dynamic::array(1, 2, 3); |
103 | EXPECT_EQ(d3.count("foo" ), 1); |
104 | |
105 | d3[123] = 321; |
106 | EXPECT_EQ(d3.at(123), 321); |
107 | |
108 | d3["123" ] = 42; |
109 | EXPECT_EQ(d3.at("123" ), 42); |
110 | EXPECT_EQ(d3.at(123), 321); |
111 | |
112 | dynamic objInsert = folly::dynamic::object(); |
113 | dynamic objA = folly::dynamic::object("1" , "2" ); |
114 | dynamic objB = folly::dynamic::object("1" , "2" ); |
115 | |
116 | objInsert.insert("1" , std::move(objA)); |
117 | objInsert.insert("1" , std::move(objB)); |
118 | |
119 | EXPECT_EQ(objInsert.find("1" )->second.size(), 1); |
120 | |
121 | // Looking up objects as keys |
122 | // clang-format off |
123 | dynamic objDefinedInOneOrder = folly::dynamic::object |
124 | ("bar" , "987" ) |
125 | ("baz" , folly::dynamic::array(1, 2, 3)) |
126 | ("foo2" , folly::dynamic::object("1" , "2" )); |
127 | dynamic sameObjInDifferentOrder = folly::dynamic::object |
128 | ("bar" , "987" ) |
129 | ("foo2" , folly::dynamic::object("1" , "2" )) |
130 | ("baz" , folly::dynamic::array(1, 2, 3)); |
131 | // clang-format on |
132 | |
133 | newObject[objDefinedInOneOrder] = 12; |
134 | EXPECT_EQ(newObject.at(objDefinedInOneOrder).getInt(), 12); |
135 | EXPECT_EQ(newObject.at(sameObjInDifferentOrder).getInt(), 12); |
136 | |
137 | // Merge two objects |
138 | dynamic origMergeObj1 = folly::dynamic::object(); |
139 | // clang-format off |
140 | dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object |
141 | ("key1" , "value1" ) |
142 | ("key2" , "value2" ); |
143 | dynamic mergeObj2 = folly::dynamic::object |
144 | ("key2" , "value3" ) |
145 | ("key3" , "value4" ); |
146 | // clang-format on |
147 | |
148 | // Merged object where we prefer the values in mergeObj2 |
149 | // clang-format off |
150 | dynamic combinedPreferObj2 = folly::dynamic::object |
151 | ("key1" , "value1" ) |
152 | ("key2" , "value3" ) |
153 | ("key3" , "value4" ); |
154 | // clang-format on |
155 | |
156 | // Merged object where we prefer the values in mergeObj1 |
157 | // clang-format off |
158 | dynamic combinedPreferObj1 = folly::dynamic::object |
159 | ("key1" , "value1" ) |
160 | ("key2" , "value2" ) |
161 | ("key3" , "value4" ); |
162 | // clang-format on |
163 | |
164 | auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2); |
165 | EXPECT_EQ(newMergeObj, combinedPreferObj2); |
166 | EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged |
167 | |
168 | mergeObj1.update(mergeObj2); |
169 | EXPECT_EQ(mergeObj1, combinedPreferObj2); |
170 | dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6); |
171 | EXPECT_THROW(mergeObj1.update(arr), std::exception); |
172 | |
173 | mergeObj1 = origMergeObj1; // reset it |
174 | mergeObj1.update_missing(mergeObj2); |
175 | EXPECT_EQ(mergeObj1, combinedPreferObj1); |
176 | } |
177 | |
178 | TEST(Dynamic, ArrayInsertErase) { |
179 | auto arr = dynamic::array(1, 2, 3, 4, 5, 6); |
180 | |
181 | arr.erase(arr.begin() + 3); |
182 | EXPECT_EQ(5, arr[3].asInt()); |
183 | |
184 | arr.insert(arr.begin() + 3, 4); |
185 | EXPECT_EQ(4, arr[3].asInt()); |
186 | EXPECT_EQ(5, arr[4].asInt()); |
187 | |
188 | auto x = dynamic::array(55, 66); |
189 | arr.insert(arr.begin() + 4, std::move(x)); |
190 | EXPECT_EQ(55, arr[4][0].asInt()); |
191 | EXPECT_EQ(66, arr[4][1].asInt()); |
192 | EXPECT_EQ(5, arr[5].asInt()); |
193 | |
194 | dynamic obj = dynamic::object; |
195 | obj.insert(3, 4); |
196 | EXPECT_EQ(4, obj[3].asInt()); |
197 | } |
198 | |
199 | namespace { |
200 | |
201 | struct StaticStrings { |
202 | static constexpr auto kA = "a" ; |
203 | static constexpr const char* kB = "b" ; |
204 | static const folly::StringPiece kFoo; |
205 | static const std::string kBar; |
206 | }; |
207 | /* static */ const folly::StringPiece StaticStrings::kFoo{"foo" }; |
208 | /* static */ const std::string StaticStrings::kBar{"bar" }; |
209 | |
210 | } // namespace |
211 | |
212 | TEST(Dynamic, ObjectHeterogeneousAccess) { |
213 | dynamic empty; |
214 | dynamic foo{"foo" }; |
215 | const char* a = "a" ; |
216 | StringPiece sp{"a" }; |
217 | std::string str{"a" }; |
218 | dynamic bar{"bar" }; |
219 | const char* b = "b" ; |
220 | |
221 | dynamic obj = dynamic::object("a" , 123)(empty, 456)(foo, 789); |
222 | |
223 | // at() |
224 | EXPECT_EQ(obj.at(empty), 456); |
225 | EXPECT_EQ(obj.at(nullptr), 456); |
226 | EXPECT_EQ(obj.at(foo), 789); |
227 | |
228 | EXPECT_EQ(obj.at(a), 123); |
229 | EXPECT_EQ(obj.at(StaticStrings::kA), 123); |
230 | EXPECT_EQ(obj.at("a" ), 123); |
231 | |
232 | EXPECT_EQ(obj.at(sp), 123); |
233 | EXPECT_EQ(obj.at(StringPiece{"a" }), 123); |
234 | EXPECT_EQ(obj.at(StaticStrings::kFoo), 789); |
235 | |
236 | EXPECT_EQ(obj.at(std::string{"a" }), 123); |
237 | EXPECT_EQ(obj.at(str), 123); |
238 | |
239 | EXPECT_THROW(obj.at(b), std::out_of_range); |
240 | EXPECT_THROW(obj.at(StringPiece{b}), std::out_of_range); |
241 | EXPECT_THROW(obj.at(StaticStrings::kBar), std::out_of_range); |
242 | |
243 | // get_ptr() |
244 | EXPECT_NE(obj.get_ptr(empty), nullptr); |
245 | EXPECT_EQ(*obj.get_ptr(empty), 456); |
246 | EXPECT_NE(obj.get_ptr(nullptr), nullptr); |
247 | EXPECT_EQ(*obj.get_ptr(nullptr), 456); |
248 | EXPECT_NE(obj.get_ptr(foo), nullptr); |
249 | EXPECT_EQ(*obj.get_ptr(foo), 789); |
250 | |
251 | EXPECT_NE(obj.get_ptr(a), nullptr); |
252 | EXPECT_EQ(*obj.get_ptr(a), 123); |
253 | EXPECT_NE(obj.get_ptr(StaticStrings::kA), nullptr); |
254 | EXPECT_EQ(*obj.get_ptr(StaticStrings::kA), 123); |
255 | EXPECT_NE(obj.get_ptr("a" ), nullptr); |
256 | EXPECT_EQ(*obj.get_ptr("a" ), 123); |
257 | |
258 | EXPECT_NE(obj.get_ptr(sp), nullptr); |
259 | EXPECT_EQ(*obj.get_ptr(sp), 123); |
260 | EXPECT_NE(obj.get_ptr(StringPiece{"a" }), nullptr); |
261 | EXPECT_EQ(*obj.get_ptr(StringPiece{"a" }), 123); |
262 | EXPECT_NE(obj.get_ptr(StaticStrings::kFoo), nullptr); |
263 | EXPECT_EQ(*obj.get_ptr(StaticStrings::kFoo), 789); |
264 | |
265 | EXPECT_NE(obj.get_ptr(std::string{"a" }), nullptr); |
266 | EXPECT_EQ(*obj.get_ptr(std::string{"a" }), 123); |
267 | EXPECT_NE(obj.get_ptr(str), nullptr); |
268 | EXPECT_EQ(*obj.get_ptr(str), 123); |
269 | |
270 | EXPECT_EQ(obj.get_ptr(b), nullptr); |
271 | EXPECT_EQ(obj.get_ptr(StringPiece{b}), nullptr); |
272 | EXPECT_EQ(obj.get_ptr(StaticStrings::kBar), nullptr); |
273 | |
274 | // find() |
275 | EXPECT_EQ(obj.find(empty)->second, 456); |
276 | EXPECT_EQ(obj.find(nullptr)->second, 456); |
277 | EXPECT_EQ(obj.find(foo)->second, 789); |
278 | |
279 | EXPECT_EQ(obj.find(a)->second, 123); |
280 | EXPECT_EQ(obj.find(StaticStrings::kA)->second, 123); |
281 | EXPECT_EQ(obj.find("a" )->second, 123); |
282 | |
283 | EXPECT_EQ(obj.find(sp)->second, 123); |
284 | EXPECT_EQ(obj.find(StringPiece{"a" })->second, 123); |
285 | EXPECT_EQ(obj.find(StaticStrings::kFoo)->second, 789); |
286 | |
287 | EXPECT_EQ(obj.find(std::string{"a" })->second, 123); |
288 | EXPECT_EQ(obj.find(str)->second, 123); |
289 | |
290 | EXPECT_TRUE(obj.find(b) == obj.items().end()); |
291 | EXPECT_TRUE(obj.find(StringPiece{b}) == obj.items().end()); |
292 | EXPECT_TRUE(obj.find(StaticStrings::kBar) == obj.items().end()); |
293 | |
294 | // count() |
295 | EXPECT_EQ(obj.count(empty), 1); |
296 | EXPECT_EQ(obj.count(nullptr), 1); |
297 | EXPECT_EQ(obj.count(foo), 1); |
298 | |
299 | EXPECT_EQ(obj.count(a), 1); |
300 | EXPECT_EQ(obj.count(StaticStrings::kA), 1); |
301 | EXPECT_EQ(obj.count("a" ), 1); |
302 | |
303 | EXPECT_EQ(obj.count(sp), 1); |
304 | EXPECT_EQ(obj.count(StringPiece{"a" }), 1); |
305 | EXPECT_EQ(obj.count(StaticStrings::kFoo), 1); |
306 | |
307 | EXPECT_EQ(obj.count(std::string{"a" }), 1); |
308 | EXPECT_EQ(obj.count(str), 1); |
309 | |
310 | EXPECT_EQ(obj.count(b), 0); |
311 | EXPECT_EQ(obj.count(StringPiece{b}), 0); |
312 | EXPECT_EQ(obj.count(StaticStrings::kBar), 0); |
313 | |
314 | // operator[] |
315 | EXPECT_EQ(obj[empty], 456); |
316 | EXPECT_EQ(obj[nullptr], 456); |
317 | EXPECT_EQ(obj[foo], 789); |
318 | |
319 | EXPECT_EQ(obj[a], 123); |
320 | EXPECT_EQ(obj[StaticStrings::kA], 123); |
321 | EXPECT_EQ(obj["a" ], 123); |
322 | |
323 | EXPECT_EQ(obj[sp], 123); |
324 | EXPECT_EQ(obj[StringPiece{"a" }], 123); |
325 | EXPECT_EQ(obj[StaticStrings::kFoo], 789); |
326 | |
327 | EXPECT_EQ(obj[std::string{"a" }], 123); |
328 | EXPECT_EQ(obj[str], 123); |
329 | |
330 | EXPECT_EQ(obj[b], nullptr); |
331 | obj[b] = 42; |
332 | EXPECT_EQ(obj[StringPiece{b}], 42); |
333 | obj[StaticStrings::kBar] = 43; |
334 | EXPECT_EQ(obj["bar" ], 43); |
335 | |
336 | // erase() + dynamic&& |
337 | EXPECT_EQ(obj.erase(StaticStrings::kB), /* num elements erased */ 1); |
338 | |
339 | dynamic obj2 = obj; |
340 | dynamic obj3 = obj; |
341 | dynamic obj4 = obj; |
342 | EXPECT_EQ(std::move(obj).find(StaticStrings::kFoo)->second, 789); |
343 | EXPECT_EQ(std::move(obj2).at(StaticStrings::kA), 123); |
344 | EXPECT_EQ(std::move(obj3)[nullptr], 456); |
345 | EXPECT_EQ(std::move(obj4).erase(StaticStrings::kBar), 1); |
346 | } |
347 | |
348 | TEST(Dynamic, CastFromVectorOfBooleans) { |
349 | std::vector<bool> b; |
350 | b.push_back(true); |
351 | b.push_back(false); |
352 | dynamic obj = dynamic::object("a" , b[0])("b" , b[1]); |
353 | EXPECT_EQ(obj.at("a" ), true); |
354 | EXPECT_EQ(obj.at("b" ), false); |
355 | } |
356 | |
357 | TEST(Dynamic, CastFromConstVectorOfBooleans) { |
358 | const std::vector<bool> b = {true, false}; |
359 | dynamic obj = dynamic::object("a" , b[0])("b" , b[1]); |
360 | EXPECT_EQ(obj.at("a" ), true); |
361 | EXPECT_EQ(obj.at("b" ), false); |
362 | } |
363 | |
364 | TEST(Dynamic, ObjectErase) { |
365 | dynamic obj = dynamic::object("key1" , "val" )("key2" , "val2" ); |
366 | EXPECT_EQ(obj.count("key1" ), 1); |
367 | EXPECT_EQ(obj.count("key2" ), 1); |
368 | EXPECT_EQ(obj.erase("key1" ), 1); |
369 | EXPECT_EQ(obj.count("key1" ), 0); |
370 | EXPECT_EQ(obj.count("key2" ), 1); |
371 | EXPECT_EQ(obj.erase("key1" ), 0); |
372 | obj["key1" ] = 12; |
373 | EXPECT_EQ(obj.count("key1" ), 1); |
374 | EXPECT_EQ(obj.count("key2" ), 1); |
375 | auto it = obj.find("key2" ); |
376 | obj.erase(it); |
377 | EXPECT_EQ(obj.count("key1" ), 1); |
378 | EXPECT_EQ(obj.count("key2" ), 0); |
379 | |
380 | obj["asd" ] = 42.0; |
381 | obj["foo" ] = 42.0; |
382 | EXPECT_EQ(obj.size(), 3); |
383 | auto ret = obj.erase(std::next(obj.items().begin()), obj.items().end()); |
384 | EXPECT_TRUE(ret == obj.items().end()); |
385 | EXPECT_EQ(obj.size(), 1); |
386 | obj.erase(obj.items().begin()); |
387 | EXPECT_TRUE(obj.empty()); |
388 | } |
389 | |
390 | TEST(Dynamic, ArrayErase) { |
391 | dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6); |
392 | |
393 | EXPECT_THROW(arr.erase(1), std::exception); |
394 | EXPECT_EQ(arr.size(), 6); |
395 | EXPECT_EQ(arr[0], 1); |
396 | arr.erase(arr.begin()); |
397 | EXPECT_EQ(arr.size(), 5); |
398 | |
399 | arr.erase(std::next(arr.begin()), std::prev(arr.end())); |
400 | EXPECT_EQ(arr.size(), 2); |
401 | EXPECT_EQ(arr[0], 2); |
402 | EXPECT_EQ(arr[1], 6); |
403 | } |
404 | |
405 | TEST(Dynamic, StringBasics) { |
406 | dynamic str = "hello world" ; |
407 | EXPECT_EQ(11, str.size()); |
408 | EXPECT_FALSE(str.empty()); |
409 | str = "" ; |
410 | EXPECT_TRUE(str.empty()); |
411 | } |
412 | |
413 | TEST(Dynamic, ArrayBasics) { |
414 | dynamic array = dynamic::array(1, 2, 3); |
415 | EXPECT_EQ(array.size(), 3); |
416 | EXPECT_EQ(array.at(0), 1); |
417 | EXPECT_EQ(array.at(1), 2); |
418 | EXPECT_EQ(array.at(2), 3); |
419 | |
420 | EXPECT_ANY_THROW(array.at(-1)); |
421 | EXPECT_ANY_THROW(array.at(3)); |
422 | |
423 | array.push_back("foo" ); |
424 | EXPECT_EQ(array.size(), 4); |
425 | |
426 | array.resize(12, "something" ); |
427 | EXPECT_EQ(array.size(), 12); |
428 | EXPECT_EQ(array[11], "something" ); |
429 | } |
430 | |
431 | TEST(Dynamic, DeepCopy) { |
432 | dynamic val = dynamic::array("foo" , "bar" , dynamic::array("foo1" , "bar1" )); |
433 | EXPECT_EQ(val.at(2).at(0), "foo1" ); |
434 | EXPECT_EQ(val.at(2).at(1), "bar1" ); |
435 | dynamic val2 = val; |
436 | EXPECT_EQ(val2.at(2).at(0), "foo1" ); |
437 | EXPECT_EQ(val2.at(2).at(1), "bar1" ); |
438 | EXPECT_EQ(val.at(2).at(0), "foo1" ); |
439 | EXPECT_EQ(val.at(2).at(1), "bar1" ); |
440 | val2.at(2).at(0) = "foo3" ; |
441 | val2.at(2).at(1) = "bar3" ; |
442 | EXPECT_EQ(val.at(2).at(0), "foo1" ); |
443 | EXPECT_EQ(val.at(2).at(1), "bar1" ); |
444 | EXPECT_EQ(val2.at(2).at(0), "foo3" ); |
445 | EXPECT_EQ(val2.at(2).at(1), "bar3" ); |
446 | |
447 | dynamic obj = dynamic::object("a" , "b" )("c" , dynamic::array("d" , "e" , "f" )); |
448 | EXPECT_EQ(obj.at("a" ), "b" ); |
449 | dynamic obj2 = obj; |
450 | obj2.at("a" ) = dynamic::array(1, 2, 3); |
451 | EXPECT_EQ(obj.at("a" ), "b" ); |
452 | dynamic expected = dynamic::array(1, 2, 3); |
453 | EXPECT_EQ(obj2.at("a" ), expected); |
454 | } |
455 | |
456 | TEST(Dynamic, ArrayReassignment) { |
457 | dynamic o = 1; |
458 | dynamic d1 = dynamic::array(o); |
459 | EXPECT_EQ(dynamic::ARRAY, d1.type()); |
460 | |
461 | d1 = dynamic::array(o); |
462 | EXPECT_EQ(dynamic::ARRAY, d1.type()); |
463 | } |
464 | |
465 | TEST(Dynamic, Operator) { |
466 | bool caught = false; |
467 | try { |
468 | dynamic d1 = dynamic::object; |
469 | dynamic d2 = dynamic::object; |
470 | auto foo = d1 < d2; |
471 | LOG(ERROR) << "operator < returned " << static_cast<int>(foo) |
472 | << " instead of throwing" ; |
473 | } catch (std::exception const&) { |
474 | caught = true; |
475 | } |
476 | EXPECT_TRUE(caught); |
477 | |
478 | dynamic foo = "asd" ; |
479 | dynamic bar = "bar" ; |
480 | dynamic sum = foo + bar; |
481 | EXPECT_EQ(sum, "asdbar" ); |
482 | |
483 | dynamic some = 12; |
484 | dynamic nums = 4; |
485 | dynamic math = some / nums; |
486 | EXPECT_EQ(math, 3); |
487 | } |
488 | |
489 | TEST(Dynamic, Conversions) { |
490 | dynamic str = "12.0" ; |
491 | EXPECT_EQ(str.asDouble(), 12.0); |
492 | EXPECT_ANY_THROW(str.asInt()); |
493 | EXPECT_ANY_THROW(str.asBool()); |
494 | |
495 | str = "12" ; |
496 | EXPECT_EQ(str.asInt(), 12); |
497 | EXPECT_EQ(str.asDouble(), 12.0); |
498 | str = "0" ; |
499 | EXPECT_EQ(str.asBool(), false); |
500 | EXPECT_EQ(str.asInt(), 0); |
501 | EXPECT_EQ(str.asDouble(), 0); |
502 | EXPECT_EQ(str.asString(), "0" ); |
503 | |
504 | dynamic num = 12; |
505 | EXPECT_EQ("12" , num.asString()); |
506 | EXPECT_EQ(12.0, num.asDouble()); |
507 | } |
508 | |
509 | TEST(Dynamic, GetSetDefaultTest) { |
510 | dynamic d1 = dynamic::object("foo" , "bar" ); |
511 | EXPECT_EQ(d1.getDefault("foo" , "baz" ), "bar" ); |
512 | EXPECT_EQ(d1.getDefault("quux" , "baz" ), "baz" ); |
513 | |
514 | dynamic d2 = dynamic::object("foo" , "bar" ); |
515 | EXPECT_EQ(d2.setDefault("foo" , "quux" ), "bar" ); |
516 | d2.setDefault("bar" , dynamic::array).push_back(42); |
517 | EXPECT_EQ(d2["bar" ][0], 42); |
518 | |
519 | dynamic d3 = dynamic::object, empty = dynamic::object; |
520 | EXPECT_EQ(d3.getDefault("foo" ), empty); |
521 | d3.setDefault("foo" )["bar" ] = "baz" ; |
522 | EXPECT_EQ(d3["foo" ]["bar" ], "baz" ); |
523 | |
524 | // we do not allow getDefault/setDefault on arrays |
525 | dynamic d4 = dynamic::array; |
526 | EXPECT_ANY_THROW(d4.getDefault("foo" , "bar" )); |
527 | EXPECT_ANY_THROW(d4.setDefault("foo" , "bar" )); |
528 | |
529 | // Using dynamic keys |
530 | dynamic k10{10}, k20{20}, kTrue{true}; |
531 | dynamic d5 = dynamic::object(k10, "foo" ); |
532 | EXPECT_EQ(d5.setDefault(k10, "bar" ), "foo" ); |
533 | EXPECT_EQ(d5.setDefault(k20, "bar" ), "bar" ); |
534 | EXPECT_EQ(d5.setDefault(kTrue, "baz" ), "baz" ); |
535 | EXPECT_EQ(d5.setDefault(StaticStrings::kA, "foo" ), "foo" ); |
536 | EXPECT_EQ(d5.setDefault(StaticStrings::kB, "foo" ), "foo" ); |
537 | EXPECT_EQ(d5.setDefault(StaticStrings::kFoo, "bar" ), "bar" ); |
538 | EXPECT_EQ(d5.setDefault(StaticStrings::kBar, "foo" ), "foo" ); |
539 | } |
540 | |
541 | TEST(Dynamic, ObjectForwarding) { |
542 | // Make sure dynamic::object can be constructed the same way as any |
543 | // dynamic. |
544 | dynamic d = dynamic::object("asd" , dynamic::array("foo" , "bar" )); |
545 | // clang-format off |
546 | dynamic d2 = dynamic::object("key2" , dynamic::array("value" , "words" )) |
547 | ("key" , "value1" ); |
548 | // clang-format on |
549 | } |
550 | |
551 | TEST(Dynamic, GetPtr) { |
552 | dynamic array = dynamic::array(1, 2, "three" ); |
553 | EXPECT_TRUE(array.get_ptr(0)); |
554 | EXPECT_FALSE(array.get_ptr(-1)); |
555 | EXPECT_FALSE(array.get_ptr(3)); |
556 | EXPECT_EQ(dynamic("three" ), *array.get_ptr(2)); |
557 | const dynamic& carray = array; |
558 | EXPECT_EQ(dynamic("three" ), *carray.get_ptr(2)); |
559 | |
560 | dynamic object = dynamic::object("one" , 1)("two" , 2); |
561 | EXPECT_TRUE(object.get_ptr("one" )); |
562 | EXPECT_FALSE(object.get_ptr("three" )); |
563 | EXPECT_EQ(dynamic(2), *object.get_ptr("two" )); |
564 | *object.get_ptr("one" ) = 11; |
565 | EXPECT_EQ(dynamic(11), *object.get_ptr("one" )); |
566 | const dynamic& cobject = object; |
567 | EXPECT_EQ(dynamic(2), *cobject.get_ptr("two" )); |
568 | } |
569 | |
570 | TEST(Dynamic, Assignment) { |
571 | const dynamic ds[] = { |
572 | dynamic::array(1, 2, 3), |
573 | dynamic::object("a" , true), |
574 | 24, |
575 | 26.5, |
576 | true, |
577 | "hello" , |
578 | }; |
579 | const dynamic dd[] = { |
580 | dynamic::array(5, 6), |
581 | dynamic::object("t" , "T" )(1, 7), |
582 | 9000, |
583 | 3.14159, |
584 | false, |
585 | "world" , |
586 | }; |
587 | for (const auto& source : ds) { |
588 | for (const auto& dest : dd) { |
589 | dynamic tmp(dest); |
590 | EXPECT_EQ(tmp, dest); |
591 | tmp = source; |
592 | EXPECT_EQ(tmp, source); |
593 | } |
594 | } |
595 | } |
596 | |
597 | std::string make_long_string() { |
598 | return std::string(100, 'a'); |
599 | } |
600 | |
601 | TEST(Dynamic, GetDefault) { |
602 | const auto s = make_long_string(); |
603 | dynamic kDynamicKey{10}; |
604 | dynamic ds(s); |
605 | dynamic tmp(s); |
606 | dynamic d1 = dynamic::object("key1" , s); |
607 | dynamic d2 = dynamic::object("key2" , s); |
608 | dynamic d3 = dynamic::object("key3" , s); |
609 | dynamic d4 = dynamic::object("key4" , s); |
610 | // lvalue - lvalue |
611 | dynamic ayy("ayy" ); |
612 | EXPECT_EQ(ds, d1.getDefault("key1" , ayy)); |
613 | EXPECT_EQ(ds, d1.getDefault("key1" , ayy)); |
614 | EXPECT_EQ(ds, d1.getDefault("not-a-key" , tmp)); |
615 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kA, tmp)); |
616 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kB, tmp)); |
617 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kFoo, tmp)); |
618 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kBar, tmp)); |
619 | EXPECT_EQ(ds, d1.getDefault(kDynamicKey, tmp)); |
620 | EXPECT_EQ(ds, tmp); |
621 | // lvalue - rvalue |
622 | EXPECT_EQ(ds, d1.getDefault("key1" , "ayy" )); |
623 | EXPECT_EQ(ds, d1.getDefault("key1" , "ayy" )); |
624 | EXPECT_EQ(ds, d1.getDefault("not-a-key" , std::move(tmp))); |
625 | EXPECT_NE(ds, tmp); |
626 | tmp = s; |
627 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kA, std::move(tmp))); |
628 | EXPECT_NE(ds, tmp); |
629 | tmp = s; |
630 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kB, std::move(tmp))); |
631 | EXPECT_NE(ds, tmp); |
632 | tmp = s; |
633 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kFoo, std::move(tmp))); |
634 | EXPECT_NE(ds, tmp); |
635 | tmp = s; |
636 | EXPECT_EQ(ds, d1.getDefault(StaticStrings::kBar, std::move(tmp))); |
637 | EXPECT_NE(ds, tmp); |
638 | tmp = s; |
639 | EXPECT_EQ(ds, d1.getDefault(kDynamicKey, std::move(tmp))); |
640 | EXPECT_NE(ds, tmp); |
641 | // rvalue - lvalue |
642 | tmp = s; |
643 | EXPECT_EQ(ds, std::move(d1).getDefault("key1" , ayy)); |
644 | EXPECT_NE(ds, d1["key1" ]); |
645 | EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key" , tmp)); |
646 | EXPECT_EQ(dynamic(dynamic::object("key2" , s)), d2); |
647 | EXPECT_EQ(ds, tmp); |
648 | EXPECT_EQ(ds, std::move(d2).getDefault(StaticStrings::kA, tmp)); |
649 | EXPECT_EQ(dynamic(dynamic::object("key2" , s)), d2); |
650 | EXPECT_EQ(ds, tmp); |
651 | EXPECT_EQ(ds, std::move(d2).getDefault(StaticStrings::kB, tmp)); |
652 | EXPECT_EQ(dynamic(dynamic::object("key2" , s)), d2); |
653 | EXPECT_EQ(ds, tmp); |
654 | EXPECT_EQ(ds, std::move(d2).getDefault(StaticStrings::kFoo, tmp)); |
655 | EXPECT_EQ(dynamic(dynamic::object("key2" , s)), d2); |
656 | EXPECT_EQ(ds, tmp); |
657 | EXPECT_EQ(ds, std::move(d2).getDefault(StaticStrings::kBar, tmp)); |
658 | EXPECT_EQ(dynamic(dynamic::object("key2" , s)), d2); |
659 | EXPECT_EQ(ds, tmp); |
660 | EXPECT_EQ(ds, std::move(d2).getDefault(kDynamicKey, tmp)); |
661 | EXPECT_EQ(dynamic(dynamic::object("key2" , s)), d2); |
662 | EXPECT_EQ(ds, tmp); |
663 | // rvalue - rvalue |
664 | EXPECT_EQ(ds, std::move(d3).getDefault("key3" , std::move(tmp))); |
665 | EXPECT_NE(ds, d3["key3" ]); |
666 | EXPECT_EQ(ds, tmp); |
667 | EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key" , std::move(tmp))); |
668 | EXPECT_EQ(dynamic(dynamic::object("key4" , s)), d4); |
669 | EXPECT_NE(ds, tmp); |
670 | tmp = s; |
671 | EXPECT_EQ(ds, std::move(d4).getDefault(StaticStrings::kA, std::move(tmp))); |
672 | EXPECT_EQ(dynamic(dynamic::object("key4" , s)), d4); |
673 | EXPECT_NE(ds, tmp); |
674 | tmp = s; |
675 | EXPECT_EQ(ds, std::move(d4).getDefault(StaticStrings::kB, std::move(tmp))); |
676 | EXPECT_EQ(dynamic(dynamic::object("key4" , s)), d4); |
677 | EXPECT_NE(ds, tmp); |
678 | tmp = s; |
679 | EXPECT_EQ(ds, std::move(d4).getDefault(StaticStrings::kFoo, std::move(tmp))); |
680 | EXPECT_EQ(dynamic(dynamic::object("key4" , s)), d4); |
681 | EXPECT_NE(ds, tmp); |
682 | tmp = s; |
683 | EXPECT_EQ(ds, std::move(d4).getDefault(StaticStrings::kBar, std::move(tmp))); |
684 | EXPECT_EQ(dynamic(dynamic::object("key4" , s)), d4); |
685 | EXPECT_NE(ds, tmp); |
686 | tmp = s; |
687 | EXPECT_EQ(ds, std::move(d4).getDefault(kDynamicKey, std::move(tmp))); |
688 | EXPECT_EQ(dynamic(dynamic::object("key4" , s)), d4); |
689 | EXPECT_NE(ds, tmp); |
690 | } |
691 | |
692 | TEST(Dynamic, GetString) { |
693 | const dynamic c(make_long_string()); |
694 | dynamic d(make_long_string()); |
695 | dynamic m(make_long_string()); |
696 | |
697 | auto s = make_long_string(); |
698 | |
699 | EXPECT_EQ(s, c.getString()); |
700 | EXPECT_EQ(s, c.getString()); |
701 | |
702 | d.getString() += " hello" ; |
703 | EXPECT_EQ(s + " hello" , d.getString()); |
704 | EXPECT_EQ(s + " hello" , d.getString()); |
705 | |
706 | EXPECT_EQ(s, std::move(m).getString()); |
707 | EXPECT_EQ(s, m.getString()); |
708 | auto moved = std::move(m).getString(); |
709 | EXPECT_EQ(s, moved); |
710 | EXPECT_NE(dynamic(s), m); |
711 | } |
712 | |
713 | TEST(Dynamic, GetSmallThings) { |
714 | const dynamic cint(5); |
715 | const dynamic cdouble(5.0); |
716 | const dynamic cbool(true); |
717 | dynamic dint(5); |
718 | dynamic ddouble(5.0); |
719 | dynamic dbool(true); |
720 | dynamic mint(5); |
721 | dynamic mdouble(5.0); |
722 | dynamic mbool(true); |
723 | |
724 | EXPECT_EQ(5, cint.getInt()); |
725 | dint.getInt() = 6; |
726 | EXPECT_EQ(6, dint.getInt()); |
727 | EXPECT_EQ(5, std::move(mint).getInt()); |
728 | |
729 | EXPECT_EQ(5.0, cdouble.getDouble()); |
730 | ddouble.getDouble() = 6.0; |
731 | EXPECT_EQ(6.0, ddouble.getDouble()); |
732 | EXPECT_EQ(5.0, std::move(mdouble).getDouble()); |
733 | |
734 | EXPECT_TRUE(cbool.getBool()); |
735 | dbool.getBool() = false; |
736 | EXPECT_FALSE(dbool.getBool()); |
737 | EXPECT_TRUE(std::move(mbool).getBool()); |
738 | } |
739 | |
740 | TEST(Dynamic, At) { |
741 | const dynamic cd = dynamic::object("key1" , make_long_string()); |
742 | dynamic dd = dynamic::object("key1" , make_long_string()); |
743 | dynamic md = dynamic::object("key1" , make_long_string()); |
744 | |
745 | dynamic ds(make_long_string()); |
746 | EXPECT_EQ(ds, cd.at("key1" )); |
747 | EXPECT_EQ(ds, cd.at("key1" )); |
748 | |
749 | dd.at("key1" ).getString() += " hello" ; |
750 | EXPECT_EQ(dynamic(make_long_string() + " hello" ), dd.at("key1" )); |
751 | EXPECT_EQ(dynamic(make_long_string() + " hello" ), dd.at("key1" )); |
752 | |
753 | EXPECT_EQ(ds, std::move(md).at("key1" )); // move available, but not performed |
754 | EXPECT_EQ(ds, md.at("key1" )); |
755 | dynamic moved = std::move(md).at("key1" ); // move performed |
756 | EXPECT_EQ(ds, moved); |
757 | EXPECT_NE(ds, md.at("key1" )); |
758 | } |
759 | |
760 | TEST(Dynamic, Brackets) { |
761 | const dynamic cd = dynamic::object("key1" , make_long_string()); |
762 | dynamic dd = dynamic::object("key1" , make_long_string()); |
763 | dynamic md = dynamic::object("key1" , make_long_string()); |
764 | |
765 | dynamic ds(make_long_string()); |
766 | EXPECT_EQ(ds, cd["key1" ]); |
767 | EXPECT_EQ(ds, cd["key1" ]); |
768 | |
769 | dd["key1" ].getString() += " hello" ; |
770 | EXPECT_EQ(dynamic(make_long_string() + " hello" ), dd["key1" ]); |
771 | EXPECT_EQ(dynamic(make_long_string() + " hello" ), dd["key1" ]); |
772 | |
773 | EXPECT_EQ(ds, std::move(md)["key1" ]); // move available, but not performed |
774 | EXPECT_EQ(ds, md["key1" ]); |
775 | dynamic moved = std::move(md)["key1" ]; // move performed |
776 | EXPECT_EQ(ds, moved); |
777 | EXPECT_NE(ds, md["key1" ]); |
778 | } |
779 | |
780 | TEST(Dynamic, PrintNull) { |
781 | std::stringstream ss; |
782 | ss << folly::dynamic(nullptr); |
783 | EXPECT_EQ("null" , ss.str()); |
784 | } |
785 | |
786 | TEST(Dynamic, WriteThroughArrayIterators) { |
787 | dynamic const cint(0); |
788 | dynamic d = dynamic::array(cint, cint, cint); |
789 | size_t size = d.size(); |
790 | |
791 | for (auto& val : d) { |
792 | EXPECT_EQ(val, cint); |
793 | } |
794 | EXPECT_EQ(d.size(), size); |
795 | |
796 | dynamic ds(make_long_string()); |
797 | for (auto& val : d) { |
798 | val = ds; // assign through reference |
799 | } |
800 | |
801 | ds = "short string" ; |
802 | dynamic ds2(make_long_string()); |
803 | |
804 | for (auto& val : d) { |
805 | EXPECT_EQ(val, ds2); |
806 | } |
807 | EXPECT_EQ(d.size(), size); |
808 | } |
809 | |
810 | TEST(Dynamic, MoveOutOfArrayIterators) { |
811 | dynamic ds(make_long_string()); |
812 | dynamic d = dynamic::array(ds, ds, ds); |
813 | size_t size = d.size(); |
814 | |
815 | for (auto& val : d) { |
816 | EXPECT_EQ(val, ds); |
817 | } |
818 | EXPECT_EQ(d.size(), size); |
819 | |
820 | for (auto& val : d) { |
821 | dynamic waste = std::move(val); // force moving out |
822 | EXPECT_EQ(waste, ds); |
823 | } |
824 | |
825 | for (auto& val : d) { |
826 | EXPECT_NE(val, ds); |
827 | } |
828 | EXPECT_EQ(d.size(), size); |
829 | } |
830 | |
831 | TEST(Dynamic, WriteThroughObjectIterators) { |
832 | dynamic const cint(0); |
833 | dynamic d = dynamic::object("key1" , cint)("key2" , cint); |
834 | size_t size = d.size(); |
835 | |
836 | for (auto& val : d.items()) { |
837 | EXPECT_EQ(val.second, cint); |
838 | } |
839 | EXPECT_EQ(d.size(), size); |
840 | |
841 | dynamic ds(make_long_string()); |
842 | for (auto& val : d.items()) { |
843 | val.second = ds; // assign through reference |
844 | } |
845 | |
846 | ds = "short string" ; |
847 | dynamic ds2(make_long_string()); |
848 | for (auto& val : d.items()) { |
849 | EXPECT_EQ(val.second, ds2); |
850 | } |
851 | EXPECT_EQ(d.size(), size); |
852 | } |
853 | |
854 | TEST(Dynamic, MoveOutOfObjectIterators) { |
855 | dynamic ds(make_long_string()); |
856 | dynamic d = dynamic::object("key1" , ds)("key2" , ds); |
857 | size_t size = d.size(); |
858 | |
859 | for (auto& val : d.items()) { |
860 | EXPECT_EQ(val.second, ds); |
861 | } |
862 | EXPECT_EQ(d.size(), size); |
863 | |
864 | for (auto& val : d.items()) { |
865 | dynamic waste = std::move(val.second); // force moving out |
866 | EXPECT_EQ(waste, ds); |
867 | } |
868 | |
869 | for (auto& val : d.items()) { |
870 | EXPECT_NE(val.second, ds); |
871 | } |
872 | EXPECT_EQ(d.size(), size); |
873 | } |
874 | |
875 | TEST(Dynamic, ArrayIteratorInterop) { |
876 | dynamic d = dynamic::array(0, 1, 2); |
877 | dynamic const& cdref = d; |
878 | |
879 | auto it = d.begin(); |
880 | auto cit = cdref.begin(); |
881 | |
882 | EXPECT_EQ(it, cit); |
883 | EXPECT_EQ(cit, d.begin()); |
884 | EXPECT_EQ(it, cdref.begin()); |
885 | |
886 | // Erase using non-const iterator |
887 | it = d.erase(it); |
888 | cit = cdref.begin(); |
889 | EXPECT_EQ(*it, 1); |
890 | EXPECT_EQ(cit, it); |
891 | |
892 | // Assign from non-const to const, preserve equality |
893 | decltype(cit) cit2 = it; |
894 | EXPECT_EQ(cit, cit2); |
895 | } |
896 | |
897 | TEST(Dynamic, ObjectIteratorInterop) { |
898 | dynamic ds = make_long_string(); |
899 | dynamic d = dynamic::object(0, ds)(1, ds)(2, ds); |
900 | dynamic const& cdref = d; |
901 | |
902 | auto it = d.find(0); |
903 | auto cit = cdref.find(0); |
904 | EXPECT_NE(it, cdref.items().end()); |
905 | EXPECT_NE(cit, cdref.items().end()); |
906 | EXPECT_EQ(it, cit); |
907 | |
908 | ++cit; |
909 | // Erase using non-const iterator |
910 | auto it2 = d.erase(it); |
911 | EXPECT_EQ(cit, it2); |
912 | |
913 | // Assign from non-const to const, preserve equality |
914 | decltype(cit) cit2 = it2; |
915 | EXPECT_EQ(cit, cit2); |
916 | } |
917 | |
918 | TEST(Dynamic, MergePatchWithNonObject) { |
919 | dynamic target = dynamic::object("a" , "b" )("c" , "d" ); |
920 | |
921 | dynamic patch = dynamic::array(1, 2, 3); |
922 | target.merge_patch(patch); |
923 | |
924 | EXPECT_TRUE(target.isArray()); |
925 | } |
926 | |
927 | TEST(Dynamic, MergePatchReplaceInFlatObject) { |
928 | dynamic target = dynamic::object("a" , "b" )("c" , "d" ); |
929 | dynamic patch = dynamic::object("a" , "z" ); |
930 | |
931 | target.merge_patch(patch); |
932 | |
933 | EXPECT_EQ("z" , target["a" ].getString()); |
934 | EXPECT_EQ("d" , target["c" ].getString()); |
935 | } |
936 | |
937 | TEST(Dynamic, MergePatchAddInFlatObject) { |
938 | dynamic target = dynamic::object("a" , "b" )("c" , "d" ); |
939 | dynamic patch = dynamic::object("e" , "f" ); |
940 | target.merge_patch(patch); |
941 | |
942 | EXPECT_EQ("b" , target["a" ].getString()); |
943 | EXPECT_EQ("d" , target["c" ].getString()); |
944 | EXPECT_EQ("f" , target["e" ].getString()); |
945 | } |
946 | |
947 | TEST(Dynamic, MergePatchReplaceInNestedObject) { |
948 | dynamic target = dynamic::object("a" , dynamic::object("d" , 10))("b" , "c" ); |
949 | dynamic patch = dynamic::object("a" , dynamic::object("d" , 100)); |
950 | target.merge_patch(patch); |
951 | |
952 | EXPECT_EQ(100, target["a" ]["d" ].getInt()); |
953 | EXPECT_EQ("c" , target["b" ].getString()); |
954 | } |
955 | |
956 | TEST(Dynamic, MergePatchAddInNestedObject) { |
957 | dynamic target = dynamic::object("a" , dynamic::object("d" , 10))("b" , "c" ); |
958 | dynamic patch = dynamic::object("a" , dynamic::object("e" , "f" )); |
959 | |
960 | target.merge_patch(patch); |
961 | |
962 | EXPECT_EQ(10, target["a" ]["d" ].getInt()); |
963 | EXPECT_EQ("f" , target["a" ]["e" ].getString()); |
964 | EXPECT_EQ("c" , target["b" ].getString()); |
965 | } |
966 | |
967 | TEST(Dynamic, MergeNestePatch) { |
968 | dynamic target = dynamic::object("a" , dynamic::object("d" , 10))("b" , "c" ); |
969 | dynamic patch = dynamic::object( |
970 | "a" , dynamic::object("d" , dynamic::array(1, 2, 3)))("b" , 100); |
971 | target.merge_patch(patch); |
972 | |
973 | EXPECT_EQ(100, target["b" ].getInt()); |
974 | { |
975 | auto ary = patch["a" ]["d" ]; |
976 | ASSERT_TRUE(ary.isArray()); |
977 | EXPECT_EQ(1, ary[0].getInt()); |
978 | EXPECT_EQ(2, ary[1].getInt()); |
979 | EXPECT_EQ(3, ary[2].getInt()); |
980 | } |
981 | } |
982 | |
983 | TEST(Dynamic, MergePatchRemoveInFlatObject) { |
984 | dynamic target = dynamic::object("a" , "b" )("c" , "d" ); |
985 | dynamic patch = dynamic::object("c" , nullptr); |
986 | target.merge_patch(patch); |
987 | |
988 | EXPECT_EQ("b" , target["a" ].getString()); |
989 | EXPECT_EQ(0, target.count("c" )); |
990 | } |
991 | |
992 | TEST(Dynamic, MergePatchRemoveInNestedObject) { |
993 | dynamic target = |
994 | dynamic::object("a" , dynamic::object("d" , 10)("e" , "f" ))("b" , "c" ); |
995 | dynamic patch = dynamic::object("a" , dynamic::object("e" , nullptr)); |
996 | target.merge_patch(patch); |
997 | |
998 | EXPECT_EQ(10, target["a" ]["d" ].getInt()); |
999 | EXPECT_EQ(0, target["a" ].count("e" )); |
1000 | EXPECT_EQ("c" , target["b" ].getString()); |
1001 | } |
1002 | |
1003 | TEST(Dynamic, MergePatchRemoveNonExistent) { |
1004 | dynamic target = dynamic::object("a" , "b" )("c" , "d" ); |
1005 | dynamic patch = dynamic::object("e" , nullptr); |
1006 | target.merge_patch(patch); |
1007 | |
1008 | EXPECT_EQ("b" , target["a" ].getString()); |
1009 | EXPECT_EQ("d" , target["c" ].getString()); |
1010 | EXPECT_EQ(2, target.size()); |
1011 | } |
1012 | |
1013 | TEST(Dynamic, MergeDiffFlatObjects) { |
1014 | dynamic source = dynamic::object("a" , 0)("b" , 1)("c" , 2); |
1015 | dynamic target = dynamic::object("a" , 1)("b" , 2); |
1016 | auto patch = dynamic::merge_diff(source, target); |
1017 | |
1018 | EXPECT_EQ(3, patch.size()); |
1019 | EXPECT_EQ(1, patch["a" ].getInt()); |
1020 | EXPECT_EQ(2, patch["b" ].getInt()); |
1021 | EXPECT_TRUE(patch["c" ].isNull()); |
1022 | |
1023 | source.merge_patch(patch); |
1024 | EXPECT_EQ(source, target); |
1025 | } |
1026 | |
1027 | TEST(Dynamic, MergeDiffNestedObjects) { |
1028 | dynamic source = dynamic::object("a" , dynamic::object("b" , 1)("c" , 2))( |
1029 | "d" , dynamic::array(1, 2, 3)); |
1030 | dynamic target = dynamic::object("a" , dynamic::object("b" , 2))( |
1031 | "d" , dynamic::array(2, 3, 4)); |
1032 | |
1033 | auto patch = dynamic::merge_diff(source, target); |
1034 | |
1035 | EXPECT_EQ(2, patch.size()); |
1036 | EXPECT_EQ(2, patch["a" ].size()); |
1037 | |
1038 | EXPECT_EQ(2, patch["a" ]["b" ].getInt()); |
1039 | EXPECT_TRUE(patch["a" ]["c" ].isNull()); |
1040 | |
1041 | EXPECT_TRUE(patch["d" ].isArray()); |
1042 | EXPECT_EQ(3, patch["d" ].size()); |
1043 | EXPECT_EQ(2, patch["d" ][0].getInt()); |
1044 | EXPECT_EQ(3, patch["d" ][1].getInt()); |
1045 | EXPECT_EQ(4, patch["d" ][2].getInt()); |
1046 | |
1047 | source.merge_patch(patch); |
1048 | EXPECT_EQ(source, target); |
1049 | } |
1050 | |
1051 | using folly::json_pointer; |
1052 | |
1053 | TEST(Dynamic, JSONPointer) { |
1054 | using err_code = folly::dynamic::json_pointer_resolution_error_code; |
1055 | |
1056 | dynamic target = dynamic::object; |
1057 | dynamic ary = dynamic::array("bar" , "baz" , dynamic::array("bletch" , "xyzzy" )); |
1058 | target["foo" ] = ary; |
1059 | target["" ] = 0; |
1060 | target["a/b" ] = 1; |
1061 | target["c%d" ] = 2; |
1062 | target["e^f" ] = 3; |
1063 | target["g|h" ] = 4; |
1064 | target["i\\j" ] = 5; |
1065 | target["k\"l" ] = 6; |
1066 | target[" " ] = 7; |
1067 | target["m~n" ] = 8; |
1068 | target["xyz" ] = dynamic::object; |
1069 | target["xyz" ]["" ] = dynamic::object("nested" , "abc" ); |
1070 | target["xyz" ]["def" ] = dynamic::array(1, 2, 3); |
1071 | target["long_array" ] = dynamic::array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12); |
1072 | target["-" ] = dynamic::object("x" , "y" ); |
1073 | |
1074 | EXPECT_EQ(target, *target.get_ptr(json_pointer::parse("" ))); |
1075 | EXPECT_EQ(ary, *(target.get_ptr(json_pointer::parse("/foo" )))); |
1076 | EXPECT_EQ("bar" , target.get_ptr(json_pointer::parse("/foo/0" ))->getString()); |
1077 | EXPECT_EQ(0, target.get_ptr(json_pointer::parse("/" ))->getInt()); |
1078 | EXPECT_EQ(1, target.get_ptr(json_pointer::parse("/a~1b" ))->getInt()); |
1079 | EXPECT_EQ(2, target.get_ptr(json_pointer::parse("/c%d" ))->getInt()); |
1080 | EXPECT_EQ(3, target.get_ptr(json_pointer::parse("/e^f" ))->getInt()); |
1081 | EXPECT_EQ(4, target.get_ptr(json_pointer::parse("/g|h" ))->getInt()); |
1082 | EXPECT_EQ(5, target.get_ptr(json_pointer::parse("/i\\j" ))->getInt()); |
1083 | EXPECT_EQ(6, target.get_ptr(json_pointer::parse("/k\"l" ))->getInt()); |
1084 | EXPECT_EQ(7, target.get_ptr(json_pointer::parse("/ " ))->getInt()); |
1085 | EXPECT_EQ(8, target.get_ptr(json_pointer::parse("/m~0n" ))->getInt()); |
1086 | // empty key in path |
1087 | EXPECT_EQ( |
1088 | "abc" , target.get_ptr(json_pointer::parse("/xyz//nested" ))->getString()); |
1089 | EXPECT_EQ(3, target.get_ptr(json_pointer::parse("/xyz/def/2" ))->getInt()); |
1090 | EXPECT_EQ("baz" , ary.get_ptr(json_pointer::parse("/1" ))->getString()); |
1091 | EXPECT_EQ("bletch" , ary.get_ptr(json_pointer::parse("/2/0" ))->getString()); |
1092 | // double-digit index |
1093 | EXPECT_EQ( |
1094 | 12, target.get_ptr(json_pointer::parse("/long_array/11" ))->getInt()); |
1095 | // allow '-' to index in objects |
1096 | EXPECT_EQ("y" , target.get_ptr(json_pointer::parse("/-/x" ))->getString()); |
1097 | |
1098 | // validate parent pointer functionality |
1099 | |
1100 | { |
1101 | auto const resolved_value = |
1102 | target.try_get_ptr(json_pointer::parse("" )).value(); |
1103 | EXPECT_EQ(nullptr, resolved_value.parent); |
1104 | } |
1105 | |
1106 | { |
1107 | auto parent_json_ptr = json_pointer::parse("/xyz" ); |
1108 | auto json_ptr = json_pointer::parse("/xyz/def" ); |
1109 | |
1110 | auto const parent = target.get_ptr(parent_json_ptr); |
1111 | auto const resolved_value = target.try_get_ptr(json_ptr); |
1112 | |
1113 | EXPECT_EQ(parent, resolved_value.value().parent); |
1114 | EXPECT_TRUE(parent->isObject()); |
1115 | EXPECT_EQ("def" , resolved_value.value().parent_key); |
1116 | } |
1117 | |
1118 | { |
1119 | auto parent_json_ptr = json_pointer::parse("/foo" ); |
1120 | auto json_ptr = json_pointer::parse("/foo/1" ); |
1121 | |
1122 | auto const parent = target.get_ptr(parent_json_ptr); |
1123 | auto const resolved_value = target.try_get_ptr(json_ptr); |
1124 | |
1125 | EXPECT_EQ(parent, resolved_value.value().parent); |
1126 | EXPECT_TRUE(parent->isArray()); |
1127 | EXPECT_EQ(1, resolved_value.value().parent_index); |
1128 | } |
1129 | |
1130 | // |
1131 | // invalid pointer resolution cases |
1132 | // |
1133 | |
1134 | // invalid index formatting when accessing array |
1135 | { |
1136 | auto err = target.try_get_ptr(json_pointer::parse("/foo/01" )).error(); |
1137 | EXPECT_EQ(err_code::index_has_leading_zero, err.error_code); |
1138 | EXPECT_EQ(1, err.index); |
1139 | EXPECT_EQ(target.get_ptr(json_pointer::parse("/foo" )), err.context); |
1140 | EXPECT_THROW( |
1141 | target.get_ptr(json_pointer::parse("/foo/01" )), std::invalid_argument); |
1142 | } |
1143 | |
1144 | // non-existent keys/indexes |
1145 | { |
1146 | auto err = ary.try_get_ptr(json_pointer::parse("/3" )).error(); |
1147 | EXPECT_EQ(err_code::index_out_of_bounds, err.error_code); |
1148 | EXPECT_EQ(0, err.index); |
1149 | EXPECT_EQ(ary.get_ptr(json_pointer::parse("" )), err.context); |
1150 | EXPECT_EQ(nullptr, ary.get_ptr(json_pointer::parse("/3" ))); |
1151 | } |
1152 | |
1153 | { |
1154 | auto err = target.try_get_ptr(json_pointer::parse("/unknown_key" )).error(); |
1155 | EXPECT_EQ(err_code::key_not_found, err.error_code); |
1156 | EXPECT_EQ(0, err.index); |
1157 | EXPECT_EQ(target.get_ptr(json_pointer::parse("" )), err.context); |
1158 | EXPECT_EQ(nullptr, target.get_ptr(json_pointer::parse("/unknown_key" ))); |
1159 | } |
1160 | |
1161 | // fail to resolve index inside string |
1162 | { |
1163 | auto err = target.try_get_ptr(json_pointer::parse("/foo/0/0" )).error(); |
1164 | EXPECT_EQ(err_code::element_not_object_or_array, err.error_code); |
1165 | EXPECT_EQ(2, err.index); |
1166 | EXPECT_EQ(target.get_ptr(json_pointer::parse("/foo/0" )), err.context); |
1167 | EXPECT_THROW( |
1168 | target.get_ptr(json_pointer::parse("/foo/0/0" )), folly::TypeError); |
1169 | } |
1170 | |
1171 | // intermediate key not found |
1172 | { |
1173 | auto err = target.try_get_ptr(json_pointer::parse("/foox/test" )).error(); |
1174 | EXPECT_EQ(err_code::key_not_found, err.error_code); |
1175 | EXPECT_EQ(0, err.index); |
1176 | EXPECT_EQ(target.get_ptr(json_pointer::parse("" )), err.context); |
1177 | EXPECT_EQ(nullptr, target.get_ptr(json_pointer::parse("/foox/test" ))); |
1178 | } |
1179 | |
1180 | // Intermediate key is '-' in _array_ |
1181 | { |
1182 | auto err = target.try_get_ptr(json_pointer::parse("/foo/-/key" )).error(); |
1183 | EXPECT_EQ(err_code::json_pointer_out_of_bounds, err.error_code); |
1184 | EXPECT_EQ(2, err.index); |
1185 | EXPECT_EQ(target.get_ptr(json_pointer::parse("/foo" )), err.context); |
1186 | EXPECT_EQ(nullptr, target.get_ptr(json_pointer::parse("/foo/-/key" ))); |
1187 | } |
1188 | |
1189 | // invalid path in object (non-numeric index in array) |
1190 | { |
1191 | auto err = target.try_get_ptr(json_pointer::parse("/foo/2/bar" )).error(); |
1192 | EXPECT_EQ(err_code::index_not_numeric, err.error_code); |
1193 | EXPECT_EQ(2, err.index); |
1194 | EXPECT_EQ(target.get_ptr(json_pointer::parse("/foo/2" )), err.context); |
1195 | EXPECT_THROW( |
1196 | target.get_ptr(json_pointer::parse("/foo/2/bar" )), |
1197 | std::invalid_argument); |
1198 | } |
1199 | |
1200 | // Allow "-" index in the array |
1201 | { |
1202 | auto err = target.try_get_ptr(json_pointer::parse("/foo/-" )).error(); |
1203 | EXPECT_EQ(err_code::append_requested, err.error_code); |
1204 | EXPECT_EQ(1, err.index); |
1205 | EXPECT_EQ(target.get_ptr(json_pointer::parse("/foo" )), err.context); |
1206 | EXPECT_EQ(nullptr, target.get_ptr(json_pointer::parse("/foo/-" ))); |
1207 | } |
1208 | } |
1209 | |