qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 07/11] qapi: add json output visitor


From: Eric Blake
Subject: [Qemu-devel] [PATCH 07/11] qapi: add json output visitor
Date: Thu, 10 Dec 2015 16:53:37 -0700

We have several places that want to go from qapi to JSON; right now,
they have to create an intermediate QObject to do the work.  That
also has the drawback that the JSON formatting of a QDict will
rearrange keys (according to a deterministic, but unpredictable,
hash), when humans have an easier time if dicts are produced in
the same order as the qapi type.

For these reasons, it is time to add a new JSON output visitor.
This patch just adds the basic visitor and tests that it works;
later patches will add pretty-printing, and convert clients to
use the visitor.

Design choices: Unlike the QMP output visitor, the JSON visitor
refuses to visit a required string with a NULL value (abort), as
well as a non-finite number (raises an error message).  Reusing
QString to grow the contents means that we easily share code with
both qobject-json.c and qjson.c; although it might be nice to
enhance things to take an optional output callback function so
that the output can truly be streamed instead of collected in
memory.

Signed-off-by: Eric Blake <address@hidden>
---
 include/qapi/json-output-visitor.h |  25 +++
 qapi/Makefile.objs                 |   2 +-
 qapi/json-output-visitor.c         | 202 ++++++++++++++++++
 tests/.gitignore                   |   1 +
 tests/Makefile                     |   4 +
 tests/test-json-output-visitor.c   | 410 +++++++++++++++++++++++++++++++++++++
 6 files changed, 643 insertions(+), 1 deletion(-)
 create mode 100644 include/qapi/json-output-visitor.h
 create mode 100644 qapi/json-output-visitor.c
 create mode 100644 tests/test-json-output-visitor.c

