]>
Commit | Line | Data |
---|---|---|
75459410 JH |
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 | { | |
1caaa858 ÆAB |
184 | make_obj1(0); |
185 | make_arr1(0); | |
186 | ||
75459410 JH |
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); | |
1caaa858 ÆAB |
193 | |
194 | jw_release(&obj1); | |
195 | jw_release(&arr1); | |
75459410 JH |
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); | |
1caaa858 ÆAB |
322 | |
323 | jw_release(&obj1); | |
324 | jw_release(&arr1); | |
75459410 JH |
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 | ||
1caaa858 ÆAB |
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) | |
75459410 JH |
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); | |
75459410 JH |
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 | ||
1caaa858 | 555 | jw_release(&jw); |
75459410 JH |
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 | } |