TestJSONWriter.cpp (15076B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/Assertions.h" 8 #include "mozilla/JSONWriter.h" 9 #include "mozilla/UniquePtr.h" 10 #include <stdio.h> 11 #include <string> 12 #include <string.h> 13 14 using mozilla::JSONWriteFunc; 15 using mozilla::JSONWriter; 16 using mozilla::MakeStringSpan; 17 using mozilla::MakeUnique; 18 using mozilla::Span; 19 20 // This writes all the output into a big buffer. 21 struct StringWriteFunc final : public JSONWriteFunc { 22 std::string mString; 23 24 void Write(const mozilla::Span<const char>& aStr) final { 25 mString.append(aStr.data(), aStr.size()); 26 } 27 }; 28 29 void Check(JSONWriter& aWriter, const char* aExpected) { 30 JSONWriteFunc& func = aWriter.WriteFunc(); 31 const std::string& actual = static_cast<StringWriteFunc&>(func).mString; 32 if (strcmp(aExpected, actual.c_str()) != 0) { 33 fprintf(stderr, 34 "---- EXPECTED ----\n<<<%s>>>\n" 35 "---- ACTUAL ----\n<<<%s>>>\n", 36 aExpected, actual.c_str()); 37 MOZ_RELEASE_ASSERT(false, "expected and actual output don't match"); 38 } 39 } 40 41 // Note: to convert actual output into |expected| strings that C++ can handle, 42 // apply the following substitutions, in order, to each line. 43 // - s/\\/\\\\/g # escapes backslashes 44 // - s/"/\\"/g # escapes quotes 45 // - s/$/\\n\\/ # adds a newline and string continuation char to each line 46 47 void TestBasicProperties() { 48 const char* expected = 49 "\ 50 {\n\ 51 \"null\": null,\n\ 52 \"bool1\": true,\n\ 53 \"bool2\": false,\n\ 54 \"int1\": 123,\n\ 55 \"int2\": -123,\n\ 56 \"int3\": -123456789000,\n\ 57 \"double1\": 1.2345,\n\ 58 \"double2\": -3,\n\ 59 \"double3\": 1e-7,\n\ 60 \"double4\": 1.1111111111111111e+21,\n\ 61 \"string1\": \"\",\n\ 62 \"string2\": \"1234\",\n\ 63 \"string3\": \"hello\",\n\ 64 \"string4\": \"\\\" \\\\ \\u0007 \\b \\t \\n \\u000b \\f \\r\",\n\ 65 \"string5\": \"hello\",\n\ 66 \"string6\": \"\\\" \\\\ \\u0007 \\b \\t \",\n\ 67 \"span1\": \"buf1\",\n\ 68 \"span2\": \"buf2\",\n\ 69 \"span3\": \"buf3\",\n\ 70 \"span4\": \"buf\\n4\",\n\ 71 \"span5\": \"MakeStringSpan\",\n\ 72 \"len 0 array, multi-line\": [\n\ 73 ],\n\ 74 \"len 0 array, single-line\": [],\n\ 75 \"len 1 array\": [\n\ 76 1\n\ 77 ],\n\ 78 \"len 5 array, multi-line\": [\n\ 79 1,\n\ 80 2,\n\ 81 3,\n\ 82 4,\n\ 83 5\n\ 84 ],\n\ 85 \"len 3 array, single-line\": [1, [{}, 2, []], 3],\n\ 86 \"len 0 object, multi-line\": {\n\ 87 },\n\ 88 \"len 0 object, single-line\": {},\n\ 89 \"len 1 object\": {\n\ 90 \"one\": 1\n\ 91 },\n\ 92 \"len 5 object\": {\n\ 93 \"one\": 1,\n\ 94 \"two\": 2,\n\ 95 \"three\": 3,\n\ 96 \"four\": 4,\n\ 97 \"five\": 5\n\ 98 },\n\ 99 \"len 3 object, single-line\": {\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\ 100 }\n\ 101 "; 102 103 JSONWriter w(MakeUnique<StringWriteFunc>()); 104 105 w.Start(); 106 { 107 w.NullProperty("null"); 108 109 w.BoolProperty("bool1", true); 110 w.BoolProperty("bool2", false); 111 112 w.IntProperty("int1", 123); 113 w.IntProperty("int2", -0x7b); 114 w.IntProperty("int3", -123456789000ll); 115 116 w.DoubleProperty("double1", 1.2345); 117 w.DoubleProperty("double2", -3); 118 w.DoubleProperty("double3", 1e-7); 119 w.DoubleProperty("double4", 1.1111111111111111e+21); 120 121 w.StringProperty("string1", ""); 122 w.StringProperty("string2", "1234"); 123 w.StringProperty("string3", "hello"); 124 w.StringProperty("string4", "\" \\ \a \b \t \n \v \f \r"); 125 w.StringProperty("string5", "hello\0cut"); // '\0' marks the end. 126 w.StringProperty("string6", "\" \\ \a \b \t \0 \n \v \f \r"); 127 128 const char buf1[] = {'b', 'u', 'f', '1'}; 129 w.StringProperty("span1", buf1); 130 const char buf2[] = {'b', 'u', 'f', '2', '\0'}; 131 w.StringProperty("span2", buf2); 132 const char buf3[] = {'b', 'u', 'f', '3', '\0', '?'}; 133 w.StringProperty("span3", buf3); 134 const char buf4[] = {'b', 'u', 'f', '\n', '4', '\0', '?'}; 135 w.StringProperty("span4", buf4); 136 w.StringProperty("span5", MakeStringSpan("MakeStringSpan")); 137 138 w.StartArrayProperty("len 0 array, multi-line", w.MultiLineStyle); 139 w.EndArray(); 140 141 w.StartArrayProperty("len 0 array, single-line", w.SingleLineStyle); 142 w.EndArray(); 143 144 w.StartArrayProperty("len 1 array"); 145 { 146 w.IntElement(1); 147 } 148 w.EndArray(); 149 150 w.StartArrayProperty("len 5 array, multi-line", w.MultiLineStyle); 151 { 152 w.IntElement(1); 153 w.IntElement(2); 154 w.IntElement(3); 155 w.IntElement(4); 156 w.IntElement(5); 157 } 158 w.EndArray(); 159 160 w.StartArrayProperty("len 3 array, single-line", w.SingleLineStyle); 161 { 162 w.IntElement(1); 163 w.StartArrayElement(); 164 { 165 w.StartObjectElement(w.SingleLineStyle); 166 w.EndObject(); 167 168 w.IntElement(2); 169 170 w.StartArrayElement(w.MultiLineStyle); // style overridden from above 171 w.EndArray(); 172 } 173 w.EndArray(); 174 w.IntElement(3); 175 } 176 w.EndArray(); 177 178 w.StartObjectProperty("len 0 object, multi-line"); 179 w.EndObject(); 180 181 w.StartObjectProperty("len 0 object, single-line", w.SingleLineStyle); 182 w.EndObject(); 183 184 w.StartObjectProperty("len 1 object"); 185 { 186 w.IntProperty("one", 1); 187 } 188 w.EndObject(); 189 190 w.StartObjectProperty("len 5 object"); 191 { 192 w.IntProperty("one", 1); 193 w.IntProperty("two", 2); 194 w.IntProperty("three", 3); 195 w.IntProperty("four", 4); 196 w.IntProperty("five", 5); 197 } 198 w.EndObject(); 199 200 w.StartObjectProperty("len 3 object, single-line", w.SingleLineStyle); 201 { 202 w.IntProperty("a", 1); 203 w.StartArrayProperty("b"); 204 { 205 w.StartObjectElement(); 206 w.EndObject(); 207 208 w.IntElement(2); 209 210 w.StartArrayElement(w.SingleLineStyle); 211 w.EndArray(); 212 } 213 w.EndArray(); 214 w.IntProperty("c", 3); 215 } 216 w.EndObject(); 217 } 218 w.End(); 219 220 Check(w, expected); 221 } 222 223 void TestBasicElements() { 224 const char* expected = 225 "\ 226 {\n\ 227 \"array\": [\n\ 228 null,\n\ 229 true,\n\ 230 false,\n\ 231 123,\n\ 232 -123,\n\ 233 -123456789000,\n\ 234 1.2345,\n\ 235 -3,\n\ 236 1e-7,\n\ 237 1.1111111111111111e+21,\n\ 238 \"\",\n\ 239 \"1234\",\n\ 240 \"hello\",\n\ 241 \"\\\" \\\\ \\u0007 \\b \\t \\n \\u000b \\f \\r\",\n\ 242 \"hello\",\n\ 243 \"\\\" \\\\ \\u0007 \\b \\t \",\n\ 244 \"buf1\",\n\ 245 \"buf2\",\n\ 246 \"buf3\",\n\ 247 \"buf\\n4\",\n\ 248 \"MakeStringSpan\",\n\ 249 [\n\ 250 ],\n\ 251 [],\n\ 252 [\n\ 253 1\n\ 254 ],\n\ 255 [\n\ 256 1,\n\ 257 2,\n\ 258 3,\n\ 259 4,\n\ 260 5\n\ 261 ],\n\ 262 [1, [{}, 2, []], 3],\n\ 263 {\n\ 264 },\n\ 265 {},\n\ 266 {\n\ 267 \"one\": 1\n\ 268 },\n\ 269 {\n\ 270 \"one\": 1,\n\ 271 \"two\": 2,\n\ 272 \"three\": 3,\n\ 273 \"four\": 4,\n\ 274 \"five\": 5\n\ 275 },\n\ 276 {\"a\": 1, \"b\": [{}, 2, []], \"c\": 3}\n\ 277 ]\n\ 278 }\n\ 279 "; 280 281 JSONWriter w(MakeUnique<StringWriteFunc>()); 282 283 w.Start(); 284 w.StartArrayProperty("array"); 285 { 286 w.NullElement(); 287 288 w.BoolElement(true); 289 w.BoolElement(false); 290 291 w.IntElement(123); 292 w.IntElement(-0x7b); 293 w.IntElement(-123456789000ll); 294 295 w.DoubleElement(1.2345); 296 w.DoubleElement(-3); 297 w.DoubleElement(1e-7); 298 w.DoubleElement(1.1111111111111111e+21); 299 300 w.StringElement(""); 301 w.StringElement("1234"); 302 w.StringElement("hello"); 303 w.StringElement("\" \\ \a \b \t \n \v \f \r"); 304 w.StringElement("hello\0cut"); // '\0' marks the end. 305 w.StringElement("\" \\ \a \b \t \0 \n \v \f \r"); 306 307 const char buf1[] = {'b', 'u', 'f', '1'}; 308 w.StringElement(buf1); 309 const char buf2[] = {'b', 'u', 'f', '2', '\0'}; 310 w.StringElement(buf2); 311 const char buf3[] = {'b', 'u', 'f', '3', '\0', '?'}; 312 w.StringElement(buf3); 313 const char buf4[] = {'b', 'u', 'f', '\n', '4', '\0', '?'}; 314 w.StringElement(buf4); 315 w.StringElement(MakeStringSpan("MakeStringSpan")); 316 317 w.StartArrayElement(); 318 w.EndArray(); 319 320 w.StartArrayElement(w.SingleLineStyle); 321 w.EndArray(); 322 323 w.StartArrayElement(); 324 { 325 w.IntElement(1); 326 } 327 w.EndArray(); 328 329 w.StartArrayElement(); 330 { 331 w.IntElement(1); 332 w.IntElement(2); 333 w.IntElement(3); 334 w.IntElement(4); 335 w.IntElement(5); 336 } 337 w.EndArray(); 338 339 w.StartArrayElement(w.SingleLineStyle); 340 { 341 w.IntElement(1); 342 w.StartArrayElement(); 343 { 344 w.StartObjectElement(w.SingleLineStyle); 345 w.EndObject(); 346 347 w.IntElement(2); 348 349 w.StartArrayElement(w.MultiLineStyle); // style overridden from above 350 w.EndArray(); 351 } 352 w.EndArray(); 353 w.IntElement(3); 354 } 355 w.EndArray(); 356 357 w.StartObjectElement(); 358 w.EndObject(); 359 360 w.StartObjectElement(w.SingleLineStyle); 361 w.EndObject(); 362 363 w.StartObjectElement(); 364 { 365 w.IntProperty("one", 1); 366 } 367 w.EndObject(); 368 369 w.StartObjectElement(); 370 { 371 w.IntProperty("one", 1); 372 w.IntProperty("two", 2); 373 w.IntProperty("three", 3); 374 w.IntProperty("four", 4); 375 w.IntProperty("five", 5); 376 } 377 w.EndObject(); 378 379 w.StartObjectElement(w.SingleLineStyle); 380 { 381 w.IntProperty("a", 1); 382 w.StartArrayProperty("b"); 383 { 384 w.StartObjectElement(); 385 w.EndObject(); 386 387 w.IntElement(2); 388 389 w.StartArrayElement(w.SingleLineStyle); 390 w.EndArray(); 391 } 392 w.EndArray(); 393 w.IntProperty("c", 3); 394 } 395 w.EndObject(); 396 } 397 w.EndArray(); 398 w.End(); 399 400 Check(w, expected); 401 } 402 403 void TestOneLineObject() { 404 const char* expected = 405 "\ 406 {\"i\": 1, \"array\": [null, [{}], {\"o\": {}}, \"s\"], \"d\": 3.33}\n\ 407 "; 408 409 JSONWriter w(MakeUnique<StringWriteFunc>()); 410 411 w.Start(w.SingleLineStyle); 412 413 w.IntProperty("i", 1); 414 415 w.StartArrayProperty("array"); 416 { 417 w.NullElement(); 418 419 w.StartArrayElement(w.MultiLineStyle); // style overridden from above 420 { 421 w.StartObjectElement(); 422 w.EndObject(); 423 } 424 w.EndArray(); 425 426 w.StartObjectElement(); 427 { 428 w.StartObjectProperty("o"); 429 w.EndObject(); 430 } 431 w.EndObject(); 432 433 w.StringElement("s"); 434 } 435 w.EndArray(); 436 437 w.DoubleProperty("d", 3.33); 438 439 w.End(); 440 441 Check(w, expected); 442 } 443 444 void TestOneLineJson() { 445 const char* expected = 446 "\ 447 {\"i\":1,\"array\":[null,[{}],{\"o\":{}},\"s\"],\"d\":3.33}\ 448 "; 449 450 StringWriteFunc func; 451 JSONWriter w(func, JSONWriter::SingleLineStyle); 452 453 w.Start(w.MultiLineStyle); // style overridden from above 454 455 w.IntProperty("i", 1); 456 457 w.StartArrayProperty("array"); 458 { 459 w.NullElement(); 460 461 w.StartArrayElement(w.MultiLineStyle); // style overridden from above 462 { 463 w.StartObjectElement(); 464 w.EndObject(); 465 } 466 w.EndArray(); 467 468 w.StartObjectElement(); 469 { 470 w.StartObjectProperty("o"); 471 w.EndObject(); 472 } 473 w.EndObject(); 474 475 w.StringElement("s"); 476 } 477 w.EndArray(); 478 479 w.DoubleProperty("d", 3.33); 480 481 w.End(); // No newline in this case. 482 483 Check(w, expected); 484 } 485 486 void TestStringEscaping() { 487 // This test uses hexadecimal character escapes because UTF8 literals cause 488 // problems for some compilers (see bug 1069726). 489 const char* expected = 490 "\ 491 {\n\ 492 \"ascii\": \"\x7F~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\ 493 \"\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83\": true,\n\ 494 \"\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1\": -123,\n\ 495 \"\xE4\xBD\xA0\xE5\xA5\xBD\": 1.234,\n\ 496 \"\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF\": \"\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\",\n\ 497 \"hall\xC3\xB3 \xC3\xBE" 498 "arna\": 4660,\n\ 499 \"\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\": {\n\ 500 \"\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82\": [\n\ 501 ]\n\ 502 }\n\ 503 }\n\ 504 "; 505 506 JSONWriter w(MakeUnique<StringWriteFunc>()); 507 508 // Test the string escaping behaviour. 509 w.Start(); 510 { 511 // Test all 127 ascii values. Do it in reverse order so that the 0 512 // at the end serves as the null char. 513 char buf[128]; 514 for (int i = 0; i < 128; i++) { 515 buf[i] = 127 - i; 516 } 517 w.StringProperty("ascii", buf); 518 519 // Test lots of unicode stuff. Note that this file is encoded as UTF-8. 520 w.BoolProperty( 521 "\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 " 522 "\xD9\x87\xD9\x86\xD8\xA7\xD9\x83", 523 true); 524 w.IntProperty( 525 "\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1", 526 -123); 527 w.DoubleProperty("\xE4\xBD\xA0\xE5\xA5\xBD", 1.234); 528 w.StringProperty( 529 "\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF", 530 "\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85"); 531 w.IntProperty( 532 "hall\xC3\xB3 \xC3\xBE" 533 "arna", 534 0x1234); 535 w.StartObjectProperty( 536 "\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF"); 537 { 538 w.StartArrayProperty("\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82"); 539 w.EndArray(); 540 } 541 w.EndObject(); 542 } 543 w.End(); 544 545 Check(w, expected); 546 } 547 548 void TestDeepNesting() { 549 const char* expected = 550 "\ 551 {\n\ 552 \"a\": [\n\ 553 {\n\ 554 \"a\": [\n\ 555 {\n\ 556 \"a\": [\n\ 557 {\n\ 558 \"a\": [\n\ 559 {\n\ 560 \"a\": [\n\ 561 {\n\ 562 \"a\": [\n\ 563 {\n\ 564 \"a\": [\n\ 565 {\n\ 566 \"a\": [\n\ 567 {\n\ 568 \"a\": [\n\ 569 {\n\ 570 \"a\": [\n\ 571 {\n\ 572 }\n\ 573 ]\n\ 574 }\n\ 575 ]\n\ 576 }\n\ 577 ]\n\ 578 }\n\ 579 ]\n\ 580 }\n\ 581 ]\n\ 582 }\n\ 583 ]\n\ 584 }\n\ 585 ]\n\ 586 }\n\ 587 ]\n\ 588 }\n\ 589 ]\n\ 590 }\n\ 591 ]\n\ 592 }\n\ 593 "; 594 595 JSONWriter w(MakeUnique<StringWriteFunc>()); 596 597 w.Start(); 598 { 599 static const int n = 10; 600 for (int i = 0; i < n; i++) { 601 w.StartArrayProperty("a"); 602 w.StartObjectElement(); 603 } 604 for (int i = 0; i < n; i++) { 605 w.EndObject(); 606 w.EndArray(); 607 } 608 } 609 w.End(); 610 611 Check(w, expected); 612 } 613 614 void TestEscapedPropertyNames() { 615 const char* expected = 616 "\ 617 {\"i\\t\": 1, \"array\\t\": [null, [{}], {\"o\\t\": {}}, \"s\"], \"d\": 3.33}\n\ 618 "; 619 620 JSONWriter w(MakeUnique<StringWriteFunc>()); 621 622 w.Start(w.SingleLineStyle); 623 624 w.IntProperty("i\t\0cut", 1); // '\0' marks the end. 625 626 w.StartArrayProperty("array\t"); 627 { 628 w.NullElement(); 629 630 w.StartArrayElement(w.MultiLineStyle); // style overridden from above 631 { 632 w.StartObjectElement(); 633 w.EndObject(); 634 } 635 w.EndArray(); 636 637 w.StartObjectElement(); 638 { 639 w.StartObjectProperty("o\t"); 640 w.EndObject(); 641 } 642 w.EndObject(); 643 644 w.StringElement("s"); 645 } 646 w.EndArray(); 647 648 w.DoubleProperty("d\0\t", 3.33); 649 650 w.End(); 651 652 Check(w, expected); 653 } 654 655 int main(void) { 656 TestBasicProperties(); 657 TestBasicElements(); 658 TestOneLineObject(); 659 TestOneLineJson(); 660 TestStringEscaping(); 661 TestDeepNesting(); 662 TestEscapedPropertyNames(); 663 664 return 0; 665 }