Add ability to merge dynamic objects
[folly.git] / folly / test / DynamicTest.cpp
1 /*
2  * Copyright 2015 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 <boost/next_prior.hpp>
20 #include <gflags/gflags.h>
21 #include <gtest/gtest.h>
22
23 using folly::dynamic;
24
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_ << ">";
32 }
33
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);
44
45   dynamic newObject = dynamic::object;
46
47   newObject["z"] = 12;
48   EXPECT_EQ(newObject.size(), 1);
49   newObject["a"] = true;
50   EXPECT_EQ(newObject.size(), 2);
51
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());
57
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()));
64
65   std::sort(found.begin(), found.end());
66
67   EXPECT_EQ("a", found[0].first);
68   EXPECT_TRUE(found[0].second.asBool());
69
70   EXPECT_EQ("z", found[1].first);
71   EXPECT_EQ(12, found[1].second.asInt());
72
73   dynamic obj2 = dynamic::object;
74   EXPECT_TRUE(obj2.isObject());
75
76   dynamic d3 = nullptr;
77   EXPECT_TRUE(d3 == nullptr);
78   d3 = dynamic::object;
79   EXPECT_TRUE(d3.isObject());
80   d3["foo"] = { 1, 2, 3 };
81   EXPECT_EQ(d3.count("foo"), 1);
82
83   d3[123] = 321;
84   EXPECT_EQ(d3.at(123), 321);
85
86   d3["123"] = 42;
87   EXPECT_EQ(d3.at("123"), 42);
88   EXPECT_EQ(d3.at(123), 321);
89
90   dynamic objInsert = folly::dynamic::object();
91   dynamic objA = folly::dynamic::object("1", "2");
92   dynamic objB = folly::dynamic::object("1", "2");
93
94   objInsert.insert("1", std::move(objA));
95   objInsert.insert("1", std::move(objB));
96
97   EXPECT_EQ(objInsert.find("1")->second.size(), 1);
98
99   // We don't allow objects as keys in objects.
100   EXPECT_ANY_THROW(newObject[d3] = 12);
101
102   // Merge two objects
103   dynamic origMergeObj1 = folly::dynamic::object();
104   dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object
105     ("key1", "value1")
106     ("key2", "value2");
107   dynamic mergeObj2 = folly::dynamic::object
108     ("key2", "value3")
109     ("key3", "value4");
110   dynamic combinedObj = folly::dynamic::object
111     ("key1", "value1")
112     ("key2", "value3")
113     ("key3", "value4");
114   auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
115   EXPECT_EQ(newMergeObj, combinedObj);
116   EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
117
118   mergeObj1.update(mergeObj2);
119   EXPECT_EQ(mergeObj1, combinedObj);
120   dynamic arr = { 1, 2, 3, 4, 5, 6 };
121   EXPECT_THROW(mergeObj1.update(arr), std::exception);
122 }
123
124 TEST(Dynamic, ObjectErase) {
125   dynamic obj = dynamic::object("key1", "val")
126                                ("key2", "val2");
127   EXPECT_EQ(obj.count("key1"), 1);
128   EXPECT_EQ(obj.count("key2"), 1);
129   EXPECT_EQ(obj.erase("key1"), 1);
130   EXPECT_EQ(obj.count("key1"), 0);
131   EXPECT_EQ(obj.count("key2"), 1);
132   EXPECT_EQ(obj.erase("key1"), 0);
133   obj["key1"] = 12;
134   EXPECT_EQ(obj.count("key1"), 1);
135   EXPECT_EQ(obj.count("key2"), 1);
136   auto it = obj.find("key2");
137   obj.erase(it);
138   EXPECT_EQ(obj.count("key1"), 1);
139   EXPECT_EQ(obj.count("key2"), 0);
140
141   obj["asd"] = 42.0;
142   obj["foo"] = 42.0;
143   EXPECT_EQ(obj.size(), 3);
144   auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
145   EXPECT_TRUE(ret == obj.items().end());
146   EXPECT_EQ(obj.size(), 1);
147   obj.erase(obj.items().begin());
148   EXPECT_TRUE(obj.empty());
149 }
150
151 TEST(Dynamic, ArrayErase) {
152   dynamic arr = { 1, 2, 3, 4, 5, 6 };
153
154   EXPECT_THROW(arr.erase(1), std::exception);
155   EXPECT_EQ(arr.size(), 6);
156   EXPECT_EQ(arr[0], 1);
157   arr.erase(arr.begin());
158   EXPECT_EQ(arr.size(), 5);
159
160   arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
161   EXPECT_EQ(arr.size(), 2);
162   EXPECT_EQ(arr[0], 2);
163   EXPECT_EQ(arr[1], 6);
164 }
165
166 TEST(Dynamic, StringBasics) {
167   dynamic str = "hello world";
168   EXPECT_EQ(11, str.size());
169   EXPECT_FALSE(str.empty());
170   str = "";
171   EXPECT_TRUE(str.empty());
172 }
173
174 TEST(Dynamic, ArrayBasics) {
175   dynamic array = { 1, 2, 3 };
176   EXPECT_EQ(array.size(), 3);
177   EXPECT_EQ(array.at(0), 1);
178   EXPECT_EQ(array.at(1), 2);
179   EXPECT_EQ(array.at(2), 3);
180
181   EXPECT_ANY_THROW(array.at(-1));
182   EXPECT_ANY_THROW(array.at(3));
183
184   array.push_back("foo");
185   EXPECT_EQ(array.size(), 4);
186
187   array.resize(12, "something");
188   EXPECT_EQ(array.size(), 12);
189   EXPECT_EQ(array[11], "something");
190 }
191
192 TEST(Dynamic, DeepCopy) {
193   dynamic val = { "foo", "bar", { "foo1", "bar1" } };
194   EXPECT_EQ(val.at(2).at(0), "foo1");
195   EXPECT_EQ(val.at(2).at(1), "bar1");
196   dynamic val2 = val;
197   EXPECT_EQ(val2.at(2).at(0), "foo1");
198   EXPECT_EQ(val2.at(2).at(1), "bar1");
199   EXPECT_EQ(val.at(2).at(0), "foo1");
200   EXPECT_EQ(val.at(2).at(1), "bar1");
201   val2.at(2).at(0) = "foo3";
202   val2.at(2).at(1) = "bar3";
203   EXPECT_EQ(val.at(2).at(0), "foo1");
204   EXPECT_EQ(val.at(2).at(1), "bar1");
205   EXPECT_EQ(val2.at(2).at(0), "foo3");
206   EXPECT_EQ(val2.at(2).at(1), "bar3");
207
208   dynamic obj = dynamic::object("a", "b")
209                                ("c", {"d", "e", "f"})
210                                ;
211   EXPECT_EQ(obj.at("a"), "b");
212   dynamic obj2 = obj;
213   obj2.at("a") = {1, 2, 3};
214   EXPECT_EQ(obj.at("a"), "b");
215   dynamic expected = {1, 2, 3};
216   EXPECT_EQ(obj2.at("a"), expected);
217 }
218
219 TEST(Dynamic, Operator) {
220   bool caught = false;
221   try {
222     dynamic d1 = dynamic::object;
223     dynamic d2 = dynamic::object;
224     auto foo = d1 < d2;
225   } catch (std::exception const& e) {
226     caught = true;
227   }
228   EXPECT_TRUE(caught);
229
230   dynamic foo = "asd";
231   dynamic bar = "bar";
232   dynamic sum = foo + bar;
233   EXPECT_EQ(sum, "asdbar");
234
235   dynamic some = 12;
236   dynamic nums = 4;
237   dynamic math = some / nums;
238   EXPECT_EQ(math, 3);
239 }
240
241 TEST(Dynamic, Conversions) {
242   dynamic str = "12.0";
243   EXPECT_EQ(str.asDouble(), 12.0);
244   EXPECT_ANY_THROW(str.asInt());
245   EXPECT_ANY_THROW(str.asBool());
246
247   str = "12";
248   EXPECT_EQ(str.asInt(), 12);
249   EXPECT_EQ(str.asDouble(), 12.0);
250   str = "0";
251   EXPECT_EQ(str.asBool(), false);
252   EXPECT_EQ(str.asInt(), 0);
253   EXPECT_EQ(str.asDouble(), 0);
254   EXPECT_EQ(str.asString(), "0");
255
256   dynamic num = 12;
257   EXPECT_EQ("12", num.asString());
258   EXPECT_EQ(12.0, num.asDouble());
259 }
260
261 TEST(Dynamic, GetSetDefaultTest) {
262   dynamic d1 = dynamic::object("foo", "bar");
263   EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
264   EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
265
266   dynamic d2 = dynamic::object("foo", "bar");
267   EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
268   d2.setDefault("bar", dynamic({})).push_back(42);
269   EXPECT_EQ(d2["bar"][0], 42);
270
271   dynamic d3 = dynamic::object, empty = dynamic::object;
272   EXPECT_EQ(d3.getDefault("foo"), empty);
273   d3.setDefault("foo")["bar"] = "baz";
274   EXPECT_EQ(d3["foo"]["bar"], "baz");
275
276   // we do not allow getDefault/setDefault on arrays
277   dynamic d4 = dynamic({});
278   EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
279   EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
280 }
281
282 TEST(Dynamic, ObjectForwarding) {
283   // Make sure dynamic::object can be constructed the same way as any
284   // dynamic.
285   dynamic d = dynamic::object("asd", {"foo", "bar"});
286   dynamic d2 = dynamic::object("key2", {"value", "words"})
287                               ("key", "value1");
288 }
289
290 TEST(Dynamic, GetPtr) {
291   dynamic array = { 1, 2, "three" };
292   EXPECT_TRUE(array.get_ptr(0));
293   EXPECT_FALSE(array.get_ptr(-1));
294   EXPECT_FALSE(array.get_ptr(3));
295   EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
296   const dynamic& carray = array;
297   EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
298
299   dynamic object = dynamic::object("one", 1)("two", 2);
300   EXPECT_TRUE(object.get_ptr("one"));
301   EXPECT_FALSE(object.get_ptr("three"));
302   EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
303   *object.get_ptr("one") = 11;
304   EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
305   const dynamic& cobject = object;
306   EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
307 }
308
309 TEST(Dynamic, Assignment) {
310   const dynamic ds[] = { { 1, 2, 3 },
311                          dynamic::object("a", true),
312                          24,
313                          26.5,
314                          true,
315                          "hello", };
316   const dynamic dd[] = { { 5, 6 },
317                          dynamic::object("t", "T")(1, 7),
318                          9000,
319                          3.14159,
320                          false,
321                          "world", };
322   for (const auto& source : ds) {
323     for (const auto& dest : dd) {
324       dynamic tmp(dest);
325       EXPECT_EQ(tmp, dest);
326       tmp = source;
327       EXPECT_EQ(tmp, source);
328     }
329   }
330 }
331
332 folly::fbstring make_long_string() {
333   return folly::fbstring(100, 'a');
334 }
335
336 TEST(Dynamic, GetDefault) {
337   const auto s = make_long_string();
338   dynamic ds(s);
339   dynamic tmp(s);
340   dynamic d1 = dynamic::object("key1", s);
341   dynamic d2 = dynamic::object("key2", s);
342   dynamic d3 = dynamic::object("key3", s);
343   dynamic d4 = dynamic::object("key4", s);
344   // lvalue - lvalue
345   dynamic ayy("ayy");
346   EXPECT_EQ(ds, d1.getDefault("key1", ayy));
347   EXPECT_EQ(ds, d1.getDefault("key1", ayy));
348   EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
349   EXPECT_EQ(ds, tmp);
350   // lvalue - rvalue
351   EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
352   EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
353   EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
354   EXPECT_NE(ds, tmp);
355   // rvalue - lvalue
356   tmp = s;
357   EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
358   EXPECT_NE(ds, d1["key1"]);
359   EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
360   EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
361   EXPECT_EQ(ds, tmp);
362   // rvalue - rvalue
363   EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
364   EXPECT_NE(ds, d3["key3"]);
365   EXPECT_EQ(ds, tmp);
366   EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
367   EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
368   EXPECT_NE(ds, tmp);
369 }
370
371 TEST(Dynamic, GetString) {
372   const dynamic c(make_long_string());
373   dynamic d(make_long_string());
374   dynamic m(make_long_string());
375
376   auto s = make_long_string();
377
378   EXPECT_EQ(s, c.getString());
379   EXPECT_EQ(s, c.getString());
380
381   d.getString() += " hello";
382   EXPECT_EQ(s + " hello", d.getString());
383   EXPECT_EQ(s + " hello", d.getString());
384
385   EXPECT_EQ(s, std::move(m).getString());
386   EXPECT_NE(dynamic(s), m);
387 }
388
389 TEST(Dynamic, GetSmallThings) {
390   const dynamic cint(5);
391   const dynamic cdouble(5.0);
392   const dynamic cbool(true);
393   dynamic dint(5);
394   dynamic ddouble(5.0);
395   dynamic dbool(true);
396   dynamic mint(5);
397   dynamic mdouble(5.0);
398   dynamic mbool(true);
399
400   EXPECT_EQ(5, cint.getInt());
401   dint.getInt() = 6;
402   EXPECT_EQ(6, dint.getInt());
403   EXPECT_EQ(5, std::move(mint).getInt());
404
405   EXPECT_EQ(5.0, cdouble.getDouble());
406   ddouble.getDouble() = 6.0;
407   EXPECT_EQ(6.0, ddouble.getDouble());
408   EXPECT_EQ(5.0, std::move(mdouble).getDouble());
409
410   EXPECT_EQ(true, cbool.getBool());
411   dbool.getBool() = false;
412   EXPECT_FALSE(dbool.getBool());
413   EXPECT_EQ(true, std::move(mbool).getBool());
414 }
415
416 TEST(Dynamic, At) {
417   const dynamic cd = dynamic::object("key1", make_long_string());
418   dynamic dd = dynamic::object("key1", make_long_string());
419   dynamic md = dynamic::object("key1", make_long_string());
420
421   dynamic ds(make_long_string());
422   EXPECT_EQ(ds, cd.at("key1"));
423   EXPECT_EQ(ds, cd.at("key1"));
424
425   dd.at("key1").getString() += " hello";
426   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
427   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
428
429   EXPECT_EQ(ds, std::move(md).at("key1"));
430   EXPECT_NE(ds, md.at("key1"));
431 }
432
433 TEST(Dynamic, Brackets) {
434   const dynamic cd = dynamic::object("key1", make_long_string());
435   dynamic dd = dynamic::object("key1", make_long_string());
436   dynamic md = dynamic::object("key1", make_long_string());
437
438   dynamic ds(make_long_string());
439   EXPECT_EQ(ds, cd["key1"]);
440   EXPECT_EQ(ds, cd["key1"]);
441
442   dd["key1"].getString() += " hello";
443   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
444   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
445
446   EXPECT_EQ(ds, std::move(md)["key1"]);
447   EXPECT_NE(ds, md["key1"]);
448 }
449
450 int main(int argc, char** argv) {
451   testing::InitGoogleTest(&argc, argv);
452   gflags::ParseCommandLineFlags(&argc, &argv, true);
453   return RUN_ALL_TESTS();
454 }