qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Eric Blake <eblake@redhat.com>
To: qemu-devel@nongnu.org
Cc: Amit Shah <amit.shah@redhat.com>,
	pbonzini@redhat.com, Juan Quintela <quintela@redhat.com>,
	armbru@redhat.com, Michael Roth <mdroth@linux.vnet.ibm.com>
Subject: [Qemu-devel] [PATCH v2 13/14] qapi: Support pretty printing in JSON output visitor
Date: Mon, 21 Dec 2015 17:31:07 -0700	[thread overview]
Message-ID: <1450744268-25052-14-git-send-email-eblake@redhat.com> (raw)
In-Reply-To: <1450744268-25052-1-git-send-email-eblake@redhat.com>

Similar to pretty printing in the QObject visitor.  The rickiest
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>

---
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(-)

diff --git a/include/qapi/json-output-visitor.h b/include/qapi/json-output-visitor.h
index 5be5a13..e5e16e6 100644
--- a/include/qapi/json-output-visitor.h
+++ b/include/qapi/json-output-visitor.h
@@ -15,7 +15,7 @@

 typedef struct JsonOutputVisitor JsonOutputVisitor;

-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);

diff --git a/migration/savevm.c b/migration/savevm.c
index a739b8b..9a05a3d 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1077,7 +1077,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;
diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
index 8183b85..0c5be38 100644
--- a/qapi/json-output-visitor.c
+++ b/qapi/json-output-visitor.c
@@ -18,6 +18,7 @@
 struct JsonOutputVisitor {
     Visitor visitor;
     QString *str;
+    bool pretty;
     bool comma;
     unsigned int depth;
 };
@@ -33,10 +34,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;
 }
@@ -86,6 +93,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;
 }
@@ -135,7 +145,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));
@@ -179,11 +197,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.start_struct = json_output_start_struct;
     v->visitor.end_struct = json_output_end_struct;
diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
index f884a03..0ddb7da 100644
--- a/tests/test-json-output-visitor.c
+++ b/tests/test-json-output-visitor.c
@@ -22,9 +22,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);
@@ -157,8 +158,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";
@@ -188,27 +190,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);
 }
@@ -287,16 +313,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);

@@ -305,15 +338,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_empty(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/empty", test_visitor_out_empty);
+    output_visitor_test_add("/visitor/json/empty", &plain,
+                            test_visitor_out_empty);

     g_test_run();

-- 
2.4.3

  parent reply	other threads:[~2015-12-22  0:31 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-12-22  0:30 [Qemu-devel] [PATCH v2 00/14] Add qapi-to-JSON output visitor Eric Blake
2015-12-22  0:30 ` [Qemu-devel] [PATCH v2 01/14] qapi: Rename (one) qjson.h to qobject-json.h Eric Blake
2015-12-22  0:30 ` [Qemu-devel] [PATCH v2 02/14] qapi: Improve use of qmp/types.h Eric Blake
2015-12-23  7:14   ` Fam Zheng
2015-12-22  0:30 ` [Qemu-devel] [PATCH v2 03/14] qapi: Factor out JSON string escaping Eric Blake
2015-12-23  7:20   ` Fam Zheng
2015-12-22  0:30 ` [Qemu-devel] [PATCH v2 04/14] qapi: Factor out JSON number formatting Eric Blake
2015-12-23  7:32   ` Fam Zheng
2015-12-22  0:30 ` [Qemu-devel] [PATCH v2 05/14] qapi: Use qstring_append_chr() where appropriate Eric Blake
2015-12-23  7:32   ` Fam Zheng
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 06/14] qapi: Add qstring_append_format() Eric Blake
2015-12-23  8:31   ` Fam Zheng
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 07/14] qapi: add json output visitor Eric Blake
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 08/14] qjson: Simplify by using json-output-visitor Eric Blake
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 09/14] Revert "qjson: Simplify by using json-output-visitor" Eric Blake
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 10/14] vmstate: use new JSON output visitor Eric Blake
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 11/14] qjson: Remove unused file Eric Blake
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 12/14] qapi: Add qobject_to_json_pretty_prefix() Eric Blake
2015-12-22  0:31 ` Eric Blake [this message]
2015-12-23  9:24   ` [Qemu-devel] [PATCH v2 13/14] qapi: Support pretty printing in JSON output visitor Fam Zheng
2015-12-23 16:27     ` Eric Blake
2015-12-22  0:31 ` [Qemu-devel] [PATCH v2 14/14] qemu-img: Use new JSON output formatter Eric Blake
2015-12-23 20:57 ` [Qemu-devel] [PATCH v2 00/14] Add qapi-to-JSON output visitor Eric Blake

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1450744268-25052-14-git-send-email-eblake@redhat.com \
    --to=eblake@redhat.com \
    --cc=amit.shah@redhat.com \
    --cc=armbru@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=quintela@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).