blob: f4dbe32809428c63e20aa89a0072e57427e74261 [file] [log] [blame]
/*
* Copyright 2015 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <folly/dynamic.h>
#include <boost/next_prior.hpp>
#include <gflags/gflags.h>
#include <gtest/gtest.h>
using folly::dynamic;
TEST(Dynamic, ObjectBasics) {
dynamic obj = dynamic::object("a", false);
EXPECT_EQ(obj.at("a"), false);
EXPECT_EQ(obj.size(), 1);
obj.insert("a", true);
EXPECT_EQ(obj.size(), 1);
EXPECT_EQ(obj.at("a"), true);
obj.at("a") = nullptr;
EXPECT_EQ(obj.size(), 1);
EXPECT_TRUE(obj.at("a") == nullptr);
dynamic newObject = dynamic::object;
newObject["z"] = 12;
EXPECT_EQ(newObject.size(), 1);
newObject["a"] = true;
EXPECT_EQ(newObject.size(), 2);
EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
std::vector<std::pair<folly::fbstring, dynamic>> found;
found.emplace_back(newObject.keys().begin()->asString(),
*newObject.values().begin());
EXPECT_EQ(*boost::next(newObject.keys().begin()),
boost::next(newObject.items().begin())->first);
EXPECT_EQ(*boost::next(newObject.values().begin()),
boost::next(newObject.items().begin())->second);
found.emplace_back(boost::next(newObject.keys().begin())->asString(),
*boost::next(newObject.values().begin()));
std::sort(found.begin(), found.end());
EXPECT_EQ("a", found[0].first);
EXPECT_TRUE(found[0].second.asBool());
EXPECT_EQ("z", found[1].first);
EXPECT_EQ(12, found[1].second.asInt());
dynamic obj2 = dynamic::object;
EXPECT_TRUE(obj2.isObject());
dynamic d3 = nullptr;
EXPECT_TRUE(d3 == nullptr);
d3 = dynamic::object;
EXPECT_TRUE(d3.isObject());
d3["foo"] = { 1, 2, 3 };
EXPECT_EQ(d3.count("foo"), 1);
d3[123] = 321;
EXPECT_EQ(d3.at(123), 321);
d3["123"] = 42;
EXPECT_EQ(d3.at("123"), 42);
EXPECT_EQ(d3.at(123), 321);
dynamic objInsert = folly::dynamic::object();
dynamic objA = folly::dynamic::object("1", "2");
dynamic objB = folly::dynamic::object("1", "2");
objInsert.insert("1", std::move(objA));
objInsert.insert("1", std::move(objB));
EXPECT_EQ(objInsert.find("1")->second.size(), 1);
// We don't allow objects as keys in objects.
EXPECT_ANY_THROW(newObject[d3] = 12);
}
TEST(Dynamic, ObjectErase) {
dynamic obj = dynamic::object("key1", "val")
("key2", "val2");
EXPECT_EQ(obj.count("key1"), 1);
EXPECT_EQ(obj.count("key2"), 1);
EXPECT_EQ(obj.erase("key1"), 1);
EXPECT_EQ(obj.count("key1"), 0);
EXPECT_EQ(obj.count("key2"), 1);
EXPECT_EQ(obj.erase("key1"), 0);
obj["key1"] = 12;
EXPECT_EQ(obj.count("key1"), 1);
EXPECT_EQ(obj.count("key2"), 1);
auto it = obj.find("key2");
obj.erase(it);
EXPECT_EQ(obj.count("key1"), 1);
EXPECT_EQ(obj.count("key2"), 0);
obj["asd"] = 42.0;
obj["foo"] = 42.0;
EXPECT_EQ(obj.size(), 3);
auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
EXPECT_TRUE(ret == obj.items().end());
EXPECT_EQ(obj.size(), 1);
obj.erase(obj.items().begin());
EXPECT_TRUE(obj.empty());
}
TEST(Dynamic, ArrayErase) {
dynamic arr = { 1, 2, 3, 4, 5, 6 };
EXPECT_THROW(arr.erase(1), std::exception);
EXPECT_EQ(arr.size(), 6);
EXPECT_EQ(arr[0], 1);
arr.erase(arr.begin());
EXPECT_EQ(arr.size(), 5);
arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
EXPECT_EQ(arr.size(), 2);
EXPECT_EQ(arr[0], 2);
EXPECT_EQ(arr[1], 6);
}
TEST(Dynamic, StringBasics) {
dynamic str = "hello world";
EXPECT_EQ(11, str.size());
EXPECT_FALSE(str.empty());
str = "";
EXPECT_TRUE(str.empty());
}
TEST(Dynamic, ArrayBasics) {
dynamic array = { 1, 2, 3 };
EXPECT_EQ(array.size(), 3);
EXPECT_EQ(array.at(0), 1);
EXPECT_EQ(array.at(1), 2);
EXPECT_EQ(array.at(2), 3);
EXPECT_ANY_THROW(array.at(-1));
EXPECT_ANY_THROW(array.at(3));
array.push_back("foo");
EXPECT_EQ(array.size(), 4);
array.resize(12, "something");
EXPECT_EQ(array.size(), 12);
EXPECT_EQ(array[11], "something");
}
TEST(Dynamic, DeepCopy) {
dynamic val = { "foo", "bar", { "foo1", "bar1" } };
EXPECT_EQ(val.at(2).at(0), "foo1");
EXPECT_EQ(val.at(2).at(1), "bar1");
dynamic val2 = val;
EXPECT_EQ(val2.at(2).at(0), "foo1");
EXPECT_EQ(val2.at(2).at(1), "bar1");
EXPECT_EQ(val.at(2).at(0), "foo1");
EXPECT_EQ(val.at(2).at(1), "bar1");
val2.at(2).at(0) = "foo3";
val2.at(2).at(1) = "bar3";
EXPECT_EQ(val.at(2).at(0), "foo1");
EXPECT_EQ(val.at(2).at(1), "bar1");
EXPECT_EQ(val2.at(2).at(0), "foo3");
EXPECT_EQ(val2.at(2).at(1), "bar3");
dynamic obj = dynamic::object("a", "b")
("c", {"d", "e", "f"})
;
EXPECT_EQ(obj.at("a"), "b");
dynamic obj2 = obj;
obj2.at("a") = {1, 2, 3};
EXPECT_EQ(obj.at("a"), "b");
dynamic expected = {1, 2, 3};
EXPECT_EQ(obj2.at("a"), expected);
}
TEST(Dynamic, Operator) {
bool caught = false;
try {
dynamic d1 = dynamic::object;
dynamic d2 = dynamic::object;
auto foo = d1 < d2;
} catch (std::exception const& e) {
caught = true;
}
EXPECT_TRUE(caught);
dynamic foo = "asd";
dynamic bar = "bar";
dynamic sum = foo + bar;
EXPECT_EQ(sum, "asdbar");
dynamic some = 12;
dynamic nums = 4;
dynamic math = some / nums;
EXPECT_EQ(math, 3);
}
TEST(Dynamic, Conversions) {
dynamic str = "12.0";
EXPECT_EQ(str.asDouble(), 12.0);
EXPECT_ANY_THROW(str.asInt());
EXPECT_ANY_THROW(str.asBool());
str = "12";
EXPECT_EQ(str.asInt(), 12);
EXPECT_EQ(str.asDouble(), 12.0);
str = "0";
EXPECT_EQ(str.asBool(), false);
EXPECT_EQ(str.asInt(), 0);
EXPECT_EQ(str.asDouble(), 0);
EXPECT_EQ(str.asString(), "0");
dynamic num = 12;
EXPECT_EQ("12", num.asString());
EXPECT_EQ(12.0, num.asDouble());
}
TEST(Dynamic, GetSetDefaultTest) {
dynamic d1 = dynamic::object("foo", "bar");
EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
dynamic d2 = dynamic::object("foo", "bar");
EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
d2.setDefault("bar", dynamic({})).push_back(42);
EXPECT_EQ(d2["bar"][0], 42);
dynamic d3 = dynamic::object, empty = dynamic::object;
EXPECT_EQ(d3.getDefault("foo"), empty);
d3.setDefault("foo")["bar"] = "baz";
EXPECT_EQ(d3["foo"]["bar"], "baz");
// we do not allow getDefault/setDefault on arrays
dynamic d4 = dynamic({});
EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
}
TEST(Dynamic, ObjectForwarding) {
// Make sure dynamic::object can be constructed the same way as any
// dynamic.
dynamic d = dynamic::object("asd", {"foo", "bar"});
dynamic d2 = dynamic::object("key2", {"value", "words"})
("key", "value1");
}
TEST(Dynamic, GetPtr) {
dynamic array = { 1, 2, "three" };
EXPECT_TRUE(array.get_ptr(0));
EXPECT_FALSE(array.get_ptr(-1));
EXPECT_FALSE(array.get_ptr(3));
EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
const dynamic& carray = array;
EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
dynamic object = dynamic::object("one", 1)("two", 2);
EXPECT_TRUE(object.get_ptr("one"));
EXPECT_FALSE(object.get_ptr("three"));
EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
*object.get_ptr("one") = 11;
EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
const dynamic& cobject = object;
EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
}
TEST(Dynamic, Assignment) {
const dynamic ds[] = { { 1, 2, 3 },
dynamic::object("a", true),
24,
26.5,
true,
"hello", };
const dynamic dd[] = { { 5, 6 },
dynamic::object("t", "T")(1, 7),
9000,
3.14159,
false,
"world", };
for (const auto& source : ds) {
for (const auto& dest : dd) {
dynamic tmp(dest);
EXPECT_EQ(tmp, dest);
tmp = source;
EXPECT_EQ(tmp, source);
}
}
}
folly::fbstring make_long_string() {
return folly::fbstring(100, 'a');
}
TEST(Dynamic, GetDefault) {
const auto s = make_long_string();
dynamic ds(s);
dynamic tmp(s);
dynamic d1 = dynamic::object("key1", s);
dynamic d2 = dynamic::object("key2", s);
dynamic d3 = dynamic::object("key3", s);
dynamic d4 = dynamic::object("key4", s);
// lvalue - lvalue
dynamic ayy("ayy");
EXPECT_EQ(ds, d1.getDefault("key1", ayy));
EXPECT_EQ(ds, d1.getDefault("key1", ayy));
EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
EXPECT_EQ(ds, tmp);
// lvalue - rvalue
EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
EXPECT_NE(ds, tmp);
// rvalue - lvalue
tmp = s;
EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
EXPECT_NE(ds, d1["key1"]);
EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
EXPECT_EQ(ds, tmp);
// rvalue - rvalue
EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
EXPECT_NE(ds, d3["key3"]);
EXPECT_EQ(ds, tmp);
EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
EXPECT_NE(ds, tmp);
}
TEST(Dynamic, GetString) {
const dynamic c(make_long_string());
dynamic d(make_long_string());
dynamic m(make_long_string());
auto s = make_long_string();
EXPECT_EQ(s, c.getString());
EXPECT_EQ(s, c.getString());
d.getString() += " hello";
EXPECT_EQ(s + " hello", d.getString());
EXPECT_EQ(s + " hello", d.getString());
EXPECT_EQ(s, std::move(m).getString());
EXPECT_NE(dynamic(s), m);
}
TEST(Dynamic, GetSmallThings) {
const dynamic cint(5);
const dynamic cdouble(5.0);
const dynamic cbool(true);
dynamic dint(5);
dynamic ddouble(5.0);
dynamic dbool(true);
dynamic mint(5);
dynamic mdouble(5.0);
dynamic mbool(true);
EXPECT_EQ(5, cint.getInt());
dint.getInt() = 6;
EXPECT_EQ(6, dint.getInt());
EXPECT_EQ(5, std::move(mint).getInt());
EXPECT_EQ(5.0, cdouble.getDouble());
ddouble.getDouble() = 6.0;
EXPECT_EQ(6.0, ddouble.getDouble());
EXPECT_EQ(5.0, std::move(mdouble).getDouble());
EXPECT_EQ(true, cbool.getBool());
dbool.getBool() = false;
EXPECT_FALSE(dbool.getBool());
EXPECT_EQ(true, std::move(mbool).getBool());
}
TEST(Dynamic, At) {
const dynamic cd = dynamic::object("key1", make_long_string());
dynamic dd = dynamic::object("key1", make_long_string());
dynamic md = dynamic::object("key1", make_long_string());
dynamic ds(make_long_string());
EXPECT_EQ(ds, cd.at("key1"));
EXPECT_EQ(ds, cd.at("key1"));
dd.at("key1").getString() += " hello";
EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
EXPECT_EQ(ds, std::move(md).at("key1"));
EXPECT_NE(ds, md.at("key1"));
}
TEST(Dynamic, Brackets) {
const dynamic cd = dynamic::object("key1", make_long_string());
dynamic dd = dynamic::object("key1", make_long_string());
dynamic md = dynamic::object("key1", make_long_string());
dynamic ds(make_long_string());
EXPECT_EQ(ds, cd["key1"]);
EXPECT_EQ(ds, cd["key1"]);
dd["key1"].getString() += " hello";
EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
EXPECT_EQ(ds, std::move(md)["key1"]);
EXPECT_NE(ds, md["key1"]);
}
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, true);
return RUN_ALL_TESTS();
}