2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/dynamic.h>
19 #include <folly/portability/GTest.h>
21 #include <boost/next_prior.hpp>
25 #ifndef FOLLY_SANITIZE_ADDRESS
26 // This test runs without any external dependencies, including json.
27 // This means that if there's a test failure, there's no way to print
28 // a useful runtime representation of the folly::dynamic. We will
29 // live with this in order to test dependencies. This method is
30 // normally provided by json.cpp.
31 void dynamic::print_as_pseudo_json(std::ostream& out) const {
32 out << "<folly::dynamic object of type " << type_ << ">";
36 TEST(Dynamic, Default) {
38 EXPECT_TRUE(obj.isNull());
41 TEST(Dynamic, ObjectBasics) {
42 dynamic obj = dynamic::object("a", false);
43 EXPECT_EQ(obj.at("a"), false);
44 EXPECT_EQ(obj.size(), 1);
45 obj.insert("a", true);
46 EXPECT_EQ(obj.size(), 1);
47 EXPECT_EQ(obj.at("a"), true);
48 obj.at("a") = nullptr;
49 EXPECT_EQ(obj.size(), 1);
50 EXPECT_TRUE(obj.at("a") == nullptr);
52 dynamic newObject = dynamic::object;
55 EXPECT_EQ(newObject.size(), 1);
56 newObject["a"] = true;
57 EXPECT_EQ(newObject.size(), 2);
59 EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
60 EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
61 std::vector<std::pair<std::string, dynamic>> found;
62 found.emplace_back(newObject.keys().begin()->asString(),
63 *newObject.values().begin());
65 EXPECT_EQ(*boost::next(newObject.keys().begin()),
66 boost::next(newObject.items().begin())->first);
67 EXPECT_EQ(*boost::next(newObject.values().begin()),
68 boost::next(newObject.items().begin())->second);
69 found.emplace_back(boost::next(newObject.keys().begin())->asString(),
70 *boost::next(newObject.values().begin()));
72 std::sort(found.begin(), found.end());
74 EXPECT_EQ("a", found[0].first);
75 EXPECT_TRUE(found[0].second.asBool());
77 EXPECT_EQ("z", found[1].first);
78 EXPECT_EQ(12, found[1].second.asInt());
80 dynamic obj2 = dynamic::object;
81 EXPECT_TRUE(obj2.isObject());
84 EXPECT_TRUE(d3 == nullptr);
86 EXPECT_TRUE(d3.isObject());
87 d3["foo"] = dynamic::array(1, 2, 3);
88 EXPECT_EQ(d3.count("foo"), 1);
91 EXPECT_EQ(d3.at(123), 321);
94 EXPECT_EQ(d3.at("123"), 42);
95 EXPECT_EQ(d3.at(123), 321);
97 dynamic objInsert = folly::dynamic::object();
98 dynamic objA = folly::dynamic::object("1", "2");
99 dynamic objB = folly::dynamic::object("1", "2");
101 objInsert.insert("1", std::move(objA));
102 objInsert.insert("1", std::move(objB));
104 EXPECT_EQ(objInsert.find("1")->second.size(), 1);
106 // We don't allow objects as keys in objects.
107 EXPECT_ANY_THROW(newObject[d3] = 12);
110 dynamic origMergeObj1 = folly::dynamic::object();
111 dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object
114 dynamic mergeObj2 = folly::dynamic::object
118 // Merged object where we prefer the values in mergeObj2
119 dynamic combinedPreferObj2 = folly::dynamic::object
124 // Merged object where we prefer the values in mergeObj1
125 dynamic combinedPreferObj1 = folly::dynamic::object
130 auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
131 EXPECT_EQ(newMergeObj, combinedPreferObj2);
132 EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
134 mergeObj1.update(mergeObj2);
135 EXPECT_EQ(mergeObj1, combinedPreferObj2);
136 dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
137 EXPECT_THROW(mergeObj1.update(arr), std::exception);
139 mergeObj1 = origMergeObj1; // reset it
140 mergeObj1.update_missing(mergeObj2);
141 EXPECT_EQ(mergeObj1, combinedPreferObj1);
144 TEST(Dynamic, ObjectErase) {
145 dynamic obj = dynamic::object("key1", "val")
147 EXPECT_EQ(obj.count("key1"), 1);
148 EXPECT_EQ(obj.count("key2"), 1);
149 EXPECT_EQ(obj.erase("key1"), 1);
150 EXPECT_EQ(obj.count("key1"), 0);
151 EXPECT_EQ(obj.count("key2"), 1);
152 EXPECT_EQ(obj.erase("key1"), 0);
154 EXPECT_EQ(obj.count("key1"), 1);
155 EXPECT_EQ(obj.count("key2"), 1);
156 auto it = obj.find("key2");
158 EXPECT_EQ(obj.count("key1"), 1);
159 EXPECT_EQ(obj.count("key2"), 0);
163 EXPECT_EQ(obj.size(), 3);
164 auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
165 EXPECT_TRUE(ret == obj.items().end());
166 EXPECT_EQ(obj.size(), 1);
167 obj.erase(obj.items().begin());
168 EXPECT_TRUE(obj.empty());
171 TEST(Dynamic, ArrayErase) {
172 dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
174 EXPECT_THROW(arr.erase(1), std::exception);
175 EXPECT_EQ(arr.size(), 6);
176 EXPECT_EQ(arr[0], 1);
177 arr.erase(arr.begin());
178 EXPECT_EQ(arr.size(), 5);
180 arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
181 EXPECT_EQ(arr.size(), 2);
182 EXPECT_EQ(arr[0], 2);
183 EXPECT_EQ(arr[1], 6);
186 TEST(Dynamic, StringBasics) {
187 dynamic str = "hello world";
188 EXPECT_EQ(11, str.size());
189 EXPECT_FALSE(str.empty());
191 EXPECT_TRUE(str.empty());
194 TEST(Dynamic, ArrayBasics) {
195 dynamic array = dynamic::array(1, 2, 3);
196 EXPECT_EQ(array.size(), 3);
197 EXPECT_EQ(array.at(0), 1);
198 EXPECT_EQ(array.at(1), 2);
199 EXPECT_EQ(array.at(2), 3);
201 EXPECT_ANY_THROW(array.at(-1));
202 EXPECT_ANY_THROW(array.at(3));
204 array.push_back("foo");
205 EXPECT_EQ(array.size(), 4);
207 array.resize(12, "something");
208 EXPECT_EQ(array.size(), 12);
209 EXPECT_EQ(array[11], "something");
212 TEST(Dynamic, DeepCopy) {
213 dynamic val = dynamic::array("foo", "bar", dynamic::array("foo1", "bar1"));
214 EXPECT_EQ(val.at(2).at(0), "foo1");
215 EXPECT_EQ(val.at(2).at(1), "bar1");
217 EXPECT_EQ(val2.at(2).at(0), "foo1");
218 EXPECT_EQ(val2.at(2).at(1), "bar1");
219 EXPECT_EQ(val.at(2).at(0), "foo1");
220 EXPECT_EQ(val.at(2).at(1), "bar1");
221 val2.at(2).at(0) = "foo3";
222 val2.at(2).at(1) = "bar3";
223 EXPECT_EQ(val.at(2).at(0), "foo1");
224 EXPECT_EQ(val.at(2).at(1), "bar1");
225 EXPECT_EQ(val2.at(2).at(0), "foo3");
226 EXPECT_EQ(val2.at(2).at(1), "bar3");
229 dynamic::object("a", "b")
230 ("c", dynamic::array("d", "e", "f"));
231 EXPECT_EQ(obj.at("a"), "b");
233 obj2.at("a") = dynamic::array(1, 2, 3);
234 EXPECT_EQ(obj.at("a"), "b");
235 dynamic expected = dynamic::array(1, 2, 3);
236 EXPECT_EQ(obj2.at("a"), expected);
239 TEST(Dynamic, ArrayReassignment) {
241 dynamic d1 = dynamic::array(o);
242 EXPECT_EQ(dynamic::ARRAY, d1.type());
244 d1 = dynamic::array(o);
245 EXPECT_EQ(dynamic::ARRAY, d1.type());
248 TEST(Dynamic, Operator) {
251 dynamic d1 = dynamic::object;
252 dynamic d2 = dynamic::object;
254 LOG(ERROR) << "operator < returned "
255 << static_cast<int>(foo)
256 << " instead of throwing";
257 } catch (std::exception const&) {
264 dynamic sum = foo + bar;
265 EXPECT_EQ(sum, "asdbar");
269 dynamic math = some / nums;
273 TEST(Dynamic, Conversions) {
274 dynamic str = "12.0";
275 EXPECT_EQ(str.asDouble(), 12.0);
276 EXPECT_ANY_THROW(str.asInt());
277 EXPECT_ANY_THROW(str.asBool());
280 EXPECT_EQ(str.asInt(), 12);
281 EXPECT_EQ(str.asDouble(), 12.0);
283 EXPECT_EQ(str.asBool(), false);
284 EXPECT_EQ(str.asInt(), 0);
285 EXPECT_EQ(str.asDouble(), 0);
286 EXPECT_EQ(str.asString(), "0");
289 EXPECT_EQ("12", num.asString());
290 EXPECT_EQ(12.0, num.asDouble());
293 TEST(Dynamic, GetSetDefaultTest) {
294 dynamic d1 = dynamic::object("foo", "bar");
295 EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
296 EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
298 dynamic d2 = dynamic::object("foo", "bar");
299 EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
300 d2.setDefault("bar", dynamic::array).push_back(42);
301 EXPECT_EQ(d2["bar"][0], 42);
303 dynamic d3 = dynamic::object, empty = dynamic::object;
304 EXPECT_EQ(d3.getDefault("foo"), empty);
305 d3.setDefault("foo")["bar"] = "baz";
306 EXPECT_EQ(d3["foo"]["bar"], "baz");
308 // we do not allow getDefault/setDefault on arrays
309 dynamic d4 = dynamic::array;
310 EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
311 EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
314 TEST(Dynamic, ObjectForwarding) {
315 // Make sure dynamic::object can be constructed the same way as any
317 dynamic d = dynamic::object("asd", dynamic::array("foo", "bar"));
318 dynamic d2 = dynamic::object("key2", dynamic::array("value", "words"))
322 TEST(Dynamic, GetPtr) {
323 dynamic array = dynamic::array(1, 2, "three");
324 EXPECT_TRUE(array.get_ptr(0));
325 EXPECT_FALSE(array.get_ptr(-1));
326 EXPECT_FALSE(array.get_ptr(3));
327 EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
328 const dynamic& carray = array;
329 EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
331 dynamic object = dynamic::object("one", 1)("two", 2);
332 EXPECT_TRUE(object.get_ptr("one"));
333 EXPECT_FALSE(object.get_ptr("three"));
334 EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
335 *object.get_ptr("one") = 11;
336 EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
337 const dynamic& cobject = object;
338 EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
341 TEST(Dynamic, Assignment) {
342 const dynamic ds[] = { dynamic::array(1, 2, 3),
343 dynamic::object("a", true),
348 const dynamic dd[] = { dynamic::array(5, 6),
349 dynamic::object("t", "T")(1, 7),
354 for (const auto& source : ds) {
355 for (const auto& dest : dd) {
357 EXPECT_EQ(tmp, dest);
359 EXPECT_EQ(tmp, source);
364 std::string make_long_string() {
365 return std::string(100, 'a');
368 TEST(Dynamic, GetDefault) {
369 const auto s = make_long_string();
372 dynamic d1 = dynamic::object("key1", s);
373 dynamic d2 = dynamic::object("key2", s);
374 dynamic d3 = dynamic::object("key3", s);
375 dynamic d4 = dynamic::object("key4", s);
378 EXPECT_EQ(ds, d1.getDefault("key1", ayy));
379 EXPECT_EQ(ds, d1.getDefault("key1", ayy));
380 EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
383 EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
384 EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
385 EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
389 EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
390 EXPECT_NE(ds, d1["key1"]);
391 EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
392 EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
395 EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
396 EXPECT_NE(ds, d3["key3"]);
398 EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
399 EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
403 TEST(Dynamic, GetString) {
404 const dynamic c(make_long_string());
405 dynamic d(make_long_string());
406 dynamic m(make_long_string());
408 auto s = make_long_string();
410 EXPECT_EQ(s, c.getString());
411 EXPECT_EQ(s, c.getString());
413 d.getString() += " hello";
414 EXPECT_EQ(s + " hello", d.getString());
415 EXPECT_EQ(s + " hello", d.getString());
417 EXPECT_EQ(s, std::move(m).getString());
418 EXPECT_EQ(s, m.getString());
419 auto moved = std::move(m).getString();
421 EXPECT_NE(dynamic(s), m);
424 TEST(Dynamic, GetSmallThings) {
425 const dynamic cint(5);
426 const dynamic cdouble(5.0);
427 const dynamic cbool(true);
429 dynamic ddouble(5.0);
432 dynamic mdouble(5.0);
435 EXPECT_EQ(5, cint.getInt());
437 EXPECT_EQ(6, dint.getInt());
438 EXPECT_EQ(5, std::move(mint).getInt());
440 EXPECT_EQ(5.0, cdouble.getDouble());
441 ddouble.getDouble() = 6.0;
442 EXPECT_EQ(6.0, ddouble.getDouble());
443 EXPECT_EQ(5.0, std::move(mdouble).getDouble());
445 EXPECT_TRUE(cbool.getBool());
446 dbool.getBool() = false;
447 EXPECT_FALSE(dbool.getBool());
448 EXPECT_TRUE(std::move(mbool).getBool());
452 const dynamic cd = dynamic::object("key1", make_long_string());
453 dynamic dd = dynamic::object("key1", make_long_string());
454 dynamic md = dynamic::object("key1", make_long_string());
456 dynamic ds(make_long_string());
457 EXPECT_EQ(ds, cd.at("key1"));
458 EXPECT_EQ(ds, cd.at("key1"));
460 dd.at("key1").getString() += " hello";
461 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
462 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
464 EXPECT_EQ(ds, std::move(md).at("key1")); // move available, but not performed
465 EXPECT_EQ(ds, md.at("key1"));
466 dynamic moved = std::move(md).at("key1"); // move performed
467 EXPECT_EQ(ds, moved);
468 EXPECT_NE(ds, md.at("key1"));
471 TEST(Dynamic, Brackets) {
472 const dynamic cd = dynamic::object("key1", make_long_string());
473 dynamic dd = dynamic::object("key1", make_long_string());
474 dynamic md = dynamic::object("key1", make_long_string());
476 dynamic ds(make_long_string());
477 EXPECT_EQ(ds, cd["key1"]);
478 EXPECT_EQ(ds, cd["key1"]);
480 dd["key1"].getString() += " hello";
481 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
482 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
484 EXPECT_EQ(ds, std::move(md)["key1"]); // move available, but not performed
485 EXPECT_EQ(ds, md["key1"]);
486 dynamic moved = std::move(md)["key1"]; // move performed
487 EXPECT_EQ(ds, moved);
488 EXPECT_NE(ds, md["key1"]);
491 TEST(Dynamic, PrintNull) {
492 std::stringstream ss;
493 ss << folly::dynamic(nullptr);
494 EXPECT_EQ("null", ss.str());
497 TEST(Dynamic, WriteThroughArrayIterators) {
498 dynamic const cint(0);
499 dynamic d = dynamic::array(cint, cint, cint);
500 size_t size = d.size();
502 for (auto& val : d) {
503 EXPECT_EQ(val, cint);
505 EXPECT_EQ(d.size(), size);
507 dynamic ds(make_long_string());
508 for (auto& val : d) {
509 val = ds; // assign through reference
513 dynamic ds2(make_long_string());
515 for (auto& val : d) {
518 EXPECT_EQ(d.size(), size);
521 TEST(Dynamic, MoveOutOfArrayIterators) {
522 dynamic ds(make_long_string());
523 dynamic d = dynamic::array(ds, ds, ds);
524 size_t size = d.size();
526 for (auto& val : d) {
529 EXPECT_EQ(d.size(), size);
531 for (auto& val : d) {
532 dynamic waste = std::move(val); // force moving out
533 EXPECT_EQ(waste, ds);
536 for (auto& val : d) {
539 EXPECT_EQ(d.size(), size);
542 TEST(Dynamic, WriteThroughObjectIterators) {
543 dynamic const cint(0);
544 dynamic d = dynamic::object("key1", cint)("key2", cint);
545 size_t size = d.size();
547 for (auto& val : d.items()) {
548 EXPECT_EQ(val.second, cint);
550 EXPECT_EQ(d.size(), size);
552 dynamic ds(make_long_string());
553 for (auto& val : d.items()) {
554 val.second = ds; // assign through reference
558 dynamic ds2(make_long_string());
559 for (auto& val : d.items()) {
560 EXPECT_EQ(val.second, ds2);
562 EXPECT_EQ(d.size(), size);
565 TEST(Dynamic, MoveOutOfObjectIterators) {
566 dynamic ds(make_long_string());
567 dynamic d = dynamic::object("key1", ds)("key2", ds);
568 size_t size = d.size();
570 for (auto& val : d.items()) {
571 EXPECT_EQ(val.second, ds);
573 EXPECT_EQ(d.size(), size);
575 for (auto& val : d.items()) {
576 dynamic waste = std::move(val.second); // force moving out
577 EXPECT_EQ(waste, ds);
580 for (auto& val : d.items()) {
581 EXPECT_NE(val.second, ds);
583 EXPECT_EQ(d.size(), size);
586 TEST(Dynamic, ArrayIteratorInterop) {
587 dynamic d = dynamic::array(0, 1, 2);
588 dynamic const& cdref = d;
591 auto cit = cdref.begin();
594 EXPECT_EQ(cit, d.begin());
595 EXPECT_EQ(it, cdref.begin());
597 // Erase using non-const iterator
603 // Assign from non-const to const, preserve equality
604 decltype(cit) cit2 = it;
605 EXPECT_EQ(cit, cit2);
608 TEST(Dynamic, ObjectIteratorInterop) {
609 dynamic ds = make_long_string();
610 dynamic d = dynamic::object(0, ds)(1, ds)(2, ds);
611 dynamic const& cdref = d;
614 auto cit = cdref.find(0);
615 EXPECT_NE(it, cdref.items().end());
616 EXPECT_NE(cit, cdref.items().end());
620 // Erase using non-const iterator
621 auto it2 = d.erase(it);
624 // Assign from non-const to const, preserve equality
625 decltype(cit) cit2 = it2;
626 EXPECT_EQ(cit, cit2);