parser_unittest.cc 19.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
//
// 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>

using namespace isc::data;
using namespace std;

namespace {

16
void compareJSON(ConstElementPtr a, ConstElementPtr b, bool print = true) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
17 18
    ASSERT_TRUE(a);
    ASSERT_TRUE(b);
19
    if (print) {
20 21 22
        // std::cout << "JSON A: -----" << endl << a->str() << std::endl;
        // std::cout << "JSON B: -----" << endl << b->str() << std::endl;
        // cout << "---------" << endl << endl;
23
    }
24 25 26
    EXPECT_EQ(a->str(), b->str());
}

27
void testParser(const std::string& txt, Parser6Context::ParserType parser_type) {
28 29 30
    ElementPtr reference_json;
    ConstElementPtr test_json;

31 32 33
    ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
    ASSERT_NO_THROW({
            try {
34 35
                Parser6Context ctx;
                test_json = ctx.parseString(txt, parser_type);
36 37 38 39 40
            } catch (const std::exception &e) {
                cout << "EXCEPTION: " << e.what() << endl;
                throw;
            }

41 42 43 44 45 46
    });

    // Now compare if both representations are the same.
    compareJSON(reference_json, test_json);
}

47
void testParser2(const std::string& txt, Parser6Context::ParserType parser_type) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
48 49
    ConstElementPtr test_json;

50 51
    ASSERT_NO_THROW({
            try {
52 53
                Parser6Context ctx;
                test_json = ctx.parseString(txt, parser_type);
54 55 56 57
            } catch (const std::exception &e) {
                cout << "EXCEPTION: " << e.what() << endl;
                throw;
            }
Tomek Mrugalski's avatar
Tomek Mrugalski committed
58 59 60 61
    });
    /// @todo: Implement actual validation here. since the original
    /// Element::fromJSON does not support several comment types, we don't
    /// have anything to compare with.
62 63
    /// std::cout << "Original text:" << txt << endl;
    /// std::cout << "Parsed text  :" << test_json->str() << endl;
Tomek Mrugalski's avatar
Tomek Mrugalski committed
64 65
}

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

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

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

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

87 88 89
TEST(ParserTest, listsInMaps) {
    string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelguese\" ], "
                    "\"cygnus\": [ \"deneb\", \"albireo\"] } }";
90
    testParser(txt, Parser6Context::PARSER_JSON);
91 92 93
}

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

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

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

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 } }";
     testParser2(txt, Parser6Context::PARSER_DHCP6);
}

Tomek Mrugalski's avatar
Tomek Mrugalski committed
131
TEST(ParserTest, bashComments) {
132
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
133 134 135 136 137 138 139 140 141 142 143 144 145
                "  \"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\""
                " } ],"
146 147
                "\"valid-lifetime\": 4000 } }";
    testParser2(txt, Parser6Context::PARSER_DHCP6);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
148 149 150
}

TEST(ParserTest, cComments) {
151
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
152 153 154 155 156 157 158 159 160 161
                "  \"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\""
                " } ],"
162 163
                "\"valid-lifetime\": 4000 } }";
    testParser2(txt, Parser6Context::PARSER_DHCP6);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
164 165
}

166 167
TEST(ParserTest, bashCommentsInline) {
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
168 169 170 171 172 173 174 175 176 177
                "  \"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\""
                " } ],"
178 179
                "\"valid-lifetime\": 4000 } }";
    testParser2(txt, Parser6Context::PARSER_DHCP6);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
180 181 182
}

TEST(ParserTest, multilineComments) {
183
    string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
Tomek Mrugalski's avatar
Tomek Mrugalski committed
184 185 186 187 188 189 190 191 192 193 194
                "  \"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\""
                " } ],"
195 196
                "\"valid-lifetime\": 4000 } }";
    testParser2(txt, Parser6Context::PARSER_DHCP6);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
197 198
}

199

200
void testFile(const std::string& fname, bool print) {
201 202 203
    ElementPtr reference_json;
    ConstElementPtr test_json;

204
    cout << "Attempting to load file " << fname << endl;
205 206

    EXPECT_NO_THROW(reference_json = Element::fromJSONFile(fname, true));
207

208
    EXPECT_NO_THROW(
209
    try {
210
        Parser6Context ctx;
211
        test_json = ctx.parseFile(fname, Parser6Context::PARSER_DHCP6);
212 213
    } catch (const std::exception &x) {
        cout << "EXCEPTION: " << x.what() << endl;
214 215
        throw;
    });
216 217 218 219

    ASSERT_TRUE(reference_json);
    ASSERT_TRUE(test_json);

220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    compareJSON(reference_json, test_json, print);


}

