parser_unittest.cc 22 KB

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