2 * Copyright 2015 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <boost/next_prior.hpp>
18 #include <gflags/gflags.h>
19 #include <gtest/gtest.h>
21 #include <folly/Benchmark.h>
22 #include <folly/dynamic.h>
23 #include <folly/gen/Base.h>
24 #include <folly/json.h>
27 using folly::TypeError;
29 TEST(Dynamic, ObjectBasics) {
30 dynamic obj = dynamic::object("a", false);
31 EXPECT_EQ(obj.at("a"), false);
32 EXPECT_EQ(obj.size(), 1);
33 obj.insert("a", true);
34 EXPECT_EQ(obj.size(), 1);
35 EXPECT_EQ(obj.at("a"), true);
36 obj.at("a") = nullptr;
37 EXPECT_EQ(obj.size(), 1);
38 EXPECT_TRUE(obj.at("a") == nullptr);
40 dynamic newObject = dynamic::object;
43 EXPECT_EQ(newObject.size(), 1);
44 newObject["a"] = true;
45 EXPECT_EQ(newObject.size(), 2);
47 EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
48 EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
49 std::vector<std::pair<folly::fbstring, dynamic>> found;
50 found.push_back(std::make_pair(
51 newObject.keys().begin()->asString(),
52 *newObject.values().begin()));
54 EXPECT_EQ(*boost::next(newObject.keys().begin()),
55 boost::next(newObject.items().begin())->first);
56 EXPECT_EQ(*boost::next(newObject.values().begin()),
57 boost::next(newObject.items().begin())->second);
58 found.push_back(std::make_pair(
59 boost::next(newObject.keys().begin())->asString(),
60 *boost::next(newObject.values().begin())));
62 std::sort(found.begin(), found.end());
64 EXPECT_EQ("a", found[0].first);
65 EXPECT_TRUE(found[0].second.asBool());
67 EXPECT_EQ("z", found[1].first);
68 EXPECT_EQ(12, found[1].second.asInt());
70 dynamic obj2 = dynamic::object;
71 EXPECT_TRUE(obj2.isObject());
74 EXPECT_TRUE(d3 == nullptr);
76 EXPECT_TRUE(d3.isObject());
77 d3["foo"] = { 1, 2, 3 };
78 EXPECT_EQ(d3.count("foo"), 1);
81 EXPECT_EQ(d3.at(123), 321);
84 EXPECT_EQ(d3.at("123"), 42);
85 EXPECT_EQ(d3.at(123), 321);
87 dynamic objInsert = folly::dynamic::object();
88 dynamic objA = folly::dynamic::object("1", "2");
89 dynamic objB = folly::dynamic::object("1", "2");
91 objInsert.insert("1", std::move(objA));
92 objInsert.insert("1", std::move(objB));
94 EXPECT_EQ(objInsert.find("1")->second.size(), 1);
96 // We don't allow objects as keys in objects.
97 EXPECT_ANY_THROW(newObject[d3] = 12);
100 TEST(Dynamic, ObjectErase) {
101 dynamic obj = dynamic::object("key1", "val")
103 EXPECT_EQ(obj.count("key1"), 1);
104 EXPECT_EQ(obj.count("key2"), 1);
105 EXPECT_EQ(obj.erase("key1"), 1);
106 EXPECT_EQ(obj.count("key1"), 0);
107 EXPECT_EQ(obj.count("key2"), 1);
108 EXPECT_EQ(obj.erase("key1"), 0);
110 EXPECT_EQ(obj.count("key1"), 1);
111 EXPECT_EQ(obj.count("key2"), 1);
112 auto it = obj.find("key2");
114 EXPECT_EQ(obj.count("key1"), 1);
115 EXPECT_EQ(obj.count("key2"), 0);
119 EXPECT_EQ(obj.size(), 3);
120 auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
121 EXPECT_TRUE(ret == obj.items().end());
122 EXPECT_EQ(obj.size(), 1);
123 obj.erase(obj.items().begin());
124 EXPECT_TRUE(obj.empty());
127 TEST(Dynamic, ArrayErase) {
128 dynamic arr = { 1, 2, 3, 4, 5, 6 };
130 EXPECT_THROW(arr.erase(1), std::exception);
131 EXPECT_EQ(arr.size(), 6);
132 EXPECT_EQ(arr[0], 1);
133 arr.erase(arr.begin());
134 EXPECT_EQ(arr.size(), 5);
136 arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
137 EXPECT_EQ(arr.size(), 2);
138 EXPECT_EQ(arr[0], 2);
139 EXPECT_EQ(arr[1], 6);
142 TEST(Dynamic, StringBasics) {
143 dynamic str = "hello world";
144 EXPECT_EQ(11, str.size());
145 EXPECT_FALSE(str.empty());
147 EXPECT_TRUE(str.empty());
150 TEST(Dynamic, ArrayBasics) {
151 dynamic array = { 1, 2, 3 };
152 EXPECT_EQ(array.size(), 3);
153 EXPECT_EQ(array.at(0), 1);
154 EXPECT_EQ(array.at(1), 2);
155 EXPECT_EQ(array.at(2), 3);
157 EXPECT_ANY_THROW(array.at(3));
159 array.push_back("foo");
160 EXPECT_EQ(array.size(), 4);
162 array.resize(12, "something");
163 EXPECT_EQ(array.size(), 12);
164 EXPECT_EQ(array[11], "something");
167 TEST(Dynamic, DeepCopy) {
168 dynamic val = { "foo", "bar", { "foo1", "bar1" } };
169 EXPECT_EQ(val.at(2).at(0), "foo1");
170 EXPECT_EQ(val.at(2).at(1), "bar1");
172 EXPECT_EQ(val2.at(2).at(0), "foo1");
173 EXPECT_EQ(val2.at(2).at(1), "bar1");
174 EXPECT_EQ(val.at(2).at(0), "foo1");
175 EXPECT_EQ(val.at(2).at(1), "bar1");
176 val2.at(2).at(0) = "foo3";
177 val2.at(2).at(1) = "bar3";
178 EXPECT_EQ(val.at(2).at(0), "foo1");
179 EXPECT_EQ(val.at(2).at(1), "bar1");
180 EXPECT_EQ(val2.at(2).at(0), "foo3");
181 EXPECT_EQ(val2.at(2).at(1), "bar3");
183 dynamic obj = dynamic::object("a", "b")
184 ("c", {"d", "e", "f"})
186 EXPECT_EQ(obj.at("a"), "b");
188 obj2.at("a") = {1, 2, 3};
189 EXPECT_EQ(obj.at("a"), "b");
190 dynamic expected = {1, 2, 3};
191 EXPECT_EQ(obj2.at("a"), expected);
194 TEST(Dynamic, Operator) {
197 dynamic d1 = dynamic::object;
198 dynamic d2 = dynamic::object;
200 } catch (std::exception const& e) {
207 dynamic sum = foo + bar;
208 EXPECT_EQ(sum, "asdbar");
212 dynamic math = some / nums;
216 TEST(Dynamic, Conversions) {
217 dynamic str = "12.0";
218 EXPECT_EQ(str.asDouble(), 12.0);
219 EXPECT_ANY_THROW(str.asInt());
220 EXPECT_ANY_THROW(str.asBool());
223 EXPECT_EQ(str.asInt(), 12);
224 EXPECT_EQ(str.asDouble(), 12.0);
226 EXPECT_EQ(str.asBool(), false);
227 EXPECT_EQ(str.asInt(), 0);
228 EXPECT_EQ(str.asDouble(), 0);
229 EXPECT_EQ(str.asString(), "0");
232 EXPECT_EQ("12", num.asString());
233 EXPECT_EQ(12.0, num.asDouble());
236 TEST(Dynamic, StringPtrs) {
237 dynamic str = "12.0";
239 dynamic nullStr = folly::parseJson("\"foo\\u0000bar\"");
241 EXPECT_EQ(0, strcmp(str.c_str(), "12.0"));
242 EXPECT_EQ(0, strncmp(str.data(), "12.0", str.asString().length()));
243 EXPECT_EQ(str.stringPiece(), "12.0");
245 EXPECT_THROW(num.c_str(), TypeError);
246 EXPECT_THROW(num.data(), TypeError);
247 EXPECT_THROW(num.stringPiece(), TypeError);
249 EXPECT_EQ(nullStr.stringPiece(), folly::StringPiece("foo\0bar", 7));
251 nullStr.getString()[3] = '|';
252 EXPECT_EQ(nullStr.stringPiece(), "foo|bar");
255 TEST(Dynamic, FormattedIO) {
256 std::ostringstream out;
257 dynamic doubl = 123.33;
259 out << "0x" << std::hex << ++dint << ' ' << std::setprecision(1)
261 EXPECT_EQ(out.str(), "0xd 1e+02\n");
264 dynamic arrr = { 1, 2, 3 };
266 EXPECT_EQ(out.str(), "[1,2,3]");
269 dynamic objy = dynamic::object("a", 12);
271 EXPECT_EQ(out.str(), R"({"a":12})");
274 dynamic objy2 = { objy, dynamic::object(12, "str"),
275 dynamic::object(true, false) };
277 EXPECT_EQ(out.str(), R"([{"a":12},{12:"str"},{true:false}])");
280 TEST(Dynamic, GetSetDefaultTest) {
281 dynamic d1 = dynamic::object("foo", "bar");
282 EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
283 EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
285 dynamic d2 = dynamic::object("foo", "bar");
286 EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
287 d2.setDefault("bar", dynamic({})).push_back(42);
288 EXPECT_EQ(d2["bar"][0], 42);
290 dynamic d3 = dynamic::object, empty = dynamic::object;
291 EXPECT_EQ(d3.getDefault("foo"), empty);
292 d3.setDefault("foo")["bar"] = "baz";
293 EXPECT_EQ(d3["foo"]["bar"], "baz");
295 // we do not allow getDefault/setDefault on arrays
296 dynamic d4 = dynamic({});
297 EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
298 EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
301 TEST(Dynamic, ObjectForwarding) {
302 // Make sure dynamic::object can be constructed the same way as any
304 dynamic d = dynamic::object("asd", {"foo", "bar"});
305 dynamic d2 = dynamic::object("key2", {"value", "words"})
309 TEST(Dynamic, GetPtr) {
310 dynamic array = { 1, 2, "three" };
311 EXPECT_TRUE(array.get_ptr(0));
312 EXPECT_FALSE(array.get_ptr(3));
313 EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
314 const dynamic& carray = array;
315 EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
317 dynamic object = dynamic::object("one", 1)("two", 2);
318 EXPECT_TRUE(object.get_ptr("one"));
319 EXPECT_FALSE(object.get_ptr("three"));
320 EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
321 *object.get_ptr("one") = 11;
322 EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
323 const dynamic& cobject = object;
324 EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
327 TEST(Dynamic, ArrayGenerator) {
328 // Make sure arrays can be used with folly::gen.
329 using namespace folly::gen;
330 dynamic arr { 1, 2, 3, 4 };
331 EXPECT_EQ(from(arr) | take(3) | member(&dynamic::asInt) | sum, 6);
334 TEST(Dynamic, Getters) {
335 dynamic dStr = folly::parseJson("\"foo\\u0000bar\"");
337 dynamic dDouble = 0.5;
338 dynamic dBool = true;
340 EXPECT_EQ(dStr.getString(), std::string("foo\0bar", 7));
341 EXPECT_EQ(dInt.getInt(), 1);
342 EXPECT_EQ(dDouble.getDouble(), 0.5);
343 EXPECT_EQ(dBool.getBool(), true);
345 dStr.getString()[3] = '|';
346 EXPECT_EQ(dStr.getString(), "foo|bar");
349 EXPECT_EQ(dInt.getInt(), 2);
351 dDouble.getDouble() = 0.7;
352 EXPECT_EQ(dDouble.getDouble(), 0.7);
354 dBool.getBool() = false;
355 EXPECT_EQ(dBool.getBool(), false);
357 EXPECT_THROW(dStr.getInt(), TypeError);
358 EXPECT_THROW(dStr.getDouble(), TypeError);
359 EXPECT_THROW(dStr.getBool(), TypeError);
361 EXPECT_THROW(dInt.getString(), TypeError);
362 EXPECT_THROW(dInt.getDouble(), TypeError);
363 EXPECT_THROW(dInt.getBool(), TypeError);
365 EXPECT_THROW(dDouble.getString(), TypeError);
366 EXPECT_THROW(dDouble.getInt(), TypeError);
367 EXPECT_THROW(dDouble.getBool(), TypeError);
369 EXPECT_THROW(dBool.getString(), TypeError);
370 EXPECT_THROW(dBool.getInt(), TypeError);
371 EXPECT_THROW(dBool.getDouble(), TypeError);
374 int main(int argc, char** argv) {
375 testing::InitGoogleTest(&argc, argv);
376 gflags::ParseCommandLineFlags(&argc, &argv, true);
377 if (FLAGS_benchmark) {
378 folly::runBenchmarks();
380 return RUN_ALL_TESTS();