// 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");
    configs.push_back("classify.json");
    configs.push_back("dhcpv4-over-dhcpv6.json");
    configs.push_back("duid.json");
    configs.push_back("hooks.json");
    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");
    configs.push_back("simple.json");
    configs.push_back("stateless.json");

    for (int i = 0; i<configs.size(); i++) {
        testFile(string(CFG_EXAMPLES) + "/" + configs[i], false);
    }
248 249
}

250 251 252 253 254 255 256
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
257 258
        FAIL() << "Expected Dhcp6ParseError but nothing was raised (expected: "
               << msg << ")";
259 260 261 262 263 264 265 266 267
    }
    catch (const Dhcp6ParseError& ex) {
        EXPECT_EQ(msg, ex.what());
    }
    catch (...) {
        FAIL() << "Expected Dhcp6ParseError but something else was raised";
    }
}

268 269 270
// Check errors
TEST(ParserTest, errors) {
    // no input
271
    testError("", Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
272
              "<string>:1.1: syntax error, unexpected end of file");
273
    testError(" ", Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
274
              "<string>:1.2: syntax error, unexpected end of file");
275
    testError("\n", Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
276
              "<string>:2.1: syntax error, unexpected end of file");
277 278 279 280
    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");
281 282

    // comments
283
    testError("# nothing\n",
284
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
285
              "<string>:2.1: syntax error, unexpected end of file");
286
    testError(" #\n",
287
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
288
              "<string>:2.1: syntax error, unexpected end of file");
289
    testError("// nothing\n",
290
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
291
              "<string>:2.1: syntax error, unexpected end of file");
292
    testError("/* nothing */\n",
293
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
294
              "<string>:2.1: syntax error, unexpected end of file");
295
    testError("/* no\nthing */\n",
296
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
297
              "<string>:3.1: syntax error, unexpected end of file");
298
    testError("/* no\nthing */\n\n",
299
              Parser6Context::PARSER_JSON,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
300
              "<string>:4.1: syntax error, unexpected end of file");
301
    testError("/* nothing\n",
302
              Parser6Context::PARSER_JSON,
303
              "Comment not closed. (/* in line 1");
304
    testError("\n\n\n/* nothing\n",
305
              Parser6Context::PARSER_JSON,
306
              "Comment not closed. (/* in line 4");
307
    testError("{ /* */*/ }\n",
308
              Parser6Context::PARSER_JSON,
309 310
              "<string>:1.3-8: Invalid character: *");
    testError("{ /* // *// }\n",
311
              Parser6Context::PARSER_JSON,
312 313
              "<string>:1.3-11: Invalid character: /");
    testError("{ /* // *///  }\n",
314
              Parser6Context::PARSER_JSON,
315 316 317 318
              "<string>:2.1: syntax error, unexpected end of file, "
              "expecting }");

    // includes
319
    testError("<?\n",
320
              Parser6Context::PARSER_JSON,
321 322
              "Directive not closed.");
    testError("<?include\n",
323
              Parser6Context::PARSER_JSON,
324 325 326
              "Directive not closed.");
    string file = string(CFG_EXAMPLES) + "/" + "stateless.json";
    testError("<?include \"" + file + "\"\n",
327
              Parser6Context::PARSER_JSON,
328
              "Directive not closed.");
329
    testError("<?include \"/foo/bar\" ?>/n",
330
              Parser6Context::PARSER_JSON,
331 332
              "Can't open include file /foo/bar");

333
    // JSON keywords
334
    testError("{ \"foo\": True }",
335 336 337 338 339 340 341 342 343
              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 }",
344 345
              Parser6Context::PARSER_JSON,
              "<string>:1.10: Invalid character: T");
346
    testError("{ \"foo\": nul }",
347
              Parser6Context::PARSER_JSON,
348
              "<string>:1.10: Invalid character: n");
349

