dynamic::get_ptr
[folly.git] / folly / test / DynamicTest.cpp
1 /*
2  * Copyright 2013 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
221 TEST(Dynamic, FormattedIO) {
222   std::ostringstream out;
223   dynamic doubl = 123.33;
224   dynamic dint = 12;
225   out << "0x" << std::hex << ++dint << ' ' << std::setprecision(1)
226       << doubl << '\n';
227   EXPECT_EQ(out.str(), "0xd 1e+02\n");
228
229   out.str("");
230   dynamic arrr = { 1, 2, 3 };
231   out << arrr;
232   EXPECT_EQ(out.str(), "[1,2,3]");
233
234   out.str("");
235   dynamic objy = dynamic::object("a", 12);
236   out << objy;
237   EXPECT_EQ(out.str(), R"({"a":12})");
238
239   out.str("");
240   dynamic objy2 = { objy, dynamic::object(12, "str"),
241                           dynamic::object(true, false) };
242   out << objy2;
243   EXPECT_EQ(out.str(), R"([{"a":12},{12:"str"},{true:false}])");
244 }
245
246 TEST(Dynamic, GetSetDefaultTest) {
247   dynamic d1 = dynamic::object("foo", "bar");
248   EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
249   EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
250
251   dynamic d2 = dynamic::object("foo", "bar");
252   EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
253   d2.setDefault("bar", dynamic({})).push_back(42);
254   EXPECT_EQ(d2["bar"][0], 42);
255
256   dynamic d3 = dynamic::object, empty = dynamic::object;
257   EXPECT_EQ(d3.getDefault("foo"), empty);
258   d3.setDefault("foo")["bar"] = "baz";
259   EXPECT_EQ(d3["foo"]["bar"], "baz");
260
261   // we do not allow getDefault/setDefault on arrays
262   dynamic d4 = dynamic({});
263   EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
264   EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
265 }
266
267 TEST(Dynamic, ObjectForwarding) {
268   // Make sure dynamic::object can be constructed the same way as any
269   // dynamic.
270   dynamic d = dynamic::object("asd", {"foo", "bar"});
271   dynamic d2 = dynamic::object("key2", {"value", "words"})
272                               ("key", "value1");
273 }
274
275 TEST(Dynamic, GetPtr) {
276   dynamic array = { 1, 2, "three" };
277   EXPECT_TRUE(array.get_ptr(0));
278   EXPECT_FALSE(array.get_ptr(3));
279   EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
280   const dynamic& carray = array;
281   EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
282
283   dynamic object = dynamic::object("one", 1)("two", 2);
284   EXPECT_TRUE(object.get_ptr("one"));
285   EXPECT_FALSE(object.get_ptr("three"));
286   EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
287   *object.get_ptr("one") = 11;
288   EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
289   const dynamic& cobject = object;
290   EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
291 }
292
293 int main(int argc, char** argv) {
294   testing::InitGoogleTest(&argc, argv);
295   google::ParseCommandLineFlags(&argc, &argv, true);
296   if (FLAGS_benchmark) {
297     folly::runBenchmarks();
298   }
299   return RUN_ALL_TESTS();
300 }
301