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
+}