t / helper / test-json-writer.con commit Sync with maint (a9a63af)
   1#include "test-tool.h"
   2#include "cache.h"
   3#include "json-writer.h"
   4
   5static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}";
   6static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}";
   7static const char *expect_obj3 = "{\"a\":0,\"b\":4294967295,\"c\":9223372036854775807}";
   8static const char *expect_obj4 = "{\"t\":true,\"f\":false,\"n\":null}";
   9static const char *expect_obj5 = "{\"abc\\tdef\":\"abc\\\\def\"}";
  10static const char *expect_obj6 = "{\"a\":3.14}";
  11
  12static const char *pretty_obj1 = ("{\n"
  13                                  "  \"a\": \"abc\",\n"
  14                                  "  \"b\": 42,\n"
  15                                  "  \"c\": true\n"
  16                                  "}");
  17static const char *pretty_obj2 = ("{\n"
  18                                  "  \"a\": -1,\n"
  19                                  "  \"b\": 2147483647,\n"
  20                                  "  \"c\": 0\n"
  21                                  "}");
  22static const char *pretty_obj3 = ("{\n"
  23                                  "  \"a\": 0,\n"
  24                                  "  \"b\": 4294967295,\n"
  25                                  "  \"c\": 9223372036854775807\n"
  26                                  "}");
  27static const char *pretty_obj4 = ("{\n"
  28                                  "  \"t\": true,\n"
  29                                  "  \"f\": false,\n"
  30                                  "  \"n\": null\n"
  31                                  "}");
  32
  33static struct json_writer obj1 = JSON_WRITER_INIT;
  34static struct json_writer obj2 = JSON_WRITER_INIT;
  35static struct json_writer obj3 = JSON_WRITER_INIT;
  36static struct json_writer obj4 = JSON_WRITER_INIT;
  37static struct json_writer obj5 = JSON_WRITER_INIT;
  38static struct json_writer obj6 = JSON_WRITER_INIT;
  39
  40static void make_obj1(int pretty)
  41{
  42        jw_object_begin(&obj1, pretty);
  43        {
  44                jw_object_string(&obj1, "a", "abc");
  45                jw_object_intmax(&obj1, "b", 42);
  46                jw_object_true(&obj1, "c");
  47        }
  48        jw_end(&obj1);
  49}
  50
  51static void make_obj2(int pretty)
  52{
  53        jw_object_begin(&obj2, pretty);
  54        {
  55                jw_object_intmax(&obj2, "a", -1);
  56                jw_object_intmax(&obj2, "b", 0x7fffffff);
  57                jw_object_intmax(&obj2, "c", 0);
  58        }
  59        jw_end(&obj2);
  60}
  61
  62static void make_obj3(int pretty)
  63{
  64        jw_object_begin(&obj3, pretty);
  65        {
  66                jw_object_intmax(&obj3, "a", 0);
  67                jw_object_intmax(&obj3, "b", 0xffffffff);
  68                jw_object_intmax(&obj3, "c", 0x7fffffffffffffffULL);
  69        }
  70        jw_end(&obj3);
  71}
  72
  73static void make_obj4(int pretty)
  74{
  75        jw_object_begin(&obj4, pretty);
  76        {
  77                jw_object_true(&obj4, "t");
  78                jw_object_false(&obj4, "f");
  79                jw_object_null(&obj4, "n");
  80        }
  81        jw_end(&obj4);
  82}
  83
  84static void make_obj5(int pretty)
  85{
  86        jw_object_begin(&obj5, pretty);
  87        {
  88                jw_object_string(&obj5, "abc" "\x09" "def", "abc" "\\" "def");
  89        }
  90        jw_end(&obj5);
  91}
  92
  93static void make_obj6(int pretty)
  94{
  95        jw_object_begin(&obj6, pretty);
  96        {
  97                jw_object_double(&obj6, "a", 2, 3.14159);
  98        }
  99        jw_end(&obj6);
 100}
 101
 102static const char *expect_arr1 = "[\"abc\",42,true]";
 103static const char *expect_arr2 = "[-1,2147483647,0]";
 104static const char *expect_arr3 = "[0,4294967295,9223372036854775807]";
 105static const char *expect_arr4 = "[true,false,null]";
 106
 107static const char *pretty_arr1 = ("[\n"
 108                                  "  \"abc\",\n"
 109                                  "  42,\n"
 110                                  "  true\n"
 111                                  "]");
 112static const char *pretty_arr2 = ("[\n"
 113                                  "  -1,\n"
 114                                  "  2147483647,\n"
 115                                  "  0\n"
 116                                  "]");
 117static const char *pretty_arr3 = ("[\n"
 118                                  "  0,\n"
 119                                  "  4294967295,\n"
 120                                  "  9223372036854775807\n"
 121                                  "]");
 122static const char *pretty_arr4 = ("[\n"
 123                                  "  true,\n"
 124                                  "  false,\n"
 125                                  "  null\n"
 126                                  "]");
 127
 128static struct json_writer arr1 = JSON_WRITER_INIT;
 129static struct json_writer arr2 = JSON_WRITER_INIT;
 130static struct json_writer arr3 = JSON_WRITER_INIT;
 131static struct json_writer arr4 = JSON_WRITER_INIT;
 132
 133static void make_arr1(int pretty)
 134{
 135        jw_array_begin(&arr1, pretty);
 136        {
 137                jw_array_string(&arr1, "abc");
 138                jw_array_intmax(&arr1, 42);
 139                jw_array_true(&arr1);
 140        }
 141        jw_end(&arr1);
 142}
 143
 144static void make_arr2(int pretty)
 145{
 146        jw_array_begin(&arr2, pretty);
 147        {
 148                jw_array_intmax(&arr2, -1);
 149                jw_array_intmax(&arr2, 0x7fffffff);
 150                jw_array_intmax(&arr2, 0);
 151        }
 152        jw_end(&arr2);
 153}
 154
 155static void make_arr3(int pretty)
 156{
 157        jw_array_begin(&arr3, pretty);
 158        {
 159                jw_array_intmax(&arr3, 0);
 160                jw_array_intmax(&arr3, 0xffffffff);
 161                jw_array_intmax(&arr3, 0x7fffffffffffffffULL);
 162        }
 163        jw_end(&arr3);
 164}
 165
 166static void make_arr4(int pretty)
 167{
 168        jw_array_begin(&arr4, pretty);
 169        {
 170                jw_array_true(&arr4);
 171                jw_array_false(&arr4);
 172                jw_array_null(&arr4);
 173        }
 174        jw_end(&arr4);
 175}
 176
 177static char *expect_nest1 =
 178        "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 179
 180static struct json_writer nest1 = JSON_WRITER_INIT;
 181
 182static void make_nest1(int pretty)
 183{
 184        jw_object_begin(&nest1, pretty);
 185        {
 186                jw_object_sub_jw(&nest1, "obj1", &obj1);
 187                jw_object_sub_jw(&nest1, "arr1", &arr1);
 188        }
 189        jw_end(&nest1);
 190}
 191
 192static char *expect_inline1 =
 193        "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
 194
 195static char *pretty_inline1 =
 196        ("{\n"
 197         "  \"obj1\": {\n"
 198         "    \"a\": \"abc\",\n"
 199         "    \"b\": 42,\n"
 200         "    \"c\": true\n"
 201         "  },\n"
 202         "  \"arr1\": [\n"
 203         "    \"abc\",\n"
 204         "    42,\n"
 205         "    true\n"
 206         "  ]\n"
 207         "}");
 208
 209static struct json_writer inline1 = JSON_WRITER_INIT;
 210
 211static void make_inline1(int pretty)
 212{
 213        jw_object_begin(&inline1, pretty);
 214        {
 215                jw_object_inline_begin_object(&inline1, "obj1");
 216                {
 217                        jw_object_string(&inline1, "a", "abc");
 218                        jw_object_intmax(&inline1, "b", 42);
 219                        jw_object_true(&inline1, "c");
 220                }
 221                jw_end(&inline1);
 222                jw_object_inline_begin_array(&inline1, "arr1");
 223                {
 224                        jw_array_string(&inline1, "abc");
 225                        jw_array_intmax(&inline1, 42);
 226                        jw_array_true(&inline1);
 227                }
 228                jw_end(&inline1);
 229        }
 230        jw_end(&inline1);
 231}
 232
 233static char *expect_inline2 =
 234        "[[1,2],[3,4],{\"a\":\"abc\"}]";
 235
 236static char *pretty_inline2 =
 237        ("[\n"
 238         "  [\n"
 239         "    1,\n"
 240         "    2\n"
 241         "  ],\n"
 242         "  [\n"
 243         "    3,\n"
 244         "    4\n"
 245         "  ],\n"
 246         "  {\n"
 247         "    \"a\": \"abc\"\n"
 248         "  }\n"
 249         "]");
 250
 251static struct json_writer inline2 = JSON_WRITER_INIT;
 252
 253static void make_inline2(int pretty)
 254{
 255        jw_array_begin(&inline2, pretty);
 256        {
 257                jw_array_inline_begin_array(&inline2);
 258                {
 259                        jw_array_intmax(&inline2, 1);
 260                        jw_array_intmax(&inline2, 2);
 261                }
 262                jw_end(&inline2);
 263                jw_array_inline_begin_array(&inline2);
 264                {
 265                        jw_array_intmax(&inline2, 3);
 266                        jw_array_intmax(&inline2, 4);
 267                }
 268                jw_end(&inline2);
 269                jw_array_inline_begin_object(&inline2);
 270                {
 271                        jw_object_string(&inline2, "a", "abc");
 272                }
 273                jw_end(&inline2);
 274        }
 275        jw_end(&inline2);
 276}
 277
 278/*
 279 * When super is compact, we expect subs to be compacted (even if originally
 280 * pretty).
 281 */
 282static const char *expect_mixed1 =
 283        ("{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},"
 284         "\"arr1\":[\"abc\",42,true]}");
 285
 286/*
 287 * When super is pretty, a compact sub (obj1) is kept compact and a pretty
 288 * sub (arr1) is re-indented.
 289 */
 290static const char *pretty_mixed1 =
 291        ("{\n"
 292         "  \"obj1\": {\"a\":\"abc\",\"b\":42,\"c\":true},\n"
 293         "  \"arr1\": [\n"
 294         "    \"abc\",\n"
 295         "    42,\n"
 296         "    true\n"
 297         "  ]\n"
 298         "}");
 299
 300static struct json_writer mixed1 = JSON_WRITER_INIT;
 301
 302static void make_mixed1(int pretty)
 303{
 304        jw_init(&obj1);
 305        jw_init(&arr1);
 306
 307        make_obj1(0); /* obj1 is compact */
 308        make_arr1(1); /* arr1 is pretty */
 309
 310        jw_object_begin(&mixed1, pretty);
 311        {
 312                jw_object_sub_jw(&mixed1, "obj1", &obj1);
 313                jw_object_sub_jw(&mixed1, "arr1", &arr1);
 314        }
 315        jw_end(&mixed1);
 316}
 317
 318static void cmp(const char *test, const struct json_writer *jw, const char *exp)
 319{
 320        if (!strcmp(jw->json.buf, exp))
 321                return;
 322
 323        printf("error[%s]: observed '%s' expected '%s'\n",
 324               test, jw->json.buf, exp);
 325        exit(1);
 326}
 327
 328#define t(v) do { make_##v(0); cmp(#v, &v, expect_##v); } while (0)
 329#define p(v) do { make_##v(1); cmp(#v, &v, pretty_##v); } while (0)
 330
 331/*
 332 * Run some basic regression tests with some known patterns.
 333 * These tests also demonstrate how to use the jw_ API.
 334 */
 335static int unit_tests(void)
 336{
 337        /* comptact (canonical) forms */
 338        t(obj1);
 339        t(obj2);
 340        t(obj3);
 341        t(obj4);
 342        t(obj5);
 343        t(obj6);
 344
 345        t(arr1);
 346        t(arr2);
 347        t(arr3);
 348        t(arr4);
 349
 350        t(nest1);
 351
 352        t(inline1);
 353        t(inline2);
 354
 355        jw_init(&obj1);
 356        jw_init(&obj2);
 357        jw_init(&obj3);
 358        jw_init(&obj4);
 359
 360        jw_init(&arr1);
 361        jw_init(&arr2);
 362        jw_init(&arr3);
 363        jw_init(&arr4);
 364
 365        jw_init(&inline1);
 366        jw_init(&inline2);
 367
 368        /* pretty forms */
 369        p(obj1);
 370        p(obj2);
 371        p(obj3);
 372        p(obj4);
 373
 374        p(arr1);
 375        p(arr2);
 376        p(arr3);
 377        p(arr4);
 378
 379        p(inline1);
 380        p(inline2);
 381
 382        /* mixed forms */
 383        t(mixed1);
 384        jw_init(&mixed1);
 385        p(mixed1);
 386
 387        return 0;
 388}
 389
 390static void get_s(int line_nr, char **s_in)
 391{
 392        *s_in = strtok(NULL, " ");
 393        if (!*s_in)
 394                die("line[%d]: expected: <s>", line_nr);
 395}
 396
 397static void get_i(int line_nr, intmax_t *s_in)
 398{
 399        char *s;
 400        char *endptr;
 401
 402        get_s(line_nr, &s);
 403
 404        *s_in = strtol(s, &endptr, 10);
 405        if (*endptr || errno == ERANGE)
 406                die("line[%d]: invalid integer value", line_nr);
 407}
 408
 409static void get_d(int line_nr, double *s_in)
 410{
 411        char *s;
 412        char *endptr;
 413
 414        get_s(line_nr, &s);
 415
 416        *s_in = strtod(s, &endptr);
 417        if (*endptr || errno == ERANGE)
 418                die("line[%d]: invalid float value", line_nr);
 419}
 420
 421static int pretty;
 422
 423#define MAX_LINE_LENGTH (64 * 1024)
 424
 425static char *get_trimmed_line(char *buf, int buf_size)
 426{
 427        int len;
 428
 429        if (!fgets(buf, buf_size, stdin))
 430                return NULL;
 431
 432        len = strlen(buf);
 433        while (len > 0) {
 434                char c = buf[len - 1];
 435                if (c == '\n' || c == '\r' || c == ' ' || c == '\t')
 436                        buf[--len] = 0;
 437                else
 438                        break;
 439        }
 440
 441        while (*buf == ' ' || *buf == '\t')
 442                buf++;
 443
 444        return buf;
 445}
 446
 447static int scripted(void)
 448{
 449        struct json_writer jw = JSON_WRITER_INIT;
 450        char buf[MAX_LINE_LENGTH];
 451        char *line;
 452        int line_nr = 0;
 453
 454        line = get_trimmed_line(buf, MAX_LINE_LENGTH);
 455        if (!line)
 456                return 0;
 457
 458        if (!strcmp(line, "object"))
 459                jw_object_begin(&jw, pretty);
 460        else if (!strcmp(line, "array"))
 461                jw_array_begin(&jw, pretty);
 462        else
 463                die("expected first line to be 'object' or 'array'");
 464
 465        while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) {
 466                char *verb;
 467                char *key;
 468                char *s_value;
 469                intmax_t i_value;
 470                double d_value;
 471
 472                line_nr++;
 473
 474                verb = strtok(line, " ");
 475
 476                if (!strcmp(verb, "end")) {
 477                        jw_end(&jw);
 478                }
 479                else if (!strcmp(verb, "object-string")) {
 480                        get_s(line_nr, &key);
 481                        get_s(line_nr, &s_value);
 482                        jw_object_string(&jw, key, s_value);
 483                }
 484                else if (!strcmp(verb, "object-int")) {
 485                        get_s(line_nr, &key);
 486                        get_i(line_nr, &i_value);
 487                        jw_object_intmax(&jw, key, i_value);
 488                }
 489                else if (!strcmp(verb, "object-double")) {
 490                        get_s(line_nr, &key);
 491                        get_i(line_nr, &i_value);
 492                        get_d(line_nr, &d_value);
 493                        jw_object_double(&jw, key, i_value, d_value);
 494                }
 495                else if (!strcmp(verb, "object-true")) {
 496                        get_s(line_nr, &key);
 497                        jw_object_true(&jw, key);
 498                }
 499                else if (!strcmp(verb, "object-false")) {
 500                        get_s(line_nr, &key);
 501                        jw_object_false(&jw, key);
 502                }
 503                else if (!strcmp(verb, "object-null")) {
 504                        get_s(line_nr, &key);
 505                        jw_object_null(&jw, key);
 506                }
 507                else if (!strcmp(verb, "object-object")) {
 508                        get_s(line_nr, &key);
 509                        jw_object_inline_begin_object(&jw, key);
 510                }
 511                else if (!strcmp(verb, "object-array")) {
 512                        get_s(line_nr, &key);
 513                        jw_object_inline_begin_array(&jw, key);
 514                }
 515                else if (!strcmp(verb, "array-string")) {
 516                        get_s(line_nr, &s_value);
 517                        jw_array_string(&jw, s_value);
 518                }
 519                else if (!strcmp(verb, "array-int")) {
 520                        get_i(line_nr, &i_value);
 521                        jw_array_intmax(&jw, i_value);
 522                }
 523                else if (!strcmp(verb, "array-double")) {
 524                        get_i(line_nr, &i_value);
 525                        get_d(line_nr, &d_value);
 526                        jw_array_double(&jw, i_value, d_value);
 527                }
 528                else if (!strcmp(verb, "array-true"))
 529                        jw_array_true(&jw);
 530                else if (!strcmp(verb, "array-false"))
 531                        jw_array_false(&jw);
 532                else if (!strcmp(verb, "array-null"))
 533                        jw_array_null(&jw);
 534                else if (!strcmp(verb, "array-object"))
 535                        jw_array_inline_begin_object(&jw);
 536                else if (!strcmp(verb, "array-array"))
 537                        jw_array_inline_begin_array(&jw);
 538                else
 539                        die("unrecognized token: '%s'", verb);
 540        }
 541
 542        if (!jw_is_terminated(&jw))
 543                die("json not terminated: '%s'", jw.json.buf);
 544
 545        printf("%s\n", jw.json.buf);
 546
 547        strbuf_release(&jw.json);
 548        return 0;
 549}
 550
 551int cmd__json_writer(int argc, const char **argv)
 552{
 553        argc--; /* skip over "json-writer" arg */
 554        argv++;
 555
 556        if (argc > 0 && argv[0][0] == '-') {
 557                if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--unit"))
 558                        return unit_tests();
 559
 560                if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--pretty"))
 561                        pretty = 1;
 562        }
 563
 564        return scripted();
 565}