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 
make_obj1(int pretty)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 
make_obj2(int pretty)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 
make_obj3(int pretty)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 
make_obj4(int pretty)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 
make_obj5(int pretty)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 
make_obj6(int pretty)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 
make_arr1(int pretty)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 
make_arr2(int pretty)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 
make_arr3(int pretty)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 
make_arr4(int pretty)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 
make_nest1(int pretty)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 
make_inline1(int pretty)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 
make_inline2(int pretty)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 
make_mixed1(int pretty)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 
cmp(const char * test,const struct json_writer * jw,const char * exp)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  */
unit_tests(void)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 
get_s(int line_nr,char ** s_in)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 
get_i(int line_nr,intmax_t * s_in)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 
get_d(int line_nr,double * s_in)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 
get_trimmed_line(char * buf,int buf_size)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 
scripted(void)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 
cmd__json_writer(int argc,const char ** argv)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 }
566