parser_unittest.cc 20.9 KB
Newer Older
1
// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <gtest/gtest.h>
#include <cc/data.h>
#include <dhcp6/parser_context.h>
10
#include <testutils/io_utils.h>
11 12 13

using namespace isc::data;
using namespace std;
14
using namespace isc::test;
15

16 17 18 19 20 21 22 23 24 25 26
namespace isc {
namespace dhcp {
namespace test {

/// @brief compares two JSON trees
///
/// If differences are discovered, gtest failure is reported (using EXPECT_EQ)
///
/// @param a first to be compared
/// @param b second to be compared
void compareJSON(ConstElementPtr a, ConstElementPtr b) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
27 28
    ASSERT_TRUE(a);
    ASSERT_TRUE(b);
29 30 31
    EXPECT_EQ(a->str(), b->str());
}

32 33 34 35 36 37 38 39 40 41 42
/// @brief Tests if the input string can be parsed with specific parser
///
/// The input text will be passed to bison parser of specified type.
/// Then the same input text is passed to legacy JSON parser and outputs
/// from both parsers are compared. The legacy comparison can be disabled,
/// if the feature tested is not supported by the old parser (e.g.
/// new comment styles)
///
/// @param txt text to be compared
/// @param parser_type bison parser type to be instantiated
/// @param compare whether to compare the output with legacy JSON parser
Tomek Mrugalski's avatar
Tomek Mrugalski committed
43 44
void testParser(const std::string& txt, Parser6Context::ParserType parser_type,
    bool compare = true) {
45 46
    ConstElementPtr test_json;

47 48
    ASSERT_NO_THROW({
            try {
49 50
                Parser6Context ctx;
                test_json = ctx.parseString(txt, parser_type);
51 52 53 54 55
            } catch (const std::exception &e) {
                cout << "EXCEPTION: " << e.what() << endl;
                throw;
            }

56 57
    });

Tomek Mrugalski's avatar
Tomek Mrugalski committed
58 59 60 61
    if (!compare) {
        return;
    }

62
    // Now compare if both representations are the same.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
63 64
    ElementPtr reference_json;
    ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
65 66 67 68
    compareJSON(reference_json, test_json);
}

TEST(ParserTest, mapInMap) {
69
    string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }";
70
    testParser(txt, Parser6Context::PARSER_JSON);
71
}
72

73
TEST(ParserTest, listInList) {
74 75
    string txt = "[ [ \"Britain\", \"Wales\", \"Scotland\" ], "
                 "[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ]";
76
    testParser(txt, Parser6Context::PARSER_JSON);
77 78 79 80
}

TEST(ParserTest, nestedMaps) {
    string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}";
81
    testParser(txt, Parser6Context::PARSER_JSON);
82
}
83

84
TEST(ParserTest, nestedLists) {
85
    string txt = "[ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]]";
86
    testParser(txt, Parser6Context::PARSER_JSON);
87
}
88

89
TEST(ParserTest, listsInMaps) {
Francis Dupont's avatar
Francis Dupont committed
90
    string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelgeuse\" ], "
91
                    "\"cygnus\": [ \"deneb\", \"albireo\"] } }";
92
    testParser(txt, Parser6Context::PARSER_JSON);
93 94 95
}

TEST(ParserTest, mapsInLists) {
96 97
    string txt = "[ { \"body\": \"earth\", \"gravity\": 1.0 },"
                 " { \"body\": \"mars\", \"gravity\": 0.376 } ]";
98
    testParser(txt, Parser6Context::PARSER_JSON);
99
}
100

101 102 103 104 105 106 107
TEST(ParserTest, types) {
    string txt = "{ \"string\": \"foo\","
                   "\"integer\": 42,"
                   "\"boolean\": true,"
                   "\"map\": { \"foo\": \"bar\" },"
                   "\"list\": [ 1, 2, 3 ],"
                   "\"null\": null }";
108
    testParser(txt, Parser6Context::PARSER_JSON);
109 110
}

