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