2 * Copyright 2017 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>
21 #include <folly/portability/GTest.h>
27 using namespace folly;
28 using namespace folly::dynamicconverter_detail;
30 TEST(DynamicConverter, template_metaprogramming) {
33 bool c1f = is_container<int>::value;
34 bool c2f = is_container<std::pair<int, int>>::value;
35 bool c3f = is_container<A>::value;
36 bool c4f = class_is_container<A>::value;
38 bool c1t = is_container<std::vector<int>>::value;
39 bool c2t = is_container<std::set<int>>::value;
40 bool c3t = is_container<std::map<int, int>>::value;
41 bool c4t = class_is_container<std::vector<A>>::value;
43 EXPECT_EQ(c1f, false);
44 EXPECT_EQ(c2f, false);
45 EXPECT_EQ(c3f, false);
46 EXPECT_EQ(c4f, false);
53 bool m1f = is_map<int>::value;
54 bool m2f = is_map<std::set<int>>::value;
56 bool m1t = is_map<std::map<int, int>>::value;
58 EXPECT_EQ(m1f, false);
59 EXPECT_EQ(m2f, false);
63 bool r1f = is_range<int>::value;
65 bool r1t = is_range<std::set<int>>::value;
66 bool r2t = is_range<std::vector<int>>::value;
68 EXPECT_EQ(r1f, false);
73 TEST(DynamicConverter, arithmetic_types) {
75 auto i1 = convertTo<int>(d1);
78 dynamic d2 = 123456789012345;
79 auto i2 = convertTo<int64_t>(d2);
80 EXPECT_EQ(i2, 123456789012345);
83 auto i4 = convertTo<float>(d4);
84 EXPECT_EQ((int)(i4*100), 314);
87 auto i5 = convertTo<bool>(d5);
91 const auto i6 = convertTo<const int>(d6);
95 auto i7 = convertTo<int>(d7);
99 auto i8 = convertTo<bool>(d8);
100 EXPECT_EQ(i8, false);
103 TEST(DynamicConverter, enums) {
104 enum enum1 { foo = 1, bar = 2 };
107 auto i1 = convertTo<enum1>(d1);
111 auto i2 = convertTo<enum1>(d2);
114 enum class enum2 { FOO = 1, BAR = 2 };
117 auto i3 = convertTo<enum2>(d3);
118 EXPECT_EQ(i3, enum2::FOO);
121 auto i4 = convertTo<enum2>(d4);
122 EXPECT_EQ(i4, enum2::BAR);
125 TEST(DynamicConverter, simple_builtins) {
126 dynamic d1 = "Haskell";
127 auto i1 = convertTo<folly::fbstring>(d1);
128 EXPECT_EQ(i1, "Haskell");
131 auto i2 = convertTo<std::string>(d2);
134 dynamic d3 = dynamic::array(12, "Scala");
135 auto i3 = convertTo<std::pair<int, std::string>>(d3);
136 EXPECT_EQ(i3.first, 12);
137 EXPECT_EQ(i3.second, "Scala");
139 dynamic d4 = dynamic::object("C", "C++");
140 auto i4 = convertTo<std::pair<std::string, folly::fbstring>>(d4);
141 EXPECT_EQ(i4.first, "C");
142 EXPECT_EQ(i4.second, "C++");
145 TEST(DynamicConverter, simple_fbvector) {
146 dynamic d1 = dynamic::array(1, 2, 3);
147 auto i1 = convertTo<folly::fbvector<int>>(d1);
148 decltype(i1) i1b = { 1, 2, 3 };
152 TEST(DynamicConverter, simple_container) {
153 dynamic d1 = dynamic::array(1, 2, 3);
154 auto i1 = convertTo<std::vector<int>>(d1);
155 decltype(i1) i1b = { 1, 2, 3 };
158 dynamic d2 = dynamic::array(1, 3, 5, 2, 4);
159 auto i2 = convertTo<std::set<int>>(d2);
160 decltype(i2) i2b = { 1, 2, 3, 5, 4 };
164 TEST(DynamicConverter, simple_map) {
165 dynamic d1 = dynamic::object(1, "one")(2, "two");
166 auto i1 = convertTo<std::map<int, std::string>>(d1);
167 decltype(i1) i1b = { { 1, "one" }, { 2, "two" } };
170 dynamic d2 = dynamic::array(dynamic::array(3, "three"),
171 dynamic::array(4, "four"));
172 auto i2 = convertTo<std::unordered_map<int, std::string>>(d2);
173 decltype(i2) i2b = { { 3, "three" }, { 4, "four" } };
177 TEST(DynamicConverter, map_keyed_by_string) {
178 dynamic d1 = dynamic::object("1", "one")("2", "two");
179 auto i1 = convertTo<std::map<std::string, std::string>>(d1);
180 decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
183 dynamic d2 = dynamic::array(dynamic::array("3", "three"),
184 dynamic::array("4", "four"));
185 auto i2 = convertTo<std::unordered_map<std::string, std::string>>(d2);
186 decltype(i2) i2b = { { "3", "three" }, { "4", "four" } };
190 TEST(DynamicConverter, map_to_vector_of_pairs) {
191 dynamic d1 = dynamic::object("1", "one")("2", "two");
192 auto i1 = convertTo<std::vector<std::pair<std::string, std::string>>>(d1);
193 std::sort(i1.begin(), i1.end());
194 decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
198 TEST(DynamicConverter, nested_containers) {
199 dynamic d1 = dynamic::array(dynamic::array(1),
201 dynamic::array(2, 3));
202 auto i1 = convertTo<folly::fbvector<std::vector<uint8_t>>>(d1);
203 decltype(i1) i1b = { { 1 }, { }, { 2, 3 } };
206 dynamic h2a = dynamic::array("3", ".", "1", "4");
207 dynamic h2b = dynamic::array("2", ".", "7", "2");
208 dynamic d2 = dynamic::object(3.14, h2a)(2.72, h2b);
209 auto i2 = convertTo<std::map<double, std::vector<folly::fbstring>>>(d2);
211 { { 3.14, { "3", ".", "1", "4" } },
212 { 2.72, { "2", ".", "7", "2" } } };
218 bool operator==(const A & o) const { return i == o.i; }
221 template <> struct DynamicConverter<A> {
222 static A convert(const dynamic & d) {
223 return { convertTo<int>(d["i"]) };
227 TEST(DynamicConverter, custom_class) {
228 dynamic d1 = dynamic::object("i", 17);
229 auto i1 = convertTo<A>(d1);
232 dynamic d2 = dynamic::array(dynamic::object("i", 18),
233 dynamic::object("i", 19));
234 auto i2 = convertTo<std::vector<A>>(d2);
235 decltype(i2) i2b = { { 18 }, { 19 } };
239 TEST(DynamicConverter, crazy) {
240 // we are going to create a vector<unordered_map<bool, T>>
241 // we will construct some of the maps from dynamic objects,
242 // some from a vector of KV pairs.
243 // T will be vector<set<string>>
245 std::set<std::string>
246 s1 = { "a", "e", "i", "o", "u" },
247 s2 = { "2", "3", "5", "7" },
248 s3 = { "Hello", "World" };
250 std::vector<std::set<std::string>>
255 std::unordered_map<bool, std::vector<std::set<std::string>>>
256 m1 = { { true, v1 }, { false, v2 } },
257 m2 = { { true, v3 } };
259 std::vector<std::unordered_map<bool, std::vector<std::set<std::string>>>>
264 ds1 = dynamic::array("a", "e", "i", "o", "u"),
265 ds2 = dynamic::array("2", "3", "5", "7"),
266 ds3 = dynamic::array("Hello", "World");
269 dv1 = dynamic::array,
270 dv2 = dynamic::array(ds1, ds2),
271 dv3(dynamic::array(ds3));
274 dm1 = dynamic::object(true, dv1)(false, dv2),
275 dm2 = dynamic::array(dynamic::array(true, dv3));
278 df1 = dynamic::array(dm1, dm2);
281 auto i = convertTo<std::vector<std::unordered_map<bool, std::vector<
282 std::set<std::string>>>>>(df1); // yes, that is 5 close-chevrons
287 TEST(DynamicConverter, consts) {
289 auto i1 = convertTo<const double>(d1);
292 dynamic d2 = "Hello";
293 auto i2 = convertTo<const std::string>(d2);
294 decltype(i2) i2b = "Hello";
298 auto i3 = convertTo<const bool>(d3);
302 auto i4 = convertTo<const bool>(d4);
305 dynamic d5 = dynamic::array(1, 2);
306 auto i5 = convertTo<const std::pair<const int, const int>>(d5);
307 decltype(i5) i5b = { 1, 2 };
315 explicit Token(int kind, const fbstring& lexeme)
316 : kind_(kind), lexeme_(lexeme) {}
320 template <> struct DynamicConverter<Token> {
321 static Token convert(const dynamic& d) {
322 int k = convertTo<int>(d["KIND"]);
323 fbstring lex = convertTo<fbstring>(d["LEXEME"]);
324 return Token(k, lex);
329 TEST(DynamicConverter, example) {
330 dynamic d1 = dynamic::object("KIND", 2)("LEXEME", "a token");
331 auto i1 = convertTo<Token>(d1);
332 EXPECT_EQ(i1.kind_, 2);
333 EXPECT_EQ(i1.lexeme_, "a token");
336 TEST(DynamicConverter, construct) {
342 vector<int> c { 1, 2, 3 };
343 dynamic d = dynamic::array(1, 2, 3);
344 EXPECT_EQ(d, toDynamic(c));
348 vector<float> c{1.0f, 2.0f, 4.0f};
349 dynamic d = dynamic::array(1.0, 2.0, 4.0);
350 EXPECT_EQ(d, toDynamic(c));
354 map<int, int> c { { 2, 4 }, { 3, 9 } };
355 dynamic d = dynamic::object(2, 4)(3, 9);
356 EXPECT_EQ(d, toDynamic(c));
360 map<string, string> c { { "a", "b" } };
361 dynamic d = dynamic::object("a", "b");
362 EXPECT_EQ(d, toDynamic(c));
366 map<string, pair<string, int>> c { { "a", { "b", 3 } } };
367 dynamic d = dynamic::object("a", dynamic::array("b", 3));
368 EXPECT_EQ(d, toDynamic(c));
372 map<string, pair<string, int>> c { { "a", { "b", 3 } } };
373 dynamic d = dynamic::object("a", dynamic::array("b", 3));
374 EXPECT_EQ(d, toDynamic(c));
378 vector<int> vi { 2, 3, 4, 5 };
379 auto c = std::make_pair(range(vi.begin(), vi.begin() + 3),
380 range(vi.begin() + 1, vi.begin() + 4));
381 dynamic d = dynamic::array(dynamic::array(2, 3, 4),
382 dynamic::array(3, 4, 5));
383 EXPECT_EQ(d, toDynamic(c));
387 vector<bool> vb{true, false};
388 dynamic d = dynamic::array(true, false);
389 EXPECT_EQ(d, toDynamic(vb));
393 TEST(DynamicConverter, errors) {
394 const auto int32Over =
395 static_cast<int64_t>(std::numeric_limits<int32_t>().max()) + 1;
396 const auto floatOver =
397 static_cast<double>(std::numeric_limits<float>().max()) * 2;
399 dynamic d1 = int32Over;
400 EXPECT_THROW(convertTo<int32_t>(d1), std::range_error);
402 dynamic d2 = floatOver;
403 EXPECT_THROW(convertTo<float>(d2), std::range_error);
406 TEST(DynamicConverter, partial_dynamics) {
407 std::vector<dynamic> c{
408 dynamic::array(2, 3, 4), dynamic::array(3, 4, 5),
410 dynamic d = dynamic::array(dynamic::array(2, 3, 4), dynamic::array(3, 4, 5));
411 EXPECT_EQ(d, toDynamic(c));
413 std::unordered_map<std::string, dynamic> m{{"one", 1}, {"two", 2}};
414 dynamic md = dynamic::object("one", 1)("two", 2);
415 EXPECT_EQ(md, toDynamic(m));
418 TEST(DynamicConverter, asan_exception_case_umap) {
420 (convertTo<std::unordered_map<int, int>>(dynamic::array(1))), TypeError);
423 TEST(DynamicConverter, asan_exception_case_uset) {
425 (convertTo<std::unordered_set<int>>(
426 dynamic::array(1, dynamic::array(), 3))),
430 static int constructB = 0;
431 static int destroyB = 0;
432 static int ticker = 0;
434 struct BException : std::exception {};
436 /* implicit */ B(int x) : x_(x) {
442 B(const B& o) : x_(o.x_) {
452 struct DynamicConverter<B> {
453 static B convert(const dynamic& d) {
454 return B(convertTo<int>(d));
459 TEST(DynamicConverter, double_destroy) {
460 dynamic d = dynamic::array(1, 3, 5, 7, 9, 11, 13, 15, 17);
463 EXPECT_THROW(convertTo<std::vector<B>>(d), B::BException);
464 EXPECT_EQ(constructB, destroyB);