2 * Copyright 2015 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 <gflags/gflags.h>
21 #include <gtest/gtest.h>
25 // This test runs without any external dependencies, including json.
26 // This means that if there's a test failure, there's no way to print
27 // a useful runtime representation of the folly::dynamic. We will
28 // live with this in order to test dependencies. This method is
29 // normally provided by json.cpp.
30 void dynamic::print_as_pseudo_json(std::ostream& out) const {
31 out << "<folly::dynamic object of type " << type_ << ">";
34 TEST(Dynamic, ObjectBasics) {
35 dynamic obj = dynamic::object("a", false);
36 EXPECT_EQ(obj.at("a"), false);
37 EXPECT_EQ(obj.size(), 1);
38 obj.insert("a", true);
39 EXPECT_EQ(obj.size(), 1);
40 EXPECT_EQ(obj.at("a"), true);
41 obj.at("a") = nullptr;
42 EXPECT_EQ(obj.size(), 1);
43 EXPECT_TRUE(obj.at("a") == nullptr);
45 dynamic newObject = dynamic::object;
48 EXPECT_EQ(newObject.size(), 1);
49 newObject["a"] = true;
50 EXPECT_EQ(newObject.size(), 2);
52 EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
53 EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
54 std::vector<std::pair<folly::fbstring, dynamic>> found;
55 found.emplace_back(newObject.keys().begin()->asString(),
56 *newObject.values().begin());
58 EXPECT_EQ(*boost::next(newObject.keys().begin()),
59 boost::next(newObject.items().begin())->first);
60 EXPECT_EQ(*boost::next(newObject.values().begin()),
61 boost::next(newObject.items().begin())->second);
62 found.emplace_back(boost::next(newObject.keys().begin())->asString(),
63 *boost::next(newObject.values().begin()));
65 std::sort(found.begin(), found.end());
67 EXPECT_EQ("a", found[0].first);
68 EXPECT_TRUE(found[0].second.asBool());
70 EXPECT_EQ("z", found[1].first);
71 EXPECT_EQ(12, found[1].second.asInt());
73 dynamic obj2 = dynamic::object;
74 EXPECT_TRUE(obj2.isObject());
77 EXPECT_TRUE(d3 == nullptr);
79 EXPECT_TRUE(d3.isObject());
80 d3["foo"] = { 1, 2, 3 };
81 EXPECT_EQ(d3.count("foo"), 1);
84 EXPECT_EQ(d3.at(123), 321);
87 EXPECT_EQ(d3.at("123"), 42);
88 EXPECT_EQ(d3.at(123), 321);
90 dynamic objInsert = folly::dynamic::object();
91 dynamic objA = folly::dynamic::object("1", "2");
92 dynamic objB = folly::dynamic::object("1", "2");
94 objInsert.insert("1", std::move(objA));
95 objInsert.insert("1", std::move(objB));
97 EXPECT_EQ(objInsert.find("1")->second.size(), 1);
99 // We don't allow objects as keys in objects.
100 EXPECT_ANY_THROW(newObject[d3] = 12);
103 dynamic origMergeObj1 = folly::dynamic::object();
104 dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object
107 dynamic mergeObj2 = folly::dynamic::object
111 // Merged object where we prefer the values in mergeObj2
112 dynamic combinedPreferObj2 = folly::dynamic::object
117 // Merged object where we prefer the values in mergeObj1
118 dynamic combinedPreferObj1 = folly::dynamic::object
123 auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
124 EXPECT_EQ(newMergeObj, combinedPreferObj2);
125 EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
127 mergeObj1.update(mergeObj2);
128 EXPECT_EQ(mergeObj1, combinedPreferObj2);
129 dynamic arr = { 1, 2, 3, 4, 5, 6 };
130 EXPECT_THROW(mergeObj1.update(arr), std::exception);
132 mergeObj1 = origMergeObj1; // reset it
133 mergeObj1.update_missing(mergeObj2);
134 EXPECT_EQ(mergeObj1, combinedPreferObj1);
137 TEST(Dynamic, ObjectErase) {
138 dynamic obj = dynamic::object("key1", "val")
140 EXPECT_EQ(obj.count("key1"), 1);
141 EXPECT_EQ(obj.count("key2"), 1);
142 EXPECT_EQ(obj.erase("key1"), 1);
143 EXPECT_EQ(obj.count("key1"), 0);
144 EXPECT_EQ(obj.count("key2"), 1);
145 EXPECT_EQ(obj.erase("key1"), 0);
147 EXPECT_EQ(obj.count("key1"), 1);
148 EXPECT_EQ(obj.count("key2"), 1);
149 auto it = obj.find("key2");
151 EXPECT_EQ(obj.count("key1"), 1);
152 EXPECT_EQ(obj.count("key2"), 0);
156 EXPECT_EQ(obj.size(), 3);
157 auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
158 EXPECT_TRUE(ret == obj.items().end());
159 EXPECT_EQ(obj.size(), 1);
160 obj.erase(obj.items().begin());
161 EXPECT_TRUE(obj.empty());
164 TEST(Dynamic, ArrayErase) {
165 dynamic arr = { 1, 2, 3, 4, 5, 6 };
167 EXPECT_THROW(arr.erase(1), std::exception);
168 EXPECT_EQ(arr.size(), 6);
169 EXPECT_EQ(arr[0], 1);
170 arr.erase(arr.begin());
171 EXPECT_EQ(arr.size(), 5);
173 arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
174 EXPECT_EQ(arr.size(), 2);
175 EXPECT_EQ(arr[0], 2);
176 EXPECT_EQ(arr[1], 6);
179 TEST(Dynamic, StringBasics) {
180 dynamic str = "hello world";
181 EXPECT_EQ(11, str.size());
182 EXPECT_FALSE(str.empty());
184 EXPECT_TRUE(str.empty());
187 TEST(Dynamic, ArrayBasics) {
188 dynamic array = { 1, 2, 3 };
189 EXPECT_EQ(array.size(), 3);
190 EXPECT_EQ(array.at(0), 1);
191 EXPECT_EQ(array.at(1), 2);
192 EXPECT_EQ(array.at(2), 3);
194 EXPECT_ANY_THROW(array.at(-1));
195 EXPECT_ANY_THROW(array.at(3));
197 array.push_back("foo");
198 EXPECT_EQ(array.size(), 4);
200 array.resize(12, "something");
201 EXPECT_EQ(array.size(), 12);
202 EXPECT_EQ(array[11], "something");
205 TEST(Dynamic, DeepCopy) {
206 dynamic val = { "foo", "bar", { "foo1", "bar1" } };
207 EXPECT_EQ(val.at(2).at(0), "foo1");
208 EXPECT_EQ(val.at(2).at(1), "bar1");
210 EXPECT_EQ(val2.at(2).at(0), "foo1");
211 EXPECT_EQ(val2.at(2).at(1), "bar1");
212 EXPECT_EQ(val.at(2).at(0), "foo1");
213 EXPECT_EQ(val.at(2).at(1), "bar1");
214 val2.at(2).at(0) = "foo3";
215 val2.at(2).at(1) = "bar3";
216 EXPECT_EQ(val.at(2).at(0), "foo1");
217 EXPECT_EQ(val.at(2).at(1), "bar1");
218 EXPECT_EQ(val2.at(2).at(0), "foo3");
219 EXPECT_EQ(val2.at(2).at(1), "bar3");
221 dynamic obj = dynamic::object("a", "b")
222 ("c", {"d", "e", "f"})
224 EXPECT_EQ(obj.at("a"), "b");
226 obj2.at("a") = {1, 2, 3};
227 EXPECT_EQ(obj.at("a"), "b");
228 dynamic expected = {1, 2, 3};
229 EXPECT_EQ(obj2.at("a"), expected);
232 TEST(Dynamic, Operator) {
235 dynamic d1 = dynamic::object;
236 dynamic d2 = dynamic::object;
238 } catch (std::exception const& e) {
245 dynamic sum = foo + bar;
246 EXPECT_EQ(sum, "asdbar");
250 dynamic math = some / nums;
254 TEST(Dynamic, Conversions) {
255 dynamic str = "12.0";
256 EXPECT_EQ(str.asDouble(), 12.0);
257 EXPECT_ANY_THROW(str.asInt());
258 EXPECT_ANY_THROW(str.asBool());
261 EXPECT_EQ(str.asInt(), 12);
262 EXPECT_EQ(str.asDouble(), 12.0);
264 EXPECT_EQ(str.asBool(), false);
265 EXPECT_EQ(str.asInt(), 0);
266 EXPECT_EQ(str.asDouble(), 0);
267 EXPECT_EQ(str.asString(), "0");
270 EXPECT_EQ("12", num.asString());
271 EXPECT_EQ(12.0, num.asDouble());
274 TEST(Dynamic, GetSetDefaultTest) {
275 dynamic d1 = dynamic::object("foo", "bar");
276 EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
277 EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
279 dynamic d2 = dynamic::object("foo", "bar");
280 EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
281 d2.setDefault("bar", dynamic({})).push_back(42);
282 EXPECT_EQ(d2["bar"][0], 42);
284 dynamic d3 = dynamic::object, empty = dynamic::object;
285 EXPECT_EQ(d3.getDefault("foo"), empty);
286 d3.setDefault("foo")["bar"] = "baz";
287 EXPECT_EQ(d3["foo"]["bar"], "baz");
289 // we do not allow getDefault/setDefault on arrays
290 dynamic d4 = dynamic({});
291 EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
292 EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
295 TEST(Dynamic, ObjectForwarding) {
296 // Make sure dynamic::object can be constructed the same way as any
298 dynamic d = dynamic::object("asd", {"foo", "bar"});
299 dynamic d2 = dynamic::object("key2", {"value", "words"})
303 TEST(Dynamic, GetPtr) {
304 dynamic array = { 1, 2, "three" };
305 EXPECT_TRUE(array.get_ptr(0));
306 EXPECT_FALSE(array.get_ptr(-1));
307 EXPECT_FALSE(array.get_ptr(3));
308 EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
309 const dynamic& carray = array;
310 EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
312 dynamic object = dynamic::object("one", 1)("two", 2);
313 EXPECT_TRUE(object.get_ptr("one"));
314 EXPECT_FALSE(object.get_ptr("three"));
315 EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
316 *object.get_ptr("one") = 11;
317 EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
318 const dynamic& cobject = object;
319 EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
322 TEST(Dynamic, Assignment) {
323 const dynamic ds[] = { { 1, 2, 3 },
324 dynamic::object("a", true),
329 const dynamic dd[] = { { 5, 6 },
330 dynamic::object("t", "T")(1, 7),
335 for (const auto& source : ds) {
336 for (const auto& dest : dd) {
338 EXPECT_EQ(tmp, dest);
340 EXPECT_EQ(tmp, source);
345 folly::fbstring make_long_string() {
346 return folly::fbstring(100, 'a');
349 TEST(Dynamic, GetDefault) {
350 const auto s = make_long_string();
353 dynamic d1 = dynamic::object("key1", s);
354 dynamic d2 = dynamic::object("key2", s);
355 dynamic d3 = dynamic::object("key3", s);
356 dynamic d4 = dynamic::object("key4", s);
359 EXPECT_EQ(ds, d1.getDefault("key1", ayy));
360 EXPECT_EQ(ds, d1.getDefault("key1", ayy));
361 EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
364 EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
365 EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
366 EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
370 EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
371 EXPECT_NE(ds, d1["key1"]);
372 EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
373 EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
376 EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
377 EXPECT_NE(ds, d3["key3"]);
379 EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
380 EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
384 TEST(Dynamic, GetString) {
385 const dynamic c(make_long_string());
386 dynamic d(make_long_string());
387 dynamic m(make_long_string());
389 auto s = make_long_string();
391 EXPECT_EQ(s, c.getString());
392 EXPECT_EQ(s, c.getString());
394 d.getString() += " hello";
395 EXPECT_EQ(s + " hello", d.getString());
396 EXPECT_EQ(s + " hello", d.getString());
398 EXPECT_EQ(s, std::move(m).getString());
399 EXPECT_NE(dynamic(s), m);
402 TEST(Dynamic, GetSmallThings) {
403 const dynamic cint(5);
404 const dynamic cdouble(5.0);
405 const dynamic cbool(true);
407 dynamic ddouble(5.0);
410 dynamic mdouble(5.0);
413 EXPECT_EQ(5, cint.getInt());
415 EXPECT_EQ(6, dint.getInt());
416 EXPECT_EQ(5, std::move(mint).getInt());
418 EXPECT_EQ(5.0, cdouble.getDouble());
419 ddouble.getDouble() = 6.0;
420 EXPECT_EQ(6.0, ddouble.getDouble());
421 EXPECT_EQ(5.0, std::move(mdouble).getDouble());
423 EXPECT_EQ(true, cbool.getBool());
424 dbool.getBool() = false;
425 EXPECT_FALSE(dbool.getBool());
426 EXPECT_EQ(true, std::move(mbool).getBool());
430 const dynamic cd = dynamic::object("key1", make_long_string());
431 dynamic dd = dynamic::object("key1", make_long_string());
432 dynamic md = dynamic::object("key1", make_long_string());
434 dynamic ds(make_long_string());
435 EXPECT_EQ(ds, cd.at("key1"));
436 EXPECT_EQ(ds, cd.at("key1"));
438 dd.at("key1").getString() += " hello";
439 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
440 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
442 EXPECT_EQ(ds, std::move(md).at("key1"));
443 EXPECT_NE(ds, md.at("key1"));
446 TEST(Dynamic, Brackets) {
447 const dynamic cd = dynamic::object("key1", make_long_string());
448 dynamic dd = dynamic::object("key1", make_long_string());
449 dynamic md = dynamic::object("key1", make_long_string());
451 dynamic ds(make_long_string());
452 EXPECT_EQ(ds, cd["key1"]);
453 EXPECT_EQ(ds, cd["key1"]);
455 dd["key1"].getString() += " hello";
456 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
457 EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
459 EXPECT_EQ(ds, std::move(md)["key1"]);
460 EXPECT_NE(ds, md["key1"]);
463 int main(int argc, char** argv) {
464 testing::InitGoogleTest(&argc, argv);
465 gflags::ParseCommandLineFlags(&argc, &argv, true);
466 return RUN_ALL_TESTS();