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