Fix folly conversions for Clang with GCC5's libstdc++
[folly.git] / folly / test / DynamicConverterTest.cpp
1 /*
2  * Copyright 2016 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 // @author Nicholas Ormrod <njormrod@fb.com>
18
19 #include <folly/DynamicConverter.h>
20
21 #include <algorithm>
22 #include <gtest/gtest.h>
23 #include <map>
24 #include <vector>
25
26 using namespace folly;
27 using namespace folly::dynamicconverter_detail;
28
29 TEST(DynamicConverter, template_metaprogramming) {
30   struct A {};
31
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;
36
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;
41
42   EXPECT_EQ(c1f, false);
43   EXPECT_EQ(c2f, false);
44   EXPECT_EQ(c3f, false);
45   EXPECT_EQ(c4f, false);
46   EXPECT_EQ(c1t, true);
47   EXPECT_EQ(c2t, true);
48   EXPECT_EQ(c3t, true);
49   EXPECT_EQ(c4t, true);
50
51
52   bool m1f = is_map<int>::value;
53   bool m2f = is_map<std::set<int>>::value;
54
55   bool m1t = is_map<std::map<int, int>>::value;
56
57   EXPECT_EQ(m1f, false);
58   EXPECT_EQ(m2f, false);
59   EXPECT_EQ(m1t, true);
60
61
62   bool r1f = is_range<int>::value;
63
64   bool r1t = is_range<std::set<int>>::value;
65   bool r2t = is_range<std::vector<int>>::value;
66
67   EXPECT_EQ(r1f, false);
68   EXPECT_EQ(r1t, true);
69   EXPECT_EQ(r2t, true);
70 }
71
72 TEST(DynamicConverter, arithmetic_types) {
73   dynamic d1 = 12;
74   auto i1 = convertTo<int>(d1);
75   EXPECT_EQ(i1, 12);
76
77   dynamic d2 = 123456789012345;
78   auto i2 = convertTo<int64_t>(d2);
79   EXPECT_EQ(i2, 123456789012345);
80
81   dynamic d4 = 3.141;
82   auto i4 = convertTo<float>(d4);
83   EXPECT_EQ((int)(i4*100), 314);
84
85   dynamic d5 = true;
86   auto i5 = convertTo<bool>(d5);
87   EXPECT_EQ(i5, true);
88
89   dynamic d6 = 15;
90   const auto i6 = convertTo<const int>(d6);
91   EXPECT_EQ(i6, 15);
92
93   dynamic d7 = "87";
94   auto i7 = convertTo<int>(d7);
95   EXPECT_EQ(i7, 87);
96
97   dynamic d8 = "false";
98   auto i8 = convertTo<bool>(d8);
99   EXPECT_EQ(i8, false);
100 }
101
102 TEST(DynamicConverter, enums) {
103   enum enum1 { foo = 1, bar = 2 };
104
105   dynamic d1 = 1;
106   auto i1 = convertTo<enum1>(d1);
107   EXPECT_EQ(i1, foo);
108
109   dynamic d2 = 2;
110   auto i2 = convertTo<enum1>(d2);
111   EXPECT_EQ(i2, bar);
112
113   enum class enum2 { FOO = 1, BAR = 2 };
114
115   dynamic d3 = 1;
116   auto i3 = convertTo<enum2>(d3);
117   EXPECT_EQ(i3, enum2::FOO);
118
119   dynamic d4 = 2;
120   auto i4 = convertTo<enum2>(d4);
121   EXPECT_EQ(i4, enum2::BAR);
122 }
123
124 TEST(DynamicConverter, simple_builtins) {
125   dynamic d1 = "Haskell";
126   auto i1 = convertTo<folly::fbstring>(d1);
127   EXPECT_EQ(i1, "Haskell");
128
129   dynamic d2 = 13;
130   auto i2 = convertTo<std::string>(d2);
131   EXPECT_EQ(i2, "13");
132
133   dynamic d3 = dynamic::array(12, "Scala");
134   auto i3 = convertTo<std::pair<int, std::string>>(d3);
135   EXPECT_EQ(i3.first, 12);
136   EXPECT_EQ(i3.second, "Scala");
137
138   dynamic d4 = dynamic::object("C", "C++");
139   auto i4 = convertTo<std::pair<std::string, folly::fbstring>>(d4);
140   EXPECT_EQ(i4.first, "C");
141   EXPECT_EQ(i4.second, "C++");
142 }
143
144 TEST(DynamicConverter, simple_fbvector) {
145   dynamic d1 = dynamic::array(1, 2, 3);
146   auto i1 = convertTo<folly::fbvector<int>>(d1);
147   decltype(i1) i1b = { 1, 2, 3 };
148   EXPECT_EQ(i1, i1b);
149 }
150
151 TEST(DynamicConverter, simple_container) {
152   dynamic d1 = dynamic::array(1, 2, 3);
153   auto i1 = convertTo<std::vector<int>>(d1);
154   decltype(i1) i1b = { 1, 2, 3 };
155   EXPECT_EQ(i1, i1b);
156
157   dynamic d2 = dynamic::array(1, 3, 5, 2, 4);
158   auto i2 = convertTo<std::set<int>>(d2);
159   decltype(i2) i2b = { 1, 2, 3, 5, 4 };
160   EXPECT_EQ(i2, i2b);
161 }
162
163 TEST(DynamicConverter, simple_map) {
164   dynamic d1 = dynamic::object(1, "one")(2, "two");
165   auto i1 = convertTo<std::map<int, std::string>>(d1);
166   decltype(i1) i1b = { { 1, "one" }, { 2, "two" } };
167   EXPECT_EQ(i1, i1b);
168
169   dynamic d2 = dynamic::array(dynamic::array(3, "three"),
170                               dynamic::array(4, "four"));
171   auto i2 = convertTo<std::unordered_map<int, std::string>>(d2);
172   decltype(i2) i2b = { { 3, "three" }, { 4, "four" } };
173   EXPECT_EQ(i2, i2b);
174 }
175
176 TEST(DynamicConverter, map_keyed_by_string) {
177   dynamic d1 = dynamic::object("1", "one")("2", "two");
178   auto i1 = convertTo<std::map<std::string, std::string>>(d1);
179   decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
180   EXPECT_EQ(i1, i1b);
181
182   dynamic d2 = dynamic::array(dynamic::array("3", "three"),
183                               dynamic::array("4", "four"));
184   auto i2 = convertTo<std::unordered_map<std::string, std::string>>(d2);
185   decltype(i2) i2b = { { "3", "three" }, { "4", "four" } };
186   EXPECT_EQ(i2, i2b);
187 }
188
189 TEST(DynamicConverter, map_to_vector_of_pairs) {
190   dynamic d1 = dynamic::object("1", "one")("2", "two");
191   auto i1 = convertTo<std::vector<std::pair<std::string, std::string>>>(d1);
192   std::sort(i1.begin(), i1.end());
193   decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
194   EXPECT_EQ(i1, i1b);
195 }
196
197 TEST(DynamicConverter, nested_containers) {
198   dynamic d1 = dynamic::array(dynamic::array(1),
199                               dynamic::array(),
200                               dynamic::array(2, 3));
201   auto i1 = convertTo<folly::fbvector<std::vector<uint8_t>>>(d1);
202   decltype(i1) i1b = { { 1 }, { }, { 2, 3 } };
203   EXPECT_EQ(i1, i1b);
204
205   dynamic h2a = dynamic::array("3", ".", "1", "4");
206   dynamic h2b = dynamic::array("2", ".", "7", "2");
207   dynamic d2 = dynamic::object(3.14, h2a)(2.72, h2b);
208   auto i2 = convertTo<std::map<double, std::vector<folly::fbstring>>>(d2);
209   decltype(i2) i2b =
210     { { 3.14, { "3", ".", "1", "4" } },
211       { 2.72, { "2", ".", "7", "2" } } };
212   EXPECT_EQ(i2, i2b);
213 }
214
215 struct A {
216   int i;
217   bool operator==(const A & o) const { return i == o.i; }
218 };
219 namespace folly {
220 template <> struct DynamicConverter<A> {
221   static A convert(const dynamic & d) {
222     return { convertTo<int>(d["i"]) };
223   }
224 };
225 }
226 TEST(DynamicConverter, custom_class) {
227   dynamic d1 = dynamic::object("i", 17);
228   auto i1 = convertTo<A>(d1);
229   EXPECT_EQ(i1.i, 17);
230
231   dynamic d2 = dynamic::array(dynamic::object("i", 18),
232                               dynamic::object("i", 19));
233   auto i2 = convertTo<std::vector<A>>(d2);
234   decltype(i2) i2b = { { 18 }, { 19 } };
235   EXPECT_EQ(i2, i2b);
236 }
237
238 TEST(DynamicConverter, crazy) {
239   // we are going to create a vector<unordered_map<bool, T>>
240   // we will construct some of the maps from dynamic objects,
241   //   some from a vector of KV pairs.
242   // T will be vector<set<string>>
243
244   std::set<std::string>
245     s1 = { "a", "e", "i", "o", "u" },
246     s2 = { "2", "3", "5", "7" },
247     s3 = { "Hello", "World" };
248
249   std::vector<std::set<std::string>>
250     v1 = {},
251     v2 = { s1, s2 },
252     v3 = { s3 };
253
254   std::unordered_map<bool, std::vector<std::set<std::string>>>
255     m1 = { { true, v1 }, { false, v2 } },
256     m2 = { { true, v3 } };
257
258   std::vector<std::unordered_map<bool, std::vector<std::set<std::string>>>>
259     f1 = { m1, m2 };
260
261
262   dynamic
263     ds1 = dynamic::array("a", "e", "i", "o", "u"),
264     ds2 = dynamic::array("2", "3", "5", "7"),
265     ds3 = dynamic::array("Hello", "World");
266
267   dynamic
268     dv1 = dynamic::array,
269     dv2 = dynamic::array(ds1, ds2),
270     dv3(dynamic::array(ds3));
271
272   dynamic
273     dm1 = dynamic::object(true, dv1)(false, dv2),
274     dm2 = dynamic::array(dynamic::array(true, dv3));
275
276   dynamic
277     df1 = dynamic::array(dm1, dm2);
278
279
280   auto i = convertTo<std::vector<std::unordered_map<bool, std::vector<
281           std::set<std::string>>>>>(df1); // yes, that is 5 close-chevrons
282
283   EXPECT_EQ(f1, i);
284 }
285
286 TEST(DynamicConverter, consts) {
287   dynamic d1 = 7.5;
288   auto i1 = convertTo<const double>(d1);
289   EXPECT_EQ(7.5, i1);
290
291   dynamic d2 = "Hello";
292   auto i2 = convertTo<const std::string>(d2);
293   decltype(i2) i2b = "Hello";
294   EXPECT_EQ(i2b, i2);
295
296   dynamic d3 = true;
297   auto i3 = convertTo<const bool>(d3);
298   EXPECT_EQ(true, i3);
299
300   dynamic d4 = "true";
301   auto i4 = convertTo<const bool>(d4);
302   EXPECT_EQ(true, i4);
303
304   dynamic d5 = dynamic::array(1, 2);
305   auto i5 = convertTo<const std::pair<const int, const int>>(d5);
306   decltype(i5) i5b = { 1, 2 };
307   EXPECT_EQ(i5b, i5);
308 }
309
310 struct Token {
311   int kind_;
312   fbstring lexeme_;
313
314   explicit Token(int kind, const fbstring& lexeme)
315     : kind_(kind), lexeme_(lexeme) {}
316 };
317
318 namespace folly {
319 template <> struct DynamicConverter<Token> {
320   static Token convert(const dynamic& d) {
321     int k = convertTo<int>(d["KIND"]);
322     fbstring lex = convertTo<fbstring>(d["LEXEME"]);
323     return Token(k, lex);
324   }
325 };
326 }
327
328 TEST(DynamicConverter, example) {
329   dynamic d1 = dynamic::object("KIND", 2)("LEXEME", "a token");
330   auto i1 = convertTo<Token>(d1);
331   EXPECT_EQ(i1.kind_, 2);
332   EXPECT_EQ(i1.lexeme_, "a token");
333 }
334
335 TEST(DynamicConverter, construct) {
336   using std::vector;
337   using std::map;
338   using std::pair;
339   using std::string;
340   {
341     vector<int> c { 1, 2, 3 };
342     dynamic d = dynamic::array(1, 2, 3);
343     EXPECT_EQ(d, toDynamic(c));
344   }
345
346   {
347     vector<float> c{1.0f, 2.0f, 4.0f};
348     dynamic d = dynamic::array(1.0, 2.0, 4.0);
349     EXPECT_EQ(d, toDynamic(c));
350   }
351
352   {
353     map<int, int> c { { 2, 4 }, { 3, 9 } };
354     dynamic d = dynamic::object(2, 4)(3, 9);
355     EXPECT_EQ(d, toDynamic(c));
356   }
357
358   {
359     map<string, string> c { { "a", "b" } };
360     dynamic d = dynamic::object("a", "b");
361     EXPECT_EQ(d, toDynamic(c));
362   }
363
364   {
365     map<string, pair<string, int>> c { { "a", { "b", 3 } } };
366     dynamic d = dynamic::object("a", dynamic::array("b", 3));
367     EXPECT_EQ(d, toDynamic(c));
368   }
369
370   {
371     map<string, pair<string, int>> c { { "a", { "b", 3 } } };
372     dynamic d = dynamic::object("a", dynamic::array("b", 3));
373     EXPECT_EQ(d, toDynamic(c));
374   }
375
376   {
377     vector<int> vi { 2, 3, 4, 5 };
378     auto c = std::make_pair(range(vi.begin(), vi.begin() + 3),
379                             range(vi.begin() + 1, vi.begin() + 4));
380     dynamic d = dynamic::array(dynamic::array(2, 3, 4),
381                                dynamic::array(3, 4, 5));
382     EXPECT_EQ(d, toDynamic(c));
383   }
384 }
385
386 TEST(DynamicConverter, errors) {
387   const auto int32Over =
388     static_cast<int64_t>(std::numeric_limits<int32_t>().max()) + 1;
389   const auto floatOver =
390     static_cast<double>(std::numeric_limits<float>().max()) * 2;
391
392   dynamic d1 = int32Over;
393   EXPECT_THROW(convertTo<int32_t>(d1), std::range_error);
394
395   dynamic d2 = floatOver;
396   EXPECT_THROW(convertTo<float>(d2), std::range_error);
397 }