Merge branch 'sg/t4051-fix'
[git] / t / helper / test-json-writer.c
1 #include "test-tool.h"
2 #include "cache.h"
3 #include "json-writer.h"
4
5 static const char *expect_obj1 = "{\"a\":\"abc\",\"b\":42,\"c\":true}";
6 static const char *expect_obj2 = "{\"a\":-1,\"b\":2147483647,\"c\":0}";
7 static const char *expect_obj3 = "{\"a\":0,\"b\":4294967295,\"c\":9223372036854775807}";
8 static const char *expect_obj4 = "{\"t\":true,\"f\":false,\"n\":null}";
9 static const char *expect_obj5 = "{\"abc\\tdef\":\"abc\\\\def\"}";
10 static const char *expect_obj6 = "{\"a\":3.14}";
11
12 static const char *pretty_obj1 = ("{\n"
13                                   "  \"a\": \"abc\",\n"
14                                   "  \"b\": 42,\n"
15                                   "  \"c\": true\n"
16                                   "}");
17 static const char *pretty_obj2 = ("{\n"
18                                   "  \"a\": -1,\n"
19                                   "  \"b\": 2147483647,\n"
20                                   "  \"c\": 0\n"
21                                   "}");
22 static const char *pretty_obj3 = ("{\n"
23                                   "  \"a\": 0,\n"
24                                   "  \"b\": 4294967295,\n"
25                                   "  \"c\": 9223372036854775807\n"
26                                   "}");
27 static const char *pretty_obj4 = ("{\n"
28                                   "  \"t\": true,\n"
29                                   "  \"f\": false,\n"
30                                   "  \"n\": null\n"
31                                   "}");
32
33 static struct json_writer obj1 = JSON_WRITER_INIT;
34 static struct json_writer obj2 = JSON_WRITER_INIT;
35 static struct json_writer obj3 = JSON_WRITER_INIT;
36 static struct json_writer obj4 = JSON_WRITER_INIT;
37 static struct json_writer obj5 = JSON_WRITER_INIT;
38 static struct json_writer obj6 = JSON_WRITER_INIT;
39
40 static 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
51 static 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
62 static 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
73 static 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
84 static 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
93 static 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
102 static const char *expect_arr1 = "[\"abc\",42,true]";
103 static const char *expect_arr2 = "[-1,2147483647,0]";
104 static const char *expect_arr3 = "[0,4294967295,9223372036854775807]";
105 static const char *expect_arr4 = "[true,false,null]";
106
107 static const char *pretty_arr1 = ("[\n"
108                                   "  \"abc\",\n"
109                                   "  42,\n"
110                                   "  true\n"
111                                   "]");
112 static const char *pretty_arr2 = ("[\n"
113                                   "  -1,\n"
114                                   "  2147483647,\n"
115                                   "  0\n"
116                                   "]");
117 static const char *pretty_arr3 = ("[\n"
118                                   "  0,\n"
119                                   "  4294967295,\n"
120                                   "  9223372036854775807\n"
121                                   "]");
122 static const char *pretty_arr4 = ("[\n"
123                                   "  true,\n"
124                                   "  false,\n"
125                                   "  null\n"
126                                   "]");
127
128 static struct json_writer arr1 = JSON_WRITER_INIT;
129 static struct json_writer arr2 = JSON_WRITER_INIT;
130 static struct json_writer arr3 = JSON_WRITER_INIT;
131 static struct json_writer arr4 = JSON_WRITER_INIT;
132
133 static 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
144 static 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
155 static 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
166 static 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
177 static char *expect_nest1 =
178         "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
179
180 static struct json_writer nest1 = JSON_WRITER_INIT;
181
182 static 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
192 static char *expect_inline1 =
193         "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
194
195 static 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
209 static struct json_writer inline1 = JSON_WRITER_INIT;
210
211 static 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
233 static char *expect_inline2 =
234         "[[1,2],[3,4],{\"a\":\"abc\"}]";
235
236 static 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
251 static struct json_writer inline2 = JSON_WRITER_INIT;
252
253 static 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  */
282 static 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  */
290 static 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
300 static struct json_writer mixed1 = JSON_WRITER_INIT;
301
302 static 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
318 static 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  */
335 static 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
390 static 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
397 static 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
409 static 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
421 static int pretty;
422
423 #define MAX_LINE_LENGTH (64 * 1024)
424
425 static 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
447 static 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
551 int 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 }