111 112 113 114 115
TEST(ParserTest, keywordJSON) {
    string txt = "{ \"name\": \"user\","
                   "\"type\": \"password\","
                   "\"user\": \"name\","
                   "\"password\": \"type\" }";
116
    testParser(txt, Parser6Context::PARSER_JSON);
117 118 119 120 121 122 123 124 125 126 127 128 129
}

TEST(ParserTest, keywordDhcp6) {
     string txt = "{ \"Dhcp6\": { \"interfaces-config\": {"
                  " \"interfaces\": [ \"type\", \"htype\" ] },\n"
                  "\"preferred-lifetime\": 3000,\n"
                  "\"rebind-timer\": 2000, \n"
                  "\"renew-timer\": 1000, \n"
                  "\"subnet6\": [ { "
                  "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
                  "    \"subnet\": \"2001:db8:1::/48\", "
                  "    \"interface\": \"test\" } ],\n"
                   "\"valid-lifetime\": 4000 } }";
130
     testParser(txt, Parser6Context::PARSER_DHCP6);
131 132
}

133 134
// Tests if bash (#) comments are supported. That's the only comment type that
// was supported by the old parser.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
135
TEST(ParserTest, bashComments) {
136
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
137 138 139 140 141 142 143 144 145 146 147 148 149
                "  \"interfaces\": [ \"*\" ]"
                "},\n"
                "\"preferred-lifetime\": 3000,\n"
                "# this is a comment\n"
                "\"rebind-timer\": 2000, \n"
                "# lots of comments here\n"
                "# and here\n"
                "\"renew-timer\": 1000, \n"
                "\"subnet6\": [ { "
                "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
                "    \"subnet\": \"2001:db8:1::/48\", "
                "    \"interface\": \"eth0\""
                " } ],"
150
                "\"valid-lifetime\": 4000 } }";
151
    testParser(txt, Parser6Context::PARSER_DHCP6);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
152 153
}

154 155
// Tests if C++ (//) comments can start anywhere, not just in the first line.
TEST(ParserTest, cppComments) {
156
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
157 158 159 160 161 162 163 164 165 166
                "  \"interfaces\": [ \"*\" ]"
                "},\n"
                "\"preferred-lifetime\": 3000, // this is a comment \n"
                "\"rebind-timer\": 2000, // everything after // is ignored\n"
                "\"renew-timer\": 1000, // this will be ignored, too\n"
                "\"subnet6\": [ { "
                "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
                "    \"subnet\": \"2001:db8:1::/48\", "
                "    \"interface\": \"eth0\""
                " } ],"
167
                "\"valid-lifetime\": 4000 } }";
Tomek Mrugalski's avatar
Tomek Mrugalski committed
168
    testParser(txt, Parser6Context::PARSER_DHCP6, false);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
169 170
}

171
// Tests if bash (#) comments can start anywhere, not just in the first line.
172 173
TEST(ParserTest, bashCommentsInline) {
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
174 175 176 177 178 179 180 181 182 183
                "  \"interfaces\": [ \"*\" ]"
                "},\n"
                "\"preferred-lifetime\": 3000, # this is a comment \n"
                "\"rebind-timer\": 2000, # everything after # is ignored\n"
                "\"renew-timer\": 1000, # this will be ignored, too\n"
                "\"subnet6\": [ { "
                "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
                "    \"subnet\": \"2001:db8:1::/48\", "
                "    \"interface\": \"eth0\""
                " } ],"
184
                "\"valid-lifetime\": 4000 } }";
Tomek Mrugalski's avatar
Tomek Mrugalski committed
185
    testParser(txt, Parser6Context::PARSER_DHCP6, false);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
186 187
}

