DynamicConverterTest.cpp 12 KB

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