xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/emitter.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1 #include "test/jemalloc_test.h"
2 #include "jemalloc/internal/emitter.h"
3 
4 /*
5  * This is so useful for debugging and feature work, we'll leave printing
6  * functionality committed but disabled by default.
7  */
8 /* Print the text as it will appear. */
9 static bool print_raw = false;
10 /* Print the text escaped, so it can be copied back into the test case. */
11 static bool print_escaped = false;
12 
13 typedef struct buf_descriptor_s buf_descriptor_t;
14 struct buf_descriptor_s {
15 	char *buf;
16 	size_t len;
17 	bool mid_quote;
18 };
19 
20 /*
21  * Forwards all writes to the passed-in buf_v (which should be cast from a
22  * buf_descriptor_t *).
23  */
24 static void
25 forwarding_cb(void *buf_descriptor_v, const char *str) {
26 	buf_descriptor_t *buf_descriptor = (buf_descriptor_t *)buf_descriptor_v;
27 
28 	if (print_raw) {
29 		malloc_printf("%s", str);
30 	}
31 	if (print_escaped) {
32 		const char *it = str;
33 		while (*it != '\0') {
34 			if (!buf_descriptor->mid_quote) {
35 				malloc_printf("\"");
36 				buf_descriptor->mid_quote = true;
37 			}
38 			switch (*it) {
39 			case '\\':
40 				malloc_printf("\\");
41 				break;
42 			case '\"':
43 				malloc_printf("\\\"");
44 				break;
45 			case '\t':
46 				malloc_printf("\\t");
47 				break;
48 			case '\n':
49 				malloc_printf("\\n\"\n");
50 				buf_descriptor->mid_quote = false;
51 				break;
52 			default:
53 				malloc_printf("%c", *it);
54 			}
55 			it++;
56 		}
57 	}
58 
59 	size_t written = malloc_snprintf(buf_descriptor->buf,
60 	    buf_descriptor->len, "%s", str);
61 	expect_zu_eq(written, strlen(str), "Buffer overflow!");
62 	buf_descriptor->buf += written;
63 	buf_descriptor->len -= written;
64 	expect_zu_gt(buf_descriptor->len, 0, "Buffer out of space!");
65 }
66 
67 static void
68 expect_emit_output(void (*emit_fn)(emitter_t *),
69     const char *expected_json_output,
70     const char *expected_json_compact_output,
71     const char *expected_table_output) {
72 	emitter_t emitter;
73 	char buf[MALLOC_PRINTF_BUFSIZE];
74 	buf_descriptor_t buf_descriptor;
75 
76 	buf_descriptor.buf = buf;
77 	buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
78 	buf_descriptor.mid_quote = false;
79 
80 	emitter_init(&emitter, emitter_output_json, &forwarding_cb,
81 	    &buf_descriptor);
82 	(*emit_fn)(&emitter);
83 	expect_str_eq(expected_json_output, buf, "json output failure");
84 
85 	buf_descriptor.buf = buf;
86 	buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
87 	buf_descriptor.mid_quote = false;
88 
89 	emitter_init(&emitter, emitter_output_json_compact, &forwarding_cb,
90 	    &buf_descriptor);
91 	(*emit_fn)(&emitter);
92 	expect_str_eq(expected_json_compact_output, buf,
93 	    "compact json output failure");
94 
95 	buf_descriptor.buf = buf;
96 	buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
97 	buf_descriptor.mid_quote = false;
98 
99 	emitter_init(&emitter, emitter_output_table, &forwarding_cb,
100 	    &buf_descriptor);
101 	(*emit_fn)(&emitter);
102 	expect_str_eq(expected_table_output, buf, "table output failure");
103 }
104 
105 static void
106 emit_dict(emitter_t *emitter) {
107 	bool b_false = false;
108 	bool b_true = true;
109 	int i_123 = 123;
110 	const char *str = "a string";
111 
112 	emitter_begin(emitter);
113 	emitter_dict_begin(emitter, "foo", "This is the foo table:");
114 	emitter_kv(emitter, "abc", "ABC", emitter_type_bool, &b_false);
115 	emitter_kv(emitter, "def", "DEF", emitter_type_bool, &b_true);
116 	emitter_kv_note(emitter, "ghi", "GHI", emitter_type_int, &i_123,
117 	    "note_key1", emitter_type_string, &str);
118 	emitter_kv_note(emitter, "jkl", "JKL", emitter_type_string, &str,
119 	    "note_key2", emitter_type_bool, &b_false);
120 	emitter_dict_end(emitter);
121 	emitter_end(emitter);
122 }
123 
124 static const char *dict_json =
125 "{\n"
126 "\t\"foo\": {\n"
127 "\t\t\"abc\": false,\n"
128 "\t\t\"def\": true,\n"
129 "\t\t\"ghi\": 123,\n"
130 "\t\t\"jkl\": \"a string\"\n"
131 "\t}\n"
132 "}\n";
133 static const char *dict_json_compact =
134 "{"
135 	"\"foo\":{"
136 		"\"abc\":false,"
137 		"\"def\":true,"
138 		"\"ghi\":123,"
139 		"\"jkl\":\"a string\""
140 	"}"
141 "}";
142 static const char *dict_table =
143 "This is the foo table:\n"
144 "  ABC: false\n"
145 "  DEF: true\n"
146 "  GHI: 123 (note_key1: \"a string\")\n"
147 "  JKL: \"a string\" (note_key2: false)\n";
148 
149 static void
150 emit_table_printf(emitter_t *emitter) {
151 	emitter_begin(emitter);
152 	emitter_table_printf(emitter, "Table note 1\n");
153 	emitter_table_printf(emitter, "Table note 2 %s\n",
154 	    "with format string");
155 	emitter_end(emitter);
156 }
157 
158 static const char *table_printf_json =
159 "{\n"
160 "}\n";
161 static const char *table_printf_json_compact = "{}";
162 static const char *table_printf_table =
163 "Table note 1\n"
164 "Table note 2 with format string\n";
165 
166 static void emit_nested_dict(emitter_t *emitter) {
167 	int val = 123;
168 	emitter_begin(emitter);
169 	emitter_dict_begin(emitter, "json1", "Dict 1");
170 	emitter_dict_begin(emitter, "json2", "Dict 2");
171 	emitter_kv(emitter, "primitive", "A primitive", emitter_type_int, &val);
172 	emitter_dict_end(emitter); /* Close 2 */
173 	emitter_dict_begin(emitter, "json3", "Dict 3");
174 	emitter_dict_end(emitter); /* Close 3 */
175 	emitter_dict_end(emitter); /* Close 1 */
176 	emitter_dict_begin(emitter, "json4", "Dict 4");
177 	emitter_kv(emitter, "primitive", "Another primitive",
178 	    emitter_type_int, &val);
179 	emitter_dict_end(emitter); /* Close 4 */
180 	emitter_end(emitter);
181 }
182 
183 static const char *nested_dict_json =
184 "{\n"
185 "\t\"json1\": {\n"
186 "\t\t\"json2\": {\n"
187 "\t\t\t\"primitive\": 123\n"
188 "\t\t},\n"
189 "\t\t\"json3\": {\n"
190 "\t\t}\n"
191 "\t},\n"
192 "\t\"json4\": {\n"
193 "\t\t\"primitive\": 123\n"
194 "\t}\n"
195 "}\n";
196 static const char *nested_dict_json_compact =
197 "{"
198 	"\"json1\":{"
199 		"\"json2\":{"
200 			"\"primitive\":123"
201 		"},"
202 		"\"json3\":{"
203 		"}"
204 	"},"
205 	"\"json4\":{"
206 		"\"primitive\":123"
207 	"}"
208 "}";
209 static const char *nested_dict_table =
210 "Dict 1\n"
211 "  Dict 2\n"
212 "    A primitive: 123\n"
213 "  Dict 3\n"
214 "Dict 4\n"
215 "  Another primitive: 123\n";
216 
217 static void
218 emit_types(emitter_t *emitter) {
219 	bool b = false;
220 	int i = -123;
221 	unsigned u = 123;
222 	ssize_t zd = -456;
223 	size_t zu = 456;
224 	const char *str = "string";
225 	uint32_t u32 = 789;
226 	uint64_t u64 = 10000000000ULL;
227 
228 	emitter_begin(emitter);
229 	emitter_kv(emitter, "k1", "K1", emitter_type_bool, &b);
230 	emitter_kv(emitter, "k2", "K2", emitter_type_int, &i);
231 	emitter_kv(emitter, "k3", "K3", emitter_type_unsigned, &u);
232 	emitter_kv(emitter, "k4", "K4", emitter_type_ssize, &zd);
233 	emitter_kv(emitter, "k5", "K5", emitter_type_size, &zu);
234 	emitter_kv(emitter, "k6", "K6", emitter_type_string, &str);
235 	emitter_kv(emitter, "k7", "K7", emitter_type_uint32, &u32);
236 	emitter_kv(emitter, "k8", "K8", emitter_type_uint64, &u64);
237 	/*
238 	 * We don't test the title type, since it's only used for tables.  It's
239 	 * tested in the emitter_table_row tests.
240 	 */
241 	emitter_end(emitter);
242 }
243 
244 static const char *types_json =
245 "{\n"
246 "\t\"k1\": false,\n"
247 "\t\"k2\": -123,\n"
248 "\t\"k3\": 123,\n"
249 "\t\"k4\": -456,\n"
250 "\t\"k5\": 456,\n"
251 "\t\"k6\": \"string\",\n"
252 "\t\"k7\": 789,\n"
253 "\t\"k8\": 10000000000\n"
254 "}\n";
255 static const char *types_json_compact =
256 "{"
257 	"\"k1\":false,"
258 	"\"k2\":-123,"
259 	"\"k3\":123,"
260 	"\"k4\":-456,"
261 	"\"k5\":456,"
262 	"\"k6\":\"string\","
263 	"\"k7\":789,"
264 	"\"k8\":10000000000"
265 "}";
266 static const char *types_table =
267 "K1: false\n"
268 "K2: -123\n"
269 "K3: 123\n"
270 "K4: -456\n"
271 "K5: 456\n"
272 "K6: \"string\"\n"
273 "K7: 789\n"
274 "K8: 10000000000\n";
275 
276 static void
277 emit_modal(emitter_t *emitter) {
278 	int val = 123;
279 	emitter_begin(emitter);
280 	emitter_dict_begin(emitter, "j0", "T0");
281 	emitter_json_key(emitter, "j1");
282 	emitter_json_object_begin(emitter);
283 	emitter_kv(emitter, "i1", "I1", emitter_type_int, &val);
284 	emitter_json_kv(emitter, "i2", emitter_type_int, &val);
285 	emitter_table_kv(emitter, "I3", emitter_type_int, &val);
286 	emitter_table_dict_begin(emitter, "T1");
287 	emitter_kv(emitter, "i4", "I4", emitter_type_int, &val);
288 	emitter_json_object_end(emitter); /* Close j1 */
289 	emitter_kv(emitter, "i5", "I5", emitter_type_int, &val);
290 	emitter_table_dict_end(emitter); /* Close T1 */
291 	emitter_kv(emitter, "i6", "I6", emitter_type_int, &val);
292 	emitter_dict_end(emitter); /* Close j0 / T0 */
293 	emitter_end(emitter);
294 }
295 
296 const char *modal_json =
297 "{\n"
298 "\t\"j0\": {\n"
299 "\t\t\"j1\": {\n"
300 "\t\t\t\"i1\": 123,\n"
301 "\t\t\t\"i2\": 123,\n"
302 "\t\t\t\"i4\": 123\n"
303 "\t\t},\n"
304 "\t\t\"i5\": 123,\n"
305 "\t\t\"i6\": 123\n"
306 "\t}\n"
307 "}\n";
308 const char *modal_json_compact =
309 "{"
310 	"\"j0\":{"
311 		"\"j1\":{"
312 			"\"i1\":123,"
313 			"\"i2\":123,"
314 			"\"i4\":123"
315 		"},"
316 		"\"i5\":123,"
317 		"\"i6\":123"
318 	"}"
319 "}";
320 const char *modal_table =
321 "T0\n"
322 "  I1: 123\n"
323 "  I3: 123\n"
324 "  T1\n"
325 "    I4: 123\n"
326 "    I5: 123\n"
327 "  I6: 123\n";
328 
329 static void
330 emit_json_array(emitter_t *emitter) {
331 	int ival = 123;
332 
333 	emitter_begin(emitter);
334 	emitter_json_key(emitter, "dict");
335 	emitter_json_object_begin(emitter);
336 	emitter_json_key(emitter, "arr");
337 	emitter_json_array_begin(emitter);
338 	emitter_json_object_begin(emitter);
339 	emitter_json_kv(emitter, "foo", emitter_type_int, &ival);
340 	emitter_json_object_end(emitter); /* Close arr[0] */
341 	/* arr[1] and arr[2] are primitives. */
342 	emitter_json_value(emitter, emitter_type_int, &ival);
343 	emitter_json_value(emitter, emitter_type_int, &ival);
344 	emitter_json_object_begin(emitter);
345 	emitter_json_kv(emitter, "bar", emitter_type_int, &ival);
346 	emitter_json_kv(emitter, "baz", emitter_type_int, &ival);
347 	emitter_json_object_end(emitter); /* Close arr[3]. */
348 	emitter_json_array_end(emitter); /* Close arr. */
349 	emitter_json_object_end(emitter); /* Close dict. */
350 	emitter_end(emitter);
351 }
352 
353 static const char *json_array_json =
354 "{\n"
355 "\t\"dict\": {\n"
356 "\t\t\"arr\": [\n"
357 "\t\t\t{\n"
358 "\t\t\t\t\"foo\": 123\n"
359 "\t\t\t},\n"
360 "\t\t\t123,\n"
361 "\t\t\t123,\n"
362 "\t\t\t{\n"
363 "\t\t\t\t\"bar\": 123,\n"
364 "\t\t\t\t\"baz\": 123\n"
365 "\t\t\t}\n"
366 "\t\t]\n"
367 "\t}\n"
368 "}\n";
369 static const char *json_array_json_compact =
370 "{"
371 	"\"dict\":{"
372 		"\"arr\":["
373 			"{"
374 				"\"foo\":123"
375 			"},"
376 			"123,"
377 			"123,"
378 			"{"
379 				"\"bar\":123,"
380 				"\"baz\":123"
381 			"}"
382 		"]"
383 	"}"
384 "}";
385 static const char *json_array_table = "";
386 
387 static void
388 emit_json_nested_array(emitter_t *emitter) {
389 	int ival = 123;
390 	char *sval = "foo";
391 	emitter_begin(emitter);
392 	emitter_json_array_begin(emitter);
393 		emitter_json_array_begin(emitter);
394 		emitter_json_value(emitter, emitter_type_int, &ival);
395 		emitter_json_value(emitter, emitter_type_string, &sval);
396 		emitter_json_value(emitter, emitter_type_int, &ival);
397 		emitter_json_value(emitter, emitter_type_string, &sval);
398 		emitter_json_array_end(emitter);
399 		emitter_json_array_begin(emitter);
400 		emitter_json_value(emitter, emitter_type_int, &ival);
401 		emitter_json_array_end(emitter);
402 		emitter_json_array_begin(emitter);
403 		emitter_json_value(emitter, emitter_type_string, &sval);
404 		emitter_json_value(emitter, emitter_type_int, &ival);
405 		emitter_json_array_end(emitter);
406 		emitter_json_array_begin(emitter);
407 		emitter_json_array_end(emitter);
408 	emitter_json_array_end(emitter);
409 	emitter_end(emitter);
410 }
411 
412 static const char *json_nested_array_json =
413 "{\n"
414 "\t[\n"
415 "\t\t[\n"
416 "\t\t\t123,\n"
417 "\t\t\t\"foo\",\n"
418 "\t\t\t123,\n"
419 "\t\t\t\"foo\"\n"
420 "\t\t],\n"
421 "\t\t[\n"
422 "\t\t\t123\n"
423 "\t\t],\n"
424 "\t\t[\n"
425 "\t\t\t\"foo\",\n"
426 "\t\t\t123\n"
427 "\t\t],\n"
428 "\t\t[\n"
429 "\t\t]\n"
430 "\t]\n"
431 "}\n";
432 static const char *json_nested_array_json_compact =
433 "{"
434 	"["
435 		"["
436 			"123,"
437 			"\"foo\","
438 			"123,"
439 			"\"foo\""
440 		"],"
441 		"["
442 			"123"
443 		"],"
444 		"["
445 			"\"foo\","
446 			"123"
447 		"],"
448 		"["
449 		"]"
450 	"]"
451 "}";
452 static const char *json_nested_array_table = "";
453 
454 static void
455 emit_table_row(emitter_t *emitter) {
456 	emitter_begin(emitter);
457 	emitter_row_t row;
458 	emitter_col_t abc = {emitter_justify_left, 10, emitter_type_title, {0}, {0, 0}};
459 	abc.str_val = "ABC title";
460 	emitter_col_t def = {emitter_justify_right, 15, emitter_type_title, {0}, {0, 0}};
461 	def.str_val = "DEF title";
462 	emitter_col_t ghi = {emitter_justify_right, 5, emitter_type_title, {0}, {0, 0}};
463 	ghi.str_val = "GHI";
464 
465 	emitter_row_init(&row);
466 	emitter_col_init(&abc, &row);
467 	emitter_col_init(&def, &row);
468 	emitter_col_init(&ghi, &row);
469 
470 	emitter_table_row(emitter, &row);
471 
472 	abc.type = emitter_type_int;
473 	def.type = emitter_type_bool;
474 	ghi.type = emitter_type_int;
475 
476 	abc.int_val = 123;
477 	def.bool_val = true;
478 	ghi.int_val = 456;
479 	emitter_table_row(emitter, &row);
480 
481 	abc.int_val = 789;
482 	def.bool_val = false;
483 	ghi.int_val = 1011;
484 	emitter_table_row(emitter, &row);
485 
486 	abc.type = emitter_type_string;
487 	abc.str_val = "a string";
488 	def.bool_val = false;
489 	ghi.type = emitter_type_title;
490 	ghi.str_val = "ghi";
491 	emitter_table_row(emitter, &row);
492 
493 	emitter_end(emitter);
494 }
495 
496 static const char *table_row_json =
497 "{\n"
498 "}\n";
499 static const char *table_row_json_compact = "{}";
500 static const char *table_row_table =
501 "ABC title       DEF title  GHI\n"
502 "123                  true  456\n"
503 "789                 false 1011\n"
504 "\"a string\"          false  ghi\n";
505 
506 #define GENERATE_TEST(feature)					\
507 TEST_BEGIN(test_##feature) {					\
508 	expect_emit_output(emit_##feature, feature##_json,	\
509 	    feature##_json_compact, feature##_table);		\
510 }								\
511 TEST_END
512 
513 GENERATE_TEST(dict)
514 GENERATE_TEST(table_printf)
515 GENERATE_TEST(nested_dict)
516 GENERATE_TEST(types)
517 GENERATE_TEST(modal)
518 GENERATE_TEST(json_array)
519 GENERATE_TEST(json_nested_array)
520 GENERATE_TEST(table_row)
521 
522 int
523 main(void) {
524 	return test_no_reentrancy(
525 	    test_dict,
526 	    test_table_printf,
527 	    test_nested_dict,
528 	    test_types,
529 	    test_modal,
530 	    test_json_array,
531 	    test_json_nested_array,
532 	    test_table_row);
533 }
534