188
// Tests if multi-line C style comments are handled correctly.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
189
TEST(ParserTest, multilineComments) {
190
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
191 192 193 194 195 196 197 198 199 200 201
                "  \"interfaces\": [ \"*\" ]"
                "},\n"
                "\"preferred-lifetime\": 3000, /* this is a C style comment\n"
                "that\n can \n span \n multiple \n lines */ \n"
                "\"rebind-timer\": 2000,\n"
                "\"renew-timer\": 1000, \n"
                "\"subnet6\": [ { "
                "    \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
                "    \"subnet\": \"2001:db8:1::/48\", "
                "    \"interface\": \"eth0\""
                " } ],"
202
                "\"valid-lifetime\": 4000 } }";
Tomek Mrugalski's avatar
Tomek Mrugalski committed
203
    testParser(txt, Parser6Context::PARSER_DHCP6, false);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
204 205
}

206 207 208 209 210 211 212 213 214
/// @brief Loads specified example config file
///
/// This test loads specified example file twice: first, using the legacy
/// JSON file and then second time using bison parser. Two created Element
/// trees are then compared. The input is decommented before it is passed
/// to legacy parser (as legacy support for comments is very limited).
///
/// @param fname name of the file to be loaded
void testFile(const std::string& fname) {
215 216 217
    ElementPtr reference_json;
    ConstElementPtr test_json;

218 219 220
    string decommented = decommentJSONfile(fname);

    cout << "Parsing file " << fname << "(" << decommented << ")" << endl;
221

222
    EXPECT_NO_THROW(reference_json = Element::fromJSONFile(decommented, true));
223

Tomek Mrugalski's avatar
Tomek Mrugalski committed
224 225 226
    // remove the temporary file
    EXPECT_NO_THROW(::remove(decommented.c_str()));

227
    EXPECT_NO_THROW(
228
    try {
229
        Parser6Context ctx;
230
        test_json = ctx.parseFile(fname, Parser6Context::PARSER_DHCP6);
231 232
    } catch (const std::exception &x) {
        cout << "EXCEPTION: " << x.what() << endl;
233 234
        throw;
    });
235 236 237 238

    ASSERT_TRUE(reference_json);
    ASSERT_TRUE(test_json);

239
    compareJSON(reference_json, test_json);
240 241 242 243 244 245 246 247 248
}

// This test loads all available existing files. Each config is loaded
// twice: first with the existing Element::fromJSONFile() and then
// the second time with Parser6. Both JSON trees are then compared.
TEST(ParserTest, file) {
    vector<string> configs;
    configs.push_back("advanced.json");
    configs.push_back("backends.json");
249
    configs.push_back("cassandra.json");
250 251 252 253
    configs.push_back("classify.json");
    configs.push_back("dhcpv4-over-dhcpv6.json");
    configs.push_back("duid.json");
    configs.push_back("hooks.json");
254
    configs.push_back("iPXE.json");
255 256 257 258 259 260
    configs.push_back("leases-expiration.json");
    configs.push_back("multiple-options.json");
    configs.push_back("mysql-reservations.json");
    configs.push_back("pgsql-reservations.json");
    configs.push_back("reservations.json");
    configs.push_back("several-subnets.json");
261
    configs.push_back("shared-network.json");
262
    configs.push_back("simple.json");
263
    configs.push_back("softwire46.json");
264
    configs.push_back("stateless.json");
265
    configs.push_back("with-ddns.json");
266 267

    for (int i = 0; i<configs.size(); i++) {
268
        testFile(string(CFG_EXAMPLES) + "/" + configs[i]);
269
    }
270 271
}

272 273 274 275 276
/// @brief Tests error conditions in Dhcp6Parser
///
/// @param txt text to be parsed
/// @param parser_type type of the parser to be used in the test
/// @param msg expected content of the exception
277 278 279 280 281 282 283
void testError(const std::string& txt,
               Parser6Context::ParserType parser_type,
               const std::string& msg)
{
    try {
        Parser6Context ctx;
        ConstElementPtr parsed = ctx.parseString(txt, parser_type);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
284 285
        FAIL() << "Expected Dhcp6ParseError but nothing was raised (expected: "
               << msg << ")";
286 287 288 289 290 291 292 293 294
    }
    catch (const Dhcp6ParseError& ex) {
        EXPECT_EQ(msg, ex.what());
    }
    catch (...) {
        FAIL() << "Expected Dhcp6ParseError but something else was raised";
    }
}

