Return rvalue references from &&-qualified members of dynamic
[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 <folly/portability/GTest.h>
20
21 #include <boost/next_prior.hpp>
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<std::string, 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"] = dynamic::array(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
111   // Merged object where we prefer the values in mergeObj2
112   dynamic combinedPreferObj2 = folly::dynamic::object
113     ("key1", "value1")
114     ("key2", "value3")
115     ("key3", "value4");
116
117   // Merged object where we prefer the values in mergeObj1
118   dynamic combinedPreferObj1 = folly::dynamic::object
119     ("key1", "value1")
120     ("key2", "value2")
121     ("key3", "value4");
122
123   auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
124   EXPECT_EQ(newMergeObj, combinedPreferObj2);
125   EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
126
127   mergeObj1.update(mergeObj2);
128   EXPECT_EQ(mergeObj1, combinedPreferObj2);
129   dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
130   EXPECT_THROW(mergeObj1.update(arr), std::exception);
131
132   mergeObj1 = origMergeObj1; // reset it
133   mergeObj1.update_missing(mergeObj2);
134   EXPECT_EQ(mergeObj1, combinedPreferObj1);
135 }
136
137 TEST(Dynamic, ObjectErase) {
138   dynamic obj = dynamic::object("key1", "val")
139                                ("key2", "val2");
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);
146   obj["key1"] = 12;
147   EXPECT_EQ(obj.count("key1"), 1);
148   EXPECT_EQ(obj.count("key2"), 1);
149   auto it = obj.find("key2");
150   obj.erase(it);
151   EXPECT_EQ(obj.count("key1"), 1);
152   EXPECT_EQ(obj.count("key2"), 0);
153
154   obj["asd"] = 42.0;
155   obj["foo"] = 42.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());
162 }
163
164 TEST(Dynamic, ArrayErase) {
165   dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
166
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);
172
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);
177 }
178
179 TEST(Dynamic, StringBasics) {
180   dynamic str = "hello world";
181   EXPECT_EQ(11, str.size());
182   EXPECT_FALSE(str.empty());
183   str = "";
184   EXPECT_TRUE(str.empty());
185 }
186
187 TEST(Dynamic, ArrayBasics) {
188   dynamic array = 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);
193
194   EXPECT_ANY_THROW(array.at(-1));
195   EXPECT_ANY_THROW(array.at(3));
196
197   array.push_back("foo");
198   EXPECT_EQ(array.size(), 4);
199
200   array.resize(12, "something");
201   EXPECT_EQ(array.size(), 12);
202   EXPECT_EQ(array[11], "something");
203 }
204
205 TEST(Dynamic, DeepCopy) {
206   dynamic val = dynamic::array("foo", "bar", dynamic::array("foo1", "bar1"));
207   EXPECT_EQ(val.at(2).at(0), "foo1");
208   EXPECT_EQ(val.at(2).at(1), "bar1");
209   dynamic val2 = val;
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");
220
221   dynamic obj =
222     dynamic::object("a", "b")
223                    ("c", dynamic::array("d", "e", "f"));
224   EXPECT_EQ(obj.at("a"), "b");
225   dynamic obj2 = obj;
226   obj2.at("a") = dynamic::array(1, 2, 3);
227   EXPECT_EQ(obj.at("a"), "b");
228   dynamic expected = dynamic::array(1, 2, 3);
229   EXPECT_EQ(obj2.at("a"), expected);
230 }
231
232 TEST(Dynamic, ArrayReassignment) {
233   dynamic o = 1;
234   dynamic d1 = dynamic::array(o);
235   EXPECT_EQ(dynamic::ARRAY, d1.type());
236
237   d1 = dynamic::array(o);
238   EXPECT_EQ(dynamic::ARRAY, d1.type());
239 }
240
241 TEST(Dynamic, Operator) {
242   bool caught = false;
243   try {
244     dynamic d1 = dynamic::object;
245     dynamic d2 = dynamic::object;
246     auto foo = d1 < d2;
247     LOG(ERROR) << "operator < returned "
248                << static_cast<int>(foo)
249                << " instead of throwing";
250   } catch (std::exception const& e) {
251     caught = true;
252   }
253   EXPECT_TRUE(caught);
254
255   dynamic foo = "asd";
256   dynamic bar = "bar";
257   dynamic sum = foo + bar;
258   EXPECT_EQ(sum, "asdbar");
259
260   dynamic some = 12;
261   dynamic nums = 4;
262   dynamic math = some / nums;
263   EXPECT_EQ(math, 3);
264 }
265
266 TEST(Dynamic, Conversions) {
267   dynamic str = "12.0";
268   EXPECT_EQ(str.asDouble(), 12.0);
269   EXPECT_ANY_THROW(str.asInt());
270   EXPECT_ANY_THROW(str.asBool());
271
272   str = "12";
273   EXPECT_EQ(str.asInt(), 12);
274   EXPECT_EQ(str.asDouble(), 12.0);
275   str = "0";
276   EXPECT_EQ(str.asBool(), false);
277   EXPECT_EQ(str.asInt(), 0);
278   EXPECT_EQ(str.asDouble(), 0);
279   EXPECT_EQ(str.asString(), "0");
280
281   dynamic num = 12;
282   EXPECT_EQ("12", num.asString());
283   EXPECT_EQ(12.0, num.asDouble());
284 }
285
286 TEST(Dynamic, GetSetDefaultTest) {
287   dynamic d1 = dynamic::object("foo", "bar");
288   EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
289   EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
290
291   dynamic d2 = dynamic::object("foo", "bar");
292   EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
293   d2.setDefault("bar", dynamic::array).push_back(42);
294   EXPECT_EQ(d2["bar"][0], 42);
295
296   dynamic d3 = dynamic::object, empty = dynamic::object;
297   EXPECT_EQ(d3.getDefault("foo"), empty);
298   d3.setDefault("foo")["bar"] = "baz";
299   EXPECT_EQ(d3["foo"]["bar"], "baz");
300
301   // we do not allow getDefault/setDefault on arrays
302   dynamic d4 = dynamic::array;
303   EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
304   EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
305 }
306
307 TEST(Dynamic, ObjectForwarding) {
308   // Make sure dynamic::object can be constructed the same way as any
309   // dynamic.
310   dynamic d = dynamic::object("asd", dynamic::array("foo", "bar"));
311   dynamic d2 = dynamic::object("key2", dynamic::array("value", "words"))
312                               ("key", "value1");
313 }
314
315 TEST(Dynamic, GetPtr) {
316   dynamic array = dynamic::array(1, 2, "three");
317   EXPECT_TRUE(array.get_ptr(0));
318   EXPECT_FALSE(array.get_ptr(-1));
319   EXPECT_FALSE(array.get_ptr(3));
320   EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
321   const dynamic& carray = array;
322   EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
323
324   dynamic object = dynamic::object("one", 1)("two", 2);
325   EXPECT_TRUE(object.get_ptr("one"));
326   EXPECT_FALSE(object.get_ptr("three"));
327   EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
328   *object.get_ptr("one") = 11;
329   EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
330   const dynamic& cobject = object;
331   EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
332 }
333
334 TEST(Dynamic, Assignment) {
335   const dynamic ds[] = { dynamic::array(1, 2, 3),
336                          dynamic::object("a", true),
337                          24,
338                          26.5,
339                          true,
340                          "hello", };
341   const dynamic dd[] = { dynamic::array(5, 6),
342                          dynamic::object("t", "T")(1, 7),
343                          9000,
344                          3.14159,
345                          false,
346                          "world", };
347   for (const auto& source : ds) {
348     for (const auto& dest : dd) {
349       dynamic tmp(dest);
350       EXPECT_EQ(tmp, dest);
351       tmp = source;
352       EXPECT_EQ(tmp, source);
353     }
354   }
355 }
356
357 std::string make_long_string() {
358   return std::string(100, 'a');
359 }
360
361 TEST(Dynamic, GetDefault) {
362   const auto s = make_long_string();
363   dynamic ds(s);
364   dynamic tmp(s);
365   dynamic d1 = dynamic::object("key1", s);
366   dynamic d2 = dynamic::object("key2", s);
367   dynamic d3 = dynamic::object("key3", s);
368   dynamic d4 = dynamic::object("key4", s);
369   // lvalue - lvalue
370   dynamic ayy("ayy");
371   EXPECT_EQ(ds, d1.getDefault("key1", ayy));
372   EXPECT_EQ(ds, d1.getDefault("key1", ayy));
373   EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
374   EXPECT_EQ(ds, tmp);
375   // lvalue - rvalue
376   EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
377   EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
378   EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
379   EXPECT_NE(ds, tmp);
380   // rvalue - lvalue
381   tmp = s;
382   EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
383   EXPECT_NE(ds, d1["key1"]);
384   EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
385   EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
386   EXPECT_EQ(ds, tmp);
387   // rvalue - rvalue
388   EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
389   EXPECT_NE(ds, d3["key3"]);
390   EXPECT_EQ(ds, tmp);
391   EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
392   EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
393   EXPECT_NE(ds, tmp);
394 }
395
396 TEST(Dynamic, GetString) {
397   const dynamic c(make_long_string());
398   dynamic d(make_long_string());
399   dynamic m(make_long_string());
400
401   auto s = make_long_string();
402
403   EXPECT_EQ(s, c.getString());
404   EXPECT_EQ(s, c.getString());
405
406   d.getString() += " hello";
407   EXPECT_EQ(s + " hello", d.getString());
408   EXPECT_EQ(s + " hello", d.getString());
409
410   EXPECT_EQ(s, std::move(m).getString());
411   EXPECT_EQ(s, m.getString());
412   auto moved = std::move(m).getString();
413   EXPECT_EQ(s, moved);
414   EXPECT_NE(dynamic(s), m);
415 }
416
417 TEST(Dynamic, GetSmallThings) {
418   const dynamic cint(5);
419   const dynamic cdouble(5.0);
420   const dynamic cbool(true);
421   dynamic dint(5);
422   dynamic ddouble(5.0);
423   dynamic dbool(true);
424   dynamic mint(5);
425   dynamic mdouble(5.0);
426   dynamic mbool(true);
427
428   EXPECT_EQ(5, cint.getInt());
429   dint.getInt() = 6;
430   EXPECT_EQ(6, dint.getInt());
431   EXPECT_EQ(5, std::move(mint).getInt());
432
433   EXPECT_EQ(5.0, cdouble.getDouble());
434   ddouble.getDouble() = 6.0;
435   EXPECT_EQ(6.0, ddouble.getDouble());
436   EXPECT_EQ(5.0, std::move(mdouble).getDouble());
437
438   EXPECT_EQ(true, cbool.getBool());
439   dbool.getBool() = false;
440   EXPECT_FALSE(dbool.getBool());
441   EXPECT_EQ(true, std::move(mbool).getBool());
442 }
443
444 TEST(Dynamic, At) {
445   const dynamic cd = dynamic::object("key1", make_long_string());
446   dynamic dd = dynamic::object("key1", make_long_string());
447   dynamic md = dynamic::object("key1", make_long_string());
448
449   dynamic ds(make_long_string());
450   EXPECT_EQ(ds, cd.at("key1"));
451   EXPECT_EQ(ds, cd.at("key1"));
452
453   dd.at("key1").getString() += " hello";
454   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
455   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
456
457   EXPECT_EQ(ds, std::move(md).at("key1")); // move available, but not performed
458   EXPECT_EQ(ds, md.at("key1"));
459   dynamic moved = std::move(md).at("key1"); // move performed
460   EXPECT_EQ(ds, moved);
461   EXPECT_NE(ds, md.at("key1"));
462 }
463
464 TEST(Dynamic, Brackets) {
465   const dynamic cd = dynamic::object("key1", make_long_string());
466   dynamic dd = dynamic::object("key1", make_long_string());
467   dynamic md = dynamic::object("key1", make_long_string());
468
469   dynamic ds(make_long_string());
470   EXPECT_EQ(ds, cd["key1"]);
471   EXPECT_EQ(ds, cd["key1"]);
472
473   dd["key1"].getString() += " hello";
474   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
475   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
476
477   EXPECT_EQ(ds, std::move(md)["key1"]); // move available, but not performed
478   EXPECT_EQ(ds, md["key1"]);
479   dynamic moved = std::move(md)["key1"]; // move performed
480   EXPECT_EQ(ds, moved);
481   EXPECT_NE(ds, md["key1"]);
482 }
483
484 TEST(Dynamic, PrintNull) {
485   std::stringstream ss;
486   ss << folly::dynamic(nullptr);
487   EXPECT_EQ("null", ss.str());
488 }