350 351
    // numbers
    testError("123",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
352
              Parser6Context::PARSER_DHCP6,
353 354 355
              "<string>:1.1-3: syntax error, unexpected integer, "
              "expecting {");
    testError("-456",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
356
              Parser6Context::PARSER_DHCP6,
357 358 359
              "<string>:1.1-4: syntax error, unexpected integer, "
              "expecting {");
    testError("-0001",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
360
              Parser6Context::PARSER_DHCP6,
361 362 363
              "<string>:1.1-5: syntax error, unexpected integer, "
              "expecting {");
    testError("1234567890123456789012345678901234567890",
364
              Parser6Context::PARSER_JSON,
365 366 367 368
              "<string>:1.1-40: Failed to convert "
              "1234567890123456789012345678901234567890"
              " to an integer.");
    testError("-3.14e+0",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
369
              Parser6Context::PARSER_DHCP6,
370 371 372
              "<string>:1.1-8: syntax error, unexpected floating point, "
              "expecting {");
    testError("1e50000",
373
              Parser6Context::PARSER_JSON,
374 375 376 377 378
              "<string>:1.1-7: Failed to convert 1e50000 "
              "to a floating point.");

    // strings
    testError("\"aabb\"",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
379
              Parser6Context::PARSER_DHCP6,
380 381 382
              "<string>:1.1-6: syntax error, unexpected constant string, "
              "expecting {");
    testError("{ \"aabb\"err",
383
              Parser6Context::PARSER_JSON,
384 385
              "<string>:1.9: Invalid character: e");
    testError("{ err\"aabb\"",
386
              Parser6Context::PARSER_JSON,
387 388
              "<string>:1.3: Invalid character: e");
    testError("\"a\n\tb\"",
389
              Parser6Context::PARSER_JSON,
390
              "<string>:1.1-6: Invalid control in \"a\n\tb\"");
391
    testError("\"a\\n\\tb\"",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
392
              Parser6Context::PARSER_DHCP6,
393 394 395
              "<string>:1.1-8: syntax error, unexpected constant string, "
              "expecting {");
    testError("\"a\\x01b\"",
396
              Parser6Context::PARSER_JSON,
397
              "<string>:1.1-8: Bad escape in \"a\\x01b\"");
398
    testError("\"a\\u0162\"",
399
              Parser6Context::PARSER_JSON,
400
              "<string>:1.1-9: Unsupported unicode escape in \"a\\u0162\"");
401
    testError("\"a\\u062z\"",
402
              Parser6Context::PARSER_JSON,
403 404
              "<string>:1.1-9: Bad escape in \"a\\u062z\"");
    testError("\"abc\\\"",
405
              Parser6Context::PARSER_JSON,
406 407 408 409
              "<string>:1.1-6: Overflow escape in \"abc\\\"");

    // from data_unittest.c
    testError("\\a",
410
              Parser6Context::PARSER_JSON,
411 412
              "<string>:1.1: Invalid character: \\");
    testError("\\",
413
              Parser6Context::PARSER_JSON,
414 415
              "<string>:1.1: Invalid character: \\");
    testError("\\\"\\\"",
416
              Parser6Context::PARSER_JSON,
417
              "<string>:1.1: Invalid character: \\");
418 419 420

    // want a map
    testError("[]\n",
Tomek Mrugalski's avatar
Tomek Mrugalski committed
421
              Parser6Context::PARSER_DHCP6,
422 423 424 425 426 427 428
              "<string>:1.1: syntax error, unexpected [, "
              "expecting {");
    testError("[]\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:1.1: syntax error, unexpected [, "
              "expecting {");
    testError("{ 123 }\n",
429
              Parser6Context::PARSER_JSON,
430 431 432 433
              "<string>:1.3-5: syntax error, unexpected integer, "
              "expecting }");
    testError("{ 123 }\n",
              Parser6Context::PARSER_DHCP6,
434
              "<string>:1.3-5: syntax error, unexpected integer");
435
    testError("{ \"foo\" }\n",
436
              Parser6Context::PARSER_JSON,
437 438 439 440
              "<string>:1.9: syntax error, unexpected }, "
              "expecting :");
    testError("{ \"foo\" }\n",
              Parser6Context::PARSER_DHCP6,
441 442 443 444 445
              "<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.");
446 447 448 449
    testError("{ \"Dhcp6\" }\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:1.11: syntax error, unexpected }, "
              "expecting :");
450 451 452 453 454
    testError("{ \"Dhcp4\":[]\n",
              Parser6Context::PARSER_DHCP6,
              "<string>:2.1: syntax error, unexpected end of file, "
              "expecting \",\" or }");
    testError("{}{}\n",
455
              Parser6Context::PARSER_JSON,
456 457 458 459 460
              "<string>:1.3: syntax error, unexpected {, "
              "expecting end of file");

    // bad commas
    testError("{ , }\n",
461
              Parser6Context::PARSER_JSON,
462 463 464
              "<string>:1.3: syntax error, unexpected \",\", "
              "expecting }");
    testError("{ , \"foo\":true }\n",
465
              Parser6Context::PARSER_JSON,
466 467 468
              "<string>:1.3: syntax error, unexpected \",\", "
              "expecting }");
    testError("{ \"foo\":true, }\n",
469
              Parser6Context::PARSER_JSON,
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
              "<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.");
486 487
}

488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
// 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());
    }

    // check the 4 possible encodings of solidus '/'
    json = "\"/\\/\\u002f\\u002F\"";
    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());
}       

525
};