parser_unittest.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. // Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <gtest/gtest.h>
  7. #include <cc/data.h>
  8. #include <dhcp6/parser_context.h>
  9. #include <testutils/io_utils.h>
  10. using namespace isc::data;
  11. using namespace std;
  12. using namespace isc::test;
  13. namespace isc {
  14. namespace dhcp {
  15. namespace test {
  16. /// @brief compares two JSON trees
  17. ///
  18. /// If differences are discovered, gtest failure is reported (using EXPECT_EQ)
  19. ///
  20. /// @param a first to be compared
  21. /// @param b second to be compared
  22. void compareJSON(ConstElementPtr a, ConstElementPtr b) {
  23. ASSERT_TRUE(a);
  24. ASSERT_TRUE(b);
  25. EXPECT_EQ(a->str(), b->str());
  26. }
  27. /// @brief Tests if the input string can be parsed with specific parser
  28. ///
  29. /// The input text will be passed to bison parser of specified type.
  30. /// Then the same input text is passed to legacy JSON parser and outputs
  31. /// from both parsers are compared. The legacy comparison can be disabled,
  32. /// if the feature tested is not supported by the old parser (e.g.
  33. /// new comment styles)
  34. ///
  35. /// @param txt text to be compared
  36. /// @param parser_type bison parser type to be instantiated
  37. /// @param compare whether to compare the output with legacy JSON parser
  38. void testParser(const std::string& txt, Parser6Context::ParserType parser_type,
  39. bool compare = true) {
  40. ConstElementPtr test_json;
  41. ASSERT_NO_THROW({
  42. try {
  43. Parser6Context ctx;
  44. test_json = ctx.parseString(txt, parser_type);
  45. } catch (const std::exception &e) {
  46. cout << "EXCEPTION: " << e.what() << endl;
  47. throw;
  48. }
  49. });
  50. if (!compare) {
  51. return;
  52. }
  53. // Now compare if both representations are the same.
  54. ElementPtr reference_json;
  55. ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
  56. compareJSON(reference_json, test_json);
  57. }
  58. TEST(ParserTest, mapInMap) {
  59. string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }";
  60. testParser(txt, Parser6Context::PARSER_JSON);
  61. }
  62. TEST(ParserTest, listInList) {
  63. string txt = "[ [ \"Britain\", \"Wales\", \"Scotland\" ], "
  64. "[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ]";
  65. testParser(txt, Parser6Context::PARSER_JSON);
  66. }
  67. TEST(ParserTest, nestedMaps) {
  68. string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}";
  69. testParser(txt, Parser6Context::PARSER_JSON);
  70. }
  71. TEST(ParserTest, nestedLists) {
  72. string txt = "[ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]]";
  73. testParser(txt, Parser6Context::PARSER_JSON);
  74. }
  75. TEST(ParserTest, listsInMaps) {
  76. string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelgeuse\" ], "
  77. "\"cygnus\": [ \"deneb\", \"albireo\"] } }";
  78. testParser(txt, Parser6Context::PARSER_JSON);
  79. }
  80. TEST(ParserTest, mapsInLists) {
  81. string txt = "[ { \"body\": \"earth\", \"gravity\": 1.0 },"
  82. " { \"body\": \"mars\", \"gravity\": 0.376 } ]";
  83. testParser(txt, Parser6Context::PARSER_JSON);
  84. }
  85. TEST(ParserTest, types) {
  86. string txt = "{ \"string\": \"foo\","
  87. "\"integer\": 42,"
  88. "\"boolean\": true,"
  89. "\"map\": { \"foo\": \"bar\" },"
  90. "\"list\": [ 1, 2, 3 ],"
  91. "\"null\": null }";
  92. testParser(txt, Parser6Context::PARSER_JSON);
  93. }
  94. TEST(ParserTest, keywordJSON) {
  95. string txt = "{ \"name\": \"user\","
  96. "\"type\": \"password\","
  97. "\"user\": \"name\","
  98. "\"password\": \"type\" }";
  99. testParser(txt, Parser6Context::PARSER_JSON);
  100. }
  101. TEST(ParserTest, keywordDhcp6) {
  102. string txt = "{ \"Dhcp6\": { \"interfaces-config\": {"
  103. " \"interfaces\": [ \"type\", \"htype\" ] },\n"
  104. "\"preferred-lifetime\": 3000,\n"
  105. "\"rebind-timer\": 2000, \n"
  106. "\"renew-timer\": 1000, \n"
  107. "\"subnet6\": [ { "
  108. " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
  109. " \"subnet\": \"2001:db8:1::/48\", "
  110. " \"interface\": \"test\" } ],\n"
  111. "\"valid-lifetime\": 4000 } }";
  112. testParser(txt, Parser6Context::PARSER_DHCP6);
  113. }
  114. // Tests if bash (#) comments are supported. That's the only comment type that
  115. // was supported by the old parser.
  116. TEST(ParserTest, bashComments) {
  117. string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
  118. " \"interfaces\": [ \"*\" ]"
  119. "},\n"
  120. "\"preferred-lifetime\": 3000,\n"
  121. "# this is a comment\n"
  122. "\"rebind-timer\": 2000, \n"
  123. "# lots of comments here\n"
  124. "# and here\n"
  125. "\"renew-timer\": 1000, \n"
  126. "\"subnet6\": [ { "
  127. " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
  128. " \"subnet\": \"2001:db8:1::/48\", "
  129. " \"interface\": \"eth0\""
  130. " } ],"
  131. "\"valid-lifetime\": 4000 } }";
  132. testParser(txt, Parser6Context::PARSER_DHCP6);
  133. }
  134. // Tests if C++ (//) comments can start anywhere, not just in the first line.
  135. TEST(ParserTest, cppComments) {
  136. string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
  137. " \"interfaces\": [ \"*\" ]"
  138. "},\n"
  139. "\"preferred-lifetime\": 3000, // this is a comment \n"
  140. "\"rebind-timer\": 2000, // everything after // is ignored\n"
  141. "\"renew-timer\": 1000, // this will be ignored, too\n"
  142. "\"subnet6\": [ { "
  143. " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
  144. " \"subnet\": \"2001:db8:1::/48\", "
  145. " \"interface\": \"eth0\""
  146. " } ],"
  147. "\"valid-lifetime\": 4000 } }";
  148. testParser(txt, Parser6Context::PARSER_DHCP6, false);
  149. }
  150. // Tests if bash (#) comments can start anywhere, not just in the first line.
  151. TEST(ParserTest, bashCommentsInline) {
  152. string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
  153. " \"interfaces\": [ \"*\" ]"
  154. "},\n"
  155. "\"preferred-lifetime\": 3000, # this is a comment \n"
  156. "\"rebind-timer\": 2000, # everything after # is ignored\n"
  157. "\"renew-timer\": 1000, # this will be ignored, too\n"
  158. "\"subnet6\": [ { "
  159. " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
  160. " \"subnet\": \"2001:db8:1::/48\", "
  161. " \"interface\": \"eth0\""
  162. " } ],"
  163. "\"valid-lifetime\": 4000 } }";
  164. testParser(txt, Parser6Context::PARSER_DHCP6, false);
  165. }
  166. // Tests if multi-line C style comments are handled correctly.
  167. TEST(ParserTest, multilineComments) {
  168. string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
  169. " \"interfaces\": [ \"*\" ]"
  170. "},\n"
  171. "\"preferred-lifetime\": 3000, /* this is a C style comment\n"
  172. "that\n can \n span \n multiple \n lines */ \n"
  173. "\"rebind-timer\": 2000,\n"
  174. "\"renew-timer\": 1000, \n"
  175. "\"subnet6\": [ { "
  176. " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
  177. " \"subnet\": \"2001:db8:1::/48\", "
  178. " \"interface\": \"eth0\""
  179. " } ],"
  180. "\"valid-lifetime\": 4000 } }";
  181. testParser(txt, Parser6Context::PARSER_DHCP6, false);
  182. }
  183. /// @brief Loads specified example config file
  184. ///
  185. /// This test loads specified example file twice: first, using the legacy
  186. /// JSON file and then second time using bison parser. Two created Element
  187. /// trees are then compared. The input is decommented before it is passed
  188. /// to legacy parser (as legacy support for comments is very limited).
  189. ///
  190. /// @param fname name of the file to be loaded
  191. void testFile(const std::string& fname) {
  192. ElementPtr reference_json;
  193. ConstElementPtr test_json;
  194. string decommented = decommentJSONfile(fname);
  195. cout << "Parsing file " << fname << "(" << decommented << ")" << endl;
  196. EXPECT_NO_THROW(reference_json = Element::fromJSONFile(decommented, true));
  197. // remove the temporary file
  198. EXPECT_NO_THROW(::remove(decommented.c_str()));
  199. EXPECT_NO_THROW(
  200. try {
  201. Parser6Context ctx;
  202. test_json = ctx.parseFile(fname, Parser6Context::PARSER_DHCP6);
  203. } catch (const std::exception &x) {
  204. cout << "EXCEPTION: " << x.what() << endl;
  205. throw;
  206. });
  207. ASSERT_TRUE(reference_json);
  208. ASSERT_TRUE(test_json);
  209. compareJSON(reference_json, test_json);
  210. }
  211. // This test loads all available existing files. Each config is loaded
  212. // twice: first with the existing Element::fromJSONFile() and then
  213. // the second time with Parser6. Both JSON trees are then compared.
  214. TEST(ParserTest, file) {
  215. vector<string> configs;
  216. configs.push_back("advanced.json");
  217. configs.push_back("backends.json");
  218. configs.push_back("classify.json");
  219. configs.push_back("dhcpv4-over-dhcpv6.json");
  220. configs.push_back("duid.json");
  221. configs.push_back("hooks.json");
  222. configs.push_back("leases-expiration.json");
  223. configs.push_back("multiple-options.json");
  224. configs.push_back("mysql-reservations.json");
  225. configs.push_back("pgsql-reservations.json");
  226. configs.push_back("reservations.json");
  227. configs.push_back("several-subnets.json");
  228. configs.push_back("simple.json");
  229. configs.push_back("stateless.json");
  230. configs.push_back("with-ddns.json");
  231. for (int i = 0; i<configs.size(); i++) {
  232. testFile(string(CFG_EXAMPLES) + "/" + configs[i]);
  233. }
  234. }
  235. /// @brief Tests error conditions in Dhcp6Parser
  236. ///
  237. /// @param txt text to be parsed
  238. /// @param parser_type type of the parser to be used in the test
  239. /// @param msg expected content of the exception
  240. void testError(const std::string& txt,
  241. Parser6Context::ParserType parser_type,
  242. const std::string& msg)
  243. {
  244. try {
  245. Parser6Context ctx;
  246. ConstElementPtr parsed = ctx.parseString(txt, parser_type);
  247. FAIL() << "Expected Dhcp6ParseError but nothing was raised (expected: "
  248. << msg << ")";
  249. }
  250. catch (const Dhcp6ParseError& ex) {
  251. EXPECT_EQ(msg, ex.what());
  252. }
  253. catch (...) {
  254. FAIL() << "Expected Dhcp6ParseError but something else was raised";
  255. }
  256. }
  257. // Verify that error conditions are handled correctly.
  258. TEST(ParserTest, errors) {
  259. // no input
  260. testError("", Parser6Context::PARSER_JSON,
  261. "<string>:1.1: syntax error, unexpected end of file");
  262. testError(" ", Parser6Context::PARSER_JSON,
  263. "<string>:1.2: syntax error, unexpected end of file");
  264. testError("\n", Parser6Context::PARSER_JSON,
  265. "<string>:2.1: syntax error, unexpected end of file");
  266. testError("\t", Parser6Context::PARSER_JSON,
  267. "<string>:1.2: syntax error, unexpected end of file");
  268. testError("\r", Parser6Context::PARSER_JSON,
  269. "<string>:1.2: syntax error, unexpected end of file");
  270. // comments
  271. testError("# nothing\n",
  272. Parser6Context::PARSER_JSON,
  273. "<string>:2.1: syntax error, unexpected end of file");
  274. testError(" #\n",
  275. Parser6Context::PARSER_JSON,
  276. "<string>:2.1: syntax error, unexpected end of file");
  277. testError("// nothing\n",
  278. Parser6Context::PARSER_JSON,
  279. "<string>:2.1: syntax error, unexpected end of file");
  280. testError("/* nothing */\n",
  281. Parser6Context::PARSER_JSON,
  282. "<string>:2.1: syntax error, unexpected end of file");
  283. testError("/* no\nthing */\n",
  284. Parser6Context::PARSER_JSON,
  285. "<string>:3.1: syntax error, unexpected end of file");
  286. testError("/* no\nthing */\n\n",
  287. Parser6Context::PARSER_JSON,
  288. "<string>:4.1: syntax error, unexpected end of file");
  289. testError("/* nothing\n",
  290. Parser6Context::PARSER_JSON,
  291. "Comment not closed. (/* in line 1");
  292. testError("\n\n\n/* nothing\n",
  293. Parser6Context::PARSER_JSON,
  294. "Comment not closed. (/* in line 4");
  295. testError("{ /* */*/ }\n",
  296. Parser6Context::PARSER_JSON,
  297. "<string>:1.3-8: Invalid character: *");
  298. testError("{ /* // *// }\n",
  299. Parser6Context::PARSER_JSON,
  300. "<string>:1.3-11: Invalid character: /");
  301. testError("{ /* // */// }\n",
  302. Parser6Context::PARSER_JSON,
  303. "<string>:2.1: syntax error, unexpected end of file, "
  304. "expecting }");
  305. // includes
  306. testError("<?\n",
  307. Parser6Context::PARSER_JSON,
  308. "Directive not closed.");
  309. testError("<?include\n",
  310. Parser6Context::PARSER_JSON,
  311. "Directive not closed.");
  312. string file = string(CFG_EXAMPLES) + "/" + "stateless.json";
  313. testError("<?include \"" + file + "\"\n",
  314. Parser6Context::PARSER_JSON,
  315. "Directive not closed.");
  316. testError("<?include \"/foo/bar\" ?>/n",
  317. Parser6Context::PARSER_JSON,
  318. "Can't open include file /foo/bar");
  319. // JSON keywords
  320. testError("{ \"foo\": True }",
  321. Parser6Context::PARSER_JSON,
  322. "<string>:1.10-13: JSON true reserved keyword is lower case only");
  323. testError("{ \"foo\": False }",
  324. Parser6Context::PARSER_JSON,
  325. "<string>:1.10-14: JSON false reserved keyword is lower case only");
  326. testError("{ \"foo\": NULL }",
  327. Parser6Context::PARSER_JSON,
  328. "<string>:1.10-13: JSON null reserved keyword is lower case only");
  329. testError("{ \"foo\": Tru }",
  330. Parser6Context::PARSER_JSON,
  331. "<string>:1.10: Invalid character: T");
  332. testError("{ \"foo\": nul }",
  333. Parser6Context::PARSER_JSON,
  334. "<string>:1.10: Invalid character: n");
  335. // numbers
  336. testError("123",
  337. Parser6Context::PARSER_DHCP6,
  338. "<string>:1.1-3: syntax error, unexpected integer, "
  339. "expecting {");
  340. testError("-456",
  341. Parser6Context::PARSER_DHCP6,
  342. "<string>:1.1-4: syntax error, unexpected integer, "
  343. "expecting {");
  344. testError("-0001",
  345. Parser6Context::PARSER_DHCP6,
  346. "<string>:1.1-5: syntax error, unexpected integer, "
  347. "expecting {");
  348. testError("1234567890123456789012345678901234567890",
  349. Parser6Context::PARSER_JSON,
  350. "<string>:1.1-40: Failed to convert "
  351. "1234567890123456789012345678901234567890"
  352. " to an integer.");
  353. testError("-3.14e+0",
  354. Parser6Context::PARSER_DHCP6,
  355. "<string>:1.1-8: syntax error, unexpected floating point, "
  356. "expecting {");
  357. testError("1e50000",
  358. Parser6Context::PARSER_JSON,
  359. "<string>:1.1-7: Failed to convert 1e50000 "
  360. "to a floating point.");
  361. // strings
  362. testError("\"aabb\"",
  363. Parser6Context::PARSER_DHCP6,
  364. "<string>:1.1-6: syntax error, unexpected constant string, "
  365. "expecting {");
  366. testError("{ \"aabb\"err",
  367. Parser6Context::PARSER_JSON,
  368. "<string>:1.9: Invalid character: e");
  369. testError("{ err\"aabb\"",
  370. Parser6Context::PARSER_JSON,
  371. "<string>:1.3: Invalid character: e");
  372. testError("\"a\n\tb\"",
  373. Parser6Context::PARSER_JSON,
  374. "<string>:1.1-6: Invalid control in \"a\n\tb\"");
  375. testError("\"a\\n\\tb\"",
  376. Parser6Context::PARSER_DHCP6,
  377. "<string>:1.1-8: syntax error, unexpected constant string, "
  378. "expecting {");
  379. testError("\"a\\x01b\"",
  380. Parser6Context::PARSER_JSON,
  381. "<string>:1.1-8: Bad escape in \"a\\x01b\"");
  382. testError("\"a\\u0162\"",
  383. Parser6Context::PARSER_JSON,
  384. "<string>:1.1-9: Unsupported unicode escape in \"a\\u0162\"");
  385. testError("\"a\\u062z\"",
  386. Parser6Context::PARSER_JSON,
  387. "<string>:1.1-9: Bad escape in \"a\\u062z\"");
  388. testError("\"abc\\\"",
  389. Parser6Context::PARSER_JSON,
  390. "<string>:1.1-6: Overflow escape in \"abc\\\"");
  391. // from data_unittest.c
  392. testError("\\a",
  393. Parser6Context::PARSER_JSON,
  394. "<string>:1.1: Invalid character: \\");
  395. testError("\\",
  396. Parser6Context::PARSER_JSON,
  397. "<string>:1.1: Invalid character: \\");
  398. testError("\\\"\\\"",
  399. Parser6Context::PARSER_JSON,
  400. "<string>:1.1: Invalid character: \\");
  401. // want a map
  402. testError("[]\n",
  403. Parser6Context::PARSER_DHCP6,
  404. "<string>:1.1: syntax error, unexpected [, "
  405. "expecting {");
  406. testError("[]\n",
  407. Parser6Context::PARSER_DHCP6,
  408. "<string>:1.1: syntax error, unexpected [, "
  409. "expecting {");
  410. testError("{ 123 }\n",
  411. Parser6Context::PARSER_JSON,
  412. "<string>:1.3-5: syntax error, unexpected integer, "
  413. "expecting }");
  414. testError("{ 123 }\n",
  415. Parser6Context::PARSER_DHCP6,
  416. "<string>:1.3-5: syntax error, unexpected integer");
  417. testError("{ \"foo\" }\n",
  418. Parser6Context::PARSER_JSON,
  419. "<string>:1.9: syntax error, unexpected }, "
  420. "expecting :");
  421. testError("{ \"foo\" }\n",
  422. Parser6Context::PARSER_DHCP6,
  423. "<string>:1.9: syntax error, unexpected }, expecting :");
  424. testError("{ \"foo\":null }\n",
  425. Parser6Context::PARSER_DHCP6,
  426. "<string>:1.3-7: got unexpected keyword "
  427. "\"foo\" in toplevel map.");
  428. testError("{ \"Dhcp6\" }\n",
  429. Parser6Context::PARSER_DHCP6,
  430. "<string>:1.11: syntax error, unexpected }, "
  431. "expecting :");
  432. testError("{ \"Dhcp4\":[]\n",
  433. Parser6Context::PARSER_DHCP6,
  434. "<string>:2.1: syntax error, unexpected end of file, "
  435. "expecting \",\" or }");
  436. testError("{}{}\n",
  437. Parser6Context::PARSER_JSON,
  438. "<string>:1.3: syntax error, unexpected {, "
  439. "expecting end of file");
  440. // bad commas
  441. testError("{ , }\n",
  442. Parser6Context::PARSER_JSON,
  443. "<string>:1.3: syntax error, unexpected \",\", "
  444. "expecting }");
  445. testError("{ , \"foo\":true }\n",
  446. Parser6Context::PARSER_JSON,
  447. "<string>:1.3: syntax error, unexpected \",\", "
  448. "expecting }");
  449. testError("{ \"foo\":true, }\n",
  450. Parser6Context::PARSER_JSON,
  451. "<string>:1.15: syntax error, unexpected }, "
  452. "expecting constant string");
  453. // bad type
  454. testError("{ \"Dhcp6\":{\n"
  455. " \"preferred-lifetime\":false }}\n",
  456. Parser6Context::PARSER_DHCP6,
  457. "<string>:2.24-28: syntax error, unexpected boolean, "
  458. "expecting integer");
  459. // unknown keyword
  460. testError("{ \"Dhcp6\":{\n"
  461. " \"preferred_lifetime\":600 }}\n",
  462. Parser6Context::PARSER_DHCP6,
  463. "<string>:2.2-21: got unexpected keyword "
  464. "\"preferred_lifetime\" in Dhcp6 map.");
  465. // missing parameter
  466. testError("{ \"name\": \"foo\",\n"
  467. " \"code\": 123 }\n",
  468. Parser6Context::PARSER_OPTION_DEF,
  469. "missing parameter 'type' (<string>:1:1) "
  470. "[option-def map between <string>:1:1 and <string>:2:15]");
  471. }
  472. // Check unicode escapes
  473. TEST(ParserTest, unicodeEscapes) {
  474. ConstElementPtr result;
  475. string json;
  476. // check we can reread output
  477. for (char c = -128; c < 127; ++c) {
  478. string ins(" ");
  479. ins[1] = c;
  480. ConstElementPtr e(new StringElement(ins));
  481. json = e->str();
  482. ASSERT_NO_THROW(
  483. try {
  484. Parser6Context ctx;
  485. result = ctx.parseString(json, Parser6Context::PARSER_JSON);
  486. } catch (const std::exception &x) {
  487. cout << "EXCEPTION: " << x.what() << endl;
  488. throw;
  489. });
  490. ASSERT_EQ(Element::string, result->getType());
  491. EXPECT_EQ(ins, result->stringValue());
  492. }
  493. }
  494. // This test checks that all representations of a slash is recognized properly.
  495. TEST(ParserTest, unicodeSlash) {
  496. // check the 4 possible encodings of solidus '/'
  497. ConstElementPtr result;
  498. string json = "\"/\\/\\u002f\\u002F\"";
  499. ASSERT_NO_THROW(
  500. try {
  501. Parser6Context ctx;
  502. result = ctx.parseString(json, Parser6Context::PARSER_JSON);
  503. } catch (const std::exception &x) {
  504. cout << "EXCEPTION: " << x.what() << endl;
  505. throw;
  506. });
  507. ASSERT_EQ(Element::string, result->getType());
  508. EXPECT_EQ("////", result->stringValue());
  509. }
  510. };
  511. };
  512. };