mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-19 07:07:18 +03:00
800 lines
22 KiB
C
800 lines
22 KiB
C
#include "test/jemalloc_test.h"
|
|
#include "jemalloc/internal/emitter.h"
|
|
|
|
/*
|
|
* This is so useful for debugging and feature work, we'll leave printing
|
|
* functionality committed but disabled by default.
|
|
*/
|
|
/* Print the text as it will appear. */
|
|
static bool print_raw = false;
|
|
/* Print the text escaped, so it can be copied back into the test case. */
|
|
static bool print_escaped = false;
|
|
|
|
typedef struct buf_descriptor_s buf_descriptor_t;
|
|
struct buf_descriptor_s {
|
|
char *buf;
|
|
size_t len;
|
|
bool mid_quote;
|
|
};
|
|
|
|
/*
|
|
* Forwards all writes to the passed-in buf_v (which should be cast from a
|
|
* buf_descriptor_t *).
|
|
*/
|
|
static void
|
|
forwarding_cb(void *buf_descriptor_v, const char *str) {
|
|
buf_descriptor_t *buf_descriptor = (buf_descriptor_t *)buf_descriptor_v;
|
|
|
|
if (print_raw) {
|
|
malloc_printf("%s", str);
|
|
}
|
|
if (print_escaped) {
|
|
const char *it = str;
|
|
while (*it != '\0') {
|
|
if (!buf_descriptor->mid_quote) {
|
|
malloc_printf("\"");
|
|
buf_descriptor->mid_quote = true;
|
|
}
|
|
switch (*it) {
|
|
case '\\':
|
|
malloc_printf("\\");
|
|
break;
|
|
case '\"':
|
|
malloc_printf("\\\"");
|
|
break;
|
|
case '\t':
|
|
malloc_printf("\\t");
|
|
break;
|
|
case '\n':
|
|
malloc_printf("\\n\"\n");
|
|
buf_descriptor->mid_quote = false;
|
|
break;
|
|
default:
|
|
malloc_printf("%c", *it);
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
|
|
size_t written = malloc_snprintf(
|
|
buf_descriptor->buf, buf_descriptor->len, "%s", str);
|
|
expect_zu_eq(written, strlen(str), "Buffer overflow!");
|
|
buf_descriptor->buf += written;
|
|
buf_descriptor->len -= written;
|
|
expect_zu_gt(buf_descriptor->len, 0, "Buffer out of space!");
|
|
}
|
|
|
|
static void
|
|
expect_emit_output(void (*emit_fn)(emitter_t *),
|
|
const char *expected_json_output, const char *expected_json_compact_output,
|
|
const char *expected_table_output) {
|
|
emitter_t emitter;
|
|
char buf[MALLOC_PRINTF_BUFSIZE];
|
|
buf_descriptor_t buf_descriptor;
|
|
|
|
buf_descriptor.buf = buf;
|
|
buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
|
|
buf_descriptor.mid_quote = false;
|
|
|
|
emitter_init(
|
|
&emitter, emitter_output_json, &forwarding_cb, &buf_descriptor);
|
|
(*emit_fn)(&emitter);
|
|
expect_str_eq(expected_json_output, buf, "json output failure");
|
|
|
|
buf_descriptor.buf = buf;
|
|
buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
|
|
buf_descriptor.mid_quote = false;
|
|
|
|
emitter_init(&emitter, emitter_output_json_compact, &forwarding_cb,
|
|
&buf_descriptor);
|
|
(*emit_fn)(&emitter);
|
|
expect_str_eq(
|
|
expected_json_compact_output, buf, "compact json output failure");
|
|
|
|
buf_descriptor.buf = buf;
|
|
buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
|
|
buf_descriptor.mid_quote = false;
|
|
|
|
emitter_init(
|
|
&emitter, emitter_output_table, &forwarding_cb, &buf_descriptor);
|
|
(*emit_fn)(&emitter);
|
|
expect_str_eq(expected_table_output, buf, "table output failure");
|
|
}
|
|
|
|
static void
|
|
emit_dict(emitter_t *emitter) {
|
|
bool b_false = false;
|
|
bool b_true = true;
|
|
int i_123 = 123;
|
|
const char *str = "a string";
|
|
|
|
emitter_begin(emitter);
|
|
emitter_dict_begin(emitter, "foo", "This is the foo table:");
|
|
emitter_kv(emitter, "abc", "ABC", emitter_type_bool, &b_false);
|
|
emitter_kv(emitter, "def", "DEF", emitter_type_bool, &b_true);
|
|
emitter_kv_note(emitter, "ghi", "GHI", emitter_type_int, &i_123,
|
|
"note_key1", emitter_type_string, &str);
|
|
emitter_kv_note(emitter, "jkl", "JKL", emitter_type_string, &str,
|
|
"note_key2", emitter_type_bool, &b_false);
|
|
emitter_dict_end(emitter);
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *dict_json =
|
|
"{\n"
|
|
"\t\"foo\": {\n"
|
|
"\t\t\"abc\": false,\n"
|
|
"\t\t\"def\": true,\n"
|
|
"\t\t\"ghi\": 123,\n"
|
|
"\t\t\"jkl\": \"a string\"\n"
|
|
"\t}\n"
|
|
"}\n";
|
|
static const char *dict_json_compact =
|
|
"{"
|
|
"\"foo\":{"
|
|
"\"abc\":false,"
|
|
"\"def\":true,"
|
|
"\"ghi\":123,"
|
|
"\"jkl\":\"a string\""
|
|
"}"
|
|
"}";
|
|
static const char *dict_table =
|
|
"This is the foo table:\n"
|
|
" ABC: false\n"
|
|
" DEF: true\n"
|
|
" GHI: 123 (note_key1: \"a string\")\n"
|
|
" JKL: \"a string\" (note_key2: false)\n";
|
|
|
|
static void
|
|
emit_table_printf(emitter_t *emitter) {
|
|
emitter_begin(emitter);
|
|
emitter_table_printf(emitter, "Table note 1\n");
|
|
emitter_table_printf(
|
|
emitter, "Table note 2 %s\n", "with format string");
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *table_printf_json =
|
|
"{\n"
|
|
"}\n";
|
|
static const char *table_printf_json_compact = "{}";
|
|
static const char *table_printf_table =
|
|
"Table note 1\n"
|
|
"Table note 2 with format string\n";
|
|
|
|
static void
|
|
emit_nested_dict(emitter_t *emitter) {
|
|
int val = 123;
|
|
emitter_begin(emitter);
|
|
emitter_dict_begin(emitter, "json1", "Dict 1");
|
|
emitter_dict_begin(emitter, "json2", "Dict 2");
|
|
emitter_kv(emitter, "primitive", "A primitive", emitter_type_int, &val);
|
|
emitter_dict_end(emitter); /* Close 2 */
|
|
emitter_dict_begin(emitter, "json3", "Dict 3");
|
|
emitter_dict_end(emitter); /* Close 3 */
|
|
emitter_dict_end(emitter); /* Close 1 */
|
|
emitter_dict_begin(emitter, "json4", "Dict 4");
|
|
emitter_kv(
|
|
emitter, "primitive", "Another primitive", emitter_type_int, &val);
|
|
emitter_dict_end(emitter); /* Close 4 */
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *nested_dict_json =
|
|
"{\n"
|
|
"\t\"json1\": {\n"
|
|
"\t\t\"json2\": {\n"
|
|
"\t\t\t\"primitive\": 123\n"
|
|
"\t\t},\n"
|
|
"\t\t\"json3\": {\n"
|
|
"\t\t}\n"
|
|
"\t},\n"
|
|
"\t\"json4\": {\n"
|
|
"\t\t\"primitive\": 123\n"
|
|
"\t}\n"
|
|
"}\n";
|
|
static const char *nested_dict_json_compact =
|
|
"{"
|
|
"\"json1\":{"
|
|
"\"json2\":{"
|
|
"\"primitive\":123"
|
|
"},"
|
|
"\"json3\":{"
|
|
"}"
|
|
"},"
|
|
"\"json4\":{"
|
|
"\"primitive\":123"
|
|
"}"
|
|
"}";
|
|
static const char *nested_dict_table =
|
|
"Dict 1\n"
|
|
" Dict 2\n"
|
|
" A primitive: 123\n"
|
|
" Dict 3\n"
|
|
"Dict 4\n"
|
|
" Another primitive: 123\n";
|
|
|
|
static void
|
|
emit_types(emitter_t *emitter) {
|
|
bool b = false;
|
|
int i = -123;
|
|
unsigned u = 123;
|
|
ssize_t zd = -456;
|
|
size_t zu = 456;
|
|
const char *str = "string";
|
|
const char *long_str =
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz";
|
|
uint32_t u32 = 789;
|
|
uint64_t u64 = 10000000000ULL;
|
|
|
|
emitter_begin(emitter);
|
|
emitter_kv(emitter, "k1", "K1", emitter_type_bool, &b);
|
|
emitter_kv(emitter, "k2", "K2", emitter_type_int, &i);
|
|
emitter_kv(emitter, "k3", "K3", emitter_type_unsigned, &u);
|
|
emitter_kv(emitter, "k4", "K4", emitter_type_ssize, &zd);
|
|
emitter_kv(emitter, "k5", "K5", emitter_type_size, &zu);
|
|
emitter_kv(emitter, "k6", "K6", emitter_type_string, &str);
|
|
emitter_kv(emitter, "k7", "K7", emitter_type_string, &long_str);
|
|
emitter_kv(emitter, "k8", "K8", emitter_type_uint32, &u32);
|
|
emitter_kv(emitter, "k9", "K9", emitter_type_uint64, &u64);
|
|
/*
|
|
* We don't test the title type, since it's only used for tables. It's
|
|
* tested in the emitter_table_row tests.
|
|
*/
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *types_json =
|
|
"{\n"
|
|
"\t\"k1\": false,\n"
|
|
"\t\"k2\": -123,\n"
|
|
"\t\"k3\": 123,\n"
|
|
"\t\"k4\": -456,\n"
|
|
"\t\"k5\": 456,\n"
|
|
"\t\"k6\": \"string\",\n"
|
|
"\t\"k7\": \"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz\",\n"
|
|
"\t\"k8\": 789,\n"
|
|
"\t\"k9\": 10000000000\n"
|
|
"}\n";
|
|
static const char *types_json_compact =
|
|
"{"
|
|
"\"k1\":false,"
|
|
"\"k2\":-123,"
|
|
"\"k3\":123,"
|
|
"\"k4\":-456,"
|
|
"\"k5\":456,"
|
|
"\"k6\":\"string\","
|
|
"\"k7\":\"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz\","
|
|
"\"k8\":789,"
|
|
"\"k9\":10000000000"
|
|
"}";
|
|
static const char *types_table =
|
|
"K1: false\n"
|
|
"K2: -123\n"
|
|
"K3: 123\n"
|
|
"K4: -456\n"
|
|
"K5: 456\n"
|
|
"K6: \"string\"\n"
|
|
"K7: \"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz "
|
|
"abcdefghijklmnopqrstuvwxyz\"\n"
|
|
"K8: 789\n"
|
|
"K9: 10000000000\n";
|
|
|
|
static void
|
|
emit_modal(emitter_t *emitter) {
|
|
int val = 123;
|
|
emitter_begin(emitter);
|
|
emitter_dict_begin(emitter, "j0", "T0");
|
|
emitter_json_key(emitter, "j1");
|
|
emitter_json_object_begin(emitter);
|
|
emitter_kv(emitter, "i1", "I1", emitter_type_int, &val);
|
|
emitter_json_kv(emitter, "i2", emitter_type_int, &val);
|
|
emitter_table_kv(emitter, "I3", emitter_type_int, &val);
|
|
emitter_table_dict_begin(emitter, "T1");
|
|
emitter_kv(emitter, "i4", "I4", emitter_type_int, &val);
|
|
emitter_json_object_end(emitter); /* Close j1 */
|
|
emitter_kv(emitter, "i5", "I5", emitter_type_int, &val);
|
|
emitter_table_dict_end(emitter); /* Close T1 */
|
|
emitter_kv(emitter, "i6", "I6", emitter_type_int, &val);
|
|
emitter_dict_end(emitter); /* Close j0 / T0 */
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
const char *modal_json =
|
|
"{\n"
|
|
"\t\"j0\": {\n"
|
|
"\t\t\"j1\": {\n"
|
|
"\t\t\t\"i1\": 123,\n"
|
|
"\t\t\t\"i2\": 123,\n"
|
|
"\t\t\t\"i4\": 123\n"
|
|
"\t\t},\n"
|
|
"\t\t\"i5\": 123,\n"
|
|
"\t\t\"i6\": 123\n"
|
|
"\t}\n"
|
|
"}\n";
|
|
const char *modal_json_compact =
|
|
"{"
|
|
"\"j0\":{"
|
|
"\"j1\":{"
|
|
"\"i1\":123,"
|
|
"\"i2\":123,"
|
|
"\"i4\":123"
|
|
"},"
|
|
"\"i5\":123,"
|
|
"\"i6\":123"
|
|
"}"
|
|
"}";
|
|
const char *modal_table =
|
|
"T0\n"
|
|
" I1: 123\n"
|
|
" I3: 123\n"
|
|
" T1\n"
|
|
" I4: 123\n"
|
|
" I5: 123\n"
|
|
" I6: 123\n";
|
|
|
|
static void
|
|
emit_json_array(emitter_t *emitter) {
|
|
int ival = 123;
|
|
|
|
emitter_begin(emitter);
|
|
emitter_json_key(emitter, "dict");
|
|
emitter_json_object_begin(emitter);
|
|
emitter_json_key(emitter, "arr");
|
|
emitter_json_array_begin(emitter);
|
|
emitter_json_object_begin(emitter);
|
|
emitter_json_kv(emitter, "foo", emitter_type_int, &ival);
|
|
emitter_json_object_end(emitter); /* Close arr[0] */
|
|
/* arr[1] and arr[2] are primitives. */
|
|
emitter_json_value(emitter, emitter_type_int, &ival);
|
|
emitter_json_value(emitter, emitter_type_int, &ival);
|
|
emitter_json_object_begin(emitter);
|
|
emitter_json_kv(emitter, "bar", emitter_type_int, &ival);
|
|
emitter_json_kv(emitter, "baz", emitter_type_int, &ival);
|
|
emitter_json_object_end(emitter); /* Close arr[3]. */
|
|
emitter_json_array_end(emitter); /* Close arr. */
|
|
emitter_json_object_end(emitter); /* Close dict. */
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *json_array_json =
|
|
"{\n"
|
|
"\t\"dict\": {\n"
|
|
"\t\t\"arr\": [\n"
|
|
"\t\t\t{\n"
|
|
"\t\t\t\t\"foo\": 123\n"
|
|
"\t\t\t},\n"
|
|
"\t\t\t123,\n"
|
|
"\t\t\t123,\n"
|
|
"\t\t\t{\n"
|
|
"\t\t\t\t\"bar\": 123,\n"
|
|
"\t\t\t\t\"baz\": 123\n"
|
|
"\t\t\t}\n"
|
|
"\t\t]\n"
|
|
"\t}\n"
|
|
"}\n";
|
|
static const char *json_array_json_compact =
|
|
"{"
|
|
"\"dict\":{"
|
|
"\"arr\":["
|
|
"{"
|
|
"\"foo\":123"
|
|
"},"
|
|
"123,"
|
|
"123,"
|
|
"{"
|
|
"\"bar\":123,"
|
|
"\"baz\":123"
|
|
"}"
|
|
"]"
|
|
"}"
|
|
"}";
|
|
static const char *json_array_table = "";
|
|
|
|
static void
|
|
emit_json_nested_array(emitter_t *emitter) {
|
|
int ival = 123;
|
|
char *sval = "foo";
|
|
emitter_begin(emitter);
|
|
emitter_json_array_begin(emitter);
|
|
emitter_json_array_begin(emitter);
|
|
emitter_json_value(emitter, emitter_type_int, &ival);
|
|
emitter_json_value(emitter, emitter_type_string, &sval);
|
|
emitter_json_value(emitter, emitter_type_int, &ival);
|
|
emitter_json_value(emitter, emitter_type_string, &sval);
|
|
emitter_json_array_end(emitter);
|
|
emitter_json_array_begin(emitter);
|
|
emitter_json_value(emitter, emitter_type_int, &ival);
|
|
emitter_json_array_end(emitter);
|
|
emitter_json_array_begin(emitter);
|
|
emitter_json_value(emitter, emitter_type_string, &sval);
|
|
emitter_json_value(emitter, emitter_type_int, &ival);
|
|
emitter_json_array_end(emitter);
|
|
emitter_json_array_begin(emitter);
|
|
emitter_json_array_end(emitter);
|
|
emitter_json_array_end(emitter);
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *json_nested_array_json =
|
|
"{\n"
|
|
"\t[\n"
|
|
"\t\t[\n"
|
|
"\t\t\t123,\n"
|
|
"\t\t\t\"foo\",\n"
|
|
"\t\t\t123,\n"
|
|
"\t\t\t\"foo\"\n"
|
|
"\t\t],\n"
|
|
"\t\t[\n"
|
|
"\t\t\t123\n"
|
|
"\t\t],\n"
|
|
"\t\t[\n"
|
|
"\t\t\t\"foo\",\n"
|
|
"\t\t\t123\n"
|
|
"\t\t],\n"
|
|
"\t\t[\n"
|
|
"\t\t]\n"
|
|
"\t]\n"
|
|
"}\n";
|
|
static const char *json_nested_array_json_compact =
|
|
"{"
|
|
"["
|
|
"["
|
|
"123,"
|
|
"\"foo\","
|
|
"123,"
|
|
"\"foo\""
|
|
"],"
|
|
"["
|
|
"123"
|
|
"],"
|
|
"["
|
|
"\"foo\","
|
|
"123"
|
|
"],"
|
|
"["
|
|
"]"
|
|
"]"
|
|
"}";
|
|
static const char *json_nested_array_table = "";
|
|
|
|
static void
|
|
emit_table_row(emitter_t *emitter) {
|
|
emitter_begin(emitter);
|
|
emitter_row_t row;
|
|
emitter_col_t abc = {
|
|
emitter_justify_left, 10, emitter_type_title};
|
|
abc.str_val = "ABC title";
|
|
emitter_col_t def = {
|
|
emitter_justify_right, 15, emitter_type_title};
|
|
def.str_val = "DEF title";
|
|
emitter_col_t ghi = {
|
|
emitter_justify_right, 5, emitter_type_title};
|
|
ghi.str_val = "GHI";
|
|
|
|
emitter_row_init(&row);
|
|
emitter_col_init(&abc, &row);
|
|
emitter_col_init(&def, &row);
|
|
emitter_col_init(&ghi, &row);
|
|
|
|
emitter_table_row(emitter, &row);
|
|
|
|
abc.type = emitter_type_int;
|
|
def.type = emitter_type_bool;
|
|
ghi.type = emitter_type_int;
|
|
|
|
abc.int_val = 123;
|
|
def.bool_val = true;
|
|
ghi.int_val = 456;
|
|
emitter_table_row(emitter, &row);
|
|
|
|
abc.int_val = 789;
|
|
def.bool_val = false;
|
|
ghi.int_val = 1011;
|
|
emitter_table_row(emitter, &row);
|
|
|
|
abc.type = emitter_type_string;
|
|
abc.str_val = "a string";
|
|
def.bool_val = false;
|
|
ghi.type = emitter_type_title;
|
|
ghi.str_val = "ghi";
|
|
emitter_table_row(emitter, &row);
|
|
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *table_row_json =
|
|
"{\n"
|
|
"}\n";
|
|
static const char *table_row_json_compact = "{}";
|
|
static const char *table_row_table =
|
|
"ABC title DEF title GHI\n"
|
|
"123 true 456\n"
|
|
"789 false 1011\n"
|
|
"\"a string\" false ghi\n";
|
|
|
|
/*
|
|
* emitter_row(): columns that carry a json_key render both to the aligned
|
|
* table (all columns) and, in JSON mode, to "json_key": value pairs in column
|
|
* order; columns without a json_key are table-only.
|
|
*/
|
|
static void
|
|
emit_row(emitter_t *emitter) {
|
|
emitter_begin(emitter);
|
|
|
|
emitter_row_t row;
|
|
emitter_row_init(&row);
|
|
|
|
/* Table-only column (no json_key set). */
|
|
emitter_col_t col_label;
|
|
emitter_col_init(&col_label, &row);
|
|
col_label.justify = emitter_justify_left;
|
|
col_label.width = 6;
|
|
col_label.type = emitter_type_title;
|
|
col_label.str_val = "row:";
|
|
|
|
/* Dual columns: rendered in both table and JSON. */
|
|
emitter_col_t col_x;
|
|
emitter_col_init(&col_x, &row);
|
|
col_x.justify = emitter_justify_right;
|
|
col_x.width = 6;
|
|
col_x.type = emitter_type_unsigned;
|
|
col_x.unsigned_val = 10;
|
|
col_x.json_key = "x";
|
|
|
|
emitter_col_t col_y;
|
|
emitter_col_init(&col_y, &row);
|
|
col_y.justify = emitter_justify_right;
|
|
col_y.width = 6;
|
|
col_y.type = emitter_type_unsigned;
|
|
col_y.unsigned_val = 20;
|
|
col_y.json_key = "y";
|
|
|
|
emitter_json_object_kv_begin(emitter, "obj");
|
|
emitter_row(emitter, &row);
|
|
emitter_json_object_end(emitter);
|
|
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *row_json =
|
|
"{\n"
|
|
"\t\"obj\": {\n"
|
|
"\t\t\"x\": 10,\n"
|
|
"\t\t\"y\": 20\n"
|
|
"\t}\n"
|
|
"}\n";
|
|
static const char *row_json_compact = "{\"obj\":{\"x\":10,\"y\":20}}";
|
|
static const char *row_table = "row: 10 20\n";
|
|
|
|
/*
|
|
* emitter_sparse_row(): the table side collapses runs of all-zero ("gap") rows
|
|
* to a single "---" separator (on a gap->non-gap transition and at a trailing
|
|
* gap), while the JSON side still emits every row's json-keyed columns.
|
|
*/
|
|
static void
|
|
emit_sparse_row(emitter_t *emitter) {
|
|
emitter_begin(emitter);
|
|
|
|
emitter_row_t row;
|
|
emitter_row_init(&row);
|
|
emitter_col_t col_v;
|
|
emitter_col_init(&col_v, &row);
|
|
col_v.justify = emitter_justify_right;
|
|
col_v.width = 6;
|
|
col_v.type = emitter_type_unsigned;
|
|
col_v.json_key = "v";
|
|
|
|
unsigned vals[] = {1, 0, 0, 2, 0, 0};
|
|
bool gap[] = {false, true, true, false, true, true};
|
|
|
|
emitter_json_array_kv_begin(emitter, "arr");
|
|
emitter_table_sparse_begin(emitter);
|
|
for (unsigned i = 0; i < sizeof(vals) / sizeof(vals[0]); i++) {
|
|
col_v.unsigned_val = vals[i];
|
|
emitter_json_object_begin(emitter);
|
|
emitter_sparse_row(emitter, &row, gap[i]);
|
|
emitter_json_object_end(emitter);
|
|
}
|
|
emitter_table_sparse_end(emitter);
|
|
emitter_json_array_end(emitter);
|
|
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *sparse_row_json =
|
|
"{\n"
|
|
"\t\"arr\": [\n"
|
|
"\t\t{\n"
|
|
"\t\t\t\"v\": 1\n"
|
|
"\t\t},\n"
|
|
"\t\t{\n"
|
|
"\t\t\t\"v\": 0\n"
|
|
"\t\t},\n"
|
|
"\t\t{\n"
|
|
"\t\t\t\"v\": 0\n"
|
|
"\t\t},\n"
|
|
"\t\t{\n"
|
|
"\t\t\t\"v\": 2\n"
|
|
"\t\t},\n"
|
|
"\t\t{\n"
|
|
"\t\t\t\"v\": 0\n"
|
|
"\t\t},\n"
|
|
"\t\t{\n"
|
|
"\t\t\t\"v\": 0\n"
|
|
"\t\t}\n"
|
|
"\t]\n"
|
|
"}\n";
|
|
static const char *sparse_row_json_compact =
|
|
"{\"arr\":[{\"v\":1},{\"v\":0},{\"v\":0},{\"v\":2},{\"v\":0},{\"v\":0}]}";
|
|
static const char *sparse_row_table =
|
|
" 1\n"
|
|
" ---\n"
|
|
" 2\n"
|
|
" ---\n";
|
|
|
|
typedef struct {
|
|
size_t size;
|
|
uint64_t count;
|
|
uint64_t prof_count;
|
|
const char *label;
|
|
uint64_t requests;
|
|
} col_table_test_row_t;
|
|
|
|
#define COL_TABLE_TEST_GETTER(name, member, value_member) \
|
|
static void \
|
|
col_table_test_get_##name(const void *vrow, emitter_col_t *col) { \
|
|
const col_table_test_row_t *row = vrow; \
|
|
col->value_member = row->member; \
|
|
}
|
|
COL_TABLE_TEST_GETTER(size, size, size_val)
|
|
COL_TABLE_TEST_GETTER(count, count, uint64_val)
|
|
COL_TABLE_TEST_GETTER(prof_count, prof_count, uint64_val)
|
|
COL_TABLE_TEST_GETTER(label, label, str_val)
|
|
COL_TABLE_TEST_GETTER(requests, requests, uint64_val)
|
|
#undef COL_TABLE_TEST_GETTER
|
|
|
|
enum {
|
|
COL_TABLE_TEST_SIZE,
|
|
COL_TABLE_TEST_COUNT,
|
|
COL_TABLE_TEST_PROF_COUNT,
|
|
COL_TABLE_TEST_LABEL,
|
|
COL_TABLE_TEST_REQUESTS,
|
|
COL_TABLE_TEST_NCOLS
|
|
};
|
|
|
|
#define COL_TABLE_TEST_FLAG_PROF (1U << 0)
|
|
|
|
static const emitter_col_desc_t col_table_test_descs[] = {
|
|
{NULL, "size", emitter_justify_right, 8, emitter_type_size, 0,
|
|
col_table_test_get_size},
|
|
{"count", "count", emitter_justify_right, 8, emitter_type_uint64,
|
|
0, col_table_test_get_count},
|
|
{"prof_count", "prof", emitter_justify_right, 6, emitter_type_uint64,
|
|
COL_TABLE_TEST_FLAG_PROF, col_table_test_get_prof_count},
|
|
{"label", "label", emitter_justify_right, 7, emitter_type_title, 0,
|
|
col_table_test_get_label},
|
|
{"requests", "requests", emitter_justify_right, 10,
|
|
emitter_type_uint64, 0, col_table_test_get_requests},
|
|
};
|
|
_Static_assert(
|
|
sizeof(col_table_test_descs) / sizeof(col_table_test_descs[0]) ==
|
|
COL_TABLE_TEST_NCOLS,
|
|
"col_table_test_descs must match COL_TABLE_TEST_NCOLS");
|
|
|
|
TEST_BEGIN(test_col_desc_flags) {
|
|
expect_true(emitter_col_desc_active(
|
|
&col_table_test_descs[COL_TABLE_TEST_COUNT], 0),
|
|
"A column without requirements should always be active");
|
|
expect_false(emitter_col_desc_active(
|
|
&col_table_test_descs[COL_TABLE_TEST_PROF_COUNT], 0),
|
|
"A column with an unmet requirement should be inactive");
|
|
expect_true(emitter_col_desc_active(
|
|
&col_table_test_descs[COL_TABLE_TEST_PROF_COUNT],
|
|
COL_TABLE_TEST_FLAG_PROF),
|
|
"A column with a satisfied requirement should be active");
|
|
}
|
|
TEST_END
|
|
|
|
static void
|
|
emit_col_table(emitter_t *emitter) {
|
|
col_table_test_row_t value = {42, 7, 100, "row", 9};
|
|
emitter_row_t row, header_row;
|
|
emitter_col_t cols[COL_TABLE_TEST_NCOLS];
|
|
emitter_col_t header_cols[COL_TABLE_TEST_NCOLS];
|
|
|
|
emitter_begin(emitter);
|
|
emitter_col_table_build(col_table_test_descs, COL_TABLE_TEST_NCOLS,
|
|
COL_TABLE_TEST_FLAG_PROF, &row, cols, &header_row, header_cols);
|
|
emitter_col_table_header(
|
|
emitter, &header_row, &header_cols[0], "mock:", "rows");
|
|
emitter_col_table_fill(col_table_test_descs, COL_TABLE_TEST_NCOLS,
|
|
COL_TABLE_TEST_FLAG_PROF, cols, &value);
|
|
emitter_json_object_begin(emitter);
|
|
emitter_col_table_emit_json(emitter, col_table_test_descs,
|
|
COL_TABLE_TEST_NCOLS, COL_TABLE_TEST_FLAG_PROF, cols);
|
|
emitter_json_object_end(emitter);
|
|
emitter_table_row(emitter, &row);
|
|
emitter_json_array_end(emitter);
|
|
emitter_end(emitter);
|
|
}
|
|
|
|
static const char *col_table_json =
|
|
"{\n"
|
|
"\t\"rows\": [\n"
|
|
"\t\t{\n"
|
|
"\t\t\t\"count\": 7,\n"
|
|
"\t\t\t\"prof_count\": 100,\n"
|
|
"\t\t\t\"label\": \"row\",\n"
|
|
"\t\t\t\"requests\": 9\n"
|
|
"\t\t}\n"
|
|
"\t]\n"
|
|
"}\n";
|
|
static const char *col_table_json_compact =
|
|
"{\"rows\":[{\"count\":7,\"prof_count\":100,\"label\":\"row\","
|
|
"\"requests\":9}]}";
|
|
static const char *col_table_table =
|
|
"mock:size count prof label requests\n"
|
|
" 42 7 100 row 9\n";
|
|
|
|
#define GENERATE_TEST(feature) \
|
|
TEST_BEGIN(test_##feature) { \
|
|
expect_emit_output(emit_##feature, feature##_json, \
|
|
feature##_json_compact, feature##_table); \
|
|
} \
|
|
TEST_END
|
|
|
|
GENERATE_TEST(dict)
|
|
GENERATE_TEST(table_printf)
|
|
GENERATE_TEST(nested_dict)
|
|
GENERATE_TEST(types)
|
|
GENERATE_TEST(modal)
|
|
GENERATE_TEST(json_array)
|
|
GENERATE_TEST(json_nested_array)
|
|
GENERATE_TEST(table_row)
|
|
GENERATE_TEST(row)
|
|
GENERATE_TEST(sparse_row)
|
|
GENERATE_TEST(col_table)
|
|
|
|
int
|
|
main(void) {
|
|
return test_no_reentrancy(test_dict, test_table_printf,
|
|
test_nested_dict, test_types, test_modal, test_json_array,
|
|
test_json_nested_array, test_table_row, test_row, test_sparse_row,
|
|
test_col_table, test_col_desc_flags);
|
|
}
|