diff --git a/include/qapi/json-output-visitor.h 
b/include/qapi/json-output-visitor.h
new file mode 100644
index 0000000..5be5a13
--- /dev/null
+++ b/include/qapi/json-output-visitor.h
@@ -0,0 +1,25 @@
+/*
+ * JSON Output Visitor
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#ifndef JSON_OUTPUT_VISITOR_H
+#define JSON_OUTPUT_VISITOR_H
+
+#include "qapi/visitor.h"
+
+typedef struct JsonOutputVisitor JsonOutputVisitor;
+
+JsonOutputVisitor *json_output_visitor_new(void);
+void json_output_visitor_cleanup(JsonOutputVisitor *v);
+void json_output_visitor_reset(JsonOutputVisitor *v);
+
+char *json_output_get_string(JsonOutputVisitor *v);
+Visitor *json_output_get_visitor(JsonOutputVisitor *v);
+
+#endif
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 2278970..b60e11b 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -1,6 +1,6 @@
 util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
 util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
 util-obj-y += string-input-visitor.o string-output-visitor.o
-util-obj-y += opts-visitor.o
+util-obj-y += opts-visitor.o json-output-visitor.o
 util-obj-y += qmp-event.o
 util-obj-y += qapi-util.o
diff --git a/qapi/json-output-visitor.c b/qapi/json-output-visitor.c
new file mode 100644
index 0000000..0d759c4
--- /dev/null
+++ b/qapi/json-output-visitor.c
@@ -0,0 +1,202 @@
+/*
+ * Convert QAPI to JSON
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include "qapi/json-output-visitor.h"
+#include "qapi/visitor-impl.h"
+#include "qemu/queue.h"
+#include "qemu-common.h"
+#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qobject-json.h"
+
+struct JsonOutputVisitor {
+    Visitor visitor;
+    QString *str;
+    bool comma;
+    int depth;
+};
+
+static JsonOutputVisitor *to_jov(Visitor *v)
+{
+    return container_of(v, JsonOutputVisitor, visitor);
+}
+
+static void json_output_name(JsonOutputVisitor *jov, const char *name)
+{
+    if (!jov->str) {
+        jov->str = qstring_new();
+    }
+    if (jov->comma) {
+        qstring_append(jov->str, ", ");
+    } else {
+        jov->comma = true;
+    }
+    if (name && jov->depth) {
+        qstring_append_json_string(jov->str, name);
+        qstring_append(jov->str, ": ");
+    }
+}
+
+static bool json_output_start_struct(Visitor *v, void **obj, size_t unused,
+                                     const char *name, Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    json_output_name(jov, name);
+    qstring_append(jov->str, "{");
+    jov->comma = false;
+    jov->depth++;
+    return false;
+}
+
+static void json_output_end_struct(Visitor *v)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    assert(jov->depth);
+    jov->depth--;
+    qstring_append(jov->str, "}");
+    jov->comma = true;
+}
+
+static bool json_output_start_list(Visitor *v, GenericList **listp,
+                                   size_t size, const char *name, Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    json_output_name(jov, name);
+    qstring_append(jov->str, "[");
+    jov->comma = false;
+    jov->depth++;
+    return false;
+}
+
+static GenericList *json_output_next_list(Visitor *v, GenericList *list,
+                                          size_t size, Error **errp)
+{
+    return list->next;
+}
+
+static void json_output_end_list(Visitor *v)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    assert(jov->depth);
+    jov->depth--;
+    qstring_append(jov->str, "]");
+    jov->comma = true;
+}
+
+static void json_output_type_int64(Visitor *v, int64_t *obj, const char *name,
+                                   Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    json_output_name(jov, name);
+    qstring_append_format(jov->str, "%" PRId64, *obj);
+}
+
+static void json_output_type_uint64(Visitor *v, uint64_t *obj,
+                                    const char *name, Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    json_output_name(jov, name);
+    qstring_append_format(jov->str, "%" PRIu64, *obj);
+}
+
+static void json_output_type_bool(Visitor *v, bool *obj, const char *name,
+                                  Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    json_output_name(jov, name);
+    qstring_append(jov->str, *obj ? "true" : "false");
+}
+
+static void json_output_type_str(Visitor *v, char **obj, const char *name,
+                                 Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    assert(*obj);
+    json_output_name(jov, name);
+    qstring_append_json_string(jov->str, *obj);
+}
+
+static void json_output_type_number(Visitor *v, double *obj, const char *name,
+                                    Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    json_output_name(jov, name);
+    qstring_append_json_number(jov->str, *obj, errp);
+}
+
+static void json_output_type_any(Visitor *v, QObject **obj, const char *name,
+                                 Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    QString *str = qobject_to_json(*obj);
+    assert(str);
+    json_output_name(jov, name);
+    qstring_append(jov->str, qstring_get_str(str));
+    QDECREF(str);
+}
+
+static void json_output_type_null(Visitor *v, const char *name, Error **errp)
+{
+    JsonOutputVisitor *jov = to_jov(v);
+    json_output_name(jov, name);
+    qstring_append(jov->str, "null");
+}
+
+/* Finish building, and return the resulting string. Will not be NULL. */
+char *json_output_get_string(JsonOutputVisitor *jov)
+{
+    char *result;
+
+    assert(!jov->depth);
+    result = g_strdup(qstring_get_str(jov->str));
+    json_output_visitor_reset(jov);
+    return result;
+}
+
+Visitor *json_output_get_visitor(JsonOutputVisitor *v)
+{
+    return &v->visitor;
+}
+
+void json_output_visitor_reset(JsonOutputVisitor *v)
+{
+    QDECREF(v->str);
+    v->str = NULL;
+    v->depth = 0;
+    v->comma = false;
+}
+
+void json_output_visitor_cleanup(JsonOutputVisitor *v)
+{
+    json_output_visitor_reset(v);
+    g_free(v);
+}
+
+JsonOutputVisitor *json_output_visitor_new(void)
+{
+    JsonOutputVisitor *v;
+
+    v = g_malloc0(sizeof(*v));
+
+    v->visitor.start_struct = json_output_start_struct;
+    v->visitor.end_struct = json_output_end_struct;
+    v->visitor.start_list = json_output_start_list;
+    v->visitor.next_list = json_output_next_list;
+    v->visitor.end_list = json_output_end_list;
+    v->visitor.type_enum = output_type_enum;
+    v->visitor.type_int64 = json_output_type_int64;
+    v->visitor.type_uint64 = json_output_type_uint64;
+    v->visitor.type_bool = json_output_type_bool;
+    v->visitor.type_str = json_output_type_str;
+    v->visitor.type_number = json_output_type_number;
+    v->visitor.type_any = json_output_type_any;
+    v->visitor.type_null = json_output_type_null;
+
+    return v;
+}
diff --git a/tests/.gitignore b/tests/.gitignore
index 6d3416d..b4f371c 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -24,6 +24,7 @@ test-cutils
 test-hbitmap
 test-int128
 test-iov
