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