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