+test-json-output-visitor
 test-mul64
 test-opts-visitor
 test-qapi-event.[ch]
diff --git a/tests/Makefile b/tests/Makefile
index 17d74e2..f617e2d 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -20,6 +20,8 @@ check-unit-y += tests/check-qobject-json$(EXESUF)
 gcov-files-check-qobject-json-y = qobject/qobject-json.c
 check-unit-y += tests/test-qmp-output-visitor$(EXESUF)
 gcov-files-test-qmp-output-visitor-y = qapi/qmp-output-visitor.c
+check-unit-y += tests/test-json-output-visitor$(EXESUF)
+gcov-files-test-json-output-visitor-y = qapi/json-output-visitor.c
 check-unit-y += tests/test-qmp-input-visitor$(EXESUF)
 gcov-files-test-qmp-input-visitor-y = qapi/qmp-input-visitor.c
 check-unit-y += tests/test-qmp-input-strict$(EXESUF)
@@ -363,6 +365,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o 
tests/check-qdict.o \
        tests/check-qlist.o tests/check-qfloat.o tests/check-qobject-json.o \
        tests/test-coroutine.o tests/test-string-output-visitor.o \
        tests/test-string-input-visitor.o tests/test-qmp-output-visitor.o \
+       tests/test-json-output-visitor.o \
        tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \
        tests/test-qmp-commands.o tests/test-visitor-serialization.o \
        tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
@@ -447,6 +450,7 @@ tests/test-string-output-visitor$(EXESUF): 
tests/test-string-output-visitor.o $(
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o 
$(test-qapi-obj-y)
 tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y)
 tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o 
$(test-qapi-obj-y)
+tests/test-json-output-visitor$(EXESUF): tests/test-json-output-visitor.o 
$(test-qapi-obj-y)
 tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o 
$(test-qapi-obj-y)
 tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o 
$(test-qapi-obj-y)
 tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o 
