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
25using folly::dynamic;
26using folly::StringPiece;
27
28TEST(Dynamic, Default) {
29 dynamic obj;
30 EXPECT_TRUE(obj.isNull());
31}
32
33TEST(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
178TEST(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
199namespace {
200
201struct 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
212TEST(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
348TEST(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
357TEST(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
364TEST(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
390TEST(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
405TEST(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
413TEST(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
431TEST(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
456TEST(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
465TEST(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
489TEST(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
509TEST(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
541TEST(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
551TEST(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
570TEST(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
597std::string make_long_string() {
598 return std::string(100, 'a');
599}
600
601TEST(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
692TEST(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
713TEST(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
740TEST(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
760TEST(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
780TEST(Dynamic, PrintNull) {
781 std::stringstream ss;
782 ss << folly::dynamic(nullptr);
783 EXPECT_EQ("null", ss.str());
784}
785
786TEST(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
810TEST(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
831TEST(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
854TEST(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
875TEST(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
897TEST(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
918TEST(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
927TEST(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
937TEST(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
947TEST(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
956TEST(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
967TEST(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
983TEST(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
992TEST(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
1003TEST(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
1013TEST(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
1027TEST(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
1051using folly::json_pointer;
1052
1053TEST(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