]> git.ipfire.org Git - thirdparty/git.git/blob - t/helper/test-json-writer.c
Merge branch 'jk/clone-allow-bare-and-o-together'
[thirdparty/git.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 make_obj1(0);
185 make_arr1(0);
186
187 jw_object_begin(&nest1, pretty);
188 {
189 jw_object_sub_jw(&nest1, "obj1", &obj1);
190 jw_object_sub_jw(&nest1, "arr1", &arr1);
191 }
192 jw_end(&nest1);
193
194 jw_release(&obj1);
195 jw_release(&arr1);
196 }
197
198 static char *expect_inline1 =
199 "{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},\"arr1\":[\"abc\",42,true]}";
200
201 static char *pretty_inline1 =
202 ("{\n"
203 " \"obj1\": {\n"
204 " \"a\": \"abc\",\n"
205 " \"b\": 42,\n"
206 " \"c\": true\n"
207 " },\n"
208 " \"arr1\": [\n"
209 " \"abc\",\n"
210 " 42,\n"
211 " true\n"
212 " ]\n"
213 "}");
214
215 static struct json_writer inline1 = JSON_WRITER_INIT;
216
217 static void make_inline1(int pretty)
218 {
219 jw_object_begin(&inline1, pretty);
220 {
221 jw_object_inline_begin_object(&inline1, "obj1");
222 {
223 jw_object_string(&inline1, "a", "abc");
224 jw_object_intmax(&inline1, "b", 42);
225 jw_object_true(&inline1, "c");
226 }
227 jw_end(&inline1);
228 jw_object_inline_begin_array(&inline1, "arr1");
229 {
230 jw_array_string(&inline1, "abc");
231 jw_array_intmax(&inline1, 42);
232 jw_array_true(&inline1);
233 }
234 jw_end(&inline1);
235 }
236 jw_end(&inline1);
237 }
238
239 static char *expect_inline2 =
240 "[[1,2],[3,4],{\"a\":\"abc\"}]";
241
242 static char *pretty_inline2 =
243 ("[\n"
244 " [\n"
245 " 1,\n"
246 " 2\n"
247 " ],\n"
248 " [\n"
249 " 3,\n"
250 " 4\n"
251 " ],\n"
252 " {\n"
253 " \"a\": \"abc\"\n"
254 " }\n"
255 "]");
256
257 static struct json_writer inline2 = JSON_WRITER_INIT;
258
259 static void make_inline2(int pretty)
260 {
261 jw_array_begin(&inline2, pretty);
262 {
263 jw_array_inline_begin_array(&inline2);
264 {
265 jw_array_intmax(&inline2, 1);
266 jw_array_intmax(&inline2, 2);
267 }
268 jw_end(&inline2);
269 jw_array_inline_begin_array(&inline2);
270 {
271 jw_array_intmax(&inline2, 3);
272 jw_array_intmax(&inline2, 4);
273 }
274 jw_end(&inline2);
275 jw_array_inline_begin_object(&inline2);
276 {
277 jw_object_string(&inline2, "a", "abc");
278 }
279 jw_end(&inline2);
280 }
281 jw_end(&inline2);
282 }
283
284 /*
285 * When super is compact, we expect subs to be compacted (even if originally
286 * pretty).
287 */
288 static const char *expect_mixed1 =
289 ("{\"obj1\":{\"a\":\"abc\",\"b\":42,\"c\":true},"
290 "\"arr1\":[\"abc\",42,true]}");
291
292 /*
293 * When super is pretty, a compact sub (obj1) is kept compact and a pretty
294 * sub (arr1) is re-indented.
295 */
296 static const char *pretty_mixed1 =
297 ("{\n"
298 " \"obj1\": {\"a\":\"abc\",\"b\":42,\"c\":true},\n"
299 " \"arr1\": [\n"
300 " \"abc\",\n"
301 " 42,\n"
302 " true\n"
303 " ]\n"
304 "}");
305
306 static struct json_writer mixed1 = JSON_WRITER_INIT;
307
308 static void make_mixed1(int pretty)
309 {
310 jw_init(&obj1);
311 jw_init(&arr1);
312
313 make_obj1(0); /* obj1 is compact */
314 make_arr1(1); /* arr1 is pretty */
315
316 jw_object_begin(&mixed1, pretty);
317 {
318 jw_object_sub_jw(&mixed1, "obj1", &obj1);
319 jw_object_sub_jw(&mixed1, "arr1", &arr1);
320 }
321 jw_end(&mixed1);
322
323 jw_release(&obj1);
324 jw_release(&arr1);
325 }
326
327 static void cmp(const char *test, const struct json_writer *jw, const char *exp)
328 {
329 if (!strcmp(jw->json.buf, exp))
330 return;
331
332 printf("error[%s]: observed '%s' expected '%s'\n",
333 test, jw->json.buf, exp);
334 exit(1);
335 }
336
337 #define t(v) do { make_##v(0); cmp(#v, &v, expect_##v); jw_release(&v); } while (0)
338 #define p(v) do { make_##v(1); cmp(#v, &v, pretty_##v); jw_release(&v); } while (0)
339
340 /*
341 * Run some basic regression tests with some known patterns.
342 * These tests also demonstrate how to use the jw_ API.
343 */
344 static int unit_tests(void)
345 {
346 /* comptact (canonical) forms */
347 t(obj1);
348 t(obj2);
349 t(obj3);
350 t(obj4);
351 t(obj5);
352 t(obj6);
353
354 t(arr1);
355 t(arr2);
356 t(arr3);
357 t(arr4);
358
359 t(nest1);
360
361 t(inline1);
362 t(inline2);
363
364 jw_init(&obj1);
365 jw_init(&obj2);
366 jw_init(&obj3);
367 jw_init(&obj4);
368
369 jw_init(&arr1);
370 jw_init(&arr2);
371 jw_init(&arr3);
372 jw_init(&arr4);
373
374 jw_init(&inline1);
375 jw_init(&inline2);
376
377 /* pretty forms */
378 p(obj1);
379 p(obj2);
380 p(obj3);
381 p(obj4);
382
383 p(arr1);
384 p(arr2);
385 p(arr3);
386 p(arr4);
387
388 p(inline1);
389 p(inline2);
390
391 /* mixed forms */
392 t(mixed1);
393 p(mixed1);
394
395 return 0;
396 }
397
398 static void get_s(int line_nr, char **s_in)
399 {
400 *s_in = strtok(NULL, " ");
401 if (!*s_in)
402 die("line[%d]: expected: <s>", line_nr);
403 }
404
405 static void get_i(int line_nr, intmax_t *s_in)
406 {
407 char *s;
408 char *endptr;
409
410 get_s(line_nr, &s);
411
412 *s_in = strtol(s, &endptr, 10);
413 if (*endptr || errno == ERANGE)
414 die("line[%d]: invalid integer value", line_nr);
415 }
416
417 static void get_d(int line_nr, double *s_in)
418 {
419 char *s;
420 char *endptr;
421
422 get_s(line_nr, &s);
423
424 *s_in = strtod(s, &endptr);
425 if (*endptr || errno == ERANGE)
426 die("line[%d]: invalid float value", line_nr);
427 }
428
429 static int pretty;
430
431 #define MAX_LINE_LENGTH (64 * 1024)
432
433 static char *get_trimmed_line(char *buf, int buf_size)
434 {
435 int len;
436
437 if (!fgets(buf, buf_size, stdin))
438 return NULL;
439
440 len = strlen(buf);
441 while (len > 0) {
442 char c = buf[len - 1];
443 if (c == '\n' || c == '\r' || c == ' ' || c == '\t')
444 buf[--len] = 0;
445 else
446 break;
447 }
448
449 while (*buf == ' ' || *buf == '\t')
450 buf++;
451
452 return buf;
453 }
454
455 static int scripted(void)
456 {
457 struct json_writer jw = JSON_WRITER_INIT;
458 char buf[MAX_LINE_LENGTH];
459 char *line;
460 int line_nr = 0;
461
462 line = get_trimmed_line(buf, MAX_LINE_LENGTH);
463 if (!line)
464 return 0;
465
466 if (!strcmp(line, "object"))
467 jw_object_begin(&jw, pretty);
468 else if (!strcmp(line, "array"))
469 jw_array_begin(&jw, pretty);
470 else
471 die("expected first line to be 'object' or 'array'");
472
473 while ((line = get_trimmed_line(buf, MAX_LINE_LENGTH)) != NULL) {
474 char *verb;
475 char *key;
476 char *s_value;
477 intmax_t i_value;
478 double d_value;
479
480 line_nr++;
481
482 verb = strtok(line, " ");
483
484 if (!strcmp(verb, "end")) {
485 jw_end(&jw);
486 }
487 else if (!strcmp(verb, "object-string")) {
488 get_s(line_nr, &key);
489 get_s(line_nr, &s_value);
490 jw_object_string(&jw, key, s_value);
491 }
492 else if (!strcmp(verb, "object-int")) {
493 get_s(line_nr, &key);
494 get_i(line_nr, &i_value);
495 jw_object_intmax(&jw, key, i_value);
496 }
497 else if (!strcmp(verb, "object-double")) {
498 get_s(line_nr, &key);
499 get_i(line_nr, &i_value);
500 get_d(line_nr, &d_value);
501 jw_object_double(&jw, key, i_value, d_value);
502 }
503 else if (!strcmp(verb, "object-true")) {
504 get_s(line_nr, &key);
505 jw_object_true(&jw, key);
506 }
507 else if (!strcmp(verb, "object-false")) {
508 get_s(line_nr, &key);
509 jw_object_false(&jw, key);
510 }
511 else if (!strcmp(verb, "object-null")) {
512 get_s(line_nr, &key);
513 jw_object_null(&jw, key);
514 }
515 else if (!strcmp(verb, "object-object")) {
516 get_s(line_nr, &key);
517 jw_object_inline_begin_object(&jw, key);
518 }
519 else if (!strcmp(verb, "object-array")) {
520 get_s(line_nr, &key);
521 jw_object_inline_begin_array(&jw, key);
522 }
523 else if (!strcmp(verb, "array-string")) {
524 get_s(line_nr, &s_value);
525 jw_array_string(&jw, s_value);
526 }
527 else if (!strcmp(verb, "array-int")) {
528 get_i(line_nr, &i_value);
529 jw_array_intmax(&jw, i_value);
530 }
531 else if (!strcmp(verb, "array-double")) {
532 get_i(line_nr, &i_value);
533 get_d(line_nr, &d_value);
534 jw_array_double(&jw, i_value, d_value);
535 }
536 else if (!strcmp(verb, "array-true"))
537 jw_array_true(&jw);
538 else if (!strcmp(verb, "array-false"))
539 jw_array_false(&jw);
540 else if (!strcmp(verb, "array-null"))
541 jw_array_null(&jw);
542 else if (!strcmp(verb, "array-object"))
543 jw_array_inline_begin_object(&jw);
544 else if (!strcmp(verb, "array-array"))
545 jw_array_inline_begin_array(&jw);
546 else
547 die("unrecognized token: '%s'", verb);
548 }
549
550 if (!jw_is_terminated(&jw))
551 die("json not terminated: '%s'", jw.json.buf);
552
553 printf("%s\n", jw.json.buf);
554
555 jw_release(&jw);
556 return 0;
557 }
558
559 int cmd__json_writer(int argc, const char **argv)
560 {
561 argc--; /* skip over "json-writer" arg */
562 argv++;
563
564 if (argc > 0 && argv[0][0] == '-') {
565 if (!strcmp(argv[0], "-u") || !strcmp(argv[0], "--unit"))
566 return unit_tests();
567
568 if (!strcmp(argv[0], "-p") || !strcmp(argv[0], "--pretty"))
569 pretty = 1;
570 }
571
572 return scripted();
573 }