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