295
// Verify that error conditions are handled correctly.
296 297
TEST(ParserTest, errors) {
    // no input
298
    testError("", Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
299
              "<string>:1.1: syntax error, unexpected end of file");
300
    testError(" ", Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
301
              "<string>:1.2: syntax error, unexpected end of file");
302
    testError("\n", Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
303
              "<string>:2.1: syntax error, unexpected end of file");
304 305 306 307
    testError("\t", Parser6Context::PARSER_JSON,
              "<string>:1.2: syntax error, unexpected end of file");
    testError("\r", Parser6Context::PARSER_JSON,
              "<string>:1.2: syntax error, unexpected end of file");
308 309

    // comments
310
    testError("# nothing\n",
311
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
312
              "<string>:2.1: syntax error, unexpected end of file");
313
    testError(" #\n",
314
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
315
              "<string>:2.1: syntax error, unexpected end of file");
316
    testError("// nothing\n",
317
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
318
              "<string>:2.1: syntax error, unexpected end of file");
319
    testError("/* nothing */\n",
320
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
321
              "<string>:2.1: syntax error, unexpected end of file");
322
    testError("/* no\nthing */\n",
323
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
324
              "<string>:3.1: syntax error, unexpected end of file");
325
    testError("/* no\nthing */\n\n",
326
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
327
              "<string>:4.1: syntax error, unexpected end of file");
328
    testError("/* nothing\n",
329
              Parser6Context::PARSER_JSON,
330
              "Comment not closed. (/* in line 1");
331
    testError("\n\n\n/* nothing\n",
332
              Parser6Context::PARSER_JSON,
333
              "Comment not closed. (/* in line 4");
334
    testError("{ /* */*/ }\n",
335
              Parser6Context::PARSER_JSON,
336 337
              "<string>:1.3-8: Invalid character: *");
    testError("{ /* // *// }\n",
338
              Parser6Context::PARSER_JSON,
339 340
              "<string>:1.3-11: Invalid character: /");
    testError("{ /* // *///  }\n",
341
              Parser6Context::PARSER_JSON,
342 343 344 345
              "<string>:2.1: syntax error, unexpected end of file, "
              "expecting }");

    // includes
346
    testError("<?\n",
347
              Parser6Context::PARSER_JSON,
348 349
              "Directive not closed.");
    testError("<?include\n",
350
              Parser6Context::PARSER_JSON,
351 352 353
              "Directive not closed.");
    string file = string(CFG_EXAMPLES) + "/" + "stateless.json";
    testError("<?include \"" + file + "\"\n",
354
              Parser6Context::PARSER_JSON,
355
              "Directive not closed.");
356
    testError("<?include \"/foo/bar\" ?>/n",
357
              Parser6Context::PARSER_JSON,
358 359
              "Can't open include file /foo/bar");

360
    // JSON keywords
361
    testError("{ \"foo\": True }",
362 363 364 365 366 367 368 369 370
              Parser6Context::PARSER_JSON,
              "<string>:1.10-13: JSON true reserved keyword is lower case only");
    testError("{ \"foo\": False }",
              Parser6Context::PARSER_JSON,
              "<string>:1.10-14: JSON false reserved keyword is lower case only");
    testError("{ \"foo\": NULL }",
              Parser6Context::PARSER_JSON,
              "<string>:1.10-13: JSON null reserved keyword is lower case only");
    testError("{ \"foo\": Tru }",
371 372
              Parser6Context::PARSER_JSON,
              "<string>:1.10: Invalid character: T");
373
    testError("{ \"foo\": nul }",
374
              Parser6Context::PARSER_JSON,
375
              "<string>:1.10: Invalid character: n");
376

377 378
    // numbers
    testError("123",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
379
              Parser6Context::PARSER_DHCP6,
380 381 382
              "<string>:1.1-3: syntax error, unexpected integer, "
              "expecting {");
    testError("-456",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
383
              Parser6Context::PARSER_DHCP6,
384 385 386
              "<string>:1.1-4: syntax error, unexpected integer, "
              "expecting {");
    testError("-0001",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
387
              Parser6Context::PARSER_DHCP6,
388 389 390
              "<string>:1.1-5: syntax error, unexpected integer, "
              "expecting {");
    testError("1234567890123456789012345678901234567890",
391
              Parser6Context::PARSER_JSON,
392 393 394 395
              "<string>:1.1-40: Failed to convert "
              "1234567890123456789012345678901234567890"
              " to an integer.");
    testError("-3.14e+0",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
396
              Parser6Context::PARSER_DHCP6,
397 398 399
              "<string>:1.1-8: syntax error, unexpected floating point, "
              "expecting {");
    testError("1e50000",
400
              Parser6Context::PARSER_JSON,
401 402 403 404 405
              "<string>:1.1-7: Failed to convert 1e50000 "
              "to a floating point.");

    // strings
    testError("\"aabb\"",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
406
              Parser6Context::PARSER_DHCP6,
407 408 409
              "<string>:1.1-6: syntax error, unexpected constant string, "
              "expecting {");
    testError("{ \"aabb\"err",
410
              Parser6Context::PARSER_JSON,
411 412
              "<string>:1.9: Invalid character: e");
    testError("{ err\"aabb\"",
413
              Parser6Context::PARSER_JSON,
414 415
              "<string>:1.3: Invalid character: e");
    testError("\"a\n\tb\"",
416
              Parser6Context::PARSER_JSON,
417
              "<string>:1.1-6: Invalid control in \"a\n\tb\"");
418
    testError("\"a\\n\\tb\"",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
419
              Parser6Context::PARSER_DHCP6,
420 421 422
              "<string>:1.1-8: syntax error, unexpected constant string, "
              "expecting {");
    testError("\"a\\x01b\"",
423
              Parser6Context::PARSER_JSON,
424
              "<string>:1.1-8: Bad escape in \"a\\x01b\"");
425
    testError("\"a\\u0162\"",
426
              Parser6Context::PARSER_JSON,
427
              "<string>:1.1-9: Unsupported unicode escape in \"a\\u0162\"");
428
    testError("\"a\\u062z\"",
429
              Parser6Context::PARSER_JSON,
430 431
              "<string>:1.1-9: Bad escape in \"a\\u062z\"");
    testError("\"abc\\\"",
432
              Parser6Context::PARSER_JSON,
433 434 435 436
              "<string>:1.1-6: Overflow escape in \"abc\\\"");

    // from data_unittest.c
    testError("\\a",
437
              Parser6Context::PARSER_JSON,
438 439
              "<string>:1.1: Invalid character: \\");
    testError("\\",
440
              Parser6Context::PARSER_JSON,
441 442
              "<string>:1.1: Invalid character: \\");
    testError("\\\"\\\"",
443
              Parser6Context::PARSER_JSON,
444
              "<string>:1.1: Invalid character: \\");
445 446 447

    // want a map
    testError("[]\n",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
448
              Parser6Context::PARSER_DHCP6,
449 450 451 452 453 454 455
              "<string>:1.1: syntax error, unexpected [, "
              "expecting {");
    testError("[]\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:1.1: syntax error, unexpected [, "
              "expecting {");
    testError("{ 123 }\n",
456
              Parser6Context::PARSER_JSON,
457 458 459 460
              "<string>:1.3-5: syntax error, unexpected integer, "
              "expecting }");
    testError("{ 123 }\n",
              Parser6Context::PARSER_DHCP6,
461
              "<string>:1.3-5: syntax error, unexpected integer");
462
    testError("{ \"foo\" }\n",
463
              Parser6Context::PARSER_JSON,
464 465 466 467
              "<string>:1.9: syntax error, unexpected }, "
              "expecting :");
    testError("{ \"foo\" }\n",
              Parser6Context::PARSER_DHCP6,
468 469 470 471 472
              "<string>:1.9: syntax error, unexpected }, expecting :");
    testError("{ \"foo\":null }\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:1.3-7: got unexpected keyword "
              "\"foo\" in toplevel map.");
473 474 475 476
    testError("{ \"Dhcp6\" }\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:1.11: syntax error, unexpected }, "
              "expecting :");
477 478 479 480 481
    testError("{ \"Dhcp4\":[]\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:2.1: syntax error, unexpected end of file, "
              "expecting \",\" or }");
    testError("{}{}\n",
482
              Parser6Context::PARSER_JSON,
483 484 485 486 487
              "<string>:1.3: syntax error, unexpected {, "
              "expecting end of file");

    // bad commas
    testError("{ , }\n",
488
              Parser6Context::PARSER_JSON,
489 490 491
              "<string>:1.3: syntax error, unexpected \",\", "
              "expecting }");
    testError("{ , \"foo\":true }\n",
492
              Parser6Context::PARSER_JSON,
493 494 495
              "<string>:1.3: syntax error, unexpected \",\", "
              "expecting }");
    testError("{ \"foo\":true, }\n",
496
              Parser6Context::PARSER_JSON,
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
              "<string>:1.15: syntax error, unexpected }, "
              "expecting constant string");

    // bad type
    testError("{ \"Dhcp6\":{\n"
              "  \"preferred-lifetime\":false }}\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:2.24-28: syntax error, unexpected boolean, "
              "expecting integer");

    // unknown keyword
    testError("{ \"Dhcp6\":{\n"
              " \"preferred_lifetime\":600 }}\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:2.2-21: got unexpected keyword "
              "\"preferred_lifetime\" in Dhcp6 map.");
Francis Dupont's avatar
Francis Dupont committed
513 514 515 516 517 518 519

    // missing parameter
    testError("{ \"name\": \"foo\",\n"
              "  \"code\": 123 }\n",
              Parser6Context::PARSER_OPTION_DEF,
              "missing parameter 'type' (<string>:1:1) "
              "[option-def map between <string>:1:1 and <string>:2:15]");
520 521
}

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
// Check unicode escapes
TEST(ParserTest, unicodeEscapes) {
    ConstElementPtr result;
    string json;

    // check we can reread output
    for (char c = -128; c < 127; ++c) {
        string ins(" ");
        ins[1] = c;
        ConstElementPtr e(new StringElement(ins));
        json = e->str();
        ASSERT_NO_THROW(
        try {
            Parser6Context ctx;
            result = ctx.parseString(json, Parser6Context::PARSER_JSON);
        } catch (const std::exception &x) {
            cout << "EXCEPTION: " << x.what() << endl;
            throw;
        });
        ASSERT_EQ(Element::string, result->getType());
        EXPECT_EQ(ins, result->stringValue());
    }
544
}
545

546 547
// This test checks that all representations of a slash is recognized properly.
TEST(ParserTest, unicodeSlash) {
548
    // check the 4 possible encodings of solidus '/'
549 550
    ConstElementPtr result;
    string json = "\"/\\/\\u002f\\u002F\"";
551 552 553 554 555 556 557 558 559 560
    ASSERT_NO_THROW(
    try {
        Parser6Context ctx;
        result = ctx.parseString(json, Parser6Context::PARSER_JSON);
    } catch (const std::exception &x) {
        cout << "EXCEPTION: " << x.what() << endl;
        throw;
    });
    ASSERT_EQ(Element::string, result->getType());
    EXPECT_EQ("////", result->stringValue());
561
}
562

563
};
564 565
};
};