2 * Copyright 2016 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 // @author Nicholas Ormrod <njormrod@fb.com>
19 #include <folly/DynamicConverter.h>
22 #include <gtest/gtest.h>
26 using namespace folly;
27 using namespace folly::dynamicconverter_detail;
29 TEST(DynamicConverter, template_metaprogramming) {
32 bool c1f = is_container<int>::value;
33 bool c2f = is_container<std::pair<int, int>>::value;
34 bool c3f = is_container<A>::value;
35 bool c4f = class_is_container<A>::value;
37 bool c1t = is_container<std::vector<int>>::value;
38 bool c2t = is_container<std::set<int>>::value;
39 bool c3t = is_container<std::map<int, int>>::value;
40 bool c4t = class_is_container<std::vector<A>>::value;
42 EXPECT_EQ(c1f, false);
43 EXPECT_EQ(c2f, false);
44 EXPECT_EQ(c3f, false);
45 EXPECT_EQ(c4f, false);
52 bool m1f = is_map<int>::value;
53 bool m2f = is_map<std::set<int>>::value;
55 bool m1t = is_map<std::map<int, int>>::value;
57 EXPECT_EQ(m1f, false);
58 EXPECT_EQ(m2f, false);
62 bool r1f = is_range<int>::value;
64 bool r1t = is_range<std::set<int>>::value;
65 bool r2t = is_range<std::vector<int>>::value;
67 EXPECT_EQ(r1f, false);
72 TEST(DynamicConverter, arithmetic_types) {
74 auto i1 = convertTo<int>(d1);
77 dynamic d2 = 123456789012345;
78 auto i2 = convertTo<int64_t>(d2);
79 EXPECT_EQ(i2, 123456789012345);
82 auto i4 = convertTo<float>(d4);
83 EXPECT_EQ((int)(i4*100), 314);
86 auto i5 = convertTo<bool>(d5);
90 const auto i6 = convertTo<const int>(d6);
94 auto i7 = convertTo<int>(d7);
98 auto i8 = convertTo<bool>(d8);
102 TEST(DynamicConverter, simple_builtins) {
103 dynamic d1 = "Haskell";
104 auto i1 = convertTo<folly::fbstring>(d1);
105 EXPECT_EQ(i1, "Haskell");
108 auto i2 = convertTo<std::string>(d2);
111 dynamic d3 = { 12, "Scala" };
112 auto i3 = convertTo<std::pair<int, std::string>>(d3);
113 EXPECT_EQ(i3.first, 12);
114 EXPECT_EQ(i3.second, "Scala");
116 dynamic d4 = dynamic::object("C", "C++");
117 auto i4 = convertTo<std::pair<std::string, folly::fbstring>>(d4);
118 EXPECT_EQ(i4.first, "C");
119 EXPECT_EQ(i4.second, "C++");
122 TEST(DynamicConverter, simple_fbvector) {
123 dynamic d1 = { 1, 2, 3 };
124 auto i1 = convertTo<folly::fbvector<int>>(d1);
125 decltype(i1) i1b = { 1, 2, 3 };
129 TEST(DynamicConverter, simple_container) {
130 dynamic d1 = { 1, 2, 3 };
131 auto i1 = convertTo<std::vector<int>>(d1);
132 decltype(i1) i1b = { 1, 2, 3 };
135 dynamic d2 = { 1, 3, 5, 2, 4 };
136 auto i2 = convertTo<std::set<int>>(d2);
137 decltype(i2) i2b = { 1, 2, 3, 5, 4 };
141 TEST(DynamicConverter, simple_map) {
142 dynamic d1 = dynamic::object(1, "one")(2, "two");
143 auto i1 = convertTo<std::map<int, std::string>>(d1);
144 decltype(i1) i1b = { { 1, "one" }, { 2, "two" } };
147 dynamic d2 = { { 3, "three" }, { 4, "four" } };
148 auto i2 = convertTo<std::unordered_map<int, std::string>>(d2);
149 decltype(i2) i2b = { { 3, "three" }, { 4, "four" } };
153 TEST(DynamicConverter, map_keyed_by_string) {
154 dynamic d1 = dynamic::object("1", "one")("2", "two");
155 auto i1 = convertTo<std::map<std::string, std::string>>(d1);
156 decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
159 dynamic d2 = { { "3", "three" }, { "4", "four" } };
160 auto i2 = convertTo<std::unordered_map<std::string, std::string>>(d2);
161 decltype(i2) i2b = { { "3", "three" }, { "4", "four" } };
165 TEST(DynamicConverter, map_to_vector_of_pairs) {
166 dynamic d1 = dynamic::object("1", "one")("2", "two");
167 auto i1 = convertTo<std::vector<std::pair<std::string, std::string>>>(d1);
168 std::sort(i1.begin(), i1.end());
169 decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
173 TEST(DynamicConverter, nested_containers) {
174 dynamic d1 = { { 1 }, { }, { 2, 3 } };
175 auto i1 = convertTo<folly::fbvector<std::vector<uint8_t>>>(d1);
176 decltype(i1) i1b = { { 1 }, { }, { 2, 3 } };
179 dynamic h2a = { "3", ".", "1", "4" };
180 dynamic h2b = { "2", ".", "7", "2" };
181 dynamic d2 = dynamic::object(3.14, h2a)(2.72, h2b);
182 auto i2 = convertTo<std::map<double, std::vector<folly::fbstring>>>(d2);
184 { { 3.14, { "3", ".", "1", "4" } },
185 { 2.72, { "2", ".", "7", "2" } } };
191 bool operator==(const A & o) const { return i == o.i; }
194 template <> struct DynamicConverter<A> {
195 static A convert(const dynamic & d) {
196 return { convertTo<int>(d["i"]) };
200 TEST(DynamicConverter, custom_class) {
201 dynamic d1 = dynamic::object("i", 17);
202 auto i1 = convertTo<A>(d1);
205 dynamic d2 = { dynamic::object("i", 18), dynamic::object("i", 19) };
206 auto i2 = convertTo<std::vector<A>>(d2);
207 decltype(i2) i2b = { { 18 }, { 19 } };
211 TEST(DynamicConverter, crazy) {
212 // we are going to create a vector<unordered_map<bool, T>>
213 // we will construct some of the maps from dynamic objects,
214 // some from a vector of KV pairs.
215 // T will be vector<set<string>>
217 std::set<std::string>
218 s1 = { "a", "e", "i", "o", "u" },
219 s2 = { "2", "3", "5", "7" },
220 s3 = { "Hello", "World" };
222 std::vector<std::set<std::string>>
227 std::unordered_map<bool, std::vector<std::set<std::string>>>
228 m1 = { { true, v1 }, { false, v2 } },
229 m2 = { { true, v3 } };
231 std::vector<std::unordered_map<bool, std::vector<std::set<std::string>>>>
236 ds1 = { "a", "e", "i", "o", "u" },
237 ds2 = { "2", "3", "5", "7" },
238 ds3 = { "Hello", "World" };
246 dm1 = dynamic::object(true, dv1)(false, dv2),
247 dm2 = { { true, dv3 } };
253 auto i = convertTo<std::vector<std::unordered_map<bool, std::vector<
254 std::set<std::string>>>>>(df1); // yes, that is 5 close-chevrons
259 TEST(DynamicConverter, consts) {
261 auto i1 = convertTo<const double>(d1);
264 dynamic d2 = "Hello";
265 auto i2 = convertTo<const std::string>(d2);
266 decltype(i2) i2b = "Hello";
270 auto i3 = convertTo<const bool>(d3);
274 auto i4 = convertTo<const bool>(d4);
277 dynamic d5 = { 1, 2 };
278 auto i5 = convertTo<const std::pair<const int, const int>>(d5);
279 decltype(i5) i5b = { 1, 2 };
287 explicit Token(int kind, const fbstring& lexeme)
288 : kind_(kind), lexeme_(lexeme) {}
292 template <> struct DynamicConverter<Token> {
293 static Token convert(const dynamic& d) {
294 int k = convertTo<int>(d["KIND"]);
295 fbstring lex = convertTo<fbstring>(d["LEXEME"]);
296 return Token(k, lex);
301 TEST(DynamicConverter, example) {
302 dynamic d1 = dynamic::object("KIND", 2)("LEXEME", "a token");
303 auto i1 = convertTo<Token>(d1);
304 EXPECT_EQ(i1.kind_, 2);
305 EXPECT_EQ(i1.lexeme_, "a token");
308 TEST(DynamicConverter, construct) {
314 vector<int> c { 1, 2, 3 };
315 dynamic d = { 1, 2, 3 };
316 EXPECT_EQ(d, toDynamic(c));
320 map<int, int> c { { 2, 4 }, { 3, 9 } };
321 dynamic d = dynamic::object(2, 4)(3, 9);
322 EXPECT_EQ(d, toDynamic(c));
326 map<string, string> c { { "a", "b" } };
327 dynamic d = dynamic::object("a", "b");
328 EXPECT_EQ(d, toDynamic(c));
332 map<string, pair<string, int>> c { { "a", { "b", 3 } } };
333 dynamic d = dynamic::object("a", dynamic { "b", 3 });
334 EXPECT_EQ(d, toDynamic(c));
338 map<string, pair<string, int>> c { { "a", { "b", 3 } } };
339 dynamic d = dynamic::object("a", dynamic { "b", 3 });
340 EXPECT_EQ(d, toDynamic(c));
344 vector<int> vi { 2, 3, 4, 5 };
345 auto c = std::make_pair(range(vi.begin(), vi.begin() + 3),
346 range(vi.begin() + 1, vi.begin() + 4));
347 dynamic d = { { 2, 3, 4 }, { 3, 4, 5 } };
348 EXPECT_EQ(d, toDynamic(c));
352 TEST(DynamicConverter, errors) {
353 const auto int32Over =
354 static_cast<int64_t>(std::numeric_limits<int32_t>().max()) + 1;
355 const auto floatOver =
356 static_cast<double>(std::numeric_limits<float>().max()) * 2;
358 dynamic d1 = int32Over;
359 EXPECT_THROW(convertTo<int32_t>(d1), std::range_error);
361 dynamic d2 = floatOver;
362 EXPECT_THROW(convertTo<float>(d2), std::range_error);