tests/test-qmp-marshal.o $(test-qapi-obj-y)
diff --git a/tests/test-json-output-visitor.c b/tests/test-json-output-visitor.c
new file mode 100644
index 0000000..e47571a
--- /dev/null
+++ b/tests/test-json-output-visitor.c
@@ -0,0 +1,410 @@
+/*
+ * JSON Output Visitor unit-tests.
+ *
+ * Copyright (C) 2011, 2015 Red Hat Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib.h>
+
+#include "qemu-common.h"
+#include "qapi/json-output-visitor.h"
+#include "test-qapi-types.h"
+#include "test-qapi-visit.h"
+#include "qapi/qmp/types.h"
+
+typedef struct TestOutputVisitorData {
+    JsonOutputVisitor *jov;
+    Visitor *ov;
+} TestOutputVisitorData;
+
+static void visitor_output_setup(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    data->jov = json_output_visitor_new();
+    g_assert(data->jov);
+
+    data->ov = json_output_get_visitor(data->jov);
+    g_assert(data->ov);
+}
+
+static void visitor_output_teardown(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    json_output_visitor_cleanup(data->jov);
+    data->jov = NULL;
+    data->ov = NULL;
+}
+
+static void test_visitor_out_int(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    int64_t value = -42;
+    char *out;
+
+    visit_type_int(data->ov, &value, NULL, &error_abort);
+
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "-42");
+    g_free(out);
+}
+
+static void test_visitor_out_bool(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    bool value = true;
+    char *out;
+
+    visit_type_bool(data->ov, &value, NULL, &error_abort);
+
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "true");
+    g_free(out);
+}
+
+static void test_visitor_out_number(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    double value = 3.14;
+    char *out;
+
+    visit_type_number(data->ov, &value, NULL, &error_abort);
+
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "3.14");
+    g_free(out);
+}
+
+static void test_visitor_out_string(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    char *string = (char *) "Q E M U";
+    char *out;
+
+    visit_type_str(data->ov, &string, NULL, &error_abort);
+
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "\"Q E M U\"");
+    g_free(out);
+}
+
+static void test_visitor_out_enum(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    char *out;
+    char *tmp;
+    EnumOne i;
+    size_t len;
+
+    for (i = 0; i < ENUM_ONE__MAX; i++) {
+        visit_type_EnumOne(data->ov, &i, "unused", &error_abort);
+
+        out = json_output_get_string(data->jov);
+        g_assert(*out == '"');
+        len = strlen(out);
+        g_assert(out[len - 1] == '"');
+        tmp = out + 1;
+        out[len - 1] = 0;
+        g_assert_cmpstr(tmp, ==, EnumOne_lookup[i]);
+        g_free(out);
+    }
+}
+
+static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
+                                         const void *unused)
+{
+    EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 };
+    Error *err;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+        err = NULL;
+        visit_type_EnumOne(data->ov, &bad_values[i], "unused", &err);
+        g_assert(err);
+        error_free(err);
+    }
+}
+
+
+static void test_visitor_out_struct(TestOutputVisitorData *data,
+                                    const void *unused)
+{
+    TestStruct test_struct = { .integer = 42,
+                               .boolean = false,
+                               .string = (char *) "foo"};
+    TestStruct *p = &test_struct;
+    char *out;
+
+    visit_type_TestStruct(data->ov, &p, NULL, &error_abort);
+
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==,
+                    "{"
+                     "\"integer\": 42, "
+                     "\"boolean\": false, "
+                     "\"string\": \"foo\""
+                    "}");
+    g_free(out);
+}
+
+static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
+                                           const void *unused)
+{
+    int64_t value = 42;
+    UserDefTwo *ud2;
+    const char *string = "user def string";
+    const char *strings[] = { "forty two", "forty three", "forty four",
+                              "forty five" };
+    char *out;
+
+    ud2 = g_malloc0(sizeof(*ud2));
+    ud2->string0 = g_strdup(strings[0]);
+
+    ud2->dict1 = g_malloc0(sizeof(*ud2->dict1));
+    ud2->dict1->string1 = g_strdup(strings[1]);
+
+    ud2->dict1->dict2 = g_malloc0(sizeof(*ud2->dict1->dict2));
+    ud2->dict1->dict2->userdef = g_new0(UserDefOne, 1);
+    ud2->dict1->dict2->userdef->string = g_strdup(string);
+    ud2->dict1->dict2->userdef->integer = value;
+    ud2->dict1->dict2->string = g_strdup(strings[2]);
+
+    ud2->dict1->dict3 = g_malloc0(sizeof(*ud2->dict1->dict3));
+    ud2->dict1->has_dict3 = true;
+    ud2->dict1->dict3->userdef = g_new0(UserDefOne, 1);
+    ud2->dict1->dict3->userdef->string = g_strdup(string);
+    ud2->dict1->dict3->userdef->integer = value;
+    ud2->dict1->dict3->string = g_strdup(strings[3]);
+
+    visit_type_UserDefTwo(data->ov, &ud2, "unused", &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\""
+                      "}"
+                     "}"
+                    "}");
+    qapi_free_UserDefTwo(ud2);
+    g_free(out);
+}
+
+static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
+                                           const void *unused)
+{
+    EnumOne bad_values[] = { ENUM_ONE__MAX, -1 };
+    UserDefOne u = { .string = (char *)"" };
+    UserDefOne *pu = &u;
+    Error *err;
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
+        err = NULL;
+        u.has_enum1 = true;
+        u.enum1 = bad_values[i];
+        visit_type_UserDefOne(data->ov, &pu, "unused", &err);
+        g_assert(err);
+        error_free(err);
+        json_output_visitor_reset(data->jov);
+    }
+}
+
+
+static void test_visitor_out_list(TestOutputVisitorData *data,
+                                  const void *unused)
+{
+    const char *value_str = "list value";
+    TestStructList *p, *head = NULL;
+    const int max_items = 10;
+    bool value_bool = true;
+    int value_int = 10;
+    int i;
+    char *out;
+
+    for (i = 0; i < max_items; i++) {
+        p = g_malloc0(sizeof(*p));
+        p->value = g_malloc0(sizeof(*p->value));
+        p->value->integer = value_int + (max_items - i - 1);
+        p->value->boolean = value_bool;
+        p->value->string = g_strdup(value_str);
+
+        p->next = head;
+        head = p;
+    }
+
+    visit_type_TestStructList(data->ov, &head, NULL, &error_abort);
+
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==,
+                    "["
+                     "{\"integer\": 10, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 11, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 12, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 13, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 14, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 15, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 16, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 17, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 18, \"boolean\": true, "
+                      "\"string\": \"list value\"}, "
+                     "{\"integer\": 19, \"boolean\": true, "
+                      "\"string\": \"list value\"}"
+                    "]");
+    qapi_free_TestStructList(head);
+    g_free(out);
+}
+
+static void test_visitor_out_any(TestOutputVisitorData *data,
+                                 const void *unused)
+{
+    QObject *qobj;
+    QDict *qdict;
+    char *out;
+
+    qobj = QOBJECT(qint_from_int(-42));
+    visit_type_any(data->ov, &qobj, NULL, &error_abort);
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "-42");
+    qobject_decref(qobj);
+    g_free(out);
+
+    qdict = qdict_new();
+    qdict_put(qdict, "integer", qint_from_int(-42));
+    qdict_put(qdict, "boolean", qbool_from_bool(true));
+    qdict_put(qdict, "string", qstring_from_str("foo"));
+    qobj = QOBJECT(qdict);
+    visit_type_any(data->ov, &qobj, NULL, &error_abort);
+    qobject_decref(qobj);
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==,
+                    "{"
+                     "\"integer\": -42, "
+                     "\"boolean\": true, "
+                     "\"string\": \"foo\""
+                    "}");
+    g_free(out);
+}
+
+static void test_visitor_out_union_flat(TestOutputVisitorData *data,
+                                        const void *unused)
+{
+    char *out;
+    UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion));
+
+    tmp->enum1 = ENUM_ONE_VALUE1;
+    tmp->string = g_strdup("str");
+    tmp->u.value1 = g_malloc0(sizeof(UserDefA));
+    tmp->integer = 41;
+    tmp->u.value1->boolean = true;
+
+    visit_type_UserDefFlatUnion(data->ov, &tmp, NULL, &error_abort);
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==,
+                    "{"
+                     "\"integer\": 41, "
+                     "\"string\": \"str\", "
+                     "\"enum1\": \"value1\", "
+                     "\"boolean\": true"
+                    "}");
+    g_free(out);
+    qapi_free_UserDefFlatUnion(tmp);
+}
+
+static void test_visitor_out_alternate(TestOutputVisitorData *data,
+                                       const void *unused)
+{
+    UserDefAlternate *tmp;
+    char *out;
+
+    tmp = g_new0(UserDefAlternate, 1);
+    tmp->type = QTYPE_QINT;
+    tmp->u.i = 42;
+
+    visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "42");
+    g_free(out);
+    qapi_free_UserDefAlternate(tmp);
+
+    tmp = g_new0(UserDefAlternate, 1);
+    tmp->type = QTYPE_QSTRING;
+    tmp->u.s = g_strdup("hello");
+
+    visit_type_UserDefAlternate(data->ov, &tmp, NULL, &error_abort);
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "\"hello\"");
+    g_free(out);
+    qapi_free_UserDefAlternate(tmp);
+}
+
+static void test_visitor_out_empty(TestOutputVisitorData *data,
+                                   const void *unused)
+{
+    char *out;
+
+    visit_type_null(data->ov, NULL, &error_abort);
+    out = json_output_get_string(data->jov);
+    g_assert_cmpstr(out, ==, "null");
+    g_free(out);
+}
+
+static void output_visitor_test_add(const char *testpath,
+                                    void (*test_func)(TestOutputVisitorData *,
+                                                      const void *))
+{
+    g_test_add(testpath, TestOutputVisitorData, NULL, visitor_output_setup,
+               test_func, visitor_output_teardown);
+}
+
+int main(int argc, char **argv)
+{
+    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",
+                            test_visitor_out_enum_errors);
+    output_visitor_test_add("/visitor/json/struct", test_visitor_out_struct);
+    output_visitor_test_add("/visitor/json/struct-nested",
+                            test_visitor_out_struct_nested);
+    output_visitor_test_add("/visitor/json/struct-errors",
+                            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",
+                            test_visitor_out_union_flat);
+    output_visitor_test_add("/visitor/json/alternate",
+                            test_visitor_out_alternate);
+    output_visitor_test_add("/visitor/json/empty", test_visitor_out_empty);
+
+    g_test_run();
+
+    return 0;
+}
-- 
2.4.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]