2 * Copyright 2016 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 <boost/next_prior.hpp>
20 #include <gtest/gtest.h>
24 // This test runs without any external dependencies, including json.
25 // This means that if there's a test failure, there's no way to print
26 // a useful runtime representation of the folly::dynamic. We will
27 // live with this in order to test dependencies. This method is
28 // normally provided by json.cpp.
29 void dynamic::print_as_pseudo_json(std::ostream& out) const {
30 out << "<folly::dynamic object of type " << type_ << ">";
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 EXPECT_EQ(obj.size(), 1);
39 EXPECT_EQ(obj.at("a"), true);
40 obj.at("a") = nullptr;
41 EXPECT_EQ(obj.size(), 1);
42 EXPECT_TRUE(obj.at("a") == nullptr);
44 dynamic newObject = dynamic::object;
47 EXPECT_EQ(newObject.size(), 1);
48 newObject["a"] = true;
49 EXPECT_EQ(newObject.size(), 2);
51 EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
52 EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
53 std::vector<std::pair<std::string, dynamic>> found;
54 found.emplace_back(newObject.keys().begin()->asString(),
55 *newObject.values().begin());
57 EXPECT_EQ(*boost::next(newObject.keys().begin()),
58 boost::next(newObject.items().begin())->first);
59 EXPECT_EQ(*boost::next(newObject.values().begin()),
60 boost::next(newObject.items().begin())->second);
61 found.emplace_back(boost::next(newObject.keys().begin())->asString(),
62 *boost::next(newObject.values().begin()));
64 std::sort(found.begin(), found.end());
66 EXPECT_EQ("a", found[0].first);
67 EXPECT_TRUE(found[0].second.asBool());
69 EXPECT_EQ("z", found[1].first);
70 EXPECT_EQ(12, found[1].second.asInt());
72 dynamic obj2 = dynamic::object;
73 EXPECT_TRUE(obj2.isObject());
76 EXPECT_TRUE(d3 == nullptr);
78 EXPECT_TRUE(d3.isObject());
79 d3["foo"] = dynamic::array(1, 2, 3);
80 EXPECT_EQ(d3.count("foo"), 1);
83 EXPECT_EQ(d3.at(123), 321);
86 EXPECT_EQ(d3.at("123"), 42);
87 EXPECT_EQ(d3.at(123), 321);
89 dynamic objInsert = folly::dynamic::object();
90 dynamic objA = folly::dynamic::object("1", "2");
91 dynamic objB = folly::dynamic::object("1", "2");
93 objInsert.insert("1", std::move(objA));
94 objInsert.insert("1", std::move(objB));
96 EXPECT_EQ(objInsert.find("1")->second.size(), 1);
98 // We don't allow objects as keys in objects.
99 EXPECT_ANY_THROW(newObject[d3] = 12);
102 dynamic origMergeObj1 = folly::dynamic::object();
103 dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object
106 dynamic mergeObj2 = folly::dynamic::object
110 // Merged object where we prefer the values in mergeObj2
111 dynamic combinedPreferObj2 = folly::dynamic::object
116 // Merged object where we prefer the values in mergeObj1
117 dynamic combinedPreferObj1 = folly::dynamic::object
122 auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
123 EXPECT_EQ(newMergeObj, combinedPreferObj2);
124 EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
126 mergeObj1.update(mergeObj2);
127 EXPECT_EQ(mergeObj1, combinedPreferObj2);
128 dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
129 EXPECT_THROW(mergeObj1.update(arr), std::exception);
131 mergeObj1 = origMergeObj1; // reset it
132 mergeObj1.update_missing(mergeObj2);
133 EXPECT_EQ(mergeObj1, combinedPreferObj1);
136 TEST(Dynamic, ObjectErase) {
137 dynamic obj = dynamic::object("key1", "val")
139 EXPECT_EQ(obj.count("key1"), 1);
140 EXPECT_EQ(obj.count("key2"), 1);
141 EXPECT_EQ(obj.erase("key1"), 1);
142 EXPECT_EQ(obj.count("key1"), 0);
143 EXPECT_EQ(obj.count("key2"), 1);
144 EXPECT_EQ(obj.erase("key1"), 0);
146 EXPECT_EQ(obj.count("key1"), 1);
147 EXPECT_EQ(obj.count("key2"), 1);
148 auto it = obj.find("key2");
150 EXPECT_EQ(obj.count("key1"), 1);
151 EXPECT_EQ(obj.count("key2"), 0);
155 EXPECT_EQ(obj.size(), 3);
156 auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
157 EXPECT_TRUE(ret == obj.items().end());
158 EXPECT_EQ(obj.size(), 1);
159 obj.erase(obj.items().begin());
160 EXPECT_TRUE(obj.empty());
163 TEST(Dynamic, ArrayErase) {
164 dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
166 EXPECT_THROW(arr.erase(1), std::exception);
167 EXPECT_EQ(arr.size(), 6);
168 EXPECT_EQ(arr[0], 1);
169 arr.erase(arr.begin());
170 EXPECT_EQ(arr.size(), 5);
172 arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
173 EXPECT_EQ(arr.size(), 2);
174 EXPECT_EQ(arr[0], 2);
175 EXPECT_EQ(arr[1], 6);
178 TEST(Dynamic, StringBasics) {
179 dynamic str = "hello world";
180 EXPECT_EQ(11, str.size());
181 EXPECT_FALSE(str.empty());
183 EXPECT_TRUE(str.empty());
186 TEST(Dynamic, ArrayBasics) {
187 dynamic array = dynamic::array(1, 2, 3);
188 EXPECT_EQ(array.size(), 3);
189 EXPECT_EQ(array.at(0), 1);
190 EXPECT_EQ(array.at(1), 2);
191 EXPECT_EQ(array.at(2), 3);
193 EXPECT_ANY_THROW(array.at(-1));
194 EXPECT_ANY_THROW(array.at(3));
196 array.push_back("foo");
197 EXPECT_EQ(array.size(), 4);
199 array.resize(12, "something");
200 EXPECT_EQ(array.size(), 12);
201 EXPECT_EQ(array[11], "something");
204 TEST(Dynamic, DeepCopy) {
205 dynamic val = dynamic::array("foo", "bar", dynamic::array("foo1", "bar1"));
206 EXPECT_EQ(val.at(2).at(0), "foo1");
207 EXPECT_EQ(val.at(2).at(1), "bar1");
209 EXPECT_EQ(val2.at(2).at(0), "foo1");
210 EXPECT_EQ(val2.at(2).at(1), "bar1");
211 EXPECT_EQ(val.at(2).at(0), "foo1");
212 EXPECT_EQ(val.at(2).at(1), "bar1");
213 val2.at(2).at(0) = "foo3";
214 val2.at(2).at(1) = "bar3";
215 EXPECT_EQ(val.at(2).at(0), "foo1");
216 EXPECT_EQ(val.at(2).at(1), "bar1");
217 EXPECT_EQ(val2.at(2).at(0), "foo3");
218 EXPECT_EQ(val2.at(2).at(1), "bar3");
221 dynamic::object("a", "b")
222 ("c", dynamic::array("d", "e", "f"));
223 EXPECT_EQ(obj.at("a"), "b");
225 obj2.at("a") = dynamic::array(1, 2, 3);
226 EXPECT_EQ(obj.at("a"), "b");
227 dynamic expected = dynamic::array(1, 2, 3);
228 EXPECT_EQ(obj2.at("a"), expected);
231 TEST(Dynamic, ArrayReassignment) {
233 dynamic d1 = dynamic::array(o);
234 EXPECT_EQ(dynamic::ARRAY, d1.type());
236 d1 = dynamic::array(o);
237 EXPECT_EQ(dynamic::ARRAY, d1.type());
240 TEST(Dynamic, Operator) {
243 dynamic d1 = dynamic::object;
244 dynamic d2 = dynamic::object;
246 LOG(ERROR) << "operator < returned "
247 << static_cast<int>(foo)
248 << " instead of throwing";
249 } catch (std::exception const& e) {
256 dynamic sum = foo + bar;
257 EXPECT_EQ(sum, "asdbar");
261 dynamic math = some / nums;
265 TEST(Dynamic, Conversions) {
266 dynamic str = "12.0";
267 EXPECT_EQ(str.asDouble(), 12.0);
268 EXPECT_ANY_THROW(str.asInt());
269 EXPECT_ANY_THROW(str.asBool());
272 EXPECT_EQ(str.asInt(), 12);
273 EXPECT_EQ(str.asDouble(), 12.0);
275 EXPECT_EQ(str.asBool(), false);
276 EXPECT_EQ(str.asInt(), 0);
277 EXPECT_EQ(str.asDouble(), 0);
278 EXPECT_EQ(str.asString(), "0");
281 EXPECT_EQ("12", num.asString());
282 EXPECT_EQ(12.0, num.asDouble());
285 TEST(Dynamic, GetSetDefaultTest) {
286 dynamic d1 = dynamic::object("foo", "bar");
287 EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
288 EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
290 dynamic d2 = dynamic::object("foo", "bar");
291 EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
292 d2.setDefault("bar", dynamic::array).push_back(42);
293 EXPECT_EQ(d2["bar"][0], 42);
295 dynamic d3 = dynamic::object, empty = dynamic::object;
296 EXPECT_EQ(d3.getDefault("foo"), empty);
297 d3.setDefault("foo")["bar"] = "baz";
298 EXPECT_EQ(d3["foo"]["bar"], "baz");
300 // we do not allow getDefault/setDefault on arrays
301 dynamic d4 = dynamic::array;
302 EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
303 EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
306 TEST(Dynamic, ObjectForwarding) {
307 // Make sure dynamic::object can be constructed the same way as any
309 dynamic d = dynamic::object("asd", dynamic::array("foo", "bar"));
310 dynamic d2 = dynamic::object("key2", dynamic::array("value", "words"))
314 TEST(Dynamic, GetPtr) {
315 dynamic array = dynamic::array(1, 2, "three");
316 EXPECT_TRUE(array.get_ptr(0));
317 EXPECT_FALSE(array.get_ptr(-1));
318 EXPECT_FALSE(array.get_ptr(3));
319 EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
320 const dynamic& carray = array;
321 EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
323 dynamic object = dynamic::object("one", 1)("two", 2);
324 EXPECT_TRUE(object.get_ptr("one"));
325 EXPECT_FALSE(object.get_ptr("three"));
326 EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
327 *object.get_ptr("one") = 11;
328 EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
329 const dynamic& cobject = object;
330 EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
333 TEST(Dynamic, Assignment) {
334 const dynamic ds[] = { dynamic::array(1, 2, 3),
335 dynamic::object("a", true),
340 const dynamic dd[] = { dynamic::array(5, 6),
341 dynamic::object("t", "T")(1, 7),
346 for (const auto& source : ds) {
347 for (const auto& dest : dd) {
349 EXPECT_EQ(tmp, dest);
351 EXPECT_EQ(tmp, source);
356 std::string make_long_string() {
357 return std::string(100, 'a');
360 TEST(Dynamic, GetDefault) {
361 const auto s = make_long_string();
364 dynamic d1 = dynamic::object("key1", s);
365 dynamic d2 = dynamic::object("key2", s);
366 dynamic d3 = dynamic::object("key3", s);
367 dynamic d4 = dynamic::object("key4", s);
370 EXPECT_EQ(ds, d1.getDefault("key1", ayy));
371 EXPECT_EQ(ds, d1.getDefault("key1", ayy));
372 EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
375 EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
376 EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
377 EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
381 EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
382 EXPECT_NE(ds, d1["key1"]);
383 EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
384 EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
387 EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
388 EXPECT_NE(ds, d3["key3"]);
390 EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
391 EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
395 TEST(Dynamic, GetString) {
396 const dynamic c(make_long_string());
397 dynamic d(make_long_string());
398 dynamic m(make_long_string());
400 auto s = make_long_string();
402 EXPECT_EQ(s, c.getString());
403 EXPECT_EQ(s, c.getString());
405 d.getString() += " hello";
406 EXPECT_EQ(s + " hello", d.getString());
407 EXPECT_EQ(s + " hello", d.getString());
409 EXPECT_EQ(s, std::move(m).getString());
410 EXPECT_NE(dynamic(s), m);
413 TEST(Dynamic, GetSmallThings) {
414 const dynamic cint(5);
415 const dynamic cdouble(5.0);
416 const dynamic cbool(true);
418 dynamic ddouble(5.0);
421 dynamic mdouble(5.0);
424 EXPECT_EQ(5, cint.getInt());
426 EXPECT_EQ(6, dint.getInt());
427 EXPECT_EQ(5, std::move(mint).getInt());
429 EXPECT_EQ(5.0, cdouble.getDouble());
430 ddouble.getDouble() = 6.0;
431 EXPECT_EQ(6.0, ddouble.getDouble());
432 EXPECT_EQ(5.0, std::move(mdouble).getDouble());
434 EXPECT_EQ(true, cbool.getBool());
435 dbool.getBool() = false;
436 EXPECT_FALSE(dbool.getBool());
437 EXPECT_EQ(true, std::move(mbool).getBool());
441 const dynamic cd = dynamic::object("key1", make_long_string());
442 dynamic dd = dynamic::object("key1", make_long_string());
443 dynamic md = dynamic::object("key1", make_long_string());
445 dynamic ds(make_long_string());
446 EXPECT_EQ(ds, cd.at("key1"));
447 EXPECT_EQ(ds, cd.at("key1"));
449 dd.at("key1").getString() += " hello";
450 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
451 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
453 EXPECT_EQ(ds, std::move(md).at("key1"));
454 EXPECT_NE(ds, md.at("key1"));
457 TEST(Dynamic, Brackets) {
458 const dynamic cd = dynamic::object("key1", make_long_string());
459 dynamic dd = dynamic::object("key1", make_long_string());
460 dynamic md = dynamic::object("key1", make_long_string());
462 dynamic ds(make_long_string());
463 EXPECT_EQ(ds, cd["key1"]);
464 EXPECT_EQ(ds, cd["key1"]);
466 dd["key1"].getString() += " hello";
467 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
468 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
470 EXPECT_EQ(ds, std::move(md)["key1"]);
471 EXPECT_NE(ds, md["key1"]);
474 TEST(Dynamic, PrintNull) {
475 std::stringstream ss;
476 ss << folly::dynamic(nullptr);
477 EXPECT_EQ("null", ss.str());