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