Copyright 2013 -> 2014
[folly.git] / folly / test / DynamicTest.cpp
1 /*
2  * Copyright 2014 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 <boost/next_prior.hpp>
18 #include <gflags/gflags.h>
19 #include <gtest/gtest.h>
20
21 #include "folly/Benchmark.h"
22 #include "folly/dynamic.h"
23 #include "folly/json.h"
24
25 using folly::dynamic;
26
27 TEST(Dynamic, ObjectBasics) {
28   dynamic obj = dynamic::object("a", false);
29   EXPECT_EQ(obj.at("a"), false);
30   EXPECT_EQ(obj.size(), 1);
31   obj.insert("a", true);
32   EXPECT_EQ(obj.size(), 1);
33   EXPECT_EQ(obj.at("a"), true);
34   obj.at("a") = nullptr;
35   EXPECT_EQ(obj.size(), 1);
36   EXPECT_TRUE(obj.at("a") == nullptr);
37
38   dynamic newObject = dynamic::object;
39
40   newObject["z"] = 12;
41   EXPECT_EQ(newObject.size(), 1);
42   newObject["a"] = true;
43   EXPECT_EQ(newObject.size(), 2);
44
45   EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
46   EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
47   std::vector<std::pair<folly::fbstring, dynamic>> found;
48   found.push_back(std::make_pair(
49      newObject.keys().begin()->asString(),
50      *newObject.values().begin()));
51
52   EXPECT_EQ(*boost::next(newObject.keys().begin()),
53             boost::next(newObject.items().begin())->first);
54   EXPECT_EQ(*boost::next(newObject.values().begin()),
55             boost::next(newObject.items().begin())->second);
56   found.push_back(std::make_pair(
57       boost::next(newObject.keys().begin())->asString(),
58       *boost::next(newObject.values().begin())));
59
60   std::sort(found.begin(), found.end());
61
62   EXPECT_EQ("a", found[0].first);
63   EXPECT_TRUE(found[0].second.asBool());
64
65   EXPECT_EQ("z", found[1].first);
66   EXPECT_EQ(12, found[1].second.asInt());
67
68   dynamic obj2 = dynamic::object;
69   EXPECT_TRUE(obj2.isObject());
70
71   dynamic d3 = nullptr;
72   EXPECT_TRUE(d3 == nullptr);
73   d3 = dynamic::object;
74   EXPECT_TRUE(d3.isObject());
75   d3["foo"] = { 1, 2, 3 };
76   EXPECT_EQ(d3.count("foo"), 1);
77
78   d3[123] = 321;
79   EXPECT_EQ(d3.at(123), 321);
80
81   d3["123"] = 42;
82   EXPECT_EQ(d3.at("123"), 42);
83   EXPECT_EQ(d3.at(123), 321);
84
85   // We don't allow objects as keys in objects.
86   EXPECT_ANY_THROW(newObject[d3] = 12);
87 }
88
89 TEST(Dynamic, ObjectErase) {
90   dynamic obj = dynamic::object("key1", "val")
91                                ("key2", "val2");
92   EXPECT_EQ(obj.count("key1"), 1);
93   EXPECT_EQ(obj.count("key2"), 1);
94   EXPECT_EQ(obj.erase("key1"), 1);
95   EXPECT_EQ(obj.count("key1"), 0);
96   EXPECT_EQ(obj.count("key2"), 1);
97   EXPECT_EQ(obj.erase("key1"), 0);
98   obj["key1"] = 12;
99   EXPECT_EQ(obj.count("key1"), 1);
100   EXPECT_EQ(obj.count("key2"), 1);
101   auto it = obj.find("key2");
102   obj.erase(it);
103   EXPECT_EQ(obj.count("key1"), 1);
104   EXPECT_EQ(obj.count("key2"), 0);
105
106   obj["asd"] = 42.0;
107   obj["foo"] = 42.0;
108   EXPECT_EQ(obj.size(), 3);
109   auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
110   EXPECT_TRUE(ret == obj.items().end());
111   EXPECT_EQ(obj.size(), 1);
112   obj.erase(obj.items().begin());
113   EXPECT_TRUE(obj.empty());
114 }
115
116 TEST(Dynamic, ArrayErase) {
117   dynamic arr = { 1, 2, 3, 4, 5, 6 };
118
119   EXPECT_THROW(arr.erase(1), std::exception);
120   EXPECT_EQ(arr.size(), 6);
121   EXPECT_EQ(arr[0], 1);
122   arr.erase(arr.begin());
123   EXPECT_EQ(arr.size(), 5);
124
125   arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
126   EXPECT_EQ(arr.size(), 2);
127   EXPECT_EQ(arr[0], 2);
128   EXPECT_EQ(arr[1], 6);
129 }
130
131 TEST(Dynamic, StringBasics) {
132   dynamic str = "hello world";
133   EXPECT_EQ(11, str.size());
134   EXPECT_FALSE(str.empty());
135   str = "";
136   EXPECT_TRUE(str.empty());
137 }
138
139 TEST(Dynamic, ArrayBasics) {
140   dynamic array = { 1, 2, 3 };
141   EXPECT_EQ(array.size(), 3);
142   EXPECT_EQ(array.at(0), 1);
143   EXPECT_EQ(array.at(1), 2);
144   EXPECT_EQ(array.at(2), 3);
145
146   EXPECT_ANY_THROW(array.at(3));
147
148   array.push_back("foo");
149   EXPECT_EQ(array.size(), 4);
150
151   array.resize(12, "something");
152   EXPECT_EQ(array.size(), 12);
153   EXPECT_EQ(array[11], "something");
154 }
155
156 TEST(Dynamic, DeepCopy) {
157   dynamic val = { "foo", "bar", { "foo1", "bar1" } };
158   EXPECT_EQ(val.at(2).at(0), "foo1");
159   EXPECT_EQ(val.at(2).at(1), "bar1");
160   dynamic val2 = val;
161   EXPECT_EQ(val2.at(2).at(0), "foo1");
162   EXPECT_EQ(val2.at(2).at(1), "bar1");
163   EXPECT_EQ(val.at(2).at(0), "foo1");
164   EXPECT_EQ(val.at(2).at(1), "bar1");
165   val2.at(2).at(0) = "foo3";
166   val2.at(2).at(1) = "bar3";
167   EXPECT_EQ(val.at(2).at(0), "foo1");
168   EXPECT_EQ(val.at(2).at(1), "bar1");
169   EXPECT_EQ(val2.at(2).at(0), "foo3");
170   EXPECT_EQ(val2.at(2).at(1), "bar3");
171
172   dynamic obj = dynamic::object("a", "b")
173                                ("c", {"d", "e", "f"})
174                                ;
175   EXPECT_EQ(obj.at("a"), "b");
176   dynamic obj2 = obj;
177   obj2.at("a") = {1, 2, 3};
178   EXPECT_EQ(obj.at("a"), "b");
179   dynamic expected = {1, 2, 3};
180   EXPECT_EQ(obj2.at("a"), expected);
181 }
182
183 TEST(Dynamic, Operator) {
184   bool caught = false;
185   try {
186     dynamic d1 = dynamic::object;
187     dynamic d2 = dynamic::object;
188     auto foo = d1 < d2;
189   } catch (std::exception const& e) {
190     caught = true;
191   }
192   EXPECT_TRUE(caught);
193
194   dynamic foo = "asd";
195   dynamic bar = "bar";
196   dynamic sum = foo + bar;
197   EXPECT_EQ(sum, "asdbar");
198
199   dynamic some = 12;
200   dynamic nums = 4;
201   dynamic math = some / nums;
202   EXPECT_EQ(math, 3);
203 }
204
205 TEST(Dynamic, Conversions) {
206   dynamic str = "12.0";
207   EXPECT_EQ(str.asDouble(), 12.0);
208   EXPECT_ANY_THROW(str.asInt());
209   EXPECT_ANY_THROW(str.asBool());
210
211   str = "12";
212   EXPECT_EQ(str.asInt(), 12);
213   EXPECT_EQ(str.asDouble(), 12.0);
214   str = "0";
215   EXPECT_EQ(str.asBool(), false);
216   EXPECT_EQ(str.asInt(), 0);
217   EXPECT_EQ(str.asDouble(), 0);
218   EXPECT_EQ(str.asString(), "0");
219
220   dynamic num = 12;
221   EXPECT_EQ("12", num.asString());
222   EXPECT_EQ(12.0, num.asDouble());
223 }
224
225 TEST(Dynamic, StringPtrs) {
226   dynamic str = "12.0";
227   dynamic num = 12.0;
228
229   EXPECT_EQ(0, strcmp(str.c_str(), "12.0"));
230   EXPECT_EQ(0, strncmp(str.data(), "12.0", str.asString().length()));
231
232   EXPECT_ANY_THROW(num.c_str());
233   EXPECT_ANY_THROW(num.data());
234 }
235
236 TEST(Dynamic, FormattedIO) {
237   std::ostringstream out;
238   dynamic doubl = 123.33;
239   dynamic dint = 12;
240   out << "0x" << std::hex << ++dint << ' ' << std::setprecision(1)
241       << doubl << '\n';
242   EXPECT_EQ(out.str(), "0xd 1e+02\n");
243
244   out.str("");
245   dynamic arrr = { 1, 2, 3 };
246   out << arrr;
247   EXPECT_EQ(out.str(), "[1,2,3]");
248
249   out.str("");
250   dynamic objy = dynamic::object("a", 12);
251   out << objy;
252   EXPECT_EQ(out.str(), R"({"a":12})");
253
254   out.str("");
255   dynamic objy2 = { objy, dynamic::object(12, "str"),
256                           dynamic::object(true, false) };
257   out << objy2;
258   EXPECT_EQ(out.str(), R"([{"a":12},{12:"str"},{true:false}])");
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(3));
294   EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
295   const dynamic& carray = array;
296   EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
297
298   dynamic object = dynamic::object("one", 1)("two", 2);
299   EXPECT_TRUE(object.get_ptr("one"));
300   EXPECT_FALSE(object.get_ptr("three"));
301   EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
302   *object.get_ptr("one") = 11;
303   EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
304   const dynamic& cobject = object;
305   EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
306 }
307
308 int main(int argc, char** argv) {
309   testing::InitGoogleTest(&argc, argv);
310   google::ParseCommandLineFlags(&argc, &argv, true);
311   if (FLAGS_benchmark) {
312     folly::runBenchmarks();
313   }
314   return RUN_ALL_TESTS();
315 }
316