@@ -19,7 +19,7 @@ typedef struct JsonOutputVisitor JsonOutputVisitor;
* The JSON output visitor does not accept Infinity or NaN to
* visit_type_number().
*/
-JsonOutputVisitor *json_output_visitor_new(void);
+JsonOutputVisitor *json_output_visitor_new(bool pretty);
void json_output_visitor_cleanup(JsonOutputVisitor *v);
void json_output_visitor_reset(JsonOutputVisitor *v);
@@ -1082,7 +1082,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only)
return;
}
- vmdesc_jov = json_output_visitor_new();
+ vmdesc_jov = json_output_visitor_new(false);
vmdesc = json_output_get_visitor(vmdesc_jov);
visit_start_struct(vmdesc, NULL, NULL, 0, &error_abort);
tmp_i = TARGET_PAGE_SIZE;
@@ -19,6 +19,7 @@
struct JsonOutputVisitor {
Visitor visitor;
QString *str;
+ bool pretty;
bool comma;
unsigned int depth;
};
@@ -34,10 +35,13 @@ static void json_output_name(JsonOutputVisitor *jov, const char *name)
jov->str = qstring_new();
}
if (jov->comma) {
- qstring_append(jov->str, ", ");
+ qstring_append(jov->str, jov->pretty ? "," : ", ");
} else {
jov->comma = true;
}
+ if (jov->pretty && jov->depth) {
+ qstring_append_format(jov->str, "\n%*s", 4 * jov->depth, "");
+ }
if (name && jov->depth) {
qstring_append_json_string(jov->str, name);
qstring_append(jov->str, ": ");
@@ -59,6 +63,9 @@ static void json_output_end_struct(Visitor *v)
JsonOutputVisitor *jov = to_jov(v);
assert(jov->depth);
jov->depth--;
+ if (jov->pretty) {
+ qstring_append_format(jov->str, "\n%*s", 4 * jov->depth, "");
+ }
qstring_append(jov->str, "}");
jov->comma = true;
}
@@ -85,6 +92,9 @@ static void json_output_end_list(Visitor *v)
JsonOutputVisitor *jov = to_jov(v);
assert(jov->depth);
jov->depth--;
+ if (jov->pretty) {
+ qstring_append_format(jov->str, "\n%*s", 4 * jov->depth, "");
+ }
qstring_append(jov->str, "]");
jov->comma = true;
}
@@ -134,7 +144,15 @@ static void json_output_type_any(Visitor *v, const char *name, QObject **obj,
Error **errp)
{
JsonOutputVisitor *jov = to_jov(v);
- QString *str = qobject_to_json(*obj);
+ QString *str;
+
+ if (jov->pretty) {
+ char *prefix = g_strdup_printf("%*s", 4 * jov->depth, "");
+ str = qobject_to_json_pretty_prefix(*obj, prefix);
+ g_free(prefix);
+ } else {
+ str = qobject_to_json(*obj);
+ }
assert(str);
json_output_name(jov, name);
qstring_append(jov->str, qstring_get_str(str));
@@ -178,11 +196,12 @@ void json_output_visitor_cleanup(JsonOutputVisitor *v)
g_free(v);
}
-JsonOutputVisitor *json_output_visitor_new(void)
+JsonOutputVisitor *json_output_visitor_new(bool pretty)
{
JsonOutputVisitor *v;
v = g_malloc0(sizeof(*v));
+ v->pretty = pretty;
v->visitor.type = VISITOR_OUTPUT;
v->visitor.start_struct = json_output_start_struct;
@@ -23,9 +23,10 @@ typedef struct TestOutputVisitorData {
} TestOutputVisitorData;
static void visitor_output_setup(TestOutputVisitorData *data,
- const void *unused)
+ const void *arg)
{
- data->jov = json_output_visitor_new();
+ const bool *pretty = arg;
+ data->jov = json_output_visitor_new(*pretty);
g_assert(data->jov);
data->ov = json_output_get_visitor(data->jov);
@@ -158,8 +159,9 @@ static void test_visitor_out_struct(TestOutputVisitorData *data,
}
static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
- const void *unused)
+ const void *arg)
{
+ const bool *pretty = arg;
int64_t value = 42;
UserDefTwo *ud2;
const char *string = "user def string";
@@ -189,27 +191,51 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
visit_type_UserDefTwo(data->ov, "unused", &ud2, &error_abort);
out = json_output_get_string(data->jov);
- g_assert_cmpstr(out, ==,
- "{"
- "\"string0\": \"forty two\", "
- "\"dict1\": {"
- "\"string1\": \"forty three\", "
- "\"dict2\": {"
- "\"userdef\": {"
- "\"integer\": 42, "
- "\"string\": \"user def string\""
- "}, "
- "\"string\": \"forty four\""
- "}, "
- "\"dict3\": {"
- "\"userdef\": {"
- "\"integer\": 42, "
- "\"string\": \"user def string\""
- "}, "
- "\"string\": \"forty five\""
- "}"
- "}"
- "}");
+ if (*pretty) {
+ g_assert_cmpstr(out, ==,
+ "{\n"
+ " \"string0\": \"forty two\",\n"
+ " \"dict1\": {\n"
+ " \"string1\": \"forty three\",\n"
+ " \"dict2\": {\n"
+ " \"userdef\": {\n"
+ " \"integer\": 42,\n"
+ " \"string\": \"user def string\"\n"
+ " },\n"
+ " \"string\": \"forty four\"\n"
+ " },\n"
+ " \"dict3\": {\n"
+ " \"userdef\": {\n"
+ " \"integer\": 42,\n"
+ " \"string\": \"user def string\"\n"
+ " },\n"
+ " \"string\": \"forty five\"\n"
+ " }\n"
+ " }\n"
+ "}");
+ } else {
+ g_assert_cmpstr(out, ==,
+ "{"
+ "\"string0\": \"forty two\", "
+ "\"dict1\": {"
+ "\"string1\": \"forty three\", "
+ "\"dict2\": {"
+ "\"userdef\": {"
+ "\"integer\": 42, "
+ "\"string\": \"user def string\""
+ "}, "
+ "\"string\": \"forty four\""
+ "}, "
+ "\"dict3\": {"
+ "\"userdef\": {"
+ "\"integer\": 42, "
+ "\"string\": \"user def string\""
+ "}, "
+ "\"string\": \"forty five\""
+ "}"
+ "}"
+ "}");
+ }
qapi_free_UserDefTwo(ud2);
g_free(out);
}
@@ -288,16 +314,23 @@ static void test_visitor_out_list(TestOutputVisitorData *data,
}
static void test_visitor_out_any(TestOutputVisitorData *data,
- const void *unused)
+ const void *arg)
{
+ const bool *pretty = arg;
QObject *qobj;
QDict *qdict;
char *out;
qobj = QOBJECT(qint_from_int(-42));
+ visit_start_list(data->ov, NULL, NULL, 0, &error_abort);
visit_type_any(data->ov, NULL, &qobj, &error_abort);
+ visit_end_list(data->ov);
out = json_output_get_string(data->jov);
- g_assert_cmpstr(out, ==, "-42");
+ if (*pretty) {
+ g_assert_cmpstr(out, ==, "[\n -42\n]");
+ } else {
+ g_assert_cmpstr(out, ==, "[-42]");
+ }
qobject_decref(qobj);
g_free(out);
@@ -306,15 +339,30 @@ static void test_visitor_out_any(TestOutputVisitorData *data,
qdict_put(qdict, "boolean", qbool_from_bool(true));
qdict_put(qdict, "string", qstring_from_str("foo"));
qobj = QOBJECT(qdict);
+ visit_start_list(data->ov, NULL, NULL, 0, &error_abort);
visit_type_any(data->ov, NULL, &qobj, &error_abort);
+ visit_end_list(data->ov);
qobject_decref(qobj);
out = json_output_get_string(data->jov);
- g_assert_cmpstr(out, ==,
- "{"
- "\"integer\": -42, "
- "\"boolean\": true, "
- "\"string\": \"foo\""
- "}");
+ if (*pretty) {
+ g_assert_cmpstr(out, ==,
+ "[\n"
+ " {\n"
+ " \"integer\": -42,\n"
+ " \"boolean\": true,\n"
+ " \"string\": \"foo\"\n"
+ " }\n"
+ "]");
+ } else {
+ g_assert_cmpstr(out, ==,
+ "["
+ "{"
+ "\"integer\": -42, "
+ "\"boolean\": true, "
+ "\"string\": \"foo\""
+ "}"
+ "]");
+ }
g_free(out);
}
@@ -380,37 +428,53 @@ static void test_visitor_out_null(TestOutputVisitorData *data,
g_free(out);
}
-static void output_visitor_test_add(const char *testpath,
+static void output_visitor_test_add(const char *testpath, bool *data,
void (*test_func)(TestOutputVisitorData *,
const void *))
{
- g_test_add(testpath, TestOutputVisitorData, NULL, visitor_output_setup,
+ g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup,
test_func, visitor_output_teardown);
}
int main(int argc, char **argv)
{
+ bool plain = false;
+ bool pretty = true;
+
g_test_init(&argc, &argv, NULL);
- output_visitor_test_add("/visitor/json/int", test_visitor_out_int);
- output_visitor_test_add("/visitor/json/bool", test_visitor_out_bool);
- output_visitor_test_add("/visitor/json/number", test_visitor_out_number);
- output_visitor_test_add("/visitor/json/string", test_visitor_out_string);
- output_visitor_test_add("/visitor/json/enum", test_visitor_out_enum);
- output_visitor_test_add("/visitor/json/enum-errors",
+ output_visitor_test_add("/visitor/json/int", &plain,
+ test_visitor_out_int);
+ output_visitor_test_add("/visitor/json/bool", &plain,
+ test_visitor_out_bool);
+ output_visitor_test_add("/visitor/json/number", &plain,
+ test_visitor_out_number);
+ output_visitor_test_add("/visitor/json/string", &plain,
+ test_visitor_out_string);
+ output_visitor_test_add("/visitor/json/enum", &plain,
+ test_visitor_out_enum);
+ output_visitor_test_add("/visitor/json/enum-errors", &plain,
test_visitor_out_enum_errors);
- output_visitor_test_add("/visitor/json/struct", test_visitor_out_struct);
- output_visitor_test_add("/visitor/json/struct-nested",
+ output_visitor_test_add("/visitor/json/struct", &plain,
+ test_visitor_out_struct);
+ output_visitor_test_add("/visitor/json/struct-nested", &plain,
test_visitor_out_struct_nested);
- output_visitor_test_add("/visitor/json/struct-errors",
+ output_visitor_test_add("/visitor/json-pretty/struct-nested", &pretty,
+ test_visitor_out_struct_nested);
+ output_visitor_test_add("/visitor/json/struct-errors", &plain,
test_visitor_out_struct_errors);
- output_visitor_test_add("/visitor/json/list", test_visitor_out_list);
- output_visitor_test_add("/visitor/json/any", test_visitor_out_any);
- output_visitor_test_add("/visitor/json/union-flat",
+ output_visitor_test_add("/visitor/json/list", &plain,
+ test_visitor_out_list);
+ output_visitor_test_add("/visitor/json/any", &plain,
+ test_visitor_out_any);
+ output_visitor_test_add("/visitor/json-pretty/any", &pretty,
+ test_visitor_out_any);
+ output_visitor_test_add("/visitor/json/union-flat", &plain,
test_visitor_out_union_flat);
- output_visitor_test_add("/visitor/json/alternate",
+ output_visitor_test_add("/visitor/json/alternate", &plain,
test_visitor_out_alternate);
- output_visitor_test_add("/visitor/json/null", test_visitor_out_null);
+ output_visitor_test_add("/visitor/json/null", &plain,
+ test_visitor_out_null);
g_test_run();
Similar to pretty printing in the QObject visitor. The trickiest parts are the fact that during type_any(), we have to coordinate with QObject to also print pretty; and the fact that the testsuite now has to honor parameterization on whether pretty printing is enabled. Signed-off-by: Eric Blake <eblake@redhat.com> --- v3: rebase to earlier changes v2: rebase to earlier changes --- include/qapi/json-output-visitor.h | 2 +- migration/savevm.c | 2 +- qapi/json-output-visitor.c | 25 +++++- tests/test-json-output-visitor.c | 160 ++++++++++++++++++++++++++----------- 4 files changed, 136 insertions(+), 53 deletions(-)