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