0fa5bdc2e16a07835907e8b7947a92b4941adf08
[folly.git] / folly / test / DynamicTest.cpp
1 /*
2  * Copyright 2016 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 <gtest/gtest.h>
21
22 using folly::dynamic;
23
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_ << ">";
31 }
32
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);
43
44   dynamic newObject = dynamic::object;
45
46   newObject["z"] = 12;
47   EXPECT_EQ(newObject.size(), 1);
48   newObject["a"] = true;
49   EXPECT_EQ(newObject.size(), 2);
50
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());
56
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()));
63
64   std::sort(found.begin(), found.end());
65
66   EXPECT_EQ("a", found[0].first);
67   EXPECT_TRUE(found[0].second.asBool());
68
69   EXPECT_EQ("z", found[1].first);
70   EXPECT_EQ(12, found[1].second.asInt());
71
72   dynamic obj2 = dynamic::object;
73   EXPECT_TRUE(obj2.isObject());
74
75   dynamic d3 = nullptr;
76   EXPECT_TRUE(d3 == nullptr);
77   d3 = dynamic::object;
78   EXPECT_TRUE(d3.isObject());
79   d3["foo"] = dynamic::array(1, 2, 3);
80   EXPECT_EQ(d3.count("foo"), 1);
81
82   d3[123] = 321;
83   EXPECT_EQ(d3.at(123), 321);
84
85   d3["123"] = 42;
86   EXPECT_EQ(d3.at("123"), 42);
87   EXPECT_EQ(d3.at(123), 321);
88
89   dynamic objInsert = folly::dynamic::object();
90   dynamic objA = folly::dynamic::object("1", "2");
91   dynamic objB = folly::dynamic::object("1", "2");
92
93   objInsert.insert("1", std::move(objA));
94   objInsert.insert("1", std::move(objB));
95
96   EXPECT_EQ(objInsert.find("1")->second.size(), 1);
97
98   // We don't allow objects as keys in objects.
99   EXPECT_ANY_THROW(newObject[d3] = 12);
100
101   // Merge two objects
102   dynamic origMergeObj1 = folly::dynamic::object();
103   dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object
104     ("key1", "value1")
105     ("key2", "value2");
106   dynamic mergeObj2 = folly::dynamic::object
107     ("key2", "value3")
108     ("key3", "value4");
109
110   // Merged object where we prefer the values in mergeObj2
111   dynamic combinedPreferObj2 = folly::dynamic::object
112     ("key1", "value1")
113     ("key2", "value3")
114     ("key3", "value4");
115
116   // Merged object where we prefer the values in mergeObj1
117   dynamic combinedPreferObj1 = folly::dynamic::object
118     ("key1", "value1")
119     ("key2", "value2")
120     ("key3", "value4");
121
122   auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
123   EXPECT_EQ(newMergeObj, combinedPreferObj2);
124   EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
125
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);
130
131   mergeObj1 = origMergeObj1; // reset it
132   mergeObj1.update_missing(mergeObj2);
133   EXPECT_EQ(mergeObj1, combinedPreferObj1);
134 }
135
136 TEST(Dynamic, ObjectErase) {
137   dynamic obj = dynamic::object("key1", "val")
138                                ("key2", "val2");
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);
145   obj["key1"] = 12;
146   EXPECT_EQ(obj.count("key1"), 1);
147   EXPECT_EQ(obj.count("key2"), 1);
148   auto it = obj.find("key2");
149   obj.erase(it);
150   EXPECT_EQ(obj.count("key1"), 1);
151   EXPECT_EQ(obj.count("key2"), 0);
152
153   obj["asd"] = 42.0;
154   obj["foo"] = 42.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());
161 }
162
163 TEST(Dynamic, ArrayErase) {
164   dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
165
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);
171
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);
176 }
177
178 TEST(Dynamic, StringBasics) {
179   dynamic str = "hello world";
180   EXPECT_EQ(11, str.size());
181   EXPECT_FALSE(str.empty());
182   str = "";
183   EXPECT_TRUE(str.empty());
184 }
185
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);
192
193   EXPECT_ANY_THROW(array.at(-1));
194   EXPECT_ANY_THROW(array.at(3));
195
196   array.push_back("foo");
197   EXPECT_EQ(array.size(), 4);
198
199   array.resize(12, "something");
200   EXPECT_EQ(array.size(), 12);
201   EXPECT_EQ(array[11], "something");
202 }
203
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");
208   dynamic val2 = val;
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");
219
220   dynamic obj =
221     dynamic::object("a", "b")
222                    ("c", dynamic::array("d", "e", "f"));
223   EXPECT_EQ(obj.at("a"), "b");
224   dynamic obj2 = obj;
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);
229 }
230
231 TEST(Dynamic, ArrayReassignment) {
232   dynamic o = 1;
233   dynamic d1 = dynamic::array(o);
234   EXPECT_EQ(dynamic::ARRAY, d1.type());
235
236   d1 = dynamic::array(o);
237   EXPECT_EQ(dynamic::ARRAY, d1.type());
238 }
239
240 TEST(Dynamic, Operator) {
241   bool caught = false;
242   try {
243     dynamic d1 = dynamic::object;
244     dynamic d2 = dynamic::object;
245     auto foo = d1 < d2;
246     LOG(ERROR) << "operator < returned "
247                << static_cast<int>(foo)
248                << " instead of throwing";
249   } catch (std::exception const& e) {
250     caught = true;
251   }
252   EXPECT_TRUE(caught);
253
254   dynamic foo = "asd";
255   dynamic bar = "bar";
256   dynamic sum = foo + bar;
257   EXPECT_EQ(sum, "asdbar");
258
259   dynamic some = 12;
260   dynamic nums = 4;
261   dynamic math = some / nums;
262   EXPECT_EQ(math, 3);
263 }
264
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());
270
271   str = "12";
272   EXPECT_EQ(str.asInt(), 12);
273   EXPECT_EQ(str.asDouble(), 12.0);
274   str = "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");
279
280   dynamic num = 12;
281   EXPECT_EQ("12", num.asString());
282   EXPECT_EQ(12.0, num.asDouble());
283 }
284
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");
289
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);
294
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");
299
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"));
304 }
305
306 TEST(Dynamic, ObjectForwarding) {
307   // Make sure dynamic::object can be constructed the same way as any
308   // dynamic.
309   dynamic d = dynamic::object("asd", dynamic::array("foo", "bar"));
310   dynamic d2 = dynamic::object("key2", dynamic::array("value", "words"))
311                               ("key", "value1");
312 }
313
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));
322
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"));
331 }
332
333 TEST(Dynamic, Assignment) {
334   const dynamic ds[] = { dynamic::array(1, 2, 3),
335                          dynamic::object("a", true),
336                          24,
337                          26.5,
338                          true,
339                          "hello", };
340   const dynamic dd[] = { dynamic::array(5, 6),
341                          dynamic::object("t", "T")(1, 7),
342                          9000,
343                          3.14159,
344                          false,
345                          "world", };
346   for (const auto& source : ds) {
347     for (const auto& dest : dd) {
348       dynamic tmp(dest);
349       EXPECT_EQ(tmp, dest);
350       tmp = source;
351       EXPECT_EQ(tmp, source);
352     }
353   }
354 }
355
356 std::string make_long_string() {
357   return std::string(100, 'a');
358 }
359
360 TEST(Dynamic, GetDefault) {
361   const auto s = make_long_string();
362   dynamic ds(s);
363   dynamic tmp(s);
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);
368   // lvalue - lvalue
369   dynamic ayy("ayy");
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));
373   EXPECT_EQ(ds, tmp);
374   // lvalue - rvalue
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)));
378   EXPECT_NE(ds, tmp);
379   // rvalue - lvalue
380   tmp = s;
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);
385   EXPECT_EQ(ds, tmp);
386   // rvalue - rvalue
387   EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
388   EXPECT_NE(ds, d3["key3"]);
389   EXPECT_EQ(ds, tmp);
390   EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
391   EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
392   EXPECT_NE(ds, tmp);
393 }
394
395 TEST(Dynamic, GetString) {
396   const dynamic c(make_long_string());
397   dynamic d(make_long_string());
398   dynamic m(make_long_string());
399
400   auto s = make_long_string();
401
402   EXPECT_EQ(s, c.getString());
403   EXPECT_EQ(s, c.getString());
404
405   d.getString() += " hello";
406   EXPECT_EQ(s + " hello", d.getString());
407   EXPECT_EQ(s + " hello", d.getString());
408
409   EXPECT_EQ(s, std::move(m).getString());
410   EXPECT_NE(dynamic(s), m);
411 }
412
413 TEST(Dynamic, GetSmallThings) {
414   const dynamic cint(5);
415   const dynamic cdouble(5.0);
416   const dynamic cbool(true);
417   dynamic dint(5);
418   dynamic ddouble(5.0);
419   dynamic dbool(true);
420   dynamic mint(5);
421   dynamic mdouble(5.0);
422   dynamic mbool(true);
423
424   EXPECT_EQ(5, cint.getInt());
425   dint.getInt() = 6;
426   EXPECT_EQ(6, dint.getInt());
427   EXPECT_EQ(5, std::move(mint).getInt());
428
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());
433
434   EXPECT_EQ(true, cbool.getBool());
435   dbool.getBool() = false;
436   EXPECT_FALSE(dbool.getBool());
437   EXPECT_EQ(true, std::move(mbool).getBool());
438 }
439
440 TEST(Dynamic, At) {
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());
444
445   dynamic ds(make_long_string());
446   EXPECT_EQ(ds, cd.at("key1"));
447   EXPECT_EQ(ds, cd.at("key1"));
448
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"));
452
453   EXPECT_EQ(ds, std::move(md).at("key1"));
454   EXPECT_NE(ds, md.at("key1"));
455 }
456
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());
461
462   dynamic ds(make_long_string());
463   EXPECT_EQ(ds, cd["key1"]);
464   EXPECT_EQ(ds, cd["key1"]);
465
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"]);
469
470   EXPECT_EQ(ds, std::move(md)["key1"]);
471   EXPECT_NE(ds, md["key1"]);
472 }
473
474 TEST(Dynamic, PrintNull) {
475   std::stringstream ss;
476   ss << folly::dynamic(nullptr);
477   EXPECT_EQ("null", ss.str());
478 }