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